From a4dd0ad63c00f4dee3b86dfd3075d1d61b2b3180 Mon Sep 17 00:00:00 2001 From: sanine Date: Sat, 27 Aug 2022 23:52:56 -0500 Subject: add plibsys --- 3rdparty/plibsys/.codedocs | 53 + 3rdparty/plibsys/.gear/plibsys.spec | 104 ++ 3rdparty/plibsys/.gear/rules | 2 + 3rdparty/plibsys/.travis.yml | 171 ++ .../plibsys/3dparty/scosv/FSU-threads-3.14.tgz | Bin 0 -> 320682 bytes 3rdparty/plibsys/AUTHORS | 2 + 3rdparty/plibsys/CMakeLists.txt | 93 ++ 3rdparty/plibsys/COPYING | 20 + 3rdparty/plibsys/Doxyfile.in | 180 +++ 3rdparty/plibsys/NEWS | 122 ++ 3rdparty/plibsys/README.md | 115 ++ 3rdparty/plibsys/appveyor.yml | 289 ++++ 3rdparty/plibsys/cmake/PlatformDetect.cmake | 144 ++ 3rdparty/plibsys/cmake/StdargDetect.cmake | 74 + 3rdparty/plibsys/cmake/ThreadNameDetect.cmake | 135 ++ 3rdparty/plibsys/cmake/VisibilityDetect.cmake | 127 ++ 3rdparty/plibsys/codecov.yml | 7 + 3rdparty/plibsys/platforms/aix-gcc/platform.cmake | 13 + .../platforms/amigaos-gcc/Platform/AmigaOS.cmake | 2 + .../plibsys/platforms/amigaos-gcc/amigaos.cmake | 90 ++ .../plibsys/platforms/amigaos-gcc/platform.cmake | 12 + .../plibsys/platforms/android-clang/platform.cmake | 11 + .../plibsys/platforms/android-gcc/platform.cmake | 11 + .../plibsys/platforms/bb10-qcc/blackberry.cmake | 141 ++ 3rdparty/plibsys/platforms/beos-gcc/platform.cmake | 12 + 3rdparty/plibsys/platforms/common/HPUX.cmake | 15 + 3rdparty/plibsys/platforms/common/SCOSV.cmake | 9 + .../plibsys/platforms/cygwin-clang/platform.cmake | 12 + .../plibsys/platforms/cygwin-gcc/platform.cmake | 12 + .../plibsys/platforms/darwin-clang/platform.cmake | 9 + .../plibsys/platforms/darwin-gcc/platform.cmake | 9 + .../plibsys/platforms/darwin-icc/platform.cmake | 11 + .../plibsys/platforms/darwin-pgi/platform.cmake | 27 + .../platforms/dragonfly-clang/platform.cmake | 11 + .../plibsys/platforms/dragonfly-gcc/platform.cmake | 11 + .../plibsys/platforms/freebsd-clang/platform.cmake | 11 + .../plibsys/platforms/freebsd-gcc/platform.cmake | 11 + .../plibsys/platforms/haiku-clang/platform.cmake | 7 + .../plibsys/platforms/haiku-gcc/platform.cmake | 7 + 3rdparty/plibsys/platforms/hpux-gcc/platform.cmake | 16 + 3rdparty/plibsys/platforms/ios-clang/ios.cmake | 419 +++++ .../plibsys/platforms/ios-clang/platform.cmake | 9 + .../plibsys/platforms/irix64-gcc/platform.cmake | 13 + .../platforms/irix64-mipspro/platform.cmake | 13 + .../plibsys/platforms/linux-clang/platform.cmake | 12 + .../plibsys/platforms/linux-cray/platform.cmake | 18 + .../plibsys/platforms/linux-gcc/platform.cmake | 12 + .../plibsys/platforms/linux-icc/platform.cmake | 12 + .../plibsys/platforms/linux-pgi/platform.cmake | 12 + .../plibsys/platforms/linux-xlc/platform.cmake | 11 + .../plibsys/platforms/msys-clang/platform.cmake | 12 + 3rdparty/plibsys/platforms/msys-gcc/platform.cmake | 12 + .../plibsys/platforms/netbsd-clang/platform.cmake | 11 + .../plibsys/platforms/netbsd-gcc/platform.cmake | 11 + .../plibsys/platforms/openbsd-clang/platform.cmake | 11 + .../plibsys/platforms/openbsd-gcc/platform.cmake | 11 + 3rdparty/plibsys/platforms/os2-gcc/platform.cmake | 12 + 3rdparty/plibsys/platforms/qnx-qcc/platform.cmake | 12 + .../plibsys/platforms/scosv-gcc/platform.cmake | 27 + .../plibsys/platforms/sunos-gcc/platform.cmake | 12 + .../plibsys/platforms/sunos-sunpro/platform.cmake | 12 + .../plibsys/platforms/syllable-gcc/platform.cmake | 12 + .../plibsys/platforms/tru64-compaq/platform.cmake | 17 + .../plibsys/platforms/tru64-gcc/platform.cmake | 16 + .../plibsys/platforms/unixware-gcc/platform.cmake | 11 + 3rdparty/plibsys/platforms/vms-general/README.md | 49 + .../plibsys/platforms/vms-general/build_vms.com | 616 ++++++++ 3rdparty/plibsys/platforms/vms-general/plibsys.opt | 540 +++++++ .../platforms/vms-general/vms_shorten_symbol.c | 238 +++ .../plibsys/platforms/win32-borland/platform.cmake | 8 + .../plibsys/platforms/win32-clang/platform.cmake | 8 + .../plibsys/platforms/win32-gcc/platform.cmake | 7 + .../plibsys/platforms/win32-icc/platform.cmake | 7 + .../plibsys/platforms/win32-msvc/platform.cmake | 7 + .../plibsys/platforms/win32-watcom/platform.cmake | 11 + .../plibsys/platforms/win64-clang/platform.cmake | 8 + .../plibsys/platforms/win64-gcc/platform.cmake | 7 + .../plibsys/platforms/win64-icc/platform.cmake | 7 + .../plibsys/platforms/win64-msvc/platform.cmake | 7 + 3rdparty/plibsys/scripts/run_tests.sh | 70 + 3rdparty/plibsys/src/CMakeLists.txt | 996 ++++++++++++ 3rdparty/plibsys/src/patomic-c11.c | 182 +++ 3rdparty/plibsys/src/patomic-decc.c | 286 ++++ 3rdparty/plibsys/src/patomic-sim.c | 268 ++++ 3rdparty/plibsys/src/patomic-sync.c | 190 +++ 3rdparty/plibsys/src/patomic-win.c | 272 ++++ 3rdparty/plibsys/src/patomic.h | 310 ++++ 3rdparty/plibsys/src/pcondvariable-amiga.c | 230 +++ 3rdparty/plibsys/src/pcondvariable-atheos.c | 234 +++ 3rdparty/plibsys/src/pcondvariable-beos.c | 233 +++ 3rdparty/plibsys/src/pcondvariable-none.c | 80 + 3rdparty/plibsys/src/pcondvariable-os2.c | 163 ++ 3rdparty/plibsys/src/pcondvariable-posix.c | 121 ++ 3rdparty/plibsys/src/pcondvariable-solaris.c | 122 ++ 3rdparty/plibsys/src/pcondvariable-win.c | 312 ++++ 3rdparty/plibsys/src/pcondvariable.h | 138 ++ 3rdparty/plibsys/src/pcryptohash-gost3411.c | 484 ++++++ 3rdparty/plibsys/src/pcryptohash-gost3411.h | 53 + 3rdparty/plibsys/src/pcryptohash-md5.c | 273 ++++ 3rdparty/plibsys/src/pcryptohash-md5.h | 51 + 3rdparty/plibsys/src/pcryptohash-sha1.c | 321 ++++ 3rdparty/plibsys/src/pcryptohash-sha1.h | 51 + 3rdparty/plibsys/src/pcryptohash-sha2-256.c | 286 ++++ 3rdparty/plibsys/src/pcryptohash-sha2-256.h | 59 + 3rdparty/plibsys/src/pcryptohash-sha2-512.c | 300 ++++ 3rdparty/plibsys/src/pcryptohash-sha2-512.h | 59 + 3rdparty/plibsys/src/pcryptohash-sha3.c | 297 ++++ 3rdparty/plibsys/src/pcryptohash-sha3.h | 79 + 3rdparty/plibsys/src/pcryptohash.c | 250 +++ 3rdparty/plibsys/src/pcryptohash.h | 188 +++ 3rdparty/plibsys/src/pdir-none.c | 124 ++ 3rdparty/plibsys/src/pdir-os2.c | 381 +++++ 3rdparty/plibsys/src/pdir-posix.c | 378 +++++ 3rdparty/plibsys/src/pdir-win.c | 291 ++++ 3rdparty/plibsys/src/pdir.c | 37 + 3rdparty/plibsys/src/pdir.h | 177 +++ 3rdparty/plibsys/src/perror-private.h | 67 + 3rdparty/plibsys/src/perror.c | 878 +++++++++++ 3rdparty/plibsys/src/perror.h | 249 +++ 3rdparty/plibsys/src/perrortypes.h | 106 ++ 3rdparty/plibsys/src/pfile.c | 80 + 3rdparty/plibsys/src/pfile.h | 87 ++ 3rdparty/plibsys/src/phashtable.c | 243 +++ 3rdparty/plibsys/src/phashtable.h | 167 ++ 3rdparty/plibsys/src/pinifile.c | 503 ++++++ 3rdparty/plibsys/src/pinifile.h | 261 ++++ 3rdparty/plibsys/src/pipc-private.h | 76 + 3rdparty/plibsys/src/pipc.c | 178 +++ 3rdparty/plibsys/src/plibraryloader-amiga.c | 583 +++++++ 3rdparty/plibsys/src/plibraryloader-beos.c | 142 ++ 3rdparty/plibsys/src/plibraryloader-none.c | 75 + 3rdparty/plibsys/src/plibraryloader-os2.c | 155 ++ 3rdparty/plibsys/src/plibraryloader-posix.c | 148 ++ 3rdparty/plibsys/src/plibraryloader-shl.c | 140 ++ 3rdparty/plibsys/src/plibraryloader-win.c | 140 ++ 3rdparty/plibsys/src/plibraryloader.h | 155 ++ 3rdparty/plibsys/src/plibsys-private.h | 86 + 3rdparty/plibsys/src/plibsys.h | 64 + 3rdparty/plibsys/src/plibsysconfig.h.in | 76 + 3rdparty/plibsys/src/plist.c | 174 +++ 3rdparty/plibsys/src/plist.h | 199 +++ 3rdparty/plibsys/src/pmacros.h | 302 ++++ 3rdparty/plibsys/src/pmacroscompiler.h | 253 +++ 3rdparty/plibsys/src/pmacroscpu.h | 627 ++++++++ 3rdparty/plibsys/src/pmacrosos.h | 500 ++++++ 3rdparty/plibsys/src/pmain.c | 132 ++ 3rdparty/plibsys/src/pmain.h | 209 +++ 3rdparty/plibsys/src/pmem.c | 357 +++++ 3rdparty/plibsys/src/pmem.h | 191 +++ 3rdparty/plibsys/src/pmutex-amiga.c | 101 ++ 3rdparty/plibsys/src/pmutex-atheos.c | 111 ++ 3rdparty/plibsys/src/pmutex-beos.c | 110 ++ 3rdparty/plibsys/src/pmutex-none.c | 66 + 3rdparty/plibsys/src/pmutex-os2.c | 112 ++ 3rdparty/plibsys/src/pmutex-posix.c | 104 ++ 3rdparty/plibsys/src/pmutex-solaris.c | 105 ++ 3rdparty/plibsys/src/pmutex-win.c | 92 ++ 3rdparty/plibsys/src/pmutex.h | 136 ++ 3rdparty/plibsys/src/pprocess.c | 61 + 3rdparty/plibsys/src/pprocess.h | 69 + 3rdparty/plibsys/src/prwlock-general.c | 326 ++++ 3rdparty/plibsys/src/prwlock-none.c | 103 ++ 3rdparty/plibsys/src/prwlock-posix.c | 151 ++ 3rdparty/plibsys/src/prwlock-solaris.c | 151 ++ 3rdparty/plibsys/src/prwlock-win.c | 548 +++++++ 3rdparty/plibsys/src/prwlock.h | 185 +++ 3rdparty/plibsys/src/psemaphore-amiga.c | 250 +++ 3rdparty/plibsys/src/psemaphore-none.c | 91 ++ 3rdparty/plibsys/src/psemaphore-os2.c | 91 ++ 3rdparty/plibsys/src/psemaphore-posix.c | 272 ++++ 3rdparty/plibsys/src/psemaphore-sysv.c | 319 ++++ 3rdparty/plibsys/src/psemaphore-win.c | 204 +++ 3rdparty/plibsys/src/psemaphore.h | 191 +++ 3rdparty/plibsys/src/pshm-amiga.c | 274 ++++ 3rdparty/plibsys/src/pshm-none.c | 94 ++ 3rdparty/plibsys/src/pshm-os2.c | 350 +++++ 3rdparty/plibsys/src/pshm-posix.c | 311 ++++ 3rdparty/plibsys/src/pshm-sysv.c | 307 ++++ 3rdparty/plibsys/src/pshm-win.c | 262 ++++ 3rdparty/plibsys/src/pshm.h | 195 +++ 3rdparty/plibsys/src/pshmbuffer.c | 334 ++++ 3rdparty/plibsys/src/pshmbuffer.h | 191 +++ 3rdparty/plibsys/src/psocket.c | 1644 ++++++++++++++++++++ 3rdparty/plibsys/src/psocket.h | 779 ++++++++++ 3rdparty/plibsys/src/psocketaddress.c | 619 ++++++++ 3rdparty/plibsys/src/psocketaddress.h | 284 ++++ 3rdparty/plibsys/src/pspinlock-c11.c | 103 ++ 3rdparty/plibsys/src/pspinlock-decc.c | 87 ++ 3rdparty/plibsys/src/pspinlock-sim.c | 88 ++ 3rdparty/plibsys/src/pspinlock-sync.c | 83 + 3rdparty/plibsys/src/pspinlock-win.c | 83 + 3rdparty/plibsys/src/pspinlock.h | 142 ++ 3rdparty/plibsys/src/pstdarg.h | 318 ++++ 3rdparty/plibsys/src/pstring.c | 206 +++ 3rdparty/plibsys/src/pstring.h | 117 ++ 3rdparty/plibsys/src/psysclose-darwin.c | 93 ++ 3rdparty/plibsys/src/psysclose-private.h | 47 + 3rdparty/plibsys/src/psysclose-unix.c | 54 + 3rdparty/plibsys/src/psysclose-win.c | 33 + 3rdparty/plibsys/src/ptimeprofiler-amiga.c | 70 + 3rdparty/plibsys/src/ptimeprofiler-beos.c | 51 + 3rdparty/plibsys/src/ptimeprofiler-generic.c | 58 + 3rdparty/plibsys/src/ptimeprofiler-mach.c | 73 + 3rdparty/plibsys/src/ptimeprofiler-os2.c | 107 ++ 3rdparty/plibsys/src/ptimeprofiler-posix.c | 109 ++ 3rdparty/plibsys/src/ptimeprofiler-private.h | 45 + 3rdparty/plibsys/src/ptimeprofiler-solaris.c | 53 + 3rdparty/plibsys/src/ptimeprofiler-win.c | 169 ++ 3rdparty/plibsys/src/ptimeprofiler.c | 70 + 3rdparty/plibsys/src/ptimeprofiler.h | 93 ++ 3rdparty/plibsys/src/ptree-avl.c | 481 ++++++ 3rdparty/plibsys/src/ptree-avl.h | 58 + 3rdparty/plibsys/src/ptree-bst.c | 140 ++ 3rdparty/plibsys/src/ptree-bst.h | 58 + 3rdparty/plibsys/src/ptree-private.h | 48 + 3rdparty/plibsys/src/ptree-rb.c | 484 ++++++ 3rdparty/plibsys/src/ptree-rb.h | 58 + 3rdparty/plibsys/src/ptree.c | 315 ++++ 3rdparty/plibsys/src/ptree.h | 230 +++ 3rdparty/plibsys/src/ptypes.h | 1122 +++++++++++++ 3rdparty/plibsys/src/puthread-amiga.c | 673 ++++++++ 3rdparty/plibsys/src/puthread-atheos.c | 317 ++++ 3rdparty/plibsys/src/puthread-beos.c | 431 +++++ 3rdparty/plibsys/src/puthread-none.c | 142 ++ 3rdparty/plibsys/src/puthread-os2.c | 458 ++++++ 3rdparty/plibsys/src/puthread-posix.c | 580 +++++++ 3rdparty/plibsys/src/puthread-private.h | 53 + 3rdparty/plibsys/src/puthread-solaris.c | 352 +++++ 3rdparty/plibsys/src/puthread-win.c | 511 ++++++ 3rdparty/plibsys/src/puthread.c | 558 +++++++ 3rdparty/plibsys/src/puthread.h | 311 ++++ 3rdparty/plibsys/tests/CMakeLists.txt | 96 ++ 3rdparty/plibsys/tests/patomic_test.cpp | 118 ++ 3rdparty/plibsys/tests/pcondvariable_test.cpp | 230 +++ 3rdparty/plibsys/tests/pcryptohash_test.cpp | 662 ++++++++ 3rdparty/plibsys/tests/pdir_test.cpp | 248 +++ 3rdparty/plibsys/tests/perror_test.cpp | 236 +++ 3rdparty/plibsys/tests/pfile_test.cpp | 61 + 3rdparty/plibsys/tests/phashtable_test.cpp | 320 ++++ 3rdparty/plibsys/tests/pinifile_test.cpp | 357 +++++ 3rdparty/plibsys/tests/plibraryloader_test.cpp | 179 +++ 3rdparty/plibsys/tests/plist_test.cpp | 200 +++ 3rdparty/plibsys/tests/pmacros_test.cpp | 646 ++++++++ 3rdparty/plibsys/tests/pmain_test.cpp | 111 ++ 3rdparty/plibsys/tests/pmem_test.cpp | 171 ++ 3rdparty/plibsys/tests/pmutex_test.cpp | 146 ++ 3rdparty/plibsys/tests/pprocess_test.cpp | 49 + 3rdparty/plibsys/tests/prwlock_test.cpp | 224 +++ 3rdparty/plibsys/tests/psemaphore_test.cpp | 226 +++ 3rdparty/plibsys/tests/pshm_test.cpp | 313 ++++ 3rdparty/plibsys/tests/pshmbuffer_test.cpp | 352 +++++ 3rdparty/plibsys/tests/psocket_test.cpp | 1131 ++++++++++++++ 3rdparty/plibsys/tests/psocketaddress_test.cpp | 338 ++++ 3rdparty/plibsys/tests/pspinlock_test.cpp | 148 ++ 3rdparty/plibsys/tests/pstdarg_test.cpp | 205 +++ 3rdparty/plibsys/tests/pstring_test.cpp | 289 ++++ 3rdparty/plibsys/tests/ptestmacros.h | 131 ++ 3rdparty/plibsys/tests/ptimeprofiler_test.cpp | 126 ++ 3rdparty/plibsys/tests/ptree_test.cpp | 633 ++++++++ 3rdparty/plibsys/tests/ptypes_test.cpp | 456 ++++++ 3rdparty/plibsys/tests/puthread_test.cpp | 481 ++++++ 261 files changed, 48515 insertions(+) create mode 100644 3rdparty/plibsys/.codedocs create mode 100644 3rdparty/plibsys/.gear/plibsys.spec create mode 100644 3rdparty/plibsys/.gear/rules create mode 100644 3rdparty/plibsys/.travis.yml create mode 100644 3rdparty/plibsys/3dparty/scosv/FSU-threads-3.14.tgz create mode 100644 3rdparty/plibsys/AUTHORS create mode 100644 3rdparty/plibsys/CMakeLists.txt create mode 100644 3rdparty/plibsys/COPYING create mode 100644 3rdparty/plibsys/Doxyfile.in create mode 100644 3rdparty/plibsys/NEWS create mode 100644 3rdparty/plibsys/README.md create mode 100644 3rdparty/plibsys/appveyor.yml create mode 100644 3rdparty/plibsys/cmake/PlatformDetect.cmake create mode 100644 3rdparty/plibsys/cmake/StdargDetect.cmake create mode 100644 3rdparty/plibsys/cmake/ThreadNameDetect.cmake create mode 100644 3rdparty/plibsys/cmake/VisibilityDetect.cmake create mode 100644 3rdparty/plibsys/codecov.yml create mode 100644 3rdparty/plibsys/platforms/aix-gcc/platform.cmake create mode 100644 3rdparty/plibsys/platforms/amigaos-gcc/Platform/AmigaOS.cmake create mode 100644 3rdparty/plibsys/platforms/amigaos-gcc/amigaos.cmake create mode 100644 3rdparty/plibsys/platforms/amigaos-gcc/platform.cmake create mode 100644 3rdparty/plibsys/platforms/android-clang/platform.cmake create mode 100644 3rdparty/plibsys/platforms/android-gcc/platform.cmake create mode 100644 3rdparty/plibsys/platforms/bb10-qcc/blackberry.cmake create mode 100644 3rdparty/plibsys/platforms/beos-gcc/platform.cmake create mode 100644 3rdparty/plibsys/platforms/common/HPUX.cmake create mode 100644 3rdparty/plibsys/platforms/common/SCOSV.cmake create mode 100644 3rdparty/plibsys/platforms/cygwin-clang/platform.cmake create mode 100644 3rdparty/plibsys/platforms/cygwin-gcc/platform.cmake create mode 100644 3rdparty/plibsys/platforms/darwin-clang/platform.cmake create mode 100644 3rdparty/plibsys/platforms/darwin-gcc/platform.cmake create mode 100644 3rdparty/plibsys/platforms/darwin-icc/platform.cmake create mode 100644 3rdparty/plibsys/platforms/darwin-pgi/platform.cmake create mode 100644 3rdparty/plibsys/platforms/dragonfly-clang/platform.cmake create mode 100644 3rdparty/plibsys/platforms/dragonfly-gcc/platform.cmake create mode 100644 3rdparty/plibsys/platforms/freebsd-clang/platform.cmake create mode 100644 3rdparty/plibsys/platforms/freebsd-gcc/platform.cmake create mode 100644 3rdparty/plibsys/platforms/haiku-clang/platform.cmake create mode 100644 3rdparty/plibsys/platforms/haiku-gcc/platform.cmake create mode 100644 3rdparty/plibsys/platforms/hpux-gcc/platform.cmake create mode 100644 3rdparty/plibsys/platforms/ios-clang/ios.cmake create mode 100644 3rdparty/plibsys/platforms/ios-clang/platform.cmake create mode 100644 3rdparty/plibsys/platforms/irix64-gcc/platform.cmake create mode 100644 3rdparty/plibsys/platforms/irix64-mipspro/platform.cmake create mode 100644 3rdparty/plibsys/platforms/linux-clang/platform.cmake create mode 100644 3rdparty/plibsys/platforms/linux-cray/platform.cmake create mode 100644 3rdparty/plibsys/platforms/linux-gcc/platform.cmake create mode 100644 3rdparty/plibsys/platforms/linux-icc/platform.cmake create mode 100644 3rdparty/plibsys/platforms/linux-pgi/platform.cmake create mode 100644 3rdparty/plibsys/platforms/linux-xlc/platform.cmake create mode 100644 3rdparty/plibsys/platforms/msys-clang/platform.cmake create mode 100644 3rdparty/plibsys/platforms/msys-gcc/platform.cmake create mode 100644 3rdparty/plibsys/platforms/netbsd-clang/platform.cmake create mode 100644 3rdparty/plibsys/platforms/netbsd-gcc/platform.cmake create mode 100644 3rdparty/plibsys/platforms/openbsd-clang/platform.cmake create mode 100644 3rdparty/plibsys/platforms/openbsd-gcc/platform.cmake create mode 100644 3rdparty/plibsys/platforms/os2-gcc/platform.cmake create mode 100644 3rdparty/plibsys/platforms/qnx-qcc/platform.cmake create mode 100644 3rdparty/plibsys/platforms/scosv-gcc/platform.cmake create mode 100644 3rdparty/plibsys/platforms/sunos-gcc/platform.cmake create mode 100644 3rdparty/plibsys/platforms/sunos-sunpro/platform.cmake create mode 100644 3rdparty/plibsys/platforms/syllable-gcc/platform.cmake create mode 100644 3rdparty/plibsys/platforms/tru64-compaq/platform.cmake create mode 100644 3rdparty/plibsys/platforms/tru64-gcc/platform.cmake create mode 100644 3rdparty/plibsys/platforms/unixware-gcc/platform.cmake create mode 100644 3rdparty/plibsys/platforms/vms-general/README.md create mode 100644 3rdparty/plibsys/platforms/vms-general/build_vms.com create mode 100644 3rdparty/plibsys/platforms/vms-general/plibsys.opt create mode 100644 3rdparty/plibsys/platforms/vms-general/vms_shorten_symbol.c create mode 100644 3rdparty/plibsys/platforms/win32-borland/platform.cmake create mode 100644 3rdparty/plibsys/platforms/win32-clang/platform.cmake create mode 100644 3rdparty/plibsys/platforms/win32-gcc/platform.cmake create mode 100644 3rdparty/plibsys/platforms/win32-icc/platform.cmake create mode 100644 3rdparty/plibsys/platforms/win32-msvc/platform.cmake create mode 100644 3rdparty/plibsys/platforms/win32-watcom/platform.cmake create mode 100644 3rdparty/plibsys/platforms/win64-clang/platform.cmake create mode 100644 3rdparty/plibsys/platforms/win64-gcc/platform.cmake create mode 100644 3rdparty/plibsys/platforms/win64-icc/platform.cmake create mode 100644 3rdparty/plibsys/platforms/win64-msvc/platform.cmake create mode 100755 3rdparty/plibsys/scripts/run_tests.sh create mode 100644 3rdparty/plibsys/src/CMakeLists.txt create mode 100644 3rdparty/plibsys/src/patomic-c11.c create mode 100644 3rdparty/plibsys/src/patomic-decc.c create mode 100644 3rdparty/plibsys/src/patomic-sim.c create mode 100644 3rdparty/plibsys/src/patomic-sync.c create mode 100644 3rdparty/plibsys/src/patomic-win.c create mode 100644 3rdparty/plibsys/src/patomic.h create mode 100644 3rdparty/plibsys/src/pcondvariable-amiga.c create mode 100644 3rdparty/plibsys/src/pcondvariable-atheos.c create mode 100644 3rdparty/plibsys/src/pcondvariable-beos.c create mode 100644 3rdparty/plibsys/src/pcondvariable-none.c create mode 100644 3rdparty/plibsys/src/pcondvariable-os2.c create mode 100644 3rdparty/plibsys/src/pcondvariable-posix.c create mode 100644 3rdparty/plibsys/src/pcondvariable-solaris.c create mode 100644 3rdparty/plibsys/src/pcondvariable-win.c create mode 100644 3rdparty/plibsys/src/pcondvariable.h create mode 100644 3rdparty/plibsys/src/pcryptohash-gost3411.c create mode 100644 3rdparty/plibsys/src/pcryptohash-gost3411.h create mode 100644 3rdparty/plibsys/src/pcryptohash-md5.c create mode 100644 3rdparty/plibsys/src/pcryptohash-md5.h create mode 100644 3rdparty/plibsys/src/pcryptohash-sha1.c create mode 100644 3rdparty/plibsys/src/pcryptohash-sha1.h create mode 100644 3rdparty/plibsys/src/pcryptohash-sha2-256.c create mode 100644 3rdparty/plibsys/src/pcryptohash-sha2-256.h create mode 100644 3rdparty/plibsys/src/pcryptohash-sha2-512.c create mode 100644 3rdparty/plibsys/src/pcryptohash-sha2-512.h create mode 100644 3rdparty/plibsys/src/pcryptohash-sha3.c create mode 100644 3rdparty/plibsys/src/pcryptohash-sha3.h create mode 100644 3rdparty/plibsys/src/pcryptohash.c create mode 100644 3rdparty/plibsys/src/pcryptohash.h create mode 100644 3rdparty/plibsys/src/pdir-none.c create mode 100644 3rdparty/plibsys/src/pdir-os2.c create mode 100644 3rdparty/plibsys/src/pdir-posix.c create mode 100644 3rdparty/plibsys/src/pdir-win.c create mode 100644 3rdparty/plibsys/src/pdir.c create mode 100644 3rdparty/plibsys/src/pdir.h create mode 100644 3rdparty/plibsys/src/perror-private.h create mode 100644 3rdparty/plibsys/src/perror.c create mode 100644 3rdparty/plibsys/src/perror.h create mode 100644 3rdparty/plibsys/src/perrortypes.h create mode 100644 3rdparty/plibsys/src/pfile.c create mode 100644 3rdparty/plibsys/src/pfile.h create mode 100644 3rdparty/plibsys/src/phashtable.c create mode 100644 3rdparty/plibsys/src/phashtable.h create mode 100644 3rdparty/plibsys/src/pinifile.c create mode 100644 3rdparty/plibsys/src/pinifile.h create mode 100644 3rdparty/plibsys/src/pipc-private.h create mode 100644 3rdparty/plibsys/src/pipc.c create mode 100644 3rdparty/plibsys/src/plibraryloader-amiga.c create mode 100644 3rdparty/plibsys/src/plibraryloader-beos.c create mode 100644 3rdparty/plibsys/src/plibraryloader-none.c create mode 100644 3rdparty/plibsys/src/plibraryloader-os2.c create mode 100644 3rdparty/plibsys/src/plibraryloader-posix.c create mode 100644 3rdparty/plibsys/src/plibraryloader-shl.c create mode 100644 3rdparty/plibsys/src/plibraryloader-win.c create mode 100644 3rdparty/plibsys/src/plibraryloader.h create mode 100644 3rdparty/plibsys/src/plibsys-private.h create mode 100644 3rdparty/plibsys/src/plibsys.h create mode 100644 3rdparty/plibsys/src/plibsysconfig.h.in create mode 100644 3rdparty/plibsys/src/plist.c create mode 100644 3rdparty/plibsys/src/plist.h create mode 100644 3rdparty/plibsys/src/pmacros.h create mode 100644 3rdparty/plibsys/src/pmacroscompiler.h create mode 100644 3rdparty/plibsys/src/pmacroscpu.h create mode 100644 3rdparty/plibsys/src/pmacrosos.h create mode 100644 3rdparty/plibsys/src/pmain.c create mode 100644 3rdparty/plibsys/src/pmain.h create mode 100644 3rdparty/plibsys/src/pmem.c create mode 100644 3rdparty/plibsys/src/pmem.h create mode 100644 3rdparty/plibsys/src/pmutex-amiga.c create mode 100644 3rdparty/plibsys/src/pmutex-atheos.c create mode 100644 3rdparty/plibsys/src/pmutex-beos.c create mode 100644 3rdparty/plibsys/src/pmutex-none.c create mode 100644 3rdparty/plibsys/src/pmutex-os2.c create mode 100644 3rdparty/plibsys/src/pmutex-posix.c create mode 100644 3rdparty/plibsys/src/pmutex-solaris.c create mode 100644 3rdparty/plibsys/src/pmutex-win.c create mode 100644 3rdparty/plibsys/src/pmutex.h create mode 100644 3rdparty/plibsys/src/pprocess.c create mode 100644 3rdparty/plibsys/src/pprocess.h create mode 100644 3rdparty/plibsys/src/prwlock-general.c create mode 100644 3rdparty/plibsys/src/prwlock-none.c create mode 100644 3rdparty/plibsys/src/prwlock-posix.c create mode 100644 3rdparty/plibsys/src/prwlock-solaris.c create mode 100644 3rdparty/plibsys/src/prwlock-win.c create mode 100644 3rdparty/plibsys/src/prwlock.h create mode 100644 3rdparty/plibsys/src/psemaphore-amiga.c create mode 100644 3rdparty/plibsys/src/psemaphore-none.c create mode 100644 3rdparty/plibsys/src/psemaphore-os2.c create mode 100644 3rdparty/plibsys/src/psemaphore-posix.c create mode 100644 3rdparty/plibsys/src/psemaphore-sysv.c create mode 100644 3rdparty/plibsys/src/psemaphore-win.c create mode 100644 3rdparty/plibsys/src/psemaphore.h create mode 100644 3rdparty/plibsys/src/pshm-amiga.c create mode 100644 3rdparty/plibsys/src/pshm-none.c create mode 100644 3rdparty/plibsys/src/pshm-os2.c create mode 100644 3rdparty/plibsys/src/pshm-posix.c create mode 100644 3rdparty/plibsys/src/pshm-sysv.c create mode 100644 3rdparty/plibsys/src/pshm-win.c create mode 100644 3rdparty/plibsys/src/pshm.h create mode 100644 3rdparty/plibsys/src/pshmbuffer.c create mode 100644 3rdparty/plibsys/src/pshmbuffer.h create mode 100644 3rdparty/plibsys/src/psocket.c create mode 100644 3rdparty/plibsys/src/psocket.h create mode 100644 3rdparty/plibsys/src/psocketaddress.c create mode 100644 3rdparty/plibsys/src/psocketaddress.h create mode 100644 3rdparty/plibsys/src/pspinlock-c11.c create mode 100644 3rdparty/plibsys/src/pspinlock-decc.c create mode 100644 3rdparty/plibsys/src/pspinlock-sim.c create mode 100644 3rdparty/plibsys/src/pspinlock-sync.c create mode 100644 3rdparty/plibsys/src/pspinlock-win.c create mode 100644 3rdparty/plibsys/src/pspinlock.h create mode 100644 3rdparty/plibsys/src/pstdarg.h create mode 100644 3rdparty/plibsys/src/pstring.c create mode 100644 3rdparty/plibsys/src/pstring.h create mode 100644 3rdparty/plibsys/src/psysclose-darwin.c create mode 100644 3rdparty/plibsys/src/psysclose-private.h create mode 100644 3rdparty/plibsys/src/psysclose-unix.c create mode 100644 3rdparty/plibsys/src/psysclose-win.c create mode 100644 3rdparty/plibsys/src/ptimeprofiler-amiga.c create mode 100644 3rdparty/plibsys/src/ptimeprofiler-beos.c create mode 100644 3rdparty/plibsys/src/ptimeprofiler-generic.c create mode 100644 3rdparty/plibsys/src/ptimeprofiler-mach.c create mode 100644 3rdparty/plibsys/src/ptimeprofiler-os2.c create mode 100644 3rdparty/plibsys/src/ptimeprofiler-posix.c create mode 100644 3rdparty/plibsys/src/ptimeprofiler-private.h create mode 100644 3rdparty/plibsys/src/ptimeprofiler-solaris.c create mode 100644 3rdparty/plibsys/src/ptimeprofiler-win.c create mode 100644 3rdparty/plibsys/src/ptimeprofiler.c create mode 100644 3rdparty/plibsys/src/ptimeprofiler.h create mode 100644 3rdparty/plibsys/src/ptree-avl.c create mode 100644 3rdparty/plibsys/src/ptree-avl.h create mode 100644 3rdparty/plibsys/src/ptree-bst.c create mode 100644 3rdparty/plibsys/src/ptree-bst.h create mode 100644 3rdparty/plibsys/src/ptree-private.h create mode 100644 3rdparty/plibsys/src/ptree-rb.c create mode 100644 3rdparty/plibsys/src/ptree-rb.h create mode 100644 3rdparty/plibsys/src/ptree.c create mode 100644 3rdparty/plibsys/src/ptree.h create mode 100644 3rdparty/plibsys/src/ptypes.h create mode 100644 3rdparty/plibsys/src/puthread-amiga.c create mode 100644 3rdparty/plibsys/src/puthread-atheos.c create mode 100644 3rdparty/plibsys/src/puthread-beos.c create mode 100644 3rdparty/plibsys/src/puthread-none.c create mode 100644 3rdparty/plibsys/src/puthread-os2.c create mode 100644 3rdparty/plibsys/src/puthread-posix.c create mode 100644 3rdparty/plibsys/src/puthread-private.h create mode 100644 3rdparty/plibsys/src/puthread-solaris.c create mode 100644 3rdparty/plibsys/src/puthread-win.c create mode 100644 3rdparty/plibsys/src/puthread.c create mode 100644 3rdparty/plibsys/src/puthread.h create mode 100644 3rdparty/plibsys/tests/CMakeLists.txt create mode 100644 3rdparty/plibsys/tests/patomic_test.cpp create mode 100644 3rdparty/plibsys/tests/pcondvariable_test.cpp create mode 100644 3rdparty/plibsys/tests/pcryptohash_test.cpp create mode 100644 3rdparty/plibsys/tests/pdir_test.cpp create mode 100644 3rdparty/plibsys/tests/perror_test.cpp create mode 100644 3rdparty/plibsys/tests/pfile_test.cpp create mode 100644 3rdparty/plibsys/tests/phashtable_test.cpp create mode 100644 3rdparty/plibsys/tests/pinifile_test.cpp create mode 100644 3rdparty/plibsys/tests/plibraryloader_test.cpp create mode 100644 3rdparty/plibsys/tests/plist_test.cpp create mode 100644 3rdparty/plibsys/tests/pmacros_test.cpp create mode 100644 3rdparty/plibsys/tests/pmain_test.cpp create mode 100644 3rdparty/plibsys/tests/pmem_test.cpp create mode 100644 3rdparty/plibsys/tests/pmutex_test.cpp create mode 100644 3rdparty/plibsys/tests/pprocess_test.cpp create mode 100644 3rdparty/plibsys/tests/prwlock_test.cpp create mode 100644 3rdparty/plibsys/tests/psemaphore_test.cpp create mode 100644 3rdparty/plibsys/tests/pshm_test.cpp create mode 100644 3rdparty/plibsys/tests/pshmbuffer_test.cpp create mode 100644 3rdparty/plibsys/tests/psocket_test.cpp create mode 100644 3rdparty/plibsys/tests/psocketaddress_test.cpp create mode 100644 3rdparty/plibsys/tests/pspinlock_test.cpp create mode 100644 3rdparty/plibsys/tests/pstdarg_test.cpp create mode 100644 3rdparty/plibsys/tests/pstring_test.cpp create mode 100644 3rdparty/plibsys/tests/ptestmacros.h create mode 100644 3rdparty/plibsys/tests/ptimeprofiler_test.cpp create mode 100644 3rdparty/plibsys/tests/ptree_test.cpp create mode 100644 3rdparty/plibsys/tests/ptypes_test.cpp create mode 100644 3rdparty/plibsys/tests/puthread_test.cpp (limited to '3rdparty/plibsys') diff --git a/3rdparty/plibsys/.codedocs b/3rdparty/plibsys/.codedocs new file mode 100644 index 0000000..1811d03 --- /dev/null +++ b/3rdparty/plibsys/.codedocs @@ -0,0 +1,53 @@ +# Optional project name, if left empty the GitHub repository name will be used. +PROJECT_NAME = plibsys + +# One or more directories and files that contain example code to be included. +EXAMPLE_PATH = + +# One or more directories and files to exclude from documentation generation. +# Use relative paths with respect to the repository root directory. +EXCLUDE = + +# One or more wildcard patterns to exclude files and directories from document +# generation. +EXCLUDE_PATTERNS = pcryptohash-*.* \ + ptree-*.* \ + plibsys.h \ + *-private.h \ + CMakeLists.txt + +# One or more symbols to exclude from document generation. Symbols can be +# namespaces, classes, or functions. +EXCLUDE_SYMBOLS = __has_attribute \ + __has_builtin + +# Override the default parser (language) used for each file extension. +EXTENSION_MAPPING = + +# Set the wildcard patterns used to filter out the source-files. +# If left blank the default is: +# *.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, +# *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, +# *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox, *.py, +# *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, *.qsf, *.as and *.js. +FILE_PATTERNS = *.h + +# Hide undocumented class members. +HIDE_UNDOC_MEMBERS = NO + +# Hide undocumented classes. +HIDE_UNDOC_CLASSES = NO + +# Specify a markdown page whose contents should be used as the main page +# (index.html). This will override a page marked as \mainpage. For example, a +# README.md file usually serves as a useful main page. +USE_MDFILE_AS_MAINPAGE = README.md + +# Specify external repository to link documentation with. +# This is similar to Doxygen's TAGFILES option, but will automatically link to +# tags of other repositories already using CodeDocs. List each repository to +# link with by giving its location in the form of owner/repository. +# For example: +# TAGLINKS = doxygen/doxygen CodeDocs/osg +# Note: these repositories must already be built on CodeDocs. +TAGLINKS = diff --git a/3rdparty/plibsys/.gear/plibsys.spec b/3rdparty/plibsys/.gear/plibsys.spec new file mode 100644 index 0000000..0b86e24 --- /dev/null +++ b/3rdparty/plibsys/.gear/plibsys.spec @@ -0,0 +1,104 @@ +%define soname 0 + +Name: plibsys +Version: 0.0.4 +Release: alt1 + +Summary: Highly portable C system library +License: MIT +Group: Development/C + +Url: https://github.com/saprykin/plibsys +Source: %name-%version.tar + +BuildPreReq: gcc-c++ cmake rpm-macros-cmake doxygen + +%description +plibsys is a cross-platform system C library with some helpful routines. +It has zero third-party dependencies and uses only native system calls. + +plibsys provides: + +Platform independent data types +Threads +Mutexes +Condition variables +Read-write locks +System-wide semaphores +System-wide shared memory +Optimized spinlock +Atomic operations +Socket support (UDP, TCP, SCTP) with IPv4 and IPv6 +Hash functions: MD5, SHA-1, SHA-2, SHA-3, GOST (R 34.11-94) +Binary trees: BST, red-black, AVL +INI file parser +High resolution time profiler +Files and directories +Shared library loading +Useful routines for linked lists, strings, hash tables +Macros for CPU architecture, OS and compiler detection +Macros for variable arguments + +%package -n lib%name%soname +Summary: %summary +Group: System/Libraries + +%description -n lib%name%soname +Highly portable C system library: threads and synchronization primitives, +sockets (TCP, UDP, SCTP), IPv4 and IPv6, IPC, hash functions (MD5, SHA-1, +SHA-2, SHA-3, GOST), binary trees (RB, AVL) and more. +Native code performance. + +%package -n lib%name-devel +Summary: %summary +Group: Development/C + +%description -n lib%name-devel +Highly portable C system library: threads and synchronization primitives, +sockets (TCP, UDP, SCTP), IPv4 and IPv6, IPC, hash functions (MD5, SHA-1, +SHA-2, SHA-3, GOST), binary trees (RB, AVL) and more. +Native code performance. + +This package provides headers to build software using %name. + +%package -n %name-doc +Summary: %name docs +Group: Development/Documentation + +%description -n %name-doc +HTML API documentation for the plibsys library + +%prep +%setup + +%build +%cmake_insource \ + -DCMAKE_INSTALL_LIBDIR=%_lib \ + -DPLIBSYS_BUILD_STATIC=OFF \ + -DPLIBSYS_TESTS=OFF \ + -DPLIBSYS_BUILD_DOC=ON \ + -DCMAKE_SKIP_RPATH=ON \ + # +%make_build + +%install +%makeinstall_std +rm -rf %buildroot%_defaultdocdir/* + +%files -n lib%name%soname +%doc AUTHORS COPYING NEWS README.md +%_libdir/*.so.* + +%files -n lib%name-devel +%_includedir/* +%_libdir/*.so + +%files -n %name-doc +%doc doc/html/* + +%changelog +* Sat May 26 2018 Alexander Saprykin 0.0.4-alt1 +- Update to version 0.0.4 +* Tue Sep 19 2017 Michael Shigorin 0.0.3-alt1 +- Initial build + diff --git a/3rdparty/plibsys/.gear/rules b/3rdparty/plibsys/.gear/rules new file mode 100644 index 0000000..9ae2212 --- /dev/null +++ b/3rdparty/plibsys/.gear/rules @@ -0,0 +1,2 @@ +tar: . +spec: .gear/plibsys.spec diff --git a/3rdparty/plibsys/.travis.yml b/3rdparty/plibsys/.travis.yml new file mode 100644 index 0000000..55641b1 --- /dev/null +++ b/3rdparty/plibsys/.travis.yml @@ -0,0 +1,171 @@ +env: + global: + # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created + # via the "travis encrypt" command using the project repo's public key + - secure: "Jw8Q8kFOfP4kiicGpZX496H0Hhi2+PzQ5wSpSJ/+e4rdQ/PnQuCvJfyAJ/k6+7BmgeS80T4sfK5xB5nX3yZG1FA4VE4kXuppJlVSwTZer0dlbbK3rvdSChBf5/Nnfjsj/Ch2SjjwhOt8zhjVutWuT2wz9RwOO39heQf3KFb4qbaVbnaBRTiGYJ+6D/28dX5foMehTohtcN1WKUZULum4cP8tqdn2+0cH+afwDwODap4v8brEW8HifS4QGDejAE7zR9fr3BaJl2m9s8art5lGzoK6Qaf2SS8WBW0MVzv2CP6zfsLUmPm3WijGUzAIPPi5GRFSy7O0GdkhHziMof1vyE3irXLSFlhg5g76lHEI2vpE6m+/PQ4I9jp7+oBT0N4SadgiXReXxewAZRrInCOstNN/mWIF0C0r1gWvPS3uM5u4PfGRa72GJ39SWmDvQqmOHOy15ZGA2eyw8piLAVkVJKvhufNfwEULITHE9xQXCV3e0YZrNIjMofuRszz7yLHEXlhwJxG++E4h0uCKnCqLTcLas8sBcRw98H/k9s8yZR3u9v0F0qqX/wus2TvfMjsfOqMm9MyqTXexpsMiODiN0a8zpdhufDlyxgr+2S96NlER7Iu0aRPBVgfDP2COPxmVW4ZwDpZYxK6iANeiYVuQGHccDHjZRyiSCG2c47EKV14=" + - COVERITY_SCAN_PROJECT_NAME="saprykin/plibsys" + - COVERITY_SCAN_BRANCH_PATTERN="coverity_scan" + - COVERITY_SCAN_NOTIFICATION_EMAIL="saprykin.spb@gmail.com" + - COVERITY_SCAN_BUILD_COMMAND="make" + +language: generic +dist: xenial + +matrix: + include: + # Code coverage build + - os: linux + addons: { apt: { packages: [lcov] } } + env: CC=gcc CXX=g++ GCOV=gcov USE_GCOV=1 + # Linux x64 builds (GCC) + - os: linux + addons: { apt: { sources: ubuntu-toolchain-r-test, packages: [gcc-4.9, g++-4.9] } } + env: CC=gcc-4.9 CXX=g++-4.9 ARCH=64 USE_COVERITY=1 + - os: linux + addons: { apt: { sources: ubuntu-toolchain-r-test, packages: [gcc-5, g++-5] } } + env: CC=gcc-5 CXX=g++-5 ARCH=64 + - os: linux + addons: { apt: { sources: ubuntu-toolchain-r-test, packages: [gcc-6, g++-6] } } + env: CC=gcc-6 CXX=g++-6 ARCH=64 + - os: linux + addons: { apt: { sources: ubuntu-toolchain-r-test, packages: [gcc-7, g++-7] } } + env: CC=gcc-7 CXX=g++-7 ARCH=64 + - os: linux + addons: { apt: { sources: ubuntu-toolchain-r-test, packages: [gcc-8, g++-8] } } + env: CC=gcc-8 CXX=g++-8 ARCH=64 + - os: linux + addons: { apt: { sources: ubuntu-toolchain-r-test, packages: [gcc-9, g++-9] } } + env: CC=gcc-9 CXX=g++-9 ARCH=64 + # IBM Power builds (GCC) + - os: linux + arch: ppc64le + env: CC=gcc CXX=g++ + # IBM Z builds (GCC) + - os: linux + arch: s390x + env: CC=gcc CXX=g++ + # Amazon Graviton-2 ARM + - os: linux + arch: arm64 + env: CC=gcc CXX=g++ + # Linux x86 builds (GCC) + - os: linux + addons: { apt: { sources: ubuntu-toolchain-r-test, packages: [gcc-4.9, g++-4.9, gcc-multilib, g++-multilib, gcc-4.9-multilib, g++-4.9-multilib, linux-libc-dev] } } + env: CC=gcc-4.9 CXX=g++-4.9 ARCH=32 + - os: linux + addons: { apt: { sources: ubuntu-toolchain-r-test, packages: [gcc-5, g++-5, gcc-multilib, g++-multilib, gcc-5-multilib, g++-5-multilib, linux-libc-dev] } } + env: CC=gcc-5 CXX=g++-5 ARCH=32 + - os: linux + addons: { apt: { sources: ubuntu-toolchain-r-test, packages: [gcc-6, g++-6, gcc-multilib, g++-multilib, gcc-6-multilib, g++-6-multilib, linux-libc-dev] } } + env: CC=gcc-6 CXX=g++-6 ARCH=32 + - os: linux + addons: { apt: { sources: ubuntu-toolchain-r-test, packages: [gcc-7, g++-7, gcc-multilib, g++-multilib, gcc-7-multilib, g++-7-multilib, linux-libc-dev] } } + env: CC=gcc-7 CXX=g++-7 ARCH=32 + - os: linux + addons: { apt: { sources: ubuntu-toolchain-r-test, packages: [gcc-8, g++-8, gcc-multilib, g++-multilib, gcc-8-multilib, g++-8-multilib, linux-libc-dev] } } + env: CC=gcc-8 CXX=g++-8 ARCH=32 + - os: linux + addons: { apt: { sources: ubuntu-toolchain-r-test, packages: [gcc-9, g++-9, gcc-multilib, g++-multilib, gcc-9-multilib, g++-9-multilib, linux-libc-dev] } } + env: CC=gcc-9 CXX=g++-9 ARCH=32 + # Linux x64 builds (Clang) + - os: linux + addons: { apt: { sources: [ubuntu-toolchain-r-test, llvm-toolchain-trusty-3.6], packages: [clang-3.6, libc++-dev] } } + env: CC=clang-3.6 CXX=clang++-3.6 + - os: linux + addons: { apt: { sources: [ubuntu-toolchain-r-test, llvm-toolchain-trusty-3.7], packages: [clang-3.7, libc++-dev] } } + env: CC=clang-3.7 CXX=clang++-3.7 + - os: linux + addons: { apt: { sources: [ubuntu-toolchain-r-test, llvm-toolchain-xenial-3.8], packages: [clang-3.8, libc++-dev] } } + env: CC=clang-3.8 CXX=clang++-3.8 + - os: linux + addons: { apt: { sources: [ubuntu-toolchain-r-test, llvm-toolchain-xenial-3.9], packages: [clang-3.9, libc++-dev] } } + env: CC=clang-3.9 CXX=clang++-3.9 + - os: linux + addons: { apt: { sources: [ubuntu-toolchain-r-test, llvm-toolchain-xenial-4.0], packages: [clang-4.0, libc++-dev] } } + env: CC=clang-4.0 CXX=clang++-4.0 + - os: linux + addons: { apt: { sources: [ubuntu-toolchain-r-test, llvm-toolchain-xenial-5.0], packages: [clang-5.0, libc++-dev] } } + env: CC=clang-5.0 CXX=clang++-5.0 + - os: linux + addons: { apt: { sources: [ubuntu-toolchain-r-test, llvm-toolchain-xenial-6.0], packages: [clang-6.0, libc++-dev] } } + env: CC=clang-6.0 CXX=clang++-6.0 + - os: linux + addons: { apt: { sources: [ubuntu-toolchain-r-test, llvm-toolchain-xenial-7], packages: [clang-7, libc++-dev] } } + env: CC=clang-7 CXX=clang++-7 + - os: linux + addons: { apt: { sources: [ubuntu-toolchain-r-test, llvm-toolchain-xenial-8], packages: [clang-8, libc++-dev] } } + env: CC=clang-8 CXX=clang++-8 + # macOS builds (GCC) + - os: osx + osx_image: xcode10.2 + env: FORMULA=gcc@6 CC=gcc-6 CXX=g++-6 + - os: osx + osx_image: xcode10.2 + env: FORMULA=gcc@7 CC=gcc-7 CXX=g++-7 + - os: osx + osx_image: xcode10.2 + env: FORMULA=gcc@8 CC=gcc-8 CXX=g++-8 + - os: osx + osx_image: xcode10.2 + env: FORMULA=gcc@9 CC=gcc-9 CXX=g++-9 + # macOS builds (Clang) + - os: osx + osx_image: xcode6.4 + env: CC=clang CXX=clang++ + - os: osx + osx_image: xcode7.3 + env: CC=clang CXX=clang++ + - os: osx + osx_image: xcode8.3 + env: CC=clang CXX=clang++ + - os: osx + osx_image: xcode9.2 + env: CC=clang CXX=clang++ + - os: osx + osx_image: xcode9.4 + env: CC=clang CXX=clang++ + - os: osx + osx_image: xcode10.3 + env: CC=clang CXX=clang++ + - os: osx + osx_image: xcode11.3 + env: CC=clang CXX=clang++ + - os: osx + osx_image: xcode12 + env: CC=clang CXX=clang++ + +branches: + only: + - master + - coverity_scan + +before_install: + - if [[ "${TRAVIS_BRANCH}" == "coverity_scan" ]]; then export USE_GCOV="" ; fi + - if [[ "${TRAVIS_OS_NAME}" == "osx" ]] && (! [[ -z ${FORMULA+x} ]] || ! [[ -z ${USE_GCOV} ]]) ; then brew update ; fi + - if [[ "${TRAVIS_OS_NAME}" == "osx" ]] && [[ "${USE_GCOV}" == "1" ]]; then brew install lcov ; fi + - if [[ "${TRAVIS_OS_NAME}" == "osx" ]] && ! [[ -z ${FORMULA+x} ]]; then brew cask uninstall oclint || true; fi + - if [[ "${TRAVIS_OS_NAME}" == "osx" ]] && ! [[ -z ${FORMULA+x} ]]; then brew unlink gcc || true; fi + - if [[ "${TRAVIS_OS_NAME}" == "osx" ]] && ! [[ -z ${FORMULA+x} ]]; then brew unlink ${FORMULA} || true; fi + - if [[ "${TRAVIS_OS_NAME}" == "osx" ]] && ! [[ -z ${FORMULA+x} ]]; then brew install ${FORMULA}; fi + - if [[ "${TRAVIS_OS_NAME}" == "osx" ]] && ! [[ -z ${FORMULA+x} ]]; then brew link ${FORMULA}; fi + +before_script: + - mkdir ./plibsys-build + - cd ./plibsys-build + - ${CC} --version + - ${CC} -dM -E - < /dev/null + - ${CXX} --version + - ${CXX} -dM -E -x c++ - < /dev/null + - if ! [[ -z ${ARCH+x} ]]; then export CMAKE_ARCH_PARAM=-m${ARCH} ; fi + - if [[ "${USE_GCOV}" == "1" ]]; then cmake -DPLIBSYS_COVERAGE=ON -DCMAKE_C_COMPILER=${CC} -DCMAKE_CXX_COMPILER=${CXX} -DCMAKE_C_FLAGS=${CMAKE_ARCH_PARAM} -DCMAKE_CXX_FLAGS=${CMAKE_ARCH_PARAM} .. ; fi + - if ! [[ "${USE_GCOV}" == "1" ]]; then cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=${CC} -DCMAKE_CXX_COMPILER=${CXX} -DCMAKE_C_FLAGS=${CMAKE_ARCH_PARAM} -DCMAKE_CXX_FLAGS=${CMAKE_ARCH_PARAM} .. ; fi + - if [[ "${TRAVIS_BRANCH}" == "coverity_scan" ]] && [[ "${USE_COVERITY}" == "1" ]]; then export COVERITY_SCAN_BUILD_COMMAND_PREPEND="cov-configure --comptype gcc --compiler ${CC} --template" ; fi + - if [[ "${TRAVIS_BRANCH}" == "coverity_scan" ]] && [[ "${USE_COVERITY}" == "1" ]]; then curl -s "https://scan.coverity.com/scripts/travisci_build_coverity_scan.sh" | bash || true ; fi + +script: + - make -j 2 + - if [[ "${TRAVIS_BRANCH}" != "coverity_scan" ]]; then ctest --output-on-failure -V -C Release; fi + +after_success: + - if [[ "${USE_GCOV}" == "1" ]]; then lcov --gcov-tool ${GCOV} --directory . --capture --output-file coverage.info ; fi + - if [[ "${USE_GCOV}" == "1" ]]; then bash <(curl -s https://codecov.io/bash) -x ${GCOV} ; fi diff --git a/3rdparty/plibsys/3dparty/scosv/FSU-threads-3.14.tgz b/3rdparty/plibsys/3dparty/scosv/FSU-threads-3.14.tgz new file mode 100644 index 0000000..ee9a9d0 Binary files /dev/null and b/3rdparty/plibsys/3dparty/scosv/FSU-threads-3.14.tgz differ diff --git a/3rdparty/plibsys/AUTHORS b/3rdparty/plibsys/AUTHORS new file mode 100644 index 0000000..fbdf37a --- /dev/null +++ b/3rdparty/plibsys/AUTHORS @@ -0,0 +1,2 @@ +Alexander Saprykin +Jean-Damien Durand diff --git a/3rdparty/plibsys/CMakeLists.txt b/3rdparty/plibsys/CMakeLists.txt new file mode 100644 index 0000000..8a47585 --- /dev/null +++ b/3rdparty/plibsys/CMakeLists.txt @@ -0,0 +1,93 @@ +# The MIT License +# +# Copyright (C) 2018-2019 Alexander Saprykin +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# 'Software'), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +set (CMAKE_LEGACY_CYGWIN_WIN32 0) + +cmake_minimum_required (VERSION 2.8.0) +project (plibsys C) + +set (PLIBSYS_VERSION_MAJOR 0) +set (PLIBSYS_VERSION_MINOR 0) +set (PLIBSYS_VERSION_PATCH 4) +set (PLIBSYS_VERSION_NUM 0x000004) +set (PLIBSYS_VERSION ${PLIBSYS_VERSION_MAJOR}.${PLIBSYS_VERSION_MINOR}.${PLIBSYS_VERSION_PATCH}) +set (PLIBSYS_SOVERSION ${PLIBSYS_VERSION_MAJOR}) +set (VERSION ${PLIBSYS_VERSION}) + +set (top_srcdir ${PROJECT_SOURCE_DIR}) + +option (PLIBSYS_TESTS "Build unit tests" OFF) +option (PLIBSYS_BUILD_STATIC "Also build static version of the library" ON) +option (PLIBSYS_COVERAGE "Enable gcov coverage (GCC and Clang)" OFF) +option (PLIBSYS_VISIBILITY "Use explicit symbols visibility if possible" OFF) +option (PLIBSYS_BUILD_DOC "Enable building HTML documentation" OFF) + +if (NOT CMAKE_BUILD_TYPE) + set (CMAKE_BUILD_TYPE "Debug") +endif() + +subdirs (src) + +if (PLIBSYS_TESTS) + enable_testing () + subdirs (tests) +endif() + +if (PLIBSYS_BUILD_DOC) + find_package (Doxygen) + + if (DOXYGEN_FOUND) + configure_file (${PROJECT_SOURCE_DIR}/Doxyfile.in ${PROJECT_BINARY_DIR}/Doxyfile) + + add_custom_target (doc ALL + COMMAND ${DOXYGEN_EXECUTABLE} Doxyfile + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + COMMENT "Generating API documentation with Doxygen" + VERBATIM + ) + + install (DIRECTORY ${PROJECT_BINARY_DIR}/doc/html DESTINATION share/doc) + endif() +endif() + +set (CPACK_PACKAGE_NAME ${PROJECT_NAME}-installer) +set (CPACK_PACKAGE_VENDOR "Alexander Saprykin") +set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "${PROJECT_NAME} - System routines library") +set (CPACK_PACKAGE_VERSION_MAJOR ${PLIBSYS_VERSION_MAJOR}) +set (CPACK_PACKAGE_VERSION_MINOR ${PLIBSYS_VERSION_MINOR}) +set (CPACK_PACKAGE_VERSION_PATCH ${PLIBSYS_VERSION_PATCH}) +set (CPACK_PACKAGE_VERSION ${PLIBSYS_VERSION}) +set (CPACK_PACKAGE_INSTALL_DIRECTORY ${PROJECT_NAME}-${CPACK_PACKAGE_VERSION}) +set (CPACK_COMPONENTS_ALL Core) +set (CPACK_COMPONENT_CORE_DISPLAY_NAME "Core components") +set (CPACK_COMPONENT_CORE_DESCRIPTION "Core library with headers") +set (CPACK_COMPONENT_CORE_REQUIRED TRUE) + +if (WIN32 AND NOT UNIX) + set (CPACK_NSIS_DISPLAY_NAME ${PROJECT_NAME}) +elseif (UNIX) + set (CPACK_GENERATOR STGZ) +endif() + +include (CPack) diff --git a/3rdparty/plibsys/COPYING b/3rdparty/plibsys/COPYING new file mode 100644 index 0000000..09d2d2e --- /dev/null +++ b/3rdparty/plibsys/COPYING @@ -0,0 +1,20 @@ +The MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/3rdparty/plibsys/Doxyfile.in b/3rdparty/plibsys/Doxyfile.in new file mode 100644 index 0000000..0c59d23 --- /dev/null +++ b/3rdparty/plibsys/Doxyfile.in @@ -0,0 +1,180 @@ +# Doxyfile 0.0.1 + +#--------------------------------------------------------------------------- +# General configuration options +#--------------------------------------------------------------------------- +PROJECT_NAME = plibsys +PROJECT_NUMBER = @VERSION@ +OUTPUT_DIRECTORY = doc +OUTPUT_LANGUAGE = English +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ALWAYS_DETAILED_SEC = NO +FULL_PATH_NAMES = NO +STRIP_FROM_PATH = +INTERNAL_DOCS = NO +STRIP_CODE_COMMENTS = YES +CASE_SENSE_NAMES = YES +SHORT_NAMES = NO +HIDE_SCOPE_NAMES = NO +VERBATIM_HEADERS = YES +SHOW_INCLUDE_FILES = YES +JAVADOC_AUTOBRIEF = YES +INHERIT_DOCS = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +DISTRIBUTE_GROUP_DOC = NO +TAB_SIZE = 8 +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +ALIASES = +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +OPTIMIZE_OUTPUT_FOR_C = YES +SHOW_USED_FILES = YES +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = YES +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_FORMAT = +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = @top_srcdir@/src +FILE_PATTERNS = *.h +RECURSIVE = YES + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. + +EXCLUDE_PATTERNS = Makefile.* \ + pcryptohash-*.* \ + ptree-*.* \ + plibsys.h \ + *-private.h \ + CMakeLists.txt + +EXCLUDE_SYMBOLS = __has_attribute \ + __has_builtin + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = +EXAMPLE_PATTERNS = +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_SOURCE_FILES = NO +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = YES +INLINE_SOURCES = NO +REFERENCED_BY_RELATION = YES +REFERENCES_RELATION = YES +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = NO +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +GENERATE_HTMLHELP = NO +GENERATE_CHI = NO +BINARY_TOC = NO +TOC_EXPAND = NO +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = NO +TREEVIEW_WIDTH = 250 +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = +COMPACT_LATEX = NO +PAPER_TYPE = a4 +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = NO +USE_PDFLATEX = NO +LATEX_BATCHMODE = NO +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3plibsys +MAN_LINKS = YES +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = YES +EXPAND_ONLY_PREDEF = YES +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = "P_BEGIN_DECLS=" \ + "P_END_DECLS=" \ + "DOXYGEN_SHOULD_SKIP_THIS" \ + "DOXYGEN" +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::addtions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +HAVE_DOT = NO +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +TEMPLATE_RELATIONS = YES +HIDE_UNDOC_RELATIONS = YES +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +GRAPHICAL_HIERARCHY = YES +DOT_PATH = +DOTFILE_DIRS = +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +#--------------------------------------------------------------------------- +# Configuration::addtions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = NO diff --git a/3rdparty/plibsys/NEWS b/3rdparty/plibsys/NEWS new file mode 100644 index 0000000..fb79bde --- /dev/null +++ b/3rdparty/plibsys/NEWS @@ -0,0 +1,122 @@ +Changes in plibsys 0.0.4 +======================== + +* Switch to MIT license from LGPLv2+ (#71) +* Replace Boost test framework with own one (#56) +* Add visibility support for APIs +* Add Debian package (in separate branch) (#54) +* Add gear package (ALT Linux) +* Add Conan packages +* Add option to generate Doxygen documentation + +* New supported platforms and compilers: + BlackBerry 10 (AArch32, x86) + Cray Linux Environment + AmigaOS 4.1 (PPC) (#59) + Android (x86, x64, AArch32, AArch64) (#69) + iOS, tvOS, watchOS (x86, x64, AArch32, AArch64) (#70) + PGI (x64) compiler on macOS + +* New API + Macros for variable arguments + +* AppVeyor + Add Visual Studio 2017 builds (#53) + Build tests for Watcom compiler (#57) + +* Travis + Add GCC 7.x builds (#48) + Add Clang 4.0 and 5.0 builds + Add MinGW 7.1.x (x64) builds + Update MinGW builds to 6.3.x (x64) + Add Xcode 8.3 and 9.1 builds + +* Fixes + Do not use deprecated readdir_r() with glibc >= 2.24 + Use CryptoPro S-box instead of testing in GOST hashing + Initialize POSIX semaphore with given value in create mode + Setup proper SONAME value (not the same as API version) + +Changes in plibsys 0.0.3 +======================== + +* Introduce models for shared library loading (#37) +* HP-UX shared library loading model (#39) +* Use lldiv() to improve time profiler accuracy +* Better error handling for shared library loading (#40) +* CPU architecture detection macros (#44) +* Prefer clock_nanosleep() over nanosleep() if available (#47) + +* New supported platforms and compilers: + BeOS on x86 (GCC) + OS/2 on x86 (GCC + kLIBC) (#41) + PGI (x64) compiler on Linux + PGI (x86) compiler on macOS + +* New API + Add routine to check for IPv6 support + Add routine to check if library loading is reference counted + Add detection of number of the CPU cores (#43) + +* AppVeyor + MSYS2 build is disabled due to a bug in CMake package + +* Travis + Correct Brew formula names for GCC on macOS + Update Xcode8 image to 8.3 version + Fix Coverity scan + +* Fixes + Do not use poll() on macOS as it can be broken + Do not treat some non-UNIX systems as UNIX (#42) + Always define P_SOCKET_FAMILY_INET6 + Use PLIBSYS_PLATFORM_LDFLAGS to perform system checks + Prevent Doxygen from generating duplicate macros (#46) + +Changes in plibsys 0.0.2 +======================== + +* Rename project to lower case letters +* Move to LGPL license +* Print routine names in warning and error output +* Add CMake option PLIBSYS_TESTS to disable tests completely +* Add ability to use general model for read-write locks explicitly + +* New API: + New hashing algorithms: SHA-256/224, SHA-512/384, SHA-3 + Add routines to get and set last native error codes + +* New supported platforms and compilers: + OpenVMS on Alpha and IA64 (DEC C) + Tru64 on Alpha (Compaq, GCC) + Linux on PPC64le (IBM XL C, GCC) + Syllable (GCC) + +* AppVeyor: + Add parallel builds for Microsoft compilers + Add LLVM (x64) build + Add MinGW 4.9, 5.4 and 6.2 builds + Add MSYS64 (x64) build + Add MSYS64 (MinGW x86) build + Add Cygwin (x86, x64) builds + Add OpenWatcom 1.9 build + +* Travis: + Move to container-based builds + Add GCC 4.9, 5.4 and 6.x builds (x86, x64) for Linux + Add Clang 3.6, 3.7 and 3.8 builds (x64) for Linux + Add GCC 4.9, 5.4 and 6.x builds (x64) for macOS + Add Xcode 6.4, 7.3 and 8 builds (x64) for macOS + +* Boost: + Backport changes from 1.62 to fix warnings with GCC 6.x + +* Fixes: + Fix race condition on Solaris when creating a TLS key + Fix potential leak on SCO when creating a TLS key + Fix building tests in QNX + +Changes in plibsys 0.0.1 +======================== + +* Initial release diff --git a/3rdparty/plibsys/README.md b/3rdparty/plibsys/README.md new file mode 100644 index 0000000..a25b247 --- /dev/null +++ b/3rdparty/plibsys/README.md @@ -0,0 +1,115 @@ + +[![](https://api.travis-ci.org/saprykin/plibsys.svg?branch=master)](https://travis-ci.org/saprykin/plibsys) +[![](https://ci.appveyor.com/api/projects/status/github/saprykin/plibsys?branch=appveyor_test&svg=true)](https://ci.appveyor.com/project/saprykin/plibsys) +[![](https://scan.coverity.com/projects/8333/badge.svg)](https://scan.coverity.com/projects/saprykin-plibsys) +[![](https://codecov.io/gh/saprykin/plibsys/branch/master/graph/badge.svg)](https://codecov.io/gh/saprykin/plibsys) +[![](https://codedocs.xyz/saprykin/plibsys.svg)](https://codedocs.xyz/saprykin/plibsys/) +[![](https://img.shields.io/badge/license-MIT-ff69b4.svg)](https://opensource.org/licenses/MIT) + +## About + +plibsys is a cross-platform system C library with some helpful routines. +It has zero third-party dependencies and uses only native system calls. + +## Features + +plibsys gives you: + +* Platform independent data types +* Threads +* Mutexes +* Condition variables +* Read-write locks +* System-wide semaphores +* System-wide shared memory +* Optimized spinlock +* Atomic operations +* Socket support (UDP, TCP, SCTP) with IPv4 and IPv6 +* Hash functions: MD5, SHA-1, SHA-2, SHA-3, GOST (R 34.11-94) +* Binary trees: BST, red-black, AVL +* INI file parser +* High resolution time profiler +* Files and directories +* Shared library loading +* Useful routines for linked lists, strings, hash tables +* Macros for CPU architecture, OS and compiler detection +* Macros for variable arguments + +To achieve maximum native performance on each platform several implementation models are used: + +* Threading models: POSIX, Solaris, OS/2, BeOS, AtheOS, AmigaOS and Win32 +* IPC models: POSIX, System V, OS/2, AmigaOS and Win32 +* Time profiler models: POSIX, Solaris, Mach, OS/2, BeOS, AmigaOS, Win32 and generic +* Directory iterating models: POSIX, OS/2 and Win32 +* Shared library loading models: POSIX, HP-UX, OS/2, BeOS, AmigaOS and Win32 +* Atomic operations models: sync, C11, DECC, Win32 and simulated +* Sockets: BSD with Win32 support + +## Platforms + +plibsys is a cross-platform, highly portable library, it is fully covered +with unit tests and was tested on the following desktop platforms: + +* GNU/Linux +* macOS +* Windows +* Cygwin, MSYS +* FreeBSD, NetBSD, OpenBSD +* DragonFlyBSD +* Solaris +* AIX +* HP-UX +* Tru64 +* OpenVMS +* OS/2 +* IRIX +* QNX Neutrino +* UnixWare 7 +* SCO OpenServer 5 +* Haiku +* Syllable +* BeOS +* AmigaOS + +plibsys also supports the following mobile platforms: + +* iOS, watchOS, tvOS +* Android +* BlackBerry 10 + +It should also work on other *nix systems with or without minimal efforts. + +## Compilers + +plibsys was tested with the following compilers: + +* MSVC (x86, x64) 2003 and above +* MinGW (x86, x64) +* Open Watcom (x86) +* Borland (x86) +* GCC (x86, x64, PPC32be, PPC64be/le, IA-64/32, IA-64, Alpha, HPPA2.0-32, MIPS32, AArch32, SPARCv9) +* Clang (x86, x64, PPC32be, AArch32, AArch64) +* Intel (x86, x64) +* QCC (x86, AArch32) +* Oracle Solaris Studio (x86, x64, SPARCv9) +* MIPSpro (MIPS32) +* XL C (PPC64le) +* DEC C (Alpha) +* PGI (x86, x64) +* Cray (x64) + +## Building + +Use CMake to build plibsys for target platform. For OpenVMS see platforms/vms-general directory. + +## Documentation + +Documentation for the latest stable verison is avaialble through the [GitHub Pages](http://saprykin.github.io/plibsys-docs). + +## License + +plibsys is distributed under the terms of MIT license. + +## More + +More information about the library is available on the [Wiki](https://github.com/saprykin/plibsys/wiki). diff --git a/3rdparty/plibsys/appveyor.yml b/3rdparty/plibsys/appveyor.yml new file mode 100644 index 0000000..8b654ca --- /dev/null +++ b/3rdparty/plibsys/appveyor.yml @@ -0,0 +1,289 @@ +environment: + COVERITY_SCAN_PROJECT_NAME: "saprykin/plibsys" + COVERITY_SCAN_NOTIFICATION_EMAIL: "saprykin.spb@gmail.com" + COVERITY_SCAN_TOKEN: + secure: 6WFXzLxAMuUovtYc+u8OoruE/V6zhK2M4mZlrszRBIA= + + matrix: + - CMAKE_GENERATOR: "Watcom WMake" + USE_WATCOM: "1" + APPVEYOR_BUILD_WORKER_IMAGE: "Visual Studio 2015" + - CMAKE_GENERATOR: "MinGW Makefiles" + USE_MINGW: "1" + USE_MINGW_493: "1" + APPVEYOR_BUILD_WORKER_IMAGE: "Visual Studio 2015" + - CMAKE_GENERATOR: "MinGW Makefiles" + USE_MINGW: "1" + USE_MINGW64: "1" + USE_MINGW_540: "1" + APPVEYOR_BUILD_WORKER_IMAGE: "Visual Studio 2015" + - CMAKE_GENERATOR: "MinGW Makefiles" + USE_MINGW: "1" + USE_MINGW64: "1" + USE_MINGW_630: "1" + APPVEYOR_BUILD_WORKER_IMAGE: "Visual Studio 2015" + - CMAKE_GENERATOR: "MinGW Makefiles" + USE_MINGW: "1" + USE_MINGW64: "1" + USE_MINGW_730: "1" + APPVEYOR_BUILD_WORKER_IMAGE: "Visual Studio 2015" + - CMAKE_GENERATOR: "MinGW Makefiles" + USE_MINGW: "1" + USE_MINGW64: "1" + USE_MINGW_810: "1" + APPVEYOR_BUILD_WORKER_IMAGE: "Visual Studio 2015" + - CMAKE_GENERATOR: "Unix Makefiles" + USE_MSYS64: "1" + APPVEYOR_BUILD_WORKER_IMAGE: "Visual Studio 2019" + - CMAKE_GENERATOR: "MSYS Makefiles" + USE_MSYS64: "1" + USE_MINGW64: "1" + APPVEYOR_BUILD_WORKER_IMAGE: "Visual Studio 2019" + - CMAKE_GENERATOR: "Unix Makefiles" + USE_CYGWIN: "1" + USE_CYGWIN64: "1" + APPVEYOR_BUILD_WORKER_IMAGE: "Visual Studio 2015" + - CMAKE_GENERATOR: "Unix Makefiles" + USE_CYGWIN: "1" + APPVEYOR_BUILD_WORKER_IMAGE: "Visual Studio 2015" + - CMAKE_GENERATOR: "Visual Studio 9 2008" + USE_OLD_MSBUILD: "1" + APPVEYOR_BUILD_WORKER_IMAGE: "Visual Studio 2015" + - CMAKE_GENERATOR: "Visual Studio 10 2010" + APPVEYOR_BUILD_WORKER_IMAGE: "Visual Studio 2015" + - CMAKE_GENERATOR: "Visual Studio 10 2010 Win64" + APPVEYOR_BUILD_WORKER_IMAGE: "Visual Studio 2015" + - CMAKE_GENERATOR: "Visual Studio 11 2012" + APPVEYOR_BUILD_WORKER_IMAGE: "Visual Studio 2015" + - CMAKE_GENERATOR: "Visual Studio 11 2012 Win64" + APPVEYOR_BUILD_WORKER_IMAGE: "Visual Studio 2015" + - CMAKE_GENERATOR: "Visual Studio 12 2013" + APPVEYOR_BUILD_WORKER_IMAGE: "Visual Studio 2015" + - CMAKE_GENERATOR: "Visual Studio 12 2013 Win64" + APPVEYOR_BUILD_WORKER_IMAGE: "Visual Studio 2015" + - CMAKE_GENERATOR: "Visual Studio 14 2015" + APPVEYOR_BUILD_WORKER_IMAGE: "Visual Studio 2015" + - CMAKE_GENERATOR: "Visual Studio 14 2015 Win64" + USE_COVERITY: "1" + APPVEYOR_BUILD_WORKER_IMAGE: "Visual Studio 2015" + - CMAKE_GENERATOR: "Visual Studio 15 2017" + APPVEYOR_BUILD_WORKER_IMAGE: "Visual Studio 2017" + - CMAKE_GENERATOR: "Visual Studio 15 2017 Win64" + APPVEYOR_BUILD_WORKER_IMAGE: "Visual Studio 2017" + - CMAKE_GENERATOR: "Visual Studio 16 2019" + APPVEYOR_BUILD_WORKER_IMAGE: "Visual Studio 2019" + - CMAKE_GENERATOR: "Visual Studio 16 2019" + CMAKE_GENERATOR_ARCH: "x64" + APPVEYOR_BUILD_WORKER_IMAGE: "Visual Studio 2019" + - CMAKE_GENERATOR: "NMake Makefiles" + USE_LLVM: "1" + APPVEYOR_BUILD_WORKER_IMAGE: "Visual Studio 2015" + +configuration: Release + +version: 0.0.1.{build} + +branches: + only: + - master + - appveyor_test + +clone_folder: c:\projects\plibsys + +cache: + - c:\projects\i686-4.9.3-release-win32-dwarf-rt_v4-rev1.7z + - c:\projects\x86_64-5.4.0-release-win32-seh-rt_v5-rev0.7z + - c:\projects\x86_64-6.3.0-release-win32-seh-rt_v5-rev1.7z + - c:\projects\x86_64-7.3.0-release-win32-seh-rt_v5-rev0.7z + - c:\projects\x86_64-8.1.0-release-win32-seh-rt_v6-rev0.7z + - c:\projects\open-watcom-c-win32-1.9.exe + - c:\cygwin-setup-cache + +install: + - If "%USE_CYGWIN%" == "1" ( + If "%USE_CYGWIN64%" == "1" ( + c:\cygwin64\setup-x86_64.exe -gqnNdO -R c:\cygwin64 -s http://cygwin.mirror.constant.com -l "c:\cygwin-setup-cache" -P cmake -P cygrunsrv + )) + + - If "%USE_CYGWIN%" == "1" ( + If NOT "%USE_CYGWIN64%" == "1" ( + c:\cygwin\setup-x86.exe -gqnNdO -R c:\cygwin -s http://cygwin.mirror.constant.com -l "c:\cygwin-setup-cache" -P cmake -P cygrunsrv + )) + + - ps: >- + If ($env:USE_MSYS64 -eq "1") { + $env:PATH = "c:\msys64\usr\bin;$env:PATH"; + + If ($env:USE_MINGW64 -eq "1") { + $env:PATH = "c:\msys64\mingw64\bin;$env:PATH"; + } Else { + & C:\msys64\usr\bin\mkdir -p /var/cache/pacman/pkg; + & pacman --sync --noconfirm cmake doxygen; + } + } ElseIf ($env:USE_MINGW -eq "1") { + If ($env:USE_MINGW_493 -eq "1") { + $MINGW_BASE = "i686-4.9.3-release-win32-dwarf-rt_v4-rev1"; + $MINGW_VERSION = "4.9.3"; + } ElseIf ($env:USE_MINGW_540 -eq "1") { + $MINGW_BASE = "x86_64-5.4.0-release-win32-seh-rt_v5-rev0"; + $MINGW_VERSION = "5.4.0"; + } ElseIf ($env:USE_MINGW_630 -eq "1") { + $MINGW_BASE = "x86_64-6.3.0-release-win32-seh-rt_v5-rev1"; + $MINGW_VERSION = "6.3.0"; + } ElseIf ($env:USE_MINGW_730 -eq "1") { + $MINGW_BASE = "x86_64-7.3.0-release-win32-seh-rt_v5-rev0"; + $MINGW_VERSION = "7.3.0"; + } ElseIf ($env:USE_MINGW_810 -eq "1") { + $MINGW_BASE = "x86_64-8.1.0-release-win32-seh-rt_v6-rev0"; + $MINGW_VERSION = "8.1.0"; + } + + $MINGW_ARCHIVE = "$MINGW_BASE.7z"; + + If ($env:USE_MINGW64 -eq "1") { + $MINGW_URL = "https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/$MINGW_VERSION/threads-win32/seh/$MINGW_ARCHIVE/download"; + } Else { + $MINGW_URL = "https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/$MINGW_VERSION/threads-win32/dwarf/$MINGW_ARCHIVE/download"; + } + + Set-Location c:\projects | Out-Null; + + If (-Not (Test-Path "$MINGW_ARCHIVE")) { + & appveyor DownloadFile "$MINGW_URL" -FileName "$MINGW_ARCHIVE"; + } + + New-Item -ItemType directory -Path "$MINGW_BASE" | Out-Null; + Copy-Item "$MINGW_ARCHIVE" "$MINGW_BASE"; + + Set-Location "$MINGW_BASE" | Out-Null; + & 7z x -y "$MINGW_ARCHIVE"; + + If ($env:USE_MINGW64 -eq "1") { + $env:PATH = "c:\projects\$MINGW_BASE\mingw64\bin;C:\Program Files (x86)\CMake\bin"; + } Else { + $env:PATH = "c:\projects\$MINGW_BASE\mingw32\bin;C:\Program Files (x86)\CMake\bin"; + } + } ElseIf ($env:USE_WATCOM -eq "1") { + $WATCOM_BASE = "open-watcom-c-win32-1.9"; + $WATCOM_INSTALLER = "$WATCOM_BASE.exe"; + $WATCOM_URL = "https://sourceforge.net/projects/openwatcom/files/open-watcom-1.9/$WATCOM_INSTALLER/download"; + + Set-Location c:\projects | Out-Null; + + If (-Not (Test-Path "$WATCOM_INSTALLER")) { + & appveyor DownloadFile "$WATCOM_URL" -FileName "$WATCOM_INSTALLER"; + } + + New-Item -ItemType directory -Path "$WATCOM_BASE" | Out-Null; + Copy-Item "$WATCOM_INSTALLER" "$WATCOM_BASE"; + + Set-Location "$WATCOM_BASE" | Out-Null; + & 7z x -y "$WATCOM_INSTALLER"; + + $CUR_LOCATION = "c:\projects\$WATCOM_BASE"; + + $env:PATH = "$CUR_LOCATION\binw;$env:PATH"; + $env:PATH = "$CUR_LOCATION\binnt;$env:PATH"; + $env:INCLUDE = "$CUR_LOCATION\h\nt;$env:INCLUDE"; + $env:INCLUDE = "$env:INCLUDE;$CUR_LOCATION\h\nt\directx"; + $env:INCLUDE = "$env:INCLUDE;$CUR_LOCATION\h\nt\ddk"; + $env:INCLUDE = "$CUR_LOCATION\h;$env:INCLUDE"; + $env:WATCOM = "$CUR_LOCATION"; + $env:EDPATH = "$CUR_LOCATION\eddat"; + } + +build_script: + - ps: >- + $BUILD_DIR = "c:\projects\plibsys-build"; + + New-Item -ItemType directory -Path $BUILD_DIR | Out-Null; + Set-Location $BUILD_DIR | Out-Null; + + If ($env:USE_MSYS64 -eq "1") { + if ($env:USE_MINGW64 -eq "1") { + $BUILD_TYPE = ""; + } Else { + $BUILD_TYPE = "-DCMAKE_BUILD_TYPE=$env:configuration"; + $CTEST_PARAMS = @("-E", "pshm|psemaphore"); + } + + $BUILD_COMMAND = "make"; + $BUILD_PARAMS = @("-j", "2"); + } ElseIf ($env:USE_MINGW -eq "1") { + if ($env:USE_MINGW_493 -eq "1") { + $BUILD_TYPE = "-DCMAKE_BUILD_TYPE=$env:configuration"; + } Else { + $BUILD_TYPE = ""; + } + + $BUILD_COMMAND = "mingw32-make"; + $BUILD_PARAMS = @("-j", "2"); + } ElseIf ($env:USE_CYGWIN -eq "1") { + If ($env:USE_CYGWIN64 -eq "1") { + $env:PATH = "c:\cygwin64\bin"; + } Else { + $env:PATH = "c:\cygwin\bin"; + } + + $BUILD_COMMAND = "make"; + $BUILD_PARAMS = @("-j", "2"); + + If ($env:APPVEYOR_REPO_BRANCH -eq "appveyor_test") { + & bash -lc 'cygserver-config --yes'; + & cygrunsrv -S cygserver; + } + } ElseIf ($env:USE_LLVM -eq "1") { + pushd "$env:VS140COMNTOOLS\..\..\VC\bin\amd64" + cmd /c "vcvars64.bat&set" | + foreach { + if ($_ -match "=") { + $v = $_.split("="); set-item -force -path "ENV:\$($v[0])" -value "$($v[1])" + } + } + popd + + $env:PATH = "C:\Program Files\LLVM\bin;$env:PATH"; + $BUILD_COMMAND = "nmake"; + $BUILD_PARAMS = "/NOLOGO" + $BUILD_TYPE = "-DCMAKE_BUILD_TYPE=$env:configuration"; + $CMAKE_C_COMPILER = "-DCMAKE_C_COMPILER=clang-cl.exe" + $CMAKE_CXX_COMPILER = "-DCMAKE_CXX_COMPILER=cl.exe" + } ElseIf ($env:USE_WATCOM -eq "1") { + $BUILD_COMMAND = "wmake"; + } Else { + If ($env:USE_OLD_MSBUILD -eq "1") { + $env:PATH = "C:\Windows\Microsoft.NET\Framework\v3.5;$env:PATH"; + } + + $BUILD_COMMAND = "msbuild"; + $BUILD_PARAMS = @("/verbosity:normal", "$BUILD_DIR\plibsys.sln", "/p:Configuration=$env:configuration", "/m"); + } + + $CMAKE_GENERATOR_ARGS = "-G`"$env:CMAKE_GENERATOR`""; + + If (Test-Path variable:global:CMAKE_GENERATOR_ARGS) { + $CMAKE_GENERATOR_ARGS = "$CMAKE_GENERATOR_ARGS -A $CMAKE_GENERATOR_ARGS"; + } + + If($env:USE_COVERITY -eq "1" -And $env:APPVEYOR_REPO_BRANCH -eq "appveyor_test") { + & nuget install PublishCoverity -o $BUILD_DIR -excludeversion; + + $COVERITY_EXE = "$BUILD_DIR\PublishCoverity\tools\PublishCoverity.exe"; + + # Do not build tests for Coverity + & cmake $CMAKE_GENERATOR_ARGS $CMAKE_C_COMPILER $CMAKE_CXX_COMPILER ../plibsys; + + & cov-build --dir "$BUILD_DIR\cov-int" $BUILD_COMMAND $BUILD_PARAMS; + + & $COVERITY_EXE compress -o "$BUILD_DIR\coverity.zip" -i "$BUILD_DIR\cov-int"; + & $COVERITY_EXE publish -z "$BUILD_DIR\coverity.zip" -r $env:COVERITY_SCAN_PROJECT_NAME -t $env:COVERITY_SCAN_TOKEN -e $env:COVERITY_SCAN_NOTIFICATION_EMAIL --codeVersion $env:APPVEYOR_BUILD_VERSION; + + Get-ChildItem -Path ./ -Recurse | Remove-Item -Force -Recurse; + } + + & cmake '$CMAKE_GENERATOR_ARGS' $BUILD_TYPE $CMAKE_C_COMPILER $CMAKE_CXX_COMPILER ../plibsys; + & $BUILD_COMMAND $BUILD_PARAMS; + + If ($env:APPVEYOR_REPO_BRANCH -eq "appveyor_test") { + & ctest $CTEST_PARAMS --output-on-failure -V -C Release; + } diff --git a/3rdparty/plibsys/cmake/PlatformDetect.cmake b/3rdparty/plibsys/cmake/PlatformDetect.cmake new file mode 100644 index 0000000..ab05065 --- /dev/null +++ b/3rdparty/plibsys/cmake/PlatformDetect.cmake @@ -0,0 +1,144 @@ +# The MIT License +# +# Copyright (C) 2018 Alexander Saprykin +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# 'Software'), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +function (plibsys_detect_c_compiler result) + # Get target system OS + string (TOLOWER ${CMAKE_SYSTEM_NAME} PLIBSYS_TARGET_OS) + + # Detect C compiler by it's ID + if (CMAKE_C_COMPILER_ID STREQUAL GNU) + set (PLIBSYS_C_COMPILER gcc) + elseif (CMAKE_C_COMPILER_ID STREQUAL MSVC) + set (PLIBSYS_C_COMPILER msvc) + else() + string (TOLOWER ${CMAKE_C_COMPILER_ID} PLIBSYS_C_COMPILER) + endif() + + # Fix gcc -> qcc naming on QNX 6 + if ((PLIBSYS_TARGET_OS STREQUAL qnx) AND (PLIBSYS_C_COMPILER STREQUAL gcc)) + set (PLIBSYS_C_COMPILER qcc) + endif() + + # Rename intel -> icc + if (PLIBSYS_C_COMPILER STREQUAL intel) + set (PLIBSYS_C_COMPILER icc) + endif() + + # Rename openwatcom -> watcom + if (PLIBSYS_C_COMPILER STREQUAL openwatcom) + set (PLIBSYS_C_COMPILER watcom) + endif() + + # Rename xl -> xlc + if (PLIBSYS_C_COMPILER STREQUAL xl) + set (PLIBSYS_C_COMPILER xlc) + endif() + + # Assign result + set (${result} ${PLIBSYS_C_COMPILER} PARENT_SCOPE) +endfunction (plibsys_detect_c_compiler) + +function (plibsys_detect_os_bits result) + if (PLIBSYS_SIZEOF_VOID_P EQUAL 8) + set (PLIBSYS_ARCH_BITS 64) + elseif (PLIBSYS_SIZEOF_VOID_P EQUAL 4) + set (PLIBSYS_ARCH_BITS 32) + elseif(PLIBSYS_SIZEOF_VOID_P EQUAL 0) + set (PLIBSYS_ARCH_BITS "universal") + else() + set (PLIBSYS_ARCH_BITS "unknown") + endif() + + set (${result} ${PLIBSYS_ARCH_BITS} PARENT_SCOPE) +endfunction (plibsys_detect_os_bits) + +function (plibsys_detect_cpu_arch result) + if (CMAKE_SYSTEM_PROCESSOR MATCHES "(i[1-9]86)|(x86_64)") + if (CMAKE_CROSSCOMPILING) + if (CMAKE_SYSTEM_PROCESSOR MATCHES "i[1-9]86") + set (PLIBSYS_PROCESSOR_ARCH "x86") + else() + set (PLIBSYS_PROCESSOR_ARCH "x64") + endif() + else() + plibsys_detect_os_bits (PLIBSYS_OS_BITS) + if (PLIBSYS_OS_BITS STREQUAL "32") + set (PLIBSYS_PROCESSOR_ARCH "x86") + elseif (PLIBSYS_OS_BITS STREQUAL "64") + set (PLIBSYS_PROCESSOR_ARCH "x64") + else() + set (PLIBSYS_PROCESSOR_ARCH "x${PLIBSYS_OS_BITS}") + endif() + endif() + else() + set (PLIBSYS_PROCESSOR_ARCH ${CMAKE_SYSTEM_PROCESSOR}) + endif() + + set (${result} ${PLIBSYS_PROCESSOR_ARCH} PARENT_SCOPE) +endfunction (plibsys_detect_cpu_arch) + +function (plibsys_detect_target_os result) + string (TOLOWER ${CMAKE_SYSTEM_NAME} PLIBSYS_TARGET_OS) + + # Rename mingw -> windows + if (PLIBSYS_TARGET_OS MATCHES "(mingw.*)") + set (PLIBSYS_TARGET_OS windows) + endif() + + # Rename hp-ux -> hpux + if (PLIBSYS_TARGET_OS STREQUAL hp-ux) + set (PLIBSYS_TARGET_OS hpux) + endif() + + # Rename sco_sv -> scosv + if (PLIBSYS_TARGET_OS STREQUAL sco_sv) + set (PLIBSYS_TARGET_OS scosv) + endif() + + # Rename osf1 -> tru64 + if (PLIBSYS_TARGET_OS STREQUAL osf1) + set (PLIBSYS_TARGET_OS tru64) + endif() + + # Rename craylinuxenvironment -> linux + if (PLIBSYS_TARGET_OS STREQUAL craylinuxenvironment) + set (PLIBSYS_TARGET_OS linux) + endif() + + set (${result} ${PLIBSYS_TARGET_OS} PARENT_SCOPE) +endfunction (plibsys_detect_target_os) + +function (plibsys_detect_target_platform result) + plibsys_detect_target_os (PLIBSYS_TARGET_OS) + plibsys_detect_os_bits (PLIBSYS_OS_BITS) + plibsys_detect_c_compiler (PLIBSYS_C_COMPILER) + + if (PLIBSYS_TARGET_OS STREQUAL windows) + set (PLIBSYS_TARGET_PLATFORM win${PLIBSYS_OS_BITS}) + else() + set (PLIBSYS_TARGET_PLATFORM ${PLIBSYS_TARGET_OS}) + endif() + + set (PLIBSYS_TARGET_PLATFORM ${PLIBSYS_TARGET_PLATFORM}-${PLIBSYS_C_COMPILER}) + set (${result} ${PLIBSYS_TARGET_PLATFORM} PARENT_SCOPE) +endfunction (plibsys_detect_target_platform) diff --git a/3rdparty/plibsys/cmake/StdargDetect.cmake b/3rdparty/plibsys/cmake/StdargDetect.cmake new file mode 100644 index 0000000..a85f59a --- /dev/null +++ b/3rdparty/plibsys/cmake/StdargDetect.cmake @@ -0,0 +1,74 @@ +# The MIT License +# +# Copyright (C) 2017 Jean-Damien Durand +# Copyright (C) 2019 Alexander Saprykin +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# 'Software'), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +function (plibsys_detect_va_copy result) + # + # On platforms that supports va_start() and al. + # there is sometimes the need to do va_copy() when + # calling another va_list aware function. + # + # This means that va_copy() is not needed (thus not available) + # everywhere. + # + # We depend on stdarg.h in any case, stdio.h in some cases. + # + # Known implementations vary from va_copy to __va_copy (old proposed name). + # We check the _va_copy eventually. + # + set (P_VA_COPY FALSE) + foreach (KEYWORD "va_copy" "_va_copy" "__va_copy") + check_c_source_compiles (" + #include + #include + #include + + void f (int i, ...) { + va_list args1, args2; + + va_start (args1, i); + ${KEYWORD}(args2, args1); + if (va_arg (args2, int) != 42 || va_arg (args1, int) != 42) { + exit (1); + } + va_end (args1); + va_end (args2); + } + + int main () { + f (0, 42); + exit (0); + }" + PLIBSYS_${KEYWORD} + ) + + if (PLIBSYS_${KEYWORD}) + set (P_VA_COPY ${KEYWORD}) + break() + endif() + endforeach() + + # Assign result + set (${result} ${P_VA_COPY} PARENT_SCOPE) + +endfunction (plibsys_detect_va_copy) diff --git a/3rdparty/plibsys/cmake/ThreadNameDetect.cmake b/3rdparty/plibsys/cmake/ThreadNameDetect.cmake new file mode 100644 index 0000000..c850d82 --- /dev/null +++ b/3rdparty/plibsys/cmake/ThreadNameDetect.cmake @@ -0,0 +1,135 @@ +# The MIT License +# +# Copyright (C) 2019 Alexander Saprykin +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# 'Software'), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +function (plibsys_detect_thread_name has_pthread_np_h result) + set (PTHREAD_HEADERS "#include ") + + if (${has_pthread_np_h}) + set (PTHREAD_HEADERS "${PTHREAD_HEADERS}\n#include") + endif() + + # Check pthread_setname_np() with 1 arg + + if (NOT PLIBSYS_THREAD_SETTER) + check_c_source_compiles (" + ${PTHREAD_HEADERS} + + int main () { + pthread_setname_np (\"thread_name\"); + return 0; + }" + PLIBSYS_HAS_POSIX_SETNAME_NP_1 + ) + + if (PLIBSYS_HAS_POSIX_SETNAME_NP_1) + set (PLIBSYS_THREAD_SETTER "pthread_setname_np") + endif() + endif() + + # Check pthread_setname_np() with 2 args + + if (NOT PLIBSYS_THREAD_SETTER) + check_c_source_compiles (" + ${PTHREAD_HEADERS} + + int main () { + pthread_setname_np ((pthread_t) 0, \"thread_name\"); + return 0; + }" + PLIBSYS_HAS_POSIX_SETNAME_NP_2 + ) + + if (PLIBSYS_HAS_POSIX_SETNAME_NP_2) + set (PLIBSYS_THREAD_SETTER "pthread_setname_np") + endif() + endif() + + # Check pthread_setname_np() with 3 args + + if (NOT PLIBSYS_THREAD_SETTER) + check_c_source_compiles (" + ${PTHREAD_HEADERS} + + int main () { + pthread_setname_np ((pthread_t) 0, \"thread_name\", 0); + return 0; + }" + PLIBSYS_HAS_POSIX_SETNAME_NP_3 + ) + + if (PLIBSYS_HAS_POSIX_SETNAME_NP_3) + set (PLIBSYS_THREAD_SETTER "pthread_setname_np") + endif() + endif() + + # Check pthread_set_name_np() + + if (NOT PLIBSYS_THREAD_SETTER) + check_c_source_compiles (" + ${PTHREAD_HEADERS} + + int main () { + pthread_set_name_np ((pthread_t) 0, \"thread_name\"); + return 0; + }" + PLIBSYS_HAS_POSIX_SET_NAME_NP + ) + + if (PLIBSYS_HAS_POSIX_SET_NAME_NP) + set (PLIBSYS_THREAD_SETTER "pthread_set_name_np") + endif() + endif() + + # The last try is prctl() + + if (NOT PLIBSYS_THREAD_SETTER) + check_c_source_compiles (" + #include + #include + + int main () { + prctl (PR_SET_NAME, \"thread_name\", NULL, NULL, NULL); + return 0; + }" + PLIBSYS_PRCTL + ) + + if (PLIBSYS_PRCTL) + set (PLIBSYS_THREAD_SETTER "prctl") + endif() + endif() + + # It seems that CMake (old versions like 2.8.x - 2.10.x) has a bug, + # such that when passing empty values to the parent scope, these variable + # are not treated as empty, thereby use NONE value instead + + if (PLIBSYS_THREAD_SETTER STREQUAL "pthread_setname_np") + set (${result} "PLIBSYS_HAS_PTHREAD_SETNAME" PARENT_SCOPE) + elseif (PLIBSYS_THREAD_SETTER STREQUAL "pthread_set_name_np") + set (${result} "PLIBSYS_HAS_PTHREAD_SET_NAME" PARENT_SCOPE) + elseif (PLIBSYS_THREAD_SETTER STREQUAL "prctl") + set (${result} "PLIBSYS_HAS_PTHREAD_PRCTL" PARENT_SCOPE) + else() + set (${result} "NONE" PARENT_SCOPE) + endif() +endfunction (plibsys_detect_thread_name) diff --git a/3rdparty/plibsys/cmake/VisibilityDetect.cmake b/3rdparty/plibsys/cmake/VisibilityDetect.cmake new file mode 100644 index 0000000..cbf23cd --- /dev/null +++ b/3rdparty/plibsys/cmake/VisibilityDetect.cmake @@ -0,0 +1,127 @@ +# The MIT License +# +# Copyright (C) 2018 Alexander Saprykin +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# 'Software'), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +function (plibsys_detect_visibility cflags ldflags) + if (WIN32 OR CYGWIN OR MSYS) + set (${cflags} "" PARENT_SCOPE) + set (${ldflags} "" PARENT_SCOPE) + return() + endif() + + # Check GCC + check_c_source_compiles ("int main () { + #if (__GNUC__ >= 4) && !defined(_CRAYC) && \\\\ + !defined(__sun) && !defined(sun) && \\\\ + !defined(__hpux) && !defined(hpux) && \\\\ + !defined(__sgi) && !defined(sgi) && \\\\ + !defined(__osf__) && !defined(__osf) && \\\\ + !defined(__OS2__) \\\\ + !defined(_AIX) && !defined(__CYGWIN__) && !defined(__MSYS__) + return 0; + #else + stop_compile_here + #endif + }" + PLIBSYS_HAS_GCC_VISIBILITY + ) + + if (PLIBSYS_HAS_GCC_VISIBILITY) + set (${cflags} "-fvisibility=hidden" PARENT_SCOPE) + set (${ldflags} "" PARENT_SCOPE) + return() + endif() + + # Check Sun Studio + check_c_source_compiles ("int main () { + #if (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550)) || \\\\ + (defined(__SUNPRO_CC) && (__SUNPRO_CC >= 0x5110)) + return 0; + #else + stop_compile_here + #endif + }" + PLIBSYS_HAS_SUN_VISIBILITY + ) + + if (PLIBSYS_HAS_SUN_VISIBILITY) + set (${cflags} "" PARENT_SCOPE) + set (${ldlags} "-xldscope=__hidden" PARENT_SCOPE) + return() + endif() + + # Check IBM XL C + check_c_source_compiles ("int main () { + #if (defined(__xlC__) && (__xlC__ >= 0x0D01)) + return 0; + #else + stop_compile_here + #endif + }" + PLIBSYS_HAS_XLC_VISIBILITY + ) + + if (PLIBSYS_HAS_XLC_VISIBILITY) + set (${cflags} "-qvisibility=hidden" PARENT_SCOPE) + set (${ldflags} "" PARENT_SCOPE) + return() + endif() + + # Check HP C/aC++ + check_c_source_compiles ("int main () { + #if (defined(__HP_cc) && (__HP_cc >= 0x061500)) || \\\\ + (defined(__HP_aCC) && (__HP_aCC >= 0x061500)) + return 0; + #else + stop_compile_here + #endif + }" + PLIBSYS_HAS_HP_VISIBILITY + ) + + if (PLIBSYS_HAS_HP_VISIBILITY) + set (${cflags} "-Bhidden" PARENT_SCOPE) + set (${ldflags} "" PARENT_SCOPE) + return() + endif() + + # Check Clang + check_c_source_compiles ("int main () { + #if defined(__has_attribute) && __has_attribute(visibility) + return 0; + #else + stop_compile_here + #endif + }" + PLIBSYS_HAS_CLANG_VISIBILITY + ) + + if (PLIBSYS_HAS_CLANG_VISIBILITY) + set (${cflags} "-fvisibility=hidden" PARENT_SCOPE) + set (${ldflags} "" PARENT_SCOPE) + return() + endif() + + # Empty result + set (${cflags} "" PARENT_SCOPE) + set (${ldflags} "" PARENT_SCOPE) +endfunction (plibsys_detect_visibility) diff --git a/3rdparty/plibsys/codecov.yml b/3rdparty/plibsys/codecov.yml new file mode 100644 index 0000000..fb9246c --- /dev/null +++ b/3rdparty/plibsys/codecov.yml @@ -0,0 +1,7 @@ +comment: + layout: header, changes, diff +coverage: + precision: 0 + round: down + status: + patch: false diff --git a/3rdparty/plibsys/platforms/aix-gcc/platform.cmake b/3rdparty/plibsys/platforms/aix-gcc/platform.cmake new file mode 100644 index 0000000..be1d25a --- /dev/null +++ b/3rdparty/plibsys/platforms/aix-gcc/platform.cmake @@ -0,0 +1,13 @@ +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL sysv) +set (PLIBSYS_TIME_PROFILER_MODEL posix) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_LDFLAGS -L/usr/lib/threads) +set (PLIBSYS_PLATFORM_LINK_LIBRARIES c_r -pthread) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT + -D_THREAD_SAFE +) diff --git a/3rdparty/plibsys/platforms/amigaos-gcc/Platform/AmigaOS.cmake b/3rdparty/plibsys/platforms/amigaos-gcc/Platform/AmigaOS.cmake new file mode 100644 index 0000000..2889670 --- /dev/null +++ b/3rdparty/plibsys/platforms/amigaos-gcc/Platform/AmigaOS.cmake @@ -0,0 +1,2 @@ +# This is a dummy file to suppress CMake warnings about unknown platform. +# As we are doing it during cross-compilation, it is completely fine. diff --git a/3rdparty/plibsys/platforms/amigaos-gcc/amigaos.cmake b/3rdparty/plibsys/platforms/amigaos-gcc/amigaos.cmake new file mode 100644 index 0000000..23e88e6 --- /dev/null +++ b/3rdparty/plibsys/platforms/amigaos-gcc/amigaos.cmake @@ -0,0 +1,90 @@ +# In order to suppress CMake warnings about unknown platform +get_filename_component (cur_dir ${CMAKE_CURRENT_LIST_FILE} PATH) +list (APPEND CMAKE_MODULE_PATH "${cur_dir}") + +set (CMAKE_SYSTEM_NAME AmigaOS) +set (CMAKE_SYSTEM_VERSION 4) +set (CMAKE_SYSTEM_PROCESSOR ppc) + +if (NOT AMIGA_ROOT) + set (AMIGA_ROOT $ENV{AMIGA_ROOT}) + + if (NOT AMIGA_ROOT) + message (FATAL_ERROR "You must define AMIGA_ROOT environment variable for toolchain") + endif() +endif() + +set (CMAKE_SHARED_LIBRARY_PREFIX "lib") +set (CMAKE_SHARED_LIBRARY_SUFFIX ".so") +set (CMAKE_STATIC_LIBRARY_PREFIX "lib") +set (CMAKE_STATIC_LIBRARY_SUFFIX ".a") + +if (CMAKE_HOST_WIN32) + set (HOST_EXECUTABLE_SUFFIX ".exe") +endif() + +set (CMAKE_AR + "${AMIGA_ROOT}/bin/${CMAKE_SYSTEM_PROCESSOR}-amigaos-ar${HOST_EXECUTABLE_SUFFIX}" + CACHE PATH "AmigaOS ar program" +) + +set (CMAKE_RANLIB + "${AMIGA_ROOT}/bin/${CMAKE_SYSTEM_PROCESSOR}-amigaos-ranlib${HOST_EXECUTABLE_SUFFIX}" + CACHE PATH "AmigaOS ranlib program" +) + +set (CMAKE_NM + "${AMIGA_ROOT}/bin/${CMAKE_SYSTEM_PROCESSOR}-amigaos-nm${HOST_EXECUTABLE_SUFFIX}" + CACHE PATH "AmigaOS nm program" +) + +set (CMAKE_OBJCOPY + "${AMIGA_ROOT}/bin/${CMAKE_SYSTEM_PROCESSOR}-amigaos-objcopy${HOST_EXECUTABLE_SUFFIX}" + CACHE PATH "AmigaOS objcopy program" +) + +set (CMAKE_OBJDUMP + "${AMIGA_ROOT}/bin/${CMAKE_SYSTEM_PROCESSOR}-amigaos-objdump${HOST_EXECUTABLE_SUFFIX}" + CACHE PATH "AmigaOS objdump program" +) + +set (CMAKE_LINKER + "${AMIGA_ROOT}/bin/${CMAKE_SYSTEM_PROCESSOR}-amigaos-ld${HOST_EXECUTABLE_SUFFIX}" + CACHE PATH "AmigaOS linker program" +) + +set (CMAKE_STRIP + "${AMIGA_ROOT}/bin/${CMAKE_SYSTEM_PROCESSOR}-amigaos-strip${HOST_EXECUTABLE_SUFFIX}" + CACHE PATH "AmigaOS strip program" +) + +set (CMAKE_C_COMPILER "${AMIGA_ROOT}/bin/${CMAKE_SYSTEM_PROCESSOR}-amigaos-gcc${HOST_EXECUTABLE_SUFFIX}") +set (CMAKE_CXX_COMPILER "${AMIGA_ROOT}/bin/${CMAKE_SYSTEM_PROCESSOR}-amigaos-g++${HOST_EXECUTABLE_SUFFIX}") +set (CMAKE_ASM_COMPILER "${AMIGA_ROOT}/bin/${CMAKE_SYSTEM_PROCESSOR}-amigaos-as${HOST_EXECUTABLE_SUFFIX}") + +set (CMAKE_SHARED_LIBRARY_CXX_FLAGS -fPIC) +set (CMAKE_SHARED_LIBRARY_C_FLAGS -fPIC) +set (CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS -shared) +set (CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS -shared) + +set (AMIGA_CLIB2_PATH "${AMIGA_ROOT}/ppc-amigaos/SDK/clib2/lib") + +set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -use-dynld -L${AMIGA_CLIB2_PATH}") +set (CMAKE_SHARED_LINKER_FLAGS "-L${AMIGA_CLIB2_PATH}") + +set (CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG "-Wl,-rpath,") +set (CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG_SEP ":") +set (CMAKE_SHARED_LIBRARY_RPATH_LINK_CXX_FLAG "-Wl,-rpath-link,") +set (CMAKE_SHARED_LIBRARY_SONAME_CXX_FLAG "-Wl,-soname,") +set (CMAKE_EXE_EXPORTS_CXX_FLAG "-Wl,--export-dynamic") + +set (CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG "-Wl,-rpath,") +set (CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG_SEP ":") +set (CMAKE_SHARED_LIBRARY_RPATH_LINK_C_FLAG "-Wl,-rpath-link,") +set (CMAKE_SHARED_LIBRARY_SONAME_C_FLAG "-Wl,-soname,") +set (CMAKE_EXE_EXPORTS_C_FLAG "-Wl,--export-dynamic") + +set (CMAKE_FIND_ROOT_PATH ${AMIGA_ROOT}) +set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/3rdparty/plibsys/platforms/amigaos-gcc/platform.cmake b/3rdparty/plibsys/platforms/amigaos-gcc/platform.cmake new file mode 100644 index 0000000..2e589e3 --- /dev/null +++ b/3rdparty/plibsys/platforms/amigaos-gcc/platform.cmake @@ -0,0 +1,12 @@ +set (PLIBSYS_THREAD_MODEL amiga) +set (PLIBSYS_IPC_MODEL amiga) +set (PLIBSYS_TIME_PROFILER_MODEL amiga) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL amiga) +set (PLIBSYS_RWLOCK_MODEL general) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES auto amiga unix) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT +) diff --git a/3rdparty/plibsys/platforms/android-clang/platform.cmake b/3rdparty/plibsys/platforms/android-clang/platform.cmake new file mode 100644 index 0000000..3295b5c --- /dev/null +++ b/3rdparty/plibsys/platforms/android-clang/platform.cmake @@ -0,0 +1,11 @@ +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL none) +set (PLIBSYS_TIME_PROFILER_MODEL posix) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES -pthread dl) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT +) diff --git a/3rdparty/plibsys/platforms/android-gcc/platform.cmake b/3rdparty/plibsys/platforms/android-gcc/platform.cmake new file mode 100644 index 0000000..3295b5c --- /dev/null +++ b/3rdparty/plibsys/platforms/android-gcc/platform.cmake @@ -0,0 +1,11 @@ +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL none) +set (PLIBSYS_TIME_PROFILER_MODEL posix) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES -pthread dl) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT +) diff --git a/3rdparty/plibsys/platforms/bb10-qcc/blackberry.cmake b/3rdparty/plibsys/platforms/bb10-qcc/blackberry.cmake new file mode 100644 index 0000000..6509d24 --- /dev/null +++ b/3rdparty/plibsys/platforms/bb10-qcc/blackberry.cmake @@ -0,0 +1,141 @@ +set (CMAKE_SYSTEM_NAME QNX) +set (CMAKE_SYSTEM_VERSION 8.0.0) +set (TOOLCHAIN QNX) + +set (CPUVARDIR $ENV{CPUVARDIR}) + +if (NOT CPUVARDIR) + message (FATAL_ERROR "CPU architecture not set") +endif() + +if (${CPUVARDIR} STREQUAL "armle-v7") + set (CMAKE_SYSTEM_PROCESSOR armv7) +elseif (${CPUVARDIR} STREQUAL "x86") + set (CMAKE_SYSTEM_PROCESSOR x86) +else() + message (FATAL_ERROR "Unsupported CPU architecture: ${CPUVARDIR}") +endif() + +set (QNX_HOST $ENV{QNX_HOST}) +set (QNX_TARGET $ENV{QNX_TARGET}) + +if (NOT QNX_HOST) + message (FATAL_ERROR "You must define QNX_HOST environment variable for toolchain") +endif() + +if (NOT QNX_TARGET) + message (FATAL_ERROR "You must define QNX_TARGET environment variable for toolchain") +endif() + +set (CMAKE_SHARED_LIBRARY_PREFIX "lib") +set (CMAKE_SHARED_LIBRARY_SUFFIX ".so") +set (CMAKE_STATIC_LIBRARY_PREFIX "lib") +set (CMAKE_STATIC_LIBRARY_SUFFIX ".a") + +if (CMAKE_HOST_WIN32) + set (HOST_EXECUTABLE_SUFFIX ".exe") +endif() + +set (CMAKE_MAKE_PROGRAM + "${QNX_HOST}/usr/bin/make${HOST_EXECUTABLE_SUFFIX}" + CACHE PATH "QNX make program" +) + +set (CMAKE_SH + "${QNX_HOST}/usr/bin/sh${HOST_EXECUTABLE_SUFFIX}" + CACHE PATH "QNX shell program" +) + +set (CMAKE_AR + "${QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-ar${HOST_EXECUTABLE_SUFFIX}" + CACHE PATH "QNX ar program" +) + +set (CMAKE_RANLIB + "${QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-ranlib${HOST_EXECUTABLE_SUFFIX}" + CACHE PATH "QNX ranlib program" +) + +set (CMAKE_NM + "${QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-nm${HOST_EXECUTABLE_SUFFIX}" + CACHE PATH "QNX nm program" +) + +set (CMAKE_OBJCOPY + "${QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-objcopy${HOST_EXECUTABLE_SUFFIX}" + CACHE PATH "QNX objcopy program" +) + +set (CMAKE_OBJDUMP + "${QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-objdump${HOST_EXECUTABLE_SUFFIX}" + CACHE PATH "QNX objdump program" +) + +set (CMAKE_LINKER + "${QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-ld${HOST_EXECUTABLE_SUFFIX}" + CACHE PATH "QNX linker program" +) + +set (CMAKE_STRIP + "${QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-strip${HOST_EXECUTABLE_SUFFIX}" + CACHE PATH "QNX strip program" +) + +set (CMAKE_C_COMPILER "${QNX_HOST}/usr/bin/qcc${HOST_EXECUTABLE_SUFFIX}") +set (CMAKE_CXX_COMPILER "${QNX_HOST}/usr/bin/qcc${HOST_EXECUTABLE_SUFFIX}") +set (CMAKE_ASM_COMPILER "${QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-as${HOST_EXECUTABLE_SUFFIX}") + +execute_process (COMMAND nto${CMAKE_SYSTEM_PROCESSOR}-gcc${HOST_EXECUTABLE_SUFFIX} --version + OUTPUT_VARIABLE QCC_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +string (REGEX MATCH "[0-9]+.[0-9]+.[0-9]+" QCC_VERSION "${QCC_VERSION}") + +set (CMAKE_C_COMPILER_VERSION ${QCC_VERSION}) +set (CMAKE_CXX_COMPILER_VERSION ${QCC_VERSION}) + +set (BLACKBERRY_BASE_FLAGS "-D_REENTRANT -Wno-psabi -fstack-protector -fstack-protector-all") + +if (CMAKE_SYSTEM_PROCESSOR STREQUAL "armv7") + set (BLACKBERRY_CPU_FLAGS "-mcpu=cortex-a9 -mthumb") +endif() + +if (CMAKE_SYSTEM_PROCESSOR STREQUAL "armv7") + set (BLACKBERRY_QCC_FLAGS "-Vgcc_ntoarmv7le") +elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "x86") + set (BLACKBERRY_QCC_FLAGS "-Vgcc_ntox86") +endif() + +set (BLACKBERRY_FLAGS "${BLACKBERRY_QCC_FLAGS} ${BLACKBERRY_BASE_FLAGS} ${BLACKBERRY_CPU_FLAGS}") + +set (CMAKE_C_FLAGS_DEBUG "${BLACKBERRY_FLAGS} -g" CACHE STRING "" FORCE) +set (CMAKE_C_FLAGS_MINSIZEREL "${BLACKBERRY_FLAGS} -O2 -fstack-protector-strong -Os" CACHE STRING "" FORCE) +set (CMAKE_C_FLAGS_RELEASE "${BLACKBERRY_FLAGS} -O2 -fstack-protector-strong -Os" CACHE STRING "" FORCE) +set (CMAKE_C_FLAGS_RELWITHDEBINFO "${BLACKBERRY_FLAGS} -O2 -g -fstack-protector-strong" CACHE STRING "" FORCE) + +set (CMAKE_CXX_FLAGS_DEBUG "${BLACKBERRY_FLAGS} -lang-c++ -lstdc++ -g" CACHE STRING "" FORCE) +set (CMAKE_CXX_FLAGS_MINSIZEREL "${BLACKBERRY_FLAGS} -lang-c++ -lstdc++ -O2 -fstack-protector-strong -Os" CACHE STRING "" FORCE) +set (CMAKE_CXX_FLAGS_RELEASE "${BLACKBERRY_FLAGS} -lang-c++ -lstdc++ -O2 -fstack-protector-strong -Os" CACHE STRING "" FORCE) +set (CMAKE_CXX_FLAGS_RELWITHDEBINFO "${BLACKBERRY_FLAGS} -lang-c++ -lstdc++ -O2 -g -fstack-protector-strong" CACHE STRING "" FORCE) + +set (CMAKE_FIND_ROOT_PATH ${QNX_TARGET}) +set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +# To remove full linking paths +link_directories (${QNX_TARGET}/${CPUVARDIR}/lib ${QNX_TARGET}/${CPUVARDIR}/usr/lib) + +# To distinguish from QNX +add_definitions (-D__BLACKBERRY10__) + +set (CMAKE_LIBRARY_PATH + ${QNX_TARGET}/${CPUVARDIR}/lib + ${QNX_TARGET}/${CPUVARDIR}/usr/lib +) + +set (CMAKE_INCLUDE_PATH + ${QNX_TARGET}/usr/include/c++/${QCC_VERSION} + ${QNX_TARGET}/usr/include/c++/${QCC_VERSION}/arm-unknown-nto-qnx8.0.0eabi +) diff --git a/3rdparty/plibsys/platforms/beos-gcc/platform.cmake b/3rdparty/plibsys/platforms/beos-gcc/platform.cmake new file mode 100644 index 0000000..502313f --- /dev/null +++ b/3rdparty/plibsys/platforms/beos-gcc/platform.cmake @@ -0,0 +1,12 @@ +set (PLIBSYS_THREAD_MODEL beos) +set (PLIBSYS_IPC_MODEL none) +set (PLIBSYS_TIME_PROFILER_MODEL beos) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL beos) +set (PLIBSYS_RWLOCK_MODEL general) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT +) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES root socket bind) diff --git a/3rdparty/plibsys/platforms/common/HPUX.cmake b/3rdparty/plibsys/platforms/common/HPUX.cmake new file mode 100644 index 0000000..d411066 --- /dev/null +++ b/3rdparty/plibsys/platforms/common/HPUX.cmake @@ -0,0 +1,15 @@ +include (CheckFunctionExists) + +function (plibsys_hpux_detect_libraryloader_model result) + message (STATUS "Checking whether dlopen() presents") + + check_function_exists (dlopen PLIBSYS_HPUX_HAS_DLOPEN) + + if (PLIBSYS_HPUX_HAS_DLOPEN) + message (STATUS "Checking whether dlopen() presents - yes") + set (${result} posix PARENT_SCOPE) + else() + message (STATUS "Checking whether dlopen() presents - no") + set (${result} shl PARENT_SCOPE) + endif() +endfunction (plibsys_hpux_detect_libraryloader_model) diff --git a/3rdparty/plibsys/platforms/common/SCOSV.cmake b/3rdparty/plibsys/platforms/common/SCOSV.cmake new file mode 100644 index 0000000..6ef2416 --- /dev/null +++ b/3rdparty/plibsys/platforms/common/SCOSV.cmake @@ -0,0 +1,9 @@ +function (plibsys_scosv_print_threading_message) + message (" + You need a working port of FSU Pthreads in order to + compile with multi-threading support. Please refer to + http://moss.csc.ncsu.edu/~mueller/pthreads/ for more + details. Make sure that it is compiled with thread-safe + memory allocation (usually -DMALLOC macro definition). + ") +endfunction (plibsys_scosv_print_threading_message) diff --git a/3rdparty/plibsys/platforms/cygwin-clang/platform.cmake b/3rdparty/plibsys/platforms/cygwin-clang/platform.cmake new file mode 100644 index 0000000..53796bd --- /dev/null +++ b/3rdparty/plibsys/platforms/cygwin-clang/platform.cmake @@ -0,0 +1,12 @@ +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL sysv) +set (PLIBSYS_TIME_PROFILER_MODEL posix) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES -pthread rt dl) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT + -D_GNU_SOURCE +) diff --git a/3rdparty/plibsys/platforms/cygwin-gcc/platform.cmake b/3rdparty/plibsys/platforms/cygwin-gcc/platform.cmake new file mode 100644 index 0000000..53796bd --- /dev/null +++ b/3rdparty/plibsys/platforms/cygwin-gcc/platform.cmake @@ -0,0 +1,12 @@ +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL sysv) +set (PLIBSYS_TIME_PROFILER_MODEL posix) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES -pthread rt dl) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT + -D_GNU_SOURCE +) diff --git a/3rdparty/plibsys/platforms/darwin-clang/platform.cmake b/3rdparty/plibsys/platforms/darwin-clang/platform.cmake new file mode 100644 index 0000000..38771be --- /dev/null +++ b/3rdparty/plibsys/platforms/darwin-clang/platform.cmake @@ -0,0 +1,9 @@ +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL posix) +set (PLIBSYS_TIME_PROFILER_MODEL mach) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT +) diff --git a/3rdparty/plibsys/platforms/darwin-gcc/platform.cmake b/3rdparty/plibsys/platforms/darwin-gcc/platform.cmake new file mode 100644 index 0000000..38771be --- /dev/null +++ b/3rdparty/plibsys/platforms/darwin-gcc/platform.cmake @@ -0,0 +1,9 @@ +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL posix) +set (PLIBSYS_TIME_PROFILER_MODEL mach) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT +) diff --git a/3rdparty/plibsys/platforms/darwin-icc/platform.cmake b/3rdparty/plibsys/platforms/darwin-icc/platform.cmake new file mode 100644 index 0000000..7b69004 --- /dev/null +++ b/3rdparty/plibsys/platforms/darwin-icc/platform.cmake @@ -0,0 +1,11 @@ +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL posix) +set (PLIBSYS_TIME_PROFILER_MODEL posix) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES -pthread imf svml irng intlc) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT +) diff --git a/3rdparty/plibsys/platforms/darwin-pgi/platform.cmake b/3rdparty/plibsys/platforms/darwin-pgi/platform.cmake new file mode 100644 index 0000000..791ff79 --- /dev/null +++ b/3rdparty/plibsys/platforms/darwin-pgi/platform.cmake @@ -0,0 +1,27 @@ +if (NOT PLIBSYS_SIZEOF_VOID_P EQUAL 4) + if (CMAKE_VERSION VERSION_LESS 2.8.10) + message (WARNING + " + Unable to detect PGI compiler version. Beware that old + PGI versions doesn't support shared libraries on 64-bit + macOS. + ") + elseif (CMAKE_C_COMPILER_VERSION VERSION_LESS 18.4) + message (FATAL_ERROR + " + PGI compiler before verison 18.4 doesn't support 64-bit + shared libraries on macOS. Use older 32-bit version of + the compiler instead. + ") + endif() +endif() + +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL posix) +set (PLIBSYS_TIME_PROFILER_MODEL mach) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT +) diff --git a/3rdparty/plibsys/platforms/dragonfly-clang/platform.cmake b/3rdparty/plibsys/platforms/dragonfly-clang/platform.cmake new file mode 100644 index 0000000..b801c3e --- /dev/null +++ b/3rdparty/plibsys/platforms/dragonfly-clang/platform.cmake @@ -0,0 +1,11 @@ +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL posix) +set (PLIBSYS_TIME_PROFILER_MODEL posix) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES -pthread) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT +) diff --git a/3rdparty/plibsys/platforms/dragonfly-gcc/platform.cmake b/3rdparty/plibsys/platforms/dragonfly-gcc/platform.cmake new file mode 100644 index 0000000..b801c3e --- /dev/null +++ b/3rdparty/plibsys/platforms/dragonfly-gcc/platform.cmake @@ -0,0 +1,11 @@ +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL posix) +set (PLIBSYS_TIME_PROFILER_MODEL posix) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES -pthread) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT +) diff --git a/3rdparty/plibsys/platforms/freebsd-clang/platform.cmake b/3rdparty/plibsys/platforms/freebsd-clang/platform.cmake new file mode 100644 index 0000000..975c5df --- /dev/null +++ b/3rdparty/plibsys/platforms/freebsd-clang/platform.cmake @@ -0,0 +1,11 @@ +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL sysv) +set (PLIBSYS_TIME_PROFILER_MODEL posix) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES -pthread) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT +) diff --git a/3rdparty/plibsys/platforms/freebsd-gcc/platform.cmake b/3rdparty/plibsys/platforms/freebsd-gcc/platform.cmake new file mode 100644 index 0000000..975c5df --- /dev/null +++ b/3rdparty/plibsys/platforms/freebsd-gcc/platform.cmake @@ -0,0 +1,11 @@ +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL sysv) +set (PLIBSYS_TIME_PROFILER_MODEL posix) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES -pthread) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT +) diff --git a/3rdparty/plibsys/platforms/haiku-clang/platform.cmake b/3rdparty/plibsys/platforms/haiku-clang/platform.cmake new file mode 100644 index 0000000..0c93b2d --- /dev/null +++ b/3rdparty/plibsys/platforms/haiku-clang/platform.cmake @@ -0,0 +1,7 @@ +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL posix) +set (PLIBSYS_TIME_PROFILER_MODEL posix) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES root network) diff --git a/3rdparty/plibsys/platforms/haiku-gcc/platform.cmake b/3rdparty/plibsys/platforms/haiku-gcc/platform.cmake new file mode 100644 index 0000000..0c93b2d --- /dev/null +++ b/3rdparty/plibsys/platforms/haiku-gcc/platform.cmake @@ -0,0 +1,7 @@ +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL posix) +set (PLIBSYS_TIME_PROFILER_MODEL posix) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES root network) diff --git a/3rdparty/plibsys/platforms/hpux-gcc/platform.cmake b/3rdparty/plibsys/platforms/hpux-gcc/platform.cmake new file mode 100644 index 0000000..bfaa7d8 --- /dev/null +++ b/3rdparty/plibsys/platforms/hpux-gcc/platform.cmake @@ -0,0 +1,16 @@ +include (${PROJECT_SOURCE_DIR}/platforms/common/HPUX.cmake) + +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL sysv) +set (PLIBSYS_TIME_PROFILER_MODEL solaris) +set (PLIBSYS_DIR_MODEL posix) + +plibsys_hpux_detect_libraryloader_model (PLIBSYS_LIBRARYLOADER_MODEL) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES xnet rt -pthread) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT + -D_THREAD_SAFE + -D_XOPEN_SOURCE_EXTENDED=1 +) diff --git a/3rdparty/plibsys/platforms/ios-clang/ios.cmake b/3rdparty/plibsys/platforms/ios-clang/ios.cmake new file mode 100644 index 0000000..e0df03b --- /dev/null +++ b/3rdparty/plibsys/platforms/ios-clang/ios.cmake @@ -0,0 +1,419 @@ +# Copyright (c) 2014, Bogdan Cristea and LTE Engineering Software, +# Kitware, Inc., Insight Software Consortium. All rights reserved. +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# Updated by Alex Stewart (alexs.mac@gmail.com) +# +# ***************************************************************************** +# Now maintained by Alexander Widerberg (widerbergaren [at] gmail.com) +# under the BSD-Clause-3 licence +# ***************************************************************************** +# +# INFORMATION / HELP +# +# The following variables control the behaviour of this toolchain: +# +# IOS_PLATFORM: OS (default), SIMULATOR, SIMULATOR64, TVOS or SIMULATOR_TVOS +# OS = Build for iOS. +# SIMULATOR = Build for x86 iPhone simulator. +# SIMULATOR64 = Build for x64 iPhone simulator. +# TVOS = Build for Apple tvOS. +# SIMULATOR_TVOS = Build for x64 Apple TV Simulator. +# WATCHOS = Build for Apple watchOS. +# SIMULATOR_WATCHOS = Build for x86 watchOS Simulator. +# SIMULATOR64_WATCHOS = Build for x64 watchOS Simulator. +# IOS_DEPLOYMENT_TARGET: Minimum version for deployment target. +# CMAKE_OSX_SYSROOT: Path to the iOS SDK to use. By default this is +# automatically determined from IOS_PLATFORM and xcodebuild, but can also be +# manually specified (although this should not be required). +# CMAKE_IOS_DEVELOPER_ROOT: Path to the Developer directory for the iOS +# platform being compiled for. By default this is automatically determined +# from CMAKE_OSX_SYSROOT, but can also be manually specified (although this +# should not be required). +# ENABLE_BITCODE: (ON / OFF) Enables or disables bitcode support. Default: ON. +# ENABLE_ARC: (ON / OFF) Enables or disables ARC support. Default: ON. +# IOS_ARCH: (armv7 armv7s armv7k arm64 i386 x86_64) If specified, will override the +# default architectures for the given IOS_PLATFORM. Default architectures: +# OS = armv7 armv7s arm64 +# SIMULATOR = i386 +# SIMULATOR64 = x86_64 +# TVOS = arm64 +# SIMULATOR_TVOS = x86_64 +# WATCHOS = armv7k +# SIMULATOR_WATCHOS = i386 +# SIMULATOR64_WATCHOS = x86_64 +# +# Copyright 2018, Alexander Saprykin +# + +# Get the Xcode version being used. +execute_process (COMMAND xcodebuild -version OUTPUT_VARIABLE XCODE_VERSION + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE +) + +string (REGEX MATCH "Xcode [0-9\\.]+" XCODE_VERSION "${XCODE_VERSION}") +string (REGEX REPLACE "Xcode ([0-9\\.]+)" "\\1" XCODE_VERSION "${XCODE_VERSION}") + +message (STATUS "Building with Xcode version: ${XCODE_VERSION}") + +# Default to building for iOS if not specified otherwise, and we cannot +# determine the platform from the CMAKE_OSX_ARCHITECTURES variable. The use +# of CMAKE_OSX_ARCHITECTURES is such that try_compile() projects can correctly +# determine the value of IOS_PLATFORM from the root project, as +# CMAKE_OSX_ARCHITECTURES is propagated to them by CMake. + +if (NOT DEFINED IOS_PLATFORM) + if (CMAKE_OSX_ARCHITECTURES) + if (CMAKE_OSX_ARCHITECTURES MATCHES ".*arm.*") + set (IOS_PLATFORM "OS") + elseif (CMAKE_OSX_ARCHITECTURES MATCHES "i386") + set (IOS_PLATFORM "SIMULATOR") + elseif (CMAKE_OSX_ARCHITECTURES MATCHES "x86_64") + set (IOS_PLATFORM "SIMULATOR64") + endif() + endif() + + if (NOT IOS_PLATFORM) + set (IOS_PLATFORM "OS") + endif() +endif() + +set (IOS_PLATFORM ${IOS_PLATFORM} CACHE STRING "Type of iOS platform for which to build.") + +# Determine the platform name and architectures for use in xcodebuild commands +# from the specified IOS_PLATFORM name. + +if (IOS_PLATFORM STREQUAL "OS") + set (XCODE_IOS_PLATFORM "iphoneos") + + if (NOT IOS_ARCH) + set (IOS_ARCH "armv7;armv7s;arm64") + endif() +elseif (IOS_PLATFORM STREQUAL "SIMULATOR") + set (XCODE_IOS_PLATFORM "iphonesimulator") + set (ENABLE_BITCODE OFF) + + if (NOT IOS_ARCH) + set (IOS_ARCH "i386") + endif() +elseif (IOS_PLATFORM STREQUAL "SIMULATOR64") + set (XCODE_IOS_PLATFORM "iphonesimulator") + set (ENABLE_BITCODE OFF) + + if (NOT IOS_ARCH) + set (IOS_ARCH "x86_64") + endif() +elseif (IOS_PLATFORM STREQUAL "TVOS") + set (XCODE_IOS_PLATFORM "appletvos") + + if (NOT IOS_ARCH) + set (IOS_ARCH "arm64") + endif() +elseif (IOS_PLATFORM STREQUAL "SIMULATOR_TVOS") + set (XCODE_IOS_PLATFORM "appletvsimulator") + set (ENABLE_BITCODE OFF) + + if (NOT IOS_ARCH) + set (IOS_ARCH "x86_64") + endif() +elseif (IOS_PLATFORM STREQUAL "WATCHOS") + set (XCODE_IOS_PLATFORM "watchos") + + if (NOT IOS_ARCH) + set (IOS_ARCH "armv7k") + endif() +elseif (IOS_PLATFORM STREQUAL "SIMULATOR_WATCHOS") + set (XCODE_IOS_PLATFORM "watchsimulator") + set (ENABLE_BITCODE OFF) + + if (NOT IOS_ARCH) + set (IOS_ARCH "i386") + endif() +elseif (IOS_PLATFORM STREQUAL "SIMULATOR64_WATCHOS") + set (XCODE_IOS_PLATFORM "watchsimulator") + set (ENABLE_BITCODE OFF) + + if (NOT IOS_ARCH) + set (IOS_ARCH "x86_64") + endif() +else() + message (FATAL_ERROR "Invalid IOS_PLATFORM: ${IOS_PLATFORM}") +endif() + +message (STATUS "Configuring iOS build for platform: ${IOS_PLATFORM}, architecture(s): ${IOS_ARCH}") + +# If user did not specify the SDK root to use, then query xcodebuild for it. + +if (NOT CMAKE_OSX_SYSROOT) + execute_process (COMMAND xcodebuild -version -sdk ${XCODE_IOS_PLATFORM} Path + OUTPUT_VARIABLE CMAKE_OSX_SYSROOT + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + message (STATUS "Using SDK: ${CMAKE_OSX_SYSROOT} for platform: ${IOS_PLATFORM}") +endif() + +if (NOT EXISTS ${CMAKE_OSX_SYSROOT}) + message(FATAL_ERROR "Invalid CMAKE_OSX_SYSROOT: ${CMAKE_OSX_SYSROOT} does not exist.") +endif() + +# Specify minimum version of deployment target. + +if (NOT DEFINED IOS_DEPLOYMENT_TARGET) + if (IOS_PLATFORM MATCHES ".*WATCHOS") + set (IOS_DEPLOYMENT_TARGET "2.0" CACHE STRING "Minimum watchOS version to build for." ) + else() + set (IOS_DEPLOYMENT_TARGET "8.0" CACHE STRING "Minimum iOS version to build for." ) + endif() + + message (STATUS "Using the default min-version since IOS_DEPLOYMENT_TARGET not provided.") +endif() + +if (NOT IOS_DEPLOYMENT_TARGET VERSION_LESS 11.0 AND NOT IOS_PLATFORM MATCHES ".*WATCHOS") + # iOS 11 does not support 32-bit (armv7*). + foreach (ARCH ${IOS_ARCH}) + if (ARCH MATCHES "armv7.*") + message (STATUS "iOS architecture removed: ${ARCH} is not supported by " + "the minimum deployment iOS version ${IOS_DEPLOYMENT_TARGET}." + ) + else() + list (APPEND VALID_IOS_ARCH ${ARCH}) + endif() + endforeach() + + set (IOS_ARCH ${VALID_IOS_ARCH}) +endif() + +# Use bitcode or not + +if (NOT DEFINED ENABLE_BITCODE) + # Unless specified, enable bitcode support by default + set (ENABLE_BITCODE ON CACHE BOOL "Wheter or not to enable bitcode") + message (STATUS "Enabling bitcode support by default.") +endif() + +# Use ARC or not + +if (NOT DEFINED ENABLE_ARC) + # Unless specified, enable ARC support by default + set (ENABLE_ARC ON CACHE BOOL "Wheter or not to enable ARC") + message (STATUS "Enabling ARC support by default.") +endif() + +# Get the SDK version information. + +execute_process (COMMAND xcodebuild -sdk ${CMAKE_OSX_SYSROOT} -version SDKVersion + OUTPUT_VARIABLE IOS_SDK_VERSION + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE +) + +# Find the Developer root for the specific iOS platform being compiled for +# from CMAKE_OSX_SYSROOT. Should be ../../ from SDK specified in +# CMAKE_OSX_SYSROOT. There does not appear to be a direct way to obtain +# this information from xcrun or xcodebuild. + +if (NOT CMAKE_IOS_DEVELOPER_ROOT) + get_filename_component (IOS_PLATFORM_SDK_DIR ${CMAKE_OSX_SYSROOT} PATH) + get_filename_component (CMAKE_IOS_DEVELOPER_ROOT ${IOS_PLATFORM_SDK_DIR} PATH) +endif() + +if (NOT EXISTS ${CMAKE_IOS_DEVELOPER_ROOT}) + message (FATAL_ERROR "Invalid CMAKE_IOS_DEVELOPER_ROOT: ${CMAKE_IOS_DEVELOPER_ROOT} does not exist.") +endif() + +# Find the C & C++ compilers for the specified SDK. + +if (NOT CMAKE_C_COMPILER) + execute_process (COMMAND xcrun -sdk ${CMAKE_OSX_SYSROOT} -find clang + OUTPUT_VARIABLE CMAKE_C_COMPILER + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + message (STATUS "Using C compiler: ${CMAKE_C_COMPILER}") +endif() + +if (NOT CMAKE_CXX_COMPILER) + execute_process (COMMAND xcrun -sdk ${CMAKE_OSX_SYSROOT} -find clang++ + OUTPUT_VARIABLE CMAKE_CXX_COMPILER + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + message (STATUS "Using CXX compiler: ${CMAKE_CXX_COMPILER}") +endif() + +# Find (Apple's) libtool. + +execute_process (COMMAND xcrun -sdk ${CMAKE_OSX_SYSROOT} -find libtool + OUTPUT_VARIABLE IOS_LIBTOOL + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) +message (STATUS "Using libtool: ${IOS_LIBTOOL}") + +# Configure libtool to be used instead of ar + ranlib to build static libraries. +# This is required on Xcode 7+, but should also work on previous versions of +# Xcode. + +set (CMAKE_C_CREATE_STATIC_LIBRARY "${IOS_LIBTOOL} -static -o ") +set (CMAKE_CXX_CREATE_STATIC_LIBRARY "${IOS_LIBTOOL} -static -o ") + +# Standard settings. + +set (CMAKE_SYSTEM_NAME Darwin CACHE INTERNAL "") +set (CMAKE_SYSTEM_VERSION ${IOS_SDK_VERSION} CACHE INTERNAL "") +set (UNIX TRUE CACHE BOOL "") +set (APPLE TRUE CACHE BOOL "") +set (IOS TRUE CACHE BOOL "") +set (CMAKE_AR ar CACHE FILEPATH "" FORCE) +set (CMAKE_RANLIB ranlib CACHE FILEPATH "" FORCE) + +# Force unset of OS X-specific deployment target (otherwise autopopulated), +# required as of cmake 2.8.10. + +set (CMAKE_OSX_DEPLOYMENT_TARGET "" CACHE STRING "Must be empty for iOS builds." FORCE) + +# Set the architectures for which to build. + +set (CMAKE_OSX_ARCHITECTURES ${IOS_ARCH} CACHE STRING "Build architecture for iOS") + +# All iOS/Darwin specific settings - some may be redundant. + +set (CMAKE_SHARED_LIBRARY_PREFIX "lib") +set (CMAKE_SHARED_LIBRARY_SUFFIX ".dylib") +set (CMAKE_SHARED_MODULE_PREFIX "lib") +set (CMAKE_SHARED_MODULE_SUFFIX ".so") +set (CMAKE_MODULE_EXISTS 1) +set (CMAKE_DL_LIBS "") + +set (CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG "-compatibility_version ") +set (CMAKE_C_OSX_CURRENT_VERSION_FLAG "-current_version ") +set (CMAKE_CXX_OSX_COMPATIBILITY_VERSION_FLAG "${CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG}") +set (CMAKE_CXX_OSX_CURRENT_VERSION_FLAG "${CMAKE_C_OSX_CURRENT_VERSION_FLAG}") + +message (STATUS "Building for minimum OS version: ${IOS_DEPLOYMENT_TARGET} (SDK version: ${IOS_SDK_VERSION})") + +# Note that only Xcode 7+ supports the newer more specific: +# -m${XCODE_IOS_PLATFORM}-version-min flags, older versions of Xcode use: +# -m(ios/ios-simulator)-version-min instead. +if (IOS_PLATFORM STREQUAL "OS") + if (XCODE_VERSION VERSION_LESS 7.0) + set (XCODE_IOS_PLATFORM_VERSION_FLAGS "-mios-version-min=${IOS_DEPLOYMENT_TARGET}") + else() + # Xcode 7.0+ uses flags we can build directly from XCODE_IOS_PLATFORM. + set (XCODE_IOS_PLATFORM_VERSION_FLAGS "-m${XCODE_IOS_PLATFORM}-version-min=${IOS_DEPLOYMENT_TARGET}") + endif() +elseif (IOS_PLATFORM STREQUAL "TVOS") + set (XCODE_IOS_PLATFORM_VERSION_FLAGS "-mtvos-version-min=${IOS_DEPLOYMENT_TARGET}") +elseif (IOS_PLATFORM STREQUAL "SIMULATOR_TVOS") + set (XCODE_IOS_PLATFORM_VERSION_FLAGS "-mtvos-simulator-version-min=${IOS_DEPLOYMENT_TARGET}") +elseif (IOS_PLATFORM STREQUAL "WATCHOS") + set (XCODE_IOS_PLATFORM_VERSION_FLAGS "-mwatchos-version-min=${IOS_DEPLOYMENT_TARGET}") +elseif (IOS_PLATFORM STREQUAL "SIMULATOR_WATCHOS" OR IOS_PLATFORM STREQUAL "SIMULATOR64_WATCHOS") + set (XCODE_IOS_PLATFORM_VERSION_FLAGS "-mwatchos-simulator-version-min=${IOS_DEPLOYMENT_TARGET}") +else() + # SIMULATOR or SIMULATOR64 both use -mios-simulator-version-min. + set (XCODE_IOS_PLATFORM_VERSION_FLAGS "-mios-simulator-version-min=${IOS_DEPLOYMENT_TARGET}") +endif() + +message (STATUS "Version flags set to: ${XCODE_IOS_PLATFORM_VERSION_FLAGS}") + +if (ENABLE_BITCODE) + set (BITCODE "-fembed-bitcode") + message (STATUS "Enabling bitcode support.") +else() + set (BITCODE "") + message (STATUS "Disabling bitcode support.") +endif() + +if (ENABLE_ARC) + set (FOBJC_ARC "-fobjc-arc") + message (STATUS "Enabling ARC support.") +else() + set (FOBJC_ARC "-fno-objc-arc") + message (STATUS "Disabling ARC support.") +endif() + +set (CMAKE_C_FLAGS "${XCODE_IOS_PLATFORM_VERSION_FLAGS} ${BITCODE} -fobjc-abi-version=2 ${FOBJC_ARC} ${C_FLAGS}") +set (CMAKE_CXX_FLAGS "${XCODE_IOS_PLATFORM_VERSION_FLAGS} ${BITCODE} -fobjc-abi-version=2 ${FOBJC_ARC} ${CXX_FLAGS}") +set (CMAKE_C_LINK_FLAGS "${XCODE_IOS_PLATFORM_VERSION_FLAGS} -Wl,-search_paths_first ${C_LINK_FLAGS}") +set (CMAKE_CXX_LINK_FLAGS "${XCODE_IOS_PLATFORM_VERSION_FLAGS} -Wl,-search_paths_first ${CXX_LINK_FLAGS}") + +# In order to ensure that the updated compiler flags are used in try_compile() +# tests, we have to forcibly set them in the CMake cache, not merely set them +# in the local scope. + +list (APPEND VARS_TO_FORCE_IN_CACHE + CMAKE_C_FLAGS + CMAKE_CXX_FLAGS + CMAKE_C_LINK_FLAGS + CMAKE_CXX_LINK_FLAGS +) + +foreach (VAR_TO_FORCE ${VARS_TO_FORCE_IN_CACHE}) + set (${VAR_TO_FORCE} "${${VAR_TO_FORCE}}" CACHE STRING "" FORCE) +endforeach() + +set (CMAKE_PLATFORM_HAS_INSTALLNAME 1) + +set (CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-dynamiclib") +set (CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS "-dynamiclib") +set (CMAKE_SHARED_MODULE_CREATE_C_FLAGS "-bundle") +set (CMAKE_SHARED_MODULE_CREATE_CXX_FLAGS "-bundle") +set (CMAKE_SHARED_MODULE_LOADER_C_FLAG "-Wl,-bundle_loader,") +set (CMAKE_SHARED_MODULE_LOADER_CXX_FLAG "-Wl,-bundle_loader,") +set (CMAKE_FIND_LIBRARY_SUFFIXES ".dylib" ".so" ".a") + +# Hack: If a new CMake (which uses CMAKE_INSTALL_NAME_TOOL) runs on an old +# build tree (where install_name_tool was hardcoded) and where +# CMAKE_INSTALL_NAME_TOOL isn't in the cache and still CMake didn't fail in +# CMakeFindBinUtils.cmake (because it isn't rerun) hardcode +# CMAKE_INSTALL_NAME_TOOL here to install_name_tool, so it behaves as it did +# before, Alex. + +if (NOT DEFINED CMAKE_INSTALL_NAME_TOOL) + find_program (CMAKE_INSTALL_NAME_TOOL install_name_tool) +endif (NOT DEFINED CMAKE_INSTALL_NAME_TOOL) + +# Set the find root to the iOS developer roots and to user defined paths. +set (CMAKE_FIND_ROOT_PATH ${CMAKE_IOS_DEVELOPER_ROOT} + ${CMAKE_OSX_SYSROOT} + ${CMAKE_PREFIX_PATH} + CACHE string "iOS find search path root" FORCE +) + +# Default to searching for frameworks first. + +set (CMAKE_FIND_FRAMEWORK FIRST) + +# Set up the default search directories for frameworks. + +set (CMAKE_SYSTEM_FRAMEWORK_PATH + ${CMAKE_OSX_SYSROOT}/System/Library/Frameworks + ${CMAKE_OSX_SYSROOT}/System/Library/PrivateFrameworks + ${CMAKE_OSX_SYSROOT}/Developer/Library/Frameworks +) + +# Only search the specified iOS SDK, not the remainder of the host filesystem. + +set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY) +set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/3rdparty/plibsys/platforms/ios-clang/platform.cmake b/3rdparty/plibsys/platforms/ios-clang/platform.cmake new file mode 100644 index 0000000..38771be --- /dev/null +++ b/3rdparty/plibsys/platforms/ios-clang/platform.cmake @@ -0,0 +1,9 @@ +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL posix) +set (PLIBSYS_TIME_PROFILER_MODEL mach) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT +) diff --git a/3rdparty/plibsys/platforms/irix64-gcc/platform.cmake b/3rdparty/plibsys/platforms/irix64-gcc/platform.cmake new file mode 100644 index 0000000..1a7e78e --- /dev/null +++ b/3rdparty/plibsys/platforms/irix64-gcc/platform.cmake @@ -0,0 +1,13 @@ +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL posix) +set (PLIBSYS_TIME_PROFILER_MODEL posix) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_DEFINES + -D_PTHREADS + -D_POSIX_C_SOURCE=199506L + -D_BSD_TYPES +) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES pthread) diff --git a/3rdparty/plibsys/platforms/irix64-mipspro/platform.cmake b/3rdparty/plibsys/platforms/irix64-mipspro/platform.cmake new file mode 100644 index 0000000..1a7e78e --- /dev/null +++ b/3rdparty/plibsys/platforms/irix64-mipspro/platform.cmake @@ -0,0 +1,13 @@ +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL posix) +set (PLIBSYS_TIME_PROFILER_MODEL posix) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_DEFINES + -D_PTHREADS + -D_POSIX_C_SOURCE=199506L + -D_BSD_TYPES +) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES pthread) diff --git a/3rdparty/plibsys/platforms/linux-clang/platform.cmake b/3rdparty/plibsys/platforms/linux-clang/platform.cmake new file mode 100644 index 0000000..f2067c4 --- /dev/null +++ b/3rdparty/plibsys/platforms/linux-clang/platform.cmake @@ -0,0 +1,12 @@ +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL posix) +set (PLIBSYS_TIME_PROFILER_MODEL posix) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES -pthread rt dl) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT + -D_GNU_SOURCE +) diff --git a/3rdparty/plibsys/platforms/linux-cray/platform.cmake b/3rdparty/plibsys/platforms/linux-cray/platform.cmake new file mode 100644 index 0000000..fc125c0 --- /dev/null +++ b/3rdparty/plibsys/platforms/linux-cray/platform.cmake @@ -0,0 +1,18 @@ +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL posix) +set (PLIBSYS_TIME_PROFILER_MODEL posix) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES rt dl) +set (PLIBSYS_PLATFORM_CFLAGS "-h threadsafe -h nomessage=186 -O1") + +set (PLIBSYS_PLATFORM_DEFINES + -D_GNU_SOURCE +) + +message (" + Cray compiler has an optimization bug on SHA-1 algorithm + when using optimization levels higher than O1. Thus + O1 is enabled by default. + ") diff --git a/3rdparty/plibsys/platforms/linux-gcc/platform.cmake b/3rdparty/plibsys/platforms/linux-gcc/platform.cmake new file mode 100644 index 0000000..f2067c4 --- /dev/null +++ b/3rdparty/plibsys/platforms/linux-gcc/platform.cmake @@ -0,0 +1,12 @@ +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL posix) +set (PLIBSYS_TIME_PROFILER_MODEL posix) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES -pthread rt dl) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT + -D_GNU_SOURCE +) diff --git a/3rdparty/plibsys/platforms/linux-icc/platform.cmake b/3rdparty/plibsys/platforms/linux-icc/platform.cmake new file mode 100644 index 0000000..edf371a --- /dev/null +++ b/3rdparty/plibsys/platforms/linux-icc/platform.cmake @@ -0,0 +1,12 @@ +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL posix) +set (PLIBSYS_TIME_PROFILER_MODEL posix) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES -pthread rt dl imf svml irng intlc) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT + -D_GNU_SOURCE +) diff --git a/3rdparty/plibsys/platforms/linux-pgi/platform.cmake b/3rdparty/plibsys/platforms/linux-pgi/platform.cmake new file mode 100644 index 0000000..defde72 --- /dev/null +++ b/3rdparty/plibsys/platforms/linux-pgi/platform.cmake @@ -0,0 +1,12 @@ +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL posix) +set (PLIBSYS_TIME_PROFILER_MODEL posix) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES pthread rt dl) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT + -D_GNU_SOURCE +) diff --git a/3rdparty/plibsys/platforms/linux-xlc/platform.cmake b/3rdparty/plibsys/platforms/linux-xlc/platform.cmake new file mode 100644 index 0000000..c1b573f --- /dev/null +++ b/3rdparty/plibsys/platforms/linux-xlc/platform.cmake @@ -0,0 +1,11 @@ +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL posix) +set (PLIBSYS_TIME_PROFILER_MODEL posix) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES rt dl) + +set (PLIBSYS_PLATFORM_DEFINES + -D_GNU_SOURCE +) diff --git a/3rdparty/plibsys/platforms/msys-clang/platform.cmake b/3rdparty/plibsys/platforms/msys-clang/platform.cmake new file mode 100644 index 0000000..53796bd --- /dev/null +++ b/3rdparty/plibsys/platforms/msys-clang/platform.cmake @@ -0,0 +1,12 @@ +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL sysv) +set (PLIBSYS_TIME_PROFILER_MODEL posix) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES -pthread rt dl) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT + -D_GNU_SOURCE +) diff --git a/3rdparty/plibsys/platforms/msys-gcc/platform.cmake b/3rdparty/plibsys/platforms/msys-gcc/platform.cmake new file mode 100644 index 0000000..53796bd --- /dev/null +++ b/3rdparty/plibsys/platforms/msys-gcc/platform.cmake @@ -0,0 +1,12 @@ +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL sysv) +set (PLIBSYS_TIME_PROFILER_MODEL posix) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES -pthread rt dl) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT + -D_GNU_SOURCE +) diff --git a/3rdparty/plibsys/platforms/netbsd-clang/platform.cmake b/3rdparty/plibsys/platforms/netbsd-clang/platform.cmake new file mode 100644 index 0000000..975c5df --- /dev/null +++ b/3rdparty/plibsys/platforms/netbsd-clang/platform.cmake @@ -0,0 +1,11 @@ +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL sysv) +set (PLIBSYS_TIME_PROFILER_MODEL posix) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES -pthread) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT +) diff --git a/3rdparty/plibsys/platforms/netbsd-gcc/platform.cmake b/3rdparty/plibsys/platforms/netbsd-gcc/platform.cmake new file mode 100644 index 0000000..975c5df --- /dev/null +++ b/3rdparty/plibsys/platforms/netbsd-gcc/platform.cmake @@ -0,0 +1,11 @@ +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL sysv) +set (PLIBSYS_TIME_PROFILER_MODEL posix) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES -pthread) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT +) diff --git a/3rdparty/plibsys/platforms/openbsd-clang/platform.cmake b/3rdparty/plibsys/platforms/openbsd-clang/platform.cmake new file mode 100644 index 0000000..975c5df --- /dev/null +++ b/3rdparty/plibsys/platforms/openbsd-clang/platform.cmake @@ -0,0 +1,11 @@ +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL sysv) +set (PLIBSYS_TIME_PROFILER_MODEL posix) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES -pthread) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT +) diff --git a/3rdparty/plibsys/platforms/openbsd-gcc/platform.cmake b/3rdparty/plibsys/platforms/openbsd-gcc/platform.cmake new file mode 100644 index 0000000..975c5df --- /dev/null +++ b/3rdparty/plibsys/platforms/openbsd-gcc/platform.cmake @@ -0,0 +1,11 @@ +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL sysv) +set (PLIBSYS_TIME_PROFILER_MODEL posix) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES -pthread) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT +) diff --git a/3rdparty/plibsys/platforms/os2-gcc/platform.cmake b/3rdparty/plibsys/platforms/os2-gcc/platform.cmake new file mode 100644 index 0000000..258bbb5 --- /dev/null +++ b/3rdparty/plibsys/platforms/os2-gcc/platform.cmake @@ -0,0 +1,12 @@ +set (PLIBSYS_THREAD_MODEL os2) +set (PLIBSYS_IPC_MODEL os2) +set (PLIBSYS_TIME_PROFILER_MODEL os2) +set (PLIBSYS_DIR_MODEL os2) +set (PLIBSYS_RWLOCK_MODEL general) +set (PLIBSYS_LIBRARYLOADER_MODEL os2) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES os2386) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT +) diff --git a/3rdparty/plibsys/platforms/qnx-qcc/platform.cmake b/3rdparty/plibsys/platforms/qnx-qcc/platform.cmake new file mode 100644 index 0000000..de75bc2 --- /dev/null +++ b/3rdparty/plibsys/platforms/qnx-qcc/platform.cmake @@ -0,0 +1,12 @@ +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL posix) +set (PLIBSYS_TIME_PROFILER_MODEL posix) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES socket) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT + -D_POSIX_THREAD_SAFE_FUNCTIONS +) diff --git a/3rdparty/plibsys/platforms/scosv-gcc/platform.cmake b/3rdparty/plibsys/platforms/scosv-gcc/platform.cmake new file mode 100644 index 0000000..d1a2d8d --- /dev/null +++ b/3rdparty/plibsys/platforms/scosv-gcc/platform.cmake @@ -0,0 +1,27 @@ +include (${PROJECT_SOURCE_DIR}/platforms/common/SCOSV.cmake) + +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL sysv) +set (PLIBSYS_TIME_PROFILER_MODEL posix) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT + -D_SIMPLE_R +) + +if (CMAKE_SYSTEM_VERSION VERSION_LESS "5.0") + set (PLIBSYS_PLATFORM_LINK_LIBRARIES socket nsl gthreads malloc) + + plibsys_scosv_print_threading_message () +else() + set (PLIBSYS_PLATFORM_LINK_LIBRARIES socket nsl -pthread) + set (PLIBSYS_PLATFORM_CFLAGS -pthread) + + message (" + SCO OpenServer 6 was not actually tested with GCC + compiler. This build may or may not work properly. + Consider running tests before usage. + ") +endif() diff --git a/3rdparty/plibsys/platforms/sunos-gcc/platform.cmake b/3rdparty/plibsys/platforms/sunos-gcc/platform.cmake new file mode 100644 index 0000000..a980d6e --- /dev/null +++ b/3rdparty/plibsys/platforms/sunos-gcc/platform.cmake @@ -0,0 +1,12 @@ +set (PLIBSYS_THREAD_MODEL solaris) +set (PLIBSYS_IPC_MODEL sysv) +set (PLIBSYS_TIME_PROFILER_MODEL solaris) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES socket nsl rt) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT + -D_POSIX_PTHREAD_SEMANTICS +) diff --git a/3rdparty/plibsys/platforms/sunos-sunpro/platform.cmake b/3rdparty/plibsys/platforms/sunos-sunpro/platform.cmake new file mode 100644 index 0000000..a980d6e --- /dev/null +++ b/3rdparty/plibsys/platforms/sunos-sunpro/platform.cmake @@ -0,0 +1,12 @@ +set (PLIBSYS_THREAD_MODEL solaris) +set (PLIBSYS_IPC_MODEL sysv) +set (PLIBSYS_TIME_PROFILER_MODEL solaris) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES socket nsl rt) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT + -D_POSIX_PTHREAD_SEMANTICS +) diff --git a/3rdparty/plibsys/platforms/syllable-gcc/platform.cmake b/3rdparty/plibsys/platforms/syllable-gcc/platform.cmake new file mode 100644 index 0000000..a61e1e0 --- /dev/null +++ b/3rdparty/plibsys/platforms/syllable-gcc/platform.cmake @@ -0,0 +1,12 @@ +set (PLIBSYS_THREAD_MODEL atheos) +set (PLIBSYS_IPC_MODEL none) +set (PLIBSYS_TIME_PROFILER_MODEL posix) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_RWLOCK_MODEL general) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES rt dl) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT +) diff --git a/3rdparty/plibsys/platforms/tru64-compaq/platform.cmake b/3rdparty/plibsys/platforms/tru64-compaq/platform.cmake new file mode 100644 index 0000000..693ba57 --- /dev/null +++ b/3rdparty/plibsys/platforms/tru64-compaq/platform.cmake @@ -0,0 +1,17 @@ +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL posix) +set (PLIBSYS_TIME_PROFILER_MODEL posix) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_ATOMIC_MODEL decc) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES -pthread rt) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT + -D_XOPEN_SOURCE_EXTENDED=1 + -D_POSIX_PII_SOCKET + -D_OSF_SOURCE + -D_XOPEN_SOURCE=500 + -D_POSIX_SOURCE=199506L +) diff --git a/3rdparty/plibsys/platforms/tru64-gcc/platform.cmake b/3rdparty/plibsys/platforms/tru64-gcc/platform.cmake new file mode 100644 index 0000000..ecacc1d --- /dev/null +++ b/3rdparty/plibsys/platforms/tru64-gcc/platform.cmake @@ -0,0 +1,16 @@ +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL posix) +set (PLIBSYS_TIME_PROFILER_MODEL posix) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES -pthread rt) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT + -D_XOPEN_SOURCE_EXTENDED=1 + -D_POSIX_PII_SOCKET + -D_OSF_SOURCE + -D_XOPEN_SOURCE=500 + -D_POSIX_SOURCE=199506L +) diff --git a/3rdparty/plibsys/platforms/unixware-gcc/platform.cmake b/3rdparty/plibsys/platforms/unixware-gcc/platform.cmake new file mode 100644 index 0000000..96ca256 --- /dev/null +++ b/3rdparty/plibsys/platforms/unixware-gcc/platform.cmake @@ -0,0 +1,11 @@ +set (PLIBSYS_THREAD_MODEL posix) +set (PLIBSYS_IPC_MODEL sysv) +set (PLIBSYS_TIME_PROFILER_MODEL posix) +set (PLIBSYS_DIR_MODEL posix) +set (PLIBSYS_LIBRARYLOADER_MODEL posix) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES socket nsl -pthread) + +set (PLIBSYS_PLATFORM_DEFINES + -D_REENTRANT +) diff --git a/3rdparty/plibsys/platforms/vms-general/README.md b/3rdparty/plibsys/platforms/vms-general/README.md new file mode 100644 index 0000000..9bbfc85 --- /dev/null +++ b/3rdparty/plibsys/platforms/vms-general/README.md @@ -0,0 +1,49 @@ +## OpenVMS + +This directory contains mainly a build script for OpenVMS. + +## Requirements + +* OpenVMS 8.4 or later (Alpha or IA64), VAX is not supported +* DEC CC 6.5 or later +* DEC CXX 7.1 or later (for tests only) + +## Building + +Library can be built with 32-bit or 64-bit pointers. By default 64-bit +pointers are used. Use `32` parameter to switch behaviour. + +Test suit is optional and is not built by default. Use `TESTS` parameter to +enable tests. + +There are other build parametes available, plese look inside the +`build_vms.com` (a DCL-based script) to see the detailed description for +all of them. + +Object library (.OLB) and shareable image (.EXE) are built. An object +library acts like a widely-known static library, and a shareable image +acts like a shared library. All libraries and tests are placed inside the +`[.ALPHA]` or `[.IA64]` directory depending on a host architecture. + +Do not forget to define a logical name for a shareable image of the library +before running programs which use it: + +`DEFINE PLIBSYS SYS$SYSROOT:[BUILD_DIR]PLIBSYS.EXE` + +You can also place an image into the `SYS$SHARE` directory instead of +defining a logical name. + +Here are some examples of the build commands: + +* `@build_vms.com` builds only the libraries (64-bit pointers). +* `@build_vms.com 32 TESTS` builds libraries (32-bit pointers) and all +the tests. +* `@build_vms.com NOLIB RUN_TESTS` only runs already built tests. +* `@build_vms.com CLEAN` cleans all the files produced during a build. + +## More + +OpenVMS can mangle long (> 31 characters) symbol names in a compiled object +to fit the limit. Sometimes it is useful to know the mangled name of a +symbol. Use the `vms_shorten_symbol.c` program to get a mangled name. See +details inside. diff --git a/3rdparty/plibsys/platforms/vms-general/build_vms.com b/3rdparty/plibsys/platforms/vms-general/build_vms.com new file mode 100644 index 0000000..ce5e95c --- /dev/null +++ b/3rdparty/plibsys/platforms/vms-general/build_vms.com @@ -0,0 +1,616 @@ +$! +$! Copyright 2011, Richard Levitte +$! Copyright 2014, John Malmberg +$! Copyright 2016-2018, Alexander Saprykin +$! +$! Permission to use, copy, modify, and/or distribute this software for any +$! purpose with or without fee is hereby granted, provided that the above +$! copyright notice and this permission notice appear in all copies. +$! +$! THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +$! WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +$! MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +$! ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +$! WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +$! ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +$! OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +$! +$!=========================================================================== +$! Command-line options: +$! +$! 32 Compile with 32-bit pointers. +$! BIGENDIAN Compile for a big endian host. +$! CCQUAL=x Add "x" to the C compiler qualifiers. +$! DEBUG Build in debug mode. +$! CLEAN Only perform clean after the previous build. +$! TESTS=(x) Build library tests. Comma separated test names or leave +$! empty to build all the tests. +$! Example 1 (curtain tests): TESTS=(pmem,puthread) +$! Example 2 (all tests): TESTS +$! RUN_TESTS Runs all tests. +$! NOLIB Skip library buidling. Useful when you want to rebuild +$! particular tests. +$!=========================================================================== +$! +$! +$! Save the original default dev:[dir], and arrange for its restoration +$! at exit. +$!--------------------------------------------------------------------- +$ orig_def = f$environment("DEFAULT") +$ on error then goto common_exit +$ on severe_error then goto common_exit +$ on control_y then goto common_exit +$! +$ ctrl_y = 1556 +$ proc = f$environment("PROCEDURE") +$ proc_fid = f$file_attributes(proc, "FID") +$ proc_dev = f$parse(proc, , , "DEVICE") +$ proc_dir = f$parse(proc, , , "DIRECTORY") +$ proc_name = f$parse(proc, , , "NAME") +$ proc_type = f$parse(proc, , , "TYPE") +$ proc_dev_dir = proc_dev + proc_dir +$! +$! Have to manually parse the device for a search list. +$! Can not use the f$parse() as it will return the first name +$! in the search list. +$! +$ orig_def_dev = f$element(0, ":", orig_def) + ":" +$ if orig_def_dev .eqs. "::" then orig_def_dev = "sys$disk:" +$ test_proc = orig_def_dev + proc_dir + proc_name + proc_type +$! +$! If we can find this file using the default directory +$! then we know that we should use the original device from the +$! default directory which could be a search list. +$! +$ test_proc_fid = f$file_attributes(test_proc, "FID") +$! +$ if (test_proc_fid .eq. proc_fid) +$ then +$ proc_dev_dir = orig_def_dev + proc_dir +$ endif +$! +$! Verbose output message stuff. Define symbol to "write sys$output". +$! vo_c - verbose output for compile +$! +$ vo_c := "write sys$output" +$! +$! Determine the main distribution directory ("[--]") in an +$! ODS5-tolerant (case-insensitive) way. (We do assume that the only +$! "]" or ">" is the one at the end.) +$! +$! Some non-US VMS installations report ">" for the directory delimiter +$! so do not assume that it is "]". +$! +$ orig_def_len = f$length(orig_def) +$ delim = f$extract(orig_def_len - 1, 1, orig_def) +$! +$ set default 'proc_dev_dir' +$ set default [--.src] +$ base_src_dir = f$environment("default") +$ set default 'proc_dev_dir' +$! +$! Define the architecture-specific destination directory name +$! ----------------------------------------------------------- +$! +$ if (f$getsyi("HW_MODEL") .lt. 1024) +$ then +$ 'vo_c' "%PLIBSYS-F-NOTSUP, VAX platform is not supported, sorry :(" +$ goto common_exit +$ else +$ arch_name = "" +$ arch_name = arch_name + f$edit(f$getsyi("ARCH_NAME"), "UPCASE") +$! +$ if (arch_name .eqs. "") then arch_name = "UNK" +$! +$ node_swvers = f$getsyi("node_swvers") +$ version_patch = f$extract(1, f$length(node_swvers), node_swvers) +$ maj_ver = f$element(0, ".", version_patch) +$ min_ver_patch = f$element(1, ".", version_patch) +$ min_ver = f$element(0, "-", min_ver_patch) +$ patch = f$element(1, "-", min_ver_patch) +$! +$ if maj_ver .lts. "8" .or. min_ver .lts. "4" +$ then +$ 'vo_c' "%PLIBSYS-F-NOTSUP, only OpenVMS 8.4 and above are supported, sorry :(" +$ goto common_exit +$ endif +$ endif +$! +$ objdir = proc_dev_dir - delim + ".''arch_name'" + delim +$! +$! Parse input arguments +$! --------------------- +$! Allow arguments to be grouped together with comma or separated by spaces +$! Do no know if we will need more than 8. +$ args = "," + p1 + "," + p2 + "," + p3 + "," + p4 + "," +$ args = args + p5 + "," + p6 + "," + p7 + "," + p8 + "," +$! +$! Provide lower case version to simplify parsing. +$ args_lower = f$edit(args, "LOWERCASE,COLLAPSE") +$! +$ args_len = f$length(args) +$ args_lower_len = f$length(args_lower) +$! +$ if f$locate(",clean,", args_lower) .lt. args_lower_len +$ then +$ 'vo_c' "Cleaning up previous build..." +$ set default 'proc_dev_dir' +$! +$ if f$search("''arch_name'.DIR") .nes. "" +$ then +$ set prot=w:d []'arch_name'.DIR;* +$ delete/tree [.'arch_name'...]*.*;* +$ delete []'arch_name'.DIR;* +$ endif +$! +$ goto common_exit +$ endif +$! +$ build_64 = 1 +$ if f$locate(",32,", args_lower) .lt. args_lower_len +$ then +$ build_64 = 0 +$ endif +$! +$ big_endian = 0 +$ if f$locate(",bigendian,", args_lower) .lt. args_lower_len +$ then +$ big_endian = 1 +$ endif +$! +$ cc_extra = "" +$ args_loc = f$locate(",ccqual=", args_lower) +$ if args_loc .lt. args_lower_len +$ then +$ arg = f$extract(args_loc + 1, args_lower_len, args_lower) +$ arg_val = f$element(0, ",", arg) +$ cc_extra = f$element(1, "=", arg_val) +$ endif +$! +$ is_debug = 0 +$ if f$locate(",debug,", args_lower) .lt. args_lower_len +$ then +$ is_debug = 1 +$ endif +$! +$ is_tests = 0 +$ test_list = "" +$ if f$locate(",tests,", args_lower) .lt. args_lower_len +$ then +$ is_tests = 1 +$ else +$ args_loc = f$locate(",tests=(", args_lower) +$ if args_loc .lt. args_lower_len +$ then +$ is_tests = 1 +$ arg = f$extract(args_loc + 1, args_lower_len, args_lower) +$ arg_val = f$element(0, ")", arg) +$ test_list_val = f$element(1, "=", arg_val) - "(" - ")" +$ test_list_val = f$edit(test_list_val, "COLLAPSE") +$ test_list_counter = 0 +$ +$ test_list_loop: +$ next_test_val = f$element (test_list_counter, ",", test_list_val) +$ if next_test_val .nes. "" .and. next_test_val .nes. "," +$ then +$ test_list = test_list + next_test_val + " " +$ test_list_counter = test_list_counter + 1 +$ goto test_list_loop +$ endif +$ endif +$ endif +$! +$ run_tests = 0 +$ if f$locate(",run_tests,", args_lower) .lt. args_lower_len +$ then +$ run_tests = 1 +$ endif +$! +$! Prepare build directory +$! ----------------------- +$! +$! When building on a search list, need to do a create to make sure that +$! the output directory exists, since the clean procedure tries to delete +$! it. +$! +$ if f$search("''proc_dev_dir'''arch_name'.DIR") .eqs. "" +$ then +$ create/dir 'objdir'/prot=o:rwed +$ endif +$! +$ set default 'objdir' +$ if f$search("CXX_REPOSITORY.DIR") .nes. "" +$ then +$ set prot=w:d []CXX_REPOSITORY.DIR;* +$ delete/tree [.CXX_REPOSITORY...]*.*;* +$ delete []CXX_REPOSITORY.DIR;* +$ endif +$! +$ if f$locate(",nolib,", args_lower) .lt. args_lower_len +$ then +$ goto build_tests +$ endif +$! +$! Generate platform-specific config file +$! -------------------------------------- +$! +$ if f$search("plibsysconfig.h") .nes. "" then delete plibsysconfig.h;* +$! +$! Get the version number +$! ---------------------- +$! +$ i = 0 +$ open/read/error=version_loop_end vhf [---]CMakeLists.txt +$ version_loop: +$ read/end=version_loop_end vhf line_in +$! +$ if line_in .eqs. "" then goto version_loop +$! +$ if f$locate("set (PLIBSYS_VERSION_MAJOR ", line_in) .eq. 0 +$ then +$ plibsys_vmajor = f$element(2, " ", line_in) - ")" +$ i = i + 1 +$ endif +$! +$ if f$locate("set (PLIBSYS_VERSION_MINOR ", line_in) .eq. 0 +$ then +$ plibsys_vminor = f$element(2, " ", line_in) - ")" +$ i = i + 1 +$ endif +$! +$ if f$locate("set (PLIBSYS_VERSION_PATCH ", line_in) .eq. 0 +$ then +$ plibsys_vpatch = f$element(2, " ", line_in) - ")" +$ i = i + 1 +$ endif +$! +$ if f$locate("set (PLIBSYS_VERSION_NUM ", line_in) .eq. 0 +$ then +$ plibsys_vnum = f$element(2, " ", line_in) - ")" +$ i = i + 1 +$ endif +$! +$ if i .lt 4 then goto version_loop +$ version_loop_end: +$ close vhf +$! +$! Write config file +$! ----------------- +$! +$ open/write/error=config_write_end chf plibsysconfig.h +$ write chf "#ifndef PLIBSYS_HEADER_PLIBSYSCONFIG_H" +$ write chf "#define PLIBSYS_HEADER_PLIBSYSCONFIG_H" +$ write chf "" +$ write chf "#define PLIBSYS_VERSION_MAJOR ''plibsys_vmajor'" +$ write chf "#define PLIBSYS_VERSION_MINOR ''plibsys_vminor'" +$ write chf "#define PLIBSYS_VERSION_PATCH ''plibsys_vpatch'" +$ write chf "#define PLIBSYS_VERSION_STR ""''plibsys_vmajor'.''plibsys_vminor'.''plibsys_vpatch'""" +$ write chf "#define PLIBSYS_VERSION ''plibsys_vnum'" +$ write chf "" +$ write chf "#define PLIBSYS_SIZEOF_SAFAMILY_T 1" +$ write chf "" +$ write chf "#include " +$ write chf "" +$ write chf "#include " +$ write chf "#include " +$ write chf "" +$ write chf "P_BEGIN_DECLS" +$ write chf "" +$ write chf "#define P_MINFLOAT FLT_MIN" +$ write chf "#define P_MAXFLOAT FLT_MAX" +$ write chf "#define P_MINDOUBLE DBL_MIN" +$ write chf "#define P_MAXDOUBLE DBL_MAX" +$ write chf "#define P_MINSHORT SHRT_MIN" +$ write chf "#define P_MAXSHORT SHRT_MAX" +$ write chf "#define P_MAXUSHORT USHRT_MAX" +$ write chf "#define P_MININT INT_MIN" +$ write chf "#define P_MAXINT INT_MAX" +$ write chf "#define P_MAXUINT UINT_MAX" +$ write chf "#define P_MINLONG LONG_MIN" +$ write chf "#define P_MAXLONG LONG_MAX" +$ write chf "#define P_MAXULONG ULONG_MAX" +$ write chf "" +$ write chf "#define PLIBSYS_MMAP_HAS_MAP_ANONYMOUS" +$ write chf "#define PLIBSYS_HAS_NANOSLEEP" +$ write chf "#define PLIBSYS_HAS_GETADDRINFO" +$ write chf "#define PLIBSYS_HAS_POSIX_SCHEDULING" +$ write chf "#define PLIBSYS_HAS_POSIX_STACKSIZE" +$ write chf "#define PLIBSYS_HAS_SOCKADDR_STORAGE" +$ write chf "#define PLIBSYS_SOCKADDR_HAS_SA_LEN" +$ write chf "#define PLIBSYS_SOCKADDR_IN6_HAS_SCOPEID" +$ write chf "#define PLIBSYS_SOCKADDR_IN6_HAS_FLOWINFO" +$ write chf "" +$! +$ if build_64 .eqs. "1" +$ then +$ write chf "#define PLIBSYS_SIZEOF_VOID_P 8" +$ write chf "#define PLIBSYS_SIZEOF_SIZE_T 8" +$ else +$ write chf "#define PLIBSYS_SIZEOF_VOID_P 4" +$ write chf "#define PLIBSYS_SIZEOF_SIZE_T 4" +$ endif +$! +$ write chf "#define PLIBSYS_SIZEOF_LONG 4" +$ write chf "" +$! +$ if big_endian .eqs. "1" +$ then +$ write chf "#define P_BYTE_ORDER P_BIG_ENDIAN" +$ else +$ write chf "#define P_BYTE_ORDER P_LITTLE_ENDIAN" +$ endif +$! +$ write chf "" +$ write chf "P_END_DECLS" +$ write chf "" +$ write chf "#endif /* PLIBSYS_HEADER_PLIBSYSCONFIG_H */" +$ config_write_end: +$ close chf +$! +$! Prepare sources for compilation +$! ------------------------------- +$! +$ cc_link_params = "" +$ cc_params = "/NAMES=(AS_IS,SHORTENED)" +$ cc_params = cc_params + "/DEFINE=(PLIBSYS_COMPILATION,_REENTRANT,_POSIX_EXIT)" +$ cc_params = cc_params + "/INCLUDE_DIRECTORY=(''objdir',''base_src_dir')" +$ cc_params = cc_params + "/FLOAT=IEEE/IEEE_MODE=DENORM_RESULTS" +$! +$ if build_64 .eqs. "1" +$ then +$ cc_params = cc_params + "/POINTER_SIZE=64" +$ else +$ cc_params = cc_params + "/POINTER_SIZE=32" +$ endif +$! +$ if cc_extra .nes. "" +$ then +$ cc_params = cc_params + " " + cc_extra +$ endif +$! +$ if is_debug .eqs. "1" +$ then +$ cc_params = cc_params + "/DEBUG/NOOPTIMIZE/LIST/SHOW=ALL" +$ cc_link_params = "/DEBUG/TRACEBACK" +$ else +$ cc_link_params = "/NODEBUG/NOTRACEBACK" +$ endif +$! +$ plibsys_src = "patomic-decc.c pcondvariable-posix.c pcryptohash-gost3411.c pcryptohash-md5.c" +$ plibsys_src = plibsys_src + " pcryptohash-sha1.c pcryptohash-sha2-256.c pcryptohash-sha2-512.c" +$ plibsys_src = plibsys_src + " pcryptohash-sha3.c pcryptohash.c pdir-posix.c pdir.c" +$ plibsys_src = plibsys_src + " perror.c pfile.c phashtable.c pinifile.c pipc.c plibraryloader-posix.c" +$ plibsys_src = plibsys_src + " plist.c pmain.c pmem.c pmutex-posix.c pprocess.c prwlock-posix.c" +$ plibsys_src = plibsys_src + " psemaphore-posix.c pshm-posix.c pshmbuffer.c psocket.c" +$ plibsys_src = plibsys_src + " psocketaddress.c pspinlock-decc.c pstring.c psysclose-unix.c" +$ plibsys_src = plibsys_src + " ptimeprofiler-posix.c ptimeprofiler.c ptree-avl.c ptree-bst.c" +$ plibsys_src = plibsys_src + " ptree-rb.c ptree.c puthread-posix.c puthread.c" +$! +$! Inform about building +$! --------------------- +$! +$ if build_64 .eqs. "1" +$ then +$ 'vo_c' "Building for ''arch_name' (64-bit)" +$ else +$ 'vo_c' "Building for ''arch_name' (32-bit)" +$ endif +$! +$! Compile library modules +$! ----------------------- +$! +$ 'vo_c' "Compiling object modules..." +$ src_counter = 0 +$ plibsys_src = f$edit(plibsys_src, "COMPRESS") +$ plibsys_objs = "" +$! +$ src_loop: +$ next_src = f$element (src_counter, " ", plibsys_src) +$ if next_src .nes. "" .and. next_src .nes. " " +$ then +$ 'vo_c' "[CC] ''next_src'" +$ cc [---.src]'next_src' 'cc_params' +$! +$ src_counter = src_counter + 1 +$! +$ obj_file = f$extract (0, f$length (next_src) - 1, next_src) + "obj" +$ plibsys_objs = plibsys_objs + "''obj_file'," +$ purge 'obj_file' +$! +$ goto src_loop +$ endif +$! +$ plibsys_objs = f$extract (0, f$length (plibsys_objs) - 1, plibsys_objs) +$! +$! Create library +$! -------------- +$! +$ 'vo_c' "Creating object library..." +$ library/CREATE/INSERT/REPLACE /LIST=PLIBSYS.LIS PLIBSYS.OLB 'plibsys_objs' +$ library/COMPRESS PLIBSYS.OLB +$ purge PLIBSYS.OLB +$ purge PLIBSYS.LIS +$! +$ 'vo_c' "Creating shared library..." +$ link/SHAREABLE=PLIBSYS.EXE /MAP=PLIBSYS.MAP 'cc_link_params' 'plibsys_objs', [-]plibsys.opt/OPTION +$ purge PLIBSYS.EXE +$ purge PLIBSYS.MAP +$! +$! Testing area +$! ------------ +$! +$ build_tests: +$ test_list_full = "patomic pcondvariable pcryptohash pdir" +$ test_list_full = test_list_full + " perror pfile phashtable pinifile plibraryloader plist" +$ test_list_full = test_list_full + " pmacros pmain pmem pmutex pprocess prwlock psemaphore" +$ test_list_full = test_list_full + " pshm pshmbuffer psocket psocketaddress pspinlock pstdarg" +$ test_list_full = test_list_full + " pstring ptimeprofiler ptree ptypes puthread" +$! +$ if is_tests .eqs. "0" +$ then +$ goto build_done +$ endif +$! +$! Write link options file +$! ----------------------- +$! +$ if f$search("plibsys_link.opt") .nes. "" then delete plibsys_link.opt;* +$! +$ open/write/error=link_write_end lhf plibsys_link.opt +$ write lhf "''objdir'PLIBSYS.EXE/SHARE" +$ write lhf "" +$ link_write_end: +$ close lhf +$! +$! Compile tests +$! ------------------------- +$! +$ if test_list .nes. "" +$ then +$ plibsys_tests = f$edit(test_list, "TRIM") +$ else +$ plibsys_tests = test_list_full +$ endif +$! +$ 'vo_c' "Compiling test executables..." +$ test_counter = 0 +$ plibsys_tests = f$edit(plibsys_tests, "COMPRESS") +$! +$ cxx_params = "/INCLUDE=(''objdir',''base_src_dir')" +$ cxx_params = cxx_params + "/DEFINE=(__USE_STD_IOSTREAM)/NAMES=(AS_IS, SHORTENED)" +$ cxx_params = cxx_params + "/FLOAT=IEEE/IEEE_MODE=DENORM_RESULTS" +$! +$ if build_64 .eqs. "1" +$ then +$ set noon +$ define/user/nolog sys$output NL: +$ define/user/nolog sys$error NL: +$ cxx/POINTER_SIZE=64=ARGV NL: +$! +$ if ($STATUS .and. %X0FFF0000) .eq. %X00030000 +$ then +$! +$! If we got here, it means DCL complained like this: +$! %DCL-W-NOVALU, value not allowed - remove value specification +$! \64=\ +$! +$! If the compiler was run, logicals defined in /USER would +$! have been deassigned automatically. However, when DCL +$! complains, they aren't, so we do it here (it might be +$! unnecessary, but just in case there will be another error +$! message further on that we don't want to miss). +$! +$ deassign/user/nolog sys$error +$ deassign/user/nolog sys$output +$ cxx_params = cxx_params + "/POINTER_SIZE=64" +$ else +$ cxx_params = cxx_params + "/POINTER_SIZE=64=ARGV" +$ endif +$ else +$ cxx_params = cxx_params + "/POINTER_SIZE=32" +$ endif +$! +$ if is_debug .eqs. "1" +$ then +$ cxx_params = cxx_params + "/DEBUG/NOOPTIMIZE/LIST/SHOW=ALL" +$ endif +$! +$ test_loop: +$ next_test = f$element (test_counter, " ", plibsys_tests) +$ if next_test .nes. "" .and. next_test .nes. " " +$ then +$ next_test = next_test + "_test" +$ 'vo_c' "[CXX ] ''next_test'.cpp" +$ cxx [---.tests]'next_test'.cpp 'cxx_params' +$! +$ 'vo_c' "[CXXLINK] ''next_test'.obj" +$ cxxlink 'next_test'.obj,'objdir'plibsys_link.opt/OPTION /THREADS_ENABLE +$! +$ if f$search("CXX_REPOSITORY.DIR") .nes. "" +$ then +$ set prot=w:d []CXX_REPOSITORY.DIR;* +$ delete/tree [.CXX_REPOSITORY...]*.*;* +$ delete []CXX_REPOSITORY.DIR;* +$ endif +$! +$ purge 'next_test'.obj +$ purge 'next_test'.exe +$! +$ test_counter = test_counter + 1 +$ goto test_loop +$ endif +$! +$ build_done: +$ 'vo_c' "Build done." +$! +$! Run unit tests +$! -------------- +$! +$ if run_tests .eqs. "0" +$ then +$ if is_tests .eqs. "1" +$ then +$ 'vo_c' "To run tests invoke: @build_vms.com NOLIB RUN_TESTS" +$ endif +$ goto common_exit +$ endif +$! +$ 'vo_c' "Running tests..." +$ test_counter = 0 +$ tests_passed = 0 +$! +$ run_loop: +$ next_test = f$element (test_counter, " ", test_list_full) +$ if next_test .nes. "" .and. next_test .nes. " " +$ then +$ if f$search("''next_test'_test.exe") .eqs. "" +$ then +$ 'vo_c' "[SKIP] Test not found: ''next_test'" +$ goto run_loop_next +$ endif +$! +$ 'vo_c' "[RUN ] ''next_test'" +$! +$ define/user/nolog sys$error NL: +$ define/user/nolog sys$output NL: +$ define/user/nolog plibsys 'objdir'PLIBSYS.EXE +$ define/user/nolog test_imgdir 'objdir' +$! +$! Disable error cheking for the test binary +$! +$ set noon +$! +$ xrun := $test_imgdir:'next_test'_test.exe +$ if next_test .eqs. "plibraryloader" +$ then +$ xrun 'objdir'PLIBSYS.EXE +$ else +$ xrun +$ endif +$! +$ if $STATUS .eqs. "%X00000001" +$ then +$ 'vo_c' "[PASS] Test passed: ''next_test'" +$ tests_passed = tests_passed + 1 +$ else +$ 'vo_c' "[FAIL] *** Test failed: ''next_test'" +$ endif +$! +$ set on +$! +$ run_loop_next: +$ test_counter = test_counter + 1 +$ goto run_loop +$ endif +$! +$ 'vo_c' "Tests passed: ''tests_passed'/''test_counter'" +$! +$! In case of error during the last test +$ deassign/user/nolog sys$error +$ deassign/user/nolog sys$output +$ deassign/user/nolog plibsys +$ deassign/user/nolog test_imgdir +$! +$ common_exit: +$ set default 'orig_def' +$ exit diff --git a/3rdparty/plibsys/platforms/vms-general/plibsys.opt b/3rdparty/plibsys/platforms/vms-general/plibsys.opt new file mode 100644 index 0000000..408b884 --- /dev/null +++ b/3rdparty/plibsys/platforms/vms-general/plibsys.opt @@ -0,0 +1,540 @@ +GSMATCH=LEQUAL,3,0 +IDENTIFICATION="plibsys" +CASE_SENSITIVE=YES +! +! patomic.h +! +SYMBOL_VECTOR=(p_atomic_int_get=PROCEDURE) +SYMBOL_VECTOR=(P_ATOMIC_INT_GET/p_atomic_int_get=PROCEDURE) +SYMBOL_VECTOR=(p_atomic_int_set=PROCEDURE) +SYMBOL_VECTOR=(P_ATOMIC_INT_SET/p_atomic_int_set=PROCEDURE) +SYMBOL_VECTOR=(p_atomic_int_inc=PROCEDURE) +SYMBOL_VECTOR=(P_ATOMIC_INT_INC/p_atomic_int_inc=PROCEDURE) +SYMBOL_VECTOR=(p_atomic_int_dec_and_test=PROCEDURE) +SYMBOL_VECTOR=(P_ATOMIC_INT_DEC_AND_TEST/p_atomic_int_dec_and_test=PROCEDURE) +SYMBOL_VECTOR=(p_atomic_int_compare_an2fhrots$=PROCEDURE) +SYMBOL_VECTOR=(P_ATOMIC_INT_COMPARE_AN2UA3PBG$/p_atomic_int_compare_an2fhrots$=PROCEDURE) +SYMBOL_VECTOR=(p_atomic_int_add=PROCEDURE) +SYMBOL_VECTOR=(P_ATOMIC_INT_ADD/p_atomic_int_add=PROCEDURE) +SYMBOL_VECTOR=(p_atomic_int_and=PROCEDURE) +SYMBOL_VECTOR=(P_ATOMIC_INT_AND/p_atomic_int_and=PROCEDURE) +SYMBOL_VECTOR=(p_atomic_int_or=PROCEDURE) +SYMBOL_VECTOR=(P_ATOMIC_INT_OR/p_atomic_int_or=PROCEDURE) +SYMBOL_VECTOR=(p_atomic_int_xor=PROCEDURE) +SYMBOL_VECTOR=(P_ATOMIC_INT_XOR/p_atomic_int_xor=PROCEDURE) +SYMBOL_VECTOR=(p_atomic_pointer_get=PROCEDURE) +SYMBOL_VECTOR=(P_ATOMIC_POINTER_GET/p_atomic_pointer_get=PROCEDURE) +SYMBOL_VECTOR=(p_atomic_pointer_set=PROCEDURE) +SYMBOL_VECTOR=(P_ATOMIC_POINTER_SET/p_atomic_pointer_set=PROCEDURE) +SYMBOL_VECTOR=(p_atomic_pointer_compar0usq95l$=PROCEDURE) +SYMBOL_VECTOR=(P_ATOMIC_POINTER_COMPAR19ERBSM$/p_atomic_pointer_compar0usq95l$=PROCEDURE) +SYMBOL_VECTOR=(p_atomic_pointer_add=PROCEDURE) +SYMBOL_VECTOR=(P_ATOMIC_POINTER_ADD/p_atomic_pointer_add=PROCEDURE) +SYMBOL_VECTOR=(p_atomic_pointer_and=PROCEDURE) +SYMBOL_VECTOR=(P_ATOMIC_POINTER_AND/p_atomic_pointer_and=PROCEDURE) +SYMBOL_VECTOR=(p_atomic_pointer_or=PROCEDURE) +SYMBOL_VECTOR=(P_ATOMIC_POINTER_OR/p_atomic_pointer_or=PROCEDURE) +SYMBOL_VECTOR=(p_atomic_pointer_xor=PROCEDURE) +SYMBOL_VECTOR=(P_ATOMIC_POINTER_XOR/p_atomic_pointer_xor=PROCEDURE) +SYMBOL_VECTOR=(p_atomic_is_lock_free=PROCEDURE) +SYMBOL_VECTOR=(P_ATOMIC_IS_LOCK_FREE/p_atomic_is_lock_free=PROCEDURE) +! +! pcondvariable.h +! +SYMBOL_VECTOR=(p_cond_variable_new=PROCEDURE) +SYMBOL_VECTOR=(P_COND_VARIABLE_NEW/p_cond_variable_new=PROCEDURE) +SYMBOL_VECTOR=(p_cond_variable_free=PROCEDURE) +SYMBOL_VECTOR=(P_COND_VARIABLE_FREE/p_cond_variable_free=PROCEDURE) +SYMBOL_VECTOR=(p_cond_variable_wait=PROCEDURE) +SYMBOL_VECTOR=(P_COND_VARIABLE_WAIT/p_cond_variable_wait=PROCEDURE) +SYMBOL_VECTOR=(p_cond_variable_signal=PROCEDURE) +SYMBOL_VECTOR=(P_COND_VARIABLE_SIGNAL/p_cond_variable_signal=PROCEDURE) +SYMBOL_VECTOR=(p_cond_variable_broadcast=PROCEDURE) +SYMBOL_VECTOR=(P_COND_VARIABLE_BROADCAST/p_cond_variable_broadcast=PROCEDURE) +! +! pcryptohash.h +! +SYMBOL_VECTOR=(p_crypto_hash_new=PROCEDURE) +SYMBOL_VECTOR=(P_CRYPTO_HASH_NEW/p_crypto_hash_new=PROCEDURE) +SYMBOL_VECTOR=(p_crypto_hash_update=PROCEDURE) +SYMBOL_VECTOR=(P_CRYPTO_HASH_UPDATE/p_crypto_hash_update=PROCEDURE) +SYMBOL_VECTOR=(p_crypto_hash_reset=PROCEDURE) +SYMBOL_VECTOR=(P_CRYPTO_HASH_RESET/p_crypto_hash_reset=PROCEDURE) +SYMBOL_VECTOR=(p_crypto_hash_get_string=PROCEDURE) +SYMBOL_VECTOR=(P_CRYPTO_HASH_GET_STRING/p_crypto_hash_get_string=PROCEDURE) +SYMBOL_VECTOR=(p_crypto_hash_get_digest=PROCEDURE) +SYMBOL_VECTOR=(P_CRYPTO_HASH_GET_DIGEST/p_crypto_hash_get_digest=PROCEDURE) +SYMBOL_VECTOR=(p_crypto_hash_get_length=PROCEDURE) +SYMBOL_VECTOR=(P_CRYPTO_HASH_GET_LENGTH/p_crypto_hash_get_length=PROCEDURE) +SYMBOL_VECTOR=(p_crypto_hash_get_type=PROCEDURE) +SYMBOL_VECTOR=(P_CRYPTO_HASH_GET_TYPE/p_crypto_hash_get_type=PROCEDURE) +SYMBOL_VECTOR=(p_crypto_hash_free=PROCEDURE) +SYMBOL_VECTOR=(P_CRYPTO_HASH_FREE/p_crypto_hash_free=PROCEDURE) +! +! pdir.h +! +SYMBOL_VECTOR=(p_dir_new=PROCEDURE) +SYMBOL_VECTOR=(P_DIR_NEW/p_dir_new=PROCEDURE) +SYMBOL_VECTOR=(p_dir_create=PROCEDURE) +SYMBOL_VECTOR=(P_DIR_CREATE/p_dir_create=PROCEDURE) +SYMBOL_VECTOR=(p_dir_remove=PROCEDURE) +SYMBOL_VECTOR=(P_DIR_REMOVE/p_dir_remove=PROCEDURE) +SYMBOL_VECTOR=(p_dir_is_exists=PROCEDURE) +SYMBOL_VECTOR=(P_DIR_IS_EXISTS/p_dir_is_exists=PROCEDURE) +SYMBOL_VECTOR=(p_dir_get_path=PROCEDURE) +SYMBOL_VECTOR=(P_DIR_GET_PATH/p_dir_get_path=PROCEDURE) +SYMBOL_VECTOR=(p_dir_get_next_entry=PROCEDURE) +SYMBOL_VECTOR=(P_DIR_GET_NEXT_ENTRY/p_dir_get_next_entry=PROCEDURE) +SYMBOL_VECTOR=(p_dir_rewind=PROCEDURE) +SYMBOL_VECTOR=(P_DIR_REWIND/p_dir_rewind=PROCEDURE) +SYMBOL_VECTOR=(p_dir_entry_free=PROCEDURE) +SYMBOL_VECTOR=(P_DIR_ENTRY_FREE/p_dir_entry_free=PROCEDURE) +SYMBOL_VECTOR=(p_dir_free=PROCEDURE) +SYMBOL_VECTOR=(P_DIR_FREE/p_dir_free=PROCEDURE) +! +! perror.h +! +SYMBOL_VECTOR=(p_error_new=PROCEDURE) +SYMBOL_VECTOR=(P_ERROR_NEW/p_error_new=PROCEDURE) +SYMBOL_VECTOR=(p_error_new_literal=PROCEDURE) +SYMBOL_VECTOR=(P_ERROR_NEW_LITERAL/p_error_new_literal=PROCEDURE) +SYMBOL_VECTOR=(p_error_get_message=PROCEDURE) +SYMBOL_VECTOR=(P_ERROR_GET_MESSAGE/p_error_get_message=PROCEDURE) +SYMBOL_VECTOR=(p_error_get_code=PROCEDURE) +SYMBOL_VECTOR=(P_ERROR_GET_CODE/p_error_get_code=PROCEDURE) +SYMBOL_VECTOR=(p_error_get_native_code=PROCEDURE) +SYMBOL_VECTOR=(P_ERROR_GET_NATIVE_CODE/p_error_get_native_code=PROCEDURE) +SYMBOL_VECTOR=(p_error_get_domain=PROCEDURE) +SYMBOL_VECTOR=(P_ERROR_GET_DOMAIN/p_error_get_domain=PROCEDURE) +SYMBOL_VECTOR=(p_error_get_last_system=PROCEDURE) +SYMBOL_VECTOR=(P_ERROR_GET_LAST_SYSTEM/p_error_get_last_system=PROCEDURE) +SYMBOL_VECTOR=(p_error_get_last_net=PROCEDURE) +SYMBOL_VECTOR=(P_ERROR_GET_LAST_NET/p_error_get_last_net=PROCEDURE) +SYMBOL_VECTOR=(p_error_copy=PROCEDURE) +SYMBOL_VECTOR=(P_ERROR_COPY/p_error_copy=PROCEDURE) +SYMBOL_VECTOR=(p_error_set_error=PROCEDURE) +SYMBOL_VECTOR=(P_ERROR_SET_ERROR/p_error_set_error=PROCEDURE) +SYMBOL_VECTOR=(p_error_set_error_p=PROCEDURE) +SYMBOL_VECTOR=(P_ERROR_SET_ERROR_P/p_error_set_error_p=PROCEDURE) +SYMBOL_VECTOR=(p_error_set_code=PROCEDURE) +SYMBOL_VECTOR=(P_ERROR_SET_CODE/p_error_set_code=PROCEDURE) +SYMBOL_VECTOR=(p_error_set_native_code=PROCEDURE) +SYMBOL_VECTOR=(P_ERROR_SET_NATIVE_CODE/p_error_set_native_code=PROCEDURE) +SYMBOL_VECTOR=(p_error_set_message=PROCEDURE) +SYMBOL_VECTOR=(P_ERROR_SET_MESSAGE/p_error_set_message=PROCEDURE) +SYMBOL_VECTOR=(p_error_set_last_system=PROCEDURE) +SYMBOL_VECTOR=(P_ERROR_SET_LAST_SYSTEM/p_error_set_last_system=PROCEDURE) +SYMBOL_VECTOR=(p_error_set_last_net=PROCEDURE) +SYMBOL_VECTOR=(P_ERROR_SET_LAST_NET/p_error_set_last_net=PROCEDURE) +SYMBOL_VECTOR=(p_error_clear=PROCEDURE) +SYMBOL_VECTOR=(P_ERROR_CLEAR/p_error_clear=PROCEDURE) +SYMBOL_VECTOR=(p_error_free=PROCEDURE) +SYMBOL_VECTOR=(P_ERROR_FREE/p_error_free=PROCEDURE) +! +! pfile.h +! +SYMBOL_VECTOR=(p_file_is_exists=PROCEDURE) +SYMBOL_VECTOR=(P_ERROR_IS_EXISTS/p_file_is_exists=PROCEDURE) +SYMBOL_VECTOR=(p_file_remove=PROCEDURE) +SYMBOL_VECTOR=(P_FILE_REMOVE/p_file_remove=PROCEDURE) +! +! phashtable.h +! +SYMBOL_VECTOR=(p_hash_table_new=PROCEDURE) +SYMBOL_VECTOR=(P_HASH_TABLE_NEW/p_hash_table_new=PROCEDURE) +SYMBOL_VECTOR=(p_hash_table_insert=PROCEDURE) +SYMBOL_VECTOR=(P_HASH_TABLE_INSERT/p_hash_table_insert=PROCEDURE) +SYMBOL_VECTOR=(p_hash_table_lookup=PROCEDURE) +SYMBOL_VECTOR=(P_HASH_TABLE_LOOKUP/p_hash_table_lookup=PROCEDURE) +SYMBOL_VECTOR=(p_hash_table_keys=PROCEDURE) +SYMBOL_VECTOR=(P_HASH_TABLE_KEYS/p_hash_table_keys=PROCEDURE) +SYMBOL_VECTOR=(p_hash_table_values=PROCEDURE) +SYMBOL_VECTOR=(P_HASH_TABLE_VALUES/p_hash_table_values=PROCEDURE) +SYMBOL_VECTOR=(p_hash_table_free=PROCEDURE) +SYMBOL_VECTOR=(P_HASH_TABLE_FREE/p_hash_table_free=PROCEDURE) +SYMBOL_VECTOR=(p_hash_table_remove=PROCEDURE) +SYMBOL_VECTOR=(P_HASH_TABLE_REMOVE/p_hash_table_remove=PROCEDURE) +SYMBOL_VECTOR=(p_hash_table_lookup_by_value=PROCEDURE) +SYMBOL_VECTOR=(P_HASH_TABLE_LOOKUP_BY_VALUE/p_hash_table_lookup_by_value=PROCEDURE) +! +! pinifile.h +! +SYMBOL_VECTOR=(p_ini_file_new=PROCEDURE) +SYMBOL_VECTOR=(P_INI_FILE_NEW/p_ini_file_new=PROCEDURE) +SYMBOL_VECTOR=(p_ini_file_free=PROCEDURE) +SYMBOL_VECTOR=(P_INI_FILE_FREE/p_ini_file_free=PROCEDURE) +SYMBOL_VECTOR=(p_ini_file_parse=PROCEDURE) +SYMBOL_VECTOR=(P_INI_FILE_PARSE/p_ini_file_parse=PROCEDURE) +SYMBOL_VECTOR=(p_ini_file_is_parsed=PROCEDURE) +SYMBOL_VECTOR=(P_INI_FILE_IS_PARSED/p_ini_file_is_parsed=PROCEDURE) +SYMBOL_VECTOR=(p_ini_file_sections=PROCEDURE) +SYMBOL_VECTOR=(P_INI_FILE_SECTIONS/p_ini_file_sections=PROCEDURE) +SYMBOL_VECTOR=(p_ini_file_keys=PROCEDURE) +SYMBOL_VECTOR=(P_INI_FILE_KEYS/p_ini_file_keys=PROCEDURE) +SYMBOL_VECTOR=(p_ini_file_is_key_exists=PROCEDURE) +SYMBOL_VECTOR=(P_INI_FILE_IS_KEY_EXISTS/p_ini_file_is_key_exists=PROCEDURE) +SYMBOL_VECTOR=(p_ini_file_parameter_string=PROCEDURE) +SYMBOL_VECTOR=(P_INI_FILE_PARAMETER_STRING/p_ini_file_parameter_string=PROCEDURE) +SYMBOL_VECTOR=(p_ini_file_parameter_int=PROCEDURE) +SYMBOL_VECTOR=(P_INI_FILE_PARAMETER_INT/p_ini_file_parameter_int=PROCEDURE) +SYMBOL_VECTOR=(p_ini_file_parameter_double=PROCEDURE) +SYMBOL_VECTOR=(P_INI_FILE_PARAMETER_DOUBLE/p_ini_file_parameter_double=PROCEDURE) +SYMBOL_VECTOR=(p_ini_file_parameter_boolean=PROCEDURE) +SYMBOL_VECTOR=(P_INI_FILE_PARAMETER_BOOLEAN/p_ini_file_parameter_boolean=PROCEDURE) +SYMBOL_VECTOR=(p_ini_file_parameter_list=PROCEDURE) +SYMBOL_VECTOR=(P_INI_FILE_PARAMETER_LIST/p_ini_file_parameter_list=PROCEDURE) +! +! plibraryloader.h +! +SYMBOL_VECTOR=(p_library_loader_new=PROCEDURE) +SYMBOL_VECTOR=(P_LIBRARY_LOADER_NEW/p_library_loader_new=PROCEDURE) +SYMBOL_VECTOR=(p_library_loader_get_symbol=PROCEDURE) +SYMBOL_VECTOR=(P_LIBRARY_LOADER_GET_SYMBOL/p_library_loader_get_symbol=PROCEDURE) +SYMBOL_VECTOR=(p_library_loader_free=PROCEDURE) +SYMBOL_VECTOR=(P_LIBRARY_LOADER_FREE/p_library_loader_free=PROCEDURE) +SYMBOL_VECTOR=(p_library_loader_get_last_error=PROCEDURE) +SYMBOL_VECTOR=(P_LIBRARY_LOADER_GET_LAST_ERROR/p_library_loader_get_last_error=PROCEDURE) +SYMBOL_VECTOR=(p_library_loader_is_ref_counted=PROCEDURE) +SYMBOL_VECTOR=(P_LIBRARY_LOADER_IS_REF_COUNTED/p_library_loader_is_ref_counted=PROCEDURE) +! +! plist.h +! +SYMBOL_VECTOR=(p_list_append=PROCEDURE) +SYMBOL_VECTOR=(P_LIST_APPEND/p_list_append=PROCEDURE) +SYMBOL_VECTOR=(p_list_remove=PROCEDURE) +SYMBOL_VECTOR=(P_LIST_REMOVE/p_list_remove=PROCEDURE) +SYMBOL_VECTOR=(p_list_foreach=PROCEDURE) +SYMBOL_VECTOR=(P_LIST_FOREACH/p_list_foreach=PROCEDURE) +SYMBOL_VECTOR=(p_list_free=PROCEDURE) +SYMBOL_VECTOR=(P_LIST_FREE/p_list_free=PROCEDURE) +SYMBOL_VECTOR=(p_list_last=PROCEDURE) +SYMBOL_VECTOR=(P_LIST_LAST/p_list_last=PROCEDURE) +SYMBOL_VECTOR=(p_list_length=PROCEDURE) +SYMBOL_VECTOR=(P_LIST_LENGTH/p_list_length=PROCEDURE) +SYMBOL_VECTOR=(p_list_prepend=PROCEDURE) +SYMBOL_VECTOR=(P_LIST_PREPEND/p_list_prepend=PROCEDURE) +SYMBOL_VECTOR=(p_list_reverse=PROCEDURE) +SYMBOL_VECTOR=(P_LIST_REVERSE/p_list_reverse=PROCEDURE) +! +! pmain.h +! +SYMBOL_VECTOR=(p_libsys_init=PROCEDURE) +SYMBOL_VECTOR=(P_LIBSYS_INIT/p_libsys_init=PROCEDURE) +SYMBOL_VECTOR=(p_libsys_init_full=PROCEDURE) +SYMBOL_VECTOR=(P_LIBSYS_INIT_FULL/p_libsys_init_full=PROCEDURE) +SYMBOL_VECTOR=(p_libsys_shutdown=PROCEDURE) +SYMBOL_VECTOR=(P_LIBSYS_SHUTDOWN/p_libsys_shutdown=PROCEDURE) +SYMBOL_VECTOR=(p_libsys_version=PROCEDURE) +SYMBOL_VECTOR=(P_LIBSYS_VERSION/p_libsys_version=PROCEDURE) +! +! pmem.h +! +SYMBOL_VECTOR=(p_malloc=PROCEDURE) +SYMBOL_VECTOR=(P_MALLOC/p_malloc=PROCEDURE) +SYMBOL_VECTOR=(p_malloc0=PROCEDURE) +SYMBOL_VECTOR=(P_MALLOC0/p_malloc0=PROCEDURE) +SYMBOL_VECTOR=(p_realloc=PROCEDURE) +SYMBOL_VECTOR=(P_REALLOC/p_realloc=PROCEDURE) +SYMBOL_VECTOR=(p_free=PROCEDURE) +SYMBOL_VECTOR=(P_FREE/p_free=PROCEDURE) +SYMBOL_VECTOR=(p_mem_set_vtable=PROCEDURE) +SYMBOL_VECTOR=(P_MEM_SET_VTABLE/p_mem_set_vtable=PROCEDURE) +SYMBOL_VECTOR=(p_mem_restore_vtable=PROCEDURE) +SYMBOL_VECTOR=(P_MEM_RESTORE_VTABLE/p_mem_restore_vtable=PROCEDURE) +SYMBOL_VECTOR=(p_mem_mmap=PROCEDURE) +SYMBOL_VECTOR=(P_MEM_MMAP/p_mem_mmap=PROCEDURE) +SYMBOL_VECTOR=(p_mem_munmap=PROCEDURE) +SYMBOL_VECTOR=(P_MEM_MUNMAP/p_mem_munmap=PROCEDURE) +! +! pmutex.h +! +SYMBOL_VECTOR=(p_mutex_new=PROCEDURE) +SYMBOL_VECTOR=(P_MUTEX_NEW/p_mutex_new=PROCEDURE) +SYMBOL_VECTOR=(p_mutex_lock=PROCEDURE) +SYMBOL_VECTOR=(P_MUTEX_LOCK/p_mutex_lock=PROCEDURE) +SYMBOL_VECTOR=(p_mutex_trylock=PROCEDURE) +SYMBOL_VECTOR=(P_MUTEX_TRYLOCK/p_mutex_trylock=PROCEDURE) +SYMBOL_VECTOR=(p_mutex_unlock=PROCEDURE) +SYMBOL_VECTOR=(P_MUTEX_UNLOCK/p_mutex_unlock=PROCEDURE) +SYMBOL_VECTOR=(p_mutex_free=PROCEDURE) +SYMBOL_VECTOR=(P_MUTEX_FREE/p_mutex_free=PROCEDURE) +! +! pprocess.h +! +SYMBOL_VECTOR=(p_process_get_current_pid=PROCEDURE) +SYMBOL_VECTOR=(P_PROCESS_GET_CURRENT_PID/p_process_get_current_pid=PROCEDURE) +SYMBOL_VECTOR=(p_process_is_running=PROCEDURE) +SYMBOL_VECTOR=(P_PROCESS_IS_RUNNING/p_process_is_running=PROCEDURE) +! +! prwlock.h +! +SYMBOL_VECTOR=(p_rwlock_new=PROCEDURE) +SYMBOL_VECTOR=(P_RWLOCK_NEW/p_rwlock_new=PROCEDURE) +SYMBOL_VECTOR=(p_rwlock_reader_lock=PROCEDURE) +SYMBOL_VECTOR=(P_RWLOCK_READER_LOCK/p_rwlock_reader_lock=PROCEDURE) +SYMBOL_VECTOR=(p_rwlock_reader_trylock=PROCEDURE) +SYMBOL_VECTOR=(P_RWLOCK_READER_TRYLOCK/p_rwlock_reader_trylock=PROCEDURE) +SYMBOL_VECTOR=(p_rwlock_reader_unlock=PROCEDURE) +SYMBOL_VECTOR=(P_RWLOCK_READER_UNLOCK/p_rwlock_reader_unlock=PROCEDURE) +SYMBOL_VECTOR=(p_rwlock_writer_lock=PROCEDURE) +SYMBOL_VECTOR=(P_RWLOCK_WRITER_LOCK/p_rwlock_writer_lock=PROCEDURE) +SYMBOL_VECTOR=(p_rwlock_writer_trylock=PROCEDURE) +SYMBOL_VECTOR=(P_RWLOCK_WRITER_TRYLOCK/p_rwlock_writer_trylock=PROCEDURE) +SYMBOL_VECTOR=(p_rwlock_writer_unlock=PROCEDURE) +SYMBOL_VECTOR=(P_RWLOCK_WRITER_UNLOCK/p_rwlock_writer_unlock=PROCEDURE) +SYMBOL_VECTOR=(p_rwlock_free=PROCEDURE) +SYMBOL_VECTOR=(P_RWLOCK_FREE/p_rwlock_free=PROCEDURE) +! +! psemaphore.h +! +SYMBOL_VECTOR=(p_semaphore_new=PROCEDURE) +SYMBOL_VECTOR=(P_SEMAPHORE_NEW/p_semaphore_new=PROCEDURE) +SYMBOL_VECTOR=(p_semaphore_take_ownership=PROCEDURE) +SYMBOL_VECTOR=(P_SEMAPHORE_TAKE_OWNERSHIP/p_semaphore_take_ownership=PROCEDURE) +SYMBOL_VECTOR=(p_semaphore_acquire=PROCEDURE) +SYMBOL_VECTOR=(P_SEMAPHORE_ACQUIRE/p_semaphore_acquire=PROCEDURE) +SYMBOL_VECTOR=(p_semaphore_release=PROCEDURE) +SYMBOL_VECTOR=(P_SEMAPHORE_RELEASE/p_semaphore_release=PROCEDURE) +SYMBOL_VECTOR=(p_semaphore_free=PROCEDURE) +SYMBOL_VECTOR=(P_SEMAPHORE_FREE/p_semaphore_free=PROCEDURE) +! +! pshm.h +! +SYMBOL_VECTOR=(p_shm_new=PROCEDURE) +SYMBOL_VECTOR=(P_SHM_NEW/p_shm_new=PROCEDURE) +SYMBOL_VECTOR=(p_shm_take_ownership=PROCEDURE) +SYMBOL_VECTOR=(P_SHM_TAKE_OWNERSHIP/p_shm_take_ownership=PROCEDURE) +SYMBOL_VECTOR=(p_shm_free=PROCEDURE) +SYMBOL_VECTOR=(P_SHM_FREE/p_shm_free=PROCEDURE) +SYMBOL_VECTOR=(p_shm_lock=PROCEDURE) +SYMBOL_VECTOR=(P_SHM_LOCK/p_shm_lock=PROCEDURE) +SYMBOL_VECTOR=(p_shm_unlock=PROCEDURE) +SYMBOL_VECTOR=(P_SHM_UNLOCK/p_shm_unlock=PROCEDURE) +SYMBOL_VECTOR=(p_shm_get_address=PROCEDURE) +SYMBOL_VECTOR=(P_SHM_GET_ADDRESS/p_shm_get_address=PROCEDURE) +SYMBOL_VECTOR=(p_shm_get_size=PROCEDURE) +SYMBOL_VECTOR=(P_SHM_GET_SIZE/p_shm_get_size=PROCEDURE) +! +! pshmbuffer.h +! +SYMBOL_VECTOR=(p_shm_buffer_new=PROCEDURE) +SYMBOL_VECTOR=(P_SHM_BUFFER_NEW/p_shm_buffer_new=PROCEDURE) +SYMBOL_VECTOR=(p_shm_buffer_free=PROCEDURE) +SYMBOL_VECTOR=(P_SHM_BUFFER_FREE/p_shm_buffer_free=PROCEDURE) +SYMBOL_VECTOR=(p_shm_buffer_take_ownership=PROCEDURE) +SYMBOL_VECTOR=(P_SHM_BUFFER_TAKE_OWNERSHIP/p_shm_buffer_take_ownership=PROCEDURE) +SYMBOL_VECTOR=(p_shm_buffer_read=PROCEDURE) +SYMBOL_VECTOR=(P_SHM_BUFFER_READ/p_shm_buffer_read=PROCEDURE) +SYMBOL_VECTOR=(p_shm_buffer_write=PROCEDURE) +SYMBOL_VECTOR=(P_SHM_BUFFER_WRITE/p_shm_buffer_write=PROCEDURE) +SYMBOL_VECTOR=(p_shm_buffer_get_free_space=PROCEDURE) +SYMBOL_VECTOR=(P_SHM_BUFFER_GET_FREE_SPACE/p_shm_buffer_get_free_space=PROCEDURE) +SYMBOL_VECTOR=(p_shm_buffer_get_used_space=PROCEDURE) +SYMBOL_VECTOR=(P_SHM_BUFFER_GET_USED_SPACE/p_shm_buffer_get_used_space=PROCEDURE) +SYMBOL_VECTOR=(p_shm_buffer_clear=PROCEDURE) +SYMBOL_VECTOR=(P_SHM_BUFFER_CLEAR/p_shm_buffer_clear=PROCEDURE) +! +! psocket.h +! +SYMBOL_VECTOR=(p_socket_new_from_fd=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_NEW_FROM_FD/p_socket_new_from_fd=PROCEDURE) +SYMBOL_VECTOR=(p_socket_new=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_NEW/p_socket_new=PROCEDURE) +SYMBOL_VECTOR=(p_socket_get_fd=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_GET_FD/p_socket_get_fd=PROCEDURE) +SYMBOL_VECTOR=(p_socket_get_family=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_GET_FAMILY/p_socket_get_family=PROCEDURE) +SYMBOL_VECTOR=(p_socket_get_type=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_GET_TYPE/p_socket_get_type=PROCEDURE) +SYMBOL_VECTOR=(p_socket_get_protocol=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_GET_PROTOCOL/p_socket_get_protocol=PROCEDURE) +SYMBOL_VECTOR=(p_socket_get_keepalive=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_GET_KEEPALIVE/p_socket_get_keepalive=PROCEDURE) +SYMBOL_VECTOR=(p_socket_get_blocking=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_GET_BLOCKING/p_socket_get_blocking=PROCEDURE) +SYMBOL_VECTOR=(p_socket_get_listen_backlog=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_GET_LISTEN_BACKLOG/p_socket_get_listen_backlog=PROCEDURE) +SYMBOL_VECTOR=(p_socket_get_timeout=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_GET_TIMEOUT/p_socket_get_timeout=PROCEDURE) +SYMBOL_VECTOR=(p_socket_get_local_address=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_GET_LOCAL_ADDRESS/p_socket_get_local_address=PROCEDURE) +SYMBOL_VECTOR=(p_socket_get_remote_address=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_GET_REMOTE_ADDRESS/p_socket_get_remote_address=PROCEDURE) +SYMBOL_VECTOR=(p_socket_is_connected=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_IS_CONNECTED/p_socket_is_connected=PROCEDURE) +SYMBOL_VECTOR=(p_socket_is_closed=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_IS_CLOSED/p_socket_is_closed=PROCEDURE) +SYMBOL_VECTOR=(p_socket_check_connect_result=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_CHECK_CONNECT_RESULT/p_socket_check_connect_result=PROCEDURE) +SYMBOL_VECTOR=(p_socket_set_keepalive=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_SET_KEEPALIVE/p_socket_set_keepalive=PROCEDURE) +SYMBOL_VECTOR=(p_socket_set_blocking=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_SET_BLOCKING/p_socket_set_blocking=PROCEDURE) +SYMBOL_VECTOR=(p_socket_set_listen_backlog=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_SET_LISTEN_BACKLOG/p_socket_set_listen_backlog=PROCEDURE) +SYMBOL_VECTOR=(p_socket_set_timeout=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_SET_TIMEOUT/p_socket_set_timeout=PROCEDURE) +SYMBOL_VECTOR=(p_socket_bind=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_BIND/p_socket_bind=PROCEDURE) +SYMBOL_VECTOR=(p_socket_connect=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_CONNECT/p_socket_connect=PROCEDURE) +SYMBOL_VECTOR=(p_socket_listen=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_LISTEN/p_socket_listen=PROCEDURE) +SYMBOL_VECTOR=(p_socket_accept=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_ACCEPT/p_socket_accept=PROCEDURE) +SYMBOL_VECTOR=(p_socket_receive=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_RECEIVE/p_socket_receive=PROCEDURE) +SYMBOL_VECTOR=(p_socket_receive_from=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_RECEIVE_FROM/p_socket_receive_from=PROCEDURE) +SYMBOL_VECTOR=(p_socket_send=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_SEND/p_socket_send=PROCEDURE) +SYMBOL_VECTOR=(p_socket_send_to=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_SEND_TO/p_socket_send_to=PROCEDURE) +SYMBOL_VECTOR=(p_socket_close=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_CLOSE/p_socket_close=PROCEDURE) +SYMBOL_VECTOR=(p_socket_shutdown=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_SHUTDOWN/p_socket_shutdown=PROCEDURE) +SYMBOL_VECTOR=(p_socket_free=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_FREE/p_socket_free=PROCEDURE) +SYMBOL_VECTOR=(p_socket_set_buffer_size=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_SET_BUFFER_SIZE/p_socket_set_buffer_size=PROCEDURE) +SYMBOL_VECTOR=(p_socket_io_condition_wait=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_IO_CONDITION_WAIT/p_socket_io_condition_wait=PROCEDURE) +! +! psocketaddress.h +! +SYMBOL_VECTOR=(p_socket_address_new_fr1g0bamc$=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_ADDRESS_NEW_FR0V6PAD7$/p_socket_address_new_fr1g0bamc$=PROCEDURE) +SYMBOL_VECTOR=(p_socket_address_new=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_ADDRESS_NEW/p_socket_address_new=PROCEDURE) +SYMBOL_VECTOR=(p_socket_address_new_any=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_ADDRESS_NEW_ANY/p_socket_address_new_any=PROCEDURE) +SYMBOL_VECTOR=(p_socket_address_new_loopback=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_ADDRESS_NEW_LOOPBACK/p_socket_address_new_loopback=PROCEDURE) +SYMBOL_VECTOR=(p_socket_address_to_native=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_ADDRESS_TO_NATIVE/p_socket_address_to_native=PROCEDURE) +SYMBOL_VECTOR=(p_socket_address_get_na1kai9ab$=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_ADDRESS_GET_NA0SBOC5O$/p_socket_address_get_na1kai9ab$=PROCEDURE) +SYMBOL_VECTOR=(p_socket_address_get_family=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_ADDRESS_GET_FAMILY/p_socket_address_get_family=PROCEDURE) +SYMBOL_VECTOR=(p_socket_address_get_address=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_ADDRESS_GET_ADDRESS/p_socket_address_get_address=PROCEDURE) +SYMBOL_VECTOR=(p_socket_address_get_port=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_ADDRESS_GET_PORT/p_socket_address_get_port=PROCEDURE) +SYMBOL_VECTOR=(p_socket_address_get_flow_info=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_ADDRESS_GET_FLOW_INFO/p_socket_address_get_flow_info=PROCEDURE) +SYMBOL_VECTOR=(p_socket_address_get_scope_id=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_ADDRESS_GET_SCOPE_ID/p_socket_address_get_scope_id=PROCEDURE) +SYMBOL_VECTOR=(p_socket_address_set_flow_info=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_ADDRESS_SET_FLOW_INFO/p_socket_address_set_flow_info=PROCEDURE) +SYMBOL_VECTOR=(p_socket_address_set_scope_id=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_ADDRESS_SET_SCOPE_ID/p_socket_address_set_scope_id=PROCEDURE) +SYMBOL_VECTOR=(p_socket_address_is_flo3bgg0hi$=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_ADDRESS_IS_FLO1N1P65T$/p_socket_address_is_flo3bgg0hi$=PROCEDURE) +SYMBOL_VECTOR=(p_socket_address_is_sco2c7455c$=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_ADDRESS_IS_SCO1COK854$/p_socket_address_is_sco2c7455c$=PROCEDURE) +SYMBOL_VECTOR=(p_socket_address_is_ipv3s9t3vi$=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_ADDRESS_IS_IPV13UAVM5$/p_socket_address_is_ipv3s9t3vi$=PROCEDURE) +SYMBOL_VECTOR=(p_socket_address_is_any=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_ADDRESS_IS_ANY/p_socket_address_is_any=PROCEDURE) +SYMBOL_VECTOR=(p_socket_address_is_loopback=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_ADDRESS_IS_LOOPBACK/p_socket_address_is_loopback=PROCEDURE) +SYMBOL_VECTOR=(p_socket_address_free=PROCEDURE) +SYMBOL_VECTOR=(P_SOCKET_ADDRESS_FREE/p_socket_address_free=PROCEDURE) +! +! pspinlock.h +! +SYMBOL_VECTOR=(p_spinlock_new=PROCEDURE) +SYMBOL_VECTOR=(P_SPINLOCK_NEW/p_spinlock_new=PROCEDURE) +SYMBOL_VECTOR=(p_spinlock_lock=PROCEDURE) +SYMBOL_VECTOR=(P_SPINLOCK_LOCK/p_spinlock_lock=PROCEDURE) +SYMBOL_VECTOR=(p_spinlock_trylock=PROCEDURE) +SYMBOL_VECTOR=(P_SPINLOCK_TRYLOCK/p_spinlock_trylock=PROCEDURE) +SYMBOL_VECTOR=(p_spinlock_unlock=PROCEDURE) +SYMBOL_VECTOR=(P_SPINLOCK_UNLOCK/p_spinlock_unlock=PROCEDURE) +SYMBOL_VECTOR=(p_spinlock_free=PROCEDURE) +SYMBOL_VECTOR=(P_SPINLOCK_FREE/p_spinlock_free=PROCEDURE) +! +! pstring.h +! +SYMBOL_VECTOR=(p_strdup=PROCEDURE) +SYMBOL_VECTOR=(P_STRDUP/p_strdup=PROCEDURE) +SYMBOL_VECTOR=(p_strchomp=PROCEDURE) +SYMBOL_VECTOR=(P_STRCHOMP/p_strchomp=PROCEDURE) +SYMBOL_VECTOR=(p_strtok=PROCEDURE) +SYMBOL_VECTOR=(P_STRTOK/p_strtok=PROCEDURE) +SYMBOL_VECTOR=(p_strtod=PROCEDURE) +SYMBOL_VECTOR=(P_STRTOD/p_strtod=PROCEDURE) +! +! ptimeprofiler.h +! +SYMBOL_VECTOR=(p_time_profiler_new=PROCEDURE) +SYMBOL_VECTOR=(P_TIME_PROFILER_NEW/p_time_profiler_new=PROCEDURE) +SYMBOL_VECTOR=(p_time_profiler_reset=PROCEDURE) +SYMBOL_VECTOR=(P_TIME_PROFILER_RESET/p_time_profiler_reset=PROCEDURE) +SYMBOL_VECTOR=(p_time_profiler_elapsed_usecs=PROCEDURE) +SYMBOL_VECTOR=(P_TIME_PROFILER_ELAPSED_USECS/p_time_profiler_elapsed_usecs=PROCEDURE) +SYMBOL_VECTOR=(p_time_profiler_free=PROCEDURE) +SYMBOL_VECTOR=(P_TIME_PROFILER_FREE/p_time_profiler_free=PROCEDURE) +! +! ptree.h +! +SYMBOL_VECTOR=(p_tree_new=PROCEDURE) +SYMBOL_VECTOR=(P_TREE_NEW/p_tree_new=PROCEDURE) +SYMBOL_VECTOR=(p_tree_new_with_data=PROCEDURE) +SYMBOL_VECTOR=(P_TREE_NEW_WITH_DATA/p_tree_new_with_data=PROCEDURE) +SYMBOL_VECTOR=(p_tree_new_full=PROCEDURE) +SYMBOL_VECTOR=(P_TREE_NEW_FULL/p_tree_new_full=PROCEDURE) +SYMBOL_VECTOR=(p_tree_insert=PROCEDURE) +SYMBOL_VECTOR=(P_TREE_INSERT/p_tree_insert=PROCEDURE) +SYMBOL_VECTOR=(p_tree_remove=PROCEDURE) +SYMBOL_VECTOR=(P_TREE_REMOVE/p_tree_remove=PROCEDURE) +SYMBOL_VECTOR=(p_tree_lookup=PROCEDURE) +SYMBOL_VECTOR=(P_TREE_LOOKUP/p_tree_lookup=PROCEDURE) +SYMBOL_VECTOR=(p_tree_foreach=PROCEDURE) +SYMBOL_VECTOR=(P_TREE_FOREACH/p_tree_foreach=PROCEDURE) +SYMBOL_VECTOR=(p_tree_clear=PROCEDURE) +SYMBOL_VECTOR=(P_TREE_CLEAR/p_tree_clear=PROCEDURE) +SYMBOL_VECTOR=(p_tree_get_type=PROCEDURE) +SYMBOL_VECTOR=(P_TREE_GET_TYPE/p_tree_get_type=PROCEDURE) +SYMBOL_VECTOR=(p_tree_get_nnodes=PROCEDURE) +SYMBOL_VECTOR=(P_TREE_GET_NNODES/p_tree_get_nnodes=PROCEDURE) +SYMBOL_VECTOR=(p_tree_free=PROCEDURE) +SYMBOL_VECTOR=(P_TREE_FREE/p_tree_free=PROCEDURE) +! +! puthread.h +! +SYMBOL_VECTOR=(p_uthread_create_full=PROCEDURE) +SYMBOL_VECTOR=(P_UTHREAD_CREATE_FULL/p_uthread_create_full=PROCEDURE) +SYMBOL_VECTOR=(p_uthread_create=PROCEDURE) +SYMBOL_VECTOR=(P_UTHREAD_CREATE/p_uthread_create=PROCEDURE) +SYMBOL_VECTOR=(p_uthread_exit=PROCEDURE) +SYMBOL_VECTOR=(P_UTHREAD_EXIT/p_uthread_exit=PROCEDURE) +SYMBOL_VECTOR=(p_uthread_join=PROCEDURE) +SYMBOL_VECTOR=(P_UTHREAD_JOIN/p_uthread_join=PROCEDURE) +SYMBOL_VECTOR=(p_uthread_sleep=PROCEDURE) +SYMBOL_VECTOR=(P_UTHREAD_SLEEP/p_uthread_sleep=PROCEDURE) +SYMBOL_VECTOR=(p_uthread_set_priority=PROCEDURE) +SYMBOL_VECTOR=(P_UTHREAD_SET_PRIORITY/p_uthread_set_priority=PROCEDURE) +SYMBOL_VECTOR=(p_uthread_yield=PROCEDURE) +SYMBOL_VECTOR=(P_UTHREAD_YIELD/p_uthread_yield=PROCEDURE) +SYMBOL_VECTOR=(p_uthread_current_id=PROCEDURE) +SYMBOL_VECTOR=(P_UTHREAD_CURRENT_ID/p_uthread_current_id=PROCEDURE) +SYMBOL_VECTOR=(p_uthread_current=PROCEDURE) +SYMBOL_VECTOR=(P_UTHREAD_CURRENT/p_uthread_current=PROCEDURE) +SYMBOL_VECTOR=(p_uthread_ideal_count=PROCEDURE) +SYMBOL_VECTOR=(P_UTHREAD_IDEAL_COUNT/p_uthread_ideal_count=PROCEDURE) +SYMBOL_VECTOR=(p_uthread_ref=PROCEDURE) +SYMBOL_VECTOR=(P_UTHREAD_REF/p_uthread_ref=PROCEDURE) +SYMBOL_VECTOR=(p_uthread_unref=PROCEDURE) +SYMBOL_VECTOR=(P_UTHREAD_UNREF/p_uthread_unref=PROCEDURE) +SYMBOL_VECTOR=(p_uthread_local_new=PROCEDURE) +SYMBOL_VECTOR=(P_UTHREAD_LOCAL_NEW/p_uthread_local_new=PROCEDURE) +SYMBOL_VECTOR=(p_uthread_local_free=PROCEDURE) +SYMBOL_VECTOR=(P_UTHREAD_LOCAL_FREE/p_uthread_local_free=PROCEDURE) +SYMBOL_VECTOR=(p_uthread_get_local=PROCEDURE) +SYMBOL_VECTOR=(P_UTHREAD_GET_LOCAL/p_uthread_get_local=PROCEDURE) +SYMBOL_VECTOR=(p_uthread_set_local=PROCEDURE) +SYMBOL_VECTOR=(P_UTHREAD_SET_LOCAL/p_uthread_set_local=PROCEDURE) +SYMBOL_VECTOR=(p_uthread_replace_local=PROCEDURE) +SYMBOL_VECTOR=(P_UTHREAD_REPLACE_LOCAL/p_uthread_replace_local=PROCEDURE) diff --git a/3rdparty/plibsys/platforms/vms-general/vms_shorten_symbol.c b/3rdparty/plibsys/platforms/vms-general/vms_shorten_symbol.c new file mode 100644 index 0000000..fbb6c5a --- /dev/null +++ b/3rdparty/plibsys/platforms/vms-general/vms_shorten_symbol.c @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2010 Craig A. Berry + * Copyright (c) 2016 Alexander Saprykin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* vms_shorten_symbol + * + * This program provides shortening of long symbols (> 31 characters) using the + * same mechanism as the OpenVMS C compiler. The basic procedure is to compute + * an AUTODIN II checksum of the entire symbol, encode the checksum in base32, + * and glue together a shortened symbol from the first 23 characters of the + * original symbol plus the encoded checksum appended. The output format is + * the same as that of the name mangler database, stored by default in + * [.CXX_REPOSITORY]CXX$DEMANGLER_DB. + * + * To obtain the same result as CC/NAMES=SHORTENED, run like so: + * + * $ mcr []vms_shorten_symbol "Please_forgive_this_absurdly_long_symbol_name" + * PLEASE_FORGIVE_THIS_ABS1ARO4QU$Please_forgive_this_absurdly_long_symbol_name + * + * To obtain the same result as CC/NAMES=(SHORTENED,AS_IS), pass a non-zero + * value as the second argument, like so: + * + * $ mcr []vms_shorten_symbol "Please_forgive_this_absurdly_long_symbol_name" 1 + * Please_forgive_this_abs3rv8rnn$Please_forgive_this_absurdly_long_symbol_name + */ + +#include +#include +#include +#include + +#ifdef __VMS +# define UINT32 unsigned int +#else +# include +# define UINT32 uint32_t +#endif + +static UINT32 crc32 (const char *input_string); +static UINT32 u32_to_base32 (UINT32 input, char *output); +static UINT32 vms_shorten_symbol (const char *symbol, char *shortened, char as_is_flag); + +/* + * This routine implements the AUTODIN II polynomial. + */ +UINT32 +crc32 (const char *input_string) +{ + + /* + * CRC code and data based partly on FreeBSD implementation, which + * notes: + * + * The crc32 functions and data was originally written by Spencer + * Garrett >s...@quick.com> and was cleaned from the PostgreSQL source + * tree via the files contrib/ltree/crc32.[ch]. No license was + * included, therefore it is assumed that this code is public + * domain. Attribution still noted. + * + * (I think they mean "gleaned" not "cleaned".) + */ + static const UINT32 autodin_ii_table[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, + }; + + UINT32 crc = ~0UL; + char * c; + + for (c = (char *) input_string; *c; ++c) + crc = (crc >> 8) ^ autodin_ii_table[(crc ^ *c) & 0xff]; + + return ~crc; +} + +/* + * This is the RFC2938 variant of base32, not RFC3548, Crockford's, or + * other newer variant. It produces an 8-byte encoded character string + * (plus trailing null) from a 32-bit integer input. + */ +static UINT32 +u32_to_base32 (UINT32 input, char *output) +{ + static const char base32hex_table[32] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v' + }; + + int i; + + /* + * Grab lowest 5 bits and look up conversion in table. Lather, rinse, + * repeat for 6, 5-bit chunks to accommodate 32 bits of input. + */ + for (i = 0; i < 7; i++) { + output[6 - i] = base32hex_table[input & 0x1f]; + + /* Position to look at next 5. */ + input >>= 5; + } + + /* It's DEC, so use '$' not '=' to pad. */ + output[7] = '$'; + output[8] = '\0'; + + return 0; +} + +static UINT32 +vms_shorten_symbol (const char *symbol, char *shortened, char as_is_flag) +{ + char b32str[9]; + UINT32 crc; + char * c; + + crc = crc32 (symbol); + + /* + * The compiler does not use the inverted checksum, so we invert it + * back before encoding. + */ + (void) u32_to_base32 (~crc, (char *) &b32str); + + if (!as_is_flag) { + for (c = (char *) &b32str; *c; c++) + *c = toupper (*c); + } + + sprintf (shortened, "%.23s%.8s\n", symbol, b32str); + shortened[31] = '\0'; + + return 0; +} + +int +main (int argc, char **argv) +{ + char short_symbol[32]; + char as_is_flag = 0; + char * c; + char * input_symbol; + int symlen; + + if (argc < 2) { + fprintf (stderr, "Usage: %s []\n", argv[0]); + return EXIT_FAILURE; + } + + if (argc > 2) + as_is_flag = 1; + + symlen = strlen (argv[1]); + + if (symlen <= 31) { + printf ("No need for shorten symbol\n"); + return EXIT_SUCCESS; + } + + input_symbol = malloc (symlen + 1); + + if (input_symbol == NULL) + return EXIT_FAILURE; + + strncpy (input_symbol, argv[1], symlen); + input_symbol[symlen] = '\0'; + + if (!as_is_flag) { + for (c = input_symbol; *c; c++) + *c = toupper (*c); + } + + vms_shorten_symbol (input_symbol, (char *) &short_symbol, as_is_flag); + printf ("%s%s\n", &short_symbol, argv[1]); + free (input_symbol); + + return EXIT_SUCCESS; +} diff --git a/3rdparty/plibsys/platforms/win32-borland/platform.cmake b/3rdparty/plibsys/platforms/win32-borland/platform.cmake new file mode 100644 index 0000000..564ae9a --- /dev/null +++ b/3rdparty/plibsys/platforms/win32-borland/platform.cmake @@ -0,0 +1,8 @@ +set (PLIBSYS_THREAD_MODEL win) +set (PLIBSYS_IPC_MODEL win) +set (PLIBSYS_TIME_PROFILER_MODEL win) +set (PLIBSYS_DIR_MODEL win) +set (PLIBSYS_LIBRARYLOADER_MODEL win) + +set (PLIBSYS_PLATFORM_CFLAGS "-w-8065 -w-8008 -w-8057 -w-8059 -w-8066 -w-8004") +set (PLIBSYS_PLATFORM_LINK_LIBRARIES ws2_32) diff --git a/3rdparty/plibsys/platforms/win32-clang/platform.cmake b/3rdparty/plibsys/platforms/win32-clang/platform.cmake new file mode 100644 index 0000000..bd115ff --- /dev/null +++ b/3rdparty/plibsys/platforms/win32-clang/platform.cmake @@ -0,0 +1,8 @@ +set (PLIBSYS_THREAD_MODEL win) +set (PLIBSYS_IPC_MODEL win) +set (PLIBSYS_TIME_PROFILER_MODEL win) +set (PLIBSYS_DIR_MODEL win) +set (PLIBSYS_LIBRARYLOADER_MODEL win) + +set (PLIBSYS_PLATFORM_CFLAGS "-Wno-missing-braces -Wmissing-field-initializers") +set (PLIBSYS_PLATFORM_LINK_LIBRARIES ws2_32) diff --git a/3rdparty/plibsys/platforms/win32-gcc/platform.cmake b/3rdparty/plibsys/platforms/win32-gcc/platform.cmake new file mode 100644 index 0000000..05ac5d8 --- /dev/null +++ b/3rdparty/plibsys/platforms/win32-gcc/platform.cmake @@ -0,0 +1,7 @@ +set (PLIBSYS_THREAD_MODEL win) +set (PLIBSYS_IPC_MODEL win) +set (PLIBSYS_TIME_PROFILER_MODEL win) +set (PLIBSYS_DIR_MODEL win) +set (PLIBSYS_LIBRARYLOADER_MODEL win) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES ws2_32) diff --git a/3rdparty/plibsys/platforms/win32-icc/platform.cmake b/3rdparty/plibsys/platforms/win32-icc/platform.cmake new file mode 100644 index 0000000..05ac5d8 --- /dev/null +++ b/3rdparty/plibsys/platforms/win32-icc/platform.cmake @@ -0,0 +1,7 @@ +set (PLIBSYS_THREAD_MODEL win) +set (PLIBSYS_IPC_MODEL win) +set (PLIBSYS_TIME_PROFILER_MODEL win) +set (PLIBSYS_DIR_MODEL win) +set (PLIBSYS_LIBRARYLOADER_MODEL win) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES ws2_32) diff --git a/3rdparty/plibsys/platforms/win32-msvc/platform.cmake b/3rdparty/plibsys/platforms/win32-msvc/platform.cmake new file mode 100644 index 0000000..05ac5d8 --- /dev/null +++ b/3rdparty/plibsys/platforms/win32-msvc/platform.cmake @@ -0,0 +1,7 @@ +set (PLIBSYS_THREAD_MODEL win) +set (PLIBSYS_IPC_MODEL win) +set (PLIBSYS_TIME_PROFILER_MODEL win) +set (PLIBSYS_DIR_MODEL win) +set (PLIBSYS_LIBRARYLOADER_MODEL win) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES ws2_32) diff --git a/3rdparty/plibsys/platforms/win32-watcom/platform.cmake b/3rdparty/plibsys/platforms/win32-watcom/platform.cmake new file mode 100644 index 0000000..ef5a855 --- /dev/null +++ b/3rdparty/plibsys/platforms/win32-watcom/platform.cmake @@ -0,0 +1,11 @@ +set (PLIBSYS_THREAD_MODEL win) +set (PLIBSYS_IPC_MODEL win) +set (PLIBSYS_TIME_PROFILER_MODEL win) +set (PLIBSYS_DIR_MODEL win) +set (PLIBSYS_LIBRARYLOADER_MODEL win) + +if (NOT PLIBSYS_SIZEOF_VOID_P) + set (PLIBSYS_SIZEOF_VOID_P 4) +endif() + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES ws2_32) diff --git a/3rdparty/plibsys/platforms/win64-clang/platform.cmake b/3rdparty/plibsys/platforms/win64-clang/platform.cmake new file mode 100644 index 0000000..bd115ff --- /dev/null +++ b/3rdparty/plibsys/platforms/win64-clang/platform.cmake @@ -0,0 +1,8 @@ +set (PLIBSYS_THREAD_MODEL win) +set (PLIBSYS_IPC_MODEL win) +set (PLIBSYS_TIME_PROFILER_MODEL win) +set (PLIBSYS_DIR_MODEL win) +set (PLIBSYS_LIBRARYLOADER_MODEL win) + +set (PLIBSYS_PLATFORM_CFLAGS "-Wno-missing-braces -Wmissing-field-initializers") +set (PLIBSYS_PLATFORM_LINK_LIBRARIES ws2_32) diff --git a/3rdparty/plibsys/platforms/win64-gcc/platform.cmake b/3rdparty/plibsys/platforms/win64-gcc/platform.cmake new file mode 100644 index 0000000..05ac5d8 --- /dev/null +++ b/3rdparty/plibsys/platforms/win64-gcc/platform.cmake @@ -0,0 +1,7 @@ +set (PLIBSYS_THREAD_MODEL win) +set (PLIBSYS_IPC_MODEL win) +set (PLIBSYS_TIME_PROFILER_MODEL win) +set (PLIBSYS_DIR_MODEL win) +set (PLIBSYS_LIBRARYLOADER_MODEL win) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES ws2_32) diff --git a/3rdparty/plibsys/platforms/win64-icc/platform.cmake b/3rdparty/plibsys/platforms/win64-icc/platform.cmake new file mode 100644 index 0000000..05ac5d8 --- /dev/null +++ b/3rdparty/plibsys/platforms/win64-icc/platform.cmake @@ -0,0 +1,7 @@ +set (PLIBSYS_THREAD_MODEL win) +set (PLIBSYS_IPC_MODEL win) +set (PLIBSYS_TIME_PROFILER_MODEL win) +set (PLIBSYS_DIR_MODEL win) +set (PLIBSYS_LIBRARYLOADER_MODEL win) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES ws2_32) diff --git a/3rdparty/plibsys/platforms/win64-msvc/platform.cmake b/3rdparty/plibsys/platforms/win64-msvc/platform.cmake new file mode 100644 index 0000000..05ac5d8 --- /dev/null +++ b/3rdparty/plibsys/platforms/win64-msvc/platform.cmake @@ -0,0 +1,7 @@ +set (PLIBSYS_THREAD_MODEL win) +set (PLIBSYS_IPC_MODEL win) +set (PLIBSYS_TIME_PROFILER_MODEL win) +set (PLIBSYS_DIR_MODEL win) +set (PLIBSYS_LIBRARYLOADER_MODEL win) + +set (PLIBSYS_PLATFORM_LINK_LIBRARIES ws2_32) diff --git a/3rdparty/plibsys/scripts/run_tests.sh b/3rdparty/plibsys/scripts/run_tests.sh new file mode 100755 index 0000000..e6faada --- /dev/null +++ b/3rdparty/plibsys/scripts/run_tests.sh @@ -0,0 +1,70 @@ +#!/bin/sh + +# +# The MIT License +# +# Copyright 2017-2018, Alexander Saprykin +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# 'Software'), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 [[ $# -ne 2 ]]; then + echo "Usage: run_tests.sh " + echo "Where: is a directory containing tests to run" + echo " is a path to the shared library to be tested" + exit 1 +fi + +total_counter=0 +pass_counter=0 + +IFS=$'\n' +files=$(ls $1) + +export LD_LIBRARY_PATH=$1:$LD_LIBRARY_PATH + +selfname=$(basename $0) + +echo "Running tests..." + +for file in $files +do + if [[ $file == *"_test"* && $file != $selfname ]]; then + test_name=${file%.*} + total_counter=$((total_counter + 1)) + echo "[RUN ] $test_name" + + if [[ $test_name == "plibraryloader_test" ]]; then + $($1/${file} $2 > /dev/null 2>&1) + else + $($1/${file} > /dev/null 2>&1) + fi + + if [[ $? -ne 0 ]]; then + echo "[FAIL] *** Test failed: $test_name" + else + echo "[PASS] Test passed: $test_name" + pass_counter=$((pass_counter + 1)) + fi + fi +done + +echo "Tests passed: $pass_counter/$total_counter" 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 +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# 'Software'), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + #include + 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 + #include + 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 + 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 + 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 + #include + 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 + #include + #include + + 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 + + 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 + + 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 + #include + + 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 + #include + #include ") +else() + set (PLIBSYS_SOCKET_INCLUDES "#include + #include + #include ") +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 + 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 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#else +# include +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +# 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 instead." +#endif + +#ifndef PLIBSYS_HEADER_PATOMIC_H +#define PLIBSYS_HEADER_PATOMIC_H + +#include +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include + +#include +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include + +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + +#define INCL_DOSSEMAPHORES +#define INCL_DOSERRORS +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include +#include +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include +#include +#include +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 instead." +#endif + +#ifndef PLIBSYS_HEADER_PCONDVARIABLE_H +#define PLIBSYS_HEADER_PCONDVARIABLE_H + +#include +#include +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include + +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include + +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include + +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include + +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 instead." +#endif + +#ifndef PLIBSYS_HEADER_PCRYPTOHASH_H +#define PLIBSYS_HEADER_PCRYPTOHASH_H + +#include +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + +#include +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include +#include +#include +#include +#include +#include + +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 instead." +#endif + +#ifndef PLIBSYS_HEADER_PDIR_H +#define PLIBSYS_HEADER_PDIR_H + +#include +#include +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +# include +# endif +# include +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 instead." +#endif + +#ifndef PLIBSYS_HEADER_PERROR_H +#define PLIBSYS_HEADER_PERROR_H + +#include +#include +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 instead." +#endif + +#ifndef PLIBSYS_HEADER_PERRORTYPES_H +#define PLIBSYS_HEADER_PERRORTYPES_H + +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 instead." +#endif + +#ifndef PLIBSYS_HEADER_PFILE_H +#define PLIBSYS_HEADER_PFILE_H + +#include +#include +#include + +/** + * @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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 instead." +#endif + +#ifndef PLIBSYS_HEADER_PHASHTABLE_H +#define PLIBSYS_HEADER_PHASHTABLE_H + +#include +#include +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include +#include +#include + +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 instead." +#endif + +#ifndef PLIBSYS_HEADER_PINIFILE_H +#define PLIBSYS_HEADER_PINIFILE_H + +#include +#include +#include +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include + +#if !defined (P_OS_WIN) && !defined (P_OS_OS2) && !defined (P_OS_AMIGA) +# include +# include +# include +# include +# include +# include +#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 + * Copyright (C) 2002-2015 by Olaf Barthel + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + +#include +#include + +#if defined (__CLIB2__) +# include +#elif defined (__NEWLIB__) +# include +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + +/* 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 +# include +# include +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 instead." +#endif + +#ifndef PLIBSYS_HEADER_PLIBRARYLOADER_H +#define PLIBSYS_HEADER_PLIBRARYLOADER_H + +#include +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +# include +# include +#endif + +#include + +#ifdef PLIBSYS_NEED_FLOAT_H +# include +#endif + +#ifdef PLIBSYS_NEED_LIMITS_H +# include +#endif + +#ifdef PLIBSYS_NEED_VALUES_H +# include +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 instead." +#endif + +#ifndef PLIBSYS_HEADER_PLIST_H +#define PLIBSYS_HEADER_PLIST_H + +#include +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 instead." +#endif + +#ifndef PLIBSYS_HEADER_PMACROS_H +#define PLIBSYS_HEADER_PMACROS_H + +#include +#include +#include + +#include + +/* 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 instead." +#endif + +#ifndef PLIBSYS_HEADER_PMAIN_H +#define PLIBSYS_HEADER_PMAIN_H + +#include +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include + +#include "perror.h" +#include "pmem.h" +#include "perror-private.h" +#include "psysclose-private.h" + +#ifndef P_OS_WIN +# if defined (P_OS_BEOS) +# include +# elif defined (P_OS_OS2) +# define INCL_DOSMEMMGR +# define INCL_DOSERRORS +# include +# elif !defined (P_OS_AMIGA) +# include +# include +# include +# include +# include +# 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 instead." +#endif + +#ifndef PLIBSYS_HEADER_PMEM_H +#define PLIBSYS_HEADER_PMEM_H + +#include +#include +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + +#include +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include + +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + +#define INCL_DOSSEMAPHORES +#define INCL_DOSERRORS +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 instead." +#endif + +#ifndef PLIBSYS_HEADER_PMUTEX_H +#define PLIBSYS_HEADER_PMUTEX_H + +#include +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +# include +# include +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 instead." +#endif + +#ifndef PLIBSYS_HEADER_PPROCESS_H +#define PLIBSYS_HEADER_PPROCESS_H + +#include +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 instead." +#endif + +#ifndef PLIBSYS_HEADER_PRWLOCK_H +#define PLIBSYS_HEADER_PRWLOCK_H + +#include +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include + +#include +#include +#include + +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include + +#include +#include +#include +#include + +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include + +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 instead." +#endif + +#ifndef PLIBSYS_HEADER_PSEMAPHORE_H +#define PLIBSYS_HEADER_PSEMAPHORE_H + +#include +#include +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + +#include +#include + +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include + +#define INCL_DOSMEMMGR +#define INCL_DOSSEMAPHORES +#define INCL_DOSERRORS +#include + +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include +#include +#include +#include +#include +#include + +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include +#include +#include +#include +#include +#include +#include + +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include + +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 instead." +#endif + +#ifndef PLIBSYS_HEADER_PSHM_H +#define PLIBSYS_HEADER_PSHM_H + +#include +#include +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include + +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 instead." +#endif + +#ifndef PLIBSYS_HEADER_PSHMBUFFER_H +#define PLIBSYS_HEADER_PSHMBUFFER_H + +#include +#include +#include + +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 + * 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 +#include + +#ifndef P_OS_WIN +# include +# include +# include +# include +# ifdef P_OS_VMS +# include +# 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 +# include +# else +# define P_SOCKET_USE_POLL +# include +# 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 instead." +#endif + +#ifndef PLIBSYS_HEADER_PSOCKET_H +#define PLIBSYS_HEADER_PSOCKET_H + +#include +#include +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include + +#ifndef P_OS_WIN +# include +#endif + +#if defined (PLIBSYS_HAS_GETADDRINFO) && !defined (PLIBSYS_SOCKADDR_IN6_HAS_SCOPEID) +# undef PLIBSYS_HAS_GETADDRINFO +#endif + +#ifdef PLIBSYS_HAS_GETADDRINFO +# include +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 instead." +#endif + +#ifndef PLIBSYS_HEADER_PSOCKETADDRESS_H +#define PLIBSYS_HEADER_PSOCKETADDRESS_H + +#include +#include + +#ifndef P_OS_WIN +# include +# include +# include +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#else +# include +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 instead." +#endif + +#ifndef PLIBSYS_HEADER_PSPINLOCK_H +#define PLIBSYS_HEADER_PSPINLOCK_H + +#include +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 instead." +#endif + +#ifndef PLIBSYS_HEADER_PSTDARG_H +#define PLIBSYS_HEADER_PSTDARG_H + +#include +#include + +#include +#include +#ifndef PLIBSYS_VA_COPY +# include +#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 + * 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 +#include + +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 instead." +#endif + +#ifndef PLIBSYS_HEADER_PSTRING_H +#define PLIBSYS_HEADER_PSTRING_H + +#include +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + * and (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 + +/* 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + +#if PLIBSYS_HAS_LLDIV +# ifdef P_CC_GNU +# define __USE_ISOC99 +# endif +# include +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include +#include + +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + +#if PLIBSYS_HAS_LLDIV +# include +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 instead." +#endif + +#ifndef PLIBSYS_HEADER_PTIMEPROFILER_H +#define PLIBSYS_HEADER_PTIMEPROFILER_H + +#include +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 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 + * 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 instead." +#endif + +#ifndef PLIBSYS_HEADER_PTREE_H +#define PLIBSYS_HEADER_PTREE_H + +#include +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 instead." +#endif + +#ifndef PLIBSYS_HEADER_PTYPES_H +#define PLIBSYS_HEADER_PTYPES_H + +#include +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include +#include + +#include +#include + +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include +#include +#include + +#include +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include +#include + +#include +#include +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include + +#ifdef P_DEBUG +# undef P_DEBUG +#endif + +#include "pmem.h" +#include "patomic.h" +#include "pmutex.h" +#include "puthread.h" +#include "puthread-private.h" + +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include +#include +#include +#include +#include + +/* On some OS like OpenBSD it must follow */ +#ifdef PLIBSYS_NEED_PTHREAD_NP_H +# include +#endif + +#ifdef PLIBSYS_HAS_POSIX_SCHEDULING +# ifndef P_OS_VMS +# include +# endif +#endif + +#ifdef PLIBSYS_HAS_PTHREAD_PRCTL +# include +# include +#endif + +#ifdef P_OS_QNX6 +# include +#endif + +#ifdef P_OS_HAIKU +# include +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include +#include +#include +#include + +#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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#include +#include + +#include + +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 +#endif + +#include +#include +#include + +#ifndef P_OS_WIN +# include +#endif + +#ifdef P_OS_WIN +typedef void (WINAPI * SystemInfoFunc) (LPSYSTEM_INFO); +#endif + +#ifdef P_OS_HPUX +# include +#endif + +#ifdef P_OS_BSD4 +# include +# include +# include +#endif + +#ifdef P_OS_VMS +# define __NEW_STARLET 1 +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +#ifdef P_OS_QNX6 +# include +#endif + +#ifdef P_OS_BEOS +# include +#endif + +#ifdef P_OS_SYLLABLE +# include +#endif + +#if defined (P_OS_SCO) && !defined (_SC_NPROCESSORS_ONLN) +# include +#endif + +#ifdef P_OS_AMIGA +# include +# include +# include +#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 +# if !defined (PLIBSYS_HAS_CLOCKNANOSLEEP) && !defined (PLIBSYS_HAS_NANOSLEEP) +# include +# include +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 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 instead." +#endif + +#ifndef PLIBSYS_HEADER_PUTHREAD_H +#define PLIBSYS_HEADER_PUTHREAD_H + +#include +#include + +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 */ diff --git a/3rdparty/plibsys/tests/CMakeLists.txt b/3rdparty/plibsys/tests/CMakeLists.txt new file mode 100644 index 0000000..54f747b --- /dev/null +++ b/3rdparty/plibsys/tests/CMakeLists.txt @@ -0,0 +1,96 @@ +# The MIT License +# +# Copyright (C) 2018 Alexander Saprykin +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# 'Software'), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +project (tests CXX) +set (OUTPUT_DIR ${CMAKE_BINARY_DIR}) + +include (${PROJECT_SOURCE_DIR}/../cmake/PlatformDetect.cmake) +plibsys_detect_target_os (PLIBSYS_TESTS_TARGET_OS) + +list (APPEND PLIBSYS_TEST_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/../src ${CMAKE_BINARY_DIR}) + +if (MSVC) + list (APPEND PLIBSYS_TEST_COMPILE_DEFS -D_CRT_SECURE_NO_WARNINGS) +endif() + +macro (plibsys_add_test_executable TEST_NAME SRC_FILE) + add_executable (${TEST_NAME} ${SRC_FILE}) + target_link_libraries (${TEST_NAME} plibsys) + set_target_properties (${TEST_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${OUTPUT_DIR}) + + # QNX requires libm for sqrt() and friends + if (PLIBSYS_TESTS_TARGET_OS STREQUAL qnx) + target_link_libraries (${TEST_NAME} m) + endif() + + # Add include directories + if (COMMAND target_include_directories) + target_include_directories (${TEST_NAME} PUBLIC ${PLIBSYS_TEST_INCLUDE_DIRS}) + else() + include_directories (${PLIBSYS_TEST_INCLUDE_DIRS}) + endif() + + # Add compile definitions + if (PLIBSYS_TEST_COMPILE_DEFS) + if (COMMAND target_compile_definitions) + target_compile_definitions (${TEST_NAME} PRIVATE ${PLIBSYS_TEST_COMPILE_DEFS}) + else() + add_definitions (${PLIBSYS_TEST_COMPILE_DEFS}) + endif() + endif() + + if (${TEST_NAME} STREQUAL "plibraryloader_test") + add_test (NAME ${TEST_NAME} COMMAND ${TEST_NAME} -- "$") + else() + add_test (NAME ${TEST_NAME} COMMAND ${TEST_NAME}) + endif() +endmacro() + +plibsys_add_test_executable (patomic_test patomic_test.cpp) +plibsys_add_test_executable (pcondvariable_test pcondvariable_test.cpp) +plibsys_add_test_executable (pcryptohash_test pcryptohash_test.cpp) +plibsys_add_test_executable (perror_test perror_test.cpp) +plibsys_add_test_executable (pdir_test pdir_test.cpp) +plibsys_add_test_executable (pfile_test pfile_test.cpp) +plibsys_add_test_executable (phashtable_test phashtable_test.cpp) +plibsys_add_test_executable (pinifile_test pinifile_test.cpp) +plibsys_add_test_executable (plibraryloader_test plibraryloader_test.cpp) +plibsys_add_test_executable (plist_test plist_test.cpp) +plibsys_add_test_executable (pmacros_test pmacros_test.cpp) +plibsys_add_test_executable (pmain_test pmain_test.cpp) +plibsys_add_test_executable (pmem_test pmem_test.cpp) +plibsys_add_test_executable (pmutex_test pmutex_test.cpp) +plibsys_add_test_executable (pprocess_test pprocess_test.cpp) +plibsys_add_test_executable (prwlock_test prwlock_test.cpp) +plibsys_add_test_executable (psemaphore_test psemaphore_test.cpp) +plibsys_add_test_executable (pshmbuffer_test pshmbuffer_test.cpp) +plibsys_add_test_executable (pshm_test pshm_test.cpp) +plibsys_add_test_executable (psocket_test psocket_test.cpp) +plibsys_add_test_executable (psocketaddress_test psocketaddress_test.cpp) +plibsys_add_test_executable (pspinlock_test pspinlock_test.cpp) +plibsys_add_test_executable (pstdarg_test pstdarg_test.cpp) +plibsys_add_test_executable (pstring_test pstring_test.cpp) +plibsys_add_test_executable (ptimeprofiler_test ptimeprofiler_test.cpp) +plibsys_add_test_executable (ptree_test ptree_test.cpp) +plibsys_add_test_executable (ptypes_test ptypes_test.cpp) +plibsys_add_test_executable (puthread_test puthread_test.cpp) diff --git a/3rdparty/plibsys/tests/patomic_test.cpp b/3rdparty/plibsys/tests/patomic_test.cpp new file mode 100644 index 0000000..6efb2d4 --- /dev/null +++ b/3rdparty/plibsys/tests/patomic_test.cpp @@ -0,0 +1,118 @@ +/* + * The MIT License + * + * Copyright (C) 2013-2017 Alexander Saprykin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "plibsys.h" +#include "ptestmacros.h" + +/* Actually we couldn't test the work of the atomic operations across the + * threads, but at least we can test the sanity of operations */ + +P_TEST_MODULE_INIT (); + +P_TEST_CASE_BEGIN (patomic_general_test) +{ + p_libsys_init (); + + (void) p_atomic_is_lock_free (); + + pint atomic_int = 0; + p_atomic_int_set (&atomic_int, 10); + + P_TEST_CHECK (p_atomic_int_add (&atomic_int, 5) == 10); + P_TEST_CHECK (p_atomic_int_get (&atomic_int) == 15); + + p_atomic_int_add (&atomic_int, -5); + P_TEST_CHECK (p_atomic_int_get (&atomic_int) == 10); + + p_atomic_int_inc (&atomic_int); + P_TEST_CHECK (p_atomic_int_get (&atomic_int) == 11); + + P_TEST_CHECK (p_atomic_int_dec_and_test (&atomic_int) == FALSE); + P_TEST_CHECK (p_atomic_int_get (&atomic_int) == 10); + + P_TEST_CHECK (p_atomic_int_compare_and_exchange (&atomic_int, 10, -10) == TRUE); + P_TEST_CHECK (p_atomic_int_get (&atomic_int) == -10); + P_TEST_CHECK (p_atomic_int_compare_and_exchange (&atomic_int, 10, 20) == FALSE); + P_TEST_CHECK (p_atomic_int_get (&atomic_int) == -10); + + p_atomic_int_inc (&atomic_int); + P_TEST_CHECK (p_atomic_int_get (&atomic_int) == -9); + + p_atomic_int_set (&atomic_int, 4); + P_TEST_CHECK (p_atomic_int_get (&atomic_int) == 4); + + P_TEST_CHECK (p_atomic_int_xor ((puint *) &atomic_int, (puint) 1) == 4); + P_TEST_CHECK (p_atomic_int_get (&atomic_int) == 5); + + P_TEST_CHECK (p_atomic_int_or ((puint *) &atomic_int, (puint) 2) == 5); + P_TEST_CHECK (p_atomic_int_get (&atomic_int) == 7); + + P_TEST_CHECK (p_atomic_int_and ((puint *) &atomic_int, (puint) 1) == 7); + P_TEST_CHECK (p_atomic_int_get (&atomic_int) == 1); + + p_atomic_int_set (&atomic_int, 51); + P_TEST_CHECK (p_atomic_int_get (&atomic_int) == 51); + + for (pint i = 51; i > 1; --i) { + P_TEST_CHECK (p_atomic_int_dec_and_test (&atomic_int) == FALSE); + P_TEST_CHECK (p_atomic_int_get (&atomic_int) == (i - 1)); + } + + P_TEST_CHECK (p_atomic_int_dec_and_test (&atomic_int) == TRUE); + P_TEST_CHECK (p_atomic_int_get (&atomic_int) == 0); + + ppointer atomic_pointer = NULL; + p_atomic_pointer_set (&atomic_pointer, PUINT_TO_POINTER (P_MAXSIZE)); + P_TEST_CHECK (p_atomic_pointer_get (&atomic_pointer) == PUINT_TO_POINTER (P_MAXSIZE)); + + p_atomic_pointer_set (&atomic_pointer, PUINT_TO_POINTER (100)); + P_TEST_CHECK (p_atomic_pointer_get (&atomic_pointer) == PUINT_TO_POINTER (100)); + P_TEST_CHECK (p_atomic_pointer_add (&atomic_pointer, (pssize) 100) == 100); + P_TEST_CHECK (p_atomic_pointer_get (&atomic_pointer) == PUINT_TO_POINTER (200)); + + p_atomic_pointer_set (&atomic_pointer, PINT_TO_POINTER (4)); + P_TEST_CHECK (p_atomic_pointer_get (&atomic_pointer) == PINT_TO_POINTER (4)); + + P_TEST_CHECK (p_atomic_pointer_xor (&atomic_pointer, (psize) 1) == 4); + P_TEST_CHECK (p_atomic_pointer_get (&atomic_pointer) == PINT_TO_POINTER (5)); + + P_TEST_CHECK (p_atomic_pointer_or (&atomic_pointer, (psize) 2) == 5); + P_TEST_CHECK (p_atomic_pointer_get (&atomic_pointer) == PINT_TO_POINTER (7)); + + P_TEST_CHECK (p_atomic_pointer_and (&atomic_pointer, (psize) 1) == 7); + P_TEST_CHECK (p_atomic_pointer_get (&atomic_pointer) == PINT_TO_POINTER (1)); + + P_TEST_CHECK (p_atomic_pointer_compare_and_exchange (&atomic_pointer, PUINT_TO_POINTER (1), NULL) == TRUE); + P_TEST_CHECK (p_atomic_pointer_get (&atomic_pointer) == NULL); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_SUITE_BEGIN() +{ + P_TEST_SUITE_RUN_CASE (patomic_general_test); +} +P_TEST_SUITE_END() diff --git a/3rdparty/plibsys/tests/pcondvariable_test.cpp b/3rdparty/plibsys/tests/pcondvariable_test.cpp new file mode 100644 index 0000000..887b77f --- /dev/null +++ b/3rdparty/plibsys/tests/pcondvariable_test.cpp @@ -0,0 +1,230 @@ +/* + * The MIT License + * + * Copyright (C) 2013-2019 Alexander Saprykin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "plibsys.h" +#include "ptestmacros.h" + +P_TEST_MODULE_INIT (); + +#define PCONDTEST_MAX_QUEUE 10 + +static pint thread_wakeups = 0; +static pint thread_queue = 0; +static PCondVariable * queue_empty_cond = NULL; +static PCondVariable * queue_full_cond = NULL; +static PMutex * cond_mutex = NULL; +volatile static pboolean is_working = TRUE; + +extern "C" ppointer pmem_alloc (psize nbytes) +{ + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" ppointer pmem_realloc (ppointer block, psize nbytes) +{ + P_UNUSED (block); + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" void pmem_free (ppointer block) +{ + P_UNUSED (block); +} + +static void * producer_test_thread (void *) +{ + while (is_working == TRUE) { + if (!p_mutex_lock (cond_mutex)) { + is_working = FALSE; + p_cond_variable_broadcast (queue_full_cond); + p_uthread_exit (1); + } + + while (thread_queue >= PCONDTEST_MAX_QUEUE && is_working == TRUE) { + if (!p_cond_variable_wait (queue_empty_cond, cond_mutex)) { + is_working = FALSE; + p_cond_variable_broadcast (queue_full_cond); + p_mutex_unlock (cond_mutex); + p_uthread_exit (1); + } + } + + if (is_working) { + ++thread_queue; + ++thread_wakeups; + } + + if (!p_cond_variable_broadcast (queue_full_cond)) { + is_working = FALSE; + p_mutex_unlock (cond_mutex); + p_uthread_exit (1); + } + + if (!p_mutex_unlock (cond_mutex)) { + is_working = FALSE; + p_cond_variable_broadcast (queue_full_cond); + p_uthread_exit (1); + } + } + + p_cond_variable_broadcast (queue_full_cond); + p_uthread_exit (0); + + return NULL; +} + +static void * consumer_test_thread (void *) +{ + while (is_working == TRUE) { + if (!p_mutex_lock (cond_mutex)) { + is_working = FALSE; + p_cond_variable_signal (queue_empty_cond); + p_uthread_exit (1); + } + + while (thread_queue <= 0 && is_working == TRUE) { + if (!p_cond_variable_wait (queue_full_cond, cond_mutex)) { + is_working = FALSE; + p_cond_variable_signal (queue_empty_cond); + p_mutex_unlock (cond_mutex); + p_uthread_exit (1); + } + } + + if (is_working) { + --thread_queue; + ++thread_wakeups; + } + + if (!p_cond_variable_signal (queue_empty_cond)) { + is_working = FALSE; + p_mutex_unlock (cond_mutex); + p_uthread_exit (1); + } + + if (!p_mutex_unlock (cond_mutex)) { + is_working = FALSE; + p_cond_variable_signal (queue_empty_cond); + p_uthread_exit (1); + } + } + + p_cond_variable_signal (queue_empty_cond); + p_uthread_exit (0); + + return NULL; +} + +P_TEST_CASE_BEGIN (pcondvariable_nomem_test) +{ + p_libsys_init (); + + PMemVTable vtable; + + vtable.free = pmem_free; + vtable.malloc = pmem_alloc; + vtable.realloc = pmem_realloc; + + P_TEST_CHECK (p_mem_set_vtable (&vtable) == TRUE); + P_TEST_CHECK (p_cond_variable_new () == NULL); + + p_mem_restore_vtable (); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (pcondvariable_bad_input_test) +{ + p_libsys_init (); + + P_TEST_REQUIRE (p_cond_variable_broadcast (NULL) == FALSE); + P_TEST_REQUIRE (p_cond_variable_signal (NULL) == FALSE); + P_TEST_REQUIRE (p_cond_variable_wait (NULL, NULL) == FALSE); + p_cond_variable_free (NULL); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (pcondvariable_general_test) +{ + PUThread *thr1, *thr2, *thr3; + + p_libsys_init (); + + queue_empty_cond = p_cond_variable_new (); + P_TEST_REQUIRE (queue_empty_cond != NULL); + queue_full_cond = p_cond_variable_new (); + P_TEST_REQUIRE (queue_full_cond != NULL); + cond_mutex = p_mutex_new (); + P_TEST_REQUIRE (cond_mutex != NULL); + + is_working = TRUE; + thread_wakeups = 0; + thread_queue = 0; + + thr1 = p_uthread_create ((PUThreadFunc) producer_test_thread, NULL, TRUE, NULL); + P_TEST_REQUIRE (thr1 != NULL); + + thr2 = p_uthread_create ((PUThreadFunc) consumer_test_thread, NULL, TRUE, NULL); + P_TEST_REQUIRE (thr2 != NULL); + + thr3 = p_uthread_create ((PUThreadFunc) consumer_test_thread, NULL, TRUE, NULL); + P_TEST_REQUIRE (thr3 != NULL); + + P_TEST_REQUIRE (p_cond_variable_broadcast (queue_empty_cond) == TRUE); + P_TEST_REQUIRE (p_cond_variable_broadcast (queue_full_cond) == TRUE); + + p_uthread_sleep (4000); + + is_working = FALSE; + + P_TEST_CHECK (p_uthread_join (thr1) == 0); + P_TEST_CHECK (p_uthread_join (thr2) == 0); + P_TEST_CHECK (p_uthread_join (thr3) == 0); + + P_TEST_REQUIRE (thread_wakeups > 0 && thread_queue >= 0 && thread_queue <= 10); + + p_uthread_unref (thr1); + p_uthread_unref (thr2); + p_uthread_unref (thr3); + p_cond_variable_free (queue_empty_cond); + p_cond_variable_free (queue_full_cond); + p_mutex_free (cond_mutex); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_SUITE_BEGIN() +{ + P_TEST_SUITE_RUN_CASE (pcondvariable_nomem_test); + P_TEST_SUITE_RUN_CASE (pcondvariable_bad_input_test); + P_TEST_SUITE_RUN_CASE (pcondvariable_general_test); +} +P_TEST_SUITE_END() diff --git a/3rdparty/plibsys/tests/pcryptohash_test.cpp b/3rdparty/plibsys/tests/pcryptohash_test.cpp new file mode 100644 index 0000000..30c1f91 --- /dev/null +++ b/3rdparty/plibsys/tests/pcryptohash_test.cpp @@ -0,0 +1,662 @@ +/* + * The MIT License + * + * Copyright (C) 2013-2017 Alexander Saprykin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "plibsys.h" +#include "ptestmacros.h" + +#include + +P_TEST_MODULE_INIT (); + +#define PCRYPTO_STRESS_LENGTH 10000 +#define PCRYPTO_MAX_UPDATES 1000000 + +extern "C" ppointer pmem_alloc (psize nbytes) +{ + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" ppointer pmem_realloc (ppointer block, psize nbytes) +{ + P_UNUSED (block); + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" void pmem_free (ppointer block) +{ + P_UNUSED (block); +} + +static void +general_hash_test (PCryptoHashType type, + psize hash_len, + const pchar *msg1, + const pchar *msg2, + const puchar *etalon1, + const puchar *etalon2, + const puchar *etalon3, + const pchar *hash1, + const pchar *hash2, + const pchar *hash3, + const pchar *hash_stress) +{ + PCryptoHash *crypto_hash; + psize dig_len; + pchar *hash_str; + pchar *long_str; + puchar *hash_dig; + + crypto_hash = p_crypto_hash_new (type); + + P_TEST_REQUIRE ((psize) p_crypto_hash_get_length (crypto_hash) == hash_len); + P_TEST_REQUIRE (p_crypto_hash_get_type (crypto_hash) == type); + + hash_str = p_crypto_hash_get_string (crypto_hash); + P_TEST_REQUIRE (hash_str != NULL); + p_crypto_hash_reset (crypto_hash); + p_free (hash_str); + + hash_dig = (puchar *) p_malloc0 (hash_len); + P_TEST_REQUIRE (hash_dig != NULL); + + long_str = (pchar *) p_malloc0 (PCRYPTO_STRESS_LENGTH); + P_TEST_REQUIRE (long_str != NULL); + + for (int i = 0; i < PCRYPTO_STRESS_LENGTH; ++i) + long_str[i] = (pchar) (97 + i % 20); + + /* Case 1 */ + + /* Check string */ + p_crypto_hash_update (crypto_hash, (const puchar *) msg1, strlen (msg1)); + hash_str = p_crypto_hash_get_string (crypto_hash); + P_TEST_CHECK (strcmp (hash_str, hash1) == 0); + p_free (hash_str); + + p_crypto_hash_reset (crypto_hash); + + /* Check digest */ + dig_len = hash_len; + p_crypto_hash_update (crypto_hash, (const puchar *) msg1, strlen (msg1)); + p_crypto_hash_get_digest (crypto_hash, hash_dig, &dig_len); + + P_TEST_CHECK (dig_len == hash_len); + + for (unsigned int i = 0; i < hash_len; ++i) + P_TEST_CHECK (hash_dig[i] == etalon1[i]); + + p_crypto_hash_reset (crypto_hash); + + /* Case 2 */ + + /* Check string */ + p_crypto_hash_update (crypto_hash, (const puchar *) msg2, strlen (msg2)); + hash_str = p_crypto_hash_get_string (crypto_hash); + P_TEST_CHECK (strcmp (hash_str, hash2) == 0); + p_free (hash_str); + + p_crypto_hash_reset (crypto_hash); + + /* Check digest */ + dig_len = hash_len; + p_crypto_hash_update (crypto_hash, (const puchar *) msg2, strlen (msg2)); + p_crypto_hash_get_digest (crypto_hash, hash_dig, &dig_len); + + P_TEST_CHECK (dig_len == hash_len); + + for (unsigned int i = 0; i < hash_len; ++i) + P_TEST_CHECK (hash_dig[i] == etalon2[i]); + + p_crypto_hash_reset (crypto_hash); + + /* Case 3 */ + + /* Check string */ + for (int i = 0; i < PCRYPTO_MAX_UPDATES; ++i) + p_crypto_hash_update (crypto_hash, (const puchar *) "a", 1); + + hash_str = p_crypto_hash_get_string (crypto_hash); + + P_TEST_CHECK (strcmp (hash_str, hash3) == 0); + p_free (hash_str); + + p_crypto_hash_reset (crypto_hash); + + /* Check digest */ + dig_len = hash_len; + for (int i = 0; i < PCRYPTO_MAX_UPDATES; ++i) + p_crypto_hash_update (crypto_hash, (const puchar *) "a", 1); + + p_crypto_hash_get_digest (crypto_hash, hash_dig, &dig_len); + P_TEST_CHECK (dig_len == hash_len); + + for (unsigned int i = 0; i < hash_len; ++i) + P_TEST_CHECK (hash_dig[i] == etalon3[i]); + + p_crypto_hash_reset (crypto_hash); + + /* Stress test */ + p_crypto_hash_update (crypto_hash, (const puchar *) long_str, PCRYPTO_STRESS_LENGTH); + hash_str = p_crypto_hash_get_string (crypto_hash); + + P_TEST_CHECK (strcmp (hash_str, hash_stress) == 0); + p_free (hash_str); + + p_crypto_hash_reset (crypto_hash); + + p_free (long_str); + p_free (hash_dig); + p_crypto_hash_free (crypto_hash); +} + +P_TEST_CASE_BEGIN (pcryptohash_nomem_test) +{ + p_libsys_init (); + + PMemVTable vtable; + + vtable.free = pmem_free; + vtable.malloc = pmem_alloc; + vtable.realloc = pmem_realloc; + + P_TEST_CHECK (p_mem_set_vtable (&vtable) == TRUE); + + P_TEST_CHECK (p_crypto_hash_new (P_CRYPTO_HASH_TYPE_MD5) == NULL); + P_TEST_CHECK (p_crypto_hash_new (P_CRYPTO_HASH_TYPE_SHA1) == NULL); + P_TEST_CHECK (p_crypto_hash_new (P_CRYPTO_HASH_TYPE_GOST) == NULL); + + p_mem_restore_vtable (); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (pcryptohash_invalid_test) +{ + PCryptoHash *hash; + psize len; + pssize md5_len; + pchar *hash_str; + puchar *buf; + + p_libsys_init (); + + P_TEST_CHECK (p_crypto_hash_new ((PCryptoHashType) -1) == NULL); + P_TEST_CHECK (p_crypto_hash_get_length (NULL) == 0); + P_TEST_CHECK (p_crypto_hash_get_string (NULL) == NULL); + P_TEST_CHECK ((pint) p_crypto_hash_get_type (NULL) == -1); + p_crypto_hash_free (NULL); + + p_crypto_hash_update (NULL, NULL, 0); + p_crypto_hash_get_digest (NULL, NULL, NULL); + + p_crypto_hash_get_digest (NULL, NULL, &len); + P_TEST_CHECK (len == 0); + + p_crypto_hash_reset (NULL); + + hash = p_crypto_hash_new (P_CRYPTO_HASH_TYPE_MD5); + P_TEST_CHECK (hash != NULL); + + md5_len = p_crypto_hash_get_length (hash); + P_TEST_CHECK (md5_len > 0); + + buf = (puchar *) p_malloc0 (md5_len); + P_TEST_CHECK (buf != NULL); + + p_crypto_hash_get_digest (hash, buf, &len); + P_TEST_CHECK (len == 0); + + p_crypto_hash_update (hash, (const puchar *) ("abc"), 3); + len = ((psize) md5_len) - 1; + p_crypto_hash_get_digest (hash, buf, &len); + P_TEST_CHECK (len == 0); + + hash_str = p_crypto_hash_get_string (hash); + P_TEST_CHECK (strcmp (hash_str, "900150983cd24fb0d6963f7d28e17f72") == 0); + p_free (hash_str); + + p_crypto_hash_update (hash, (const puchar *) ("abc"), 3); + hash_str = p_crypto_hash_get_string (hash); + P_TEST_CHECK (strcmp (hash_str, "900150983cd24fb0d6963f7d28e17f72") == 0); + p_free (hash_str); + + p_crypto_hash_free (hash); + p_free (buf); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (md5_test) +{ + const puchar hash_etalon_1[] = {144, 1, 80, 152, 60, 210, 79, 176, + 214, 150, 63, 125, 40, 225, 127, 114}; + const puchar hash_etalon_2[] = {130, 21, 239, 7, 150, 162, 11, 202, + 170, 225, 22, 211, 135, 108, 102, 74}; + const puchar hash_etalon_3[] = {119, 7, 214, 174, 78, 2, 124, 112, + 238, 162, 169, 53, 194, 41, 111, 33}; + + p_libsys_init (); + + general_hash_test (P_CRYPTO_HASH_TYPE_MD5, + 16, + "abc", + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + hash_etalon_1, + hash_etalon_2, + hash_etalon_3, + "900150983cd24fb0d6963f7d28e17f72", + "8215ef0796a20bcaaae116d3876c664a", + "7707d6ae4e027c70eea2a935c2296f21", + "e19ea4a77c97fa6c2521ae1ca66982b9"); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (sha1_test) +{ + const puchar hash_etalon_1[] = {169, 153, 62, 54, 71, 6, 129, 106, + 186, 62, 37, 113, 120, 80, 194, 108, + 156, 208, 216, 157}; + const puchar hash_etalon_2[] = {132, 152, 62, 68, 28, 59, 210, 110, + 186, 174, 74, 161, 249, 81, 41, 229, + 229, 70, 112, 241}; + const puchar hash_etalon_3[] = { 52, 170, 151, 60, 212, 196, 218, 164, + 246, 30, 235, 43, 219, 173, 39, 49, + 101, 52, 1, 111}; + + p_libsys_init (); + + general_hash_test (P_CRYPTO_HASH_TYPE_SHA1, + 20, + "abc", + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + hash_etalon_1, + hash_etalon_2, + hash_etalon_3, + "a9993e364706816aba3e25717850c26c9cd0d89d", + "84983e441c3bd26ebaae4aa1f95129e5e54670f1", + "34aa973cd4c4daa4f61eeb2bdbad27316534016f", + "56309c2dbe04a348ec801ca5f40b035bad01f907"); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (sha2_224_test) +{ + const puchar hash_etalon_1[] = { 35, 9, 125, 34, 52, 5, 216, 34, 134, 66, + 164, 119, 189, 162, 85, 179, 42, 173, 188, 228, + 189, 160, 179, 247, 227, 108, 157, 167}; + const puchar hash_etalon_2[] = {117, 56, 139, 22, 81, 39, 118, 204, 93, 186, + 93, 161, 253, 137, 1, 80, 176, 198, 69, 92, + 180, 245, 139, 25, 82, 82, 37, 37}; + const puchar hash_etalon_3[] = { 32, 121, 70, 85, 152, 12, 145, 216, 187, 180, + 193, 234, 151, 97, 138, 75, 240, 63, 66, 88, + 25, 72, 178, 238, 78, 231, 173, 103, }; + + p_libsys_init (); + + general_hash_test (P_CRYPTO_HASH_TYPE_SHA2_224, + 28, + "abc", + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + hash_etalon_1, + hash_etalon_2, + hash_etalon_3, + "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", + "75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525", + "20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67", + "4cf3d45b57e0d54981c4d86954e8378168d5a9f6ceab9e0aae5dd2f6"); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (sha2_256_test) +{ + const puchar hash_etalon_1[] = {186, 120, 22, 191, 143, 1, 207, 234, + 65, 65, 64, 222, 93, 174, 34, 35, + 176, 3, 97, 163, 150, 23, 122, 156, + 180, 16, 255, 97, 242, 0, 21, 173}; + const puchar hash_etalon_2[] = { 36, 141, 106, 97, 210, 6, 56, 184, + 229, 192, 38, 147, 12, 62, 96, 57, + 163, 60, 228, 89, 100, 255, 33, 103, + 246, 236, 237, 212, 25, 219, 6, 193}; + const puchar hash_etalon_3[] = {205, 199, 110, 92, 153, 20, 251, 146, + 129, 161, 199, 226, 132, 215, 62, 103, + 241, 128, 154, 72, 164, 151, 32, 14, + 4, 109, 57, 204, 199, 17, 44, 208}; + + p_libsys_init (); + + general_hash_test (P_CRYPTO_HASH_TYPE_SHA2_256, + 32, + "abc", + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + hash_etalon_1, + hash_etalon_2, + hash_etalon_3, + "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", + "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1", + "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0", + "4c2d7749e1b711ca652fda20dd29fe378fd9988f19eadadfa570682e2c55349f"); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (sha2_384_test) +{ + const puchar hash_etalon_1[] = {203, 0, 117, 63, 69, 163, 94, 139, 181, 160, 61, 105, + 154, 198, 80, 7, 39, 44, 50, 171, 14, 222, 209, 99, + 26, 139, 96, 90, 67, 255, 91, 237, 128, 134, 7, 43, + 161, 231, 204, 35, 88, 186, 236, 161, 52, 200, 37, 167}; + const puchar hash_etalon_2[] = { 51, 145, 253, 221, 252, 141, 199, 57, 55, 7, 166, 91, + 27, 71, 9, 57, 124, 248, 177, 209, 98, 175, 5, 171, + 254, 143, 69, 13, 229, 243, 107, 198, 176, 69, 90, 133, + 32, 188, 78, 111, 95, 233, 91, 31, 227, 200, 69, 43}; + const puchar hash_etalon_3[] = {157, 14, 24, 9, 113, 100, 116, 203, 8, 110, 131, 78, + 49, 10, 74, 28, 237, 20, 158, 156, 0, 242, 72, 82, + 121, 114, 206, 197, 112, 76, 42, 91, 7, 184, 179, 220, + 56, 236, 196, 235, 174, 151, 221, 216, 127, 61, 137, 133}; + + p_libsys_init (); + + general_hash_test (P_CRYPTO_HASH_TYPE_SHA2_384, + 48, + "abc", + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + hash_etalon_1, + hash_etalon_2, + hash_etalon_3, + "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7", + "3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b", + "9d0e1809716474cb086e834e310a4a1ced149e9c00f248527972cec5704c2a5b07b8b3dc38ecc4ebae97ddd87f3d8985", + "533e016fd92dd8a8c339328bb5401c3e700e27cd72d8230059e1d4583a506fe8187607bf899a86961af2bf5521b359eb"); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (sha2_512_test) +{ + const puchar hash_etalon_1[] = {221, 175, 53, 161, 147, 97, 122, 186, + 204, 65, 115, 73, 174, 32, 65, 49, + 18, 230, 250, 78, 137, 169, 126, 162, + 10, 158, 238, 230, 75, 85, 211, 154, + 33, 146, 153, 42, 39, 79, 193, 168, + 54, 186, 60, 35, 163, 254, 235, 189, + 69, 77, 68, 35, 100, 60, 232, 14, + 42, 154, 201, 79, 165, 76, 164, 159}; + const puchar hash_etalon_2[] = { 32, 74, 143, 198, 221, 168, 47, 10, + 12, 237, 123, 235, 142, 8, 164, 22, + 87, 193, 110, 244, 104, 178, 40, 168, + 39, 155, 227, 49, 167, 3, 195, 53, + 150, 253, 21, 193, 59, 27, 7, 249, + 170, 29, 59, 234, 87, 120, 156, 160, + 49, 173, 133, 199, 167, 29, 215, 3, + 84, 236, 99, 18, 56, 202, 52, 69}; + const puchar hash_etalon_3[] = {231, 24, 72, 61, 12, 231, 105, 100, + 78, 46, 66, 199, 188, 21, 180, 99, + 142, 31, 152, 177, 59, 32, 68, 40, + 86, 50, 168, 3, 175, 169, 115, 235, + 222, 15, 242, 68, 135, 126, 166, 10, + 76, 176, 67, 44, 229, 119, 195, 27, + 235, 0, 156, 92, 44, 73, 170, 46, + 78, 173, 178, 23, 173, 140, 192, 155}; + + p_libsys_init (); + + general_hash_test (P_CRYPTO_HASH_TYPE_SHA2_512, + 64, + "abc", + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + hash_etalon_1, + hash_etalon_2, + hash_etalon_3, + "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f", + "204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445", + "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973ebde0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b", + "411525772d02eef0e2ce1107d89b79b8cf6d704e88d4509f726c963d411df6df178c1c9473718f70b0e06c2fda6a9c25f6c91a925849f372634d5f63e6047a20"); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (sha3_224_test) +{ + const puchar hash_etalon_1[] = {230, 66, 130, 76, 63, 140, 242, 74, 208, 146, + 52, 238, 125, 60, 118, 111, 201, 163, 165, 22, + 141, 12, 148, 173, 115, 180, 111, 223}; + const puchar hash_etalon_2[] = {138, 36, 16, 139, 21, 74, 218, 33, 201, 253, + 85, 116, 73, 68, 121, 186, 92, 126, 122, 183, + 110, 242, 100, 234, 208, 252, 206, 51}; + const puchar hash_etalon_3[] = {214, 147, 53, 185, 51, 37, 25, 46, 81, 106, + 145, 46, 109, 25, 161, 92, 181, 28, 110, 213, + 193, 82, 67, 231, 167, 253, 101, 60}; + + p_libsys_init (); + + general_hash_test (P_CRYPTO_HASH_TYPE_SHA3_224, + 28, + "abc", + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + hash_etalon_1, + hash_etalon_2, + hash_etalon_3, + "e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf", + "8a24108b154ada21c9fd5574494479ba5c7e7ab76ef264ead0fcce33", + "d69335b93325192e516a912e6d19a15cb51c6ed5c15243e7a7fd653c", + "425fbad801bf675651dcf61af1138831480b562e714c70a2a0050ad3"); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (sha3_256_test) +{ + const puchar hash_etalon_1[] = { 58, 152, 93, 167, 79, 226, 37, 178, + 4, 92, 23, 45, 107, 211, 144, 189, + 133, 95, 8, 110, 62, 157, 82, 91, + 70, 191, 226, 69, 17, 67, 21, 50}; + const puchar hash_etalon_2[] = { 65, 192, 219, 162, 169, 214, 36, 8, + 73, 16, 3, 118, 168, 35, 94, 44, + 130, 225, 185, 153, 138, 153, 158, 33, + 219, 50, 221, 151, 73, 109, 51, 118}; + const puchar hash_etalon_3[] = { 92, 136, 117, 174, 71, 74, 54, 52, + 186, 79, 213, 94, 200, 91, 255, 214, + 97, 243, 42, 202, 117, 198, 214, 153, + 208, 205, 203, 108, 17, 88, 145, 193}; + + p_libsys_init (); + + general_hash_test (P_CRYPTO_HASH_TYPE_SHA3_256, + 32, + "abc", + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + hash_etalon_1, + hash_etalon_2, + hash_etalon_3, + "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532", + "41c0dba2a9d6240849100376a8235e2c82e1b9998a999e21db32dd97496d3376", + "5c8875ae474a3634ba4fd55ec85bffd661f32aca75c6d699d0cdcb6c115891c1", + "e37ed9f31da3d61740e04c3124a2da5dbe8be0a2ef5c8b5932d45eb1958219e2"); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (sha3_384_test) +{ + const puchar hash_etalon_1[] = {236, 1, 73, 130, 136, 81, 111, 201, 38, 69, 159, 88, + 226, 198, 173, 141, 249, 180, 115, 203, 15, 192, 140, 37, + 150, 218, 124, 240, 228, 155, 228, 178, 152, 216, 140, 234, + 146, 122, 199, 245, 57, 241, 237, 242, 40, 55, 109, 37}; + const puchar hash_etalon_2[] = {153, 28, 102, 87, 85, 235, 58, 75, 107, 189, 251, 117, + 199, 138, 73, 46, 140, 86, 162, 44, 92, 77, 126, 66, + 155, 253, 188, 50, 185, 212, 173, 90, 160, 74, 31, 7, + 110, 98, 254, 161, 158, 239, 81, 172, 208, 101, 124, 34}; + const puchar hash_etalon_3[] = {238, 233, 226, 77, 120, 193, 133, 83, 55, 152, 52, 81, + 223, 151, 200, 173, 158, 237, 242, 86, 198, 51, 79, 142, + 148, 141, 37, 45, 94, 14, 118, 132, 122, 160, 119, 77, + 219, 144, 168, 66, 25, 13, 44, 85, 139, 75, 131, 64}; + + p_libsys_init (); + + general_hash_test (P_CRYPTO_HASH_TYPE_SHA3_384, + 48, + "abc", + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + hash_etalon_1, + hash_etalon_2, + hash_etalon_3, + "ec01498288516fc926459f58e2c6ad8df9b473cb0fc08c2596da7cf0e49be4b298d88cea927ac7f539f1edf228376d25", + "991c665755eb3a4b6bbdfb75c78a492e8c56a22c5c4d7e429bfdbc32b9d4ad5aa04a1f076e62fea19eef51acd0657c22", + "eee9e24d78c1855337983451df97c8ad9eedf256c6334f8e948d252d5e0e76847aa0774ddb90a842190d2c558b4b8340", + "3836508de3aa893ad8bd18df238a79e534bc55a6fae84a557bde0820ccfc3ad58e3eaab29a7d0d3bfc071c6d69b2e9d3"); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (sha3_512_test) +{ + const puchar hash_etalon_1[] = {183, 81, 133, 11, 26, 87, 22, 138, + 86, 147, 205, 146, 75, 107, 9, 110, + 8, 246, 33, 130, 116, 68, 247, 13, + 136, 79, 93, 2, 64, 210, 113, 46, + 16, 225, 22, 233, 25, 42, 243, 201, + 26, 126, 197, 118, 71, 227, 147, 64, + 87, 52, 11, 76, 244, 8, 213, 165, + 101, 146, 248, 39, 78, 236, 83, 240}; + const puchar hash_etalon_2[] = { 4, 163, 113, 232, 78, 207, 181, 184, + 183, 124, 180, 134, 16, 252, 168, 24, + 45, 212, 87, 206, 111, 50, 106, 15, + 211, 215, 236, 47, 30, 145, 99, 109, + 238, 105, 31, 190, 12, 152, 83, 2, + 186, 27, 13, 141, 199, 140, 8, 99, + 70, 181, 51, 180, 156, 3, 13, 153, + 162, 125, 175, 17, 57, 214, 231, 94}; + const puchar hash_etalon_3[] = { 60, 58, 135, 109, 161, 64, 52, 171, + 96, 98, 124, 7, 123, 185, 143, 126, + 18, 10, 42, 83, 112, 33, 45, 255, + 179, 56, 90, 24, 212, 243, 136, 89, + 237, 49, 29, 10, 157, 81, 65, 206, + 156, 197, 198, 110, 230, 137, 178, 102, + 168, 170, 24, 172, 232, 40, 42, 14, + 13, 181, 150, 201, 11, 10, 123, 135}; + + p_libsys_init (); + + general_hash_test (P_CRYPTO_HASH_TYPE_SHA3_512, + 64, + "abc", + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + hash_etalon_1, + hash_etalon_2, + hash_etalon_3, + "b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0", + "04a371e84ecfb5b8b77cb48610fca8182dd457ce6f326a0fd3d7ec2f1e91636dee691fbe0c985302ba1b0d8dc78c086346b533b49c030d99a27daf1139d6e75e", + "3c3a876da14034ab60627c077bb98f7e120a2a5370212dffb3385a18d4f38859ed311d0a9d5141ce9cc5c66ee689b266a8aa18ace8282a0e0db596c90b0a7b87", + "16f59fe0b4344af86b37eb145afe41e9dadb45279d074c5bf5c649dd3d2952e47c0ac3a59ea19dc8395d04e8a72fddd9307b839c35fc4bc44a0463003b80dcf1"); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (gost3411_94_test) +{ + PCryptoHash *gost3411_94_hash; + pchar *hash_str; + const puchar hash_etalon_1[] = { 44, 239, 194, 247, 183, 189, 197, 20, + 225, 142, 165, 127, 167, 79, 243, 87, + 231, 250, 23, 214, 82, 199, 95, 105, + 203, 27, 231, 137, 62, 222, 72, 235}; + const puchar hash_etalon_2[] = {195, 115, 12, 92, 188, 202, 207, 145, + 90, 194, 146, 103, 111, 33, 232, 189, + 78, 247, 83, 49, 217, 64, 94, 95, + 26, 97, 220, 49, 48, 166, 80, 17}; + const puchar hash_etalon_3[] = {134, 147, 40, 122, 166, 47, 148, 120, + 247, 203, 49, 46, 192, 134, 107, 108, + 78, 74, 15, 17, 22, 4, 65, 232, + 244, 255, 205, 39, 21, 221, 85, 79}; + + p_libsys_init (); + + general_hash_test (P_CRYPTO_HASH_TYPE_GOST, + 32, + "This is message, length=32 bytes", + "Suppose the original message has length = 50 bytes", + hash_etalon_1, + hash_etalon_2, + hash_etalon_3, + "2cefc2f7b7bdc514e18ea57fa74ff357e7fa17d652c75f69cb1be7893ede48eb", + "c3730c5cbccacf915ac292676f21e8bd4ef75331d9405e5f1a61dc3130a65011", + "8693287aa62f9478f7cb312ec0866b6c4e4a0f11160441e8f4ffcd2715dd554f", + "3738fb45a7f0de6a5447163c0b441ead7a23e48e7af553829dc4300a99f86343"); + + gost3411_94_hash = p_crypto_hash_new (P_CRYPTO_HASH_TYPE_GOST); + + P_TEST_REQUIRE (gost3411_94_hash != NULL); + + /* Repeat test */ + p_crypto_hash_update (gost3411_94_hash, (const puchar *) "message digest", 14); + p_crypto_hash_update (gost3411_94_hash, (const puchar *) "message digest", 14); + p_crypto_hash_update (gost3411_94_hash, (const puchar *) "message digest", 14); + p_crypto_hash_update (gost3411_94_hash, (const puchar *) "message digest", 14); + + hash_str = p_crypto_hash_get_string (gost3411_94_hash); + P_TEST_CHECK (strcmp (hash_str, "9c7b5288c8b3343b29e8ee4a5579593bd90131db7f6fed9b13af4399698b5d29") == 0); + p_free (hash_str); + + p_crypto_hash_reset (gost3411_94_hash); + p_crypto_hash_free (gost3411_94_hash); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_SUITE_BEGIN() +{ + P_TEST_SUITE_RUN_CASE (pcryptohash_nomem_test); + P_TEST_SUITE_RUN_CASE (pcryptohash_invalid_test); + P_TEST_SUITE_RUN_CASE (md5_test); + P_TEST_SUITE_RUN_CASE (sha1_test); + P_TEST_SUITE_RUN_CASE (sha2_224_test); + P_TEST_SUITE_RUN_CASE (sha2_256_test); + P_TEST_SUITE_RUN_CASE (sha2_384_test); + P_TEST_SUITE_RUN_CASE (sha2_512_test); + P_TEST_SUITE_RUN_CASE (sha3_224_test); + P_TEST_SUITE_RUN_CASE (sha3_256_test); + P_TEST_SUITE_RUN_CASE (sha3_384_test); + P_TEST_SUITE_RUN_CASE (sha3_512_test); + P_TEST_SUITE_RUN_CASE (gost3411_94_test); +} +P_TEST_SUITE_END() diff --git a/3rdparty/plibsys/tests/pdir_test.cpp b/3rdparty/plibsys/tests/pdir_test.cpp new file mode 100644 index 0000000..7b1689d --- /dev/null +++ b/3rdparty/plibsys/tests/pdir_test.cpp @@ -0,0 +1,248 @@ +/* + * The MIT License + * + * Copyright (C) 2015-2017 Alexander Saprykin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "plibsys.h" +#include "ptestmacros.h" + +#include + +P_TEST_MODULE_INIT (); + +#define PDIR_ENTRY_DIR "test_2" +#define PDIR_ENTRY_FILE "test_file.txt" +#define PDIR_TEST_DIR "." P_DIR_SEPARATOR "pdir_test_dir" +#define PDIR_TEST_DIR_IN "." P_DIR_SEPARATOR "pdir_test_dir" P_DIR_SEPARATOR "test_2" +#define PDIR_TEST_FILE "." P_DIR_SEPARATOR "pdir_test_dir" P_DIR_SEPARATOR "test_file.txt" + +extern "C" ppointer pmem_alloc (psize nbytes) +{ + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" ppointer pmem_realloc (ppointer block, psize nbytes) +{ + P_UNUSED (block); + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" void pmem_free (ppointer block) +{ + P_UNUSED (block); +} + +P_TEST_CASE_BEGIN (pdir_nomem_test) +{ + p_libsys_init (); + + PMemVTable vtable; + + vtable.free = pmem_free; + vtable.malloc = pmem_alloc; + vtable.realloc = pmem_realloc; + + P_TEST_CHECK (p_mem_set_vtable (&vtable) == TRUE); + + /* Cleanup previous run */ + p_dir_remove (PDIR_TEST_DIR_IN, NULL); + p_dir_remove (PDIR_TEST_DIR, NULL); + + P_TEST_REQUIRE (p_dir_create (PDIR_TEST_DIR, 0777, NULL) == TRUE); + P_TEST_REQUIRE (p_dir_create (PDIR_TEST_DIR_IN, 0777, NULL) == TRUE); + + P_TEST_CHECK (p_dir_new (PDIR_TEST_DIR"/", NULL) == NULL); + + /* Revert memory management back */ + p_mem_restore_vtable (); + + /* Try out of memory when iterating */ + PDir *dir = p_dir_new (PDIR_TEST_DIR"/", NULL); + P_TEST_CHECK (dir != NULL); + + vtable.free = pmem_free; + vtable.malloc = pmem_alloc; + vtable.realloc = pmem_realloc; + + P_TEST_CHECK (p_mem_set_vtable (&vtable) == TRUE); + + P_TEST_CHECK (p_dir_get_next_entry (dir, NULL) == NULL); + + /* Cleanup */ + p_mem_restore_vtable (); + + p_dir_free (dir); + + P_TEST_CHECK (p_dir_remove (PDIR_TEST_DIR_IN, NULL) == TRUE); + P_TEST_CHECK (p_dir_remove (PDIR_TEST_DIR, NULL) == TRUE); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (pdir_general_test) +{ + p_libsys_init (); + + P_TEST_CHECK (p_dir_new (NULL, NULL) == NULL); + P_TEST_CHECK (p_dir_new ("." P_DIR_SEPARATOR "pdir_test_dir_new", NULL) == NULL); + P_TEST_CHECK (p_dir_create (NULL, -1, NULL) == FALSE); +#ifndef P_OS_VMS + P_TEST_CHECK (p_dir_create ("." P_DIR_SEPARATOR "pdir_test_dir_new" P_DIR_SEPARATOR "test_dir", -1, NULL) == FALSE); +#endif + P_TEST_CHECK (p_dir_remove (NULL, NULL) == FALSE); + P_TEST_CHECK (p_dir_remove ("." P_DIR_SEPARATOR "pdir_test_dir_new", NULL) == FALSE); + P_TEST_CHECK (p_dir_is_exists (NULL) == FALSE); + P_TEST_CHECK (p_dir_is_exists ("." P_DIR_SEPARATOR "pdir_test_dir_new") == FALSE); + P_TEST_CHECK (p_dir_get_path (NULL) == NULL); + P_TEST_CHECK (p_dir_get_next_entry (NULL, NULL) == NULL); + P_TEST_CHECK (p_dir_rewind (NULL, NULL) == FALSE); + + p_dir_entry_free (NULL); + p_dir_free (NULL); + + /* Cleanup previous run */ + p_dir_remove (PDIR_TEST_DIR_IN, NULL); + p_dir_remove (PDIR_TEST_DIR, NULL); + + P_TEST_REQUIRE (p_dir_create (PDIR_TEST_DIR, 0777, NULL) == TRUE); + P_TEST_REQUIRE (p_dir_create (PDIR_TEST_DIR, 0777, NULL) == TRUE); + P_TEST_REQUIRE (p_dir_create (PDIR_TEST_DIR_IN, 0777, NULL) == TRUE); + P_TEST_REQUIRE (p_dir_create (PDIR_TEST_DIR_IN, 0777, NULL) == TRUE); + + FILE *file = fopen (PDIR_TEST_FILE, "w"); + P_TEST_REQUIRE (file != NULL); + P_TEST_REQUIRE (p_file_is_exists (PDIR_TEST_FILE) == TRUE); + + fprintf (file, "This is a test file string\n"); + + P_TEST_CHECK (fclose (file) == 0); + + P_TEST_CHECK (p_dir_is_exists (PDIR_TEST_DIR) == TRUE); + P_TEST_CHECK (p_dir_is_exists (PDIR_TEST_DIR_IN) == TRUE); + + PDir *dir = p_dir_new (PDIR_TEST_DIR"/", NULL); + + P_TEST_CHECK (dir != NULL); + + pint dir_count = 0; + pint file_count = 0; + pboolean has_entry_dir = FALSE; + pboolean has_entry_file = FALSE; + + PDirEntry *entry; + + while ((entry = p_dir_get_next_entry (dir, NULL)) != NULL) { + P_TEST_CHECK (entry->name != NULL); + + switch (entry->type) { + case P_DIR_ENTRY_TYPE_DIR: + ++dir_count; + break; + case P_DIR_ENTRY_TYPE_FILE: + ++file_count; + break; + case P_DIR_ENTRY_TYPE_OTHER: + default: + break; + } + + if (strcmp (entry->name, PDIR_ENTRY_DIR) == 0) + has_entry_dir = TRUE; + else if (strcmp (entry->name, PDIR_ENTRY_FILE) == 0) + has_entry_file = TRUE; + + p_dir_entry_free (entry); + } + + P_TEST_CHECK (dir_count > 0 && dir_count < 4); + P_TEST_CHECK (file_count == 1); + P_TEST_CHECK (has_entry_dir == TRUE); + P_TEST_CHECK (has_entry_file == TRUE); + + P_TEST_CHECK (p_dir_rewind (dir, NULL) == TRUE); + + pint dir_count_2 = 0; + pint file_count_2 = 0; + has_entry_dir = FALSE; + has_entry_file = FALSE; + + while ((entry = p_dir_get_next_entry (dir, NULL)) != NULL) { + P_TEST_CHECK (entry->name != NULL); + + switch (entry->type) { + case P_DIR_ENTRY_TYPE_DIR: + ++dir_count_2; + break; + case P_DIR_ENTRY_TYPE_FILE: + ++file_count_2; + break; + case P_DIR_ENTRY_TYPE_OTHER: + default: + break; + } + + if (strcmp (entry->name, PDIR_ENTRY_DIR) == 0) + has_entry_dir = TRUE; + else if (strcmp (entry->name, PDIR_ENTRY_FILE) == 0) + has_entry_file = TRUE; + + p_dir_entry_free (entry); + } + + P_TEST_CHECK (dir_count_2 > 0 && dir_count_2 < 4); + P_TEST_CHECK (file_count_2 == 1); + P_TEST_CHECK (has_entry_dir == TRUE); + P_TEST_CHECK (has_entry_file == TRUE); + + /* Compare two previous attempts */ + P_TEST_CHECK (dir_count == dir_count_2); + P_TEST_CHECK (file_count == file_count_2); + + /* Remove all stuff */ + P_TEST_CHECK (p_file_remove (PDIR_TEST_FILE, NULL) == TRUE); + P_TEST_CHECK (p_dir_remove (PDIR_TEST_DIR, NULL) == FALSE); + P_TEST_CHECK (p_dir_remove (PDIR_TEST_DIR_IN, NULL) == TRUE); + P_TEST_CHECK (p_dir_remove (PDIR_TEST_DIR, NULL) == TRUE); + + P_TEST_CHECK (p_dir_is_exists (PDIR_TEST_DIR_IN) == FALSE); + P_TEST_CHECK (p_dir_is_exists (PDIR_TEST_DIR) == FALSE); + + pchar *orig_path = p_dir_get_path (dir); + P_TEST_CHECK (strcmp (orig_path, PDIR_TEST_DIR"/") == 0); + p_free (orig_path); + + p_dir_free (dir); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_SUITE_BEGIN() +{ + P_TEST_SUITE_RUN_CASE (pdir_nomem_test); + P_TEST_SUITE_RUN_CASE (pdir_general_test); +} +P_TEST_SUITE_END() diff --git a/3rdparty/plibsys/tests/perror_test.cpp b/3rdparty/plibsys/tests/perror_test.cpp new file mode 100644 index 0000000..1db2f4d --- /dev/null +++ b/3rdparty/plibsys/tests/perror_test.cpp @@ -0,0 +1,236 @@ +/* + * The MIT License + * + * Copyright (C) 2016-2017 Alexander Saprykin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "plibsys.h" +#include "ptestmacros.h" + +#include + +P_TEST_MODULE_INIT (); + +#define PERROR_TEST_MESSAGE "PError test error message" +#define PERROR_TEST_MESSAGE_2 "Another PError test error message" + +extern "C" ppointer pmem_alloc (psize nbytes) +{ + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" ppointer pmem_realloc (ppointer block, psize nbytes) +{ + P_UNUSED (block); + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" void pmem_free (ppointer block) +{ + P_UNUSED (block); +} + +P_TEST_CASE_BEGIN (perror_nomem_test) +{ + p_libsys_init (); + + PError *error = p_error_new_literal (0, 0, NULL); + P_TEST_CHECK (error != NULL); + + PMemVTable vtable; + + vtable.free = pmem_free; + vtable.malloc = pmem_alloc; + vtable.realloc = pmem_realloc; + + P_TEST_CHECK (p_mem_set_vtable (&vtable) == TRUE); + + P_TEST_CHECK (p_error_new () == NULL); + P_TEST_CHECK (p_error_new_literal (0, 0, NULL) == NULL); + P_TEST_CHECK (p_error_copy (error) == NULL); + + p_mem_restore_vtable (); + + p_error_free (error); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (perror_invalid_test) +{ + p_libsys_init (); + + P_TEST_CHECK (p_error_get_message (NULL) == NULL); + P_TEST_CHECK (p_error_get_code (NULL) == 0); + P_TEST_CHECK (p_error_get_native_code (NULL) == 0); + P_TEST_CHECK (p_error_get_domain (NULL) == P_ERROR_DOMAIN_NONE); + P_TEST_CHECK (p_error_copy (NULL) == NULL); + + PError *error = (PError *) 0x1; + + p_error_set_code (NULL, 0); + p_error_set_native_code (NULL, 0); + p_error_set_message (NULL, NULL); + + p_error_set_error (NULL, 0, 0, NULL); + p_error_set_error_p (NULL, 0, 0, NULL); + + p_error_set_error_p (&error, 0, 0, NULL); + P_TEST_CHECK (error == (PError *) 0x1); + + p_error_clear (NULL); + p_error_free (NULL); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (perror_general_test) +{ + p_libsys_init (); + + /* Empty initialization test */ + PError *error = p_error_new (); + + P_TEST_CHECK (error != NULL); + P_TEST_CHECK (p_error_get_code (error) == 0); + P_TEST_CHECK (p_error_get_domain (error) == P_ERROR_DOMAIN_NONE); + P_TEST_CHECK (p_error_get_message (error) == NULL); + + PError *copy_error = p_error_copy (error); + + P_TEST_CHECK (copy_error != NULL); + P_TEST_CHECK (p_error_get_code (copy_error) == 0); + P_TEST_CHECK (p_error_get_domain (error) == P_ERROR_DOMAIN_NONE); + P_TEST_CHECK (p_error_get_message (copy_error) == NULL); + + p_error_free (copy_error); + copy_error = NULL; + + p_error_set_error (error, (pint) P_ERROR_DOMAIN_IO, -10, PERROR_TEST_MESSAGE); + + P_TEST_CHECK (p_error_get_code (error) == (pint) P_ERROR_DOMAIN_IO); + P_TEST_CHECK (p_error_get_native_code (error) == -10); + P_TEST_CHECK (p_error_get_domain (error) == P_ERROR_DOMAIN_IO); + P_TEST_CHECK (strcmp (p_error_get_message (error), PERROR_TEST_MESSAGE) == 0); + + /* Change internal data */ + p_error_set_code (error, (pint) P_ERROR_DOMAIN_IPC); + p_error_set_native_code (error, -20); + p_error_set_message (error, PERROR_TEST_MESSAGE_2); + + P_TEST_CHECK (p_error_get_code (error) == (pint) P_ERROR_DOMAIN_IPC); + P_TEST_CHECK (p_error_get_native_code (error) == -20); + P_TEST_CHECK (p_error_get_domain (error) == P_ERROR_DOMAIN_IPC); + P_TEST_CHECK (strcmp (p_error_get_message (error), PERROR_TEST_MESSAGE_2) == 0); + + /* Revert data back */ + p_error_set_code (error, 10); + p_error_set_native_code (error, -10); + p_error_set_message (error, PERROR_TEST_MESSAGE); + + copy_error = p_error_copy (error); + + P_TEST_CHECK (copy_error != NULL); + P_TEST_CHECK (p_error_get_code (copy_error) == 10); + P_TEST_CHECK (p_error_get_domain (error) == P_ERROR_DOMAIN_NONE); + P_TEST_CHECK (p_error_get_native_code (copy_error) == -10); + + P_TEST_CHECK (strcmp (p_error_get_message (copy_error), PERROR_TEST_MESSAGE) == 0); + + p_error_free (copy_error); + copy_error = NULL; + + p_error_set_error (error, 20, -20, PERROR_TEST_MESSAGE_2); + + P_TEST_CHECK (p_error_get_code (error) == 20); + P_TEST_CHECK (p_error_get_native_code (error) == -20); + P_TEST_CHECK (p_error_get_domain (error) == P_ERROR_DOMAIN_NONE); + P_TEST_CHECK (strcmp (p_error_get_message (error), PERROR_TEST_MESSAGE_2) == 0); + + p_error_clear (error); + + P_TEST_CHECK (p_error_get_code (error) == 0); + P_TEST_CHECK (p_error_get_native_code (error) == 0); + P_TEST_CHECK (p_error_get_domain (error) == P_ERROR_DOMAIN_NONE); + P_TEST_CHECK (p_error_get_message (error) == NULL); + + p_error_free (error); + error = NULL; + + /* Literal initialization test */ + error = p_error_new_literal (30, -30, PERROR_TEST_MESSAGE); + + P_TEST_CHECK (p_error_get_code (error) == 30); + P_TEST_CHECK (p_error_get_native_code (error) == -30); + P_TEST_CHECK (p_error_get_domain (error) == P_ERROR_DOMAIN_NONE); + P_TEST_CHECK (strcmp (p_error_get_message (error), PERROR_TEST_MESSAGE) == 0); + + copy_error = p_error_copy (error); + + P_TEST_CHECK (copy_error != NULL); + P_TEST_CHECK (p_error_get_code (copy_error) == 30); + P_TEST_CHECK (p_error_get_native_code (copy_error) == -30); + P_TEST_CHECK (p_error_get_domain (error) == P_ERROR_DOMAIN_NONE); + P_TEST_CHECK (strcmp (p_error_get_message (copy_error), PERROR_TEST_MESSAGE) == 0); + + p_error_free (copy_error); + p_error_free (error); + + /* Through the double pointer */ + error = NULL; + p_error_set_error_p (&error, 10, -10, PERROR_TEST_MESSAGE); + + P_TEST_CHECK (p_error_get_code (error) == 10); + P_TEST_CHECK (p_error_get_native_code (error) == -10); + P_TEST_CHECK (p_error_get_domain (error) == P_ERROR_DOMAIN_NONE); + P_TEST_CHECK (strcmp (p_error_get_message (error), PERROR_TEST_MESSAGE) == 0); + + p_error_free (error); + + /* System codes */ + p_error_set_last_system (10); + P_TEST_CHECK (p_error_get_last_system () == 10); + p_error_set_last_system (0); + P_TEST_CHECK (p_error_get_last_system () == 0); + +#ifndef P_OS_OS2 + p_error_set_last_net (20); + P_TEST_CHECK (p_error_get_last_net () == 20); + p_error_set_last_net (0); + P_TEST_CHECK (p_error_get_last_net () == 0); +#endif + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_SUITE_BEGIN() +{ + P_TEST_SUITE_RUN_CASE (perror_nomem_test); + P_TEST_SUITE_RUN_CASE (perror_invalid_test); + P_TEST_SUITE_RUN_CASE (perror_general_test); +} +P_TEST_SUITE_END() diff --git a/3rdparty/plibsys/tests/pfile_test.cpp b/3rdparty/plibsys/tests/pfile_test.cpp new file mode 100644 index 0000000..7f3c643 --- /dev/null +++ b/3rdparty/plibsys/tests/pfile_test.cpp @@ -0,0 +1,61 @@ +/* + * The MIT License + * + * Copyright (C) 2013-2017 Alexander Saprykin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "plibsys.h" +#include "ptestmacros.h" + +#include + +P_TEST_MODULE_INIT (); + +#define PFILE_TEST_FILE "." P_DIR_SEPARATOR "pfile_test_file.txt" + +P_TEST_CASE_BEGIN (pfile_general_test) +{ + p_libsys_init (); + + P_TEST_CHECK (p_file_remove (NULL, NULL) == FALSE); + + P_TEST_CHECK (p_file_is_exists (PFILE_TEST_FILE) == FALSE); + P_TEST_CHECK (p_file_remove ("." P_DIR_SEPARATOR" pfile_test_file_remove.txt", NULL) == FALSE); + + FILE *file = fopen (PFILE_TEST_FILE, "w"); + P_TEST_REQUIRE (file != NULL); + P_TEST_CHECK (p_file_is_exists (PFILE_TEST_FILE) == TRUE); + + fprintf (file, "This is a test file string\n"); + + P_TEST_CHECK (fclose (file) == 0); + P_TEST_CHECK (p_file_remove (PFILE_TEST_FILE, NULL) == TRUE); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_SUITE_BEGIN() +{ + P_TEST_SUITE_RUN_CASE (pfile_general_test); +} +P_TEST_SUITE_END() diff --git a/3rdparty/plibsys/tests/phashtable_test.cpp b/3rdparty/plibsys/tests/phashtable_test.cpp new file mode 100644 index 0000000..2975fde --- /dev/null +++ b/3rdparty/plibsys/tests/phashtable_test.cpp @@ -0,0 +1,320 @@ +/* + * The MIT License + * + * Copyright (C) 2013-2017 Alexander Saprykin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "plibsys.h" +#include "ptestmacros.h" + +#include +#include + +P_TEST_MODULE_INIT (); + +#define PHASHTABLE_STRESS_COUNT 10000 + +extern "C" ppointer pmem_alloc (psize nbytes) +{ + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" ppointer pmem_realloc (ppointer block, psize nbytes) +{ + P_UNUSED (block); + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" void pmem_free (ppointer block) +{ + P_UNUSED (block); +} + +static int test_hash_table_values (pconstpointer a, pconstpointer b) +{ + return a > b ? 0 : (a < b ? -1 : 1); +} + +P_TEST_CASE_BEGIN (phashtable_nomem_test) +{ + p_libsys_init (); + + PHashTable *table = p_hash_table_new (); + P_TEST_CHECK (table != NULL); + + PMemVTable vtable; + + vtable.free = pmem_free; + vtable.malloc = pmem_alloc; + vtable.realloc = pmem_realloc; + + P_TEST_CHECK (p_mem_set_vtable (&vtable) == TRUE); + + P_TEST_CHECK (p_hash_table_new () == NULL); + p_hash_table_insert (table, PINT_TO_POINTER (1), PINT_TO_POINTER (10)); + P_TEST_CHECK (p_hash_table_keys (table) == NULL); + P_TEST_CHECK (p_hash_table_values (table) == NULL); + + p_mem_restore_vtable (); + + p_hash_table_free (table); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (phashtable_invalid_test) +{ + p_libsys_init (); + + P_TEST_CHECK (p_hash_table_keys (NULL) == NULL); + P_TEST_CHECK (p_hash_table_values (NULL) == NULL); + P_TEST_CHECK (p_hash_table_lookup (NULL, NULL) == NULL); + P_TEST_CHECK (p_hash_table_lookup_by_value (NULL, NULL, NULL) == NULL); + p_hash_table_insert (NULL, NULL, NULL); + p_hash_table_remove (NULL, NULL); + p_hash_table_free (NULL); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (phashtable_general_test) +{ + PHashTable *table = NULL; + PList *list = NULL; + + p_libsys_init (); + + table = p_hash_table_new (); + P_TEST_REQUIRE (table != NULL); + + /* Test for NULL key */ + p_hash_table_insert (table, NULL, PINT_TO_POINTER (1)); + list = p_hash_table_keys (table); + P_TEST_REQUIRE (p_list_length (list) == 1); + P_TEST_REQUIRE (PPOINTER_TO_INT (list->data) == 0); + p_list_free (list); + list = p_hash_table_values (table); + P_TEST_REQUIRE (p_list_length (list) == 1); + P_TEST_REQUIRE (PPOINTER_TO_INT (list->data) == 1); + p_list_free (list); + p_hash_table_remove (table, NULL); + + /* Test for insertion */ + p_hash_table_insert (table, PINT_TO_POINTER (1), PINT_TO_POINTER (10)); + list = p_hash_table_values (table); + P_TEST_REQUIRE (list != NULL); + P_TEST_REQUIRE (p_list_length (list) == 1); + P_TEST_REQUIRE (PPOINTER_TO_INT (list->data) == 10); + p_list_free (list); + list = p_hash_table_keys (table); + P_TEST_REQUIRE (list != NULL); + P_TEST_REQUIRE (p_list_length (list) == 1); + P_TEST_REQUIRE (PPOINTER_TO_INT (list->data) == 1); + p_list_free (list); + + /* False remove */ + p_hash_table_remove (table, PINT_TO_POINTER (2)); + list = p_hash_table_values (table); + P_TEST_REQUIRE (list != NULL); + P_TEST_REQUIRE (p_list_length (list) == 1); + P_TEST_REQUIRE (PPOINTER_TO_INT (list->data) == 10); + p_list_free (list); + list = p_hash_table_keys (table); + P_TEST_REQUIRE (list != NULL); + P_TEST_REQUIRE (p_list_length (list) == 1); + P_TEST_REQUIRE (PPOINTER_TO_INT (list->data) == 1); + p_list_free (list); + + /* Replace existing value */ + p_hash_table_insert (table, PINT_TO_POINTER (1), PINT_TO_POINTER (15)); + list = p_hash_table_values (table); + P_TEST_REQUIRE (list != NULL); + P_TEST_REQUIRE (p_list_length (list) == 1); + P_TEST_REQUIRE (PPOINTER_TO_INT (list->data) == 15); + p_list_free (list); + list = p_hash_table_keys (table); + P_TEST_REQUIRE (list != NULL); + P_TEST_REQUIRE (p_list_length (list) == 1); + P_TEST_REQUIRE (PPOINTER_TO_INT (list->data) == 1); + p_list_free (list); + + /* More insertion */ + p_hash_table_insert (table, PINT_TO_POINTER (2), PINT_TO_POINTER (20)); + p_hash_table_insert (table, PINT_TO_POINTER (3), PINT_TO_POINTER (30)); + + list = p_hash_table_values (table); + P_TEST_REQUIRE (list != NULL); + P_TEST_REQUIRE (p_list_length (list) == 3); + P_TEST_REQUIRE (PPOINTER_TO_INT (list->data) + + PPOINTER_TO_INT (list->next->data) + + PPOINTER_TO_INT (list->next->next->data) == 65); + p_list_free (list); + list = p_hash_table_keys (table); + P_TEST_REQUIRE (list != NULL); + P_TEST_REQUIRE (p_list_length (list) == 3); + P_TEST_REQUIRE (PPOINTER_TO_INT (list->data) + + PPOINTER_TO_INT (list->next->data) + + PPOINTER_TO_INT (list->next->next->data) == 6); + p_list_free (list); + + P_TEST_CHECK (PPOINTER_TO_INT (p_hash_table_lookup (table, PINT_TO_POINTER (1))) == 15); + P_TEST_CHECK (PPOINTER_TO_INT (p_hash_table_lookup (table, PINT_TO_POINTER (2))) == 20); + P_TEST_CHECK (PPOINTER_TO_INT (p_hash_table_lookup (table, PINT_TO_POINTER (3))) == 30); + P_TEST_CHECK (p_hash_table_lookup (table, PINT_TO_POINTER (4)) == (ppointer) -1); + p_hash_table_insert (table, PINT_TO_POINTER (22), PINT_TO_POINTER (20)); + + list = p_hash_table_lookup_by_value (table, + PINT_TO_POINTER (19), + (PCompareFunc) test_hash_table_values); + P_TEST_REQUIRE (list != NULL); + P_TEST_REQUIRE (p_list_length (list) == 3); + P_TEST_REQUIRE (PPOINTER_TO_INT (list->data) + + PPOINTER_TO_INT (list->next->data) + + PPOINTER_TO_INT (list->next->next->data) == 27); + p_list_free (list); + + list = p_hash_table_lookup_by_value (table, + PINT_TO_POINTER (20), + NULL); + P_TEST_REQUIRE (list != NULL); + P_TEST_REQUIRE (p_list_length (list) == 2); + P_TEST_REQUIRE (PPOINTER_TO_INT (list->data) + + PPOINTER_TO_INT (list->next->data) == 24); + p_list_free (list); + + P_TEST_REQUIRE (PPOINTER_TO_INT (p_hash_table_lookup (table, PINT_TO_POINTER (22))) == 20); + + p_hash_table_remove (table, PINT_TO_POINTER (1)); + p_hash_table_remove (table, PINT_TO_POINTER (2)); + + list = p_hash_table_keys (table); + P_TEST_REQUIRE (p_list_length (list) == 2); + p_list_free (list); + list = p_hash_table_values (table); + P_TEST_REQUIRE (p_list_length (list) == 2); + p_list_free (list); + + p_hash_table_remove (table, PINT_TO_POINTER (3)); + + list = p_hash_table_keys (table); + P_TEST_REQUIRE (p_list_length (list) == 1); + P_TEST_REQUIRE (PPOINTER_TO_INT (list->data) == 22); + p_list_free (list); + list = p_hash_table_values (table); + P_TEST_REQUIRE (p_list_length (list) == 1); + P_TEST_REQUIRE (PPOINTER_TO_INT (list->data) == 20); + p_list_free (list); + + p_hash_table_remove (table, PINT_TO_POINTER (22)); + + P_TEST_REQUIRE (p_hash_table_keys (table) == NULL); + P_TEST_REQUIRE (p_hash_table_values (table) == NULL); + + p_hash_table_free (table); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (phashtable_stress_test) +{ + p_libsys_init (); + + PHashTable *table = p_hash_table_new (); + P_TEST_REQUIRE (table != NULL); + + srand ((unsigned int) time (NULL)); + + int counter = 0; + + pint *keys = (pint *) p_malloc0 (PHASHTABLE_STRESS_COUNT * sizeof (pint)); + pint *values = (pint *) p_malloc0 (PHASHTABLE_STRESS_COUNT * sizeof (pint)); + + P_TEST_REQUIRE (keys != NULL); + P_TEST_REQUIRE (values != NULL); + + while (counter != PHASHTABLE_STRESS_COUNT) { + pint rand_number = rand (); + + if (p_hash_table_lookup (table, PINT_TO_POINTER (rand_number)) != (ppointer) (-1)) + continue; + + keys[counter] = rand_number; + values[counter] = rand () + 1; + + p_hash_table_remove (table, PINT_TO_POINTER (keys[counter])); + p_hash_table_insert (table, PINT_TO_POINTER (keys[counter]), PINT_TO_POINTER (values[counter])); + + ++counter; + } + + for (int i = 0; i < PHASHTABLE_STRESS_COUNT; ++i) { + P_TEST_CHECK (p_hash_table_lookup (table, PINT_TO_POINTER (keys[i])) == + PINT_TO_POINTER (values[i])); + + p_hash_table_remove (table, PINT_TO_POINTER (keys[i])); + P_TEST_CHECK (p_hash_table_lookup (table, PINT_TO_POINTER (keys[i])) == (ppointer) (-1)); + } + + P_TEST_CHECK (p_hash_table_keys (table) == NULL); + P_TEST_CHECK (p_hash_table_values (table) == NULL); + + p_free (keys); + p_free (values); + + p_hash_table_free (table); + + /* Try to free at once */ + table = p_hash_table_new (); + P_TEST_REQUIRE (table != NULL); + + counter = 0; + + while (counter != PHASHTABLE_STRESS_COUNT) { + pint rand_number = rand (); + + if (p_hash_table_lookup (table, PINT_TO_POINTER (rand_number)) != (ppointer) (-1)) + continue; + + p_hash_table_insert (table, PINT_TO_POINTER (rand_number), PINT_TO_POINTER (rand () + 1)); + + ++counter; + } + + p_hash_table_free (table); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_SUITE_BEGIN() +{ + P_TEST_SUITE_RUN_CASE (phashtable_nomem_test); + P_TEST_SUITE_RUN_CASE (phashtable_invalid_test); + P_TEST_SUITE_RUN_CASE (phashtable_general_test); + P_TEST_SUITE_RUN_CASE (phashtable_stress_test); +} +P_TEST_SUITE_END() diff --git a/3rdparty/plibsys/tests/pinifile_test.cpp b/3rdparty/plibsys/tests/pinifile_test.cpp new file mode 100644 index 0000000..3d4d4c8 --- /dev/null +++ b/3rdparty/plibsys/tests/pinifile_test.cpp @@ -0,0 +1,357 @@ +/* + * The MIT License + * + * Copyright (C) 2013-2017 Alexander Saprykin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "plibsys.h" +#include "ptestmacros.h" + +#include +#include +#include +#include + +P_TEST_MODULE_INIT (); + +#define PINIFILE_STRESS_LINE 2048 +#define PINIFILE_MAX_LINE 1024 + +extern "C" ppointer pmem_alloc (psize nbytes) +{ + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" ppointer pmem_realloc (ppointer block, psize nbytes) +{ + P_UNUSED (block); + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" void pmem_free (ppointer block) +{ + P_UNUSED (block); +} + +static bool create_test_ini_file (bool last_empty_section) +{ + FILE *file = fopen ("." P_DIR_SEPARATOR "p_ini_test_file.ini", "w"); + + if (file == NULL) + return false; + + pchar *buf = (pchar *) p_malloc0 (PINIFILE_STRESS_LINE + 1); + + for (int i = 0; i < PINIFILE_STRESS_LINE; ++i) + buf[i] = (pchar) (97 + i % 20); + + /* Empty section */ + fprintf (file, "[empty_section]\n"); + + /* Numeric section */ + fprintf (file, "[numeric_section]\n"); + fprintf (file, "int_parameter_1 = 4\n"); + fprintf (file, "int_parameter_2 = 5 ;This is a comment\n"); + fprintf (file, "int_parameter_3 = 6 #This is another type of a comment\n"); + fprintf (file, "# Whole line is a comment\n"); + fprintf (file, "; Yet another comment line\n"); + fprintf (file, "float_parameter_1 = 3.24\n"); + fprintf (file, "float_parameter_2 = 0.15\n"); + + /* String section */ + fprintf (file, "[string_section]\n"); + fprintf (file, "string_parameter_1 = Test string\n"); + fprintf (file, "string_parameter_2 = \"Test string with #'\"\n"); + fprintf (file, "string_parameter_3 = \n"); + fprintf (file, "string_parameter_4 = 12345 ;Comment\n"); + fprintf (file, "string_parameter_4 = 54321\n"); + fprintf (file, "string_parameter_5 = 'Test string'\n"); + fprintf (file, "string_parameter_6 = %s\n", buf); + fprintf (file, "string_parameter_7 = ''\n"); + fprintf (file, "string_parameter_8 = \"\"\n"); + fprintf (file, "%s = stress line\n", buf); + + /* Boolean section */ + fprintf (file, "[boolean_section]\n"); + fprintf (file, "boolean_parameter_1 = TRUE ;True value\n"); + fprintf (file, "boolean_parameter_2 = 0 ;False value\n"); + fprintf (file, "boolean_parameter_3 = false ;False value\n"); + fprintf (file, "boolean_parameter_4 = 1 ;True value\n"); + + /* List section */ + fprintf (file, "[list_section]\n"); + fprintf (file, "list_parameter_1 = {1\t2\t5\t10} ;First list\n"); + fprintf (file, "list_parameter_2 = {2.0 3.0 5.0} #Second list\n"); + fprintf (file, "list_parameter_3 = {true FALSE 1} #Last list\n"); + + /* Empty section */ + if (last_empty_section) + fprintf (file, "[empty_section_2]\n"); + + p_free (buf); + + return fclose (file) == 0; +} + +P_TEST_CASE_BEGIN (pinifile_nomem_test) +{ + p_libsys_init (); + + P_TEST_REQUIRE (create_test_ini_file (false)); + + PIniFile *ini = p_ini_file_new ("." P_DIR_SEPARATOR "p_ini_test_file.ini"); + P_TEST_CHECK (ini != NULL); + + PMemVTable vtable; + + vtable.free = pmem_free; + vtable.malloc = pmem_alloc; + vtable.realloc = pmem_realloc; + + P_TEST_CHECK (p_mem_set_vtable (&vtable) == TRUE); + + P_TEST_CHECK (p_ini_file_new ("." P_DIR_SEPARATOR "p_ini_test_file.ini") == NULL); + P_TEST_CHECK (p_ini_file_parse (ini, NULL) == TRUE); + P_TEST_CHECK (p_ini_file_sections (ini) == NULL); + + p_mem_restore_vtable (); + + p_ini_file_free (ini); + + ini = p_ini_file_new ("." P_DIR_SEPARATOR "p_ini_test_file.ini"); + P_TEST_CHECK (ini != NULL); + + P_TEST_CHECK (p_ini_file_parse (ini, NULL) == TRUE); + PList *section_list = p_ini_file_sections (ini); + P_TEST_CHECK (section_list != NULL); + P_TEST_CHECK (p_list_length (section_list) == 4); + + p_list_foreach (section_list, (PFunc) p_free, NULL); + p_list_free (section_list); + p_ini_file_free (ini); + + P_TEST_CHECK (p_file_remove ("." P_DIR_SEPARATOR "p_ini_test_file.ini", NULL) == TRUE); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (pinifile_bad_input_test) +{ + PIniFile *ini = NULL; + + p_libsys_init (); + + p_ini_file_free (ini); + P_TEST_CHECK (p_ini_file_new (NULL) == NULL); + P_TEST_CHECK (p_ini_file_parse (ini, NULL) == FALSE); + P_TEST_CHECK (p_ini_file_is_parsed (ini) == FALSE); + P_TEST_CHECK (p_ini_file_is_key_exists (ini, "string_section", "string_paramter_1") == FALSE); + P_TEST_CHECK (p_ini_file_sections (ini) == NULL); + P_TEST_CHECK (p_ini_file_keys (ini, "string_section") == NULL); + P_TEST_CHECK (p_ini_file_parameter_boolean (ini, "boolean_section", "boolean_parameter_1", FALSE) == FALSE); + P_TEST_CHECK_CLOSE (p_ini_file_parameter_double (ini, "numeric_section", "float_parameter_1", 1.0), 1.0, 0.0001); + P_TEST_CHECK (p_ini_file_parameter_int (ini, "numeric_section", "int_parameter_1", 0) == 0); + P_TEST_CHECK (p_ini_file_parameter_list (ini, "list_section", "list_parameter_1") == NULL); + P_TEST_CHECK (p_ini_file_parameter_string (ini, "string_section", "string_parameter_1", NULL) == NULL); + + ini = p_ini_file_new ("./bad_file_path/fake.ini"); + P_TEST_CHECK (ini != NULL); + P_TEST_CHECK (p_ini_file_parse (ini, NULL) == FALSE); + p_ini_file_free (ini); + + P_TEST_REQUIRE (create_test_ini_file (true)); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (pinifile_read_test) +{ + p_libsys_init (); + + PIniFile *ini = p_ini_file_new ("." P_DIR_SEPARATOR "p_ini_test_file.ini"); + P_TEST_REQUIRE (ini != NULL); + P_TEST_CHECK (p_ini_file_is_parsed (ini) == FALSE); + + P_TEST_REQUIRE (p_ini_file_parse (ini, NULL) == TRUE); + P_TEST_CHECK (p_ini_file_is_parsed (ini) == TRUE); + P_TEST_REQUIRE (p_ini_file_parse (ini, NULL) == TRUE); + P_TEST_CHECK (p_ini_file_is_parsed (ini) == TRUE); + + /* Test list of sections */ + PList *list = p_ini_file_sections (ini); + P_TEST_CHECK (list != NULL); + P_TEST_CHECK (p_list_length (list) == 4); + + p_list_foreach (list, (PFunc) p_free, NULL); + p_list_free (list); + + /* Test empty section */ + list = p_ini_file_keys (ini, "empty_section"); + P_TEST_CHECK (list == NULL); + + /* Test numeric section */ + list = p_ini_file_keys (ini, "numeric_section"); + P_TEST_CHECK (p_list_length (list) == 5); + p_list_foreach (list, (PFunc) p_free, NULL); + p_list_free (list); + + P_TEST_CHECK (p_ini_file_parameter_list (ini, "numeric_section", "int_parameter_1") == NULL); + P_TEST_CHECK (p_ini_file_parameter_int (ini, "numeric_section", "int_parameter_1", -1) == 4); + P_TEST_CHECK (p_ini_file_parameter_int (ini, "numeric_section", "int_parameter_2", -1) == 5); + P_TEST_CHECK (p_ini_file_parameter_int (ini, "numeric_section", "int_parameter_3", -1) == 6); + P_TEST_CHECK (p_ini_file_parameter_int (ini, "numeric_section", "int_parameter_def", 10) == 10); + P_TEST_CHECK_CLOSE (p_ini_file_parameter_double (ini, "numeric_section", "float_parameter_1", -1.0), 3.24, 0.0001); + P_TEST_CHECK_CLOSE (p_ini_file_parameter_double (ini, "numeric_section", "float_parameter_2", -1.0), 0.15, 0.0001); + P_TEST_CHECK_CLOSE (p_ini_file_parameter_double (ini, "numeric_section_no", "float_parameter_def", 10.0), 10.0, 0.0001); + P_TEST_CHECK (p_ini_file_is_key_exists (ini, "numeric_section", "int_parameter_1") == TRUE); + P_TEST_CHECK (p_ini_file_is_key_exists (ini, "numeric_section", "float_parameter_1") == TRUE); + P_TEST_CHECK (p_ini_file_is_key_exists (ini, "numeric_section_false", "float_parameter_1") == FALSE); + + /* Test string section */ + list = p_ini_file_keys (ini, "string_section"); + P_TEST_CHECK (p_list_length (list) == 8); + p_list_foreach (list, (PFunc) p_free, NULL); + p_list_free (list); + + pchar *str = p_ini_file_parameter_string (ini, "string_section", "string_parameter_1", NULL); + P_TEST_REQUIRE (str != NULL); + P_TEST_CHECK (strcmp (str, "Test string") == 0); + p_free (str); + + str = p_ini_file_parameter_string (ini, "string_section", "string_parameter_2", NULL); + P_TEST_REQUIRE (str != NULL); + P_TEST_CHECK (strcmp (str, "Test string with #'") == 0); + p_free (str); + + str = p_ini_file_parameter_string (ini, "string_section", "string_parameter_3", NULL); + P_TEST_REQUIRE (str == NULL); + P_TEST_CHECK (p_ini_file_is_key_exists (ini, "string_section", "string_parameter_3") == FALSE); + + str = p_ini_file_parameter_string (ini, "string_section", "string_parameter_4", NULL); + P_TEST_REQUIRE (str != NULL); + P_TEST_CHECK (strcmp (str, "54321") == 0); + p_free (str); + + str = p_ini_file_parameter_string (ini, "string_section", "string_parameter_5", NULL); + P_TEST_REQUIRE (str != NULL); + P_TEST_CHECK (strcmp (str, "Test string") == 0); + p_free (str); + + str = p_ini_file_parameter_string (ini, "string_section", "string_parameter_6", NULL); + P_TEST_REQUIRE (str != NULL); + P_TEST_CHECK (strlen (str) > 0 && strlen (str) < PINIFILE_MAX_LINE); + p_free (str); + + str = p_ini_file_parameter_string (ini, "string_section", "string_parameter_7", NULL); + P_TEST_REQUIRE (str != NULL); + P_TEST_CHECK (strcmp (str, "") == 0); + p_free (str); + + str = p_ini_file_parameter_string (ini, "string_section", "string_parameter_8", NULL); + P_TEST_REQUIRE (str != NULL); + P_TEST_CHECK (strcmp (str, "") == 0); + p_free (str); + + str = p_ini_file_parameter_string (ini, "string_section", "string_parameter_def", "default_value"); + P_TEST_REQUIRE (str != NULL); + P_TEST_CHECK (strcmp (str, "default_value") == 0); + p_free (str); + + /* Test boolean section */ + list = p_ini_file_keys (ini, "boolean_section"); + P_TEST_CHECK (p_list_length (list) == 4); + p_list_foreach (list, (PFunc) p_free, NULL); + p_list_free (list); + + P_TEST_CHECK (p_ini_file_parameter_boolean (ini, "boolean_section", "boolean_parameter_1", FALSE) == TRUE); + P_TEST_CHECK (p_ini_file_parameter_boolean (ini, "boolean_section", "boolean_parameter_2", TRUE) == FALSE); + P_TEST_CHECK (p_ini_file_parameter_boolean (ini, "boolean_section", "boolean_parameter_3", TRUE) == FALSE); + P_TEST_CHECK (p_ini_file_parameter_boolean (ini, "boolean_section", "boolean_parameter_4", FALSE) == TRUE); + P_TEST_CHECK (p_ini_file_parameter_boolean (ini, "boolean_section", "boolean_section_def", TRUE) == TRUE); + + /* Test list section */ + list = p_ini_file_keys (ini, "list_section"); + P_TEST_CHECK (p_list_length (list) == 3); + p_list_foreach (list, (PFunc) p_free, NULL); + p_list_free (list); + + /* -- First list parameter */ + PList *list_val = p_ini_file_parameter_list (ini, "list_section", "list_parameter_1"); + P_TEST_CHECK (list_val != NULL); + P_TEST_CHECK (p_list_length (list_val) == 4); + + pint int_sum = 0; + for (PList *iter = list_val; iter != NULL; iter = iter->next) + int_sum += atoi ((const pchar *) (iter->data)); + + P_TEST_CHECK (int_sum == 18); + p_list_foreach (list_val, (PFunc) p_free, NULL); + p_list_free (list_val); + + /* -- Second list parameter */ + list_val = p_ini_file_parameter_list (ini, "list_section", "list_parameter_2"); + P_TEST_CHECK (list_val != NULL); + P_TEST_CHECK (p_list_length (list_val) == 3); + + double flt_sum = 0; + for (PList *iter = list_val; iter != NULL; iter = iter->next) + flt_sum += atof ((const pchar *) (iter->data)); + + P_TEST_CHECK_CLOSE (flt_sum, 10.0, 0.0001); + p_list_foreach (list_val, (PFunc) p_free, NULL); + p_list_free (list_val); + + /* -- Third list parameter */ + list_val = p_ini_file_parameter_list (ini, "list_section", "list_parameter_3"); + P_TEST_CHECK (list_val != NULL); + P_TEST_CHECK (p_list_length (list_val) == 3); + + pboolean bool_sum = TRUE; + for (PList *iter = list_val; iter != NULL; iter = iter->next) + bool_sum = bool_sum && atoi ((const pchar *) (iter->data)); + + P_TEST_CHECK (bool_sum == FALSE); + p_list_foreach (list_val, (PFunc) p_free, NULL); + p_list_free (list_val); + + /* -- False list parameter */ + P_TEST_CHECK (p_ini_file_parameter_list (ini, "list_section_no", "list_parameter_def") == NULL); + + p_ini_file_free (ini); + + P_TEST_CHECK (p_file_remove ("." P_DIR_SEPARATOR "p_ini_test_file.ini", NULL) == TRUE); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_SUITE_BEGIN() +{ + P_TEST_SUITE_RUN_CASE (pinifile_nomem_test); + P_TEST_SUITE_RUN_CASE (pinifile_bad_input_test); + P_TEST_SUITE_RUN_CASE (pinifile_read_test); +} +P_TEST_SUITE_END() diff --git a/3rdparty/plibsys/tests/plibraryloader_test.cpp b/3rdparty/plibsys/tests/plibraryloader_test.cpp new file mode 100644 index 0000000..52ce1b0 --- /dev/null +++ b/3rdparty/plibsys/tests/plibraryloader_test.cpp @@ -0,0 +1,179 @@ +/* + * The MIT License + * + * Copyright (C) 2015-2017 Alexander Saprykin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "plibsys.h" +#include "ptestmacros.h" + +#include + +P_TEST_MODULE_INIT (); + +static int g_argc = 0; +static char **g_argv = NULL; + +extern "C" ppointer pmem_alloc (psize nbytes) +{ + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" ppointer pmem_realloc (ppointer block, psize nbytes) +{ + P_UNUSED (block); + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" void pmem_free (ppointer block) +{ + P_UNUSED (block); +} + +P_TEST_CASE_BEGIN (plibraryloader_nomem_test) +{ + p_libsys_init (); + + if (P_UNLIKELY (p_library_loader_is_ref_counted () == FALSE)) { + p_libsys_shutdown (); + P_TEST_CASE_RETURN (); + } + + /* We assume that 3rd argument is ourself library path */ + P_TEST_REQUIRE (g_argc > 1); + + /* Cleanup from previous run */ + p_file_remove ("." P_DIR_SEPARATOR "p_empty_file.txt", NULL); + + FILE *file = fopen ("." P_DIR_SEPARATOR "p_empty_file.txt", "w"); + P_TEST_CHECK (file != NULL); + P_TEST_CHECK (fclose (file) == 0); + + PMemVTable vtable; + + vtable.free = pmem_free; + vtable.malloc = pmem_alloc; + vtable.realloc = pmem_realloc; + + P_TEST_CHECK (p_mem_set_vtable (&vtable) == TRUE); + +#ifdef P_OS_WIN + SetErrorMode (SEM_FAILCRITICALERRORS); +#endif + + P_TEST_CHECK (p_library_loader_new ("." P_DIR_SEPARATOR "p_empty_file.txt") == NULL); + P_TEST_CHECK (p_library_loader_new (g_argv[g_argc - 1]) == NULL); + +#ifdef P_OS_WIN + SetErrorMode (0); +#endif + + p_mem_restore_vtable (); + + P_TEST_CHECK (p_file_remove ("." P_DIR_SEPARATOR "p_empty_file.txt", NULL) == TRUE); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (plibraryloader_general_test) +{ + PLibraryLoader *loader; + pchar *err_msg; + void (*shutdown_func) (void); + + p_libsys_init (); + + /* We assume that 3rd argument is ourself library path */ + P_TEST_REQUIRE (g_argc > 1); + + /* Invalid usage */ + P_TEST_CHECK (p_library_loader_new (NULL) == NULL); + P_TEST_CHECK (p_library_loader_new ("./unexistent_file.nofile") == NULL); + P_TEST_CHECK (p_library_loader_get_symbol (NULL, NULL) == NULL); + P_TEST_CHECK (p_library_loader_get_symbol (NULL, "unexistent_symbol") == NULL); + + p_library_loader_free (NULL); + + /* General tests */ + + /* At least not on HP-UX it should be true */ +#if !defined (P_OS_HPUX) + P_TEST_CHECK (p_library_loader_is_ref_counted () == TRUE); +#else + p_library_loader_is_ref_counted (); +#endif + + err_msg = p_library_loader_get_last_error (NULL); + p_free (err_msg); + + if (P_UNLIKELY (p_library_loader_is_ref_counted () == FALSE)) { + p_libsys_shutdown (); + P_TEST_CASE_RETURN (); + } + + loader = p_library_loader_new (g_argv[g_argc - 1]); + P_TEST_REQUIRE (loader != NULL); + + P_TEST_CHECK (p_library_loader_get_symbol (loader, "there_is_no_such_a_symbol") == (PFuncAddr) NULL); + + err_msg = p_library_loader_get_last_error (loader); + P_TEST_CHECK (err_msg != NULL); + p_free (err_msg); + + shutdown_func = (void (*) (void)) p_library_loader_get_symbol (loader, "p_libsys_shutdown"); + + if (shutdown_func == NULL) + shutdown_func = (void (*) (void)) p_library_loader_get_symbol (loader, "_p_libsys_shutdown"); + + /* For Watcom C */ + + if (shutdown_func == NULL) + shutdown_func = (void (*) (void)) p_library_loader_get_symbol (loader, "p_libsys_shutdown_"); + + P_TEST_REQUIRE (shutdown_func != NULL); + + err_msg = p_library_loader_get_last_error (loader); + p_free (err_msg); + + p_library_loader_free (loader); + +#ifdef P_OS_BEOS + p_libsys_shutdown (); +#else + /* We have already loaded reference to ourself library, it's OK */ + shutdown_func (); +#endif +} +P_TEST_CASE_END () + +P_TEST_SUITE_ARGS_BEGIN() +{ + g_argc = argc; + g_argv = argv; + + P_TEST_SUITE_RUN_CASE (plibraryloader_nomem_test); + P_TEST_SUITE_RUN_CASE (plibraryloader_general_test); +} +P_TEST_SUITE_END() diff --git a/3rdparty/plibsys/tests/plist_test.cpp b/3rdparty/plibsys/tests/plist_test.cpp new file mode 100644 index 0000000..87933ae --- /dev/null +++ b/3rdparty/plibsys/tests/plist_test.cpp @@ -0,0 +1,200 @@ +/* + * The MIT License + * + * Copyright (C) 2013-2017 Alexander Saprykin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "plibsys.h" +#include "ptestmacros.h" + +#include + +P_TEST_MODULE_INIT (); + +typedef struct _TestData { + pint test_array[3]; + pint index; +} TestData; + +extern "C" ppointer pmem_alloc (psize nbytes) +{ + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" ppointer pmem_realloc (ppointer block, psize nbytes) +{ + P_UNUSED (block); + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" void pmem_free (ppointer block) +{ + P_UNUSED (block); +} + +static void foreach_test_func (ppointer data, ppointer user_data) +{ + if (user_data == NULL) + return; + + TestData *test_data = (TestData *) user_data; + + if (test_data->index < 0 || test_data->index > 2) + return; + + test_data->test_array[test_data->index] = P_POINTER_TO_INT (data); + ++test_data->index; +} + +P_TEST_CASE_BEGIN (plist_nomem_test) +{ + p_libsys_init (); + + PMemVTable vtable; + + vtable.free = pmem_free; + vtable.malloc = pmem_alloc; + vtable.realloc = pmem_realloc; + + P_TEST_CHECK (p_mem_set_vtable (&vtable) == TRUE); + + P_TEST_CHECK (p_list_append (NULL, PINT_TO_POINTER (10)) == NULL); + P_TEST_CHECK (p_list_prepend (NULL, PINT_TO_POINTER (10)) == NULL); + + p_mem_restore_vtable (); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (plist_invalid_test) +{ + p_libsys_init (); + + P_TEST_CHECK (p_list_remove (NULL, NULL) == NULL); + P_TEST_CHECK (p_list_last (NULL) == NULL); + P_TEST_CHECK (p_list_length (NULL) == 0); + P_TEST_CHECK (p_list_reverse (NULL) == NULL); + + p_list_free (NULL); + p_list_foreach (NULL, NULL, NULL); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (plist_general_test) +{ + PList *list = NULL; + TestData test_data; + + p_libsys_init (); + + /* Testing append */ + list = p_list_append (list, P_INT_TO_POINTER (32)); + list = p_list_append (list, P_INT_TO_POINTER (64)); + + P_TEST_REQUIRE (list != NULL); + P_TEST_CHECK (p_list_length (list) == 2); + + /* Testing data access */ + P_TEST_CHECK (P_POINTER_TO_INT (list->data) == 32); + P_TEST_CHECK (P_POINTER_TO_INT (p_list_last(list)->data) == 64); + + /* Testing prepend */ + list = p_list_prepend (list, P_INT_TO_POINTER (128)); + P_TEST_REQUIRE (list != NULL); + P_TEST_CHECK (p_list_length (list) == 3); + P_TEST_CHECK (P_POINTER_TO_INT (list->data) == 128); + P_TEST_CHECK (P_POINTER_TO_INT (p_list_last(list)->data) == 64); + + /* Testing for each loop */ + memset (&test_data, 0, sizeof (test_data)); + + P_TEST_REQUIRE (test_data.test_array[0] == 0); + P_TEST_REQUIRE (test_data.test_array[1] == 0); + P_TEST_REQUIRE (test_data.test_array[2] == 0); + P_TEST_REQUIRE (test_data.index == 0); + + p_list_foreach (list, (PFunc) foreach_test_func, (ppointer) &test_data); + + P_TEST_CHECK (test_data.index == 3); + P_TEST_CHECK (test_data.test_array[0] == 128); + P_TEST_CHECK (test_data.test_array[1] == 32); + P_TEST_CHECK (test_data.test_array[2] == 64); + + /* Testing reverse */ + + list = p_list_reverse (list); + + P_TEST_CHECK (list != NULL); + P_TEST_CHECK (p_list_length (list) == 3); + P_TEST_CHECK (P_POINTER_TO_INT (list->data) == 64); + P_TEST_CHECK (P_POINTER_TO_INT (p_list_last(list)->data) == 128); + + /* Testing for each loop */ + memset (&test_data, 0, sizeof (test_data)); + + P_TEST_REQUIRE (test_data.test_array[0] == 0); + P_TEST_REQUIRE (test_data.test_array[1] == 0); + P_TEST_REQUIRE (test_data.test_array[2] == 0); + P_TEST_REQUIRE (test_data.index == 0); + + p_list_foreach (list, (PFunc) foreach_test_func, (ppointer) &test_data); + + P_TEST_CHECK (test_data.index == 3); + P_TEST_CHECK (test_data.test_array[0] == 64); + P_TEST_CHECK (test_data.test_array[1] == 32); + P_TEST_CHECK (test_data.test_array[2] == 128); + + /* Testing remove */ + list = p_list_remove (list, P_INT_TO_POINTER (32)); + P_TEST_REQUIRE (list != NULL); + P_TEST_CHECK (p_list_length (list) == 2); + + list = p_list_remove (list, P_INT_TO_POINTER (128)); + P_TEST_REQUIRE (list != NULL); + P_TEST_CHECK (p_list_length (list) == 1); + + list = p_list_remove (list, P_INT_TO_POINTER (256)); + P_TEST_REQUIRE (list != NULL); + P_TEST_CHECK (p_list_length (list) == 1); + + list = p_list_remove (list, P_INT_TO_POINTER (64)); + P_TEST_REQUIRE (list == NULL); + P_TEST_CHECK (p_list_length (list) == 0); + + p_list_free (list); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_SUITE_BEGIN() +{ + P_TEST_SUITE_RUN_CASE (plist_nomem_test); + P_TEST_SUITE_RUN_CASE (plist_invalid_test); + P_TEST_SUITE_RUN_CASE (plist_general_test); +} +P_TEST_SUITE_END() diff --git a/3rdparty/plibsys/tests/pmacros_test.cpp b/3rdparty/plibsys/tests/pmacros_test.cpp new file mode 100644 index 0000000..fed35a2 --- /dev/null +++ b/3rdparty/plibsys/tests/pmacros_test.cpp @@ -0,0 +1,646 @@ +/* + * The MIT License + * + * Copyright (C) 2014-2018 Alexander Saprykin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "plibsys.h" +#include "ptestmacros.h" + +#include +#include + +P_TEST_MODULE_INIT (); + +static P_GNUC_WARN_UNUSED_RESULT pint unused_result_test_func () +{ + return 0; +} + +P_LIB_INTERNAL_API int internal_api_test () +{ + return 0; +} + +P_LIB_GLOBAL_API int global_api_test () +{ + return 0; +} + +P_TEST_CASE_BEGIN (pmacros_general_test) +{ + p_libsys_init (); + + /* Test OS detection macros */ +#if !defined (P_OS_DARWIN) && !defined (P_OS_MAC9) && !defined (P_OS_BSD4) && \ + !defined (P_OS_AIX) && !defined (P_OS_HPUX) && !defined (P_OS_SOLARIS) && \ + !defined (P_OS_QNX) && !defined (P_OS_QNX6) && !defined (P_OS_UNIX) && \ + !defined (P_OS_LINUX) && !defined (P_OS_WIN) && !defined (P_OS_CYGWIN) && \ + !defined (P_OS_SCO) && !defined (P_OS_UNIXWARE) && !defined (P_OS_VMS) && \ + !defined (P_OS_IRIX) && !defined (P_OS_MSYS) && !defined (P_OS_DRAGONFLY) && \ + !defined (P_OS_HAIKU) && !defined (P_OS_TRU64) && !defined (P_OS_SYLLABLE) && \ + !defined (P_OS_BEOS) && !defined (P_OS_OS2) && !defined (P_OS_AMIGA) + P_TEST_CHECK (false); +#endif + + /* Test for Mac OS */ +#if defined (P_OS_MAC9) && defined (P_OS_UNIX) + P_TEST_CHECK (false); +#endif + +#if defined (P_OS_MAC9) && defined (P_OS_MAC) + P_TEST_CHECK (false); +#endif + +#if defined (P_OS_MAC) && !defined (P_OS_MAC32) && !defined (P_OS_MAC64) + P_TEST_CHECK (false); +#endif + +#if defined (P_OS_MAC) && (!defined (P_OS_DARWIN) || !defined (P_OS_BSD4)) + P_TEST_CHECK (false); +#endif + +#if defined (P_OS_MAC32) && !defined (P_OS_DARWIN32) + P_TEST_CHECK (false); +#endif + +#if defined (P_OS_MAC64) && !defined (P_OS_DARWIN64) + P_TEST_CHECK (false); +#endif + +#if defined (P_OS_MAC32) && defined (P_OS_MAC64) + P_TEST_CHECK (false); +#endif + + /* Test for Windows */ +#if defined (P_OS_WIN64) && !defined (P_OS_WIN) + P_TEST_CHECK (false); +#endif + +#if defined (P_OS_WIN) && defined (P_OS_UNIX) + P_TEST_CHECK (false); +#endif + + /* Test for FreeBSD */ +#if defined (P_OS_FREEBSD) && !defined (P_OS_BSD4) + P_TEST_CHECK (false); +#endif + + /* Test for DragonFlyBSD */ +#if defined (P_OS_DRAGONFLY) && !defined (P_OS_BSD4) + P_TEST_CHECK (false); +#endif + + /* Test for NetBSD */ +#if defined (P_OS_NETBSD) && !defined (P_OS_BSD4) + P_TEST_CHECK (false); +#endif + + /* Test for OpenBSD */ +#if defined (P_OS_OPENBSD) && !defined (P_OS_BSD4) + P_TEST_CHECK (false); +#endif + + /* Test for Android */ +#if defined (P_OS_ANDROID) && !defined (P_OS_LINUX) + P_TEST_CHECK (false); +#endif + + /* Test for others */ +#if defined (P_OS_HAIKU) || defined (P_OS_BEOS) || defined (P_OS_OS2) || defined (P_OS_VMS) || \ + defined (P_OS_AMIGA) +# if defined (P_OS_UNIX) + P_TEST_CHECK (false); +# endif +#endif + + /* Test for compiler detection macros */ +#if !defined (P_CC_MSVC) && !defined (P_CC_GNU) && !defined (P_CC_MINGW) && \ + !defined (P_CC_INTEL) && !defined (P_CC_CLANG) && !defined (P_CC_SUN) && \ + !defined (P_CC_XLC) && !defined (P_CC_HP) && !defined (P_CC_WATCOM) && \ + !defined (P_CC_BORLAND) && !defined (P_CC_MIPS) && !defined (P_CC_USLC) && \ + !defined (P_CC_DEC) && !defined (P_CC_PGI) && !defined (P_CC_CRAY) + P_TEST_CHECK (false); +#endif + +#if defined (P_CC_MSVC) +# if !defined (P_OS_WIN) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CC_INTEL) +# if !defined (P_OS_WIN) && !defined (P_OS_MAC) && \ + !defined (P_OS_LINUX) && !defined (P_OS_FREEBSD) && \ + !defined (P_OS_QNX6) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CC_SUN) +# if !defined (P_OS_SOLARIS) && !defined (P_OS_LINUX) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CC_XLC) +# if !defined (P_OS_AIX) && !defined (P_OS_LINUX) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CC_HP) +# if !defined (P_OS_HPUX) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CC_WATCOM) +# if !defined (P_OS_WIN) && !defined (P_OS_LINUX) && \ + !defined (P_OS_OS2) && !defined (P_OS_QNX) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CC_BORLAND) +# if !defined (P_OS_WIN) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CC_MIPS) +# if !defined (P_OS_IRIX) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CC_USLC) +# if !defined (P_OS_SCO) && !defined (P_OS_UNIXWARE) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CC_DEC) +# if !defined (P_OS_VMS) && !defined (P_OS_TRU64) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CC_PGI) +# if !defined (P_OS_WIN) && !defined (P_OS_MAC) && !defined (P_OS_LINUX) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CC_CRAY) +# if !defined (P_OS_LINUX) + P_TEST_CHECK (false); +# endif +#endif + + /* Test for CPU architecture detection macros */ +#if defined (P_OS_VMS) +# if !defined (P_CPU_ALPHA) && !defined (P_CPU_IA64) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_OS_AMIGA) +# if !defined (P_CPU_POWER) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_OS_TRU64) +# if !defined (P_CPU_ALPHA) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_OS_AIX) +# if !defined (P_CPU_POWER) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_OS_HPUX) +# if !defined (P_CPU_HPPA) && !defined (P_CPU_IA64) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_OS_SOLARIS) +# if !defined (P_CPU_X86) && !defined (P_CPU_SPARC) && !defined (P_CPU_POWER) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_OS_QNX) +# if !defined (P_CPU_X86) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_OS_QNX6) +# if !defined (P_CPU_X86) && !defined (P_CPU_ARM) && \ + !defined (P_CPU_MIPS) && !defined (P_CPU_POWER) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_OS_BB10) +# if !defined(P_CPU_X86) && !defined (P_CPU_ARM) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_OS_SCO) +# if !defined (P_CPU_X86) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_OS_UNIXWARE) +# if !defined (P_CPU_X86) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_OS_IRIX) +# if !defined (P_CPU_MIPS) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_OS_HAIKU) +# if !defined (P_CPU_X86) && !defined (P_CPU_ARM) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_OS_SYLLABLE) +# if !defined (P_CPU_X86) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_OS_BEOS) +# if !defined (P_CPU_X86) && !defined (P_CPU_POWER) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_OS_OS2) +# if !defined (P_CPU_X86) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_OS_MAC9) +# if !defined (P_CPU_POWER) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_OS_MAC) +# if !defined (P_CPU_X86) && !defined (P_CPU_POWER) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_OS_WIN) +# if !defined (P_CPU_X86) && !defined (P_CPU_ARM) && !defined (P_CPU_IA64) && \ + !defined (P_CPU_MIPS) && !defined (P_CPU_POWER) && !defined (P_CPU_ALPHA) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_OS_ANDROID) +# if !defined (P_CPU_X86) && !defined (P_CPU_ARM) && !defined (P_CPU_MIPS) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CC_MSVC) +# if !defined (P_CPU_X86) && !defined (P_CPU_ARM) && !defined (P_CPU_IA64) && \ + !defined (P_CPU_MIPS) && !defined (P_CPU_POWER) && !defined (P_CPU_ALPHA) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CC_SUN) +# if !defined (P_CPU_X86) && !defined (P_CPU_SPARC) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CC_XLC) +# if !defined (P_CPU_POWER) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CC_HP) +# if !defined (P_CPU_HPPA) && !defined (P_CPU_IA64) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CC_DEC) +# if !defined (P_CPU_ALPHA) && !defined (P_CPU_IA64) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CC_MIPS) +# if !defined (P_CPU_MIPS) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CC_USLC) +# if !defined (P_CPU_X86) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CC_WATCOM) +# if !defined (P_CPU_X86) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CC_BORLAND) +# if !defined (P_CPU_X86) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CC_PGI) +# if !defined (P_CPU_X86) && !defined (P_CPU_POWER) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CPU_ARM) +# if !defined (P_CPU_ARM_V2) && !defined (P_CPU_ARM_V3) && !defined (P_CPU_ARM_V4) && \ + !defined (P_CPU_ARM_V5) && !defined (P_CPU_ARM_V6) && !defined (P_CPU_ARM_V7) && \ + !defined (P_CPU_ARM_V8) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CPU_ARM_V2) +# if !defined (P_CPU_ARM) || !(P_CPU_ARM - 0 == 2) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CPU_ARM_V3) +# if !defined (P_CPU_ARM) || !(P_CPU_ARM - 0 == 3) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CPU_ARM_V4) +# if !defined (P_CPU_ARM) || !(P_CPU_ARM - 0 == 4) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CPU_ARM_V5) +# if !defined (P_CPU_ARM) || !(P_CPU_ARM - 0 == 5) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CPU_ARM_V6) +# if !defined (P_CPU_ARM) || !(P_CPU_ARM - 0 == 6) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CPU_ARM_V7) +# if !defined (P_CPU_ARM) || !(P_CPU_ARM - 0 == 7) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CPU_ARM_V8) +# if !defined (P_CPU_ARM) || !(P_CPU_ARM - 0 == 8) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CPU_ARM) && !(P_CPU_ARM - 0 > 0) + P_TEST_CHECK (false); +#endif + +#if defined (P_CPU_ARM) +# if !defined (P_CPU_ARM_32) && !defined (P_CPU_ARM_64) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CPU_ARM_32) || defined (P_CPU_ARM_64) +# if !defined (P_CPU_ARM) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CPU_ARM_32) +# ifdef P_CPU_ARM_64 + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CPU_ARM_64) +# ifdef P_CPU_ARM_32 + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CPU_X86) +# if !((P_CPU_X86 >= 3) && (P_CPU_X86 <= 6)) + P_TEST_CHECK (false); +# endif +# if !defined (P_CPU_X86_32) && !defined (P_CPU_X86_64) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CPU_X86_32) || defined (P_CPU_X86_64) +# if !defined (P_CPU_X86) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CPU_X86_32) && defined (P_CPU_X86_64) + P_TEST_CHECK (false); +#endif + +#if defined (P_CPU_X86_64) +# if !defined (P_CPU_X86) || !(P_CPU_X86 - 0 == 6) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CPU_MIPS) +# if !defined (P_CPU_MIPS_I) && !defined (P_CPU_MIPS_II) && !defined (P_CPU_MIPS_III) && \ + !defined (P_CPU_MIPS_IV) && !defined (P_CPU_MIPS_V) && !defined (P_CPU_MIPS_32) && \ + !defined (P_CPU_MIPS_64) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CPU_MIPS_I) || defined (P_CPU_MIPS_II) || defined (P_CPU_MIPS_III) || \ + defined (P_CPU_MIPS_IV) || defined (P_CPU_MIPS_V) || defined (P_CPU_MIPS_32) || \ + defined (P_CPU_MIPS_64) +# if !defined (P_CPU_MIPS) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CPU_MIPS_II) +# if !defined (P_CPU_MIPS_I) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CPU_MIPS_III) +# if !defined (P_CPU_MIPS_II) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CPU_MIPS_IV) +# if !defined (P_CPU_MIPS_III) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CPU_MIPS_V) +# if !defined (P_CPU_MIPS_IV) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CPU_MIPS_32) +# if !defined (P_CPU_MIPS_II) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CPU_MIPS_64) +# if !defined (P_CPU_MIPS_V) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CPU_POWER) +# if !defined (P_CPU_POWER_32) && !defined (P_CPU_POWER_64) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CPU_POWER_32) || defined (P_CPU_POWER_64) +# if !defined (P_CPU_POWER) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CPU_POWER_32) && defined (P_CPU_POWER_64) + P_TEST_CHECK (false); +#endif + +#if defined (P_CPU_SPARC_V8) || defined (P_CPU_SPARC_V9) +# if !defined (P_CPU_SPARC) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CPU_SPARC_V8) && defined (P_CPU_SPARC_V9) + P_TEST_CHECK (false); +#endif + +#if defined (P_CPU_HPPA) +# if !defined (P_CPU_HPPA_32) && !defined (P_CPU_HPPA_64) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CPU_HPPA_32) || defined (P_CPU_HPPA_64) +# if !defined (P_CPU_HPPA) + P_TEST_CHECK (false); +# endif +#endif + +#if defined (P_CPU_HPPA_32) && defined (P_CPU_HPPA_64) + P_TEST_CHECK (false); +#endif + + /* Test other macros */ + pint unused; + P_UNUSED (unused); + + pint result = unused_result_test_func (); + + P_UNUSED (result); + + P_TEST_CHECK (internal_api_test () == 0); + P_TEST_CHECK (global_api_test () == 0); + + P_WARNING ("Test warning output"); + P_ERROR ("Test error output"); + P_DEBUG ("Test debug output"); + + srand ((unsigned int) time (NULL)); + + pint rand_number = rand (); + + if (P_LIKELY (rand_number > 0)) + P_DEBUG ("Likely condition triggered"); + + if (P_UNLIKELY (rand_number == 0)) + P_DEBUG ("Unlikely condition triggered"); + + /* Test version macros */ + P_TEST_CHECK (PLIBSYS_VERSION_MAJOR >= 0); + P_TEST_CHECK (PLIBSYS_VERSION_MINOR >= 0); + P_TEST_CHECK (PLIBSYS_VERSION_PATCH >= 0); + P_TEST_CHECK (PLIBSYS_VERSION >= 0); + +#if !defined (PLIBSYS_VERSION_STR) + P_TEST_CHECK (false); +#endif + + P_TEST_CHECK (PLIBSYS_VERSION >= PLIBSYS_VERSION_CHECK (0, 0, 1)); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_SUITE_BEGIN() +{ + P_TEST_SUITE_RUN_CASE (pmacros_general_test); +} +P_TEST_SUITE_END() diff --git a/3rdparty/plibsys/tests/pmain_test.cpp b/3rdparty/plibsys/tests/pmain_test.cpp new file mode 100644 index 0000000..ec1b553 --- /dev/null +++ b/3rdparty/plibsys/tests/pmain_test.cpp @@ -0,0 +1,111 @@ +/* + * The MIT License + * + * Copyright (C) 2013-2017 Alexander Saprykin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "plibsys.h" +#include "ptestmacros.h" + +#include +#include + +P_TEST_MODULE_INIT (); + +static pint alloc_counter = 0; +static pint realloc_counter = 0; +static pint free_counter = 0; + +extern "C" ppointer pmem_alloc (psize nbytes) +{ + ++alloc_counter; + return (ppointer) malloc (nbytes); +} + +extern "C" ppointer pmem_realloc (ppointer block, psize nbytes) +{ + ++realloc_counter; + return (ppointer) realloc (block, nbytes); +} + +extern "C" void pmem_free (ppointer block) +{ + ++free_counter; + free (block); +} + +P_TEST_CASE_BEGIN (pmain_general_test) +{ + p_libsys_init (); + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (pmain_double_test) +{ + p_libsys_init_full (NULL); + p_libsys_init (); + p_libsys_shutdown (); + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (pmain_vtable_test) +{ + PMemVTable vtable; + + vtable.free = pmem_free; + vtable.malloc = pmem_alloc; + vtable.realloc = pmem_realloc; + + p_libsys_init_full (&vtable); + + alloc_counter = 0; + realloc_counter = 0; + free_counter = 0; + + pchar *buf = (pchar *) p_malloc0 (10); + pchar *new_buf = (pchar *) p_realloc ((ppointer) buf, 20); + + P_TEST_REQUIRE (new_buf != NULL); + + buf = new_buf; + + p_free (buf); + + P_TEST_CHECK (alloc_counter > 0); + P_TEST_CHECK (realloc_counter > 0); + P_TEST_CHECK (free_counter > 0); + + P_TEST_CHECK (strcmp (p_libsys_version (), PLIBSYS_VERSION_STR) == 0); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_SUITE_BEGIN() +{ + P_TEST_SUITE_RUN_CASE (pmain_general_test); + P_TEST_SUITE_RUN_CASE (pmain_double_test); + P_TEST_SUITE_RUN_CASE (pmain_vtable_test); +} +P_TEST_SUITE_END() diff --git a/3rdparty/plibsys/tests/pmem_test.cpp b/3rdparty/plibsys/tests/pmem_test.cpp new file mode 100644 index 0000000..c9504b6 --- /dev/null +++ b/3rdparty/plibsys/tests/pmem_test.cpp @@ -0,0 +1,171 @@ +/* + * The MIT License + * + * Copyright (C) 2013-2017 Alexander Saprykin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "plibsys.h" +#include "ptestmacros.h" + +#include +#include + +P_TEST_MODULE_INIT (); + +static pint alloc_counter = 0; +static pint realloc_counter = 0; +static pint free_counter = 0; + +extern "C" ppointer pmem_alloc (psize nbytes) +{ + ++alloc_counter; + return (ppointer) malloc (nbytes); +} + +extern "C" ppointer pmem_realloc (ppointer block, psize nbytes) +{ + ++realloc_counter; + return (ppointer) realloc (block, nbytes); +} + +extern "C" void pmem_free (ppointer block) +{ + ++free_counter; + free (block); +} + +P_TEST_CASE_BEGIN (pmem_bad_input_test) +{ + PMemVTable vtable; + + p_libsys_init (); + + vtable.free = NULL; + vtable.malloc = NULL; + vtable.realloc = NULL; + + P_TEST_CHECK (p_malloc (0) == NULL); + P_TEST_CHECK (p_malloc0 (0) == NULL); + P_TEST_CHECK (p_realloc (NULL, 0) == NULL); + P_TEST_CHECK (p_mem_set_vtable (NULL) == FALSE); + P_TEST_CHECK (p_mem_set_vtable (&vtable) == FALSE); + p_free (NULL); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (pmem_general_test) +{ + PMemVTable vtable; + ppointer ptr = NULL; + pint i; + + p_libsys_init (); + + alloc_counter = 0; + realloc_counter = 0; + free_counter = 0; + + vtable.free = pmem_free; + vtable.malloc = pmem_alloc; + vtable.realloc = pmem_realloc; + + P_TEST_CHECK (p_mem_set_vtable (&vtable) == TRUE); + + /* Test memory allocation using system functions */ + ptr = p_malloc (1024); + P_TEST_REQUIRE (ptr != NULL); + + for (int i = 0; i < 1024; ++i) + *(((pchar *) ptr) + i) = (pchar) (i % 127); + + for (int i = 0; i < 1024; ++i) + P_TEST_CHECK (*(((pchar *) ptr) + i) == (pchar) (i % 127)); + + p_free (ptr); + + ptr = p_malloc0 (2048); + P_TEST_REQUIRE (ptr != NULL); + + for (int i = 0; i < 2048; ++i) + P_TEST_CHECK (*(((pchar *) ptr) + i) == 0); + + for (int i = 0; i < 2048; ++i) + *(((pchar *) ptr) + i) = (pchar) (i % 127); + + for (int i = 0; i < 2048; ++i) + P_TEST_CHECK (*(((pchar *) ptr) + i) == (pchar) (i % 127)); + + p_free (ptr); + + ptr = p_realloc (NULL, 1024); + P_TEST_REQUIRE (ptr != NULL); + + for (int i = 0; i < 1024; ++i) + *(((pchar *) ptr) + i) = (pchar) (i % 127); + + ptr = p_realloc (ptr, 2048); + + for (int i = 1024; i < 2048; ++i) + *(((pchar *) ptr) + i) = (pchar) ((i - 1) % 127); + + for (int i = 0; i < 1024; ++i) + P_TEST_CHECK (*(((pchar *) ptr) + i) == (pchar) (i % 127)); + + for (int i = 1024; i < 2048; ++i) + P_TEST_CHECK (*(((pchar *) ptr) + i) == (pchar) ((i - 1) % 127)); + + p_free (ptr); + + P_TEST_CHECK (alloc_counter > 0); + P_TEST_CHECK (realloc_counter > 0); + P_TEST_CHECK (free_counter > 0); + + p_mem_restore_vtable (); + + /* Test memory mapping */ + ptr = p_mem_mmap (0, NULL); + P_TEST_CHECK (ptr == NULL); + + ptr = p_mem_mmap (1024, NULL); + P_TEST_REQUIRE (ptr != NULL); + + for (i = 0; i < 1024; ++i) + *(((pchar *) ptr) + i) = i % 127; + + for (i = 0; i < 1024; ++i) + P_TEST_CHECK (*(((pchar *) ptr) + i) == i % 127); + + P_TEST_CHECK (p_mem_munmap (NULL, 1024, NULL) == FALSE); + P_TEST_CHECK (p_mem_munmap (ptr, 1024, NULL) == TRUE); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_SUITE_BEGIN() +{ + P_TEST_SUITE_RUN_CASE (pmem_bad_input_test); + P_TEST_SUITE_RUN_CASE (pmem_general_test); +} +P_TEST_SUITE_END() diff --git a/3rdparty/plibsys/tests/pmutex_test.cpp b/3rdparty/plibsys/tests/pmutex_test.cpp new file mode 100644 index 0000000..4d16909 --- /dev/null +++ b/3rdparty/plibsys/tests/pmutex_test.cpp @@ -0,0 +1,146 @@ +/* + * The MIT License + * + * Copyright (C) 2013-2019 Alexander Saprykin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "plibsys.h" +#include "ptestmacros.h" + +P_TEST_MODULE_INIT (); + +static pint mutex_test_val = 0; +static PMutex *global_mutex = NULL; + +extern "C" ppointer pmem_alloc (psize nbytes) +{ + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" ppointer pmem_realloc (ppointer block, psize nbytes) +{ + P_UNUSED (block); + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" void pmem_free (ppointer block) +{ + P_UNUSED (block); +} + +static void * mutex_test_thread (void *) +{ + pint i; + + for (i = 0; i < 1000; ++i) { + if (!p_mutex_trylock (global_mutex)) { + if (!p_mutex_lock (global_mutex)) + p_uthread_exit (1); + } + + if (mutex_test_val == 10) + --mutex_test_val; + else { + p_uthread_sleep (1); + ++mutex_test_val; + } + + if (!p_mutex_unlock (global_mutex)) + p_uthread_exit (1); + } + + p_uthread_exit (0); + + return NULL; +} + +P_TEST_CASE_BEGIN (pmutex_nomem_test) +{ + p_libsys_init (); + + PMemVTable vtable; + + vtable.free = pmem_free; + vtable.malloc = pmem_alloc; + vtable.realloc = pmem_realloc; + + P_TEST_CHECK (p_mem_set_vtable (&vtable) == TRUE); + P_TEST_CHECK (p_mutex_new () == NULL); + + p_mem_restore_vtable (); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (pmutex_bad_input_test) +{ + p_libsys_init (); + + P_TEST_REQUIRE (p_mutex_lock (NULL) == FALSE); + P_TEST_REQUIRE (p_mutex_unlock (NULL) == FALSE); + P_TEST_REQUIRE (p_mutex_trylock (NULL) == FALSE); + p_mutex_free (NULL); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (pmutex_general_test) +{ + PUThread *thr1, *thr2; + + p_libsys_init (); + + global_mutex = p_mutex_new (); + P_TEST_REQUIRE (global_mutex != NULL); + + mutex_test_val = 10; + + thr1 = p_uthread_create ((PUThreadFunc) mutex_test_thread, NULL, TRUE, NULL); + P_TEST_REQUIRE (thr1 != NULL); + + thr2 = p_uthread_create ((PUThreadFunc) mutex_test_thread, NULL, TRUE, NULL); + P_TEST_REQUIRE (thr2 != NULL); + + P_TEST_CHECK (p_uthread_join (thr1) == 0); + P_TEST_CHECK (p_uthread_join (thr2) == 0); + + P_TEST_REQUIRE (mutex_test_val == 10); + + p_uthread_unref (thr1); + p_uthread_unref (thr2); + p_mutex_free (global_mutex); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_SUITE_BEGIN() +{ + P_TEST_SUITE_RUN_CASE (pmutex_nomem_test); + P_TEST_SUITE_RUN_CASE (pmutex_bad_input_test); + P_TEST_SUITE_RUN_CASE (pmutex_general_test); +} +P_TEST_SUITE_END() diff --git a/3rdparty/plibsys/tests/pprocess_test.cpp b/3rdparty/plibsys/tests/pprocess_test.cpp new file mode 100644 index 0000000..c58cf13 --- /dev/null +++ b/3rdparty/plibsys/tests/pprocess_test.cpp @@ -0,0 +1,49 @@ +/* + * The MIT License + * + * Copyright (C) 2013-2017 Alexander Saprykin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "plibsys.h" +#include "ptestmacros.h" + +P_TEST_MODULE_INIT (); + +P_TEST_CASE_BEGIN (pprocess_general_test) +{ + puint32 pid; + + p_libsys_init (); + + pid = p_process_get_current_pid (); + P_TEST_CHECK (pid > 0); + P_TEST_REQUIRE (p_process_is_running (pid) == TRUE); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_SUITE_BEGIN() +{ + P_TEST_SUITE_RUN_CASE (pprocess_general_test); +} +P_TEST_SUITE_END() diff --git a/3rdparty/plibsys/tests/prwlock_test.cpp b/3rdparty/plibsys/tests/prwlock_test.cpp new file mode 100644 index 0000000..c8bce38 --- /dev/null +++ b/3rdparty/plibsys/tests/prwlock_test.cpp @@ -0,0 +1,224 @@ +/* + * The MIT License + * + * Copyright (C) 2016-2019 Alexander Saprykin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "plibsys.h" +#include "ptestmacros.h" + +#include + +P_TEST_MODULE_INIT (); + +#define PRWLOCK_TEST_STRING_1 "This is a test string." +#define PRWLOCK_TEST_STRING_2 "Ouh, yet another string to check!" + +static PRWLock * test_rwlock = NULL; +static volatile pboolean is_threads_working = FALSE; +static volatile pint writers_counter = 0; +static pchar string_buf[50]; + +extern "C" ppointer pmem_alloc (psize nbytes) +{ + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" ppointer pmem_realloc (ppointer block, psize nbytes) +{ + P_UNUSED (block); + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" void pmem_free (ppointer block) +{ + P_UNUSED (block); +} + +static void * reader_thread_func (void *data) +{ + P_UNUSED (data); + + pint counter = 0; + + while (p_atomic_int_get (&writers_counter) == 0) + p_uthread_sleep (10); + + while (is_threads_working == TRUE) { + p_uthread_sleep (10); + + if (p_rwlock_reader_trylock (test_rwlock) == FALSE) { + if (p_rwlock_reader_lock (test_rwlock) == FALSE) + p_uthread_exit (-1); + } + + if (strcmp (string_buf, PRWLOCK_TEST_STRING_1) != 0 && + strcmp (string_buf, PRWLOCK_TEST_STRING_2) != 0) { + p_rwlock_reader_unlock (test_rwlock); + p_uthread_exit (-1); + } + + if (p_rwlock_reader_unlock (test_rwlock) == FALSE) + p_uthread_exit (-1); + + ++counter; + } + + p_uthread_exit (counter); + + return NULL; +} + +static void * writer_thread_func (void *data) +{ + pint string_num = PPOINTER_TO_INT (data); + pint counter = 0; + + while (is_threads_working == TRUE) { + p_uthread_sleep (10); + + if (p_rwlock_writer_trylock (test_rwlock) == FALSE) { + if (p_rwlock_writer_lock (test_rwlock) == FALSE) + p_uthread_exit (-1); + } + + memset (string_buf, 0, sizeof (string_buf)); + + if (string_num == 1) + strcpy (string_buf, PRWLOCK_TEST_STRING_1); + else + strcpy (string_buf, PRWLOCK_TEST_STRING_1); + + if (p_rwlock_writer_unlock (test_rwlock) == FALSE) + p_uthread_exit (-1); + + ++counter; + + p_atomic_int_inc ((&writers_counter)); + } + + p_uthread_exit (counter); + + return NULL; +} + +P_TEST_CASE_BEGIN (prwlock_nomem_test) +{ + p_libsys_init (); + + PMemVTable vtable; + + vtable.free = pmem_free; + vtable.malloc = pmem_alloc; + vtable.realloc = pmem_realloc; + + P_TEST_CHECK (p_mem_set_vtable (&vtable) == TRUE); + + P_TEST_CHECK (p_rwlock_new () == NULL); + + p_mem_restore_vtable (); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (prwlock_bad_input_test) +{ + p_libsys_init (); + + P_TEST_CHECK (p_rwlock_reader_lock (NULL) == FALSE); + P_TEST_CHECK (p_rwlock_reader_trylock (NULL) == FALSE); + P_TEST_CHECK (p_rwlock_reader_unlock (NULL) == FALSE); + P_TEST_CHECK (p_rwlock_writer_lock (NULL) == FALSE); + P_TEST_CHECK (p_rwlock_writer_trylock (NULL) == FALSE); + P_TEST_CHECK (p_rwlock_writer_unlock (NULL) == FALSE); + p_rwlock_free (NULL); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (prwlock_general_test) +{ + p_libsys_init (); + + test_rwlock = p_rwlock_new (); + + P_TEST_REQUIRE (test_rwlock != NULL); + + is_threads_working = TRUE; + writers_counter = 0; + + PUThread *reader_thr1 = p_uthread_create ((PUThreadFunc) reader_thread_func, + NULL, + TRUE, + NULL); + + PUThread *reader_thr2 = p_uthread_create ((PUThreadFunc) reader_thread_func, + NULL, + TRUE, + NULL); + + PUThread *writer_thr1 = p_uthread_create ((PUThreadFunc) writer_thread_func, + NULL, + TRUE, + NULL); + + PUThread *writer_thr2 = p_uthread_create ((PUThreadFunc) writer_thread_func, + NULL, + TRUE, + NULL); + + P_TEST_REQUIRE (reader_thr1 != NULL); + P_TEST_REQUIRE (reader_thr2 != NULL); + P_TEST_REQUIRE (writer_thr1 != NULL); + P_TEST_REQUIRE (writer_thr2 != NULL); + + p_uthread_sleep (10000); + + is_threads_working = FALSE; + + P_TEST_CHECK (p_uthread_join (reader_thr1) > 0); + P_TEST_CHECK (p_uthread_join (reader_thr2) > 0); + P_TEST_CHECK (p_uthread_join (writer_thr1) > 0); + P_TEST_CHECK (p_uthread_join (writer_thr2) > 0); + + p_uthread_unref (reader_thr1); + p_uthread_unref (reader_thr2); + p_uthread_unref (writer_thr1); + p_uthread_unref (writer_thr2); + + p_rwlock_free (test_rwlock); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_SUITE_BEGIN() +{ + P_TEST_SUITE_RUN_CASE (prwlock_nomem_test); + P_TEST_SUITE_RUN_CASE (prwlock_bad_input_test); + P_TEST_SUITE_RUN_CASE (prwlock_general_test); +} +P_TEST_SUITE_END() diff --git a/3rdparty/plibsys/tests/psemaphore_test.cpp b/3rdparty/plibsys/tests/psemaphore_test.cpp new file mode 100644 index 0000000..0b445bd --- /dev/null +++ b/3rdparty/plibsys/tests/psemaphore_test.cpp @@ -0,0 +1,226 @@ +/* + * The MIT License + * + * Copyright (C) 2013-2019 Alexander Saprykin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "plibsys.h" +#include "ptestmacros.h" + +P_TEST_MODULE_INIT (); + +#define PSEMAPHORE_MAX_VAL 10 + +static pint semaphore_test_val = 0; +static pint is_thread_exit = 0; + +static void clean_error (PError **error) +{ + if (error == NULL || *error == NULL) + return; + + p_error_free (*error); + *error = NULL; +} + +static void * semaphore_test_thread (void *) +{ + PSemaphore *sem; + pint i; + + sem = p_semaphore_new ("p_semaphore_test_object", 1, P_SEM_ACCESS_OPEN, NULL); + + if (sem == NULL) + p_uthread_exit (1); + + for (i = 0; i < 1000; ++i) { + if (!p_semaphore_acquire (sem, NULL)) { + if (is_thread_exit > 0) { + semaphore_test_val = PSEMAPHORE_MAX_VAL; + break; + } + + p_uthread_exit (1); + } + + if (semaphore_test_val == PSEMAPHORE_MAX_VAL) + --semaphore_test_val; + else { + p_uthread_sleep (1); + ++semaphore_test_val; + } + + if (!p_semaphore_release (sem, NULL)) { + if (is_thread_exit > 0) { + semaphore_test_val = PSEMAPHORE_MAX_VAL; + break; + } + + p_uthread_exit (1); + } + } + + ++is_thread_exit; + + p_semaphore_free (sem); + p_uthread_exit (0); + + return NULL; +} + +extern "C" ppointer pmem_alloc (psize nbytes) +{ + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" ppointer pmem_realloc (ppointer block, psize nbytes) +{ + P_UNUSED (block); + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" void pmem_free (ppointer block) +{ + P_UNUSED (block); +} + +P_TEST_CASE_BEGIN (psemaphore_nomem_test) +{ + p_libsys_init (); + + PMemVTable vtable; + + vtable.free = pmem_free; + vtable.malloc = pmem_alloc; + vtable.realloc = pmem_realloc; + + P_TEST_CHECK (p_mem_set_vtable (&vtable) == TRUE); + + P_TEST_CHECK (p_semaphore_new ("p_semaphore_test_object", 1, P_SEM_ACCESS_CREATE, NULL) == NULL); + + p_mem_restore_vtable (); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (psemaphore_general_test) +{ + PSemaphore *sem = NULL; + PError *error = NULL; + pint i; + + p_libsys_init (); + + P_TEST_CHECK (p_semaphore_new (NULL, 0, P_SEM_ACCESS_CREATE, &error) == NULL); + P_TEST_CHECK (error != NULL); + clean_error (&error); + + P_TEST_REQUIRE (p_semaphore_acquire (sem, &error) == FALSE); + P_TEST_CHECK (error != NULL); + clean_error (&error); + + P_TEST_REQUIRE (p_semaphore_release (sem, &error) == FALSE); + P_TEST_CHECK (error != NULL); + clean_error (&error); + + p_semaphore_take_ownership (sem); + p_semaphore_free (NULL); + + sem = p_semaphore_new ("p_semaphore_test_object", 10, P_SEM_ACCESS_CREATE, NULL); + P_TEST_REQUIRE (sem != NULL); + p_semaphore_take_ownership (sem); + p_semaphore_free (sem); + + sem = p_semaphore_new ("p_semaphore_test_object", 10, P_SEM_ACCESS_CREATE, NULL); + P_TEST_REQUIRE (sem != NULL); + + for (i = 0; i < 10; ++i) + P_TEST_CHECK (p_semaphore_acquire (sem, NULL)); + + for (i = 0; i < 10; ++i) + P_TEST_CHECK (p_semaphore_release (sem, NULL)); + + for (i = 0; i < 1000; ++i) { + P_TEST_CHECK (p_semaphore_acquire (sem, NULL)); + P_TEST_CHECK (p_semaphore_release (sem, NULL)); + } + + p_semaphore_free (sem); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (psemaphore_thread_test) +{ + PUThread *thr1, *thr2; + PSemaphore *sem = NULL; + + p_libsys_init (); + + sem = p_semaphore_new ("p_semaphore_test_object", 10, P_SEM_ACCESS_CREATE, NULL); + P_TEST_REQUIRE (sem != NULL); + p_semaphore_take_ownership (sem); + p_semaphore_free (sem); + + sem = NULL; + is_thread_exit = 0; + semaphore_test_val = PSEMAPHORE_MAX_VAL; + + thr1 = p_uthread_create ((PUThreadFunc) semaphore_test_thread, NULL, TRUE, NULL); + P_TEST_REQUIRE (thr1 != NULL); + + thr2 = p_uthread_create ((PUThreadFunc) semaphore_test_thread, NULL, TRUE, NULL); + P_TEST_REQUIRE (thr2 != NULL); + + P_TEST_CHECK (p_uthread_join (thr1) == 0); + P_TEST_CHECK (p_uthread_join (thr2) == 0); + + P_TEST_REQUIRE (semaphore_test_val == PSEMAPHORE_MAX_VAL); + + P_TEST_REQUIRE (p_semaphore_acquire (sem, NULL) == FALSE); + P_TEST_REQUIRE (p_semaphore_release (sem, NULL) == FALSE); + p_semaphore_free (sem); + p_semaphore_take_ownership (sem); + + sem = p_semaphore_new ("p_semaphore_test_object", 1, P_SEM_ACCESS_OPEN, NULL); + P_TEST_REQUIRE (sem != NULL); + p_semaphore_take_ownership (sem); + p_semaphore_free (sem); + + p_uthread_unref (thr1); + p_uthread_unref (thr2); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_SUITE_BEGIN() +{ + P_TEST_SUITE_RUN_CASE (psemaphore_nomem_test); + P_TEST_SUITE_RUN_CASE (psemaphore_general_test); + P_TEST_SUITE_RUN_CASE (psemaphore_thread_test); +} +P_TEST_SUITE_END() diff --git a/3rdparty/plibsys/tests/pshm_test.cpp b/3rdparty/plibsys/tests/pshm_test.cpp new file mode 100644 index 0000000..bc89692 --- /dev/null +++ b/3rdparty/plibsys/tests/pshm_test.cpp @@ -0,0 +1,313 @@ +/* + * The MIT License + * + * Copyright (C) 2013-2019 Alexander Saprykin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "plibsys.h" +#include "ptestmacros.h" + +#include +#include + +P_TEST_MODULE_INIT (); + +static void * shm_test_thread (void *arg) +{ + pint rand_num; + psize shm_size; + ppointer addr; + PShm *shm; + + if (arg == NULL) + p_uthread_exit (1); + + shm = (PShm *) arg; + rand_num = rand () % 127; + shm_size = p_shm_get_size (shm); + addr = p_shm_get_address (shm); + + if (shm_size == 0 || addr == NULL) + p_uthread_exit (1); + + if (!p_shm_lock (shm, NULL)) + p_uthread_exit (1); + + for (puint i = 0; i < shm_size; ++i) + *(((pchar *) addr) + i) = (pchar) rand_num; + + if (!p_shm_unlock (shm, NULL)) + p_uthread_exit (1); + + p_uthread_exit (0); + + return NULL; +} + +extern "C" ppointer pmem_alloc (psize nbytes) +{ + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" ppointer pmem_realloc (ppointer block, psize nbytes) +{ + P_UNUSED (block); + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" void pmem_free (ppointer block) +{ + P_UNUSED (block); +} + +P_TEST_CASE_BEGIN (pshm_nomem_test) +{ + p_libsys_init (); + + PMemVTable vtable; + + vtable.free = pmem_free; + vtable.malloc = pmem_alloc; + vtable.realloc = pmem_realloc; + + P_TEST_CHECK (p_mem_set_vtable (&vtable) == TRUE); + + P_TEST_CHECK (p_shm_new ("p_shm_test_memory_block", 1024, P_SHM_ACCESS_READWRITE, NULL) == NULL); + + p_mem_restore_vtable (); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (pshm_invalid_test) +{ + p_libsys_init (); + + P_TEST_CHECK (p_shm_new (NULL, 0, P_SHM_ACCESS_READWRITE, NULL) == NULL); + P_TEST_CHECK (p_shm_lock (NULL, NULL) == FALSE); + P_TEST_CHECK (p_shm_unlock (NULL, NULL) == FALSE); + P_TEST_CHECK (p_shm_get_address (NULL) == NULL); + P_TEST_CHECK (p_shm_get_size (NULL) == 0); + p_shm_take_ownership (NULL); + + PShm *shm = p_shm_new ("p_shm_invalid_test", 0, P_SHM_ACCESS_READWRITE, NULL); + p_shm_take_ownership (shm); + p_shm_free (shm); + + shm = p_shm_new ("p_shm_invalid_test", 10, (PShmAccessPerms) -1, NULL); + p_shm_take_ownership (shm); + p_shm_free (shm); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (pshm_general_test) +{ + PShm *shm = NULL; +#ifndef P_OS_HPUX + PShm *shm2 = NULL; +#endif + ppointer addr, addr2; + pint i; + + p_libsys_init (); + + shm = p_shm_new ("p_shm_test_memory_block", 1024, P_SHM_ACCESS_READWRITE, NULL); + P_TEST_REQUIRE (shm != NULL); + p_shm_take_ownership (shm); + p_shm_free (shm); + + shm = p_shm_new ("p_shm_test_memory_block", 1024, P_SHM_ACCESS_READWRITE, NULL); + P_TEST_REQUIRE (shm != NULL); + P_TEST_REQUIRE (p_shm_get_size (shm) == 1024); + + addr = p_shm_get_address (shm); + P_TEST_REQUIRE (addr != NULL); + +#ifndef P_OS_HPUX + shm2 = p_shm_new ("p_shm_test_memory_block", 1024, P_SHM_ACCESS_READONLY, NULL); + + if (shm2 == NULL) { + /* OK, some systems may want exactly the same permissions */ + shm2 = p_shm_new ("p_shm_test_memory_block", 1024, P_SHM_ACCESS_READWRITE, NULL); + } + + P_TEST_REQUIRE (shm2 != NULL); + P_TEST_REQUIRE (p_shm_get_size (shm2) == 1024); + + addr2 = p_shm_get_address (shm2); + P_TEST_REQUIRE (addr2 != NULL); +#endif + + for (i = 0; i < 512; ++i) { + P_TEST_CHECK (p_shm_lock (shm, NULL)); + *(((pchar *) addr) + i) = 'a'; + P_TEST_CHECK (p_shm_unlock (shm, NULL)); + } + +#ifndef P_OS_HPUX + for (i = 0; i < 512; ++i) { + P_TEST_CHECK (p_shm_lock (shm2, NULL)); + P_TEST_CHECK (*(((pchar *) addr) + i) == 'a'); + P_TEST_CHECK (p_shm_unlock (shm2, NULL)); + } +#else + for (i = 0; i < 512; ++i) { + P_TEST_CHECK (p_shm_lock (shm, NULL)); + P_TEST_CHECK (*(((pchar *) addr) + i) == 'a'); + P_TEST_CHECK (p_shm_unlock (shm, NULL)); + } +#endif + + for (i = 0; i < 1024; ++i) { + P_TEST_CHECK (p_shm_lock (shm, NULL)); + *(((pchar *) addr) + i) = 'b'; + P_TEST_CHECK (p_shm_unlock (shm, NULL)); + } + +#ifndef P_OS_HPUX + for (i = 0; i < 1024; ++i) { + P_TEST_CHECK (p_shm_lock (shm2, NULL)); + P_TEST_CHECK (*(((pchar *) addr) + i) != 'c'); + P_TEST_CHECK (p_shm_unlock (shm2, NULL)); + } + + for (i = 0; i < 1024; ++i) { + P_TEST_CHECK (p_shm_lock (shm2, NULL)); + P_TEST_CHECK (*(((pchar *) addr) + i) == 'b'); + P_TEST_CHECK (p_shm_unlock (shm2, NULL)); + } +#else + for (i = 0; i < 1024; ++i) { + P_TEST_CHECK (p_shm_lock (shm, NULL)); + P_TEST_CHECK (*(((pchar *) addr) + i) != 'c'); + P_TEST_CHECK (p_shm_unlock (shm, NULL)); + } + + for (i = 0; i < 1024; ++i) { + P_TEST_CHECK (p_shm_lock (shm, NULL)); + P_TEST_CHECK (*(((pchar *) addr) + i) == 'b'); + P_TEST_CHECK (p_shm_unlock (shm, NULL)); + } +#endif + + p_shm_free (shm); + + shm = p_shm_new ("p_shm_test_memory_block_2", 1024, P_SHM_ACCESS_READWRITE, NULL); + P_TEST_REQUIRE (shm != NULL); + P_TEST_REQUIRE (p_shm_get_size (shm) == 1024); + + addr = p_shm_get_address (shm); + P_TEST_REQUIRE (addr != NULL); + + for (i = 0; i < 1024; ++i) { + P_TEST_CHECK (p_shm_lock (shm, NULL)); + P_TEST_CHECK (*(((pchar *) addr) + i) != 'b'); + P_TEST_CHECK (p_shm_unlock (shm, NULL)); + } + + p_shm_free (shm); + +#ifndef P_OS_HPUX + p_shm_free (shm2); +#endif + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (pshm_thread_test) +{ + PShm *shm; + PUThread *thr1, *thr2, *thr3; + ppointer addr; + pint i, val; + pboolean test_ok; + + p_libsys_init (); + + srand ((puint) time (NULL)); + + shm = p_shm_new ("p_shm_test_memory_block", 1024 * 1024, P_SHM_ACCESS_READWRITE, NULL); + P_TEST_REQUIRE (shm != NULL); + p_shm_take_ownership (shm); + p_shm_free (shm); + + shm = p_shm_new ("p_shm_test_memory_block", 1024 * 1024, P_SHM_ACCESS_READWRITE, NULL); + P_TEST_REQUIRE (shm != NULL); + + if (p_shm_get_size (shm) != 1024 * 1024) { + p_shm_free (shm); + shm = p_shm_new ("p_shm_test_memory_block", 1024 * 1024, P_SHM_ACCESS_READWRITE, NULL); + P_TEST_REQUIRE (shm != NULL); + } + + P_TEST_REQUIRE (p_shm_get_size (shm) == 1024 * 1024); + + addr = p_shm_get_address (shm); + P_TEST_REQUIRE (addr != NULL); + + thr1 = p_uthread_create ((PUThreadFunc) shm_test_thread, (ppointer) shm, TRUE, NULL); + P_TEST_REQUIRE (thr1 != NULL); + + thr2 = p_uthread_create ((PUThreadFunc) shm_test_thread, (ppointer) shm, TRUE, NULL); + P_TEST_REQUIRE (thr2 != NULL); + + thr3 = p_uthread_create ((PUThreadFunc) shm_test_thread, (ppointer) shm, TRUE, NULL); + P_TEST_REQUIRE (thr3 != NULL); + + P_TEST_CHECK (p_uthread_join (thr1) == 0); + P_TEST_CHECK (p_uthread_join (thr2) == 0); + P_TEST_CHECK (p_uthread_join (thr3) == 0); + + test_ok = TRUE; + val = *((pchar *) addr); + + for (i = 1; i < 1024 * 1024; ++i) + if (*(((pchar *) addr) + i) != val) { + test_ok = FALSE; + break; + } + + P_TEST_REQUIRE (test_ok == TRUE); + + p_uthread_unref (thr1); + p_uthread_unref (thr2); + p_uthread_unref (thr3); + p_shm_free (shm); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_SUITE_BEGIN() +{ + P_TEST_SUITE_RUN_CASE (pshm_nomem_test); + P_TEST_SUITE_RUN_CASE (pshm_invalid_test); + P_TEST_SUITE_RUN_CASE (pshm_general_test); + P_TEST_SUITE_RUN_CASE (pshm_thread_test); +} +P_TEST_SUITE_END() diff --git a/3rdparty/plibsys/tests/pshmbuffer_test.cpp b/3rdparty/plibsys/tests/pshmbuffer_test.cpp new file mode 100644 index 0000000..7e48f56 --- /dev/null +++ b/3rdparty/plibsys/tests/pshmbuffer_test.cpp @@ -0,0 +1,352 @@ +/* + * The MIT License + * + * Copyright (C) 2013-2020 Alexander Saprykin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "plibsys.h" +#include "ptestmacros.h" + +#include + +P_TEST_MODULE_INIT (); + +static pchar test_str[] = "This is a test string!"; +static pchar test_str_sm[] = "Small"; +static pint is_thread_exit = 0; +static pint read_count = 0; +static pint write_count = 0; + +#ifndef P_OS_HPUX +volatile static pboolean is_working = FALSE; + +static void * shm_buffer_test_write_thread (void *) +{ + PShmBuffer *buffer = p_shm_buffer_new ("pshm_test_buffer", 1024, NULL); + + if (buffer == NULL) + p_uthread_exit (1); + + while (is_working == TRUE) { + p_uthread_sleep (3); + + pssize op_result = p_shm_buffer_get_free_space (buffer, NULL); + + if (op_result < 0) { + if (is_thread_exit > 0) + break; + else { + ++is_thread_exit; + p_shm_buffer_free (buffer); + p_uthread_exit (1); + } + } + + if ((psize) op_result < sizeof (test_str)) + continue; + + op_result = p_shm_buffer_write (buffer, (ppointer) test_str, sizeof (test_str), NULL); + + if (op_result < 0) { + if (is_thread_exit > 0) + break; + else { + ++is_thread_exit; + p_shm_buffer_free (buffer); + p_uthread_exit (1); + } + } + + if (op_result != sizeof (test_str)) { + ++is_thread_exit; + p_shm_buffer_free (buffer); + p_uthread_exit (1); + } + + ++read_count; + } + + ++is_thread_exit; + + p_shm_buffer_free (buffer); + p_uthread_exit (0); + + return NULL; +} + +static void * shm_buffer_test_read_thread (void *) +{ + PShmBuffer *buffer = p_shm_buffer_new ("pshm_test_buffer", 1024, NULL); + pchar test_buf[sizeof (test_str)]; + + if (buffer == NULL) + p_uthread_exit (1); + + while (is_working == TRUE) { + p_uthread_sleep (3); + + pssize op_result = p_shm_buffer_get_used_space (buffer, NULL); + + if (op_result < 0) { + if (is_thread_exit > 0) + break; + else { + ++is_thread_exit; + p_shm_buffer_free (buffer); + p_uthread_exit (1); + } + } + + if ((psize) op_result < sizeof (test_str)) + continue; + + op_result = p_shm_buffer_read (buffer, (ppointer) test_buf, sizeof (test_buf), NULL); + + if (op_result < 0) { + if (is_thread_exit > 0) + break; + else { + ++is_thread_exit; + p_shm_buffer_free (buffer); + p_uthread_exit (1); + } + } + + if (op_result != sizeof (test_buf)) { + ++is_thread_exit; + p_shm_buffer_free (buffer); + p_uthread_exit (1); + } + + if (strncmp (test_buf, test_str, sizeof (test_buf)) != 0) { + ++is_thread_exit; + p_shm_buffer_free (buffer); + p_uthread_exit (1); + } + + ++write_count; + } + + ++is_thread_exit; + + p_shm_buffer_free (buffer); + p_uthread_exit (0); + + return NULL; +} +#endif /* !P_OS_HPUX */ + +extern "C" ppointer pmem_alloc (psize nbytes) +{ + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" ppointer pmem_realloc (ppointer block, psize nbytes) +{ + P_UNUSED (block); + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" void pmem_free (ppointer block) +{ + P_UNUSED (block); +} + +P_TEST_CASE_BEGIN (pshmbuffer_nomem_test) +{ + p_libsys_init (); + + PMemVTable vtable; + + vtable.free = pmem_free; + vtable.malloc = pmem_alloc; + vtable.realloc = pmem_realloc; + + P_TEST_CHECK (p_mem_set_vtable (&vtable) == TRUE); + + P_TEST_CHECK (p_shm_buffer_new ("pshm_test_buffer", 1024, NULL) == NULL); + + p_mem_restore_vtable (); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (pshmbuffer_bad_input_test) +{ + p_libsys_init (); + + P_TEST_CHECK (p_shm_buffer_new (NULL, 0, NULL) == NULL); + P_TEST_CHECK (p_shm_buffer_read (NULL, NULL, 0, NULL) == -1); + P_TEST_CHECK (p_shm_buffer_write (NULL, NULL, 0, NULL) == -1); + P_TEST_CHECK (p_shm_buffer_get_free_space (NULL, NULL) == -1); + P_TEST_CHECK (p_shm_buffer_get_used_space (NULL, NULL) == -1); + + PShmBuffer *buf = p_shm_buffer_new ("pshm_invalid_buffer", 0, NULL); + p_shm_buffer_take_ownership (buf); + p_shm_buffer_free (buf); + + p_shm_buffer_clear (NULL); + p_shm_buffer_free (NULL); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (pshmbuffer_general_test) +{ + p_libsys_init (); + + pchar test_buf[sizeof (test_str)]; + pchar *large_buf; + PShmBuffer *buffer = NULL; + + /* Buffer may be from the previous test on UNIX systems */ + buffer = p_shm_buffer_new ("pshm_test_buffer", 1024, NULL); + P_TEST_REQUIRE (buffer != NULL); + p_shm_buffer_take_ownership (buffer); + p_shm_buffer_free (buffer); + buffer = p_shm_buffer_new ("pshm_test_buffer", 1024, NULL); + P_TEST_REQUIRE (buffer != NULL); + + P_TEST_CHECK (p_shm_buffer_get_free_space (buffer, NULL) == 1024); + P_TEST_CHECK (p_shm_buffer_get_used_space (buffer, NULL) == 0); + p_shm_buffer_clear (buffer); + P_TEST_CHECK (p_shm_buffer_get_free_space (buffer, NULL) == 1024); + P_TEST_CHECK (p_shm_buffer_get_used_space (buffer, NULL) == 0); + + memset (test_buf, 0, sizeof (test_buf)); + + P_TEST_CHECK (p_shm_buffer_write (buffer, (ppointer) test_str, sizeof (test_str), NULL) == sizeof (test_str)); + P_TEST_CHECK (p_shm_buffer_get_free_space (buffer, NULL) == (1024 - sizeof (test_str))); + P_TEST_CHECK (p_shm_buffer_get_used_space (buffer, NULL) == sizeof (test_str)); + P_TEST_CHECK (p_shm_buffer_read (buffer, (ppointer) test_buf, sizeof (test_buf), NULL) == sizeof (test_str)); + P_TEST_CHECK (p_shm_buffer_read (buffer, (ppointer) test_buf, sizeof (test_buf), NULL) == 0); + + P_TEST_CHECK (strncmp (test_buf, test_str, sizeof (test_str)) == 0); + P_TEST_CHECK (p_shm_buffer_get_free_space (buffer, NULL) == 1024); + P_TEST_CHECK (p_shm_buffer_get_used_space (buffer, NULL) == 0); + + p_shm_buffer_clear (buffer); + + large_buf = (pchar *) p_malloc0 (2048); + P_TEST_REQUIRE (large_buf != NULL); + P_TEST_CHECK (p_shm_buffer_write (buffer, (ppointer) large_buf, 2048, NULL) == 0); + + p_free (large_buf); + p_shm_buffer_free (buffer); + + /* Test read-write positions */ + + buffer = p_shm_buffer_new ("pshm_test_buffer_small", 10, NULL); + P_TEST_REQUIRE (buffer != NULL); + p_shm_buffer_take_ownership (buffer); + p_shm_buffer_free (buffer); + buffer = p_shm_buffer_new ("pshm_test_buffer_small", 10, NULL); + P_TEST_REQUIRE (buffer != NULL); + + P_TEST_CHECK (p_shm_buffer_get_free_space (buffer, NULL) == 10); + P_TEST_CHECK (p_shm_buffer_get_used_space (buffer, NULL) == 0); + + /* Case 1: write position > read position */ + P_TEST_CHECK (p_shm_buffer_write (buffer, (ppointer) test_str_sm, sizeof (test_str_sm), NULL) == sizeof (test_str_sm)); + P_TEST_CHECK (p_shm_buffer_get_free_space (buffer, NULL) == (10 - sizeof (test_str_sm))); + P_TEST_CHECK (p_shm_buffer_get_used_space (buffer, NULL) == sizeof (test_str_sm)); + + /* Case 2: write position == read position */ + memset (test_buf, 0, sizeof (test_buf)); + P_TEST_CHECK (p_shm_buffer_read (buffer, (ppointer) test_buf, sizeof (test_buf), NULL) == sizeof (test_str_sm)); + P_TEST_CHECK (strncmp (test_buf, test_str_sm, sizeof (test_str_sm)) == 0); + P_TEST_CHECK (p_shm_buffer_get_free_space (buffer, NULL) == 10); + P_TEST_CHECK (p_shm_buffer_get_used_space (buffer, NULL) == 0); + + /* Case 3: write position < read position */ + P_TEST_CHECK (p_shm_buffer_write (buffer, (ppointer) test_str_sm, sizeof (test_str_sm), NULL) == sizeof (test_str_sm)); + P_TEST_CHECK (p_shm_buffer_get_free_space (buffer, NULL) == (10 - sizeof (test_str_sm))); + P_TEST_CHECK (p_shm_buffer_get_used_space (buffer, NULL) == sizeof (test_str_sm)); + + p_shm_buffer_free (buffer); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +#ifndef P_OS_HPUX +P_TEST_CASE_BEGIN (pshmbuffer_thread_test) +{ + p_libsys_init (); + + PShmBuffer *buffer = NULL; + PUThread *thr1, *thr2; + + /* Buffer may be from the previous test on UNIX systems */ + buffer = p_shm_buffer_new ("pshm_test_buffer", 1024, NULL); + P_TEST_REQUIRE (buffer != NULL); + p_shm_buffer_take_ownership (buffer); + p_shm_buffer_free (buffer); + + is_thread_exit = 0; + read_count = 0; + write_count = 0; + is_working = TRUE; + + buffer = p_shm_buffer_new ("pshm_test_buffer", 1024, NULL); + P_TEST_REQUIRE (buffer != NULL); + + thr1 = p_uthread_create ((PUThreadFunc) shm_buffer_test_write_thread, NULL, TRUE, NULL); + P_TEST_REQUIRE (thr1 != NULL); + + thr2 = p_uthread_create ((PUThreadFunc) shm_buffer_test_read_thread, NULL, TRUE, NULL); + P_TEST_REQUIRE (thr1 != NULL); + + p_uthread_sleep (5000); + + is_working = FALSE; + + P_TEST_CHECK (p_uthread_join (thr1) == 0); + P_TEST_CHECK (p_uthread_join (thr2) == 0); + + P_TEST_CHECK (read_count > 0); + P_TEST_CHECK (write_count > 0); + + p_shm_buffer_free (buffer); + p_uthread_unref (thr1); + p_uthread_unref (thr2); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () +#endif /* !P_OS_HPUX */ + +P_TEST_SUITE_BEGIN() +{ + P_TEST_SUITE_RUN_CASE (pshmbuffer_nomem_test); + P_TEST_SUITE_RUN_CASE (pshmbuffer_bad_input_test); + P_TEST_SUITE_RUN_CASE (pshmbuffer_general_test); + +#ifndef P_OS_HPUX + P_TEST_SUITE_RUN_CASE (pshmbuffer_thread_test); +#endif +} +P_TEST_SUITE_END() diff --git a/3rdparty/plibsys/tests/psocket_test.cpp b/3rdparty/plibsys/tests/psocket_test.cpp new file mode 100644 index 0000000..c1a44a4 --- /dev/null +++ b/3rdparty/plibsys/tests/psocket_test.cpp @@ -0,0 +1,1131 @@ +/* + * The MIT License + * + * Copyright (C) 2013-2019 Alexander Saprykin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "plibsys.h" +#include "ptestmacros.h" + +#include + +P_TEST_MODULE_INIT (); + +static pchar socket_data[] = "This is a socket test data!"; +volatile static pboolean is_sender_working = FALSE; +volatile static pboolean is_receiver_working = FALSE; + +typedef struct _SocketTestData { + puint16 sender_port; + puint16 receiver_port; + pboolean shutdown_channel; +} SocketTestData; + +extern "C" ppointer pmem_alloc (psize nbytes) +{ + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" ppointer pmem_realloc (ppointer block, psize nbytes) +{ + P_UNUSED (block); + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" void pmem_free (ppointer block) +{ + P_UNUSED (block); +} + +static void clean_error (PError **error) +{ + if (error == NULL || *error == NULL) + return; + + p_error_free (*error); + *error = NULL; +} + +static pboolean test_socket_address_directly (const PSocketAddress *addr, puint16 port) +{ + if (addr == NULL) + return FALSE; + + pchar *addr_str = p_socket_address_get_address (addr); + PSocketFamily remote_family = p_socket_address_get_family (addr); + puint16 remote_port = p_socket_address_get_port (addr); + psize remote_size = p_socket_address_get_native_size (addr); + + pboolean ret = (strcmp (addr_str, "127.0.0.1") == 0 && remote_family == P_SOCKET_FAMILY_INET && + remote_port == port && remote_size > 0) ? TRUE : FALSE; + + p_free (addr_str); + + return ret; +} + +static pboolean test_socket_address (PSocket *socket, puint16 port) +{ + /* Test remote address */ + PSocketAddress *remote_addr = p_socket_get_remote_address (socket, NULL); + + if (remote_addr == NULL) + return FALSE; + + pboolean ret = test_socket_address_directly (remote_addr, port); + + p_socket_address_free (remote_addr); + + return ret; +} + +static pboolean compare_socket_addresses (const PSocketAddress *addr1, const PSocketAddress *addr2) +{ + if (addr1 == NULL || addr2 == NULL) + return FALSE; + + pchar *addr_str1 = p_socket_address_get_address (addr1); + pchar *addr_str2 = p_socket_address_get_address (addr2); + + if (addr_str1 == NULL || addr_str2 == NULL) { + p_free (addr_str1); + p_free (addr_str2); + + return FALSE; + } + + pboolean addr_cmp = (strcmp (addr_str1, addr_str2) == 0 ? TRUE : FALSE); + + p_free (addr_str1); + p_free (addr_str2); + + if (addr_cmp == FALSE) + return FALSE; + + if (p_socket_address_get_family (addr1) != p_socket_address_get_family (addr2)) + return FALSE; + + if (p_socket_address_get_native_size (addr1) != p_socket_address_get_native_size (addr2)) + return FALSE; + + return TRUE; +} + +static void * udp_socket_sender_thread (void *arg) +{ + pint send_counter = 0; + + if (arg == NULL) + p_uthread_exit (-1); + + SocketTestData *data = (SocketTestData *) (arg); + + /* Create sender socket */ + PSocket *skt_sender = p_socket_new (P_SOCKET_FAMILY_INET, + P_SOCKET_TYPE_DATAGRAM, + P_SOCKET_PROTOCOL_UDP, + NULL); + + if (skt_sender == NULL) + p_uthread_exit (-1); + + PSocketAddress *addr_sender = p_socket_address_new ("127.0.0.1", data->sender_port); + + if (addr_sender == NULL) { + p_socket_free (skt_sender); + p_uthread_exit (-1); + } + + if (p_socket_bind (skt_sender, addr_sender, FALSE, NULL) == FALSE) { + p_socket_free (skt_sender); + p_socket_address_free (addr_sender); + p_uthread_exit (-1); + } else { + p_socket_address_free (addr_sender); + + PSocketAddress *local_addr = p_socket_get_local_address (skt_sender, NULL); + + if (local_addr == NULL) { + p_socket_free (skt_sender); + p_uthread_exit (-1); + } + + data->sender_port = p_socket_address_get_port (local_addr); + + p_socket_address_free (local_addr); + } + + p_socket_set_timeout (skt_sender, 50); + + /* Test that remote address is NULL */ + PSocketAddress *remote_addr = p_socket_get_remote_address (skt_sender, NULL); + + if (remote_addr != NULL) { + if (p_socket_address_is_any (remote_addr) == FALSE) { + p_socket_address_free (remote_addr); + p_socket_free (skt_sender); + p_uthread_exit (-1); + } else { + p_socket_address_free (remote_addr); + remote_addr = NULL; + } + } + + /* Test that we are not connected */ + if (p_socket_is_connected (skt_sender) == TRUE) { + p_socket_free (skt_sender); + p_uthread_exit (-1); + } + + while (is_sender_working == TRUE && data->receiver_port == 0) { + p_uthread_sleep (1); + continue; + } + + PSocketAddress *addr_receiver = NULL; + + if (data->receiver_port != 0) + addr_receiver = p_socket_address_new ("127.0.0.1", data->receiver_port); + + while (is_sender_working == TRUE) { + if (data->receiver_port == 0) + break; + + if (p_socket_send_to (skt_sender, + addr_receiver, + socket_data, + sizeof (socket_data), + NULL) == sizeof (socket_data)) + ++send_counter; + + p_uthread_sleep (1); + } + + p_socket_address_free (addr_receiver); + p_socket_free (skt_sender); + p_uthread_exit (send_counter); + + return NULL; +} + +static void * udp_socket_receiver_thread (void *arg) +{ + pchar recv_buffer[sizeof (socket_data) * 3]; + pint recv_counter = 0; + + if (arg == NULL) + p_uthread_exit (-1); + + SocketTestData *data = (SocketTestData *) (arg); + + /* Create receiving socket */ + PSocket *skt_receiver = p_socket_new (P_SOCKET_FAMILY_INET, + P_SOCKET_TYPE_DATAGRAM, + P_SOCKET_PROTOCOL_UDP, + NULL); + + if (skt_receiver == NULL) + p_uthread_exit (-1); + + p_socket_set_blocking (skt_receiver, FALSE); + + PSocketAddress *addr_receiver = p_socket_address_new ("127.0.0.1", data->receiver_port); + + if (addr_receiver == NULL) { + p_socket_free (skt_receiver); + p_uthread_exit (-1); + } + + if (p_socket_bind (skt_receiver, addr_receiver, TRUE, NULL) == FALSE) { + p_socket_free (skt_receiver); + p_socket_address_free (addr_receiver); + p_uthread_exit (-1); + } else { + p_socket_address_free (addr_receiver); + + PSocketAddress *local_addr = p_socket_get_local_address (skt_receiver, NULL); + + if (local_addr == NULL) { + p_socket_free (skt_receiver); + p_uthread_exit (-1); + } + + data->receiver_port = p_socket_address_get_port (local_addr); + + p_socket_address_free (local_addr); + } + + p_socket_set_timeout (skt_receiver, 50); + + /* Test that remote address is NULL */ + PSocketAddress *remote_addr = p_socket_get_remote_address (skt_receiver, NULL); + + if (remote_addr != NULL) { + if (p_socket_address_is_any (remote_addr) == FALSE) { + p_socket_address_free (remote_addr); + p_socket_free (skt_receiver); + p_uthread_exit (-1); + } else { + p_socket_address_free (remote_addr); + remote_addr = NULL; + } + } + + /* Test that we are not connected */ + if (p_socket_is_connected (skt_receiver) == TRUE) { + p_socket_free (skt_receiver); + p_uthread_exit (-1); + } + + while (is_receiver_working == TRUE) { + PSocketAddress *remote_addr = NULL; + + pssize received = p_socket_receive_from (skt_receiver, + &remote_addr, + recv_buffer, + sizeof (recv_buffer), + NULL); + + if (remote_addr != NULL && test_socket_address_directly (remote_addr, data->sender_port) == FALSE) { + p_socket_address_free (remote_addr); + break; + } + + p_socket_address_free (remote_addr); + + if (received == sizeof (socket_data)) + ++recv_counter; + else if (received > 0) { + p_socket_free (skt_receiver); + p_uthread_exit (-1); + } + + p_uthread_sleep (1); + } + + p_socket_free (skt_receiver); + p_uthread_exit (recv_counter); + + return NULL; +} + +static void * tcp_socket_sender_thread (void *arg) +{ + pint send_counter = 0; + psize send_total; + pssize send_now; + pboolean is_connected = FALSE; + + if (arg == NULL) + p_uthread_exit (-1); + + SocketTestData *data = (SocketTestData *) (arg); + + /* Create sender socket */ + PSocket *skt_sender = p_socket_new (P_SOCKET_FAMILY_INET, + P_SOCKET_TYPE_STREAM, + P_SOCKET_PROTOCOL_DEFAULT, + NULL); + + if (skt_sender == NULL) + p_uthread_exit (-1); + + p_socket_set_timeout (skt_sender, 2000); + + if (p_socket_get_fd (skt_sender) < 0) { + p_socket_free (skt_sender); + p_uthread_exit (-1); + } + + while (is_sender_working == TRUE && data->receiver_port == 0) { + p_uthread_sleep (1); + continue; + } + + PSocketAddress *addr_sender = p_socket_address_new ("127.0.0.1", data->sender_port); + + if (addr_sender == NULL) { + p_socket_free (skt_sender); + p_uthread_exit (-1); + } + + if (p_socket_bind (skt_sender, addr_sender, FALSE, NULL) == FALSE) { + p_socket_free (skt_sender); + p_socket_address_free (addr_sender); + p_uthread_exit (-1); + } else { + p_socket_address_free (addr_sender); + + PSocketAddress *local_addr = p_socket_get_local_address (skt_sender, NULL); + + if (local_addr == NULL) { + p_socket_free (skt_sender); + p_uthread_exit (-1); + } + + data->sender_port = p_socket_address_get_port (local_addr); + + p_socket_address_free (local_addr); + } + + send_total = 0; + send_now = 0; + + while (is_sender_working == TRUE && data->receiver_port == 0) { + p_uthread_sleep (1); + continue; + } + + PSocketAddress *addr_receiver = NULL; + + /* Try to connect in non-blocking mode */ + p_socket_set_blocking (skt_sender, FALSE); + + if (data->receiver_port != 0) { + addr_receiver = p_socket_address_new ("127.0.0.1", data->receiver_port); + is_connected = p_socket_connect (skt_sender, addr_receiver, NULL); + + if (is_connected == FALSE) { + if (p_socket_io_condition_wait (skt_sender, P_SOCKET_IO_CONDITION_POLLOUT, NULL) == TRUE && + p_socket_check_connect_result (skt_sender, NULL) == FALSE) { + p_socket_address_free (addr_receiver); + p_socket_free (skt_sender); + p_uthread_exit (-1); + } + } + + is_connected = p_socket_is_connected (skt_sender); + + if (is_connected == TRUE && p_socket_shutdown (skt_sender, + FALSE, + data->shutdown_channel, + NULL) == FALSE) + is_connected = FALSE; + } + + if (data->shutdown_channel == TRUE && p_socket_is_closed (skt_sender) == TRUE) { + p_socket_address_free (addr_receiver); + p_socket_free (skt_sender); + p_uthread_exit (-1); + } + + p_socket_set_blocking (skt_sender, TRUE); + + while (is_sender_working == TRUE) { + if (data->receiver_port == 0 || is_connected == FALSE) + break; + + if (test_socket_address (skt_sender, data->receiver_port) == FALSE) + break; + + if (data->shutdown_channel == FALSE && p_socket_is_connected (skt_sender) == FALSE) { + p_socket_address_free (addr_receiver); + p_socket_free (skt_sender); + p_uthread_exit (-1); + } + + send_now = p_socket_send (skt_sender, + socket_data + send_total, + sizeof (socket_data) - send_total, + NULL); + + if (send_now > 0) + send_total += (psize) send_now; + + if (send_total == sizeof (socket_data)) { + send_total = 0; + ++send_counter; + } + + p_uthread_sleep (1); + } + + if (p_socket_close (skt_sender, NULL) == FALSE) + send_counter = -1; + + p_socket_address_free (addr_receiver); + p_socket_free (skt_sender); + p_uthread_exit (send_counter); + + return NULL; +} + +static void * tcp_socket_receiver_thread (void *arg) +{ + pchar recv_buffer[sizeof (socket_data)]; + pint recv_counter = 0; + psize recv_total; + pssize recv_now; + + if (arg == NULL) + p_uthread_exit (-1); + + SocketTestData *data = (SocketTestData *) (arg); + + /* Create receiving socket */ + PSocket *skt_receiver = p_socket_new (P_SOCKET_FAMILY_INET, + P_SOCKET_TYPE_STREAM, + P_SOCKET_PROTOCOL_TCP, + NULL); + + if (skt_receiver == NULL) + p_uthread_exit (-1); + + PSocketAddress *addr_receiver = p_socket_address_new ("127.0.0.1", data->receiver_port); + + if (addr_receiver == NULL) { + p_socket_free (skt_receiver); + p_uthread_exit (-1); + } + + p_socket_set_timeout (skt_receiver, 2000); + + if (p_socket_bind (skt_receiver, addr_receiver, TRUE, NULL) == FALSE || + p_socket_listen (skt_receiver, NULL) == FALSE) { + p_socket_free (skt_receiver); + p_socket_address_free (addr_receiver); + p_uthread_exit (-1); + } else { + p_socket_address_free (addr_receiver); + + PSocketAddress *local_addr = p_socket_get_local_address (skt_receiver, NULL); + + if (local_addr == NULL) { + p_socket_free (skt_receiver); + p_uthread_exit (-1); + } + + data->receiver_port = p_socket_address_get_port (local_addr); + + p_socket_address_free (local_addr); + } + + PSocket *conn_socket = NULL; + recv_total = 0; + recv_now = 0; + + while (is_receiver_working == TRUE) { + if (conn_socket == NULL) { + conn_socket = p_socket_accept (skt_receiver, NULL); + + if (conn_socket == NULL) { + p_uthread_sleep (1); + continue; + } else { + /* On Syllable there is a bug in TCP which changes a local port + * of the client socket which connects to a server */ +#ifndef P_OS_SYLLABLE + if (test_socket_address (conn_socket, data->sender_port) == FALSE) + break; +#endif + + if (p_socket_shutdown (conn_socket, data->shutdown_channel, FALSE, NULL) == FALSE) + break; + + p_socket_set_timeout (conn_socket, 2000); + } + } + + if ((data->shutdown_channel == FALSE && p_socket_is_connected (conn_socket) == FALSE) || + (data->shutdown_channel == TRUE && p_socket_is_closed (conn_socket) == TRUE)) { + p_socket_free (conn_socket); + p_socket_free (skt_receiver); + p_uthread_exit (-1); + } + + recv_now = p_socket_receive (conn_socket, + recv_buffer + recv_total, + sizeof (recv_buffer) - recv_total, + NULL); + + if (recv_now > 0) + recv_total += (psize) recv_now; + + if (recv_total == sizeof (recv_buffer)) { + recv_total = 0; + + if (strncmp (recv_buffer, socket_data, sizeof (recv_buffer)) == 0) + ++recv_counter; + + memset (recv_buffer, 0, sizeof (recv_buffer)); + } + + p_uthread_sleep (1); + } + + if (p_socket_close (skt_receiver, NULL) == FALSE) + recv_counter = -1; + + p_socket_free (conn_socket); + p_socket_free (skt_receiver); + + p_uthread_exit (recv_counter); + + return NULL; +} + +P_TEST_CASE_BEGIN (psocket_nomem_test) +{ + p_libsys_init (); + + PSocket *socket = p_socket_new (P_SOCKET_FAMILY_INET, + P_SOCKET_TYPE_DATAGRAM, + P_SOCKET_PROTOCOL_UDP, + NULL); + P_TEST_CHECK (socket != NULL); + + PSocketAddress *sock_addr = p_socket_address_new ("127.0.0.1", 32211); + + P_TEST_CHECK (sock_addr != NULL); + P_TEST_CHECK (p_socket_bind (socket, sock_addr, TRUE, NULL) == TRUE); + + p_socket_address_free (sock_addr); + + p_socket_set_timeout (socket, 1000); + sock_addr = p_socket_address_new ("127.0.0.1", 32215); + P_TEST_CHECK (sock_addr != NULL); + P_TEST_CHECK (p_socket_connect (socket, sock_addr, NULL) == TRUE); + + p_socket_address_free (sock_addr); + + PMemVTable vtable; + + vtable.free = pmem_free; + vtable.malloc = pmem_alloc; + vtable.realloc = pmem_realloc; + + P_TEST_CHECK (p_mem_set_vtable (&vtable) == TRUE); + + P_TEST_CHECK (p_socket_new (P_SOCKET_FAMILY_INET, + P_SOCKET_TYPE_DATAGRAM, + P_SOCKET_PROTOCOL_UDP, + NULL) == NULL); + P_TEST_CHECK (p_socket_new_from_fd (p_socket_get_fd (socket), NULL) == NULL); + P_TEST_CHECK (p_socket_get_local_address (socket, NULL) == NULL); + P_TEST_CHECK (p_socket_get_remote_address (socket, NULL) == NULL); + + p_mem_restore_vtable (); + + p_socket_close (socket, NULL); + p_socket_free (socket); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (psocket_bad_input_test) +{ + p_libsys_init (); + + PError *error = NULL; + + P_TEST_CHECK (p_socket_new_from_fd (-1, &error) == NULL); + P_TEST_CHECK (error != NULL); + clean_error (&error); + + P_TEST_CHECK (p_socket_new (P_SOCKET_FAMILY_INET, + (PSocketType) -1, + P_SOCKET_PROTOCOL_TCP, + NULL) == NULL); + /* Syllable doesn't validate socket family */ +#ifndef P_OS_SYLLABLE + P_TEST_CHECK (p_socket_new ((PSocketFamily) -1, + P_SOCKET_TYPE_SEQPACKET, + P_SOCKET_PROTOCOL_TCP, + NULL) == NULL); +#endif + P_TEST_CHECK (p_socket_new (P_SOCKET_FAMILY_UNKNOWN, + P_SOCKET_TYPE_UNKNOWN, + P_SOCKET_PROTOCOL_UNKNOWN, + &error) == NULL); + P_TEST_CHECK (p_socket_new_from_fd (1, NULL) == NULL); + P_TEST_CHECK (error != NULL); + clean_error (&error); + + P_TEST_CHECK (p_socket_get_fd (NULL) == -1); + P_TEST_CHECK (p_socket_get_family (NULL) == P_SOCKET_FAMILY_UNKNOWN); + P_TEST_CHECK (p_socket_get_type (NULL) == P_SOCKET_TYPE_UNKNOWN); + P_TEST_CHECK (p_socket_get_protocol (NULL) == P_SOCKET_PROTOCOL_UNKNOWN); + P_TEST_CHECK (p_socket_get_keepalive (NULL) == FALSE); + P_TEST_CHECK (p_socket_get_blocking (NULL) == FALSE); + P_TEST_CHECK (p_socket_get_timeout (NULL) == -1); + P_TEST_CHECK (p_socket_get_listen_backlog (NULL) == -1); + P_TEST_CHECK (p_socket_io_condition_wait (NULL, P_SOCKET_IO_CONDITION_POLLIN, NULL) == FALSE); + P_TEST_CHECK (p_socket_io_condition_wait (NULL, P_SOCKET_IO_CONDITION_POLLOUT, NULL) == FALSE); + + P_TEST_CHECK (p_socket_get_local_address (NULL, &error) == NULL); + P_TEST_CHECK (error != NULL); + clean_error (&error); + + P_TEST_CHECK (p_socket_get_remote_address (NULL, &error) == NULL); + P_TEST_CHECK (error != NULL); + clean_error (&error); + + P_TEST_CHECK (p_socket_is_connected (NULL) == FALSE); + P_TEST_CHECK (p_socket_is_closed (NULL) == TRUE); + + P_TEST_CHECK (p_socket_check_connect_result (NULL, &error) == FALSE); + P_TEST_CHECK (error != NULL); + clean_error (&error); + + p_socket_set_keepalive (NULL, FALSE); + p_socket_set_blocking (NULL, FALSE); + p_socket_set_timeout (NULL, 0); + p_socket_set_listen_backlog (NULL, 0); + + P_TEST_CHECK (p_socket_bind (NULL, NULL, FALSE, &error) == FALSE); + P_TEST_CHECK (error != NULL); + clean_error (&error); + + P_TEST_CHECK (p_socket_connect (NULL, NULL, &error) == FALSE); + P_TEST_CHECK (error != NULL); + clean_error (&error); + + P_TEST_CHECK (p_socket_listen (NULL, &error) == FALSE); + P_TEST_CHECK (error != NULL); + clean_error (&error); + + P_TEST_CHECK (p_socket_accept (NULL, &error) == NULL); + P_TEST_CHECK (error != NULL); + clean_error (&error); + + P_TEST_CHECK (p_socket_receive (NULL, NULL, 0, &error) == -1); + P_TEST_CHECK (error != NULL); + clean_error (&error); + + P_TEST_CHECK (p_socket_receive_from (NULL, NULL, NULL, 0, &error) == -1); + P_TEST_CHECK (error != NULL); + clean_error (&error); + + P_TEST_CHECK (p_socket_send (NULL, NULL, 0, &error) == -1); + P_TEST_CHECK (error != NULL); + clean_error (&error); + + P_TEST_CHECK (p_socket_send_to (NULL, NULL, NULL, 0, &error) == -1); + P_TEST_CHECK (error != NULL); + clean_error (&error); + + P_TEST_CHECK (p_socket_close (NULL, &error) == FALSE); + P_TEST_CHECK (error != NULL); + clean_error (&error); + + P_TEST_CHECK (p_socket_shutdown (NULL, FALSE, FALSE, &error) == FALSE); + P_TEST_CHECK (error != NULL); + clean_error (&error); + + P_TEST_CHECK (p_socket_set_buffer_size (NULL, P_SOCKET_DIRECTION_RCV, 0, &error) == FALSE); + P_TEST_CHECK (error != NULL); + clean_error (&error); + + P_TEST_CHECK (p_socket_set_buffer_size (NULL, P_SOCKET_DIRECTION_SND, 0, &error) == FALSE); + P_TEST_CHECK (error != NULL); + clean_error (&error); + + p_socket_free (NULL); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (psocket_general_udp_test) +{ + p_libsys_init (); + + /* Test UDP socket */ + PSocket *socket = p_socket_new (P_SOCKET_FAMILY_INET, + P_SOCKET_TYPE_DATAGRAM, + P_SOCKET_PROTOCOL_UDP, + NULL); + + P_TEST_CHECK (socket != NULL); + P_TEST_CHECK (p_socket_get_family (socket) == P_SOCKET_FAMILY_INET); + P_TEST_CHECK (p_socket_get_fd (socket) >= 0); + P_TEST_CHECK (p_socket_get_listen_backlog (socket) == 5); + P_TEST_CHECK (p_socket_get_timeout (socket) == 0); + + /* On some operating systems (i.e. OpenVMS) remote address is not NULL */ + PSocketAddress *remote_addr = p_socket_get_remote_address (socket, NULL); + + if (remote_addr != NULL) { + P_TEST_CHECK (p_socket_address_is_any (remote_addr) == TRUE); + p_socket_address_free (remote_addr); + remote_addr = NULL; + } + + P_TEST_CHECK (p_socket_get_protocol (socket) == P_SOCKET_PROTOCOL_UDP); + P_TEST_CHECK (p_socket_get_blocking (socket) == TRUE); + P_TEST_CHECK (p_socket_get_type (socket) == P_SOCKET_TYPE_DATAGRAM); + P_TEST_CHECK (p_socket_get_keepalive (socket) == FALSE); + P_TEST_CHECK (p_socket_is_closed (socket) == FALSE); + + p_socket_set_listen_backlog (socket, 12); + p_socket_set_timeout (socket, -10); + P_TEST_CHECK (p_socket_get_timeout (socket) == 0); + p_socket_set_timeout (socket, 10); + + P_TEST_CHECK (p_socket_get_listen_backlog (socket) == 12); + P_TEST_CHECK (p_socket_get_timeout (socket) == 10); + + PSocketAddress *sock_addr = p_socket_address_new ("127.0.0.1", 32111); + P_TEST_CHECK (sock_addr != NULL); + + P_TEST_CHECK (p_socket_bind (socket, sock_addr, TRUE, NULL) == TRUE); + + /* Test creating socket from descriptor */ + PSocket *fd_socket = p_socket_new_from_fd (p_socket_get_fd (socket), NULL); + P_TEST_CHECK (fd_socket != NULL); + P_TEST_CHECK (p_socket_get_family (fd_socket) == P_SOCKET_FAMILY_INET); + P_TEST_CHECK (p_socket_get_fd (fd_socket) >= 0); + P_TEST_CHECK (p_socket_get_listen_backlog (fd_socket) == 5); + P_TEST_CHECK (p_socket_get_timeout (fd_socket) == 0); + + remote_addr = p_socket_get_remote_address (fd_socket, NULL); + + if (remote_addr != NULL) { + P_TEST_CHECK (p_socket_address_is_any (remote_addr) == TRUE); + p_socket_address_free (remote_addr); + remote_addr = NULL; + } + + P_TEST_CHECK (p_socket_get_protocol (fd_socket) == P_SOCKET_PROTOCOL_UDP); + P_TEST_CHECK (p_socket_get_blocking (fd_socket) == TRUE); + P_TEST_CHECK (p_socket_get_type (fd_socket) == P_SOCKET_TYPE_DATAGRAM); + P_TEST_CHECK (p_socket_get_keepalive (fd_socket) == FALSE); + P_TEST_CHECK (p_socket_is_closed (fd_socket) == FALSE); + + p_socket_set_keepalive (fd_socket, FALSE); + P_TEST_CHECK (p_socket_get_keepalive (fd_socket) == FALSE); + + p_socket_set_keepalive (fd_socket, TRUE); + p_socket_set_keepalive (fd_socket, FALSE); + P_TEST_CHECK (p_socket_get_keepalive (fd_socket) == FALSE); + + /* Test UDP local address */ + PSocketAddress *addr = p_socket_get_local_address (socket, NULL); + P_TEST_CHECK (addr != NULL); + + P_TEST_CHECK (compare_socket_addresses (sock_addr, addr) == TRUE); + + p_socket_address_free (sock_addr); + p_socket_address_free (addr); + + /* Test UDP connecting to remote address */ + p_socket_set_timeout (socket, 1000); + addr = p_socket_address_new ("127.0.0.1", 32115); + P_TEST_CHECK (addr != NULL); + P_TEST_CHECK (p_socket_connect (socket, addr, NULL) == TRUE); + + P_TEST_CHECK (p_socket_io_condition_wait (socket, P_SOCKET_IO_CONDITION_POLLIN, NULL) == FALSE); + P_TEST_CHECK (p_socket_io_condition_wait (socket, P_SOCKET_IO_CONDITION_POLLOUT, NULL) == TRUE); + + sock_addr = p_socket_get_remote_address (socket, NULL); + + /* Syllable doesn't support getpeername() for UDP sockets */ +#ifdef P_OS_SYLLABLE + P_TEST_CHECK (sock_addr == NULL); + sock_addr = p_socket_address_new ("127.0.0.1", 32115); + P_TEST_CHECK (addr != NULL); +#else + P_TEST_CHECK (sock_addr != NULL); + P_TEST_CHECK (compare_socket_addresses (sock_addr, addr) == TRUE); +#endif + + /* Not supported on Syllable */ +#ifndef P_OS_SYLLABLE + P_TEST_CHECK (p_socket_set_buffer_size (socket, P_SOCKET_DIRECTION_RCV, 72 * 1024, NULL) == TRUE); + P_TEST_CHECK (p_socket_set_buffer_size (socket, P_SOCKET_DIRECTION_SND, 72 * 1024, NULL) == TRUE); + P_TEST_CHECK (p_socket_check_connect_result (socket, NULL) == TRUE); +#endif + + P_TEST_CHECK (p_socket_is_connected (socket) == TRUE); + P_TEST_CHECK (p_socket_close (socket, NULL) == TRUE); + + pchar sock_buf[10]; + + P_TEST_CHECK (p_socket_bind (socket, sock_addr, TRUE, NULL) == FALSE); + P_TEST_CHECK (p_socket_connect (socket, addr, NULL) == FALSE); + P_TEST_CHECK (p_socket_listen (socket, NULL) == FALSE); + P_TEST_CHECK (p_socket_accept (socket, NULL) == FALSE); + P_TEST_CHECK (p_socket_receive (socket, sock_buf, sizeof (sock_buf), NULL) == -1); + P_TEST_CHECK (p_socket_receive_from (socket, NULL, sock_buf, sizeof (sock_buf), NULL) == -1); + P_TEST_CHECK (p_socket_send (socket, sock_buf, sizeof (sock_buf), NULL) == -1); + P_TEST_CHECK (p_socket_send_to (socket, addr, sock_buf, sizeof (sock_buf), NULL) == -1); + P_TEST_CHECK (p_socket_shutdown (socket, TRUE, TRUE, NULL) == FALSE); + P_TEST_CHECK (p_socket_get_local_address (socket, NULL) == NULL); + P_TEST_CHECK (p_socket_check_connect_result (socket, NULL) == FALSE); + P_TEST_CHECK (p_socket_get_fd (socket) == -1); + P_TEST_CHECK (p_socket_is_connected (socket) == FALSE); + P_TEST_CHECK (p_socket_is_closed (socket) == TRUE); + + p_socket_set_keepalive (socket, TRUE); + P_TEST_CHECK (p_socket_get_keepalive (socket) == FALSE); + + P_TEST_CHECK (p_socket_io_condition_wait (socket, P_SOCKET_IO_CONDITION_POLLIN, NULL) == FALSE); + P_TEST_CHECK (p_socket_io_condition_wait (socket, P_SOCKET_IO_CONDITION_POLLOUT, NULL) == FALSE); + + P_TEST_CHECK (p_socket_set_buffer_size (socket, P_SOCKET_DIRECTION_RCV, 72 * 1024, NULL) == FALSE); + P_TEST_CHECK (p_socket_set_buffer_size (socket, P_SOCKET_DIRECTION_SND, 72 * 1024, NULL) == FALSE); + + p_socket_address_free (sock_addr); + p_socket_address_free (addr); + p_socket_free (socket); + p_socket_free (fd_socket); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (psocket_general_tcp_test) +{ + p_libsys_init (); + + /* Test TCP socket */ + PSocket *socket = p_socket_new (P_SOCKET_FAMILY_INET, + P_SOCKET_TYPE_STREAM, + P_SOCKET_PROTOCOL_TCP, + NULL); + p_socket_set_blocking (socket, FALSE); + p_socket_set_listen_backlog (socket, 11); + + p_socket_set_timeout (socket, -12); + P_TEST_CHECK (p_socket_get_timeout (socket) == 0); + p_socket_set_timeout (socket, 12); + + P_TEST_CHECK (socket != NULL); + P_TEST_CHECK (p_socket_get_family (socket) == P_SOCKET_FAMILY_INET); + P_TEST_CHECK (p_socket_get_fd (socket) >= 0); + P_TEST_CHECK (p_socket_get_listen_backlog (socket) == 11); + P_TEST_CHECK (p_socket_get_timeout (socket) == 12); + P_TEST_CHECK (p_socket_get_remote_address (socket, NULL) == NULL); + P_TEST_CHECK (p_socket_get_protocol (socket) == P_SOCKET_PROTOCOL_TCP); + P_TEST_CHECK (p_socket_get_blocking (socket) == FALSE); + P_TEST_CHECK (p_socket_get_type (socket) == P_SOCKET_TYPE_STREAM); + P_TEST_CHECK (p_socket_get_keepalive (socket) == FALSE); + P_TEST_CHECK (p_socket_is_closed (socket) == FALSE); + + p_socket_set_keepalive (socket, FALSE); + P_TEST_CHECK (p_socket_get_keepalive (socket) == FALSE); + + p_socket_set_keepalive (socket, TRUE); + p_socket_set_keepalive (socket, FALSE); + P_TEST_CHECK (p_socket_get_keepalive (socket) == FALSE); + + PSocketAddress *sock_addr = p_socket_address_new ("127.0.0.1", 0); + P_TEST_CHECK (sock_addr != NULL); + + P_TEST_CHECK (p_socket_bind (socket, sock_addr, TRUE, NULL) == TRUE); + + PSocketAddress *addr = p_socket_get_local_address (socket, NULL); + P_TEST_CHECK (addr != NULL); + + P_TEST_CHECK (compare_socket_addresses (sock_addr, addr) == TRUE); + + P_TEST_CHECK (p_socket_set_buffer_size (socket, P_SOCKET_DIRECTION_RCV, 72 * 1024, NULL) == TRUE); + P_TEST_CHECK (p_socket_set_buffer_size (socket, P_SOCKET_DIRECTION_SND, 72 * 1024, NULL) == TRUE); + + /* In case of success p_socket_check_connect_result() marks socket as connected */ + P_TEST_CHECK (p_socket_is_connected (socket) == FALSE); + P_TEST_CHECK (p_socket_check_connect_result (socket, NULL) == TRUE); + P_TEST_CHECK (p_socket_close (socket, NULL) == TRUE); + + pchar sock_buf[10]; + + P_TEST_CHECK (p_socket_bind (socket, sock_addr, TRUE, NULL) == FALSE); + P_TEST_CHECK (p_socket_connect (socket, addr, NULL) == FALSE); + P_TEST_CHECK (p_socket_listen (socket, NULL) == FALSE); + P_TEST_CHECK (p_socket_accept (socket, NULL) == FALSE); + P_TEST_CHECK (p_socket_receive (socket, sock_buf, sizeof (sock_buf), NULL) == -1); + P_TEST_CHECK (p_socket_receive_from (socket, NULL, sock_buf, sizeof (sock_buf), NULL) == -1); + P_TEST_CHECK (p_socket_send (socket, sock_buf, sizeof (sock_buf), NULL) == -1); + P_TEST_CHECK (p_socket_send_to (socket, addr, sock_buf, sizeof (sock_buf), NULL) == -1); + P_TEST_CHECK (p_socket_shutdown (socket, TRUE, TRUE, NULL) == FALSE); + P_TEST_CHECK (p_socket_get_local_address (socket, NULL) == NULL); + P_TEST_CHECK (p_socket_check_connect_result (socket, NULL) == FALSE); + P_TEST_CHECK (p_socket_is_closed (socket) == TRUE); + P_TEST_CHECK (p_socket_get_fd (socket) == -1); + + p_socket_set_keepalive (socket, TRUE); + P_TEST_CHECK (p_socket_get_keepalive (socket) == FALSE); + + P_TEST_CHECK (p_socket_io_condition_wait (socket, P_SOCKET_IO_CONDITION_POLLIN, NULL) == FALSE); + P_TEST_CHECK (p_socket_io_condition_wait (socket, P_SOCKET_IO_CONDITION_POLLOUT, NULL) == FALSE); + + P_TEST_CHECK (p_socket_set_buffer_size (socket, P_SOCKET_DIRECTION_RCV, 72 * 1024, NULL) == FALSE); + P_TEST_CHECK (p_socket_set_buffer_size (socket, P_SOCKET_DIRECTION_SND, 72 * 1024, NULL) == FALSE); + + p_socket_address_free (sock_addr); + p_socket_address_free (addr); + + p_socket_free (socket); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (psocket_udp_test) +{ + p_libsys_init (); + + is_sender_working = TRUE; + is_receiver_working = TRUE; + + SocketTestData data; + data.receiver_port = 0; + data.sender_port = 0; + data.shutdown_channel = FALSE; + + PUThread *receiver_thr = p_uthread_create ((PUThreadFunc) udp_socket_receiver_thread, + (ppointer) &data, + TRUE, + NULL); + + PUThread *sender_thr = p_uthread_create ((PUThreadFunc) udp_socket_sender_thread, + (ppointer) &data, + TRUE, + NULL); + + P_TEST_CHECK (sender_thr != NULL); + P_TEST_CHECK (receiver_thr != NULL); + + p_uthread_sleep (8000); + + is_sender_working = FALSE; + pint send_counter = p_uthread_join (sender_thr); + + p_uthread_sleep (2000); + + is_receiver_working = FALSE; + pint recv_counter = p_uthread_join (receiver_thr); + + P_TEST_CHECK (send_counter > 0); + P_TEST_CHECK (recv_counter > 0); + + p_uthread_unref (sender_thr); + p_uthread_unref (receiver_thr); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (psocket_tcp_test) +{ + p_libsys_init (); + + is_sender_working = TRUE; + is_receiver_working = TRUE; + + SocketTestData data; + data.receiver_port = 0; + data.sender_port = 0; + data.shutdown_channel = FALSE; + + PUThread *receiver_thr = p_uthread_create ((PUThreadFunc) tcp_socket_receiver_thread, + (ppointer) &data, + TRUE, + NULL); + + PUThread *sender_thr = p_uthread_create ((PUThreadFunc) tcp_socket_sender_thread, + (ppointer) &data, + TRUE, + NULL); + + P_TEST_CHECK (receiver_thr != NULL); + P_TEST_CHECK (sender_thr != NULL); + + p_uthread_sleep (8000); + + is_sender_working = FALSE; + pint send_counter = p_uthread_join (sender_thr); + + p_uthread_sleep (2000); + + is_receiver_working = FALSE; + pint recv_counter = p_uthread_join (receiver_thr); + + P_TEST_CHECK (send_counter > 0); + P_TEST_CHECK (recv_counter > 0); + + p_uthread_unref (sender_thr); + p_uthread_unref (receiver_thr); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (psocket_shutdown_test) +{ + p_libsys_init (); + + is_sender_working = TRUE; + is_receiver_working = TRUE; + + SocketTestData data; + data.receiver_port = 0; + data.sender_port = 0; + data.shutdown_channel = TRUE; + + PUThread *receiver_thr = p_uthread_create ((PUThreadFunc) tcp_socket_receiver_thread, + (ppointer) &data, + TRUE, + NULL); + + PUThread *sender_thr = p_uthread_create ((PUThreadFunc) tcp_socket_sender_thread, + (ppointer) &data, + TRUE, + NULL); + + P_TEST_CHECK (receiver_thr != NULL); + P_TEST_CHECK (sender_thr != NULL); + + p_uthread_sleep (8000); + + is_sender_working = FALSE; + pint send_counter = p_uthread_join (sender_thr); + + p_uthread_sleep (2000); + + is_receiver_working = FALSE; + pint recv_counter = p_uthread_join (receiver_thr); + + P_TEST_CHECK (send_counter == 0); + P_TEST_CHECK (recv_counter == 0); + + p_uthread_unref (sender_thr); + p_uthread_unref (receiver_thr); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_SUITE_BEGIN() +{ + P_TEST_SUITE_RUN_CASE (psocket_nomem_test); + P_TEST_SUITE_RUN_CASE (psocket_bad_input_test); + P_TEST_SUITE_RUN_CASE (psocket_general_udp_test); + P_TEST_SUITE_RUN_CASE (psocket_general_tcp_test); + P_TEST_SUITE_RUN_CASE (psocket_udp_test); + P_TEST_SUITE_RUN_CASE (psocket_tcp_test); + P_TEST_SUITE_RUN_CASE (psocket_shutdown_test); +} +P_TEST_SUITE_END() diff --git a/3rdparty/plibsys/tests/psocketaddress_test.cpp b/3rdparty/plibsys/tests/psocketaddress_test.cpp new file mode 100644 index 0000000..2085725 --- /dev/null +++ b/3rdparty/plibsys/tests/psocketaddress_test.cpp @@ -0,0 +1,338 @@ +/* + * The MIT License + * + * Copyright (C) 2013-2017 Alexander Saprykin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "plibsys.h" +#include "ptestmacros.h" + +#include + +P_TEST_MODULE_INIT (); + +extern "C" ppointer pmem_alloc (psize nbytes) +{ + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" ppointer pmem_realloc (ppointer block, psize nbytes) +{ + P_UNUSED (block); + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" void pmem_free (ppointer block) +{ + P_UNUSED (block); +} + +P_TEST_CASE_BEGIN (psocketaddress_nomem_test) +{ + p_libsys_init (); + + PSocketAddress *sock_addr = p_socket_address_new ("192.168.0.1", 1058); + P_TEST_CHECK (sock_addr != NULL); + + psize native_size = p_socket_address_get_native_size (sock_addr); + P_TEST_CHECK (native_size > 0); + + ppointer addr_buf = p_malloc0 (native_size); + P_TEST_CHECK (addr_buf != NULL); + + P_TEST_CHECK (p_socket_address_to_native (sock_addr, addr_buf, native_size - 1) == FALSE); + P_TEST_CHECK (p_socket_address_to_native (sock_addr, addr_buf, native_size) == TRUE); + p_socket_address_free (sock_addr); + + PSocketAddress *sock_addr6; + psize native_size6; + ppointer addr_buf6; + + if (p_socket_address_is_ipv6_supported ()) { + sock_addr6 = p_socket_address_new ("2001:cdba:345f:24ab:fe45:5423:3257:9652", 1058); + P_TEST_CHECK (sock_addr6 != NULL); + + native_size6 = p_socket_address_get_native_size (sock_addr6); + P_TEST_CHECK (native_size6 > 0); + + addr_buf6 = p_malloc0 (native_size6); + P_TEST_CHECK (addr_buf6 != NULL); + + P_TEST_CHECK (p_socket_address_to_native (sock_addr6, addr_buf6, native_size6 - 1) == FALSE); + P_TEST_CHECK (p_socket_address_to_native (sock_addr6, addr_buf6, native_size6) == TRUE); + p_socket_address_free (sock_addr6); + } + + PMemVTable vtable; + + vtable.free = pmem_free; + vtable.malloc = pmem_alloc; + vtable.realloc = pmem_realloc; + + P_TEST_CHECK (p_mem_set_vtable (&vtable) == TRUE); + + P_TEST_CHECK (p_socket_address_new ("192.168.0.1", 1058) == NULL); + P_TEST_CHECK (p_socket_address_new_any (P_SOCKET_FAMILY_INET, 1058) == NULL); + P_TEST_CHECK (p_socket_address_new_loopback (P_SOCKET_FAMILY_INET, 1058) == NULL); + P_TEST_CHECK (p_socket_address_new_from_native (addr_buf, native_size) == NULL); + + if (p_socket_address_is_ipv6_supported ()) + P_TEST_CHECK (p_socket_address_new_from_native (addr_buf6, native_size6) == NULL); + + p_mem_restore_vtable (); + + P_TEST_CHECK (p_socket_address_new_from_native (addr_buf, native_size - 1) == NULL); + + if (p_socket_address_is_ipv6_supported ()) { + P_TEST_CHECK (p_socket_address_new_from_native (addr_buf6, native_size6 - 1) == NULL); + p_free (addr_buf6); + } + + p_free (addr_buf); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (psocketaddress_bad_input_test) +{ + p_libsys_init (); + + P_TEST_CHECK (p_socket_address_new_from_native (NULL, 0) == NULL); + P_TEST_CHECK (p_socket_address_new (NULL, 0) == NULL); + P_TEST_CHECK (p_socket_address_new ("bad_address", 0) == NULL); + P_TEST_CHECK (p_socket_address_new_any (P_SOCKET_FAMILY_UNKNOWN, 0) == NULL); + P_TEST_CHECK (p_socket_address_new_loopback (P_SOCKET_FAMILY_UNKNOWN, 0) == NULL); + P_TEST_CHECK (p_socket_address_to_native (NULL, NULL, 0) == FALSE); + P_TEST_CHECK (p_socket_address_get_native_size (NULL) == 0); + P_TEST_CHECK (p_socket_address_get_family (NULL) == P_SOCKET_FAMILY_UNKNOWN); + P_TEST_CHECK (p_socket_address_get_address (NULL) == NULL); + P_TEST_CHECK (p_socket_address_get_port (NULL) == 0); + P_TEST_CHECK (p_socket_address_get_flow_info (NULL) == 0); + P_TEST_CHECK (p_socket_address_get_scope_id (NULL) == 0); + P_TEST_CHECK (p_socket_address_is_any (NULL) == FALSE); + P_TEST_CHECK (p_socket_address_is_loopback (NULL) == FALSE); + + p_socket_address_set_flow_info (NULL, 0); + p_socket_address_set_scope_id (NULL, 0); + p_socket_address_free (NULL); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (psocketaddress_general_test) +{ + p_libsys_init (); + + /* Test IPv4 LAN address */ + PSocketAddress *addr = p_socket_address_new ("192.168.0.1", 2345); + + P_TEST_REQUIRE (addr != NULL); + P_TEST_CHECK (p_socket_address_is_loopback (addr) == FALSE); + P_TEST_CHECK (p_socket_address_is_any (addr) == FALSE); + P_TEST_CHECK (p_socket_address_get_family (addr) == P_SOCKET_FAMILY_INET); + P_TEST_CHECK (p_socket_address_get_port (addr) == 2345); + P_TEST_CHECK (p_socket_address_get_native_size (addr) > 0); + P_TEST_CHECK (p_socket_address_get_flow_info (addr) == 0); + P_TEST_CHECK (p_socket_address_get_scope_id (addr) == 0); + + pchar *addr_str = p_socket_address_get_address (addr); + + P_TEST_REQUIRE (addr_str != NULL); + P_TEST_CHECK (strcmp (addr_str, "192.168.0.1") == 0); + + p_free (addr_str); + p_socket_address_free (addr); + + if (p_socket_address_is_ipv6_supported ()) { + /* Test IPv6 LAN address */ + addr = p_socket_address_new ("2001:cdba:345f:24ab:fe45:5423:3257:9652", 2345); + + P_TEST_REQUIRE (addr != NULL); + P_TEST_CHECK (p_socket_address_is_loopback (addr) == FALSE); + P_TEST_CHECK (p_socket_address_is_any (addr) == FALSE); + P_TEST_CHECK (p_socket_address_get_family (addr) == P_SOCKET_FAMILY_INET6); + P_TEST_CHECK (p_socket_address_get_port (addr) == 2345); + P_TEST_CHECK (p_socket_address_get_native_size (addr) > 0); + P_TEST_CHECK (p_socket_address_get_flow_info (addr) == 0); + P_TEST_CHECK (p_socket_address_get_scope_id (addr) == 0); + + addr_str = p_socket_address_get_address (addr); + + P_TEST_REQUIRE (addr_str != NULL); + P_TEST_CHECK (strcmp (addr_str, "2001:cdba:345f:24ab:fe45:5423:3257:9652") == 0); + + p_free (addr_str); + p_socket_address_free (addr); + } + + /* Test IPv4 loopback address */ + addr = p_socket_address_new_loopback (P_SOCKET_FAMILY_INET, 2345); + + P_TEST_REQUIRE (addr != NULL); + P_TEST_CHECK (p_socket_address_is_loopback (addr) == TRUE); + P_TEST_CHECK (p_socket_address_is_any (addr) == FALSE); + P_TEST_CHECK (p_socket_address_get_family (addr) == P_SOCKET_FAMILY_INET); + P_TEST_CHECK (p_socket_address_get_port (addr) == 2345); + P_TEST_CHECK (p_socket_address_get_native_size (addr) > 0); + P_TEST_CHECK (p_socket_address_get_flow_info (addr) == 0); + P_TEST_CHECK (p_socket_address_get_scope_id (addr) == 0); + + p_socket_address_free (addr); + + if (p_socket_address_is_ipv6_supported ()) { + /* Test IPv6 loopback address */ + addr = p_socket_address_new_loopback (P_SOCKET_FAMILY_INET6, 2345); + + P_TEST_REQUIRE (addr != NULL); + P_TEST_CHECK (p_socket_address_is_loopback (addr) == TRUE); + P_TEST_CHECK (p_socket_address_is_any (addr) == FALSE); + P_TEST_CHECK (p_socket_address_get_family (addr) == P_SOCKET_FAMILY_INET6); + P_TEST_CHECK (p_socket_address_get_port (addr) == 2345); + P_TEST_CHECK (p_socket_address_get_native_size (addr) > 0); + P_TEST_CHECK (p_socket_address_get_flow_info (addr) == 0); + P_TEST_CHECK (p_socket_address_get_scope_id (addr) == 0); + + p_socket_address_free (addr); + } + + /* Test IPv4 any interface */ + addr = p_socket_address_new_any (P_SOCKET_FAMILY_INET, 2345); + + P_TEST_REQUIRE (addr != NULL); + P_TEST_CHECK (p_socket_address_is_loopback (addr) == FALSE); + P_TEST_CHECK (p_socket_address_is_any (addr) == TRUE); + P_TEST_CHECK (p_socket_address_get_family (addr) == P_SOCKET_FAMILY_INET); + P_TEST_CHECK (p_socket_address_get_port (addr) == 2345); + P_TEST_CHECK (p_socket_address_get_native_size (addr) > 0); + P_TEST_CHECK (p_socket_address_get_flow_info (addr) == 0); + P_TEST_CHECK (p_socket_address_get_scope_id (addr) == 0); + + psize native_size = p_socket_address_get_native_size (addr); + + p_socket_address_free (addr); + + /* Test IPv4 native raw data */ + ppointer native_buf = p_malloc0 (native_size); + P_TEST_CHECK (native_buf != NULL); + P_TEST_CHECK (p_socket_address_new_from_native (native_buf, native_size) == NULL); + addr = p_socket_address_new ("192.168.0.2", 2345); + P_TEST_REQUIRE (addr != NULL); + + p_socket_address_set_flow_info (addr, 1); + p_socket_address_set_scope_id (addr, 1); + + P_TEST_CHECK (p_socket_address_to_native (addr, native_buf, native_size) == TRUE); + p_socket_address_free (addr); + + addr = p_socket_address_new_from_native (native_buf, native_size); + + P_TEST_CHECK (addr != NULL); + P_TEST_CHECK (p_socket_address_is_loopback (addr) == FALSE); + P_TEST_CHECK (p_socket_address_is_any (addr) == FALSE); + P_TEST_CHECK (p_socket_address_get_family (addr) == P_SOCKET_FAMILY_INET); + P_TEST_CHECK (p_socket_address_get_port (addr) == 2345); + P_TEST_CHECK (p_socket_address_get_native_size (addr) == native_size); + P_TEST_CHECK (p_socket_address_get_flow_info (addr) == 0); + P_TEST_CHECK (p_socket_address_get_scope_id (addr) == 0); + + addr_str = p_socket_address_get_address (addr); + + P_TEST_REQUIRE (addr_str != NULL); + P_TEST_CHECK (strcmp (addr_str, "192.168.0.2") == 0); + + p_free (native_buf); + p_free (addr_str); + p_socket_address_free (addr); + + if (p_socket_address_is_ipv6_supported ()) { + /* Test IPv6 any interface */ + addr = p_socket_address_new_any (P_SOCKET_FAMILY_INET6, 2345); + + P_TEST_REQUIRE (addr != NULL); + P_TEST_CHECK (p_socket_address_is_loopback (addr) == FALSE); + P_TEST_CHECK (p_socket_address_is_any (addr) == TRUE); + P_TEST_CHECK (p_socket_address_get_family (addr) == P_SOCKET_FAMILY_INET6); + P_TEST_CHECK (p_socket_address_get_port (addr) == 2345); + P_TEST_CHECK (p_socket_address_get_native_size (addr) > 0); + P_TEST_CHECK (p_socket_address_get_flow_info (addr) == 0); + P_TEST_CHECK (p_socket_address_get_scope_id (addr) == 0); + + native_size = p_socket_address_get_native_size (addr); + + p_socket_address_free (addr); + + /* Test IPv6 native raw data */ + native_buf = p_malloc0 (native_size); + P_TEST_CHECK (native_buf != NULL); + P_TEST_CHECK (p_socket_address_new_from_native (native_buf, native_size) == NULL); + addr = p_socket_address_new ("2001:cdba:345f:24ab:fe45:5423:3257:9652", 2345); + P_TEST_REQUIRE (addr != NULL); + + p_socket_address_set_flow_info (addr, 1); + p_socket_address_set_scope_id (addr, 1); + + P_TEST_CHECK (p_socket_address_to_native (addr, native_buf, native_size) == TRUE); + p_socket_address_free (addr); + + addr = p_socket_address_new_from_native (native_buf, native_size); + + P_TEST_CHECK (addr != NULL); + P_TEST_CHECK (p_socket_address_is_loopback (addr) == FALSE); + P_TEST_CHECK (p_socket_address_is_any (addr) == FALSE); + P_TEST_CHECK (p_socket_address_get_family (addr) == P_SOCKET_FAMILY_INET6); + P_TEST_CHECK (p_socket_address_get_port (addr) == 2345); + P_TEST_CHECK (p_socket_address_get_native_size (addr) == native_size); + + if (p_socket_address_is_flow_info_supported ()) + P_TEST_CHECK (p_socket_address_get_flow_info (addr) == 1); + + if (p_socket_address_is_scope_id_supported ()) + P_TEST_CHECK (p_socket_address_get_scope_id (addr) == 1); + + addr_str = p_socket_address_get_address (addr); + + P_TEST_REQUIRE (addr_str != NULL); + P_TEST_CHECK (strcmp (addr_str, "2001:cdba:345f:24ab:fe45:5423:3257:9652") == 0); + + p_free (native_buf); + p_free (addr_str); + p_socket_address_free (addr); + } + + if (p_socket_address_is_flow_info_supported () || p_socket_address_is_scope_id_supported ()) + P_TEST_CHECK (p_socket_address_is_ipv6_supported () == TRUE); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_SUITE_BEGIN() +{ + P_TEST_SUITE_RUN_CASE (psocketaddress_nomem_test); + P_TEST_SUITE_RUN_CASE (psocketaddress_bad_input_test); + P_TEST_SUITE_RUN_CASE (psocketaddress_general_test); +} +P_TEST_SUITE_END() diff --git a/3rdparty/plibsys/tests/pspinlock_test.cpp b/3rdparty/plibsys/tests/pspinlock_test.cpp new file mode 100644 index 0000000..1f23dab --- /dev/null +++ b/3rdparty/plibsys/tests/pspinlock_test.cpp @@ -0,0 +1,148 @@ +/* + * The MIT License + * + * Copyright (C) 2016-2019 Alexander Saprykin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "plibsys.h" +#include "ptestmacros.h" + +P_TEST_MODULE_INIT (); + +#define PSPINLOCK_MAX_VAL 10 + +static pint spinlock_test_val = 0; +static PSpinLock * global_spinlock = NULL; + +extern "C" ppointer pmem_alloc (psize nbytes) +{ + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" ppointer pmem_realloc (ppointer block, psize nbytes) +{ + P_UNUSED (block); + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" void pmem_free (ppointer block) +{ + P_UNUSED (block); +} + +static void * spinlock_test_thread (void *) +{ + pint i; + + for (i = 0; i < 1000; ++i) { + if (!p_spinlock_trylock (global_spinlock)) { + if (!p_spinlock_lock (global_spinlock)) + p_uthread_exit (1); + } + + if (spinlock_test_val == PSPINLOCK_MAX_VAL) + --spinlock_test_val; + else { + p_uthread_sleep (1); + ++spinlock_test_val; + } + + if (!p_spinlock_unlock (global_spinlock)) + p_uthread_exit (1); + } + + p_uthread_exit (0); + + return NULL; +} + +P_TEST_CASE_BEGIN (pspinlock_nomem_test) +{ + p_libsys_init (); + + PMemVTable vtable; + + vtable.free = pmem_free; + vtable.malloc = pmem_alloc; + vtable.realloc = pmem_realloc; + + P_TEST_CHECK (p_mem_set_vtable (&vtable) == TRUE); + P_TEST_CHECK (p_spinlock_new () == NULL); + + p_mem_restore_vtable (); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (pspinlock_bad_input_test) +{ + p_libsys_init (); + + P_TEST_REQUIRE (p_spinlock_lock (NULL) == FALSE); + P_TEST_REQUIRE (p_spinlock_unlock (NULL) == FALSE); + P_TEST_REQUIRE (p_spinlock_trylock (NULL) == FALSE); + p_spinlock_free (NULL); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (pspinlock_general_test) +{ + PUThread *thr1, *thr2; + + p_libsys_init (); + + spinlock_test_val = PSPINLOCK_MAX_VAL; + global_spinlock = p_spinlock_new (); + + P_TEST_REQUIRE (global_spinlock != NULL); + + thr1 = p_uthread_create ((PUThreadFunc) spinlock_test_thread, NULL, TRUE, NULL); + P_TEST_REQUIRE (thr1 != NULL); + + thr2 = p_uthread_create ((PUThreadFunc) spinlock_test_thread, NULL, TRUE, NULL); + P_TEST_REQUIRE (thr2 != NULL); + + P_TEST_CHECK (p_uthread_join (thr1) == 0); + P_TEST_CHECK (p_uthread_join (thr2) == 0); + + P_TEST_REQUIRE (spinlock_test_val == PSPINLOCK_MAX_VAL); + + p_uthread_unref (thr1); + p_uthread_unref (thr2); + p_spinlock_free (global_spinlock); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_SUITE_BEGIN() +{ + P_TEST_SUITE_RUN_CASE (pspinlock_nomem_test); + P_TEST_SUITE_RUN_CASE (pspinlock_bad_input_test); + P_TEST_SUITE_RUN_CASE (pspinlock_general_test); +} +P_TEST_SUITE_END() diff --git a/3rdparty/plibsys/tests/pstdarg_test.cpp b/3rdparty/plibsys/tests/pstdarg_test.cpp new file mode 100644 index 0000000..7df041f --- /dev/null +++ b/3rdparty/plibsys/tests/pstdarg_test.cpp @@ -0,0 +1,205 @@ +/* + * The MIT License + * + * Copyright (C) 2017 Jean-Damien Durand + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "plibsys.h" +#include "ptestmacros.h" + +#if defined(P_CC_WATCOM) +# pragma disable_message (7) +#endif + +P_TEST_MODULE_INIT (); + +static void variadic_function (pint unused, ...); +static void variadic_function_copy_all (pint unused, p_va_list ap); +static void variadic_function_copy_trail (pint unused, p_va_list ap); + +/* The "runtime" thingies here are just to avoid compiler warnings */ +/* In head, In trail */ +static const pint8 pint8_var[2] = { P_MININT8, P_MAXINT8 }; +static const puint8 puint8_var[2] = { 0, P_MAXUINT8 }; +static const pint16 pint16_var[2] = { P_MININT16, P_MAXINT16 }; +static const puint16 puint16_var[2] = { 0, P_MAXUINT16 }; +static pint32 pint32_var[2] = { 0 /* runtime */, 0 /* runtime */ }; +static puint32 puint32_var[2] = { 0, 0 /* runtime */ }; +static pint64 pint64_var[2] = { 0 /* runtime */, 0 /* runtime */ }; +static puint64 puint64_var[2] = { 0, 0 /* runtime */ }; +static const pint pint_var[2] = { P_MININT, P_MAXINT }; +static const puint puint_var[2] = { 0, P_MAXUINT }; +static const pshort pshort_var[2] = { P_MINSHORT, P_MAXSHORT }; +static const plong plong_var[2] = { P_MINLONG, P_MAXLONG }; +static const pchar pchar_var[2] = { '\0', 'z' }; +static const ppointer ppointer_var[2] = { NULL, (ppointer) p_libsys_init }; +static const pfloat pfloat_var[2] = { -1.234f, 1.234f }; +static const pdouble pdouble_var[2] = { -1.567, 1.567 }; + +/* Macros are used for testing because these tests MUST play with the CURRENT stack. */ + +#define P_TEST_VA_ARG(ap, type, wantedvalue) do { \ + P_DEBUG ("Unstacking a " #type); \ + P_TEST_CHECK (type##_va_arg(ap) == wantedvalue); \ + } while (0) + +#define P_TEST_VA_ARG_HEAD(ap) do { \ + P_TEST_VA_ARG(ap, pint8, pint8_var[0]); \ + P_TEST_VA_ARG(ap, puint8, puint8_var[0]); \ + P_TEST_VA_ARG(ap, pint16, pint16_var[0]); \ + P_TEST_VA_ARG(ap, puint16, puint16_var[0]); \ + P_TEST_VA_ARG(ap, pint32, pint32_var[0]); \ + P_TEST_VA_ARG(ap, puint32, puint32_var[0]); \ + P_TEST_VA_ARG(ap, pint64, pint64_var[0]); \ + P_TEST_VA_ARG(ap, puint64, puint64_var[0]); \ + P_TEST_VA_ARG(ap, pint, pint_var[0]); \ + P_TEST_VA_ARG(ap, puint, puint_var[0]); \ + P_TEST_VA_ARG(ap, pshort, pshort_var[0]); \ + P_TEST_VA_ARG(ap, plong, plong_var[0]); \ + P_TEST_VA_ARG(ap, pchar, pchar_var[0]); \ + P_TEST_VA_ARG(ap, ppointer, ppointer_var[0]); \ + P_TEST_VA_ARG(ap, pfloat, pfloat_var[0]); \ + P_TEST_VA_ARG(ap, pdouble, pdouble_var[0]); \ + } while (0) + +#define P_TEST_VA_ARG_TRAIL(ap) do { \ + P_TEST_VA_ARG(ap, pdouble, pdouble_var[1]); \ + P_TEST_VA_ARG(ap, pfloat, pfloat_var[1]); \ + P_TEST_VA_ARG(ap, ppointer, ppointer_var[1]); \ + P_TEST_VA_ARG(ap, pchar, pchar_var[1]); \ + P_TEST_VA_ARG(ap, plong, plong_var[1]); \ + P_TEST_VA_ARG(ap, pshort, pshort_var[1]); \ + P_TEST_VA_ARG(ap, puint, puint_var[1]); \ + P_TEST_VA_ARG(ap, pint, pint_var[1]); \ + P_TEST_VA_ARG(ap, puint64, puint64_var[1]); \ + P_TEST_VA_ARG(ap, pint64, pint64_var[1]); \ + P_TEST_VA_ARG(ap, puint32, puint32_var[1]); \ + P_TEST_VA_ARG(ap, pint32, pint32_var[1]); \ + P_TEST_VA_ARG(ap, puint16, puint16_var[1]); \ + P_TEST_VA_ARG(ap, pint16, pint16_var[1]); \ + P_TEST_VA_ARG(ap, puint8, puint8_var[1]); \ + P_TEST_VA_ARG(ap, pint8, pint8_var[1]); \ + } while (0) + +static void variadic_function_copy_all (pint unused, p_va_list ap) +{ + P_UNUSED (unused); + P_DEBUG ("Unstacking a copy of all the arguments"); + P_TEST_VA_ARG_HEAD (ap); + P_TEST_VA_ARG_TRAIL (ap); +} + +static void variadic_function_copy_trail (pint unused, p_va_list ap) +{ + P_UNUSED (unused); + P_DEBUG ("Unstacking second part of the arguments"); + P_TEST_VA_ARG_TRAIL (ap); +} + +static void variadic_function (pint unused, ...) +{ + p_va_list ap; + p_va_list ap2; + + p_va_start (ap, unused); + + P_DEBUG ("Copy of arguments"); + p_va_copy (ap2, ap); + variadic_function_copy_all (unused, ap2); + p_va_end (ap2); + + P_DEBUG ("Unstacking first part of arguments"); + P_TEST_VA_ARG_HEAD (ap); + + P_DEBUG ("Copy of arguments at current unstack state, i.e. in the middle"); + p_va_copy (ap2, ap); + variadic_function_copy_trail (unused, ap2); + p_va_end (ap2); + + P_DEBUG ("Unstacking second part of arguments"); + P_TEST_VA_ARG_TRAIL (ap); + + p_va_end (ap); +} + +P_TEST_CASE_BEGIN (pstdarg_general_test) +{ + p_libsys_init (); + + pint32_var[0] = P_MININT16; + pint32_var[0] <<= 16; + pint32_var[1] = P_MAXINT16; + pint32_var[1] <<= 16; + puint32_var[1] = P_MAXUINT16; + puint32_var[1] <<= 16; + + pint64_var[0] = pint32_var[0]; + pint64_var[0] <<= 32; + pint64_var[1] = pint32_var[1]; + pint64_var[1] <<= 32; + puint64_var[1] = puint32_var[1]; + puint64_var[1] <<= 32; + + variadic_function (0, + pint8_var[0], + puint8_var[0], + pint16_var[0], + puint16_var[0], + pint32_var[0], + puint32_var[0], + pint64_var[0], + puint64_var[0], + pint_var[0], + puint_var[0], + pshort_var[0], + plong_var[0], + pchar_var[0], + ppointer_var[0], + pfloat_var[0], + pdouble_var[0], + /* Cut is here when testing p_va_copy, we stack in reverse order */ + pdouble_var[1], + pfloat_var[1], + ppointer_var[1], + pchar_var[1], + plong_var[1], + pshort_var[1], + puint_var[1], + pint_var[1], + puint64_var[1], + pint64_var[1], + puint32_var[1], + pint32_var[1], + puint16_var[1], + pint16_var[1], + puint8_var[1], + pint8_var[1]); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_SUITE_BEGIN() +{ + P_TEST_SUITE_RUN_CASE (pstdarg_general_test); +} +P_TEST_SUITE_END() diff --git a/3rdparty/plibsys/tests/pstring_test.cpp b/3rdparty/plibsys/tests/pstring_test.cpp new file mode 100644 index 0000000..73e5805 --- /dev/null +++ b/3rdparty/plibsys/tests/pstring_test.cpp @@ -0,0 +1,289 @@ +/* + * The MIT License + * + * Copyright (C) 2013-2017 Alexander Saprykin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "plibsys.h" +#include "ptestmacros.h" + +#include +#include +#include + +P_TEST_MODULE_INIT (); + +extern "C" ppointer pmem_alloc (psize nbytes) +{ + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" ppointer pmem_realloc (ppointer block, psize nbytes) +{ + P_UNUSED (block); + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" void pmem_free (ppointer block) +{ + P_UNUSED (block); +} + +P_TEST_CASE_BEGIN (pstring_nomem_test) +{ + p_libsys_init (); + + PMemVTable vtable; + + vtable.free = pmem_free; + vtable.malloc = pmem_alloc; + vtable.realloc = pmem_realloc; + + P_TEST_CHECK (p_mem_set_vtable (&vtable) == TRUE); + + P_TEST_CHECK (p_strdup ("test string") == NULL); + + p_mem_restore_vtable (); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (pstring_strdup_test) +{ + p_libsys_init (); + + const pchar *test_str_1 = "Test string"; + + pchar *new_string = p_strdup (test_str_1); + P_TEST_CHECK (new_string != NULL); + p_free (new_string); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (pstring_strchomp_test) +{ + p_libsys_init (); + + const pchar *test_chomp_str_orig = "Test chomp string"; + const pchar *test_chomp_str_1 = "Test chomp string "; + const pchar *test_chomp_str_2 = "\n\nTest chomp string "; + const pchar *test_chomp_str_3 = "\n\rTest chomp string \n"; + const pchar *test_chomp_str_4 = "Test chomp string\n\n"; + const pchar *test_chomp_str_5 = " \rTest chomp string \n\n "; + const pchar *test_chomp_str_6 = " \rI\n\n "; + const pchar *test_chomp_str_7 = "\n"; + const pchar *test_chomp_str_8 = "I"; + const pchar *test_chomp_str_9 = ""; + const pchar *test_chomp_str_10 = " "; + const pchar *test_chomp_str_11 = NULL; + + pchar *new_string = p_strchomp (test_chomp_str_1); + P_TEST_REQUIRE (new_string != NULL); + P_TEST_CHECK (strcmp (test_chomp_str_orig, new_string) == 0); + p_free (new_string); + + new_string = p_strchomp (test_chomp_str_2); + P_TEST_REQUIRE (new_string != NULL); + P_TEST_CHECK (strcmp (test_chomp_str_orig, new_string) == 0); + p_free (new_string); + + new_string = p_strchomp (test_chomp_str_3); + P_TEST_REQUIRE (new_string != NULL); + P_TEST_CHECK (strcmp (test_chomp_str_orig, new_string) == 0); + p_free (new_string); + + new_string = p_strchomp (test_chomp_str_4); + P_TEST_REQUIRE (new_string != NULL); + P_TEST_CHECK (strcmp (test_chomp_str_orig, new_string) == 0); + p_free (new_string); + + new_string = p_strchomp (test_chomp_str_5); + P_TEST_REQUIRE (new_string != NULL); + P_TEST_CHECK (strcmp (test_chomp_str_orig, new_string) == 0); + p_free (new_string); + + new_string = p_strchomp (test_chomp_str_6); + P_TEST_REQUIRE (new_string != NULL); + P_TEST_CHECK (strcmp ("I", new_string) == 0); + p_free (new_string); + + new_string = p_strchomp (test_chomp_str_7); + P_TEST_REQUIRE (new_string != NULL); + P_TEST_CHECK (*new_string == '\0'); + p_free (new_string); + + new_string = p_strchomp (test_chomp_str_8); + P_TEST_REQUIRE (new_string != NULL); + P_TEST_CHECK (strcmp ("I", new_string) == 0); + p_free (new_string); + + new_string = p_strchomp (test_chomp_str_9); + P_TEST_REQUIRE (new_string != NULL); + P_TEST_CHECK (*new_string == '\0'); + p_free (new_string); + + new_string = p_strchomp (test_chomp_str_10); + P_TEST_REQUIRE (new_string != NULL); + P_TEST_CHECK (*new_string == '\0'); + p_free (new_string); + + new_string = p_strchomp (test_chomp_str_11); + P_TEST_CHECK (new_string == NULL); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (pstring_strtok_test) +{ + p_libsys_init (); + + P_TEST_CHECK (p_strtok (NULL, NULL, NULL) == NULL); + + /* First string */ + pchar test_string[] = "1,2,3"; + pchar *token, *next_token; + + /* Check third parameter for possible NULL */ + token = p_strtok (test_string, ",", NULL); + + if (strcmp (token, "1") != 0) { + token = p_strtok (test_string, ",", &next_token); + P_TEST_CHECK (token != NULL); + P_TEST_CHECK (strcmp (token, "1") == 0); + } + + token = p_strtok (NULL, ",", &next_token); + P_TEST_CHECK (token != NULL); + P_TEST_CHECK (strcmp (token, "2") == 0); + + token = p_strtok (NULL, ",", &next_token); + P_TEST_CHECK (token != NULL); + P_TEST_CHECK (strcmp (token, "3") == 0); + + token = p_strtok (NULL, ",", &next_token); + P_TEST_CHECK (token == NULL); + + /* Second string */ + pchar test_string_2[] = "Test string, to test"; + + token = p_strtok (test_string_2, " ", &next_token); + P_TEST_CHECK (token != NULL); + P_TEST_CHECK (strcmp (token, "Test") == 0); + + token = p_strtok (NULL, ", ", &next_token); + P_TEST_CHECK (token != NULL); + P_TEST_CHECK (strcmp (token, "string") == 0); + + token = p_strtok (NULL, ", ", &next_token); + P_TEST_CHECK (token != NULL); + P_TEST_CHECK (strcmp (token, "to") == 0); + + token = p_strtok (NULL, ", \t\n", &next_token); + P_TEST_CHECK (token != NULL); + P_TEST_CHECK (strcmp (token, "test") == 0); + + token = p_strtok (NULL, ", \t\n", &next_token); + P_TEST_CHECK (token == NULL); + + /* Third string */ + pchar test_string_3[] = "compile\ttest\ndeploy"; + + token = p_strtok (test_string_3, "\t\n", &next_token); + P_TEST_CHECK (token != NULL); + P_TEST_CHECK (strcmp (token, "compile") == 0); + + token = p_strtok (NULL, "\t\n", &next_token); + P_TEST_CHECK (token != NULL); + P_TEST_CHECK (strcmp (token, "test") == 0); + + token = p_strtok (NULL, "\t\n", &next_token); + P_TEST_CHECK (token != NULL); + P_TEST_CHECK (strcmp (token, "deploy") == 0); + + token = p_strtok (NULL, ", \t\n", &next_token); + P_TEST_CHECK (token == NULL); + + /* Fourth string */ + pchar test_string_4[] = "\t \t\n \t"; + + token = p_strtok (test_string_4, "\t\n ", &next_token); + P_TEST_CHECK (token == NULL); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (pstring_strtod_test) +{ + p_libsys_init (); + + /* Incorrect input */ + P_TEST_CHECK_CLOSE (p_strtod (NULL), 0.0, 0.0001); + P_TEST_CHECK_CLOSE (p_strtod ("e2"), 0.0, 0.0001); + P_TEST_CHECK_CLOSE (p_strtod ("e-2"), 0.0, 0.0001); + P_TEST_CHECK_CLOSE (p_strtod ("-e2"), 0.0, 0.0001); + P_TEST_CHECK_CLOSE (p_strtod ("-e-2"), 0.0, 0.0001); + P_TEST_CHECK_CLOSE (p_strtod ("0,3"), 0.0, 0.0001); + P_TEST_CHECK_CLOSE (p_strtod ("12,3"), 12.0, 0.0001); + + /* Correct input */ + P_TEST_CHECK_CLOSE (p_strtod ("0"), 0.0, 0.0001); + P_TEST_CHECK_CLOSE (p_strtod ("0.0"), 0.0, 0.0001); + P_TEST_CHECK_CLOSE (p_strtod ("-0"), 0.0, 0.0001); + P_TEST_CHECK_CLOSE (p_strtod ("-0.0"), 0.0, 0.0001); + P_TEST_CHECK_CLOSE (p_strtod ("3.14"), 3.14, 0.0001); + P_TEST_CHECK_CLOSE (p_strtod ("+3.14"), 3.14, 0.0001); + P_TEST_CHECK_CLOSE (p_strtod ("-12.256"), -12.256, 0.0001); + P_TEST_CHECK_CLOSE (p_strtod ("0.056"), 0.056, 0.0001); + P_TEST_CHECK_CLOSE (p_strtod ("-0.057"), -0.057, 0.0001); + P_TEST_CHECK_CLOSE (p_strtod ("1.5423e2"), 154.23, 0.0001); + P_TEST_CHECK_CLOSE (p_strtod ("1e3"), 1000.0, 0.0001); + P_TEST_CHECK_CLOSE (p_strtod ("1e+3"), 1000.0, 0.0001); + P_TEST_CHECK_CLOSE (p_strtod ("-2.56e1"), -25.6, 0.0001); + P_TEST_CHECK_CLOSE (p_strtod ("-2.56e+1"), -25.6, 0.0001); + P_TEST_CHECK_CLOSE (p_strtod ("123e-2"), 1.23, 0.0001); + P_TEST_CHECK_CLOSE (p_strtod ("3.14e-1"), 0.314, 0.0001); + P_TEST_CHECK_CLOSE (p_strtod ("3.14e60"), 3.14e60, 0.0001); + P_TEST_CHECK_CLOSE (p_strtod ("3.14e-60"), 3.14e-60, 0.0001); + P_TEST_CHECK_CLOSE (p_strtod ("2.14e10"), 2.14e10, 0.0001); + P_TEST_CHECK_CLOSE (p_strtod ("2.14e-10"), 2.14e-10, 0.0001); + P_TEST_CHECK_CLOSE (p_strtod ("1.10e310"), 1.10e308, 0.0001); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_SUITE_BEGIN() +{ + P_TEST_SUITE_RUN_CASE (pstring_nomem_test); + P_TEST_SUITE_RUN_CASE (pstring_strdup_test); + P_TEST_SUITE_RUN_CASE (pstring_strchomp_test); + P_TEST_SUITE_RUN_CASE (pstring_strtok_test); + P_TEST_SUITE_RUN_CASE (pstring_strtod_test); +} +P_TEST_SUITE_END() diff --git a/3rdparty/plibsys/tests/ptestmacros.h b/3rdparty/plibsys/tests/ptestmacros.h new file mode 100644 index 0000000..206a8a6 --- /dev/null +++ b/3rdparty/plibsys/tests/ptestmacros.h @@ -0,0 +1,131 @@ +/* + * The MIT License + * + * Copyright (C) 2017 Alexander Saprykin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 ptestmacros.h + * @brief Macros for unit-tests + * @author Alexander Saprykin + */ + +#ifndef PLIBSYS_HEADER_PTESTMACROS_H +#define PLIBSYS_HEADER_PTESTMACROS_H + +#include + +#include + +#if defined (P_CC_MSVC) +# pragma warning (push) +# pragma warning (disable : 4723) +#elif defined (P_CC_BORLAND) +# pragma option -w-8008 +# pragma option -w-8066 +#elif defined (P_CC_WATCOM) +# pragma disable_message (13) +# pragma disable_message (367) +# pragma disable_message (368) +#endif + +inline double p_test_safe_division (double f1, double f2) +{ + return (f2 < 1.0 && f1 > f2 * P_MAXDOUBLE) ? P_MAXDOUBLE : + (((f2 > 1.0 && f1 < f2 * P_MINDOUBLE) || f1 == 0) ? 0 : f1 / f2); +} + +#ifdef P_CC_MSVC +# pragma warning (pop) +#endif + +#define P_TEST_MODULE_FAIL_COUNTER p_test_module_fail_counter +#define P_TEST_SUITE_FAIL_COUNTER p_test_suite_fail_counter + +#define P_TEST_MODULE_INIT() static pint P_TEST_MODULE_FAIL_COUNTER = 0 + +#define P_TEST_CASE_BEGIN(test_case_name) \ + pint p_test_case_##test_case_name (void) \ + { \ + P_TEST_MODULE_FAIL_COUNTER = 0; + +#define P_TEST_CASE_END() \ + return (P_TEST_MODULE_FAIL_COUNTER == 0) ? 0 : -1; \ + } + +#define P_TEST_CASE_RETURN() return 0 + +#define P_TEST_SUITE_BEGIN() \ + int main (void) \ + { \ + pint P_TEST_SUITE_FAIL_COUNTER = 0; + +#define P_TEST_SUITE_ARGS_BEGIN() \ + int main (int argc, char *argv[]) \ + { \ + pint P_TEST_SUITE_FAIL_COUNTER = 0; + +#define P_TEST_SUITE_END() \ + if (P_TEST_SUITE_FAIL_COUNTER == 0) \ + printf ("Test passed\n"); \ + else \ + printf ("Test failed\n"); \ + \ + return P_TEST_SUITE_FAIL_COUNTER == 0 ? 0 : -1; \ + } + +#define P_TEST_SUITE_RUN_CASE(a) \ + printf ("Running test case: %s\n", #a); \ + P_TEST_SUITE_FAIL_COUNTER += (p_test_case_##a)() + +#define P_TEST_CHECK(a) \ + do { \ + if (!(a)) { \ + printf ("%s:%d: check failed\n", __FILE__, __LINE__); \ + p_atomic_int_inc (&P_TEST_MODULE_FAIL_COUNTER); \ + } \ + } while (0) + +#define P_TEST_CHECK_CLOSE(a, b, eps) \ + do { \ + double p_test_eps_diff = (a) > (b) ? (a) - (b) : (b) - (a); \ + double p_test_d1 = p_test_safe_division (p_test_eps_diff, \ + ((a) < 0.0 ? (-(a)) : (a))); \ + double p_test_d2 = p_test_safe_division (p_test_eps_diff, \ + ((b) < 0.0 ? (-(b)) : (b))); \ + double p_test_tol = (eps) * 0.01; \ + \ + if (!(p_test_d1 <= p_test_tol && p_test_d2 <= p_test_tol)) { \ + printf ("%s:%d: check failed\n", __FILE__, __LINE__); \ + p_atomic_int_inc (&P_TEST_MODULE_FAIL_COUNTER); \ + } \ + } while (0) + +#define P_TEST_REQUIRE(a) \ + do { \ + if (!(a)) { \ + printf ("%s:%d: required check failed\n", __FILE__, __LINE__); \ + exit (-1); \ + } \ + } while (0) + +#endif /* PLIBSYS_HEADER_PTESTMACROS_H */ diff --git a/3rdparty/plibsys/tests/ptimeprofiler_test.cpp b/3rdparty/plibsys/tests/ptimeprofiler_test.cpp new file mode 100644 index 0000000..dd9e2b7 --- /dev/null +++ b/3rdparty/plibsys/tests/ptimeprofiler_test.cpp @@ -0,0 +1,126 @@ +/* + * The MIT License + * + * Copyright (C) 2013-2017 Alexander Saprykin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "plibsys.h" +#include "ptestmacros.h" + +P_TEST_MODULE_INIT (); + +extern "C" ppointer pmem_alloc (psize nbytes) +{ + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" ppointer pmem_realloc (ppointer block, psize nbytes) +{ + P_UNUSED (block); + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" void pmem_free (ppointer block) +{ + P_UNUSED (block); +} + +P_TEST_CASE_BEGIN (ptimeprofiler_nomem_test) +{ + p_libsys_init (); + + PMemVTable vtable; + + vtable.free = pmem_free; + vtable.malloc = pmem_alloc; + vtable.realloc = pmem_realloc; + + P_TEST_CHECK (p_mem_set_vtable (&vtable) == TRUE); + + P_TEST_CHECK (p_time_profiler_new () == NULL); + + p_mem_restore_vtable (); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (ptimeprofiler_bad_input_test) +{ + p_libsys_init (); + + P_TEST_CHECK (p_time_profiler_elapsed_usecs (NULL) == 0); + p_time_profiler_reset (NULL); + p_time_profiler_free (NULL); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (ptimeprofiler_general_test) +{ + PTimeProfiler *profiler = NULL; + puint64 prev_val, val; + + p_libsys_init (); + + profiler = p_time_profiler_new (); + P_TEST_REQUIRE (profiler != NULL); + + p_uthread_sleep (50); + prev_val = p_time_profiler_elapsed_usecs (profiler); + P_TEST_CHECK (prev_val > 0); + + p_uthread_sleep (100); + val = p_time_profiler_elapsed_usecs (profiler); + P_TEST_CHECK (val > prev_val); + prev_val = val; + + p_uthread_sleep (1000); + val = p_time_profiler_elapsed_usecs (profiler); + P_TEST_CHECK (val > prev_val); + + p_time_profiler_reset (profiler); + + p_uthread_sleep (15); + prev_val = p_time_profiler_elapsed_usecs (profiler); + P_TEST_CHECK (prev_val > 0); + + p_uthread_sleep (178); + val = p_time_profiler_elapsed_usecs (profiler); + P_TEST_CHECK (val > prev_val); + + p_time_profiler_free (profiler); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_SUITE_BEGIN() +{ + P_TEST_SUITE_RUN_CASE (ptimeprofiler_nomem_test); + P_TEST_SUITE_RUN_CASE (ptimeprofiler_bad_input_test); + P_TEST_SUITE_RUN_CASE (ptimeprofiler_general_test); +} +P_TEST_SUITE_END() diff --git a/3rdparty/plibsys/tests/ptree_test.cpp b/3rdparty/plibsys/tests/ptree_test.cpp new file mode 100644 index 0000000..e038ebb --- /dev/null +++ b/3rdparty/plibsys/tests/ptree_test.cpp @@ -0,0 +1,633 @@ +/* + * The MIT License + * + * Copyright (C) 2015-2017 Alexander Saprykin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "plibsys.h" +#include "ptestmacros.h" + +#include +#include +#include +#include + +P_TEST_MODULE_INIT (); + +#define PTREE_STRESS_ITERATIONS 20 +#define PTREE_STRESS_NODES 10000 +#define PTREE_STRESS_ROOT_MIN 10000 +#define PTREE_STRESS_TRAVS 30 + +typedef struct _TreeData { + pint cmp_counter; + pint key_destroy_counter; + pint value_destroy_counter; + pint traverse_counter; + pint traverse_thres; + pint key_sum; + pint value_sum; + pint last_key; + pint key_order_errors; +} TreeData; + +static TreeData tree_data = {0, 0, 0, 0, 0, 0, 0, 0, 0}; + +extern "C" ppointer pmem_alloc (psize nbytes) +{ + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" ppointer pmem_realloc (ppointer block, psize nbytes) +{ + P_UNUSED (block); + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" void pmem_free (ppointer block) +{ + P_UNUSED (block); +} + +static pint +tree_complexity (PTree *tree) +{ + if (tree == NULL || p_tree_get_nnodes (tree) == 0) + return 0; + + switch (p_tree_get_type (tree)) { + case P_TREE_TYPE_BINARY: + return p_tree_get_nnodes (tree); + case P_TREE_TYPE_RB: + return 2 * ((pint) (log ((double) p_tree_get_nnodes (tree) + 1) / log (2.0))); + case P_TREE_TYPE_AVL: + { + double phi = (1 + sqrt (5.0)) / 2.0; + return (pint) (log (sqrt (5.0) * (p_tree_get_nnodes (tree) + 2)) / log (phi) - 2); + } + default: + return p_tree_get_nnodes (tree); + } +} + +static pint +compare_keys (pconstpointer a, pconstpointer b) +{ + int p1 = PPOINTER_TO_INT (a); + int p2 = PPOINTER_TO_INT (b); + + if (p1 < p2) + return -1; + else if (p1 > p2) + return 1; + else + return 0; +} + +static pint +compare_keys_data (pconstpointer a, pconstpointer b, ppointer data) +{ + int p1 = PPOINTER_TO_INT (a); + int p2 = PPOINTER_TO_INT (b); + + if (data != NULL) + ((TreeData *) data)->cmp_counter++; + + if (p1 < p2) + return -1; + else if (p1 > p2) + return 1; + else + return 0; +} + +static void +key_destroy_notify (ppointer data) +{ + tree_data.key_destroy_counter++; + tree_data.key_sum += PPOINTER_TO_INT (data); +} + +static void +value_destroy_notify (ppointer data) +{ + tree_data.value_destroy_counter++; + tree_data.value_sum += PPOINTER_TO_INT (data); +} + +static pboolean +tree_traverse (ppointer key, ppointer value, ppointer data) +{ + TreeData* tdata = ((TreeData *) data); + + tdata->key_sum += PPOINTER_TO_INT (key); + tdata->value_sum += PPOINTER_TO_INT (value); + tdata->traverse_counter++; + + if (tdata->last_key >= PPOINTER_TO_INT (key)) + tdata->key_order_errors++; + + tdata->last_key = PPOINTER_TO_INT (key); + + return FALSE; +} + +static pboolean +tree_traverse_thres (ppointer key, ppointer value, ppointer data) +{ + TreeData* tdata = ((TreeData *) data); + + tree_traverse (key, value, data); + + return tdata->traverse_counter >= tdata->traverse_thres ? TRUE : FALSE; +} + +static bool +check_tree_data_is_zero () +{ + return tree_data.cmp_counter == 0 && + tree_data.key_destroy_counter == 0 && + tree_data.value_destroy_counter == 0 && + tree_data.traverse_counter == 0 && + tree_data.traverse_thres == 0 && + tree_data.key_sum == 0 && + tree_data.value_sum == 0 && + tree_data.last_key == 0 && + tree_data.key_order_errors == 0; +} + +static bool +general_tree_test (PTree *tree, PTreeType type, bool check_cmp, bool check_notify) +{ + memset (&tree_data, 0, sizeof (tree_data)); + + P_TEST_REQUIRE (tree != NULL); + P_TEST_CHECK (p_tree_get_nnodes (tree) == 0); + P_TEST_CHECK (p_tree_get_type (tree) == type); + P_TEST_CHECK (p_tree_lookup (tree, NULL) == NULL); + P_TEST_CHECK (p_tree_remove (tree, NULL) == FALSE); + + p_tree_insert (tree, NULL, PINT_TO_POINTER (10)); + P_TEST_CHECK (p_tree_get_nnodes (tree) == 1); + P_TEST_CHECK (p_tree_lookup (tree, NULL) == PINT_TO_POINTER (10)); + P_TEST_CHECK (p_tree_lookup (tree, PINT_TO_POINTER (2)) == NULL); + P_TEST_CHECK (p_tree_remove (tree, NULL) == TRUE); + P_TEST_CHECK (p_tree_get_nnodes (tree) == 0); + + p_tree_foreach (tree, (PTraverseFunc) tree_traverse, &tree_data); + P_TEST_CHECK (tree_data.traverse_counter == 0); + P_TEST_CHECK (tree_data.key_order_errors == 0); + + /* Because we have NULL-key node */ + P_TEST_CHECK (tree_data.key_sum == 0); + + if (check_notify) + P_TEST_CHECK (tree_data.value_sum == 10); + else + P_TEST_CHECK (tree_data.value_sum == 0); + + memset (&tree_data, 0, sizeof (tree_data)); + + p_tree_insert (tree, PINT_TO_POINTER (4), PINT_TO_POINTER (40)); + p_tree_insert (tree, PINT_TO_POINTER (1), PINT_TO_POINTER (10)); + p_tree_insert (tree, PINT_TO_POINTER (5), PINT_TO_POINTER (50)); + p_tree_insert (tree, PINT_TO_POINTER (2), PINT_TO_POINTER (20)); + p_tree_insert (tree, PINT_TO_POINTER (6), PINT_TO_POINTER (60)); + p_tree_insert (tree, PINT_TO_POINTER (3), PINT_TO_POINTER (30)); + + P_TEST_CHECK (p_tree_get_nnodes (tree) == 6); + + p_tree_insert (tree, PINT_TO_POINTER (1), PINT_TO_POINTER (100)); + p_tree_insert (tree, PINT_TO_POINTER (5), PINT_TO_POINTER (500)); + + P_TEST_CHECK (p_tree_get_nnodes (tree) == 6); + + p_tree_insert (tree, PINT_TO_POINTER (1), PINT_TO_POINTER (10)); + p_tree_insert (tree, PINT_TO_POINTER (5), PINT_TO_POINTER (50)); + + P_TEST_CHECK (p_tree_get_nnodes (tree) == 6); + + if (check_cmp) + P_TEST_CHECK (tree_data.cmp_counter > 0); + else + P_TEST_CHECK (tree_data.cmp_counter == 0); + + if (check_notify) { + P_TEST_CHECK (tree_data.key_sum == 12); + P_TEST_CHECK (tree_data.value_sum == 660); + } else { + P_TEST_CHECK (tree_data.key_sum == 0); + P_TEST_CHECK (tree_data.value_sum == 0); + } + + P_TEST_CHECK (tree_data.traverse_counter == 0); + P_TEST_CHECK (tree_data.key_order_errors == 0); + + memset (&tree_data, 0, sizeof (tree_data)); + + p_tree_foreach (tree, (PTraverseFunc) tree_traverse, &tree_data); + P_TEST_CHECK (p_tree_get_nnodes (tree) == 6); + + P_TEST_CHECK (tree_data.cmp_counter == 0); + P_TEST_CHECK (tree_data.key_sum == 21); + P_TEST_CHECK (tree_data.value_sum == 210); + P_TEST_CHECK (tree_data.traverse_counter == 6); + P_TEST_CHECK (tree_data.key_order_errors == 0); + + memset (&tree_data, 0, sizeof (tree_data)); + + for (int i = 0; i < 7; ++i) + P_TEST_CHECK (p_tree_lookup (tree, PINT_TO_POINTER (i)) == PINT_TO_POINTER (i * 10)); + + if (check_cmp) + P_TEST_CHECK (tree_data.cmp_counter > 0); + else + P_TEST_CHECK (tree_data.cmp_counter == 0); + + P_TEST_CHECK (tree_data.key_sum == 0); + P_TEST_CHECK (tree_data.value_sum == 0); + P_TEST_CHECK (tree_data.key_order_errors == 0); + + tree_data.cmp_counter = 0; + + P_TEST_CHECK (p_tree_remove (tree, PINT_TO_POINTER (7)) == FALSE); + + if (check_cmp) + P_TEST_CHECK (tree_data.cmp_counter > 0 && + tree_data.cmp_counter <= tree_complexity (tree)); + else + P_TEST_CHECK (tree_data.cmp_counter == 0); + + if (check_notify) { + P_TEST_CHECK (tree_data.key_sum == 0); + P_TEST_CHECK (tree_data.value_sum == 0); + } + + tree_data.cmp_counter = 0; + + for (int i = 0; i < 7; ++i) + P_TEST_CHECK (p_tree_lookup (tree, PINT_TO_POINTER (i)) == PINT_TO_POINTER (i * 10)); + + if (check_cmp) + P_TEST_CHECK (tree_data.cmp_counter > 0); + else + P_TEST_CHECK (tree_data.cmp_counter == 0); + + P_TEST_CHECK (tree_data.key_sum == 0); + P_TEST_CHECK (tree_data.value_sum == 0); + P_TEST_CHECK (tree_data.key_order_errors == 0); + + memset (&tree_data, 0, sizeof (tree_data)); + + tree_data.traverse_thres = 5; + + p_tree_foreach (tree, (PTraverseFunc) tree_traverse_thres, &tree_data); + P_TEST_CHECK (p_tree_get_nnodes (tree) == 6); + + P_TEST_CHECK (tree_data.cmp_counter == 0); + P_TEST_CHECK (tree_data.key_sum == 15); + P_TEST_CHECK (tree_data.value_sum == 150); + P_TEST_CHECK (tree_data.traverse_counter == 5); + P_TEST_CHECK (tree_data.key_order_errors == 0); + + memset (&tree_data, 0, sizeof (tree_data)); + + tree_data.traverse_thres = 3; + + p_tree_foreach (tree, (PTraverseFunc) tree_traverse_thres, &tree_data); + P_TEST_CHECK (p_tree_get_nnodes (tree) == 6); + + P_TEST_CHECK (tree_data.cmp_counter == 0); + P_TEST_CHECK (tree_data.key_sum == 6); + P_TEST_CHECK (tree_data.value_sum == 60); + P_TEST_CHECK (tree_data.traverse_counter == 3); + P_TEST_CHECK (tree_data.key_order_errors == 0); + + memset (&tree_data, 0, sizeof (tree_data)); + + P_TEST_CHECK (p_tree_remove (tree, PINT_TO_POINTER (1)) == TRUE); + P_TEST_CHECK (p_tree_remove (tree, PINT_TO_POINTER (6)) == TRUE); + P_TEST_CHECK (p_tree_lookup (tree, PINT_TO_POINTER (1)) == NULL); + P_TEST_CHECK (p_tree_lookup (tree, PINT_TO_POINTER (6)) == NULL); + + if (check_cmp) + P_TEST_CHECK (tree_data.cmp_counter > 0); + else + P_TEST_CHECK (tree_data.cmp_counter == 0); + + if (check_notify) { + P_TEST_CHECK (tree_data.key_sum == 7); + P_TEST_CHECK (tree_data.value_sum == 70); + } else { + P_TEST_CHECK (tree_data.key_sum == 0); + P_TEST_CHECK (tree_data.value_sum == 0); + } + + tree_data.cmp_counter = 0; + + for (int i = 2; i < 6; ++i) + P_TEST_CHECK (p_tree_lookup (tree, PINT_TO_POINTER (i)) == PINT_TO_POINTER (i * 10)); + + if (check_cmp) + P_TEST_CHECK (tree_data.cmp_counter > 0); + else + P_TEST_CHECK (tree_data.cmp_counter == 0); + + if (check_notify) { + P_TEST_CHECK (tree_data.key_sum == 7); + P_TEST_CHECK (tree_data.value_sum == 70); + } else { + P_TEST_CHECK (tree_data.key_sum == 0); + P_TEST_CHECK (tree_data.value_sum == 0); + } + + P_TEST_CHECK (tree_data.key_order_errors == 0); + + tree_data.cmp_counter = 0; + + p_tree_foreach (tree, NULL, NULL); + + P_TEST_CHECK (tree_data.cmp_counter == 0); + P_TEST_CHECK (tree_data.key_order_errors == 0); + + p_tree_clear (tree); + + P_TEST_CHECK (tree_data.cmp_counter == 0); + P_TEST_CHECK (tree_data.key_order_errors == 0); + + if (check_notify) { + P_TEST_CHECK (tree_data.key_sum == 21); + P_TEST_CHECK (tree_data.value_sum == 210); + } else { + P_TEST_CHECK (tree_data.key_sum == 0); + P_TEST_CHECK (tree_data.value_sum == 0); + } + + P_TEST_CHECK (p_tree_get_nnodes (tree) == 0); + + return true; +} + +static bool +stress_tree_test (PTree *tree, int node_count) +{ + P_TEST_REQUIRE (tree != NULL); + P_TEST_REQUIRE (node_count > 0); + P_TEST_CHECK (p_tree_get_nnodes (tree) == 0); + + srand ((unsigned int) time (NULL)); + + int counter = 0; + + memset (&tree_data, 0, sizeof (tree_data)); + + pint *keys = (pint *) p_malloc0 ((psize) node_count * sizeof (pint)); + pint *values = (pint *) p_malloc0 ((psize) node_count * sizeof (pint)); + + P_TEST_REQUIRE (keys != NULL); + P_TEST_REQUIRE (values != NULL); + + while (counter != node_count) { + pint rand_number = rand (); + + if (counter == 0 && rand_number < PTREE_STRESS_ROOT_MIN) + continue; + + memset (&tree_data, 0, sizeof (tree_data)); + + if (p_tree_lookup (tree, PINT_TO_POINTER (rand_number)) != NULL) + continue; + + if (counter > 0) + P_TEST_CHECK (tree_data.cmp_counter > 0 && + tree_data.cmp_counter <= tree_complexity (tree)); + + memset (&tree_data, 0, sizeof (tree_data)); + + keys[counter] = rand_number; + values[counter] = rand () + 1; + + p_tree_insert (tree, PINT_TO_POINTER (keys[counter]), PINT_TO_POINTER (values[counter])); + + if (counter > 0) + P_TEST_CHECK (tree_data.cmp_counter > 0 && + tree_data.cmp_counter <= tree_complexity (tree)); + + ++counter; + } + + for (int i = 0; i < PTREE_STRESS_TRAVS; ++i) { + memset (&tree_data, 0, sizeof (tree_data)); + + tree_data.traverse_thres = i + 1; + tree_data.last_key = -1; + + p_tree_foreach (tree, (PTraverseFunc) tree_traverse_thres, &tree_data); + + P_TEST_CHECK (tree_data.traverse_counter == i + 1); + P_TEST_CHECK (tree_data.key_order_errors == 0); + } + + for (int i = 0; i < node_count; ++i) { + memset (&tree_data, 0, sizeof (tree_data)); + + P_TEST_CHECK (p_tree_lookup (tree, PINT_TO_POINTER (keys[i])) == + PINT_TO_POINTER (values[i])); + + P_TEST_CHECK (tree_data.cmp_counter > 0 && + tree_data.cmp_counter <= tree_complexity (tree)); + + P_TEST_CHECK (p_tree_remove (tree, PINT_TO_POINTER (keys[i])) == TRUE); + P_TEST_CHECK (p_tree_lookup (tree, PINT_TO_POINTER (keys[i])) == NULL); + } + + P_TEST_CHECK (p_tree_get_nnodes (tree) == 0); + + for (int i = 0; i < node_count; ++i) + p_tree_insert (tree, PINT_TO_POINTER (keys[i]), PINT_TO_POINTER (values[i])); + + P_TEST_CHECK (p_tree_get_nnodes (tree) == node_count); + + p_tree_clear (tree); + + P_TEST_CHECK (p_tree_get_nnodes (tree) == 0); + + p_free (keys); + p_free (values); + + return true; +} + +P_TEST_CASE_BEGIN (ptree_nomem_test) +{ + p_libsys_init (); + + PMemVTable vtable; + + for (int i = (int) P_TREE_TYPE_BINARY; i <= (int) P_TREE_TYPE_AVL; ++i) { + PTree *tree = p_tree_new ((PTreeType) i, (PCompareFunc) compare_keys); + P_TEST_CHECK (tree != NULL); + + vtable.free = pmem_free; + vtable.malloc = pmem_alloc; + vtable.realloc = pmem_realloc; + + P_TEST_CHECK (p_mem_set_vtable (&vtable) == TRUE); + + P_TEST_CHECK (p_tree_new ((PTreeType) i, (PCompareFunc) compare_keys) == NULL); + p_tree_insert (tree, PINT_TO_POINTER (1), PINT_TO_POINTER (10)); + P_TEST_CHECK (p_tree_get_nnodes (tree) == 0); + + p_mem_restore_vtable (); + + p_tree_free (tree); + } + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (ptree_invalid_test) +{ + p_libsys_init (); + + for (int i = (int) P_TREE_TYPE_BINARY; i <= (int) P_TREE_TYPE_AVL; ++i) { + /* Invalid usage */ + P_TEST_CHECK (p_tree_new ((PTreeType) i, NULL) == NULL); + P_TEST_CHECK (p_tree_new ((PTreeType) -1, (PCompareFunc) compare_keys) == NULL); + P_TEST_CHECK (p_tree_new ((PTreeType) -1, NULL) == NULL); + + P_TEST_CHECK (p_tree_new_with_data ((PTreeType) i, NULL, NULL) == NULL); + P_TEST_CHECK (p_tree_new_with_data ((PTreeType) -1, (PCompareDataFunc) compare_keys, NULL) == NULL); + P_TEST_CHECK (p_tree_new_with_data ((PTreeType) -1, NULL, NULL) == NULL); + + P_TEST_CHECK (p_tree_new_full ((PTreeType) i, + NULL, + NULL, + NULL, + NULL) == NULL); + P_TEST_CHECK (p_tree_new_full ((PTreeType) -1, + (PCompareDataFunc) compare_keys, + NULL, + NULL, + NULL) == NULL); + P_TEST_CHECK (p_tree_new_full ((PTreeType) -1, + NULL, + NULL, + NULL, + NULL) == NULL); + + P_TEST_CHECK (p_tree_remove (NULL, NULL) == FALSE); + P_TEST_CHECK (p_tree_lookup (NULL, NULL) == NULL); + P_TEST_CHECK (p_tree_get_type (NULL) == (PTreeType) -1); + P_TEST_CHECK (p_tree_get_nnodes (NULL) == 0); + + p_tree_insert (NULL, NULL, NULL); + p_tree_foreach (NULL, NULL, NULL); + p_tree_clear (NULL); + p_tree_free (NULL); + } + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (ptree_general_test) +{ + PTree *tree; + + p_libsys_init (); + + for (int i = (int) P_TREE_TYPE_BINARY; i <= (int) P_TREE_TYPE_AVL; ++i) { + /* Test 1 */ + tree = p_tree_new ((PTreeType) i, (PCompareFunc) compare_keys); + + P_TEST_CHECK (general_tree_test (tree, (PTreeType) i, false, false) == true); + + memset (&tree_data, 0, sizeof (tree_data)); + p_tree_free (tree); + + P_TEST_CHECK (check_tree_data_is_zero () == true); + + /* Test 2 */ + tree = p_tree_new_with_data ((PTreeType) i, + (PCompareDataFunc) compare_keys_data, + &tree_data); + + P_TEST_CHECK (general_tree_test (tree, (PTreeType) i, true, false) == true); + + memset (&tree_data, 0, sizeof (tree_data)); + p_tree_free (tree); + + P_TEST_CHECK (check_tree_data_is_zero () == true); + + /* Test 3 */ + tree = p_tree_new_full ((PTreeType) i, + (PCompareDataFunc) compare_keys_data, + &tree_data, + (PDestroyFunc) key_destroy_notify, + (PDestroyFunc) value_destroy_notify); + P_TEST_CHECK (general_tree_test (tree, (PTreeType) i, true, true) == true); + + memset (&tree_data, 0, sizeof (tree_data)); + p_tree_free (tree); + + P_TEST_CHECK (check_tree_data_is_zero () == true); + } + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (ptree_stress_test) +{ + PTree *tree; + + p_libsys_init (); + + for (int i = (int) P_TREE_TYPE_BINARY; i <= (int) P_TREE_TYPE_AVL; ++i) { + tree = p_tree_new_full ((PTreeType) i, + (PCompareDataFunc) compare_keys_data, + &tree_data, + (PDestroyFunc) key_destroy_notify, + (PDestroyFunc) value_destroy_notify); + + for (int j = 0; j < PTREE_STRESS_ITERATIONS; ++j) + P_TEST_CHECK (stress_tree_test (tree, PTREE_STRESS_NODES) == true); + + p_tree_free (tree); + } + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_SUITE_BEGIN() +{ + P_TEST_SUITE_RUN_CASE (ptree_nomem_test); + P_TEST_SUITE_RUN_CASE (ptree_invalid_test); + P_TEST_SUITE_RUN_CASE (ptree_general_test); + P_TEST_SUITE_RUN_CASE (ptree_stress_test); +} +P_TEST_SUITE_END() diff --git a/3rdparty/plibsys/tests/ptypes_test.cpp b/3rdparty/plibsys/tests/ptypes_test.cpp new file mode 100644 index 0000000..1e9c0e9 --- /dev/null +++ b/3rdparty/plibsys/tests/ptypes_test.cpp @@ -0,0 +1,456 @@ +/* + * The MIT License + * + * Copyright (C) 2014-2017 Alexander Saprykin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "plibsys.h" +#include "ptestmacros.h" + +P_TEST_MODULE_INIT (); + +P_TEST_CASE_BEGIN (ptypes_general_test) +{ + p_libsys_init (); + + P_TEST_CHECK (P_BYTE_ORDER == P_LITTLE_ENDIAN || + P_BYTE_ORDER == P_BIG_ENDIAN); + + P_TEST_CHECK (sizeof (pint8) == 1); + P_TEST_CHECK (sizeof (puint8) == 1); + P_TEST_CHECK (sizeof (pint16) == 2); + P_TEST_CHECK (sizeof (puint16) == 2); + P_TEST_CHECK (sizeof (pint32) == 4); + P_TEST_CHECK (sizeof (puint32) == 4); + P_TEST_CHECK (sizeof (pint64) == 8); + P_TEST_CHECK (sizeof (puint64) == 8); + P_TEST_CHECK (sizeof (void *) == sizeof (ppointer)); + P_TEST_CHECK (sizeof (const void *) == sizeof (pconstpointer)); + P_TEST_CHECK (sizeof (int) == sizeof (pboolean)); + P_TEST_CHECK (sizeof (char) == sizeof (pchar)); + P_TEST_CHECK (sizeof (short) == sizeof (pshort)); + P_TEST_CHECK (sizeof (int) == sizeof (pint)); + P_TEST_CHECK (sizeof (long) == sizeof (plong)); + P_TEST_CHECK (sizeof (unsigned char) == sizeof (puchar)); + P_TEST_CHECK (sizeof (unsigned short) == sizeof (pushort)); + P_TEST_CHECK (sizeof (unsigned int) == sizeof (puint)); + P_TEST_CHECK (sizeof (unsigned long) == sizeof (pulong)); + P_TEST_CHECK (sizeof (float) == sizeof (pfloat)); + P_TEST_CHECK (sizeof (double) == sizeof (pdouble)); + P_TEST_CHECK (sizeof (pintptr) == PLIBSYS_SIZEOF_VOID_P); + P_TEST_CHECK (sizeof (puintptr) == PLIBSYS_SIZEOF_VOID_P); + P_TEST_CHECK (sizeof (psize) == PLIBSYS_SIZEOF_SIZE_T); + P_TEST_CHECK (sizeof (pssize) == PLIBSYS_SIZEOF_SIZE_T); + P_TEST_CHECK (sizeof (plong) == PLIBSYS_SIZEOF_LONG); + P_TEST_CHECK (sizeof (pulong) == PLIBSYS_SIZEOF_LONG); + P_TEST_CHECK (sizeof (poffset) == 8); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (ptypes_pointers_convert_test) +{ + p_libsys_init (); + + ppointer pointer = P_INT_TO_POINTER (128); + P_TEST_CHECK (P_POINTER_TO_INT (pointer) == 128); + + pint pint_val = -64; + pointer = PINT_TO_POINTER (pint_val); + P_TEST_CHECK (PPOINTER_TO_INT (pointer) == -64); + + puint puint_val = 64; + pointer = PUINT_TO_POINTER (puint_val); + P_TEST_CHECK (PPOINTER_TO_UINT (pointer) == 64); + + psize psize_val = 1024; + pointer = PSIZE_TO_POINTER (psize_val); + P_TEST_CHECK (PPOINTER_TO_PSIZE (psize_val) == 1024); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (ptypes_min_max_test) +{ + p_libsys_init (); + + P_TEST_CHECK (P_MININT8 == (pint8) 0x80); + P_TEST_CHECK (P_MAXINT8 == (pint8) 0x7F); + P_TEST_CHECK (P_MAXUINT8 == (puint8) 0xFF); + P_TEST_CHECK (P_MININT16 == (pint16) 0x8000); + P_TEST_CHECK (P_MAXINT16 == (pint16) 0x7FFF); + P_TEST_CHECK (P_MAXUINT16 == (puint16) 0xFFFF); + P_TEST_CHECK (P_MININT32 == (pint32) 0x80000000); + P_TEST_CHECK (P_MAXINT32 == (pint32) 0x7FFFFFFF); + P_TEST_CHECK (P_MAXUINT32 == (puint32) 0xFFFFFFFF); + P_TEST_CHECK (P_MININT64 == (pint64) 0x8000000000000000LL); + P_TEST_CHECK (P_MAXINT64 == (pint64) 0x7FFFFFFFFFFFFFFFLL); + P_TEST_CHECK (P_MAXUINT64 == (puint64) 0xFFFFFFFFFFFFFFFFULL); + + if (PLIBSYS_SIZEOF_SIZE_T == 8) { + P_TEST_CHECK (P_MINSSIZE == P_MININT64); + P_TEST_CHECK (P_MAXSSIZE == P_MAXINT64); + P_TEST_CHECK (P_MAXSIZE == P_MAXUINT64); + + if (PLIBSYS_SIZEOF_LONG == 8) { + P_TEST_CHECK (P_MINSSIZE == P_MINLONG); + P_TEST_CHECK (P_MAXSSIZE == P_MAXLONG); + P_TEST_CHECK (P_MAXSIZE == P_MAXULONG); + } + } else { + P_TEST_CHECK (P_MINSSIZE == P_MININT32); + P_TEST_CHECK (P_MAXSSIZE == P_MAXINT32); + P_TEST_CHECK (P_MAXSIZE == P_MAXUINT32); + + if (PLIBSYS_SIZEOF_LONG == 4) { + P_TEST_CHECK (P_MINSSIZE == P_MINLONG); + P_TEST_CHECK (P_MAXSSIZE == P_MAXLONG); + P_TEST_CHECK (P_MAXSIZE == P_MAXULONG); + } + } + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (ptypes_modifiers_test) +{ + p_libsys_init (); + + psize size_val = 256; + printf ("%#" PSIZE_MODIFIER "x\n", size_val); + pssize ssize_val = -256; + printf ("%#" PSIZE_MODIFIER "x\n", ssize_val); + + puintptr puintptr_val = 512; + printf ("%#" PINTPTR_MODIFIER "x\n", puintptr_val); + pintptr pintptr_val = -512; + printf ("%#" PINTPTR_MODIFIER "x\n", pintptr_val); + + puint16 puint16_val = 1024; + printf ("%#" PINT16_MODIFIER "x\n", puint16_val); + pint16 pint16_val = -1024; + printf ("%#" PINT16_MODIFIER "x\n", pint16_val); + + puint32 puint32_val = 2048; + printf ("%#" PINT32_MODIFIER "x\n", puint32_val); + pint32 pint32_val = -2048; + printf ("%#" PINT32_MODIFIER "x\n", pint32_val); + + puint64 puint64_val = 4096; + printf ("%#" PINT64_MODIFIER "x\n", puint64_val); + pint64 pint64_val = -4096; + printf ("%#" PINT64_MODIFIER "x\n", pint64_val); + + poffset poffset_val = 8192; + printf ("%#" POFFSET_MODIFIER "x\n", poffset_val); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (ptypes_formats_test) +{ + p_libsys_init (); + + pssize ssize_val = -256; + printf ("%" PSSIZE_FORMAT "\n", ssize_val); + psize size_val = 256; + printf ("%" PSIZE_FORMAT "\n", size_val); + + puintptr puintptr_val = 512; + printf ("%" PUINTPTR_FORMAT "\n", puintptr_val); + pintptr pintptr_val = -512; + printf ("%" PINTPTR_FORMAT "\n", pintptr_val); + + puint16 puint16_val = 1024; + printf ("%" PUINT16_FORMAT "\n", puint16_val); + pint16 pint16_val = -1024; + printf ("%" PINT16_FORMAT "\n", pint16_val); + + puint32 puint32_val = 2048; + printf ("%" PUINT32_FORMAT "\n", puint32_val); + pint32 pint32_val = -2048; + printf ("%" PINT32_FORMAT "\n", pint32_val); + + puint64 puint64_val = 4096; + printf ("%" PUINT64_FORMAT "\n", puint64_val); + pint64 pint64_val = -4096; + printf ("%" PINT64_FORMAT "\n", pint64_val); + + poffset poffset_val = 8192; + printf ("%" POFFSET_FORMAT "\n", poffset_val); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (ptypes_host_network_test) +{ + p_libsys_init (); + + if (P_BYTE_ORDER == P_LITTLE_ENDIAN) { + pint16 pint16_val = PINT16_TO_BE (0xFFE0); + P_TEST_CHECK (pint16_val == (pint16) 0xE0FF); + P_TEST_CHECK (PINT16_FROM_BE (pint16_val) == (pint16) 0xFFE0); + P_TEST_CHECK (PINT16_TO_LE (pint16_val) == (pint16) 0xE0FF); + P_TEST_CHECK (PINT16_FROM_LE (pint16_val) == (pint16) 0xE0FF); + + puint16 puint16_val = PUINT16_TO_BE (0x0020); + P_TEST_CHECK (puint16_val == (puint16) 0x2000); + P_TEST_CHECK (PUINT16_FROM_BE (puint16_val) == (puint16) 0x0020); + P_TEST_CHECK (PUINT16_TO_LE (puint16_val) == (puint16) 0x2000); + P_TEST_CHECK (PUINT16_FROM_LE (puint16_val) == (puint16) 0x2000); + + pint32 pint32_val = PINT32_TO_BE (0xFFFFFFC0); + P_TEST_CHECK (pint32_val == (pint32) 0xC0FFFFFF); + P_TEST_CHECK (PINT32_FROM_BE (pint32_val) == (pint32) 0xFFFFFFC0); + P_TEST_CHECK (PINT32_TO_LE (pint32_val) == (pint32) 0xC0FFFFFF); + P_TEST_CHECK (PINT32_FROM_LE (pint32_val) == (pint32) 0xC0FFFFFF); + + puint32 puint32_val = PUINT32_TO_BE (0x00000040); + P_TEST_CHECK (puint32_val == (puint32) 0x40000000); + P_TEST_CHECK (PUINT32_FROM_BE (puint32_val) == (puint32) 0x00000040); + P_TEST_CHECK (PUINT32_TO_LE (puint32_val) == (puint32) 0x40000000); + P_TEST_CHECK (PUINT32_FROM_LE (puint32_val) == (puint32) 0x40000000); + + pint64 pint64_val = PINT64_TO_BE (0xFFFFFFFFFFFFFF80LL); + P_TEST_CHECK (pint64_val == (pint64) 0x80FFFFFFFFFFFFFFLL); + P_TEST_CHECK (PINT64_FROM_BE (pint64_val) == (pint64) 0xFFFFFFFFFFFFFF80LL); + P_TEST_CHECK (PINT64_TO_LE (pint64_val) == (pint64) 0x80FFFFFFFFFFFFFFLL); + P_TEST_CHECK (PINT64_FROM_LE (pint64_val) == (pint64) 0x80FFFFFFFFFFFFFFLL); + + puint64 puint64_val = PUINT64_TO_BE (0x0000000000000080ULL); + P_TEST_CHECK (puint64_val == (puint64) 0x8000000000000000ULL); + P_TEST_CHECK (PUINT64_FROM_BE (puint64_val) == (puint64) 0x0000000000000080ULL); + P_TEST_CHECK (PUINT64_TO_LE (puint64_val) == (puint64) 0x8000000000000000ULL); + P_TEST_CHECK (PUINT64_FROM_LE (puint64_val) == (puint64) 0x8000000000000000ULL); + + pint pint_val = PINT_TO_BE (0xFFFFFC00); + P_TEST_CHECK (pint_val == (pint) 0x00FCFFFF); + P_TEST_CHECK (PINT_FROM_BE (pint_val) == (pint) 0xFFFFFC00); + P_TEST_CHECK (PINT_TO_LE (pint_val) == (pint) 0x00FCFFFF); + P_TEST_CHECK (PINT_FROM_LE (pint_val) == (pint) 0x00FCFFFF); + + puint puint_val = PUINT_TO_BE (0x00000400); + P_TEST_CHECK (puint_val == (puint) 0x00040000); + P_TEST_CHECK (PUINT_FROM_BE (puint_val) == (puint) 0x00000400); + P_TEST_CHECK (PUINT_TO_LE (puint_val) == (puint) 0x00040000); + P_TEST_CHECK (PUINT_FROM_LE (puint_val) == (puint) 0x00040000); + + if (PLIBSYS_SIZEOF_LONG == 8) { + plong plong_val = PLONG_TO_BE (0xFFFFFFFFFFFFF800LL); + P_TEST_CHECK (plong_val == (plong) 0x00F8FFFFFFFFFFFFLL); + P_TEST_CHECK (PLONG_FROM_BE (plong_val) == (plong) 0xFFFFFFFFFFFFF800LL); + P_TEST_CHECK (PLONG_TO_LE (plong_val) == (plong) 0x00F8FFFFFFFFFFFFLL); + P_TEST_CHECK (PLONG_FROM_LE (plong_val) == (plong) 0x00F8FFFFFFFFFFFFLL); + + pulong pulong_val = PULONG_TO_BE (0x0000000000000800ULL); + P_TEST_CHECK (pulong_val == (pulong) 0x0008000000000000ULL); + P_TEST_CHECK (PULONG_FROM_BE (pulong_val) == (pulong) 0x0000000000000800ULL); + P_TEST_CHECK (PULONG_TO_LE (pulong_val) == (pulong) 0x0008000000000000ULL); + P_TEST_CHECK (PULONG_FROM_LE (pulong_val) == (pulong) 0x0008000000000000ULL); + } else { + plong plong_val = PLONG_TO_BE (0xFFFFF800); + P_TEST_CHECK (plong_val == (plong) 0x00F8FFFF); + P_TEST_CHECK (PLONG_FROM_BE (plong_val) == (plong) 0xFFFFF800); + P_TEST_CHECK (PLONG_TO_LE (plong_val) == (plong) 0x00F8FFFF); + P_TEST_CHECK (PLONG_FROM_LE (plong_val) == (plong) 0x00F8FFFF); + + pulong pulong_val = PULONG_TO_BE (0x00000800); + P_TEST_CHECK (pulong_val == (pulong) 0x00080000); + P_TEST_CHECK (PULONG_FROM_BE (pulong_val) == (pulong) 0x00000800); + P_TEST_CHECK (PULONG_TO_LE (pulong_val) == (pulong) 0x00080000); + P_TEST_CHECK (PULONG_FROM_LE (pulong_val) == (pulong) 0x00080000); + } + + if (PLIBSYS_SIZEOF_SIZE_T == 8) { + psize psize_val = PSIZE_TO_BE (0x0000000000001000ULL); + P_TEST_CHECK (psize_val == (psize) 0x0010000000000000ULL); + P_TEST_CHECK (PSIZE_FROM_BE (psize_val) == (psize) 0x0000000000001000ULL); + P_TEST_CHECK (PSIZE_TO_LE (psize_val) == (psize) 0x0010000000000000ULL); + P_TEST_CHECK (PSIZE_FROM_LE (psize_val) == (psize) 0x0010000000000000ULL); + + pssize pssize_val = PSSIZE_TO_BE (0x000000000000F000LL); + P_TEST_CHECK (pssize_val == (pssize) 0x00F0000000000000LL); + P_TEST_CHECK (PSSIZE_FROM_BE (pssize_val) == (pssize) 0x000000000000F000LL); + P_TEST_CHECK (PSSIZE_TO_LE (pssize_val) == (pssize) 0x00F0000000000000LL); + P_TEST_CHECK (PSSIZE_FROM_LE (pssize_val) == (pssize) 0x00F0000000000000LL); + } else { + psize psize_val = PSIZE_TO_BE (0x00001000); + P_TEST_CHECK (psize_val == (psize) 0x00100000); + P_TEST_CHECK (PSIZE_FROM_BE (psize_val) == (psize) 0x00001000); + P_TEST_CHECK (PSIZE_TO_LE (psize_val) == (psize) 0x00100000); + P_TEST_CHECK (PSIZE_FROM_LE (psize_val) == (psize) 0x00100000); + + pssize pssize_val = PSSIZE_TO_BE (0x0000F000); + P_TEST_CHECK (pssize_val == (pssize) 0x00F00000); + P_TEST_CHECK (PSSIZE_FROM_BE (pssize_val) == (pssize) 0x0000F000); + P_TEST_CHECK (PSSIZE_TO_LE (pssize_val) == (pssize) 0x00F00000); + P_TEST_CHECK (PSSIZE_FROM_LE (pssize_val) == (pssize) 0x00F00000); + } + + puint16_val = p_htons (0x0020); + P_TEST_CHECK (puint16_val == (puint16) 0x2000); + P_TEST_CHECK (p_ntohs (puint16_val) == (puint16) 0x0020); + + puint32_val = p_htonl (0x00000040); + P_TEST_CHECK (puint32_val == (puint32) 0x40000000); + P_TEST_CHECK (p_ntohl (puint32_val) == (puint32) 0x00000040); + } else { + pint16 pint16_val = PINT16_TO_LE (0xFFE0); + P_TEST_CHECK (pint16_val == (pint16) 0xE0FF); + P_TEST_CHECK (PINT16_FROM_LE (pint16_val) == (pint16) 0xFFE0); + P_TEST_CHECK (PINT16_TO_BE (pint16_val) == (pint16) 0xE0FF); + P_TEST_CHECK (PINT16_FROM_BE (pint16_val) == (pint16) 0xE0FF); + + puint16 puint16_val = PUINT16_TO_LE (0x0020); + P_TEST_CHECK (puint16_val == (puint16) 0x2000); + P_TEST_CHECK (PUINT16_FROM_LE (puint16_val) == (puint16) 0x0020); + P_TEST_CHECK (PUINT16_TO_BE (puint16_val) == (puint16) 0x2000); + P_TEST_CHECK (PUINT16_FROM_BE (puint16_val) == (puint16) 0x2000); + + pint32 pint32_val = PINT32_TO_LE (0xFFFFFFC0); + P_TEST_CHECK (pint32_val == (pint32) 0xC0FFFFFF); + P_TEST_CHECK (PINT32_FROM_LE (pint32_val) == (pint32) 0xFFFFFFC0); + P_TEST_CHECK (PINT32_TO_BE (pint32_val) == (pint32) 0xC0FFFFFF); + P_TEST_CHECK (PINT32_FROM_BE (pint32_val) == (pint32) 0xC0FFFFFF); + + puint32 puint32_val = PUINT32_TO_LE (0x00000040); + P_TEST_CHECK (puint32_val == (puint32) 0x40000000); + P_TEST_CHECK (PUINT32_FROM_LE (puint32_val) == (puint32) 0x00000040); + P_TEST_CHECK (PUINT32_TO_BE (puint32_val) == (puint32) 0x40000000); + P_TEST_CHECK (PUINT32_FROM_BE (puint32_val) == (puint32) 0x40000000); + + pint64 pint64_val = PINT64_TO_LE (0xFFFFFFFFFFFFFF80LL); + P_TEST_CHECK (pint64_val == (pint64) 0x80FFFFFFFFFFFFFFLL); + P_TEST_CHECK (PINT64_FROM_LE (pint64_val) == (pint64) 0xFFFFFFFFFFFFFF80LL); + P_TEST_CHECK (PINT64_TO_BE (pint64_val) == (pint64) 0x80FFFFFFFFFFFFFFLL); + P_TEST_CHECK (PINT64_FROM_BE (pint64_val) == (pint64) 0x80FFFFFFFFFFFFFFLL); + + puint64 puint64_val = PUINT64_TO_LE (0x0000000000000080ULL); + P_TEST_CHECK (puint64_val == (puint64) 0x8000000000000000ULL); + P_TEST_CHECK (PUINT64_FROM_LE (puint64_val) == (puint64) 0x0000000000000080ULL); + P_TEST_CHECK (PUINT64_TO_BE (puint64_val) == (puint64) 0x8000000000000000ULL); + P_TEST_CHECK (PUINT64_FROM_BE (puint64_val) == (puint64) 0x8000000000000000ULL); + + pint pint_val = PINT_TO_LE (0xFFFFFC00); + P_TEST_CHECK (pint_val == (pint) 0x00FCFFFF); + P_TEST_CHECK (PINT_FROM_LE (pint_val) == (pint) 0xFFFFFC00); + P_TEST_CHECK (PINT_TO_BE (pint_val) == (pint) 0x00FCFFFF); + P_TEST_CHECK (PINT_FROM_BE (pint_val) == (pint) 0x00FCFFFF); + + puint puint_val = PUINT_TO_LE (0x00000400); + P_TEST_CHECK (puint_val == (puint) 0x00040000); + P_TEST_CHECK (PUINT_FROM_LE (puint_val) == (puint) 0x00000400); + P_TEST_CHECK (PUINT_TO_BE (puint_val) == (puint) 0x00040000); + P_TEST_CHECK (PUINT_FROM_BE (puint_val) == (puint) 0x00040000); + + if (PLIBSYS_SIZEOF_LONG == 8) { + plong plong_val = PLONG_TO_LE (0xFFFFFFFFFFFFF800LL); + P_TEST_CHECK (plong_val == (plong) 0x00F8FFFFFFFFFFFFLL); + P_TEST_CHECK (PLONG_FROM_LE (plong_val) == (plong) 0xFFFFFFFFFFFFF800LL); + P_TEST_CHECK (PLONG_TO_BE (plong_val) == (plong) 0x00F8FFFFFFFFFFFFLL); + P_TEST_CHECK (PLONG_FROM_BE (plong_val) == (plong) 0x00F8FFFFFFFFFFFFLL); + + pulong pulong_val = PULONG_TO_LE (0x0000000000000800ULL); + P_TEST_CHECK (pulong_val == (pulong) 0x0008000000000000ULL); + P_TEST_CHECK (PULONG_FROM_LE (pulong_val) == (pulong) 0x0000000000000800ULL); + P_TEST_CHECK (PULONG_TO_BE (pulong_val) == (pulong) 0x0008000000000000ULL); + P_TEST_CHECK (PULONG_FROM_BE (pulong_val) == (pulong) 0x0008000000000000ULL); + } else { + plong plong_val = PLONG_TO_LE (0xFFFFF800); + P_TEST_CHECK (plong_val == (plong) 0x00F8FFFF); + P_TEST_CHECK (PLONG_FROM_LE (plong_val) == (plong) 0xFFFFF800); + P_TEST_CHECK (PLONG_TO_BE (plong_val) == (plong) 0x00F8FFFF); + P_TEST_CHECK (PLONG_FROM_BE (plong_val) == (plong) 0x00F8FFFF); + + pulong pulong_val = PULONG_TO_LE (0x00000800); + P_TEST_CHECK (pulong_val == (pulong) 0x00080000); + P_TEST_CHECK (PULONG_FROM_LE (pulong_val) == (pulong) 0x00000800); + P_TEST_CHECK (PULONG_TO_BE (pulong_val) == (pulong) 0x00080000); + P_TEST_CHECK (PULONG_FROM_BE (pulong_val) == (pulong) 0x00080000); + } + + if (PLIBSYS_SIZEOF_SIZE_T == 8) { + psize psize_val = PSIZE_TO_LE (0x0000000000001000ULL); + P_TEST_CHECK (psize_val == (psize) 0x0010000000000000ULL); + P_TEST_CHECK (PSIZE_FROM_LE (psize_val) == (psize) 0x0000000000001000ULL); + P_TEST_CHECK (PSIZE_TO_BE (psize_val) == (psize) 0x0010000000000000ULL); + P_TEST_CHECK (PSIZE_FROM_BE (psize_val) == (psize) 0x0010000000000000ULL); + + pssize pssize_val = PSSIZE_TO_LE (0x000000000000F000LL); + P_TEST_CHECK (pssize_val == (pssize) 0x00F0000000000000LL); + P_TEST_CHECK (PSSIZE_FROM_LE (pssize_val) == (pssize) 0x000000000000F000LL); + P_TEST_CHECK (PSSIZE_TO_BE (pssize_val) == (pssize) 0x00F0000000000000LL); + P_TEST_CHECK (PSSIZE_FROM_BE (pssize_val) == (pssize) 0x00F0000000000000LL); + } else { + psize psize_val = PSIZE_TO_LE (0x00001000); + P_TEST_CHECK (psize_val == (psize) 0x00100000); + P_TEST_CHECK (PSIZE_FROM_LE (psize_val) == (psize) 0x00001000); + P_TEST_CHECK (PSIZE_TO_BE (psize_val) == (psize) 0x00100000); + P_TEST_CHECK (PSIZE_FROM_BE (psize_val) == (psize) 0x00100000); + + pssize pssize_val = PSSIZE_TO_LE (0x0000F000); + P_TEST_CHECK (pssize_val == (pssize) 0x00F00000); + P_TEST_CHECK (PSSIZE_FROM_LE (pssize_val) == (pssize) 0x0000F000); + P_TEST_CHECK (PSSIZE_TO_BE (pssize_val) == (pssize) 0x00F00000); + P_TEST_CHECK (PSSIZE_FROM_BE (pssize_val) == (pssize) 0x00F00000); + } + + puint16_val = p_htons (0x0020); + P_TEST_CHECK (puint16_val == (puint16) 0x0020); + P_TEST_CHECK (p_ntohs (puint16_val) == (puint16) 0x0020); + + puint32_val = p_htonl (0x00000040); + P_TEST_CHECK (puint32_val == (puint32) 0x00000040); + P_TEST_CHECK (p_ntohl (puint32_val) == (puint32) 0x00000040); + } + + puint16 puint16_val = PUINT16_SWAP_BYTES (0x0020); + P_TEST_CHECK (puint16_val == (puint16) 0x2000); + P_TEST_CHECK (PUINT16_SWAP_BYTES (puint16_val) == (puint16) 0x0020); + + puint32 puint32_val = PUINT32_SWAP_BYTES (0x00000040); + P_TEST_CHECK (puint32_val == (puint32) 0x40000000); + P_TEST_CHECK (PUINT32_SWAP_BYTES (puint32_val) == (puint32) 0x00000040); + + puint64 puint64_val = PUINT64_SWAP_BYTES (0x0000000000000080ULL); + P_TEST_CHECK (puint64_val == (puint64) 0x8000000000000000ULL); + P_TEST_CHECK (PUINT64_SWAP_BYTES (puint64_val) == (puint64) 0x0000000000000080ULL); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_SUITE_BEGIN() +{ + P_TEST_SUITE_RUN_CASE (ptypes_general_test); + P_TEST_SUITE_RUN_CASE (ptypes_pointers_convert_test); + P_TEST_SUITE_RUN_CASE (ptypes_min_max_test); + P_TEST_SUITE_RUN_CASE (ptypes_modifiers_test); + P_TEST_SUITE_RUN_CASE (ptypes_formats_test); + P_TEST_SUITE_RUN_CASE (ptypes_host_network_test); +} +P_TEST_SUITE_END() diff --git a/3rdparty/plibsys/tests/puthread_test.cpp b/3rdparty/plibsys/tests/puthread_test.cpp new file mode 100644 index 0000000..5c31cd1 --- /dev/null +++ b/3rdparty/plibsys/tests/puthread_test.cpp @@ -0,0 +1,481 @@ +/* + * The MIT License + * + * Copyright (C) 2013-2019 Alexander Saprykin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 "plibsys.h" +#include "ptestmacros.h" + +P_TEST_MODULE_INIT (); + +static pint thread_wakes_1 = 0; +static pint thread_wakes_2 = 0; +static pint thread_to_wakes = 0; +static volatile pboolean is_threads_working = FALSE; + +static P_HANDLE thread1_id = (P_HANDLE) NULL; +static P_HANDLE thread2_id = (P_HANDLE) NULL; +static PUThread * thread1_obj = NULL; +static PUThread * thread2_obj = NULL; + +static PUThreadKey * tls_key = NULL; +static PUThreadKey * tls_key_2 = NULL; +static volatile pint free_counter = 0; + +extern "C" ppointer pmem_alloc (psize nbytes) +{ + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" ppointer pmem_realloc (ppointer block, psize nbytes) +{ + P_UNUSED (block); + P_UNUSED (nbytes); + return (ppointer) NULL; +} + +extern "C" void pmem_free (ppointer block) +{ + P_UNUSED (block); +} + +extern "C" void free_with_check (ppointer mem) +{ + p_free (mem); + p_atomic_int_inc (&free_counter); +} + +static void * test_thread_func (void *data) +{ + pint *counter = static_cast < pint * > (data); + + if ((*counter) == 1) { + thread1_id = p_uthread_current_id (); + thread1_obj = p_uthread_current (); + } else { + thread2_id = p_uthread_current_id (); + thread2_obj = p_uthread_current (); + } + + p_uthread_set_local (tls_key, (ppointer) p_uthread_current_id ()); + + *counter = 0; + + while (is_threads_working == TRUE) { + p_uthread_sleep (10); + ++(*counter); + p_uthread_yield (); + + if (p_uthread_get_local (tls_key) != (ppointer) p_uthread_current_id ()) + p_uthread_exit (-1); + } + + p_uthread_exit (*counter); + + return NULL; +} + +static void * test_thread_nonjoinable_func (void *data) +{ + pint *counter = static_cast < pint * > (data); + + is_threads_working = TRUE; + + for (int i = thread_to_wakes; i > 0; --i) { + p_uthread_sleep (10); + ++(*counter); + p_uthread_yield (); + } + + is_threads_working = FALSE; + + p_uthread_exit (0); + + return NULL; +} + +static void * test_thread_tls_func (void *data) +{ + pint self_thread_free = *((pint *) data); + + pint *tls_value = (pint *) p_malloc0 (sizeof (pint)); + *tls_value = 0; + p_uthread_set_local (tls_key, (ppointer) tls_value); + + pint prev_tls = 0; + pint counter = 0; + + while (is_threads_working == TRUE) { + p_uthread_sleep (10); + + pint *last_tls = (pint *) p_uthread_get_local (tls_key); + + if ((*last_tls) != prev_tls) + p_uthread_exit (-1); + + pint *tls_new_value = (pint *) p_malloc0 (sizeof (pint)); + + *tls_new_value = (*last_tls) + 1; + prev_tls = (*last_tls) + 1; + + p_uthread_replace_local (tls_key, (ppointer) tls_new_value); + + if (self_thread_free) + p_free (last_tls); + + ++counter; + + p_uthread_yield (); + } + + if (self_thread_free) { + pint *last_tls = (pint *) p_uthread_get_local (tls_key); + + if ((*last_tls) != prev_tls) + p_uthread_exit (-1); + + p_free (last_tls); + + p_uthread_replace_local (tls_key, (ppointer) NULL); + } + + p_uthread_exit (counter); + + return NULL; +} + +static void * test_thread_tls_create_func (void *data) +{ + P_UNUSED (data); + + pint *tls_value = (pint *) p_malloc0 (sizeof (pint)); + *tls_value = 0; + p_uthread_set_local (tls_key, (ppointer) tls_value); + + pint *tls_value_2 = (pint *) p_malloc0 (sizeof (pint)); + *tls_value_2 = 0; + p_uthread_set_local (tls_key_2, (ppointer) tls_value_2); + + return NULL; +} + +P_TEST_CASE_BEGIN (puthread_nomem_test) +{ + p_libsys_init (); + + PUThreadKey *thread_key = p_uthread_local_new (p_free); + P_TEST_CHECK (thread_key != NULL); + + PMemVTable vtable; + + vtable.free = pmem_free; + vtable.malloc = pmem_alloc; + vtable.realloc = pmem_realloc; + + P_TEST_CHECK (p_mem_set_vtable (&vtable) == TRUE); + + thread_wakes_1 = 0; + thread_wakes_2 = 0; + + P_TEST_CHECK (p_uthread_create ((PUThreadFunc) test_thread_func, + (ppointer) &thread_wakes_1, + TRUE, + NULL) == NULL); + + P_TEST_CHECK (p_uthread_create_full ((PUThreadFunc) test_thread_func, + (ppointer) &thread_wakes_2, + TRUE, + P_UTHREAD_PRIORITY_NORMAL, + 0, + NULL) == NULL); + + P_TEST_CHECK (p_uthread_current () == NULL); + P_TEST_CHECK (p_uthread_local_new (NULL) == NULL); + + p_uthread_exit (0); + + p_uthread_set_local (thread_key, PINT_TO_POINTER (10)); + + ppointer tls_value = p_uthread_get_local (thread_key); + + if (tls_value != NULL) { + P_TEST_CHECK (tls_value == PINT_TO_POINTER (10)); + p_uthread_set_local (thread_key, NULL); + } + + p_uthread_replace_local (thread_key, PINT_TO_POINTER (12)); + + tls_value = p_uthread_get_local (thread_key); + + if (tls_value != NULL) { + P_TEST_CHECK (tls_value == PINT_TO_POINTER (12)); + p_uthread_set_local (thread_key, NULL); + } + + p_mem_restore_vtable (); + + p_uthread_local_free (thread_key); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (puthread_bad_input_test) +{ + p_libsys_init (); + + P_TEST_CHECK (p_uthread_create (NULL, NULL, FALSE, NULL) == NULL); + P_TEST_CHECK (p_uthread_create_full (NULL, NULL, FALSE, P_UTHREAD_PRIORITY_NORMAL, 0, NULL) == NULL); + P_TEST_CHECK (p_uthread_join (NULL) == -1); + P_TEST_CHECK (p_uthread_set_priority (NULL, P_UTHREAD_PRIORITY_NORMAL) == FALSE); + P_TEST_CHECK (p_uthread_get_local (NULL) == NULL); + p_uthread_set_local (NULL, NULL); + p_uthread_replace_local (NULL, NULL); + p_uthread_ref (NULL); + p_uthread_unref (NULL); + p_uthread_local_free (NULL); + p_uthread_exit (0); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (puthread_general_test) +{ + p_libsys_init (); + + thread_wakes_1 = 1; + thread_wakes_2 = 2; + thread1_id = (P_HANDLE) NULL; + thread2_id = (P_HANDLE) NULL; + thread1_obj = NULL; + thread2_obj = NULL; + + tls_key = p_uthread_local_new (NULL); + P_TEST_CHECK (tls_key != NULL); + + /* Threre is no guarantee that we wouldn't get one of the IDs + * of the finished test threads */ + + P_HANDLE main_id = p_uthread_current_id (); + + is_threads_working = TRUE; + + PUThread *thr1 = p_uthread_create_full ((PUThreadFunc) test_thread_func, + (ppointer) &thread_wakes_1, + TRUE, + P_UTHREAD_PRIORITY_NORMAL, + 64 * 1024, + "thread_name"); + + PUThread *thr2 = p_uthread_create_full ((PUThreadFunc) test_thread_func, + (ppointer) &thread_wakes_2, + TRUE, + P_UTHREAD_PRIORITY_NORMAL, + 64 * 1024, + "very_long_name_for_thread_testing"); + + p_uthread_ref (thr1); + + p_uthread_set_priority (thr1, P_UTHREAD_PRIORITY_NORMAL); + + P_TEST_REQUIRE (thr1 != NULL); + P_TEST_REQUIRE (thr2 != NULL); + + p_uthread_sleep (5000); + + is_threads_working = FALSE; + + P_TEST_CHECK (p_uthread_join (thr1) == thread_wakes_1); + P_TEST_CHECK (p_uthread_join (thr2) == thread_wakes_2); + + P_TEST_REQUIRE (thread1_id != thread2_id); + P_TEST_CHECK (thread1_id != main_id && thread2_id != main_id); + + P_TEST_CHECK (thread1_obj == thr1); + P_TEST_CHECK (thread2_obj == thr2); + + p_uthread_local_free (tls_key); + p_uthread_unref (thr1); + p_uthread_unref (thr2); + + p_uthread_unref (thr1); + + PUThread *cur_thr = p_uthread_current (); + P_TEST_CHECK (cur_thr != NULL); + + P_TEST_CHECK (p_uthread_ideal_count () > 0); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (puthread_nonjoinable_test) +{ + p_libsys_init (); + + thread_wakes_1 = 0; + thread_to_wakes = 100; + is_threads_working = TRUE; + + PUThread *thr1 = p_uthread_create ((PUThreadFunc) test_thread_nonjoinable_func, + (ppointer) &thread_wakes_1, + FALSE, + NULL); + + P_TEST_REQUIRE (thr1 != NULL); + + p_uthread_sleep (3000); + + P_TEST_CHECK (p_uthread_join (thr1) == -1); + + while (is_threads_working == TRUE) + p_uthread_sleep (10); + + P_TEST_CHECK (thread_wakes_1 == thread_to_wakes); + + p_uthread_unref (thr1); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_CASE_BEGIN (puthread_tls_test) +{ + p_libsys_init (); + + /* With destroy notification */ + tls_key = p_uthread_local_new (free_with_check); + + is_threads_working = TRUE; + free_counter = 0; + + pint self_thread_free = 0; + + PUThread *thr1 = p_uthread_create ((PUThreadFunc) test_thread_tls_func, + (ppointer) &self_thread_free, + TRUE, + NULL); + + PUThread *thr2 = p_uthread_create ((PUThreadFunc) test_thread_tls_func, + (ppointer) &self_thread_free, + TRUE, + NULL); + + P_TEST_REQUIRE (thr1 != NULL); + P_TEST_REQUIRE (thr2 != NULL); + + p_uthread_sleep (5000); + + is_threads_working = FALSE; + + pint total_counter = 0; + + total_counter += (p_uthread_join (thr1) + 1); + total_counter += (p_uthread_join (thr2) + 1); + + P_TEST_CHECK (total_counter == free_counter); + + p_uthread_local_free (tls_key); + p_uthread_unref (thr1); + p_uthread_unref (thr2); + + /* Without destroy notification */ + tls_key = p_uthread_local_new (NULL); + + free_counter = 0; + is_threads_working = TRUE; + self_thread_free = 1; + + thr1 = p_uthread_create ((PUThreadFunc) test_thread_tls_func, + (ppointer) &self_thread_free, + TRUE, + NULL); + + thr2 = p_uthread_create ((PUThreadFunc) test_thread_tls_func, + (ppointer) &self_thread_free, + TRUE, + NULL); + + P_TEST_REQUIRE (thr1 != NULL); + P_TEST_REQUIRE (thr2 != NULL); + + p_uthread_sleep (5000); + + is_threads_working = FALSE; + + total_counter = 0; + + total_counter += (p_uthread_join (thr1) + 1); + total_counter += (p_uthread_join (thr2) + 1); + + P_TEST_CHECK (total_counter > 0); + P_TEST_CHECK (free_counter == 0); + + p_uthread_local_free (tls_key); + p_uthread_unref (thr1); + p_uthread_unref (thr2); + + /* With implicit thread exit */ + tls_key = p_uthread_local_new (free_with_check); + tls_key_2 = p_uthread_local_new (free_with_check); + + free_counter = 0; + + thr1 = p_uthread_create ((PUThreadFunc) test_thread_tls_create_func, + NULL, + TRUE, + NULL); + + thr2 = p_uthread_create ((PUThreadFunc) test_thread_tls_create_func, + NULL, + TRUE, + NULL); + + P_TEST_REQUIRE (thr1 != NULL); + P_TEST_REQUIRE (thr2 != NULL); + + p_uthread_join (thr1); + p_uthread_join (thr2); + + P_TEST_CHECK (free_counter == 4); + + p_uthread_local_free (tls_key); + p_uthread_local_free (tls_key_2); + p_uthread_unref (thr1); + p_uthread_unref (thr2); + + p_libsys_shutdown (); +} +P_TEST_CASE_END () + +P_TEST_SUITE_BEGIN() +{ + P_TEST_SUITE_RUN_CASE (puthread_nomem_test); + P_TEST_SUITE_RUN_CASE (puthread_bad_input_test); + P_TEST_SUITE_RUN_CASE (puthread_general_test); + P_TEST_SUITE_RUN_CASE (puthread_nonjoinable_test); + P_TEST_SUITE_RUN_CASE (puthread_tls_test); +} +P_TEST_SUITE_END() -- cgit v1.2.1