Add:libhv open source code.

This commit is contained in:
xiaojiazhu 2024-03-08 07:16:14 -08:00
parent 2d0bf0ff64
commit 44a74ac4aa
356 changed files with 91673 additions and 1 deletions

View File

@ -2,6 +2,7 @@
add_subdirectory(sqlite3/sqlite-3430000)
add_subdirectory(goahead-5.2.0)
# ================= httpserver ================= #
find_program(M4 m4)
if(NOT M4)
message("m4 not found. Install before continuing.")
@ -15,5 +16,7 @@ if(NOT RAGEL)
WORKING_DIRECTORY ${EXTERNAL_SOURCE_PATH}/gtest/)
endif()
add_subdirectory(httpserver.h-master/src)
# ================= httpserver end ================= #
add_subdirectory(cJSON-1.7.17)
add_subdirectory(libhv/libhv-1.3.2)

View File

@ -0,0 +1,95 @@
---
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlinesLeft: true
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: true
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: true
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: false
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
BeforeCatch: true
BeforeElse: true
IndentBraces: false
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 160
CommentPragmas: '^ IWYU pragma:'
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: true
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
IncludeCategories:
- Regex: '.*'
Priority: 1
- Regex: '^".*/'
Priority: 2
- Regex: '^<)'
Priority: 3
IncludeIsMainRegex: '$'
IndentCaseLabels: false
IndentWidth: 4
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBlockIndentWidth: 4
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
ReflowComments: true
SortIncludes: false
SpaceAfterCStyleCast: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 4
UseTab: Never
...

View File

@ -0,0 +1 @@
cpputil/json.hpp linguist-vendored

72
external/libhv/libhv-1.3.2/.gitignore vendored Normal file
View File

@ -0,0 +1,72 @@
# Compiled Object files
*.o
*.lo
*.slo
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Compiled Static libraries
*.a
*.la
*.lai
*.lib
# Executables
*.exe
*.out
*.app
# cache
*~
*.bk
*.bak
*.old
*.new
# IDE
.vs
.vscode
.DS_Store
tags
cscope*
.ycm*
# generated
examples/protorpc/generated
# output
*.pid
*.log
*.db
include
lib
bin
tmp
dist
test
*_test
build
config.mk
hconfig.h
html/uploads
# msvc
*.VC.*
*.vcxproj.*
Debug
Release
# cmake
CMakeFiles
CMakeCache.txt
cmake_install.cmake

39
external/libhv/libhv-1.3.2/.travis.yml vendored Normal file
View File

@ -0,0 +1,39 @@
language: cpp
jobs:
include:
- os: linux
dist: xenial
compiler: gcc
env: COVERALLS=no
script:
- ./configure
- make libhv examples unittest evpp
- os: osx
compiler: clang
env: COVERALLS=no
script:
- ./configure
- make libhv examples unittest evpp
- os: windows
compiler: msvc
env: COVERALLS=no
script:
- mkdir win64
- cd win64
- cmake .. -G "Visual Studio 15 2017 Win64"
- cmake --build .
before_script:
- if [ "$COVERALLS" = "yes" ]; then
pip install --user cpp-coveralls;
export CC="$CC --coverage" CXX="$CXX --coverage";
fi
after_success:
- if [ "$COVERALLS" = "yes" ]; then
scripts/test-coverage.sh;
coveralls --gcov-options '\-lp' --include base --include event --include http;
fi

156
external/libhv/libhv-1.3.2/BUILD.md vendored Normal file
View File

@ -0,0 +1,156 @@
## Prequired
- c99
- c++11
gcc4.8+, msvc2015 or later
## Makefile
options see [config.ini](config.ini)
```
./configure --with-openssl
make
sudo make install
```
## cmake
options see [CMakeLists.txt](CMakeLists.txt)
```
mkdir build
cd build
cmake .. -DWITH_OPENSSL=ON
cmake --build .
```
## Unix
use Makefile or cmake
## Windows
use cmake
```
mkdir win64
cd win64
cmake .. -G "Visual Studio 15 2017 Win64"
#cmake .. -G "Visual Studio 16 2019" -A x64
#cmake .. -G "Visual Studio 17 2022" -A x64
cmake --build .
```
## CROSS_COMPILE
use Makefile
```
sudo apt install gcc-arm-linux-gnueabi g++-arm-linux-gnueabi # ubuntu
export CROSS_COMPILE=arm-linux-gnueabi-
./configure
make clean
make libhv
```
or use cmake
```
mkdir build
cd build
cmake .. -DCMAKE_C_COMPILER=arm-linux-gnueabi-gcc -DCMAKE_CXX_COMPILER=arm-linux-gnueabi-g++
cmake --build . --target libhv libhv_static
```
### mingw
see CROSS_COMPILE
```
sudo apt install mingw-w64 # ubuntu
#export CROSS_COMPILE=i686-w64-mingw32-
export CROSS_COMPILE=x86_64-w64-mingw32-
./configure
make clean
make libhv
```
### Android
```
#https://developer.android.com/ndk/downloads
#export ANDROID_NDK_ROOT=~/Downloads/android-ndk-r21b
mkdir build
cd build
cmake .. -DCMAKE_TOOLCHAIN_FILE="$ANDROID_NDK_ROOT/build/cmake/android.toolchain.cmake" -DANDROID_ABI="arm64-v8a" -DANDROID_PLATFORM=android-21
cmake --build . --target hv --config Release
```
### iOS
```
mkdir build
cd build
cmake .. -G Xcode -DCMAKE_TOOLCHAIN_FILE=../cmake/ios.toolchain.cmake -DPLATFORM=OS64 -DARCHS="arm64" -DDEPLOYMENT_TARGET=9.0
cmake --build . --target hv_static --config Release
```
## targets
### lib
- make libhv
### examples
- make examples
### unittest
- make unittest
## options
### compile without c++
```
./configure --without-evpp
make clean && make libhv
```
### compile WITH_OPENSSL
Enable SSL/TLS in libhv is so easy :)
```
// see ssl/hssl.h
hssl_ctx_t hssl_ctx_new(hssl_ctx_opt_t* opt);
// see event/hloop.h
int hio_new_ssl_ctx(hio_t* io, hssl_ctx_opt_t* opt);
```
https is the best example.
```
sudo apt install openssl libssl-dev # ubuntu
./configure --with-openssl
make clean && make
bin/httpd -s restart -d
bin/curl -v http://localhost:8080
bin/curl -v https://localhost:8443
```
### compile WITH_CURL
```
./configure --with-curl
make clean && make
bin/httpd -s restart -d
bin/curl -v http://localhost:8080
```
### compile WITH_NGHTTP2
```
sudo apt install libnghttp2-dev # ubuntu
./configure --with-nghttp2
make clean && make
bin/httpd -s restart -d
bin/curl -v http://localhost:8080 --http2
```
### compile WITH_KCP
```
./configure --with-kcp
make clean && make
```
### compile WITH_MQTT
```
./configure --with-mqtt
make clean && make
```
### More
```
./configure --help
```

View File

@ -0,0 +1,281 @@
cmake_minimum_required(VERSION 3.6)
project(hv VERSION 1.3.2)
option(BUILD_SHARED "build shared library" ON)
option(BUILD_STATIC "build static library" ON)
option(BUILD_EXAMPLES "build examples" ON)
option(BUILD_UNITTEST "build unittest" OFF)
# see config.ini
option(WITH_PROTOCOL "compile protocol" OFF)
option(WITH_EVPP "compile evpp" ON)
option(WITH_HTTP "compile http" ON)
option(WITH_HTTP_SERVER "compile http/server" ON)
option(WITH_HTTP_CLIENT "compile http/client" ON)
option(WITH_MQTT "compile mqtt" OFF)
option(ENABLE_UDS "Unix Domain Socket" OFF)
option(USE_MULTIMAP "MultiMap" OFF)
option(WITH_CURL "with curl library (deprecated)" OFF)
option(WITH_NGHTTP2 "with nghttp2 library" OFF)
option(WITH_OPENSSL "with openssl library" OFF)
option(WITH_GNUTLS "with gnutls library" OFF)
option(WITH_MBEDTLS "with mbedtls library" OFF)
option(WITH_KCP "compile event/kcp" OFF)
if(WIN32)
option(WITH_WEPOLL "compile event/wepoll -> use iocp" ON)
option(ENABLE_WINDUMP "Windows MiniDumpWriteDump" OFF)
option(BUILD_FOR_MT "build for /MT" OFF)
if(BUILD_FOR_MT)
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /MTd")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MT")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT")
endif()
endif()
message(STATUS "CMAKE_SOURCE_DIR=${CMAKE_SOURCE_DIR}")
message(STATUS "CMAKE_CURRENT_SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}")
if(NOT "${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_SOURCE_DIR}")
set(BUILD_EXAMPLES OFF)
endif()
if(IOS)
set(BUILD_SHARED OFF)
set(BUILD_EXAMPLES OFF)
endif()
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}")
include(utils)
include(vars)
# see configure
# Checks for header files
check_header("stdbool.h")
check_header("stdint.h")
check_header("stdatomic.h")
check_header("sys/types.h")
check_header("sys/stat.h")
check_header("sys/time.h")
check_header("fcntl.h")
check_header("pthread.h")
check_header("endian.h")
check_header("sys/endian.h")
# Checks for functions
if(NOT MSVC)
set(CMAKE_REQUIRED_LIBRARIES "-pthread")
endif()
check_function("gettid" "unistd.h")
check_function("strlcpy" "string.h")
check_function("strlcat" "string.h")
check_function("clock_gettime" "time.h")
check_function("gettimeofday" "sys/time.h")
check_function("pthread_spin_lock" "pthread.h")
check_function("pthread_mutex_timedlock" "pthread.h")
check_function("sem_timedwait" "semaphore.h")
check_function("pipe" "unistd.h")
check_function("socketpair" "sys/socket.h")
check_function("eventfd" "sys/eventfd.h")
check_function("setproctitle" "unistd.h")
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/hconfig.h.in ${CMAKE_CURRENT_SOURCE_DIR}/hconfig.h)
# see Makefile.in
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED True)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
set(INCDIR include)
set(SRCDIR src)
set(LIBDIR lib)
set(BINDIR bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${LIBDIR})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${LIBDIR})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${BINDIR})
message(STATUS "CMAKE_LIBRARY_OUTPUT_DIRECTORY=${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
set(INCDIRS . include 3rd/include)
set(LIBDIRS . lib 3rd/lib)
include_directories(${INCDIRS} ${SRCDIR})
link_directories(${LIBDIRS})
message(STATUS "CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
add_definitions(-DDEBUG)
else()
add_definitions(-DNDEBUG)
endif()
if(ENABLE_UDS)
add_definitions(-DENABLE_UDS)
endif()
if(USE_MULTIMAP)
add_definitions(-DUSE_MULTIMAP)
endif()
if(WITH_CURL)
add_definitions(-DWITH_CURL)
set(LIBS ${LIBS} curl)
if(WIN32)
set(LIBS ${LIBS} wldap32 advapi32 crypt32)
endif()
endif()
if(WITH_NGHTTP2)
add_definitions(-DWITH_NGHTTP2)
set(LIBS ${LIBS} nghttp2)
endif()
if(WITH_OPENSSL)
add_definitions(-DWITH_OPENSSL)
find_package(OpenSSL)
if(OpenSSL_FOUND)
set(LIBS ${LIBS} OpenSSL::SSL OpenSSL::Crypto)
else()
set(LIBS ${LIBS} ssl crypto)
endif()
endif()
if(WITH_GNUTLS)
add_definitions(-DWITH_GNUTLS)
set(LIBS ${LIBS} gnutls)
endif()
if(WITH_MBEDTLS)
add_definitions(-DWITH_MBEDTLS)
set(LIBS ${LIBS} mbedtls mbedx509 mbedcrypto)
endif()
if(WIN32)
add_definitions(-DWIN32_LEAN_AND_MEAN -D_CRT_SECURE_NO_WARNINGS -D_WIN32_WINNT=0x0600)
set(LIBS ${LIBS} secur32 crypt32 winmm iphlpapi ws2_32)
if(ENABLE_WINDUMP)
add_definitions(-DENABLE_WINDUMP)
set(LIBS ${LIBS} dbghelp)
endif()
endif()
if(ANDROID)
set(LIBS ${LIBS} log)
elseif(UNIX)
set(LIBS ${LIBS} pthread m dl)
if(CMAKE_COMPILER_IS_GNUCC)
set(LIBS ${LIBS} rt)
endif()
endif()
if(APPLE)
set(LIBS ${LIBS} "-framework CoreFoundation" "-framework Security")
endif()
# see Makefile
set(ALL_SRCDIRS . base ssl event event/kcp util cpputil evpp protocol http http/client http/server mqtt)
set(CORE_SRCDIRS . base ssl event)
if(WIN32)
if(WITH_WEPOLL)
set(CORE_SRCDIRS ${CORE_SRCDIRS} event/wepoll)
endif()
endif()
if(WITH_KCP)
set(CORE_SRCDIRS ${CORE_SRCDIRS} event/kcp)
endif()
set(LIBHV_SRCDIRS ${CORE_SRCDIRS} util)
set(LIBHV_HEADERS hv.h hconfig.h hexport.h)
set(LIBHV_HEADERS ${LIBHV_HEADERS} ${BASE_HEADERS} ${SSL_HEADERS} ${EVENT_HEADERS} ${UTIL_HEADERS})
if(WITH_PROTOCOL)
set(LIBHV_HEADERS ${LIBHV_HEADERS} ${PROTOCOL_HEADERS})
set(LIBHV_SRCDIRS ${LIBHV_SRCDIRS} protocol)
endif()
if(WITH_EVPP)
set(LIBHV_HEADERS ${LIBHV_HEADERS} ${CPPUTIL_HEADERS} ${EVPP_HEADERS})
set(LIBHV_SRCDIRS ${LIBHV_SRCDIRS} cpputil evpp)
if(WITH_HTTP)
set(LIBHV_HEADERS ${LIBHV_HEADERS} ${HTTP_HEADERS})
set(LIBHV_SRCDIRS ${LIBHV_SRCDIRS} http)
if(WITH_NGHTTP2)
set(LIBHV_HEADERS ${LIBHV_HEADERS} ${HTTP2_HEADERS})
endif()
if(WITH_HTTP_SERVER)
set(LIBHV_HEADERS ${LIBHV_HEADERS} ${HTTP_SERVER_HEADERS})
set(LIBHV_SRCDIRS ${LIBHV_SRCDIRS} http/server)
endif()
if(WITH_HTTP_CLIENT)
set(LIBHV_HEADERS ${LIBHV_HEADERS} ${HTTP_CLIENT_HEADERS})
set(LIBHV_SRCDIRS ${LIBHV_SRCDIRS} http/client)
endif()
endif()
if(CMAKE_SYSTEM_NAME MATCHES "Linux" AND CMAKE_COMPILER_IS_GNUCC)
set(LIBS ${LIBS} stdc++)
endif()
endif()
if(WITH_MQTT)
set(LIBHV_HEADERS ${LIBHV_HEADERS} ${MQTT_HEADERS})
set(LIBHV_SRCDIRS ${LIBHV_SRCDIRS} mqtt)
endif()
list_source_directories(LIBHV_SRCS ${LIBHV_SRCDIRS})
file(INSTALL ${LIBHV_HEADERS} DESTINATION include/hv)
file(INSTALL ${LIBHV_HEADERS} DESTINATION ${PROJECT_SOURCE_DIR}/include/hv)
if(BUILD_SHARED)
add_library(hv SHARED ${LIBHV_SRCS})
target_compile_definitions(hv PRIVATE HV_DYNAMICLIB)
target_include_directories(hv PRIVATE ${LIBHV_SRCDIRS}
INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<INSTALL_INTERFACE:include>)
target_link_libraries(hv ${LIBS})
install(TARGETS hv
EXPORT libhvConfig
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin)
add_custom_target(libhv DEPENDS hv)
endif()
if(BUILD_STATIC)
add_library(hv_static STATIC ${LIBHV_SRCS})
target_compile_definitions(hv_static PUBLIC HV_STATICLIB)
target_include_directories(hv_static PRIVATE ${LIBHV_SRCDIRS}
INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<INSTALL_INTERFACE:include>)
target_link_libraries(hv_static ${LIBS})
install(TARGETS hv_static
EXPORT libhvConfig
ARCHIVE DESTINATION lib)
add_custom_target(libhv_static DEPENDS hv_static)
endif()
install(FILES ${LIBHV_HEADERS} DESTINATION include/hv)
install(EXPORT libhvConfig DESTINATION lib/cmake/libhv)
if(BUILD_SHARED)
set(HV_LIBRARIES hv CACHE INTERNAL "link hv libraries")
else()
add_definitions(-DHV_STATICLIB)
set(HV_LIBRARIES hv_static ${LIBS} CACHE INTERNAL "link hv libraries")
endif()
if(BUILD_EXAMPLES)
add_subdirectory(examples)
# for httpd -c etc/httpd.conf
file(INSTALL etc DESTINATION ${CMAKE_BINARY_DIR})
file(INSTALL etc DESTINATION ${CMAKE_BINARY_DIR}/bin)
file(INSTALL etc DESTINATION ${CMAKE_BINARY_DIR}/examples)
endif()
if(BUILD_UNITTEST)
add_subdirectory(unittest)
endif()

30
external/libhv/libhv-1.3.2/LICENSE vendored Normal file
View File

@ -0,0 +1,30 @@
BSD 3-Clause License
Copyright (c) 2020, ithewei
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the 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.

283
external/libhv/libhv-1.3.2/Makefile vendored Normal file
View File

@ -0,0 +1,283 @@
include config.mk
include Makefile.vars
MAKEF=$(MAKE) -f Makefile.in
ALL_SRCDIRS=. base ssl event event/kcp util cpputil evpp protocol http http/client http/server mqtt
CORE_SRCDIRS=. base ssl event
ifeq ($(WITH_KCP), yes)
CORE_SRCDIRS += event/kcp
endif
LIBHV_SRCDIRS = $(CORE_SRCDIRS) util
LIBHV_HEADERS = hv.h hconfig.h hexport.h
LIBHV_HEADERS += $(BASE_HEADERS) $(SSL_HEADERS) $(EVENT_HEADERS) $(UTIL_HEADERS)
ifeq ($(WITH_PROTOCOL), yes)
LIBHV_HEADERS += $(PROTOCOL_HEADERS)
LIBHV_SRCDIRS += protocol
endif
ifeq ($(WITH_EVPP), yes)
LIBHV_HEADERS += $(CPPUTIL_HEADERS) $(EVPP_HEADERS)
LIBHV_SRCDIRS += cpputil evpp
ifeq ($(WITH_HTTP), yes)
LIBHV_HEADERS += $(HTTP_HEADERS)
LIBHV_SRCDIRS += http
ifeq ($(WITH_NGHTTP2), yes)
LIBHV_HEADERS += $(HTTP2_HEADERS)
endif
ifeq ($(WITH_HTTP_SERVER), yes)
LIBHV_HEADERS += $(HTTP_SERVER_HEADERS)
LIBHV_SRCDIRS += http/server
endif
ifeq ($(WITH_HTTP_CLIENT), yes)
LIBHV_HEADERS += $(HTTP_CLIENT_HEADERS)
LIBHV_SRCDIRS += http/client
endif
endif
endif
ifeq ($(WITH_MQTT), yes)
LIBHV_HEADERS += $(MQTT_HEADERS)
LIBHV_SRCDIRS += mqtt
endif
default: all
all: libhv examples
@echo "make all done, please enjoy libhv."
examples: hmain_test htimer_test hloop_test \
nc nmap tinyhttpd tinyproxyd httpd curl wget wrk consul \
tcp_client_test \
tcp_echo_server \
tcp_chat_server \
tcp_proxy_server \
udp_echo_server \
udp_proxy_server \
socks5_proxy_server \
multi-acceptor-processes \
multi-acceptor-threads \
one-acceptor-multi-workers \
http_server_test http_client_test \
websocket_server_test \
websocket_client_test \
mqtt_sub \
mqtt_pub \
mqtt_client_test \
jsonrpc
@echo "make examples done."
clean:
$(MAKEF) clean SRCDIRS="$(ALL_SRCDIRS)"
$(RM) examples/*.o examples/*/*.o
$(RM) include/hv
@echo "make clean done."
prepare:
$(MKDIR) bin
libhv:
$(MKDIR) lib
$(MAKEF) TARGET=$@ TARGET_TYPE="SHARED|STATIC" SRCDIRS="$(LIBHV_SRCDIRS)"
$(MKDIR) include/hv
$(CP) $(LIBHV_HEADERS) include/hv
@echo "make libhv done."
install:
$(MKDIR) $(INSTALL_INCDIR)
$(MKDIR) $(INSTALL_LIBDIR)
$(CP) include/hv/* $(INSTALL_INCDIR)
$(CP) lib/libhv.* $(INSTALL_LIBDIR)
$(LDCONFIG)
@echo "make install done."
uninstall: clean
$(RM) $(PREFIX)/include/hv
$(RM) $(PREFIX)/lib/libhv.*
@echo "make uninstall done."
hmain_test: prepare
$(MAKEF) TARGET=$@ SRCDIRS=". base cpputil" SRCS="examples/hmain_test.cpp"
htimer_test: prepare
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS)" SRCS="examples/htimer_test.c"
hloop_test: prepare
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS)" SRCS="examples/hloop_test.c"
tcp_client_test: prepare
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS)" SRCS="examples/tcp_client_test.c"
tcp_echo_server: prepare
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS)" SRCS="examples/tcp_echo_server.c"
tcp_chat_server: prepare
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS)" SRCS="examples/tcp_chat_server.c"
tcp_proxy_server: prepare
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS)" SRCS="examples/tcp_proxy_server.c"
udp_echo_server: prepare
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS)" SRCS="examples/udp_echo_server.c"
udp_proxy_server: prepare
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS)" SRCS="examples/udp_proxy_server.c"
socks5_proxy_server: prepare
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS)" SRCS="examples/socks5_proxy_server.c"
multi-acceptor-processes: prepare
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS)" SRCS="examples/multi-thread/multi-acceptor-processes.c"
multi-acceptor-threads: prepare
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS)" SRCS="examples/multi-thread/multi-acceptor-threads.c"
one-acceptor-multi-workers: prepare
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS)" SRCS="examples/multi-thread/one-acceptor-multi-workers.c"
nc: prepare
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS)" SRCS="examples/nc.c"
tinyhttpd: prepare
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS)" SRCS="examples/tinyhttpd.c"
tinyproxyd: prepare
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS)" SRCS="examples/tinyproxyd.c"
nmap: prepare libhv
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS) cpputil examples/nmap" DEFINES="PRINT_DEBUG"
wrk: prepare
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS) util cpputil evpp http" SRCS="examples/wrk.cpp"
httpd: prepare
$(RM) examples/httpd/*.o
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS) util cpputil evpp http http/client http/server examples/httpd"
consul: prepare libhv
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS) util cpputil evpp http http/client examples/consul" DEFINES="PRINT_DEBUG"
curl: prepare
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS) util cpputil evpp http http/client" SRCS="examples/curl.cpp"
# $(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS) util cpputil evpp http http/client" SRCS="examples/curl.cpp" WITH_CURL=yes
wget: prepare
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS) util cpputil evpp http http/client" SRCS="examples/wget.cpp"
http_server_test: prepare
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS) util cpputil evpp http http/server" SRCS="examples/http_server_test.cpp"
http_client_test: prepare
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS) util cpputil evpp http http/client" SRCS="examples/http_client_test.cpp"
websocket_server_test: prepare
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS) util cpputil evpp http http/server" SRCS="examples/websocket_server_test.cpp"
websocket_client_test: prepare
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS) util cpputil evpp http http/client" SRCS="examples/websocket_client_test.cpp"
mqtt_sub: prepare
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS) mqtt" SRCS="examples/mqtt/mqtt_sub.c"
mqtt_pub: prepare
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS) mqtt" SRCS="examples/mqtt/mqtt_pub.c"
mqtt_client_test: prepare
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS) mqtt" SRCS="examples/mqtt/mqtt_client_test.cpp"
jsonrpc: jsonrpc_client jsonrpc_server
jsonrpc_client: prepare
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS)" SRCS="examples/jsonrpc/jsonrpc_client.c examples/jsonrpc/cJSON.c"
jsonrpc_server: prepare
$(RM) examples/jsonrpc/*.o
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS)" SRCS="examples/jsonrpc/jsonrpc_server.c examples/jsonrpc/cJSON.c"
protorpc: protorpc_client protorpc_server
protorpc_protoc:
bash examples/protorpc/proto/protoc.sh
protorpc_client: prepare protorpc_protoc
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS) cpputil evpp examples/protorpc/generated" \
SRCS="examples/protorpc/protorpc_client.cpp examples/protorpc/protorpc.c" \
LIBS="protobuf"
protorpc_server: prepare protorpc_protoc
$(RM) examples/protorpc/*.o
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS) cpputil evpp examples/protorpc/generated" \
SRCS="examples/protorpc/protorpc_server.cpp examples/protorpc/protorpc.c" \
LIBS="protobuf"
unittest: prepare
$(CC) -g -Wall -O0 -std=c99 -I. -Ibase -o bin/rbtree_test unittest/rbtree_test.c base/rbtree.c
$(CC) -g -Wall -O0 -std=c99 -I. -Ibase -o bin/hbase_test unittest/hbase_test.c base/hbase.c
$(CC) -g -Wall -O0 -std=c99 -I. -Ibase -o bin/mkdir_p unittest/mkdir_test.c base/hbase.c
$(CC) -g -Wall -O0 -std=c99 -I. -Ibase -o bin/rmdir_p unittest/rmdir_test.c base/hbase.c
$(CC) -g -Wall -O0 -std=c99 -I. -Ibase -o bin/date unittest/date_test.c base/htime.c
$(CC) -g -Wall -O0 -std=c99 -I. -Ibase -o bin/hatomic_test unittest/hatomic_test.c -pthread
$(CXX) -g -Wall -O0 -std=c++11 -I. -Ibase -o bin/hatomic_cpp_test unittest/hatomic_test.cpp -pthread
$(CXX) -g -Wall -O0 -std=c++11 -I. -Ibase -o bin/hthread_test unittest/hthread_test.cpp -pthread
$(CC) -g -Wall -O0 -std=c99 -I. -Ibase -o bin/hmutex_test unittest/hmutex_test.c base/htime.c -pthread
$(CC) -g -Wall -O0 -std=c99 -I. -Ibase -o bin/connect_test unittest/connect_test.c base/hsocket.c base/htime.c
$(CC) -g -Wall -O0 -std=c99 -I. -Ibase -o bin/socketpair_test unittest/socketpair_test.c base/hsocket.c
$(CC) -g -Wall -O0 -std=c99 -I. -Iutil -o bin/base64 unittest/base64_test.c util/base64.c
$(CC) -g -Wall -O0 -std=c99 -I. -Iutil -o bin/md5 unittest/md5_test.c util/md5.c
$(CC) -g -Wall -O0 -std=c99 -I. -Iutil -o bin/sha1 unittest/sha1_test.c util/sha1.c
$(CXX) -g -Wall -O0 -std=c++11 -I. -Ibase -Icpputil -o bin/hstring_test unittest/hstring_test.cpp cpputil/hstring.cpp
$(CXX) -g -Wall -O0 -std=c++11 -I. -Ibase -Icpputil -o bin/hpath_test unittest/hpath_test.cpp cpputil/hpath.cpp
$(CXX) -g -Wall -O0 -std=c++11 -I. -Ibase -Icpputil -o bin/hurl_test unittest/hurl_test.cpp cpputil/hurl.cpp base/hbase.c
$(CXX) -g -Wall -O0 -std=c++11 -I. -Ibase -Icpputil -o bin/ls unittest/listdir_test.cpp cpputil/hdir.cpp
$(CXX) -g -Wall -O0 -std=c++11 -I. -Ibase -Icpputil -o bin/ifconfig unittest/ifconfig_test.cpp cpputil/ifconfig.cpp
$(CXX) -g -Wall -O0 -std=c++11 -I. -Ibase -Icpputil -o bin/defer_test unittest/defer_test.cpp
$(CXX) -g -Wall -O0 -std=c++11 -I. -Ibase -Icpputil -o bin/synchronized_test unittest/synchronized_test.cpp -pthread
$(CXX) -g -Wall -O0 -std=c++11 -I. -Ibase -Icpputil -o bin/threadpool_test unittest/threadpool_test.cpp -pthread
$(CXX) -g -Wall -O0 -std=c++11 -I. -Ibase -Icpputil -o bin/objectpool_test unittest/objectpool_test.cpp -pthread
$(CXX) -g -Wall -O0 -std=c++11 -I. -Ibase -Issl -Ievent -Ievpp -Icpputil -Ihttp -Ihttp/client -Ihttp/server -o bin/sizeof_test unittest/sizeof_test.cpp
$(CC) -g -Wall -O0 -std=c99 -I. -Ibase -Iprotocol -o bin/nslookup unittest/nslookup_test.c protocol/dns.c base/hsocket.c
$(CC) -g -Wall -O0 -std=c99 -I. -Ibase -Iprotocol -o bin/ping unittest/ping_test.c protocol/icmp.c base/hsocket.c base/htime.c -DPRINT_DEBUG
$(CC) -g -Wall -O0 -std=c99 -I. -Ibase -Iprotocol -o bin/ftp unittest/ftp_test.c protocol/ftp.c base/hsocket.c
$(CC) -g -Wall -O0 -std=c99 -I. -Ibase -Iprotocol -Iutil -o bin/sendmail unittest/sendmail_test.c protocol/smtp.c base/hsocket.c util/base64.c
run-unittest: unittest
bash scripts/unittest.sh
check: examples
bash scripts/check.sh
evpp: prepare libhv
$(CXX) -g -Wall -O0 -std=c++11 -I. -Ibase -Issl -Ievent -Icpputil -Ievpp -o bin/EventLoop_test evpp/EventLoop_test.cpp -Llib -lhv -pthread
$(CXX) -g -Wall -O0 -std=c++11 -I. -Ibase -Issl -Ievent -Icpputil -Ievpp -o bin/EventLoopThread_test evpp/EventLoopThread_test.cpp -Llib -lhv -pthread
$(CXX) -g -Wall -O0 -std=c++11 -I. -Ibase -Issl -Ievent -Icpputil -Ievpp -o bin/EventLoopThreadPool_test evpp/EventLoopThreadPool_test.cpp -Llib -lhv -pthread
$(CXX) -g -Wall -O0 -std=c++11 -I. -Ibase -Issl -Ievent -Icpputil -Ievpp -o bin/TimerThread_test evpp/TimerThread_test.cpp -Llib -lhv -pthread
$(CXX) -g -Wall -O0 -std=c++11 -I. -Ibase -Issl -Ievent -Icpputil -Ievpp -o bin/TcpServer_test evpp/TcpServer_test.cpp -Llib -lhv -pthread
$(CXX) -g -Wall -O0 -std=c++11 -I. -Ibase -Issl -Ievent -Icpputil -Ievpp -o bin/TcpClient_test evpp/TcpClient_test.cpp -Llib -lhv -pthread
$(CXX) -g -Wall -O0 -std=c++11 -I. -Ibase -Issl -Ievent -Icpputil -Ievpp -o bin/TcpClientEventLoop_test evpp/TcpClientEventLoop_test.cpp -Llib -lhv -pthread
$(CXX) -g -Wall -O0 -std=c++11 -I. -Ibase -Issl -Ievent -Icpputil -Ievpp -o bin/UdpServer_test evpp/UdpServer_test.cpp -Llib -lhv -pthread
$(CXX) -g -Wall -O0 -std=c++11 -I. -Ibase -Issl -Ievent -Icpputil -Ievpp -o bin/UdpClient_test evpp/UdpClient_test.cpp -Llib -lhv -pthread
# UNIX only
webbench: prepare
$(CC) -o bin/webbench unittest/webbench.c
echo-servers:
$(CXX) -g -Wall -std=c++11 -O3 -o bin/pingpong_client echo-servers/pingpong_client.cpp -lhv -pthread
$(CC) -g -Wall -std=c99 -O3 -o bin/libevent_echo echo-servers/libevent_echo.c -levent
$(CC) -g -Wall -std=c99 -O3 -o bin/libev_echo echo-servers/libev_echo.c -lev
$(CC) -g -Wall -std=c99 -O3 -o bin/libuv_echo echo-servers/libuv_echo.c -luv
$(CC) -g -Wall -std=c99 -O3 -o bin/libhv_echo echo-servers/libhv_echo.c -lhv
$(CXX) -g -Wall -std=c++11 -O3 -o bin/asio_echo echo-servers/asio_echo.cpp -lboost_system -pthread
$(CXX) -g -Wall -std=c++11 -O3 -o bin/poco_echo echo-servers/poco_echo.cpp -lPocoNet -lPocoUtil -lPocoFoundation
# $(CXX) -g -Wall -std=c++11 -O3 -o bin/muduo_echo echo-servers/muduo_echo.cpp -lmuduo_net -lmuduo_base -pthread
echo-benchmark: echo-servers
bash echo-servers/benchmark.sh
.PHONY: clean prepare install uninstall libhv examples unittest evpp echo-servers

306
external/libhv/libhv-1.3.2/Makefile.in vendored Normal file
View File

@ -0,0 +1,306 @@
#+++++++++++++++++++++++++++++++++configure++++++++++++++++++++++++++++++++++++++++
# OS=Windows,Linux,Android
# ARCH=x86,x86_64,arm,aarch64
# CC = $(CROSS_COMPILE)gcc
# CXX = $(CROSS_COMPILE)g++
# CPPFLAGS += $(addprefix -D, $(DEFINES))
# CPPFLAGS += $(addprefix -I, $(SRCDIRS))
# CPPFLAGS += $(addprefix -I, $(INCDIRS))
# LDFLAGS += $(addprefix -L, $(LIBDIRS))
# LDFLAGS += $(addprefix -l, $(LIBS))
#
# Usage:
# make all \
# TARGET=libxx \
# TARGET_TYPE=SHARED \
# BUILD_TYPE=DEBUG \
# CROSS_COMPILE=arm-linux-androideabi- \
# SRCDIRS="src/base src/event" \
# INCDIRS="src/util" \
# SRCS="src/util/hmain.cpp src/util/iniparser.cpp" \
# DEFINES=USE_OPENCV \
# LIBS="opencv_core opencv_highgui"
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-include config.mk
# VARIABLES
TARGET ?= test
# BUILD_TYPE=DEBUG,RELEASE
BUILD_TYPE ?= RELEASE
# TARGET_TYPE=EXECUTABLE,SHARED,STATIC,SHARED|STATIC
TARGET_TYPE ?= EXECUTABLE
# COMMANDS
ifdef CROSS_COMPILE
CC = $(CROSS_COMPILE)gcc
CXX = $(CROSS_COMPILE)g++
CPP = $(CC) -E
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
AR = $(CROSS_COMPILE)ar
NM = $(CROSS_COMPILE)nm
STRIP = $(CROSS_COMPILE)strip
endif
MKDIR = -mkdir -p 2>/dev/null
CP = -cp -r 2>/dev/null
RM = -rm -r 2>/dev/null
# PLATFORM: OS, ARCH
CC_VERSION=$(shell $(CC) --version 2>&1 | head -n 1)
TARGET_PLATFORM=$(shell $(CC) -v 2>&1 | grep Target | sed 's/Target: //')
ifneq ($(findstring mingw, $(TARGET_PLATFORM)), )
OS=Windows
endif
ifneq ($(findstring android, $(TARGET_PLATFORM)), )
OS=Android
endif
ifneq ($(findstring darwin, $(TARGET_PLATFORM)), )
OS=Darwin
endif
ifndef OS
OS=Linux
endif
ifndef ARCH
ARCH=$(shell echo $(TARGET_PLATFORM) | awk -F'-' '{print $$1}')
endif
# CFLAGS, CXXFLAGS, ARFLAGS
ifeq ($(BUILD_TYPE), DEBUG)
DEFAULT_CFLAGS = -g -Wall -O0
else
DEFAULT_CFLAGS += -O2
endif
CFLAGS ?= $(DEFAULT_CFLAGS)
CXXFLAGS ?= $(DEFAULT_CFLAGS)
ifneq ($(OS), Windows)
ifeq ($(findstring -fPIC, $(CFLAGS)), )
override CFLAGS += -fPIC
endif
ifeq ($(findstring -fPIC, $(CXXFLAGS)), )
override CXXFLAGS += -fPIC
endif
endif
ifeq ($(findstring -std, $(CFLAGS)), )
override CFLAGS += -std=c99
endif
ifeq ($(findstring -std, $(CXXFLAGS)), )
override CXXFLAGS += -std=c++11
endif
ARFLAGS ?= cr
# DIRS
ifeq ($(OS), Linux)
PREFIX ?= /usr/local
else
PREFIX ?= install
endif
INCDIR = include
LIBDIR = lib
SRCDIR = src
BINDIR = bin
DEPDIR = 3rd
CONFDIR = etc
DISTDIR = dist
DOCDIR = docs
SRCDIRS += $(shell find $(SRCDIR) -type d)
override INCDIRS += $(INCDIR) $(DEPDIR) $(DEPDIR)/include
override LIBDIRS += $(LIBDIR) $(DEPDIR)/lib $(DEPDIR)/lib/$(TARGET_PLATFORM)
ALL_SRCS += $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c $(dir)/*.cc $(dir)/*.cpp))
ifeq ($(ALL_SRCS), )
ALL_SRCS = $(wildcard *.c *.cc *.cpp)
endif
override SRCS += $(filter-out %_test.c %_test.cc %_test.cpp, $(ALL_SRCS))
# OBJS += $(patsubst %.c, %.o, $(SRCS))
# OBJS += $(patsubst %.cc, %.o, $(SRCS))
# OBJS += $(patsubst %.cpp, %.o, $(SRCS))
OBJS := $(addsuffix .o, $(basename $(SRCS)))
INSTALLED_INCS=$(addprefix $(PREFIX)/$(INCDIR)/, $(shell ls $(INCDIR)))
INSTALLED_LIBS=$(addprefix $(PREFIX)/$(LIBDIR)/, $(shell ls $(LIBDIR)))
INSTALLED_BINS=$(addprefix $(PREFIX)/$(BINDIR)/, $(shell ls $(BINDIR)))
# CPPFLAGS
ifeq ($(OS), Windows)
CPPFLAGS += -D_WIN32_WINNT=0x600
ifeq ($(TARGET_TYPE), SHARED)
CPPFLAGS += -DDLL_EXPORTS
endif
endif
ifeq ($(BUILD_TYPE), DEBUG)
CPPFLAGS += -DDEBUG
else
CPPFLAGS += -DNDEBUG
endif
ifeq ($(ENABLE_UDS), yes)
CPPFLAGS += -DENABLE_UDS
endif
ifeq ($(USE_MULTIMAP), yes)
CPPFLAGS += -DUSE_MULTIMAP
endif
CPPFLAGS += $(addprefix -D, $(DEFINES))
CPPFLAGS += $(addprefix -I, $(INCDIRS))
CPPFLAGS += $(addprefix -I, $(SRCDIRS))
# LDFLAGS
ifeq ($(OS), Windows)
LDFLAGS += -static-libgcc -static-libstdc++
endif
ifeq ($(WITH_CURL), yes)
CPPFLAGS += -DWITH_CURL
LDFLAGS += -lcurl
ifeq ($(OS), Windows)
LDFLAGS += -lwldap32 -ladvapi32 -lcrypt32
endif
endif
ifeq ($(WITH_NGHTTP2), yes)
CPPFLAGS += -DWITH_NGHTTP2
LDFLAGS += -lnghttp2
endif
ifeq ($(WITH_OPENSSL), yes)
CPPFLAGS += -DWITH_OPENSSL
LDFLAGS += -lssl -lcrypto
else
ifeq ($(WITH_GNUTLS), yes)
CPPFLAGS += -DWITH_GNUTLS
LDFLAGS += -lgnutls
else
ifeq ($(WITH_MBEDTLS), yes)
CPPFLAGS += -DWITH_MBEDTLS
LDFLAGS += -lmbedtls -lmbedx509 -lmbedcrypto
endif
endif
endif
LDFLAGS += $(addprefix -L, $(LIBDIRS))
LDFLAGS += $(addprefix -l, $(LIBS))
ifeq ($(OS), Windows)
LDFLAGS += -lsecur32 -lcrypt32 -lwinmm -liphlpapi -lws2_32
ifeq ($(ENABLE_WINDUMP), yes)
CPPFLAGS += -DENABLE_WINDUMP
LDFLAGS += -ldbghelp
endif
LDFLAGS += -Wl,-Bstatic -lstdc++ -lpthread -lm
else
ifeq ($(filter %.cc %.cpp, $(SRCS)), )
LINK = $(CC)
else
LINK = $(CXX)
LDFLAGS += -lstdc++
endif
ifeq ($(OS), Android)
LDFLAGS += -lm -llog -ldl
else
LDFLAGS += -lpthread -lm -ldl
LINK_RT=$(shell echo "int main(){return 0;}" | $(CC) -x c - -lrt 2>&1)
ifeq ($(LINK_RT), )
LDFLAGS += -lrt
endif
endif
endif
ifeq ($(OS), Darwin)
LDFLAGS += -framework CoreFoundation -framework Security
endif
LINK ?= $(CC)
# info
$(info $(CC_VERSION))
$(info OS = $(OS))
$(info ARCH = $(ARCH))
$(info MAKE = $(MAKE))
$(info CC = $(CC))
$(info CXX = $(CXX))
$(info CFLAGS = $(CFLAGS))
$(info CXXFLAGS = $(CXXFLAGS))
$(info CPPFLAGS = $(CPPFLAGS))
$(info LDFLAGS = $(LDFLAGS))
$(info TARGET = $(TARGET))
$(info TARGET_TYPE = $(TARGET_TYPE))
$(info TARGET_PLATFORM = $(TARGET_PLATFORM))
$(info BUILD_TYPE = $(BUILD_TYPE))
$(info SRCS=$(SRCS))
$(info OBJS=$(OBJS))
# $(info INSTALLED_INCS=$(INSTALLED_INCS))
# $(info INSTALLED_LIBS=$(INSTALLED_LIBS))
# $(info INSTALLED_BINS=$(INSTALLED_BINS))
default: all
all: prepare $(TARGET)
prepare:
$(MKDIR) $(BINDIR) $(LIBDIR)
$(TARGET): $(OBJS)
ifneq ($(findstring SHARED, $(TARGET_TYPE)), )
ifeq ($(OS), Windows)
$(LINK) -shared $^ -o $(LIBDIR)/$@.dll $(LDFLAGS) -Wl,--output-def,$(LIBDIR)/$(@).def
else
ifeq ($(OS), Darwin)
$(LINK) -dynamiclib -install_name @rpath/$@.dylib $^ -o $(LIBDIR)/$@.dylib $(LDFLAGS)
else
$(LINK) -shared $^ -o $(LIBDIR)/$@.so $(LDFLAGS)
endif
endif
endif
ifneq ($(findstring STATIC, $(TARGET_TYPE)), )
$(AR) $(ARFLAGS) $(LIBDIR)/$@.a $^
endif
ifneq ($(findstring EXECUTABLE, $(TARGET_TYPE)), )
ifeq ($(OS), Windows)
$(LINK) $^ -o $(BINDIR)/$@.exe $(LDFLAGS)
else
$(LINK) $^ -o $(BINDIR)/$@ $(LDFLAGS)
endif
endif
clean:
$(RM) $(OBJS)
#$(RM) $(LIBDIR)
#$(RM) $(BINDIR)
install:
$(CP) $(INCDIR)/* $(PREFIX)/$(INCDIR)/
$(CP) $(LIBDIR)/* $(PREFIX)/$(LIBDIR)/
$(CP) $(BINDIR)/* $(PREFIX)/$(BINDIR)/
$(LDCONFIG)
uninstall:
$(RM) $(INSTALLED_INCS)
$(RM) $(INSTALLED_LIBS)
$(RM) $(INSTALLED_BINS)
dist:
$(MKDIR) $(DISTDIR)
$(CP) $(INCDIR) $(LIBDIR) $(BINDIR) $(CONFDIR) $(DOCDIR) $(DISTDIR)
undist:
$(RM) $(DISTDIR)
.PHONY: default all prepare clean install uninstall dist undist

View File

@ -0,0 +1,95 @@
MKDIR = -mkdir -p 2>/dev/null
CP = -cp -r 2>/dev/null
RM = -rm -r 2>/dev/null
LDCONFIG = -ldconfig 2>/dev/null
PREFIX ?= /usr/local
INSTALL_INCDIR ?= $(PREFIX)/include/hv
INSTALL_LIBDIR ?= $(PREFIX)/lib
BASE_HEADERS = base/hplatform.h\
\
base/hdef.h\
base/hatomic.h\
base/herr.h\
base/htime.h\
base/hmath.h\
base/hbase.h\
base/hversion.h\
base/hsysinfo.h\
base/hproc.h\
base/hthread.h\
base/hmutex.h\
base/hsocket.h\
base/hlog.h\
base/hbuf.h\
base/hmain.h\
base/hendian.h\
SSL_HEADERS = ssl/hssl.h
EVENT_HEADERS = event/hloop.h\
event/nlog.h\
UTIL_HEADERS = util/base64.h\
util/md5.h\
util/sha1.h\
CPPUTIL_HEADERS = cpputil/hmap.h\
cpputil/hstring.h\
cpputil/hfile.h\
cpputil/hpath.h\
cpputil/hdir.h\
cpputil/hurl.h\
cpputil/hscope.h\
cpputil/hthreadpool.h\
cpputil/hasync.h\
cpputil/hobjectpool.h\
cpputil/ifconfig.h\
cpputil/iniparser.h\
cpputil/json.hpp\
cpputil/singleton.h\
cpputil/ThreadLocalStorage.h\
EVPP_HEADERS = evpp/Buffer.h\
evpp/Channel.h\
evpp/Event.h\
evpp/EventLoop.h\
evpp/EventLoopThread.h\
evpp/EventLoopThreadPool.h\
evpp/Status.h\
evpp/TcpClient.h\
evpp/TcpServer.h\
evpp/UdpClient.h\
evpp/UdpServer.h\
PROTOCOL_HEADERS = protocol/icmp.h\
protocol/dns.h\
protocol/ftp.h\
protocol/smtp.h
HTTP_HEADERS = http/httpdef.h\
http/wsdef.h\
http/http_content.h\
http/HttpMessage.h\
http/HttpParser.h\
http/WebSocketParser.h\
http/WebSocketChannel.h\
HTTP2_HEADERS = http/http2def.h\
http/grpcdef.h\
HTTP_CLIENT_HEADERS = http/client/HttpClient.h\
http/client/requests.h\
http/client/axios.h\
http/client/AsyncHttpClient.h\
http/client/WebSocketClient.h\
HTTP_SERVER_HEADERS = http/server/HttpServer.h\
http/server/HttpService.h\
http/server/HttpContext.h\
http/server/HttpResponseWriter.h\
http/server/WebSocketServer.h\
MQTT_HEADERS = mqtt/mqtt_protocol.h\
mqtt/mqtt_client.h\

537
external/libhv/libhv-1.3.2/README-CN.md vendored Normal file
View File

@ -0,0 +1,537 @@
[English](README.md) | 中文
# libhv
[![Linux](https://badgen.net/badge/Linux/success/green?icon=github)](https://github.com/ithewei/libhv/actions/workflows/CI.yml?query=branch%3Amaster)
[![Windows](https://badgen.net/badge/Windows/success/green?icon=github)](https://github.com/ithewei/libhv/actions/workflows/CI.yml?query=branch%3Amaster)
[![macOS](https://badgen.net/badge/macOS/success/green?icon=github)](https://github.com/ithewei/libhv/actions/workflows/CI.yml?query=branch%3Amaster)
[![Android](https://badgen.net/badge/Android/success/green?icon=github)](https://github.com/ithewei/libhv/actions/workflows/CI.yml?query=branch%3Amaster)
[![iOS](https://badgen.net/badge/iOS/success/green?icon=github)](https://github.com/ithewei/libhv/actions/workflows/CI.yml?query=branch%3Amaster)
[![benchmark](https://github.com/ithewei/libhv/workflows/benchmark/badge.svg?branch=master)](https://github.com/ithewei/libhv/actions/workflows/benchmark.yml?query=branch%3Amaster)
<br>
[![release](https://badgen.net/github/release/ithewei/libhv?icon=github)](https://github.com/ithewei/libhv/releases)
[![stars](https://badgen.net/github/stars/ithewei/libhv?icon=github)](https://github.com/ithewei/libhv/stargazers)
[![forks](https://badgen.net/github/forks/ithewei/libhv?icon=github)](https://github.com/ithewei/libhv/forks)
[![issues](https://badgen.net/github/issues/ithewei/libhv?icon=github)](https://github.com/ithewei/libhv/issues)
[![PRs](https://badgen.net/github/prs/ithewei/libhv?icon=github)](https://github.com/ithewei/libhv/pulls)
[![contributors](https://badgen.net/github/contributors/ithewei/libhv?icon=github)](https://github.com/ithewei/libhv/contributors)
[![license](https://badgen.net/github/license/ithewei/libhv?icon=github)](LICENSE)
<br>
[![gitee](https://badgen.net/badge/mirror/gitee/red)](https://gitee.com/libhv/libhv)
[![awesome-c](https://badgen.net/badge/icon/awesome-c/pink?icon=awesome&label&color)](https://github.com/oz123/awesome-c)
[![awesome-cpp](https://badgen.net/badge/icon/awesome-cpp/pink?icon=awesome&label&color)](https://github.com/fffaraz/awesome-cpp)
`libhv`是一个类似于`libevent、libev、libuv`的跨平台网络库,提供了更易用的接口和更丰富的协议。
## 📚 中文资料
- **libhv QQ群**: `739352073`,欢迎加群交流
- **libhv 源码剖析**: <https://hewei.blog.csdn.net/article/details/123295998>
- **libhv 接口手册**: <https://hewei.blog.csdn.net/article/details/103976875>
- **libhv 教程目录**: <https://hewei.blog.csdn.net/article/details/113733758>
- [libhv教程01--介绍与体验](https://hewei.blog.csdn.net/article/details/113702536)
- [libhv教程02--编译与安装](https://hewei.blog.csdn.net/article/details/113704737)
- [libhv教程03--链库与使用](https://hewei.blog.csdn.net/article/details/113706378)
- [libhv教程04--编写一个完整的命令行程序](https://hewei.blog.csdn.net/article/details/113719503)
- [libhv教程05--事件循环以及定时器的简单使用](https://hewei.blog.csdn.net/article/details/113724474)
- [libhv教程06--创建一个简单的TCP服务端](https://hewei.blog.csdn.net/article/details/113737580)
- [libhv教程07--创建一个简单的TCP客户端](https://hewei.blog.csdn.net/article/details/113738900)
- [libhv教程08--创建一个简单的UDP服务端](https://hewei.blog.csdn.net/article/details/113871498)
- [libhv教程09--创建一个简单的UDP客户端](https://hewei.blog.csdn.net/article/details/113871724)
- [libhv教程10--创建一个简单的HTTP服务端](https://hewei.blog.csdn.net/article/details/113982999)
- [libhv教程11--创建一个简单的HTTP客户端](https://hewei.blog.csdn.net/article/details/113984302)
- [libhv教程12--创建一个简单的WebSocket服务端](https://hewei.blog.csdn.net/article/details/113985321)
- [libhv教程13--创建一个简单的WebSocket客户端](https://hewei.blog.csdn.net/article/details/113985895)
- [libhv教程14--200行实现一个纯C版jsonrpc框架](https://hewei.blog.csdn.net/article/details/119920540)
- [libhv教程15--200行实现一个C++版protorpc框架](https://hewei.blog.csdn.net/article/details/119966701)
- [libhv教程16--多线程/多进程服务端编程](https://hewei.blog.csdn.net/article/details/120366024)
- [libhv教程17--Qt中使用libhv](https://hewei.blog.csdn.net/article/details/120699890)
- [libhv教程18--动手写一个tinyhttpd](https://hewei.blog.csdn.net/article/details/121706604)
- [libhv教程19--MQTT的实现与使用](https://hewei.blog.csdn.net/article/details/122753665)
## ✨ 特性
- 跨平台Linux, Windows, macOS, Android, iOS, BSD, Solaris
- 高性能事件循环网络IO事件、定时器事件、空闲事件、自定义事件
- TCP/UDP服务端/客户端/代理
- TCP支持心跳、重连、转发、多线程安全write和close等特性
- 内置常见的拆包模式(固定包长、分界符、头部长度字段)
- 可靠UDP支持: WITH_KCP
- SSL/TLS加密通信可选WITH_OPENSSL、WITH_GNUTLS、WITH_MBEDTLS
- HTTP服务端/客户端支持https http1/x http2 grpc
- HTTP支持静态文件服务、目录服务、正向/反向代理服务、同步/异步API处理器
- HTTP支持RESTful风格、路由、中间件、keep-alive长连接、chunked分块、SSE等特性
- WebSocket服务端/客户端
- MQTT客户端
## ⌛️ 构建
见[BUILD.md](BUILD.md)
libhv提供了以下构建方式:
1、通过Makefile:
```shell
./configure
make
sudo make install
```
2、通过cmake:
```shell
mkdir build
cd build
cmake ..
cmake --build .
```
3、通过vcpkg:
```shell
vcpkg install libhv
```
4、通过xmake:
```shell
xrepo install libhv
```
## ⚡️ 快速入门
### 体验
运行脚本`./getting_started.sh`:
```shell
# 下载编译
git clone https://github.com/ithewei/libhv.git
cd libhv
./configure
make
# 运行httpd服务
bin/httpd -h
bin/httpd -d
#bin/httpd -c etc/httpd.conf -s restart -d
ps aux | grep httpd
# 文件服务
bin/curl -v localhost:8080
# 目录服务
bin/curl -v localhost:8080/downloads/
# API服务
bin/curl -v localhost:8080/ping
bin/curl -v localhost:8080/echo -d "hello,world!"
bin/curl -v localhost:8080/query?page_no=1\&page_size=10
bin/curl -v localhost:8080/kv -H "Content-Type:application/x-www-form-urlencoded" -d 'user=admin&pswd=123456'
bin/curl -v localhost:8080/json -H "Content-Type:application/json" -d '{"user":"admin","pswd":"123456"}'
bin/curl -v localhost:8080/form -F 'user=admin' -F 'pswd=123456'
bin/curl -v localhost:8080/upload -d "@LICENSE"
bin/curl -v localhost:8080/upload -F "file=@LICENSE"
bin/curl -v localhost:8080/test -H "Content-Type:application/x-www-form-urlencoded" -d 'bool=1&int=123&float=3.14&string=hello'
bin/curl -v localhost:8080/test -H "Content-Type:application/json" -d '{"bool":true,"int":123,"float":3.14,"string":"hello"}'
bin/curl -v localhost:8080/test -F 'bool=1' -F 'int=123' -F 'float=3.14' -F 'string=hello'
# RESTful API: /group/:group_name/user/:user_id
bin/curl -v -X DELETE localhost:8080/group/test/user/123
# 压力测试
bin/wrk -c 1000 -d 10 -t 4 http://127.0.0.1:8080/
```
### TCP
#### TCP服务端
**c版本**: [examples/tcp_echo_server.c](examples/tcp_echo_server.c)
**c++版本**: [evpp/TcpServer_test.cpp](evpp/TcpServer_test.cpp)
```c++
#include "TcpServer.h"
using namespace hv;
int main() {
int port = 1234;
TcpServer srv;
int listenfd = srv.createsocket(port);
if (listenfd < 0) {
return -1;
}
printf("server listen on port %d, listenfd=%d ...\n", port, listenfd);
srv.onConnection = [](const SocketChannelPtr& channel) {
std::string peeraddr = channel->peeraddr();
if (channel->isConnected()) {
printf("%s connected! connfd=%d\n", peeraddr.c_str(), channel->fd());
} else {
printf("%s disconnected! connfd=%d\n", peeraddr.c_str(), channel->fd());
}
};
srv.onMessage = [](const SocketChannelPtr& channel, Buffer* buf) {
// echo
channel->write(buf);
};
srv.setThreadNum(4);
srv.start();
// press Enter to stop
while (getchar() != '\n');
return 0;
}
```
**注意**:
以上示例只是简单的`echo`服务TCP是流式协议实际应用中请务必添加边界进行拆包。<br>
文本协议建议加上`\0`或者`\r\n`分隔符,可参考 [examples/jsonrpc](examples/jsonrpc);<br>
二进制协议建议加上自定义协议头,通过头部长度字段表明负载长度,可参考 [examples/protorpc](examples/protorpc);<br>
通过`setUnpack`c接口即`hio_set_unpack`)设置拆包规则,支持固定包长、分隔符、头部长度字段三种常见的拆包方式,<br>
内部根据拆包规则处理粘包与分包,保证`onMessage`回调上来的是完整的一包数据,大大节省了上层处理粘包与分包的成本。<br>
不想自定义协议和拆包组包的可直接使用现成的`HTTP/WebSocket`协议。<br>
<br>
`channel->write`c接口即`hio_write`)是非阻塞的(事件循环异步编程里所有的一切都要求是非阻塞的),且多线程安全的。<br>
发送大数据时应该做流控,通过`onWriteComplete`监听写完成事件,在可写时再发送下一帧数据。<br>
具体示例代码可参考 [examples/tinyhttpd.c](examples/tinyhttpd.c) 中的 `http_serve_file`<br>
<br>
`channel->close`c接口即`hio_close`) 也是多线程安全的这可以让网络IO事件循环线程里接收数据、拆包组包、反序列化后放入队列<br>
消费者线程/线程池从队列里取出数据、处理后发送响应和关闭连接,变得更加简单。<br>
#### TCP客户端
**c版本**: [examples/tcp_client_test.c](examples/tcp_client_test.c)
**c++版本**: [evpp/TcpClient_test.cpp](evpp/TcpClient_test.cpp)
```c++
#include <iostream>
#include "TcpClient.h"
using namespace hv;
int main() {
int port = 1234;
TcpClient cli;
int connfd = cli.createsocket(port);
if (connfd < 0) {
return -1;
}
cli.onConnection = [](const SocketChannelPtr& channel) {
std::string peeraddr = channel->peeraddr();
if (channel->isConnected()) {
printf("connected to %s! connfd=%d\n", peeraddr.c_str(), channel->fd());
} else {
printf("disconnected to %s! connfd=%d\n", peeraddr.c_str(), channel->fd());
}
};
cli.onMessage = [](const SocketChannelPtr& channel, Buffer* buf) {
printf("< %.*s\n", (int)buf->size(), (char*)buf->data());
};
cli.start();
std::string str;
while (std::getline(std::cin, str)) {
if (str == "close") {
cli.closesocket();
} else if (str == "start") {
cli.start();
} else if (str == "stop") {
cli.stop();
break;
} else {
if (!cli.isConnected()) break;
cli.send(str);
}
}
return 0;
}
```
### HTTP
#### HTTP服务端
见[examples/http_server_test.cpp](examples/http_server_test.cpp)
**golang gin 风格**
```c++
#include "HttpServer.h"
using namespace hv;
int main() {
HttpService router;
router.GET("/ping", [](HttpRequest* req, HttpResponse* resp) {
return resp->String("pong");
});
router.GET("/data", [](HttpRequest* req, HttpResponse* resp) {
static char data[] = "0123456789";
return resp->Data(data, 10);
});
router.GET("/paths", [&router](HttpRequest* req, HttpResponse* resp) {
return resp->Json(router.Paths());
});
router.GET("/get", [](HttpRequest* req, HttpResponse* resp) {
resp->json["origin"] = req->client_addr.ip;
resp->json["url"] = req->url;
resp->json["args"] = req->query_params;
resp->json["headers"] = req->headers;
return 200;
});
router.POST("/echo", [](const HttpContextPtr& ctx) {
return ctx->send(ctx->body(), ctx->type());
});
HttpServer server(&router);
server.setPort(8080);
server.setThreadNum(4);
server.run();
return 0;
}
```
**注意**:
上面示例直接运行在`main`主线程,`server.run()`会阻塞当前线程运行,所以`router`和`server`对象不会被析构,<br>
如使用`server.start()`内部会另起线程运行,不会阻塞当前线程,但需要注意`router`和`server`的生命周期,<br>
不要定义为局部变量被析构了,可定义为类成员变量或者全局变量,下面的`WebSocket`服务同理。<br>
#### HTTP客户端
见[examples/http_client_test.cpp](examples/http_client_test.cpp)
**python requests 风格**
```c++
#include "requests.h"
int main() {
auto resp = requests::get("http://www.example.com");
if (resp == NULL) {
printf("request failed!\n");
} else {
printf("%s\n", resp->body.c_str());
}
resp = requests::post("127.0.0.1:8080/echo", "hello,world!");
if (resp == NULL) {
printf("request failed!\n");
} else {
printf("%s\n", resp->body.c_str());
}
return 0;
}
```
附HTTP相关接口文档:
- [class HttpMessage](docs/cn/HttpMessage.md)
- [class HttpClient](docs/cn/HttpClient.md)
- [class HttpServer](docs/cn/HttpServer.md)
- [class HttpContext](docs/cn/HttpContext.md)
### WebSocket
#### WebSocket服务端
见[examples/websocket_server_test.cpp](examples/websocket_server_test.cpp)
```c++
#include "WebSocketServer.h"
using namespace hv;
int main(int argc, char** argv) {
WebSocketService ws;
ws.onopen = [](const WebSocketChannelPtr& channel, const HttpRequestPtr& req) {
printf("onopen: GET %s\n", req->Path().c_str());
};
ws.onmessage = [](const WebSocketChannelPtr& channel, const std::string& msg) {
printf("onmessage: %.*s\n", (int)msg.size(), msg.data());
};
ws.onclose = [](const WebSocketChannelPtr& channel) {
printf("onclose\n");
};
WebSocketServer server(&ws);
server.setPort(9999);
server.setThreadNum(4);
server.run();
return 0;
}
```
#### WebSocket客户端
见[examples/websocket_client_test.cpp](examples/websocket_client_test.cpp)
```c++
#include "WebSocketClient.h"
using namespace hv;
int main(int argc, char** argv) {
WebSocketClient ws;
ws.onopen = []() {
printf("onopen\n");
};
ws.onmessage = [](const std::string& msg) {
printf("onmessage: %.*s\n", (int)msg.size(), msg.data());
};
ws.onclose = []() {
printf("onclose\n");
};
// reconnect: 1,2,4,8,10,10,10...
reconn_setting_t reconn;
reconn_setting_init(&reconn);
reconn.min_delay = 1000;
reconn.max_delay = 10000;
reconn.delay_policy = 2;
ws.setReconnect(&reconn);
ws.open("ws://127.0.0.1:9999/test");
std::string str;
while (std::getline(std::cin, str)) {
if (!ws.isConnected()) break;
if (str == "quit") {
ws.close();
break;
}
ws.send(str);
}
return 0;
}
```
## 🍭 更多示例
### c版本
- 事件循环: [examples/hloop_test.c](examples/hloop_test.c)
- 定时器: [examples/htimer_test.c](examples/htimer_test.c)
- TCP回显服务: [examples/tcp_echo_server.c](examples/tcp_echo_server.c)
- TCP聊天服务: [examples/tcp_chat_server.c](examples/tcp_chat_server.c)
- TCP代理服务: [examples/tcp_proxy_server.c](examples/tcp_proxy_server.c)
- UDP回显服务: [examples/udp_echo_server.c](examples/udp_echo_server.c)
- UDP代理服务: [examples/udp_proxy_server.c](examples/udp_proxy_server.c)
- SOCKS5代理服务: [examples/socks5_proxy_server.c](examples/socks5_proxy_server.c)
- HTTP服务: [examples/tinyhttpd.c](examples/tinyhttpd.c)
- HTTP代理服务: [examples/tinyproxyd.c](examples/tinyproxyd.c)
- jsonRPC示例: [examples/jsonrpc](examples/jsonrpc)
- MQTT示例: [examples/mqtt](examples/mqtt)
- 多accept进程模式: [examples/multi-thread/multi-acceptor-processes.c](examples/multi-thread/multi-acceptor-processes.c)
- 多accept线程模式: [examples/multi-thread/multi-acceptor-threads.c](examples/multi-thread/multi-acceptor-threads.c)
- 一个accept线程+多worker线程: [examples/multi-thread/one-acceptor-multi-workers.c](examples/multi-thread/one-acceptor-multi-workers.c)
### c++版本
- 事件循环: [evpp/EventLoop_test.cpp](evpp/EventLoop_test.cpp)
- 事件循环线程: [evpp/EventLoopThread_test.cpp](evpp/EventLoopThread_test.cpp)
- 事件循环线程池: [evpp/EventLoopThreadPool_test.cpp](evpp/EventLoopThreadPool_test.cpp)
- 定时器: [evpp/TimerThread_test.cpp](evpp/TimerThread_test.cpp)
- TCP服务端: [evpp/TcpServer_test.cpp](evpp/TcpServer_test.cpp)
- TCP客户端: [evpp/TcpClient_test.cpp](evpp/TcpClient_test.cpp)
- UDP服务端: [evpp/UdpServer_test.cpp](evpp/UdpServer_test.cpp)
- UDP客户端: [evpp/UdpClient_test.cpp](evpp/UdpClient_test.cpp)
- HTTP服务端: [examples/http_server_test.cpp](examples/http_server_test.cpp)
- HTTP客户端: [examples/http_client_test.cpp](examples/http_client_test.cpp)
- WebSocket服务端: [examples/websocket_server_test.cpp](examples/websocket_server_test.cpp)
- WebSocket客户端: [examples/websocket_client_test.cpp](examples/websocket_client_test.cpp)
- protobufRPC示例: [examples/protorpc](examples/protorpc)
- Qt中使用libhv示例: [hv-projects/QtDemo](https://github.com/hv-projects/QtDemo)
### 模拟实现著名的命令行工具
- 网络连接工具: [examples/nc](examples/nc.c)
- 网络扫描工具: [examples/nmap](examples/nmap)
- HTTP服务程序: [examples/httpd](examples/httpd)
- HTTP压测工具: [examples/wrk](examples/wrk.cpp)
- URL请求工具: [examples/curl](examples/curl.cpp)
- 文件下载工具: [examples/wget](examples/wget.cpp)
- 服务注册与发现: [examples/consul](examples/consul)
## 🥇 性能测试
### TCP回显服务pingpong测试
```shell
cd echo-servers
./build.sh
./benchmark.sh
```
**吞吐量**:
```shell
libevent running on port 2001
libev running on port 2002
libuv running on port 2003
libhv running on port 2004
asio running on port 2005
poco running on port 2006
==============2001=====================================
[127.0.0.1:2001] 4 threads 1000 connections run 10s
total readcount=1616761 readbytes=1655563264
throughput = 157 MB/s
==============2002=====================================
[127.0.0.1:2002] 4 threads 1000 connections run 10s
total readcount=2153171 readbytes=2204847104
throughput = 210 MB/s
==============2003=====================================
[127.0.0.1:2003] 4 threads 1000 connections run 10s
total readcount=1599727 readbytes=1638120448
throughput = 156 MB/s
==============2004=====================================
[127.0.0.1:2004] 4 threads 1000 connections run 10s
total readcount=2202271 readbytes=2255125504
throughput = 215 MB/s
==============2005=====================================
[127.0.0.1:2005] 4 threads 1000 connections run 10s
total readcount=1354230 readbytes=1386731520
throughput = 132 MB/s
==============2006=====================================
[127.0.0.1:2006] 4 threads 1000 connections run 10s
total readcount=1699652 readbytes=1740443648
throughput = 165 MB/s
```
### TCP代理服务压测
```shell
# sudo apt install iperf
iperf -s -p 5001 > /dev/null &
bin/tcp_proxy_server 1212 127.0.0.1:5001 &
iperf -c 127.0.0.1 -p 5001 -l 8K
iperf -c 127.0.0.1 -p 1212 -l 8K
```
**带宽**:
```shell
------------------------------------------------------------
[ 3] local 127.0.0.1 port 52560 connected with 127.0.0.1 port 5001
[ ID] Interval Transfer Bandwidth
[ 3] 0.0-10.0 sec 20.8 GBytes 17.9 Gbits/sec
------------------------------------------------------------
[ 3] local 127.0.0.1 port 48142 connected with 127.0.0.1 port 1212
[ ID] Interval Transfer Bandwidth
[ 3] 0.0-10.0 sec 11.9 GBytes 10.2 Gbits/sec
```
### HTTP压测
```shell
# sudo apt install wrk
wrk -c 100 -t 4 -d 10s http://127.0.0.1:8080/
# sudo apt install apache2-utils
ab -c 100 -n 100000 http://127.0.0.1:8080/
```
**libhv(port:8080) vs nginx(port:80)**
![libhv-vs-nginx.png](html/downloads/libhv-vs-nginx.png)
以上测试结果可以在 [Github Actions](https://github.com/ithewei/libhv/actions/workflows/benchmark.yml) 中查看。
## 💎 用户案例
如果您在使用`libhv`欢迎通过PR将信息提交至此列表让更多的用户了解`libhv`的实际使用场景,以建立更好的网络生态。
| 用户 (公司名/项目名/个人联系方式) | 案例 (项目简介/业务场景) |
| :--- | :--- |
| [阅面科技](https://www.readsense.cn) | [猎户AIoT平台](https://orionweb.readsense.cn)设备管理、人脸检测HTTP服务、人脸搜索HTTP服务 |
| [socks5-libhv](https://gitee.com/billykang/socks5-libhv) | socks5代理 |
| [hvloop](https://github.com/xiispace/hvloop) | 类似[uvloop](https://github.com/MagicStack/uvloop)的python异步IO事件循环 |
| [tsproxyd-android](https://github.com/Haiwen-GitHub/tsproxyd-android) | 一个基于libhv实现的android端web代理服务 |
| [玄舟智维](https://zjzwxw.com) | C100K设备连接网关服务 |

462
external/libhv/libhv-1.3.2/README.md vendored Normal file
View File

@ -0,0 +1,462 @@
English | [中文](README-CN.md)
# libhv
[![Linux](https://badgen.net/badge/Linux/success/green?icon=github)](https://github.com/ithewei/libhv/actions/workflows/CI.yml?query=branch%3Amaster)
[![Windows](https://badgen.net/badge/Windows/success/green?icon=github)](https://github.com/ithewei/libhv/actions/workflows/CI.yml?query=branch%3Amaster)
[![macOS](https://badgen.net/badge/macOS/success/green?icon=github)](https://github.com/ithewei/libhv/actions/workflows/CI.yml?query=branch%3Amaster)
[![Android](https://badgen.net/badge/Android/success/green?icon=github)](https://github.com/ithewei/libhv/actions/workflows/CI.yml?query=branch%3Amaster)
[![iOS](https://badgen.net/badge/iOS/success/green?icon=github)](https://github.com/ithewei/libhv/actions/workflows/CI.yml?query=branch%3Amaster)
[![benchmark](https://github.com/ithewei/libhv/workflows/benchmark/badge.svg?branch=master)](https://github.com/ithewei/libhv/actions/workflows/benchmark.yml?query=branch%3Amaster)
<br>
[![release](https://badgen.net/github/release/ithewei/libhv?icon=github)](https://github.com/ithewei/libhv/releases)
[![stars](https://badgen.net/github/stars/ithewei/libhv?icon=github)](https://github.com/ithewei/libhv/stargazers)
[![forks](https://badgen.net/github/forks/ithewei/libhv?icon=github)](https://github.com/ithewei/libhv/forks)
[![issues](https://badgen.net/github/issues/ithewei/libhv?icon=github)](https://github.com/ithewei/libhv/issues)
[![PRs](https://badgen.net/github/prs/ithewei/libhv?icon=github)](https://github.com/ithewei/libhv/pulls)
[![contributors](https://badgen.net/github/contributors/ithewei/libhv?icon=github)](https://github.com/ithewei/libhv/contributors)
[![license](https://badgen.net/github/license/ithewei/libhv?icon=github)](LICENSE)
<br>
[![gitee](https://badgen.net/badge/mirror/gitee/red)](https://gitee.com/libhv/libhv)
[![awesome-c](https://badgen.net/badge/icon/awesome-c/pink?icon=awesome&label&color)](https://github.com/oz123/awesome-c)
[![awesome-cpp](https://badgen.net/badge/icon/awesome-cpp/pink?icon=awesome&label&color)](https://github.com/fffaraz/awesome-cpp)
Like `libevent, libev, and libuv`,
`libhv` provides event-loop with non-blocking IO and timer,
but simpler api and richer protocols.
## ✨ Features
- Cross-platform (Linux, Windows, macOS, Android, iOS, BSD, Solaris)
- High-performance EventLoop (IO, timer, idle, custom)
- TCP/UDP client/server/proxy
- TCP supports heartbeat, reconnect, upstream, MultiThread-safe write and close, etc.
- Built-in common unpacking modes (FixedLength, Delimiter, LengthField)
- RUDP support: WITH_KCP
- SSL/TLS support: (via WITH_OPENSSL or WITH_GNUTLS or WITH_MBEDTLS)
- HTTP client/server (support https http1/x http2 grpc)
- HTTP supports static service, indexof service, forward/reverse proxy service, sync/async API handler
- HTTP supports RESTful, router, middleware, keep-alive, chunked, SSE, etc.
- WebSocket client/server
- MQTT client
## ⌛️ Build
see [BUILD.md](BUILD.md)
Makefile:
```shell
./configure
make
sudo make install
```
or cmake:
```shell
mkdir build
cd build
cmake ..
cmake --build .
```
or vcpkg:
```shell
vcpkg install libhv
```
or xmake:
```shell
xrepo install libhv
```
## ⚡️ Getting Started
run `./getting_started.sh`:
```shell
git clone https://github.com/ithewei/libhv.git
cd libhv
./configure
make
bin/httpd -h
bin/httpd -d
#bin/httpd -c etc/httpd.conf -s restart -d
ps aux | grep httpd
# http file service
bin/curl -v localhost:8080
# http indexof service
bin/curl -v localhost:8080/downloads/
# http api service
bin/curl -v localhost:8080/ping
bin/curl -v localhost:8080/echo -d "hello,world!"
bin/curl -v localhost:8080/query?page_no=1\&page_size=10
bin/curl -v localhost:8080/kv -H "Content-Type:application/x-www-form-urlencoded" -d 'user=admin&pswd=123456'
bin/curl -v localhost:8080/json -H "Content-Type:application/json" -d '{"user":"admin","pswd":"123456"}'
bin/curl -v localhost:8080/form -F 'user=admin' -F 'pswd=123456'
bin/curl -v localhost:8080/upload -d "@LICENSE"
bin/curl -v localhost:8080/upload -F "file=@LICENSE"
bin/curl -v localhost:8080/test -H "Content-Type:application/x-www-form-urlencoded" -d 'bool=1&int=123&float=3.14&string=hello'
bin/curl -v localhost:8080/test -H "Content-Type:application/json" -d '{"bool":true,"int":123,"float":3.14,"string":"hello"}'
bin/curl -v localhost:8080/test -F 'bool=1' -F 'int=123' -F 'float=3.14' -F 'string=hello'
# RESTful API: /group/:group_name/user/:user_id
bin/curl -v -X DELETE localhost:8080/group/test/user/123
# benchmark
bin/wrk -c 1000 -d 10 -t 4 http://127.0.0.1:8080/
```
### TCP
#### tcp server
**c version**: [examples/tcp_echo_server.c](examples/tcp_echo_server.c)
**c++ version**: [evpp/TcpServer_test.cpp](evpp/TcpServer_test.cpp)
```c++
#include "TcpServer.h"
using namespace hv;
int main() {
int port = 1234;
TcpServer srv;
int listenfd = srv.createsocket(port);
if (listenfd < 0) {
return -1;
}
printf("server listen on port %d, listenfd=%d ...\n", port, listenfd);
srv.onConnection = [](const SocketChannelPtr& channel) {
std::string peeraddr = channel->peeraddr();
if (channel->isConnected()) {
printf("%s connected! connfd=%d\n", peeraddr.c_str(), channel->fd());
} else {
printf("%s disconnected! connfd=%d\n", peeraddr.c_str(), channel->fd());
}
};
srv.onMessage = [](const SocketChannelPtr& channel, Buffer* buf) {
// echo
channel->write(buf);
};
srv.setThreadNum(4);
srv.start();
// press Enter to stop
while (getchar() != '\n');
return 0;
}
```
#### tcp client
**c version**: [examples/tcp_client_test.c](examples/tcp_client_test.c)
**c++ version**: [evpp/TcpClient_test.cpp](evpp/TcpClient_test.cpp)
```c++
#include <iostream>
#include "TcpClient.h"
using namespace hv;
int main() {
int port = 1234;
TcpClient cli;
int connfd = cli.createsocket(port);
if (connfd < 0) {
return -1;
}
cli.onConnection = [](const SocketChannelPtr& channel) {
std::string peeraddr = channel->peeraddr();
if (channel->isConnected()) {
printf("connected to %s! connfd=%d\n", peeraddr.c_str(), channel->fd());
} else {
printf("disconnected to %s! connfd=%d\n", peeraddr.c_str(), channel->fd());
}
};
cli.onMessage = [](const SocketChannelPtr& channel, Buffer* buf) {
printf("< %.*s\n", (int)buf->size(), (char*)buf->data());
};
cli.start();
std::string str;
while (std::getline(std::cin, str)) {
if (str == "close") {
cli.closesocket();
} else if (str == "start") {
cli.start();
} else if (str == "stop") {
cli.stop();
break;
} else {
if (!cli.isConnected()) break;
cli.send(str);
}
}
return 0;
}
```
### HTTP
#### http server
see [examples/http_server_test.cpp](examples/http_server_test.cpp)
**golang gin style**
```c++
#include "HttpServer.h"
using namespace hv;
int main() {
HttpService router;
router.GET("/ping", [](HttpRequest* req, HttpResponse* resp) {
return resp->String("pong");
});
router.GET("/data", [](HttpRequest* req, HttpResponse* resp) {
static char data[] = "0123456789";
return resp->Data(data, 10);
});
router.GET("/paths", [&router](HttpRequest* req, HttpResponse* resp) {
return resp->Json(router.Paths());
});
router.GET("/get", [](HttpRequest* req, HttpResponse* resp) {
resp->json["origin"] = req->client_addr.ip;
resp->json["url"] = req->url;
resp->json["args"] = req->query_params;
resp->json["headers"] = req->headers;
return 200;
});
router.POST("/echo", [](const HttpContextPtr& ctx) {
return ctx->send(ctx->body(), ctx->type());
});
HttpServer server(&router);
server.setPort(8080);
server.setThreadNum(4);
server.run();
return 0;
}
```
#### http client
see [examples/http_client_test.cpp](examples/http_client_test.cpp)
**python requests style**
```c++
#include "requests.h"
int main() {
auto resp = requests::get("http://www.example.com");
if (resp == NULL) {
printf("request failed!\n");
} else {
printf("%s\n", resp->body.c_str());
}
resp = requests::post("127.0.0.1:8080/echo", "hello,world!");
if (resp == NULL) {
printf("request failed!\n");
} else {
printf("%s\n", resp->body.c_str());
}
return 0;
}
```
### WebSocket
#### WebSocket server
see [examples/websocket_server_test.cpp](examples/websocket_server_test.cpp)
```c++
#include "WebSocketServer.h"
using namespace hv;
int main(int argc, char** argv) {
WebSocketService ws;
ws.onopen = [](const WebSocketChannelPtr& channel, const HttpRequestPtr& req) {
printf("onopen: GET %s\n", req->Path().c_str());
};
ws.onmessage = [](const WebSocketChannelPtr& channel, const std::string& msg) {
printf("onmessage: %.*s\n", (int)msg.size(), msg.data());
};
ws.onclose = [](const WebSocketChannelPtr& channel) {
printf("onclose\n");
};
WebSocketServer server(&ws);
server.setPort(9999);
server.setThreadNum(4);
server.run();
return 0;
}
```
#### WebSocket client
see [examples/websocket_client_test.cpp](examples/websocket_client_test.cpp)
```c++
#include "WebSocketClient.h"
using namespace hv;
int main(int argc, char** argv) {
WebSocketClient ws;
ws.onopen = []() {
printf("onopen\n");
};
ws.onmessage = [](const std::string& msg) {
printf("onmessage: %.*s\n", (int)msg.size(), msg.data());
};
ws.onclose = []() {
printf("onclose\n");
};
// reconnect: 1,2,4,8,10,10,10...
reconn_setting_t reconn;
reconn_setting_init(&reconn);
reconn.min_delay = 1000;
reconn.max_delay = 10000;
reconn.delay_policy = 2;
ws.setReconnect(&reconn);
ws.open("ws://127.0.0.1:9999/test");
std::string str;
while (std::getline(std::cin, str)) {
if (!ws.isConnected()) break;
if (str == "quit") {
ws.close();
break;
}
ws.send(str);
}
return 0;
}
```
## 🍭 More examples
### c version
- [examples/hloop_test.c](examples/hloop_test.c)
- [examples/htimer_test.c](examples/htimer_test.c)
- [examples/tcp_echo_server.c](examples/tcp_echo_server.c)
- [examples/tcp_chat_server.c](examples/tcp_chat_server.c)
- [examples/tcp_proxy_server.c](examples/tcp_proxy_server.c)
- [examples/udp_echo_server.c](examples/udp_echo_server.c)
- [examples/udp_proxy_server.c](examples/udp_proxy_server.c)
- [examples/socks5_proxy_server.c](examples/socks5_proxy_server.c)
- [examples/tinyhttpd.c](examples/tinyhttpd.c)
- [examples/tinyproxyd.c](examples/tinyproxyd.c)
- [examples/jsonrpc](examples/jsonrpc)
- [examples/mqtt](examples/mqtt)
- [examples/multi-thread/multi-acceptor-processes.c](examples/multi-thread/multi-acceptor-processes.c)
- [examples/multi-thread/multi-acceptor-threads.c](examples/multi-thread/multi-acceptor-threads.c)
- [examples/multi-thread/one-acceptor-multi-workers.c](examples/multi-thread/one-acceptor-multi-workers.c)
### c++ version
- [evpp/EventLoop_test.cpp](evpp/EventLoop_test.cpp)
- [evpp/EventLoopThread_test.cpp](evpp/EventLoopThread_test.cpp)
- [evpp/EventLoopThreadPool_test.cpp](evpp/EventLoopThreadPool_test.cpp)
- [evpp/TimerThread_test.cpp](evpp/TimerThread_test.cpp)
- [evpp/TcpServer_test.cpp](evpp/TcpServer_test.cpp)
- [evpp/TcpClient_test.cpp](evpp/TcpClient_test.cpp)
- [evpp/UdpServer_test.cpp](evpp/UdpServer_test.cpp)
- [evpp/UdpClient_test.cpp](evpp/UdpClient_test.cpp)
- [examples/http_server_test.cpp](examples/http_server_test.cpp)
- [examples/http_client_test.cpp](examples/http_client_test.cpp)
- [examples/websocket_server_test.cpp](examples/websocket_server_test.cpp)
- [examples/websocket_client_test.cpp](examples/websocket_client_test.cpp)
- [examples/protorpc](examples/protorpc)
- [hv-projects/QtDemo](https://github.com/hv-projects/QtDemo)
### simulate well-known command line tools
- [examples/nc](examples/nc.c)
- [examples/nmap](examples/nmap)
- [examples/httpd](examples/httpd)
- [examples/wrk](examples/wrk.cpp)
- [examples/curl](examples/curl.cpp)
- [examples/wget](examples/wget.cpp)
- [examples/consul](examples/consul)
## 🥇 Benchmark
### `pingpong echo-servers`
```shell
cd echo-servers
./build.sh
./benchmark.sh
```
**throughput**:
```shell
libevent running on port 2001
libev running on port 2002
libuv running on port 2003
libhv running on port 2004
asio running on port 2005
poco running on port 2006
==============2001=====================================
[127.0.0.1:2001] 4 threads 1000 connections run 10s
total readcount=1616761 readbytes=1655563264
throughput = 157 MB/s
==============2002=====================================
[127.0.0.1:2002] 4 threads 1000 connections run 10s
total readcount=2153171 readbytes=2204847104
throughput = 210 MB/s
==============2003=====================================
[127.0.0.1:2003] 4 threads 1000 connections run 10s
total readcount=1599727 readbytes=1638120448
throughput = 156 MB/s
==============2004=====================================
[127.0.0.1:2004] 4 threads 1000 connections run 10s
total readcount=2202271 readbytes=2255125504
throughput = 215 MB/s
==============2005=====================================
[127.0.0.1:2005] 4 threads 1000 connections run 10s
total readcount=1354230 readbytes=1386731520
throughput = 132 MB/s
==============2006=====================================
[127.0.0.1:2006] 4 threads 1000 connections run 10s
total readcount=1699652 readbytes=1740443648
throughput = 165 MB/s
```
### `iperf tcp_proxy_server`
```shell
# sudo apt install iperf
iperf -s -p 5001 > /dev/null &
bin/tcp_proxy_server 1212 127.0.0.1:5001 &
iperf -c 127.0.0.1 -p 5001 -l 8K
iperf -c 127.0.0.1 -p 1212 -l 8K
```
**Bandwidth**:
```shell
------------------------------------------------------------
[ 3] local 127.0.0.1 port 52560 connected with 127.0.0.1 port 5001
[ ID] Interval Transfer Bandwidth
[ 3] 0.0-10.0 sec 20.8 GBytes 17.9 Gbits/sec
------------------------------------------------------------
[ 3] local 127.0.0.1 port 48142 connected with 127.0.0.1 port 1212
[ ID] Interval Transfer Bandwidth
[ 3] 0.0-10.0 sec 11.9 GBytes 10.2 Gbits/sec
```
### `webbench`
```shell
# sudo apt install wrk
wrk -c 100 -t 4 -d 10s http://127.0.0.1:8080/
# sudo apt install apache2-utils
ab -c 100 -n 100000 http://127.0.0.1:8080/
```
**libhv(port:8080) vs nginx(port:80)**
![libhv-vs-nginx.png](html/downloads/libhv-vs-nginx.png)
Above test results can be found on [Github Actions](https://github.com/ithewei/libhv/actions/workflows/benchmark.yml).

36
external/libhv/libhv-1.3.2/TREE.md vendored Normal file
View File

@ -0,0 +1,36 @@
## 目录结构
```
.
├── base libhv基础设施如常用宏定义、数据结构、日期时间、线程、进程、日志、套接字
├── bin 可执行文件安装目录
├── build cmake默认构建目录
├── cert SSL证书存放目录
├── cmake cmake脚本存放目录
├── cpputil libhv工具类如字符串、文件、路径、线程池、json解析、ini解析
├── docs 文档存放目录
├── echo-servers 包含libevent、libev、libuv、libhv、asio、poco、muduo等多个网络库的tcp echo server写法并做压力测试
├── etc 应用程序配置目录
├── event libhv事件循环模块
├── evpp 事件循环c++封装类
├── examples 示例代码
│   └── httpd
├── html 网页document_root目录
│   ├── downloads 下载目录
│   └── uploads 上传目录
├── http libhv http模块
│   ├── client
│   └── server
├── include 头文件安装目录
│   └── hv
├── lib 库文件安装目录
├── logs 日志生成目录
├── misc 杂项
├── mqtt MQTT协议
├── protocol 包含icmp、dns、ftp、smtp等协议的实现
├── scripts shell脚本存放目录
├── ssl SSL/TLS加密通信
├── unittest 单元测试代码
└── util libhv工具函数如base64、md5、sha1
```

View File

@ -0,0 +1,28 @@
## 目录结构
```
.
├── array.h 动态数组
├── hatomic.h 原子操作
├── hbase.h 基础函数
├── hbuf.h 缓存
├── hdef.h 常见宏定义
├── heap.h 堆
├── hendian.h 大小端
├── herr.h 错误码表
├── hlog.h 日志
├── hmain.h 命令行解析
├── hmath.h 数学函数
├── hmutex.h 线程同步锁
├── hplatform.h 平台相关宏
├── hproc.h 进程
├── hsocket.h 套接字
├── hsysinfo.h 系统信息
├── hthread.h 线程
├── htime.h 时间
├── hversion.h 版本
├── list.h 链表
├── queue.h 队列
└── rbtree.h 红黑树
```

152
external/libhv/libhv-1.3.2/base/array.h vendored Normal file
View File

@ -0,0 +1,152 @@
#ifndef HV_ARRAY_H_
#define HV_ARRAY_H_
/*
* array
* at: random access by pos
* @effective
* push_back,pop_back,del_nomove,swap
* @ineffective
* add,del
*/
#include <assert.h> // for assert
#include <stddef.h> // for NULL
#include <stdlib.h> // for malloc,realloc,free
#include <string.h> // for memset,memmove
#include "hbase.h" // for HV_ALLOC, HV_FREE
#define ARRAY_INIT_SIZE 16
// #include <vector>
// typedef std::vector<type> atype;
#define ARRAY_DECL(type, atype) \
struct atype { \
type* ptr; \
size_t size; \
size_t maxsize;\
}; \
typedef struct atype atype;\
\
static inline type* atype##_data(atype* p) {\
return p->ptr;\
}\
\
static inline int atype##_size(atype* p) {\
return p->size;\
}\
\
static inline int atype##_maxsize(atype* p) {\
return p->maxsize;\
}\
\
static inline int atype##_empty(atype* p) {\
return p->size == 0;\
}\
\
static inline type* atype##_at(atype* p, int pos) {\
if (pos < 0) {\
pos += p->size;\
}\
assert(pos >= 0 && pos < p->size);\
return p->ptr + pos;\
}\
\
static inline type* atype##_front(atype* p) {\
return p->size == 0 ? NULL : p->ptr;\
}\
\
static inline type* atype##_back(atype* p) {\
return p->size == 0 ? NULL : p->ptr + p->size - 1;\
}\
\
static inline void atype##_init(atype* p, int maxsize) {\
p->size = 0;\
p->maxsize = maxsize;\
HV_ALLOC(p->ptr, sizeof(type) * maxsize);\
}\
\
static inline void atype##_clear(atype* p) {\
p->size = 0;\
memset(p->ptr, 0, sizeof(type) * p->maxsize);\
}\
\
static inline void atype##_cleanup(atype* p) {\
HV_FREE(p->ptr);\
p->size = p->maxsize = 0;\
}\
\
static inline void atype##_resize(atype* p, int maxsize) {\
if (maxsize == 0) maxsize = ARRAY_INIT_SIZE;\
p->ptr = (type*)hv_realloc(p->ptr, sizeof(type) * maxsize, sizeof(type) * p->maxsize);\
p->maxsize = maxsize;\
}\
\
static inline void atype##_double_resize(atype* p) {\
atype##_resize(p, p->maxsize * 2);\
}\
\
static inline void atype##_push_back(atype* p, type* elem) {\
if (p->size == p->maxsize) {\
atype##_double_resize(p);\
}\
p->ptr[p->size] = *elem;\
p->size++;\
}\
\
static inline void atype##_pop_back(atype* p) {\
assert(p->size > 0);\
p->size--;\
}\
\
static inline void atype##_add(atype* p, type* elem, int pos) {\
if (pos < 0) {\
pos += p->size;\
}\
assert(pos >= 0 && pos <= p->size);\
if (p->size == p->maxsize) {\
atype##_double_resize(p);\
}\
if (pos < p->size) {\
memmove(p->ptr + pos+1, p->ptr + pos, sizeof(type) * (p->size - pos));\
}\
p->ptr[pos] = *elem;\
p->size++;\
}\
\
static inline void atype##_del(atype* p, int pos) {\
if (pos < 0) {\
pos += p->size;\
}\
assert(pos >= 0 && pos < p->size);\
p->size--;\
if (pos < p->size) {\
memmove(p->ptr + pos, p->ptr + pos+1, sizeof(type) * (p->size - pos));\
}\
}\
\
static inline void atype##_del_nomove(atype* p, int pos) {\
if (pos < 0) {\
pos += p->size;\
}\
assert(pos >= 0 && pos < p->size);\
p->size--;\
if (pos < p->size) {\
p->ptr[pos] = p->ptr[p->size];\
}\
}\
\
static inline void atype##_swap(atype* p, int pos1, int pos2) {\
if (pos1 < 0) {\
pos1 += p->size;\
}\
if (pos2 < 0) {\
pos2 += p->size;\
}\
type tmp = p->ptr[pos1];\
p->ptr[pos1] = p->ptr[pos2];\
p->ptr[pos2] = tmp;\
}\
#endif // HV_ARRAY_H_

View File

@ -0,0 +1,130 @@
#ifndef HV_ATOMIC_H_
#define HV_ATOMIC_H_
#ifdef __cplusplus
// c++11
#include <atomic>
using std::atomic_flag;
using std::atomic_long;
#define ATOMIC_FLAG_TEST_AND_SET(p) ((p)->test_and_set())
#define ATOMIC_FLAG_CLEAR(p) ((p)->clear())
#else
#include "hplatform.h" // for HAVE_STDATOMIC_H
#if HAVE_STDATOMIC_H
// c11
#include <stdatomic.h>
#define ATOMIC_FLAG_TEST_AND_SET atomic_flag_test_and_set
#define ATOMIC_FLAG_CLEAR atomic_flag_clear
#define ATOMIC_ADD atomic_fetch_add
#define ATOMIC_SUB atomic_fetch_sub
#define ATOMIC_INC(p) ATOMIC_ADD(p, 1)
#define ATOMIC_DEC(p) ATOMIC_SUB(p, 1)
#else
typedef volatile bool atomic_bool;
typedef volatile char atomic_char;
typedef volatile unsigned char atomic_uchar;
typedef volatile short atomic_short;
typedef volatile unsigned short atomic_ushort;
typedef volatile int atomic_int;
typedef volatile unsigned int atomic_uint;
typedef volatile long atomic_long;
typedef volatile unsigned long atomic_ulong;
typedef volatile long long atomic_llong;
typedef volatile unsigned long long atomic_ullong;
typedef volatile size_t atomic_size_t;
typedef struct atomic_flag { atomic_bool _Value; } atomic_flag;
#ifdef _WIN32
#define ATOMIC_FLAG_TEST_AND_SET atomic_flag_test_and_set
static inline bool atomic_flag_test_and_set(atomic_flag* p) {
// return InterlockedIncrement((LONG*)&p->_Value, 1);
return InterlockedCompareExchange((LONG*)&p->_Value, 1, 0);
}
#define ATOMIC_ADD InterlockedAdd
#define ATOMIC_SUB(p, n) InterlockedAdd(p, -n)
#define ATOMIC_INC InterlockedIncrement
#define ATOMIC_DEC InterlockedDecrement
#elif defined(__GNUC__)
#define ATOMIC_FLAG_TEST_AND_SET atomic_flag_test_and_set
static inline bool atomic_flag_test_and_set(atomic_flag* p) {
return !__sync_bool_compare_and_swap(&p->_Value, 0, 1);
}
#define ATOMIC_ADD __sync_fetch_and_add
#define ATOMIC_SUB __sync_fetch_and_sub
#define ATOMIC_INC(p) ATOMIC_ADD(p, 1)
#define ATOMIC_DEC(p) ATOMIC_SUB(p, 1)
#endif
#endif // HAVE_STDATOMIC_H
#endif // __cplusplus
#ifndef ATOMIC_FLAG_INIT
#define ATOMIC_FLAG_INIT { 0 }
#endif
#ifndef ATOMIC_VAR_INIT
#define ATOMIC_VAR_INIT(value) (value)
#endif
#ifndef ATOMIC_FLAG_TEST_AND_SET
#define ATOMIC_FLAG_TEST_AND_SET atomic_flag_test_and_set
static inline bool atomic_flag_test_and_set(atomic_flag* p) {
bool ret = p->_Value;
p->_Value = 1;
return ret;
}
#endif
#ifndef ATOMIC_FLAG_CLEAR
#define ATOMIC_FLAG_CLEAR atomic_flag_clear
static inline void atomic_flag_clear(atomic_flag* p) {
p->_Value = 0;
}
#endif
#ifndef ATOMIC_ADD
#define ATOMIC_ADD(p, n) (*(p) += (n))
#endif
#ifndef ATOMIC_SUB
#define ATOMIC_SUB(p, n) (*(p) -= (n))
#endif
#ifndef ATOMIC_INC
#define ATOMIC_INC(p) ((*(p))++)
#endif
#ifndef ATOMIC_DEC
#define ATOMIC_DEC(p) ((*(p))--)
#endif
typedef atomic_flag hatomic_flag_t;
#define HATOMIC_FLAG_INIT ATOMIC_FLAG_INIT
#define hatomic_flag_test_and_set ATOMIC_FLAG_TEST_AND_SET
#define hatomic_flag_clear ATOMIC_FLAG_CLEAR
typedef atomic_long hatomic_t;
#define HATOMIC_VAR_INIT ATOMIC_VAR_INIT
#define hatomic_add ATOMIC_ADD
#define hatomic_sub ATOMIC_SUB
#define hatomic_inc ATOMIC_INC
#define hatomic_dec ATOMIC_DEC
#endif // HV_ATOMIC_H_

519
external/libhv/libhv-1.3.2/base/hbase.c vendored Normal file
View File

@ -0,0 +1,519 @@
#include "hbase.h"
#ifdef OS_DARWIN
#include <mach-o/dyld.h> // for _NSGetExecutablePath
#endif
#include "hatomic.h"
#ifndef RAND_MAX
#define RAND_MAX 2147483647
#endif
static hatomic_t s_alloc_cnt = HATOMIC_VAR_INIT(0);
static hatomic_t s_free_cnt = HATOMIC_VAR_INIT(0);
long hv_alloc_cnt() {
return s_alloc_cnt;
}
long hv_free_cnt() {
return s_free_cnt;
}
void* hv_malloc(size_t size) {
hatomic_inc(&s_alloc_cnt);
void* ptr = malloc(size);
if (!ptr) {
fprintf(stderr, "malloc failed!\n");
exit(-1);
}
return ptr;
}
void* hv_realloc(void* oldptr, size_t newsize, size_t oldsize) {
hatomic_inc(&s_alloc_cnt);
if (oldptr) hatomic_inc(&s_free_cnt);
void* ptr = realloc(oldptr, newsize);
if (!ptr) {
fprintf(stderr, "realloc failed!\n");
exit(-1);
}
if (newsize > oldsize) {
memset((char*)ptr + oldsize, 0, newsize - oldsize);
}
return ptr;
}
void* hv_calloc(size_t nmemb, size_t size) {
hatomic_inc(&s_alloc_cnt);
void* ptr = calloc(nmemb, size);
if (!ptr) {
fprintf(stderr, "calloc failed!\n");
exit(-1);
}
return ptr;
}
void* hv_zalloc(size_t size) {
hatomic_inc(&s_alloc_cnt);
void* ptr = malloc(size);
if (!ptr) {
fprintf(stderr, "malloc failed!\n");
exit(-1);
}
memset(ptr, 0, size);
return ptr;
}
void hv_free(void* ptr) {
if (ptr) {
free(ptr);
ptr = NULL;
hatomic_inc(&s_free_cnt);
}
}
char* hv_strupper(char* str) {
char* p = str;
while (*p != '\0') {
if (*p >= 'a' && *p <= 'z') {
*p &= ~0x20;
}
++p;
}
return str;
}
char* hv_strlower(char* str) {
char* p = str;
while (*p != '\0') {
if (*p >= 'A' && *p <= 'Z') {
*p |= 0x20;
}
++p;
}
return str;
}
char* hv_strreverse(char* str) {
if (str == NULL) return NULL;
char* b = str;
char* e = str;
while(*e) {++e;}
--e;
char tmp;
while (e > b) {
tmp = *e;
*e = *b;
*b = tmp;
--e;
++b;
}
return str;
}
// n = sizeof(dest_buf)
char* hv_strncpy(char* dest, const char* src, size_t n) {
assert(dest != NULL && src != NULL);
char* ret = dest;
while (*src != '\0' && --n > 0) {
*dest++ = *src++;
}
*dest = '\0';
return ret;
}
// n = sizeof(dest_buf)
char* hv_strncat(char* dest, const char* src, size_t n) {
assert(dest != NULL && src != NULL);
char* ret = dest;
while (*dest) {++dest;--n;}
while (*src != '\0' && --n > 0) {
*dest++ = *src++;
}
*dest = '\0';
return ret;
}
bool hv_strstartswith(const char* str, const char* start) {
assert(str != NULL && start != NULL);
while (*str && *start && *str == *start) {
++str;
++start;
}
return *start == '\0';
}
bool hv_strendswith(const char* str, const char* end) {
assert(str != NULL && end != NULL);
int len1 = 0;
int len2 = 0;
while (*str) {++str; ++len1;}
while (*end) {++end; ++len2;}
if (len1 < len2) return false;
while (len2-- > 0) {
--str;
--end;
if (*str != *end) {
return false;
}
}
return true;
}
bool hv_strcontains(const char* str, const char* sub) {
assert(str != NULL && sub != NULL);
return strstr(str, sub) != NULL;
}
bool hv_wildcard_match(const char* str, const char* pattern) {
assert(str != NULL && pattern != NULL);
bool match = false;
while (*str && *pattern) {
if (*pattern == '*') {
match = hv_strendswith(str, pattern + 1);
break;
} else if (*str != *pattern) {
match = false;
break;
} else {
++str;
++pattern;
}
}
return match ? match : (*str == '\0' && *pattern == '\0');
}
char* hv_strnchr(const char* s, char c, size_t n) {
assert(s != NULL);
const char* p = s;
while (*p != '\0' && n-- > 0) {
if (*p == c) return (char*)p;
++p;
}
return NULL;
}
char* hv_strrchr_dir(const char* filepath) {
char* p = (char*)filepath;
while (*p) ++p;
while (--p >= filepath) {
#ifdef OS_WIN
if (*p == '/' || *p == '\\')
#else
if (*p == '/')
#endif
return p;
}
return NULL;
}
const char* hv_basename(const char* filepath) {
const char* pos = hv_strrchr_dir(filepath);
return pos ? pos+1 : filepath;
}
const char* hv_suffixname(const char* filename) {
const char* pos = hv_strrchr_dot(filename);
return pos ? pos+1 : "";
}
int hv_mkdir_p(const char* dir) {
if (access(dir, 0) == 0) {
return EEXIST;
}
char tmp[MAX_PATH] = {0};
hv_strncpy(tmp, dir, sizeof(tmp));
char* p = tmp;
char delim = '/';
while (*p) {
#ifdef OS_WIN
if (*p == '/' || *p == '\\') {
delim = *p;
#else
if (*p == '/') {
#endif
*p = '\0';
hv_mkdir(tmp);
*p = delim;
}
++p;
}
if (hv_mkdir(tmp) != 0) {
return EPERM;
}
return 0;
}
int hv_rmdir_p(const char* dir) {
if (access(dir, 0) != 0) {
return ENOENT;
}
if (rmdir(dir) != 0) {
return EPERM;
}
char tmp[MAX_PATH] = {0};
hv_strncpy(tmp, dir, sizeof(tmp));
char* p = tmp;
while (*p) ++p;
while (--p >= tmp) {
#ifdef OS_WIN
if (*p == '/' || *p == '\\') {
#else
if (*p == '/') {
#endif
*p = '\0';
if (rmdir(tmp) != 0) {
return 0;
}
}
}
return 0;
}
bool hv_exists(const char* path) {
return access(path, 0) == 0;
}
bool hv_isdir(const char* path) {
if (access(path, 0) != 0) return false;
struct stat st;
memset(&st, 0, sizeof(st));
stat(path, &st);
return S_ISDIR(st.st_mode);
}
bool hv_isfile(const char* path) {
if (access(path, 0) != 0) return false;
struct stat st;
memset(&st, 0, sizeof(st));
stat(path, &st);
return S_ISREG(st.st_mode);
}
bool hv_islink(const char* path) {
#ifdef OS_WIN
return hv_isdir(path) && (GetFileAttributes(path) & FILE_ATTRIBUTE_REPARSE_POINT);
#else
if (access(path, 0) != 0) return false;
struct stat st;
memset(&st, 0, sizeof(st));
lstat(path, &st);
return S_ISLNK(st.st_mode);
#endif
}
size_t hv_filesize(const char* filepath) {
struct stat st;
memset(&st, 0, sizeof(st));
stat(filepath, &st);
return st.st_size;
}
char* get_executable_path(char* buf, int size) {
#ifdef OS_WIN
GetModuleFileName(NULL, buf, size);
#elif defined(OS_LINUX)
if (readlink("/proc/self/exe", buf, size) == -1) {
return NULL;
}
#elif defined(OS_DARWIN)
_NSGetExecutablePath(buf, (uint32_t*)&size);
#endif
return buf;
}
char* get_executable_dir(char* buf, int size) {
char filepath[MAX_PATH] = {0};
get_executable_path(filepath, sizeof(filepath));
char* pos = hv_strrchr_dir(filepath);
if (pos) {
*pos = '\0';
strncpy(buf, filepath, size);
}
return buf;
}
char* get_executable_file(char* buf, int size) {
char filepath[MAX_PATH] = {0};
get_executable_path(filepath, sizeof(filepath));
char* pos = hv_strrchr_dir(filepath);
if (pos) {
strncpy(buf, pos+1, size);
}
return buf;
}
char* get_run_dir(char* buf, int size) {
return getcwd(buf, size);
}
int hv_rand(int min, int max) {
static int s_seed = 0;
assert(max > min);
if (s_seed == 0) {
s_seed = time(NULL);
srand(s_seed);
}
int _rand = rand();
_rand = min + (int) ((double) ((double) (max) - (min) + 1.0) * ((_rand) / ((RAND_MAX) + 1.0)));
return _rand;
}
char* hv_random_string(char *buf, int len) {
static char s_characters[] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
};
int i = 0;
for (; i < len; i++) {
buf[i] = s_characters[hv_rand(0, sizeof(s_characters) - 1)];
}
buf[i] = '\0';
return buf;
}
bool hv_getboolean(const char* str) {
if (str == NULL) return false;
int len = strlen(str);
if (len == 0) return false;
switch (len) {
case 1: return *str == '1' || *str == 'y' || *str == 'Y';
case 2: return stricmp(str, "on") == 0;
case 3: return stricmp(str, "yes") == 0;
case 4: return stricmp(str, "true") == 0;
case 6: return stricmp(str, "enable") == 0;
default: return false;
}
}
size_t hv_parse_size(const char* str) {
size_t size = 0, n = 0;
const char* p = str;
char c;
while ((c = *p) != '\0') {
if (c >= '0' && c <= '9') {
n = n * 10 + c - '0';
} else {
switch (c) {
case 'K': case 'k': n <<= 10; break;
case 'M': case 'm': n <<= 20; break;
case 'G': case 'g': n <<= 30; break;
case 'T': case 't': n <<= 40; break;
default: break;
}
size += n;
n = 0;
}
++p;
}
return size + n;
}
time_t hv_parse_time(const char* str) {
time_t time = 0, n = 0;
const char* p = str;
char c;
while ((c = *p) != '\0') {
if (c >= '0' && c <= '9') {
n = n * 10 + c - '0';
} else {
switch (c) {
case 's': break;
case 'm': n *= 60; break;
case 'h': n *= 60 * 60; break;
case 'd': n *= 24 * 60 * 60; break;
case 'w': n *= 7 * 24 * 60 * 60; break;
default: break;
}
time += n;
n = 0;
}
++p;
}
return time + n;
}
int hv_parse_url(hurl_t* stURL, const char* strURL) {
if (stURL == NULL || strURL == NULL) return -1;
memset(stURL, 0, sizeof(hurl_t));
const char* begin = strURL;
const char* end = strURL;
while (*end != '\0') ++end;
if (end - begin > 65535) return -2;
// scheme://
const char* sp = strURL;
const char* ep = strstr(sp, "://");
if (ep) {
// stURL->fields[HV_URL_SCHEME].off = sp - begin;
stURL->fields[HV_URL_SCHEME].len = ep - sp;
sp = ep + 3;
}
// user:pswd@host:port
ep = strchr(sp, '/');
if (ep == NULL) ep = end;
const char* user = sp;
const char* host = sp;
const char* pos = hv_strnchr(sp, '@', ep - sp);
if (pos) {
// user:pswd
const char* pswd = hv_strnchr(user, ':', pos - user);
if (pswd) {
stURL->fields[HV_URL_PASSWORD].off = pswd + 1 - begin;
stURL->fields[HV_URL_PASSWORD].len = pos - pswd - 1;
} else {
pswd = pos;
}
stURL->fields[HV_URL_USERNAME].off = user - begin;
stURL->fields[HV_URL_USERNAME].len = pswd - user;
// @
host = pos + 1;
}
// port
const char* port = hv_strnchr(host, ':', ep - host);
if (port) {
stURL->fields[HV_URL_PORT].off = port + 1 - begin;
stURL->fields[HV_URL_PORT].len = ep - port - 1;
// atoi
for (unsigned short i = 1; i <= stURL->fields[HV_URL_PORT].len; ++i) {
stURL->port = stURL->port * 10 + (port[i] - '0');
}
} else {
port = ep;
// set default port
stURL->port = 80;
if (stURL->fields[HV_URL_SCHEME].len > 0) {
if (strncmp(strURL, "https://", 8) == 0) {
stURL->port = 443;
}
}
}
// host
stURL->fields[HV_URL_HOST].off = host - begin;
stURL->fields[HV_URL_HOST].len = port - host;
if (ep == end) return 0;
// /path
sp = ep;
ep = strchr(sp, '?');
if (ep == NULL) ep = end;
stURL->fields[HV_URL_PATH].off = sp - begin;
stURL->fields[HV_URL_PATH].len = ep - sp;
if (ep == end) return 0;
// ?query
sp = ep + 1;
ep = strchr(sp, '#');
if (ep == NULL) ep = end;
stURL->fields[HV_URL_QUERY].off = sp - begin;
stURL->fields[HV_URL_QUERY].len = ep - sp;
if (ep == end) return 0;
// #fragment
sp = ep + 1;
ep = end;
stURL->fields[HV_URL_FRAGMENT].off = sp - begin;
stURL->fields[HV_URL_FRAGMENT].len = ep - sp;
return 0;
}

144
external/libhv/libhv-1.3.2/base/hbase.h vendored Normal file
View File

@ -0,0 +1,144 @@
#ifndef HV_BASE_H_
#define HV_BASE_H_
#include "hexport.h"
#include "hplatform.h" // for bool
#include "hdef.h" // for printd
BEGIN_EXTERN_C
//--------------------alloc/free---------------------------
HV_EXPORT void* hv_malloc(size_t size);
HV_EXPORT void* hv_realloc(void* oldptr, size_t newsize, size_t oldsize);
HV_EXPORT void* hv_calloc(size_t nmemb, size_t size);
HV_EXPORT void* hv_zalloc(size_t size);
HV_EXPORT void hv_free(void* ptr);
#define HV_ALLOC(ptr, size)\
do {\
*(void**)&(ptr) = hv_zalloc(size);\
printd("alloc(%p, size=%llu)\tat [%s:%d:%s]\n", ptr, (unsigned long long)size, __FILE__, __LINE__, __FUNCTION__);\
} while(0)
#define HV_ALLOC_SIZEOF(ptr) HV_ALLOC(ptr, sizeof(*(ptr)))
#define HV_FREE(ptr)\
do {\
if (ptr) {\
hv_free(ptr);\
printd("free( %p )\tat [%s:%d:%s]\n", ptr, __FILE__, __LINE__, __FUNCTION__);\
ptr = NULL;\
}\
} while(0)
#define STACK_OR_HEAP_ALLOC(ptr, size, stack_size)\
unsigned char _stackbuf_[stack_size] = { 0 };\
if ((size) > (stack_size)) {\
HV_ALLOC(ptr, size);\
} else {\
*(unsigned char**)&(ptr) = _stackbuf_;\
}
#define STACK_OR_HEAP_FREE(ptr)\
if ((unsigned char*)(ptr) != _stackbuf_) {\
HV_FREE(ptr);\
}
#define HV_DEFAULT_STACKBUF_SIZE 1024
#define HV_STACK_ALLOC(ptr, size) STACK_OR_HEAP_ALLOC(ptr, size, HV_DEFAULT_STACKBUF_SIZE)
#define HV_STACK_FREE(ptr) STACK_OR_HEAP_FREE(ptr)
HV_EXPORT long hv_alloc_cnt();
HV_EXPORT long hv_free_cnt();
HV_INLINE void hv_memcheck(void) {
printf("Memcheck => alloc:%ld free:%ld\n", hv_alloc_cnt(), hv_free_cnt());
}
#define HV_MEMCHECK atexit(hv_memcheck);
//--------------------string-------------------------------
HV_EXPORT char* hv_strupper(char* str);
HV_EXPORT char* hv_strlower(char* str);
HV_EXPORT char* hv_strreverse(char* str);
HV_EXPORT bool hv_strstartswith(const char* str, const char* start);
HV_EXPORT bool hv_strendswith(const char* str, const char* end);
HV_EXPORT bool hv_strcontains(const char* str, const char* sub);
HV_EXPORT bool hv_wildcard_match(const char* str, const char* pattern);
// strncpy n = sizeof(dest_buf)-1
// hv_strncpy n = sizeof(dest_buf)
HV_EXPORT char* hv_strncpy(char* dest, const char* src, size_t n);
// strncat n = sizeof(dest_buf)-1-strlen(dest)
// hv_strncpy n = sizeof(dest_buf)
HV_EXPORT char* hv_strncat(char* dest, const char* src, size_t n);
#if !HAVE_STRLCPY
#define strlcpy hv_strncpy
#endif
#if !HAVE_STRLCAT
#define strlcat hv_strncat
#endif
HV_EXPORT char* hv_strnchr(const char* s, char c, size_t n);
#define hv_strrchr_dot(str) strrchr(str, '.')
HV_EXPORT char* hv_strrchr_dir(const char* filepath);
// basename
HV_EXPORT const char* hv_basename(const char* filepath);
HV_EXPORT const char* hv_suffixname(const char* filename);
// mkdir -p
HV_EXPORT int hv_mkdir_p(const char* dir);
// rmdir -p
HV_EXPORT int hv_rmdir_p(const char* dir);
// path
HV_EXPORT bool hv_exists(const char* path);
HV_EXPORT bool hv_isdir(const char* path);
HV_EXPORT bool hv_isfile(const char* path);
HV_EXPORT bool hv_islink(const char* path);
HV_EXPORT size_t hv_filesize(const char* filepath);
HV_EXPORT char* get_executable_path(char* buf, int size);
HV_EXPORT char* get_executable_dir(char* buf, int size);
HV_EXPORT char* get_executable_file(char* buf, int size);
HV_EXPORT char* get_run_dir(char* buf, int size);
// random
HV_EXPORT int hv_rand(int min, int max);
HV_EXPORT char* hv_random_string(char *buf, int len);
// 1 y on yes true enable => true
HV_EXPORT bool hv_getboolean(const char* str);
// 1T2G3M4K5B => ?B
HV_EXPORT size_t hv_parse_size(const char* str);
// 1w2d3h4m5s => ?s
HV_EXPORT time_t hv_parse_time(const char* str);
// scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment]
typedef enum {
HV_URL_SCHEME,
HV_URL_USERNAME,
HV_URL_PASSWORD,
HV_URL_HOST,
HV_URL_PORT,
HV_URL_PATH,
HV_URL_QUERY,
HV_URL_FRAGMENT,
HV_URL_FIELD_NUM,
} hurl_field_e;
typedef struct hurl_s {
struct {
unsigned short off;
unsigned short len;
} fields[HV_URL_FIELD_NUM];
unsigned short port;
} hurl_t;
HV_EXPORT int hv_parse_url(hurl_t* stURL, const char* strURL);
END_EXTERN_C
#endif // HV_BASE_H_

257
external/libhv/libhv-1.3.2/base/hbuf.h vendored Normal file
View File

@ -0,0 +1,257 @@
#ifndef HV_BUF_H_
#define HV_BUF_H_
#include "hdef.h" // for MAX
#include "hbase.h" // for HV_ALLOC, HV_FREE
typedef struct hbuf_s {
char* base;
size_t len;
#ifdef __cplusplus
hbuf_s() {
base = NULL;
len = 0;
}
hbuf_s(void* data, size_t len) {
this->base = (char*)data;
this->len = len;
}
#endif
} hbuf_t;
typedef struct offset_buf_s {
char* base;
size_t len;
size_t offset;
#ifdef __cplusplus
offset_buf_s() {
base = NULL;
len = 0;
offset = 0;
}
offset_buf_s(void* data, size_t len) {
this->base = (char*)data;
this->len = len;
offset = 0;
}
#endif
} offset_buf_t;
typedef struct fifo_buf_s {
char* base;
size_t len;
size_t head;
size_t tail;
#ifdef __cplusplus
fifo_buf_s() {
base = NULL;
len = 0;
head = tail = 0;
}
fifo_buf_s(void* data, size_t len) {
this->base = (char*)data;
this->len = len;
head = tail = 0;
}
#endif
} fifo_buf_t;
#ifdef __cplusplus
class HBuf : public hbuf_t {
public:
HBuf() : hbuf_t() {
cleanup_ = false;
}
HBuf(void* data, size_t len) : hbuf_t(data, len) {
cleanup_ = false;
}
HBuf(size_t cap) { resize(cap); }
virtual ~HBuf() {
cleanup();
}
void* data() { return base; }
size_t size() { return len; }
bool isNull() { return base == NULL || len == 0; }
void cleanup() {
if (cleanup_) {
HV_FREE(base);
len = 0;
cleanup_ = false;
}
}
void resize(size_t cap) {
if (cap == len) return;
if (base == NULL) {
HV_ALLOC(base, cap);
}
else {
base = (char*)hv_realloc(base, cap, len);
}
len = cap;
cleanup_ = true;
}
void copy(void* data, size_t len) {
resize(len);
memcpy(base, data, len);
}
void copy(hbuf_t* buf) {
copy(buf->base, buf->len);
}
private:
bool cleanup_;
};
// VL: Variable-Length
class HVLBuf : public HBuf {
public:
HVLBuf() : HBuf() {_offset = _size = 0;}
HVLBuf(void* data, size_t len) : HBuf(data, len) {_offset = 0; _size = len;}
HVLBuf(size_t cap) : HBuf(cap) {_offset = _size = 0;}
virtual ~HVLBuf() {}
char* data() { return base + _offset; }
size_t size() { return _size; }
void push_front(void* ptr, size_t len) {
if (len > this->len - _size) {
size_t newsize = MAX(this->len, len)*2;
resize(newsize);
}
if (_offset < len) {
// move => end
memmove(base+this->len-_size, data(), _size);
_offset = this->len-_size;
}
memcpy(data()-len, ptr, len);
_offset -= len;
_size += len;
}
void push_back(void* ptr, size_t len) {
if (len > this->len - _size) {
size_t newsize = MAX(this->len, len)*2;
resize(newsize);
}
else if (len > this->len - _offset - _size) {
// move => start
memmove(base, data(), _size);
_offset = 0;
}
memcpy(data()+_size, ptr, len);
_size += len;
}
void pop_front(void* ptr, size_t len) {
if (len <= _size) {
if (ptr) {
memcpy(ptr, data(), len);
}
_offset += len;
if (_offset >= this->len) _offset = 0;
_size -= len;
}
}
void pop_back(void* ptr, size_t len) {
if (len <= _size) {
if (ptr) {
memcpy(ptr, data()+_size-len, len);
}
_size -= len;
}
}
void clear() {
_offset = _size = 0;
}
void prepend(void* ptr, size_t len) {
push_front(ptr, len);
}
void append(void* ptr, size_t len) {
push_back(ptr, len);
}
void insert(void* ptr, size_t len) {
push_back(ptr, len);
}
void remove(size_t len) {
pop_front(NULL, len);
}
private:
size_t _offset;
size_t _size;
};
class HRingBuf : public HBuf {
public:
HRingBuf() : HBuf() {_head = _tail = _size = 0;}
HRingBuf(size_t cap) : HBuf(cap) {_head = _tail = _size = 0;}
virtual ~HRingBuf() {}
char* alloc(size_t len) {
char* ret = NULL;
if (_head < _tail || _size == 0) {
// [_tail, this->len) && [0, _head)
if (this->len - _tail >= len) {
ret = base + _tail;
_tail += len;
if (_tail == this->len) _tail = 0;
}
else if (_head >= len) {
ret = base;
_tail = len;
}
}
else {
// [_tail, _head)
if (_head - _tail >= len) {
ret = base + _tail;
_tail += len;
}
}
_size += ret ? len : 0;
return ret;
}
void free(size_t len) {
_size -= len;
if (len <= this->len - _head) {
_head += len;
if (_head == this->len) _head = 0;
}
else {
_head = len;
}
}
void clear() {_head = _tail = _size = 0;}
size_t size() {return _size;}
private:
size_t _head;
size_t _tail;
size_t _size;
};
#endif
#endif // HV_BUF_H_

271
external/libhv/libhv-1.3.2/base/hdef.h vendored Normal file
View File

@ -0,0 +1,271 @@
#ifndef HV_DEF_H_
#define HV_DEF_H_
#include "hplatform.h"
#ifndef ABS
#define ABS(n) ((n) > 0 ? (n) : -(n))
#endif
#ifndef NABS
#define NABS(n) ((n) < 0 ? (n) : -(n))
#endif
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
#endif
#ifndef BITSET
#define BITSET(p, n) (*(p) |= (1u << (n)))
#endif
#ifndef BITCLR
#define BITCLR(p, n) (*(p) &= ~(1u << (n)))
#endif
#ifndef BITGET
#define BITGET(i, n) ((i) & (1u << (n)))
#endif
/*
#ifndef CR
#define CR '\r'
#endif
#ifndef LF
#define LF '\n'
#endif
#ifndef CRLF
#define CRLF "\r\n"
#endif
*/
#define FLOAT_PRECISION 1e-6
#define FLOAT_EQUAL_ZERO(f) (ABS(f) < FLOAT_PRECISION)
#ifndef INFINITE
#define INFINITE (uint32_t)-1
#endif
/*
ASCII:
[0, 0x20) control-charaters
[0x20, 0x7F) printable-charaters
0x0A => LF
0x0D => CR
0x20 => SPACE
0x7F => DEL
[0x09, 0x0D] => \t\n\v\f\r
[0x30, 0x39] => 0~9
[0x41, 0x5A] => A~Z
[0x61, 0x7A] => a~z
*/
#ifndef IS_ALPHA
#define IS_ALPHA(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z'))
#endif
// NOTE: IS_NUM conflicts with mysql.h
#ifndef IS_DIGIT
#define IS_DIGIT(c) ((c) >= '0' && (c) <= '9')
#endif
#ifndef IS_ALPHANUM
#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_DIGIT(c))
#endif
#ifndef IS_CNTRL
#define IS_CNTRL(c) ((c) >= 0 && (c) < 0x20)
#endif
#ifndef IS_GRAPH
#define IS_GRAPH(c) ((c) >= 0x20 && (c) < 0x7F)
#endif
#ifndef IS_HEX
#define IS_HEX(c) (IS_DIGIT(c) || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F'))
#endif
#ifndef IS_LOWER
#define IS_LOWER(c) (((c) >= 'a' && (c) <= 'z'))
#endif
#ifndef IS_UPPER
#define IS_UPPER(c) (((c) >= 'A' && (c) <= 'Z'))
#endif
#ifndef LOWER
#define LOWER(c) ((c) | 0x20)
#endif
#ifndef UPPER
#define UPPER(c) ((c) & ~0x20)
#endif
// LD, LU, LLD, LLU for explicit conversion of integer
// #ifndef LD
// #define LD(v) ((long)(v))
// #endif
// #ifndef LU
// #define LU(v) ((unsigned long)(v))
// #endif
#ifndef LLD
#define LLD(v) ((long long)(v))
#endif
#ifndef LLU
#define LLU(v) ((unsigned long long)(v))
#endif
#ifndef _WIN32
// MAKEWORD, HIBYTE, LOBYTE
#ifndef MAKEWORD
#define MAKEWORD(h, l) ( (((WORD)h) << 8) | (l & 0xff) )
#endif
#ifndef HIBYTE
#define HIBYTE(w) ( (BYTE)(((WORD)w) >> 8) )
#endif
#ifndef LOBYTE
#define LOBYTE(w) ( (BYTE)(w & 0xff) )
#endif
// MAKELONG, HIWORD, LOWORD
#ifndef MAKELONG
#define MAKELONG(h, l) ( ((int32_t)h) << 16 | (l & 0xffff) )
#endif
#ifndef HIWORD
#define HIWORD(n) ( (WORD)(((int32_t)n) >> 16) )
#endif
#ifndef LOWORD
#define LOWORD(n) ( (WORD)(n & 0xffff) )
#endif
#endif // _WIN32
// MAKEINT64, HIINT, LOINT
#ifndef MAKEINT64
#define MAKEINT64(h, l) ( ((int64_t)h) << 32 | (l & 0xffffffff) )
#endif
#ifndef HIINT
#define HIINT(n) ( (int32_t)(((int64_t)n) >> 32) )
#endif
#ifndef LOINT
#define LOINT(n) ( (int32_t)(n & 0xffffffff) )
#endif
#ifndef MAKE_FOURCC
#define MAKE_FOURCC(a, b, c, d) \
( ((uint32)d) | ( ((uint32)c) << 8 ) | ( ((uint32)b) << 16 ) | ( ((uint32)a) << 24 ) )
#endif
#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
#ifndef LIMIT
#define LIMIT(lower, v, upper) ((v) < (lower) ? (lower) : (v) > (upper) ? (upper) : (v))
#endif
#ifndef MAX_PATH
#define MAX_PATH 260
#endif
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void*)0)
#endif
#endif
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef SAFE_ALLOC
#define SAFE_ALLOC(p, size)\
do {\
void* ptr = malloc(size);\
if (!ptr) {\
fprintf(stderr, "malloc failed!\n");\
exit(-1);\
}\
memset(ptr, 0, size);\
*(void**)&(p) = ptr;\
} while(0)
#endif
#ifndef SAFE_FREE
#define SAFE_FREE(p) do {if (p) {free(p); (p) = NULL;}} while(0)
#endif
#ifndef SAFE_DELETE
#define SAFE_DELETE(p) do {if (p) {delete (p); (p) = NULL;}} while(0)
#endif
#ifndef SAFE_DELETE_ARRAY
#define SAFE_DELETE_ARRAY(p) do {if (p) {delete[] (p); (p) = NULL;}} while(0)
#endif
#ifndef SAFE_RELEASE
#define SAFE_RELEASE(p) do {if (p) {(p)->release(); (p) = NULL;}} while(0)
#endif
#ifndef SAFE_CLOSE
#define SAFE_CLOSE(fd) do {if ((fd) >= 0) {close(fd); (fd) = -1;}} while(0)
#endif
#define STRINGIFY(x) STRINGIFY_HELPER(x)
#define STRINGIFY_HELPER(x) #x
#define STRINGCAT(x, y) STRINGCAT_HELPER(x, y)
#define STRINGCAT_HELPER(x, y) x##y
#ifndef offsetof
#define offsetof(type, member) \
((size_t)(&((type*)0)->member))
#endif
#ifndef offsetofend
#define offsetofend(type, member) \
(offsetof(type, member) + sizeof(((type*)0)->member))
#endif
#ifndef container_of
#define container_of(ptr, type, member) \
((type*)((char*)(ptr) - offsetof(type, member)))
#endif
#ifdef PRINT_DEBUG
#define printd(...) printf(__VA_ARGS__)
#else
#define printd(...)
#endif
#ifdef PRINT_ERROR
#define printe(...) fprintf(stderr, __VA_ARGS__)
#else
#define printe(...)
#endif
#endif // HV_DEF_H_

172
external/libhv/libhv-1.3.2/base/heap.h vendored Normal file
View File

@ -0,0 +1,172 @@
#ifndef HV_HEAP_H_
#define HV_HEAP_H_
#include <assert.h> // for assert
#include <stddef.h> // for NULL
struct heap_node {
struct heap_node* parent;
struct heap_node* left;
struct heap_node* right;
};
typedef int (*heap_compare_fn)(const struct heap_node* lhs, const struct heap_node* rhs);
struct heap {
struct heap_node* root;
int nelts;
// if compare is less_than, root is min of heap
// if compare is larger_than, root is max of heap
heap_compare_fn compare;
};
static inline void heap_init(struct heap* heap, heap_compare_fn fn) {
heap->root = NULL;
heap->nelts = 0;
heap->compare = fn;
}
// replace s with r
static inline void heap_replace(struct heap* heap, struct heap_node* s, struct heap_node* r) {
// s->parent->child, s->left->parent, s->right->parent
if (s->parent == NULL) heap->root = r;
else if (s->parent->left == s) s->parent->left = r;
else if (s->parent->right == s) s->parent->right = r;
if (s->left) s->left->parent = r;
if (s->right) s->right->parent = r;
if (r) {
//*r = *s;
r->parent = s->parent;
r->left = s->left;
r->right = s->right;
}
}
static inline void heap_swap(struct heap* heap, struct heap_node* parent, struct heap_node* child) {
assert(child->parent == parent && (parent->left == child || parent->right == child));
struct heap_node* pparent = parent->parent;
struct heap_node* lchild = child->left;
struct heap_node* rchild = child->right;
struct heap_node* sibling = NULL;
if (pparent == NULL) heap->root = child;
else if (pparent->left == parent) pparent->left = child;
else if (pparent->right == parent) pparent->right = child;
if (lchild) lchild->parent = parent;
if (rchild) rchild->parent = parent;
child->parent = pparent;
if (parent->left == child) {
sibling = parent->right;
child->left = parent;
child->right = sibling;
} else {
sibling = parent->left;
child->left = sibling;
child->right = parent;
}
if (sibling) sibling->parent = child;
parent->parent = child;
parent->left = lchild;
parent->right = rchild;
}
static inline void heap_insert(struct heap* heap, struct heap_node* node) {
// get last => insert node => sift up
// 0: left, 1: right
int path = 0;
int n,d;
++heap->nelts;
// traverse from bottom to up, get path of last node
for (d = 0, n = heap->nelts; n >= 2; ++d, n>>=1) {
path = (path << 1) | (n & 1);
}
// get last->parent by path
struct heap_node* parent = heap->root;
while(d > 1) {
parent = (path & 1) ? parent->right : parent->left;
--d;
path >>= 1;
}
// insert node
node->parent = parent;
if (parent == NULL) heap->root = node;
else if (path & 1) parent->right = node;
else parent->left = node;
// sift up
if (heap->compare) {
while (node->parent && heap->compare(node, node->parent)) {
heap_swap(heap, node->parent, node);
}
}
}
static inline void heap_remove(struct heap* heap, struct heap_node* node) {
if (heap->nelts == 0) return;
// get last => replace node with last => sift down and sift up
// 0: left, 1: right
int path = 0;
int n,d;
// traverse from bottom to up, get path of last node
for (d = 0, n = heap->nelts; n >= 2; ++d, n>>=1) {
path = (path << 1) | (n & 1);
}
--heap->nelts;
// get last->parent by path
struct heap_node* parent = heap->root;
while(d > 1) {
parent = (path & 1) ? parent->right : parent->left;
--d;
path >>= 1;
}
// replace node with last
struct heap_node* last = NULL;
if (parent == NULL) {
return;
}
else if (path & 1) {
last = parent->right;
parent->right = NULL;
}
else {
last = parent->left;
parent->left = NULL;
}
if (last == NULL) {
if (heap->root == node) {
heap->root = NULL;
}
return;
}
heap_replace(heap, node, last);
node->parent = node->left = node->right = NULL;
if (!heap->compare) return;
struct heap_node* v = last;
struct heap_node* est = NULL;
// sift down
while (1) {
est = v;
if (v->left) est = heap->compare(est, v->left) ? est : v->left;
if (v->right) est = heap->compare(est, v->right) ? est : v->right;
if (est == v) break;
heap_swap(heap, v, est);
}
// sift up
while (v->parent && heap->compare(v, v->parent)) {
heap_swap(heap, v->parent, v);
}
}
static inline void heap_dequeue(struct heap* heap) {
heap_remove(heap, heap->root);
}
#endif // HV_HEAP_H_

View File

@ -0,0 +1,244 @@
#ifndef HV_ENDIAN_H_
#define HV_ENDIAN_H_
#include "hplatform.h"
#if defined(OS_MAC)
#include <libkern/OSByteOrder.h>
#define htobe16(v) OSSwapHostToBigInt16(v)
#define htobe32(v) OSSwapHostToBigInt32(v)
#define htobe64(v) OSSwapHostToBigInt64(v)
#define be16toh(v) OSSwapBigToHostInt16(v)
#define be32toh(v) OSSwapBigToHostInt32(v)
#define be64toh(v) OSSwapBigToHostInt64(v)
#define htole16(v) OSSwapHostToLittleInt16(v)
#define htole32(v) OSSwapHostToLittleInt32(v)
#define htole64(v) OSSwapHostToLittleInt64(v)
#define le16toh(v) OSSwapLittleToHostInt16(v)
#define le32toh(v) OSSwapLittleToHostInt32(v)
#define le64toh(v) OSSwapLittleToHostInt64(v)
#elif defined(OS_WIN)
#if _WIN32_WINNT < _WIN32_WINNT_WIN8
/*
* Byte order conversion functions for 64-bit integers and 32 + 64 bit
* floating-point numbers. IEEE big-endian format is used for the
* network floating point format.
*/
#define _WS2_32_WINSOCK_SWAP_LONG(l) \
( ( ((l) >> 24) & 0x000000FFL ) | \
( ((l) >> 8) & 0x0000FF00L ) | \
( ((l) << 8) & 0x00FF0000L ) | \
( ((l) << 24) & 0xFF000000L ) )
#define _WS2_32_WINSOCK_SWAP_LONGLONG(l) \
( ( ((l) >> 56) & 0x00000000000000FFLL ) | \
( ((l) >> 40) & 0x000000000000FF00LL ) | \
( ((l) >> 24) & 0x0000000000FF0000LL ) | \
( ((l) >> 8) & 0x00000000FF000000LL ) | \
( ((l) << 8) & 0x000000FF00000000LL ) | \
( ((l) << 24) & 0x0000FF0000000000LL ) | \
( ((l) << 40) & 0x00FF000000000000LL ) | \
( ((l) << 56) & 0xFF00000000000000LL ) )
#ifndef htonll
__inline unsigned __int64 htonll ( unsigned __int64 Value )
{
const unsigned __int64 Retval = _WS2_32_WINSOCK_SWAP_LONGLONG (Value);
return Retval;
}
#endif /* htonll */
#ifndef ntohll
__inline unsigned __int64 ntohll ( unsigned __int64 Value )
{
const unsigned __int64 Retval = _WS2_32_WINSOCK_SWAP_LONGLONG (Value);
return Retval;
}
#endif /* ntohll */
#ifndef htonf
__inline unsigned __int32 htonf ( float Value )
{
unsigned __int32 Tempval;
unsigned __int32 Retval;
Tempval = *(unsigned __int32*)(&Value);
Retval = _WS2_32_WINSOCK_SWAP_LONG (Tempval);
return Retval;
}
#endif /* htonf */
#ifndef ntohf
__inline float ntohf ( unsigned __int32 Value )
{
const unsigned __int32 Tempval = _WS2_32_WINSOCK_SWAP_LONG (Value);
float Retval;
*((unsigned __int32*)&Retval) = Tempval;
return Retval;
}
#endif /* ntohf */
#ifndef htond
__inline unsigned __int64 htond ( double Value )
{
unsigned __int64 Tempval;
unsigned __int64 Retval;
Tempval = *(unsigned __int64*)(&Value);
Retval = _WS2_32_WINSOCK_SWAP_LONGLONG (Tempval);
return Retval;
}
#endif /* htond */
#ifndef ntohd
__inline double ntohd ( unsigned __int64 Value )
{
const unsigned __int64 Tempval = _WS2_32_WINSOCK_SWAP_LONGLONG (Value);
double Retval;
*((unsigned __int64*)&Retval) = Tempval;
return Retval;
}
#endif /* ntohd */
#endif
#define htobe16(v) htons(v)
#define htobe32(v) htonl(v)
#define htobe64(v) htonll(v)
#define be16toh(v) ntohs(v)
#define be32toh(v) ntohl(v)
#define be64toh(v) ntohll(v)
#if (BYTE_ORDER == LITTLE_ENDIAN)
#define htole16(v) (v)
#define htole32(v) (v)
#define htole64(v) (v)
#define le16toh(v) (v)
#define le32toh(v) (v)
#define le64toh(v) (v)
#elif (BYTE_ORDER == BIG_ENDIAN)
#define htole16(v) __builtin_bswap16(v)
#define htole32(v) __builtin_bswap32(v)
#define htole64(v) __builtin_bswap64(v)
#define le16toh(v) __builtin_bswap16(v)
#define le32toh(v) __builtin_bswap32(v)
#define le64toh(v) __builtin_bswap64(v)
#endif
#elif HAVE_ENDIAN_H
#include <endian.h>
#elif HAVE_SYS_ENDIAN_H
#include <sys/endian.h>
#else
#warning "Not found endian.h!"
#endif
#define PI8(p) *(int8_t*)(p)
#define PI16(p) *(int16_t*)(p)
#define PI32(p) *(int32_t*)(p)
#define PI64(p) *(int64_t*)(p)
#define PU8(p) *(uint8_t*)(p)
#define PU16(p) *(uint16_t*)(p)
#define PU32(p) *(uint32_t*)(p)
#define PU64(p) *(uint64_t*)(p)
#define PF32(p) *(float*)(p)
#define PF64(p) *(double*)(p)
#define GET_BE16(p) be16toh(PU16(p))
#define GET_BE32(p) be32toh(PU32(p))
#define GET_BE64(p) be64toh(PU64(p))
#define GET_LE16(p) le16toh(PU16(p))
#define GET_LE32(p) le32toh(PU32(p))
#define GET_LE64(p) le64toh(PU64(p))
#define PUT_BE16(p, v) PU16(p) = htobe16(v)
#define PUT_BE32(p, v) PU32(p) = htobe32(v)
#define PUT_BE64(p, v) PU64(p) = htobe64(v)
#define PUT_LE16(p, v) PU16(p) = htole16(v)
#define PUT_LE32(p, v) PU32(p) = htole32(v)
#define PUT_LE64(p, v) PU64(p) = htole64(v)
// NOTE: uint8_t* p = (uint8_t*)buf;
#define POP_BE8(p, v) v = *p; ++p
#define POP_BE16(p, v) v = be16toh(PU16(p)); p += 2
#define POP_BE32(p, v) v = be32toh(PU32(p)); p += 4
#define POP_BE64(p, v) v = be64toh(PU64(p)); p += 8
#define POP_LE8(p, v) v= *p; ++p
#define POP_LE16(p, v) v = le16toh(PU16(p)); p += 2
#define POP_LE32(p, v) v = le32toh(PU32(p)); p += 4
#define POP_LE64(p, v) v = le64toh(PU64(p)); p += 8
#define PUSH_BE8(p, v) *p = v; ++p
#define PUSH_BE16(p, v) PU16(p) = htobe16(v); p += 2
#define PUSH_BE32(p, v) PU32(p) = htobe32(v); p += 4
#define PUSH_BE64(p, v) PU64(p) = htobe64(v); p += 8
#define PUSH_LE8(p, v) *p = v; ++p
#define PUSH_LE16(p, v) PU16(p) = htole16(v); p += 2
#define PUSH_LE32(p, v) PU32(p) = htole32(v); p += 4
#define PUSH_LE64(p, v) PU64(p) = htole64(v); p += 8
// NOTE: NET_ENDIAN = BIG_ENDIAN
#define POP8(p, v) POP_BE8(p, v)
#define POP16(p, v) POP_BE16(p, v)
#define POP32(p, v) POP_BE32(p, v)
#define POP64(p, v) POP_BE64(p, v)
#define POP_N(p, v, n) memcpy(v, p, n); p += n
#define PUSH8(p, v) PUSH_BE8(p, v)
#define PUSH16(p, v) PUSH_BE16(p, v)
#define PUSH32(p, v) PUSH_BE32(p, v)
#define PUSH64(p, v) PUSH_BE64(p, v)
#define PUSH_N(p, v, n) memcpy(p, v, n); p += n
static inline int detect_endian() {
union {
char c;
short s;
} u;
u.s = 0x1122;
return u.c ==0x11 ? BIG_ENDIAN : LITTLE_ENDIAN;
}
#ifdef __cplusplus
template <typename T>
uint8_t* serialize(uint8_t* buf, T value, int host_endian = LITTLE_ENDIAN, int buf_endian = BIG_ENDIAN) {
size_t size = sizeof(T);
uint8_t* pDst = buf;
uint8_t* pSrc = (uint8_t*)&value;
if (host_endian == buf_endian) {
memcpy(pDst, pSrc, size);
}
else {
for (int i = 0; i < size; ++i) {
pDst[i] = pSrc[size-i-1];
}
}
return buf+size;
}
template <typename T>
uint8_t* deserialize(uint8_t* buf, T* value, int host_endian = LITTLE_ENDIAN, int buf_endian = BIG_ENDIAN) {
size_t size = sizeof(T);
uint8_t* pSrc = buf;
uint8_t* pDst = (uint8_t*)value;
if (host_endian == buf_endian) {
memcpy(pDst, pSrc, size);
}
else {
for (int i = 0; i < size; ++i) {
pDst[i] = pSrc[size-i-1];
}
}
return buf+size;
}
#endif // __cplusplus
#endif // HV_ENDIAN_H_

19
external/libhv/libhv-1.3.2/base/herr.c vendored Normal file
View File

@ -0,0 +1,19 @@
#include "herr.h"
#include <string.h> // for strerror
// errcode => errmsg
const char* hv_strerror(int err) {
if (err > 0 && err <= SYS_NERR) {
return strerror(err);
}
switch (err) {
#define F(errcode, name, errmsg) \
case errcode: return errmsg;
FOREACH_ERR(F)
#undef F
default:
return "Undefined error";
}
}

122
external/libhv/libhv-1.3.2/base/herr.h vendored Normal file
View File

@ -0,0 +1,122 @@
#ifndef HV_ERR_H_
#define HV_ERR_H_
#include <errno.h>
#include "hexport.h"
#ifndef SYS_NERR
#define SYS_NERR 133
#endif
// F(errcode, name, errmsg)
// [1, 133]
#define FOREACH_ERR_SYS(F)
// [1xx~5xx]
#define FOREACH_ERR_STATUS(F)
// [1xxx]
#define FOREACH_ERR_COMMON(F) \
F(0, OK, "OK") \
F(1000, UNKNOWN, "Unknown error") \
\
F(1001, NULL_PARAM, "Null parameter") \
F(1002, NULL_POINTER, "Null pointer") \
F(1003, NULL_DATA, "Null data") \
F(1004, NULL_HANDLE, "Null handle") \
\
F(1011, INVALID_PARAM, "Invalid parameter")\
F(1012, INVALID_POINTER, "Invalid pointer") \
F(1013, INVALID_DATA, "Invalid data") \
F(1014, INVALID_HANDLE, "Invalid handle") \
F(1015, INVALID_JSON, "Invalid json") \
F(1016, INVALID_XML, "Invalid xml") \
F(1017, INVALID_FMT, "Invalid format") \
F(1018, INVALID_PROTOCOL, "Invalid protocol") \
F(1019, INVALID_PACKAGE, "Invalid package") \
\
F(1021, OUT_OF_RANGE, "Out of range") \
F(1022, OVER_LIMIT, "Over the limit") \
F(1023, MISMATCH, "Mismatch") \
F(1024, PARSE, "Parse failed") \
\
F(1030, OPEN_FILE, "Open file failed") \
F(1031, SAVE_FILE, "Save file failed") \
F(1032, READ_FILE, "Read file failed") \
F(1033, WRITE_FILE, "Write file failed")\
\
F(1040, SSL, "SSL/TLS error") \
F(1041, NEW_SSL_CTX, "New SSL_CTX failed") \
F(1042, NEW_SSL, "New SSL failed") \
F(1043, SSL_HANDSHAKE, "SSL handshake failed") \
\
F(1100, TASK_TIMEOUT, "Task timeout") \
F(1101, TASK_QUEUE_FULL, "Task queue full") \
F(1102, TASK_QUEUE_EMPTY, "Task queue empty") \
\
F(1400, REQUEST, "Bad request") \
F(1401, RESPONSE, "Bad response") \
// [-1xxx]
#define FOREACH_ERR_FUNC(F) \
F(-1001, MALLOC, "malloc() error") \
F(-1002, REALLOC, "realloc() error") \
F(-1003, CALLOC, "calloc() error") \
F(-1004, FREE, "free() error") \
\
F(-1011, SOCKET, "socket() error") \
F(-1012, BIND, "bind() error") \
F(-1013, LISTEN, "listen() error") \
F(-1014, ACCEPT, "accept() error") \
F(-1015, CONNECT, "connect() error") \
F(-1016, RECV, "recv() error") \
F(-1017, SEND, "send() error") \
F(-1018, RECVFROM, "recvfrom() error") \
F(-1019, SENDTO, "sendto() error") \
F(-1020, SETSOCKOPT, "setsockopt() error") \
F(-1021, GETSOCKOPT, "getsockopt() error") \
// grpc [4xxx]
#define FOREACH_ERR_GRPC(F) \
F(4000, GRPC_FIRST, "grpc no error") \
F(4001, GRPC_STATUS_CANCELLED, "grpc status: cancelled") \
F(4002, GRPC_STATUS_UNKNOWN, "grpc unknown error") \
F(4003, GRPC_STATUS_INVALID_ARGUMENT, "grpc status: invalid argument")\
F(4004, GRPC_STATUS_DEADLINE, "grpc status: deadline") \
F(4005, GRPC_STATUS_NOT_FOUND, "grpc status: not found") \
F(4006, GRPC_STATUS_ALREADY_EXISTS, "grpc status: already exists") \
F(4007, GRPC_STATUS_PERMISSION_DENIED, "grpc status: permission denied") \
F(4008, GRPC_STATUS_RESOURCE_EXHAUSTED, "grpc status: resource exhausted") \
F(4009, GRPC_STATUS_FAILED_PRECONDITION,"grpc status: failed precondition") \
F(4010, GRPC_STATUS_ABORTED, "grpc status: aborted") \
F(4011, GRPC_STATUS_OUT_OF_RANGE, "grpc status: out of range") \
F(4012, GRPC_STATUS_UNIMPLEMENTED, "grpc status: unimplemented") \
F(4013, GRPC_STATUS_INTERNAL, "grpc internal error") \
F(4014, GRPC_STATUS_UNAVAILABLE, "grpc service unavailable") \
F(4015, GRPC_STATUS_DATA_LOSS, "grpc status: data loss") \
#define FOREACH_ERR(F) \
FOREACH_ERR_COMMON(F) \
FOREACH_ERR_FUNC(F) \
FOREACH_ERR_GRPC(F) \
#undef ERR_OK // prevent conflict
enum {
#define F(errcode, name, errmsg) ERR_##name = errcode,
FOREACH_ERR(F)
#undef F
};
#ifdef __cplusplus
extern "C" {
#endif
// errcode => errmsg
HV_EXPORT const char* hv_strerror(int err);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // HV_ERR_H_

463
external/libhv/libhv-1.3.2/base/hlog.c vendored Normal file
View File

@ -0,0 +1,463 @@
#include "hlog.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>
//#include "hmutex.h"
#ifdef _WIN32
#pragma warning (disable: 4244) // conversion loss of data
#include <windows.h>
#define hmutex_t CRITICAL_SECTION
#define hmutex_init InitializeCriticalSection
#define hmutex_destroy DeleteCriticalSection
#define hmutex_lock EnterCriticalSection
#define hmutex_unlock LeaveCriticalSection
#else
#include <sys/time.h> // for gettimeofday
#include <pthread.h>
#define hmutex_t pthread_mutex_t
#define hmutex_init(mutex) pthread_mutex_init(mutex, NULL)
#define hmutex_destroy pthread_mutex_destroy
#define hmutex_lock pthread_mutex_lock
#define hmutex_unlock pthread_mutex_unlock
#endif
//#include "htime.h"
#define SECONDS_PER_HOUR 3600
#define SECONDS_PER_DAY 86400 // 24*3600
#define SECONDS_PER_WEEK 604800 // 7*24*3600;
static int s_gmtoff = 28800; // 8*3600
struct logger_s {
logger_handler handler;
unsigned int bufsize;
char* buf;
int level;
int enable_color;
char format[64];
// for file logger
char filepath[256];
unsigned long long max_filesize;
int remain_days;
int enable_fsync;
FILE* fp_;
char cur_logfile[256];
time_t last_logfile_ts;
int can_write_cnt;
hmutex_t mutex_; // thread-safe
};
static void logger_init(logger_t* logger) {
logger->handler = NULL;
logger->bufsize = DEFAULT_LOG_MAX_BUFSIZE;
logger->buf = (char*)malloc(logger->bufsize);
logger->level = DEFAULT_LOG_LEVEL;
logger->enable_color = 0;
// NOTE: format is faster 6% than snprintf
// logger->format[0] = '\0';
strncpy(logger->format, DEFAULT_LOG_FORMAT, sizeof(logger->format) - 1);
logger->fp_ = NULL;
logger->max_filesize = DEFAULT_LOG_MAX_FILESIZE;
logger->remain_days = DEFAULT_LOG_REMAIN_DAYS;
logger->enable_fsync = 1;
logger_set_file(logger, DEFAULT_LOG_FILE);
logger->last_logfile_ts = 0;
logger->can_write_cnt = -1;
hmutex_init(&logger->mutex_);
}
logger_t* logger_create() {
// init gmtoff here
time_t ts = time(NULL);
struct tm* local_tm = localtime(&ts);
int local_hour = local_tm->tm_hour;
struct tm* gmt_tm = gmtime(&ts);
int gmt_hour = gmt_tm->tm_hour;
s_gmtoff = (local_hour - gmt_hour) * SECONDS_PER_HOUR;
logger_t* logger = (logger_t*)malloc(sizeof(logger_t));
logger_init(logger);
return logger;
}
void logger_destroy(logger_t* logger) {
if (logger) {
if (logger->buf) {
free(logger->buf);
logger->buf = NULL;
}
if (logger->fp_) {
fclose(logger->fp_);
logger->fp_ = NULL;
}
hmutex_destroy(&logger->mutex_);
free(logger);
}
}
void logger_set_handler(logger_t* logger, logger_handler fn) {
logger->handler = fn;
}
void logger_set_level(logger_t* logger, int level) {
logger->level = level;
}
void logger_set_level_by_str(logger_t* logger, const char* szLoglevel) {
int loglevel = DEFAULT_LOG_LEVEL;
if (strcmp(szLoglevel, "VERBOSE") == 0) {
loglevel = LOG_LEVEL_VERBOSE;
} else if (strcmp(szLoglevel, "DEBUG") == 0) {
loglevel = LOG_LEVEL_DEBUG;
} else if (strcmp(szLoglevel, "INFO") == 0) {
loglevel = LOG_LEVEL_INFO;
} else if (strcmp(szLoglevel, "WARN") == 0) {
loglevel = LOG_LEVEL_WARN;
} else if (strcmp(szLoglevel, "ERROR") == 0) {
loglevel = LOG_LEVEL_ERROR;
} else if (strcmp(szLoglevel, "FATAL") == 0) {
loglevel = LOG_LEVEL_FATAL;
} else if (strcmp(szLoglevel, "SILENT") == 0) {
loglevel = LOG_LEVEL_SILENT;
} else {
loglevel = DEFAULT_LOG_LEVEL;
}
logger->level = loglevel;
}
void logger_set_format(logger_t* logger, const char* format) {
if (format) {
strncpy(logger->format, format, sizeof(logger->format) - 1);
} else {
logger->format[0] = '\0';
}
}
void logger_set_remain_days(logger_t* logger, int days) {
logger->remain_days = days;
}
void logger_set_max_bufsize(logger_t* logger, unsigned int bufsize) {
logger->bufsize = bufsize;
logger->buf = (char*)realloc(logger->buf, bufsize);
}
void logger_enable_color(logger_t* logger, int on) {
logger->enable_color = on;
}
void logger_set_file(logger_t* logger, const char* filepath) {
strncpy(logger->filepath, filepath, sizeof(logger->filepath) - 1);
// remove suffix .log
char* suffix = strrchr(logger->filepath, '.');
if (suffix && strcmp(suffix, ".log") == 0) {
*suffix = '\0';
}
}
void logger_set_max_filesize(logger_t* logger, unsigned long long filesize) {
logger->max_filesize = filesize;
}
void logger_set_max_filesize_by_str(logger_t* logger, const char* str) {
int num = atoi(str);
if (num <= 0) return;
// 16 16M 16MB
const char* e = str;
while (*e != '\0') ++e;
--e;
char unit;
if (*e >= '0' && *e <= '9') unit = 'M';
else if (*e == 'B') unit = *(e-1);
else unit = *e;
unsigned long long filesize = num;
switch (unit) {
case 'K': filesize <<= 10; break;
case 'M': filesize <<= 20; break;
case 'G': filesize <<= 30; break;
default: filesize <<= 20; break;
}
logger->max_filesize = filesize;
}
void logger_enable_fsync(logger_t* logger, int on) {
logger->enable_fsync = on;
}
void logger_fsync(logger_t* logger) {
hmutex_lock(&logger->mutex_);
if (logger->fp_) {
fflush(logger->fp_);
}
hmutex_unlock(&logger->mutex_);
}
const char* logger_get_cur_file(logger_t* logger) {
return logger->cur_logfile;
}
static void logfile_name(const char* filepath, time_t ts, char* buf, int len) {
struct tm* tm = localtime(&ts);
snprintf(buf, len, "%s.%04d%02d%02d.log",
filepath,
tm->tm_year+1900,
tm->tm_mon+1,
tm->tm_mday);
}
static FILE* logfile_shift(logger_t* logger) {
time_t ts_now = time(NULL);
int interval_days = logger->last_logfile_ts == 0 ? 0 : (ts_now+s_gmtoff) / SECONDS_PER_DAY - (logger->last_logfile_ts+s_gmtoff) / SECONDS_PER_DAY;
if (logger->fp_ == NULL || interval_days > 0) {
// close old logfile
if (logger->fp_) {
fclose(logger->fp_);
logger->fp_ = NULL;
}
else {
interval_days = 30;
}
if (logger->remain_days >= 0) {
char rm_logfile[256] = {0};
if (interval_days >= logger->remain_days) {
// remove [today-interval_days, today-remain_days] logfile
for (int i = interval_days; i >= logger->remain_days; --i) {
time_t ts_rm = ts_now - i * SECONDS_PER_DAY;
logfile_name(logger->filepath, ts_rm, rm_logfile, sizeof(rm_logfile));
remove(rm_logfile);
}
}
else {
// remove today-remain_days logfile
time_t ts_rm = ts_now - logger->remain_days * SECONDS_PER_DAY;
logfile_name(logger->filepath, ts_rm, rm_logfile, sizeof(rm_logfile));
remove(rm_logfile);
}
}
}
// open today logfile
if (logger->fp_ == NULL) {
logfile_name(logger->filepath, ts_now, logger->cur_logfile, sizeof(logger->cur_logfile));
logger->fp_ = fopen(logger->cur_logfile, "a");
logger->last_logfile_ts = ts_now;
}
// NOTE: estimate can_write_cnt to avoid frequent fseek/ftell
if (logger->fp_ && --logger->can_write_cnt < 0) {
fseek(logger->fp_, 0, SEEK_END);
long filesize = ftell(logger->fp_);
if (filesize > logger->max_filesize) {
fclose(logger->fp_);
logger->fp_ = NULL;
// ftruncate
logger->fp_ = fopen(logger->cur_logfile, "w");
// reopen with O_APPEND for multi-processes
if (logger->fp_) {
fclose(logger->fp_);
logger->fp_ = fopen(logger->cur_logfile, "a");
}
}
else {
logger->can_write_cnt = (logger->max_filesize - filesize) / logger->bufsize;
}
}
return logger->fp_;
}
static void logfile_write(logger_t* logger, const char* buf, int len) {
FILE* fp = logfile_shift(logger);
if (fp) {
fwrite(buf, 1, len, fp);
if (logger->enable_fsync) {
fflush(fp);
}
}
}
static int i2a(int i, char* buf, int len) {
for (int l = len - 1; l >= 0; --l) {
if (i == 0) {
buf[l] = '0';
} else {
buf[l] = i % 10 + '0';
i /= 10;
}
}
return len;
}
int logger_print(logger_t* logger, int level, const char* fmt, ...) {
if (level < logger->level)
return -10;
int year,month,day,hour,min,sec,us;
#ifdef _WIN32
SYSTEMTIME tm;
GetLocalTime(&tm);
year = tm.wYear;
month = tm.wMonth;
day = tm.wDay;
hour = tm.wHour;
min = tm.wMinute;
sec = tm.wSecond;
us = tm.wMilliseconds * 1000;
#else
struct timeval tv;
struct tm* tm = NULL;
gettimeofday(&tv, NULL);
time_t tt = tv.tv_sec;
tm = localtime(&tt);
year = tm->tm_year + 1900;
month = tm->tm_mon + 1;
day = tm->tm_mday;
hour = tm->tm_hour;
min = tm->tm_min;
sec = tm->tm_sec;
us = tv.tv_usec;
#endif
const char* pcolor = "";
const char* plevel = "";
#define XXX(id, str, clr) \
case id: plevel = str; pcolor = clr; break;
switch (level) {
LOG_LEVEL_MAP(XXX)
}
#undef XXX
// lock logger->buf
hmutex_lock(&logger->mutex_);
char* buf = logger->buf;
int bufsize = logger->bufsize;
int len = 0;
if (logger->enable_color) {
len = snprintf(buf, bufsize, "%s", pcolor);
}
const char* p = logger->format;
if (*p) {
while (*p) {
if (*p == '%') {
switch(*++p) {
case 'y':
len += i2a(year, buf + len, 4);
break;
case 'm':
len += i2a(month, buf + len, 2);
break;
case 'd':
len += i2a(day, buf + len, 2);
break;
case 'H':
len += i2a(hour, buf + len, 2);
break;
case 'M':
len += i2a(min, buf + len, 2);
break;
case 'S':
len += i2a(sec, buf + len, 2);
break;
case 'z':
len += i2a(us/1000, buf + len, 3);
break;
case 'Z':
len += i2a(us, buf + len, 6);
break;
case 'l':
buf[len++] = *plevel;
break;
case 'L':
for (int i = 0; i < 5; ++i) {
buf[len++] = plevel[i];
}
break;
case 's':
{
va_list ap;
va_start(ap, fmt);
len += vsnprintf(buf + len, bufsize - len, fmt, ap);
va_end(ap);
}
break;
case '%':
buf[len++] = '%';
break;
default: break;
}
} else {
buf[len++] = *p;
}
++p;
}
} else {
len += snprintf(buf + len, bufsize - len, "%04d-%02d-%02d %02d:%02d:%02d.%03d %s ",
year, month, day, hour, min, sec, us/1000,
plevel);
va_list ap;
va_start(ap, fmt);
len += vsnprintf(buf + len, bufsize - len, fmt, ap);
va_end(ap);
}
if (logger->enable_color) {
len += snprintf(buf + len, bufsize - len, "%s", CLR_CLR);
}
if(len<bufsize) {
buf[len++] = '\n';
}
if (logger->handler) {
logger->handler(level, buf, len);
}
else {
logfile_write(logger, buf, len);
}
hmutex_unlock(&logger->mutex_);
return len;
}
static logger_t* s_logger = NULL;
logger_t* hv_default_logger() {
if (s_logger == NULL) {
s_logger = logger_create();
atexit(hv_destroy_default_logger);
}
return s_logger;
}
void hv_destroy_default_logger(void) {
if (s_logger) {
logger_fsync(s_logger);
logger_destroy(s_logger);
s_logger = NULL;
}
}
void stdout_logger(int loglevel, const char* buf, int len) {
fprintf(stdout, "%.*s", len, buf);
}
void stderr_logger(int loglevel, const char* buf, int len) {
fprintf(stderr, "%.*s", len, buf);
}
void file_logger(int loglevel, const char* buf, int len) {
logfile_write(hv_default_logger(), buf, len);
}

176
external/libhv/libhv-1.3.2/base/hlog.h vendored Normal file
View File

@ -0,0 +1,176 @@
#ifndef HV_LOG_H_
#define HV_LOG_H_
/*
* hlog is thread-safe
*/
#include <string.h>
#ifdef _WIN32
#define DIR_SEPARATOR '\\'
#define DIR_SEPARATOR_STR "\\"
#else
#define DIR_SEPARATOR '/'
#define DIR_SEPARATOR_STR "/"
#endif
#ifndef __FILENAME__
// #define __FILENAME__ (strrchr(__FILE__, DIR_SEPARATOR) ? strrchr(__FILE__, DIR_SEPARATOR) + 1 : __FILE__)
#define __FILENAME__ (strrchr(DIR_SEPARATOR_STR __FILE__, DIR_SEPARATOR) + 1)
#endif
#include "hexport.h"
#ifdef __cplusplus
extern "C" {
#endif
#define CLR_CLR "\033[0m" /* 恢复颜色 */
#define CLR_BLACK "\033[30m" /* 黑色字 */
#define CLR_RED "\033[31m" /* 红色字 */
#define CLR_GREEN "\033[32m" /* 绿色字 */
#define CLR_YELLOW "\033[33m" /* 黄色字 */
#define CLR_BLUE "\033[34m" /* 蓝色字 */
#define CLR_PURPLE "\033[35m" /* 紫色字 */
#define CLR_SKYBLUE "\033[36m" /* 天蓝字 */
#define CLR_WHITE "\033[37m" /* 白色字 */
#define CLR_BLK_WHT "\033[40;37m" /* 黑底白字 */
#define CLR_RED_WHT "\033[41;37m" /* 红底白字 */
#define CLR_GREEN_WHT "\033[42;37m" /* 绿底白字 */
#define CLR_YELLOW_WHT "\033[43;37m" /* 黄底白字 */
#define CLR_BLUE_WHT "\033[44;37m" /* 蓝底白字 */
#define CLR_PURPLE_WHT "\033[45;37m" /* 紫底白字 */
#define CLR_SKYBLUE_WHT "\033[46;37m" /* 天蓝底白字 */
#define CLR_WHT_BLK "\033[47;30m" /* 白底黑字 */
// XXX(id, str, clr)
#define LOG_LEVEL_MAP(XXX) \
XXX(LOG_LEVEL_DEBUG, "DEBUG", CLR_WHITE) \
XXX(LOG_LEVEL_INFO, "INFO ", CLR_GREEN) \
XXX(LOG_LEVEL_WARN, "WARN ", CLR_YELLOW) \
XXX(LOG_LEVEL_ERROR, "ERROR", CLR_RED) \
XXX(LOG_LEVEL_FATAL, "FATAL", CLR_RED_WHT)
typedef enum {
LOG_LEVEL_VERBOSE = 0,
#define XXX(id, str, clr) id,
LOG_LEVEL_MAP(XXX)
#undef XXX
LOG_LEVEL_SILENT
} log_level_e;
#define DEFAULT_LOG_FILE "libhv"
#define DEFAULT_LOG_LEVEL LOG_LEVEL_INFO
#define DEFAULT_LOG_FORMAT "%y-%m-%d %H:%M:%S.%z %L %s"
#define DEFAULT_LOG_REMAIN_DAYS 1
#define DEFAULT_LOG_MAX_BUFSIZE (1<<14) // 16k
#define DEFAULT_LOG_MAX_FILESIZE (1<<24) // 16M
// logger: default file_logger
// network_logger() see event/nlog.h
typedef void (*logger_handler)(int loglevel, const char* buf, int len);
HV_EXPORT void stdout_logger(int loglevel, const char* buf, int len);
HV_EXPORT void stderr_logger(int loglevel, const char* buf, int len);
HV_EXPORT void file_logger(int loglevel, const char* buf, int len);
// network_logger implement see event/nlog.h
// HV_EXPORT void network_logger(int loglevel, const char* buf, int len);
typedef struct logger_s logger_t;
HV_EXPORT logger_t* logger_create();
HV_EXPORT void logger_destroy(logger_t* logger);
HV_EXPORT void logger_set_handler(logger_t* logger, logger_handler fn);
HV_EXPORT void logger_set_level(logger_t* logger, int level);
// level = [VERBOSE,DEBUG,INFO,WARN,ERROR,FATAL,SILENT]
HV_EXPORT void logger_set_level_by_str(logger_t* logger, const char* level);
/*
* format = "%y-%m-%d %H:%M:%S.%z %L %s"
* message = "2020-01-02 03:04:05.067 DEBUG message"
* %y year
* %m month
* %d day
* %H hour
* %M min
* %S sec
* %z ms
* %Z us
* %l First character of level
* %L All characters of level
* %s message
* %% %
*/
HV_EXPORT void logger_set_format(logger_t* logger, const char* format);
HV_EXPORT void logger_set_max_bufsize(logger_t* logger, unsigned int bufsize);
HV_EXPORT void logger_enable_color(logger_t* logger, int on);
HV_EXPORT int logger_print(logger_t* logger, int level, const char* fmt, ...);
// below for file logger
HV_EXPORT void logger_set_file(logger_t* logger, const char* filepath);
HV_EXPORT void logger_set_max_filesize(logger_t* logger, unsigned long long filesize);
// 16, 16M, 16MB
HV_EXPORT void logger_set_max_filesize_by_str(logger_t* logger, const char* filesize);
HV_EXPORT void logger_set_remain_days(logger_t* logger, int days);
HV_EXPORT void logger_enable_fsync(logger_t* logger, int on);
HV_EXPORT void logger_fsync(logger_t* logger);
HV_EXPORT const char* logger_get_cur_file(logger_t* logger);
// hlog: default logger instance
HV_EXPORT logger_t* hv_default_logger();
HV_EXPORT void hv_destroy_default_logger(void);
// macro hlog*
#define hlog hv_default_logger()
#define hlog_destory() hv_destroy_default_logger()
#define hlog_disable() logger_set_level(hlog, LOG_LEVEL_SILENT)
#define hlog_set_file(filepath) logger_set_file(hlog, filepath)
#define hlog_set_level(level) logger_set_level(hlog, level)
#define hlog_set_level_by_str(level) logger_set_level_by_str(hlog, level)
#define hlog_set_handler(fn) logger_set_handler(hlog, fn)
#define hlog_set_format(format) logger_set_format(hlog, format)
#define hlog_set_max_filesize(filesize) logger_set_max_filesize(hlog, filesize)
#define hlog_set_max_filesize_by_str(filesize) logger_set_max_filesize_by_str(hlog, filesize)
#define hlog_set_remain_days(days) logger_set_remain_days(hlog, days)
#define hlog_enable_fsync() logger_enable_fsync(hlog, 1)
#define hlog_disable_fsync() logger_enable_fsync(hlog, 0)
#define hlog_fsync() logger_fsync(hlog)
#define hlog_get_cur_file() logger_get_cur_file(hlog)
#define hlogd(fmt, ...) logger_print(hlog, LOG_LEVEL_DEBUG, fmt " [%s:%d:%s]", ## __VA_ARGS__, __FILENAME__, __LINE__, __FUNCTION__)
#define hlogi(fmt, ...) logger_print(hlog, LOG_LEVEL_INFO, fmt " [%s:%d:%s]", ## __VA_ARGS__, __FILENAME__, __LINE__, __FUNCTION__)
#define hlogw(fmt, ...) logger_print(hlog, LOG_LEVEL_WARN, fmt " [%s:%d:%s]", ## __VA_ARGS__, __FILENAME__, __LINE__, __FUNCTION__)
#define hloge(fmt, ...) logger_print(hlog, LOG_LEVEL_ERROR, fmt " [%s:%d:%s]", ## __VA_ARGS__, __FILENAME__, __LINE__, __FUNCTION__)
#define hlogf(fmt, ...) logger_print(hlog, LOG_LEVEL_FATAL, fmt " [%s:%d:%s]", ## __VA_ARGS__, __FILENAME__, __LINE__, __FUNCTION__)
// below for android
#if defined(ANDROID) || defined(__ANDROID__)
#include <android/log.h>
#define LOG_TAG "JNI"
#undef hlogd
#undef hlogi
#undef hlogw
#undef hloge
#undef hlogf
#define hlogd(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define hlogi(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define hlogw(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define hloge(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define hlogf(...) __android_log_print(ANDROID_LOG_FATAL, LOG_TAG, __VA_ARGS__)
#endif
// macro alias
#if !defined(LOGD) && !defined(LOGI) && !defined(LOGW) && !defined(LOGE) && !defined(LOGF)
#define LOGD hlogd
#define LOGI hlogi
#define LOGW hlogw
#define LOGE hloge
#define LOGF hlogf
#endif
#ifdef __cplusplus
} // extern "C"
#endif
#endif // HV_LOG_H_

674
external/libhv/libhv-1.3.2/base/hmain.c vendored Normal file
View File

@ -0,0 +1,674 @@
#include "hmain.h"
#include "hbase.h"
#include "hlog.h"
#include "herr.h"
#include "htime.h"
#include "hthread.h"
#ifdef OS_DARWIN
#include <crt_externs.h>
#define environ (*_NSGetEnviron())
#endif
main_ctx_t g_main_ctx;
static void init_arg_kv(int maxsize) {
g_main_ctx.arg_kv_size = 0;
SAFE_ALLOC(g_main_ctx.arg_kv, sizeof(char*) * maxsize);
}
static void save_arg_kv(const char* key, int key_len, const char* val, int val_len) {
if (key_len <= 0) key_len = strlen(key);
if (val_len <= 0) val_len = strlen(val);
char* arg = NULL;
SAFE_ALLOC(arg, key_len + val_len + 2);
memcpy(arg, key, key_len);
arg[key_len] = '=';
memcpy(arg + key_len + 1, val, val_len);
// printf("save_arg_kv: %s\n", arg);
g_main_ctx.arg_kv[g_main_ctx.arg_kv_size++] = arg;
}
static void init_arg_list(int maxsize) {
g_main_ctx.arg_list_size = 0;
SAFE_ALLOC(g_main_ctx.arg_list, sizeof(char*) * maxsize);
}
static void save_arg_list(const char* arg) {
// printf("save_arg_list: %s\n", arg);
g_main_ctx.arg_list[g_main_ctx.arg_list_size++] = strdup(arg);
}
static const char* get_val(char** kvs, const char* key) {
if (kvs == NULL) return NULL;
int key_len = strlen(key);
char* kv = NULL;
int kv_len = 0;
for (int i = 0; kvs[i]; ++i) {
kv = kvs[i];
kv_len = strlen(kv);
if (kv_len <= key_len) continue;
// key=val
if (memcmp(kv, key, key_len) == 0 && kv[key_len] == '=') {
return kv + key_len + 1;
}
}
return NULL;
}
const char* get_arg(const char* key) {
return get_val(g_main_ctx.arg_kv, key);
}
const char* get_env(const char* key) {
return get_val(g_main_ctx.save_envp, key);
}
int main_ctx_init(int argc, char** argv) {
if (argc == 0 || argv == NULL) {
argc = 1;
SAFE_ALLOC(argv, 2 * sizeof(char*));
SAFE_ALLOC(argv[0], MAX_PATH);
get_executable_path(argv[0], MAX_PATH);
}
get_run_dir(g_main_ctx.run_dir, sizeof(g_main_ctx.run_dir));
//printf("run_dir=%s\n", g_main_ctx.run_dir);
strncpy(g_main_ctx.program_name, hv_basename(argv[0]), sizeof(g_main_ctx.program_name));
#ifdef OS_WIN
if (strcmp(g_main_ctx.program_name+strlen(g_main_ctx.program_name)-4, ".exe") == 0) {
*(g_main_ctx.program_name+strlen(g_main_ctx.program_name)-4) = '\0';
}
#endif
//printf("program_name=%s\n", g_main_ctx.program_name);
char logdir[MAX_PATH] = {0};
snprintf(logdir, sizeof(logdir), "%s/logs", g_main_ctx.run_dir);
hv_mkdir(logdir);
snprintf(g_main_ctx.confile, sizeof(g_main_ctx.confile), "%s/etc/%s.conf", g_main_ctx.run_dir, g_main_ctx.program_name);
snprintf(g_main_ctx.pidfile, sizeof(g_main_ctx.pidfile), "%s/logs/%s.pid", g_main_ctx.run_dir, g_main_ctx.program_name);
snprintf(g_main_ctx.logfile, sizeof(g_main_ctx.logfile), "%s/logs/%s.log", g_main_ctx.run_dir, g_main_ctx.program_name);
hlog_set_file(g_main_ctx.logfile);
g_main_ctx.pid = getpid();
g_main_ctx.oldpid = getpid_from_pidfile();
#ifdef OS_UNIX
if (kill(g_main_ctx.oldpid, 0) == -1 && errno == ESRCH) {
g_main_ctx.oldpid = -1;
}
#else
HANDLE hproc = OpenProcess(PROCESS_TERMINATE, FALSE, g_main_ctx.oldpid);
if (hproc == NULL) {
g_main_ctx.oldpid = -1;
}
else {
CloseHandle(hproc);
}
#endif
// save arg
int i = 0;
g_main_ctx.os_argv = argv;
g_main_ctx.argc = 0;
g_main_ctx.arg_len = 0;
for (i = 0; argv[i]; ++i) {
g_main_ctx.arg_len += strlen(argv[i]) + 1;
}
g_main_ctx.argc = i;
char* argp = NULL;
SAFE_ALLOC(argp, g_main_ctx.arg_len);
SAFE_ALLOC(g_main_ctx.save_argv, (g_main_ctx.argc + 1) * sizeof(char*));
char* cmdline = NULL;
SAFE_ALLOC(cmdline, g_main_ctx.arg_len);
g_main_ctx.cmdline = cmdline;
for (i = 0; argv[i]; ++i) {
strcpy(argp, argv[i]);
g_main_ctx.save_argv[i] = argp;
argp += strlen(argv[i]) + 1;
strcpy(cmdline, argv[i]);
cmdline += strlen(argv[i]);
*cmdline = ' ';
++cmdline;
}
g_main_ctx.save_argv[g_main_ctx.argc] = NULL;
g_main_ctx.cmdline[g_main_ctx.arg_len-1] = '\0';
#if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_DARWIN)
// save env
g_main_ctx.os_envp = environ;
g_main_ctx.envc = 0;
g_main_ctx.env_len = 0;
for (i = 0; environ[i]; ++i) {
g_main_ctx.env_len += strlen(environ[i]) + 1;
}
g_main_ctx.envc = i;
char* envp = NULL;
SAFE_ALLOC(envp, g_main_ctx.env_len);
SAFE_ALLOC(g_main_ctx.save_envp, (g_main_ctx.envc + 1) * sizeof(char*));
for (i = 0; environ[i]; ++i) {
g_main_ctx.save_envp[i] = envp;
strcpy(g_main_ctx.save_envp[i], environ[i]);
envp += strlen(environ[i]) + 1;
}
g_main_ctx.save_envp[g_main_ctx.envc] = NULL;
#endif
// signals
g_main_ctx.reload_fn = NULL;
g_main_ctx.reload_userdata = NULL;
// master workers
g_main_ctx.worker_processes = 0;
g_main_ctx.worker_threads = 0;
g_main_ctx.worker_fn = 0;
g_main_ctx.worker_userdata = 0;
g_main_ctx.proc_ctxs = NULL;
atexit(main_ctx_free);
return 0;
}
void main_ctx_free(void) {
if (g_main_ctx.save_argv) {
SAFE_FREE(g_main_ctx.save_argv[0]);
SAFE_FREE(g_main_ctx.save_argv);
}
SAFE_FREE(g_main_ctx.cmdline);
if (g_main_ctx.save_envp) {
SAFE_FREE(g_main_ctx.save_envp[0]);
SAFE_FREE(g_main_ctx.save_envp);
}
if (g_main_ctx.arg_kv) {
for (int i = 0; i < g_main_ctx.arg_kv_size; ++i) {
SAFE_FREE(g_main_ctx.arg_kv[i]);
}
SAFE_FREE(g_main_ctx.arg_kv);
}
if (g_main_ctx.arg_list) {
for (int i = 0; i < g_main_ctx.arg_list_size; ++i) {
SAFE_FREE(g_main_ctx.arg_list[i]);
}
SAFE_FREE(g_main_ctx.arg_list);
}
}
#define UNDEFINED_OPTION -1
static int get_arg_type(int short_opt, const char* options) {
if (options == NULL) return UNDEFINED_OPTION;
const char* p = options;
while (*p && *p != short_opt) ++p;
if (*p == '\0') return UNDEFINED_OPTION;
if (*(p+1) == ':') return REQUIRED_ARGUMENT;
return NO_ARGUMENT;
}
int parse_opt(int argc, char** argv, const char* options) {
if (argc < 1) return 0;
init_arg_kv(strlen(options) + 1);
init_arg_list(argc);
for (int i = 1; argv[i]; ++i) {
char* p = argv[i];
if (*p != '-') {
save_arg_list(argv[i]);
continue;
}
while (*++p) {
int arg_type = get_arg_type(*p, options);
if (arg_type == UNDEFINED_OPTION) {
printf("Invalid option '%c'\n", *p);
return -20;
} else if (arg_type == NO_ARGUMENT) {
save_arg_kv(p, 1, OPTION_ENABLE, 0);
continue;
} else if (arg_type == REQUIRED_ARGUMENT) {
if (*(p+1) != '\0') {
save_arg_kv(p, 1, p+1, 0);
break;
} else if (argv[i+1] != NULL) {
save_arg_kv(p, 1, argv[++i], 0);
break;
} else {
printf("Option '%c' requires param\n", *p);
return -30;
}
}
}
}
return 0;
}
static const option_t* get_option(const char* opt, const option_t* long_options, int size) {
if (opt == NULL || long_options == NULL) return NULL;
int len = strlen(opt);
if (len == 0) return NULL;
if (len == 1) {
for (int i = 0; i < size; ++i) {
if (long_options[i].short_opt == *opt) {
return &long_options[i];
}
}
} else {
for (int i = 0; i < size; ++i) {
if (strcmp(long_options[i].long_opt, opt) == 0) {
return &long_options[i];
}
}
}
return NULL;
}
#define MAX_OPTION 32
// opt type
#define NOPREFIX_OPTION 0
#define SHORT_OPTION -1
#define LONG_OPTION -2
int parse_opt_long(int argc, char** argv, const option_t* long_options, int size) {
if (argc < 1) return 0;
init_arg_kv(size + 1);
init_arg_list(argc);
char opt[MAX_OPTION+1] = {0};
for (int i = 1; argv[i]; ++i) {
char* arg = argv[i];
int opt_type = NOPREFIX_OPTION;
// prefix
if (*arg == OPTION_PREFIX) {
++arg;
opt_type = SHORT_OPTION;
if (*arg == OPTION_PREFIX) {
++arg;
opt_type = LONG_OPTION;
}
}
int arg_len = strlen(arg);
// delim
char* delim = strchr(arg, OPTION_DELIM);
if (delim) {
if (delim == arg || delim == arg+arg_len-1 || delim-arg > MAX_OPTION) {
printf("Invalid option '%s'\n", argv[i]);
return -10;
}
memcpy(opt, arg, delim-arg);
opt[delim-arg] = '\0';
} else {
if (opt_type == SHORT_OPTION) {
*opt = *arg;
opt[1] = '\0';
} else {
strncpy(opt, arg, MAX_OPTION);
}
}
// get_option
const option_t* pOption = get_option(opt, long_options, size);
if (pOption == NULL) {
if (delim == NULL && opt_type == NOPREFIX_OPTION) {
save_arg_list(arg);
continue;
} else {
printf("Invalid option: '%s'\n", argv[i]);
return -10;
}
}
const char* value = NULL;
if (pOption->arg_type == NO_ARGUMENT) {
// -h
value = OPTION_ENABLE;
} else if (pOption->arg_type == REQUIRED_ARGUMENT) {
if (delim) {
// --port=80
value = delim+1;
} else {
if (opt_type == SHORT_OPTION && *(arg+1) != '\0') {
// p80
value = arg+1;
} else if (argv[i+1] != NULL) {
// --port 80
value = argv[++i];
} else {
printf("Option '%s' requires parament\n", opt);
return -20;
}
}
}
// preferred to use short_opt as key
if (pOption->short_opt > 0) {
save_arg_kv(&pOption->short_opt, 1, value, 0);
} else if (pOption->long_opt) {
save_arg_kv(pOption->long_opt, 0, value, 0);
}
}
return 0;
}
#if defined(OS_UNIX) && !HAVE_SETPROCTITLE
/*
* memory layout
* argv[0]\0argv[1]\0argv[n]\0env[0]\0env[1]\0env[n]\0
*/
void setproctitle(const char* fmt, ...) {
char buf[256] = {0};
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
va_end(ap);
int len = g_main_ctx.arg_len + g_main_ctx.env_len;
if (g_main_ctx.os_argv && len) {
strncpy(g_main_ctx.os_argv[0], buf, len-1);
}
}
#endif
int create_pidfile() {
FILE* fp = fopen(g_main_ctx.pidfile, "w");
if (fp == NULL) {
hloge("fopen('%s') error: %d", g_main_ctx.pidfile, errno);
return -1;
}
g_main_ctx.pid = hv_getpid();
fprintf(fp, "%d\n", (int)g_main_ctx.pid);
fclose(fp);
hlogi("create_pidfile('%s') pid=%d", g_main_ctx.pidfile, g_main_ctx.pid);
atexit(delete_pidfile);
return 0;
}
void delete_pidfile(void) {
hlogi("delete_pidfile('%s') pid=%d", g_main_ctx.pidfile, g_main_ctx.pid);
remove(g_main_ctx.pidfile);
}
pid_t getpid_from_pidfile() {
FILE* fp = fopen(g_main_ctx.pidfile, "r");
if (fp == NULL) {
// hloge("fopen('%s') error: %d", g_main_ctx.pidfile, errno);
return -1;
}
int pid = -1;
fscanf(fp, "%d", &pid);
fclose(fp);
return pid;
}
#ifdef OS_UNIX
// unix use signal
#include <sys/wait.h>
void signal_handler(int signo) {
hlogi("pid=%d recv signo=%d", getpid(), signo);
switch (signo) {
case SIGINT:
case SIGNAL_TERMINATE:
hlogi("killall processes");
signal(SIGCHLD, SIG_IGN);
// master send SIGKILL => workers
for (int i = 0; i < g_main_ctx.worker_processes; ++i) {
if (g_main_ctx.proc_ctxs[i].pid <= 0) break;
kill(g_main_ctx.proc_ctxs[i].pid, SIGKILL);
g_main_ctx.proc_ctxs[i].pid = -1;
}
exit(0);
break;
case SIGCHLD:
{
pid_t pid = 0;
int status = 0;
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
hlogw("proc stop/waiting, pid=%d status=%d", pid, status);
for (int i = 0; i < g_main_ctx.worker_processes; ++i) {
proc_ctx_t* ctx = g_main_ctx.proc_ctxs + i;
if (ctx->pid == pid) {
ctx->pid = -1;
// NOTE: avoid frequent crash and restart
time_t run_time = time(NULL) - ctx->start_time;
if (ctx->spawn_cnt < 3 || run_time > 3600) {
hproc_spawn(ctx);
}
else {
hloge("proc crash, pid=%d spawn_cnt=%d run_time=%us",
pid, ctx->spawn_cnt, (unsigned int)run_time);
bool have_worker = false;
for (int i = 0; i < g_main_ctx.worker_processes; ++i) {
if (g_main_ctx.proc_ctxs[i].pid > 0) {
have_worker = true;
break;
}
}
if (!have_worker) {
hlogw("No alive worker process, exit master process!");
exit(0);
}
}
break;
}
}
}
}
break;
case SIGNAL_RELOAD:
if (g_main_ctx.reload_fn) {
g_main_ctx.reload_fn(g_main_ctx.reload_userdata);
if (getpid_from_pidfile() == getpid()) {
// master send SIGNAL_RELOAD => workers
for (int i = 0; i < g_main_ctx.worker_processes; ++i) {
if (g_main_ctx.proc_ctxs[i].pid <= 0) break;
kill(g_main_ctx.proc_ctxs[i].pid, SIGNAL_RELOAD);
}
}
}
break;
default:
break;
}
}
int signal_init(procedure_t reload_fn, void* reload_userdata) {
g_main_ctx.reload_fn = reload_fn;
g_main_ctx.reload_userdata = reload_userdata;
signal(SIGINT, signal_handler);
signal(SIGCHLD, signal_handler);
signal(SIGNAL_TERMINATE, signal_handler);
signal(SIGNAL_RELOAD, signal_handler);
return 0;
}
#elif defined(OS_WIN)
#include <mmsystem.h> // for timeSetEvent
// win32 use Event
//static HANDLE s_hEventTerm = NULL;
static HANDLE s_hEventReload = NULL;
static void WINAPI on_timer(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2) {
DWORD ret;
/*
ret = WaitForSingleObject(s_hEventTerm, 0);
if (ret == WAIT_OBJECT_0) {
hlogi("pid=%d recv event [TERM]", getpid());
if (getpid_from_pidfile() == getpid()) {
timeKillEvent(uTimerID);
exit(0);
}
}
*/
ret = WaitForSingleObject(s_hEventReload, 0);
if (ret == WAIT_OBJECT_0) {
hlogi("pid=%d recv event [RELOAD]", getpid());
if (g_main_ctx.reload_fn) {
g_main_ctx.reload_fn(g_main_ctx.reload_userdata);
}
}
}
static void signal_cleanup(void) {
//CloseHandle(s_hEventTerm);
//s_hEventTerm = NULL;
CloseHandle(s_hEventReload);
s_hEventReload = NULL;
}
int signal_init(procedure_t reload_fn, void* reload_userdata) {
g_main_ctx.reload_fn = reload_fn;
g_main_ctx.reload_userdata = reload_userdata;
char eventname[MAX_PATH] = {0};
//snprintf(eventname, sizeof(eventname), "%s_term_event", g_main_ctx.program_name);
//s_hEventTerm = CreateEvent(NULL, FALSE, FALSE, eventname);
//s_hEventTerm = OpenEvent(EVENT_ALL_ACCESS, FALSE, eventname);
snprintf(eventname, sizeof(eventname), "%s_reload_event", g_main_ctx.program_name);
s_hEventReload = CreateEvent(NULL, FALSE, FALSE, eventname);
timeSetEvent(1000, 1000, on_timer, 0, TIME_PERIODIC);
atexit(signal_cleanup);
return 0;
}
#endif
static void kill_proc(int pid) {
#ifdef OS_UNIX
kill(pid, SIGNAL_TERMINATE);
#else
//SetEvent(s_hEventTerm);
//hv_sleep(1);
HANDLE hproc = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
if (hproc) {
TerminateProcess(hproc, 0);
CloseHandle(hproc);
}
#endif
}
void signal_handle(const char* signal) {
if (strcmp(signal, "start") == 0) {
if (g_main_ctx.oldpid > 0) {
printf("%s is already running, pid=%d\n", g_main_ctx.program_name, g_main_ctx.oldpid);
exit(0);
}
} else if (strcmp(signal, "stop") == 0) {
if (g_main_ctx.oldpid > 0) {
kill_proc(g_main_ctx.oldpid);
printf("%s stop/waiting\n", g_main_ctx.program_name);
} else {
printf("%s is already stopped\n", g_main_ctx.program_name);
}
exit(0);
} else if (strcmp(signal, "restart") == 0) {
if (g_main_ctx.oldpid > 0) {
kill_proc(g_main_ctx.oldpid);
printf("%s stop/waiting\n", g_main_ctx.program_name);
hv_sleep(1);
}
} else if (strcmp(signal, "status") == 0) {
if (g_main_ctx.oldpid > 0) {
printf("%s start/running, pid=%d\n", g_main_ctx.program_name, g_main_ctx.oldpid);
} else {
printf("%s stop/waiting\n", g_main_ctx.program_name);
}
exit(0);
} else if (strcmp(signal, "reload") == 0) {
if (g_main_ctx.oldpid > 0) {
printf("reload confile [%s]\n", g_main_ctx.confile);
#ifdef OS_UNIX
kill(g_main_ctx.oldpid, SIGNAL_RELOAD);
#else
SetEvent(s_hEventReload);
#endif
}
hv_sleep(1);
exit(0);
} else {
printf("Invalid signal: '%s'\n", signal);
exit(0);
}
printf("%s start/running\n", g_main_ctx.program_name);
}
// master-workers processes
static HTHREAD_ROUTINE(worker_thread) {
hlogi("worker_thread pid=%ld tid=%ld", hv_getpid(), hv_gettid());
if (g_main_ctx.worker_fn) {
g_main_ctx.worker_fn(g_main_ctx.worker_userdata);
}
return 0;
}
static void worker_init(void* userdata) {
#ifdef OS_UNIX
setproctitle("%s: worker process", g_main_ctx.program_name);
signal(SIGNAL_RELOAD, signal_handler);
#endif
}
static void worker_proc(void* userdata) {
for (int i = 1; i < g_main_ctx.worker_threads; ++i) {
hthread_create(worker_thread, NULL);
}
worker_thread(NULL);
}
int master_workers_run(procedure_t worker_fn, void* worker_userdata,
int worker_processes, int worker_threads, bool wait) {
#ifdef OS_WIN
// NOTE: Windows not provide MultiProcesses
if (worker_threads == 0) {
// MultiProcesses => MultiThreads
worker_threads = worker_processes;
}
worker_processes = 0;
#endif
if (worker_threads == 0) worker_threads = 1;
g_main_ctx.worker_threads = worker_threads;
g_main_ctx.worker_fn = worker_fn;
g_main_ctx.worker_userdata = worker_userdata;
if (worker_processes == 0) {
// single process
if (wait) {
for (int i = 1; i < worker_threads; ++i) {
hthread_create(worker_thread, NULL);
}
worker_thread(NULL);
}
else {
for (int i = 0; i < worker_threads; ++i) {
hthread_create(worker_thread, NULL);
}
}
}
else {
if (g_main_ctx.worker_processes != 0) {
return ERR_OVER_LIMIT;
}
// master-workers processes
#ifdef OS_UNIX
setproctitle("%s: master process", g_main_ctx.program_name);
signal(SIGNAL_RELOAD, signal_handler);
#endif
g_main_ctx.worker_processes = worker_processes;
int bytes = g_main_ctx.worker_processes * sizeof(proc_ctx_t);
SAFE_ALLOC(g_main_ctx.proc_ctxs, bytes);
proc_ctx_t* ctx = g_main_ctx.proc_ctxs;
for (int i = 0; i < g_main_ctx.worker_processes; ++i, ++ctx) {
ctx->init = worker_init;
ctx->proc = worker_proc;
hproc_spawn(ctx);
hlogi("workers[%d] start/running, pid=%d", i, ctx->pid);
}
g_main_ctx.pid = getpid();
hlogi("master start/running, pid=%d", g_main_ctx.pid);
if (wait) {
while (1) hv_sleep (1);
}
}
return 0;
}

117
external/libhv/libhv-1.3.2/base/hmain.h vendored Normal file
View File

@ -0,0 +1,117 @@
#ifndef HV_MAIN_H_
#define HV_MAIN_H_
#include "hexport.h"
#include "hplatform.h"
#include "hdef.h"
#include "hproc.h"
#ifdef _MSC_VER
#pragma comment(lib, "winmm.lib") // for timeSetEvent
#endif
BEGIN_EXTERN_C
typedef struct main_ctx_s {
char run_dir[MAX_PATH];
char program_name[MAX_PATH];
char confile[MAX_PATH]; // default etc/${program}.conf
char pidfile[MAX_PATH]; // default logs/${program}.pid
char logfile[MAX_PATH]; // default logs/${program}.log
pid_t pid; // getpid
pid_t oldpid; // getpid_from_pidfile
// arg
int argc;
int arg_len;
char** os_argv;
char** save_argv;
char* cmdline;
// parsed arg
int arg_kv_size;
char** arg_kv;
int arg_list_size;
char** arg_list;
// env
int envc;
int env_len;
char** os_envp;
char** save_envp;
// signals
procedure_t reload_fn;
void* reload_userdata;
// master workers model
int worker_processes;
int worker_threads;
procedure_t worker_fn;
void* worker_userdata;
proc_ctx_t* proc_ctxs;
} main_ctx_t;
// arg_type
#define NO_ARGUMENT 0
#define REQUIRED_ARGUMENT 1
#define OPTIONAL_ARGUMENT 2
// option define
#define OPTION_PREFIX '-'
#define OPTION_DELIM '='
#define OPTION_ENABLE "1"
#define OPTION_DISABLE "0"
typedef struct option_s {
char short_opt;
const char* long_opt;
int arg_type;
} option_t;
HV_EXPORT int main_ctx_init(int argc, char** argv);
HV_EXPORT void main_ctx_free(void);
// ls -a -l
// ls -al
// watch -n 10 ls
// watch -n10 ls
HV_EXPORT int parse_opt(int argc, char** argv, const char* opt);
// gcc -g -Wall -O3 -std=cpp main.c
HV_EXPORT int parse_opt_long(int argc, char** argv, const option_t* long_options, int size);
HV_EXPORT const char* get_arg(const char* key);
HV_EXPORT const char* get_env(const char* key);
#if defined(OS_UNIX) && !HAVE_SETPROCTITLE
HV_EXPORT void setproctitle(const char* fmt, ...);
#endif
// pidfile
HV_EXPORT int create_pidfile();
HV_EXPORT void delete_pidfile(void);
HV_EXPORT pid_t getpid_from_pidfile();
// signal=[start,stop,restart,status,reload]
HV_EXPORT int signal_init(procedure_t reload_fn DEFAULT(NULL), void* reload_userdata DEFAULT(NULL));
HV_EXPORT void signal_handle(const char* signal);
#ifdef OS_UNIX
// we use SIGTERM to quit process, SIGUSR1 to reload confile
#define SIGNAL_TERMINATE SIGTERM
#define SIGNAL_RELOAD SIGUSR1
void signal_handler(int signo);
#endif
// global var
#define DEFAULT_WORKER_PROCESSES 4
#define MAXNUM_WORKER_PROCESSES 256
HV_EXPORT extern main_ctx_t g_main_ctx;
// master-workers processes
HV_EXPORT int master_workers_run(
procedure_t worker_fn,
void* worker_userdata DEFAULT(NULL),
int worker_processes DEFAULT(DEFAULT_WORKER_PROCESSES),
int worker_threads DEFAULT(0),
bool wait DEFAULT(true));
END_EXTERN_C
#endif // HV_MAIN_H_

68
external/libhv/libhv-1.3.2/base/hmath.h vendored Normal file
View File

@ -0,0 +1,68 @@
#ifndef HV_MATH_H_
#define HV_MATH_H_
#include <math.h>
static inline unsigned long floor2e(unsigned long num) {
unsigned long n = num;
int e = 0;
while (n>>=1) ++e;
unsigned long ret = 1;
while (e--) ret<<=1;
return ret;
}
static inline unsigned long ceil2e(unsigned long num) {
// 2**0 = 1
if (num == 0 || num == 1) return 1;
unsigned long n = num - 1;
int e = 1;
while (n>>=1) ++e;
unsigned long ret = 1;
while (e--) ret<<=1;
return ret;
}
// varint little-endian
// MSB
static inline int varint_encode(long long value, unsigned char* buf) {
unsigned char ch;
unsigned char *p = buf;
int bytes = 0;
do {
ch = value & 0x7F;
value >>= 7;
*p++ = value == 0 ? ch : (ch | 0x80);
++bytes;
} while (value);
return bytes;
}
// @param[IN|OUT] len: in=>buflen, out=>varint bytesize
static inline long long varint_decode(const unsigned char* buf, int* len) {
long long ret = 0;
int bytes = 0, bits = 0;
const unsigned char *p = buf;
do {
if (len && *len && bytes == *len) {
// Not enough length
*len = 0;
return 0;
}
ret |= ((long long)(*p & 0x7F)) << bits;
++bytes;
if ((*p & 0x80) == 0) {
// Found end
if (len) *len = bytes;
return ret;
}
++p;
bits += 7;
} while(bytes < 10);
// Not found end
if (len) *len = -1;
return ret;
}
#endif // HV_MATH_H_

268
external/libhv/libhv-1.3.2/base/hmutex.h vendored Normal file
View File

@ -0,0 +1,268 @@
#ifndef HV_MUTEX_H_
#define HV_MUTEX_H_
#include "hexport.h"
#include "hplatform.h"
#include "htime.h"
BEGIN_EXTERN_C
#ifdef OS_WIN
#define hmutex_t CRITICAL_SECTION
#define hmutex_init InitializeCriticalSection
#define hmutex_destroy DeleteCriticalSection
#define hmutex_lock EnterCriticalSection
#define hmutex_unlock LeaveCriticalSection
#define hrecursive_mutex_t CRITICAL_SECTION
#define hrecursive_mutex_init InitializeCriticalSection
#define hrecursive_mutex_destroy DeleteCriticalSection
#define hrecursive_mutex_lock EnterCriticalSection
#define hrecursive_mutex_unlock LeaveCriticalSection
#define HSPINLOCK_COUNT -1
#define hspinlock_t CRITICAL_SECTION
#define hspinlock_init(pspin) InitializeCriticalSectionAndSpinCount(pspin, HSPINLOCK_COUNT)
#define hspinlock_destroy DeleteCriticalSection
#define hspinlock_lock EnterCriticalSection
#define hspinlock_unlock LeaveCriticalSection
#define hrwlock_t SRWLOCK
#define hrwlock_init InitializeSRWLock
#define hrwlock_destroy(plock)
#define hrwlock_rdlock AcquireSRWLockShared
#define hrwlock_rdunlock ReleaseSRWLockShared
#define hrwlock_wrlock AcquireSRWLockExclusive
#define hrwlock_wrunlock ReleaseSRWLockExclusive
#define htimed_mutex_t HANDLE
#define htimed_mutex_init(pmutex) *(pmutex) = CreateMutex(NULL, FALSE, NULL)
#define htimed_mutex_destroy(pmutex) CloseHandle(*(pmutex))
#define htimed_mutex_lock(pmutex) WaitForSingleObject(*(pmutex), INFINITE)
#define htimed_mutex_unlock(pmutex) ReleaseMutex(*(pmutex))
// true: WAIT_OBJECT_0
// false: WAIT_OBJECT_TIMEOUT
#define htimed_mutex_lock_for(pmutex, ms) ( WaitForSingleObject(*(pmutex), ms) == WAIT_OBJECT_0 )
#define hcondvar_t CONDITION_VARIABLE
#define hcondvar_init InitializeConditionVariable
#define hcondvar_destroy(pcond)
#define hcondvar_wait(pcond, pmutex) SleepConditionVariableCS(pcond, pmutex, INFINITE)
#define hcondvar_wait_for(pcond, pmutex, ms) SleepConditionVariableCS(pcond, pmutex, ms)
#define hcondvar_signal WakeConditionVariable
#define hcondvar_broadcast WakeAllConditionVariable
#define honce_t INIT_ONCE
#define HONCE_INIT INIT_ONCE_STATIC_INIT
typedef void (*honce_fn)();
static inline BOOL WINAPI s_once_func(INIT_ONCE* once, PVOID arg, PVOID* _) {
honce_fn fn = (honce_fn)arg;
fn();
return TRUE;
}
static inline void honce(honce_t* once, honce_fn fn) {
PVOID dummy = NULL;
InitOnceExecuteOnce(once, s_once_func, (PVOID)fn, &dummy);
}
#define hsem_t HANDLE
#define hsem_init(psem, value) *(psem) = CreateSemaphore(NULL, value, value+100000, NULL)
#define hsem_destroy(psem) CloseHandle(*(psem))
#define hsem_wait(psem) WaitForSingleObject(*(psem), INFINITE)
#define hsem_post(psem) ReleaseSemaphore(*(psem), 1, NULL)
// true: WAIT_OBJECT_0
// false: WAIT_OBJECT_TIMEOUT
#define hsem_wait_for(psem, ms) ( WaitForSingleObject(*(psem), ms) == WAIT_OBJECT_0 )
#else
#define hmutex_t pthread_mutex_t
#define hmutex_init(pmutex) pthread_mutex_init(pmutex, NULL)
#define hmutex_destroy pthread_mutex_destroy
#define hmutex_lock pthread_mutex_lock
#define hmutex_unlock pthread_mutex_unlock
#define hrecursive_mutex_t pthread_mutex_t
#define hrecursive_mutex_init(pmutex) \
do {\
pthread_mutexattr_t attr;\
pthread_mutexattr_init(&attr);\
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);\
pthread_mutex_init(pmutex, &attr);\
} while(0)
#define hrecursive_mutex_destroy pthread_mutex_destroy
#define hrecursive_mutex_lock pthread_mutex_lock
#define hrecursive_mutex_unlock pthread_mutex_unlock
#if HAVE_PTHREAD_SPIN_LOCK
#define hspinlock_t pthread_spinlock_t
#define hspinlock_init(pspin) pthread_spin_init(pspin, PTHREAD_PROCESS_PRIVATE)
#define hspinlock_destroy pthread_spin_destroy
#define hspinlock_lock pthread_spin_lock
#define hspinlock_unlock pthread_spin_unlock
#else
#define hspinlock_t pthread_mutex_t
#define hspinlock_init(pmutex) pthread_mutex_init(pmutex, NULL)
#define hspinlock_destroy pthread_mutex_destroy
#define hspinlock_lock pthread_mutex_lock
#define hspinlock_unlock pthread_mutex_unlock
#endif
#define hrwlock_t pthread_rwlock_t
#define hrwlock_init(prwlock) pthread_rwlock_init(prwlock, NULL)
#define hrwlock_destroy pthread_rwlock_destroy
#define hrwlock_rdlock pthread_rwlock_rdlock
#define hrwlock_rdunlock pthread_rwlock_unlock
#define hrwlock_wrlock pthread_rwlock_wrlock
#define hrwlock_wrunlock pthread_rwlock_unlock
#define htimed_mutex_t pthread_mutex_t
#define htimed_mutex_init(pmutex) pthread_mutex_init(pmutex, NULL)
#define htimed_mutex_destroy pthread_mutex_destroy
#define htimed_mutex_lock pthread_mutex_lock
#define htimed_mutex_unlock pthread_mutex_unlock
static inline void timespec_after(struct timespec* ts, unsigned int ms) {
struct timeval tv;
gettimeofday(&tv, NULL);
ts->tv_sec = tv.tv_sec + ms / 1000;
ts->tv_nsec = tv.tv_usec * 1000 + ms % 1000 * 1000000;
if (ts->tv_nsec >= 1000000000) {
ts->tv_nsec -= 1000000000;
ts->tv_sec += 1;
}
}
// true: OK
// false: ETIMEDOUT
static inline int htimed_mutex_lock_for(htimed_mutex_t* mutex, unsigned int ms) {
#if HAVE_PTHREAD_MUTEX_TIMEDLOCK
struct timespec ts;
timespec_after(&ts, ms);
return pthread_mutex_timedlock(mutex, &ts) != ETIMEDOUT;
#else
int ret = 0;
unsigned int end = gettick_ms() + ms;
while ((ret = pthread_mutex_trylock(mutex)) != 0) {
if (gettick_ms() >= end) {
break;
}
hv_msleep(1);
}
return ret == 0;
#endif
}
#define hcondvar_t pthread_cond_t
#define hcondvar_init(pcond) pthread_cond_init(pcond, NULL)
#define hcondvar_destroy pthread_cond_destroy
#define hcondvar_wait pthread_cond_wait
#define hcondvar_signal pthread_cond_signal
#define hcondvar_broadcast pthread_cond_broadcast
// true: OK
// false: ETIMEDOUT
static inline int hcondvar_wait_for(hcondvar_t* cond, hmutex_t* mutex, unsigned int ms) {
struct timespec ts;
timespec_after(&ts, ms);
return pthread_cond_timedwait(cond, mutex, &ts) != ETIMEDOUT;
}
#define honce_t pthread_once_t
#define HONCE_INIT PTHREAD_ONCE_INIT
#define honce pthread_once
#include <semaphore.h>
#define hsem_t sem_t
#define hsem_init(psem, value) sem_init(psem, 0, value)
#define hsem_destroy sem_destroy
#define hsem_wait sem_wait
#define hsem_post sem_post
// true: OK
// false: ETIMEDOUT
static inline int hsem_wait_for(hsem_t* sem, unsigned int ms) {
#if HAVE_SEM_TIMEDWAIT
struct timespec ts;
timespec_after(&ts, ms);
return sem_timedwait(sem, &ts) != ETIMEDOUT;
#else
int ret = 0;
unsigned int end = gettick_ms() + ms;
while ((ret = sem_trywait(sem)) != 0) {
if (gettick_ms() >= end) {
break;
}
hv_msleep(1);
}
return ret == 0;
#endif
}
#endif
END_EXTERN_C
#ifdef __cplusplus
#include <mutex>
#include <condition_variable>
// using std::mutex;
// NOTE: test std::timed_mutex incorrect in some platforms, use htimed_mutex_t
// using std::timed_mutex;
using std::condition_variable;
using std::lock_guard;
using std::unique_lock;
BEGIN_NAMESPACE_HV
class MutexLock {
public:
MutexLock() { hmutex_init(&_mutex); }
~MutexLock() { hmutex_destroy(&_mutex); }
void lock() { hmutex_lock(&_mutex); }
void unlock() { hmutex_unlock(&_mutex); }
protected:
hmutex_t _mutex;
};
class SpinLock {
public:
SpinLock() { hspinlock_init(&_spin); }
~SpinLock() { hspinlock_destroy(&_spin); }
void lock() { hspinlock_lock(&_spin); }
void unlock() { hspinlock_unlock(&_spin); }
protected:
hspinlock_t _spin;
};
class RWLock {
public:
RWLock() { hrwlock_init(&_rwlock); }
~RWLock() { hrwlock_destroy(&_rwlock); }
void rdlock() { hrwlock_rdlock(&_rwlock); }
void rdunlock() { hrwlock_rdunlock(&_rwlock); }
void wrlock() { hrwlock_wrlock(&_rwlock); }
void wrunlock() { hrwlock_wrunlock(&_rwlock); }
void lock() { rdlock(); }
void unlock() { rdunlock(); }
protected:
hrwlock_t _rwlock;
};
template<class T>
class LockGuard {
public:
LockGuard(T& t) : _lock(t) { _lock.lock(); }
~LockGuard() { _lock.unlock(); }
protected:
T& _lock;
};
END_NAMESPACE_HV
// same as java synchronized(lock) { ... }
#define synchronized(lock) for (std::lock_guard<std::mutex> _lock_(lock), *p = &_lock_; p != NULL; p = NULL)
#endif // __cplusplus
#endif // HV_MUTEX_H_

View File

@ -0,0 +1,330 @@
#ifndef HV_PLATFORM_H_
#define HV_PLATFORM_H_
#include "hconfig.h"
// OS
#if defined(WIN64) || defined(_WIN64)
#define OS_WIN64
#define OS_WIN32
#elif defined(WIN32)|| defined(_WIN32)
#define OS_WIN32
#elif defined(ANDROID) || defined(__ANDROID__)
#define OS_ANDROID
#define OS_LINUX
#elif defined(linux) || defined(__linux) || defined(__linux__)
#define OS_LINUX
#elif defined(__APPLE__) && (defined(__GNUC__) || defined(__xlC__) || defined(__xlc__))
#include <TargetConditionals.h>
#if defined(TARGET_OS_MAC) && TARGET_OS_MAC
#define OS_MAC
#elif defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
#define OS_IOS
#endif
#define OS_DARWIN
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#define OS_FREEBSD
#define OS_BSD
#elif defined(__NetBSD__)
#define OS_NETBSD
#define OS_BSD
#elif defined(__OpenBSD__)
#define OS_OPENBSD
#define OS_BSD
#elif defined(sun) || defined(__sun) || defined(__sun__)
#define OS_SOLARIS
#else
#warning "Untested operating system platform!"
#endif
#if defined(OS_WIN32) || defined(OS_WIN64)
#undef OS_UNIX
#define OS_WIN
#else
#define OS_UNIX
#endif
// ARCH
#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(_M_X64)
#define ARCH_X64
#define ARCH_X86_64
#elif defined(__i386) || defined(__i386__) || defined(_M_IX86)
#define ARCH_X86
#define ARCH_X86_32
#elif defined(__aarch64__) || defined(__ARM64__) || defined(_M_ARM64)
#define ARCH_ARM64
#elif defined(__arm__) || defined(_M_ARM)
#define ARCH_ARM
#elif defined(__mips64__)
#define ARCH_MIPS64
#elif defined(__mips__)
#define ARCH_MIPS
#else
#warning "Untested hardware architecture!"
#endif
// COMPILER
#if defined (_MSC_VER)
#define COMPILER_MSVC
#if (_MSC_VER < 1200) // Visual C++ 6.0
#define MSVS_VERSION 1998
#define MSVC_VERSION 60
#elif (_MSC_VER >= 1200) && (_MSC_VER < 1300) // Visual Studio 2002, MSVC++ 7.0
#define MSVS_VERSION 2002
#define MSVC_VERSION 70
#elif (_MSC_VER >= 1300) && (_MSC_VER < 1400) // Visual Studio 2003, MSVC++ 7.1
#define MSVS_VERSION 2003
#define MSVC_VERSION 71
#elif (_MSC_VER >= 1400) && (_MSC_VER < 1500) // Visual Studio 2005, MSVC++ 8.0
#define MSVS_VERSION 2005
#define MSVC_VERSION 80
#elif (_MSC_VER >= 1500) && (_MSC_VER < 1600) // Visual Studio 2008, MSVC++ 9.0
#define MSVS_VERSION 2008
#define MSVC_VERSION 90
#elif (_MSC_VER >= 1600) && (_MSC_VER < 1700) // Visual Studio 2010, MSVC++ 10.0
#define MSVS_VERSION 2010
#define MSVC_VERSION 100
#elif (_MSC_VER >= 1700) && (_MSC_VER < 1800) // Visual Studio 2012, MSVC++ 11.0
#define MSVS_VERSION 2012
#define MSVC_VERSION 110
#elif (_MSC_VER >= 1800) && (_MSC_VER < 1900) // Visual Studio 2013, MSVC++ 12.0
#define MSVS_VERSION 2013
#define MSVC_VERSION 120
#elif (_MSC_VER >= 1900) && (_MSC_VER < 1910) // Visual Studio 2015, MSVC++ 14.0
#define MSVS_VERSION 2015
#define MSVC_VERSION 140
#elif (_MSC_VER >= 1910) && (_MSC_VER < 1920) // Visual Studio 2017, MSVC++ 15.0
#define MSVS_VERSION 2017
#define MSVC_VERSION 150
#elif (_MSC_VER >= 1920) && (_MSC_VER < 2000) // Visual Studio 2019, MSVC++ 16.0
#define MSVS_VERSION 2019
#define MSVC_VERSION 160
#endif
#undef HAVE_STDATOMIC_H
#define HAVE_STDATOMIC_H 0
#undef HAVE_SYS_TIME_H
#define HAVE_SYS_TIME_H 0
#undef HAVE_PTHREAD_H
#define HAVE_PTHREAD_H 0
#pragma warning (disable: 4018) // signed/unsigned comparison
#pragma warning (disable: 4100) // unused param
#pragma warning (disable: 4102) // unreferenced label
#pragma warning (disable: 4244) // conversion loss of data
#pragma warning (disable: 4267) // size_t => int
#pragma warning (disable: 4819) // Unicode
#pragma warning (disable: 4996) // _CRT_SECURE_NO_WARNINGS
#elif defined(__GNUC__)
#define COMPILER_GCC
#ifndef _GNU_SOURCE
#define _GNU_SOURCE 1
#endif
#elif defined(__clang__)
#define COMPILER_CLANG
#elif defined(__MINGW32__) || defined(__MINGW64__)
#define COMPILER_MINGW
#elif defined(__MSYS__)
#define COMPILER_MSYS
#elif defined(__CYGWIN__)
#define COMPILER_CYGWIN
#else
#warning "Untested compiler!"
#endif
// headers
#ifdef OS_WIN
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef _CRT_NONSTDC_NO_DEPRECATE
#define _CRT_NONSTDC_NO_DEPRECATE
#endif
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#endif
#include <winsock2.h>
#include <ws2tcpip.h> // for inet_pton,inet_ntop
#include <windows.h>
#include <process.h> // for getpid,exec
#include <direct.h> // for mkdir,rmdir,chdir,getcwd
#include <io.h> // for open,close,read,write,lseek,tell
#define hv_sleep(s) Sleep((s) * 1000)
#define hv_msleep(ms) Sleep(ms)
#define hv_usleep(us) Sleep((us) / 1000)
#define hv_delay(ms) hv_msleep(ms)
#define hv_mkdir(dir) mkdir(dir)
// access
#ifndef F_OK
#define F_OK 0 /* test for existence of file */
#endif
#ifndef X_OK
#define X_OK (1<<0) /* test for execute or search permission */
#endif
#ifndef W_OK
#define W_OK (1<<1) /* test for write permission */
#endif
#ifndef R_OK
#define R_OK (1<<2) /* test for read permission */
#endif
// stat
#ifndef S_ISREG
#define S_ISREG(st_mode) (((st_mode) & S_IFMT) == S_IFREG)
#endif
#ifndef S_ISDIR
#define S_ISDIR(st_mode) (((st_mode) & S_IFMT) == S_IFDIR)
#endif
#else
#include <unistd.h>
#include <dirent.h> // for mkdir,rmdir,chdir,getcwd
// socket
#include <sys/socket.h>
#include <sys/select.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netdb.h> // for gethostbyname
#define hv_sleep(s) sleep(s)
#define hv_msleep(ms) usleep((ms) * 1000)
#define hv_usleep(us) usleep(us)
#define hv_delay(ms) hv_msleep(ms)
#define hv_mkdir(dir) mkdir(dir, 0777)
#endif
#ifdef _MSC_VER
typedef int pid_t;
typedef int gid_t;
typedef int uid_t;
#define strcasecmp stricmp
#define strncasecmp strnicmp
#else
typedef int BOOL;
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef void* HANDLE;
#include <strings.h>
#define stricmp strcasecmp
#define strnicmp strncasecmp
#endif
// ENDIAN
#ifndef BIG_ENDIAN
#define BIG_ENDIAN 4321
#endif
#ifndef LITTLE_ENDIAN
#define LITTLE_ENDIAN 1234
#endif
#ifndef NET_ENDIAN
#define NET_ENDIAN BIG_ENDIAN
#endif
// BYTE_ORDER
#ifndef BYTE_ORDER
#if defined(__BYTE_ORDER)
#define BYTE_ORDER __BYTE_ORDER
#elif defined(__BYTE_ORDER__)
#define BYTE_ORDER __BYTE_ORDER__
#elif defined(ARCH_X86) || defined(ARCH_X86_64) || \
defined(__ARMEL__) || defined(__AARCH64EL__) || \
defined(__MIPSEL) || defined(__MIPS64EL)
#define BYTE_ORDER LITTLE_ENDIAN
#elif defined(__ARMEB__) || defined(__AARCH64EB__) || \
defined(__MIPSEB) || defined(__MIPS64EB)
#define BYTE_ORDER BIG_ENDIAN
#elif defined(OS_WIN)
#define BYTE_ORDER LITTLE_ENDIAN
#else
#warning "Unknown byte order!"
#endif
#endif
// ANSI C
#include <assert.h>
#include <stddef.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <float.h>
#include <limits.h>
#include <errno.h>
#include <time.h>
#include <math.h>
#include <signal.h>
#ifndef __cplusplus
#if HAVE_STDBOOL_H
#include <stdbool.h>
#else
#ifndef bool
#define bool char
#endif
#ifndef true
#define true 1
#endif
#ifndef false
#define false 0
#endif
#endif
#endif
#if HAVE_STDINT_H
#include <stdint.h>
#elif defined(_MSC_VER) && _MSC_VER < 1700
typedef __int8 int8_t;
typedef __int16 int16_t;
typedef __int32 int32_t;
typedef __int64 int64_t;
typedef unsigned __int8 uint8_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;
typedef unsigned __int64 uint64_t;
#endif
typedef float float32_t;
typedef double float64_t;
typedef int (*method_t)(void* userdata);
typedef void (*procedure_t)(void* userdata);
#if HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#if HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#if HAVE_SYS_TIME_H
#include <sys/time.h> // for gettimeofday
#endif
#if HAVE_FCNTL_H
#include <fcntl.h>
#endif
#if HAVE_PTHREAD_H
#include <pthread.h>
#endif
#endif // HV_PLATFORM_H_

69
external/libhv/libhv-1.3.2/base/hproc.h vendored Normal file
View File

@ -0,0 +1,69 @@
#ifndef HV_PROC_H_
#define HV_PROC_H_
#include "hplatform.h"
typedef struct proc_ctx_s {
pid_t pid; // tid in Windows
time_t start_time;
int spawn_cnt;
procedure_t init;
void* init_userdata;
procedure_t proc;
void* proc_userdata;
procedure_t exit;
void* exit_userdata;
} proc_ctx_t;
static inline void hproc_run(proc_ctx_t* ctx) {
if (ctx->init) {
ctx->init(ctx->init_userdata);
}
if (ctx->proc) {
ctx->proc(ctx->proc_userdata);
}
if (ctx->exit) {
ctx->exit(ctx->exit_userdata);
}
}
#ifdef OS_UNIX
// unix use multi-processes
static inline int hproc_spawn(proc_ctx_t* ctx) {
++ctx->spawn_cnt;
ctx->start_time = time(NULL);
pid_t pid = fork();
if (pid < 0) {
perror("fork");
return -1;
} else if (pid == 0) {
// child process
ctx->pid = getpid();
hproc_run(ctx);
exit(0);
} else if (pid > 0) {
// parent process
ctx->pid = pid;
}
return pid;
}
#elif defined(OS_WIN)
// win32 use multi-threads
static void win_thread(void* userdata) {
proc_ctx_t* ctx = (proc_ctx_t*)userdata;
ctx->pid = GetCurrentThreadId(); // tid in Windows
hproc_run(ctx);
}
static inline int hproc_spawn(proc_ctx_t* ctx) {
++ctx->spawn_cnt;
ctx->start_time = time(NULL);
HANDLE h = (HANDLE)_beginthread(win_thread, 0, ctx);
if (h == NULL) {
return -1;
}
ctx->pid = GetThreadId(h); // tid in Windows
return ctx->pid;
}
#endif
#endif // HV_PROC_H_

View File

@ -0,0 +1,408 @@
#include "hsocket.h"
#include "hdef.h"
#ifdef OS_WIN
#include "hatomic.h"
static hatomic_flag_t s_wsa_initialized = HATOMIC_FLAG_INIT;
void WSAInit() {
if (!hatomic_flag_test_and_set(&s_wsa_initialized)) {
WSADATA wsadata;
WSAStartup(MAKEWORD(2, 2), &wsadata);
}
}
void WSADeinit() {
if (hatomic_flag_test_and_set(&s_wsa_initialized)) {
hatomic_flag_clear(&s_wsa_initialized);
WSACleanup();
}
}
#endif
static inline int socket_errno_negative(int sockfd) {
int err = socket_errno();
if (sockfd >= 0) closesocket(sockfd);
return err > 0 ? -err : -1;
}
const char* socket_strerror(int err) {
#ifdef OS_WIN
static char buffer[128];
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_MAX_WIDTH_MASK,
0, ABS(err), 0, buffer, sizeof(buffer), NULL);
return buffer;
#else
return strerror(ABS(err));
#endif
}
bool is_ipv4(const char* host) {
struct sockaddr_in sin;
return inet_pton(AF_INET, host, &sin) == 1;
}
bool is_ipv6(const char* host) {
struct sockaddr_in6 sin6;
return inet_pton(AF_INET6, host, &sin6) == 1;
}
int ResolveAddr(const char* host, sockaddr_u* addr) {
#ifdef OS_WIN
WSAInit();
#endif
if (inet_pton(AF_INET, host, &addr->sin.sin_addr) == 1) {
addr->sa.sa_family = AF_INET; // host is ipv4, so easy ;)
return 0;
}
if (inet_pton(AF_INET6, host, &addr->sin6.sin6_addr) == 1) {
addr->sa.sa_family = AF_INET6; // host is ipv6
}
struct addrinfo* ais = NULL;
int ret = getaddrinfo(host, NULL, NULL, &ais);
if (ret != 0 || ais == NULL || ais->ai_addr == NULL || ais->ai_addrlen == 0) {
printd("unknown host: %s err:%d:%s\n", host, ret, gai_strerror(ret));
return ret;
}
struct addrinfo* pai = ais;
while (pai != NULL) {
if (pai->ai_family == AF_INET) break;
pai = pai->ai_next;
}
if (pai == NULL) pai = ais;
memcpy(addr, pai->ai_addr, pai->ai_addrlen);
freeaddrinfo(ais);
return 0;
}
const char* sockaddr_ip(sockaddr_u* addr, char *ip, int len) {
if (addr->sa.sa_family == AF_INET) {
return inet_ntop(AF_INET, &addr->sin.sin_addr, ip, len);
}
else if (addr->sa.sa_family == AF_INET6) {
return inet_ntop(AF_INET6, &addr->sin6.sin6_addr, ip, len);
}
return ip;
}
uint16_t sockaddr_port(sockaddr_u* addr) {
uint16_t port = 0;
if (addr->sa.sa_family == AF_INET) {
port = ntohs(addr->sin.sin_port);
}
else if (addr->sa.sa_family == AF_INET6) {
port = ntohs(addr->sin6.sin6_port);
}
return port;
}
int sockaddr_set_ip(sockaddr_u* addr, const char* host) {
if (!host || *host == '\0') {
addr->sin.sin_family = AF_INET;
addr->sin.sin_addr.s_addr = htonl(INADDR_ANY);
return 0;
}
return ResolveAddr(host, addr);
}
void sockaddr_set_port(sockaddr_u* addr, int port) {
if (addr->sa.sa_family == AF_INET) {
addr->sin.sin_port = htons(port);
}
else if (addr->sa.sa_family == AF_INET6) {
addr->sin6.sin6_port = htons(port);
}
}
int sockaddr_set_ipport(sockaddr_u* addr, const char* host, int port) {
#ifdef ENABLE_UDS
if (port < 0) {
sockaddr_set_path(addr, host);
return 0;
}
#endif
int ret = sockaddr_set_ip(addr, host);
if (ret != 0) return ret;
sockaddr_set_port(addr, port);
// SOCKADDR_PRINT(addr);
return 0;
}
socklen_t sockaddr_len(sockaddr_u* addr) {
if (addr->sa.sa_family == AF_INET) {
return sizeof(struct sockaddr_in);
}
else if (addr->sa.sa_family == AF_INET6) {
return sizeof(struct sockaddr_in6);
}
#ifdef ENABLE_UDS
else if (addr->sa.sa_family == AF_UNIX) {
return sizeof(struct sockaddr_un);
}
#endif
return sizeof(sockaddr_u);
}
const char* sockaddr_str(sockaddr_u* addr, char* buf, int len) {
char ip[SOCKADDR_STRLEN] = {0};
uint16_t port = 0;
if (addr->sa.sa_family == AF_INET) {
inet_ntop(AF_INET, &addr->sin.sin_addr, ip, len);
port = ntohs(addr->sin.sin_port);
snprintf(buf, len, "%s:%d", ip, port);
}
else if (addr->sa.sa_family == AF_INET6) {
inet_ntop(AF_INET6, &addr->sin6.sin6_addr, ip, len);
port = ntohs(addr->sin6.sin6_port);
snprintf(buf, len, "[%s]:%d", ip, port);
}
#ifdef ENABLE_UDS
else if (addr->sa.sa_family == AF_UNIX) {
snprintf(buf, len, "%s", addr->sun.sun_path);
}
#endif
return buf;
}
static int sockaddr_bind(sockaddr_u* localaddr, int type) {
// socket -> setsockopt -> bind
#ifdef SOCK_CLOEXEC
type |= SOCK_CLOEXEC;
#endif
int sockfd = socket(localaddr->sa.sa_family, type, 0);
if (sockfd < 0) {
perror("socket");
goto error;
}
#ifdef OS_UNIX
so_reuseaddr(sockfd, 1);
// so_reuseport(sockfd, 1);
#endif
if (localaddr->sa.sa_family == AF_INET6) {
ip_v6only(sockfd, 0);
}
if (bind(sockfd, &localaddr->sa, sockaddr_len(localaddr)) < 0) {
perror("bind");
goto error;
}
return sockfd;
error:
return socket_errno_negative(sockfd);
}
static int sockaddr_connect(sockaddr_u* peeraddr, int nonblock) {
// socket -> nonblocking -> connect
int ret = 0;
int connfd = socket(peeraddr->sa.sa_family, SOCK_STREAM, 0);
if (connfd < 0) {
perror("socket");
goto error;
}
if (nonblock) {
nonblocking(connfd);
}
ret = connect(connfd, &peeraddr->sa, sockaddr_len(peeraddr));
#ifdef OS_WIN
if (ret < 0 && socket_errno() != WSAEWOULDBLOCK) {
#else
if (ret < 0 && socket_errno() != EINPROGRESS) {
#endif
// perror("connect");
goto error;
}
return connfd;
error:
return socket_errno_negative(connfd);
}
static int ListenFD(int sockfd) {
if (sockfd < 0) return sockfd;
if (listen(sockfd, SOMAXCONN) < 0) {
perror("listen");
return socket_errno_negative(sockfd);
}
return sockfd;
}
static int ConnectFDTimeout(int connfd, int ms) {
int err = 0;
socklen_t optlen = sizeof(err);
struct timeval tv = { ms / 1000, (ms % 1000) * 1000 };
fd_set writefds;
FD_ZERO(&writefds);
FD_SET(connfd, &writefds);
int ret = select(connfd+1, 0, &writefds, 0, &tv);
if (ret < 0) {
perror("select");
goto error;
}
if (ret == 0) {
errno = ETIMEDOUT;
goto error;
}
if (getsockopt(connfd, SOL_SOCKET, SO_ERROR, (char*)&err, &optlen) < 0 || err != 0) {
if (err != 0) errno = err;
goto error;
}
blocking(connfd);
return connfd;
error:
return socket_errno_negative(connfd);
}
int Bind(int port, const char* host, int type) {
#ifdef OS_WIN
WSAInit();
#endif
sockaddr_u localaddr;
memset(&localaddr, 0, sizeof(localaddr));
int ret = sockaddr_set_ipport(&localaddr, host, port);
if (ret != 0) {
return NABS(ret);
}
return sockaddr_bind(&localaddr, type);
}
int Listen(int port, const char* host) {
int sockfd = Bind(port, host, SOCK_STREAM);
if (sockfd < 0) return sockfd;
return ListenFD(sockfd);
}
int Connect(const char* host, int port, int nonblock) {
#ifdef OS_WIN
WSAInit();
#endif
sockaddr_u peeraddr;
memset(&peeraddr, 0, sizeof(peeraddr));
int ret = sockaddr_set_ipport(&peeraddr, host, port);
if (ret != 0) {
return NABS(ret);
}
return sockaddr_connect(&peeraddr, nonblock);
}
int ConnectNonblock(const char* host, int port) {
return Connect(host, port, 1);
}
int ConnectTimeout(const char* host, int port, int ms) {
int connfd = Connect(host, port, 1);
if (connfd < 0) return connfd;
return ConnectFDTimeout(connfd, ms);
}
#ifdef ENABLE_UDS
int BindUnix(const char* path, int type) {
sockaddr_u localaddr;
memset(&localaddr, 0, sizeof(localaddr));
sockaddr_set_path(&localaddr, path);
return sockaddr_bind(&localaddr, type);
}
int ListenUnix(const char* path) {
int sockfd = BindUnix(path, SOCK_STREAM);
if (sockfd < 0) return sockfd;
return ListenFD(sockfd);
}
int ConnectUnix(const char* path, int nonblock) {
sockaddr_u peeraddr;
memset(&peeraddr, 0, sizeof(peeraddr));
sockaddr_set_path(&peeraddr, path);
return sockaddr_connect(&peeraddr, nonblock);
}
int ConnectUnixNonblock(const char* path) {
return ConnectUnix(path, 1);
}
int ConnectUnixTimeout(const char* path, int ms) {
int connfd = ConnectUnix(path, 1);
if (connfd < 0) return connfd;
return ConnectFDTimeout(connfd, ms);
}
#endif
int Socketpair(int family, int type, int protocol, int sv[2]) {
#if defined(OS_UNIX) && HAVE_SOCKETPAIR
return socketpair(AF_LOCAL, type, protocol, sv);
#endif
if (family != AF_INET || type != SOCK_STREAM) {
return -1;
}
#ifdef OS_WIN
WSAInit();
#endif
int listenfd, connfd, acceptfd;
listenfd = connfd = acceptfd = INVALID_SOCKET;
struct sockaddr_in localaddr;
socklen_t addrlen = sizeof(localaddr);
memset(&localaddr, 0, addrlen);
localaddr.sin_family = AF_INET;
localaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
localaddr.sin_port = 0;
// listener
listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (listenfd < 0) {
perror("socket");
goto error;
}
if (bind(listenfd, (struct sockaddr*)&localaddr, addrlen) < 0) {
perror("bind");
goto error;
}
if (listen(listenfd, 1) < 0) {
perror("listen");
goto error;
}
if (getsockname(listenfd, (struct sockaddr*)&localaddr, &addrlen) < 0) {
perror("getsockname");
goto error;
}
// connector
connfd = socket(AF_INET, SOCK_STREAM, 0);
if (connfd < 0) {
perror("socket");
goto error;
}
if (connect(connfd, (struct sockaddr*)&localaddr, addrlen) < 0) {
perror("connect");
goto error;
}
// acceptor
acceptfd = accept(listenfd, (struct sockaddr*)&localaddr, &addrlen);
if (acceptfd < 0) {
perror("accept");
goto error;
}
closesocket(listenfd);
sv[0] = connfd;
sv[1] = acceptfd;
return 0;
error:
if (listenfd != INVALID_SOCKET) {
closesocket(listenfd);
}
if (connfd != INVALID_SOCKET) {
closesocket(connfd);
}
if (acceptfd != INVALID_SOCKET) {
closesocket(acceptfd);
}
return -1;
}

View File

@ -0,0 +1,272 @@
#ifndef HV_SOCKET_H_
#define HV_SOCKET_H_
#include "hexport.h"
#include "hplatform.h"
#ifdef ENABLE_UDS
#ifdef OS_WIN
#include <afunix.h> // import struct sockaddr_un
#else
#include <sys/un.h> // import struct sockaddr_un
#endif
#endif
#ifdef _MSC_VER
#pragma comment(lib, "ws2_32.lib")
#endif
#define LOCALHOST "127.0.0.1"
#define ANYADDR "0.0.0.0"
BEGIN_EXTERN_C
HV_INLINE int socket_errno() {
#ifdef OS_WIN
return WSAGetLastError();
#else
return errno;
#endif
}
HV_EXPORT const char* socket_strerror(int err);
#ifdef OS_WIN
typedef int socklen_t;
void WSAInit();
void WSADeinit();
HV_INLINE int blocking(int sockfd) {
unsigned long nb = 0;
return ioctlsocket(sockfd, FIONBIO, &nb);
}
HV_INLINE int nonblocking(int sockfd) {
unsigned long nb = 1;
return ioctlsocket(sockfd, FIONBIO, &nb);
}
#undef EAGAIN
#define EAGAIN WSAEWOULDBLOCK
#undef EINPROGRESS
#define EINPROGRESS WSAEINPROGRESS
#undef EINTR
#define EINTR WSAEINTR
#undef ENOTSOCK
#define ENOTSOCK WSAENOTSOCK
#undef EMSGSIZE
#define EMSGSIZE WSAEMSGSIZE
#else
#define blocking(s) fcntl(s, F_SETFL, fcntl(s, F_GETFL) & ~O_NONBLOCK)
#define nonblocking(s) fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK)
typedef int SOCKET;
#define INVALID_SOCKET -1
HV_INLINE int closesocket(int sockfd) {
return close(sockfd);
}
#endif
#ifndef SAFE_CLOSESOCKET
#define SAFE_CLOSESOCKET(fd) do {if ((fd) >= 0) {closesocket(fd); (fd) = -1;}} while(0)
#endif
//-----------------------------sockaddr_u----------------------------------------------
typedef union {
struct sockaddr sa;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
#ifdef ENABLE_UDS
struct sockaddr_un sun;
#endif
} sockaddr_u;
HV_EXPORT bool is_ipv4(const char* host);
HV_EXPORT bool is_ipv6(const char* host);
HV_INLINE bool is_ipaddr(const char* host) {
return is_ipv4(host) || is_ipv6(host);
}
// @param host: domain or ip
// @retval 0:succeed
HV_EXPORT int ResolveAddr(const char* host, sockaddr_u* addr);
HV_EXPORT const char* sockaddr_ip(sockaddr_u* addr, char *ip, int len);
HV_EXPORT uint16_t sockaddr_port(sockaddr_u* addr);
HV_EXPORT int sockaddr_set_ip(sockaddr_u* addr, const char* host);
HV_EXPORT void sockaddr_set_port(sockaddr_u* addr, int port);
HV_EXPORT int sockaddr_set_ipport(sockaddr_u* addr, const char* host, int port);
HV_EXPORT socklen_t sockaddr_len(sockaddr_u* addr);
HV_EXPORT const char* sockaddr_str(sockaddr_u* addr, char* buf, int len);
//#define INET_ADDRSTRLEN 16
//#define INET6_ADDRSTRLEN 46
#ifdef ENABLE_UDS
#define SOCKADDR_STRLEN sizeof(((struct sockaddr_un*)(NULL))->sun_path)
HV_INLINE void sockaddr_set_path(sockaddr_u* addr, const char* path) {
addr->sa.sa_family = AF_UNIX;
strncpy(addr->sun.sun_path, path, sizeof(addr->sun.sun_path));
}
#else
#define SOCKADDR_STRLEN 64 // ipv4:port | [ipv6]:port
#endif
HV_INLINE void sockaddr_print(sockaddr_u* addr) {
char buf[SOCKADDR_STRLEN] = {0};
sockaddr_str(addr, buf, sizeof(buf));
puts(buf);
}
#define SOCKADDR_LEN(addr) sockaddr_len((sockaddr_u*)addr)
#define SOCKADDR_STR(addr, buf) sockaddr_str((sockaddr_u*)addr, buf, sizeof(buf))
#define SOCKADDR_PRINT(addr) sockaddr_print((sockaddr_u*)addr)
//=====================================================================================
// socket -> setsockopt -> bind
// @param type: SOCK_STREAM(tcp) SOCK_DGRAM(udp)
// @return sockfd
HV_EXPORT int Bind(int port, const char* host DEFAULT(ANYADDR), int type DEFAULT(SOCK_STREAM));
// Bind -> listen
// @return listenfd
HV_EXPORT int Listen(int port, const char* host DEFAULT(ANYADDR));
// @return connfd
// ResolveAddr -> socket -> nonblocking -> connect
HV_EXPORT int Connect(const char* host, int port, int nonblock DEFAULT(0));
// Connect(host, port, 1)
HV_EXPORT int ConnectNonblock(const char* host, int port);
// Connect(host, port, 1) -> select -> blocking
#define DEFAULT_CONNECT_TIMEOUT 10000 // ms
HV_EXPORT int ConnectTimeout(const char* host, int port, int ms DEFAULT(DEFAULT_CONNECT_TIMEOUT));
#ifdef ENABLE_UDS
HV_EXPORT int BindUnix(const char* path, int type DEFAULT(SOCK_STREAM));
HV_EXPORT int ListenUnix(const char* path);
HV_EXPORT int ConnectUnix(const char* path, int nonblock DEFAULT(0));
HV_EXPORT int ConnectUnixNonblock(const char* path);
HV_EXPORT int ConnectUnixTimeout(const char* path, int ms DEFAULT(DEFAULT_CONNECT_TIMEOUT));
#endif
// Just implement Socketpair(AF_INET, SOCK_STREAM, 0, sv);
HV_EXPORT int Socketpair(int family, int type, int protocol, int sv[2]);
HV_INLINE int tcp_nodelay(int sockfd, int on DEFAULT(1)) {
return setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (const char*)&on, sizeof(int));
}
HV_INLINE int tcp_nopush(int sockfd, int on DEFAULT(1)) {
#ifdef TCP_NOPUSH
return setsockopt(sockfd, IPPROTO_TCP, TCP_NOPUSH, (const char*)&on, sizeof(int));
#elif defined(TCP_CORK)
return setsockopt(sockfd, IPPROTO_TCP, TCP_CORK, (const char*)&on, sizeof(int));
#else
return 0;
#endif
}
HV_INLINE int tcp_keepalive(int sockfd, int on DEFAULT(1), int delay DEFAULT(60)) {
if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (const char*)&on, sizeof(int)) != 0) {
return socket_errno();
}
#ifdef TCP_KEEPALIVE
return setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE, (const char*)&delay, sizeof(int));
#elif defined(TCP_KEEPIDLE)
// TCP_KEEPIDLE => tcp_keepalive_time
// TCP_KEEPCNT => tcp_keepalive_probes
// TCP_KEEPINTVL => tcp_keepalive_intvl
return setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, (const char*)&delay, sizeof(int));
#else
return 0;
#endif
}
HV_INLINE int udp_broadcast(int sockfd, int on DEFAULT(1)) {
return setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, (const char*)&on, sizeof(int));
}
HV_INLINE int ip_v6only(int sockfd, int on DEFAULT(1)) {
#ifdef IPV6_V6ONLY
return setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&on, sizeof(int));
#else
return 0;
#endif
}
// send timeout
HV_INLINE int so_sndtimeo(int sockfd, int timeout) {
#ifdef OS_WIN
return setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeout, sizeof(int));
#else
struct timeval tv = {timeout/1000, (timeout%1000)*1000};
return setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
#endif
}
// recv timeout
HV_INLINE int so_rcvtimeo(int sockfd, int timeout) {
#ifdef OS_WIN
return setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(int));
#else
struct timeval tv = {timeout/1000, (timeout%1000)*1000};
return setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
#endif
}
// send buffer size
HV_INLINE int so_sndbuf(int sockfd, int len) {
return setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char*)&len, sizeof(int));
}
// recv buffer size
HV_INLINE int so_rcvbuf(int sockfd, int len) {
return setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (const char*)&len, sizeof(int));
}
HV_INLINE int so_reuseaddr(int sockfd, int on DEFAULT(1)) {
#ifdef SO_REUSEADDR
// NOTE: SO_REUSEADDR allow to reuse sockaddr of TIME_WAIT status
return setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(int));
#else
return 0;
#endif
}
HV_INLINE int so_reuseport(int sockfd, int on DEFAULT(1)) {
#ifdef SO_REUSEPORT
// NOTE: SO_REUSEPORT allow multiple sockets to bind same port
return setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, (const char*)&on, sizeof(int));
#else
return 0;
#endif
}
HV_INLINE int so_linger(int sockfd, int timeout DEFAULT(1)) {
#ifdef SO_LINGER
struct linger linger;
if (timeout >= 0) {
linger.l_onoff = 1;
linger.l_linger = timeout;
} else {
linger.l_onoff = 0;
linger.l_linger = 0;
}
// NOTE: SO_LINGER change the default behavior of close, send RST, avoid TIME_WAIT
return setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (const char*)&linger, sizeof(linger));
#else
return 0;
#endif
}
END_EXTERN_C
#endif // HV_SOCKET_H_

View File

@ -0,0 +1,68 @@
#ifndef HV_SYS_INFO_H_
#define HV_SYS_INFO_H_
#include "hplatform.h"
#ifdef OS_LINUX
#include <sys/sysinfo.h>
#endif
#ifdef OS_DARWIN
#include <mach/mach_host.h>
#include <sys/sysctl.h>
#endif
static inline int get_ncpu() {
#ifdef OS_WIN
SYSTEM_INFO si;
GetSystemInfo(&si);
return si.dwNumberOfProcessors;
#else
//return get_nprocs();
//return get_nprocs_conf();
//return sysconf(_SC_NPROCESSORS_ONLN); // processors available
return sysconf(_SC_NPROCESSORS_CONF); // processors configured
#endif
}
typedef struct meminfo_s {
unsigned long total; // KB
unsigned long free; // KB
} meminfo_t;
static inline int get_meminfo(meminfo_t* mem) {
#ifdef OS_WIN
MEMORYSTATUSEX memstat;
memset(&memstat, 0, sizeof(memstat));
memstat.dwLength = sizeof(memstat);
GlobalMemoryStatusEx(&memstat);
mem->total = (unsigned long)(memstat.ullTotalPhys >> 10);
mem->free = (unsigned long)(memstat.ullAvailPhys >> 10);
return 0;
#elif defined(OS_LINUX)
struct sysinfo info;
if (sysinfo(&info) < 0) {
return errno;
}
mem->total = info.totalram * info.mem_unit >> 10;
mem->free = info.freeram * info.mem_unit >> 10;
return 0;
#elif defined(OS_DARWIN)
uint64_t memsize = 0;
size_t size = sizeof(memsize);
int which[2] = {CTL_HW, HW_MEMSIZE};
sysctl(which, 2, &memsize, &size, NULL, 0);
mem->total = memsize >> 10;
vm_statistics_data_t info;
mach_msg_type_number_t count = sizeof(info) / sizeof(integer_t);
host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&info, &count);
mem->free = ((uint64_t)info.free_count * sysconf(_SC_PAGESIZE)) >> 10;
return 0;
#else
(void)(mem);
return -10;
#endif
}
#endif // HV_SYS_INFO_H_

View File

@ -0,0 +1,217 @@
#ifndef HV_THREAD_H_
#define HV_THREAD_H_
#include "hplatform.h"
#ifdef OS_WIN
#define hv_getpid (long)GetCurrentProcessId
#else
#define hv_getpid (long)getpid
#endif
#ifdef OS_WIN
#define hv_gettid (long)GetCurrentThreadId
#elif HAVE_GETTID || defined(OS_ANDROID)
#define hv_gettid (long)gettid
#elif defined(OS_LINUX)
#include <sys/syscall.h>
#define hv_gettid() (long)syscall(SYS_gettid)
#elif defined(OS_DARWIN)
static inline long hv_gettid() {
uint64_t tid = 0;
pthread_threadid_np(NULL, &tid);
return tid;
}
#elif HAVE_PTHREAD_H
#define hv_gettid (long)pthread_self
#else
#define hv_gettid hv_getpid
#endif
/*
#include "hthread.h"
HTHREAD_ROUTINE(thread_demo) {
printf("thread[%ld] start\n", hv_gettid());
hv_delay(3000);
printf("thread[%ld] end\n", hv_gettid());
return 0;
}
int main() {
hthread_t th = hthread_create(thread_demo, NULL);
hthread_join(th);
return 0;
}
*/
#ifdef OS_WIN
typedef HANDLE hthread_t;
typedef DWORD (WINAPI *hthread_routine)(void*);
#define HTHREAD_RETTYPE DWORD
#define HTHREAD_ROUTINE(fname) DWORD WINAPI fname(void* userdata)
static inline hthread_t hthread_create(hthread_routine fn, void* userdata) {
return CreateThread(NULL, 0, fn, userdata, 0, NULL);
}
static inline int hthread_join(hthread_t th) {
WaitForSingleObject(th, INFINITE);
CloseHandle(th);
return 0;
}
#else
typedef pthread_t hthread_t;
typedef void* (*hthread_routine)(void*);
#define HTHREAD_RETTYPE void*
#define HTHREAD_ROUTINE(fname) void* fname(void* userdata)
static inline hthread_t hthread_create(hthread_routine fn, void* userdata) {
pthread_t th;
pthread_create(&th, NULL, fn, userdata);
return th;
}
static inline int hthread_join(hthread_t th) {
return pthread_join(th, NULL);
}
#endif
#ifdef __cplusplus
/************************************************
* HThread
* Status: STOP,RUNNING,PAUSE
* Control: start,stop,pause,resume
* first-level virtual: doTask
* second-level virtual: run
************************************************/
#include <thread>
#include <atomic>
#include <chrono>
class HThread {
public:
enum Status {
STOP,
RUNNING,
PAUSE,
};
enum SleepPolicy {
YIELD,
SLEEP_FOR,
SLEEP_UNTIL,
NO_SLEEP,
};
HThread() {
status = STOP;
status_changed = false;
dotask_cnt = 0;
sleep_policy = YIELD;
sleep_ms = 0;
}
virtual ~HThread() {}
void setStatus(Status stat) {
status_changed = true;
status = stat;
}
void setSleepPolicy(SleepPolicy policy, uint32_t ms = 0) {
sleep_policy = policy;
sleep_ms = ms;
setStatus(status);
}
virtual int start() {
if (status == STOP) {
thread = std::thread([this] {
if (!doPrepare()) return;
setStatus(RUNNING);
run();
setStatus(STOP);
if (!doFinish()) return;
});
}
return 0;
}
virtual int stop() {
if (status != STOP) {
setStatus(STOP);
}
if (thread.joinable()) {
thread.join(); // wait thread exit
}
return 0;
}
virtual int pause() {
if (status == RUNNING) {
setStatus(PAUSE);
}
return 0;
}
virtual int resume() {
if (status == PAUSE) {
setStatus(RUNNING);
}
return 0;
}
virtual void run() {
while (status != STOP) {
while (status == PAUSE) {
std::this_thread::yield();
}
doTask();
++dotask_cnt;
HThread::sleep();
}
}
virtual bool doPrepare() {return true;}
virtual void doTask() {}
virtual bool doFinish() {return true;}
std::thread thread;
std::atomic<Status> status;
uint32_t dotask_cnt;
protected:
void sleep() {
switch (sleep_policy) {
case YIELD:
std::this_thread::yield();
break;
case SLEEP_FOR:
std::this_thread::sleep_for(std::chrono::milliseconds(sleep_ms));
break;
case SLEEP_UNTIL: {
if (status_changed) {
status_changed = false;
base_tp = std::chrono::steady_clock::now();
}
base_tp += std::chrono::milliseconds(sleep_ms);
std::this_thread::sleep_until(base_tp);
}
break;
default: // donothing, go all out.
break;
}
}
SleepPolicy sleep_policy;
uint32_t sleep_ms;
// for SLEEP_UNTIL
std::atomic<bool> status_changed;
std::chrono::steady_clock::time_point base_tp;
};
#endif
#endif // HV_THREAD_H_

290
external/libhv/libhv-1.3.2/base/htime.c vendored Normal file
View File

@ -0,0 +1,290 @@
#include "htime.h"
static const char* s_weekdays[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
static const char* s_months[] = {"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"};
static const uint8_t s_days[] = \
// 1 3 5 7 8 10 12
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
unsigned int gettick_ms() {
#ifdef OS_WIN
return GetTickCount();
#elif HAVE_CLOCK_GETTIME
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
#else
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * 1000 + tv.tv_usec / 1000;
#endif
}
unsigned long long gethrtime_us() {
#ifdef OS_WIN
static LONGLONG s_freq = 0;
if (s_freq == 0) {
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
s_freq = freq.QuadPart;
}
if (s_freq != 0) {
LARGE_INTEGER count;
QueryPerformanceCounter(&count);
return (unsigned long long)(count.QuadPart / (double)s_freq * 1000000);
}
return 0;
#elif defined(OS_SOLARIS)
return gethrtime() / 1000;
#elif HAVE_CLOCK_GETTIME
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec*(unsigned long long)1000000 + ts.tv_nsec / 1000;
#else
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec*(unsigned long long)1000000 + tv.tv_usec;
#endif
}
datetime_t datetime_now() {
#ifdef OS_WIN
SYSTEMTIME tm;
GetLocalTime(&tm);
datetime_t dt;
dt.year = tm.wYear;
dt.month = tm.wMonth;
dt.day = tm.wDay;
dt.hour = tm.wHour;
dt.min = tm.wMinute;
dt.sec = tm.wSecond;
dt.ms = tm.wMilliseconds;
return dt;
#else
struct timeval tv;
gettimeofday(&tv, NULL);
datetime_t dt = datetime_localtime(tv.tv_sec);
dt.ms = tv.tv_usec / 1000;
return dt;
#endif
}
datetime_t datetime_localtime(time_t seconds) {
struct tm* tm = localtime(&seconds);
datetime_t dt;
dt.year = tm->tm_year + 1900;
dt.month = tm->tm_mon + 1;
dt.day = tm->tm_mday;
dt.hour = tm->tm_hour;
dt.min = tm->tm_min;
dt.sec = tm->tm_sec;
return dt;
}
time_t datetime_mktime(datetime_t* dt) {
struct tm tm;
time_t ts;
time(&ts);
struct tm* ptm = localtime(&ts);
memcpy(&tm, ptm, sizeof(struct tm));
tm.tm_year = dt->year - 1900;
tm.tm_mon = dt->month - 1;
tm.tm_mday = dt->day;
tm.tm_hour = dt->hour;
tm.tm_min = dt->min;
tm.tm_sec = dt->sec;
return mktime(&tm);
}
int days_of_month(int month, int year) {
if (month < 1 || month > 12) {
return 0;
}
int days = s_days[month-1];
return (month == 2 && IS_LEAP_YEAR(year)) ? ++days : days;
}
datetime_t* datetime_past(datetime_t* dt, int days) {
assert(days >= 0);
int sub = days;
while (sub) {
if (dt->day > sub) {
dt->day -= sub;
break;
}
sub -= dt->day;
if (--dt->month == 0) {
dt->month = 12;
--dt->year;
}
dt->day = days_of_month(dt->month, dt->year);
}
return dt;
}
datetime_t* datetime_future(datetime_t* dt, int days) {
assert(days >= 0);
int sub = days;
int mdays;
while (sub) {
mdays = days_of_month(dt->month, dt->year);
if (dt->day + sub <= mdays) {
dt->day += sub;
break;
}
sub -= (mdays - dt->day + 1);
if (++dt->month > 12) {
dt->month = 1;
++dt->year;
}
dt->day = 1;
}
return dt;
}
char* duration_fmt(int sec, char* buf) {
int h, m, s;
m = sec / 60;
s = sec % 60;
h = m / 60;
m = m % 60;
sprintf(buf, TIME_FMT, h, m, s);
return buf;
}
char* datetime_fmt(datetime_t* dt, char* buf) {
sprintf(buf, DATETIME_FMT,
dt->year, dt->month, dt->day,
dt->hour, dt->min, dt->sec);
return buf;
}
char* datetime_fmt_iso(datetime_t* dt, char* buf) {
sprintf(buf, DATETIME_FMT_ISO,
dt->year, dt->month, dt->day,
dt->hour, dt->min, dt->sec,
dt->ms);
return buf;
}
char* gmtime_fmt(time_t time, char* buf) {
struct tm* tm = gmtime(&time);
//strftime(buf, GMTIME_FMT_BUFLEN, "%a, %d %b %Y %H:%M:%S GMT", tm);
sprintf(buf, GMTIME_FMT,
s_weekdays[tm->tm_wday],
tm->tm_mday, s_months[tm->tm_mon], tm->tm_year + 1900,
tm->tm_hour, tm->tm_min, tm->tm_sec);
return buf;
}
int month_atoi(const char* month) {
for (size_t i = 0; i < 12; ++i) {
if (strnicmp(month, s_months[i], strlen(month)) == 0)
return i+1;
}
return 0;
}
const char* month_itoa(int month) {
assert(month >= 1 && month <= 12);
return s_months[month-1];
}
int weekday_atoi(const char* weekday) {
for (size_t i = 0; i < 7; ++i) {
if (strnicmp(weekday, s_weekdays[i], strlen(weekday)) == 0)
return i;
}
return 0;
}
const char* weekday_itoa(int weekday) {
assert(weekday >= 0 && weekday <= 7);
if (weekday == 7) weekday = 0;
return s_weekdays[weekday];
}
datetime_t hv_compile_datetime() {
datetime_t dt;
char month[32];
sscanf(__DATE__, "%s %d %d", month, &dt.day, &dt.year);
sscanf(__TIME__, "%d:%d:%d", &dt.hour, &dt.min, &dt.sec);
dt.month = month_atoi(month);
return dt;
}
time_t cron_next_timeout(int minute, int hour, int day, int week, int month) {
enum {
MINUTELY,
HOURLY,
DAILY,
WEEKLY,
MONTHLY,
YEARLY,
} period_type = MINUTELY;
struct tm tm;
time_t tt;
time(&tt);
tm = *localtime(&tt);
time_t tt_round = 0;
tm.tm_sec = 0;
if (minute >= 0) {
period_type = HOURLY;
tm.tm_min = minute;
}
if (hour >= 0) {
period_type = DAILY;
tm.tm_hour = hour;
}
if (week >= 0) {
period_type = WEEKLY;
}
else if (day > 0) {
period_type = MONTHLY;
tm.tm_mday = day;
if (month > 0) {
period_type = YEARLY;
tm.tm_mon = month - 1;
}
}
tt_round = mktime(&tm);
if (week >= 0) {
tt_round += (week-tm.tm_wday)*SECONDS_PER_DAY;
}
if (tt_round > tt) {
return tt_round;
}
switch(period_type) {
case MINUTELY:
tt_round += SECONDS_PER_MINUTE;
return tt_round;
case HOURLY:
tt_round += SECONDS_PER_HOUR;
return tt_round;
case DAILY:
tt_round += SECONDS_PER_DAY;
return tt_round;
case WEEKLY:
tt_round += SECONDS_PER_WEEK;
return tt_round;
case MONTHLY:
if (++tm.tm_mon == 12) {
tm.tm_mon = 0;
++tm.tm_year;
}
break;
case YEARLY:
++tm.tm_year;
break;
default:
return -1;
}
return mktime(&tm);
}

114
external/libhv/libhv-1.3.2/base/htime.h vendored Normal file
View File

@ -0,0 +1,114 @@
#ifndef HV_TIME_H_
#define HV_TIME_H_
#include "hexport.h"
#include "hplatform.h"
BEGIN_EXTERN_C
#define SECONDS_PER_MINUTE 60
#define SECONDS_PER_HOUR 3600
#define SECONDS_PER_DAY 86400 // 24*3600
#define SECONDS_PER_WEEK 604800 // 7*24*3600
#define IS_LEAP_YEAR(year) (((year)%4 == 0 && (year)%100 != 0) || (year)%400 == 0)
typedef struct datetime_s {
int year;
int month;
int day;
int hour;
int min;
int sec;
int ms;
} datetime_t;
#ifdef _MSC_VER
/* @see winsock2.h
// Structure used in select() call, taken from the BSD file sys/time.h
struct timeval {
long tv_sec;
long tv_usec;
};
*/
struct timezone {
int tz_minuteswest; /* of Greenwich */
int tz_dsttime; /* type of dst correction to apply */
};
#include <sys/timeb.h>
HV_INLINE int gettimeofday(struct timeval *tv, struct timezone *tz) {
struct _timeb tb;
_ftime(&tb);
if (tv) {
tv->tv_sec = (long)tb.time;
tv->tv_usec = tb.millitm * 1000;
}
if (tz) {
tz->tz_minuteswest = tb.timezone;
tz->tz_dsttime = tb.dstflag;
}
return 0;
}
#endif
HV_EXPORT unsigned int gettick_ms();
HV_INLINE unsigned long long gettimeofday_ms() {
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * (unsigned long long)1000 + tv.tv_usec/1000;
}
HV_INLINE unsigned long long gettimeofday_us() {
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * (unsigned long long)1000000 + tv.tv_usec;
}
HV_EXPORT unsigned long long gethrtime_us();
HV_EXPORT datetime_t datetime_now();
HV_EXPORT datetime_t datetime_localtime(time_t seconds);
HV_EXPORT time_t datetime_mktime(datetime_t* dt);
HV_EXPORT datetime_t* datetime_past(datetime_t* dt, int days DEFAULT(1));
HV_EXPORT datetime_t* datetime_future(datetime_t* dt, int days DEFAULT(1));
#define TIME_FMT "%02d:%02d:%02d"
#define TIME_FMT_BUFLEN 12
HV_EXPORT char* duration_fmt(int sec, char* buf);
#define DATETIME_FMT "%04d-%02d-%02d %02d:%02d:%02d"
#define DATETIME_FMT_ISO "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ"
#define DATETIME_FMT_BUFLEN 30
HV_EXPORT char* datetime_fmt(datetime_t* dt, char* buf);
HV_EXPORT char* datetime_fmt_iso(datetime_t* dt, char* buf);
#define GMTIME_FMT "%.3s, %02d %.3s %04d %02d:%02d:%02d GMT"
#define GMTIME_FMT_BUFLEN 30
HV_EXPORT char* gmtime_fmt(time_t time, char* buf);
HV_EXPORT int days_of_month(int month, int year);
HV_EXPORT int month_atoi(const char* month);
HV_EXPORT const char* month_itoa(int month);
HV_EXPORT int weekday_atoi(const char* weekday);
HV_EXPORT const char* weekday_itoa(int weekday);
HV_EXPORT datetime_t hv_compile_datetime();
/*
* minute hour day week month action
* 0~59 0~23 1~31 0~6 1~12
* -1 -1 -1 -1 -1 cron.minutely
* 30 -1 -1 -1 -1 cron.hourly
* 30 1 -1 -1 -1 cron.daily
* 30 1 15 -1 -1 cron.monthly
* 30 1 -1 0 -1 cron.weekly
* 30 1 1 -1 10 cron.yearly
*/
HV_EXPORT time_t cron_next_timeout(int minute, int hour, int day, int week, int month);
END_EXTERN_C
#endif // HV_TIME_H_

View File

@ -0,0 +1,48 @@
#include "hversion.h"
#include "htime.h"
const char* hv_compile_version() {
static char s_version[16] = {0};
datetime_t dt = hv_compile_datetime();
snprintf(s_version, sizeof(s_version), "%d.%d.%d.%d",
HV_VERSION_MAJOR, dt.year%100, dt.month, dt.day);
return s_version;
}
int version_atoi(const char* str) {
int hex = 0;
// trim v1.2.3.4
const char* pv = strchr(str, 'v');
const char* pdot = pv ? pv+1 : str;
while (1) {
hex = (hex << 8) | atoi(pdot);
pdot = strchr(pdot, '.');
if (pdot == NULL) break;
++pdot;
}
return hex;
}
void version_itoa(int num, char* str) {
char* ch = (char*)&num;
sprintf(str, "%d.%d.%d.%d", ch[3], ch[2], ch[1], ch[0]);
// trim 0.1.2.3
const char* p = str;
while (1) {
if (p[0] == '0' && p[1] == '.') {
p += 2;
}
else {
break;
}
}
if (p != str) {
strcpy(str, p);
}
}

View File

@ -0,0 +1,34 @@
#ifndef HV_VERSION_H_
#define HV_VERSION_H_
#include "hexport.h"
#include "hdef.h"
BEGIN_EXTERN_C
#define HV_VERSION_MAJOR 1
#define HV_VERSION_MINOR 3
#define HV_VERSION_PATCH 2
#define HV_VERSION_STRING STRINGIFY(HV_VERSION_MAJOR) "." \
STRINGIFY(HV_VERSION_MINOR) "." \
STRINGIFY(HV_VERSION_PATCH)
#define HV_VERSION_NUMBER ((HV_VERSION_MAJOR << 16) | (HV_VERSION_MINOR << 8) | HV_VERSION_PATCH)
HV_INLINE const char* hv_version() {
return HV_VERSION_STRING;
}
HV_EXPORT const char* hv_compile_version();
// 1.2.3.4 => 0x01020304
HV_EXPORT int version_atoi(const char* str);
// 0x01020304 => 1.2.3.4
HV_EXPORT void version_itoa(int hex, char* str);
END_EXTERN_C
#endif // HV_VERSION_H_

733
external/libhv/libhv-1.3.2/base/list.h vendored Normal file
View File

@ -0,0 +1,733 @@
#ifndef _LINUX_LIST_H
#define _LINUX_LIST_H
/*
* Simple doubly linked list implementation.
*
* Some of the internal functions ("__xxx") are useful when
* manipulating whole lists rather than single entries, as
* sometimes we already know the next/prev entries and we can
* generate better code by using them directly rather than
* using the generic single-entry routines.
*/
#include <stddef.h>
#ifndef prefetch
#ifdef __GNUC__
#define prefetch(x) __builtin_prefetch(x)
#else
#define prefetch(x) (void)0
#endif
#endif
struct list_head {
struct list_head *next, *prev;
};
#define list_node list_head
struct hlist_head {
struct hlist_node *first;
};
struct hlist_node {
struct hlist_node *next, **pprev;
};
#define LIST_HEAD_INIT(name) { &(name), &(name) }
// TODO: <sys/queue.h> defined LIST_HEAD
#ifndef LIST_HEAD
#define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name)
#endif
#define INIT_LIST_HEAD list_init
static inline void list_init(struct list_head *list)
{
list->next = list;
list->prev = list;
}
/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_add(struct list_head *n,
struct list_head *prev,
struct list_head *next)
{
next->prev = n;
n->next = next;
n->prev = prev;
prev->next = n;
}
/**
* list_add - add a new entry
* @new: new entry to be added
* @head: list head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
static inline void list_add(struct list_head *n, struct list_head *head)
{
__list_add(n, head, head->next);
}
/**
* list_add_tail - add a new entry
* @new: new entry to be added
* @head: list head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
static inline void list_add_tail(struct list_head *n, struct list_head *head)
{
__list_add(n, head->prev, head);
}
/*
* Delete a list entry by making the prev/next entries
* point to each other.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
prev->next = next;
}
/**
* list_del - deletes entry from list.
* @entry: the element to delete from the list.
* Note: list_empty() on entry does not return true after this, the entry is
* in an undefined state.
*/
static inline void __list_del_entry(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
}
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
//entry->next = NULL;
//entry->prev = NULL;
}
/**
* list_replace - replace old entry by new one
* @old : the element to be replaced
* @new : the new element to insert
*
* If @old was empty, it will be overwritten.
*/
static inline void list_replace(struct list_head *old,
struct list_head *n)
{
n->next = old->next;
n->next->prev = n;
n->prev = old->prev;
n->prev->next = n;
}
static inline void list_replace_init(struct list_head *old,
struct list_head *n)
{
list_replace(old, n);
INIT_LIST_HEAD(old);
}
/**
* list_del_init - deletes entry from list and reinitialize it.
* @entry: the element to delete from the list.
*/
static inline void list_del_init(struct list_head *entry)
{
__list_del_entry(entry);
INIT_LIST_HEAD(entry);
}
/**
* list_move - delete from one list and add as another's head
* @list: the entry to move
* @head: the head that will precede our entry
*/
static inline void list_move(struct list_head *list, struct list_head *head)
{
__list_del_entry(list);
list_add(list, head);
}
/**
* list_move_tail - delete from one list and add as another's tail
* @list: the entry to move
* @head: the head that will follow our entry
*/
static inline void list_move_tail(struct list_head *list,
struct list_head *head)
{
__list_del_entry(list);
list_add_tail(list, head);
}
/**
* list_is_last - tests whether @list is the last entry in list @head
* @list: the entry to test
* @head: the head of the list
*/
static inline int list_is_last(const struct list_head *list,
const struct list_head *head)
{
return list->next == head;
}
/**
* list_empty - tests whether a list is empty
* @head: the list to test.
*/
static inline int list_empty(const struct list_head *head)
{
return head->next == head;
}
/**
* list_empty_careful - tests whether a list is empty and not being modified
* @head: the list to test
*
* Description:
* tests whether a list is empty _and_ checks that no other CPU might be
* in the process of modifying either member (next or prev)
*
* NOTE: using list_empty_careful() without synchronization
* can only be safe if the only activity that can happen
* to the list entry is list_del_init(). Eg. it cannot be used
* if another CPU could re-list_add() it.
*/
static inline int list_empty_careful(const struct list_head *head)
{
struct list_head *next = head->next;
return (next == head) && (next == head->prev);
}
/**
* list_rotate_left - rotate the list to the left
* @head: the head of the list
*/
static inline void list_rotate_left(struct list_head *head)
{
struct list_head *first;
if (!list_empty(head)) {
first = head->next;
list_move_tail(first, head);
}
}
/**
* list_is_singular - tests whether a list has just one entry.
* @head: the list to test.
*/
static inline int list_is_singular(const struct list_head *head)
{
return !list_empty(head) && (head->next == head->prev);
}
static inline void __list_cut_position(struct list_head *list,
struct list_head *head, struct list_head *entry)
{
struct list_head *new_first = entry->next;
list->next = head->next;
list->next->prev = list;
list->prev = entry;
entry->next = list;
head->next = new_first;
new_first->prev = head;
}
/**
* list_cut_position - cut a list into two
* @list: a new list to add all removed entries
* @head: a list with entries
* @entry: an entry within head, could be the head itself
* and if so we won't cut the list
*
* This helper moves the initial part of @head, up to and
* including @entry, from @head to @list. You should
* pass on @entry an element you know is on @head. @list
* should be an empty list or a list you do not care about
* losing its data.
*
*/
static inline void list_cut_position(struct list_head *list,
struct list_head *head, struct list_head *entry)
{
if (list_empty(head))
return;
if (list_is_singular(head) &&
(head->next != entry && head != entry))
return;
if (entry == head)
INIT_LIST_HEAD(list);
else
__list_cut_position(list, head, entry);
}
static inline void __list_splice(const struct list_head *list,
struct list_head *prev,
struct list_head *next)
{
struct list_head *first = list->next;
struct list_head *last = list->prev;
first->prev = prev;
prev->next = first;
last->next = next;
next->prev = last;
}
/**
* list_splice - join two lists, this is designed for stacks
* @list: the new list to add.
* @head: the place to add it in the first list.
*/
static inline void list_splice(const struct list_head *list,
struct list_head *head)
{
if (!list_empty(list))
__list_splice(list, head, head->next);
}
/**
* list_splice_tail - join two lists, each list being a queue
* @list: the new list to add.
* @head: the place to add it in the first list.
*/
static inline void list_splice_tail(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list))
__list_splice(list, head->prev, head);
}
/**
* list_splice_init - join two lists and reinitialise the emptied list.
* @list: the new list to add.
* @head: the place to add it in the first list.
*
* The list at @list is reinitialised
*/
static inline void list_splice_init(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list)) {
__list_splice(list, head, head->next);
INIT_LIST_HEAD(list);
}
}
/**
* list_splice_tail_init - join two lists and reinitialise the emptied list
* @list: the new list to add.
* @head: the place to add it in the first list.
*
* Each of the lists is a queue.
* The list at @list is reinitialised
*/
static inline void list_splice_tail_init(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list)) {
__list_splice(list, head->prev, head);
INIT_LIST_HEAD(list);
}
}
/**
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
/**
* list_first_entry - get the first element from a list
* @ptr: the list head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*
* Note, that list is expected to be not empty.
*/
#define list_first_entry(ptr, type, member) \
list_entry((ptr)->next, type, member)
/**
* list_for_each - iterate over a list
* @pos: the &struct list_head to use as a loop cursor.
* @head: the head for your list.
*/
#define list_for_each(pos, head) \
for (pos = (head)->next; prefetch(pos->next), pos != (head); \
pos = pos->next)
/**
* __list_for_each - iterate over a list
* @pos: the &struct list_head to use as a loop cursor.
* @head: the head for your list.
*
* This variant differs from list_for_each() in that it's the
* simplest possible list iteration code, no prefetching is done.
* Use this for code that knows the list to be very short (empty
* or 1 entry) most of the time.
*/
#define __list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
/**
* list_for_each_prev - iterate over a list backwards
* @pos: the &struct list_head to use as a loop cursor.
* @head: the head for your list.
*/
#define list_for_each_prev(pos, head) \
for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \
pos = pos->prev)
/**
* list_for_each_safe - iterate over a list safe against removal of list entry
* @pos: the &struct list_head to use as a loop cursor.
* @n: another &struct list_head to use as temporary storage
* @head: the head for your list.
*/
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)
/**
* list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
* @pos: the &struct list_head to use as a loop cursor.
* @n: another &struct list_head to use as temporary storage
* @head: the head for your list.
*/
#define list_for_each_prev_safe(pos, n, head) \
for (pos = (head)->prev, n = pos->prev; \
prefetch(pos->prev), pos != (head); \
pos = n, n = pos->prev)
/**
* list_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry(pos, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member); \
prefetch(pos->member.next), &pos->member != (head); \
pos = list_entry(pos->member.next, typeof(*pos), member))
/**
* list_for_each_entry_reverse - iterate backwards over list of given type.
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry_reverse(pos, head, member) \
for (pos = list_entry((head)->prev, typeof(*pos), member); \
prefetch(pos->member.prev), &pos->member != (head); \
pos = list_entry(pos->member.prev, typeof(*pos), member))
/**
* list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue()
* @pos: the type * to use as a start point
* @head: the head of the list
* @member: the name of the list_struct within the struct.
*
* Prepares a pos entry for use as a start point in list_for_each_entry_continue().
*/
#define list_prepare_entry(pos, head, member) \
((pos) ? : list_entry(head, typeof(*pos), member))
/**
* list_for_each_entry_continue - continue iteration over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*
* Continue to iterate over list of given type, continuing after
* the current position.
*/
#define list_for_each_entry_continue(pos, head, member) \
for (pos = list_entry(pos->member.next, typeof(*pos), member); \
prefetch(pos->member.next), &pos->member != (head); \
pos = list_entry(pos->member.next, typeof(*pos), member))
/**
* list_for_each_entry_continue_reverse - iterate backwards from the given point
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*
* Start to iterate over list of given type backwards, continuing after
* the current position.
*/
#define list_for_each_entry_continue_reverse(pos, head, member) \
for (pos = list_entry(pos->member.prev, typeof(*pos), member); \
prefetch(pos->member.prev), &pos->member != (head); \
pos = list_entry(pos->member.prev, typeof(*pos), member))
/**
* list_for_each_entry_from - iterate over list of given type from the current point
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*
* Iterate over list of given type, continuing from current position.
*/
#define list_for_each_entry_from(pos, head, member) \
for (; prefetch(pos->member.next), &pos->member != (head); \
pos = list_entry(pos->member.next, typeof(*pos), member))
/**
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member), \
n = list_entry(pos->member.next, typeof(*pos), member); \
&pos->member != (head); \
pos = n, n = list_entry(n->member.next, typeof(*n), member))
/**
* list_for_each_entry_safe_continue - continue list iteration safe against removal
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*
* Iterate over list of given type, continuing after current point,
* safe against removal of list entry.
*/
#define list_for_each_entry_safe_continue(pos, n, head, member) \
for (pos = list_entry(pos->member.next, typeof(*pos), member), \
n = list_entry(pos->member.next, typeof(*pos), member); \
&pos->member != (head); \
pos = n, n = list_entry(n->member.next, typeof(*n), member))
/**
* list_for_each_entry_safe_from - iterate over list from current point safe against removal
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*
* Iterate over list of given type from current point, safe against
* removal of list entry.
*/
#define list_for_each_entry_safe_from(pos, n, head, member) \
for (n = list_entry(pos->member.next, typeof(*pos), member); \
&pos->member != (head); \
pos = n, n = list_entry(n->member.next, typeof(*n), member))
/**
* list_for_each_entry_safe_reverse - iterate backwards over list safe against removal
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*
* Iterate backwards over list of given type, safe against removal
* of list entry.
*/
#define list_for_each_entry_safe_reverse(pos, n, head, member) \
for (pos = list_entry((head)->prev, typeof(*pos), member), \
n = list_entry(pos->member.prev, typeof(*pos), member); \
&pos->member != (head); \
pos = n, n = list_entry(n->member.prev, typeof(*n), member))
/**
* list_safe_reset_next - reset a stale list_for_each_entry_safe loop
* @pos: the loop cursor used in the list_for_each_entry_safe loop
* @n: temporary storage used in list_for_each_entry_safe
* @member: the name of the list_struct within the struct.
*
* list_safe_reset_next is not safe to use in general if the list may be
* modified concurrently (eg. the lock is dropped in the loop body). An
* exception to this is if the cursor element (pos) is pinned in the list,
* and list_safe_reset_next is called after re-taking the lock and before
* completing the current iteration of the loop body.
*/
#define list_safe_reset_next(pos, n, member) \
n = list_entry(pos->member.next, typeof(*pos), member)
/*
* Double linked lists with a single pointer list head.
* Mostly useful for hash tables where the two pointer list head is
* too wasteful.
* You lose the ability to access the tail in O(1).
*/
#define HLIST_HEAD_INIT { .first = NULL }
#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL }
#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
#define INIT_HLIST_NODE hlist_init
static inline void hlist_init(struct hlist_node *h)
{
h->next = NULL;
h->pprev = NULL;
}
static inline int hlist_unhashed(const struct hlist_node *h)
{
return !h->pprev;
}
static inline int hlist_empty(const struct hlist_head *h)
{
return !h->first;
}
static inline void __hlist_del(struct hlist_node *n)
{
struct hlist_node *next = n->next;
struct hlist_node **pprev = n->pprev;
*pprev = next;
if (next)
next->pprev = pprev;
}
static inline void hlist_del(struct hlist_node *n)
{
__hlist_del(n);
//n->next = NULL;
//n->pprev = NULL;
}
static inline void hlist_del_init(struct hlist_node *n)
{
if (!hlist_unhashed(n)) {
__hlist_del(n);
INIT_HLIST_NODE(n);
}
}
static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
{
struct hlist_node *first = h->first;
n->next = first;
if (first)
first->pprev = &n->next;
h->first = n;
n->pprev = &h->first;
}
/* next must be != NULL */
static inline void hlist_add_before(struct hlist_node *n,
struct hlist_node *next)
{
n->pprev = next->pprev;
n->next = next;
next->pprev = &n->next;
*(n->pprev) = n;
}
static inline void hlist_add_after(struct hlist_node *n,
struct hlist_node *next)
{
next->next = n->next;
n->next = next;
next->pprev = &n->next;
if(next->next)
next->next->pprev = &next->next;
}
/* after that we'll appear to be on some hlist and hlist_del will work */
static inline void hlist_add_fake(struct hlist_node *n)
{
n->pprev = &n->next;
}
/*
* Move a list from one list head to another. Fixup the pprev
* reference of the first entry if it exists.
*/
static inline void hlist_move_list(struct hlist_head *old,
struct hlist_head *n)
{
n->first = old->first;
if (n->first)
n->first->pprev = &n->first;
old->first = NULL;
}
#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
#define hlist_for_each(pos, head) \
for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
pos = pos->next)
#define hlist_for_each_safe(pos, n, head) \
for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
pos = n)
/**
* hlist_for_each_entry - iterate over list of given type
* @tpos: the type * to use as a loop cursor.
* @pos: the &struct hlist_node to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry(tpos, pos, head, member) \
for (pos = (head)->first; \
pos && ({ prefetch(pos->next); 1;}) && \
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
pos = pos->next)
/**
* hlist_for_each_entry_continue - iterate over a hlist continuing after current point
* @tpos: the type * to use as a loop cursor.
* @pos: the &struct hlist_node to use as a loop cursor.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry_continue(tpos, pos, member) \
for (pos = (pos)->next; \
pos && ({ prefetch(pos->next); 1;}) && \
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
pos = pos->next)
/**
* hlist_for_each_entry_from - iterate over a hlist continuing from current point
* @tpos: the type * to use as a loop cursor.
* @pos: the &struct hlist_node to use as a loop cursor.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry_from(tpos, pos, member) \
for (; pos && ({ prefetch(pos->next); 1;}) && \
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
pos = pos->next)
/**
* hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @tpos: the type * to use as a loop cursor.
* @pos: the &struct hlist_node to use as a loop cursor.
* @n: another &struct hlist_node to use as temporary storage
* @head: the head for your list.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \
for (pos = (head)->first; \
pos && ({ n = pos->next; 1; }) && \
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
pos = n)
#endif

View File

@ -0,0 +1,194 @@
#ifndef HV_NETINET_H_
#define HV_NETINET_H_
#include "hplatform.h"
/*
#ifdef OS_UNIX
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <netinet/tcp.h>
#include <netinet/ip_icmp.h>
typedef struct iphdr iphdr_t;
typedef struct udphdr udphdr_t;
typedef struct tcphdr tcphdr_t;
typedef struct icmphdr icmphdr_t;
typedef struct icmp icmp_t;
#else
*/
// sizeof(iphdr_t) = 20
typedef struct iphdr_s {
#if BYTE_ORDER == LITTLE_ENDIAN
uint8_t ihl:4; // ip header length
uint8_t version:4;
#elif BYTE_ORDER == BIG_ENDIAN
uint8_t version:4;
uint8_t ihl:4;
#else
#error "BYTE_ORDER undefined!"
#endif
uint8_t tos; // type of service
uint16_t tot_len; // total length
uint16_t id;
uint16_t frag_off; // fragment offset
uint8_t ttl; // Time To Live
uint8_t protocol;
uint16_t check; // checksum
uint32_t saddr; // srcaddr
uint32_t daddr; // dstaddr
/*The options start here.*/
} iphdr_t;
// sizeof(udphdr_t) = 8
typedef struct udphdr_s {
uint16_t source; // source port
uint16_t dest; // dest port
uint16_t len; // udp length
uint16_t check; // checksum
} udphdr_t;
// sizeof(tcphdr_t) = 20
typedef struct tcphdr_s {
uint16_t source; // source port
uint16_t dest; // dest port
uint32_t seq; // sequence
uint32_t ack_seq;
#if BYTE_ORDER == LITTLE_ENDIAN
uint16_t res1:4;
uint16_t doff:4;
uint16_t fin:1;
uint16_t syn:1;
uint16_t rst:1;
uint16_t psh:1;
uint16_t ack:1;
uint16_t urg:1;
uint16_t res2:2;
#elif BYTE_ORDER == BIG_ENDIAN
uint16_t doff:4;
uint16_t res1:4;
uint16_t res2:2;
uint16_t urg:1;
uint16_t ack:1;
uint16_t psh:1;
uint16_t rst:1;
uint16_t syn:1;
uint16_t fin:1;
#else
#error "BYTE_ORDER undefined!"
#endif
uint16_t window;
uint16_t check; // checksum
uint16_t urg_ptr; // urgent pointer
} tcphdr_t;
//----------------------icmp----------------------------------
#define ICMP_ECHOREPLY 0 /* Echo Reply */
#define ICMP_DEST_UNREACH 3 /* Destination Unreachable */
#define ICMP_SOURCE_QUENCH 4 /* Source Quench */
#define ICMP_REDIRECT 5 /* Redirect (change route) */
#define ICMP_ECHO 8 /* Echo Request */
#define ICMP_TIME_EXCEEDED 11 /* Time Exceeded */
#define ICMP_PARAMETERPROB 12 /* Parameter Problem */
#define ICMP_TIMESTAMP 13 /* Timestamp Request */
#define ICMP_TIMESTAMPREPLY 14 /* Timestamp Reply */
#define ICMP_INFO_REQUEST 15 /* Information Request */
#define ICMP_INFO_REPLY 16 /* Information Reply */
#define ICMP_ADDRESS 17 /* Address Mask Request */
#define ICMP_ADDRESSREPLY 18 /* Address Mask Reply */
// sizeof(icmphdr_t) = 8
typedef struct icmphdr_s {
uint8_t type; // message type
uint8_t code; // type sub-code
uint16_t checksum;
union {
struct {
uint16_t id;
uint16_t sequence;
} echo;
uint32_t gateway;
struct {
uint16_t reserved;
uint16_t mtu;
} frag;
} un;
} icmphdr_t;
typedef struct icmp_s {
uint8_t icmp_type;
uint8_t icmp_code;
uint16_t icmp_cksum;
union {
uint8_t ih_pptr;
struct in_addr ih_gwaddr;
struct ih_idseq {
uint16_t icd_id;
uint16_t icd_seq;
} ih_idseq;
uint32_t ih_void;
struct ih_pmtu {
uint16_t ipm_void;
uint16_t ipm_nextmtu;
} ih_pmtu;
struct ih_rtradv {
uint8_t irt_num_addrs;
uint8_t irt_wpa;
uint16_t irt_lifetime;
} ih_rtradv;
} icmp_hun;
#define icmp_pptr icmp_hun.ih_pptr
#define icmp_gwaddr icmp_hun.ih_gwaddr
#define icmp_id icmp_hun.ih_idseq.icd_id
#define icmp_seq icmp_hun.ih_idseq.icd_seq
#define icmp_void icmp_hun.ih_void
#define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void
#define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu
#define icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs
#define icmp_wpa icmp_hun.ih_rtradv.irt_wpa
#define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime
union {
struct {
uint32_t its_otime;
uint32_t its_rtime;
uint32_t its_ttime;
} id_ts;
/*
struct {
struct ip idi_ip;
} id_ip;
struct icmp_ra_addr id_radv;
*/
uint32_t id_mask;
uint8_t id_data[1];
} icmp_dun;
#define icmp_otime icmp_dun.id_ts.its_otime
#define icmp_rtime icmp_dun.id_ts.its_rtime
#define icmp_ttime icmp_dun.id_ts.its_ttime
#define icmp_ip icmp_dun.id_ip.idi_ip
#define icmp_radv icmp_dun.id_radv
#define icmp_mask icmp_dun.id_mask
#define icmp_data icmp_dun.id_data
} icmp_t;
//#endif
static inline uint16_t checksum(uint8_t* buf, int len) {
unsigned int sum = 0;
uint16_t* ptr = (uint16_t*)buf;
while(len > 1) {
sum += *ptr++;
len -= 2;
}
if(len) {
sum += *(uint8_t*)ptr;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
return (uint16_t)(~sum);
};
#endif // HV_NETINET_H_

105
external/libhv/libhv-1.3.2/base/queue.h vendored Normal file
View File

@ -0,0 +1,105 @@
#ifndef HV_QUEUE_H_
#define HV_QUEUE_H_
/*
* queue
* FIFO: push_back,pop_front
* stack
* LIFO: push_back,pop_back
*/
#include <assert.h> // for assert
#include <stddef.h> // for NULL
#include <stdlib.h> // for malloc,realloc,free
#include <string.h> // for memset,memmove
#include "hbase.h" // for HV_ALLOC, HV_FREE
#define QUEUE_INIT_SIZE 16
// #include <deque>
// typedef std::deque<type> qtype;
#define QUEUE_DECL(type, qtype) \
struct qtype { \
type* ptr; \
size_t size; \
size_t maxsize;\
size_t _offset;\
}; \
typedef struct qtype qtype;\
\
static inline type* qtype##_data(qtype* p) {\
return p->ptr + p->_offset;\
}\
\
static inline int qtype##_size(qtype* p) {\
return p->size;\
}\
\
static inline int qtype##_maxsize(qtype* p) {\
return p->maxsize;\
}\
\
static inline int qtype##_empty(qtype* p) {\
return p->size == 0;\
}\
\
static inline type* qtype##_front(qtype* p) {\
return p->size == 0 ? NULL : p->ptr + p->_offset;\
}\
\
static inline type* qtype##_back(qtype* p) {\
return p->size == 0 ? NULL : p->ptr + p->_offset + p->size - 1;\
}\
\
static inline void qtype##_init(qtype* p, int maxsize) {\
p->_offset = 0;\
p->size = 0;\
p->maxsize = maxsize;\
HV_ALLOC(p->ptr, sizeof(type) * maxsize);\
}\
\
static inline void qtype##_clear(qtype* p) {\
p->_offset = 0;\
p->size = 0;\
memset(p->ptr, 0, sizeof(type) * p->maxsize);\
}\
\
static inline void qtype##_cleanup(qtype* p) {\
HV_FREE(p->ptr);\
p->_offset = p->size = p->maxsize = 0;\
}\
\
static inline void qtype##_resize(qtype* p, int maxsize) {\
if (maxsize == 0) maxsize = QUEUE_INIT_SIZE;\
p->ptr = (type*)hv_realloc(p->ptr, sizeof(type) * maxsize, sizeof(type) * p->maxsize);\
p->maxsize = maxsize;\
}\
\
static inline void qtype##_double_resize(qtype* p) {\
qtype##_resize(p, p->maxsize * 2);\
}\
\
static inline void qtype##_push_back(qtype* p, type* elem) {\
if (p->size == p->maxsize) {\
qtype##_double_resize(p);\
}\
else if (p->_offset + p->size == p->maxsize) {\
memmove(p->ptr, p->ptr + p->_offset, sizeof(type) * p->size);\
p->_offset = 0;\
}\
p->ptr[p->_offset + p->size] = *elem;\
p->size++;\
}\
static inline void qtype##_pop_front(qtype* p) {\
assert(p->size > 0);\
p->size--;\
if (++p->_offset == p->maxsize) p->_offset = 0;\
}\
\
static inline void qtype##_pop_back(qtype* p) {\
assert(p->size > 0);\
p->size--;\
}\
#endif // HV_QUEUE_H_

386
external/libhv/libhv-1.3.2/base/rbtree.c vendored Normal file
View File

@ -0,0 +1,386 @@
/*
Red Black Trees
(C) 1999 Andrea Arcangeli <andrea@suse.de>
(C) 2002 David Woodhouse <dwmw2@infradead.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
linux/lib/rbtree.c
*/
#include "rbtree.h"
static void __rb_rotate_left(struct rb_node *node, struct rb_root *root)
{
struct rb_node *right = node->rb_right;
if ((node->rb_right = right->rb_left))
right->rb_left->rb_parent = node;
right->rb_left = node;
if ((right->rb_parent = node->rb_parent))
{
if (node == node->rb_parent->rb_left)
node->rb_parent->rb_left = right;
else
node->rb_parent->rb_right = right;
}
else
root->rb_node = right;
node->rb_parent = right;
}
static void __rb_rotate_right(struct rb_node *node, struct rb_root *root)
{
struct rb_node *left = node->rb_left;
if ((node->rb_left = left->rb_right))
left->rb_right->rb_parent = node;
left->rb_right = node;
if ((left->rb_parent = node->rb_parent))
{
if (node == node->rb_parent->rb_right)
node->rb_parent->rb_right = left;
else
node->rb_parent->rb_left = left;
}
else
root->rb_node = left;
node->rb_parent = left;
}
void rb_insert_color(struct rb_node *node, struct rb_root *root)
{
struct rb_node *parent, *gparent;
while ((parent = node->rb_parent) && parent->rb_color == RB_RED)
{
gparent = parent->rb_parent;
if (parent == gparent->rb_left)
{
{
register struct rb_node *uncle = gparent->rb_right;
if (uncle && uncle->rb_color == RB_RED)
{
uncle->rb_color = RB_BLACK;
parent->rb_color = RB_BLACK;
gparent->rb_color = RB_RED;
node = gparent;
continue;
}
}
if (parent->rb_right == node)
{
register struct rb_node *tmp;
__rb_rotate_left(parent, root);
tmp = parent;
parent = node;
node = tmp;
}
parent->rb_color = RB_BLACK;
gparent->rb_color = RB_RED;
__rb_rotate_right(gparent, root);
} else {
{
register struct rb_node *uncle = gparent->rb_left;
if (uncle && uncle->rb_color == RB_RED)
{
uncle->rb_color = RB_BLACK;
parent->rb_color = RB_BLACK;
gparent->rb_color = RB_RED;
node = gparent;
continue;
}
}
if (parent->rb_left == node)
{
register struct rb_node *tmp;
__rb_rotate_right(parent, root);
tmp = parent;
parent = node;
node = tmp;
}
parent->rb_color = RB_BLACK;
gparent->rb_color = RB_RED;
__rb_rotate_left(gparent, root);
}
}
root->rb_node->rb_color = RB_BLACK;
}
static void __rb_erase_color(struct rb_node *node, struct rb_node *parent,
struct rb_root *root)
{
struct rb_node *other;
while ((!node || node->rb_color == RB_BLACK) && node != root->rb_node)
{
if (parent->rb_left == node)
{
other = parent->rb_right;
if (other->rb_color == RB_RED)
{
other->rb_color = RB_BLACK;
parent->rb_color = RB_RED;
__rb_rotate_left(parent, root);
other = parent->rb_right;
}
if ((!other->rb_left ||
other->rb_left->rb_color == RB_BLACK)
&& (!other->rb_right ||
other->rb_right->rb_color == RB_BLACK))
{
other->rb_color = RB_RED;
node = parent;
parent = node->rb_parent;
}
else
{
if (!other->rb_right ||
other->rb_right->rb_color == RB_BLACK)
{
register struct rb_node *o_left;
if ((o_left = other->rb_left))
o_left->rb_color = RB_BLACK;
other->rb_color = RB_RED;
__rb_rotate_right(other, root);
other = parent->rb_right;
}
other->rb_color = parent->rb_color;
parent->rb_color = RB_BLACK;
if (other->rb_right)
other->rb_right->rb_color = RB_BLACK;
__rb_rotate_left(parent, root);
node = root->rb_node;
break;
}
}
else
{
other = parent->rb_left;
if (other->rb_color == RB_RED)
{
other->rb_color = RB_BLACK;
parent->rb_color = RB_RED;
__rb_rotate_right(parent, root);
other = parent->rb_left;
}
if ((!other->rb_left ||
other->rb_left->rb_color == RB_BLACK)
&& (!other->rb_right ||
other->rb_right->rb_color == RB_BLACK))
{
other->rb_color = RB_RED;
node = parent;
parent = node->rb_parent;
}
else
{
if (!other->rb_left ||
other->rb_left->rb_color == RB_BLACK)
{
register struct rb_node *o_right;
if ((o_right = other->rb_right))
o_right->rb_color = RB_BLACK;
other->rb_color = RB_RED;
__rb_rotate_left(other, root);
other = parent->rb_left;
}
other->rb_color = parent->rb_color;
parent->rb_color = RB_BLACK;
if (other->rb_left)
other->rb_left->rb_color = RB_BLACK;
__rb_rotate_right(parent, root);
node = root->rb_node;
break;
}
}
}
if (node)
node->rb_color = RB_BLACK;
}
void rb_erase(struct rb_node *node, struct rb_root *root)
{
struct rb_node *child, *parent;
int color;
if (!node->rb_left)
child = node->rb_right;
else if (!node->rb_right)
child = node->rb_left;
else
{
struct rb_node *old = node, *left;
node = node->rb_right;
while ((left = node->rb_left))
node = left;
child = node->rb_right;
parent = node->rb_parent;
color = node->rb_color;
if (child)
child->rb_parent = parent;
if (parent)
{
if (parent->rb_left == node)
parent->rb_left = child;
else
parent->rb_right = child;
}
else
root->rb_node = child;
if (node->rb_parent == old)
parent = node;
node->rb_parent = old->rb_parent;
node->rb_color = old->rb_color;
node->rb_right = old->rb_right;
node->rb_left = old->rb_left;
if (old->rb_parent)
{
if (old->rb_parent->rb_left == old)
old->rb_parent->rb_left = node;
else
old->rb_parent->rb_right = node;
} else
root->rb_node = node;
old->rb_left->rb_parent = node;
if (old->rb_right)
old->rb_right->rb_parent = node;
goto color;
}
parent = node->rb_parent;
color = node->rb_color;
if (child)
child->rb_parent = parent;
if (parent)
{
if (parent->rb_left == node)
parent->rb_left = child;
else
parent->rb_right = child;
}
else
root->rb_node = child;
color:
if (color == RB_BLACK)
__rb_erase_color(child, parent, root);
}
/*
* This function returns the first node (in sort order) of the tree.
*/
struct rb_node *rb_first(struct rb_root *root)
{
struct rb_node *n;
n = root->rb_node;
if (!n)
return (struct rb_node *)0;
while (n->rb_left)
n = n->rb_left;
return n;
}
struct rb_node *rb_last(struct rb_root *root)
{
struct rb_node *n;
n = root->rb_node;
if (!n)
return (struct rb_node *)0;
while (n->rb_right)
n = n->rb_right;
return n;
}
struct rb_node *rb_next(struct rb_node *node)
{
/* If we have a right-hand child, go down and then left as far
as we can. */
if (node->rb_right) {
node = node->rb_right;
while (node->rb_left)
node = node->rb_left;
return node;
}
/* No right-hand children. Everything down and left is
smaller than us, so any 'next' node must be in the general
direction of our parent. Go up the tree; any time the
ancestor is a right-hand child of its parent, keep going
up. First time it's a left-hand child of its parent, said
parent is our 'next' node. */
while (node->rb_parent && node == node->rb_parent->rb_right)
node = node->rb_parent;
return node->rb_parent;
}
struct rb_node *rb_prev(struct rb_node *node)
{
/* If we have a left-hand child, go down and then right as far
as we can. */
if (node->rb_left) {
node = node->rb_left;
while (node->rb_right)
node = node->rb_right;
return node;
}
/* No left-hand children. Go up till we find an ancestor which
is a right-hand child of its parent */
while (node->rb_parent && node == node->rb_parent->rb_left)
node = node->rb_parent;
return node->rb_parent;
}
void rb_replace_node(struct rb_node *victim, struct rb_node *newnode,
struct rb_root *root)
{
struct rb_node *parent = victim->rb_parent;
/* Set the surrounding nodes to point to the replacement */
if (parent) {
if (victim == parent->rb_left)
parent->rb_left = newnode;
else
parent->rb_right = newnode;
} else {
root->rb_node = newnode;
}
if (victim->rb_left)
victim->rb_left->rb_parent = newnode;
if (victim->rb_right)
victim->rb_right->rb_parent = newnode;
/* Copy the pointers/colour from the victim to the replacement */
*newnode = *victim;
}

147
external/libhv/libhv-1.3.2/base/rbtree.h vendored Normal file
View File

@ -0,0 +1,147 @@
/*
Red Black Trees
(C) 1999 Andrea Arcangeli <andrea@suse.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
linux/include/linux/rbtree.h
To use rbtrees you'll have to implement your own insert and search cores.
This will avoid us to use callbacks and to drop drammatically performances.
I know it's not the cleaner way, but in C (not in C++) to get
performances and genericity...
Some example of insert and search follows here. The search is a plain
normal search over an ordered tree. The insert instead must be implemented
in two steps: First, the code must insert the element in order as a red leaf
in the tree, and then the support library function rb_insert_color() must
be called. Such function will do the not trivial work to rebalance the
rbtree, if necessary.
-----------------------------------------------------------------------
static inline struct page * rb_search_page_cache(struct inode * inode,
unsigned long offset)
{
struct rb_node * n = inode->i_rb_page_cache.rb_node;
struct page * page;
while (n)
{
page = rb_entry(n, struct page, rb_page_cache);
if (offset < page->offset)
n = n->rb_left;
else if (offset > page->offset)
n = n->rb_right;
else
return page;
}
return NULL;
}
static inline struct page * __rb_insert_page_cache(struct inode * inode,
unsigned long offset,
struct rb_node * node)
{
struct rb_node ** p = &inode->i_rb_page_cache.rb_node;
struct rb_node * parent = NULL;
struct page * page;
while (*p)
{
parent = *p;
page = rb_entry(parent, struct page, rb_page_cache);
if (offset < page->offset)
p = &(*p)->rb_left;
else if (offset > page->offset)
p = &(*p)->rb_right;
else
return page;
}
rb_link_node(node, parent, p);
return NULL;
}
static inline struct page * rb_insert_page_cache(struct inode * inode,
unsigned long offset,
struct rb_node * node)
{
struct page * ret;
if ((ret = __rb_insert_page_cache(inode, offset, node)))
goto out;
rb_insert_color(node, &inode->i_rb_page_cache);
out:
return ret;
}
-----------------------------------------------------------------------
*/
#ifndef _LINUX_RBTREE_H
#define _LINUX_RBTREE_H
struct rb_node
{
struct rb_node *rb_parent;
struct rb_node *rb_right;
struct rb_node *rb_left;
char rb_color;
#define RB_RED 0
#define RB_BLACK 1
};
struct rb_root
{
struct rb_node *rb_node;
};
#define RB_ROOT (struct rb_root){ (struct rb_node *)0, }
#define rb_entry(ptr, type, member) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
#ifdef __cplusplus
extern "C"
{
#endif
void rb_insert_color(struct rb_node *node, struct rb_root *root);
void rb_erase(struct rb_node *node, struct rb_root *root);
/* Find logical next and previous nodes in a tree */
struct rb_node *rb_next(struct rb_node *);
struct rb_node *rb_prev(struct rb_node *);
struct rb_node *rb_first(struct rb_root *);
struct rb_node *rb_last(struct rb_root *);
/* Fast replacement of a single node without remove/rebalance/add/rebalance */
void rb_replace_node(struct rb_node *victim, struct rb_node *newnode,
struct rb_root *root);
#ifdef __cplusplus
}
#endif
static inline void rb_link_node(struct rb_node *node, struct rb_node *parent,
struct rb_node **link)
{
node->rb_parent = parent;
node->rb_color = RB_RED;
node->rb_left = node->rb_right = (struct rb_node *)0;
*link = node;
}
#endif /* _LINUX_RBTREE_H */

3476
external/libhv/libhv-1.3.2/cert/cacert.pem vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,2 @@
openssl genrsa -out server.key 2048
openssl req -new -x509 -key server.key -out server.crt -days 3650

View File

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDgjCCAmqgAwIBAgIJAL/XKTs4cCwRMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV
BAYTAkNOMREwDwYDVQQIDAhTaGFuZ2hhaTERMA8GA1UEBwwIU2hhbmdoYWkxITAf
BgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0xOTA4MjkwNDA1NDda
Fw0yOTA4MjYwNDA1NDdaMFYxCzAJBgNVBAYTAkNOMREwDwYDVQQIDAhTaGFuZ2hh
aTERMA8GA1UEBwwIU2hhbmdoYWkxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMg
UHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANJJ5DaZroEg
N34H6A3uq5qDRlJe/ELhWcmtkj57cYb5U/T39C7dEjqGfhURb/NEuYbYoEb+4/lD
xJOvn0qqXiJGPt0avecGhQUpggykEj4NZe6W1/WIX81Z1tF6QJZrpUDq4MD1S3KA
ETvnTDv0bc6lvT/YDDb8qYMKGNZUTIQKpucdrIet9kDVIrXON4icJL9PDi/zFu3S
e6feGsG7SAWv1ZInzF6wkEr8pCQc2Aab9R2BsbDsnVc8qZahfLniif7yDxiRbcEf
P4P4cJ7YCD6T1Vyv7bDCnxSEJ+b+j4Ih5XVwHUeK2wG9rrNQ6Z4sSMrv1oSoxVfG
mGdHHEhBN0ECAwEAAaNTMFEwHQYDVR0OBBYEFGPOMSeA6p3Vkf4uHdt5eRbrkoXU
MB8GA1UdIwQYMBaAFGPOMSeA6p3Vkf4uHdt5eRbrkoXUMA8GA1UdEwEB/wQFMAMB
Af8wDQYJKoZIhvcNAQELBQADggEBAEa3x7MzIBQvr43l6zuoK9SBoW6DET42yCRb
YA6as/8fFYc4A1GqJStjvOgS0nIhBeCdyLJCy40U5ERhhVKqjPLLHHB4i7sv98Ac
aXgVr91eUZcKeuiEkDHWRPCitkPj3p2v9J2VDAHpxfKvIB+r/q+7O4W6tjJT5fPq
iUgdpr/R6uBFndBtGc+k65byxSQgvAvdM4kpGA8nnypT9i8SGVp0gB7lMG8avUTU
zH16msecIEKLBHED40w9aiuOf3CnMDqGSTilP7p4gvMY4u+qm3zAXN29OHqoldbn
j/A8RE9Q1F1H3G+eSltBuug3DBGp0q0EskxiqtfJbzW9G5fwGxM=
-----END CERTIFICATE-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEA0knkNpmugSA3fgfoDe6rmoNGUl78QuFZya2SPntxhvlT9Pf0
Lt0SOoZ+FRFv80S5htigRv7j+UPEk6+fSqpeIkY+3Rq95waFBSmCDKQSPg1l7pbX
9YhfzVnW0XpAlmulQOrgwPVLcoARO+dMO/RtzqW9P9gMNvypgwoY1lRMhAqm5x2s
h632QNUitc43iJwkv08OL/MW7dJ7p94awbtIBa/VkifMXrCQSvykJBzYBpv1HYGx
sOydVzyplqF8ueKJ/vIPGJFtwR8/g/hwntgIPpPVXK/tsMKfFIQn5v6PgiHldXAd
R4rbAb2us1DpnixIyu/WhKjFV8aYZ0ccSEE3QQIDAQABAoIBAG/JthbMdbbRAI7v
9w1I/lKCTKTHN8T59PhAXAj5KG2/J0GHenhbLzCLhIUAowmoVBP6HqH/KAO/YcW8
y6oujSIdQ5fYenFQxu/qk+bSZZw1FSXTbHRrDbqlcowjOCh+ivfKpLYO8A+rQv4c
RCtvEdyTwNoqqLumbxppCLEPWSmrZOuw/EegeDMRspMpPNsFsk7mcl2w3jJjiX9D
NB/yHZZzbZoTf/ayT/ca6FueKqPF52jIiuDzOncE1PqQ8hiPrXFIh4yZYCn1IpnK
48hotm3NSixpnag6UziwCRSoPsUCue8h2h3Tr690Lr/7LzILdtBJ+4Qxs3mU8Crz
+bM7Xo0CgYEA9K94Gp0gyIxEiIPQ/zcOLeY2y3gUpS5LNaM6lxm82u4nsrC4FQJZ
H72kV6YxvedmC1JZcYc6WiaAelkv9GqSekTMFv05rTsA4zQX59habsXPa5r3vzds
NzgdjhDfwvWZCp3QXDG1n0wEMKJleG9+CK7khQ4doHmjQtCt0w16AxMCgYEA3AM9
qxJSOADePq0G4fJ+4jAr4MgsMBsWaGEWU0Z7pBaTj+hm0ULiwShWy9/bbxw+zZgL
VqE0cCiqKsMZMFtZvwX89fIN71YLkyTbl5Y/Lb65HKfKI8a8LGSNNaAd6CG9vy2C
krqDKoHILCQdBpT7tSi2vF57+uDxJDH6+VyZ0tsCgYEAsVE7o2W87TihLaEA4wJ9
1wtfKCJUK8QZorwwaHGxZ6JwyFDChg8WkSb4IsCAiZNYYtoBkYEi61O9hWx+kQxu
LAcRM5O8qWn54azNqikil+Xnw54g7cR3OqkC2gImdf1PM99bsIQhj1giLTBygk2h
sx8y4a1yEOo1QuVBIpJAmlsCgYEAv3NehXAC9dLjkny0oYeIHEG43PizYwUfQaNC
byLFUquGqtKcLfrbISR+KxjYdV6J1BQ7wZ2z6Omp8l4lnCvR8+U9E7QXpi4lEl0f
bVCEF8WAhcwInYtBkgvJyWFUxPwfhq4OkqoUm7elvauLSn/4bNNJ+K7riguWK14G
vFl1TcMCgYEA9EDTbpBl1oXICe2xevuh1HJQ1eBXAXP0l3hy0h76czBoBlnoF13A
6TOy6iSE3tpEwGPZZ33goKZdKyBLXG3TmemthWMxjBHc31C55gpIIgYPlW4Blv35
djt8AlxF68VKIhY7CkaGhNmUpdSSfANHyirfw9rx70IRwRJfnZ5MN+M=
-----END RSA PRIVATE KEY-----

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,87 @@
include(SelectLibraryConfigurations)
find_path(libhv_INCLUDE_DIRS hv/hv.h)
message("libhv_INCLUDE_DIRS: " ${libhv_INCLUDE_DIRS})
find_library(libhv_LIBRARY_RELEASE NAMES hv PATHS "${CMAKE_CURRENT_LIST_DIR}/../../lib" NO_DEFAULT_PATH)
find_library(libhv_LIBRARY_DEBUG NAMES hv PATHS "${CMAKE_CURRENT_LIST_DIR}/../../debug/lib" NO_DEFAULT_PATH)
select_library_configurations(libhv)
if(NOT libhv_LIBRARY)
set(libhv_FOUND FALSE)
set(LIBHV_FOUND FALSE)
return()
endif()
if(WIN32)
find_file(libhv_LIBRARY_RELEASE_DLL NAMES hv.dll PATHS "${CMAKE_CURRENT_LIST_DIR}/../../bin" NO_DEFAULT_PATH)
find_file(libhv_LIBRARY_DEBUG_DLL NAMES hv.dll PATHS "${CMAKE_CURRENT_LIST_DIR}/../../debug/bin" NO_DEFAULT_PATH)
endif()
# Manage Release Windows shared
if(EXISTS "${libhv_LIBRARY_RELEASE_DLL}")
add_library(libhv SHARED IMPORTED)
set_target_properties(libhv PROPERTIES
IMPORTED_CONFIGURATIONS Release
IMPORTED_LOCATION_RELEASE "${libhv_LIBRARY_RELEASE_DLL}"
IMPORTED_IMPLIB_RELEASE "${libhv_LIBRARY_RELEASE}"
INTERFACE_INCLUDE_DIRECTORIES "${libhv_INCLUDE_DIRS}"
)
endif()
# Manage Debug Windows shared
if(EXISTS "${libhv_LIBRARY_DEBUG_DLL}")
if(EXISTS "${libhv_LIBRARY_RELEASE_DLL}")
#message("Debug mode")
set_target_properties(libhv PROPERTIES
IMPORTED_CONFIGURATIONS "Release;Debug"
IMPORTED_LOCATION_RELEASE "${libhv_LIBRARY_RELEASE_DLL}"
IMPORTED_IMPLIB_RELEASE "${libhv_LIBRARY_RELEASE}"
IMPORTED_LOCATION_DEBUG "${libhv_LIBRARY_DEBUG_DLL}"
IMPORTED_IMPLIB_DEBUG "${libhv_LIBRARY_DEBUG}"
INTERFACE_INCLUDE_DIRECTORIES "${libhv_INCLUDE_DIRS}"
)
else()
add_library(libhv SHARED IMPORTED)
set_target_properties(libhv PROPERTIES
IMPORTED_CONFIGURATIONS Debug
IMPORTED_LOCATION_DEBUG "${libhv_LIBRARY_DEBUG_DLL}"
IMPORTED_IMPLIB_DEBUG "${libhv_LIBRARY_DEBUG}"
INTERFACE_INCLUDE_DIRECTORIES "${libhv_INCLUDE_DIRS}"
)
endif()
endif()
# Manage Release Windows static and Linux shared/static
if((NOT EXISTS "${libhv_LIBRARY_RELEASE_DLL}") AND (EXISTS "${libhv_LIBRARY_RELEASE}"))
add_library(libhv UNKNOWN IMPORTED)
set_target_properties(libhv PROPERTIES
IMPORTED_CONFIGURATIONS Release
IMPORTED_LOCATION_RELEASE "${libhv_LIBRARY_RELEASE}"
INTERFACE_INCLUDE_DIRECTORIES "${libhv_INCLUDE_DIRS}"
)
endif()
# Manage Debug Windows static and Linux shared/static
if((NOT EXISTS "${libhv_LIBRARY_DEBUG_DLL}") AND (EXISTS "${libhv_LIBRARY_DEBUG}"))
if(EXISTS "${libhv_LIBRARY_RELEASE}")
set_target_properties(libhv PROPERTIES
IMPORTED_CONFIGURATIONS "Release;Debug"
IMPORTED_LOCATION_RELEASE "${libhv_LIBRARY_RELEASE}"
IMPORTED_LOCATION_DEBUG "${libhv_LIBRARY_DEBUG}"
INTERFACE_INCLUDE_DIRECTORIES "${libhv_INCLUDE_DIRS}"
)
else()
add_library(libhv UNKNOWN IMPORTED)
set_target_properties(libhv PROPERTIES
IMPORTED_CONFIGURATIONS Debug
IMPORTED_LOCATION_DEBUG "${libhv_LIBRARY_DEBUG}"
INTERFACE_INCLUDE_DIRECTORIES "${libhv_INCLUDE_DIRS}"
)
endif()
endif()
set(libhv_FOUND TRUE)
set(LIBHV_FOUND TRUE)

View File

@ -0,0 +1,42 @@
include(CheckIncludeFiles)
macro(check_header header)
string(TOUPPER ${header} str1)
string(REGEX REPLACE "[/.]" "_" str2 ${str1})
set(str3 HAVE_${str2})
check_include_files(${header} ${str3})
if (${str3})
set(${str3} 1)
else()
set(${str3} 0)
endif()
endmacro()
include(CheckSymbolExists)
macro(check_function function header)
string(TOUPPER ${function} str1)
set(str2 HAVE_${str1})
check_symbol_exists(${function} ${header} ${str2})
if (${str2})
set(${str2} 1)
else()
set(${str2} 0)
endif()
endmacro()
macro(list_source_directories srcs)
unset(tmp)
foreach(dir ${ARGN})
aux_source_directory(${dir} tmp)
endforeach()
set(${srcs} ${tmp})
list(FILTER ${srcs} EXCLUDE REGEX ".*_test\\.c")
endmacro()
macro(glob_headers_and_sources files)
unset(tmp)
foreach(dir ${ARGN})
file(GLOB tmp ${dir}/*.h ${dir}/*.c ${dir}/*.hpp ${dir}/*.cpp)
list(APPEND ${files} ${tmp})
endforeach()
list(FILTER ${files} EXCLUDE REGEX ".*_test\\.c")
endmacro()

View File

@ -0,0 +1,110 @@
# see Makefile.vars
set(BASE_HEADERS
base/hplatform.h
base/hdef.h
base/hatomic.h
base/herr.h
base/htime.h
base/hmath.h
base/hbase.h
base/hversion.h
base/hsysinfo.h
base/hproc.h
base/hthread.h
base/hmutex.h
base/hsocket.h
base/hlog.h
base/hbuf.h
base/hmain.h
base/hendian.h
)
set(SSL_HEADERS
ssl/hssl.h
)
set(EVENT_HEADERS
event/hloop.h
event/nlog.h
)
set(UTIL_HEADERS
util/base64.h
util/md5.h
util/sha1.h
)
set(CPPUTIL_HEADERS
cpputil/hmap.h
cpputil/hstring.h
cpputil/hfile.h
cpputil/hpath.h
cpputil/hdir.h
cpputil/hurl.h
cpputil/hscope.h
cpputil/hthreadpool.h
cpputil/hasync.h
cpputil/hobjectpool.h
cpputil/ifconfig.h
cpputil/iniparser.h
cpputil/json.hpp
cpputil/singleton.h
cpputil/ThreadLocalStorage.h
)
set(EVPP_HEADERS
evpp/Buffer.h
evpp/Channel.h
evpp/Event.h
evpp/EventLoop.h
evpp/EventLoopThread.h
evpp/EventLoopThreadPool.h
evpp/Status.h
evpp/TcpClient.h
evpp/TcpServer.h
evpp/UdpClient.h
evpp/UdpServer.h
)
set(PROTOCOL_HEADERS
protocol/icmp.h
protocol/dns.h
protocol/ftp.h
protocol/smtp.h
)
set(HTTP_HEADERS
http/httpdef.h
http/wsdef.h
http/http_content.h
http/HttpMessage.h
http/HttpParser.h
http/WebSocketParser.h
http/WebSocketChannel.h
)
set(HTTP2_HEADERS
http/http2def.h
http/grpcdef.h
)
set(HTTP_CLIENT_HEADERS
http/client/HttpClient.h
http/client/requests.h
http/client/axios.h
http/client/AsyncHttpClient.h
http/client/WebSocketClient.h)
set(HTTP_SERVER_HEADERS
http/server/HttpServer.h
http/server/HttpService.h
http/server/HttpContext.h
http/server/HttpResponseWriter.h
http/server/WebSocketServer.h
)
set(MQTT_HEADERS
mqtt/mqtt_protocol.h
mqtt/mqtt_client.h
)

37
external/libhv/libhv-1.3.2/config.ini vendored Normal file
View File

@ -0,0 +1,37 @@
# Don't modify this file, you should modify config.mk or
# run ./configure --with-MODULE --enable-FEATURE
PREFIX=/usr/local
INSTALL_INCDIR=$(PREFIX)/include/hv
INSTALL_LIBDIR=$(PREFIX)/lib
# modules
# include icmp dns ftp smtp
WITH_PROTOCOL=no
WITH_EVPP=yes
WITH_HTTP=yes
WITH_HTTP_SERVER=yes
WITH_HTTP_CLIENT=yes
WITH_MQTT=no
# features
# base/hsocket.h: Unix Domain Socket
ENABLE_UDS=no
# base/RAII.cpp: Windows MiniDumpWriteDump
ENABLE_WINDUMP=no
# http/http_content.h: KeyValue,QueryParams,MultiPart
USE_MULTIMAP=no
# dependencies
# for http/client
WITH_CURL=no
# for http2
WITH_NGHTTP2=no
# for SSL/TLS
WITH_OPENSSL=no
WITH_GNUTLS=no
WITH_MBEDTLS=no
# rudp
WITH_KCP=no

288
external/libhv/libhv-1.3.2/configure vendored Executable file
View File

@ -0,0 +1,288 @@
#!/bin/bash
. "$(dirname "$0")/scripts/shini.sh"
print_help() {
shini_parse "config.ini"
cat << END
Usage: ./configure [--option] ...
options:
--prefix=PREFIX (DEFAULT: $PREFIX)
--incdir=INSTALL_INCDIR (DEFAULT: $PREFIX/include/hv)
--libdir=INSTALL_LIBDIR (DEFAULT: $PREFIX/lib)
--with-MODULE
--without-MODULE
--enable-FEATURE
--disable-FEATURE
modules:
--with-protocol compile protocol module? (DEFAULT: $WITH_PROTOCOL)
--with-evpp compile evpp module? (DEFAULT: $WITH_EVPP)
--with-http compile http module? (DEFAULT: $WITH_HTTP)
--with-http-client compile http client module? (DEFAULT: $WITH_HTTP_CLIENT)
--with-http-server compile http server module? (DEFAULT: $WITH_HTTP_SERVER)
--with-mqtt compile mqtt module? (DEFAULT: $WITH_MQTT)
features:
--enable-uds enable Unix Domain Socket? (DEFAULT: $ENABLE_UDS)
--enable-windump enable Windows coredump? (DEFAULT: $ENABLE_WINDUMP)
dependencies:
--with-curl compile with curl? (DEFAULT: $WITH_CURL)
--with-nghttp2 compile with nghttp2? (DEFAULT: $WITH_NGHTTP2)
--with-openssl compile with openssl? (DEFAULT: $WITH_OPENSSL)
--with-gnutls compile with gnutls? (DEFAULT: $WITH_GNUTLS)
--with-mbedtls compile with mbedtls? (DEFAULT: $WITH_MBEDTLS)
rudp:
--with-kcp compile with kcp? (DEFAULT: $WITH_KCP)
END
}
mkdir tmp 2>/dev/null
while [ -n "$1" ]
do
opt="$1"
KEY=""
VAL=yes
case $opt in
--help)
print_help
exit 0
;;
--prefix=*)
KEY="PREFIX"
VAL=${opt:9}
;;
--incdir=*)
KEY="INSTALL_INCDIR"
VAL=${opt:9}
;;
--libdir=*)
KEY="INSTALL_LIBDIR"
VAL=${opt:9}
;;
--with-*)
KEY="WITH_${opt:7}"
;;
--without-*)
KEY="WITH_${opt:10}"
VAL=no
;;
--enable-*)
KEY="ENABLE_${opt:9}"
;;
--disable-*)
KEY="ENABLE_${opt:10}"
VAL=no
;;
*)
print_help
exit 255
;;
esac
if [ -n $KEY ]; then
FEATURE=$(echo "$KEY" | tr "a-z-" "A-Z_")
if [ ! -f tmp/config.mk ]; then
cp config.ini tmp/config.mk
fi
shini_write "tmp/config.mk" "" "$FEATURE" "$VAL"
fi
shift 1
done
# config.mk
echo "[config.mk]"
if [ -f tmp/config.mk ]; then
mv tmp/config.mk config.mk
shini_write "config.mk" "" "CONFIG_DATE" "$(date +%Y%m%d)"
fi
cat config.mk
echo ""
# Checks for compiler
echo -e "\nchecking for compiler..."
if [ $CROSS_COMPILE ]; then
CC=${CROSS_COMPILE}gcc
CXX=${CROSS_COMPILE}g++
fi
if [ ! $CC ]; then
CC=gcc
CXX=g++
fi
CC_VERSION=`$CC --version 2>&1 | head -n 1`
echo "CC = $CC"
echo "CXX = $CXX"
echo "$CC_VERSION"
# Checks for os
echo -e "\nchecking for os..."
HOST_OS=`uname -s`
HOST_ARCH=`uname -m`
TARGET_PLATFORM=`$CC -v 2>&1 | grep Target | sed 's/Target: //'`
TARGET_ARCH=`echo $TARGET_PLATFORM | awk -F'-' '{print $1}'`
case $TARGET_PLATFORM in
*mingw*) TARGET_OS=Windows ;;
*android*) TARGET_OS=Android ;;
*darwin*) TARGET_OS=Darwin ;;
*) TARGET_OS=Linux ;;
esac
echo "HOST_OS = $HOST_OS"
echo "HOST_ARCH = $HOST_ARCH"
echo "TARGET_PLATFORM = $TARGET_PLATFORM"
echo "TARGET_OS = $TARGET_OS"
echo "TARGET_ARCH = $TARGET_ARCH"
# hconfig.h
echo -e "\n>> hconfig.h"
confile=hconfig.h
cat << END > $confile
#ifndef HV_CONFIG_H_
#define HV_CONFIG_H_
END
write_define() {
cat << END >> hconfig.h
#ifndef $macro
#define $macro $value
#endif
END
}
CheckHeaderExists() {
rm tmp/check 2>/dev/null
cat << END > tmp/check.c
#include <$header>
int main() {
return 0;
}
END
$CC -o tmp/check tmp/check.c 2>/dev/null
if [ -x tmp/check ]; then
value=1
else
value=0
fi
}
CheckSymbolExists() {
CheckHeaderExists
if [ $value -eq 0 ]; then
return;
fi
rm tmp/check 2>/dev/null
cat << END > tmp/check.c
#include <$header>
int $function(void** pp) {return 0;}
int main() {
void* p;
return $function(&p);
}
END
$CC -o tmp/check tmp/check.c 2>/dev/null
if [ -x tmp/check ]; then
value=0
else
value=1
fi
}
check_header() {
echo -n "checking for $header... "
CheckHeaderExists
if [ $value -eq 0 ]; then
echo "no"
else
echo "yes"
fi
macro=HAVE_$(echo $header | tr a-z./ A-Z__)
write_define
}
check_function() {
echo -n "checking for $function... "
CheckSymbolExists
if [ $value -eq 0 ]; then
echo "no"
else
echo "yes"
fi
macro=HAVE_$(echo $function | tr a-z A-Z)
write_define
}
check_option() {
value=$(eval echo \$$option)
echo "checking for $option=$value"
if [ "$value" == "yes" ]; then
cat << END >> $confile
#define $option 1
END
else
cat << END >> $confile
/* #undef $option */
END
fi
}
# Checks for programs
# Checks for libraries
# Checks for header files
header=stdbool.h && check_header
header=stdint.h && check_header
header=stdatomic.h && check_header
header=sys/types.h && check_header
header=sys/stat.h && check_header
header=sys/time.h && check_header
header=fcntl.h && check_header
header=pthread.h && check_header
header=endian.h && check_header
header=sys/endian.h && check_header
# Checks for functions
function=gettid && header=unistd.h && check_function
function=strlcpy && header=string.h && check_function
function=strlcat && header=string.h && check_function
function=clock_gettime && header=time.h && check_function
function=gettimeofday && header=sys/time.h && check_function
function=pthread_spin_lock && header=pthread.h && check_function
function=pthread_mutex_timedlock && header=pthread.h && check_function
function=sem_timedwait && header=semaphore.h && check_function
function=pipe && header=unistd.h && check_function
function=socketpair && header=sys/socket.h && check_function
function=eventfd && header=sys/eventfd.h && check_function
function=setproctitle && header=unistd.h && check_function
# Checks for options
source config.mk 2>/dev/null
option=WITH_OPENSSL && check_option
option=WITH_GNUTLS && check_option
option=WITH_MBEDTLS && check_option
option=ENABLE_UDS && check_option
option=USE_MULTIMAP && check_option
option=WITH_KCP && check_option
# end confile
cat << END >> $confile
#endif // HV_CONFIG_H_
END
echo "configure done."

View File

@ -0,0 +1,66 @@
#include "hplatform.h"
#ifdef OS_WIN
#ifdef ENABLE_WINDUMP
#include <dbghelp.h>
#ifdef _MSC_VER
#pragma comment(lib,"dbghelp.lib")
#endif
static LONG UnhandledException(EXCEPTION_POINTERS *pException) {
char modulefile[256];
GetModuleFileName(NULL, modulefile, sizeof(modulefile));
char* pos = strrchr(modulefile, '\\');
char* modulefilename = pos + 1;
SYSTEMTIME st;
GetLocalTime(&st);
char filename[256];
snprintf(filename, sizeof(filename), "core_%s_%04d%02d%02d_%02d%02d%02d_%03d.dump",
modulefilename,
st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
HANDLE hDumpFile = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
MINIDUMP_EXCEPTION_INFORMATION dumpInfo;
dumpInfo.ExceptionPointers = pException;
dumpInfo.ThreadId = GetCurrentThreadId();
dumpInfo.ClientPointers = TRUE;
MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, MiniDumpNormal, &dumpInfo, NULL, NULL);
CloseHandle(hDumpFile);
return EXCEPTION_EXECUTE_HANDLER;
}
#endif
#include "hsocket.h"
class WsaRAII {
public:
WsaRAII() {
WSAInit();
#ifdef ENABLE_WINDUMP
SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)UnhandledException);
#endif
}
~WsaRAII() {
WSADeinit();
}
};
static WsaRAII s_wsa;
#endif
#ifdef WITH_CURL
#include "curl/curl.h"
#ifdef _MSC_VER
//#pragma comment(lib, "libcurl.a")
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "wldap32.lib")
#pragma comment(lib, "advapi32.lib")
#pragma comment(lib, "crypt32.lib")
#endif
class CurlRAII {
public:
CurlRAII() {
curl_global_init(CURL_GLOBAL_ALL);
}
~CurlRAII() {
curl_global_cleanup();
}
};
static CurlRAII s_curl;
#endif

View File

@ -0,0 +1,20 @@
## 目录结构
```
.
├── hasync.h hv::async实现
├── hdir.h 目录(ls实现)
├── hfile.h 文件类
├── hobjectpool.h 对象池
├── hpath.h 路径操作
├── hscope.h 作用域模板类
├── hstring.h 字符串操作
├── hthreadpool.h 线程池
├── hurl.h URL操作
├── ifconfig.h 网络配置(ifconfig实现)
├── iniparser.h INI解析
├── json.hpp JSON解析
├── singleton.h 单例模式宏
└── ThreadLocalStorage.h 线程本地存储类
```

View File

@ -0,0 +1,32 @@
#include "ThreadLocalStorage.h"
#include "hthread.h"
namespace hv {
ThreadLocalStorage ThreadLocalStorage::tls[ThreadLocalStorage::MAX_NUM];
void ThreadLocalStorage::set(int idx, void* val) {
return tls[idx].set(val);
}
void* ThreadLocalStorage::get(int idx) {
return tls[idx].get();
}
void ThreadLocalStorage::setThreadName(const char* name) {
set(THREAD_NAME, (void*)name);
}
const char* ThreadLocalStorage::threadName() {
void* value = get(THREAD_NAME);
if (value) {
return (char*)value;
}
static char unnamed[32] = {0};
snprintf(unnamed, sizeof(unnamed)-1, "thread-%ld", hv_gettid());
return unnamed;
}
}

View File

@ -0,0 +1,67 @@
#ifndef HV_THREAD_LOCAL_STORAGE_H_
#define HV_THREAD_LOCAL_STORAGE_H_
#include "hexport.h"
#include "hplatform.h"
#ifdef OS_WIN
#define hthread_key_t DWORD
#define INVALID_HTHREAD_KEY 0xFFFFFFFF
#define hthread_key_create(pkey) *pkey = TlsAlloc()
#define hthread_key_delete TlsFree
#define hthread_get_value TlsGetValue
#define hthread_set_value TlsSetValue
#else
#define hthread_key_t pthread_key_t
#define INVALID_HTHREAD_KEY 0xFFFFFFFF
#define hthread_key_create(pkey) pthread_key_create(pkey, NULL)
#define hthread_key_delete pthread_key_delete
#define hthread_get_value pthread_getspecific
#define hthread_set_value pthread_setspecific
#endif
#ifdef __cplusplus
namespace hv {
class HV_EXPORT ThreadLocalStorage {
public:
enum {
THREAD_NAME = 0,
EVENT_LOOP = 1,
MAX_NUM = 16,
};
ThreadLocalStorage() {
hthread_key_create(&key);
}
~ThreadLocalStorage() {
hthread_key_delete(key);
}
void set(void* val) {
hthread_set_value(key, val);
}
void* get() {
return hthread_get_value(key);
}
static void set(int idx, void* val);
static void* get(int idx);
static void setThreadName(const char* name);
static const char* threadName();
private:
hthread_key_t key;
static ThreadLocalStorage tls[MAX_NUM];
};
}
#endif
#endif // HV_THREAD_LOCAL_STORAGE_H_

View File

@ -0,0 +1,7 @@
#include "hasync.h"
namespace hv {
SINGLETON_IMPL(GlobalThreadPool)
}

View File

@ -0,0 +1,49 @@
#ifndef HV_ASYNC_H_
#define HV_ASYNC_H_
#include "hexport.h"
#include "hthreadpool.h"
#include "singleton.h"
namespace hv {
class HV_EXPORT GlobalThreadPool : public HThreadPool {
SINGLETON_DECL(GlobalThreadPool)
protected:
GlobalThreadPool() : HThreadPool() {}
~GlobalThreadPool() {}
};
/*
* return a future, calling future.get() will wait task done and return RetType.
* async(fn, args...)
* async(std::bind(&Class::mem_fn, &obj))
* async(std::mem_fn(&Class::mem_fn, &obj))
*
*/
template<class Fn, class... Args>
auto async(Fn&& fn, Args&&... args) -> std::future<decltype(fn(args...))> {
return GlobalThreadPool::instance()->commit(std::forward<Fn>(fn), std::forward<Args>(args)...);
}
class async {
public:
static void startup(int min_threads = DEFAULT_THREAD_POOL_MIN_THREAD_NUM,
int max_threads = DEFAULT_THREAD_POOL_MAX_THREAD_NUM,
int max_idle_ms = DEFAULT_THREAD_POOL_MAX_IDLE_TIME) {
GlobalThreadPool* gtp = GlobalThreadPool::instance();
if (gtp->isStarted()) return;
gtp->setMinThreadNum(min_threads);
gtp->setMaxThreadNum(max_threads);
gtp->setMaxIdleTime(max_idle_ms);
gtp->start();
}
static void cleanup() {
GlobalThreadPool::exitInstance();
}
};
} // end namespace hv
#endif // HV_ASYNC_H_

View File

@ -0,0 +1,88 @@
#include "hdir.h"
#include "hplatform.h"
#ifdef OS_WIN
//FILETIME starts from 1601-01-01 UTC, epoch from 1970-01-01 UTC
//FILETIME unit (100ns)
#define FILETIME_EPOCH_DIFF 11644473600 // s
time_t FileTime2Epoch(FILETIME filetime) {
uint64_t ll = (((uint64_t)filetime.dwHighDateTime) << 32) | filetime.dwLowDateTime;
ll /= 1e7; // s
return ll - FILETIME_EPOCH_DIFF;
}
#endif
static bool less(const hdir_t& lhs, const hdir_t& rhs) {
return stricmp(lhs.name, rhs.name) < 0;
}
int listdir(const char* dir, std::list<hdir_t>& dirs) {
int dirlen = strlen(dir);
if (dirlen > 256) {
return -1;
}
char path[512];
strcpy(path, dir);
if (dir[dirlen-1] != '/') {
strcat(path, "/");
++dirlen;
}
dirs.clear();
#ifdef OS_UNIX
// opendir -> readdir -> closedir
DIR* dp = opendir(dir);
if (dp == NULL) return -1;
struct dirent* result = NULL;
struct stat st;
hdir_t tmp;
while ((result = readdir(dp))) {
memset(&tmp, 0, sizeof(hdir_t));
strncpy(tmp.name, result->d_name, sizeof(tmp.name));
strncpy(path+dirlen, result->d_name, sizeof(path)-dirlen);
if (lstat(path, &st) == 0) {
if (S_ISREG(st.st_mode)) tmp.type = 'f';
else if (S_ISDIR(st.st_mode)) tmp.type = 'd';
else if (S_ISLNK(st.st_mode)) tmp.type = 'l';
else if (S_ISBLK(st.st_mode)) tmp.type = 'b';
else if (S_ISCHR(st.st_mode)) tmp.type = 'c';
else if (S_ISSOCK(st.st_mode)) tmp.type = 's';
else if (S_ISFIFO(st.st_mode)) tmp.type = 'p';
else tmp.type = '-';
tmp.mode = st.st_mode & 0777;
tmp.size = st.st_size;
tmp.atime = st.st_atime;
tmp.mtime = st.st_mtime;
tmp.ctime = st.st_ctime;
}
dirs.push_back(tmp);
}
closedir(dp);
#elif defined(OS_WIN)
// FindFirstFile -> FindNextFile -> FindClose
strcat(path, "*");
WIN32_FIND_DATAA data;
HANDLE h = FindFirstFileA(path, &data);
if (h == NULL) {
return -1;
}
hdir_t tmp;
do {
memset(&tmp, 0, sizeof(hdir_t));
strncpy(tmp.name, data.cFileName, sizeof(tmp.name));
tmp.type = 'f';
if (data.dwFileAttributes & _A_SUBDIR) {
tmp.type = 'd';
}
tmp.mode = 0777;
tmp.size = (((uint64_t)data.nFileSizeHigh) << 32) | data.nFileSizeLow;
tmp.atime = FileTime2Epoch(data.ftLastAccessTime);
tmp.mtime = FileTime2Epoch(data.ftLastWriteTime);
tmp.ctime = FileTime2Epoch(data.ftCreationTime);
dirs.push_back(tmp);
} while (FindNextFileA(h, &data));
FindClose(h);
#endif
dirs.sort(less);
return dirs.size();
}

View File

@ -0,0 +1,69 @@
#ifndef HV_DIR_H_
#define HV_DIR_H_
/*
*@code
int main(int argc, char* argv[]) {
const char* dir = ".";
if (argc > 1) {
dir = argv[1];
}
std::list<hdir_t> dirs;
listdir(dir, dirs);
for (auto& item : dirs) {
printf("%c%c%c%c%c%c%c%c%c%c\t",
item.type,
item.mode & 0400 ? 'r' : '-',
item.mode & 0200 ? 'w' : '-',
item.mode & 0100 ? 'x' : '-',
item.mode & 0040 ? 'r' : '-',
item.mode & 0020 ? 'w' : '-',
item.mode & 0010 ? 'x' : '-',
item.mode & 0004 ? 'r' : '-',
item.mode & 0002 ? 'w' : '-',
item.mode & 0001 ? 'x' : '-');
float hsize;
if (item.size < 1024) {
printf("%lu\t", item.size);
}
else if ((hsize = item.size/1024.0f) < 1024.0f) {
printf("%.1fK\t", hsize);
}
else if ((hsize /= 1024.0f) < 1024.0f) {
printf("%.1fM\t", hsize);
}
else {
hsize /= 1024.0f;
printf("%.1fG\t", hsize);
}
struct tm* tm = localtime(&item.mtime);
printf("%04d-%02d-%02d %02d:%02d:%02d\t",
tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
printf("%s%s\n", item.name, item.type == 'd' ? "/" : "");
}
return 0;
}
*/
#include <string.h>
#include <time.h>
#include <list>
#include "hexport.h"
typedef struct hdir_s {
char name[256];
char type; // f:file d:dir l:link b:block c:char s:socket p:pipe
char reserverd;
unsigned short mode;
size_t size;
time_t atime;
time_t mtime;
time_t ctime;
} hdir_t;
// listdir: same as ls on unix, dir on win
HV_EXPORT int listdir(const char* dir, std::list<hdir_t>& dirs);
#endif // HV_DIR_H_

View File

@ -0,0 +1,134 @@
#ifndef HV_FILE_H_
#define HV_FILE_H_
#include <string> // for std::string
#include "hplatform.h" // for stat
#include "hbuf.h" // for HBuf
class HFile {
public:
HFile() {
filepath[0] = '\0';
fp = NULL;
}
~HFile() {
close();
}
int open(const char* filepath, const char* mode) {
close();
strncpy(this->filepath, filepath, MAX_PATH - 1);
fp = fopen(filepath, mode);
return fp ? 0 : errno;
}
void close() {
if (fp) {
fclose(fp);
fp = NULL;
}
}
bool isopen() {
return fp != NULL;
}
int remove() {
close();
return ::remove(filepath);
}
int rename(const char* newpath) {
close();
return ::rename(filepath, newpath);
}
size_t read(void* ptr, size_t len) {
return fread(ptr, 1, len, fp);
}
size_t write(const void* ptr, size_t len) {
return fwrite(ptr, 1, len, fp);
}
size_t write(const std::string& str) {
return write(str.c_str(), str.length());
}
int seek(size_t offset, int whence = SEEK_SET) {
return fseek(fp, offset, whence);
}
int tell() {
return ftell(fp);
}
int flush() {
return fflush(fp);
}
static size_t size(const char* filepath) {
struct stat st;
memset(&st, 0, sizeof(st));
stat(filepath, &st);
return st.st_size;
}
size_t size() {
return HFile::size(filepath);
}
size_t readall(HBuf& buf) {
size_t filesize = size();
if (filesize == 0) return 0;
buf.resize(filesize);
return fread(buf.base, 1, filesize, fp);
}
size_t readall(std::string& str) {
size_t filesize = size();
if (filesize == 0) return 0;
str.resize(filesize);
return fread((void*)str.data(), 1, filesize, fp);
}
bool readline(std::string& str) {
str.clear();
char ch;
while (fread(&ch, 1, 1, fp)) {
if (ch == '\n') {
// unix: LF
return true;
}
if (ch == '\r') {
// dos: CRLF
// read LF
if (fread(&ch, 1, 1, fp) && ch != '\n') {
// mac: CR
fseek(fp, -1, SEEK_CUR);
}
return true;
}
str += ch;
}
return str.length() != 0;
}
int readrange(std::string& str, size_t from = 0, size_t to = 0) {
size_t filesize = size();
if (filesize == 0) return 0;
if (to == 0 || to >= filesize) to = filesize - 1;
size_t readbytes = to - from + 1;
str.resize(readbytes);
fseek(fp, from, SEEK_SET);
return fread((void*)str.data(), 1, readbytes, fp);
}
public:
char filepath[MAX_PATH];
FILE* fp;
};
#endif // HV_FILE_H_

View File

@ -0,0 +1,55 @@
#ifndef HV_MAP_H_
#define HV_MAP_H_
#include "hconfig.h"
#include <map>
#include <string>
// MultiMap
namespace std {
/*
int main() {
std::MultiMap<std::string, std::string> kvs;
kvs["name"] = "hw";
kvs["filename"] = "1.jpg";
kvs["filename"] = "2.jpg";
//kvs.insert(std::pair<std::string,std::string>("name", "hw"));
//kvs.insert(std::pair<std::string,std::string>("filename", "1.jpg"));
//kvs.insert(std::pair<std::string,std::string>("filename", "2.jpg"));
for (auto& pair : kvs) {
printf("%s:%s\n", pair.first.c_str(), pair.second.c_str());
}
auto iter = kvs.find("filename");
if (iter != kvs.end()) {
for (int i = 0; i < kvs.count("filename"); ++i, ++iter) {
printf("%s:%s\n", iter->first.c_str(), iter->second.c_str());
}
}
return 0;
}
*/
template<typename Key,typename Value>
class MultiMap : public multimap<Key, Value> {
public:
Value& operator[](Key key) {
auto iter = this->insert(std::pair<Key,Value>(key,Value()));
return (*iter).second;
}
};
}
#ifdef USE_MULTIMAP
#define HV_MAP std::MultiMap
#else
#define HV_MAP std::map
#endif
// KeyValue
namespace hv {
typedef std::map<std::string, std::string> keyval_t;
typedef std::MultiMap<std::string, std::string> multi_keyval_t;
typedef HV_MAP<std::string, std::string> KeyValue;
}
#endif // HV_MAP_H_

View File

@ -0,0 +1,183 @@
#ifndef HV_OBJECT_POOL_H_
#define HV_OBJECT_POOL_H_
/*
* @usage unittest/objectpool_test.cpp
*/
#include <list>
#include <memory>
#include <mutex>
#include <condition_variable>
#define DEFAULT_OBJECT_POOL_INIT_NUM 0
#define DEFAULT_OBJECT_POOL_MAX_NUM 4
#define DEFAULT_OBJECT_POOL_TIMEOUT 3000 // ms
template<class T>
class HObjectFactory {
public:
static T* create() {
return new T;
}
};
template<class T, class TFactory = HObjectFactory<T>>
class HObjectPool {
public:
HObjectPool(
int init_num = DEFAULT_OBJECT_POOL_INIT_NUM,
int max_num = DEFAULT_OBJECT_POOL_MAX_NUM,
int timeout = DEFAULT_OBJECT_POOL_TIMEOUT)
: _max_num(max_num)
, _timeout(timeout)
{
for (int i = 0; i < init_num; ++i) {
T* p = TFactory::create();
if (p) {
objects_.push_back(std::shared_ptr<T>(p));
}
}
_object_num = objects_.size();
}
~HObjectPool() {}
int ObjectNum() { return _object_num; }
int IdleNum() { return objects_.size(); }
int BorrowNum() { return ObjectNum() - IdleNum(); }
std::shared_ptr<T> TryBorrow() {
std::shared_ptr<T> pObj = NULL;
std::lock_guard<std::mutex> locker(mutex_);
if (!objects_.empty()) {
pObj = objects_.front();
objects_.pop_front();
}
return pObj;
}
std::shared_ptr<T> Borrow() {
std::shared_ptr<T> pObj = TryBorrow();
if (pObj) {
return pObj;
}
std::unique_lock<std::mutex> locker(mutex_);
if (_object_num < _max_num) {
++_object_num;
// NOTE: unlock to avoid TFactory::create block
mutex_.unlock();
T* p = TFactory::create();
mutex_.lock();
if (!p) --_object_num;
return std::shared_ptr<T>(p);
}
if (_timeout > 0) {
std::cv_status status = cond_.wait_for(locker, std::chrono::milliseconds(_timeout));
if (status == std::cv_status::timeout) {
return NULL;
}
if (!objects_.empty()) {
pObj = objects_.front();
objects_.pop_front();
return pObj;
}
else {
// WARN: No idle object
}
}
return pObj;
}
void Return(std::shared_ptr<T>& pObj) {
if (!pObj) return;
std::lock_guard<std::mutex> locker(mutex_);
objects_.push_back(pObj);
cond_.notify_one();
}
bool Add(std::shared_ptr<T>& pObj) {
std::lock_guard<std::mutex> locker(mutex_);
if (_object_num >= _max_num) {
return false;
}
objects_.push_back(pObj);
++_object_num;
cond_.notify_one();
return true;
}
bool Remove(std::shared_ptr<T>& pObj) {
std::lock_guard<std::mutex> locker(mutex_);
auto iter = objects_.begin();
while (iter != objects_.end()) {
if (*iter == pObj) {
iter = objects_.erase(iter);
--_object_num;
return true;
}
else {
++iter;
}
}
return false;
}
void Clear() {
std::lock_guard<std::mutex> locker(mutex_);
objects_.clear();
_object_num = 0;
}
int _object_num;
int _max_num;
int _timeout;
private:
std::list<std::shared_ptr<T>> objects_;
std::mutex mutex_;
std::condition_variable cond_;
};
template<class T, class TFactory = HObjectFactory<T>>
class HPoolObject {
public:
typedef HObjectPool<T, TFactory> PoolType;
HPoolObject(PoolType& pool) : pool_(pool)
{
sptr_ = pool_.Borrow();
}
~HPoolObject() {
if (sptr_) {
pool_.Return(sptr_);
}
}
HPoolObject(const HPoolObject<T>&) = delete;
HPoolObject<T>& operator=(const HPoolObject<T>&) = delete;
T* get() {
return sptr_.get();
}
operator bool() {
return sptr_.get() != NULL;
}
T* operator->() {
return sptr_.get();
}
T operator*() {
return *sptr_.get();
}
private:
PoolType& pool_;
std::shared_ptr<T> sptr_;
};
#endif // HV_OBJECT_POOL_H_

View File

@ -0,0 +1,112 @@
#include "hpath.h"
#include "hplatform.h"
bool HPath::exists(const char* path) {
return access(path, 0) == 0;
}
bool HPath::isdir(const char* path) {
if (access(path, 0) != 0) return false;
struct stat st;
memset(&st, 0, sizeof(st));
stat(path, &st);
return S_ISDIR(st.st_mode);
}
bool HPath::isfile(const char* path) {
if (access(path, 0) != 0) return false;
struct stat st;
memset(&st, 0, sizeof(st));
stat(path, &st);
return S_ISREG(st.st_mode);
}
bool HPath::islink(const char* path) {
#ifdef OS_WIN
return HPath::isdir(path) && (GetFileAttributesA(path) & FILE_ATTRIBUTE_REPARSE_POINT);
#else
if (access(path, 0) != 0) return false;
struct stat st;
memset(&st, 0, sizeof(st));
lstat(path, &st);
return S_ISLNK(st.st_mode);
#endif
}
std::string HPath::basename(const std::string& filepath) {
std::string::size_type pos1 = filepath.find_last_not_of("/\\");
if (pos1 == std::string::npos) {
return "/";
}
std::string::size_type pos2 = filepath.find_last_of("/\\", pos1);
if (pos2 == std::string::npos) {
pos2 = 0;
} else {
pos2++;
}
return filepath.substr(pos2, pos1-pos2+1);
}
std::string HPath::dirname(const std::string& filepath) {
std::string::size_type pos1 = filepath.find_last_not_of("/\\");
if (pos1 == std::string::npos) {
return "/";
}
std::string::size_type pos2 = filepath.find_last_of("/\\", pos1);
if (pos2 == std::string::npos) {
return ".";
} else if (pos2 == 0) {
pos2 = 1;
}
return filepath.substr(0, pos2);
}
std::string HPath::filename(const std::string& filepath) {
std::string::size_type pos1 = filepath.find_last_of("/\\");
if (pos1 == std::string::npos) {
pos1 = 0;
} else {
pos1++;
}
std::string file = filepath.substr(pos1, -1);
std::string::size_type pos2 = file.find_last_of(".");
if (pos2 == std::string::npos) {
return file;
}
return file.substr(0, pos2);
}
std::string HPath::suffixname(const std::string& filepath) {
std::string::size_type pos1 = filepath.find_last_of("/\\");
if (pos1 == std::string::npos) {
pos1 = 0;
} else {
pos1++;
}
std::string file = filepath.substr(pos1, -1);
std::string::size_type pos2 = file.find_last_of(".");
if (pos2 == std::string::npos) {
return "";
}
return file.substr(pos2+1, -1);
}
std::string HPath::join(const std::string& dir, const std::string& filename) {
char separator = '/';
#ifdef OS_WIN
if (dir.find_first_of("\\") != std::string::npos) {
separator = '\\';
}
#endif
std::string filepath(dir);
if (dir[dir.length()-1] != separator) {
filepath += separator;
}
filepath += filename;
return filepath;
}

View File

@ -0,0 +1,28 @@
#ifndef HV_PATH_H_
#define HV_PATH_H_
#include <string> // for std::string
#include "hexport.h"
class HV_EXPORT HPath {
public:
static bool exists(const char* path);
static bool isdir(const char* path);
static bool isfile(const char* path);
static bool islink(const char* path);
// filepath = /mnt/share/image/test.jpg
// basename = test.jpg
// dirname = /mnt/share/image
// filename = test
// suffixname = jpg
static std::string basename(const std::string& filepath);
static std::string dirname(const std::string& filepath);
static std::string filename(const std::string& filepath);
static std::string suffixname(const std::string& filepath);
static std::string join(const std::string& dir, const std::string& filename);
};
#endif // HV_PATH_H_

View File

@ -0,0 +1,79 @@
#ifndef HV_SCOPE_H_
#define HV_SCOPE_H_
#include <functional>
typedef std::function<void()> Function;
#include "hdef.h"
// same as golang defer
class Defer {
public:
Defer(Function&& fn) : _fn(std::move(fn)) {}
~Defer() { if(_fn) _fn();}
private:
Function _fn;
};
#define defer(code) Defer STRINGCAT(_defer_, __LINE__)([&](){code});
class ScopeCleanup {
public:
template<typename Fn, typename... Args>
ScopeCleanup(Fn&& fn, Args&&... args) {
_cleanup = std::bind(std::forward<Fn>(fn), std::forward<Args>(args)...);
}
~ScopeCleanup() {
_cleanup();
}
private:
Function _cleanup;
};
template<typename T>
class ScopeFree {
public:
ScopeFree(T* p) : _p(p) {}
~ScopeFree() {SAFE_FREE(_p);}
private:
T* _p;
};
template<typename T>
class ScopeDelete {
public:
ScopeDelete(T* p) : _p(p) {}
~ScopeDelete() {SAFE_DELETE(_p);}
private:
T* _p;
};
template<typename T>
class ScopeDeleteArray {
public:
ScopeDeleteArray(T* p) : _p(p) {}
~ScopeDeleteArray() {SAFE_DELETE_ARRAY(_p);}
private:
T* _p;
};
template<typename T>
class ScopeRelease {
public:
ScopeRelease(T* p) : _p(p) {}
~ScopeRelease() {SAFE_RELEASE(_p);}
private:
T* _p;
};
template<typename T>
class ScopeLock {
public:
ScopeLock(T& mutex) : _mutex(mutex) {_mutex.lock();}
~ScopeLock() {_mutex.unlock();}
private:
T& _mutex;
};
#endif // HV_SCOPE_H_

View File

@ -0,0 +1,205 @@
#include "hstring.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
namespace hv {
std::string empty_string;
std::map<std::string, std::string> empty_map;
std::string& toupper(std::string& str) {
// std::transform(str.begin(), str.end(), str.begin(), ::toupper);
char* p = (char*)str.c_str();
while (*p != '\0') {
if (*p >= 'a' && *p <= 'z') {
*p &= ~0x20;
}
++p;
}
return str;
}
std::string& tolower(std::string& str) {
// std::transform(str.begin(), str.end(), str.begin(), ::tolower);
char* p = (char*)str.c_str();
while (*p != '\0') {
if (*p >= 'A' && *p <= 'Z') {
*p |= 0x20;
}
++p;
}
return str;
}
std::string& reverse(std::string& str) {
// std::reverse(str.begin(), str.end());
char* b = (char*)str.c_str();
char* e = b + str.length() - 1;
char tmp;
while (e > b) {
tmp = *e;
*e = *b;
*b = tmp;
--e;
++b;
}
return str;
}
bool startswith(const std::string& str, const std::string& start) {
if (str.length() < start.length()) return false;
return str.compare(0, start.length(), start) == 0;
}
bool endswith(const std::string& str, const std::string& end) {
if (str.length() < end.length()) return false;
return str.compare(str.length() - end.length(), end.length(), end) == 0;
}
bool contains(const std::string& str, const std::string& sub) {
if (str.length() < sub.length()) return false;
return str.find(sub) != std::string::npos;
}
static inline int vscprintf(const char* fmt, va_list ap) {
return vsnprintf(NULL, 0, fmt, ap);
}
std::string asprintf(const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
int len = vscprintf(fmt, ap);
va_end(ap);
std::string str;
str.reserve(len+1);
// must resize to set str.size
str.resize(len);
// must recall va_start on unix
va_start(ap, fmt);
vsnprintf((char*)str.data(), len+1, fmt, ap);
va_end(ap);
return str;
}
StringList split(const std::string& str, char delim) {
/*
std::stringstream ss;
ss << str;
string item;
StringList res;
while (std::getline(ss, item, delim)) {
res.push_back(item);
}
return res;
*/
const char* p = str.c_str();
const char* value = p;
StringList res;
while (*p != '\0') {
if (*p == delim) {
res.push_back(std::string(value, p-value));
value = p+1;
}
++p;
}
res.push_back(value);
return res;
}
hv::KeyValue splitKV(const std::string& str, char kv_kv, char k_v) {
enum {
s_key,
s_value,
} state = s_key;
const char* p = str.c_str();
const char* key = p;
const char* value = NULL;
int key_len = 0;
int value_len = 0;
hv::KeyValue kvs;
while (*p != '\0') {
if (*p == kv_kv) {
if (key_len && value_len) {
kvs[std::string(key, key_len)] = std::string(value, value_len);
key_len = value_len = 0;
}
state = s_key;
key = p+1;
}
else if (*p == k_v) {
state = s_value;
value = p+1;
}
else {
state == s_key ? ++key_len : ++value_len;
}
++p;
}
if (key_len && value_len) {
kvs[std::string(key, key_len)] = std::string(value, value_len);
}
return kvs;
}
std::string trim(const std::string& str, const char* chars) {
std::string::size_type pos1 = str.find_first_not_of(chars);
if (pos1 == std::string::npos) return "";
std::string::size_type pos2 = str.find_last_not_of(chars);
return str.substr(pos1, pos2-pos1+1);
}
std::string ltrim(const std::string& str, const char* chars) {
std::string::size_type pos = str.find_first_not_of(chars);
if (pos == std::string::npos) return "";
return str.substr(pos);
}
std::string rtrim(const std::string& str, const char* chars) {
std::string::size_type pos = str.find_last_not_of(chars);
return str.substr(0, pos+1);
}
std::string trim_pairs(const std::string& str, const char* pairs) {
const char* s = str.c_str();
const char* e = str.c_str() + str.size() - 1;
const char* p = pairs;
bool is_pair = false;
while (*p != '\0' && *(p+1) != '\0') {
if (*s == *p && *e == *(p+1)) {
is_pair = true;
break;
}
p += 2;
}
return is_pair ? str.substr(1, str.size()-2) : str;
}
std::string replace(const std::string& str, const std::string& find, const std::string& rep) {
std::string res(str);
std::string::size_type pos = res.find(find);
if (pos != std::string::npos) {
res.replace(pos, find.size(), rep);
}
return res;
}
std::string replaceAll(const std::string& str, const std::string& find, const std::string& rep) {
std::string::size_type pos = 0;
std::string::size_type a = find.size();
std::string::size_type b = rep.size();
std::string res(str);
while ((pos = res.find(find, pos)) != std::string::npos) {
res.replace(pos, a, rep);
pos += b;
}
return res;
}
} // end namespace hv

View File

@ -0,0 +1,80 @@
#ifndef HV_STRING_H_
#define HV_STRING_H_
#include <string>
#include <vector>
#include <iostream>
#include <sstream>
#include "hexport.h"
#include "hplatform.h"
#include "hmap.h"
#define SPACE_CHARS " \t\r\n"
#define PAIR_CHARS "{}[]()<>\"\"\'\'``"
namespace hv {
HV_EXPORT extern std::string empty_string;
HV_EXPORT extern std::map<std::string, std::string> empty_map;
typedef std::vector<std::string> StringList;
// std::map<std::string, std::string, StringCaseLess>
class StringCaseLess : public std::less<std::string> {
public:
bool operator()(const std::string& lhs, const std::string& rhs) const {
return strcasecmp(lhs.c_str(), rhs.c_str()) < 0;
}
};
// NOTE: low-version NDK not provide std::to_string
template<typename T>
HV_INLINE std::string to_string(const T& t) {
std::ostringstream oss;
oss << t;
return oss.str();
}
template<typename T>
HV_INLINE T from_string(const std::string& str) {
T t;
std::istringstream iss(str);
iss >> t;
return t;
}
template<typename T>
HV_INLINE void print(const T& t) {
std::cout << t;
}
template<typename T>
HV_INLINE void println(const T& t) {
std::cout << t << std::endl;
}
HV_EXPORT std::string& toupper(std::string& str);
HV_EXPORT std::string& tolower(std::string& str);
HV_EXPORT std::string& reverse(std::string& str);
HV_EXPORT bool startswith(const std::string& str, const std::string& start);
HV_EXPORT bool endswith(const std::string& str, const std::string& end);
HV_EXPORT bool contains(const std::string& str, const std::string& sub);
HV_EXPORT std::string asprintf(const char* fmt, ...);
// x,y,z
HV_EXPORT StringList split(const std::string& str, char delim = ',');
// k1=v1&k2=v2
HV_EXPORT hv::KeyValue splitKV(const std::string& str, char kv_kv = '&', char k_v = '=');
HV_EXPORT std::string trim(const std::string& str, const char* chars = SPACE_CHARS);
HV_EXPORT std::string ltrim(const std::string& str, const char* chars = SPACE_CHARS);
HV_EXPORT std::string rtrim(const std::string& str, const char* chars = SPACE_CHARS);
HV_EXPORT std::string trim_pairs(const std::string& str, const char* pairs = PAIR_CHARS);
HV_EXPORT std::string replace(const std::string& str, const std::string& find, const std::string& rep);
HV_EXPORT std::string replaceAll(const std::string& str, const std::string& find, const std::string& rep);
} // end namespace hv
#endif // HV_STRING_H_

View File

@ -0,0 +1,249 @@
#ifndef HV_THREAD_POOL_H_
#define HV_THREAD_POOL_H_
/*
* @usage unittest/threadpool_test.cpp
*/
#include <time.h>
#include <thread>
#include <list>
#include <queue>
#include <functional>
#include <atomic>
#include <mutex>
#include <condition_variable>
#include <future>
#include <memory>
#include <utility>
#include <chrono>
#define DEFAULT_THREAD_POOL_MIN_THREAD_NUM 1
#define DEFAULT_THREAD_POOL_MAX_THREAD_NUM std::thread::hardware_concurrency()
#define DEFAULT_THREAD_POOL_MAX_IDLE_TIME 60000 // ms
class HThreadPool {
public:
using Task = std::function<void()>;
HThreadPool(int min_threads = DEFAULT_THREAD_POOL_MIN_THREAD_NUM,
int max_threads = DEFAULT_THREAD_POOL_MAX_THREAD_NUM,
int max_idle_ms = DEFAULT_THREAD_POOL_MAX_IDLE_TIME)
: min_thread_num(min_threads)
, max_thread_num(max_threads)
, max_idle_time(max_idle_ms)
, status(STOP)
, cur_thread_num(0)
, idle_thread_num(0)
{}
virtual ~HThreadPool() {
stop();
}
void setMinThreadNum(int min_threads) {
min_thread_num = min_threads;
}
void setMaxThreadNum(int max_threads) {
max_thread_num = max_threads;
}
void setMaxIdleTime(int ms) {
max_idle_time = ms;
}
int currentThreadNum() {
return cur_thread_num;
}
int idleThreadNum() {
return idle_thread_num;
}
size_t taskNum() {
std::lock_guard<std::mutex> locker(task_mutex);
return tasks.size();
}
bool isStarted() {
return status != STOP;
}
bool isStopped() {
return status == STOP;
}
int start(int start_threads = 0) {
if (status != STOP) return -1;
status = RUNNING;
if (start_threads < min_thread_num) start_threads = min_thread_num;
if (start_threads > max_thread_num) start_threads = max_thread_num;
for (int i = 0; i < start_threads; ++i) {
createThread();
}
return 0;
}
int stop() {
if (status == STOP) return -1;
status = STOP;
task_cond.notify_all();
for (auto& i : threads) {
if (i.thread->joinable()) {
i.thread->join();
}
}
threads.clear();
cur_thread_num = 0;
idle_thread_num = 0;
return 0;
}
int pause() {
if (status == RUNNING) {
status = PAUSE;
}
return 0;
}
int resume() {
if (status == PAUSE) {
status = RUNNING;
}
return 0;
}
int wait() {
while (status != STOP) {
if (tasks.empty() && idle_thread_num == cur_thread_num) {
break;
}
std::this_thread::yield();
}
return 0;
}
/*
* return a future, calling future.get() will wait task done and return RetType.
* commit(fn, args...)
* commit(std::bind(&Class::mem_fn, &obj))
* commit(std::mem_fn(&Class::mem_fn, &obj))
*
*/
template<class Fn, class... Args>
auto commit(Fn&& fn, Args&&... args) -> std::future<decltype(fn(args...))> {
if (status == STOP) start();
if (idle_thread_num <= tasks.size() && cur_thread_num < max_thread_num) {
createThread();
}
using RetType = decltype(fn(args...));
auto task = std::make_shared<std::packaged_task<RetType()> >(
std::bind(std::forward<Fn>(fn), std::forward<Args>(args)...));
std::future<RetType> future = task->get_future();
{
std::lock_guard<std::mutex> locker(task_mutex);
tasks.emplace([task]{
(*task)();
});
}
task_cond.notify_one();
return future;
}
protected:
bool createThread() {
if (cur_thread_num >= max_thread_num) return false;
std::thread* thread = new std::thread([this] {
while (status != STOP) {
while (status == PAUSE) {
std::this_thread::yield();
}
Task task;
{
std::unique_lock<std::mutex> locker(task_mutex);
task_cond.wait_for(locker, std::chrono::milliseconds(max_idle_time), [this]() {
return status == STOP || !tasks.empty();
});
if (status == STOP) return;
if (tasks.empty()) {
if (cur_thread_num > min_thread_num) {
delThread(std::this_thread::get_id());
return;
}
continue;
}
--idle_thread_num;
task = std::move(tasks.front());
tasks.pop();
}
if (task) {
task();
++idle_thread_num;
}
}
});
addThread(thread);
return true;
}
void addThread(std::thread* thread) {
thread_mutex.lock();
++cur_thread_num;
++idle_thread_num;
ThreadData data;
data.thread = std::shared_ptr<std::thread>(thread);
data.id = thread->get_id();
data.status = RUNNING;
data.start_time = time(NULL);
data.stop_time = 0;
threads.emplace_back(data);
thread_mutex.unlock();
}
void delThread(std::thread::id id) {
time_t now = time(NULL);
thread_mutex.lock();
--cur_thread_num;
--idle_thread_num;
auto iter = threads.begin();
while (iter != threads.end()) {
if (iter->status == STOP && now > iter->stop_time) {
if (iter->thread->joinable()) {
iter->thread->join();
iter = threads.erase(iter);
continue;
}
} else if (iter->id == id) {
iter->status = STOP;
iter->stop_time = time(NULL);
}
++iter;
}
thread_mutex.unlock();
}
public:
int min_thread_num;
int max_thread_num;
int max_idle_time;
protected:
enum Status {
STOP,
RUNNING,
PAUSE,
};
struct ThreadData {
std::shared_ptr<std::thread> thread;
std::thread::id id;
Status status;
time_t start_time;
time_t stop_time;
};
std::atomic<Status> status;
std::atomic<int> cur_thread_num;
std::atomic<int> idle_thread_num;
std::list<ThreadData> threads;
std::mutex thread_mutex;
std::queue<Task> tasks;
std::mutex task_mutex;
std::condition_variable task_cond;
};
#endif // HV_THREAD_POOL_H_

View File

@ -0,0 +1,184 @@
#include "hurl.h"
#include "hdef.h"
#include "hbase.h"
/*
static bool Curl_isunreserved(unsigned char in)
{
switch(in) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case 'a': case 'b': case 'c': case 'd': case 'e':
case 'f': case 'g': case 'h': case 'i': case 'j':
case 'k': case 'l': case 'm': case 'n': case 'o':
case 'p': case 'q': case 'r': case 's': case 't':
case 'u': case 'v': case 'w': case 'x': case 'y': case 'z':
case 'A': case 'B': case 'C': case 'D': case 'E':
case 'F': case 'G': case 'H': case 'I': case 'J':
case 'K': case 'L': case 'M': case 'N': case 'O':
case 'P': case 'Q': case 'R': case 'S': case 'T':
case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':
case '-': case '.': case '_': case '~':
return TRUE;
default:
break;
}
return FLASE;
}
*/
static inline bool is_unambiguous(char c) {
return IS_ALPHANUM(c) ||
c == '-' ||
c == '_' ||
c == '.' ||
c == '~';
}
static inline bool char_in_str(char c, const char* str) {
const char* p = str;
while (*p && *p != c) ++p;
return *p != '\0';
}
static inline unsigned char hex2i(char hex) {
return hex <= '9' ? hex - '0' :
hex <= 'F' ? hex - 'A' + 10 : hex - 'a' + 10;
}
std::string HUrl::escape(const std::string& str, const char* unescaped_chars) {
std::string ostr;
static char tab[] = "0123456789ABCDEF";
const unsigned char* p = reinterpret_cast<const unsigned char*>(str.c_str());
char szHex[4] = "%00";
while (*p != '\0') {
if (is_unambiguous(*p) || char_in_str(*p, unescaped_chars)) {
ostr += *p;
}
else {
szHex[1] = tab[*p >> 4];
szHex[2] = tab[*p & 0xF];
ostr += szHex;
}
++p;
}
return ostr;
}
std::string HUrl::unescape(const std::string& str) {
std::string ostr;
const char* p = str.c_str();
while (*p != '\0') {
if (*p == '%' &&
IS_HEX(p[1]) &&
IS_HEX(p[2])) {
ostr += ((hex2i(p[1]) << 4) | hex2i(p[2]));
p += 3;
}
else {
if (*p == '+') {
ostr += ' ';
} else {
ostr += *p;
}
++p;
}
}
return ostr;
}
void HUrl::reset() {
url.clear();
scheme.clear();
username.clear();
password.clear();
host.clear();
port = 0;
path.clear();
query.clear();
fragment.clear();
}
bool HUrl::parse(const std::string& url) {
reset();
this->url = url;
hurl_t stURL;
if (hv_parse_url(&stURL, url.c_str()) != 0) {
return false;
}
int len = stURL.fields[HV_URL_SCHEME].len;
if (len > 0) {
scheme = url.substr(stURL.fields[HV_URL_SCHEME].off, len);
}
len = stURL.fields[HV_URL_USERNAME].len;
if (len > 0) {
username = url.substr(stURL.fields[HV_URL_USERNAME].off, len);
len = stURL.fields[HV_URL_PASSWORD].len;
if (len > 0) {
password = url.substr(stURL.fields[HV_URL_PASSWORD].off, len);
}
}
len = stURL.fields[HV_URL_HOST].len;
if (len > 0) {
host = url.substr(stURL.fields[HV_URL_HOST].off, len);
}
port = stURL.port;
len = stURL.fields[HV_URL_PATH].len;
if (len > 0) {
path = url.substr(stURL.fields[HV_URL_PATH].off, len);
} else {
path = "/";
}
len = stURL.fields[HV_URL_QUERY].len;
if (len > 0) {
query = url.substr(stURL.fields[HV_URL_QUERY].off, len);
}
len = stURL.fields[HV_URL_FRAGMENT].len;
if (len > 0) {
fragment = url.substr(stURL.fields[HV_URL_FRAGMENT].off, len);
}
return true;
}
const std::string& HUrl::dump() {
url.clear();
// scheme://
if (!scheme.empty()) {
url += scheme;
url += "://";
}
// user:pswd@
if (!username.empty()) {
url += username;
if (!password.empty()) {
url += ":";
url += password;
}
url += "@";
}
// host:port
if (!host.empty()) {
url += host;
if (port != 80 && port != 443) {
char buf[16] = {0};
snprintf(buf, sizeof(buf), ":%d", port);
url += port;
}
}
// /path
if (!path.empty()) {
url += path;
}
// ?query
if (!query.empty()) {
url += '?';
url += query;
}
// #fragment
if (!fragment.empty()) {
url += '#';
url += fragment;
}
return url;
}

View File

@ -0,0 +1,34 @@
#ifndef HV_URL_H_
#define HV_URL_H_
#include <string> // import std::string
#include "hexport.h"
class HV_EXPORT HUrl {
public:
static std::string escape(const std::string& str, const char* unescaped_chars = "");
static std::string unescape(const std::string& str);
static inline std::string escapeUrl(const std::string& url) {
return escape(url, ":/@?=&#+");
}
HUrl() : port(0) {}
~HUrl() {}
void reset();
bool parse(const std::string& url);
const std::string& dump();
std::string url;
std::string scheme;
std::string username;
std::string password;
std::string host;
int port;
std::string path;
std::string query;
std::string fragment;
};
#endif // HV_URL_H_

View File

@ -0,0 +1,223 @@
#include "ifconfig.h"
#include "hplatform.h"
#ifdef OS_LINUX
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <linux/if.h>
#include <arpa/inet.h>
int ifconfig(std::vector<ifconfig_t>& ifcs) {
int sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
return -10;
}
struct ifconf ifc;
char buf[1024];
ifc.ifc_len = sizeof(buf);
ifc.ifc_buf = buf;
int iRet = ioctl(sock, SIOCGIFCONF, &ifc);
if (iRet != 0) {
close(sock);
return iRet;
}
int cnt = ifc.ifc_len / sizeof(struct ifreq);
//printf("ifc.size=%d\n", cnt);
if (cnt == 0) {
close(sock);
return -20;
}
struct ifreq ifr;
ifcs.clear();
ifconfig_t tmp;
for (int i = 0; i < cnt; ++i) {
// name
strcpy(ifr.ifr_name, ifc.ifc_req[i].ifr_name);
//printf("name: %s\n", ifr.ifr_name);
strncpy(tmp.name, ifr.ifr_name, sizeof(tmp.name));
// flags
//iRet = ioctl(sock, SIOCGIFFLAGS, &ifr);
//short flags = ifr.ifr_flags;
// addr
iRet = ioctl(sock, SIOCGIFADDR, &ifr);
struct sockaddr_in* addr = (struct sockaddr_in*)&ifr.ifr_addr;
char* ip = inet_ntoa(addr->sin_addr);
//printf("ip: %s\n", ip);
strncpy(tmp.ip, ip, sizeof(tmp.ip));
// netmask
iRet = ioctl(sock, SIOCGIFNETMASK, &ifr);
addr = (struct sockaddr_in*)&ifr.ifr_netmask;
char* netmask = inet_ntoa(addr->sin_addr);
//printf("netmask: %s\n", netmask);
strncpy(tmp.mask, netmask, sizeof(tmp.mask));
// broadaddr
iRet = ioctl(sock, SIOCGIFBRDADDR, &ifr);
addr = (struct sockaddr_in*)&ifr.ifr_broadaddr;
char* broadaddr = inet_ntoa(addr->sin_addr);
//printf("broadaddr: %s\n", broadaddr);
strncpy(tmp.broadcast, broadaddr, sizeof(tmp.broadcast));
// hwaddr
iRet = ioctl(sock, SIOCGIFHWADDR, &ifr);
snprintf(tmp.mac, sizeof(tmp.mac), "%02x:%02x:%02x:%02x:%02x:%02x",
(unsigned char)ifr.ifr_hwaddr.sa_data[0],
(unsigned char)ifr.ifr_hwaddr.sa_data[1],
(unsigned char)ifr.ifr_hwaddr.sa_data[2],
(unsigned char)ifr.ifr_hwaddr.sa_data[3],
(unsigned char)ifr.ifr_hwaddr.sa_data[4],
(unsigned char)ifr.ifr_hwaddr.sa_data[5]);
//printf("mac: %s\n", tmp.mac);
//printf("\n");
if (strcmp(tmp.ip, "0.0.0.0") == 0 ||
strcmp(tmp.ip, "127.0.0.1") == 0 ||
strcmp(tmp.mac, "00:00:00:00:00:00") == 0) {
continue;
}
ifcs.push_back(tmp);
}
close(sock);
return 0;
}
#elif defined(OS_WIN)
#include <winsock2.h>
#include <windows.h>
#include <ws2ipdef.h>
#include <iphlpapi.h>
int ifconfig(std::vector<ifconfig_t>& ifcs) {
PIP_ADAPTER_ADDRESSES pAddrs = NULL;
ULONG buflen = 0;
GetAdaptersAddresses(AF_INET, 0, NULL, pAddrs, &buflen);
pAddrs = (PIP_ADAPTER_ADDRESSES)malloc(buflen);
GetAdaptersAddresses(AF_INET, 0, NULL, pAddrs, &buflen);
PIP_ADAPTER_INFO pInfos = NULL;
buflen = 0;
GetAdaptersInfo(pInfos, &buflen);
pInfos = (PIP_ADAPTER_INFO)malloc(buflen);
GetAdaptersInfo(pInfos, &buflen);
ifconfig_t ifc;
std::vector<ifconfig_t> tmp_ifcs;
PIP_ADAPTER_ADDRESSES pAddr = pAddrs;
char mac[32] = {'\0'};
while (pAddr) {
snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x",
pAddr->PhysicalAddress[0],
pAddr->PhysicalAddress[1],
pAddr->PhysicalAddress[2],
pAddr->PhysicalAddress[3],
pAddr->PhysicalAddress[4],
pAddr->PhysicalAddress[5]);
memset(&ifc, 0, sizeof(ifc));
strncpy(ifc.name, pAddr->AdapterName, sizeof(ifc.name));
strncpy(ifc.ip, "0.0.0.0", sizeof(ifc.ip));
strncpy(ifc.mask, "255.255.255.255", sizeof(ifc.mask));
strncpy(ifc.mac, mac, sizeof(ifc.mac));
tmp_ifcs.push_back(ifc);
pAddr = pAddr->Next;
}
PIP_ADAPTER_INFO pInfo = pInfos;
while (pInfo) {
for (auto& item : tmp_ifcs) {
if (strcmp(item.name, pInfo->AdapterName) == 0) {
// description more friendly
strncpy(item.name, pInfo->Description, sizeof(item.name));
strncpy(item.ip, pInfo->IpAddressList.IpAddress.String, sizeof(item.ip));
strncpy(item.mask, pInfo->IpAddressList.IpMask.String, sizeof(item.mask));
}
}
pInfo = pInfo->Next;
}
free(pAddrs);
free(pInfos);
// filter
ifcs.clear();
for (auto& item : tmp_ifcs) {
if (strcmp(item.ip, "0.0.0.0") == 0 ||
strcmp(item.ip, "127.0.0.1") == 0 ||
strcmp(item.mac, "00:00:00:00:00:00") == 0) {
continue;
}
ifcs.push_back(item);
}
return 0;
}
#elif defined(OS_MAC)
#include <ifaddrs.h>
#include <net/if_dl.h>
int ifconfig(std::vector<ifconfig_t>& ifcs) {
struct ifaddrs *ifas, *ifap;
int ret = getifaddrs(&ifas);
if (ret != 0) return ret;
ifconfig_s tmp;
for (ifap = ifas; ifap != NULL; ifap = ifap->ifa_next) {
if (ifap->ifa_addr->sa_family == AF_INET) {
// ipv4
struct sockaddr_in* addr = (struct sockaddr_in*)ifap->ifa_addr;
char* ip = inet_ntoa(addr->sin_addr);
// filter
if (strcmp(ip, "0.0.0.0") == 0 || strcmp(ip, "127.0.0.1") == 0) {
continue;
}
memset(&tmp, 0, sizeof(tmp));
strncpy(tmp.name, ifap->ifa_name, sizeof(tmp.name));
strncpy(tmp.ip, ip, sizeof(tmp.ip));
// netmask
addr = (struct sockaddr_in*)ifap->ifa_netmask;
char* netmask = inet_ntoa(addr->sin_addr);
strncpy(tmp.mask, netmask, sizeof(tmp.mask));
// broadaddr
addr = (struct sockaddr_in*)ifap->ifa_broadaddr;
char* broadaddr = inet_ntoa(addr->sin_addr);
strncpy(tmp.broadcast, broadaddr, sizeof(tmp.broadcast));
// push_back
ifcs.push_back(tmp);
}
}
for (ifap = ifas; ifap != NULL; ifap = ifap->ifa_next) {
if (ifap->ifa_addr->sa_family == AF_LINK) {
// hwaddr
for (auto iter = ifcs.begin(); iter != ifcs.end(); ++iter) {
if (strcmp(iter->name, ifap->ifa_name) == 0) {
struct sockaddr_dl* addr = (struct sockaddr_dl*)ifap->ifa_addr;
unsigned char* pmac = (unsigned char*)LLADDR(addr);
snprintf(iter->mac, sizeof(iter->mac), "%02x:%02x:%02x:%02x:%02x:%02x",
pmac[0], pmac[1], pmac[2], pmac[3], pmac[4], pmac[5]);
// filter
if (strcmp(iter->mac, "00:00:00:00:00:00") == 0) {
ifcs.erase(iter);
}
break;
}
}
}
}
freeifaddrs(ifas);
return 0;
}
#else
int ifconfig(std::vector<ifconfig_t>& ifcs) {
return -10; // unimplemented
}
#endif

View File

@ -0,0 +1,36 @@
#ifndef HV_IFCONFIG_H_
#define HV_IFCONFIG_H_
#include <vector>
#include "hexport.h"
#ifdef _MSC_VER
#pragma comment(lib, "iphlpapi.lib")
#pragma comment(lib, "ws2_32.lib")
#endif
typedef struct ifconfig_s {
char name[128];
char ip[16];
char mask[16];
char broadcast[16];
char mac[20];
} ifconfig_t;
/*
* @test
std::vector<ifconfig_t> ifcs;
ifconfig(ifcs);
for (auto& item : ifcs) {
printf("%s\nip: %s\nmask: %s\nbroadcast: %s\nmac: %s\n\n",
item.name,
item.ip,
item.mask,
item.broadcast,
item.mac);
}
*/
HV_EXPORT int ifconfig(std::vector<ifconfig_t>& ifcs);
#endif // HV_IFCONFIG_H_

View File

@ -0,0 +1,390 @@
#include "iniparser.h"
#include <sstream>
#include "hdef.h"
#include "herr.h"
#include "hstring.h"
#include "hfile.h"
#include "hbase.h"
using namespace hv;
/**********************************
# div
[section]
key = value # span
# div
***********************************/
class IniNode {
public:
enum Type {
INI_NODE_TYPE_UNKNOWN,
INI_NODE_TYPE_ROOT,
INI_NODE_TYPE_SECTION,
INI_NODE_TYPE_KEY_VALUE,
INI_NODE_TYPE_DIV,
INI_NODE_TYPE_SPAN,
} type;
std::string label; // section|key|comment
std::string value;
std::list<IniNode*> children;
virtual ~IniNode() {
for (auto pNode : children) {
if (pNode) {
delete pNode;
}
}
children.clear();
}
void Add(IniNode* pNode) {
children.push_back(pNode);
}
void Del(IniNode* pNode) {
for (auto iter = children.begin(); iter != children.end(); ++iter) {
if ((*iter) == pNode) {
delete (*iter);
children.erase(iter);
return;
}
}
}
IniNode* Get(const std::string& label, Type type = INI_NODE_TYPE_KEY_VALUE) {
for (auto pNode : children) {
if (pNode->type == type && pNode->label == label) {
return pNode;
}
}
return NULL;
}
};
class IniSection : public IniNode {
public:
IniSection() : IniNode(), section(label) {
type = INI_NODE_TYPE_SECTION;
}
std::string &section;
};
class IniKeyValue : public IniNode {
public:
IniKeyValue() : IniNode(), key(label) {
type = INI_NODE_TYPE_KEY_VALUE;
}
std::string &key;
};
class IniComment : public IniNode {
public:
IniComment() : IniNode(), comment(label) {
}
std::string &comment;
};
IniParser::IniParser() {
_comment = DEFAULT_INI_COMMENT;
_delim = DEFAULT_INI_DELIM;
root_ = NULL;
}
IniParser::~IniParser() {
Unload();
}
int IniParser::Unload() {
SAFE_DELETE(root_);
return 0;
}
int IniParser::Reload() {
return LoadFromFile(_filepath.c_str());
}
int IniParser::LoadFromFile(const char* filepath) {
_filepath = filepath;
HFile file;
if (file.open(filepath, "r") != 0) {
return ERR_OPEN_FILE;
}
std::string str;
file.readall(str);
const char* c_str = str.c_str();
unsigned char utf8_bom[3] = { 0xEF, 0xBB, 0xBF };
if (str.size() >= 3 && memcmp(c_str, utf8_bom, 3) == 0) {
c_str += 3;
}
return LoadFromMem(c_str);
}
int IniParser::LoadFromMem(const char* data) {
Unload();
root_ = new IniNode;
root_->type = IniNode::INI_NODE_TYPE_ROOT;
std::stringstream ss;
ss << data;
std::string strLine;
int line = 0;
std::string::size_type pos;
std::string content, comment, strDiv;
IniNode* pScopeNode = root_;
IniNode* pNewNode = NULL;
while (std::getline(ss, strLine)) {
++line;
content = ltrim(strLine);
if (content.length() == 0) {
// blank line
strDiv += '\n';
continue;
}
// trim_comment
comment = "";
pos = content.find_first_of(_comment);
if (pos != std::string::npos) {
comment = content.substr(pos);
content = content.substr(0, pos);
}
content = rtrim(content);
if (content.length() == 0) {
strDiv += strLine;
strDiv += '\n';
continue;
} else if (strDiv.length() != 0) {
IniNode* pNode = new IniNode;
pNode->type = IniNode::INI_NODE_TYPE_DIV;
pNode->label = strDiv;
pScopeNode->Add(pNode);
strDiv = "";
}
if (content[0] == '[') {
if (content[content.length()-1] == ']') {
// section
content = trim(content.substr(1, content.length()-2));
pNewNode = new IniNode;
pNewNode->type = IniNode::INI_NODE_TYPE_SECTION;
pNewNode->label = content;
root_->Add(pNewNode);
pScopeNode = pNewNode;
} else {
// hlogw("format error, line:%d", line);
continue; // ignore
}
} else {
pos = content.find_first_of(_delim);
if (pos != std::string::npos) {
// key-value
pNewNode = new IniNode;
pNewNode->type = IniNode::INI_NODE_TYPE_KEY_VALUE;
pNewNode->label = trim(content.substr(0, pos));
pNewNode->value = trim(content.substr(pos+_delim.length()));
pScopeNode->Add(pNewNode);
} else {
// hlogw("format error, line:%d", line);
continue; // ignore
}
}
if (comment.length() != 0) {
// tail_comment
IniNode* pNode = new IniNode;
pNode->type = IniNode::INI_NODE_TYPE_SPAN;
pNode->label = comment;
pNewNode->Add(pNode);
comment = "";
}
}
// file end comment
if (strDiv.length() != 0) {
IniNode* pNode = new IniNode;
pNode->type = IniNode::INI_NODE_TYPE_DIV;
pNode->label = strDiv;
root_->Add(pNode);
}
return 0;
}
void IniParser::DumpString(IniNode* pNode, std::string& str) {
if (pNode == NULL) return;
if (pNode->type != IniNode::INI_NODE_TYPE_SPAN) {
if (str.length() > 0 && str[str.length()-1] != '\n') {
str += '\n';
}
}
switch (pNode->type) {
case IniNode::INI_NODE_TYPE_SECTION: {
str += '[';
str += pNode->label;
str += ']';
}
break;
case IniNode::INI_NODE_TYPE_KEY_VALUE: {
str += asprintf("%s %s %s", pNode->label.c_str(), _delim.c_str(), pNode->value.c_str());
}
break;
case IniNode::INI_NODE_TYPE_DIV: {
str += pNode->label;
}
break;
case IniNode::INI_NODE_TYPE_SPAN: {
str += '\t';
str += pNode->label;
}
break;
default:
break;
}
for (auto p : pNode->children) {
DumpString(p, str);
}
}
std::string IniParser::DumpString() {
std::string str;
DumpString(root_, str);
return str;
}
int IniParser::Save() {
return SaveAs(_filepath.c_str());
}
int IniParser::SaveAs(const char* filepath) {
std::string str = DumpString();
if (str.length() == 0) {
return 0;
}
HFile file;
if (file.open(filepath, "w") != 0) {
return ERR_SAVE_FILE;
}
file.write(str.c_str(), str.length());
return 0;
}
std::list<std::string> IniParser::GetSections() {
std::list<std::string> ret;
if (root_ == NULL) return std::move(ret);
for (auto pNode : root_->children) {
if (pNode->type == IniNode::INI_NODE_TYPE_SECTION) {
ret.push_back(pNode->label);
}
}
return std::move(ret);
}
std::list<std::string> IniParser::GetKeys(const std::string& section) {
std::list<std::string> ret;
if (root_ == NULL) return std::move(ret);
IniNode* pSection = root_;
if (section.length() != 0) {
pSection = root_->Get(section, IniNode::INI_NODE_TYPE_SECTION);
if (pSection == NULL) return std::move(ret);
}
for (auto pNode : pSection->children) {
if (pNode->type == IniNode::INI_NODE_TYPE_KEY_VALUE) {
ret.push_back(pNode->label);
}
}
return std::move(ret);
}
std::string IniParser::GetValue(const std::string& key, const std::string& section) {
if (root_ == NULL) return "";
IniNode* pSection = root_;
if (section.length() != 0) {
pSection = root_->Get(section, IniNode::INI_NODE_TYPE_SECTION);
if (pSection == NULL) return "";
}
IniNode* pKV = pSection->Get(key, IniNode::INI_NODE_TYPE_KEY_VALUE);
if (pKV == NULL) return "";
return pKV->value;
}
void IniParser::SetValue(const std::string& key, const std::string& value, const std::string& section) {
if (root_ == NULL) {
root_ = new IniNode;
}
IniNode* pSection = root_;
if (section.length() != 0) {
pSection = root_->Get(section, IniNode::INI_NODE_TYPE_SECTION);
if (pSection == NULL) {
pSection = new IniNode;
pSection->type = IniNode::INI_NODE_TYPE_SECTION;
pSection->label = section;
root_->Add(pSection);
}
}
IniNode* pKV = pSection->Get(key, IniNode::INI_NODE_TYPE_KEY_VALUE);
if (pKV == NULL) {
pKV = new IniNode;
pKV->type = IniNode::INI_NODE_TYPE_KEY_VALUE;
pKV->label = key;
pSection->Add(pKV);
}
pKV->value = value;
}
template<>
HV_EXPORT bool IniParser::Get(const std::string& key, const std::string& section, bool defvalue) {
std::string str = GetValue(key, section);
return str.empty() ? defvalue : hv_getboolean(str.c_str());
}
template<>
HV_EXPORT int IniParser::Get(const std::string& key, const std::string& section, int defvalue) {
std::string str = GetValue(key, section);
return str.empty() ? defvalue : atoi(str.c_str());
}
template<>
HV_EXPORT float IniParser::Get(const std::string& key, const std::string& section, float defvalue) {
std::string str = GetValue(key, section);
return str.empty() ? defvalue : atof(str.c_str());
}
template<>
HV_EXPORT void IniParser::Set(const std::string& key, const bool& value, const std::string& section) {
SetValue(key, value ? "true" : "false", section);
}
template<>
HV_EXPORT void IniParser::Set(const std::string& key, const int& value, const std::string& section) {
SetValue(key, asprintf("%d", value), section);
}
template<>
HV_EXPORT void IniParser::Set(const std::string& key, const float& value, const std::string& section) {
SetValue(key, asprintf("%f", value), section);
}

View File

@ -0,0 +1,53 @@
#ifndef HV_INI_PARSER_H_
#define HV_INI_PARSER_H_
#include <string>
#include <list>
#include "hexport.h"
#define DEFAULT_INI_COMMENT "#"
#define DEFAULT_INI_DELIM "="
// fwd
class IniNode;
class HV_EXPORT IniParser {
public:
IniParser();
~IniParser();
int LoadFromFile(const char* filepath);
int LoadFromMem(const char* data);
int Unload();
int Reload();
std::string DumpString();
int Save();
int SaveAs(const char* filepath);
std::list<std::string> GetSections();
std::list<std::string> GetKeys(const std::string& section = "");
std::string GetValue(const std::string& key, const std::string& section = "");
void SetValue(const std::string& key, const std::string& value, const std::string& section = "");
// T = [bool, int, float]
template<typename T>
T Get(const std::string& key, const std::string& section = "", T defvalue = 0);
// T = [bool, int, float]
template<typename T>
void Set(const std::string& key, const T& value, const std::string& section = "");
protected:
void DumpString(IniNode* pNode, std::string& str);
public:
std::string _comment;
std::string _delim;
std::string _filepath;
private:
IniNode* root_;
};
#endif // HV_INI_PARSER_H_

25447
external/libhv/libhv-1.3.2/cpputil/json.hpp vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,41 @@
#ifndef HV_SINGLETON_H_
#define HV_SINGLETON_H_
#include <mutex>
#define DISABLE_COPY(Class) \
Class(const Class&) = delete; \
Class& operator=(const Class&) = delete;
#define SINGLETON_DECL(Class) \
public: \
static Class* instance(); \
static void exitInstance(); \
private: \
DISABLE_COPY(Class) \
static Class* s_pInstance; \
static std::mutex s_mutex;
#define SINGLETON_IMPL(Class) \
Class* Class::s_pInstance = NULL; \
std::mutex Class::s_mutex; \
Class* Class::instance() { \
if (s_pInstance == NULL) { \
s_mutex.lock(); \
if (s_pInstance == NULL) { \
s_pInstance = new Class; \
} \
s_mutex.unlock(); \
} \
return s_pInstance; \
} \
void Class::exitInstance() { \
s_mutex.lock(); \
if (s_pInstance) { \
delete s_pInstance; \
s_pInstance = NULL; \
} \
s_mutex.unlock(); \
}
#endif // HV_SINGLETON_H_

652
external/libhv/libhv-1.3.2/docs/API.md vendored Normal file
View File

@ -0,0 +1,652 @@
# libhv API Manual
## base
### hplatform.h
- OS: OS_WIN, OS_UNIX (OS_LINUX, OS_ANDROID, OS_DARWIN ...)
- ARCH: ARCH_X86, ARCH_X64, ARCH_ARM, ARCH_ARM64
- COMPILER: COMPILER_MSVC, COMPILER_MINGW, COMPILER_GCC, COMPILER_CLANG
- BYTE_ORDER: BIG_ENDIAN, LITTLE_ENDIAN
- stdbool.h: bool, true, false
- stdint.h: int8_t, int16_t, int32_t, int64_t
- hv_sleep, hv_msleep, hv_usleep, hv_delay
- hv_mkdir
- stricmp, strcasecmp
### hexport.h
- HV_EXPORT, HV_INLINE
- HV_SOURCE, HV_STATICLIB, HV_DYNAMICLIB
- HV_DEPRECATED
- HV_UNUSED
- EXTERN_C, BEGIN_EXTERN_C, END_EXTERN_C
- BEGIN_NAMESPACE, END_NAMESPACE, USING_NAMESPACE
- DEFAULT
- ENUM, STRUCT
- IN, OUT, INOUT
- OPTIONAL, REQUIRED, REPEATED
### hdef.h
- ABS, NABS
- ARRAY_SIZE
- BITSET, BITCLR, BITGET
- CR, LF, CRLF
- FLOAT_EQUAL_ZERO
- INFINITE
- IS_ALPHA, IS_DIGIT, IS_ALPHANUM
- IS_CNTRL, IS_GRAPH
- IS_HEX
- IS_LOWER, IS_UPPER
- LOWER, UPPER
- MAKEWORD, LOBYTE, HIBYTE
- MAKELONG, LOWORD, HIWORD
- MAKEINT64, LOINT, HIINT
- MAKE_FOURCC
- MAX, MIN, LIMIT
- MAX_PATH
- NULL, TRUE, FALSE
- SAFE_FREE, SAFE_DELETE, SAFE_DELETE_ARRAY, SAFE_RELEASE
- STRINGCAT
- STRINGIFY
- offsetof, offsetofend
- container_of
- prefetch
- printd, printe
### hatomic.h
- hatomic_flag_t, hatomic_t
- hatomic_flag_test_and_set
- hatomic_flag_clear
- hatomic_add
- hatomic_sub
- hatomic_inc
- hatomic_dec
### herr.h
- hv_strerror
### htime.h
- IS_LEAP_YEAR
- datetime_t
- gettick_ms
- gettimeofday
- gettimeofday_ms
- gettimeofday_us
- gethrtime_us
- datetime_now
- datetime_localtime
- datetime_mktime
- datetime_past
- datetime_future
- duration_fmt
- datetime_fmt
- gmtime_fmt
- days_of_month
- month_atoi
- month_itoa
- weekday_atoi
- weekday_itoa
- hv_compile_datetime
- cron_next_timeout
### hmath.h
- floor2e
- ceil2e
- varint_encode
- varint_decode
### hbase.h
- hv_malloc
- hv_calloc
- hv_realloc
- hv_zalloc
- hv_strncpy
- hv_strncat
- hv_strlower
- hv_strupper
- hv_strreverse
- hv_strstartswith
- hv_strendswith
- hv_strcontains
- hv_wildcard_match
- hv_strnchr
- hv_strrchr_dot
- hv_strrchr_dir
- hv_basename
- hv_suffixname
- hv_mkdir_p
- hv_rmdir_p
- hv_exists
- hv_isdir
- hv_isfile
- hv_islink
- hv_filesize
- get_executable_path
- get_executable_dir
- get_executable_file
- get_run_dir
- hv_rand
- hv_random_string
- hv_getboolean
- hv_parse_size
- hv_parse_time
- hv_parse_url
### hversion.h
- hv_version
- hv_compile_version
- version_atoi
- version_itoa
### hsysinfo.h
- get_ncpu
- get_meminfo
### hproc.h
- hproc_spawn
### hthread.h
- hv_getpid
- hv_gettid
- HTHREAD_RETTYPE
- HTHREAD_ROUTINE
- hthread_create
- hthread_join
- class HThread
### hmutex.h
- hmutex_t
- hmutex_init
- hmutex_destroy
- hmutex_lock
- hmutex_unlock
- hspinlock_t
- hspinlock_init
- hspinlock_destroy
- hspinlock_lock
- hspinlock_unlock
- hrwlock_t
- hrwlock_init
- hrwlock_destroy
- hrwlock_rdlock
- hrwlock_rdunlock
- hrwlock_wrlock
- hrwlock_wrunlock
- htimed_mutex_t
- htimed_mutex_init
- htimed_mutex_destroy
- htimed_mutex_lock
- htimed_mutex_lock_for
- htimed_mutex_unlock
- hcondvar_t
- hcondvar_init
- hcondvar_destroy
- hcondvar_wait
- hcondvar_wait_for
- hcondvar_signal
- hcondvar_broadcast
- hsem_init
- hsem_destroy
- hsem_wait
- hsem_post
- hsem_timedwait
- honce_t
- HONCE_INIT
- honce
- class `hv::MutexLock`
- class `hv::SpinLock`
- class `hv::RWLock`
- class `hv::LockGuard`
- synchronized
### hsocket.h
- INVALID_SOCKET
- closesocket
- blocking
- nonblocking
- Bind
- Listen
- Connect
- ConnectNonblock
- ConnectTimeout
- ResolveAddr
- Socketpair
- socket_errno
- socket_strerror
- sockaddr_u
- sockaddr_ip
- sockaddr_port
- sockaddr_set_ip
- sockaddr_set_port
- sockaddr_set_ipport
- sockaddr_len
- sockaddr_str
- sockaddr_print
- SOCKADDR_LEN
- SOCKADDR_STR
- SOCKADDR_PRINT
- tcp_nodelay
- tcp_nopush
- tcp_keepalive
- udp_broadcast
- ip_v6only
- so_sndtimeo
- so_rcvtimeo
- so_sndbuf
- so_rcvbuf
- so_reuseaddr
- so_reuseport
- so_linger
### hlog.h
- default_logger
- file_logger
- stderr_logger
- stdout_logger
- logger_create
- logger_destroy
- logger_enable_color
- logger_enable_fsync
- logger_fsync
- logger_print
- logger_set_file
- logger_set_handler
- logger_set_level
- logger_set_max_bufsize
- logger_set_max_filesize
- logger_set_remain_days
- logger_get_cur_file
- hlogd, hlogi, hlogw, hloge, hlogf
- LOGD, LOGI, LOGW, LOGE, LOGF
### hbuf.h
- hbuf_t
- offset_buf_t
- class HBuf
- class HVLBuf
- class HRingBuf
### hmain.h
- main_ctx_init
- parse_opt
- parse_opt_long
- get_arg
- get_env
- setproctitle
- signal_init
- signal_handle
- create_pidfile
- delete_pidfile
- getpid_form_pidfile
- master_workers_run
### hstring.h
- to_string
- from_string
- toupper
- tolower
- reverse
- startswith
- endswith
- contains
- asprintf
- trim
- ltrim
- rtrim
- trim_pairs
- split
- splitKV
- replace
- replaceAll
### hfile.h
- class HFile
### hpath.h
- exists
- isdir
- isfile
- islink
- basename
- dirname
- filename
- suffixname
- join
### hdir.h
- listdir
### hurl.h
- HUrl::escape
- HUrl::unescape
- HUrl::parse
- HUrl::dump
### hscope.h
- defer
- template ScopeCleanup
- template ScopeFree
- template ScopeDelete
- template ScopeDeleteArray
- template ScopeRelease
- template ScopeLock
### ifconfig.h
- ifconfig
## utils
### md5.h
- HV_MD5Init
- HV_MD5Update
- HV_MD5Final
- hv_md5
- hv_md5_hex
### sha1.h
- HV_SHA1Init
- HV_SHA1Update
- HV_SHA1Final
- HV_SHA1
- hv_sha1
- hv_sha1_hex
### base64.h
- hv_base64_decode
- hv_base64_encode
### json.hpp
- json::parse
- json::dump
### singleton.h
- DISABLE_COPY
- SINGLETON_DECL
- SINGLETON_IMPL
## event
### hloop.h
- hloop_create_tcp_client
- hloop_create_tcp_server
- hloop_create_udp_client
- hloop_create_udp_server
- hloop_create_ssl_client
- hloop_create_ssl_server
- hloop_new
- hloop_free
- hloop_run
- hloop_stop
- hloop_pause
- hloop_resume
- hloop_status
- hloop_pid
- hloop_tid
- hloop_now
- hloop_now_ms
- hloop_now_us
- hloop_update_time
- hloop_set_userdata
- hloop_userdata
- hloop_wakeup
- hloop_post_event
- hevent_loop
- hevent_type
- hevent_id
- hevent_priority
- hevent_userdata
- hevent_set_priority
- hevent_ser_userdata
- haccept
- hconnect
- hread
- hwrite
- hrecv
- hsend
- hrecvfrom
- hsendto
- hio_add
- hio_del
- hio_get
- hio_detach
- hio_attach
- hio_read
- hio_read_start
- hio_read_stop
- hio_read_once
- hio_read_until
- hio_read_until_length
- hio_read_until_delim
- hio_read_readline
- hio_read_readstring
- hio_read_readbytes
- hio_write
- hio_close
- hio_accept
- hio_connect
- hio_fd
- hio_id
- hio_type
- hio_error
- hio_localaddr
- hio_peeraddr
- hio_events
- hio_revents
- hio_is_opened
- hio_is_closed
- hio_enable_ssl
- hio_is_ssl
- hio_get_ssl
- hio_set_ssl
- hio_get_ssl_ctx
- hio_set_ssl_ctx
- hio_new_ssl_ctx
- hio_setcb_accept
- hio_setcb_connect
- hio_setcb_read
- hio_setcb_write
- hio_setcb_close
- hio_getcb_accept
- hio_getcb_connect
- hio_getcb_read
- hio_getcb_write
- hio_getcb_close
- hio_set_type
- hio_set_localaddr
- hio_set_peeraddr
- hio_set_readbuf
- hio_set_connect_timeout
- hio_set_close_timeout
- hio_set_read_timeout
- hio_set_write_timeout
- hio_set_keepalive_timeout
- hio_set_heartbeat
- hio_set_unpack
- hio_unset_unpack
- hio_read_upstream
- hio_write_upstream
- hio_close_upstream
- hio_setup_upstream
- hio_get_upstream
- hio_setup_tcp_upstream
- hio_setup_ssl_upstream
- hio_setup_udp_upstream
- hio_create_socket
- hio_context
- hio_set_context
- htimer_add
- htimer_add_period
- htimer_del
- htimer_reset
- hidle_add
- hidle_del
### nlog.h
- network_logger
- nlog_listen
## evpp
- class Buffer
- class Channel
- class Event
- class EventLoop
- class EventLoopThread
- class EventLoopThreadPool
- class TcpClient
- class TcpServer
- class UdpClient
- class UdpServer
## ssl
- hssl_ctx_init
- hssl_ctx_cleanup
- hssl_ctx_instance
- hssl_ctx_new
- hssl_ctx_free
- hssl_new
- hssl_free
- hssl_accept
- hssl_connnect
- hssl_read
- hssl_write
- hssl_close
- hssl_set_sni_hostname
## protocol
### dns.h
- dns_name_decode
- dns_name_encode
- dns_pack
- dns_unpack
- dns_rr_pack
- dns_rr_unpack
- dns_query
- dns_free
- nslookup
### ftp.h
- ftp_command_str
- ftp_connect
- ftp_login
- ftp_exec
- ftp_upload
- ftp_download
- ftp_download_with_cb
- ftp_quit
- ftp_status_str
### smtp.h
- smtp_command_str
- smtp_status_str
- smtp_build_command
- sendmail
### icmp.h
- ping
## http
- class HttpMessage
- class HttpRequest
- class HttpResponse
- class HttpParser
### httpdef.h
- http_content_type_enum
- http_content_type_enum_by_suffix
- http_content_type_str
- http_content_type_str_by_suffix
- http_content_type_suffix
- http_errno_description
- http_errno_name
- http_method_enum
- http_method_str
- http_status_enum
- http_status_str
### http_content.h
- parse_query_params
- parse_json
- parse_multipart
- dump_query_params
- dump_json
- dump_multipart
### HttpClient.h
- http_client_new
- http_client_del
- http_client_send
- http_client_send_async
- http_client_strerror
- http_client_set_timeout
- http_client_set_header
- http_client_del_header
- http_client_get_header
- http_client_clear_headers
- http_client_set_http_proxy
- http_client_set_https_proxy
- http_client_add_no_proxy
- class HttpClient
### requests.h
- requests::request
- requests::get
- requests::post
- requests::put
- requests::patch
- requests::Delete
- requests::head
- requests::async
### axios.h
- axios::axios
- axios::get
- axios::post
- axios::put
- axios::patch
- axios::Delete
- axios::head
- axios::async
### HttpServer.h
- http_server_run
- http_server_stop
- class HttpService
- class HttpServer
### WebSocketClient.h
- class WebSocketClient
### WebSocketServer.h
- websocket_server_run
- websocket_server_stop
- class WebSocketService
- class WebSocketServer
## mqtt
- mqtt_client_new
- mqtt_client_free
- mqtt_client_run
- mqtt_client_stop
- mqtt_client_set_id
- mqtt_client_set_will
- mqtt_client_set_auth
- mqtt_client_set_callback
- mqtt_client_set_userdata
- mqtt_client_get_userdata
- mqtt_client_get_last_error
- mqtt_client_set_ssl_ctx
- mqtt_client_new_ssl_ctx
- mqtt_client_set_reconnect
- mqtt_client_reconnect
- mqtt_client_set_connect_timeout
- mqtt_client_connect
- mqtt_client_is_connected
- mqtt_client_disconnect
- mqtt_client_publish
- mqtt_client_subscribe
- mqtt_client_unsubscribe
- class MqttClient
## other
- class HThreadPool
- class HObjectPool
- class ThreadLocalStorage

31
external/libhv/libhv-1.3.2/docs/PLAN.md vendored Normal file
View File

@ -0,0 +1,31 @@
## Done
- base: cross platfrom infrastructure
- event: select/poll/epoll/wepoll/kqueue/port
- ssl: openssl/gnutls/mbedtls/wintls/appletls
- rudp: KCP
- evpp: c++ EventLoop interface similar to muduo and evpp
- http client/server: include https http1/x http2
- websocket client/server
- mqtt client
## Improving
- Path router: optimized matching via trie?
- FileCache use LRUCache
## Plan
- redis client
- async DNS
- lua binding
- js binding
- hrpc = libhv + protobuf
- rudp: FEC, ARQ, UDT, QUIC
- kcptun
- have a taste of io_uring
- coroutine
- cppsocket.io
- IM-libhv
- MediaServer-libhv
- GameServer-libhv

View File

@ -0,0 +1,153 @@
通道类
```c++
class Channel {
// 返回底层的io结构体指针
hio_t* io() { return io_; }
// 返回socket文件描述符
int fd() { return fd_; }
// 返回一个唯一标示id
uint32_t id() { return id_; }
// 返回错误码
int error() { return hio_error(io_); }
// 获取/设置/新建/删除 上下文
void* context();
void setContext(void* ctx);
template<class T> T* newContext();
template<class T> T* getContext();
template<class T> void deleteContext();
// 获取/设置/新建/删除 上下文智能指针
std::shared_ptr<void> contextPtr();
void setContextPtr(const std::shared_ptr<void>& ctx);
void setContextPtr(std::shared_ptr<void>&& ctx);
template<class T> std::shared_ptr<T> newContextPtr();
template<class T> std::shared_ptr<T> getContextPtr();
void deleteContextPtr();
// 是否打开状态
bool isOpened();
// 是否关闭状态
bool isClosed();
// 开始读
int startRead();
// 停止读
int stopRead();
// 读一次
int readOnce();
// 读一个字符串
int readString();
// 读一行
int readLine();
// 读取N个字节
int readBytes(int len);
// 写
int write(const void* data, int size);
int write(Buffer* buf);
int write(const std::string& str);
// 设置最大读缓存
void setMaxReadBufsize(uint32_t size);
// 设置最大写缓存
void setMaxWriteBufsize(uint32_t size);
// 获取当前写缓存大小
size_t writeBufsize();
// 是否写完成
bool isWriteComplete();
// 关闭
int close(bool async = false);
// 读回调
std::function<void(Buffer*)> onread;
// 写回调
std::function<void(Buffer*)> onwrite;
// 关闭回调
std::function<void()> onclose;
};
// SocketChannel 继承自 Channel
class SocketChannel : public Channel {
// 连接状态回调
std::function<void()> onconnect;
// 心跳回调
std::function<void()> heartbeat;
// 启用SSL/TLS加密通信
int enableSSL();
// 是否是SSL/TLS加密通信
bool isSSL();
// 设置SSL
int setSSL(hssl_t ssl);
// 设置SSL_CTX
int setSslCtx(hssl_ctx_t ssl_ctx);
// 新建SSL_CTX
int newSslCtx(hssl_ctx_opt_t* opt);
// 设置主机名
int setHostname(const std::string& hostname);
// 设置连接超时
void setConnectTimeout(int timeout_ms);
// 设置关闭超时 (说明:非阻塞写队列非空时,需要等待写完成再关闭)
void setCloseTimeout(int timeout_ms);
// 设置读超时 (一段时间没有数据到来便自动关闭连接)
void setReadTimeout(int timeout_ms);
// 设置写超时 (一段时间没有数据发送便自动关闭连接)
void setWriteTimeout(int timeout_ms);
// 设置keepalive超时 (一段时间没有数据收发便自动关闭连接)
void setKeepaliveTimeout(int timeout_ms);
// 设置心跳 (定时发送心跳包)
void setHeartbeat(int interval_ms, std::function<void()> fn);
// 设置拆包规则
void setUnpack(unpack_setting_t* setting);
// 开始连接
int startConnect(int port, const char* host = "127.0.0.1");
int startConnect(struct sockaddr* peeraddr);
int startConnect();
// 是否已连接
bool isConnected();
// 返回本地地址
std::string localaddr();
// 返回对端地址
std::string peeraddr();
};
// WebSocketChannel 继承自 SocketChannel
class WebSocketChannel : public SocketChannel {
// 发送文本帧
int send(const std::string& msg, enum ws_opcode opcode = WS_OPCODE_TEXT, bool fin = true);
// 发送二进制帧
int send(const char* buf, int len, enum ws_opcode opcode = WS_OPCODE_BINARY, bool fin = true);
// 分片发送
int send(const char* buf, int len, int fragment, enum ws_opcode opcode = WS_OPCODE_BINARY);
// 关闭
int close();
};
```

View File

@ -0,0 +1,123 @@
事件循环类
```c++
class EventLoop {
// 返回底层的loop结构体指针
hloop_t* loop();
// 运行
void run();
// 停止
void stop();
// 暂停
void pause();
// 继续
void resume();
// 设置定时器
TimerID setTimer(int timeout_ms, TimerCallback cb, uint32_t repeat = INFINITE, TimerID timerID = INVALID_TIMER_ID);
// 设置一次性定时器
TimerID setTimeout(int timeout_ms, TimerCallback cb);
// 设置永久性定时器
TimerID setInterval(int interval_ms, TimerCallback cb);
// 杀掉定时器
void killTimer(TimerID timerID);
// 重置定时器
void resetTimer(TimerID timerID, int timeout_ms = 0);
// 返回事件循环所在的线程ID
long tid();
// 是否在事件循环所在线程
bool isInLoopThread();
// 断言在事件循环所在线程
void assertInLoopThread();
// 运行在事件循环里
void runInLoop(Functor fn);
// 队列在事件循环里
void queueInLoop(Functor fn);
// 投递一个事件到事件循环
void postEvent(EventCallback cb);
};
class EventLoopThread {
// 返回事件循环指针
const EventLoopPtr& loop();
// 返回底层的loop结构体指针
hloop_t* hloop();
// 是否运行中
bool isRunning();
/* 开始运行
* wait_thread_started: 是否阻塞等待线程开始
* pre: 线程开始后执行的函数
* post: 线程结束前执行的函数
*/
void start(bool wait_thread_started = true,
Functor pre = Functor(),
Functor post = Functor());
// 停止运行
void stop(bool wait_thread_stopped = false);
// 等待线程退出
void join();
};
class EventLoopThreadPool {
// 获取线程数量
int threadNum();
// 设置线程数量
void setThreadNum(int num);
// 返回下一个事件循环对象
// 支持轮询、随机、最少连接数等负载均衡策略
EventLoopPtr nextLoop(load_balance_e lb = LB_RoundRobin);
// 返回索引的事件循环对象
EventLoopPtr loop(int idx = -1);
// 返回索引的底层loop结构体指针
hloop_t* hloop(int idx = -1);
/* 开始运行
* wait_threads_started: 是否阻塞等待所有线程开始
* pre: 线程开始后执行的函数
* post: 线程结束前执行的函数
*/
void start(bool wait_threads_started = false,
std::function<void(const EventLoopPtr&)> pre = NULL,
std::function<void(const EventLoopPtr&)> post = NULL);
// 停止运行
void stop(bool wait_threads_stopped = false);
// 等待所有线程退出
void join();
};
```
测试代码见:
- [evpp/EventLoop_test.cpp](../../evpp/EventLoop_test.cpp)
- [evpp/EventLoopThread_test.cpp](../../evpp/EventLoopThread_test.cpp)
- [evpp/EventLoopThreadPool_test.cpp](../../evpp/EventLoopThreadPool_test.cpp)

View File

@ -0,0 +1,84 @@
HTTP 客户端类
```c++
class HttpClient {
// 设置超时
int setTimeout(int timeout);
// 设置SSL/TLS
int setSslCtx(hssl_ctx_t ssl_ctx);
// 新建SSL/TLS
int newSslCtx(hssl_ctx_opt_t* opt);
// 清除全部请求头部
int clearHeaders();
// 设置请求头部
int setHeader(const char* key, const char* value);
// 删除请求头部
int delHeader(const char* key);
// 获取请求头部
const char* getHeader(const char* key);
// 设置http代理
int setHttpProxy(const char* host, int port);
// 设置https代理
int setHttpsProxy(const char* host, int port);
// 添加不走代理
int addNoProxy(const char* host);
// 同步发送
int send(HttpRequest* req, HttpResponse* resp);
// 异步发送
int sendAsync(HttpRequestPtr req, HttpResponseCallback resp_cb = NULL);
// 关闭连接 (HttpClient对象析构时会自动调用)
int close();
};
namespace requests {
// 同步请求
Response request(Request req);
Response request(http_method method, const char* url, const http_body& body = NoBody, const http_headers& headers = DefaultHeaders);
// 上传文件
Response uploadFile(const char* url, const char* filepath, http_method method = HTTP_POST, const http_headers& headers = DefaultHeaders);
// 通过 `multipart/form-data` 格式上传文件
Response uploadFormFile(const char* url, const char* name, const char* filepath, std::map<std::string, std::string>& params = hv::empty_map, http_method method = HTTP_POST, const http_headers& headers = DefaultHeaders);
// 上传大文件(带上传进度回调)
Response uploadLargeFile(const char* url, const char* filepath, upload_progress_cb progress_cb = NULL, http_method method = HTTP_POST, const http_headers& headers = DefaultHeaders);
// 下载文件 (更详细的断点续传示例代码见`examples/wget.cpp`)
size_t downloadFile(const char* url, const char* filepath, download_progress_cb progress_cb = NULL);
// HEAD 请求
Response head(const char* url, const http_headers& headers = DefaultHeaders);
// GET 请求
Response get(const char* url, const http_headers& headers = DefaultHeaders);
// POST 请求
Response post(const char* url, const http_body& body = NoBody, const http_headers& headers = DefaultHeaders);
// PUT 请求
Response put(const char* url, const http_body& body = NoBody, const http_headers& headers = DefaultHeaders);
// PATCH 请求
Response patch(const char* url, const http_body& body = NoBody, const http_headers& headers = DefaultHeaders);
// DELETE 请求
Response Delete(const char* url, const http_headers& headers = DefaultHeaders);
// 异步请求
int async(Request req, ResponseCallback resp_cb);
}
```
测试代码见 [examples/http_client_test.cpp](../../examples/http_client_test.cpp)

View File

@ -0,0 +1,83 @@
```c++
class HttpContext {
/* 获取请求信息 */
// 获取客户端IP
std::string ip();
// 获取客户端端口
int port();
// 获取请求method
http_method method();
// 获取请求url
std::string url();
// 获取请求path
std::string path();
// 获取请求host
std::string host();
// 获取请求头部
const http_headers& headers();
std::string header(const char* key, const std::string& defvalue = hv::empty_string);
// 获取请求参数
const hv::QueryParams& params();
std::string param(const char* key, const std::string& defvalue = hv::empty_string);
// 获取请求cookie
const HttpCookie& cookie(const char* name);
// 获取请求 `Content-Length`
int length();
// 获取请求 `Content-Type`
http_content_type type();
// 判断请求 `Content-Type`
bool is(http_content_type content_type);
// 获取请求body
std::string& body();
// 获取 `application/json` 格式数据
const hv::Json& json();
// 获取 `multipart/form-data` 格式数据
const hv::MultiPart& form();
std::string form(const char* name, const std::string& defvalue = hv::empty_string);
// 获取 `application/x-www-urlencoded` 格式数据
const hv::KeyValue& urlencoded();
std::string urlencoded(const char* key, const std::string& defvalue = hv::empty_string);
// 根据 `Content-Type` 获取对应格式数据
template<typename T>
T get(const char* key, T defvalue = 0);
std::string get(const char* key, const std::string& defvalue = hv::empty_string);
/* 设置响应信息 */
// 设置响应状态码
void setStatus(http_status status);
// 设置响应 `Content-Type`
void setContentType(http_content_type type);
// 设置响应头部
void setHeader(const char* key, const std::string& value);
// 设置响应cookie
void setCookie(const HttpCookie& cookie);
// 设置响应body
void setBody(const std::string& body);
template<typename T>
// 根据 `Content-Type` 设置对应格式数据
void set(const char* key, const T& value);
// 发送
int send();
int send(const std::string& str, http_content_type type = APPLICATION_JSON);
// 发送文本数据
int sendString(const std::string& str);
// 发送二进制数据
int sendData(void* data, int len, bool nocopy = true);
// 发送文件
int sendFile(const char* filepath);
// 发送json数据
template<typename T>
int sendJson(const T& t);
// 重定向
int redirect(const std::string& location, http_status status = HTTP_STATUS_FOUND);
// 主动关闭连接
int close();
};
```

View File

@ -0,0 +1,110 @@
```c++
class HttpMessage {
// 设置/获取头部
void SetHeader(const char* key, const std::string& value);
std::string GetHeader(const char* key, const std::string& defvalue = hv::empty_string);
// 添加/获取cookie
void AddCookie(const HttpCookie& cookie);
const HttpCookie& GetCookie(const std::string& name);
// 设置/获取 `Content-Type`
void SetContentType(http_content_type type);
http_content_type ContentType();
// 获取 `Content-Length`
size_t ContentLength();
// 填充数据
void SetBody(const std::string& body);
// 获取数据
const std::string& Body();
// 解析数据
int ParseBody();
// 填充/获取 `application/json` 格式数据
template<typename T>
int Json(const T& t);
const hv::Json& GetJson();
// 填充/获取 `multipart/form-data` 格式数据
template<typename T>
void SetFormData(const char* name, const T& t);
void SetFormFile(const char* name, const char* filepath);
std::string GetFormData(const char* name, const std::string& defvalue = hv::empty_string);
int SaveFormFile(const char* name, const char* path);
// 填充/获取 `application/x-www-urlencoded` 格式数据
template<typename T>
void SetUrlEncoded(const char* key, const T& t);
std::string GetUrlEncoded(const char* key, const std::string& defvalue = hv::empty_string);
// 根据 `Content-Type` 填充对应格式数据
template<typename T>
void Set(const char* key, const T& value);
// 根据 `Content-Type` 获取对应格式数据
template<typename T>
T Get(const char* key, T defvalue = 0);
// 根据 `Content-Type` 获取对应格式数据并转换成字符串
std::string GetString(const char* key, const std::string& = "");
// 根据 `Content-Type` 获取对应格式数据并转换成Boolean类型
bool GetBool(const char* key, bool defvalue = 0);
// 根据 `Content-Type` 获取对应格式数据并转换成整型
int64_t GetInt(const char* key, int64_t defvalue = 0);
// 根据 `Content-Type` 获取对应格式数据并转换成浮点数
double GetFloat(const char* key, double defvalue = 0);
};
// HttpRequest 继承自 HttpMessage
class HttpRequest : public HttpMessage {
// 设置/获取method
void SetMethod(const char* method);
const char* Method();
// 设置URL
void SetUrl(const char* url);
// 获取URL
const std::string& Url();
// 解析URL
void ParseUrl();
// 获取Host
std::string Host();
// 获取Path
std::string Path();
// 设置/获取参数
template<typename T>
void SetParam(const char* key, const T& t);
std::string GetParam(const char* key, const std::string& defvalue = hv::empty_string);
// 设置代理
void SetProxy(const char* host, int port);
// 设置认证
void SetAuth(const std::string& auth);
void SetBasicAuth(const std::string& username, const std::string& password);
void SetBearerTokenAuth(const std::string& token);
// 设置请求超时
void SetTimeout(int sec);
// 设置连接超时
void SetConnectTimeout(int sec);
// 允许重定向
void AllowRedirect(bool on = true);
// 设置重试
void SetRetry(int count = DEFAULT_HTTP_FAIL_RETRY_COUNT,
int delay = DEFAULT_HTTP_FAIL_RETRY_DELAY);
// 取消
void Cancel();
};
// HttpResponse 继承自 HttpMessage
class HttpResponse : public HttpMessage {
// 状态码
http_status status_code;
// 状态字符串
const char* status_message();
};
```

View File

@ -0,0 +1,140 @@
HTTP 服务端类
```c++
// HTTP服务类
class HttpServer {
// 注册HTTP业务类
void registerHttpService(HttpService* service);
// 设置监听主机
void setHost(const char* host = "0.0.0.0");
// 设置监听端口
void setPort(int port = 0, int ssl_port = 0);
// 设置监听文件描述符
void setListenFD(int fd = -1, int ssl_fd = -1);
// 设置IO进程数 (仅`linux`下有效)
void setProcessNum(int num);
// 设置IO线程数
void setThreadNum(int num);
// 设置SSL/TLS
int setSslCtx(hssl_ctx_t ssl_ctx);
// 新建SSL/TLS
int newSslCtx(hssl_ctx_opt_t* opt);
// hooks
// 事件循环开始时执行的回调函数
std::function<void()> onWorkerStart;
// 事件循环结束时执行的回调函数
std::function<void()> onWorkerStop;
// 占用当前线程运行
int run(bool wait = true);
// 不占用当前线程运行
int start();
// 停止服务
int stop();
};
// HTTP业务类
class HttpService {
// 添加静态资源映射
void Static(const char* path, const char* dir);
// 允许跨域访问
void AllowCORS();
// 添加可信代理 (代理白名单)
void AddTrustProxy(const char* host);
// 添加不可信代理 (代理黑名单)
void AddNoProxy(const char* host);
// 开启正向转发代理
void EnableForwardProxy();
// 添加反向代理映射
void Proxy(const char* path, const char* url);
// 添加中间件
void Use(Handler handlerFunc);
// 添加路由处理器
void Handle(const char* httpMethod, const char* relativePath, Handler handlerFunc);
// 添加`HEAD`路由
void HEAD(const char* relativePath, Handler handlerFunc);
// 添加`GET`路由
void GET(const char* relativePath, Handler handlerFunc);
// 添加`POST`路由
void POST(const char* relativePath, Handler handlerFunc);
// 添加`PUT`路由
void PUT(const char* relativePath, Handler handlerFunc);
// 添加`DELETE`路由
void Delete(const char* relativePath, Handler handlerFunc);
// 添加`PATCH`路由
void PATCH(const char* relativePath, Handler handlerFunc);
// 添加任意`HTTP method`路由
void Any(const char* relativePath, Handler handlerFunc);
// 返回注册的路由路径列表
hv::StringList Paths();
// 处理流程:前处理器 -> 中间件 -> 处理器 -> 后处理器
// preprocessor -> middleware -> processor -> postprocessor
// 数据成员
http_handler preprocessor; // 前处理器
http_handlers middleware; // 中间件
http_handler processor; // 处理器
http_handler postprocessor; // 后处理器
std::string base_url; // 基本路径
std::string document_root; // 文档根目录
std::string home_page; // 主页
std::string error_page; // 默认错误页
std::string index_of; // 目录
http_handler errorHandler; // 错误处理器
int proxy_connect_timeout; // 代理连接超时
int proxy_read_timeout; // 代理读超时
int proxy_write_timeout; // 代理写超时
int keepalive_timeout; // 长连接保活超时
int max_file_cache_size; // 文件缓存最大尺寸
int file_cache_stat_interval; // 文件缓存stat间隔查询文件是否修改
int file_cache_expired_time; // 文件缓存过期时间,过期自动释放
int limit_rate; // 下载速度限制
};
/* 几种`handler`处理函数区别说明: */
// 同步`handler`运行在IO线程
typedef std::function<int(HttpRequest* req, HttpResponse* resp)> http_sync_handler;
// 异步`handler`运行在`hv::async`全局线程池,可通过`hv::async::startup`设置线程池属性
typedef std::function<void(const HttpRequestPtr& req, const HttpResponseWriterPtr& writer)> http_async_handler;
// 上下文`handler`运行在IO线程你可以很方便的将`HttpContextPtr`智能指针抛到你的消费者线程/线程池去处理
typedef std::function<int(const HttpContextPtr& ctx)> http_ctx_handler;
// 中间状态`handler`运行在IO线程用来实现大数据量的边接收边处理
typedef std::function<int(const HttpContextPtr& ctx, http_parser_state state, const char* data, size_t size)> http_state_handler;
```
测试代码见 [examples/http_server_test.cpp](../../examples/http_server_test.cpp)

View File

@ -0,0 +1,18 @@
## c接口
- [hloop: 事件循环](hloop.md)
- [hbase: 基础函数](hbase.md)
- [hlog: 日志](hlog.md)
## c++接口
- [class EventLoop: 事件循环类](EventLoop.md)
- [class Channel: 通道类](Channel.md)
- [class TcpServer: TCP服务端类](TcpServer.md)
- [class TcpClient: TCP客户端类](TcpClient.md)
- [class UdpServer: UDP服务端类](UdpServer.md)
- [class UdpClient: UDP客户端类](UdpClient.md)
- [class HttpServer: HTTP服务端类](HttpServer.md)
- [class HttpClient: HTTP客户端类](HttpClient.md)
- [class WebSocketServer: WebSocket服务端类](WebSocketServer.md)
- [class WebSocketClient: WebSocket客户端类](WebSocketClient.md)

View File

@ -0,0 +1,63 @@
TCP 客户端类
```c++
class TcpClient {
// 返回所在的事件循环
const EventLoopPtr& loop();
// 创建套接字
int createsocket(int remote_port, const char* remote_host = "127.0.0.1");
int createsocket(struct sockaddr* remote_addr);
// 绑定端口
int bind(int local_port, const char* local_host = "0.0.0.0");
int bind(struct sockaddr* local_addr);
// 关闭套接字
void closesocket();
// 开始运行
void start(bool wait_threads_started = true);
// 停止运行
void stop(bool wait_threads_stopped = true);
// 是否已连接
bool isConnected();
// 发送
int send(const void* data, int size);
int send(Buffer* buf);
int send(const std::string& str);
// 设置SSL/TLS加密通信
int withTLS(hssl_ctx_opt_t* opt = NULL);
// 设置连接超时
void setConnectTimeout(int ms);
// 设置重连
void setReconnect(reconn_setting_t* setting);
// 是否是重连
bool isReconnect();
// 设置拆包规则
void setUnpack(unpack_setting_t* setting);
// 连接状态回调
std::function<void(const TSocketChannelPtr&)> onConnection;
// 消息回调
std::function<void(const TSocketChannelPtr&, Buffer*)> onMessage;
// 写完成回调
std::function<void(const TSocketChannelPtr&, Buffer*)> onWriteComplete;
};
```
测试代码见 [evpp/TcpClient_test.cpp](../../evpp/TcpClient_test.cpp)

View File

@ -0,0 +1,60 @@
TCP 服务端类
```c++
class TcpServer {
// 返回索引的事件循环
EventLoopPtr loop(int idx = -1);
// 创建套接字
int createsocket(int port, const char* host = "0.0.0.0");
// 关闭套接字
void closesocket();
// 设置最大连接数
void setMaxConnectionNum(uint32_t num);
// 设置负载均衡策略
void setLoadBalance(load_balance_e lb);
// 设置线程数
void setThreadNum(int num);
// 开始运行
void start(bool wait_threads_started = true);
// 停止运行
void stop(bool wait_threads_stopped = true);
// 设置SSL/TLS加密通信
int withTLS(hssl_ctx_opt_t* opt = NULL);
// 设置拆包规则
void setUnpack(unpack_setting_t* setting);
// 返回当前连接数
size_t connectionNum();
// 遍历连接
int foreachChannel(std::function<void(const TSocketChannelPtr& channel)> fn);
// 广播消息
int broadcast(const void* data, int size);
int broadcast(const std::string& str);
// 连接到来/断开回调
std::function<void(const TSocketChannelPtr&)> onConnection;
// 消息回调
std::function<void(const TSocketChannelPtr&, Buffer*)> onMessage;
// 写完成回调
std::function<void(const TSocketChannelPtr&, Buffer*)> onWriteComplete;
};
```
测试代码见 [evpp/TcpServer_test.cpp](../../evpp/TcpServer_test.cpp)

View File

@ -0,0 +1,42 @@
UDP 客户端类
```c++
class UdpClient {
// 返回所在的事件循环
const EventLoopPtr& loop();
// 创建套接字
int createsocket(int remote_port, const char* remote_host = "127.0.0.1");
// 绑定端口
int bind(int local_port, const char* local_host = "0.0.0.0");
// 关闭套接字
void closesocket();
// 开始运行
void start(bool wait_threads_started = true);
// 停止运行
void stop(bool wait_threads_stopped = true);
// 发送
int sendto(const void* data, int size, struct sockaddr* peeraddr = NULL);
int sendto(Buffer* buf, struct sockaddr* peeraddr = NULL);
int sendto(const std::string& str, struct sockaddr* peeraddr = NULL);
// 设置KCP
void setKcp(kcp_setting_t* setting);
// 消息回调
std::function<void(const TSocketChannelPtr&, Buffer*)> onMessage;
// 写完成回调
std::function<void(const TSocketChannelPtr&, Buffer*)> onWriteComplete;
};
```
测试代码见 [evpp/UdpClient_test.cpp](../../evpp/UdpClient_test.cpp)

View File

@ -0,0 +1,39 @@
UDP 服务端类
```c++
class UdpServer {
// 返回所在的事件循环
const EventLoopPtr& loop();
// 创建套接字
int createsocket(int port, const char* host = "0.0.0.0");
// 关闭套接字
void closesocket();
// 开始运行
void start(bool wait_threads_started = true);
// 停止运行
void stop(bool wait_threads_stopped = true);
// 发送
int sendto(const void* data, int size, struct sockaddr* peeraddr = NULL);
int sendto(Buffer* buf, struct sockaddr* peeraddr = NULL);
int sendto(const std::string& str, struct sockaddr* peeraddr = NULL);
// 设置KCP
void setKcp(kcp_setting_t* setting);
// 消息回调
std::function<void(const TSocketChannelPtr&, Buffer*)> onMessage;
// 写完成回调
std::function<void(const TSocketChannelPtr&, Buffer*)> onWriteComplete;
};
```
测试代码见 [evpp/UdpServer_test.cpp](../../evpp/UdpServer_test.cpp)

View File

@ -0,0 +1,37 @@
WebSocket 客户端类
```c++
class WebSocketClient {
// 打开回调
std::function<void()> onopen;
// 关闭回调
std::function<void()> onclose;
// 消息回调
std::function<void(const std::string& msg)> onmessage;
// 打开
int open(const char* url, const http_headers& headers = DefaultHeaders);
// 关闭
int close();
// 发送
int send(const std::string& msg);
int send(const char* buf, int len, enum ws_opcode opcode = WS_OPCODE_BINARY);
// 设置心跳间隔
void setPingInterval(int ms);
// 设置WebSocket握手阶段的HTTP请求
void setHttpRequest(const HttpRequestPtr& req);
// 获取WebSocket握手阶段的HTTP响应
const HttpResponsePtr& getHttpResponse();
};
```
测试代码见 [examples/websocket_client_test.cpp](../../examples/websocket_client_test.cpp)

View File

@ -0,0 +1,30 @@
WebSocket 服务端类
```c++
// WebSocketServer 继承自 HttpServer
class WebSocketServer : public HttpServer {
// 注册WebSocket业务类
void registerWebSocketService(WebSocketService* service);
};
// WebSocket业务类
struct WebSocketService {
// 打开回调
std::function<void(const WebSocketChannelPtr&, const HttpRequestPtr&)> onopen;
// 消息回调
std::function<void(const WebSocketChannelPtr&, const std::string&)> onmessage;
// 关闭回调
std::function<void(const WebSocketChannelPtr&)> onclose;
// 心跳间隔
int ping_interval;
};
```
测试代码见 [examples/websocket_server_test.cpp](../../examples/websocket_server_test.cpp)

View File

@ -0,0 +1,111 @@
一些基础函数
```c
/* hv内存分配/释放函数 */
void* hv_malloc(size_t size);
void* hv_realloc(void* oldptr, size_t newsize, size_t oldsize);
void* hv_calloc(size_t nmemb, size_t size);
void* hv_zalloc(size_t size);
void hv_free(void* ptr);
// 使用hv分配内存次数
long hv_alloc_cnt();
// 使用hv释放内存次数
long hv_free_cnt();
/* 字符串操作 */
// 字符串转大写
char* hv_strupper(char* str);
// 字符串转小写
char* hv_strlower(char* str);
// 字符串翻转
char* hv_strreverse(char* str);
// 判断字符串是否以xxx开头
bool hv_strstartswith(const char* str, const char* start);
// 判断字符串是否以xxx结尾
bool hv_strendswith(const char* str, const char* end);
// 判断字符串是否包含xxx
bool hv_strcontains(const char* str, const char* sub);
// 安全的strncpy
char* hv_strncpy(char* dest, const char* src, size_t n);
// 安全的strncat
char* hv_strncat(char* dest, const char* src, size_t n);
// 字符查找
char* hv_strnchr(const char* s, char c, size_t n);
// 查找最后一个点(通常用于提取文件后缀)
#define hv_strrchr_dot(str) strrchr(str, '.')
// 查找最后的路径(通常用于分离目录和文件)
char* hv_strrchr_dir(const char* filepath);
// 获取文件名利用了上面的strrchr_dir
const char* hv_basename(const char* filepath);
// 获取文件后缀利用了上面的strrchr_dot
const char* hv_suffixname(const char* filename);
/* 文件&目录 */
// mkdir -p: 创建目录
int hv_mkdir_p(const char* dir);
// rmdir -p: 删除目录
int hv_rmdir_p(const char* dir);
// 判断路径是否存在
bool hv_exists(const char* path);
// 判断是否是目录
bool hv_isdir(const char* path);
// 判断是否是文件
bool hv_isfile(const char* path);
// 判断是否是链接
bool hv_islink(const char* path);
// 获取文件大小
size_t hv_filesize(const char* filepath);
// 获取可执行文件绝对路径,例如/usr/local/bin/httpd
char* get_executable_path(char* buf, int size);
// 获取可执行文件所在目录,例如/usr/local/bin
char* get_executable_dir(char* buf, int size);
// 获取可执行文件名例如httpd
char* get_executable_file(char* buf, int size);
// 获取运行目录,例如/home/www/html
char* get_run_dir(char* buf, int size);
// 返回一个随机数
int hv_rand(int min, int max);
// 返回一个随机字符串
char* hv_random_string(char *buf, int len);
// 1 y on yes true enable返回true通常用于配置文件
bool hv_getboolean(const char* str);
// 解析size字符串
// 1T2G3M4K5B => ?B
size_t hv_parse_size(const char* str);
// 解析时间字符串
// 1w2d3h4m5s => ?s
time_t hv_parse_time(const char* str);
// 解析url字符串
int hv_parse_url(hurl_t* stURL, const char* strURL);
```
单元测试代码见 [unittest/hbase_test.c](../../unittest/hbase_test.c)

View File

@ -0,0 +1,111 @@
日志
```c
// 标准输出日志
void stdout_logger(int loglevel, const char* buf, int len);
// 标准错误日志
void stderr_logger(int loglevel, const char* buf, int len);
// 文件日志
void file_logger(int loglevel, const char* buf, int len);
// 网络日志定义在event/nlog.h头文件里
// void network_logger(int loglevel, const char* buf, int len);
// 创建日志器
logger_t* logger_create();
// 销毁日志器
void logger_destroy(logger_t* logger);
// 设置日志处理器
void logger_set_handler(logger_t* logger, logger_handler fn);
// 设置日志等级
void logger_set_level(logger_t* logger, int level);
// level = [VERBOSE,DEBUG,INFO,WARN,ERROR,FATAL,SILENT]
void logger_set_level_by_str(logger_t* logger, const char* level);
/*
* 设置日志格式
* format = "%y-%m-%d %H:%M:%S.%z %L %s"
* message = "2020-01-02 03:04:05.067 DEBUG message"
* %y year
* %m month
* %d day
* %H hour
* %M min
* %S sec
* %z ms
* %Z us
* %l First character of level
* %L All characters of level
* %s message
* %% %
*/
void logger_set_format(logger_t* logger, const char* format);
// 设置日志缓存大小
void logger_set_max_bufsize(logger_t* logger, unsigned int bufsize);
// 启用日志颜色
void logger_enable_color(logger_t* logger, int on);
// 日志打印
int logger_print(logger_t* logger, int level, const char* fmt, ...);
// 设置日志文件
void logger_set_file(logger_t* logger, const char* filepath);
// 设置日志文件大小
void logger_set_max_filesize(logger_t* logger, unsigned long long filesize);
// 16, 16M, 16MB
void logger_set_max_filesize_by_str(logger_t* logger, const char* filesize);
// 设置日志文件保留天数
void logger_set_remain_days(logger_t* logger, int days);
// 启用每次写日志文件立即刷新到磁盘即每次都调用fsync会增加IO耗时影响性能
void logger_enable_fsync(logger_t* logger, int on);
// 刷新缓存到磁盘(如对日志文件实时性有必要的,可使用定时器定时刷新到磁盘)
void logger_fsync(logger_t* logger);
// 获取当前日志文件路径
const char* logger_get_cur_file(logger_t* logger);
// hlog: 默认的日志器
logger_t* hv_default_logger();
// 销毁默认的日志器
void hv_destroy_default_logger(void);
// 对默认日志器hlog的一些便利操作宏
#define hlog hv_default_logger()
#define hlog_destory() hv_destroy_default_logger()
/* 禁用hv的默认日志 */
#define hlog_disable() logger_set_level(hlog, LOG_LEVEL_SILENT)
#define hlog_set_file(filepath) logger_set_file(hlog, filepath)
#define hlog_set_level(level) logger_set_level(hlog, level)
#define hlog_set_level_by_str(level) logger_set_level_by_str(hlog, level)
#define hlog_set_handler(fn) logger_set_handler(hlog, fn)
#define hlog_set_format(format) logger_set_format(hlog, format)
#define hlog_set_max_filesize(filesize) logger_set_max_filesize(hlog, filesize)
#define hlog_set_max_filesize_by_str(filesize) logger_set_max_filesize_by_str(hlog, filesize)
#define hlog_set_remain_days(days) logger_set_remain_days(hlog, days)
#define hlog_enable_fsync() logger_enable_fsync(hlog, 1)
#define hlog_disable_fsync() logger_enable_fsync(hlog, 0)
#define hlog_fsync() logger_fsync(hlog)
#define hlog_get_cur_file() logger_get_cur_file(hlog)
#define hlogd(fmt, ...) logger_print(hlog, LOG_LEVEL_DEBUG, fmt " [%s:%d:%s]\n", ## __VA_ARGS__, __FILENAME__, __LINE__, __FUNCTION__)
#define hlogi(fmt, ...) logger_print(hlog, LOG_LEVEL_INFO, fmt " [%s:%d:%s]\n", ## __VA_ARGS__, __FILENAME__, __LINE__, __FUNCTION__)
#define hlogw(fmt, ...) logger_print(hlog, LOG_LEVEL_WARN, fmt " [%s:%d:%s]\n", ## __VA_ARGS__, __FILENAME__, __LINE__, __FUNCTION__)
#define hloge(fmt, ...) logger_print(hlog, LOG_LEVEL_ERROR, fmt " [%s:%d:%s]\n", ## __VA_ARGS__, __FILENAME__, __LINE__, __FUNCTION__)
#define hlogf(fmt, ...) logger_print(hlog, LOG_LEVEL_FATAL, fmt " [%s:%d:%s]\n", ## __VA_ARGS__, __FILENAME__, __LINE__, __FUNCTION__)
```
测试代码见 [examples/hloop_test.c](../../examples/hloop_test.c)

View File

@ -0,0 +1,636 @@
事件循环和IO多路复用机制介绍
事件循环是`libevent、libev、libuv、libhv`这类网络库里最核心的概念即在事件循环里处理IO读写事件、定时器事件、自定义事件等各种事件<br>
IO多路复用即在一个IO线程监听多个fd如最早期的`select`、后来的`poll``linux的epoll`、`windows的iocp`、`bsd的kqueue`、`solaris的port`等都属于IO多路复用机制。<br>
非阻塞NIO搭配IO多路复用机制就是高并发的钥匙。<br>
`libhv`下的`event`模块正是封装了多种平台的IO多路复用机制提供了统一的事件接口是`libhv`的核心模块。<br>
`hloop.h`: 事件循环模块对外头文件。<br>
```c
// 事件结构体
struct hevent_s {
hloop_t* loop; // 事件所属循环
hevent_type_e event_type; // 事件类型
uint64_t event_id; // 事件ID
hevent_cb cb; // 事件回调
void* userdata; // 用户数据
void* privdata; // 私有数据
struct hevent_s* pending_next; // 指向下一个事件,用于实现事件队列
int priority; // 事件优先级
};
// 设置事件ID
#define hevent_set_id(ev, id) ((hevent_t*)(ev))->event_id = id
// 设置事件回调
#define hevent_set_cb(ev, cb) ((hevent_t*)(ev))->cb = cb
// 设置事件优先级
#define hevent_set_priority(ev, prio) ((hevent_t*)(ev))->priority = prio
// 设置事件用户数据
#define hevent_set_userdata(ev, udata) ((hevent_t*)(ev))->userdata = (void*)udata
// 获取事件所属循环
#define hevent_loop(ev) (((hevent_t*)(ev))->loop)
// 获取事件类型
#define hevent_type(ev) (((hevent_t*)(ev))->event_type)
// 获取事件ID
#define hevent_id(ev) (((hevent_t*)(ev))->event_id)
// 获取事件回调
#define hevent_cb(ev) (((hevent_t*)(ev))->cb)
// 获取事件优先级
#define hevent_priority(ev) (((hevent_t*)(ev))->priority)
// 获取事件用户数据
#define hevent_userdata(ev) (((hevent_t*)(ev))->userdata)
// hidle_t、htimer_t、hio_t皆是继承自hevent_t继承上面的数据成员和函数方法
// 新建事件循环
hloop_t* hloop_new(int flags DEFAULT(HLOOP_FLAG_AUTO_FREE));
// 释放事件循环
void hloop_free(hloop_t** pp);
// 运行事件循环
int hloop_run(hloop_t* loop);
// 停止事件循环
int hloop_stop(hloop_t* loop);
// 暂停事件循环
int hloop_pause(hloop_t* loop);
// 继续事件循环
int hloop_resume(hloop_t* loop);
// 唤醒事件循环
int hloop_wakeup(hloop_t* loop);
// 返回事件循环状态
hloop_status_e hloop_status(hloop_t* loop);
// 更新事件循环里的时间
void hloop_update_time(hloop_t* loop);
// 返回事件循环里记录的时间
uint64_t hloop_now(hloop_t* loop); // s
uint64_t hloop_now_ms(hloop_t* loop); // ms
uint64_t hloop_now_us(hloop_t* loop); // us
// 返回事件循环所在进程ID
long hloop_pid(hloop_t* loop);
// 返回事件循环所在线程ID
long hloop_tid(hloop_t* loop);
// 返回事件循环的循环次数
uint64_t hloop_count(hloop_t* loop);
// 返回事件循环里激活的IO事件数量
uint32_t hloop_nios(hloop_t* loop);
// 返回事件循环里激活的定时器事件数量
uint32_t hloop_ntimers(hloop_t* loop);
// 返回事件循环里激活的空闲事件数量
uint32_t hloop_nidles(hloop_t* loop);
// 返回事件循环里激活的事件数量
uint32_t hloop_nactives(hloop_t* loop);
// 设置事件循环的用户数据
void hloop_set_userdata(hloop_t* loop, void* userdata);
// 获取事件循环的用户数据
void* hloop_userdata(hloop_t* loop);
// 投递事件
void hloop_post_event(hloop_t* loop, hevent_t* ev);
// 添加空闲事件
hidle_t* hidle_add(hloop_t* loop, hidle_cb cb, uint32_t repeat DEFAULT(INFINITE));
// 删除空闲事件
void hidle_del(hidle_t* idle);
// 添加超时定时器
htimer_t* htimer_add(hloop_t* loop, htimer_cb cb, uint32_t timeout_ms, uint32_t repeat DEFAULT(INFINITE));
// 添加时间定时器
htimer_t* htimer_add_period(hloop_t* loop, htimer_cb cb,
int8_t minute DEFAULT(0), int8_t hour DEFAULT(-1), int8_t day DEFAULT(-1),
int8_t week DEFAULT(-1), int8_t month DEFAULT(-1), uint32_t repeat DEFAULT(INFINITE));
// 删除定时器
void htimer_del(htimer_t* timer);
// 重置定时器
void htimer_reset(htimer_t* timer, uint32_t timeout_ms DEFAULT(0));
// 返回IO多路复用引擎 (select、poll、epoll、etc.)
const char* hio_engine();
// 获取IO对象
hio_t* hio_get(hloop_t* loop, int fd);
// 添加IO读写事件
int hio_add(hio_t* io, hio_cb cb, int events DEFAULT(HV_READ));
// 删除IO读写事件
int hio_del(hio_t* io, int events DEFAULT(HV_RDWR));
// 将IO对象从当前所属事件循环中剥离
void hio_detach(/*hloop_t* loop,*/ hio_t* io);
// 将IO对象关联到新的事件循环
void hio_attach(hloop_t* loop, hio_t* io);
// hio_detach 和 hio_attach 的示例代码见 examples/multi-thread/one-acceptor-multi-workers.c
/*
void new_conn_event(hevent_t* ev) {
hloop_t* loop = ev->loop;
hio_t* io = (hio_t*)hevent_userdata(ev);
// 关联到新的worker事件循环
hio_attach(loop, io);
}
void on_accpet(hio_t* io) {
// 从acceptor所在事件循环中剥离
hio_detach(io);
// 将新的连接按照负载均衡策略分发到worker线程
hloop_t* worker_loop = get_one_loop();
hevent_t ev;
memset(&ev, 0, sizeof(ev));
ev.loop = worker_loop;
ev.cb = new_conn_event;
ev.userdata = io;
hloop_post_event(worker_loop, &ev);
}
*/
// 判断fd是否存在于事件循环
bool hio_exists(hloop_t* loop, int fd);
// 返回一个唯一标示ID
uint32_t hio_id (hio_t* io);
// 返回文件描述符
int hio_fd (hio_t* io);
// 返回错误码
int hio_error (hio_t* io);
// 返回添加的事件
int hio_events (hio_t* io);
// 获取返回的事件
int hio_revents (hio_t* io);
// 返回IO类型
hio_type_e hio_type (hio_t* io);
// 返回本地地址
struct sockaddr* hio_localaddr(hio_t* io);
// 返回对端地址
struct sockaddr* hio_peeraddr (hio_t* io);
// 设置上下文
void hio_set_context(hio_t* io, void* ctx);
// 获取上下文
void* hio_context(hio_t* io);
// 是否已打开
bool hio_is_opened(hio_t* io);
// 是否已连接
bool hio_is_connected(hio_t* io);
// 是否已关闭
bool hio_is_closed(hio_t* io);
// 设置读缓存
void hio_set_readbuf(hio_t* io, void* buf, size_t len);
// 获取读缓存
hio_readbuf_t* hio_get_readbuf(hio_t* io);
// 设置最大读缓存
void hio_set_max_read_bufsize (hio_t* io, uint32_t size);
// 设置最大写缓存
void hio_set_max_write_bufsize(hio_t* io, uint32_t size);
// 获取当前写缓存大小
size_t hio_write_bufsize(hio_t* io);
// 判断是否写完成
#define hio_write_is_complete(io) (hio_write_bufsize(io) == 0)
// 获取最后读的时间
uint64_t hio_last_read_time(hio_t* io); // ms
// 获取最后写的时间
uint64_t hio_last_write_time(hio_t* io); // ms
// 设置accept回调
void hio_setcb_accept (hio_t* io, haccept_cb accept_cb);
// 设置连接回调
void hio_setcb_connect (hio_t* io, hconnect_cb connect_cb);
// 设置读回调
void hio_setcb_read (hio_t* io, hread_cb read_cb);
// 设置写回调
void hio_setcb_write (hio_t* io, hwrite_cb write_cb);
// 设置关闭回调
void hio_setcb_close (hio_t* io, hclose_cb close_cb);
// 获取accept回调
haccept_cb hio_getcb_accept(hio_t* io);
// 获取连接回调
hconnect_cb hio_getcb_connect(hio_t* io);
// 获取读回调
hread_cb hio_getcb_read(hio_t* io);
// 获取写回调
hwrite_cb hio_getcb_write(hio_t* io);
// 获取关闭回调
hclose_cb hio_getcb_close(hio_t* io);
// 开启SSL/TLS加密通信
int hio_enable_ssl(hio_t* io);
// 是否SSL/TLS加密通信
bool hio_is_ssl(hio_t* io);
// 设置SSL
int hio_set_ssl (hio_t* io, hssl_t ssl);
// 设置SSL_CTX
int hio_set_ssl_ctx(hio_t* io, hssl_ctx_t ssl_ctx);
// 新建SSL_CTX
int hio_new_ssl_ctx(hio_t* io, hssl_ctx_opt_t* opt);
// 获取SSL
hssl_t hio_get_ssl(hio_t* io);
// 获取SSL_CTX
hssl_ctx_t hio_get_ssl_ctx(hio_t* io);
// 设置主机名
int hio_set_hostname(hio_t* io, const char* hostname);
// 获取主机名
const char* hio_get_hostname(hio_t* io);
// 设置连接超时
void hio_set_connect_timeout(hio_t* io, int timeout_ms DEFAULT(HIO_DEFAULT_CONNECT_TIMEOUT));
// 设置关闭超时 (说明:非阻塞写队列非空时,需要等待写完成再关闭)
void hio_set_close_timeout(hio_t* io, int timeout_ms DEFAULT(HIO_DEFAULT_CLOSE_TIMEOUT));
// 设置读超时 (一段时间没有数据到来便自动关闭连接)
void hio_set_read_timeout(hio_t* io, int timeout_ms);
// 设置写超时 (一段时间没有数据发送便自动关闭连接)
void hio_set_write_timeout(hio_t* io, int timeout_ms);
// 设置keepalive超时 (一段时间没有数据收发便自动关闭连接)
void hio_set_keepalive_timeout(hio_t* io, int timeout_ms DEFAULT(HIO_DEFAULT_KEEPALIVE_TIMEOUT));
// 设置心跳 (定时发送心跳包)
typedef void (*hio_send_heartbeat_fn)(hio_t* io);
void hio_set_heartbeat(hio_t* io, int interval_ms, hio_send_heartbeat_fn fn);
// 接收连接
// hio_add(io, HV_READ) => accept => haccept_cb
int hio_accept (hio_t* io);
// 连接
// connect => hio_add(io, HV_WRITE) => hconnect_cb
int hio_connect(hio_t* io);
// 读
// hio_add(io, HV_READ) => read => hread_cb
int hio_read (hio_t* io);
// 开始读
#define hio_read_start(io) hio_read(io)
// 停止读
#define hio_read_stop(io) hio_del(io, HV_READ)
// 读一次
// hio_read_start => hread_cb => hio_read_stop
int hio_read_once (hio_t* io);
// 读取直到指定长度
// hio_read_once => hread_cb(len)
int hio_read_until_length(hio_t* io, unsigned int len);
// 读取直到遇到分隔符
// hio_read_once => hread_cb(...delim)
int hio_read_until_delim (hio_t* io, unsigned char delim);
// 读取一行
#define hio_readline(io) hio_read_until_delim(io, '\n')
// 读取字符串
#define hio_readstring(io) hio_read_until_delim(io, '\0')
// 读取N个字节
#define hio_readbytes(io, len) hio_read_until_length(io, len)
#define hio_read_until(io, len) hio_read_until_length(io, len)
// 写
// hio_try_write => hio_add(io, HV_WRITE) => write => hwrite_cb
int hio_write (hio_t* io, const void* buf, size_t len);
// 关闭
// hio_del(io, HV_RDWR) => close => hclose_cb
int hio_close (hio_t* io);
// 异步关闭 (投递一个close事件)
// NOTE: hloop_post_event(hio_close_event)
int hio_close_async(hio_t* io);
//------------------高等级的接口-------------------------------------------
// 读
// hio_get -> hio_set_readbuf -> hio_setcb_read -> hio_read
hio_t* hread (hloop_t* loop, int fd, void* buf, size_t len, hread_cb read_cb);
// 写
// hio_get -> hio_setcb_write -> hio_write
hio_t* hwrite (hloop_t* loop, int fd, const void* buf, size_t len, hwrite_cb write_cb DEFAULT(NULL));
// 关闭
// hio_get -> hio_close
void hclose (hloop_t* loop, int fd);
// tcp
// 接收连接
// hio_get -> hio_setcb_accept -> hio_accept
hio_t* haccept (hloop_t* loop, int listenfd, haccept_cb accept_cb);
// 连接
// hio_get -> hio_setcb_connect -> hio_connect
hio_t* hconnect (hloop_t* loop, int connfd, hconnect_cb connect_cb);
// 接收
// hio_get -> hio_set_readbuf -> hio_setcb_read -> hio_read
hio_t* hrecv (hloop_t* loop, int connfd, void* buf, size_t len, hread_cb read_cb);
// 发送
// hio_get -> hio_setcb_write -> hio_write
hio_t* hsend (hloop_t* loop, int connfd, const void* buf, size_t len, hwrite_cb write_cb DEFAULT(NULL));
// udp
// 设置IO类型
void hio_set_type(hio_t* io, hio_type_e type);
// 设置本地地址
void hio_set_localaddr(hio_t* io, struct sockaddr* addr, int addrlen);
// 设置对端地址
void hio_set_peeraddr (hio_t* io, struct sockaddr* addr, int addrlen);
// 接收
// hio_get -> hio_set_readbuf -> hio_setcb_read -> hio_read
hio_t* hrecvfrom (hloop_t* loop, int sockfd, void* buf, size_t len, hread_cb read_cb);
// 发送
// hio_get -> hio_setcb_write -> hio_write
hio_t* hsendto (hloop_t* loop, int sockfd, const void* buf, size_t len, hwrite_cb write_cb DEFAULT(NULL));
//-----------------顶层的接口---------------------------------------------
// 创建socket套接字返回IO对象
// @hio_create_socket: socket -> bind -> listen
// sockaddr_set_ipport -> socket -> hio_get(loop, sockfd) ->
// side == HIO_SERVER_SIDE ? bind ->
// type & HIO_TYPE_SOCK_STREAM ? listen ->
hio_t* hio_create_socket(hloop_t* loop, const char* host, int port,
hio_type_e type DEFAULT(HIO_TYPE_TCP),
hio_side_e side DEFAULT(HIO_SERVER_SIDE));
// @tcp_server: hio_create_socket(loop, host, port, HIO_TYPE_TCP, HIO_SERVER_SIDE) -> hio_setcb_accept -> hio_accept
// 创建TCP服务示例代码见 examples/tcp_echo_server.c
hio_t* hloop_create_tcp_server (hloop_t* loop, const char* host, int port, haccept_cb accept_cb);
// @tcp_client: hio_create_socket(loop, host, port, HIO_TYPE_TCP, HIO_CLIENT_SIDE) -> hio_setcb_connect -> hio_setcb_close -> hio_connect
// 创建TCP客户端示例代码见 examples/nc.c
hio_t* hloop_create_tcp_client (hloop_t* loop, const char* host, int port, hconnect_cb connect_cb, hclose_cb close_cb);
// @ssl_server: hio_create_socket(loop, host, port, HIO_TYPE_SSL, HIO_SERVER_SIDE) -> hio_setcb_accept -> hio_accept
// 创建SSL服务端示例代码见 examples/tcp_echo_server.c => #define TEST_SSL 1
hio_t* hloop_create_ssl_server (hloop_t* loop, const char* host, int port, haccept_cb accept_cb);
// @ssl_client: hio_create_socket(loop, host, port, HIO_TYPE_SSL, HIO_CLIENT_SIDE) -> hio_setcb_connect -> hio_setcb_close -> hio_connect
// 创建SSL客户端示例代码见 examples/nc.c => #define TEST_SSL 1
hio_t* hloop_create_ssl_client (hloop_t* loop, const char* host, int port, hconnect_cb connect_cb, hclose_cb close_cb);
// @udp_server: hio_create_socket(loop, host, port, HIO_TYPE_UDP, HIO_SERVER_SIDE)
// 创建UDP服务端示例代码见 examples/udp_echo_server.c
hio_t* hloop_create_udp_server (hloop_t* loop, const char* host, int port);
// @udp_server: hio_create_socket(loop, host, port, HIO_TYPE_UDP, HIO_CLIENT_SIDE)
// 创建UDP客户端示例代码见 examples/nc.c
hio_t* hloop_create_udp_client (hloop_t* loop, const char* host, int port);
//-----------------转发---------------------------------------------
// hio_read(io)
// hio_read(io->upstream_io)
void hio_read_upstream(hio_t* io);
// on_write(io) -> hio_write_is_complete(io) -> hio_read(io->upstream_io)
void hio_read_upstream_on_write_complete(hio_t* io, const void* buf, int writebytes);
// hio_write(io->upstream_io, buf, bytes)
void hio_write_upstream(hio_t* io, void* buf, int bytes);
// hio_close(io->upstream_io)
void hio_close_upstream(hio_t* io);
// io1->upstream_io = io2;
// io2->upstream_io = io1;
// 建立转发,示例代码见 examples/socks5_proxy_server.c
void hio_setup_upstream(hio_t* io1, hio_t* io2);
// @return io->upstream_io
hio_t* hio_get_upstream(hio_t* io);
// @tcp_upstream: hio_create_socket -> hio_setup_upstream -> hio_connect -> on_connect -> hio_read_upstream
// @return upstream_io
// 建立TCP转发示例代码见 examples/tcp_proxy_server.c
hio_t* hio_setup_tcp_upstream(hio_t* io, const char* host, int port, int ssl DEFAULT(0));
// 建立SSL转发
#define hio_setup_ssl_upstream(io, host, port) hio_setup_tcp_upstream(io, host, port, 1)
// @udp_upstream: hio_create_socket -> hio_setup_upstream -> hio_read_upstream
// @return upstream_io
// 建立UDP转发示例代码见 examples/udp_proxy_server.c
hio_t* hio_setup_udp_upstream(hio_t* io, const char* host, int port);
//-----------------拆包---------------------------------------------
// 拆包模式
typedef enum {
UNPACK_MODE_NONE = 0,
UNPACK_BY_FIXED_LENGTH = 1, // 固定长度拆包,不建议
UNPACK_BY_DELIMITER = 2, // 根据分隔符拆包,适用于文本协议
UNPACK_BY_LENGTH_FIELD = 3, // 根据头部长度字段拆包,适用于二进制协议
} unpack_mode_e;
// 拆包设置
typedef struct unpack_setting_s {
unpack_mode_e mode; // 拆包模式
unsigned int package_max_length; // 最大的包长
union {
// UNPACK_BY_FIXED_LENGTH: 固定长度拆包设置
struct {
unsigned int fixed_length; // 固定长度
};
// UNPACK_BY_DELIMITER: 分隔符拆包设置
struct {
unsigned char delimiter[PACKAGE_MAX_DELIMITER_BYTES]; // 分隔符
unsigned short delimiter_bytes; // 分隔符所占字节数
};
/*
* UNPACK_BY_LENGTH_FIELD: 头部长度字段拆包设置
*
* 包长 = 头部长度 + 数据长度 + 调整长度
* package_len = head_len + body_len + length_adjustment
*
* if (length_field_coding == ENCODE_BY_VARINT) head_len = body_offset + varint_bytes - length_field_bytes;
* else head_len = body_offset;
*
* 注意:头部长度字段的值仅代表数据长度,不包括头部本身长度,
* 如果你的头部长度字段代表总包长那么应该将length_adjustment设置为负的头部长度
* length_field stores body length, exclude head length,
* if length_field = head_len + body_len, then length_adjustment should be set to -head_len.
*
*/
struct {
unsigned short body_offset; // 到数据的偏移,通常等于头部长度
unsigned short length_field_offset; // 长度字段偏移
unsigned short length_field_bytes; // 长度字段所占字节数
short length_adjustment; // 调整长度
unpack_coding_e length_field_coding; // 长度字段编码方式
};
};
} unpack_setting_t;
/*
* 拆包示例代码见 examples/jsonrpc examples/protorpc
*
* 注意多个IO对象的unpack_setting_t可能是一样的所有hio_t里仅保存了unpack_setting_t的指针
* unpack_setting_t的生命周期应该被调用者所保证不应该使用局部变量。
*/
// 设置拆包
void hio_set_unpack(hio_t* io, unpack_setting_t* setting);
// 取消拆包设置
void hio_unset_unpack(hio_t* io);
// 拆包设置示例:
/*
// FTP协议通过\r\n分割符拆包
unpack_setting_t ftp_unpack_setting;
memset(&ftp_unpack_setting, 0, sizeof(unpack_setting_t));
ftp_unpack_setting.package_max_length = DEFAULT_PACKAGE_MAX_LENGTH;
ftp_unpack_setting.mode = UNPACK_BY_DELIMITER;
ftp_unpack_setting.delimiter[0] = '\r';
ftp_unpack_setting.delimiter[1] = '\n';
ftp_unpack_setting.delimiter_bytes = 2;
// MQTT协议通过头部长度字段拆包头部长度字段使用了varint编码
unpack_setting_t mqtt_unpack_setting = {
.mode = UNPACK_BY_LENGTH_FIELD,
.package_max_length = DEFAULT_PACKAGE_MAX_LENGTH,
.body_offset = 2,
.length_field_offset = 1,
.length_field_bytes = 1,
.length_field_coding = ENCODE_BY_VARINT,
};
*/
//-----------------重连----------------------------------------
// 重连设置
typedef struct reconn_setting_s {
uint32_t min_delay; // ms 重连最小延时
uint32_t max_delay; // ms 重连最大延时
uint32_t cur_delay; // ms 当前延时
/*
* @delay_policy: 延时策略
* 0: fixed 固定延时
* min_delay=3s => 3,3,3...
* 1: linear 线性增长延时
* min_delay=3s max_delay=10s => 3,6,9,10,10...
* other: exponential 指数增长延时
* min_delay=3s max_delay=60s delay_policy=2 => 3,6,12,24,48,60,60...
*/
uint32_t delay_policy; // 延时策略
uint32_t max_retry_cnt; // 最大重试次数
uint32_t cur_retry_cnt; // 当前重试次数
} reconn_setting_t;
// 重连设置初始化
void reconn_setting_init(reconn_setting_t* reconn);
// 重连设置重置
void reconn_setting_reset(reconn_setting_t* reconn);
// 增加重试次数并判断是否未超过最大重试次数
bool reconn_setting_can_retry(reconn_setting_t* reconn);
// 计算当前重连延时
uint32_t reconn_setting_calc_delay(reconn_setting_t* reconn);
//-----------------负载均衡-------------------------------------
// 负载均衡策略枚举
typedef enum {
LB_RoundRobin, // 轮询
LB_Random, // 随机
LB_LeastConnections,// 最少连接数
LB_IpHash, // IP hash
LB_UrlHash, // URL hash
} load_balance_e;
//-----------------可靠UDP---------------------------------------------
// 关闭可靠UDP
int hio_close_rudp(hio_t* io, struct sockaddr* peeraddr DEFAULT(NULL));
// KCP设置
typedef struct kcp_setting_s {
// ikcp_create(conv, ...)
unsigned int conv;
// ikcp_nodelay(kcp, nodelay, interval, fastresend, nocwnd)
int nodelay;
int interval;
int fastresend;
int nocwnd;
// ikcp_wndsize(kcp, sndwnd, rcvwnd)
int sndwnd;
int rcvwnd;
// ikcp_setmtu(kcp, mtu)
int mtu;
// ikcp_update
int update_interval;
} kcp_setting_t;
// KCP 正常模式
HV_INLINE void kcp_setting_init_with_normal_mode(kcp_setting_t* setting);
// KCP fast模式
void kcp_setting_init_with_fast_mode(kcp_setting_t* setting);
// KCP fast2模式
void kcp_setting_init_with_fast2_mode(kcp_setting_t* setting);
// KCP fast3模式
void kcp_setting_init_with_fast3_mode(kcp_setting_t* setting);
// 设置KCP示例代码见 examples/udp_echo_server.c => #define TEST_KCP 1
int hio_set_kcp(hio_t* io, kcp_setting_t* setting DEFAULT(NULL));
```
示例代码:
- 事件循环: [examples/hloop_test.c](../../examples/hloop_test.c)
- 定时器: [examples/htimer_test.c](../../examples/htimer_test.c)
- TCP回显服务: [examples/tcp_echo_server.c](../../examples/tcp_echo_server.c)
- TCP聊天服务: [examples/tcp_chat_server.c](../../examples/tcp_chat_server.c)
- TCP代理服务: [examples/tcp_proxy_server.c](../../examples/tcp_proxy_server.c)
- TCP客户端: [examples/tcp_client_test.c](../../examples/tcp_client_test.c)
- UDP回显服务: [examples/udp_echo_server.c](../../examples/udp_echo_server.c)
- UDP代理服务: [examples/udp_proxy_server.c](../../examples/udp_proxy_server.c)
- 网络客户端: [examples/nc](../../examples/nc.c)
- SOCKS5代理服务: [examples/socks5_proxy_server.c](../../examples/socks5_proxy_server.c)
- HTTP服务: [examples/tinyhttpd.c](../../examples/tinyhttpd.c)
- HTTP代理服务: [examples/tinyproxyd.c](../../examples/tinyproxyd.c)
- jsonRPC示例: [examples/jsonrpc](../../examples/jsonrpc)
- protobufRPC示例: [examples/protorpc](../../examples/protorpc)
多进程/多线程模式示例代码:
- 多accept进程模式: [examples/multi-thread/multi-acceptor-processes.c](../../examples/multi-thread/multi-acceptor-processes.c)
- 多accept线程模式: [examples/multi-thread/multi-acceptor-threads.c](../../examples/multi-thread/multi-acceptor-threads.c)
- 一个accept线程+多worker线程: [examples/multi-thread/one-acceptor-multi-workers.c](../../examples/multi-thread/one-acceptor-multi-workers.c)

View File

@ -0,0 +1,105 @@
//
// async_tcp_echo_server.cpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include <cstdlib>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
class session {
public:
session(boost::asio::io_service& io_service) :
socket_(io_service) {
}
tcp::socket& socket() {
return socket_;
}
void start() {
socket_.async_read_some(boost::asio::buffer(data_, max_length),
boost::bind(&session::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void handle_read(const boost::system::error_code& error,
size_t bytes_transferred) {
if (!error) {
boost::asio::async_write(socket_, boost::asio::buffer(data_,
bytes_transferred), boost::bind(&session::handle_write,
this, boost::asio::placeholders::error));
} else {
delete this;
}
}
void handle_write(const boost::system::error_code& error) {
if (!error) {
socket_.async_read_some(boost::asio::buffer(data_, max_length),
boost::bind(&session::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
} else {
delete this;
}
}
private:
tcp::socket socket_;
enum {
max_length = 1024
};
char data_[max_length];
};
class server {
public:
server(boost::asio::io_service& io_service, short port) :
io_service_(io_service), acceptor_(io_service, tcp::endpoint(tcp::v4(),
port)) {
session* new_session = new session(io_service_);
acceptor_.async_accept(new_session->socket(), boost::bind(
&server::handle_accept, this, new_session,
boost::asio::placeholders::error));
}
void handle_accept(session* new_session,
const boost::system::error_code& error) {
if (!error) {
new_session->start();
new_session = new session(io_service_);
acceptor_.async_accept(new_session->socket(), boost::bind(
&server::handle_accept, this, new_session,
boost::asio::placeholders::error));
} else {
delete new_session;
}
}
private:
boost::asio::io_service& io_service_;
tcp::acceptor acceptor_;
};
int main(int argc, char** argv) {
if (argc < 2) {
printf("Usage: cmd port\n");
return -10;
}
int port = atoi(argv[1]);
boost::asio::io_service io_service;
server s(io_service, port);
io_service.run();
return 0;
}

Some files were not shown because too many files have changed in this diff Show More