diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7a7069090..3b5c2a348 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -205,7 +205,7 @@ jobs: - name: Build uses: vmactions/freebsd-vm@v1 with: - release: '14.2' + release: '15.0' usesh: true sync: rsync copyback: false @@ -264,7 +264,7 @@ jobs: run: | echo Building on solaris... cmake -G "Unix Makefiles" -S . -Bbuild -DYASIO_SSL_BACKEND=2 - cmake --build build --config Release + cmake --build build --config Release --verbose echo run test icmp on solaris ... ./build/tests/icmp/icmptest echo run ssltest on solaris ... diff --git a/1k/1kiss.ps1 b/1k/1kiss.ps1 index 83aeb6230..d288749a1 100644 --- a/1k/1kiss.ps1 +++ b/1k/1kiss.ps1 @@ -219,7 +219,7 @@ $manifest = @{ # exactly match format: '14.42.*' msvc = '14.39+'; vs = '12.0+'; - ndk = 'r23c'; + ndk = 'r27d'; xcode = '13.0.0+'; # range # _EMIT_STL_ERROR(STL1000, "Unexpected compiler version, expected Clang xx.x.x or newer."); # clang-cl msvc14.37 require 16.0.0+ diff --git a/CMakeLists.txt b/CMakeLists.txt index 073f79628..1efa7f6f5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,7 +33,7 @@ cmake_dependent_option(YASIO_BUILD_LUA_EXAMPLE "Build lua example when we are the root project" ON "CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR" OFF) -option(YASIO_ENABLE_EXT_HTTP "Build yasio http extension" ON) +option(YASIO_ENABLE_EXT_HTTP "Build yasio http extension" OFF) option(YASIO_ENABLE_LUA "Build yasio with lua support" OFF) option(YASIO_ENABLE_AXLUA "Build yasio with axmol-lua support" OFF) option(YASIO_ENABLE_NI "Build yasio with native interface for interop" OFF) @@ -63,7 +63,7 @@ endif() # --- The C/C++ standard if (NOT MSVC) if (NOT CXX_STD) - set(CXX_STD 11) + set(CXX_STD 20) endif() set(CMAKE_CXX_STANDARD ${CXX_STD}) else() @@ -76,7 +76,7 @@ else() set(CXX_STD latest) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++latest") else() - set(CXX_STD 11) + set(CXX_STD 20) endif() endif() endif() @@ -499,7 +499,7 @@ if(YASIO_BUILD_TESTS) endif() if (NOT YASIO_NO_DEPS) add_subdirectory(examples/ftp_server) - add_subdirectory(tests/http) + # add_subdirectory(tests/http) endif() if (YASIO_SSL_BACKEND) add_subdirectory(tests/ssl) diff --git a/examples/ftp_server b/examples/ftp_server index 42746477b..cf822f511 160000 --- a/examples/ftp_server +++ b/examples/ftp_server @@ -1 +1 @@ -Subproject commit 42746477b8878deea016548980fc523e30236515 +Subproject commit cf822f51127780dffbbe39789a9fac5102e0a4ec diff --git a/extensions/yasio_http/.clang-format b/extensions/yasio_http/.clang-format deleted file mode 100644 index b0bbd2994..000000000 --- a/extensions/yasio_http/.clang-format +++ /dev/null @@ -1,64 +0,0 @@ -# This file is copy from https://github.com/google/angle with some modifications -# Defines the AXIS style for automatic reformatting. -# https://code.google.com/p/angleproject/wiki/CodingStandard -# See Clang docs: http://clang.llvm.org/docs/ClangFormatStyleOptions.html -BasedOnStyle: Chromium - -# Allow double brackets such as std::vector>. -Standard: Cpp11 - -# Indent 4 spaces at a time. -IndentWidth: 4 - -# Keep lines under 120 columns long. -ColumnLimit: 120 - -SortIncludes: false - -# Always break before braces -BreakBeforeBraces: Custom -BraceWrapping: - AfterCaseLabel: true - AfterClass: true - AfterControlStatement: true - AfterEnum: true - AfterFunction: true - AfterNamespace: true - AfterStruct: true - AfterUnion: true - BeforeCatch: true - BeforeElse: true - IndentBraces: false - SplitEmptyFunction: false - SplitEmptyRecord: false - SplitEmptyNamespace: false - - # Keeps extern "C" blocks unindented. - AfterExternBlock: false - -# Indent case labels. -IndentCaseLabels: false - -# Left-align pointers and references -PointerAlignment: Left - -# ANGLE likes to align things as much as possible. -AlignOperands: true -AlignConsecutiveAssignments: true - -# Use 0 space negative offset for access modifiers -AccessModifierOffset: -4 - -# TODO(jmadill): Decide if we want this on. Doesn't have an "all or none" mode. -AllowShortCaseLabelsOnASingleLine: false - -# Useful for spacing out functions in classes -KeepEmptyLinesAtTheStartOfBlocks: true - -# Indent nested PP directives. -IndentPPDirectives: AfterHash - -# Include blocks style -IncludeBlocks: Preserve -UseTab: Never -BreakConstructorInitializers: BeforeComma \ No newline at end of file diff --git a/extensions/yasio_http/CMakeLists.txt b/extensions/yasio_http/CMakeLists.txt deleted file mode 100644 index 10e5ead47..000000000 --- a/extensions/yasio_http/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ - -set(target_name yasio_http) - -FILE(GLOB_RECURSE YASIO_HTTP_SOURCES *.h;*.cpp;*.c) - -add_library(${target_name} STATIC ${YASIO_HTTP_SOURCES}) - -yasio_config_lib_depends(${target_name}) - -target_include_directories(${target_name} - PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}" - PUBLIC "${YASIO_ROOT}/thirdparty/llhttp/include" -) diff --git a/extensions/yasio_http/HttpClient.cpp b/extensions/yasio_http/HttpClient.cpp deleted file mode 100644 index 2fe75782a..000000000 --- a/extensions/yasio_http/HttpClient.cpp +++ /dev/null @@ -1,610 +0,0 @@ -/**************************************************************************** - Copyright (c) 2012 greathqy - Copyright (c) 2012 cocos2d-x.org - Copyright (c) 2013-2016 Chukong Technologies Inc. - Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - Copyright (c) 2019-present Axmol Engine contributors. - - https://axmolengine.github.io/ - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - ****************************************************************************/ - -#include "yasio_http/HttpClient.h" -#include -#include "yasio/yasio.hpp" - -using namespace yasio; - -namespace yasio_ext -{ - -namespace network -{ - -// Convert ASCII hex digit to a nibble (four bits, 0 - 15). -// -// Use unsigned to avoid signed overflow UB. -inline unsigned char hex2nibble(unsigned char c) -{ - if (c >= '0' && c <= '9') - { - return c - '0'; - } - else if (c >= 'a' && c <= 'f') - { - return 10 + (c - 'a'); - } - else if (c >= 'A' && c <= 'F') - { - return 10 + (c - 'A'); - } - return 0; -} - -// Convert a nibble ASCII hex digit -inline unsigned char nibble2hex(unsigned char c, unsigned char a = 'a') -{ - return ((c) < 0xa ? ((c) + '0') : ((c) + a - 10)); -} - -// Convert ASCII hex string (two characters) to byte. -// -// E.g., "0B" => 0x0B, "af" => 0xAF. -inline char hex2char(const char* p) -{ - return hex2nibble((uint8_t)p[0]) << 4 | hex2nibble(p[1]); -} - -// Convert byte to ASCII hex string (two characters). -inline char* char2hex(char* p, unsigned char c, unsigned char a = 'a') -{ - p[0] = nibble2hex(c >> 4, a); - p[1] = nibble2hex(c & (uint8_t)0xf, a); - return p; -} - -std::string HttpClient::urlEncode(cxx17::string_view s) -{ - std::string encoded; - if (!s.empty()) - { - encoded.reserve(s.length() * 3 / 2); - for (const char c : s) - { - if (isalnum((uint8_t)c) || c == '-' || c == '_' || c == '.' || c == '~') - { - encoded.push_back(c); - } - else - { - encoded.push_back('%'); - - char hex[2]; - encoded.append(char2hex(hex, c, 'A'), sizeof(hex)); - } - } - } - return encoded; -} - -std::string HttpClient::urlDecode(cxx17::string_view st) -{ - std::string decoded; - if (!st.empty()) - { - const char* s = st.data(); - const size_t length = st.length(); - decoded.reserve(length * 2 / 3); - for (unsigned int i = 0; i < length; ++i) - { - if (!s[i]) - break; - - if (s[i] == '%') - { - decoded.push_back(hex2char(s + i + 1)); - i = i + 2; - } - else if (s[i] == '+') - { - decoded.push_back(' '); - } - else - { - decoded.push_back(s[i]); - } - } - } - return decoded; -} - -static HttpClient* _httpClient = nullptr; // pointer to singleton - -template -static void __clearQueueUnsafe(_Cont& queue, _Fty pred) -{ - for (auto it = queue.unsafe_begin(); it != queue.unsafe_end();) - { - if (!pred || pred((*it))) - { - (*it)->release(); - it = queue.unsafe_erase(it); - } - else - { - ++it; - } - } -} - -// HttpClient implementation -HttpClient* HttpClient::getInstance() -{ - if (_httpClient == nullptr) - { - _httpClient = new HttpClient(); - } - - return _httpClient; -} - -void HttpClient::destroyInstance() -{ - if (nullptr == _httpClient) - { - YASIO_LOG("HttpClient singleton is nullptr"); - return; - } - - YASIO_LOG("HttpClient::destroyInstance begin"); - delete _httpClient; - _httpClient = nullptr; - - YASIO_LOG("HttpClient::destroyInstance() finished!"); -} - -void HttpClient::enableCookies(cxx17::string_view cookieFile) -{ - std::lock_guard lock(_cookieFileMutex); - cxx17::assign(_cookieFilename, cookieFile); - - if (!_cookie) - _cookie = new HttpCookie(); - _cookie->setCookieFileName(_cookieFilename); - _cookie->readFile(); -} - -void HttpClient::setSSLVerification(cxx17::string_view caFile) -{ - std::lock_guard lock(_sslCaFileMutex); - cxx17::assign(_sslCaFilename, caFile); - _service->set_option(yasio::YOPT_S_SSL_CACERT, _sslCaFilename.c_str()); -} - -HttpClient::HttpClient() - : _isInited(false) - , _dispatchOnWorkThread(false) - , _timeoutForConnect(30) - , _timeoutForRead(60) - , _cookie(nullptr) - , _clearResponsePredicate(nullptr) -{ - _service = new yasio::io_service(HttpClient::MAX_CHANNELS); - _service->set_option(yasio::YOPT_S_FORWARD_PACKET, 1); // forward packet immediately when got data from OS kernel - _service->set_option(yasio::YOPT_S_DNS_QUERIES_TIMEOUT, 3); - _service->set_option(yasio::YOPT_S_DNS_QUERIES_TRIES, 1); - _service->start([=](yasio::event_ptr&& e) { handleNetworkEvent(e.get()); }); - - for (int i = 0; i < HttpClient::MAX_CHANNELS; ++i) - { - _availChannelQueue.unsafe_push_back(i); - } - - _isInited = true; -} - -HttpClient::~HttpClient() -{ - delete _service; - - clearPendingResponseQueue(); - clearFinishedResponseQueue(); - if (_cookie) - { - _cookie->writeFile(); - delete _cookie; - } -} - -void HttpClient::setDispatchOnWorkThread(bool bVal) -{ - _dispatchOnWorkThread = bVal; -} - -// Poll and notify main thread if responses exists in queue -void HttpClient::dispatch() -{ - if (_finishedResponseQueue.unsafe_empty()) - return; - - auto lck = _finishedResponseQueue.get_lock(); - if (!_finishedResponseQueue.unsafe_empty()) - { - HttpResponse* response = _finishedResponseQueue.front(); - _finishedResponseQueue.unsafe_pop_front(); - lck.unlock(); - invokeResposneCallbackAndRelease(response); - } -} -void HttpClient::handleNetworkStatusChanged() -{ - _service->set_option(YOPT_S_DNS_DIRTY, 1); -} - -void HttpClient::setNameServers(cxx17::string_view servers) -{ - _service->set_option(YOPT_S_DNS_LIST, servers.data()); -} - -yasio::io_service* HttpClient::getInternalService() -{ - return _service; -} - -bool HttpClient::send(HttpRequest* request) -{ - if (!request) - return false; - - auto response = new HttpResponse(request); - response->setLocation(request->getUrl(), false); - processResponse(response, -1); - response->release(); - return true; -} - -HttpResponse* HttpClient::sendSync(HttpRequest* request) -{ - request->setSync(true); - if (this->send(request)) - return request->wait(); - return nullptr; -} - -int HttpClient::tryTakeAvailChannel() -{ - auto lck = _availChannelQueue.get_lock(); - if (!_availChannelQueue.empty()) - { - int channel = _availChannelQueue.front(); - _availChannelQueue.pop_front(); - return channel; - } - return -1; -} - -void HttpClient::processResponse(HttpResponse* response, int channelIndex) -{ - response->retain(); - - if (response->validateUri()) - { - if (channelIndex == -1) - channelIndex = tryTakeAvailChannel(); - - if (channelIndex != -1) - { - auto channelHandle = _service->channel_at(channelIndex); - - auto& requestUri = response->getRequestUri(); - - channelHandle->ud_.ptr = response; - _service->set_option(YOPT_C_REMOTE_ENDPOINT, channelIndex, requestUri.getHost().data(), - (int)requestUri.getPort()); - if (requestUri.isSecure()) - _service->open(channelIndex, YCK_SSL_CLIENT); - else - _service->open(channelIndex, YCK_TCP_CLIENT); - } - else - _pendingResponseQueue.push_back(response); - } - else - finishResponse(response); -} - -void HttpClient::handleNetworkEvent(yasio::io_event* event) -{ - int channelIndex = event->cindex(); - auto channel = _service->channel_at(event->cindex()); - HttpResponse* response = (HttpResponse*)channel->ud_.ptr; - assert(response); - - bool responseFinished = response->isFinished(); - switch (event->kind()) - { - case YEK_ON_PACKET: - if (!responseFinished) - { - auto&& pkt = event->packet_view(); - response->handleInput(pkt.data(), pkt.size()); - } - if (response->isFinished()) - { - response->updateInternalCode(yasio::errc::eof); - _service->close(event->cindex()); - } - break; - case YEK_ON_OPEN: - if (event->status() == 0) - { - obstream obs; - bool usePostData = false; - auto request = response->getHttpRequest(); - switch (request->getRequestType()) - { - case HttpRequest::Type::Get: - obs.write_bytes("GET"); - break; - case HttpRequest::Type::Post: - obs.write_bytes("POST"); - usePostData = true; - break; - case HttpRequest::Type::Delete: - obs.write_bytes("DELETE"); - break; - case HttpRequest::Type::Put: - obs.write_bytes("PUT"); - usePostData = true; - break; - default: - obs.write_bytes("GET"); - break; - } - obs.write_bytes(" "); - - auto& uri = response->getRequestUri(); - obs.write_bytes(uri.getPathEtc()); - - obs.write_bytes(" HTTP/1.1\r\n"); - - obs.write_bytes("Host: "); - obs.write_bytes(uri.getHost()); - obs.write_bytes("\r\n"); - - // process custom headers - struct HeaderFlag - { - enum - { - UESR_AGENT = 1, - CONTENT_TYPE = 1 << 1, - ACCEPT = 1 << 2, - }; - }; - int headerFlags = 0; - auto& headers = request->getHeaders(); - if (!headers.empty()) - { - for (auto& header : headers) - { - obs.write_bytes(header); - obs.write_bytes("\r\n"); - - if (cxx20::ic::starts_with(cxx17::string_view{header}, _mksv("User-Agent:"))) - headerFlags |= HeaderFlag::UESR_AGENT; - else if (cxx20::ic::starts_with(cxx17::string_view{header}, _mksv("Content-Type:"))) - headerFlags |= HeaderFlag::CONTENT_TYPE; - else if (cxx20::ic::starts_with(cxx17::string_view{header}, _mksv("Accept:"))) - headerFlags |= HeaderFlag::ACCEPT; - } - } - - if (_cookie) - { - auto cookies = _cookie->checkAndGetFormatedMatchCookies(uri); - if (!cookies.empty()) - { - obs.write_bytes("Cookie: "); - obs.write_bytes(cookies); - } - } - - if (!(headerFlags & HeaderFlag::UESR_AGENT)) - obs.write_bytes("User-Agent: yasio-http\r\n"); - - if (!(headerFlags & HeaderFlag::ACCEPT)) - obs.write_bytes("Accept: */*;q=0.8\r\n"); - - if (usePostData) - { - if (!(headerFlags & HeaderFlag::CONTENT_TYPE)) - obs.write_bytes("Content-Type: application/x-www-form-urlencoded;charset=UTF-8\r\n"); - - char strContentLength[128] = {0}; - auto requestData = request->getRequestData(); - auto requestDataSize = request->getRequestDataSize(); - snprintf(strContentLength, sizeof(strContentLength), "Content-Length: %d\r\n\r\n", - static_cast(requestDataSize)); - obs.write_bytes(strContentLength); - - if (requestData && requestDataSize > 0) - obs.write_bytes(cxx17::string_view{requestData, static_cast(requestDataSize)}); - } - else - { - obs.write_bytes("\r\n"); - } - - _service->write(event->transport(), std::move(obs.buffer())); - - auto& timerForRead = channel->get_user_timer(); - timerForRead.cancel(); - timerForRead.expires_from_now(std::chrono::seconds(this->_timeoutForRead)); - timerForRead.async_wait([=](io_service& s) { - response->updateInternalCode(yasio::errc::read_timeout); - s.close(channelIndex); // timeout - return true; - }); - } - else - { - handleNetworkEOF(response, channel, event->status()); - } - break; - case YEK_ON_CLOSE: - handleNetworkEOF(response, channel, event->status()); - break; - } -} - -void HttpClient::handleNetworkEOF(HttpResponse* response, yasio::io_channel* channel, int internalErrorCode) -{ - channel->ud_.ptr = nullptr; - - channel->get_user_timer().cancel(); - response->updateInternalCode(internalErrorCode); - auto responseCode = response->getResponseCode(); - switch (responseCode) - { - case 301: - case 302: - case 307: - if (response->tryRedirect()) - { - processResponse(response, channel->index()); - response->release(); - break; - } - default: - finishResponse(response); - - // try process pending response - auto lck = _pendingResponseQueue.get_lock(); - if (!_pendingResponseQueue.unsafe_empty()) - { - auto pendingResponse = _pendingResponseQueue.unsafe_front(); - _pendingResponseQueue.unsafe_pop_front(); - lck.unlock(); - - processResponse(pendingResponse, channel->index()); - pendingResponse->release(); - } - else - { // recycle channel - _availChannelQueue.push_front(channel->index()); - } - } -} - -void HttpClient::finishResponse(HttpResponse* response) -{ - auto request = response->getHttpRequest(); - auto syncState = request->getSyncState(); - - if (_cookie) - { - auto cookieRange = response->getResponseHeaders().equal_range("set-cookie"); - for (auto cookieIt = cookieRange.first; cookieIt != cookieRange.second; ++cookieIt) - _cookie->updateOrAddCookie(cookieIt->second, response->_requestUri); - } - - if (!syncState) - { - if (_dispatchOnWorkThread) // checking does sendRequest caller thread? - invokeResposneCallbackAndRelease(response); - else - _finishedResponseQueue.push_back(response); - } - else - { - syncState->set_value(response); - } -} - -void HttpClient::invokeResposneCallbackAndRelease(HttpResponse* response) -{ - HttpRequest* request = response->getHttpRequest(); - const ccHttpRequestCallback& callback = request->getCallback(); - - if (callback != nullptr) - callback(this, response); - - response->release(); -} - -void HttpClient::clearResponseQueue() -{ - clearPendingResponseQueue(); - clearFinishedResponseQueue(); -} - -void HttpClient::clearPendingResponseQueue() -{ - auto YASIO__UNUSED lck = _pendingResponseQueue.get_lock(); - __clearQueueUnsafe(_pendingResponseQueue, ClearResponsePredicate{}); -} - -void HttpClient::clearFinishedResponseQueue() -{ - auto YASIO__UNUSED lck = _finishedResponseQueue.get_lock(); - __clearQueueUnsafe(_finishedResponseQueue, ClearResponsePredicate{}); -} - -void HttpClient::setTimeoutForConnect(int value) -{ - std::lock_guard lock(_timeoutForConnectMutex); - _timeoutForConnect = value; - _service->set_option(YOPT_S_CONNECT_TIMEOUT, value); -} - -int HttpClient::getTimeoutForConnect() -{ - std::lock_guard lock(_timeoutForConnectMutex); - return _timeoutForConnect; -} - -void HttpClient::setTimeoutForRead(int value) -{ - std::lock_guard lock(_timeoutForReadMutex); - _timeoutForRead = value; -} - -int HttpClient::getTimeoutForRead() -{ - std::lock_guard lock(_timeoutForReadMutex); - return _timeoutForRead; -} - -cxx17::string_view HttpClient::getCookieFilename() -{ - std::lock_guard lock(_cookieFileMutex); - return _cookieFilename; -} - -cxx17::string_view HttpClient::getSSLVerification() -{ - std::lock_guard lock(_sslCaFileMutex); - return _sslCaFilename; -} - -} // namespace network - -} // namespace yasio_ext diff --git a/extensions/yasio_http/HttpClient.h b/extensions/yasio_http/HttpClient.h deleted file mode 100644 index c793ecab0..000000000 --- a/extensions/yasio_http/HttpClient.h +++ /dev/null @@ -1,271 +0,0 @@ -/**************************************************************************** - Copyright (c) 2012 greathqy - Copyright (c) 2012 cocos2d-x.org - Copyright (c) 2013-2016 Chukong Technologies Inc. - Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - Copyright (c) 2019-present Axmol Engine contributors. - - https://axmolengine.github.io/ - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - ****************************************************************************/ - -#ifndef YASIO__EXT_HTTPCLIENT_H -#define YASIO__EXT_HTTPCLIENT_H - -#include -#include - -#include "yasio_http/HttpRequest.h" -#include "yasio_http/HttpResponse.h" -#include "yasio_http/HttpCookie.h" -#include "yasio_http/Uri.h" -#include "yasio_http/utils/concurrent_deque.h" - -#include "yasio/yasio_fwd.hpp" -#include "yasio/string_view.hpp" - -/** - * @addtogroup network - * @{ - */ - -namespace yasio_ext -{ - -namespace network -{ -/** Singleton that handles asynchronous http requests. - * - * Once the request completed, a callback will issued in main thread when it provided during make request. - * - * @lua NA - */ -class HttpClient -{ -public: - /** - * How many requests could be perform concurrency. - */ - static const int MAX_CHANNELS = 21; - - /** - * Get instance of HttpClient. - * - * @return the instance of HttpClient. - */ - static HttpClient* getInstance(); - - /** - * Release the instance of HttpClient. - */ - static void destroyInstance(); - - /** - * Enable cookie support. - * - * @param cookieFile the filepath of cookie file, must be writable full path - */ - void enableCookies(cxx17::string_view cookieFile); - - /** - * Get the cookie filename - * - * @return the cookie filename - */ - cxx17::string_view getCookieFilename(); - - /** - * Set root certificate path for SSL verification. - * - * @param caFile a full path of root certificate.if it is empty, SSL verification is disabled. - */ - void setSSLVerification(cxx17::string_view caFile); - - /** - * Get the ssl CA filename - * - * @return the ssl CA filename - */ - cxx17::string_view getSSLVerification(); - - /** - * Send http request concurrently, non-blocking - * - * @param request a HttpRequest object, which includes url, response callback etc. - please make sure request->_requestData is clear before calling "send" here. - * @notes for post data - * a. By default will fill Content-Type: application/x-www-form-urlencoded;charset=UTF-8 - * b. You can specific content-type at custom header, such as: - * std::vector headers = {"Content-Type: application/json;charset=UTF-8"}; - * request->setHeaders(headers); - * c. other content type, please see: - * https://stackoverflow.com/questions/23714383/what-are-all-the-possible-values-for-http-content-type-header - */ - bool send(HttpRequest* request); - - /** - * Send http request sync, will block caller thread until request finished. - * @remark Caller must call release manually when the response never been used. - */ - HttpResponse* sendSync(HttpRequest* request); - - /** - * Set the timeout value for connecting. - * - * @param value the timeout value for connecting. - */ - void setTimeoutForConnect(int value); - - /** - * Get the timeout value for connecting. - * - * @return int the timeout value for connecting. - */ - int getTimeoutForConnect(); - - /** - * Set the timeout value for reading. - * - * @param value the timeout value for reading. - */ - void setTimeoutForRead(int value); - - /** - * Get the timeout value for reading. - * - * @return int the timeout value for reading. - */ - int getTimeoutForRead(); - - HttpCookie* getCookie() const { return _cookie; } - - std::recursive_mutex& getCookieFileMutex() { return _cookieFileMutex; } - - std::recursive_mutex& getSSLCaFileMutex() { return _sslCaFileMutex; } - - typedef std::function ClearResponsePredicate; - - /** - * Clears the pending & finished http response - */ - void clearResponseQueue(); - - /** - * Clears the pending http response - */ - void clearPendingResponseQueue(); - - /** - * Clears the finished http response - */ - void clearFinishedResponseQueue(); - - /** - Sets a predicate function that is going to be called to determine if we proceed - * each of the pending requests - * - * @param cb predicate function that will be called - */ - void setClearResponsePredicate(ClearResponsePredicate predicate) { _clearResponsePredicate = predicate; } - - void setDispatchOnWorkThread(bool bVal); - bool isDispatchOnWorkThread() const { return _dispatchOnWorkThread; } - - /* - * When the device network status chagned, you should invoke this function - */ - void handleNetworkStatusChanged(); - - /* - * Sets custom dns server list: - * format: "xxx.xxx.xxx.xxx[:port],xxx.xxx.xxx.xxx[:port] - */ - void setNameServers(cxx17::string_view servers); - - yasio::io_service* getInternalService(); - - /* - * If setDispatchOnWorkThread(false), you needs to invoke this API to dispatch http response - * on the caller thread - */ - void dispatch(); - - /* - * The urlEncode API - * Perform urlEncode for query params always - * Perform urlEncode for post data when content-type is: application/x-www-form-urlencoded - */ - static std::string urlEncode(cxx17::string_view s); - - static std::string urlDecode(cxx17::string_view st); - -private: - HttpClient(); - virtual ~HttpClient(); - - void processResponse(HttpResponse* response, int channelIndex); - - int tryTakeAvailChannel(); - - void handleNetworkEvent(yasio::io_event* event); - - void handleNetworkEOF(HttpResponse* response, yasio::io_channel* channel, int internalErrorCode); - - void finishResponse(HttpResponse* response); - - void invokeResposneCallbackAndRelease(HttpResponse* response); - -private: - bool _isInited; - - yasio::io_service* _service; - - bool _dispatchOnWorkThread; - - int _timeoutForConnect; - std::recursive_mutex _timeoutForConnectMutex; - - int _timeoutForRead; - std::recursive_mutex _timeoutForReadMutex; - - concurrent_deque _pendingResponseQueue; - concurrent_deque _finishedResponseQueue; - - concurrent_deque _availChannelQueue; - - std::string _cookieFilename; - std::recursive_mutex _cookieFileMutex; - - std::string _sslCaFilename; - std::recursive_mutex _sslCaFileMutex; - - HttpCookie* _cookie; - - ClearResponsePredicate _clearResponsePredicate; -}; - -} // namespace network - -} // namespace yasio_ext - -// end group -/// @} - -#endif //__CCHTTPCLIENT_H__ diff --git a/extensions/yasio_http/HttpCookie.cpp b/extensions/yasio_http/HttpCookie.cpp deleted file mode 100644 index a60890b4d..000000000 --- a/extensions/yasio_http/HttpCookie.cpp +++ /dev/null @@ -1,300 +0,0 @@ -/**************************************************************************** - Copyright (c) 2013-2016 Chukong Technologies Inc. - Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - Copyright (c) 2019-present Axmol Engine contributors. - - https://axmolengine.github.io/ - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - ****************************************************************************/ - -#include "yasio_http/HttpCookie.h" -#include "yasio_http/Uri.h" -#include "yasio/utils.hpp" - -#include "yasio/split.hpp" -#include "yasio/file.hpp" - -#include -#include -#include -#include -#include -#include - -namespace yasio_ext -{ - -namespace network -{ -void HttpCookie::readFile() -{ - enum - { - DOMAIN_INDEX = 0, - TAILMATCH_INDEX, - PATH_INDEX, - SECURE_INDEX, - EXPIRES_INDEX, - NAME_INDEX, - VALUE_INDEX, - }; - - auto content = yasio::read_text_file(_cookieFileName); - if (content.size() > 1) - { - yasio::split_n(content.data(), content.size() - 1, '\n', [this](char* s, char* e) { - if (*s == '#') // skip comment - return; - int count = 0; - CookieInfo cookieInfo; - yasio::split_n(s, e - s, '\t', [&, this](char* ss, char* ee) { - auto ch = *ee; // store - *ee = '\0'; - switch (count) - { - case DOMAIN_INDEX: - cookieInfo.domain.assign(ss, ee - ss); - break; - case PATH_INDEX: - cookieInfo.path.assign(ss, ee - ss); - break; - case SECURE_INDEX: - cookieInfo.secure = cxx17::string_view{ss, (size_t)(ee - ss)} == _mksv("TRUE"); - break; - case EXPIRES_INDEX: - cookieInfo.expires = static_cast(strtoll(ss, nullptr, 10)); - break; - case NAME_INDEX: - cookieInfo.name.assign(ss, ee - ss); - break; - case VALUE_INDEX: - cookieInfo.value.assign(ss, ee - ss); - break; - } - *ee = ch; // restore - ++count; - }); - if (count >= 7) - _cookies.push_back(std::move(cookieInfo)); - }); - } -} - -const std::vector* HttpCookie::getCookies() const -{ - return &_cookies; -} - -const CookieInfo* HttpCookie::getMatchCookie(const Uri& uri) const -{ - for (auto& cookie : _cookies) - { - if (cxx20::ends_with(uri.getHost(), cookie.domain) && cxx20::starts_with(uri.getPath(), cookie.path)) - return &cookie; - } - - return nullptr; -} - -void HttpCookie::updateOrAddCookie(CookieInfo* cookie) -{ - for (auto& _cookie : _cookies) - { - if (cookie->isSame(_cookie)) - { - _cookie.updateValue(*cookie); - return; - } - } - _cookies.push_back(std::move(*cookie)); -} - -std::string HttpCookie::checkAndGetFormatedMatchCookies(const Uri& uri) -{ - std::string ret; - for (auto iter = _cookies.begin(); iter != _cookies.end();) - { - auto& cookie = *iter; - if (cxx20::ends_with(uri.getHost(), cookie.domain) && cxx20::starts_with(uri.getPath(), cookie.path)) - { - if (yasio::time_now() >= cookie.expires) - { - iter = _cookies.erase(iter); - continue; - } - - if (!ret.empty()) - ret += "; "; - - ret += cookie.name; - ret.push_back('='); - ret += cookie.value; - } - ++iter; - } - return ret; -} - -bool HttpCookie::updateOrAddCookie(const cxx17::string_view& cookie, const Uri& uri) -{ - unsigned int count = 0; - CookieInfo info; - yasio::split_n(cookie.data(), cookie.length(), ';', [&](const char* start, const char* end) { - unsigned int count_ = 0; - while (*start == ' ') - ++start; // skip ws - if (++count > 1) - { - cxx17::string_view key; - cxx17::string_view value; - yasio::split_n(start, end - start, '=', [&](const char* s, const char* e) { - switch (++count_) - { - case 1: - key = cxx17::string_view(s, e - s); - break; - case 2: - value = cxx17::string_view(s, e - s); - break; - } - }); - - using namespace cxx17; - if (cxx20::ic::iequals(key, _mksv("domain"))) - { - if (!value.empty()) - info.domain.assign(value.data(), value.length()); - } - else if (cxx20::ic::iequals(key, _mksv("path"))) - { - if (!value.empty()) - info.path.assign(value.data(), value.length()); - } - else if (cxx20::ic::iequals(key, _mksv("expires"))) - { - std::string expires_ctime(!value.empty() ? value.data() : "", value.length()); - if (cxx20::ends_with(expires_ctime, _mksv(" GMT"))) - expires_ctime.resize(expires_ctime.length() - sizeof(" GMT") + 1); - if (expires_ctime.empty()) - return; - size_t off = 0; - auto p = expires_ctime.find_first_of(','); - if (p != std::string::npos) - { - p = expires_ctime.find_first_not_of(' ', p + 1); // skip ws - if (p != std::string::npos) - off = p; - } - - struct tm dt = {0}; - std::stringstream ss(&expires_ctime[off]); - ss >> std::get_time(&dt, "%d %b %Y %H:%M:%S"); - if (!ss.fail()) - info.expires = mktime(&dt); - else - { - ss.str(""); - ss.clear(); - ss << (&expires_ctime[off]); - ss >> std::get_time(&dt, "%d-%b-%Y %H:%M:%S"); - if (!ss.fail()) - info.expires = mktime(&dt); - } - } - else if (cxx20::ic::iequals(key, _mksv("secure"))) - { - info.secure = true; - } - } - else - { // first is cookie name - yasio::split_n(start, end - start, '=', [&](const char* s, const char* e) { - switch (++count_) - { - case 1: - info.name.assign(s, e - s); - break; - case 2: - info.value.assign(s, e - s); - break; - } - }); - } - }); - if (info.path.empty()) - info.path.push_back('/'); - - if (info.domain.empty()) - cxx17::append(info.domain, uri.getHost()); - - if (info.expires <= 0) - info.expires = (std::numeric_limits::max)(); - - updateOrAddCookie(&info); - return true; -} - -void HttpCookie::writeFile() -{ - FILE* out; - out = fopen(_cookieFileName.c_str(), "wb"); - fputs( - "# Netscape HTTP Cookie File\n" - "# http://curl.haxx.se/docs/http-cookies.html\n" - "# This file was generated by yasio_http! Edit at your own risk.\n" - "# Test yasio_http cookie write.\n\n", - out); - - std::string line; - - char expires[32] = {0}; // LONGLONG_STRING_SIZE=20 - for (auto& cookie : _cookies) - { - line.clear(); - line.append(cookie.domain); - line.append(1, '\t'); - cookie.tailmatch ? line.append("TRUE") : line.append("FALSE"); - line.append(1, '\t'); - line.append(cookie.path); - line.append(1, '\t'); - cookie.secure ? line.append("TRUE") : line.append("FALSE"); - line.append(1, '\t'); - snprintf(expires, sizeof(expires), "%lld", static_cast(cookie.expires)); - line.append(expires); - line.append(1, '\t'); - line.append(cookie.name); - line.append(1, '\t'); - line.append(cookie.value); - // line.append(1, '\n'); - - fprintf(out, "%s\n", line.c_str()); - } - - fclose(out); -} - -void HttpCookie::setCookieFileName(cxx17::string_view filename) -{ - cxx17::append(_cookieFileName, filename); -} - -} // namespace network - -} // namespace yasio_ext diff --git a/extensions/yasio_http/HttpCookie.h b/extensions/yasio_http/HttpCookie.h deleted file mode 100644 index 38c697ec1..000000000 --- a/extensions/yasio_http/HttpCookie.h +++ /dev/null @@ -1,123 +0,0 @@ -/**************************************************************************** - Copyright (c) 2013-2016 Chukong Technologies Inc. - Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - Copyright (c) 2019-present Axmol Engine contributors. - - https://axmolengine.github.io/ - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - ****************************************************************************/ - -#ifndef YASIO_EXT_HTTP_COOKIE_H -#define YASIO_EXT_HTTP_COOKIE_H -/// @cond DO_NOT_SHOW - -#include -#include -#include - -#include "yasio/string_view.hpp" - -namespace yasio_ext -{ - -namespace network -{ -#if YASIO__HAS_CXX14 -using namespace cxx17::string_view_literals; -# define _mksv(a) a ""_sv -#else -template -inline cxx17::string_view _mksv(const char (&str)[_Size]) -{ - return cxx17::string_view{str, _Size - 1}; -} -#endif - -class Uri; -struct CookieInfo -{ - CookieInfo() = default; - CookieInfo(const CookieInfo&) = default; - CookieInfo(CookieInfo&& rhs) - : domain(std::move(rhs.domain)) - , tailmatch(rhs.tailmatch) - , path(std::move(rhs.path)) - , secure(rhs.secure) - , name(std::move(rhs.name)) - , value(std::move(rhs.value)) - , expires(rhs.expires) - {} - - CookieInfo& operator=(CookieInfo&& rhs) - { - domain = std::move(rhs.domain); - tailmatch = rhs.tailmatch; - path = std::move(rhs.path); - secure = rhs.secure; - name = std::move(rhs.name); - value = std::move(rhs.value); - expires = rhs.expires; - return *this; - } - - bool isSame(const CookieInfo& rhs) { return name == rhs.name && domain == rhs.domain; } - - void updateValue(const CookieInfo& rhs) - { - value = rhs.value; - expires = rhs.expires; - path = rhs.path; - } - - std::string domain; - bool tailmatch = true; - std::string path; - bool secure = false; - std::string name; - std::string value; - time_t expires = 0; -}; - -class HttpCookie -{ -public: - void readFile(); - - void writeFile(); - void setCookieFileName(cxx17::string_view fileName); - - const std::vector* getCookies() const; - const CookieInfo* getMatchCookie(const Uri& uri) const; - void updateOrAddCookie(CookieInfo* cookie); - - // Check match cookies for http request - std::string checkAndGetFormatedMatchCookies(const Uri& uri); - bool updateOrAddCookie(const cxx17::string_view& cookie, const Uri& uri); - -private: - std::string _cookieFileName; - std::vector _cookies; -}; - -} // namespace network -} // namespace yasio_ext - -/// @endcond -#endif /* HTTP_COOKIE_H */ diff --git a/extensions/yasio_http/HttpRequest.h b/extensions/yasio_http/HttpRequest.h deleted file mode 100644 index 8828a2309..000000000 --- a/extensions/yasio_http/HttpRequest.h +++ /dev/null @@ -1,279 +0,0 @@ -/**************************************************************************** - Copyright (c) 2010-2012 cocos2d-x.org - Copyright (c) 2013-2016 Chukong Technologies Inc. - Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - Copyright (c) 2019-present Axmol Engine contributors. - - https://axmolengine.github.io/ - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - ****************************************************************************/ - -#ifndef __YASIO_EXT_HTTP_REQUEST_H__ -#define __YASIO_EXT_HTTP_REQUEST_H__ - -#include -#include -#include -#include -#include -#include - -#include "yasio/byte_buffer.hpp" -#include "yasio/string_view.hpp" - -/** - * @addtogroup network - * @{ - */ - -namespace yasio_ext -{ - -namespace network -{ - -class HttpClient; -class HttpResponse; - -class TSRefCountedObject -{ - -public: - TSRefCountedObject() { _refCount = 1; } - virtual ~TSRefCountedObject() {} - - void retain() { ++_refCount; } - - void release() - { - --_refCount; - if (_refCount == 0) - delete this; - } - -protected: - std::atomic_uint _refCount; -}; - -/** - * Defines the object which users must packed for HttpClient::send(HttpRequest*) method. - * Please refer to tests/test-cpp/Classes/ExtensionTest/NetworkTest/HttpClientTest.cpp as a sample - * @since v2.0.2 - * - * @lua NA - */ - -typedef std::function ccHttpRequestCallback; - -class HttpRequest : public TSRefCountedObject -{ - friend class HttpClient; - -public: - static const int MAX_REDIRECT_COUNT = 3; - - /** - * The HttpRequest type enum used in the HttpRequest::setRequestType. - */ - enum class Type - { - Get, - Post, - Put, - Delete, - Unknown, - }; - - /** - * Constructor. - * Because HttpRequest object will be used between UI thread and network thread, - requestObj->autorelease() is forbidden to avoid crashes in AutoreleasePool - new/retain/release still works, which means you need to release it manually - Please refer to HttpRequestTest.cpp to find its usage. - */ - HttpRequest() : _requestType(Type::Unknown), _pCallback(nullptr), _pUserData(nullptr) {} - - /** Destructor. */ - virtual ~HttpRequest() {} - - // setter/getters for properties - - /** - * Set request type of HttpRequest object before being sent,now it support the enum value of HttpRequest::Type. - * - * @param type the request type. - */ - void setRequestType(Type type) { _requestType = type; } - - /** - * Get the request type of HttpRequest object. - * - * @return HttpRequest::Type. - */ - Type getRequestType() const { return _requestType; } - - /** - * Set the url address of HttpRequest object. - * The url value could be like these: "http://httpbin.org/ip" or "https://httpbin.org/get" - * - * @param url the string object. - */ - void setUrl(cxx17::string_view url) { cxx17::assign(_url, url); } - - /** - * Get the url address of HttpRequest object. - * - * @return const char* the pointer of _url. - */ - cxx17::string_view getUrl() const { return _url; } - - /** - * Set the request data of HttpRequest object. - * - * @param buffer the buffer of request data, it support binary data. - * @param len the size of request data. - */ - void setRequestData(const char* buffer, size_t len) { _requestData.assign(buffer, buffer + len); } - - /** - * Get the request data pointer of HttpRequest object. - * - * @return char* the request data pointer. - */ - char* getRequestData() - { - if (!_requestData.empty()) - return _requestData.data(); - - return nullptr; - } - - /** - * Get the size of request data - * - * @return ssize_t the size of request data - */ - size_t getRequestDataSize() const { return _requestData.size(); } - - /** - * Set a string tag to identify your request. - * This tag can be found in HttpResponse->getHttpRequest->getTag(). - * - * @param tag the string object. - */ - void setTag(cxx17::string_view tag) { cxx17::assign(_tag, tag); } - - /** - * Get the string tag to identify the request. - * The best practice is to use it in your MyClass::onMyHttpRequestCompleted(sender, HttpResponse*) callback. - * - * @return const char* the pointer of _tag - */ - const char* getTag() const { return _tag.c_str(); } - - /** - * Set user-customed data of HttpRequest object. - * You can attach a customed data in each request, and get it back in response callback. - * But you need to new/delete the data pointer manually. - * - * @param pUserData the string pointer - */ - void setUserData(void* pUserData) { _pUserData = pUserData; } - - /** - * Get the user-customed data pointer which were pre-setted. - * Don't forget to delete it. HttpClient/HttpResponse/HttpRequest will do nothing with this pointer. - * - * @return void* the pointer of user-customed data. - */ - void* getUserData() const { return _pUserData; } - - /** - * Set response callback function of HttpRequest object. - * When response come back, we would call _pCallback to process response data. - * - * @param callback the ccHttpRequestCallback function. - */ - void setResponseCallback(const ccHttpRequestCallback& callback) { _pCallback = callback; } - - /** - * Get ccHttpRequestCallback callback function. - * - * @return const ccHttpRequestCallback& ccHttpRequestCallback callback function. - */ - const ccHttpRequestCallback& getCallback() const { return _pCallback; } - - /** - * Set custom-defined headers. - * - * @param headers The string vector of custom-defined headers. - */ - void setHeaders(const std::vector& headers) { _headers = headers; } - - /** - * Get custom headers. - * - * @return std::vector the string vector of custom-defined headers. - */ - const std::vector& getHeaders() const { return _headers; } - - void setHosts(std::vector hosts) { _hosts = std::move(hosts); } - const std::vector& getHosts() const { return _hosts; } - -private: - void setSync(bool sync) - { - if (sync) - _syncState = std::make_shared>(); - else - _syncState.reset(); - } - - std::shared_ptr> getSyncState() { return _syncState; } - - HttpResponse* wait() - { - if (_syncState) - return _syncState->get_future().get(); - return nullptr; - } - -protected: - // properties - Type _requestType; /// kHttpRequestGet, kHttpRequestPost or other enums - std::string _url; /// target url that this request is sent to - yasio::sbyte_buffer _requestData; /// used for POST - std::string _tag; /// user defined tag, to identify different requests in response callback - ccHttpRequestCallback _pCallback; /// C++11 style callbacks - void* _pUserData; /// You can add your customed data here - std::vector _headers; /// custom http headers - std::vector _hosts; - - std::shared_ptr> _syncState; -}; - -} // namespace network - -} // namespace yasio_ext - -// end group -/// @} - -#endif //__HTTP_REQUEST_H__ diff --git a/extensions/yasio_http/HttpResponse.h b/extensions/yasio_http/HttpResponse.h deleted file mode 100644 index 7496bca2e..000000000 --- a/extensions/yasio_http/HttpResponse.h +++ /dev/null @@ -1,277 +0,0 @@ -/**************************************************************************** - Copyright (c) 2010-2012 cocos2d-x.org - Copyright (c) 2013-2016 Chukong Technologies Inc. - Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - Copyright (c) 2019-present Axmol Engine contributors. - - https://axmolengine.github.io/ - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - ****************************************************************************/ - -#ifndef __YASIO_EXT_HTTP_RESPONSE__ -#define __YASIO_EXT_HTTP_RESPONSE__ -#include -#include -#include -#include -#include "yasio_http/HttpRequest.h" -#include "yasio_http/Uri.h" -#include "llhttp.h" - -/** - * @addtogroup network - * @{ - */ - -namespace yasio_ext -{ - -namespace network -{ - -class HttpClient; -/** - * @brief defines the object which users will receive at onHttpCompleted(sender, HttpResponse) callback. - * Please refer to samples/TestCpp/Classes/ExtensionTest/NetworkTest/HttpClientTest.cpp as a sample. - * @since v2.0.2. - * @lua NA - */ -class HttpResponse : public TSRefCountedObject -{ - friend class HttpClient; - -public: - using ResponseHeaderMap = std::multimap; - - /** - * Constructor, it's used by HttpClient internal, users don't need to create HttpResponse manually. - * @param request the corresponding HttpRequest which leads to this response. - */ - HttpResponse(HttpRequest* request) : _pHttpRequest(request) - { - if (_pHttpRequest) - { - _pHttpRequest->retain(); - } - } - - /** - * Destructor, it will be called in HttpClient internal. - * Users don't need to destruct HttpResponse object manually. - */ - virtual ~HttpResponse() - { - if (_pHttpRequest) - { - _pHttpRequest->release(); - } - } - - // getters, will be called by users - - /** - * Get the corresponding HttpRequest object which leads to this response. - * There's no paired setter for it, because it's already set in class constructor - * @return HttpRequest* the corresponding HttpRequest object which leads to this response. - */ - HttpRequest* getHttpRequest() const { return _pHttpRequest; } - - /** - * Get the http response data. - * @return yasio::sbyte_buffer* the pointer that point to the _responseData. - */ - yasio::sbyte_buffer* getResponseData() { return &_responseData; } - - bool isSucceed() const { return _responseCode == 200; } - - /** - * Get the http response code to judge whether response is successful or not. - * I know that you want to see the _responseCode is 200. - * If _responseCode is not 200, you should check the meaning for _responseCode by the net. - * @return int32_t the value of _responseCode - */ - int getResponseCode() const { return _responseCode; } - - /* - * The yasio error code, see yasio::errc - */ - int getInternalCode() const { return _internalCode; } - - int getRedirectCount() const { return _redirectCount; } - - const ResponseHeaderMap& getResponseHeaders() const { return _responseHeaders; } - -private: - void updateInternalCode(int value) - { - if (_internalCode == 0) - _internalCode = value; - } - - /** - * To see if the http request is finished. - */ - bool isFinished() const { return _finished; } - - void handleInput(const char* d, size_t n) - { - enum llhttp_errno err = llhttp_execute(&_context, d, n); - if (err != HPE_OK) - { - _finished = true; - } - } - - bool tryRedirect() - { - if ((_redirectCount < HttpRequest::MAX_REDIRECT_COUNT)) - { - auto iter = _responseHeaders.find("location"); - if (iter != _responseHeaders.end()) - { - auto redirectUrl = iter->second; - if (_responseCode == 302) - getHttpRequest()->setRequestType(HttpRequest::Type::Get); - // YASIO_LOG("Process url redirect (%d): %s", _responseCode, redirectUrl.c_str()); - return setLocation(redirectUrl, true); - } - } - return false; - } - - /** - * Set new request location with url - * @param url the actually url to request - * @param redirect wither redirect - */ - bool setLocation(cxx17::string_view url, bool redirect) - { - if (redirect) - { - ++_redirectCount; - _requestUri.invalid(); - } - - if (!_requestUri.isValid()) - { - Uri uri = Uri::parse(url); - if (!uri.isValid()) - return false; - _requestUri = std::move(uri); - - /* Resets response status */ - _responseHeaders.clear(); - _finished = false; - _responseData.clear(); - _currentHeader.clear(); - _responseCode = -1; - _internalCode = 0; - - /* Initialize user callbacks and settings */ - llhttp_settings_init(&_contextSettings); - - /* Initialize the parser in HTTP_BOTH mode, meaning that it will select between - * HTTP_REQUEST and HTTP_RESPONSE parsing automatically while reading the first - * input. - */ - llhttp_init(&_context, HTTP_RESPONSE, &_contextSettings); - - _context.data = this; - - /* Set user callbacks */ - _contextSettings.on_header_field = on_header_field; - _contextSettings.on_header_field_complete = on_header_field_complete; - _contextSettings.on_header_value = on_header_value; - _contextSettings.on_header_value_complete = on_header_value_complete; - _contextSettings.on_body = on_body; - _contextSettings.on_message_complete = on_complete; - } - - return true; - } - - bool validateUri() const { return _requestUri.isValid(); } - - const Uri& getRequestUri() const { return _requestUri; } - - static int on_header_field(llhttp_t* context, const char* at, size_t length) - { - auto thiz = (HttpResponse*)context->data; - thiz->_currentHeader.insert(thiz->_currentHeader.end(), at, at + length); - return 0; - } - static int on_header_field_complete(llhttp_t* context) - { - auto thiz = (HttpResponse*)context->data; - std::transform(thiz->_currentHeader.begin(), thiz->_currentHeader.end(), thiz->_currentHeader.begin(), - ::tolower); - return 0; - } - static int on_header_value(llhttp_t* context, const char* at, size_t length) - { - auto thiz = (HttpResponse*)context->data; - thiz->_currentHeaderValue.insert(thiz->_currentHeaderValue.end(), at, at + length); - return 0; - } - static int on_header_value_complete(llhttp_t* context) - { - auto thiz = (HttpResponse*)context->data; - thiz->_responseHeaders.emplace(std::move(thiz->_currentHeader), std::move(thiz->_currentHeaderValue)); - return 0; - } - static int on_body(llhttp_t* context, const char* at, size_t length) - { - auto thiz = (HttpResponse*)context->data; - thiz->_responseData.insert(thiz->_responseData.end(), at, at + length); - return 0; - } - static int on_complete(llhttp_t* context) - { - auto thiz = (HttpResponse*)context->data; - thiz->_responseCode = context->status_code; - thiz->_finished = true; - return 0; - } - -protected: - // properties - HttpRequest* _pHttpRequest; /// the corresponding HttpRequest pointer who leads to this response - int _redirectCount = 0; - - Uri _requestUri; - bool _finished = false; /// to indicate if the http request is successful simply - yasio::sbyte_buffer _responseData; /// the returned raw data. You can also dump it as a string - std::string _currentHeader; - std::string _currentHeaderValue; - ResponseHeaderMap _responseHeaders; /// the returned raw header data. You can also dump it as a string - int _responseCode = -1; /// the status code returned from server, e.g. 200, 404 - int _internalCode = 0; /// the ret code of perform - llhttp_t _context; - llhttp_settings_t _contextSettings; -}; - -} // namespace network - -} // namespace yasio_ext - -// end group -/// @} - -#endif //__HTTP_RESPONSE_H__ diff --git a/extensions/yasio_http/Uri.cpp b/extensions/yasio_http/Uri.cpp deleted file mode 100644 index 074878204..000000000 --- a/extensions/yasio_http/Uri.cpp +++ /dev/null @@ -1,415 +0,0 @@ -/* - * Copyright 2017 Facebook, Inc. - * Copyright (c) 2017 Chukong Technologies - * Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - * Copyright (c) 2019-present Axmol Engine contributors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * Uri class is based on the original file here https://github.com/facebook/folly/blob/master/folly/Uri.cpp - */ - -#include "yasio_http/Uri.h" - -#include -#include - -#include -#include - -#include "yasio/config.hpp" -#include "yasio/logging.hpp" - -#if defined(_WIN32) -# include -#endif - -#undef LIKELY -#undef UNLIKELY - -#if defined(__GNUC__) && __GNUC__ >= 4 -# define LIKELY(x) (__builtin_expect((x), 1)) -# define UNLIKELY(x) (__builtin_expect((x), 0)) -#else -# define LIKELY(x) (x) -# define UNLIKELY(x) (x) -#endif - -namespace -{ - -template -std::string toString(T arg) -{ - std::stringstream ss; - ss << arg; - return ss.str(); -} - -std::string submatch(const std::smatch& m, int idx) -{ - auto& sub = m[idx]; - return std::string(sub.first, sub.second); -} - -template -void toLower(String& s) -{ - for (auto& c : s) - { - c = char(tolower(c)); - } -} - -} // namespace - -namespace yasio_ext -{ - -namespace network -{ - -Uri::Uri() : _isValid(false), _isSecure(false), _hasAuthority(false), _isCustomPort(false), _port(0) {} - -Uri::Uri(const Uri& o) -{ - *this = o; -} - -Uri::Uri(Uri&& o) -{ - *this = std::move(o); -} - -Uri& Uri::operator=(const Uri& o) -{ - if (this != &o) - { - _isValid = o._isValid; - _isSecure = o._isSecure; - _scheme = o._scheme; - _username = o._username; - _password = o._password; - _host = o._host; - _hostName = o._hostName; - _hasAuthority = o._hasAuthority; - _isCustomPort = o._isCustomPort; - _port = o._port; - _authority = o._authority; - _pathEtc = o._pathEtc; - _path = o._path; - _query = o._query; - _fragment = o._fragment; - _queryParams = o._queryParams; - } - - return *this; -} - -Uri& Uri::operator=(Uri&& o) -{ - if (this != &o) - { - _isValid = o._isValid; - o._isValid = false; - _isSecure = o._isSecure; - o._isSecure = false; - _scheme = std::move(o._scheme); - _username = std::move(o._username); - _password = std::move(o._password); - _host = std::move(o._host); - _hostName = std::move(o._hostName); - _hasAuthority = o._hasAuthority; - _isCustomPort = o._isCustomPort; - o._hasAuthority = false; - o._isCustomPort = false; - _port = o._port; - o._port = 0; - _authority = std::move(o._authority); - _pathEtc = std::move(o._pathEtc); - _path = std::move(o._path); - _query = std::move(o._query); - _fragment = std::move(o._fragment); - _queryParams = std::move(o._queryParams); - } - return *this; -} - -bool Uri::operator==(const Uri& o) const -{ - return (_isValid == o._isValid && _isSecure == o._isSecure && _scheme == o._scheme && _username == o._username && - _password == o._password && _host == o._host && _hostName == o._hostName && - _hasAuthority == o._hasAuthority && _port == o._port && _authority == o._authority && - _pathEtc == o._pathEtc && _path == o._path && _query == o._query && _fragment == o._fragment && - _queryParams == o._queryParams); -} - -Uri Uri::parse(cxx17::string_view str) -{ - Uri uri; - - if (!uri.doParse(str)) - { - uri.clear(); - } - - return uri; -} - -bool Uri::doParse(cxx17::string_view str) -{ - static const std::regex uriRegex( - "([a-zA-Z][a-zA-Z0-9+.-]*):" // scheme: - "([^?#]*)" // authority and path - "(?:\\?([^#]*))?" // ?query - "(?:#(.*))?"); // #fragment - static const std::regex authorityAndPathRegex("//([^/]*)(/.*)?"); - - if (str.empty()) - { - YASIO_LOG("%s", "Empty URI is invalid!"); - return false; - } - - bool hasScheme = true; - std::string copied(str); - if (copied.find("://") == std::string::npos) - { - hasScheme = false; - copied.insert(0, "abc://"); // Just make regex happy. - } - - std::smatch match; - if (UNLIKELY(!std::regex_match(copied.cbegin(), copied.cend(), match, uriRegex))) - { - YASIO_LOG("Invalid URI: %s", str.data()); - return false; - } - - std::string authorityAndPath(match[2].first, match[2].second); - std::smatch authorityAndPathMatch; - if (!std::regex_match(authorityAndPath.cbegin(), authorityAndPath.cend(), authorityAndPathMatch, - authorityAndPathRegex)) - { - // Does not start with //, doesn't have authority - _hasAuthority = false; - _path = authorityAndPath; - } - else - { - static const std::regex authorityRegex( - "(?:([^@:]*)(?::([^@]*))?@)?" // username, password - "(\\[[^\\]]*\\]|[^\\[:]*)" // host (IP-literal (e.g. '['+IPv6+']', - // dotted-IPv4, or named host) - "(?::(\\d*))?"); // port - - auto& authority = authorityAndPathMatch[1]; - std::smatch authorityMatch; - if (!std::regex_match(authority.first, authority.second, authorityMatch, authorityRegex)) - { - std::string invalidAuthority(authority.first, authority.second); - YASIO_LOG("Invalid URI authority: %s", invalidAuthority.c_str()); - return false; - } - - std::string port(authorityMatch[4].first, authorityMatch[4].second); - if (!port.empty()) - { - _port = static_cast(atoi(port.c_str())); - } - - _hasAuthority = true; - _username = submatch(authorityMatch, 1); - _password = submatch(authorityMatch, 2); - _host = submatch(authorityMatch, 3); - _path = submatch(authorityAndPathMatch, 2); - } - - _query = submatch(match, 3); - _fragment = submatch(match, 4); - _isValid = true; - - // Assign authority part - // - // Port is 5 characters max and we have up to 3 delimiters. - _authority.reserve(getHost().size() + getUserName().size() + getPassword().size() + 8); - - if (!getUserName().empty() || !getPassword().empty()) - { - cxx17::append(_authority, getUserName()); - - if (!getPassword().empty()) - { - _authority.push_back(':'); - cxx17::append(_authority, getPassword()); - } - - _authority.push_back('@'); - } - - cxx17::append(_authority, getHost()); - - if (getPort() != 0) - { - _authority.push_back(':'); - _authority.append(::toString(getPort())); - } - - if (_path.empty()) - _path.push_back('/'); - - // Assign path etc part - _pathEtc = _path; - if (!_query.empty()) - { - _pathEtc += '?'; - _pathEtc += _query; - } - - if (!_fragment.empty()) - { - _pathEtc += '#'; - _pathEtc += _fragment; - } - - // Assign host name - if (!_host.empty() && _host[0] == '[') - { - // If it starts with '[', then it should end with ']', this is ensured by - // regex - _hostName = _host.substr(1, _host.size() - 2); - } - else - { - _hostName = _host; - } - - if (hasScheme) - { - _scheme = submatch(match, 1); - toLower(_scheme); - if (_scheme == "https" || _scheme == "wss") - { - _isSecure = true; - if (_port == 0) - _port = 443; - - _isCustomPort = _port != 443; - } - else if (_scheme == "http" || _scheme == "ws") - { - if (_port == 0) - _port = 80; - - _isCustomPort = _port != 80; - } - else if (_scheme == "ftp") - { - if (_port == 0) - _port = 21; - - _isCustomPort = _port != 21; - } - } - else - _isCustomPort = _port != 0; - - return true; -} - -void Uri::clear() -{ - _isValid = false; - _isSecure = false; - _scheme.clear(); - _username.clear(); - _password.clear(); - _host.clear(); - _hostName.clear(); - _hasAuthority = false; - _isCustomPort = false; - _port = 0; - _authority.clear(); - _pathEtc.clear(); - _path.clear(); - _query.clear(); - _fragment.clear(); - _queryParams.clear(); -} - -const std::vector>& Uri::getQueryParams() -{ - if (!_query.empty() && _queryParams.empty()) - { - // Parse query string - static const std::regex queryParamRegex( - "(^|&)" /*start of query or start of parameter "&"*/ - "([^=&]*)=?" /*parameter name and "=" if value is expected*/ - "([^=&]*)" /*parameter value*/ - "(?=(&|$))" /*forward reference, next should be end of query or - start of next parameter*/); - std::cregex_iterator paramBeginItr(_query.data(), _query.data() + _query.size(), queryParamRegex); - std::cregex_iterator paramEndItr; - for (auto itr = paramBeginItr; itr != paramEndItr; itr++) - { - if (itr->length(2) == 0) - { - // key is empty, ignore it - continue; - } - _queryParams.emplace_back(std::string((*itr)[2].first, (*itr)[2].second), // parameter name - std::string((*itr)[3].first, (*itr)[3].second) // parameter value - ); - } - } - return _queryParams; -} - -std::string Uri::toString() const -{ - std::stringstream ss; - if (_hasAuthority) - { - ss << _scheme << "://"; - if (!_password.empty()) - { - ss << _username << ":" << _password << "@"; - } - else if (!_username.empty()) - { - ss << _username << "@"; - } - ss << _host; - if (_isCustomPort) - { - ss << ":" << _port; - } - } - else - { - ss << _scheme << ":"; - } - ss << _path; - if (!_query.empty()) - { - ss << "?" << _query; - } - if (!_fragment.empty()) - { - ss << "#" << _fragment; - } - return ss.str(); -} - -} // namespace network - -} // namespace yasio_ext diff --git a/extensions/yasio_http/Uri.h b/extensions/yasio_http/Uri.h deleted file mode 100644 index bab0a2c55..000000000 --- a/extensions/yasio_http/Uri.h +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright 2017 Facebook, Inc. - * Copyright (c) 2017 Chukong Technologies - * Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Uri class is based on the original file here https://github.com/facebook/folly/blob/master/folly/Uri.cpp - */ - -#pragma once - -#include -#include -#include - -#include "yasio/string_view.hpp" - -/** - * @addtogroup network - * @{ - */ - -namespace yasio_ext -{ - -namespace network -{ - -/** - * Class representing a URI. - * - * Consider http://www.facebook.com/foo/bar?key=foo#anchor - * - * The URI is broken down into its parts: scheme ("http"), authority - * (ie. host and port, in most cases: "www.facebook.com"), path - * ("/foo/bar"), query ("key=foo") and fragment ("anchor"). The scheme is - * lower-cased. - * - * If this Uri represents a URL, note that, to prevent ambiguity, the component - * parts are NOT percent-decoded; you should do this yourself with - * uriUnescape() (for the authority and path) and uriUnescape(..., - * UriEscapeMode::QUERY) (for the query, but probably only after splitting at - * '&' to identify the individual parameters). - */ -class Uri -{ -public: - /** - * Parse a Uri from a string. Throws std::invalid_argument on parse error. - */ - static Uri parse(cxx17::string_view str); - - /** Default constructor */ - Uri(); - - /** Copy constructor */ - Uri(const Uri& o); - - /** Move constructor */ - Uri(Uri&& o); - - /** Copy assignment */ - Uri& operator=(const Uri& o); - - /** Move assignment */ - Uri& operator=(Uri&& o); - - /** Checks whether two Uri instances contain the same values */ - bool operator==(const Uri& o) const; - - /** Checks wether it's a valid URI */ - bool isValid() const { return _isValid; } - - /** Checks whether it's a SSL connection */ - bool isSecure() const { return _isSecure; } - - void invalid() { _isValid = false; } - /** Gets the scheme name for this URI. */ - cxx17::string_view getScheme() const { return _scheme; } - - /** Gets the user name with the specified URI. */ - cxx17::string_view getUserName() const { return _username; } - - /** Gets the password with the specified URI. */ - cxx17::string_view getPassword() const { return _password; } - /** - * Get host part of URI. If host is an IPv6 address, square brackets will be - * returned, for example: "[::1]". - */ - cxx17::string_view getHost() const { return _host; } - /** - * Get host part of URI. If host is an IPv6 address, square brackets will not - * be returned, for exmaple "::1"; otherwise it returns the same thing as - * getHost(). - * - * getHostName() is what one needs to call if passing the host to any other tool - * or API that connects to that host/port; e.g. getaddrinfo() only understands - * IPv6 host without square brackets - */ - cxx17::string_view getHostName() const { return _hostName; } - - /** Gets the port number of the URI. */ - uint16_t getPort() const { return _port; } - - /** Gets the path part of the URI. */ - cxx17::string_view getPath() const { return _path; } - - /// Gets the path, query and fragment parts of the URI. - cxx17::string_view getPathEtc() const { return _pathEtc; } - - /** Gets the query part of the URI. */ - cxx17::string_view getQuery() const { return _query; } - - /** Gets the fragment part of the URI */ - cxx17::string_view getFragment() const { return _fragment; } - - /** Gets the authority part (userName, password, host and port) of the URI. - * @note If the port number is a well-known port - * number for the given scheme (e.g., 80 for http), it - * is not included in the authority. - */ - cxx17::string_view getAuthority() const { return _authority; } - - bool isCustomPort() const { return _isCustomPort; } - - /** Gets a string representation of the URI. */ - std::string toString() const; - - /** - * Get query parameters as key-value pairs. - * e.g. for URI containing query string: key1=foo&key2=&key3&=bar&=bar= - * In returned list, there are 3 entries: - * "key1" => "foo" - * "key2" => "" - * "key3" => "" - * Parts "=bar" and "=bar=" are ignored, as they are not valid query - * parameters. "=bar" is missing parameter name, while "=bar=" has more than - * one equal signs, we don't know which one is the delimiter for key and - * value. - * - * Note, this method is not thread safe, it might update internal state, but - * only the first call to this method update the state. After the first call - * is finished, subsequent calls to this method are thread safe. - * - * @return query parameter key-value pairs in a vector, each element is a - * pair of which the first element is parameter name and the second - * one is parameter value - */ - const std::vector>& getQueryParams(); - - /** Clears all parts of the URI. */ - void clear(); - -private: - bool doParse(cxx17::string_view str); - - bool _isValid; - bool _isSecure; - bool _hasAuthority; - bool _isCustomPort; - uint16_t _port; - std::string _scheme; - std::string _username; - std::string _password; - std::string _host; - std::string _hostName; - std::string _authority; - std::string _pathEtc; - std::string _path; - std::string _query; - std::string _fragment; - std::vector> _queryParams; -}; - -} // namespace network - -} // namespace yasio_ext - -// end group -/// @} diff --git a/extensions/yasio_http/utils/concurrent_deque.h b/extensions/yasio_http/utils/concurrent_deque.h deleted file mode 100644 index 99d1ba0a5..000000000 --- a/extensions/yasio_http/utils/concurrent_deque.h +++ /dev/null @@ -1,137 +0,0 @@ -/**************************************************************************** -* - Copyright (c) 2019-present Axmol Engine contributors. - - https://axmolengine.github.io/ - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - ****************************************************************************/ - -#pragma once - -#include -#include - -namespace yasio_ext { -template -class concurrent_deque { -public: - /** Iterator, can be used to loop the Vector. */ - using iterator = typename std::deque<_Ty>::iterator; - /** Const iterator, can be used to loop the Vector. */ - using const_iterator = typename std::deque<_Ty>::const_iterator; - - void push_back(_Ty&& value) { - std::lock_guard lck(this->mtx_); - queue_.push_back(std::forward<_Ty>(value)); - } - void push_back(const _Ty& value) { - std::lock_guard lck(this->mtx_); - queue_.push_back(value); - } - _Ty& front() { - std::lock_guard lck(this->mtx_); - return queue_.front(); - } - void pop_front() { - std::lock_guard lck(this->mtx_); - queue_.pop_front(); - } - void push_front(_Ty&& value) - { - std::lock_guard lck(this->mtx_); - queue_.push_front(std::forward<_Ty>(value)); - } - void push_front(const _Ty& value) - { - std::lock_guard lck(this->mtx_); - queue_.push_front(value); - } - size_t size() const { - std::lock_guard lck(this->mtx_); - return this->queue_.size(); - } - bool empty() const { - std::lock_guard lck(this->mtx_); - return this->queue_.empty(); - } - void clear() { - std::lock_guard lck(this->mtx_); - this->queue_.clear(); - } - - std::unique_lock get_lock() { - return std::unique_lock{this->mtx_}; - } - - void lock() { - this->mtx_.lock(); - } - void unlock() { - this->mtx_.unlock(); - } - - void unsafe_push_back(_Ty&& value) { - queue_.push_back(std::forward<_Ty>(value)); - } - void unsafe_push_back(const _Ty& value) { - queue_.push_back(value); - } - _Ty& unsafe_front() { - return queue_.front(); - } - void unsafe_pop_front() { - queue_.pop_front(); - } - void unsafe_push_front(_Ty&& value) { queue_.push_font(std::forward<_Ty>(value)); } - void unsafe_push_front(const _Ty& value) { queue_.push_font(value); } - bool unsafe_empty() const { - return this->queue_.empty(); - } - size_t unsafe_size() const { - return this->queue_.size(); - } - void unsafe_clear() { - this->queue_.clear(); - } - - iterator unsafe_begin() { - return this->queue_.begin(); - } - iterator unsafe_end() { - return this->queue_.end(); - } - - const_iterator unsafe_begin() const { - return this->queue_.begin(); - } - - const_iterator unsafe_end() const { - return this->queue_.end(); - } - - iterator unsafe_erase(iterator iter) { - return this->queue_.erase(iter); - } - -private: - std::deque<_Ty> queue_; - mutable std::recursive_mutex mtx_; -}; -} // namespace cocos2d diff --git a/extensions/yasio_http/yasio_http.cpp b/extensions/yasio_http/yasio_http.cpp deleted file mode 100644 index 2261106d2..000000000 --- a/extensions/yasio_http/yasio_http.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "yasio/config.hpp" - -#if defined(YASIO_INSIDE_UNREAL) - -#include "Modules/ModuleManager.h" - -class yasio_http_unreal_module : public IModuleInterface -{ -public: - /** IModuleInterface implementation */ - void StartupModule() override {} - void ShutdownModule() override {} -}; -IMPLEMENT_MODULE(yasio_http_unreal_module, yasio_http) - -#endif diff --git a/tests/echo_client/main.cpp b/tests/echo_client/main.cpp index c40ea3afd..b2d0c00c0 100644 --- a/tests/echo_client/main.cpp +++ b/tests/echo_client/main.cpp @@ -7,8 +7,8 @@ using namespace yasio; -static highp_time_t s_last_send_time[3] = {0}; -static const highp_time_t s_send_interval = 2000; // (ms) +static tlx::highp_time_t s_last_send_time[3] = {0}; +static const tlx::highp_time_t s_send_interval = 2000; // (ms) void run_echo_client(const char* ip, int port, const char* protocol) { @@ -30,7 +30,7 @@ void run_echo_client(const char* ip, int port, const char* protocol) { case YEK_PACKET: { auto index = event->cindex(); - auto diff = highp_clock() - s_last_send_time[index]; + auto diff = tlx::highp_clock() - s_last_send_time[index]; auto packet = std::move(event->packet()); total_bytes_transferred += static_cast(packet.size()); @@ -57,7 +57,7 @@ void run_echo_client(const char* ip, int port, const char* protocol) obs.fill_bytes(n, 'a'); auto bytes_transferred = service.write(transport, std::move(obs.buffer())); printf("sent %d bytes ...\n", bytes_transferred); - s_last_send_time[index] = highp_clock(); + s_last_send_time[index] = tlx::highp_clock(); return false; }); } @@ -74,11 +74,11 @@ void run_echo_client(const char* ip, int port, const char* protocol) std::this_thread::sleep_for(std::chrono::seconds(1)); printf("[%s] connecting %s:%u ...\n", protocol, ip, port); - if (cxx20::ic::iequals(protocol, "udp")) + if (tlx::ic::iequals(protocol, "udp")) { service.open(0, YCK_UDP_CLIENT); } - else if (cxx20::ic::iequals(protocol, "kcp")) + else if (tlx::ic::iequals(protocol, "kcp")) { service.open(0, YCK_KCP_CLIENT); } diff --git a/tests/echo_server/main.cpp b/tests/echo_server/main.cpp index a4986a8cd..8068e06f5 100644 --- a/tests/echo_server/main.cpp +++ b/tests/echo_server/main.cpp @@ -29,9 +29,9 @@ void run_echo_server(const char* ip, u_short port, const char* protocol) timer.async_wait_once([=](io_service& server) { server.set_option(YOPT_C_UNPACK_PARAMS, 0, 65536, -1, 0, 0); printf("[%s] open server %s:%u ...\n", protocol, ip, port); - if (cxx20::ic::iequals(protocol, "udp")) + if (tlx::ic::iequals(protocol, "udp")) server.open(0, YCK_UDP_SERVER); - else if (cxx20::ic::iequals(protocol, "kcp")) + else if (tlx::ic::iequals(protocol, "kcp")) server.open(0, YCK_KCP_SERVER); else server.open(0, YCK_TCP_SERVER); diff --git a/tests/icmp/main.cpp b/tests/icmp/main.cpp index 9fbd7d812..f263f1fd3 100644 --- a/tests/icmp/main.cpp +++ b/tests/icmp/main.cpp @@ -82,12 +82,12 @@ using namespace yasio; // | | | v // +-------------------------------+------------------------------+ --- -template -static void icmp_checksum(icmp_hdr_st& header, Iterator body_begin, Iterator body_end) +template +static void icmp_checksum(icmp_hdr_st& header, _Iter body_begin, _Iter body_end) { unsigned int sum = (header.type << 8) + header.code + header.id + header.seqno; - Iterator body_iter = body_begin; + _Iter body_iter = body_begin; while (body_iter != body_end) { sum += (static_cast(*body_iter++) << 8); @@ -152,7 +152,7 @@ class icmp_ping_helper { socktype_ = schk.open(AF_INET, SOCK_RAW, IPPROTO_ICMP) ? SOCK_RAW : SOCK_DGRAM; #if defined(ICMPTEST_ENC_TSC) - if (yasio::time_now() < static_cast((std::numeric_limits::max)() - 86400)) + if (tlx::time_now() < static_cast((std::numeric_limits::max)() - 86400)) { payload_size = (std::max)(payload_size, static_cast(sizeof(yasio::icmp::timeval32) + ICMPTEST_PAYLOAD_LEN)); enc_tsc_ = true; @@ -198,7 +198,7 @@ class icmp_ping_helper { // icmp body if (enc_tsc_) { - auto tsc = yasio::highp_clock(); + auto tsc = tlx::highp_clock(); // !!! after 01/19/2038 3:14:07 the 32bit tv_sec will overflow icmp::timeval32 tv{static_cast(tsc / std::micro::den), static_cast(tsc % std::micro::den)}; icmp_pkt_.write_bytes(&tv, static_cast(sizeof(tv))); @@ -212,7 +212,7 @@ class icmp_ping_helper { // id,sum #if !defined(__linux__) req_hdr.id = get_identifier(); - icmp_checksum(req_hdr, icmp_pkt_.data() + sizeof(icmp_hdr_st), icmp_pkt_.buffer().end()); + icmp_checksum(req_hdr, icmp_pkt_.buffer().begin() + sizeof(icmp_hdr_st), icmp_pkt_.buffer().end()); #else /** Linux: * SOCK_DGRAM @@ -222,7 +222,8 @@ class icmp_ping_helper { if (socktype_ == SOCK_RAW) { req_hdr.id = get_identifier(); - icmp_checksum(req_hdr, icmp_pkt_.data() + sizeof(icmp_hdr_st), icmp_pkt_.buffer().end()); + icmp_checksum(req_hdr, icmp_pkt_.buffer().begin() + sizeof(icmp_hdr_st), + icmp_pkt_.buffer().end()); } #endif icmp_pkt_.pop(id_off, req_hdr.id); @@ -247,7 +248,7 @@ class icmp_ping_helper { watcher.mod_event(s.native_handle(), 0, socket_event::read); if (ret > 0 && watcher.is_ready(s.native_handle(), socket_event::read)) { - yasio::byte_buffer buf(ip_total_len_); + tlx::byte_buffer buf(ip_total_len_); int n = s.recvfrom(buf.data(), ip_total_len_, peer); uint8_t* icmp_raw = nullptr; @@ -368,12 +369,12 @@ int main(int argc, char** argv) int payload_size = 0; // icmp payload size for (int argi = 2; argi < argc; ++argi) { - if (cxx20::ic::iequals(argv[argi], "-c") || cxx20::ic::iequals(argv[argi], "-n")) + if (tlx::ic::iequals(argv[argi], "-c") || tlx::ic::iequals(argv[argi], "-n")) { if (++argi < argc) max_times = atoi(argv[argi]); } - else if (cxx20::ic::iequals(argv[argi], "-s") || cxx20::ic::iequals(argv[argi], "-l")) + else if (tlx::ic::iequals(argv[argi], "-s") || tlx::ic::iequals(argv[argi], "-l")) { if (++argi < argc) payload_size = atoi(argv[argi]); @@ -381,7 +382,7 @@ int main(int argc, char** argv) ++argi; } - payload_size = yasio::clamp(payload_size, ICMPTEST_PAYLOAD_LEN, ICMPTEST_PAYLOAD_MAX_LEN); + payload_size = std::clamp(payload_size, ICMPTEST_PAYLOAD_LEN, ICMPTEST_PAYLOAD_MAX_LEN); std::vector endpoints; xxsocket::resolve(endpoints, host, 0); @@ -407,11 +408,12 @@ int main(int argc, char** argv) uint8_t ttl = 0; int error = 0; - auto start_ms = yasio::highp_clock(); + auto start_ms = tlx::highp_clock(); int n = helper.ping(watcher, endpoints[0], std::chrono::seconds(3), peer, reply_hdr, ttl, error); if (n > 0) fprintf(stdout, "Reply from %s: bytes=%d icmp_seq=%u ttl=%u id=%u time=%.1lfms\n", peer.ip().c_str(), n, static_cast(reply_hdr.seqno), - static_cast(ttl), static_cast(reply_hdr.id), (yasio::highp_clock() - start_ms) / 1000.0); + static_cast(ttl), static_cast(reply_hdr.id), + (tlx::highp_clock() - start_ms) / 1000.0); else fprintf(stderr, "Ping %s [%s] fail, ec=%d, detail: %s\n", host, remote_ip.c_str(), error, yasio::icmp::strerror(error)); std::this_thread::sleep_for(std::chrono::seconds(1)); diff --git a/tests/issue201/main.cpp b/tests/issue201/main.cpp index 370b0dcd6..42b94db79 100644 --- a/tests/issue201/main.cpp +++ b/tests/issue201/main.cpp @@ -1,4 +1,3 @@ -#include "yasio/utils.hpp" #include "yasio/yasio.hpp" using namespace yasio; @@ -9,14 +8,14 @@ int main() const char* addrip = "114.114.114.114"; u_short port = 9099; YASIO_LOG("connecting %s:%u...", addrip, port); - auto start = yasio::highp_clock(); + auto start = tlx::highp_clock(); if (sock.xpconnect_n(addrip, port, std::chrono::seconds(3)) != 0) YASIO_LOG("connect %s failed, the timeout should be approximately equal to 3(s), real: " "%lf(s); but when the host network adapter isn't connected, it may be approximately " "equal to 0(s)\n", - addrip, (yasio::highp_clock() - start) / (double)std::micro::den); + addrip, (tlx::highp_clock() - start) / (double)std::micro::den); else YASIO_LOG("Aha connect %s succeed, cost: %lf(s)\n", addrip, - (yasio::highp_clock() - start) / (double)std::micro::den); + (tlx::highp_clock() - start) / (double)std::micro::den); return 0; } diff --git a/tests/issue208/main.cpp b/tests/issue208/main.cpp index 81ce4fc47..fe92af834 100644 --- a/tests/issue208/main.cpp +++ b/tests/issue208/main.cpp @@ -8,10 +8,7 @@ timer_cb_t create_timer_cb(); io_service& get_service() { return *yasio_shared_service(); } -static yasio::highp_time_t getTimeStamp() -{ - return yasio::highp_clock() / 1000; -} +static tlx::highp_time_t getTimeStamp() { return tlx::highp_clock() / 1000; } void start_exprie_timer() { diff --git a/tests/mcast/main.cpp b/tests/mcast/main.cpp index 335e20fa6..0a4d29608 100644 --- a/tests/mcast/main.cpp +++ b/tests/mcast/main.cpp @@ -75,7 +75,8 @@ void yasioMulticastTest(int mcast_role) // print packet msg auto packet = std::move(event->packet()); fwrite(packet.data(), packet.size(), 1, stdout); - fprintf(stdout, "%lld--------------------\n\n", (long long)yasio::clock() / 1000); + fprintf(stdout, "%lld--------------------\n\n", + (long long)tlx::clock() / 1000); fflush(stdout); auto transport = event->transport(); diff --git a/tests/mtu/main.cpp b/tests/mtu/main.cpp index 8753c210a..e793f43dd 100644 --- a/tests/mtu/main.cpp +++ b/tests/mtu/main.cpp @@ -1,5 +1,5 @@ #include "yasio/xxsocket.hpp" -#include "yasio/byte_buffer.hpp" +#include "yasio/tlx/byte_buffer.hpp" int main() { @@ -17,7 +17,7 @@ int main() return ENOENT; } - yasio::byte_buffer data; + tlx::byte_buffer data; data.resize(65535, '1'); // set sndbuf match with data size, while, linux kenerl will double it diff --git a/tests/speed/main.cpp b/tests/speed/main.cpp index acdefa439..f29f8f1c9 100644 --- a/tests/speed/main.cpp +++ b/tests/speed/main.cpp @@ -146,13 +146,13 @@ void setup_kcp_transfer(transport_handle_t handle) // The transport rely on low level proto UDP/TCP void ll_send_repeated(io_service* service, transport_handle_t thandle, obstream* obs) { - static long long time_start = yasio::highp_clock<>(); + static long long time_start = tlx::highp_clock<>(); static double time_elapsed = 0; static double last_print_time = 0; auto cb = [=](int, size_t bytes_transferred) { assert(bytes_transferred == obs->buffer().size()); - time_elapsed = (yasio::highp_clock<>() - time_start) / 1000000.0; + time_elapsed = (tlx::highp_clock<>() - time_start) / 1000000.0; s_send_total_bytes += bytes_transferred; s_send_speed = s_send_total_bytes / time_elapsed; ll_send_repeated(service, thandle, obs); @@ -164,13 +164,13 @@ void ll_send_repeated(io_service* service, transport_handle_t thandle, obstream* void kcp_send_repeated(io_service* service, transport_handle_t thandle, obstream* obs) { - static long long time_start = yasio::highp_clock<>(); + static long long time_start = tlx::highp_clock<>(); static double time_elapsed = 0; static double last_print_time = 0; highp_timer_ptr ignored_ret = service->schedule(std::chrono::microseconds(s_kcp_send_interval), [=](io_service&) { s_send_total_bytes += service->write(thandle, obs->buffer()); - time_elapsed = (yasio::highp_clock<>() - time_start) / 1000000.0; + time_elapsed = (tlx::highp_clock<>() - time_start) / 1000000.0; s_send_speed = s_send_total_bytes / time_elapsed; print_speed_detail(0.5, time_elapsed); if (time_elapsed < s_send_limit_time) @@ -249,7 +249,7 @@ void start_sender(io_service& service) void start_receiver(io_service& service) { - static long long time_start = yasio::highp_clock<>(); + static long long time_start = tlx::highp_clock<>(); static double last_print_time = 0; service.set_option(YOPT_S_FORWARD_PACKET, 1); service.set_option(YOPT_C_MOD_FLAGS, 0, YCF_REUSEADDR, 0); @@ -262,7 +262,7 @@ void start_receiver(io_service& service) { case YEK_PACKET: { s_recv_total_bytes += event->packet_view().size(); - auto time_elapsed = (yasio::highp_clock<>() - time_start) / 1000000.0; + auto time_elapsed = (tlx::highp_clock<>() - time_start) / 1000000.0; s_recv_speed = s_recv_total_bytes / time_elapsed; break; } @@ -307,9 +307,9 @@ int main(int argc, char** argv) if (argc > 1) mode = argv[1]; - if (cxx20::ic::iequals(mode, "server")) + if (tlx::ic::iequals(mode, "server")) start_receiver(receiver); - else if (cxx20::ic::iequals(mode, "client")) + else if (tlx::ic::iequals(mode, "client")) start_sender(sender); else { @@ -317,14 +317,14 @@ int main(int argc, char** argv) start_sender(sender); } - static long long time_start = yasio::highp_clock<>(); + static long long time_start = tlx::highp_clock<>(); while (true) { // main thread, print speed only, so sleep 200ms // !Note: sleep(10ms) will cost 0.3%CPU, 200ms %0CPU, tested on macbook pro 2019 std::this_thread::sleep_for(std::chrono::milliseconds(500)); - auto time_elapsed = (yasio::highp_clock<>() - time_start) / 1000000.0; + auto time_elapsed = (tlx::highp_clock<>() - time_start) / 1000000.0; print_speed_detail(0.5, time_elapsed); } return 0; diff --git a/tests/ssl/main.cpp b/tests/ssl/main.cpp index 769702627..7456c38f9 100644 --- a/tests/ssl/main.cpp +++ b/tests/ssl/main.cpp @@ -35,7 +35,7 @@ void yasioTest() service.set_option(YOPT_S_SSL_CERT, SSLTEST_CERT, SSLTEST_PKEY); service.set_option(YOPT_C_MOD_FLAGS, SSLTEST_CHANNEL_SERVER, YCF_REUSEADDR, 0); - yasio::sbyte_buffer http_resp_data; + tlx::sbyte_buffer http_resp_data; service.start([&](event_ptr&& event) { switch (event->kind()) @@ -47,7 +47,8 @@ void yasioTest() case SSLTEST_CHANNEL_HTTP_CLIENT: { auto packet = event->packet_view(); http_client_bytes_transferred += static_cast(packet.size()); - http_resp_data.append(packet.data(), packet.data() + packet.size()); + http_resp_data.insert(http_resp_data.end(), packet.data(), + packet.data() + packet.size()); } break; case SSLTEST_CHANNEL_CLIENT: diff --git a/tests/tcp/main.cpp b/tests/tcp/main.cpp index ad2d6e1e7..305c1632d 100644 --- a/tests/tcp/main.cpp +++ b/tests/tcp/main.cpp @@ -14,19 +14,19 @@ void yasioTest() { yasio::inet::io_hostent endpoints[] = {{HTTP_TEST_HOST, 80}}; - using namespace cxx17; - bool yes = cxx20::starts_with("hello world", "hello"); - yes = cxx20::starts_with("hello world", (int)'h'); - yes = cxx20::starts_with("hello world", std::string{"hello"}); - yes = cxx20::starts_with(std::string{"hello world"}, "hello"); - yes = cxx20::starts_with(std::string{"hello world"}, std::string{"hello"}); - yes = cxx20::starts_with(std::string{"hello world"}, (int)'h'); + using namespace std; + bool yes = tlx::starts_with("hello world", "hello"); + yes = tlx::starts_with("hello world", (int)'h'); + yes = tlx::starts_with("hello world", std::string{"hello"}); + yes = tlx::starts_with(std::string{"hello world"}, "hello"); + yes = tlx::starts_with(std::string{"hello world"}, std::string{"hello"}); + yes = tlx::starts_with(std::string{"hello world"}, (int)'h'); #if YASIO__HAS_CXX14 - yes = cxx20::starts_with("hello world"_sv, (int)'h'); - yes = cxx20::starts_with("hello world"_sv, "hello"); - yes = cxx20::starts_with("hello world", "hello"_sv); - yes = cxx20::starts_with(std::string{"hello world"}, "hello"_sv); - yes = cxx20::starts_with("hello world"_sv, std::string{"hello"}); + yes = tlx::starts_with("hello world"sv, (int)'h'); + yes = tlx::starts_with("hello world"sv, "hello"); + yes = tlx::starts_with("hello world", "hello"sv); + yes = tlx::starts_with(std::string{"hello world"}, "hello"sv); + yes = tlx::starts_with("hello world"sv, std::string{"hello"}); #endif yasio::obstream obs; @@ -57,11 +57,11 @@ void yasioTest() std::cout << r0 << ", " << r1 << ", " << f1 << ", " << v5 << ", " << v6 << ", " << v8 << "\n"; #endif - yasio::sbyte_buffer vecbuf; + tlx::sbyte_buffer vecbuf; std::string strbuf; std::array arrbuf; char raw_arrbuf[16]; - yasio::obstream_span{vecbuf}.write_bytes("hello world!"); + yasio::obstream_span{vecbuf}.write_bytes("hello world!"); yasio::obstream_span{strbuf}.write_bytes("hello world!"); yasio::obstream_span{arrbuf}.write_bytes("hello world!"); yasio::obstream_span{raw_arrbuf}.write_bytes("hello world!"); diff --git a/tools/ci.ps1 b/tools/ci.ps1 index 300b4372a..dab57001e 100644 --- a/tools/ci.ps1 +++ b/tools/ci.ps1 @@ -27,8 +27,8 @@ if (!$configOnly) { Write-Host "run issue201 on linux..." & "$buildDir/tests/issue201/issue201" - Write-Host "run httptest on linux..." - & "$buildDir/tests/http/httptest" + # Write-Host "run httptest on linux..." + # & "$buildDir/tests/http/httptest" Write-Host "run ssltest on linux..." & "$buildDir/tests/ssl/ssltest" diff --git a/yasio/bindings/lyasio.cpp b/yasio/bindings/lyasio.cpp index 327c0394b..8c053f5b4 100644 --- a/yasio/bindings/lyasio.cpp +++ b/yasio/bindings/lyasio.cpp @@ -39,7 +39,7 @@ enum }; template -static void obstream_write_v(_Stream* obs, cxx17::string_view val, int length_field_bits) +static void obstream_write_v(_Stream* obs, std::string_view val, int length_field_bits) { // default: Use variant length of length field, just like .net BinaryWriter.Write(String), // see: @@ -58,7 +58,7 @@ static void obstream_write_v(_Stream* obs, cxx17::string_view val, int length_fi } }; template -static cxx17::string_view ibstream_read_v(_Stream* ibs, int length_field_bits) +static std::string_view ibstream_read_v(_Stream* ibs, int length_field_bits) { // default: Use variant length of length field, just like .net BinaryReader.ReadString, // see: @@ -113,21 +113,21 @@ static void register_obstream(sol::table& lib, const char* usertype) "write_f16", &_Stream::template write, # endif "write_f", &_Stream::template write, "write_lf", &_Stream::template write, "write_v", - [](_Stream* obs, cxx17::string_view sv, sol::variadic_args args) { + [](_Stream* obs, std::string_view sv, sol::variadic_args args) { int lfl = -1; if (args.size() > 0) lfl = static_cast(args[0]); return obstream_write_v<_Stream>(obs, sv, lfl); }, - "write_bytes", static_cast(&_Stream::write_bytes), "length", &_Stream::length, "to_string", - [](_Stream* obs) { return cxx17::string_view(obs->data(), obs->length()); }, "save", &_Stream::save); + "write_bytes", static_cast(&_Stream::write_bytes), "length", &_Stream::length, "to_string", + [](_Stream* obs) { return std::string_view(obs->data(), obs->length()); }, "save", &_Stream::save); } template static void register_ibstream(sol::table& lib, const char* usertype) { lib.new_usertype<_Stream>( - usertype, sol::constructors<_Stream(), _Stream(yasio::sbyte_buffer), _Stream(const _OStream*)>(), "load", &_Stream::load, "read_ix", + usertype, sol::constructors<_Stream(), _Stream(tlx::sbyte_buffer), _Stream(const _OStream*)>(), "load", &_Stream::load, "read_ix", &_Stream::template read_ix, "read_bool", &_Stream::template read, "read_i8", &_Stream::template read, "read_i16", &_Stream::template read, "read_i32", &_Stream::template read, "read_i64", &_Stream::template read, "read_u8", &_Stream::template read, "read_u16", &_Stream::template read, "read_u32", &_Stream::template read, "read_u64", @@ -142,8 +142,8 @@ static void register_ibstream(sol::table& lib, const char* usertype) lfl = static_cast(args[0]); return ibstream_read_v<_Stream>(ibs, lfl); }, - "read_bytes", static_cast(&_Stream::read_bytes), "seek", &_StreamView::seek, "length", &_StreamView::length, - "to_string", [](_Stream* ibs) { return cxx17::string_view(ibs->data(), ibs->length()); }); + "read_bytes", static_cast(&_Stream::read_bytes), "seek", &_StreamView::seek, "length", &_StreamView::length, + "to_string", [](_Stream* ibs) { return std::string_view(ibs->data(), ibs->length()); }); } } // namespace lyasio @@ -158,11 +158,6 @@ YASIO_LUA_API int luaopen_yasio(lua_State* L) # else auto yasio_lib = state_view.create_named_table("yasio"); # endif - yasio_lib.new_usertype( - "endpoint", "ip", [](const ip::endpoint& ep) { return ep.ip(); }, "port", [](const ip::endpoint& ep) { return ep.port(); }); - yasio_lib.new_usertype( - "io_transport", "remote_endpoint", [](io_transport* transport) { return transport->remote_endpoint(); }, "local_endpoint", - [](io_transport* transport) { return transport->local_endpoint(); }); yasio_lib.new_usertype( "io_event", "kind", &io_event::kind, "status", &io_event::status, "passive", [](io_event* e) { return !!e->passive(); }, "packet", [](io_event* ev, sol::variadic_args args, sol::this_state s) { @@ -175,14 +170,14 @@ YASIO_LUA_API int luaopen_yasio(lua_State* L) switch (buffer_type) { case lyasio::BUFFER_RAW: - return sol::make_object(L, cxx17::string_view{packet_data(pkt), packet_len(pkt)}); + return sol::make_object(L, std::string_view{packet_data(pkt), packet_len(pkt)}); case lyasio::BUFFER_FAST: - return sol::make_object(L, cxx14::make_unique(forward_packet((packet_t&&)pkt))); + return sol::make_object(L, std::make_unique(forward_packet((packet_t &&) pkt))); default: - return sol::make_object(L, cxx14::make_unique(forward_packet((packet_t&&)pkt))); + return sol::make_object(L, std::make_unique(forward_packet((packet_t &&) pkt))); } }, - "cindex", &io_event::cindex, "source_id", &io_event::source_id, "transport", &io_event::transport + "cindex", &io_event::cindex, "transport", &io_event::transport # if !defined(YASIO_MINIFY_EVENT) , "timestamp", &io_event::timestamp @@ -197,13 +192,13 @@ YASIO_LUA_API int luaopen_yasio(lua_State* L) std::vector hosts; auto host = channel_eps["host"]; if (host.valid()) - hosts.push_back(io_hostent(host.get(), channel_eps["port"])); + hosts.push_back(io_hostent(host.get(), channel_eps["port"])); else { for (auto item : channel_eps) { auto ep = item.second.as(); - hosts.push_back(io_hostent(ep["host"].get(), ep["port"])); + hosts.push_back(io_hostent(ep["host"].get(), ep["port"])); } } return new (&uninitialized_memory) @@ -263,16 +258,16 @@ YASIO_LUA_API int luaopen_yasio(lua_State* L) sol::overload(static_cast(&io_service::close), static_cast(&io_service::close)), "write", sol::overload( - [](io_service* service, transport_handle_t transport, cxx17::string_view s) { - return service->write(transport, yasio::sbyte_buffer{s.data(), s.data() + s.length()}); + [](io_service* service, transport_handle_t transport, std::string_view s) { + return service->write(transport, tlx::sbyte_buffer{s.data(), s.data() + s.length()}); }, [](io_service* service, transport_handle_t transport, yasio::obstream* obs) { return service->write(transport, std::move(obs->buffer())); }), "write_to", sol::overload( - [](io_service* service, transport_handle_t transport, cxx17::string_view s, cxx17::string_view ip, u_short port) { - return service->write_to(transport, yasio::sbyte_buffer{s.data(), s.data() + s.length()}, ip::endpoint{ip.data(), port}); + [](io_service* service, transport_handle_t transport, std::string_view s, std::string_view ip, u_short port) { + return service->write_to(transport, tlx::sbyte_buffer{s.data(), s.data() + s.length()}, ip::endpoint{ip.data(), port}); }, - [](io_service* service, transport_handle_t transport, yasio::obstream* obs, cxx17::string_view ip, u_short port) { + [](io_service* service, transport_handle_t transport, yasio::obstream* obs, std::string_view ip, u_short port) { return service->write_to(transport, std::move(obs->buffer()), ip::endpoint{ip.data(), port}); }), "native_ptr", [](io_service* service) { return (void*)service; }); @@ -285,8 +280,8 @@ YASIO_LUA_API int luaopen_yasio(lua_State* L) lyasio::register_ibstream(yasio_lib, "ibstream"); lyasio::register_ibstream(yasio_lib, "fast_ibstream"); - yasio_lib["highp_clock"] = &highp_clock; - yasio_lib["highp_time"] = &highp_clock; + yasio_lib["highp_clock"] = &tlx::highp_clock; + yasio_lib["highp_time"] = &tlx::highp_clock; yasio_lib["unwrap_ptr"] = [](lua_State* L) -> int { auto& pkt = *(packet_t*)lua_touserdata(L, 1); @@ -376,11 +371,11 @@ YASIO_LUA_API int luaopen_yasio(lua_State* L) /// customize the type conversion from/to lua namespace kaguya { -// cxx17::string_view +// std::string_view template <> -struct lua_type_traits { - typedef cxx17::string_view get_type; - typedef cxx17::string_view push_type; +struct lua_type_traits { + typedef std::string_view get_type; + typedef std::string_view push_type; static bool strictCheckType(lua_State* l, int index) { return lua_type(l, index) == LUA_TSTRING; } static bool checkType(lua_State* l, int index) { return lua_isstring(l, index) != 0; } @@ -388,7 +383,7 @@ struct lua_type_traits { { size_t size = 0; const char* buffer = lua_tolstring(l, index, &size); - return cxx17::string_view(buffer, size); + return std::string_view(buffer, size); } static int push(lua_State* l, push_type s) { @@ -532,15 +527,15 @@ static void register_obstream(kaguya::LuaTable& lib, const char* usertype, const .addFunction("write_f", &_BaseStream::template write) .addFunction("write_lf", &_BaseStream::template write) .addStaticFunction("write_v", - [](_BaseStream* obs, cxx17::string_view sv, kaguya::VariadicArgType args) { + [](_BaseStream* obs, std::string_view sv, kaguya::VariadicArgType args) { int lfl = -1; if (args.size() > 0) lfl = static_cast(args[0]); return lyasio::obstream_write_v(obs, sv, lfl); }) - .addFunction("write_bytes", static_cast(&_BaseStream::write_bytes)) + .addFunction("write_bytes", static_cast(&_BaseStream::write_bytes)) .addFunction("length", &_BaseStream::length) - .addStaticFunction("to_string", [](_BaseStream* obs) { return cxx17::string_view(obs->data(), obs->length()); })); + .addStaticFunction("to_string", [](_BaseStream* obs) { return std::string_view(obs->data(), obs->length()); })); // ##-- obstream lib[usertype].setClass(userclass); } @@ -570,10 +565,10 @@ static void register_ibstream(kaguya::LuaTable& lib, const char* usertype, const length_field_bits = static_cast(args[0]); return lyasio::ibstream_read_v(ibs, length_field_bits); }) - .addFunction("read_bytes", static_cast(&_StreamView::read_bytes)) + .addFunction("read_bytes", static_cast(&_StreamView::read_bytes)) .addFunction("seek", &_StreamView::seek) .addFunction("length", &_StreamView::length) - .addStaticFunction("to_string", [](_StreamView* ibs) { return cxx17::string_view(ibs->data(), ibs->length()); })); + .addStaticFunction("to_string", [](_StreamView* ibs) { return std::string_view(ibs->data(), ibs->length()); })); // ##-- ibstream lib[usertype].setClass(userclass); @@ -601,21 +596,21 @@ YASIO_LUA_API int luaopen_yasio(lua_State* L) auto& pkt = ev->packet(); if (is_packet_empty(pkt)) return std::unique_ptr{}; - return cxx14::make_unique(forward_packet((packet_t&&)pkt)); + return cxx14::make_unique(forward_packet((packet_t &&) pkt)); }) .addStaticFunction("raw_packet", [](io_event* ev) { auto& pkt = ev->packet(); if (is_packet_empty(pkt)) - return cxx17::string_view{""}; - return cxx17::string_view{packet_data(pkt), packet_len(pkt)}; + return std::string_view{""}; + return std::string_view{packet_data(pkt), packet_len(pkt)}; }) .addStaticFunction("fast_packet", [](io_event* ev) { auto& pkt = ev->packet(); if (is_packet_empty(pkt)) return std::unique_ptr{}; - return cxx14::make_unique(forward_packet((packet_t&&)pkt)); + return cxx14::make_unique(forward_packet((packet_t &&) pkt)); }) .addFunction("cindex", &io_event::cindex) .addFunction("transport", &io_event::transport) @@ -623,7 +618,7 @@ YASIO_LUA_API int luaopen_yasio(lua_State* L) # if !YASIO_LUA_ENABLE_GLOBAL state["yasio"] = yasio_lib; # endif - bool succeed = state.dostring(R"( + bool succeed = state.dostring(R"( local yasio = yasio; yasio.io_event.packet = function(self, buffer_type) if buffer_type == yasio.BUFFER_RAW then @@ -657,16 +652,16 @@ end static_cast(&io_service::close)) .addOverloadedFunctions( "write", - [](io_service* service, transport_handle_t transport, cxx17::string_view s) { + [](io_service* service, transport_handle_t transport, std::string_view s) { return service->write(transport, yasio::sbyte_buffer(s.data(), s.data() + s.length())); }, [](io_service* service, transport_handle_t transport, yasio::obstream* obs) { return service->write(transport, std::move(obs->buffer())); }) .addOverloadedFunctions( "write_to", - [](io_service* service, transport_handle_t transport, cxx17::string_view s, cxx17::string_view ip, u_short port) { + [](io_service* service, transport_handle_t transport, std::string_view s, std::string_view ip, u_short port) { return service->write_to(transport, yasio::sbyte_buffer(s.data(), s.data() + s.length()), ip::endpoint{ip.data(), port}); }, - [](io_service* service, transport_handle_t transport, yasio::obstream* obs, cxx17::string_view ip, u_short port) { + [](io_service* service, transport_handle_t transport, yasio::obstream* obs, std::string_view ip, u_short port) { return service->write_to(transport, std::move(obs->buffer()), ip::endpoint{ip.data(), port}); }) .addStaticFunction("set_option", @@ -800,7 +795,7 @@ end char version[32]; snprintf(version, sizeof(version), "%x.%x.%x", (YASIO_VERSION_NUM >> 16) & 0xff, (YASIO_VERSION_NUM >> 8) & 0xff, YASIO_VERSION_NUM & 0xff); - yasio_lib["version"] = std::string(version); + YASIO_EXPORT_ANY(version); return yasio_lib.push(); /* return 'yasio' table */ } diff --git a/yasio/bindings/yasio_axlua.cpp b/yasio/bindings/yasio_axlua.cpp index ccd455283..7fa79c8da 100644 --- a/yasio/bindings/yasio_axlua.cpp +++ b/yasio/bindings/yasio_axlua.cpp @@ -30,15 +30,12 @@ SOFTWARE. #include "yasio/bindings/yasio_axlua.hpp" #include "yasio/bindings/lyasio.hpp" #include "yasio/object_pool.hpp" -#include "yasio/ref_ptr.hpp" -#include "yasio/string_view.hpp" -// A workaround to fix compile issue caused by `CCPlatformMacros.h` doesn't handle `__has_attribute` it properly -# if !__has_attribute(format) -# undef __has_attribute -# endif -#include "cocos2d.h" -using namespace cocos2d; +#include "axmol/base/Director.h" +#include "axmol/base/Scheduler.h" +#include "axmol/tlx/memory.hpp" + +using namespace ax; namespace lyasio { @@ -60,7 +57,15 @@ struct TimerObject static uintptr_t s_timerId; DEFINE_FAST_OBJECT_POOL_ALLOCATION(TimerObject, 128) - YASIO__DEFINE_REFERENCE_CLASS + + void retain() { ++_referenceCount; } + void release() + { + if (--_referenceCount == 0) + delete this; + } + + uint32_t _referenceCount{1}; }; uintptr_t TimerObject::s_timerId = 0; @@ -68,11 +73,11 @@ static TIMER_ID loop(unsigned int n, float interval, vcallback_t callback) { if (n > 0 && interval >= 0) { - yasio::ref_ptr timerObj(new TimerObject(std::move(callback))); + tlx::retain_ptr timerObj(new TimerObject(std::move(callback)), tlx::adopt_object); auto timerId = reinterpret_cast(++TimerObject::s_timerId); - std::string key = StringUtils::format("LSTMR#%p", timerId); + std::string key = fmt::format("LSTMR#{}", fmt::ptr(timerId)); Director::getInstance()->getScheduler()->schedule( [timerObj]( @@ -90,10 +95,10 @@ static TIMER_ID delay(float delay, vcallback_t callback) { if (delay > 0) { - yasio::ref_ptr timerObj(new TimerObject(std::move(callback))); + tlx::retain_ptr timerObj(new TimerObject(std::move(callback)), tlx::adopt_object); auto timerId = reinterpret_cast(++TimerObject::s_timerId); - std::string key = StringUtils::format("LSTMR#%p", timerId); + std::string key = fmt::format("LSTMR#{}", fmt::ptr(timerId)); Director::getInstance()->getScheduler()->schedule( [timerObj]( float /*dt*/) { // lambda expression hold the reference of timerObj automatically. @@ -108,7 +113,7 @@ static TIMER_ID delay(float delay, vcallback_t callback) static void kill(TIMER_ID timerId) { - std::string key = StringUtils::format("LSTMR#%p", timerId); + std::string key = fmt::format("LSTMR#{}", fmt::ptr(timerId)); Director::getInstance()->getScheduler()->unschedule(key, STIMER_TARGET_VALUE); } YASIO_LUA_API void clear() diff --git a/yasio/bindings/yasio_ni.cpp b/yasio/bindings/yasio_ni.cpp index d2a5bcdd1..68b1af2d9 100644 --- a/yasio/bindings/yasio_ni.cpp +++ b/yasio/bindings/yasio_ni.cpp @@ -32,7 +32,7 @@ SOFTWARE. #include #include #include "yasio/yasio.hpp" -#include "yasio/split.hpp" +#include "yasio/tlx/split.hpp" #if defined(_WINDLL) # define YASIO_NI_API __declspec(dllexport) @@ -46,8 +46,8 @@ using namespace yasio; namespace { -inline int svtoi(cxx17::string_view& sv) { return !sv.empty() ? atoi(sv.data()) : 0; } -inline const char* svtoa(cxx17::string_view& sv) { return !sv.empty() ? sv.data() : ""; } +inline int svtoi(std::string_view& sv) { return !sv.empty() ? atoi(sv.data()) : 0; } +inline const char* svtoa(std::string_view& sv) { return !sv.empty() ? sv.data() : ""; } } // namespace extern "C" { @@ -60,12 +60,12 @@ YASIO_NI_API void yasio_init_globals(void(YASIO_INTEROP_DECL* pfn)(int level, co YASIO_NI_API void yasio_cleanup_globals() { io_service::cleanup_globals(); } struct yasio_io_event { - int kind; // event kind - int channel; // channel index + int kind; // event kind + int channel; // channel index void* thandle; // transport union { void* hmsg; // io_packet* - int ec; // error code + int ec; // error code }; void* user; // user data }; @@ -154,16 +154,19 @@ YASIO_NI_API void yasio_set_option(void* service_ptr, int opt, const char* pszAr // split args std::string strArgs = pszArgs; - std::array args; + std::array args; int argc = 0; - yasio::split_if(&strArgs.front(), ';', [&](char* s, char* e) { - if (e) { - *e = '\0'; // to c style string - args[argc++] = cxx17::string_view(s, e - s); - } else { - args[argc++] = cxx17::string_view{s}; + tlx::split_until(&strArgs.front(), ';', [&](char* s, char* e) { + if (e) + { + *e = '\0'; // to c style string + args[argc++] = std::string_view(s, e - s); } - return (argc < YASIO_MAX_OPTION_ARGC); + else + { + args[argc++] = std::string_view{s}; + } + return (argc >= YASIO_MAX_OPTION_ARGC); }); switch (opt) @@ -233,7 +236,7 @@ YASIO_NI_API int yasio_write(void* service_ptr, void* thandle, const char* bytes { auto service = reinterpret_cast(service_ptr); if (service) - return service->write(reinterpret_cast(thandle), yasio::sbyte_buffer(bytes, bytes + len)); + return service->write(reinterpret_cast(thandle), tlx::sbyte_buffer(bytes, bytes + len)); return -1; } YASIO_NI_API int yasio_forward(void* service_ptr, void* thandle, void* bufferHandle, @@ -324,6 +327,6 @@ YASIO_NI_API void yasio_ob_write_bytes(void* obs_ptr, void* bytes, int len) obs->write_bytes(bytes, len); } -YASIO_NI_API long long yasio_highp_time(void) { return highp_clock(); } -YASIO_NI_API long long yasio_highp_clock(void) { return highp_clock(); } +YASIO_NI_API long long yasio_highp_time(void) { return tlx::highp_clock(); } +YASIO_NI_API long long yasio_highp_clock(void) { return tlx::highp_clock(); } } diff --git a/yasio/bindings/yasio_sol.hpp b/yasio/bindings/yasio_sol.hpp index 604056ca1..70d51851d 100644 --- a/yasio/bindings/yasio_sol.hpp +++ b/yasio/bindings/yasio_sol.hpp @@ -31,33 +31,33 @@ SOFTWARE. # include "sol2/sol.hpp" // sol2-2.x #endif -// use cxx17::string_view workaround with std::string_view +// use std::string_view workaround with std::string_view #if !YASIO__HAS_CXX17 namespace sol { namespace stack { template <> -struct pusher { - static int push(lua_State* L, const cxx17::string_view& str) +struct pusher { + static int push(lua_State* L, const std::string_view& str) { lua_pushlstring(L, !str.empty() ? str.c_str() : "", str.length()); return 1; } }; template <> -struct getter { - static cxx17::string_view get(lua_State* L, int index, record& tracking) +struct getter { + static std::string_view get(lua_State* L, int index, record& tracking) { tracking.use(1); // THIS IS THE ONLY BIT THAT CHANGES size_t len = 0; const char* s = lua_tolstring(L, index, &len); - return cxx17::string_view(s, len); + return std::string_view(s, len); } }; } // namespace stack template <> -struct lua_type_of : std::integral_constant {}; +struct lua_type_of : std::integral_constant {}; } // namespace sol #endif diff --git a/yasio/buffer_alloc.hpp b/yasio/buffer_alloc.hpp deleted file mode 100644 index 9a4981b30..000000000 --- a/yasio/buffer_alloc.hpp +++ /dev/null @@ -1,119 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// A multi-platform support c++11 library with focus on asynchronous socket I/O for any -// client application. -////////////////////////////////////////////////////////////////////////////////////////// -/* -The MIT License (MIT) - -Copyright (c) 2012-2025 HALX99 - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -#ifndef YASIO__BUFFER_ALLOC_HPP -#define YASIO__BUFFER_ALLOC_HPP -#include -#include -#include -#include -#include -#include "yasio/compiler/feature_test.hpp" -#include "yasio/type_traits.hpp" - -#define _YASIO_VERIFY_RANGE(cond, mesg) \ - do \ - { \ - if (cond) \ - ; /* contextually convertible to bool paranoia */ \ - else \ - { \ - throw std::out_of_range(mesg); \ - } \ - \ - } while (false) - -namespace yasio -{ -template -struct buffer_allocator_traits { - using value_type = typename _Alty::value_type; - using size_type = size_t; - static YASIO__CONSTEXPR size_type max_size() { return static_cast(-1) / sizeof(value_type); } - static value_type* reallocate(void* block, size_t size, size_t new_size) - { - return static_cast(_Alty::reallocate(block, size, new_size * sizeof(value_type))); - } - static void deallocate(void* block, size_t size) { _Alty::deallocate(block, size); } -}; -template ::value, int> = 0> -struct buffer_allocator { - using value_type = _Ty; - static value_type* reallocate(void* block, size_t /*size*/, size_t new_size) - { - return static_cast(::realloc(block, new_size * sizeof(value_type))); - } - static void deallocate(void* block, size_t /*size*/) { ::free(block); } -}; -template ::value, int> = 0> -struct std_buffer_allocator { - using value_type = _Ty; - static value_type* reallocate(void* block, size_t size, size_t new_size) - { - if (!block) - return new (std::nothrow) value_type[new_size]; - void* new_block = nullptr; - if (new_size) - { - if (new_size <= size) - return block; - new_block = new (std::nothrow) value_type[new_size]; - if (new_block) - memcpy(new_block, block, size); - } - delete[] (value_type*)block; - return (value_type*)new_block; - } - static void deallocate(void* block, size_t /*size*/) { delete[] (value_type*)block; } -}; -template -struct construct_helper { - template - static _Ty* construct_at(_Ty* p, Args&&... args) - { - return ::new (static_cast(p)) _Ty(std::forward(args)...); - } -}; -template -struct construct_helper<_Ty, false> { - template - static _Ty* construct_at(_Ty* p, Args&&... args) - { - return ::new (static_cast(p)) _Ty{std::forward(args)...}; - } -}; - -template -inline _Ty* construct_at(_Ty* p, Args&&... args) -{ - return construct_helper<_Ty, std::is_constructible<_Ty, Args&&...>::value>::construct_at(p, std::forward(args)...); -} - -} // namespace yasio - -#endif diff --git a/yasio/compiler/feature_test.hpp b/yasio/compiler/feature_test.hpp index a0ddf9ce7..fa4e839d4 100644 --- a/yasio/compiler/feature_test.hpp +++ b/yasio/compiler/feature_test.hpp @@ -259,18 +259,7 @@ SOFTWARE. #define YASIO__STD ::std:: -#if YASIO__HAS_CXX14 -namespace cxx14 -{ -using namespace std; -}; -#endif - -#if YASIO__HAS_CXX17 -namespace cxx17 -{ -using namespace std; -}; -#endif +// The yasio is the original tlx provider +#define _TLX ::tlx:: #endif diff --git a/yasio/config.hpp b/yasio/config.hpp index 403038d48..7fea9b7a7 100644 --- a/yasio/config.hpp +++ b/yasio/config.hpp @@ -199,7 +199,7 @@ SOFTWARE. /* ** The yasio version macros */ -#define YASIO_VERSION_NUM 0x040303 +#define YASIO_VERSION_NUM 0x040400 /* ** The macros used by io_service. diff --git a/yasio/endian_portable.hpp b/yasio/endian_portable.hpp index 6662ca7a3..8359458b5 100644 --- a/yasio/endian_portable.hpp +++ b/yasio/endian_portable.hpp @@ -146,8 +146,8 @@ template <> struct byte_order_impl { static inline fp16_t host_to_network(fp16_t value) { - uint16_t& underlying_value = (uint16_t&)value; - YASIO__SWAP_SHORT(underlying_value); + auto underlying_value = reinterpret_cast(&value); + YASIO__SWAP_SHORT(*underlying_value); return value; } static inline fp16_t network_to_host(fp16_t value) { return host_to_network(value); } diff --git a/yasio/ibstream.hpp b/yasio/ibstream.hpp index 9f7bb6361..b5ff39301 100644 --- a/yasio/ibstream.hpp +++ b/yasio/ibstream.hpp @@ -123,8 +123,8 @@ class binary_reader_impl { using convert_traits_type = _Traits; using this_type = binary_reader_impl<_Traits>; binary_reader_impl() { this->reset("", 0); } - binary_reader_impl(const sbyte_buffer& d) { this->reset(d); } - binary_reader_impl(const cxx17::string_view& d) { this->reset(d); } + binary_reader_impl(const tlx::sbyte_buffer& d) { this->reset(d); } + binary_reader_impl(const std::string_view& d) { this->reset(d); } binary_reader_impl(const void* data, size_t size) { this->reset(data, size); } template binary_reader_impl(const binary_writer_impl<_Traits, _BufferType>* obs) @@ -143,8 +143,8 @@ class binary_reader_impl { ~binary_reader_impl() {} - void reset(const sbyte_buffer& d) { reset(d.data(), d.size()); } - void reset(const cxx17::string_view& d) { reset(d.data(), d.length()); } + void reset(const tlx::sbyte_buffer& d) { reset(d.data(), d.size()); } + void reset(const std::string_view& d) { reset(d.data(), d.length()); } void reset(const void* data, size_t size) { first_ = ptr_ = static_cast(data); @@ -165,7 +165,7 @@ class binary_reader_impl { int read_varint(int size) { - size = yasio::clamp(size, 1, YASIO_SSIZEOF(int)); + size = std::clamp(size, 1, YASIO_SSIZEOF(int)); int value = 0; ::memcpy(&value, consume(size), size); @@ -173,7 +173,7 @@ class binary_reader_impl { } /* read blob data with '7bit encoded int' length field */ - cxx17::string_view read_v() + std::string_view read_v() { int count = read_ix(); return read_bytes(count); @@ -210,15 +210,15 @@ class binary_reader_impl { ::memcpy(oav, consume(len), len); } - cxx17::string_view read_v32() { return read_v_fx(); } - cxx17::string_view read_v16() { return read_v_fx(); } - cxx17::string_view read_v8() { return read_v_fx(); } + std::string_view read_v32() { return read_v_fx(); } + std::string_view read_v16() { return read_v_fx(); } + std::string_view read_v8() { return read_v_fx(); } - cxx17::string_view read_bytes(int len) + std::string_view read_bytes(int len) { if (len > 0) - return cxx17::string_view(consume(len), len); - return cxx17::string_view{}; + return std::string_view(consume(len), len); + return std::string_view{}; } bool empty() const { return first_ == last_; } @@ -264,7 +264,7 @@ class binary_reader_impl { } template - inline cxx17::string_view read_v_fx() + inline std::string_view read_v_fx() { _LenT n = this->read<_LenT>(); if (n > 0) @@ -272,11 +272,11 @@ class binary_reader_impl { return {}; } - cxx17::string_view range_view(size_t start, size_t end) + std::string_view range_view(size_t start, size_t end) { if (start <= end && (first_ + end) <= last_) - return cxx17::string_view{first_ + start, end - start}; - return cxx17::string_view{}; + return std::string_view{first_ + start, end - start}; + return std::string_view{}; } bool eof() const { return ptr_ == last_; } @@ -305,7 +305,7 @@ template class basic_ibstream : public binary_reader_impl<_Traits> { public: basic_ibstream() {} - basic_ibstream(sbyte_buffer blob) : binary_reader_impl<_Traits>(), blob_(std::move(blob)) { this->reset(blob_.data(), static_cast(blob_.size())); } + basic_ibstream(tlx::sbyte_buffer blob) : binary_reader_impl<_Traits>(), blob_(std::move(blob)) { this->reset(blob_.data(), static_cast(blob_.size())); } basic_ibstream(const basic_obstream<_Traits>* obs) : binary_reader_impl<_Traits>(), blob_(obs->buffer()) { this->reset(blob_.data(), static_cast(blob_.size())); @@ -332,7 +332,7 @@ class basic_ibstream : public binary_reader_impl<_Traits> { } protected: - sbyte_buffer blob_; + tlx::sbyte_buffer blob_; }; using ibstream_view = binary_reader_impl>; diff --git a/yasio/impl/mbedtls.hpp b/yasio/impl/mbedtls.hpp index 0bce89117..1174bd2f0 100644 --- a/yasio/impl/mbedtls.hpp +++ b/yasio/impl/mbedtls.hpp @@ -31,7 +31,7 @@ SOFTWARE. #if YASIO_SSL_BACKEND == 2 -# include "yasio/split.hpp" +# include "yasio/tlx/split.hpp" YASIO__DECL yssl_ctx_st* yssl_ctx_new(const yssl_options& opts) { @@ -51,7 +51,7 @@ YASIO__DECL yssl_ctx_st* yssl_ctx_new(const yssl_options& opts) YASIO_LOG("mbedtls_ssl_config_defaults fail with ret=%d", ret); // rgn engine - cxx17::string_view pers = opts.client ? cxx17::string_view{YASIO_SSL_PIN, YASIO_SSL_PIN_LEN} : cxx17::string_view{YASIO_SSL_PON, YASIO_SSL_PON_LEN}; + std::string_view pers = opts.client ? std::string_view{YASIO_SSL_PIN, YASIO_SSL_PIN_LEN} : std::string_view{YASIO_SSL_PON, YASIO_SSL_PON_LEN}; ret = ::mbedtls_ctr_drbg_seed(&ctx->ctr_drbg, ::mbedtls_entropy_func, &ctx->entropy, (const unsigned char*)pers.data(), pers.length()); if (ret != 0) { @@ -65,9 +65,9 @@ YASIO__DECL yssl_ctx_st* yssl_ctx_new(const yssl_options& opts) if (yasio__valid_str(opts.crtfile_)) // the cafile_ must be full path { int fail_count = 0; - yasio::split( + tlx::split( opts.crtfile_, ',', [&](char* first, char* last) { - yasio::split_term null_term(last); + tlx::split_term_guard null_term(last); if ((ret = ::mbedtls_x509_crt_parse_file(&ctx->cert, first)) != 0) { diff --git a/yasio/impl/openssl.hpp b/yasio/impl/openssl.hpp index f25eb2084..7cdd55907 100644 --- a/yasio/impl/openssl.hpp +++ b/yasio/impl/openssl.hpp @@ -31,7 +31,7 @@ SOFTWARE. #if YASIO_SSL_BACKEND == 1 // OpenSSL -# include "yasio/split.hpp" +# include "yasio/tlx/split.hpp" // The ssl error mask (1 << 31), a little hack, but works # define YSSL_ERR_MASK 0x80000000 @@ -53,8 +53,8 @@ YASIO__DECL yssl_ctx_st* yssl_ctx_new(const yssl_options& opts) if (yasio__valid_str(opts.crtfile_)) { // CAfile for verify fail_count = 0; - yasio::split(opts.crtfile_, ',', [&](char* first, char* last) { - yasio::split_term null_term(last); + tlx::split(opts.crtfile_, ',', [&](char* first, char* last) { + tlx::split_term_guard null_term(last); # if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) /* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */ diff --git a/yasio/impl/poll_io_watcher.hpp b/yasio/impl/poll_io_watcher.hpp index 7c65fd99d..bc41a1506 100644 --- a/yasio/impl/poll_io_watcher.hpp +++ b/yasio/impl/poll_io_watcher.hpp @@ -6,7 +6,7 @@ // Copyright (c) 2012-2025 HALX99 (halx99 at live dot com) #ifndef YASIO__POLL_IO_WATCHER_HPP #define YASIO__POLL_IO_WATCHER_HPP -#include "yasio/pod_vector.hpp" +#include "yasio/tlx/vector.hpp" #include "yasio/impl/socket.hpp" #include "yasio/impl/select_interrupter.hpp" @@ -77,7 +77,7 @@ class poll_io_watcher { } return underlying_events; } - static void pollfd_mod(yasio::pod_vector& fdset, socket_native_type fd, int add_events, int remove_events) + static void pollfd_mod(tlx::vector& fdset, socket_native_type fd, int add_events, int remove_events) { auto it = std::find_if(fdset.begin(), fdset.end(), [fd](const pollfd& pfd) { return pfd.fd == fd; }); if (it != fdset.end()) @@ -96,8 +96,8 @@ class poll_io_watcher { } protected: - yasio::pod_vector events_; - yasio::pod_vector revents_; + tlx::vector events_; + tlx::vector revents_; select_interrupter interrupter_; }; diff --git a/yasio/io_service.cpp b/yasio/io_service.cpp index 615689c41..59681f4c4 100644 --- a/yasio/io_service.cpp +++ b/yasio/io_service.cpp @@ -46,6 +46,10 @@ SOFTWARE. #include "yasio/wtimer_hres.hpp" +#include "yasio/tlx/string.hpp" + +#include "yasio/tlx/file_io.hpp" + #if defined(YASIO_ENABLE_KCP) struct yasio_kcp_options { int kcp_conv_ = 0; @@ -73,7 +77,7 @@ struct yasio_kcp_options { do \ { \ auto& __cprint = __get_cprint(); \ - auto __msg = ::yasio::strfmt(127, "[yasio][%lld]" format "\n", ::yasio::clock(), ##__VA_ARGS__); \ + auto __msg = ::yasio::strfmt(127, "[yasio][%lld]" format "\n", ::tlx::clock<::tlx::system_clock_t>(), ##__VA_ARGS__); \ if (__cprint) \ __cprint(level, __msg.c_str()); \ else { \ @@ -320,11 +324,11 @@ int io_channel::configure_multicast_group(bool onoff) return socket_->set_optval(IPPROTO_IPV6, onoff ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP, &mreq_v6, (int)sizeof(mreq_v6)); } } -void io_channel::set_host(cxx17::string_view host) +void io_channel::set_host(std::string_view host) { if (this->remote_host_ != host) { - cxx17::assign(this->remote_host_, host); + this->remote_host_ = host; yasio__setbits(properties_, YCPF_HOST_DIRTY); } } @@ -360,7 +364,7 @@ int io_channel::__builtin_decode_len(void* d, int n) return n; } // -------------------- io_transport --------------------- -io_transport::io_transport(io_channel* ctx, xxsocket_ptr&& s) : ctx_(ctx) +io_transport::io_transport(io_channel* ctx, xxsocket_ptr&& s) : ctx_(ctx), buffer_(yasio__max_rcvbuf) { this->state_ = io_base::state::OPENED; this->socket_ = std::move(s); @@ -372,15 +376,15 @@ const print_fn2_t& io_transport::__get_cprint() const { return ctx_->get_service int io_transport::write(io_send_buffer&& buffer, completion_cb_t&& handler) { int n = static_cast(buffer.size()); - send_queue_.emplace(cxx14::make_unique(std::move(buffer), std::move(handler))); + send_queue_.emplace(std::make_unique(std::move(buffer), std::move(handler))); get_service().wakeup(); return n; } -int io_transport::do_read(int revent, int& error, highp_time_t&) +int io_transport::do_read(int revent, int& error, tlx::highp_time_t&) { return this->call_read(buffer_.data() + offset_, static_cast(buffer_.size() - offset_), revent, error); } -bool io_transport::do_write(highp_time_t& wait_duration) +bool io_transport::do_write(tlx::highp_time_t& wait_duration) { bool ret = false; do @@ -623,7 +627,7 @@ int io_transport_udp::write(io_send_buffer&& buffer, completion_cb_t&& handler) int io_transport_udp::write_to(io_send_buffer&& buffer, const ip::endpoint& to, completion_cb_t&& handler) { int n = static_cast(buffer.size()); - send_queue_.emplace(cxx14::make_unique(std::move(buffer), std::move(handler), to)); + send_queue_.emplace(std::make_unique(std::move(buffer), std::move(handler), to)); get_service().wakeup(); return n; } @@ -660,7 +664,7 @@ void io_transport_udp::set_primitives() }; } } -int io_transport_udp::handle_input(char* data, int bytes_transferred, int& /*error*/, highp_time_t&) +int io_transport_udp::handle_input(char* data, int bytes_transferred, int& /*error*/, tlx::highp_time_t&) { // pure udp, dispatch to upper layer directly auto& service = get_service(); if (!service.options_.forward_packet_) @@ -707,7 +711,7 @@ void io_transport_kcp::set_primitives() return nsent; }; } -bool io_transport_kcp::do_write(highp_time_t& wait_duration) +bool io_transport_kcp::do_write(tlx::highp_time_t& wait_duration) { bool ret = io_transport_udp::do_write(wait_duration); @@ -720,7 +724,7 @@ bool io_transport_kcp::do_write(highp_time_t& wait_duration) return ret; } -int io_transport_kcp::do_read(int revent, int& error, highp_time_t& wait_duration) +int io_transport_kcp::do_read(int revent, int& error, tlx::highp_time_t& wait_duration) { int n = this->call_read(&rawbuf_.front(), static_cast(rawbuf_.size()), revent, error); if (n > 0) @@ -742,7 +746,7 @@ int io_transport_kcp::do_read(int revent, int& error, highp_time_t& wait_duratio } return n; } -int io_transport_kcp::handle_input(char* buf, int len, int& error, highp_time_t& wait_duration) +int io_transport_kcp::handle_input(char* buf, int len, int& error, tlx::highp_time_t& wait_duration) { // ikcp in event always in service thread, so no need to lock if (0 == ::ikcp_input(kcp_, buf, len)) @@ -867,7 +871,7 @@ void io_service::initialize(const io_hostent* channel_eps, int channel_count) create_channels(channel_eps, channel_count); #if !defined(YASIO_USE_CARES) - life_mutex_ = std::make_shared(); + life_mutex_ = std::make_shared(); life_token_ = std::make_shared(); #endif this->state_ = io_service::state::IDLE; @@ -877,7 +881,7 @@ void io_service::finalize() if (this->state_ == io_service::state::IDLE) { #if !defined(YASIO_USE_CARES) - std::unique_lock lck(*life_mutex_); + std::unique_lock lck(*life_mutex_); life_token_.reset(); #endif destroy_channels(); @@ -919,7 +923,7 @@ void io_service::clear_transports() for (auto transport : transports_) { cleanup_io(transport); - yasio::invoke_dtor(transport); + tlx::invoke_dtor(transport); this->tpool_.push_back(transport); } transports_.clear(); @@ -972,7 +976,7 @@ void io_service::run() do { - this->current_time_ = yasio::steady_clock_t::now(); + this->current_time_ = tlx::steady_clock_t::now(); auto waitd_usec = get_timeout(this->wait_duration_); // Gets current wait duration #if defined(YASIO_USE_CARES) @@ -1188,7 +1192,7 @@ void io_service::handle_close(transport_handle_t thandle) cleanup_channel(ctx, false); } } -int io_service::write(transport_handle_t transport, sbyte_buffer buffer, completion_cb_t handler) +int io_service::write(transport_handle_t transport, tlx::sbyte_buffer buffer, completion_cb_t handler) { if (transport && transport->is_open()) return !buffer.empty() ? transport->write(io_send_buffer{std::move(buffer)}, std::move(handler)) : 0; @@ -1208,7 +1212,7 @@ int io_service::forward(transport_handle_t transport, const void* buf, size_t le return -1; } } -int io_service::write_to(transport_handle_t transport, sbyte_buffer buffer, const ip::endpoint& to, completion_cb_t handler) +int io_service::write_to(transport_handle_t transport, tlx::sbyte_buffer buffer, const ip::endpoint& to, completion_cb_t handler) { if (transport && transport->is_open()) return !buffer.empty() ? transport->write_to(io_send_buffer{std::move(buffer)}, to, std::move(handler)) : 0; @@ -1370,7 +1374,7 @@ void io_service::ares_getaddrinfo_cb(void* data, int status, int /*timeouts*/, a auto __get_cprint = [&]() -> const print_fn2_t& { return current_service.options_.print_; }; if (!ctx->remote_eps_.empty()) { - ctx->query_success_time_ = highp_clock(); + ctx->query_success_time_ = tlx::highp_clock(); # if defined(YASIO_ENABLE_ARES_PROFILER) YASIO_KLOGD("[index: %d] ares_getaddrinfo_cb: query %s succeed, cost:%g(ms)", ctx->index_, ctx->remote_host_.c_str(), (ctx->query_success_time_ - ctx->query_start_time_) / 1000.0); @@ -1384,7 +1388,7 @@ void io_service::ares_getaddrinfo_cb(void* data, int status, int /*timeouts*/, a } current_service.wakeup(); } -int io_service::ares_get_fds(socket_native_type* socks, highp_time_t& waitd_usec) +int io_service::ares_get_fds(socket_native_type* socks, tlx::highp_time_t& waitd_usec) { int nfds = 0; if (ares_outstanding_work_) @@ -1433,7 +1437,7 @@ void io_service::recreate_ares_channel() options.sock_state_cb = io_service::ares_sock_state_cb; options.sock_state_cb_data = this; # if defined(__linux__) && !defined(__ANDROID__) - if (yasio::is_regular_file(YASIO_SYSTEMD_RESOLV_PATH)) + if (tlx::is_regular_file(YASIO_SYSTEMD_RESOLV_PATH)) { options.resolvconf_path = strndup(YASIO_SYSTEMD_RESOLV_PATH, YASIO_SYSTEMD_RESOLV_PATH_LEN); optmask |= ARES_OPT_RESOLVCONF; @@ -1760,7 +1764,7 @@ void io_service::deallocate_transport(transport_handle_t t) { if (t->is_valid()) { - yasio::invoke_dtor(t); + tlx::invoke_dtor(t); this->tpool_.push_back(t); } } @@ -1814,7 +1818,7 @@ bool io_service::do_read(transport_handle_t transport) } else if (n > 0) { // forward packet, don't perform unpack, it's useful for implement streaming based protocol, like http, websocket and ... - this->forward_packet(transport->cindex(), io_packet_view{transport->buffer_.data(), n}, transport); + this->forward_packet(transport->cindex(), io_packet_view(transport->buffer_.data(), n), transport); } } else @@ -1959,7 +1963,7 @@ void io_service::process_deferred_events() if (!options_.no_dispatch_ && dispatch() > 0) this->wait_duration_ = 0; } -highp_time_t io_service::get_timeout(highp_time_t usec) +tlx::highp_time_t io_service::get_timeout(tlx::highp_time_t usec) { this->wait_duration_ = this->sched_freq_; // Reset next wait duration per frame @@ -2062,10 +2066,10 @@ void io_service::start_query(io_channel* ctx) #endif #if !defined(YASIO_USE_CARES) // init async name query thread state - auto resolving_host = ctx->remote_host_; - auto resolving_port = ctx->remote_port_; - std::weak_ptr weak_mutex = life_mutex_; - std::weak_ptr life_token = life_token_; + auto resolving_host = ctx->remote_host_; + auto resolving_port = ctx->remote_port_; + std::weak_ptr weak_mutex = life_mutex_; + std::weak_ptr life_token = life_token_; std::thread async_resolv_thread([this, life_token, weak_mutex, resolving_host, resolving_port, ctx] { // check life token if (life_token.use_count() < 1) @@ -2079,7 +2083,7 @@ void io_service::start_query(io_channel* ctx) auto pmtx = weak_mutex.lock(); if (!pmtx) return; - cxx17::shared_lock lck(*pmtx); + tlx::shared_lock lck(*pmtx); // check life token again, when io_service cleanup done, life_token's use_count will be 0, // otherwise, we can safe to do follow assignments. @@ -2088,7 +2092,7 @@ void io_service::start_query(io_channel* ctx) if (error == 0) { ctx->remote_eps_ = std::move(remote_eps); - ctx->query_success_time_ = highp_clock(); + ctx->query_success_time_ = tlx::highp_clock(); # if defined(YASIO_ENABLE_ARES_PROFILER) YASIO_KLOGD("[index: %d] query %s succeed, cost: %g(ms)", ctx->index_, ctx->remote_host_.c_str(), (ctx->query_success_time_ - ctx->query_start_time_) / 1000.0); @@ -2209,22 +2213,22 @@ void io_service::set_option_internal(int opt, va_list ap) // lgtm [cpp/poorly-do break; #endif case YOPT_S_CONNECT_TIMEOUT: - options_.connect_timeout_ = static_cast(va_arg(ap, int)) * std::micro::den; + options_.connect_timeout_ = static_cast(va_arg(ap, int)) * std::micro::den; break; case YOPT_S_CONNECT_TIMEOUTMS: - options_.connect_timeout_ = static_cast(va_arg(ap, int)) * std::milli::den; + options_.connect_timeout_ = static_cast(va_arg(ap, int)) * std::milli::den; break; case YOPT_S_DNS_CACHE_TIMEOUT: - options_.dns_cache_timeout_ = static_cast(va_arg(ap, int)) * std::micro::den; + options_.dns_cache_timeout_ = static_cast(va_arg(ap, int)) * std::micro::den; break; case YOPT_S_DNS_CACHE_TIMEOUTMS: - options_.dns_cache_timeout_ = static_cast(va_arg(ap, int)) * std::milli::den; + options_.dns_cache_timeout_ = static_cast(va_arg(ap, int)) * std::milli::den; break; case YOPT_S_DNS_QUERIES_TIMEOUT: - options_.dns_queries_timeout_ = static_cast(va_arg(ap, int)) * std::micro::den; + options_.dns_queries_timeout_ = static_cast(va_arg(ap, int)) * std::micro::den; break; case YOPT_S_DNS_QUERIES_TIMEOUTMS: - options_.dns_queries_timeout_ = static_cast(va_arg(ap, int)) * std::milli::den; + options_.dns_queries_timeout_ = static_cast(va_arg(ap, int)) * std::milli::den; break; case YOPT_S_DNS_QUERIES_TRIES: options_.dns_queries_tries_ = va_arg(ap, int); @@ -2264,7 +2268,7 @@ void io_service::set_option_internal(int opt, va_list ap) // lgtm [cpp/poorly-do { channel->uparams_.max_frame_length = va_arg(ap, int); channel->uparams_.length_field_offset = va_arg(ap, int); - channel->uparams_.length_field_length = yasio::clamp(va_arg(ap, int), YASIO_SSIZEOF(int8_t), YASIO_SSIZEOF(int)); + channel->uparams_.length_field_length = std::clamp(va_arg(ap, int), YASIO_SSIZEOF(int8_t), YASIO_SSIZEOF(int)); channel->uparams_.length_adjustment = va_arg(ap, int); } break; @@ -2272,7 +2276,7 @@ void io_service::set_option_internal(int opt, va_list ap) // lgtm [cpp/poorly-do case YOPT_C_UNPACK_STRIP: { auto channel = channel_at(static_cast(va_arg(ap, int))); if (channel) - channel->uparams_.initial_bytes_to_strip = yasio::clamp(va_arg(ap, int), 0, YASIO_UNPACK_MAX_STRIP); + channel->uparams_.initial_bytes_to_strip = std::clamp(va_arg(ap, int), 0, YASIO_UNPACK_MAX_STRIP); break; } case YOPT_C_UNPACK_NO_BSWAP: { diff --git a/yasio/io_service.hpp b/yasio/io_service.hpp index 3040df174..b70c5d930 100644 --- a/yasio/io_service.hpp +++ b/yasio/io_service.hpp @@ -39,19 +39,19 @@ SOFTWARE. #include #include "yasio/sz.hpp" #include "yasio/config.hpp" -#include "yasio/singleton.hpp" #include "yasio/impl/concurrent_queue.hpp" -#include "yasio/utils.hpp" +#include "yasio/tlx/singleton.hpp" +#include "yasio/tlx/chrono.hpp" #include "yasio/errc.hpp" -#include "yasio/memory.hpp" -#include "yasio/string_view.hpp" +#include "yasio/tlx/memory.hpp" +#include "yasio/tlx/string_view.hpp" #include "yasio/object_pool.hpp" -#include "yasio/byte_buffer.hpp" +#include "yasio/tlx/byte_buffer.hpp" #include "yasio/xxsocket.hpp" #include "yasio/io_watcher.hpp" #if !defined(YASIO_USE_CARES) -# include "yasio/shared_mutex.hpp" +# include "yasio/tlx/shared_mutex.hpp" #endif #if defined(YASIO_ENABLE_KCP) @@ -400,7 +400,7 @@ typedef completion_cb_t io_completion_cb_t; namespace { -static const int yasio__max_rcvbuf = YASIO_SZ(64, k); +static const size_t yasio__max_rcvbuf = YASIO_SZ(64, k); } // namespace // the ssl role @@ -412,10 +412,10 @@ enum ssl_role struct io_hostent { io_hostent() = default; - io_hostent(cxx17::string_view ip, u_short port) : host_(cxx17::svtos(ip)), port_(port) {} + io_hostent(std::string_view ip, u_short port) : host_(ip), port_(port) {} io_hostent(io_hostent&& rhs) YASIO__NOEXCEPT : host_(std::move(rhs.host_)), port_(rhs.port_) {} io_hostent(const io_hostent& rhs) : host_(rhs.host_), port_(rhs.port_) {} - void set_ip(cxx17::string_view ip) { cxx17::assign(host_, ip); } + void set_ip(std::string_view ip) { host_ = ip; } const std::string& get_ip() const { return host_; } void set_port(u_short port) { port_ = port; } u_short get_port() const { return port_; } @@ -425,17 +425,17 @@ struct io_hostent { class YASIO_API highp_timer { public: - highp_timer(io_service& service) : service_(service){}; + highp_timer(io_service& service) : service_(service) {}; highp_timer(const highp_timer&) = delete; highp_timer(highp_timer&&) = delete; highp_timer& operator=(const highp_timer&) = delete; void expires_from_now(const std::chrono::microseconds& duration) { this->duration_ = duration; - this->expire_time_ = yasio::steady_clock_t::now() + this->duration_; + this->expire_time_ = tlx::steady_clock_t::now() + this->duration_; } - void expires_from_now() { this->expire_time_ = yasio::steady_clock_t::now() + this->duration_; } + void expires_from_now() { this->expire_time_ = tlx::steady_clock_t::now() + this->duration_; } // Wait timer timeout once. void async_wait_once(timerv_cb_t cb) @@ -466,8 +466,8 @@ class YASIO_API highp_timer { YASIO__DECL std::chrono::microseconds wait_duration() const; io_service& service_; - std::chrono::microseconds duration_ = {}; - std::chrono::time_point expire_time_ = {}; + std::chrono::microseconds duration_ = {}; + std::chrono::time_point expire_time_ = {}; }; struct YASIO_API io_base { @@ -561,13 +561,13 @@ class YASIO_API io_channel : public io_base { private: YASIO__DECL io_channel(io_service& service, int index); - void set_address(cxx17::string_view host, u_short port) + void set_address(std::string_view host, u_short port) { set_host(host); set_port(port); } - YASIO__DECL void set_host(cxx17::string_view host); + YASIO__DECL void set_host(std::string_view host); YASIO__DECL void set_port(u_short port); void clear_mutable_flags() { properties_ &= 0x00ffffff; } @@ -598,7 +598,7 @@ class YASIO_API io_channel : public io_base { u_short remote_port_ = 0; // The last query success time in microseconds for dns cache support - highp_time_t query_success_time_ = 0; + tlx::highp_time_t query_success_time_ = 0; #if defined(YASIO_ENABLE_ARES_PROFILER) highp_time_t query_start_time_; @@ -642,7 +642,7 @@ class YASIO_API io_channel : public io_base { ip::endpoint multiaddr_, multiif_; // Current it's only for UDP - sbyte_buffer buffer_; + tlx::sbyte_buffer buffer_; // The bytes transferred from socket low layer, currently, only works for client channel long long bytes_transferred_ = 0; @@ -655,7 +655,7 @@ class YASIO_API io_channel : public io_base { class io_send_buffer { public: - explicit io_send_buffer(yasio::sbyte_buffer&& mutable_buffer) + explicit io_send_buffer(tlx::sbyte_buffer&& mutable_buffer) { mutable_buffer_ = std::move(mutable_buffer); data_ = mutable_buffer_.data(); @@ -680,7 +680,7 @@ class io_send_buffer { size_t size() const { return size_; } private: - yasio::sbyte_buffer mutable_buffer_; + tlx::sbyte_buffer mutable_buffer_; const char* data_; size_t size_; @@ -742,7 +742,7 @@ class io_transport : public io_base { protected: io_service& get_service() const { return ctx_->get_service(); } bool is_open() const { return state_ == state::OPENED && socket_ && socket_->is_open(); } - sbyte_buffer fetch_packet() + tlx::sbyte_buffer fetch_packet() { expected_size_ = -1; return std::move(expected_packet_); @@ -766,10 +766,10 @@ class io_transport : public io_base { YASIO__DECL void complete_op(io_send_op*, int error); // Call at io_service - YASIO__DECL virtual int do_read(int revent, int& error, highp_time_t& wait_duration); + YASIO__DECL virtual int do_read(int revent, int& error, tlx::highp_time_t& wait_duration); // Call at io_service, try flush pending packet - YASIO__DECL virtual bool do_write(highp_time_t& wait_duration); + YASIO__DECL virtual bool do_write(tlx::highp_time_t& wait_duration); // Sets the underlying layer socket io primitives. YASIO__DECL virtual void set_primitives(); @@ -778,11 +778,11 @@ class io_transport : public io_base { bool is_valid() const { return ctx_ != nullptr; } - yasio::sbyte_buffer buffer_{static_cast(yasio__max_rcvbuf)}; - int offset_ = 0; // recv buffer offset + tlx::sbyte_buffer buffer_; + int offset_ = 0; // recv buffer offset int expected_size_ = -1; - sbyte_buffer expected_packet_; + tlx::sbyte_buffer expected_packet_; io_channel* ctx_; @@ -838,7 +838,7 @@ class YASIO_API io_transport_udp : public io_transport { YASIO__DECL void confgure_remote(const ip::endpoint& peer); // process received data from low level - YASIO__DECL virtual int handle_input(char* data, int bytes_transferred, int& error, highp_time_t& wait_duration); + YASIO__DECL virtual int handle_input(char* data, int bytes_transferred, int& error, tlx::highp_time_t& wait_duration); ip::endpoint peer_; // for recv only, unstable mutable ip::endpoint destination_; // for sendto only, stable @@ -856,15 +856,15 @@ class io_transport_kcp : public io_transport_udp { protected: YASIO__DECL void set_primitives() override; - YASIO__DECL int do_read(int revent, int& error, highp_time_t& wait_duration) override; + YASIO__DECL int do_read(int revent, int& error, tlx::highp_time_t& wait_duration) override; - YASIO__DECL bool do_write(highp_time_t& wait_duration) override; + YASIO__DECL bool do_write(tlx::highp_time_t& wait_duration) override; - YASIO__DECL int handle_input(char* buf, int len, int& error, highp_time_t& wait_duration) override; + YASIO__DECL int handle_input(char* buf, int len, int& error, tlx::highp_time_t& wait_duration) override; int interval() const { return kcp_->interval * std::milli::den; } - sbyte_buffer rawbuf_; // the low level raw buffer + tlx::sbyte_buffer rawbuf_; // the low level raw buffer ikcpcb* kcp_{nullptr}; IUINT32 expire_time_{0}; // the next expire time(ms) to call ikcp_update std::function underlaying_write_cb_; @@ -873,7 +873,7 @@ class io_transport_kcp : public io_transport_udp { class io_transport_kcp {}; #endif -using io_packet = sbyte_buffer; +using io_packet = tlx::sbyte_buffer; #if !defined(YASIO_USE_SHARED_PACKET) using packet_t = io_packet; inline packet_t wrap_packet(io_packet& raw_packet) { return std::move(raw_packet); } @@ -977,7 +977,7 @@ class io_event final { if (t) t->ud_.ptr = (void*)(uintptr_t)uval; } - highp_time_t timestamp() const { return timestamp_; } + tlx::highp_time_t timestamp() const { return timestamp_; } #endif #if !defined(YASIO_DISABLE_OBJECT_POOL) DEFINE_CONCURRENT_OBJECT_POOL_ALLOCATION(io_event, 128) @@ -997,7 +997,7 @@ class io_event final { io_packet_view packet_view_; #if !defined(YASIO_MINIFY_EVENT) void* source_ud_; - highp_time_t timestamp_ = highp_clock(); + tlx::highp_time_t timestamp_ = tlx::highp_clock(); #endif }; @@ -1097,9 +1097,9 @@ class YASIO_API io_service // lgtm [cpp/class-many-fields] */ int write(transport_handle_t thandle, const void* buf, size_t len, completion_cb_t completion_handler = nullptr) { - return write(thandle, sbyte_buffer{(const char*)buf, (const char*)buf + len}, std::move(completion_handler)); + return write(thandle, tlx::sbyte_buffer{(const char*)buf, (const char*)buf + len}, std::move(completion_handler)); } - YASIO__DECL int write(transport_handle_t thandle, sbyte_buffer buffer, completion_cb_t completion_handler = nullptr); + YASIO__DECL int write(transport_handle_t thandle, tlx::sbyte_buffer buffer, completion_cb_t completion_handler = nullptr); YASIO__DECL int forward(transport_handle_t thandle, const void* buf, size_t len, completion_cb_t completion_handler); /* @@ -1111,9 +1111,9 @@ class YASIO_API io_service // lgtm [cpp/class-many-fields] */ int write_to(transport_handle_t thandle, const void* buf, size_t len, const ip::endpoint& to, completion_cb_t completion_handler = nullptr) { - return write_to(thandle, sbyte_buffer{(const char*)buf, (const char*)buf + len}, to, std::move(completion_handler)); + return write_to(thandle, tlx::sbyte_buffer{(const char*)buf, (const char*)buf + len}, to, std::move(completion_handler)); } - YASIO__DECL int write_to(transport_handle_t thandle, sbyte_buffer buffer, const ip::endpoint& to, completion_cb_t completion_handler = nullptr); + YASIO__DECL int write_to(transport_handle_t thandle, tlx::sbyte_buffer buffer, const ip::endpoint& to, completion_cb_t completion_handler = nullptr); YASIO__DECL int forward_to(transport_handle_t thandle, const void* buf, size_t len, const ip::endpoint& to, completion_cb_t completion_handler); // The highp_timer support, !important, the callback is called on the thread of io_service @@ -1159,7 +1159,7 @@ class YASIO_API io_service // lgtm [cpp/class-many-fields] YASIO__DECL void wakeup(); - YASIO__DECL highp_time_t get_timeout(highp_time_t usec); + YASIO__DECL tlx::highp_time_t get_timeout(tlx::highp_time_t usec); YASIO__DECL int do_resolve(io_channel* ctx); YASIO__DECL void do_connect(io_channel*); @@ -1175,7 +1175,7 @@ class YASIO_API io_service // lgtm [cpp/class-many-fields] YASIO__DECL static void ares_sock_state_cb(void* data, socket_native_type socket_fd, int readable, int writable); YASIO__DECL void ares_work_started(); YASIO__DECL void ares_work_finished(); - YASIO__DECL int ares_get_fds(socket_native_type* socks, highp_time_t& waitd_usec); + YASIO__DECL int ares_get_fds(socket_native_type* socks, tlx::highp_time_t& waitd_usec); YASIO__DECL void do_ares_process_fds(socket_native_type* socks, int count); YASIO__DECL void recreate_ares_channel(); YASIO__DECL void config_ares_name_servers(); @@ -1207,7 +1207,7 @@ class YASIO_API io_service // lgtm [cpp/class-many-fields] template inline void fire_event(_Types&&... args) { - auto event = cxx14::make_unique(std::forward<_Types>(args)...); + auto event = std::make_unique(std::forward<_Types>(args)...); if (options_.on_defer_event_ && options_.on_defer_event_(event)) return; events_.emplace(std::move(event)); @@ -1215,7 +1215,7 @@ class YASIO_API io_service // lgtm [cpp/class-many-fields] template inline void forward_packet(_Types&&... args) { - options_.on_event_(cxx14::make_unique(std::forward<_Types>(args)...)); + options_.on_event_(std::make_unique(std::forward<_Types>(args)...)); } // new/delete client socket connection channel @@ -1239,7 +1239,7 @@ class YASIO_API io_service // lgtm [cpp/class-many-fields] YASIO__DECL void update_dns_status(); - bool address_expired(io_channel* ctx) const { return (highp_clock() - ctx->query_success_time_) > options_.dns_cache_timeout_; } + bool address_expired(io_channel* ctx) const { return (tlx::highp_clock() - ctx->query_success_time_) > options_.dns_cache_timeout_; } /* For log macro only */ inline const print_fn2_t& __get_cprint() const { return options_.print_; } @@ -1250,7 +1250,7 @@ class YASIO_API io_service // lgtm [cpp/class-many-fields] std::thread::id worker_id_; /* The current time according to the event loop. in msecs. */ - std::chrono::time_point current_time_; + std::chrono::time_point current_time_; privacy::concurrent_queue events_; @@ -1268,7 +1268,7 @@ class YASIO_API io_service // lgtm [cpp/class-many-fields] std::recursive_mutex timer_queue_mtx_; // the next wait duration for socket.select - highp_time_t wait_duration_; + tlx::highp_time_t wait_duration_; io_watcher io_watcher_; @@ -1277,10 +1277,10 @@ class YASIO_API io_service // lgtm [cpp/class-many-fields] // options struct __unnamed_options { - highp_time_t connect_timeout_ = 10LL * std::micro::den; - highp_time_t dns_cache_timeout_ = 600LL * std::micro::den; - highp_time_t dns_queries_timeout_ = 5LL * std::micro::den; - int dns_queries_tries_ = 5; + tlx::highp_time_t connect_timeout_ = 10LL * std::micro::den; + tlx::highp_time_t dns_cache_timeout_ = 600LL * std::micro::den; + tlx::highp_time_t dns_queries_timeout_ = 5LL * std::micro::den; + int dns_queries_tries_ = 5; bool dns_dirty_ = false; @@ -1339,7 +1339,7 @@ class YASIO_API io_service // lgtm [cpp/class-many-fields] // we need life_token + life_mutex struct life_token {}; std::shared_ptr life_token_; - std::shared_ptr life_mutex_; + std::shared_ptr life_mutex_; #endif }; // io_service @@ -1349,7 +1349,7 @@ using namespace yasio::inet; #endif } /* namespace yasio */ -#define yasio_shared_service yasio::singleton::instance +#define yasio_shared_service (_TLX singleton::instance) #if defined(YASIO_HEADER_ONLY) # include "yasio/io_service.cpp" // lgtm [cpp/include-non-header] diff --git a/yasio/memory.hpp b/yasio/memory.hpp deleted file mode 100644 index 830f76702..000000000 --- a/yasio/memory.hpp +++ /dev/null @@ -1,45 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// A multi-platform support c++11 library with focus on asynchronous socket I/O for any -// client application. -////////////////////////////////////////////////////////////////////////////////////////// -/* -The MIT License (MIT) - -Copyright (c) 2012-2025 HALX99 - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ -#ifndef YASIO__MEMORY -#define YASIO__MEMORY -#include - -#include "yasio/compiler/feature_test.hpp" - -/// The make_unique workaround on c++11 -#if !YASIO__HAS_CXX14 -namespace cxx14 -{ -template std::unique_ptr<_Ty> make_unique(_Args&&... args) -{ - return std::unique_ptr<_Ty>(new _Ty(std::forward<_Args>(args)...)); -} -} // namespace cxx14 -#endif - -#endif diff --git a/yasio/object_pool.hpp b/yasio/object_pool.hpp index 7ac86283a..82eb4f8c6 100644 --- a/yasio/object_pool.hpp +++ b/yasio/object_pool.hpp @@ -28,7 +28,7 @@ SOFTWARE. #ifndef YASIO__OBJECT_POOL_HPP #define YASIO__OBJECT_POOL_HPP -#include "yasio/type_traits.hpp" +#include "yasio/tlx/type_traits.hpp" #include "yasio/impl/object_pool.hpp" namespace yasio @@ -38,19 +38,22 @@ struct null_mutex { void unlock() {} }; template -class object_pool : public detail::object_pool { +class object_pool : protected detail::object_pool { public: - object_pool(size_t _ElemCount = 128) : detail::object_pool(::yasio::aligned_storage_size<_Ty>::value, _ElemCount) {} + using detail::object_pool::cleanup; + using detail::object_pool::purge; + object_pool(size_t _ElemCount = 128) : detail::object_pool(::tlx::aligned_storage_size<_Ty>::value, _ElemCount) {} template - _Ty* create(_Types&&... args) + _Ty* construt(_Types&&... args) { return new (allocate()) _Ty(std::forward<_Types>(args)...); } void destroy(void* _Ptr) { - ((_Ty*)_Ptr)->~_Ty(); // call the destructor + if constexpr (!std::is_trivially_destructible<_Ty>::value) + ((_Ty*)_Ptr)->~_Ty(); // call the destructor deallocate(_Ptr); } @@ -69,28 +72,19 @@ class object_pool : public detail::object_pool { _Mutex mutex_; }; -#define DEFINE_OBJECT_POOL_ALLOCATION_ANY(ELEMENT_TYPE, ELEMENT_COUNT, MUTEX_TYPE) \ -public: \ - using object_pool_type = yasio::object_pool; \ - static void* operator new(size_t /*size*/) \ - { \ - return get_pool().allocate(); \ - } \ - \ - static void* operator new(size_t /*size*/, std::nothrow_t) \ - { \ - return get_pool().allocate(); \ - } \ - \ - static void operator delete(void* p) \ - { \ - get_pool().deallocate(p); \ - } \ - \ - static object_pool_type& get_pool() \ - { \ - static object_pool_type s_pool(ELEMENT_COUNT); \ - return s_pool; \ +#define DEFINE_OBJECT_POOL_ALLOCATION_ANY(ELEMENT_TYPE, ELEMENT_COUNT, MUTEX_TYPE) \ +public: \ + using object_pool_type = yasio::object_pool; \ + static void* operator new(size_t /*size*/) { return get_pool().allocate(); } \ + \ + static void* operator new(size_t /*size*/, std::nothrow_t) { return get_pool().allocate(); } \ + \ + static void operator delete(void* p) { get_pool().deallocate(p); } \ + \ + static object_pool_type& get_pool() \ + { \ + static object_pool_type s_pool(ELEMENT_COUNT); \ + return s_pool; \ } // The non thread safe edition diff --git a/yasio/obstream.hpp b/yasio/obstream.hpp index caea2f73a..9c03bc3f8 100644 --- a/yasio/obstream.hpp +++ b/yasio/obstream.hpp @@ -33,10 +33,10 @@ SOFTWARE. #include #include #include -#include "yasio/string_view.hpp" +#include +#include "yasio/tlx/string_view.hpp" #include "yasio/endian_portable.hpp" -#include "yasio/utils.hpp" -#include "yasio/byte_buffer.hpp" +#include "yasio/tlx/byte_buffer.hpp" namespace yasio { enum : size_t @@ -143,7 +143,7 @@ class fixed_buffer : public fixed_buffer_span { std::array impl_; }; -template +template class dynamic_buffer_span { public: using implementation_type = _Cont; @@ -179,7 +179,7 @@ class dynamic_buffer_span { implementation_type* outs_; }; -template +template class dynamic_buffer : public dynamic_buffer_span<_Cont> { public: using super_type = dynamic_buffer_span<_Cont>; @@ -271,7 +271,7 @@ class binary_writer_impl { void push(int size) { - size = yasio::clamp(size, 1, YASIO_SSIZEOF(int)); + size = std::clamp(size, 1, YASIO_SSIZEOF(int)); auto bufsize = outs_->length(); offset_stack_.push(bufsize); @@ -280,7 +280,7 @@ class binary_writer_impl { void pop(int size) { - size = yasio::clamp(size, 1, YASIO_SSIZEOF(int)); + size = std::clamp(size, 1, YASIO_SSIZEOF(int)); auto offset = offset_stack_.top(); auto value = static_cast(outs_->length() - offset - size); @@ -291,7 +291,7 @@ class binary_writer_impl { void pop(int value, int size) { - size = yasio::clamp(size, 1, YASIO_SSIZEOF(int)); + size = std::clamp(size, 1, YASIO_SSIZEOF(int)); auto offset = offset_stack_.top(); value = convert_traits_type::toint(value, size); @@ -319,7 +319,7 @@ class binary_writer_impl { #endif /* write blob data with '7bit encoded int' length field */ - void write_v(cxx17::string_view value) + void write_v(std::string_view value) { int len = static_cast(value.length()); write_ix(len); @@ -327,15 +327,15 @@ class binary_writer_impl { } /* 32 bits length field */ - void write_v32(cxx17::string_view value) { write_v_fx(value); } + void write_v32(std::string_view value) { write_v_fx(value); } /* 16 bits length field */ - void write_v16(cxx17::string_view value) { write_v_fx(value); } + void write_v16(std::string_view value) { write_v_fx(value); } /* 8 bits length field */ - void write_v8(cxx17::string_view value) { write_v_fx(value); } + void write_v8(std::string_view value) { write_v_fx(value); } void write_byte(uint8_t value) { outs_->write_byte(value); } - void write_bytes(cxx17::string_view v) { return write_bytes(v.data(), static_cast(v.size())); } + void write_bytes(std::string_view v) { return write_bytes(v.data(), static_cast(v.size())); } void write_bytes(const void* d, int n) { outs_->write_bytes(d, n); } void write_bytes(size_t offset, const void* d, int n) { outs_->write_bytes(offset, d, n); } void fill_bytes(int n, uint8_t val = 0) { outs_->fill_bytes(n, val); } @@ -373,7 +373,7 @@ class binary_writer_impl { void write_varint(int value, int size) { - size = yasio::clamp(size, 1, YASIO_SSIZEOF(int)); + size = std::clamp(size, 1, YASIO_SSIZEOF(int)); value = convert_traits_type::toint(value, size); write_bytes(&value, size); @@ -401,7 +401,7 @@ class binary_writer_impl { private: template - inline void write_v_fx(cxx17::string_view value) + inline void write_v_fx(std::string_view value) { int size = static_cast(value.size()); this->write<_LenT>(static_cast<_LenT>(size)); @@ -500,7 +500,7 @@ using obstream = obstream_any; using fast_obstream = fast_obstream_any; //-------- basic_obstream_span -template +template class basic_obstream_span; template diff --git a/yasio/pod_vector.hpp b/yasio/pod_vector.hpp deleted file mode 100644 index 901c1e6a3..000000000 --- a/yasio/pod_vector.hpp +++ /dev/null @@ -1,516 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// A multi-platform support c++11 library with focus on asynchronous socket I/O for any -// client application. -////////////////////////////////////////////////////////////////////////////////////////// -/* -The MIT License (MIT) - -Copyright (c) 2012-2025 HALX99 - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -Version: 4.3.2 - -The pod_vector aka array_buffer concepts: - a. The memory model is similar to to std::vector, but only accept trivially_copyable(no destructor & no custom copy constructor) types - b. The resize behavior differrent stl, always allocate exactly - c. By default resize without fill (uninitialized and for overwrite), - use insert/append insetad if you want fill memory inside container - d. Support release internal buffer ownership with `release_pointer` - e. Transparent iterator - f. expand/append/insert/push_back will trigger memory allocate growth strategy MSVC - g. resize_and_overwrite (c++23) -*/ -#ifndef YASIO__POD_VECTOR_HPP -#define YASIO__POD_VECTOR_HPP -#include -#include -#include -#include -#include -#include "yasio/buffer_alloc.hpp" -#include "yasio/compiler/feature_test.hpp" - -namespace yasio -{ -template > -class pod_vector { -public: - using pointer = _Ty*; - using const_pointer = const _Ty*; - using reference = _Ty&; - using const_reference = const _Ty&; - using _Alloc_traits = buffer_allocator_traits<_Alloc>; - using size_type = typename _Alloc_traits::size_type; - using value_type = _Ty; - using iterator = _Ty*; // transparent iterator - using const_iterator = const _Ty*; - using allocator_type = _Alloc; - pod_vector() {} - explicit pod_vector(size_type count) { resize(static_cast(count)); } - pod_vector(size_type count, const_reference val) { resize(static_cast(count), val); } - template ::value, int> = 0> - pod_vector(_Iter first, _Iter last) - { - assign(first, last); - } - pod_vector(const pod_vector& rhs) { assign(rhs); }; - pod_vector(pod_vector&& rhs) YASIO__NOEXCEPT { assign(std::move(rhs)); } - /*pod_vector(std::initializer_list rhs) { _Assign_range(rhs.begin(), rhs.end()); }*/ - ~pod_vector() { _Tidy(); } - pod_vector& operator=(const pod_vector& rhs) - { - assign(rhs); - return *this; - } - pod_vector& operator=(pod_vector&& rhs) YASIO__NOEXCEPT - { - this->swap(rhs); - return *this; - } - template - pod_vector& operator+=(const _Cont& rhs) - { - return this->append(std::begin(rhs), std::end(rhs)); - } - pod_vector& operator+=(const_reference rhs) - { - this->push_back(rhs); - return *this; - } - template ::value, int> = 0> - void assign(_Iter first, _Iter last) - { - _Assign_range(first, last); - } - void assign(const pod_vector& rhs) { _Assign_range(rhs.begin(), rhs.end()); } - void assign(pod_vector&& rhs) { _Assign_rv(std::move(rhs)); } - void swap(pod_vector& rhs) YASIO__NOEXCEPT - { - std::swap(_Myfirst, rhs._Myfirst); - std::swap(_Mysize, rhs._Mysize); - std::swap(_Myres, rhs._Myres); - } - template ::value, int> = 0> - iterator insert(iterator pos, _Iter first, _Iter last) - { - auto mlast = _Myfirst + _Mysize; - _YASIO_VERIFY_RANGE(pos >= _Myfirst && pos <= mlast && first <= last, "pod_vector: out of range!"); - if (first != last) - { - auto insertion_off = static_cast(std::distance(_Myfirst, pos)); - if (pos == mlast) - append(first, last); - else - { - auto ifirst = std::addressof(*first); - static_assert(sizeof(*ifirst) == sizeof(value_type), "pod_vector: iterator type incompatible!"); - auto count = static_cast(std::distance(first, last)); - if (insertion_off >= 0) - { - expand(count); - pos = _Myfirst + insertion_off; - mlast = _Myfirst + _Mysize; - auto move_to = pos + count; - std::copy_n(pos, mlast - move_to, move_to); - std::copy_n((iterator)ifirst, count, pos); - } - } - return _Myfirst + insertion_off; - } - return pos; - } - iterator insert(iterator pos, size_type count, const_reference val) - { - auto mlast = _Myfirst + _Mysize; - _YASIO_VERIFY_RANGE(pos >= _Myfirst && pos <= mlast, "pod_vector: out of range!"); - if (count) - { - auto insertion_off = std::distance(_Myfirst, pos); - if (pos == mlast) - append(count, val); - else - { - if (insertion_off >= 0) - { - const auto old_size = _Mysize; - expand(count); - pos = _Myfirst + insertion_off; - mlast = _Myfirst + _Mysize; - auto move_to = pos + count; - std::copy_n(pos, mlast - move_to, move_to); - std::fill_n(pos, count, val); - } - } - return _Myfirst + insertion_off; - } - return pos; - } - iterator insert(iterator pos, const value_type& val) - { // insert val at pos - return emplace(pos, val); - } - iterator insert(iterator pos, value_type&& val) - { // insert by moving val at pos - return emplace(pos, std::move(val)); - } - template - iterator emplace(iterator pos, _Valty&&... val) - { - auto insertion_off = std::distance(_Myfirst, pos); - _YASIO_VERIFY_RANGE(insertion_off <= _Mysize, "pod_vector: out of range!"); -#if YASIO__HAS_CXX20 - emplace_back(std::forward<_Valty>(val)...); - std::rotate(begin() + insertion_off, end() - 1, end()); - return (begin() + insertion_off); -#else - auto mlast = _Myfirst + _Mysize; - if (pos == mlast) - emplace_back(std::forward<_Valty>(val)...); - else - { - if (insertion_off >= 0) - { - expand(1); - pos = _Myfirst + insertion_off; - mlast = _Myfirst + _Mysize; - auto move_to = pos + 1; - std::copy_n(pos, mlast - move_to, move_to); - ::yasio::construct_at(pos, std::forward<_Valty>(val)...); - } - } - return _Myfirst + insertion_off; -#endif - } - template ::value, int> = 0> - pod_vector& append(_Iter first, const _Iter last) - { - if (first != last) - { - auto ifirst = std::addressof(*first); - static_assert(sizeof(*ifirst) == sizeof(value_type), "pod_vector: iterator type incompatible!"); - auto count = static_cast(std::distance(first, last)); - if (count > 1) - { - const auto old_size = _Mysize; - expand(count); - std::copy_n((iterator)ifirst, count, _Myfirst + old_size); - } - else if (count == 1) - push_back(static_cast(*(iterator)ifirst)); - } - return *this; - } - pod_vector& append(size_type count, const_reference val) - { - expand(count, val); - return *this; - } - void push_back(value_type&& val) { push_back(val); } - void push_back(const value_type& val) { emplace_back(val); } - void pop_back() - { - if (!empty()) - _Eos(size() - 1); - } - template - inline value_type& emplace_back(_Valty&&... val) - { - if (_Mysize < _Myres) - return *::yasio::construct_at(_Myfirst + _Mysize++, std::forward<_Valty>(val)...); - return *_Emplace_back_reallocate(std::forward<_Valty>(val)...); - } - iterator erase(iterator pos) - { - const auto mlast = _Myfirst + _Mysize; - _YASIO_VERIFY_RANGE(pos >= _Myfirst && pos < mlast, "pod_vector: out of range!"); - _Mysize = static_cast(std::move(pos + 1, mlast, pos) - _Myfirst); - return pos; - } - iterator erase(iterator first, iterator last) - { - const auto mlast = _Myfirst + _Mysize; - _YASIO_VERIFY_RANGE((first <= last) && first >= _Myfirst && last <= mlast, "pod_vector: out of range!"); - _Mysize = static_cast(std::move(last, mlast, first) - _Myfirst); - return first; - } - value_type& front() - { - _YASIO_VERIFY_RANGE(!empty(), "pod_vector: out of range!"); - return *_Myfirst; - } - const value_type& front() const - { - _YASIO_VERIFY_RANGE(!empty(), "pod_vector: out of range!"); - return *_Myfirst; - } - value_type& back() - { - _YASIO_VERIFY_RANGE(!empty(), "pod_vector: out of range!"); - return _Myfirst[_Mysize - 1]; - } - const value_type& back() const - { - _YASIO_VERIFY_RANGE(!empty(), "pod_vector: out of range!"); - return _Myfirst[_Mysize - 1]; - } - static YASIO__CONSTEXPR size_type max_size() YASIO__NOEXCEPT { return _Alloc_traits::max_size(); } - iterator begin() YASIO__NOEXCEPT { return _Myfirst; } - iterator end() YASIO__NOEXCEPT { return _Myfirst + _Mysize; } - const_iterator begin() const YASIO__NOEXCEPT { return _Myfirst; } - const_iterator end() const YASIO__NOEXCEPT { return _Myfirst + _Mysize; } - pointer data() YASIO__NOEXCEPT { return _Myfirst; } - const_pointer data() const YASIO__NOEXCEPT { return _Myfirst; } - size_type capacity() const YASIO__NOEXCEPT { return _Myres; } - size_type size() const YASIO__NOEXCEPT { return _Mysize; } - size_type length() const YASIO__NOEXCEPT { return _Mysize; } - void clear() YASIO__NOEXCEPT { _Mysize = 0; } - bool empty() const YASIO__NOEXCEPT { return _Mysize == 0; } - - const_reference operator[](size_type index) const { return this->at(index); } - reference operator[](size_type index) { return this->at(index); } - const_reference at(size_type index) const - { - _YASIO_VERIFY_RANGE(index < this->size(), "pod_vector: out of range!"); - return _Myfirst[index]; - } - reference at(size_type index) - { - _YASIO_VERIFY_RANGE(index < this->size(), "pod_vector: out of range!"); - return _Myfirst[index]; - } -#pragma region modify size and capacity - void resize(size_type new_size) - { - if (this->capacity() < new_size) - _Resize_reallocate<_Reallocation_policy::_Exactly>(new_size); - else - _Eos(new_size); - } - void expand(size_type count) - { - const auto new_size = this->size() + count; - if (this->capacity() < new_size) - _Resize_reallocate<_Reallocation_policy::_At_least>(new_size); - else - _Eos(new_size); - } - void shrink_to_fit() - { // reduce capacity to size, provide strong guarantee - if (_Mysize != _Myres) - { // something to do - if (!_Mysize) - _Tidy(); - else - _Reallocate<_Reallocation_policy::_Exactly>(_Mysize); - } - } - void reserve(size_type new_cap) - { - if (this->capacity() < new_cap) - _Reallocate<_Reallocation_policy::_Exactly>(new_cap); - } - template - void resize_and_overwrite(const size_type new_size, _Operation op) - { - _Reallocate<_Reallocation_policy::_Exactly>(new_size); - _Eos(std::move(op)(_Myfirst, new_size)); - } -#pragma endregion - void resize(size_type new_size, const_reference val) - { - auto old_size = this->size(); - if (old_size != new_size) - { - resize(new_size); - if (old_size < new_size) - std::fill_n(_Myfirst + old_size, new_size - old_size, val); - } - } - void expand(size_type count, const_reference val) - { - if (count) - { - auto old_size = this->size(); - expand(count); - if (count) - std::fill_n(_Myfirst + old_size, count, val); - } - } - ptrdiff_t index_of(const_reference val) const YASIO__NOEXCEPT - { - auto it = std::find(begin(), end(), val); - if (it != this->end()) - return std::distance(begin(), it); - return -1; - } - void reset(size_type new_size) - { - resize(new_size); - memset(_Myfirst, 0x0, size_bytes()); - } - size_t size_bytes() const YASIO__NOEXCEPT { return this->size() * sizeof(value_type); } - template - pointer detach_abi(_Intty& len) YASIO__NOEXCEPT - { - len = static_cast<_Intty>(this->size()); - auto ptr = _Myfirst; - _Myfirst = nullptr; - _Mysize = _Myres = 0; - return ptr; - } - pointer detach_abi() YASIO__NOEXCEPT - { - size_type ignored_len; - return this->detach_abi(ignored_len); - } - void attach_abi(pointer ptr, size_type len) - { - _Tidy(); - _Myfirst = ptr; - _Mysize = _Myres = len; - } - pointer release_pointer() YASIO__NOEXCEPT { return detach_abi(); } - -private: - void _Eos(size_type size) YASIO__NOEXCEPT { _Mysize = size; } - template - pointer _Emplace_back_reallocate(_Valty&&... val) - { - const auto _Oldsize = _Mysize; - - if (_Oldsize == max_size()) - throw std::length_error("pod_vector too long"); - - const size_type _Newsize = _Oldsize + 1; - _Resize_reallocate<_Reallocation_policy::_At_least>(_Newsize); - const pointer _Newptr = ::yasio::construct_at(_Myfirst + _Oldsize, std::forward<_Valty>(val)...); - return _Newptr; - } - template ::value, int> = 0> - void _Assign_range(_Iter first, _Iter last) - { - auto ifirst = std::addressof(*first); - static_assert(sizeof(*ifirst) == sizeof(value_type), "pod_vector: iterator type incompatible!"); - if (ifirst != _Myfirst) - { - _Mysize = 0; - if (last > first) - { - const auto count = static_cast(std::distance(first, last)); - resize(count); - std::copy_n((iterator)ifirst, count, _Myfirst); - } - } - } - void _Assign_rv(pod_vector&& rhs) - { - memcpy((void*)this, &rhs, sizeof(rhs)); - memset((void*)&rhs, 0, sizeof(rhs)); - } - enum class _Reallocation_policy - { - _At_least, - _Exactly - }; - template <_Reallocation_policy _Policy> - void _Resize_reallocate(size_type size) - { - _Reallocate<_Policy>(size); - _Eos(size); - } - template <_Reallocation_policy _Policy> - void _Reallocate(size_type size) - { - size_type new_cap; - if YASIO__CONSTEXPR (_Policy == _Reallocation_policy::_Exactly) - new_cap = size; - else - new_cap = _Calculate_growth(size); - auto _Newvec = _Alloc::reallocate(_Myfirst, _Myres, new_cap); - if (_Newvec) - { - _Myfirst = _Newvec; - _Myres = new_cap; - } - else - throw std::bad_alloc{}; - } - size_type _Calculate_growth(const size_type new_size) const - { - // given _Oldcapacity and _Newsize, calculate geometric growth - const size_type old_cap = capacity(); - YASIO__CONSTEXPR auto max_cap = max_size(); - - if (old_cap > max_cap - old_cap / 2) - return max_cap; // geometric growth would overflow - - const size_type geometric = old_cap + (old_cap >> 1); - - if (geometric < new_size) - return new_size; // geometric growth would be insufficient - - return geometric; // geometric growth is sufficient - } - void _Tidy() YASIO__NOEXCEPT - { // free all storage - if (_Myfirst) - { - _Alloc::deallocate(_Myfirst, _Myres); - _Myfirst = nullptr; - _Mysize = _Myres = 0; - } - } - - pointer _Myfirst = nullptr; - size_type _Mysize = 0; - size_type _Myres = 0; -}; - -#pragma region c++20 like std::erase -template -void erase(pod_vector<_Ty, _Alloc>& cont, const _Ty& val) -{ - cont.erase(std::remove(cont.begin(), cont.end(), val), cont.end()); -} -template -void erase_if(pod_vector<_Ty, _Alloc>& cont, _Pr pred) -{ - cont.erase(std::remove_if(cont.begin(), cont.end(), pred), cont.end()); -} -#pragma endregion - -template -inline typename _Cont::iterator insert_sorted(_Cont& vec, typename _Cont::value_type const& val) -{ - return vec.insert(std::upper_bound(vec.begin(), vec.end(), val), val); -} - -template -inline typename _Cont::iterator insert_sorted(_Cont& vec, typename _Cont::value_type const& val, _Pred pred) -{ - return vec.insert(std::upper_bound(vec.begin(), vec.end(), val, pred), val); -} - -// alias: array_buffer -template > -using array_buffer = pod_vector<_Ty, _Alloc>; - -} // namespace yasio -#endif diff --git a/yasio/ref_ptr.hpp b/yasio/ref_ptr.hpp deleted file mode 100644 index ed64743dd..000000000 --- a/yasio/ref_ptr.hpp +++ /dev/null @@ -1,211 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// A multi-platform support c++11 library with focus on asynchronous socket I/O for any -// client application. -////////////////////////////////////////////////////////////////////////////////////////// -/* -The MIT License (MIT) - -Copyright (c) 2012-2025 HALX99 - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ -#ifndef YASIO__REF_PTR_HPP -#define YASIO__REF_PTR_HPP -#include "yasio/config.hpp" - -// clang-format off -#define YASIO__DEFINE_REFERENCE_CLASS \ -private: \ - unsigned int __strong_refs = 1; \ - \ -public: \ - void retain() { ++__strong_refs; } \ - void release() \ - { \ - --__strong_refs; \ - if (__strong_refs == 0) \ - delete this; \ - } \ - \ -private: - -#define YASIO__SAFE_RELEASE(p) \ - if (p) \ - (p)->release() - -#define YASIO__SAFE_RELEASE_NULL(p) \ - do \ - { \ - if (p) \ - { \ - (p)->release(); \ - (p) = nullptr; \ - } \ - } while (0) - -#define YASIO__SAFE_RETAIN(p) \ - if (p) \ - (p)->retain() -// clang-format on - -namespace yasio -{ -struct own_ref_t {}; - -// TEMPLATE CLASS ref_ptr, allow any time with functions 'retain' and 'release' -template -class ref_ptr; - -template -class ref_ptr { // wrap an object pointer to ensure destruction -public: - typedef ref_ptr<_Ty> _Myt; - typedef _Ty element_type; - - explicit ref_ptr(_Ty* _Ptr = nullptr) throw() : ptr_(_Ptr) - { // construct from object pointer - } - - ref_ptr(_Ty* _Ptr, own_ref_t) throw() : ptr_(_Ptr) - { // construct from object pointer - YASIO__SAFE_RETAIN(ptr_); - } - - ref_ptr(std::nullptr_t) throw() : ptr_(nullptr) {} - - ref_ptr(const _Myt& _Right) throw() - { // construct by assuming pointer from _Right ref_ptr - ptr_ = _Right.get(); - YASIO__SAFE_RETAIN(ptr_); - } - - template - ref_ptr(const ref_ptr<_Other>& _Right) throw() - { // construct by assuming pointer from _Right - ptr_ = (_Ty*)_Right.get(); - YASIO__SAFE_RETAIN(ptr_); - } - - ref_ptr(_Myt&& _Right) throw() : ptr_(_Right.release()) {} - - template - ref_ptr(ref_ptr<_Other>&& _Right) throw() : ptr_((_Ty*)_Right.release()) - {} - - _Myt& operator=(const _Myt& _Right) throw() - { // assign compatible _Right (assume pointer) - if (this == &_Right) - return *this; - - reset(_Right.get()); - YASIO__SAFE_RETAIN(ptr_); - return (*this); - } - - _Myt& operator=(_Myt&& _Right) throw() - { // assign compatible _Right (assume pointer) - reset(_Right.release()); - return (*this); - } - - // release ownership - _Ty* release() - { - auto tmp = ptr_; - ptr_ = nullptr; - return tmp; - } - - template - _Myt& operator=(const ref_ptr<_Other>& _Right) throw() - { // assign compatible _Right (assume pointer) - if (this == &_Right) - return *this; - - reset((_Ty*)_Right.get()); - YASIO__SAFE_RETAIN(ptr_); - return (*this); - } - - template - _Myt& operator=(ref_ptr<_Other>&& _Right) throw() - { // assign compatible _Right (assume pointer) - reset((_Ty*)_Right.release()); - return (*this); - } - - _Myt& operator=(std::nullptr_t) throw() - { - reset(); - return (*this); - } - - ~ref_ptr() - { // release the object - YASIO__SAFE_RELEASE(ptr_); - } - - _Ty& operator*() const throw() - { // return designated value - return (*ptr_); // return (*get()); - } - - _Ty** operator&() throw() { return &(ptr_); } - - _Ty* operator->() const throw() - { // return pointer to class object - return (ptr_); // return (get()); - } - - template - _Ty& operator[](_Int index) const throw() - { - return (ptr_[index]); - } - - _Ty* get() const throw() - { // return wrapped pointer - return (ptr_); - } - - operator _Ty*() const throw() - { // convert to basic type - return (ptr_); - } - - /* - ** if already have a valid pointer, will call release firstly - */ - void reset(_Ty* _Ptr = nullptr) - { // relese designated object and store new pointer - if (ptr_ != _Ptr) - { - if (ptr_ != nullptr) - YASIO__SAFE_RELEASE(ptr_); - ptr_ = _Ptr; - } - } - -private: - _Ty* ptr_; // the wrapped object pointer -}; - -}; // namespace yasio - -#endif diff --git a/yasio/split.hpp b/yasio/split.hpp deleted file mode 100644 index 3afbef40c..000000000 --- a/yasio/split.hpp +++ /dev/null @@ -1,127 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// A multi-platform support c++11 library with focus on asynchronous socket I/O for any -// client application. -////////////////////////////////////////////////////////////////////////////////////////// -/* -The MIT License (MIT) - -Copyright (c) 2012-2025 HALX99 - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -/* - * The standard split stub of yasio: - * The pred callback prototype: [](CStr first, CStr last) ->bool{ return true; } - * returns: - * true: want continue split - * false: abort split - * - */ - -#pragma once - -#include "yasio/string_view.hpp" -#include - -#if defined(_MSC_VER) -# pragma warning(push) -# pragma warning(disable : 4706) -#endif - -namespace yasio -{ -template -inline void split_if(_CStr s, typename std::remove_pointer<_CStr>::type delim, _Pred&& pred) -{ - auto _Start = s; // the start of every string - auto _Ptr = s; // source string iterator - while ((_Ptr = strchr(_Ptr, delim))) - { - if (_Start <= _Ptr && !pred(_Start, _Ptr)) - return; - _Start = _Ptr + 1; - ++_Ptr; - } - pred(_Start, nullptr); // last one, end is nullptr -} - -template -inline void split_if_n(_CStr s, size_t slen, typename std::remove_pointer<_CStr>::type delim, _Pred&& pred) -{ - auto _Start = s; // the start of every string - auto _Ptr = s; // source string iterator - auto _End = s + slen; - while ((_Ptr = strchr(_Ptr, delim))) - { - if (_Ptr >= _End) - break; - - if (_Start <= _Ptr && !pred(_Start, _Ptr)) - return; - _Start = _Ptr + 1; - ++_Ptr; - } - if (_Start <= _End) - pred(_Start, _End); -} - -template -inline void split(_CStr s, typename std::remove_pointer<_CStr>::type delim, _Func&& func) -{ - split_if(s, delim, [func](_CStr first, _CStr last) { - func(first, last); - return true; - }); -} - -template -inline void split_n(_CStr s, size_t slen, typename std::remove_pointer<_CStr>::type delim, _Func&& func) -{ - split_if_n(s, slen, delim, [func](_CStr first, _CStr last) { - func(first, last); - return true; - }); -} - -struct split_term { - split_term(char* end) - { - if (end) - { - this->val_ = *end; - *end = '\0'; - this->end_ = end; - } - } - ~split_term() - { - if (this->end_) - *this->end_ = this->val_; - } - -private: - char* end_ = nullptr; - char val_ = '\0'; -}; -} // namespace yasio - -#if defined(_MSC_VER) -# pragma warning(pop) -#endif diff --git a/yasio/string_view.hpp b/yasio/string_view.hpp deleted file mode 100644 index 2e51323e6..000000000 --- a/yasio/string_view.hpp +++ /dev/null @@ -1,1287 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// A multi-platform support c++11 library with focus on asynchronous socket I/O for any -// client application. -////////////////////////////////////////////////////////////////////////////////////////// -/* -The MIT License (MIT) - -Copyright (c) 2012-2025 HALX99 -Copyright (c) 2016 Matthew Rodusek(matthew.rodusek@gmail.com) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -See: https://github.com/bitwizeshift/string_view-standalone -*/ -#ifndef YASIO__STRING_VIEW_HPP -#define YASIO__STRING_VIEW_HPP -#include -#include -#include -#include -#include "yasio/impl/char_traits.hpp" -#include "yasio/compiler/feature_test.hpp" - -/// wcsncasecmp workaround for android API level < 23, copy from msvc ucrt 10.0.18362.0 'wcsnicmp' -#if (defined(__ANDROID_API__) && __ANDROID_API__ < 23) || defined(__MINGW32__) -inline int wcsncasecmp(wchar_t const* const lhs, wchar_t const* const rhs, size_t const count) -{ - if (count == 0) - { - return 0; - } - - wchar_t const* lhs_ptr = reinterpret_cast(lhs); - wchar_t const* rhs_ptr = reinterpret_cast(rhs); - - int result; - int lhs_value; - int rhs_value; - size_t remaining = count; - do - { - lhs_value = ::towlower(*lhs_ptr++); - rhs_value = ::towlower(*rhs_ptr++); - result = lhs_value - rhs_value; - } while (result == 0 && lhs_value != 0 && --remaining != 0); - - return result; -} -#endif - -/// string_view workaround on c++11 -#if YASIO__HAS_CXX17 -# if __has_include() -# include -# endif -#else -# include -# include -# include -namespace cxx17 -{ -//////////////////////////////////////////////////////////////////////////// -/// \brief A wrapper around non-owned strings. -/// -/// This is an implementation of the C++17 string_view proposal -/// -/// \ingroup core -//////////////////////////////////////////////////////////////////////////// -template > -class basic_string_view; - -template -class basic_string_view { - //------------------------------------------------------------------------ - // Public Member Types - //------------------------------------------------------------------------ -public: - using char_type = _CharT; - using traits_type = _Traits; - using size_type = size_t; - - using value_type = _CharT; - using reference = value_type&; - using const_reference = const value_type&; - using pointer = value_type*; - using const_pointer = const value_type*; - - using iterator = const _CharT*; - using const_iterator = const _CharT*; - - //------------------------------------------------------------------------ - // Public Members - //------------------------------------------------------------------------ -public: - static const size_type npos = size_type(-1); - - //------------------------------------------------------------------------ - // Constructors - //------------------------------------------------------------------------ -public: - /// \brief Default constructs a basic_string_view without any content - basic_string_view(); - - /// \brief Constructs a basic_string_view from a std::basic_string - /// - /// \param str the string to view - template - basic_string_view(const std::basic_string<_CharT, _Traits, Allocator>& str); - - /// \brief Constructs a basic_string_view from an ansi-string - /// - /// \param str the string to view - basic_string_view(const char_type* str); - - /// \brief Constructs a basic_string_view from an ansi string of a given size - /// - /// \param str the string to view - /// \param count the size of the string - basic_string_view(const char_type* str, size_type count); - - //------------------------------------------------------------------------ - // Assignment - //------------------------------------------------------------------------ -public: - //------------------------------------------------------------------------ - // Capacity - //------------------------------------------------------------------------ -public: - /// \brief Returns the length of the string, in terms of bytes - /// - /// \return the length of the string, in terms of bytes - size_type size() const; - - /// \copydoc basic_string_view::size - size_type length() const; - - /// \brief The largest possible number of char-like objects that can be - /// referred to by a basic_string_view. - /// \return Maximum number of characters - size_type max_size() const; - - /// \brief Returns whether the basic_string_view is empty - /// (i.e. whether its length is 0). - /// - /// \return whether the basic_string_view is empty - bool empty() const; - - //------------------------------------------------------------------------ - // Element Access - //------------------------------------------------------------------------ -public: - /// \brief Gets the ansi-string of the current basic_string_view - /// - /// \return the ansi-string pointer - const char_type* c_str() const; - - /// \brief Gets the data of the current basic_string_view - /// - /// \note This is an alias of #c_str - /// - /// \return the data this basic_string_view contains - const char_type* data() const; - - /// \brief Accesses the element at index \p pos - /// - /// \param pos the index to access - /// \return const reference to the character - const_reference operator[](size_t pos) const; - - /// \brief Accesses the element at index \p pos - /// - /// \param pos the index to access - /// \return const reference to the character - const_reference at(size_t pos) const; - - /// \brief Access the first character of the string - /// - /// \note Undefined behavior if basic_string_view is empty - /// - /// \return reference to the first character of the string - const_reference front() const; - - /// \brief References the last character of the string - /// - /// \note Undefined behavior if basic_string_view is empty - /// - /// \return reference to the last character of the string - const_reference back() const; - - //------------------------------------------------------------------------ - // Modifiers - //------------------------------------------------------------------------ -public: - /// \brief Moves the start of the view forward by n characters. - /// - /// The behavior is undefined if n > size(). - /// - /// \param n number of characters to remove from the start of the view - void remove_prefix(size_type n); - - /// \brief Moves the end of the view back by n characters. - /// - /// The behavior is undefined if n > size(). - /// - /// \param n number of characters to remove from the end of the view - void remove_suffix(size_type n); - - /// \brief Exchanges the view with that of v. - /// - /// \param v view to swap with - void swap(basic_string_view& v); - - //------------------------------------------------------------------------ - // Conversions - //------------------------------------------------------------------------ -public: - /// \brief Creates a basic_string with a copy of the content of the current - /// view. - /// - /// \tparam Allocator type used to allocate internal storage - /// \param a Allocator instance to use for allocating the new string - /// - /// \return A basic_string containing a copy of the characters of the current - /// view. - template > - std::basic_string<_CharT, _Traits, Allocator> to_string(const Allocator& a = Allocator()) const; - - /// \copydoc basic_string_view::to_string - template - explicit operator std::basic_string<_CharT, _Traits, Allocator>() const; - - //------------------------------------------------------------------------ - // Operations - //------------------------------------------------------------------------ -public: - /// \brief Copies the substring [pos, pos + rcount) to the character string - /// pointed - /// to by dest, where rcount is the smaller of count and size() - pos. - /// - /// \param dest pointer to the destination character string - /// \param count requested substring length - /// \param pos position of the first character - size_type copy(char_type* dest, size_type count = npos, size_type pos = 0) const; - - /// \brief Returns a substring of this viewed string - /// - /// \param pos the position of the first character in the substring - /// \param len the length of the substring - /// \return the created substring - basic_string_view substr(size_t pos = 0, size_t len = npos) const; - - //------------------------------------------------------------------------ - - /// \brief Compares two character sequences - /// - /// \param v view to compare - /// \return negative value if this view is less than the other character - /// sequence, zero if the both character sequences are equal, positive - /// value if this view is greater than the other character sequence. - int compare(basic_string_view v) const; - - /// \brief Compares two character sequences - /// - /// \param pos position of the first character in this view to compare - /// \param count number of characters of this view to compare - /// \param v view to compare - /// \return negative value if this view is less than the other character - /// sequence, zero if the both character sequences are equal, positive - /// value if this view is greater than the other character sequence. - int compare(size_type pos, size_type count, basic_string_view v) const; - - /// \brief Compares two character sequences - /// - /// \param pos1 position of the first character in this view to compare - /// \param count1 number of characters of this view to compare - /// \param v view to compare - /// \param pos2 position of the second character in this view to compare - /// \param count2 number of characters of the given view to compare - /// \return negative value if this view is less than the other character - /// sequence, zero if the both character sequences are equal, positive - /// value if this view is greater than the other character sequence. - int compare(size_type pos1, size_type count1, basic_string_view v, size_type pos2, size_type count2) const; - - /// \brief Compares two character sequences - /// - /// \param s pointer to the character string to compare to - /// \return negative value if this view is less than the other character - /// sequence, zero if the both character sequences are equal, positive - /// value if this view is greater than the other character sequence. - int compare(const char_type* s) const; - - /// \brief Compares two character sequences - /// - /// \param pos position of the first character in this view to compare - /// \param count number of characters of this view to compare - /// \param s pointer to the character string to compare to - /// \return negative value if this view is less than the other character - /// sequence, zero if the both character sequences are equal, positive - /// value if this view is greater than the other character sequence. - int compare(size_type pos, size_type count, const char_type* s) const; - - /// \brief Compares two character sequences - /// - /// \param pos position of the first character in this view to compare - /// \param count1 number of characters of this view to compare - /// \param s pointer to the character string to compare to - /// \param count2 number of characters of the given view to compare - /// \return negative value if this view is less than the other character - /// sequence, zero if the both character sequences are equal, positive - /// value if this view is greater than the other character sequence. - int compare(size_type pos, size_type count1, const char_type* s, size_type count2) const; - - //------------------------------------------------------------------------ - - size_type find(basic_string_view v, size_type pos = 0) const; - - size_type find(char_type c, size_type pos = 0) const; - - size_type find(const char_type* s, size_type pos, size_type count) const; - - size_type find(const char_type* s, size_type pos = 0) const; - - //------------------------------------------------------------------------ - - size_type rfind(basic_string_view v, size_type pos = npos) const; - - size_type rfind(char_type c, size_type pos = npos) const; - - size_type rfind(const char_type* s, size_type pos, size_type count) const; - - size_type rfind(const char_type* s, size_type pos = npos) const; - - //------------------------------------------------------------------------ - - size_type find_first_of(basic_string_view v, size_type pos = 0) const; - - size_type find_first_of(char_type c, size_type pos = 0) const; - - size_type find_first_of(const char_type* s, size_type pos, size_type count) const; - - size_type find_first_of(const char_type* s, size_type pos = 0) const; - - //------------------------------------------------------------------------ - - size_type find_last_of(basic_string_view v, size_type pos = npos) const; - - size_type find_last_of(char_type c, size_type pos = npos) const; - - size_type find_last_of(const char_type* s, size_type pos, size_type count) const; - - size_type find_last_of(const char_type* s, size_type pos = npos) const; - - //------------------------------------------------------------------------ - - size_type find_first_not_of(basic_string_view v, size_type pos = 0) const; - - size_type find_first_not_of(char_type c, size_type pos = 0) const; - - size_type find_first_not_of(const char_type* s, size_type pos, size_type count) const; - - size_type find_first_not_of(const char_type* s, size_type pos = 0) const; - - //------------------------------------------------------------------------ - - size_type find_last_not_of(basic_string_view v, size_type pos = npos) const; - - size_type find_last_not_of(char_type c, size_type pos = npos) const; - - size_type find_last_not_of(const char_type* s, size_type pos, size_type count) const; - - size_type find_last_not_of(const char_type* s, size_type pos = npos) const; - - //------------------------------------------------------------------------ - // Iterators - //------------------------------------------------------------------------ -public: - /// \brief Retrieves the begin iterator for this basic_string_view - /// - /// \return the begin iterator - const_iterator begin() const; - - /// \brief Retrieves the end iterator for this basic_string_view - /// - /// \return the end iterator - const_iterator end() const; - - /// \copydoc basic_string_view::begin() - const_iterator cbegin() const; - - /// \copydoc basic_string_view::end() - const_iterator cend() const; - - //------------------------------------------------------------------------ - // Private Member - //------------------------------------------------------------------------ -private: - const char_type* m_str; ///< The internal string type - size_type m_size; ///< The size of this string -}; - -//-------------------------------------------------------------------------- -// Type Aliases -//-------------------------------------------------------------------------- - -using string_view = basic_string_view; -# if defined(__cpp_lib_char8_t) -using u8string_view = basic_string_view; -# endif -using wstring_view = basic_string_view; -using u16string_view = basic_string_view; -using u32string_view = basic_string_view; - -//-------------------------------------------------------------------------- -// Inline Definitions -//-------------------------------------------------------------------------- - -//-------------------------------------------------------------------------- -// Constructor -//-------------------------------------------------------------------------- - -template -inline basic_string_view<_CharT, _Traits>::basic_string_view() : m_str(nullptr), m_size(0) -{} - -template -template -inline basic_string_view<_CharT, _Traits>::basic_string_view(const std::basic_string<_CharT, _Traits, Allocator>& str) : m_str(str.c_str()), m_size(str.size()) -{} - -template -inline basic_string_view<_CharT, _Traits>::basic_string_view(const char_type* str) : m_str(str), m_size(traits_type::length(str)) -{} - -template -inline basic_string_view<_CharT, _Traits>::basic_string_view(const char_type* str, size_type count) : m_str(str), m_size(count) -{} - -//-------------------------------------------------------------------------- -// Capacity -//-------------------------------------------------------------------------- - -template -inline typename basic_string_view<_CharT, _Traits>::size_type basic_string_view<_CharT, _Traits>::size() const -{ - return m_size; -} - -template -inline typename basic_string_view<_CharT, _Traits>::size_type basic_string_view<_CharT, _Traits>::length() const -{ - return size(); -} - -template -inline typename basic_string_view<_CharT, _Traits>::size_type basic_string_view<_CharT, _Traits>::max_size() const -{ - return npos - 1; -} - -template -inline bool basic_string_view<_CharT, _Traits>::empty() const -{ - return m_size == 0; -} - -//-------------------------------------------------------------------------- -// Element Access -//-------------------------------------------------------------------------- - -template -inline const typename basic_string_view<_CharT, _Traits>::char_type* basic_string_view<_CharT, _Traits>::c_str() const -{ - return m_str; -} - -template -inline const typename basic_string_view<_CharT, _Traits>::char_type* basic_string_view<_CharT, _Traits>::data() const -{ - return m_str; -} - -template -inline typename basic_string_view<_CharT, _Traits>::const_reference basic_string_view<_CharT, _Traits>::operator[](size_t pos) const -{ - return m_str[pos]; -} - -template -inline typename basic_string_view<_CharT, _Traits>::const_reference basic_string_view<_CharT, _Traits>::at(size_t pos) const -{ - if (yasio__unlikely(pos >= m_size)) - YASIO__THROW(std::out_of_range("Input out of range in basic_string_view::at"), 0); - return m_str[pos]; -} - -template -inline typename basic_string_view<_CharT, _Traits>::const_reference basic_string_view<_CharT, _Traits>::front() const -{ - return *m_str; -} - -template -inline typename basic_string_view<_CharT, _Traits>::const_reference basic_string_view<_CharT, _Traits>::back() const -{ - return m_str[m_size - 1]; -} - -//-------------------------------------------------------------------------- -// Modifiers -//-------------------------------------------------------------------------- - -template -inline void basic_string_view<_CharT, _Traits>::remove_prefix(size_type n) -{ - m_str += n, m_size -= n; -} - -template -inline void basic_string_view<_CharT, _Traits>::remove_suffix(size_type n) -{ - m_size -= n; -} - -template -inline void basic_string_view<_CharT, _Traits>::swap(basic_string_view& v) -{ - using std::swap; - swap(m_size, v.m_size); - swap(m_str, v.m_str); -} - -//-------------------------------------------------------------------------- -// Conversions -//-------------------------------------------------------------------------- - -template -template -inline std::basic_string<_CharT, _Traits, Allocator> basic_string_view<_CharT, _Traits>::to_string(const Allocator& a) const -{ - return std::basic_string<_CharT, _Traits, Allocator>(m_str, m_size, a); -} - -template -template -inline basic_string_view<_CharT, _Traits>::operator std::basic_string<_CharT, _Traits, Allocator>() const -{ - return std::basic_string<_CharT, _Traits, Allocator>(m_str, m_size); -} - -//-------------------------------------------------------------------------- -// String Operations -//-------------------------------------------------------------------------- - -template -inline typename basic_string_view<_CharT, _Traits>::size_type basic_string_view<_CharT, _Traits>::copy(char_type* dest, size_type count, size_type pos) const -{ - if (yasio__unlikely(pos >= m_size)) - YASIO__THROW(std::out_of_range("Index out of range in basic_string_view::copy"), 0); - - const size_type rcount = (std::min)(m_size - pos, count + 1); - std::copy(m_str + pos, m_str + pos + rcount, dest); - return rcount; -} - -template -inline basic_string_view<_CharT, _Traits> basic_string_view<_CharT, _Traits>::substr(size_t pos, size_t len) const -{ - const size_type max_length = pos > m_size ? 0 : m_size - pos; - if (yasio__unlikely(pos >= m_size)) - YASIO__THROW(std::out_of_range("Index out of range in basic_string_view::substr"), (basic_string_view<_CharT, _Traits>{})); - return basic_string_view<_CharT, _Traits>(m_str + pos, len > max_length ? max_length : len); -} - -//-------------------------------------------------------------------------- - -template -inline int basic_string_view<_CharT, _Traits>::compare(basic_string_view v) const -{ - const size_type rlen = (std::min)(m_size, v.m_size); - const int compare = _Traits::compare(m_str, v.m_str, rlen); - - return (compare ? compare : (m_size < v.m_size ? -1 : (m_size > v.m_size ? 1 : 0))); -} - -template -inline int basic_string_view<_CharT, _Traits>::compare(size_type pos, size_type count, basic_string_view v) const -{ - return substr(pos, count).compare(v); -} - -template -inline int basic_string_view<_CharT, _Traits>::compare(size_type pos1, size_type count1, basic_string_view v, size_type pos2, size_type count2) const -{ - return substr(pos1, count1).compare(v.substr(pos2, count2)); -} - -template -inline int basic_string_view<_CharT, _Traits>::compare(const char_type* s) const -{ - return compare(basic_string_view<_CharT, _Traits>(s)); -} - -template -inline int basic_string_view<_CharT, _Traits>::compare(size_type pos, size_type count, const char_type* s) const -{ - return substr(pos, count).compare(basic_string_view<_CharT, _Traits>(s)); -} - -template -inline int basic_string_view<_CharT, _Traits>::compare(size_type pos, size_type count1, const char_type* s, size_type count2) const -{ - return substr(pos, count1).compare(basic_string_view<_CharT, _Traits>(s, count2)); -} - -//-------------------------------------------------------------------------- - -template -inline typename basic_string_view<_CharT, _Traits>::size_type basic_string_view<_CharT, _Traits>::find(basic_string_view v, size_type pos) const -{ - return __xxtraits_find<_Traits>(m_str, m_size, pos, v.m_str, v.m_size); -} - -template -inline typename basic_string_view<_CharT, _Traits>::size_type basic_string_view<_CharT, _Traits>::find(char_type c, size_type pos) const -{ - return find(basic_string_view<_CharT, _Traits>(&c, 1), pos); -} - -template -inline typename basic_string_view<_CharT, _Traits>::size_type basic_string_view<_CharT, _Traits>::find(const char_type* s, size_type pos, size_type count) const -{ - return find(basic_string_view<_CharT, _Traits>(s, count), pos); -} - -template -inline typename basic_string_view<_CharT, _Traits>::size_type basic_string_view<_CharT, _Traits>::find(const char_type* s, size_type pos) const -{ - return find(basic_string_view<_CharT, _Traits>(s), pos); -} - -//-------------------------------------------------------------------------- - -template -inline typename basic_string_view<_CharT, _Traits>::size_type basic_string_view<_CharT, _Traits>::rfind(basic_string_view v, size_type pos) const -{ - return __xxtraits_rfind<_Traits>(m_str, m_size, pos, v.m_str, v.m_size); -} - -template -inline typename basic_string_view<_CharT, _Traits>::size_type basic_string_view<_CharT, _Traits>::rfind(char_type c, size_type pos) const -{ - return rfind(basic_string_view<_CharT, _Traits>(&c, 1), pos); -} - -template -inline typename basic_string_view<_CharT, _Traits>::size_type basic_string_view<_CharT, _Traits>::rfind(const char_type* s, size_type pos, - size_type count) const -{ - return rfind(basic_string_view<_CharT, _Traits>(s, count), pos); -} - -template -inline typename basic_string_view<_CharT, _Traits>::size_type basic_string_view<_CharT, _Traits>::rfind(const char_type* s, size_type pos) const -{ - return rfind(basic_string_view<_CharT, _Traits>(s), pos); -} - -//-------------------------------------------------------------------------- - -template -inline typename basic_string_view<_CharT, _Traits>::size_type basic_string_view<_CharT, _Traits>::find_first_of(basic_string_view v, size_type pos) const -{ - return __xxtraits_find_first_of<_Traits>(m_str, m_size, pos, v.m_str, v.m_size); -} - -template -inline typename basic_string_view<_CharT, _Traits>::size_type basic_string_view<_CharT, _Traits>::find_first_of(char_type c, size_type pos) const -{ - return __xxtraits_find_ch<_Traits>(m_str, m_size, pos, c); -} - -template -inline typename basic_string_view<_CharT, _Traits>::size_type basic_string_view<_CharT, _Traits>::find_first_of(const char_type* s, size_type pos, - size_type count) const -{ - return find_first_of(basic_string_view<_CharT, _Traits>(s, count), pos); -} - -template -inline typename basic_string_view<_CharT, _Traits>::size_type basic_string_view<_CharT, _Traits>::find_first_of(const char_type* s, size_type pos) const -{ - return find_first_of(basic_string_view<_CharT, _Traits>(s), pos); -} - -//-------------------------------------------------------------------------- - -template -inline typename basic_string_view<_CharT, _Traits>::size_type basic_string_view<_CharT, _Traits>::find_last_of(basic_string_view v, size_type pos) const -{ - return __xxtraits_find_last_of<_Traits>(m_str, m_size, pos, v.m_str, v.m_size); -} - -template -inline typename basic_string_view<_CharT, _Traits>::size_type basic_string_view<_CharT, _Traits>::find_last_of(char_type c, size_type pos) const -{ - return __xxtraits_rfind_ch<_Traits>(m_str, m_size, pos, c); -} - -template -inline typename basic_string_view<_CharT, _Traits>::size_type basic_string_view<_CharT, _Traits>::find_last_of(const char_type* s, size_type pos, - size_type count) const -{ - return find_last_of(basic_string_view<_CharT, _Traits>(s, count), pos); -} - -template -inline typename basic_string_view<_CharT, _Traits>::size_type basic_string_view<_CharT, _Traits>::find_last_of(const char_type* s, size_type pos) const -{ - return find_last_of(basic_string_view<_CharT, _Traits>(s), pos); -} - -//-------------------------------------------------------------------------- - -template -inline typename basic_string_view<_CharT, _Traits>::size_type basic_string_view<_CharT, _Traits>::find_first_not_of(basic_string_view v, size_type pos) const -{ - return __xxtraits_find_first_not_of<_Traits>(m_str, m_size, pos, v.m_str, v.m_size, pos); -} - -template -inline typename basic_string_view<_CharT, _Traits>::size_type basic_string_view<_CharT, _Traits>::find_first_not_of(char_type c, size_type pos) const -{ - return __xxtraits_find_not_ch<_Traits>(m_str, m_size, pos, c); -} - -template -inline typename basic_string_view<_CharT, _Traits>::size_type basic_string_view<_CharT, _Traits>::find_first_not_of(const char_type* s, size_type pos, - size_type count) const -{ - return find_first_not_of(basic_string_view<_CharT, _Traits>(s, count), pos); -} - -template -inline typename basic_string_view<_CharT, _Traits>::size_type basic_string_view<_CharT, _Traits>::find_first_not_of(const char_type* s, size_type pos) const -{ - return find_first_not_of(basic_string_view<_CharT, _Traits>(s), pos); -} - -//-------------------------------------------------------------------------- - -template -inline typename basic_string_view<_CharT, _Traits>::size_type basic_string_view<_CharT, _Traits>::find_last_not_of(basic_string_view v, size_type pos) const -{ - return __xxtraits_find_last_not_of<_Traits>(m_str, m_size, pos, v.m_str, v.m_size); -} - -template -inline typename basic_string_view<_CharT, _Traits>::size_type basic_string_view<_CharT, _Traits>::find_last_not_of(char_type c, size_type pos) const -{ - return __xxtraits_rfind_not_ch<_Traits>(m_str, m_size, pos, c); -} - -template -inline typename basic_string_view<_CharT, _Traits>::size_type basic_string_view<_CharT, _Traits>::find_last_not_of(const char_type* s, size_type pos, - size_type count) const -{ - return find_last_not_of(basic_string_view<_CharT, _Traits>(s, count), pos); -} - -template -inline typename basic_string_view<_CharT, _Traits>::size_type basic_string_view<_CharT, _Traits>::find_last_not_of(const char_type* s, size_type pos) const -{ - return find_last_not_of(basic_string_view<_CharT, _Traits>(s), pos); -} - -//-------------------------------------------------------------------------- -// Iterator -//-------------------------------------------------------------------------- - -template -inline typename basic_string_view<_CharT, _Traits>::const_iterator basic_string_view<_CharT, _Traits>::begin() const -{ - return m_str; -} - -template -inline typename basic_string_view<_CharT, _Traits>::const_iterator basic_string_view<_CharT, _Traits>::end() const -{ - return m_str + m_size; -} - -template -inline typename basic_string_view<_CharT, _Traits>::const_iterator basic_string_view<_CharT, _Traits>::cbegin() const -{ - return m_str; -} - -template -inline typename basic_string_view<_CharT, _Traits>::const_iterator basic_string_view<_CharT, _Traits>::cend() const -{ - return m_str + m_size; -} - -//-------------------------------------------------------------------------- -// Public Functions -//-------------------------------------------------------------------------- - -template -std::basic_ostream<_CharT, _Traits>& operator<<(std::basic_ostream<_CharT, _Traits>& o, const basic_string_view<_CharT, _Traits>& str) -{ - o.write(str.data(), str.size()); - return o; -} - -template -inline void swap(basic_string_view<_CharT, _Traits>& lhs, basic_string_view<_CharT, _Traits>& rhs) -{ - lhs.swap(rhs); -} - -//-------------------------------------------------------------------------- -// Comparison Functions -//-------------------------------------------------------------------------- - -template -inline bool operator==(const basic_string_view<_CharT, _Traits>& lhs, const basic_string_view<_CharT, _Traits>& rhs) -{ - return lhs.compare(rhs) == 0; -} - -template -inline bool operator==(basic_string_view<_CharT, _Traits> lhs, const _CharT* rhs) -{ - return lhs == basic_string_view<_CharT, _Traits>(rhs); -} - -template -inline bool operator==(const _CharT* lhs, const basic_string_view<_CharT, _Traits>& rhs) -{ - return basic_string_view<_CharT, _Traits>(lhs) == rhs; -} - -template -inline bool operator==(const std::basic_string<_CharT, _Traits, Allocator>& lhs, const basic_string_view<_CharT, _Traits>& rhs) -{ - return basic_string_view<_CharT, _Traits>(lhs) == rhs; -} - -template -inline bool operator==(const basic_string_view<_CharT, _Traits>& lhs, const std::basic_string<_CharT, _Traits, Allocator>& rhs) -{ - return lhs == basic_string_view<_CharT, _Traits>(rhs); -} - -//-------------------------------------------------------------------------- - -template -inline bool operator!=(const basic_string_view<_CharT, _Traits>& lhs, const basic_string_view<_CharT, _Traits>& rhs) -{ - return lhs.compare(rhs) != 0; -} - -template -inline bool operator!=(const basic_string_view<_CharT, _Traits>& lhs, const _CharT* rhs) -{ - return lhs != basic_string_view<_CharT, _Traits>(rhs); -} - -template -inline bool operator!=(const _CharT* lhs, const basic_string_view<_CharT, _Traits>& rhs) -{ - return basic_string_view<_CharT, _Traits>(lhs) != rhs; -} - -template -inline bool operator!=(const std::basic_string<_CharT, _Traits, Allocator>& lhs, const basic_string_view<_CharT, _Traits>& rhs) -{ - return basic_string_view<_CharT, _Traits>(lhs) != rhs; -} - -template -inline bool operator!=(const basic_string_view<_CharT, _Traits>& lhs, const std::basic_string<_CharT, _Traits, Allocator>& rhs) -{ - return lhs != basic_string_view<_CharT, _Traits>(rhs); -} -//-------------------------------------------------------------------------- - -template -inline bool operator<(const basic_string_view<_CharT, _Traits>& lhs, const basic_string_view<_CharT, _Traits>& rhs) -{ - return lhs.compare(rhs) < 0; -} - -template -inline bool operator<(const basic_string_view<_CharT, _Traits>& lhs, const _CharT* rhs) -{ - return lhs < basic_string_view<_CharT, _Traits>(rhs); -} - -template -inline bool operator<(const _CharT* lhs, const basic_string_view<_CharT, _Traits>& rhs) -{ - return basic_string_view<_CharT, _Traits>(lhs) < rhs; -} - -template -inline bool operator<(const std::basic_string<_CharT, _Traits, Allocator>& lhs, const basic_string_view<_CharT, _Traits>& rhs) -{ - return basic_string_view<_CharT, _Traits>(lhs) < rhs; -} - -template -inline bool operator<(const basic_string_view<_CharT, _Traits>& lhs, const std::basic_string<_CharT, _Traits, Allocator>& rhs) -{ - return lhs < basic_string_view<_CharT, _Traits>(rhs); -} - -//-------------------------------------------------------------------------- - -template -inline bool operator>(const basic_string_view<_CharT, _Traits>& lhs, const basic_string_view<_CharT, _Traits>& rhs) -{ - return lhs.compare(rhs) > 0; -} - -template -inline bool operator>(const basic_string_view<_CharT, _Traits>& lhs, const _CharT* rhs) -{ - return lhs > basic_string_view<_CharT, _Traits>(rhs); -} - -template -inline bool operator>(const _CharT* lhs, const basic_string_view<_CharT, _Traits>& rhs) -{ - return basic_string_view<_CharT, _Traits>(lhs) > rhs; -} - -template -inline bool operator>(const std::basic_string<_CharT, _Traits, Allocator>& lhs, const basic_string_view<_CharT, _Traits>& rhs) -{ - return basic_string_view<_CharT, _Traits>(lhs) > rhs; -} - -template -inline bool operator>(const basic_string_view<_CharT, _Traits>& lhs, const std::basic_string<_CharT, _Traits, Allocator>& rhs) -{ - return lhs > basic_string_view<_CharT, _Traits>(rhs); -} - -//-------------------------------------------------------------------------- - -template -inline bool operator<=(const basic_string_view<_CharT, _Traits>& lhs, const basic_string_view<_CharT, _Traits>& rhs) -{ - return lhs.compare(rhs) <= 0; -} - -template -inline bool operator<=(const basic_string_view<_CharT, _Traits>& lhs, const _CharT* rhs) -{ - return lhs <= basic_string_view<_CharT, _Traits>(rhs); -} - -template -inline bool operator<=(const _CharT* lhs, const basic_string_view<_CharT, _Traits>& rhs) -{ - return basic_string_view<_CharT, _Traits>(lhs) <= rhs; -} - -template -inline bool operator<=(const std::basic_string<_CharT, _Traits, Allocator>& lhs, const basic_string_view<_CharT, _Traits>& rhs) -{ - return basic_string_view<_CharT, _Traits>(lhs) <= rhs; -} - -template -inline bool operator<=(const basic_string_view<_CharT, _Traits>& lhs, const std::basic_string<_CharT, _Traits, Allocator>& rhs) -{ - return lhs <= basic_string_view<_CharT, _Traits>(rhs); -} - -//-------------------------------------------------------------------------- - -template -inline bool operator>=(const basic_string_view<_CharT, _Traits>& lhs, const basic_string_view<_CharT, _Traits>& rhs) -{ - return lhs.compare(rhs) >= 0; -} - -template -inline bool operator>=(const basic_string_view<_CharT, _Traits>& lhs, const _CharT* rhs) -{ - return lhs >= basic_string_view<_CharT, _Traits>(rhs); -} - -template -inline bool operator>=(const _CharT* lhs, const basic_string_view<_CharT, _Traits>& rhs) -{ - return basic_string_view<_CharT, _Traits>(lhs) >= rhs; -} - -template -inline bool operator>=(const std::basic_string<_CharT, _Traits, Allocator>& lhs, const basic_string_view<_CharT, _Traits>& rhs) -{ - return basic_string_view<_CharT, _Traits>(lhs) >= rhs; -} - -template -inline bool operator>=(const basic_string_view<_CharT, _Traits>& lhs, const std::basic_string<_CharT, _Traits, Allocator>& rhs) -{ - return lhs >= basic_string_view<_CharT, _Traits>(rhs); -} -//----------------------------------------------------------------- -// FNV1a hash from msvc++ -# if YASIO__64BITS -static const size_t _FNV_offset_basis = 14695981039346656037ULL; -static const size_t _FNV_prime = 1099511628211ULL; -# else /* defined(_M_X64), etc. */ -static const size_t _FNV_offset_basis = 2166136261U; -static const size_t _FNV_prime = 16777619U; -# endif /* defined(_M_X64), etc. */ -inline size_t _FNV1a_hash(const void* _First, size_t _Count) -{ // FNV-1a hash function for bytes in [_First, _First+_Count) - size_t _Val = _FNV_offset_basis; - for (size_t _Next = 0; _Next < _Count; ++_Next) - { // fold in another byte - _Val ^= (size_t) static_cast(_First)[_Next]; - _Val *= _FNV_prime; - } - - return (_Val); -} -} // namespace cxx17 -namespace std -{ -template -struct hash> { // hash functor for basic_string_view - typedef cxx17::basic_string_view<_Elem> _Kty; - - size_t operator()(const _Kty& _Keyval) const - { // hash _Keyval to size_t value by pseudorandomizing transform -# if defined(_LIBCPP_VERSION) - return __do_string_hash(_Keyval.data(), _Keyval.data() + _Keyval.size()); -# elif defined(__GLIBCXX__) - return _Hash_impl::hash(_Keyval.data(), _Keyval.size() * sizeof(_Elem)); -# else // msvc++ or other compiler without stable hash bytes function exists - return ::cxx17::_FNV1a_hash(_Keyval.data(), _Keyval.size() * sizeof(_Elem)); -# endif - } -}; -} // namespace std -#endif - -#if YASIO__HAS_CXX14 -namespace cxx17 -{ -// basic_string_view LITERALS -inline namespace literals -{ -inline namespace string_view_literals -{ -inline cxx17::string_view operator""_sv(const char* _Str, size_t _Len) { return cxx17::string_view(_Str, _Len); } -inline cxx17::wstring_view operator""_sv(const wchar_t* _Str, size_t _Len) { return cxx17::wstring_view(_Str, _Len); } -inline cxx17::u16string_view operator""_sv(const char16_t* _Str, size_t _Len) { return cxx17::u16string_view(_Str, _Len); } -inline cxx17::u32string_view operator""_sv(const char32_t* _Str, size_t _Len) { return cxx17::u32string_view(_Str, _Len); } -} // namespace string_view_literals -} // namespace literals -} // namespace cxx17 -#endif - -namespace cxx17 -{ -template -inline std::basic_string<_CharT, _Traits, Allocator>& assign(std::basic_string<_CharT, _Traits, Allocator>& lhs, const basic_string_view<_CharT, _Traits>& rhs) -{ - if (!rhs.empty()) - lhs.assign(rhs.data(), rhs.size()); - else - lhs.clear(); - return lhs; -} -template -inline std::basic_string<_CharT, _Traits, Allocator>& append(std::basic_string<_CharT, _Traits, Allocator>& lhs, const basic_string_view<_CharT, _Traits>& rhs) -{ - if (!rhs.empty()) - lhs.append(rhs.data(), rhs.size()); - else - lhs.clear(); - return lhs; -} -template > -inline std::basic_string<_CharT, _Traits, Allocator> svtos(const basic_string_view<_CharT, _Traits>& value) -{ - using string_type = std::basic_string<_CharT, _Traits, Allocator>; - return !value.empty() ? string_type(value.data(), value.size()) : string_type{}; -} -} // namespace cxx17 - -namespace cxx20 -{ -template -using decay_t = typename std::decay::type; -template -using remove_const_t = typename std::remove_const::type; -namespace char_ranges -{ // allow get char type from char*, wchar_t*, std::string, std::wstring -template -struct value_type { - using type = typename _Ty::value_type; -}; - -template -struct value_type<_Ty&> { - using type = remove_const_t<_Ty>; -}; - -template -struct value_type<_Ty*> { - using type = remove_const_t<_Ty>; -}; -} // namespace char_ranges - -// starts_with(), since C++20: -template -inline bool starts_with(cxx17::basic_string_view<_CharT> lhs, - cxx17::basic_string_view<_CharT> v) // (1) -{ - return lhs.size() >= v.size() && lhs.compare(0, v.size(), v) == 0; -} - -template -inline bool starts_with(_T1&& lhs, _T2&& v) // (2) -{ - using char_type = typename char_ranges::value_type>::type; - return starts_with(cxx17::basic_string_view{lhs}, cxx17::basic_string_view{v}); -} - -template -inline bool starts_with(cxx17::basic_string_view<_CharT> lhs, int c) // (3) -{ - return !lhs.empty() && lhs.front() == c; -} - -template -inline bool starts_with(_Ty&& lhs, int c) // (4) -{ - using char_type = typename char_ranges::value_type>::type; - return starts_with(cxx17::basic_string_view{lhs}, c); -} - -// ends_with(), since C++20: -template -inline bool ends_with(cxx17::basic_string_view<_CharT> lhs, - cxx17::basic_string_view<_CharT> v) // (1) -{ - auto offset = lhs.size() - v.size(); - return lhs.size() >= v.size() && lhs.compare(offset, v.size(), v) == 0; -} - -template -inline bool ends_with(_T1&& lhs, _T2&& v) // (2) -{ - using char_type = typename char_ranges::value_type>::type; - return ends_with(cxx17::basic_string_view{lhs}, cxx17::basic_string_view{v}); -} - -template -inline bool ends_with(cxx17::basic_string_view<_CharT> lhs, int c) // (3) -{ - return !lhs.empty() && lhs.back() == c; -} - -template -inline bool ends_with(_Ty&& lhs, int c) // (4) -{ - using char_type = typename char_ranges::value_type>::type; - return ends_with(cxx17::basic_string_view{lhs}, c); -} - -/// The case insensitive implementation of starts_with, ends_with -namespace ic -{ -template -inline bool iequals(cxx17::basic_string_view<_CharT> lhs, cxx17::basic_string_view<_CharT> v); -#if defined(_MSC_VER) -template <> -inline bool iequals(cxx17::basic_string_view lhs, cxx17::basic_string_view v) -{ - return lhs.size() == v.size() && ::_strnicmp(lhs.data(), v.data(), v.size()) == 0; -} -template <> -inline bool iequals(cxx17::basic_string_view lhs, cxx17::basic_string_view v) -{ - return lhs.size() == v.size() && ::_wcsnicmp(lhs.data(), v.data(), v.size()) == 0; -} -#else -template <> -inline bool iequals(cxx17::basic_string_view lhs, cxx17::basic_string_view v) -{ - return lhs.size() == v.size() && ::strncasecmp(lhs.data(), v.data(), v.size()) == 0; -} -template <> -inline bool iequals(cxx17::basic_string_view lhs, cxx17::basic_string_view v) -{ - return lhs.size() == v.size() && ::wcsncasecmp(lhs.data(), v.data(), v.size()) == 0; -} -#endif -template -inline bool iequals(_T1&& lhs, _T2&& v) -{ - using char_type = typename char_ranges::value_type>::type; - return iequals(cxx17::basic_string_view{lhs}, cxx17::basic_string_view{v}); -} -// starts_with(), since C++20: -template -inline bool starts_with(cxx17::basic_string_view<_CharT> lhs, - cxx17::basic_string_view<_CharT> v) // (1) -{ - return lhs.size() >= v.size() && iequals(lhs.substr(0, v.size()), v); -} - -template -inline bool starts_with(_T1&& lhs, _T2&& v) // (2) -{ - using char_type = typename char_ranges::value_type>::type; - return starts_with(cxx17::basic_string_view{lhs}, cxx17::basic_string_view{v}); -} - -template -inline bool starts_with(cxx17::basic_string_view<_CharT> lhs, int c) // (3) -{ - return !lhs.empty() && ::tolower(lhs.front()) == ::tolower(c); -} - -template -inline bool starts_with(_Ty&& lhs, int c) // (4) -{ - using char_type = typename char_ranges::value_type>::type; - return starts_with(cxx17::basic_string_view{lhs}, c); -} - -// ends_with(), since C++20: -template -inline bool ends_with(cxx17::basic_string_view<_CharT> lhs, - cxx17::basic_string_view<_CharT> v) // (1) -{ - return lhs.size() >= v.size() && iequals(lhs.substr(lhs.size() - v.size(), lhs.npos), v); -} - -template -inline bool ends_with(_T1&& lhs, _T2&& v) // (2) -{ - using char_type = typename char_ranges::value_type>::type; - return ends_with(cxx17::basic_string_view{lhs}, cxx17::basic_string_view{v}); -} - -template -inline bool ends_with(cxx17::basic_string_view<_CharT> lhs, int c) // (3) -{ - return !lhs.empty() && ::tolower(lhs.back()) == ::tolower(c); -} - -template -inline bool ends_with(_Ty&& lhs, int c) // (4) -{ - using char_type = typename char_ranges::value_type>::type; - return ends_with(cxx17::basic_string_view{lhs}, c); -} -} // namespace ic -} // namespace cxx20 - -#endif diff --git a/yasio/tlx/array_buffer.hpp b/yasio/tlx/array_buffer.hpp new file mode 100644 index 000000000..ff7ae2e07 --- /dev/null +++ b/yasio/tlx/array_buffer.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include "yasio/tlx/vector.hpp" +#include "yasio/tlx/buffer_alloc.hpp" +#include + +namespace tlx +{ +// alias: array_buffer +// Usage restriction: only valid for types that meet ALL of the following: +// 1. Trivially default constructible +// 2. Trivially destructible +// 3. Trivially copyable +// +// In other words, this alias is intended only for POD or standard-layout types, +// ensuring that zero-initialization and raw memory operations (e.g. memcpy, memset) +// are safe and well-defined. +template > +using array_buffer = typename std::enable_if::value, ::tlx::vector<_Ty, _Alloc, fill_policy::nontrivial>>::type; +} // namespace tlx diff --git a/yasio/tlx/buffer_alloc.hpp b/yasio/tlx/buffer_alloc.hpp new file mode 100644 index 000000000..332009687 --- /dev/null +++ b/yasio/tlx/buffer_alloc.hpp @@ -0,0 +1,88 @@ +////////////////////////////////////////////////////////////////////////////////////////// +// A multi-platform support c++11 library with focus on asynchronous socket I/O for any +// client application. +////////////////////////////////////////////////////////////////////////////////////////// +/* +The MIT License (MIT) + +Copyright (c) 2012-2025 HALX99 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef YASIO__BUFFER_ALLOC_HPP +#define YASIO__BUFFER_ALLOC_HPP +#include +#include +#include +#include +#include +#include +#include "yasio/compiler/feature_test.hpp" +#include "yasio/tlx/type_traits.hpp" + +namespace tlx +{ +template +struct crt_buffer_allocator { + using value_type = T; + using pointer = T*; + using const_pointer = const T*; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + // allocate n elements using CRT malloc + pointer allocate(size_type n) + { + if (n > max_size()) + throw std::bad_alloc(); + void* p = ::malloc(n * sizeof(T)); + if (!p) + throw std::bad_alloc(); + return static_cast(p); + } + + // deallocate memory using CRT free + void deallocate(pointer p, size_type /*n*/) noexcept { ::free(p); } + + // optional reallocate (non-standard, but useful for vector) + pointer reallocate(pointer p, size_type old_count, size_type new_count) + { + void* np = ::realloc(p, new_count * sizeof(T)); + if (!np) + throw std::bad_alloc(); + return static_cast(np); + } + + // max_size consistent with std::allocator + size_type max_size() const noexcept { return static_cast(-1) / sizeof(T); } + + // equality operators required by standard + template + struct rebind { + using other = crt_buffer_allocator; + }; + + bool operator==(const crt_buffer_allocator&) const noexcept { return true; } + bool operator!=(const crt_buffer_allocator&) const noexcept { return false; } +}; + +} // namespace tlx + +#endif diff --git a/yasio/byte_buffer.hpp b/yasio/tlx/byte_buffer.hpp similarity index 83% rename from yasio/byte_buffer.hpp rename to yasio/tlx/byte_buffer.hpp index 75f44050b..31b3d43ed 100644 --- a/yasio/byte_buffer.hpp +++ b/yasio/tlx/byte_buffer.hpp @@ -27,16 +27,16 @@ SOFTWARE. */ #ifndef YASIO__BYTE_BUFFER_HPP #define YASIO__BYTE_BUFFER_HPP -#include "yasio/pod_vector.hpp" +#include "yasio/tlx/array_buffer.hpp" -namespace yasio +namespace tlx { - -template , enable_if_t::value, int> = 0> -using basic_byte_buffer = array_buffer<_Ty, _Alloc>; +// basic_byte_buffer restricted to byte types +template > +using basic_byte_buffer = typename std::enable_if::value, tlx::array_buffer<_Ty, _Alloc>>::type; using sbyte_buffer = basic_byte_buffer; using byte_buffer = basic_byte_buffer; -} // namespace yasio +} // namespace tlx #endif diff --git a/yasio/utils.hpp b/yasio/tlx/chrono.hpp similarity index 85% rename from yasio/utils.hpp rename to yasio/tlx/chrono.hpp index da2234a3c..7290e151d 100644 --- a/yasio/utils.hpp +++ b/yasio/tlx/chrono.hpp @@ -33,7 +33,7 @@ SOFTWARE. #include #include "yasio/compiler/feature_test.hpp" -namespace yasio +namespace tlx { // typedefs typedef long long highp_time_t; @@ -68,29 +68,6 @@ inline highp_time_t clock() // https://docs.microsoft.com/en-us/windows/desktop/sysinfo/acquiring-high-resolution-time-stamps inline highp_time_t time_now() { return ::time(nullptr); } -#if YASIO__HAS_CXX17 -using std::clamp; -#else -template -const _Ty& clamp(const _Ty& v, const _Ty& lo, const _Ty& hi) -{ - assert(!(hi < lo)); - return v < lo ? lo : hi < v ? hi : v; -} -#endif - -template -inline void invoke_dtor(_Ty* p) -{ - p->~_Ty(); -} - -inline bool is_regular_file(const char* path) -{ - struct stat st; - return (::stat(path, &st) == 0) && (st.st_mode & S_IFREG); -} - -} // namespace yasio +} // namespace tlx #endif diff --git a/yasio/file.hpp b/yasio/tlx/file_io.hpp similarity index 84% rename from yasio/file.hpp rename to yasio/tlx/file_io.hpp index edaf0cf67..7abbd5977 100644 --- a/yasio/file.hpp +++ b/yasio/tlx/file_io.hpp @@ -28,13 +28,13 @@ SOFTWARE. #pragma once -#include "yasio/string.hpp" -#include "yasio/string_view.hpp" +#include "yasio/tlx/string.hpp" +#include "yasio/tlx/string_view.hpp" #include -namespace yasio +namespace tlx { -inline yasio::string read_text_file(cxx17::string_view file_path) +inline tlx::string read_text_file(std::string_view file_path) { std::ifstream fin(file_path.data(), std::ios_base::binary); if (fin.is_open()) @@ -43,7 +43,7 @@ inline yasio::string read_text_file(cxx17::string_view file_path) auto n = static_cast(fin.tellg()); if (n > 0) { - yasio::string ret; + tlx::string ret; ret.resize_and_overwrite(n, [&fin](char* out, size_t outlen) { fin.seekg(std::ios_base::beg); fin.read(out, outlen); @@ -52,6 +52,13 @@ inline yasio::string read_text_file(cxx17::string_view file_path) return ret; } } - return yasio::string{}; + return tlx::string{}; } -} // namespace yasio + +inline bool is_regular_file(const char* path) +{ + struct stat st; + return (::stat(path, &st) == 0) && (st.st_mode & S_IFREG); +} + +} // namespace tlx diff --git a/yasio/tlx/memory.hpp b/yasio/tlx/memory.hpp new file mode 100644 index 000000000..c0ee11779 --- /dev/null +++ b/yasio/tlx/memory.hpp @@ -0,0 +1,501 @@ +////////////////////////////////////////////////////////////////////////////////////////// +// A multi-platform support c++11 library with focus on asynchronous socket I/O for any +// client application. +////////////////////////////////////////////////////////////////////////////////////////// +/* +The MIT License (MIT) + +Copyright (c) 2012-2025 HALX99 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +#include "yasio/compiler/feature_test.hpp" + +namespace tlx +{ +template +struct construct_helper { + template + static _Ty* construct_at(_Ty* p, Args&&... args) + { + return ::new (static_cast(p)) _Ty(std::forward(args)...); + } +}; +template +struct construct_helper<_Ty, false> { + template + static _Ty* construct_at(_Ty* p, Args&&... args) + { + return ::new (static_cast(p)) _Ty{std::forward(args)...}; + } +}; + +template +inline _Ty* construct_at(_Ty* p, Args&&... args) +{ + return construct_helper<_Ty, std::is_constructible<_Ty, Args&&...>::value>::construct_at(p, std::forward(args)...); +} + +template +inline void invoke_dtor(_Ty* p) +{ + p->~_Ty(); +} + +struct __zero_then_variadic_args_t { + explicit __zero_then_variadic_args_t() = default; +}; // tag type for value-initializing first, constructing second from remaining args + +struct __one_then_variadic_args_t { + explicit __one_then_variadic_args_t() = default; +}; // tag type for constructing first from one arg, constructing second from remaining args + +template && !std::is_final_v<_Ty1>> +class __compressed_pair final : private _Ty1 { // store a pair of values, deriving from empty first +public: + _Ty2 _Myval2; + + using _Mybase = _Ty1; // for visualization + + template + constexpr explicit __compressed_pair(__zero_then_variadic_args_t, _Other2&&... _Val2) noexcept : _Ty1(), _Myval2(std::forward<_Other2>(_Val2)...) + {} + + template + constexpr __compressed_pair(__one_then_variadic_args_t, _Other1&& _Val1, _Other2&&... _Val2) noexcept + : _Ty1(std::forward<_Other1>(_Val1)), _Myval2(std::forward<_Other2>(_Val2)...) + {} + + constexpr _Ty1& _Get_first() noexcept { return *this; } + + constexpr const _Ty1& _Get_first() const noexcept { return *this; } +}; + +template +class __compressed_pair<_Ty1, _Ty2, false> final { // store a pair of values, not deriving from first +public: + _Ty1 _Myval1; + _Ty2 _Myval2; + + template + constexpr explicit __compressed_pair(__zero_then_variadic_args_t, _Other2&&... _Val2) noexcept : _Myval1(), _Myval2(std::forward<_Other2>(_Val2)...) + {} + + template + constexpr __compressed_pair(__one_then_variadic_args_t, _Other1&& _Val1, _Other2&&... _Val2) noexcept + : _Myval1(std::forward<_Other1>(_Val1)), _Myval2(std::forward<_Other2>(_Val2)...) + {} + + constexpr _Ty1& _Get_first() noexcept { return _Myval1; } + + constexpr const _Ty1& _Get_first() const noexcept { return _Myval1; } +}; + +template +using rebind_alloc_t = typename std::allocator_traits<_Alloc>::template rebind_alloc<_Value_type>; + +template +struct __tidy_guard { // class with destructor that calls _Tidy + _Ty* _Target; + YASIO__CONSTEXPR ~__tidy_guard() + { + if (_Target) + { + _Target->_Tidy(); + } + } +}; + +template +struct _Identity { + using type = _Ty; +}; +template +using identity_t = typename _Identity<_Ty>::type; + +template +class __uninitialized_backout_al { +public: + using allocator_traits = typename std::allocator_traits<_Alloc>; + using pointer = typename allocator_traits::pointer; + using value_type = typename allocator_traits::value_type; + + __uninitialized_backout_al(pointer dest, _Alloc& al) : _dest(dest), _cur(dest), _alloc(al), _released(false) {} + + ~__uninitialized_backout_al() + { + if (!_released) + { + for (auto p = _dest; p != _cur; ++p) + { + std::allocator_traits<_Alloc>::destroy(_alloc, p); + } + } + } + + template + void _Emplace_back(Args&&... args) + { + std::allocator_traits<_Alloc>::construct(_alloc, _cur, std::forward(args)...); + ++_cur; + } + + pointer _Release() + { + _released = true; + return _cur; + } + +private: + pointer _dest; + pointer _cur; + _Alloc& _alloc; + bool _released; +}; + +template +using iter_ref_t = typename std::iterator_traits<_Iter>::reference; + +inline void __xlength_error(const char* what) { throw ::std::length_error(what); } + +inline void __xout_of_range(const char* what) { throw ::std::out_of_range(what); } + +static_assert(std::is_same_v::value_type, int>); + +template +inline constexpr bool is_pod_iterator_v = std::is_trivially_copyable_v::value_type>; + +template +inline constexpr bool bitcopy_assignable_v = std::is_trivially_copyable_v::value_type> && + std::is_same_v::value_type, typename std::pointer_traits<_Ptr>::element_type>; + +template +_Ptr uninitialized_fill_n(_Ptr first, size_t count, const typename _Alloc::value_type& val, _Alloc& alloc) +{ + using value_type = typename _Alloc::value_type; + + if constexpr (std::is_trivially_copyable_v) + { + if constexpr (sizeof(value_type) == 1) + { + ::memset(first, static_cast(val), count); + } + else + { + for (size_t i = 0; i < count; ++i) + { + first[i] = val; + } + } + return first + count; + } + else + { + for (size_t i = 0; i < count; ++i) + { + std::allocator_traits<_Alloc>::construct(alloc, first + i, val); + } + return first + count; + } +} + +template +constexpr _OutPtr uninitialized_move(_InIt first, _InIt last, _OutPtr dest, _Alloc& alloc) +{ + using T = typename _Alloc::value_type; + const auto count = static_cast(last - first); + + if constexpr (std::is_trivially_copyable_v) + { + if (!std::is_constant_evaluated()) + { + ::memmove(std::to_address(dest), std::to_address(first), count * sizeof(T)); + return dest + count; + } + } + + __uninitialized_backout_al<_Alloc> backout{dest, alloc}; + for (; first != last; ++first) + { + backout._Emplace_back(std::move(*first)); + } + return backout._Release(); +} + +template +constexpr _OutPtr uninitialized_copy_n(_InIt first, size_t count, _OutPtr dest, _Alloc& alloc) +{ + using T = typename _Alloc::value_type; + + if constexpr (std::is_trivially_copyable_v) + { + ::memmove(std::to_address(dest), std::to_address(first), count * sizeof(T)); + return dest + count; + } + else + { + __uninitialized_backout_al<_Alloc> backout{dest, alloc}; + for (; count != 0; ++first, --count) + { + backout._Emplace_back(*first); + } + return backout._Release(); + } +} + +template +_Ptr uninitialized_copy(_InIt first, _InIt last, _Ptr dest, _Alloc& alloc) +{ + using T = typename _Alloc::value_type; + + const auto count = static_cast(last - first); + + if constexpr (std::is_trivially_copyable_v) + { + ::memmove(dest, &*first, count * sizeof(T)); + return dest + count; + } + else + { + __uninitialized_backout_al<_Alloc> backout{dest, alloc}; + for (; first != last; ++first) + { + backout._Emplace_back(*first); + } + return backout._Release(); + } +} + +template +constexpr void destroy_range(Ptr first, Ptr last, _Alloc& alloc) noexcept +{ + using T = typename _Alloc::value_type; + + if constexpr (!std::is_trivially_destructible_v) + { + for (; first != last; ++first) + { + std::allocator_traits<_Alloc>::destroy(alloc, std::to_address(first)); + } + } +} + +/* + * trivially_constructible types: Use memset to zero for + * non-trivially_constructible types: default constructor + */ +template +_Ptr uninitialized_value_construct_n(_Ptr first, size_t count, _Alloc& alloc) +{ + using T = typename _Alloc::value_type; + + if constexpr (std::is_trivially_constructible_v) + { + ::memset(first, 0, count * sizeof(T)); + return first + count; + } + else + { + __uninitialized_backout_al<_Alloc> backout{first, alloc}; + for (; count > 0; --count) + { + backout._Emplace_back(); + } + return backout._Release(); + } +} + +template +OutCtgIt copy_memmove_n(InCtgIt first, size_t count, OutCtgIt dest) +{ + using T = typename std::iterator_traits::value_type; + + const auto byte_count = count * sizeof(T); + + const auto src_ptr = std::to_address(first); + const auto dest_ptr = std::to_address(dest); + + ::memmove(dest_ptr, src_ptr, byte_count); + + if constexpr (std::is_pointer_v) + { + return dest_ptr + count; + } + else + { + return dest + static_cast::difference_type>(count); + } +} + +template +constexpr _OutIt move_unchecked(_InIt first, _InIt last, _OutIt dest) +{ + using T = typename std::iterator_traits<_InIt>::value_type; + const auto count = static_cast(last - first); + + if constexpr (std::is_trivially_copyable_v) + { + ::memmove(std::to_address(dest), std::to_address(first), count * sizeof(T)); + return dest + count; + } + else + { + for (; first != last; ++first, ++dest) + { + *dest = std::move(*first); + } + return dest; + } +} + +template +_CtgIt2 copy_backward_memmove(_CtgIt1 _First, _CtgIt1 _Last, _CtgIt2 _Dest) +{ + // implement copy_backward-like function as memmove + const auto _First_ptr = std::to_address(_First); + const auto _Last_ptr = std::to_address(_Last); + const auto _Dest_ptr = std::to_address(_Dest); + + const auto _First_ch = const_cast(reinterpret_cast(_First_ptr)); + const auto _Last_ch = const_cast(reinterpret_cast(_Last_ptr)); + const auto _Dest_ch = const_cast(reinterpret_cast(_Dest_ptr)); + + const auto _Count = static_cast(_Last_ch - _First_ch); + const auto _Result = ::memmove(_Dest_ch - _Count, _First_ch, _Count); + + if constexpr (std::is_pointer_v<_CtgIt2>) + { + return static_cast<_CtgIt2>(_Result); + } + else + { + using diff_t = typename std::iterator_traits<_CtgIt2>::difference_type; + return _Dest - static_cast(_Last_ptr - _First_ptr); + } +} + +template +_BidIt2 copy_backward_memmove(std::move_iterator<_BidIt1> _First, std::move_iterator<_BidIt1> _Last, _BidIt2 _Dest) +{ + return copy_backward_memmove(_First.base(), _Last.base(), _Dest); +} + +template +_BidIt2 move_backward_unchecked(_BidIt1 _First, _BidIt1 _Last, _BidIt2 _Dest) +{ + // move [_First, _Last) backwards to [..., _Dest) + // note: move_backward_unchecked has callers other than the move_backward family + if constexpr (is_pod_iterator_v<_BidIt2>) + { + if (!std::is_constant_evaluated()) + { + return copy_backward_memmove(_First, _Last, _Dest); + } + } + + while (_First != _Last) + { + *--_Dest = std::move(*--_Last); + } + + return _Dest; +} + +template +constexpr _OutIt copy_n_unchecked4(_InIt first, _SizeTy count, _OutIt dest) +{ + using T = typename std::iterator_traits<_InIt>::value_type; + + static_assert(std::is_integral_v<_SizeTy>, "must be integer-like"); + if (count < 0) + return dest; + + if constexpr (std::is_trivially_copyable_v) + { + if (!std::is_constant_evaluated()) + { + ::memmove(std::to_address(dest), std::to_address(first), count * sizeof(T)); + return dest + count; + } + } + + for (; count != 0; ++dest, ++first, --count) + { + *dest = *first; + } + return dest; +} + +// Enumeration for fill policies with positive semantics +enum class fill_policy +{ + always, // Always perform value initialization + nontrivial_dtor, // Fill if destructor is non-trivial + nontrivial, // Fill if either ctor or dtor is non-trivial +}; + +// Trait to determine whether filling is required under a given policy +template +struct __allow_auto_fill { + static constexpr bool value = true; // Default: always fill +}; + +// Specialization: fill only if destructor is non-trivial +template +struct __allow_auto_fill<_Ty, fill_policy::nontrivial_dtor> { + static constexpr bool value = !std::is_trivially_destructible_v<_Ty>; +}; + +// Specialization: fill if either ctor or dtor is non-trivial +template +struct __allow_auto_fill<_Ty, fill_policy::nontrivial> { + static constexpr bool value = !(std::is_trivially_default_constructible_v<_Ty> && std::is_trivially_destructible_v<_Ty>); +}; + +template +inline constexpr bool __allow_auto_fill_v = __allow_auto_fill<_Ty, policy>::value; + +struct __auto_value_init_t { // tag to request value-initialization + explicit __auto_value_init_t() = default; +}; + +struct value_init_t { // tag to request value-initialization + explicit value_init_t() = default; +}; +inline constexpr value_init_t value_init = value_init_t{}; + +struct no_init_t { // tag to request no-initialization + explicit no_init_t() = default; +}; +inline constexpr no_init_t no_init = no_init_t{}; + +} // namespace tlx + +#define _TLX_VERIFY(cond, mesg) assert(cond&& mesg) + +#define _TLX_INTERNAL_CHECK(cond) assert(cond) diff --git a/yasio/shared_mutex.hpp b/yasio/tlx/shared_mutex.hpp similarity index 95% rename from yasio/shared_mutex.hpp rename to yasio/tlx/shared_mutex.hpp index 25c6f83bb..8061d506e 100644 --- a/yasio/shared_mutex.hpp +++ b/yasio/tlx/shared_mutex.hpp @@ -22,10 +22,16 @@ /// The shared_mutex workaround on c++11 #if YASIO__HAS_CXX17 && !defined(__APPLE__) # include +namespace tlx +{ +using shared_mutex = ::std::shared_mutex; +template +using shared_lock = ::std::shared_lock<_Mutex>; +} // namespace tlx #else # include # include -namespace cxx17 +namespace tlx { // CLASS TEMPLATE shared_mutex class shared_mutex { @@ -60,7 +66,7 @@ class shared_mutex { bool one_or_more_readers() const { return (state_ & n_readers_) > 0; } - shared_mutex(shared_mutex const&) = delete; + shared_mutex(shared_mutex const&) = delete; shared_mutex& operator=(shared_mutex const&) = delete; public: @@ -183,7 +189,7 @@ class shared_lock { return *this; } - shared_lock(const shared_lock&) = delete; + shared_lock(const shared_lock&) = delete; shared_lock& operator=(const shared_lock&) = delete; void lock() @@ -244,7 +250,7 @@ class shared_lock { yasio__throw_error0(std::errc::resource_deadlock_would_occur); } }; -} // namespace cxx17 +} // namespace tlx #endif #endif diff --git a/yasio/singleton.hpp b/yasio/tlx/singleton.hpp similarity index 99% rename from yasio/singleton.hpp rename to yasio/tlx/singleton.hpp index eebfef83f..b868e22a6 100644 --- a/yasio/singleton.hpp +++ b/yasio/tlx/singleton.hpp @@ -50,7 +50,7 @@ SOFTWARE. #include #include -namespace yasio +namespace tlx { template class singleton_constructor { @@ -154,6 +154,6 @@ std::atomic<_Ty*> singleton<_Ty>::__single__; template std::mutex singleton<_Ty>::__mutex__; -} // namespace yasio +} // namespace tlx #endif diff --git a/yasio/tlx/split.hpp b/yasio/tlx/split.hpp new file mode 100644 index 000000000..41d933ded --- /dev/null +++ b/yasio/tlx/split.hpp @@ -0,0 +1,533 @@ +////////////////////////////////////////////////////////////////////////////////////////// +// A multi-platform support c++11 library with focus on asynchronous socket I/O for any +// client application. +////////////////////////////////////////////////////////////////////////////////////////// +/* +The MIT License (MIT) + +Copyright (c) 2012-2025 HALX99 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ +#pragma once + +#include +#include +#include + +#include // std::distance +#include + +#include // strchr, strpbrk +#include // wcschr, wcspbrk +#include + +#if defined(__cpp_lib_ranges) +# include +#endif + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable : 4706) +#endif + +#ifndef _TLX_VERIFY +# define _TLX_VERIFY(cond, mesg) assert(cond&& mesg) +#endif + +namespace tlx +{ + +template +struct has_random_access_iterator_category : std::false_type {}; + +template +struct has_random_access_iterator_category::iterator_category>> { + using cat = typename std::iterator_traits::iterator_category; + static constexpr bool value = std::is_base_of_v; +}; + +template +inline constexpr bool has_random_access_iterator_category_v = has_random_access_iterator_category>::value; + +template +struct has_data_and_size : std::false_type {}; + +template +struct has_data_and_size().data()), decltype(std::declval().size())>> : std::true_type {}; + +template +inline constexpr bool has_data_and_size_v = has_data_and_size>::value; + +template +inline constexpr bool is_contiguous_like_v = +#if defined(__cpp_lib_ranges) + std::ranges::contiguous_range> || +#endif + (has_data_and_size_v && has_random_access_iterator_category_v); + +// ----------------------------- +// element_of trait (robust) +// ----------------------------- +template +struct element_of; // primary undefined + +// If _Ty has value_type (containers, span, iterators with value_type), use it +template +struct element_of<_Ty, std::void_t::value_type>> { + using type = typename std::remove_cvref_t<_Ty>::value_type; +}; + +// Pointer specialization: keep cv of the pointee (so const char* -> const char) +template +struct element_of<_Ty*, void> { + using type = std::remove_cv_t<_Ty>; // keep cv on pointer handled by pointer type itself +}; + +// Special-case: string_view should map to const char (preserve const) +template <> +struct element_of { + using type = const char; +}; + +template <> +struct element_of { + using type = const wchar_t; +}; + +// Helper aliases +template +using element_of_t = typename element_of<_Ty>::type; + +// element_of_nocvref_t: evaluate element_of on remove_cvref_t<_Ty> +// useful for deducing delim/delims types from an input type +template +using element_of_nocvref_t = std::remove_cvref_t>>; + +// ----------------------------- +// is_span_of detection +// ----------------------------- +template +struct is_span_of : std::false_type {}; + +template +struct is_span_of> : std::true_type {}; + +template +inline constexpr bool is_span_of_v = is_span_of>::value; + +// ----------------------------- +// string_traits +// ----------------------------- +template +struct string_traits_base; + +// narrow char +template <> +struct string_traits_base { + // Unsafe: requires null-terminated input string, since no explicit length is provided + static const char* find(const char* s, char delim) { return ::strchr(s, delim); } + static char* find(char* s, char delim) { return ::strchr(s, delim); } + + static const char* find(const char* s, const char* delim) { return ::strstr(s, delim); } + static char* find(char* s, const char* delim) { return ::strstr(s, delim); } + + static const char* find_first_of(const char* s, const char* delims) { return ::strpbrk(s, delims); } + static char* find_first_of(char* s, const char* delims) { return ::strpbrk(s, delims); } + + // Safe: input string with length + static const char* find(const char* s, size_t n, char delim) { return static_cast(::memchr(s, delim, n)); } + static char* find(char* s, size_t n, char delim) { return static_cast(::memchr(s, delim, n)); } +}; + +// wide char +template <> +struct string_traits_base { + // Unsafe: + static wchar_t* find(wchar_t* s, wchar_t delim) { return ::wcschr(s, delim); } + static const wchar_t* find(const wchar_t* s, wchar_t delim) { return ::wcschr(s, delim); } + + static const wchar_t* find(const wchar_t* s, const wchar_t* delim) { return ::wcsstr(s, delim); } + static wchar_t* find(wchar_t* s, const wchar_t* delim) { return ::wcsstr(s, delim); } + + static const wchar_t* find_first_of(const wchar_t* s, const wchar_t* delims) { return ::wcspbrk(s, delims); } + static wchar_t* find_first_of(wchar_t* s, const wchar_t* delims) { return ::wcspbrk(s, delims); } + + // Safe: input string with length + static const wchar_t* find(const wchar_t* s, size_t n, wchar_t delim) { return ::wmemchr(s, delim, n); } + static wchar_t* find(wchar_t* s, size_t n, wchar_t delim) { return ::wmemchr(s, delim, n); } +}; + +template +using string_traits = string_traits_base>; + +// ----------------------------- +// detail implementations +// ----------------------------- +namespace detail +{ + +// ----------------------------- +// split_until implementations +// ----------------------------- + +// split_until by char +// unsafe stub +template +inline void split_until(Elem* s, std::remove_cv_t delim, Pred&& pred) +{ + Elem* start = s; + Elem* ptr = s; + while ((ptr = string_traits::find(ptr, delim))) + { + _TLX_VERIFY(start <= ptr, "tlx::split: out of range"); + if (pred(start, ptr)) + return; + start = ++ptr; + } + // end sentinel: nullptr (caller wrapper must accept auto last) + pred(start, nullptr); +} + +// split_until by char +// safe stub +template +inline void split_until(std::span s, std::remove_cv_t delim, Pred&& pred) +{ + Elem* start = s.data(); + Elem* ptr = start; + Elem* end = start + s.size(); + while ((ptr = string_traits::find(ptr, end - ptr, delim))) + { + _TLX_VERIFY(start <= ptr, "tlx::split: out of range"); + if (pred(start, ptr)) + return; + start = ++ptr; + } + _TLX_VERIFY(start <= end, "tlx::split: out of range"); + pred(start, end); +} + +// split_until by string +// unsafe stub +template +inline void split_until(Elem* s, std::basic_string_view> delim, Pred&& pred) +{ + _TLX_VERIFY(!delim.empty(), "tlx::split: empty delimiter not allowed"); + + auto start = s; // the start of every string + auto ptr = s; // source string iterator + auto dlen = delim.size(); + while ((ptr = string_traits::find(ptr, delim.data()))) + { + _TLX_VERIFY(start <= ptr, "tlx::split: out of range"); + if (pred(start, ptr)) + return; + start = (ptr += dlen); + } + pred(start, nullptr); +} + +// split_until by string +template +inline void split_until(std::span s, std::basic_string_view> delim, Pred&& pred) +{ + _TLX_VERIFY(!delim.empty(), "tlx::split: empty delimiter not allowed"); + + auto start = s.data(); // the start of every string + auto ptr = start; // source string iterator + auto end = start + s.size(); + auto dlen = delim.size(); + while ((ptr = std::search(ptr, end, delim.begin(), delim.end())) != end) + { + _TLX_VERIFY(start <= ptr, "tlx::split: out of range"); + if (pred(start, ptr)) + return; + start = (ptr += dlen); + } + _TLX_VERIFY(start <= end, "tlx::split: out of range"); + pred(start, end); +} + +// ----------------------------- +// split_of_if implementations +// ----------------------------- + +// pointer version: delims is pointer to (possibly const) char type; end sentinel nullptr +// unsafe stub +template +inline void split_of_until(Elem* s, std::basic_string_view> delims, Pred&& pred) +{ + _TLX_VERIFY(!delims.empty(), "tlx::split_of: empty delimiter not allowed"); + + Elem* start = s; + Elem* ptr = s; + auto delim = delims[0]; + while ((ptr = string_traits::find_first_of(ptr, delims.data()))) + { + _TLX_VERIFY(start <= ptr, "tlx::split_of: out of range"); + if (pred(start, ptr, delim)) + return; + delim = *ptr; + start = ++ptr; + } + pred(start, nullptr, delim); +} + +// span version: delims is pointer to remove_cv_t +template +inline void split_of_until(std::span s, std::basic_string_view> delims, Pred&& pred) +{ + _TLX_VERIFY(!delims.empty(), "tlx::split_of: empty delimiter not allowed"); + + Elem* start = s.data(); + Elem* ptr = start; + Elem* end = start + s.size(); + auto delim = *delims.data(); + while ((ptr = std::find_first_of(ptr, end, delims.begin(), delims.end())) != end) + { + _TLX_VERIFY(start <= ptr, "tlx::split_of: out of range"); + if (pred(start, ptr, delim)) + return; + delim = *ptr; + start = ++ptr; + } + _TLX_VERIFY(start <= end, "tlx::split_of: out of range"); + pred(start, end, delim); +} + +} // namespace detail + +// ----------------------------- +// external dispatching helpers +// ----------------------------- + +// split_until: accept string-like types, pointers, spans [delim is char] +template +inline void split_until(Str&& s, element_of_nocvref_t delim, Pred&& pred) +{ + using Tp = std::remove_cvref_t; + + if constexpr (std::is_pointer_v) + { + // pointer (char*/wchar_t*) + detail::split_until(s, delim, std::forward(pred)); + } + else if constexpr (is_span_of_v) + { + detail::split_until(s, delim, std::forward(pred)); + } + else if constexpr (is_contiguous_like_v) + { + // Any type that exposes data() and size() (std::string, std::string_view, + // std::basic_string<...>, std::span<...> etc.) + using ElemPtr = decltype(std::declval().data()); // e.g. const char* + using Elem = std::remove_pointer_t; // preserves const + detail::split_until(std::span(s.data(), s.size()), delim, std::forward(pred)); + } + else + { + static_assert(sizeof(Tp) == 0, "Unsupported type for tlx::split_until"); + } +} + +template +inline void split_until(Str&& s, std::basic_string_view> delim, Pred&& pred) +{ + using Tp = std::remove_cvref_t; + + if constexpr (std::is_pointer_v) + { + // pointer (char*/wchar_t*) + detail::split_until(s, delim, std::forward(pred)); + } + else if constexpr (is_span_of_v) + { + detail::split_until(s, delim, std::forward(pred)); + } + else if constexpr (is_contiguous_like_v) + { + // Any type that exposes data() and size() (std::string, std::string_view, + // std::basic_string<...>, std::span<...> etc.) + using ElemPtr = decltype(std::declval().data()); // e.g. const char* + using Elem = std::remove_pointer_t; // preserves const + detail::split_until(std::span(s.data(), s.size()), delim, std::forward(pred)); + } + else + { + static_assert(sizeof(Tp) == 0, "Unsupported type for tlx::split_until"); + } +} + +// split_of_until: similar dispatch +template +inline void split_of_until(Str&& s, std::basic_string_view> delims, Pred&& pred) +{ + using Tp = std::remove_cvref_t; + + if constexpr (std::is_pointer_v) + { + detail::split_of_until(s, delims, std::forward(pred)); + } + else if constexpr (is_span_of_v) + { + detail::split_of_until(s, delims, std::forward(pred)); + } + else if constexpr (is_contiguous_like_v) + { + // Any type that exposes data() and size() (std::string, std::string_view, + // std::basic_string<...>, std::span<...> etc.) + using ElemPtr = decltype(std::declval().data()); // e.g. const char* + using Elem = std::remove_pointer_t; // preserves const + detail::split_of_until(std::span(s.data(), s.size()), delims, std::forward(pred)); + } + else + { + static_assert(sizeof(Tp) == 0, "Unsupported type for tlx::split_of_until"); + } +} + +// ----------------------------- +// non-_if wrappers (callback style) +// note: wrapper lambdas accept `auto last` (not auto*) so they can receive nullptr +// ----------------------------- +template +inline void split(Str&& s, element_of_nocvref_t delim, Fn&& func) +{ + split_until(std::forward(s), delim, [func = std::forward(func)](auto* first, auto last) { + func(first, last); + return false; + }); +} + +template +inline void split(Str&& s, std::basic_string_view> delim, Fn&& func) +{ + split_until(std::forward(s), delim, [func = std::forward(func)](auto* first, auto last) { + func(first, last); + return false; + }); +} + +template +inline void split_of(Str&& s, std::basic_string_view> delims, Fn&& func) +{ + split_of_until(std::forward(s), delims, [func = std::forward(func)](auto* first, auto last, auto delim) { + func(first, last, delim); + return false; + }); +} + +// ----------------------------- +// iterator/iterator-range overloads +// support arbitrary iterators; construct span from to_address(first) +// ensure element type matches the pointer returned by to_address +// ----------------------------- +template +inline void split(Iter first, Iter last, const element_of_nocvref_t delim, Fn&& func) +{ + // deduce pointer type from std::to_address(first) + using Ptr = decltype(std::to_address(first)); + using ElemSpan = std::remove_pointer_t>; // element type for span + auto n = std::distance(first, last); + // construct span with element type matching pointer returned by to_address + std::span sp(std::to_address(first), static_cast(n)); + detail::split_until(sp, delim, [func = std::forward(func)](ElemSpan* f, ElemSpan* l) { + func(f, l); + return false; + }); +} + +template +inline void split_of(Iter first, Iter last, std::basic_string_view> delims, Fn&& func) +{ + using Ptr = decltype(std::to_address(first)); + using ElemSpan = std::remove_pointer_t>; + auto n = std::distance(first, last); + std::span sp(std::to_address(first), static_cast(n)); + detail::split_of_until(sp, delims, [func = std::forward(func)](ElemSpan* f, ElemSpan* l, ElemSpan d) { + func(f, l, d); + return false; + }); +} + +} // namespace tlx + +namespace tlx +{ +/// split_path +template +inline void split_path(_Elem* s, _Pred&& pred, _Fn&& func) +{ + _Elem* start = s; + _Elem* ptr = s; + while (pred(ptr)) + { + if (*ptr == _Elem('\\') || *ptr == _Elem('/')) + { + if (ptr != start) + { + auto _Ch = *ptr; + *ptr = _Elem('\0'); + bool brk = func(s); +#if defined(_WIN32) + *ptr = _Elem('\\'); +#else + *ptr = _Elem('/'); +#endif + if (brk) + return; + } + start = ptr + 1; + } + ++ptr; + } + if (start < ptr) + func(s); +} +} // namespace tlx + +namespace tlx +{ +// CLASS split_term_guard +struct split_term_guard { + split_term_guard(char* end) + { + if (end) + { + this->val_ = *end; + *end = '\0'; + this->end_ = end; + } + } + ~split_term_guard() + { + if (this->end_) + *this->end_ = this->val_; + } + +private: + char* end_ = nullptr; + char val_ = '\0'; +}; +} // namespace tlx + +#if defined(_MSC_VER) +# pragma warning(pop) +#endif diff --git a/yasio/string.hpp b/yasio/tlx/string.hpp similarity index 61% rename from yasio/string.hpp rename to yasio/tlx/string.hpp index 3b3c668f5..d9d83693d 100644 --- a/yasio/string.hpp +++ b/yasio/tlx/string.hpp @@ -1,4 +1,3 @@ - ////////////////////////////////////////////////////////////////////////////////////////// // A multi-platform support c++11 library with focus on asynchronous socket I/O for any // client application. @@ -26,8 +25,8 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -The yasio dedicated string (API not 100% compatible with stl) concepts: - a. no SSO, no COW, sizeof(yasio::string) = 24(x64), 12(x86) +The tlx dedicated string (API not 100% compatible with stl) concepts: + a. no SSO, no COW, sizeof(tlx::string) = 24(x64), 12(x86) b. The resize behavior differrent stl, always allocate exactly c. By default resize without fill (uninitialized and for overwrite), use insert/append insetad if you want fill memory inside container @@ -47,13 +46,16 @@ The yasio dedicated string (API not 100% compatible with stl) concepts: #include #include #include -#include "yasio/buffer_alloc.hpp" -#include "yasio/string_view.hpp" +#include "yasio/tlx/buffer_alloc.hpp" // crt_buffer_allocator +#include "yasio/tlx/string_view.hpp" +#include "yasio/tlx/memory.hpp" // compressed_pair -namespace yasio +namespace tlx { -template , enable_if_t::value, int> = 0> +template , _TLX enable_if_t::value, int> = 0> class basic_string { + YASIO__CONSTEXPR _Alloc& _Getal() noexcept { return _Mypair._Get_first(); } + public: using pointer = _Elem*; using const_pointer = const _Elem*; @@ -63,30 +65,41 @@ class basic_string { using iterator = _Elem*; // transparent iterator using const_iterator = const _Elem*; using allocator_type = _Alloc; - using _Alloc_traits = buffer_allocator_traits<_Alloc>; + using _Alloc_traits = std::allocator_traits<_Alloc>; using size_type = typename _Alloc_traits::size_type; using _Traits = std::char_traits<_Elem>; - using view_type = cxx17::basic_string_view<_Elem>; + using view_type = std::basic_string_view<_Elem>; using my_type = basic_string<_Elem, _Alloc>; - static const size_t npos = -1; - basic_string() {} + static const size_t npos = static_cast(-1); + + // compressed storage: allocator + 3 pointers (_Myfirst, _Mylast, _Myend) + struct _Str_storage { + pointer _Myfirst = nullptr; // begin + pointer _Mylast = nullptr; // one past last character + pointer _Myend = nullptr; // one past end of storage + }; + __compressed_pair _Mypair; + + basic_string() : _Mypair(__zero_then_variadic_args_t{}) {} basic_string(::std::nullptr_t) = delete; - explicit basic_string(size_type count) { resize(static_cast(count)); } - basic_string(size_type count, const_reference val) { resize(static_cast(count), val); } - template ::value, int> = 0> - basic_string(_Iter first, _Iter last) + explicit basic_string(size_type count) : _Mypair(__one_then_variadic_args_t{}, _Alloc{}) { resize(static_cast(count)); } + basic_string(size_type count, const_reference val) : _Mypair(__one_then_variadic_args_t{}, _Alloc{}) { resize(static_cast(count), val); } + template ::value, int> = 0> + basic_string(_Iter first, _Iter last) : _Mypair(__one_then_variadic_args_t{}, _Alloc{}) { assign(first, last); } - basic_string(const basic_string& rhs) { assign(rhs); }; - basic_string(basic_string&& rhs) YASIO__NOEXCEPT { assign(std::move(rhs)); } - basic_string(view_type rhs) { assign(rhs); } - basic_string(const_pointer ntcs) { assign(ntcs); } - basic_string(const_pointer ntcs, size_type count) { assign(ntcs, ntcs + count); } + basic_string(const basic_string& rhs) : _Mypair(__one_then_variadic_args_t{}, _Alloc{}) { assign(rhs); } + basic_string(basic_string&& rhs) YASIO__NOEXCEPT : _Mypair(__one_then_variadic_args_t{}, _Alloc{}) { _Assign_rv(std::move(rhs)); } + basic_string(view_type rhs) : _Mypair(__one_then_variadic_args_t{}, _Alloc{}) { assign(rhs); } + basic_string(const_pointer ntcs) : _Mypair(__one_then_variadic_args_t{}, _Alloc{}) { assign(ntcs); } + basic_string(const_pointer ntcs, size_type count) : _Mypair(__one_then_variadic_args_t{}, _Alloc{}) { assign(ntcs, ntcs + count); } /*basic_string(std::initializer_list rhs) { _Assign_range(rhs.begin(), rhs.end()); }*/ ~basic_string() { _Tidy(); } + operator view_type() const YASIO__NOEXCEPT { return this->view(); } view_type view() const YASIO__NOEXCEPT { return view_type(this->c_str(), this->size()); } + basic_string& operator=(const basic_string& rhs) { assign(rhs); @@ -97,6 +110,7 @@ class basic_string { this->swap(rhs); return *this; } + template basic_string& operator+=(const _Cont& rhs) { @@ -107,7 +121,8 @@ class basic_string { this->push_back(rhs); return *this; } - template ::value, int> = 0> + + template ::value, int> = 0> void assign(_Iter first, _Iter last) { _Assign_range(first, last); @@ -117,20 +132,27 @@ class basic_string { void assign(const_pointer ntcs, size_type count) { _Assign_range(ntcs, ntcs + count); } void assign(const basic_string& rhs) { _Assign_range(rhs.begin(), rhs.end()); } void assign(basic_string&& rhs) { _Assign_rv(std::move(rhs)); } + void swap(basic_string& rhs) YASIO__NOEXCEPT { - std::swap(_Myfirst, rhs._Myfirst); - std::swap(_Mysize, rhs._Mysize); - std::swap(_Myres, rhs._Myres); + std::swap(_Getal(), rhs._Getal()); + + auto& a = _Mypair._Myval2; + auto& b = rhs._Mypair._Myval2; + std::swap(a._Myfirst, b._Myfirst); + std::swap(a._Mylast, b._Mylast); + std::swap(a._Myend, b._Myend); } - template ::value, int> = 0> + + template ::value, int> = 0> iterator insert(iterator _Where, _Iter first, _Iter last) { - auto _Mylast = _Myfirst + _Mysize; - _YASIO_VERIFY_RANGE(_Where >= _Myfirst && _Where <= _Mylast && first <= last, "basic_string: out of range!"); + auto& st = _Mypair._Myval2; + auto _Mylast = st._Mylast; + _TLX_VERIFY(_Where >= st._Myfirst && _Where <= _Mylast && first <= last, "basic_string: out of range!"); if (first != last) { - auto insertion_pos = static_cast(std::distance(_Myfirst, _Where)); + auto insertion_pos = static_cast(std::distance(st._Myfirst, _Where)); if (_Where == _Mylast) append(first, last); else @@ -140,131 +162,173 @@ class basic_string { auto count = static_cast(std::distance(first, last)); if (insertion_pos >= 0) { - auto old_size = _Mylast - _Myfirst; + auto old_size = static_cast(_Mylast - st._Myfirst); expand(count); - _Where = _Myfirst + insertion_pos; - _Mylast = _Myfirst + _Mysize; + _Where = st._Myfirst + insertion_pos; + _Mylast = st._Mylast; auto move_to = _Where + count; - std::copy_n(_Where, _Mylast - move_to, move_to); + std::copy_n(_Where, static_cast(_Mylast - move_to), move_to); std::copy_n((iterator)ifirst, count, _Where); } } - return _Myfirst + insertion_pos; + return st._Myfirst + insertion_pos; } return _Where; } + iterator insert(iterator _Where, size_type count, const_reference val) { - auto _Mylast = _Myfirst + _Mysize; - _YASIO_VERIFY_RANGE(_Where >= _Myfirst && _Where <= _Mylast, "basic_string: out of range!"); + auto& st = _Mypair._Myval2; + auto _Mylast = st._Mylast; + _TLX_VERIFY(_Where >= st._Myfirst && _Where <= _Mylast, "basic_string: out of range!"); if (count) { - auto insertion_pos = std::distance(_Myfirst, _Where); + auto insertion_pos = std::distance(st._Myfirst, _Where); if (_Where == _Mylast) append(count, val); else { if (insertion_pos >= 0) { - const auto old_size = _Mysize; + const auto old_size = size(); expand(count); - _Where = _Myfirst + insertion_pos; - _Mylast = _Myfirst + _Mysize; + _Where = st._Myfirst + insertion_pos; + _Mylast = st._Mylast; auto move_to = _Where + count; - std::copy_n(_Where, _Mylast - move_to, move_to); + std::copy_n(_Where, static_cast(_Mylast - move_to), move_to); std::fill_n(_Where, count, val); } } - return _Myfirst + insertion_pos; + return st._Myfirst + insertion_pos; } return _Where; } + basic_string& append(view_type value) { return this->append(value.begin(), value.end()); } - template ::value, int> = 0> + template ::value, int> = 0> basic_string& append(_Iter first, const _Iter last) { if (first != last) { + auto& st = _Mypair._Myval2; auto ifirst = std::addressof(*first); static_assert(sizeof(*ifirst) == sizeof(value_type), "basic_string: iterator type incompatible!"); auto count = static_cast(std::distance(first, last)); if (count > 1) { - const auto old_size = _Mysize; + const auto old_size = size(); expand(count); - std::copy_n((iterator)ifirst, count, _Myfirst + old_size); + std::copy_n((iterator)ifirst, count, st._Myfirst + old_size); } else if (count == 1) push_back(static_cast(*(iterator)ifirst)); } return *this; } + basic_string& append(size_type count, const_reference val) { expand(count, val); return *this; } + void push_back(value_type&& v) { push_back(v); } void push_back(const value_type& v) { expand(1); back() = v; } + iterator erase(iterator _Where) { - const auto _Mylast = _Myfirst + _Mysize; - _YASIO_VERIFY_RANGE(_Where >= _Myfirst && _Where < _Mylast, "basic_string: out of range!"); - _Mysize = static_cast(std::move(_Where + 1, _Mylast, _Where) - _Myfirst); + auto& st = _Mypair._Myval2; + const auto _Mylast = st._Mylast; + _TLX_VERIFY(_Where >= st._Myfirst && _Where < _Mylast, "basic_string: out of range!"); + st._Mylast = std::move(_Where + 1, _Mylast, _Where); + // keep null terminator + *_Mylast_ptr() = value_type(0); return _Where; } + iterator erase(iterator first, iterator last) { - const auto _Mylast = _Myfirst + _Mysize; - _YASIO_VERIFY_RANGE((first <= last) && first >= _Myfirst && last <= _Mylast, "basic_string: out of range!"); - _Mysize = static_cast(std::move(last, _Mylast, first) - _Myfirst); + auto& st = _Mypair._Myval2; + const auto _Mylast = st._Mylast; + _TLX_VERIFY((first <= last) && first >= st._Myfirst && last <= _Mylast, "basic_string: out of range!"); + st._Mylast = std::move(last, _Mylast, first); + // keep null terminator + *_Mylast_ptr() = value_type(0); return first; } + value_type& front() { - _YASIO_VERIFY_RANGE(!empty(), "basic_string: out of range!"); - return *_Myfirst; + _TLX_VERIFY(!empty(), "basic_string: out of range!"); + return *_Mypair._Myval2._Myfirst; } + value_type& back() { - _YASIO_VERIFY_RANGE(!empty(), "basic_string: out of range!"); - return _Myfirst[_Mysize - 1]; + _TLX_VERIFY(!empty(), "basic_string: out of range!"); + return *(_Mypair._Myval2._Mylast - 1); + } + + static YASIO__CONSTEXPR size_type max_size() YASIO__NOEXCEPT + { + // static form to avoid needing allocator instance + return static_cast(-1) / sizeof(value_type); } - static YASIO__CONSTEXPR size_type max_size() YASIO__NOEXCEPT { return _Alloc_traits::max_size(); } #pragma region Iterators iterator begin() YASIO__NOEXCEPT { return this->data(); } - iterator end() YASIO__NOEXCEPT { return begin() + _Mysize; } + iterator end() YASIO__NOEXCEPT { return _Mypair._Myval2._Mylast; } const_iterator begin() const YASIO__NOEXCEPT { return this->data(); } - const_iterator end() const YASIO__NOEXCEPT { return begin() + _Mysize; } + const_iterator end() const YASIO__NOEXCEPT { return _Mypair._Myval2._Mylast; } #pragma endregion - pointer data() YASIO__NOEXCEPT { return _Myfirst; } - const_pointer data() const YASIO__NOEXCEPT { return _Myfirst; } - const_pointer c_str() const YASIO__NOEXCEPT { return _Myfirst ? _Myfirst : reinterpret_cast(&_Myfirst);; } + pointer data() YASIO__NOEXCEPT { return _Mypair._Myval2._Myfirst; } + const_pointer data() const YASIO__NOEXCEPT { return _Mypair._Myval2._Myfirst; } + + const_pointer c_str() const YASIO__NOEXCEPT + { + auto& st = _Mypair._Myval2; + return st._Myfirst ? st._Myfirst : reinterpret_cast(&st._Myfirst); + } + const_reference operator[](size_type index) const { return this->at(index); } reference operator[](size_type index) { return this->at(index); } const_reference at(size_type index) const { - _YASIO_VERIFY_RANGE(index < this->size(), "basic_string: out of range!"); - return _Myfirst[index]; + _TLX_VERIFY(index < this->size(), "basic_string: out of range!"); + return _Mypair._Myval2._Myfirst[index]; } reference at(size_type index) { - _YASIO_VERIFY_RANGE(index < this->size(), "basic_string: out of range!"); - return _Myfirst[index]; + _TLX_VERIFY(index < this->size(), "basic_string: out of range!"); + return _Mypair._Myval2._Myfirst[index]; } #pragma region Capacity - size_type capacity() const YASIO__NOEXCEPT { return _Myres; } - size_type size() const YASIO__NOEXCEPT { return _Mysize; } - size_type length() const YASIO__NOEXCEPT { return _Mysize; } - void clear() YASIO__NOEXCEPT { _Mysize = 0; } - bool empty() const YASIO__NOEXCEPT { return _Mysize == 0; } + size_type capacity() const YASIO__NOEXCEPT + { + auto& st = _Mypair._Myval2; + return static_cast(st._Myend - st._Myfirst); + } + size_type size() const YASIO__NOEXCEPT + { + auto& st = _Mypair._Myval2; + return static_cast(st._Mylast - st._Myfirst); + } + size_type length() const YASIO__NOEXCEPT { return size(); } + void clear() YASIO__NOEXCEPT + { + auto& st = _Mypair._Myval2; + st._Mylast = st._Myfirst; + if (st._Myfirst) + *st._Mylast = value_type(0); + } + bool empty() const YASIO__NOEXCEPT { return _Mypair._Myval2._Mylast == _Mypair._Myval2._Myfirst; } + void resize(size_type new_size) { if (this->capacity() < new_size) @@ -272,6 +336,7 @@ class basic_string { else _Eos(new_size); } + void expand(size_type count) { const auto new_size = this->size() + count; @@ -280,28 +345,36 @@ class basic_string { else _Eos(new_size); } + void shrink_to_fit() { // reduce capacity to size, provide strong guarantee - if (_Mysize != _Myres) + auto& st = _Mypair._Myval2; + if (st._Mylast != st._Myend) { // something to do - if (!_Mysize) + if (st._Mylast == st._Myfirst) _Tidy(); else - _Reallocate<_Reallocation_policy::_Exactly>(_Mysize); + _Reallocate<_Reallocation_policy::_Exactly>(size()); } } + void reserve(size_type new_cap) { if (this->capacity() < new_cap) _Reallocate<_Reallocation_policy::_Exactly>(new_cap); } + template void resize_and_overwrite(const size_type _New_size, _Operation _Op) { _Reallocate<_Reallocation_policy::_Exactly>(_New_size); - _Eos(std::move(_Op)(_Myfirst, _New_size)); + auto& st = _Mypair._Myval2; + // _Op writes up to _New_size and returns new size + size_type written = std::move(_Op)(st._Myfirst, _New_size); + _Eos(written); } #pragma endregion + void resize(size_type new_size, const_reference val) { auto old_size = this->size(); @@ -309,9 +382,10 @@ class basic_string { { resize(new_size); if (old_size < new_size) - std::fill_n(_Myfirst + old_size, new_size - old_size, val); + std::fill_n(_Mypair._Myval2._Myfirst + old_size, new_size - old_size, val); } } + void expand(size_type count, const_reference val) { if (count) @@ -319,29 +393,35 @@ class basic_string { auto old_size = this->size(); expand(count); if (count) - std::fill_n(_Myfirst + old_size, count, val); + std::fill_n(_Mypair._Myval2._Myfirst + old_size, count, val); } } + template pointer detach_abi(_Intty& len) YASIO__NOEXCEPT { - len = static_cast<_Intty>(this->size()); - auto ptr = _Myfirst; - _Myfirst = nullptr; - _Mysize = _Myres = 0; + auto& st = _Mypair._Myval2; + len = static_cast<_Intty>(this->size()); + auto ptr = st._Myfirst; + st._Myfirst = st._Mylast = st._Myend = nullptr; return ptr; } + pointer detach_abi() YASIO__NOEXCEPT { size_type ignored_len; return this->detach_abi(ignored_len); } + void attach_abi(pointer ptr, size_type len) { _Tidy(); - _Myfirst = ptr; - _Mysize = _Myres = len; + auto& st = _Mypair._Myval2; + st._Myfirst = ptr; + st._Mylast = ptr + len; + st._Myend = ptr + len; } + pointer release_pointer() YASIO__NOEXCEPT { return detach_abi(); } #pragma region find stubs, String operations @@ -367,41 +447,38 @@ class basic_string { my_type& replace(const size_type _Off, size_type _Nx, view_type value) { return this->replace(_Off, _Nx, value.data(), value.length()); } my_type& replace(const size_type _Off, size_type _Nx, const _Elem* const _Ptr, const size_type _Count) { // replace port from https://github.com/microsoft/stl - _YASIO_VERIFY_RANGE(_Off < _Mysize, "basic_string: out of range!"); - _Nx = (std::min)(_Nx, _Mysize - _Off); + auto& st = _Mypair._Myval2; + _TLX_VERIFY(_Off < size(), "basic_string: out of range!"); + _Nx = (std::min)(_Nx, size() - _Off); if (_Nx == _Count) { // size doesn't change, so a single move does the trick - _Traits::move(_Myfirst + _Off, _Ptr, _Count); + _Traits::move(st._Myfirst + _Off, _Ptr, _Count); return *this; } - const size_type _Old_size = _Mysize; + const size_type _Old_size = size(); const size_type _Suffix_size = _Old_size - _Nx - _Off + 1; if (_Count < _Nx) { // suffix shifts backwards; we don't have to move anything out of the way - _Elem* const _Old_ptr = _Myfirst; + _Elem* const _Old_ptr = st._Myfirst; _Elem* const _Insert_at = _Old_ptr + _Off; _Traits::move(_Insert_at, _Ptr, _Count); _Traits::move(_Insert_at + _Count, _Insert_at + _Nx, _Suffix_size); const auto _New_size = _Old_size - (_Nx - _Count); - // _ASAN_STRING_MODIFY(*this, _Old_size, _New_size); - _Mysize = _New_size; + _Eos(_New_size); return *this; } const size_type _Growth = static_cast(_Count - _Nx); - // checking for overlapping ranges is technically UB (considering string literals), so just always reallocate - // and copy to the new buffer if constant evaluated -#if YASIO__HAS_CXX20 if (!std::is_constant_evaluated()) -#endif // _HAS_CXX20 { - if (_Growth <= _Myres - _Old_size) + const size_type _Old_capacity = capacity(); + if (_Growth <= _Old_capacity - _Old_size) { // growth fits - _Mysize = _Old_size + _Growth; - _Elem* const _Old_ptr = _Myfirst; + _Eos(_Old_size + _Growth); + _Elem* const _Old_ptr = st._Myfirst; _Elem* const _Insert_at = _Old_ptr + _Off; _Elem* const _Suffix_at = _Insert_at + _Nx; @@ -420,12 +497,7 @@ class basic_string { } _Traits::move(_Suffix_at + _Growth, _Suffix_at, _Suffix_size); - // next case must be move, in case _Ptr begins before _Insert_at and contains part of the hole; - // this case doesn't occur in insert because the new content must come from outside the removed - // content there (because in insert there is no removed content) _Traits::move(_Insert_at, _Ptr, _Ptr_shifted_after); - // the next case can be copy, because it comes from the chunk moved out of the way in the - // first move, and the hole we're filling can't alias the chunk we moved out of the way _Traits::copy(_Insert_at + _Ptr_shifted_after, _Ptr + _Growth + _Ptr_shifted_after, _Count - _Ptr_shifted_after); return *this; } @@ -439,24 +511,26 @@ class basic_string { }, _Off, _Nx, _Ptr, _Count); } + template my_type& _Reallocate_grow_by(const size_type _Size_increase, _Fty _Fn, _ArgTys... _Args) { - const size_type _Old_size = _Mysize; + const size_type _Old_size = size(); if (max_size() - _Old_size < _Size_increase) throw std::length_error("string too long"); const size_type _New_size = _Old_size + _Size_increase; - const size_type _Old_capacity = _Myres; const size_type _New_capacity = _Calculate_growth(_New_size); - pointer _New_ptr = _Alloc::reallocate(_Myfirst, _Myres, _New_capacity + 1); // throws + auto& st = _Mypair._Myval2; + auto& alloc = _Getal(); + pointer _New_ptr = alloc.reallocate(st._Myfirst, static_cast(st._Myend - st._Myfirst), _New_capacity + 1); // throws - _Mysize = _New_size; - _Myres = _New_capacity; + _Eos(_New_size); + st._Myend = _New_ptr + (_New_capacity + 1); - const pointer _Old_ptr = _Myfirst; + const pointer _Old_ptr = st._Myfirst; _Fn(_New_ptr, _Old_size, _Args...); - _Myfirst = _New_ptr; + st._Myfirst = _New_ptr; return *this; } @@ -497,63 +571,86 @@ class basic_string { private: void _Eos(size_type size) YASIO__NOEXCEPT { - _Mysize = size; - _Myfirst[_Mysize] = static_cast(0); + auto& st = _Mypair._Myval2; + st._Mylast = st._Myfirst + size; + if (st._Myfirst) + *st._Mylast = static_cast(0); + } + + // convenience: get address of current null-terminator within capacity + pointer _Mylast_ptr() YASIO__NOEXCEPT + { + auto& st = _Mypair._Myval2; + return st._Mylast; // points to null terminator slot } - template ::value, int> = 0> + + template ::value, int> = 0> void _Assign_range(_Iter first, _Iter last) { + auto& st = _Mypair._Myval2; auto ifirst = std::addressof(*first); static_assert(sizeof(*ifirst) == sizeof(value_type), "basic_string: iterator type incompatible!"); - if (ifirst != _Myfirst) + if (ifirst != st._Myfirst) { - _Mysize = 0; + st._Mylast = st._Myfirst; // size = 0 if (last > first) { const auto count = static_cast(std::distance(first, last)); resize(count); - std::copy_n((iterator)ifirst, count, _Myfirst); + std::copy_n((iterator)ifirst, count, st._Myfirst); } } } + void _Assign_rv(basic_string&& rhs) { - memcpy(this, &rhs, sizeof(rhs)); - memset(&rhs, 0, sizeof(rhs)); + // move allocator and storage + std::swap(_Getal(), rhs._Getal()); + _Mypair._Myval2 = rhs._Mypair._Myval2; + rhs._Mypair._Myval2 = _Str_storage{}; } + enum class _Reallocation_policy { _At_least, _Exactly }; + template <_Reallocation_policy _Policy> void _Resize_reallocate(size_type size) { _Reallocate<_Policy>(size); _Eos(size); } + template <_Reallocation_policy _Policy> void _Reallocate(size_type size) { + auto& st = _Mypair._Myval2; + auto& alloc = _Getal(); size_type new_cap; if YASIO__CONSTEXPR (_Policy == _Reallocation_policy::_Exactly) new_cap = size + 1; else - new_cap = (std::max)(_Calculate_growth(size), size + 1); - auto _Newvec = _Alloc::reallocate(_Myfirst, _Myres, new_cap); + new_cap = (std::max)(_Calculate_growth(size), size) + 1; + + pointer _Newvec = alloc.reallocate(st._Myfirst, static_cast(st._Myend - st._Myfirst), new_cap); if (_Newvec) { - _Myfirst = _Newvec; - _Myres = new_cap; + st._Myend = _Newvec + new_cap; + const auto cur_size = size; // caller will set exact size via _Eos + st._Myfirst = _Newvec; + st._Mylast = _Newvec + cur_size; } else throw std::bad_alloc{}; } + size_type _Calculate_growth(const size_type _Newsize) const { // given _Oldcapacity and _Newsize, calculate geometric growth const size_type _Oldcapacity = capacity(); - YASIO__CONSTEXPR auto _Max = max_size(); + const size_type _Max = max_size(); if (_Oldcapacity > _Max - _Oldcapacity / 2) return _Max; // geometric growth would overflow @@ -565,20 +662,20 @@ class basic_string { return _Geometric; // geometric growth is sufficient } + void _Tidy() YASIO__NOEXCEPT { // free all storage - if (_Myfirst) + auto& st = _Mypair._Myval2; + auto& alloc = _Getal(); + if (st._Myfirst) { - _Alloc::deallocate(_Myfirst, _Myres); - _Myfirst = nullptr; - _Mysize = _Myres = 0; + alloc.deallocate(st._Myfirst, static_cast(st._Myend - st._Myfirst)); + st._Myfirst = st._Mylast = st._Myend = nullptr; } } - - pointer _Myfirst = nullptr; - size_type _Mysize = 0; - size_type _Myres = 0; }; + +// aliases using string = basic_string; #if defined(__cpp_lib_char8_t) using u8string = basic_string; @@ -586,4 +683,4 @@ using u8string = basic_string; using wstring = basic_string; using u16string = basic_string; using u32string = basic_string; -} // namespace yasio +} // namespace tlx diff --git a/yasio/tlx/string_view.hpp b/yasio/tlx/string_view.hpp new file mode 100644 index 000000000..e40d3518b --- /dev/null +++ b/yasio/tlx/string_view.hpp @@ -0,0 +1,240 @@ +////////////////////////////////////////////////////////////////////////////////////////// +// A multi-platform support c++11 library with focus on asynchronous socket I/O for any +// client application. +////////////////////////////////////////////////////////////////////////////////////////// +/* +The MIT License (MIT) + +Copyright (c) 2012-2025 HALX99 +Copyright (c) 2016 Matthew Rodusek(matthew.rodusek@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +See: https://github.com/bitwizeshift/string_view-standalone +*/ +#ifndef YASIO__STRING_VIEW_HPP +#define YASIO__STRING_VIEW_HPP +#include +#include +#include +#include +#include +#include "yasio/impl/char_traits.hpp" +#include "yasio/compiler/feature_test.hpp" + +/// wcsncasecmp workaround for android API level < 23, copy from msvc ucrt 10.0.18362.0 'wcsnicmp' +#if (defined(__ANDROID_API__) && __ANDROID_API__ < 23) || defined(__MINGW32__) +inline int wcsncasecmp(wchar_t const* const lhs, wchar_t const* const rhs, size_t const count) +{ + if (count == 0) + { + return 0; + } + + wchar_t const* lhs_ptr = reinterpret_cast(lhs); + wchar_t const* rhs_ptr = reinterpret_cast(rhs); + + int result; + int lhs_value; + int rhs_value; + size_t remaining = count; + do + { + lhs_value = ::towlower(*lhs_ptr++); + rhs_value = ::towlower(*rhs_ptr++); + result = lhs_value - rhs_value; + } while (result == 0 && lhs_value != 0 && --remaining != 0); + + return result; +} +#endif + +namespace tlx +{ +template +using decay_t = typename std::decay::type; +template +using remove_const_t = typename std::remove_const::type; +namespace char_ranges +{ // allow get char type from char*, wchar_t*, std::string, std::wstring +template +struct value_type { + using type = typename _Ty::value_type; +}; + +template +struct value_type<_Ty&> { + using type = remove_const_t<_Ty>; +}; + +template +struct value_type<_Ty*> { + using type = remove_const_t<_Ty>; +}; +} // namespace char_ranges + +// starts_with(), since C++20: +template +inline bool starts_with(std::basic_string_view<_CharT> lhs, + std::basic_string_view<_CharT> v) // (1) +{ + return lhs.size() >= v.size() && lhs.compare(0, v.size(), v) == 0; +} + +template +inline bool starts_with(_T1&& lhs, _T2&& v) // (2) +{ + using char_type = typename char_ranges::value_type>::type; + return starts_with(std::basic_string_view{lhs}, std::basic_string_view{v}); +} + +template +inline bool starts_with(std::basic_string_view<_CharT> lhs, int c) // (3) +{ + return !lhs.empty() && lhs.front() == c; +} + +template +inline bool starts_with(_Ty&& lhs, int c) // (4) +{ + using char_type = typename char_ranges::value_type>::type; + return starts_with(std::basic_string_view{lhs}, c); +} + +// ends_with(), since C++20: +template +inline bool ends_with(std::basic_string_view<_CharT> lhs, + std::basic_string_view<_CharT> v) // (1) +{ + auto offset = lhs.size() - v.size(); + return lhs.size() >= v.size() && lhs.compare(offset, v.size(), v) == 0; +} + +template +inline bool ends_with(_T1&& lhs, _T2&& v) // (2) +{ + using char_type = typename char_ranges::value_type>::type; + return ends_with(std::basic_string_view{lhs}, std::basic_string_view{v}); +} + +template +inline bool ends_with(std::basic_string_view<_CharT> lhs, int c) // (3) +{ + return !lhs.empty() && lhs.back() == c; +} + +template +inline bool ends_with(_Ty&& lhs, int c) // (4) +{ + using char_type = typename char_ranges::value_type>::type; + return ends_with(std::basic_string_view{lhs}, c); +} + +/// The case insensitive implementation of starts_with, ends_with +namespace ic +{ +template +inline bool iequals(std::basic_string_view<_CharT> lhs, std::basic_string_view<_CharT> v); +#if defined(_MSC_VER) +template <> +inline bool iequals(std::basic_string_view lhs, std::basic_string_view v) +{ + return lhs.size() == v.size() && ::_strnicmp(lhs.data(), v.data(), v.size()) == 0; +} +template <> +inline bool iequals(std::basic_string_view lhs, std::basic_string_view v) +{ + return lhs.size() == v.size() && ::_wcsnicmp(lhs.data(), v.data(), v.size()) == 0; +} +#else +template <> +inline bool iequals(std::basic_string_view lhs, std::basic_string_view v) +{ + return lhs.size() == v.size() && ::strncasecmp(lhs.data(), v.data(), v.size()) == 0; +} +template <> +inline bool iequals(std::basic_string_view lhs, std::basic_string_view v) +{ + return lhs.size() == v.size() && ::wcsncasecmp(lhs.data(), v.data(), v.size()) == 0; +} +#endif +template +inline bool iequals(_T1&& lhs, _T2&& v) +{ + using char_type = typename char_ranges::value_type>::type; + return iequals(std::basic_string_view{lhs}, std::basic_string_view{v}); +} +// starts_with(), since C++20: +template +inline bool starts_with(std::basic_string_view<_CharT> lhs, + std::basic_string_view<_CharT> v) // (1) +{ + return lhs.size() >= v.size() && iequals(lhs.substr(0, v.size()), v); +} + +template +inline bool starts_with(_T1&& lhs, _T2&& v) // (2) +{ + using char_type = typename char_ranges::value_type>::type; + return starts_with(std::basic_string_view{lhs}, std::basic_string_view{v}); +} + +template +inline bool starts_with(std::basic_string_view<_CharT> lhs, int c) // (3) +{ + return !lhs.empty() && ::tolower(lhs.front()) == ::tolower(c); +} + +template +inline bool starts_with(_Ty&& lhs, int c) // (4) +{ + using char_type = typename char_ranges::value_type>::type; + return starts_with(std::basic_string_view{lhs}, c); +} + +// ends_with(), since C++20: +template +inline bool ends_with(std::basic_string_view<_CharT> lhs, + std::basic_string_view<_CharT> v) // (1) +{ + return lhs.size() >= v.size() && iequals(lhs.substr(lhs.size() - v.size(), lhs.npos), v); +} + +template +inline bool ends_with(_T1&& lhs, _T2&& v) // (2) +{ + using char_type = typename char_ranges::value_type>::type; + return ends_with(std::basic_string_view{lhs}, std::basic_string_view{v}); +} + +template +inline bool ends_with(std::basic_string_view<_CharT> lhs, int c) // (3) +{ + return !lhs.empty() && ::tolower(lhs.back()) == ::tolower(c); +} + +template +inline bool ends_with(_Ty&& lhs, int c) // (4) +{ + using char_type = typename char_ranges::value_type>::type; + return ends_with(std::basic_string_view{lhs}, c); +} +} // namespace ic +} // namespace tlx + +#endif diff --git a/yasio/type_traits.hpp b/yasio/tlx/type_traits.hpp similarity index 95% rename from yasio/type_traits.hpp rename to yasio/tlx/type_traits.hpp index 8f579e95a..3c87bba01 100644 --- a/yasio/type_traits.hpp +++ b/yasio/tlx/type_traits.hpp @@ -32,7 +32,7 @@ SOFTWARE. #include #include "yasio/sz.hpp" -namespace yasio +namespace tlx { template struct aligned_storage_size { @@ -45,6 +45,9 @@ struct is_aligned_storage { template struct is_iterator : public std::integral_constant::value> {}; +template +inline constexpr bool is_iterator_v = is_iterator<_Iter>::value; + template using enable_if_t = typename ::std::enable_if<_Test, _Ty>::type; diff --git a/yasio/tlx/vector.hpp b/yasio/tlx/vector.hpp new file mode 100644 index 000000000..522384b7f --- /dev/null +++ b/yasio/tlx/vector.hpp @@ -0,0 +1,1692 @@ +////////////////////////////////////////////////////////////////////////////////////////// +// A multi-platform support c++11 library with focus on asynchronous socket I/O for any +// client application. +////////////////////////////////////////////////////////////////////////////////////////// +/* +The MIT License (MIT) + +Copyright (c) 2012-2025 HALX99 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +Version: 5.0.0 +Inspired by Microsoft STL, tailored for dedicated use cases. + +The vector aka array_buffer concepts: + a. resize(void)/extend(void) doesn't fill default constructor (uninitialized for overwrite on POD path) + b. Transparent iterator + c. Extensions APIs: + - operator+= + - extend: extend size + - resize_and_overwrite + - detach_abi: release ownership + - attach_abi: take owership +*/ +#pragma once + +#include +#include +#include +#include +#include +#include "yasio/compiler/feature_test.hpp" +#include "yasio/tlx/type_traits.hpp" +#include "yasio/tlx/memory.hpp" // for compressed_pair and ::construct_at + +namespace tlx +{ + +// Writable iterator for sequential containers +template +class sequence_const_iterator { +public: + using iterator_concept = std::contiguous_iterator_tag; + using iterator_category = std::random_access_iterator_tag; + using value_type = typename _Myvec::value_type; + using difference_type = typename _Myvec::difference_type; + using pointer = typename _Myvec::const_pointer; + using reference = const value_type&; + + using _Tptr = typename _Myvec::pointer; + + constexpr sequence_const_iterator() noexcept : _Ptr() {} + + constexpr sequence_const_iterator(_Tptr _Parg) noexcept : _Ptr(_Parg) {} + + constexpr reference operator*() const noexcept { return *_Ptr; } + + constexpr pointer operator->() const noexcept { return _Ptr; } + + constexpr sequence_const_iterator& operator++() noexcept + { + ++_Ptr; + return *this; + } + + constexpr sequence_const_iterator operator++(int) noexcept + { + sequence_const_iterator _Tmp = *this; + ++*this; + return _Tmp; + } + + constexpr sequence_const_iterator& operator--() noexcept + { + --_Ptr; + return *this; + } + + constexpr sequence_const_iterator operator--(int) noexcept + { + sequence_const_iterator _Tmp = *this; + --*this; + return _Tmp; + } + + constexpr sequence_const_iterator& operator+=(const difference_type _Off) noexcept + { + _Ptr += _Off; + return *this; + } + + constexpr sequence_const_iterator operator+(const difference_type _Off) const noexcept + { + sequence_const_iterator _Tmp = *this; + _Tmp += _Off; + return _Tmp; + } + + friend constexpr sequence_const_iterator operator+(const difference_type _Off, sequence_const_iterator _Next) noexcept + { + _Next += _Off; + return _Next; + } + + constexpr sequence_const_iterator& operator-=(const difference_type _Off) noexcept { return *this += -_Off; } + + constexpr sequence_const_iterator operator-(const difference_type _Off) const noexcept + { + sequence_const_iterator _Tmp = *this; + _Tmp -= _Off; + return _Tmp; + } + + constexpr difference_type operator-(const sequence_const_iterator& _Right) const noexcept { return static_cast(_Ptr - _Right._Ptr); } + + constexpr reference operator[](const difference_type _Off) const noexcept { return *(*this + _Off); } + + constexpr bool operator==(const sequence_const_iterator& _Right) const noexcept { return _Ptr == _Right._Ptr; } + + bool operator!=(const sequence_const_iterator& _Right) const noexcept { return !(*this == _Right); } + + bool operator<(const sequence_const_iterator& _Right) const noexcept { return _Ptr < _Right._Ptr; } + + bool operator>(const sequence_const_iterator& _Right) const noexcept { return _Right < *this; } + + bool operator<=(const sequence_const_iterator& _Right) const noexcept { return !(_Right < *this); } + + bool operator>=(const sequence_const_iterator& _Right) const noexcept { return !(*this < _Right); } + + _Tptr _Ptr; // pointer to element in vector +}; + +template +class sequence_iterator : public sequence_const_iterator<_Myvec> { +public: + using _Mybase = sequence_const_iterator<_Myvec>; + + using iterator_concept = std::contiguous_iterator_tag; + using iterator_category = std::random_access_iterator_tag; + using value_type = typename _Myvec::value_type; + using difference_type = typename _Myvec::difference_type; + using pointer = typename _Myvec::pointer; + using reference = value_type&; + + using _Mybase::_Mybase; + + constexpr reference operator*() const noexcept { return const_cast(_Mybase::operator*()); } + + constexpr pointer operator->() const noexcept { return this->_Ptr; } + + constexpr sequence_iterator& operator++() noexcept + { + _Mybase::operator++(); + return *this; + } + + constexpr sequence_iterator operator++(int) noexcept + { + sequence_iterator _Tmp = *this; + _Mybase::operator++(); + return _Tmp; + } + + constexpr sequence_iterator& operator--() noexcept + { + _Mybase::operator--(); + return *this; + } + + constexpr sequence_iterator operator--(int) noexcept + { + sequence_iterator _Tmp = *this; + _Mybase::operator--(); + return _Tmp; + } + + constexpr sequence_iterator& operator+=(const difference_type _Off) noexcept + { + _Mybase::operator+=(_Off); + return *this; + } + + constexpr sequence_iterator operator+(const difference_type _Off) const noexcept + { + sequence_iterator _Tmp = *this; + _Tmp += _Off; + return _Tmp; + } + + friend constexpr sequence_iterator operator+(const difference_type _Off, sequence_iterator _Next) noexcept + { + _Next += _Off; + return _Next; + } + + constexpr sequence_iterator& operator-=(const difference_type _Off) noexcept + { + _Mybase::operator-=(_Off); + return *this; + } + + using _Mybase::operator-; + + constexpr sequence_iterator operator-(const difference_type _Off) const noexcept + { + sequence_iterator _Tmp = *this; + _Tmp -= _Off; + return _Tmp; + } + + constexpr reference operator[](const difference_type _Off) const noexcept { return const_cast(_Mybase::operator[](_Off)); } +}; + +template +struct _Vec_iter_types { + using value_type = _Value_type; + using size_type = _Size_type; + using difference_type = _Difference_type; + using pointer = _Pointer; + using const_pointer = _Const_pointer; +}; + +template +struct _Vector_val { + using value_type = typename _Val_types::value_type; + using size_type = typename _Val_types::size_type; + using difference_type = typename _Val_types::difference_type; + using pointer = typename _Val_types::pointer; + using const_pointer = typename _Val_types::const_pointer; + using reference = value_type&; + using const_reference = const value_type&; + + constexpr _Vector_val() noexcept : _Myfirst(), _Mylast(), _Myend() {} + constexpr _Vector_val(pointer _First, pointer _Last, pointer _End) noexcept : _Myfirst(_First), _Mylast(_Last), _Myend(_End) {} + + constexpr void _Swap_val(_Vector_val& _Right) noexcept + { + std::swap(_Myfirst, _Right._Myfirst); // intentional ADL + std::swap(_Mylast, _Right._Mylast); // intentional ADL + std::swap(_Myend, _Right._Myend); // intentional ADL + } + + constexpr void _Take_contents(_Vector_val& _Right) noexcept + { + _Myfirst = _Right._Myfirst; + _Mylast = _Right._Mylast; + _Myend = _Right._Myend; + + _Right._Myfirst = nullptr; + _Right._Mylast = nullptr; + _Right._Myend = nullptr; + } + + pointer _Myfirst; + pointer _Mylast; + pointer _Myend; +}; + +template , fill_policy _FillPolicy = fill_policy::always> +class vector { // varying size array of values +private: + template + friend class _Vb_val; + friend __tidy_guard; + + using _Alty = _TLX rebind_alloc_t<_Alloc, _Ty>; + using _Alty_traits = std::allocator_traits<_Alty>; + +public: + static constexpr bool allow_auto_fill = __allow_auto_fill_v<_Ty, _FillPolicy>; + + static_assert(std::is_object_v<_Ty>, "The C++ Standard forbids containers of non-object types " + "because of [container.requirements]."); + + using value_type = _Ty; + using allocator_type = _Alloc; + using pointer = typename _Alty_traits::pointer; + using const_pointer = typename _Alty_traits::const_pointer; + using reference = _Ty&; + using const_reference = const _Ty&; + using size_type = typename _Alty_traits::size_type; + using difference_type = typename _Alty_traits::difference_type; + +private: + // _Vector_val, _Simple_types<_Ty>, _Vec_iter_types<_Ty, size_type, difference_type, pointer, const_pointer>>> + using _Scary_val = _Vector_val<_Vec_iter_types<_Ty, size_type, difference_type, pointer, const_pointer>>; + + struct _Reallocation_guard { + _Alloc& _Al; + pointer _New_begin; + size_type _New_capacity; + pointer _Constructed_first; + pointer _Constructed_last; + + _Reallocation_guard& operator=(const _Reallocation_guard&) = delete; + _Reallocation_guard& operator=(_Reallocation_guard&&) = delete; + + constexpr ~_Reallocation_guard() noexcept + { + if (_New_begin != nullptr) + { + _TLX destroy_range(_Constructed_first, _Constructed_last, _Al); + _Al.deallocate(_New_begin, _New_capacity); + } + } + }; + + struct _Simple_reallocation_guard { + _Alloc& _Al; + pointer _New_begin; + size_type _New_capacity; + + _Simple_reallocation_guard& operator=(const _Simple_reallocation_guard&) = delete; + _Simple_reallocation_guard& operator=(_Simple_reallocation_guard&&) = delete; + + constexpr ~_Simple_reallocation_guard() noexcept + { + if (_New_begin != nullptr) + { + _Al.deallocate(_New_begin, _New_capacity); + } + } + }; + + struct _Vaporization_guard { // vaporize the detached piece + vector* _Target; + pointer _Vaporized_first; + pointer _Vaporized_last; + pointer _Destroyed_first; + + _Vaporization_guard& operator=(const _Vaporization_guard&) = delete; + _Vaporization_guard& operator=(_Vaporization_guard&&) = delete; + + ~_Vaporization_guard() noexcept + { + if (_Target != nullptr) + { + auto& _Al = _Target->_Getal(); + auto& _Mylast = _Target->_Mypair._Myval2._Mylast; + _TLX destroy_range(_Destroyed_first, _Mylast, _Al); + _Mylast = _Vaporized_first; + } + } + }; + +public: + using iterator = sequence_iterator<_Scary_val>; + using const_iterator = sequence_const_iterator<_Scary_val>; + + constexpr vector() noexcept : _Mypair(_TLX __zero_then_variadic_args_t{}) {} + + constexpr explicit vector(const _Alloc& _Al) noexcept : _Mypair(_TLX __one_then_variadic_args_t{}, _Al) {} + + constexpr explicit vector(const size_type _Count, const _Alloc& _Al = _Alloc()) : _Mypair(_TLX __one_then_variadic_args_t{}, _Al) { _Construct_n(_Count); } + + constexpr vector(const size_type _Count, const _Ty& _Val, const _Alloc& _Al = _Alloc()) : _Mypair(_TLX __one_then_variadic_args_t{}, _Al) + { + _Construct_n(_Count, _Val); + } + + template ::value, int> = 0> + constexpr vector(_Iter _First, _Iter _Last, const _Alloc& _Al = _Alloc()) : _Mypair(_TLX __one_then_variadic_args_t{}, _Al) + { + auto _UFirst = _First; + auto _ULast = _Last; + + // since we only care about pointer-like iterators, we can always compute length + const auto _Length = static_cast(_ULast - _UFirst); + const auto _Count = static_cast(_Length); + + _Construct_n(_Count, _UFirst, _ULast); + } + + constexpr vector(std::initializer_list<_Ty> _Ilist, const _Alloc& _Al = _Alloc()) : _Mypair(_TLX __one_then_variadic_args_t{}, _Al) + { + _Construct_n(static_cast(_Ilist.size()), _Ilist.begin(), _Ilist.end()); + } + + constexpr vector(const vector& _Right) : _Mypair(_TLX __one_then_variadic_args_t{}, _Alty_traits::select_on_container_copy_construction(_Right._Getal())) + { + const auto& _Right_data = _Right._Mypair._Myval2; + const auto _Count = static_cast(_Right_data._Mylast - _Right_data._Myfirst); + _Construct_n(_Count, _Right_data._Myfirst, _Right_data._Mylast); + } + + constexpr vector(const vector& _Right, const _TLX identity_t<_Alloc>& _Al) : _Mypair(_TLX __one_then_variadic_args_t{}, _Al) + { + const auto& _Right_data = _Right._Mypair._Myval2; + const auto _Count = static_cast(_Right_data._Mylast - _Right_data._Myfirst); + _Construct_n(_Count, _Right_data._Myfirst, _Right_data._Mylast); + } + + constexpr vector(vector&& _Right) noexcept + : _Mypair(_TLX __one_then_variadic_args_t{}, std::move(_Right._Getal()), std::exchange(_Right._Mypair._Myval2._Myfirst, nullptr), + std::exchange(_Right._Mypair._Myval2._Mylast, nullptr), std::exchange(_Right._Mypair._Myval2._Myend, nullptr)) + {} + + constexpr vector(vector&& _Right, const _TLX identity_t<_Alloc>& _Al_) noexcept(std::allocator_traits<_Alloc>::is_always_equal::value && + std::is_nothrow_move_constructible_v<_Ty>) + : _Mypair(_TLX __one_then_variadic_args_t{}, _Al_) + { + _Alty& _Al = _Getal(); + auto& _My_data = _Mypair._Myval2; + auto& _Right_data = _Right._Mypair._Myval2; + + if constexpr (!_Alty_traits::is_always_equal::value) + { + if (_Al != _Right._Getal()) + { + const auto _Count = static_cast(_Right_data._Mylast - _Right_data._Myfirst); + if (_Count != 0) + { + _Buy_raw(_Count); + _My_data._Mylast = uninitialized_move(_Right_data._Myfirst, _Right_data._Mylast, _My_data._Myfirst, _Al); + } + return; + } + } + + _My_data._Take_contents(_Right_data); + } + + constexpr vector& operator=(vector&& _Right) noexcept + { + if (this == std::addressof(_Right)) + { + return *this; + } + + _Tidy(); + _Mypair._Myval2._Take_contents(_Right._Mypair._Myval2); + return *this; + } + + constexpr ~vector() noexcept { _Tidy(); } + +private: + template + constexpr _Ty& _Emplace_one_at_back(_Valty&&... _Val) + { + // insert by perfectly forwarding into element at end, provide strong guarantee + auto& _My_data = _Mypair._Myval2; + pointer& _Mylast = _My_data._Mylast; + + if (_Mylast != _My_data._Myend) + { + return _Emplace_back_with_unused_capacity(std::forward<_Valty>(_Val)...); + } + + return *_Emplace_reallocate(_Mylast, std::forward<_Valty>(_Val)...); + } + + template + constexpr _Ty& _Emplace_back_with_unused_capacity(Valty&&... val) + { + auto& data = _Mypair._Myval2; + pointer& last = data._Mylast; + _TLX_INTERNAL_CHECK(last != data._Myend); + + if constexpr (std::is_nothrow_constructible_v<_Ty, Valty...>) + { + tlx::construct_at(last, std::forward(val)...); + } + else + { + _Alty_traits::construct(_Getal(), std::to_address(last), std::forward(val)...); + } + + _Ty& result = *last; + ++last; + return result; + } + + template + constexpr pointer _Emplace_reallocate(const pointer _Whereptr, _Valty&&... _Val) + { + // reallocate and insert by perfectly forwarding _Val at _Whereptr + _Alty& _Al = _Getal(); + auto& _My_data = _Mypair._Myval2; + pointer& _Myfirst = _My_data._Myfirst; + pointer& _Mylast = _My_data._Mylast; + + _TLX_INTERNAL_CHECK(_Mylast == _My_data._Myend); // check that we have no unused capacity + + const auto _Whereoff = static_cast(_Whereptr - _Myfirst); + const auto _Oldsize = static_cast(_Mylast - _Myfirst); + + if (_Oldsize == max_size()) + { + _Xlength(); + } + + const size_type _Newsize = _Oldsize + 1; + size_type _Newcapacity = _Calculate_growth(_Newsize); + + const pointer _Newvec = _Al.allocate(_Newcapacity); + const pointer _Constructed_last = _Newvec + _Whereoff + 1; + + _Reallocation_guard _Guard{_Al, _Newvec, _Newcapacity, _Constructed_last, _Constructed_last}; + auto& _Constructed_first = _Guard._Constructed_first; + + _Alty_traits::construct(_Al, _Newvec + _Whereoff, std::forward<_Valty>(_Val)...); + _Constructed_first = _Newvec + _Whereoff; + + if (_Whereptr == _Mylast) + { // at back, provide strong guarantee + if constexpr (std::is_nothrow_move_constructible_v<_Ty> || !std::is_copy_constructible_v<_Ty>) + { + _TLX uninitialized_move(_Myfirst, _Mylast, _Newvec, _Al); + } + else + { + _TLX uninitialized_copy(_Myfirst, _Mylast, _Newvec, _Al); + } + } + else + { // provide basic guarantee + _TLX uninitialized_move(_Myfirst, _Whereptr, _Newvec, _Al); + _Constructed_first = _Newvec; + _TLX uninitialized_move(_Whereptr, _Mylast, _Newvec + _Whereoff + 1, _Al); + } + + _Guard._New_begin = nullptr; + _Change_array(_Newvec, _Newsize, _Newcapacity); + return _Newvec + _Whereoff; + } + +public: + template + constexpr _Ty& emplace_back(_Valty&&... _Val) + { + // insert by perfectly forwarding into element at end, provide strong guarantee + return _Emplace_one_at_back(std::forward<_Valty>(_Val)...); + } + + constexpr void push_back(const _Ty& _Val) + { // insert element at end, provide strong guarantee + _Emplace_one_at_back(_Val); + } + + constexpr void push_back(_Ty&& _Val) + { + // insert by moving into element at end, provide strong guarantee + _Emplace_one_at_back(std::move(_Val)); + } + +#pragma region extension APIs + + constexpr size_type size_bytes() const { return size() * sizeof(value_type); } + + constexpr vector& operator+=(const value_type& val) + { + push_back(val); + return *this; + } + + template , int> = 0> + constexpr void resize(const size_type _Newsize, std::nullptr_t) + { + _Resize(_Newsize, _TLX value_init); + } + + constexpr void resize(const size_type _Newsize, value_init_t) + { + // trim or append value-initialized elements, provide strong guarantee + _Resize(_Newsize, _TLX value_init); + } + + template + constexpr vector& operator+=(const _Cont& rhs) + { + return this->extend(std::begin(rhs), std::end(rhs)); + } + + template + constexpr vector& extend(_Iter first, _Iter last) + { + insert(end(), first, last); + return *this; + } + + constexpr vector& extend(size_type count) + { + resize(size() + count); + return *this; + } + + constexpr vector& extend(size_type count, const value_type& val) + { + resize(size() + count, val); + return *this; + } + + template + void resize_and_overwrite(const size_type new_size, _Operation op) + { + _Reallocate<_Reallocation_policy::_Exactly>(new_size); + + auto& _My_data = _Mypair._Myval2; + _My_data._Mylast = _My_data._Myfirst + (std::move(op)(_My_data._Myfirst, new_size)); + } + + pointer detach_abi() noexcept + { + auto& _My_data = _Mypair._Myval2; + pointer p = _My_data._Myfirst; + _My_data._Myfirst = nullptr; + _My_data._Mylast = nullptr; + _My_data._Myend = nullptr; + return p; + } + + void attach_abi(pointer ptr, size_type len, size_type capacity = (size_type)-1) + { + _Tidy(); + auto& _My_data = _Mypair._Myval2; + _My_data._Myfirst = ptr; + _My_data._Mylast = ptr + len; + _My_data._Myend = ptr + (capacity != (size_type)-1 ? capacity : len); + } + +#pragma endregion + +public: + template + constexpr iterator emplace(const_iterator _Where, _Valty&&... _Val) + { + auto _Whereptr = _Where._Ptr; + auto& _My_data = _Mypair._Myval2; + auto _Oldlast = _My_data._Mylast; + _TLX_VERIFY(_Whereptr >= _My_data._Myfirst && _Oldlast >= _Whereptr, "vector emplace iterator outside range"); + if (_Oldlast != _My_data._Myend) + { + if (_Whereptr == _Oldlast) + { // at back, strong guarantee + _Emplace_back_with_unused_capacity(std::forward<_Valty>(_Val)...); + } + else + { + auto& _Al = _Getal(); + // construct a copy of the last element at the new end + _Alty_traits::construct(_Al, std::to_address(_Oldlast), std::move(_Oldlast[-1])); + ++_My_data._Mylast; + + // shift elements one position to the right + _TLX move_backward_unchecked(_Whereptr, _Oldlast - 1, _Oldlast); + + // construct new element at the insertion position + _Alty_traits::construct(_Al, std::to_address(_Whereptr), std::forward<_Valty>(_Val)...); + } + + return iterator(_Whereptr); + } + + // no capacity left, reallocate and emplace + return iterator(_Emplace_reallocate(_Whereptr, std::forward<_Valty>(_Val)...)); + } + + constexpr iterator insert(const_iterator _Where, const _Ty& _Val) + { // insert _Val at _Where + return emplace(_Where, _Val); + } + + constexpr iterator insert(const_iterator _Where, _Ty&& _Val) + { // insert by moving _Val at _Where + return emplace(_Where, std::move(_Val)); + } + + template + constexpr iterator insert(const_iterator _Where, const size_type _Count, const _Ty& _Val) + { + // insert _Count copies of _Val at _Where + const pointer _Whereptr = _Where._Ptr; + + auto& _Al = _Getal(); + auto& _My_data = _Mypair._Myval2; + pointer& _Mylast = _My_data._Mylast; + + const pointer _Oldfirst = _My_data._Myfirst; + const pointer _Oldlast = _Mylast; + + _TLX_VERIFY(_Whereptr >= _Oldfirst && _Oldlast >= _Whereptr, "vector insert iterator outside range"); + + const auto _Whereoff = static_cast(_Whereptr - _Oldfirst); + const auto _Unused_capacity = static_cast(_My_data._Myend - _Oldlast); + const bool _One_at_back = _Count == 1 && _Whereptr == _Oldlast; + + if (_Count == 0) + { // nothing to do, avoid invalidating iterators + } + else if (_Count > _Unused_capacity) + { // reallocate + const auto _Oldsize = static_cast(_Oldlast - _Oldfirst); + + if (_Count > max_size() - _Oldsize) + { + _Xlength(); + } + + const size_type _Newsize = _Oldsize + _Count; + size_type _Newcapacity = _Calculate_growth(_Newsize); + + const pointer _Newvec = _Al.allocate(_Newcapacity); + const pointer _Constructed_last = _Newvec + _Whereoff + _Count; + + _Reallocation_guard _Guard{_Al, _Newvec, _Newcapacity, _Constructed_last, _Constructed_last}; + auto& _Constructed_first = _Guard._Constructed_first; + + _TLX uninitialized_fill_n(_Newvec + _Whereoff, _Count, _Val, _Al); + _Constructed_first = _Newvec + _Whereoff; + + if (_One_at_back) + { // strong guarantee + if constexpr (std::is_nothrow_move_constructible_v<_Ty> || !std::is_copy_constructible_v<_Ty>) + { + _TLX uninitialized_move(_Oldfirst, _Oldlast, _Newvec, _Al); + } + else + { + _TLX uninitialized_copy(_Oldfirst, _Oldlast, _Newvec, _Al); + } + } + else + { // basic guarantee + _TLX uninitialized_move(_Oldfirst, _Whereptr, _Newvec, _Al); + _Constructed_first = _Newvec; + _TLX uninitialized_move(_Whereptr, _Oldlast, _Newvec + _Whereoff + _Count, _Al); + } + + _Guard._New_begin = nullptr; + _Change_array(_Newvec, _Newsize, _Newcapacity); + } + else if (_One_at_back) + { // strong guarantee + _Emplace_back_with_unused_capacity(_Val); + } + else + { // basic guarantee + const auto _Affected_elements = static_cast(_Oldlast - _Whereptr); + + if (_Count > _Affected_elements) + { // new stuff spills off end + _Mylast = _TLX uninitialized_fill_n(_Oldlast, _Count - _Affected_elements, _Val, _Al); + _Mylast = _TLX uninitialized_move(_Whereptr, _Oldlast, _Mylast, _Al); + std::fill(_Whereptr, _Oldlast, _Val); + } + else + { // new stuff can all be assigned + _Mylast = _TLX uninitialized_move(_Oldlast - _Count, _Oldlast, _Oldlast, _Al); + _TLX move_backward_unchecked(_Whereptr, _Oldlast - _Count, _Oldlast); + std::fill_n(_Whereptr, _Count, _Val); + } + } + + return iterator(_My_data._Myfirst + _Whereoff); + } + +private: + template + constexpr void _Insert_counted_range(const_iterator _Where, _Iter _First, const size_type _Count) + { + // insert counted range _First + [0, _Count) at _Where + auto _Whereptr = _Where._Ptr; + + auto& _Al = _Getal(); + auto& _My_data = _Mypair._Myval2; + pointer& _Mylast = _My_data._Mylast; + + const pointer _Oldfirst = _My_data._Myfirst; + const pointer _Oldlast = _Mylast; + const auto _Unused_capacity = static_cast(_My_data._Myend - _Oldlast); + + if (_Count == 0) + { // nothing to do, avoid invalidating iterators + } + else if (_Count > _Unused_capacity) + { // reallocate + const auto _Oldsize = static_cast(_Oldlast - _Oldfirst); + + if (_Count > max_size() - _Oldsize) + { + _Xlength(); + } + + const size_type _Newsize = _Oldsize + _Count; + size_type _Newcapacity = _Calculate_growth(_Newsize); + + const pointer _Newvec = _Al.allocate(_Newcapacity); + const auto _Whereoff = static_cast(_Whereptr - _Oldfirst); + const pointer _Constructed_last = _Newvec + _Whereoff + _Count; + + _Reallocation_guard _Guard{_Al, _Newvec, _Newcapacity, _Constructed_last, _Constructed_last}; + auto& _Constructed_first = _Guard._Constructed_first; + + _TLX uninitialized_copy_n(std::move(_First), _Count, _Newvec + _Whereoff, _Al); + _Constructed_first = _Newvec + _Whereoff; + + if (_Count == 1 && _Whereptr == _Oldlast) + { // one at back, provide strong guarantee + if constexpr (std::is_nothrow_move_constructible_v<_Ty> || !std::is_copy_constructible_v<_Ty>) + { + _TLX uninitialized_move(_Oldfirst, _Oldlast, _Newvec, _Al); + } + else + { + _TLX uninitialized_copy(_Oldfirst, _Oldlast, _Newvec, _Al); + } + } + else + { // provide basic guarantee + _TLX uninitialized_move(_Oldfirst, _Whereptr, _Newvec, _Al); + _Constructed_first = _Newvec; + _TLX uninitialized_move(_Whereptr, _Oldlast, _Newvec + _Whereoff + _Count, _Al); + } + + _Guard._New_begin = nullptr; + _Change_array(_Newvec, _Newsize, _Newcapacity); + } + else + { // Attempt to provide the strong guarantee for EmplaceConstructible failure. + // If we encounter copy/move construction/assignment failure, provide the basic guarantee. + // (For one-at-back, this provides the strong guarantee.) + + const auto _Affected_elements = static_cast(_Oldlast - _Whereptr); + + if (_Count < _Affected_elements) + { // some affected elements must be assigned + _Mylast = _TLX uninitialized_move(_Oldlast - _Count, _Oldlast, _Oldlast, _Al); + _TLX move_backward_unchecked(_Whereptr, _Oldlast - _Count, _Oldlast); + _TLX destroy_range(_Whereptr, _Whereptr + _Count, _Al); + + try + { + _TLX uninitialized_copy_n(std::move(_First), _Count, _Whereptr, _Al); + } + catch (...) + { + // glue the broken pieces back together + + _Vaporization_guard _Guard{this, _Whereptr, _Oldlast, _Whereptr + _Count}; + _TLX uninitialized_move(_Whereptr + _Count, _Whereptr + 2 * _Count, _Whereptr, _Al); + _Guard._Target = nullptr; + + _TLX move_unchecked(_Whereptr + 2 * _Count, _Mylast, _Whereptr + _Count); + _TLX destroy_range(_Oldlast, _Mylast, _Al); + _Mylast = _Oldlast; + throw; + } + } + else + { // affected elements don't overlap before/after + const pointer _Relocated = _Whereptr + _Count; + _Mylast = _TLX uninitialized_move(_Whereptr, _Oldlast, _Relocated, _Al); + _TLX destroy_range(_Whereptr, _Oldlast, _Al); + try + { + _TLX uninitialized_copy_n(std::move(_First), _Count, _Whereptr, _Al); + // glue the broken pieces back together + } + catch (...) + { + _Vaporization_guard _Guard{this, _Whereptr, _Oldlast, _Relocated}; + _TLX uninitialized_move(_Relocated, _Mylast, _Whereptr, _Al); + _Guard._Target = nullptr; + + _TLX destroy_range(_Relocated, _Mylast, _Al); + _Mylast = _Oldlast; + throw; + } + } + } + } + +public: + template , int> = 0> + constexpr iterator insert(const_iterator _Where, _Iter _First, _Iter _Last) + { + auto _Whereptr = _Where._Ptr; + auto& _My_data = _Mypair._Myval2; + const pointer _Oldfirst = _My_data._Myfirst; + + const auto _Whereoff = static_cast(_Whereptr - _Oldfirst); + const auto _Count = static_cast(_Last - _First); + + _Insert_counted_range(_Where, _First, _Count); + + return iterator(_My_data._Myfirst + _Whereoff); + } + + constexpr iterator insert(const_iterator _Where, std::initializer_list<_Ty> _Ilist) + { + const pointer _Whereptr = _Where._Ptr; + auto& _My_data = _Mypair._Myval2; + const pointer _Oldfirst = _My_data._Myfirst; + const auto _Whereoff = static_cast(_Whereptr - _Oldfirst); + + const auto _Count = static_cast(_Ilist.size()); + _Insert_counted_range(_Where, _Ilist.begin(), _Count); + return iterator(_My_data._Myfirst + _Whereoff); + } + + constexpr void assign(const size_type _Newsize, const _Ty& _Val) + { + // assign _Newsize * _Val + auto& _Al = _Getal(); + auto& _My_data = _Mypair._Myval2; + pointer& _Myfirst = _My_data._Myfirst; + pointer& _Mylast = _My_data._Mylast; + + const auto _Oldcapacity = static_cast(_My_data._Myend - _Myfirst); + if (_Newsize > _Oldcapacity) + { // reallocate + _Clear_and_reserve_geometric(_Newsize); + _Mylast = _TLX uninitialized_fill_n(_Myfirst, _Newsize, _Val, _Al); + return; + } + + const auto _Oldsize = static_cast(_Mylast - _Myfirst); + if (_Newsize > _Oldsize) + { + std::fill(_Myfirst, _Mylast, _Val); + _Mylast = _TLX uninitialized_fill_n(_Mylast, _Newsize - _Oldsize, _Val, _Al); + } + else + { + const pointer _Newlast = _Myfirst + _Newsize; + std::fill(_Myfirst, _Newlast, _Val); + _TLX destroy_range(_Newlast, _Mylast, _Al); + _Mylast = _Newlast; + } + } + +private: + template + constexpr void _Assign_counted_range(_Iter _First, const size_type _Newsize) + { + // assign elements from counted range _First + [0, _Newsize) + auto& _Al = _Getal(); + auto& _My_data = _Mypair._Myval2; + pointer& _Myfirst = _My_data._Myfirst; + pointer& _Mylast = _My_data._Mylast; + pointer& _Myend = _My_data._Myend; + + const auto _Oldcapacity = static_cast(_Myend - _Myfirst); + if (_Newsize > _Oldcapacity) + { + _Clear_and_reserve_geometric(_Newsize); + _Mylast = _TLX uninitialized_copy_n(std::move(_First), _Newsize, _Myfirst, _Al); + return; + } + + const auto _Oldsize = static_cast(_Mylast - _Myfirst); + if (_Newsize > _Oldsize) + { + bool _Copied = false; + if constexpr (_TLX bitcopy_assignable_v<_Iter, pointer>) + { + if (!std::is_constant_evaluated()) + { + copy_memmove_n(_First, static_cast(_Oldsize), _Myfirst); + _First += _Oldsize; + _Copied = true; + } + } + + if (!_Copied) + { + for (auto _Mid = _Myfirst; _Mid != _Mylast; ++_Mid, (void)++_First) + { + *_Mid = *_First; + } + } + + _Mylast = _TLX uninitialized_copy_n(std::move(_First), _Newsize - _Oldsize, _Mylast, _Al); + } + else + { + const pointer _Newlast = _Myfirst + _Newsize; + _TLX copy_n_unchecked4(std::move(_First), _Newsize, _Myfirst); + _TLX destroy_range(_Newlast, _Mylast, _Al); + _Mylast = _Newlast; + } + } + +public: + template , int> = 0> + constexpr void assign(_Iter _First, _Iter _Last) + { + const auto _Length = static_cast(_Last - _First); // pointer difference + const auto _Count = static_cast(_Length); + + _Assign_counted_range(_First, _Count); + } + + constexpr void assign(const std::initializer_list<_Ty> _Ilist) + { + const auto _Count = static_cast(_Ilist.size()); + _Assign_counted_range(_Ilist.begin(), _Count); + } + + constexpr vector& operator=(const vector& _Right) + { + if (this == std::addressof(_Right)) + { + return *this; // self-assignment, nothing to do + } + + _Tidy(); // clear current contents + + auto& _Right_data = _Right._Mypair._Myval2; + const size_type _Count = static_cast(_Right_data._Mylast - _Right_data._Myfirst); + + _Assign_counted_range(_Right_data._Myfirst, _Count); // copy elements from _Right + + return *this; + } + + constexpr vector& operator=(std::initializer_list<_Ty> _Ilist) + { + const auto _Count = static_cast(_Ilist.size()); + _Assign_counted_range(_Ilist.begin(), _Count); + return *this; + } + +private: + template + constexpr void _Resize_reallocate(const size_type _Newsize, const _Ty2& _Val) + { + if (_Newsize > max_size()) + { + _Xlength(); + } + + auto& _Al = _Getal(); + auto& _My_data = _Mypair._Myval2; + pointer& _Myfirst = _My_data._Myfirst; + pointer& _Mylast = _My_data._Mylast; + + const auto _Oldsize = static_cast(_Mylast - _Myfirst); + size_type _Newcapacity = _Calculate_growth(_Newsize); + + const pointer _Newvec = _Al.allocate(_Newcapacity); + const pointer _Appended_first = _Newvec + _Oldsize; + + _Reallocation_guard _Guard{_Al, _Newvec, _Newcapacity, _Appended_first, _Appended_first}; + auto& _Appended_last = _Guard._Constructed_last; + + if constexpr (std::is_same_v<_Ty2, _Ty>) + { // Fill with user value: use memset for 1‑byte POD, construct otherwise + _Appended_last = _TLX uninitialized_fill_n(_Appended_first, _Newsize - _Oldsize, _Val, _Al); + } + else + { // Fill with value init: fast-pass + if constexpr ((std::is_same_v<_Ty2, _TLX __auto_value_init_t> && allow_auto_fill) || std::is_same_v<_Ty2, _TLX value_init_t>) + _Appended_last = _TLX uninitialized_value_construct_n(_Appended_first, _Newsize - _Oldsize, _Al); + else // No fill: blazing fast + _Appended_last = _Appended_first + (_Newsize - _Oldsize); + } + + if constexpr (std::is_nothrow_move_constructible_v<_Ty> || !std::is_copy_constructible_v<_Ty>) + { + _TLX uninitialized_move(_Myfirst, _Mylast, _Newvec, _Al); + } + else + { + _TLX uninitialized_copy(_Myfirst, _Mylast, _Newvec, _Al); + } + + _Guard._New_begin = nullptr; + _Change_array(_Newvec, _Newsize, _Newcapacity); + } + + template + constexpr void _Resize(const size_type _Newsize, const _Ty2& _Val) + { + // trim or append elements, provide strong guarantee + auto& _Al = _Getal(); + auto& _My_data = _Mypair._Myval2; + pointer& _Myfirst = _My_data._Myfirst; + pointer& _Mylast = _My_data._Mylast; + const auto _Oldsize = static_cast(_Mylast - _Myfirst); + if (_Newsize < _Oldsize) + { // trim + const pointer _Newlast = _Myfirst + _Newsize; + _TLX destroy_range(_Newlast, _Mylast, _Al); + _Mylast = _Newlast; + return; + } + + if (_Newsize > _Oldsize) + { // append + const auto _Oldcapacity = static_cast(_My_data._Myend - _Myfirst); + if (_Newsize > _Oldcapacity) + { // reallocate + _Resize_reallocate(_Newsize, _Val); + return; + } + + if constexpr (std::is_same_v<_Ty2, _Ty>) + { // Fill with user value: use memset for 1‑byte POD, construct otherwise + _Mylast = _TLX uninitialized_fill_n(_Mylast, _Newsize - _Oldsize, _Val, _Al); + } + else if constexpr ((std::is_same_v<_Ty2, _TLX __auto_value_init_t> && allow_auto_fill) || std::is_same_v<_Ty2, _TLX value_init_t>) + { // Fill with value init: fast-pass + _Mylast = _TLX uninitialized_value_construct_n(_Mylast, _Newsize - _Oldsize, _Al); + } + else + { // No fill: blazing fast + _Mylast = _Myfirst + _Newsize; + } + } + // if _Newsize == _Oldsize, do nothing + } + +public: + constexpr void resize(const size_type _Newsize) + { + // trim or append value-initialized elements, provide strong guarantee + _Resize(_Newsize, __auto_value_init_t{}); + } + + constexpr void resize(const size_type _Newsize, const _Ty& _Val) + { + // trim or append copies of _Val, provide strong guarantee + _Resize(_Newsize, _Val); + } + +private: + enum class _Reallocation_policy + { + _At_least, + _Exactly + }; + + template <_Reallocation_policy _Policy> + constexpr void _Reallocate(const size_type& _Newcapacity) + { + // set capacity to _Newcapacity (without geometric growth), provide strong guarantee + auto& _Al = _Getal(); + auto& _My_data = _Mypair._Myval2; + pointer& _Myfirst = _My_data._Myfirst; + pointer& _Mylast = _My_data._Mylast; + + const auto _Size = static_cast(_Mylast - _Myfirst); + + pointer _Newvec; + if constexpr (_Policy == _Reallocation_policy::_At_least) + { + _Newvec = _Al.allocate(_Newcapacity); + } + else + { + _Newvec = _Al.allocate(_Newcapacity); + } + + _Simple_reallocation_guard _Guard{_Al, _Newvec, _Newcapacity}; + + if constexpr (std::is_nothrow_move_constructible_v<_Ty> || !std::is_copy_constructible_v<_Ty>) + { + uninitialized_move(_Myfirst, _Mylast, _Newvec, _Al); + } + else + { + uninitialized_copy(_Myfirst, _Mylast, _Newvec, _Al); + } + + _Guard._New_begin = nullptr; + _Change_array(_Newvec, _Size, _Newcapacity); + } + + constexpr void _Clear_and_reserve_geometric(const size_type _Newsize) + { + auto& _Al = _Getal(); + auto& _My_data = _Mypair._Myval2; + pointer& _Myfirst = _My_data._Myfirst; + pointer& _Mylast = _My_data._Mylast; + pointer& _Myend = _My_data._Myend; + + if (_Newsize > max_size()) + { + _Xlength(); + } + + const size_type _Newcapacity = _Calculate_growth(_Newsize); + + if (_Myfirst) + { // destroy and deallocate old array + _TLX destroy_range(_Myfirst, _Mylast, _Al); + _Al.deallocate(_Myfirst, static_cast(_Myend - _Myfirst)); + + _Myfirst = nullptr; + _Mylast = nullptr; + _Myend = nullptr; + } + + _Buy_raw(_Newcapacity); + } + +public: + constexpr void reserve(size_type _Newcapacity) + { + // increase capacity to _Newcapacity (without geometric growth), provide strong guarantee + if (_Newcapacity > capacity()) + { // something to do (reserve() never shrinks) + if (_Newcapacity > max_size()) + { + _Xlength(); + } + + _Reallocate<_Reallocation_policy::_At_least>(_Newcapacity); + } + } + + constexpr void shrink_to_fit() + { // reduce capacity to size, provide strong guarantee + auto& _My_data = _Mypair._Myval2; + const pointer _Oldlast = _My_data._Mylast; + if (_Oldlast != _My_data._Myend) + { // something to do + const pointer _Oldfirst = _My_data._Myfirst; + if (_Oldfirst == _Oldlast) + { + _Tidy(); + } + else + { + size_type _Newcapacity = static_cast(_Oldlast - _Oldfirst); + _Reallocate<_Reallocation_policy::_Exactly>(_Newcapacity); + } + } + } + + constexpr void pop_back() noexcept /* strengthened */ + { + auto& _My_data = _Mypair._Myval2; + pointer& _Mylast = _My_data._Mylast; + + _TLX_VERIFY(_My_data._Myfirst != _Mylast, "pop_back() called on empty vector"); + + _Alty_traits::destroy(_Getal(), std::to_address(_Mylast - 1)); + --_Mylast; + } + + constexpr iterator erase(const_iterator _Where) noexcept /* strengthened */ + { + auto _Whereptr = _Where._Ptr; + auto& _My_data = _Mypair._Myval2; + pointer& _Mylast = _My_data._Mylast; + + move_unchecked(_Whereptr + 1, _Mylast, _Whereptr); + _Alty_traits::destroy(_Getal(), std::to_address(_Mylast - 1)); + --_Mylast; + return iterator(_Whereptr); + } + + constexpr iterator erase(const_iterator _First, const_iterator _Last) noexcept /* strengthened */ + { + auto _Firstptr = _First._Ptr; + auto _Lastptr = _Last._Ptr; + auto& _My_data = _Mypair._Myval2; + pointer& _Mylast = _My_data._Mylast; + + if (_Firstptr != _Lastptr) + { // something to do, invalidate iterators + const pointer _Newlast = move_unchecked(_Lastptr, _Mylast, _Firstptr); + _TLX destroy_range(_Newlast, _Mylast, _Getal()); + _Mylast = _Newlast; + } + + return iterator(_Firstptr); + } + + constexpr void clear() noexcept + { // erase all + auto& _My_data = _Mypair._Myval2; + pointer& _Myfirst = _My_data._Myfirst; + pointer& _Mylast = _My_data._Mylast; + + if (_Myfirst == _Mylast) + { // already empty, nothing to do + // This is an optimization for debug mode: we can avoid taking the debug lock to invalidate iterators. + // Note that when clearing an empty vector, this will preserve past-the-end iterators, which is allowed by + // N4950 [sequence.reqmts]/54 "a.clear() [...] may invalidate the past-the-end iterator". + return; + } + + _TLX destroy_range(_Myfirst, _Mylast, _Getal()); + _Mylast = _Myfirst; + } + + constexpr void swap(vector& _Right) noexcept /* strengthened */ + { + if (this != std::addressof(_Right)) + { + std::swap(_Getal(), _Right._Getal()); + _Mypair._Myval2._Swap_val(_Right._Mypair._Myval2); + } + } + + constexpr _Ty* data() noexcept { return _Mypair._Myval2._Myfirst; } + + constexpr const _Ty* data() const noexcept { return _Mypair._Myval2._Myfirst; } + + constexpr iterator begin() noexcept + { + auto& _My_data = _Mypair._Myval2; + return iterator(_My_data._Myfirst); + } + + constexpr const_iterator begin() const noexcept + { + auto& _My_data = _Mypair._Myval2; + return const_iterator(_My_data._Myfirst); + } + + constexpr iterator end() noexcept + { + auto& _My_data = _Mypair._Myval2; + return iterator(_My_data._Mylast); + } + + constexpr const_iterator end() const noexcept + { + auto& _My_data = _Mypair._Myval2; + return const_iterator(_My_data._Mylast); + } + + constexpr const_iterator cbegin() const noexcept { return begin(); } + + constexpr const_iterator cend() const noexcept { return end(); } + + constexpr bool empty() const noexcept + { + auto& _My_data = _Mypair._Myval2; + return _My_data._Myfirst == _My_data._Mylast; + } + + constexpr size_type size() const noexcept + { + auto& _My_data = _Mypair._Myval2; + return static_cast(_My_data._Mylast - _My_data._Myfirst); + } + + constexpr size_type max_size() const noexcept + { + return (std::min)(static_cast((std::numeric_limits::max)()), _Alty_traits::max_size(_Getal())); + } + + constexpr size_type capacity() const noexcept + { + auto& _My_data = _Mypair._Myval2; + return static_cast(_My_data._Myend - _My_data._Myfirst); + } + + constexpr _Ty& operator[](const size_type _Pos) noexcept /* strengthened */ + { + auto& _My_data = _Mypair._Myval2; + _TLX_VERIFY(_Pos < static_cast(_My_data._Mylast - _My_data._Myfirst), "vector subscript out of range"); + return _My_data._Myfirst[_Pos]; + } + + constexpr const _Ty& operator[](const size_type _Pos) const noexcept /* strengthened */ + { + auto& _My_data = _Mypair._Myval2; + _TLX_VERIFY(_Pos < static_cast(_My_data._Mylast - _My_data._Myfirst), "vector subscript out of range"); + return _My_data._Myfirst[_Pos]; + } + + constexpr _Ty& at(const size_type _Pos) + { + auto& _My_data = _Mypair._Myval2; + if (static_cast(_My_data._Mylast - _My_data._Myfirst) <= _Pos) + _Xrange(); + + return _My_data._Myfirst[_Pos]; + } + + constexpr const _Ty& at(const size_type _Pos) const + { + auto& _My_data = _Mypair._Myval2; + if (static_cast(_My_data._Mylast - _My_data._Myfirst) <= _Pos) + _Xrange(); + + return _My_data._Myfirst[_Pos]; + } + + constexpr _Ty& front() noexcept /* strengthened */ + { + auto& _My_data = _Mypair._Myval2; + _TLX_VERIFY(_My_data._Myfirst != _My_data._Mylast, "front() called on empty vector"); + return *_My_data._Myfirst; + } + + constexpr const _Ty& front() const noexcept /* strengthened */ + { + auto& _My_data = _Mypair._Myval2; + _TLX_VERIFY(_My_data._Myfirst != _My_data._Mylast, "front() called on empty vector"); + return *_My_data._Myfirst; + } + + constexpr _Ty& back() noexcept /* strengthened */ + { + auto& _My_data = _Mypair._Myval2; + _TLX_VERIFY(_My_data._Myfirst != _My_data._Mylast, "back() called on empty vector"); + return _My_data._Mylast[-1]; + } + + constexpr const _Ty& back() const noexcept /* strengthened */ + { + auto& _My_data = _Mypair._Myval2; + _TLX_VERIFY(_My_data._Myfirst != _My_data._Mylast, "back() called on empty vector"); + return _My_data._Mylast[-1]; + } + + constexpr allocator_type get_allocator() const noexcept { return static_cast(_Getal()); } + +private: + constexpr size_type _Calculate_growth(const size_type _Newsize) const + { + // given _Oldcapacity and _Newsize, calculate geometric growth + const size_type _Oldcapacity = capacity(); + const auto _Max = max_size(); + + if (_Oldcapacity > _Max - _Oldcapacity / 2) + { + return _Max; // geometric growth would overflow + } + + const size_type _Geometric = _Oldcapacity + _Oldcapacity / 2; + + if (_Geometric < _Newsize) + { + return _Newsize; // geometric growth would be insufficient + } + + return _Geometric; // geometric growth is sufficient + } + + constexpr void _Buy_raw(size_type _Newcapacity) + { + // allocate array with _Newcapacity elements + auto& _My_data = _Mypair._Myval2; + pointer& _Myfirst = _My_data._Myfirst; + pointer& _Mylast = _My_data._Mylast; + pointer& _Myend = _My_data._Myend; + + _TLX_INTERNAL_CHECK(!_Myfirst && !_Mylast && !_Myend); // check that *this is tidy + _TLX_INTERNAL_CHECK(0 < _Newcapacity && _Newcapacity <= max_size()); + + const pointer _Newvec = _Getal().allocate(_Newcapacity); + _Myfirst = _Newvec; + _Mylast = _Newvec; + _Myend = _Newvec + _Newcapacity; + } + + constexpr void _Buy_nonzero(const size_type _Newcapacity) + { + // allocate array with _Newcapacity elements +#ifdef _ENABLE_TLX_INTERNAL_CHECK + auto& _My_data = _Mypair._Myval2; + pointer& _Myfirst = _My_data._Myfirst; + pointer& _Mylast = _My_data._Mylast; + pointer& _Myend = _My_data._Myend; + _TLX_INTERNAL_CHECK(!_Myfirst && !_Mylast && !_Myend); // check that *this is tidy + _TLX_INTERNAL_CHECK(0 < _Newcapacity); +#endif // _ENABLE_TLX_INTERNAL_CHECK + + if (_Newcapacity > max_size()) + { + _Xlength(); + } + + _Buy_raw(_Newcapacity); + } + + constexpr void _Change_array(const pointer _Newvec, const size_type _Newsize, const size_type _Newcapacity) noexcept + { + // orphan all iterators, discard old array, acquire new array + auto& _Al = _Getal(); + auto& _My_data = _Mypair._Myval2; + pointer& _Myfirst = _My_data._Myfirst; + pointer& _Mylast = _My_data._Mylast; + pointer& _Myend = _My_data._Myend; + + if (_Myfirst) + { // destroy and deallocate old array + _TLX destroy_range(_Myfirst, _Mylast, _Al); + _Al.deallocate(_Myfirst, static_cast(_Myend - _Myfirst)); + } + + _Myfirst = _Newvec; + _Mylast = _Newvec + _Newsize; + _Myend = _Newvec + _Newcapacity; + } + + constexpr void _Tidy() noexcept + { // free all storage + auto& _Al = _Getal(); + auto& _My_data = _Mypair._Myval2; + pointer& _Myfirst = _My_data._Myfirst; + pointer& _Mylast = _My_data._Mylast; + pointer& _Myend = _My_data._Myend; + + if (_Myfirst) + { // destroy and deallocate old array + _TLX destroy_range(_Myfirst, _Mylast, _Al); + _Al.deallocate(_Myfirst, static_cast(_Myend - _Myfirst)); + + _Myfirst = nullptr; + _Mylast = nullptr; + _Myend = nullptr; + } + } + + template + constexpr void _Construct_n(const size_type _Count, _Valty&&... _Val) + { + // Dispatch between the three sized constructions: + // 1-arg -> value-construction, e.g. vector(5) + // 2-arg -> fill, e.g. vector(5, "meow") + // 3-arg -> sized range construction, e.g. vector{"Hello", "Fluffy", "World"} + + auto& _Al = _Getal(); + auto& _My_data = _Mypair._Myval2; + + if (_Count != 0) + { + _Buy_nonzero(_Count); + + if constexpr (sizeof...(_Val) == 0) + { + // value-construction + _My_data._Mylast = uninitialized_value_construct_n(_My_data._Myfirst, _Count, _Al); + } + else if constexpr (sizeof...(_Val) == 1) + { + // fill construction + _My_data._Mylast = uninitialized_fill_n(_My_data._Myfirst, _Count, _Val..., _Al); + } + else if constexpr (sizeof...(_Val) == 2) + { + // range construction + _My_data._Mylast = uninitialized_copy(std::forward<_Valty>(_Val)..., _My_data._Myfirst, _Al); + } + else + { + // unsupported overload + } + } + } + + constexpr void _Move_assign_unequal_alloc(vector& _Right) + { + auto& _Al = _Getal(); + auto& _My_data = _Mypair._Myval2; + auto& _Right_data = _Right._Mypair._Myval2; + + const pointer _First = _Right_data._Myfirst; + const pointer _Last = _Right_data._Mylast; + const auto _Newsize = static_cast(_Last - _First); + + pointer& _Myfirst = _My_data._Myfirst; + pointer& _Mylast = _My_data._Mylast; + + const auto _Oldcapacity = static_cast(_My_data._Myend - _Myfirst); + if (_Newsize > _Oldcapacity) + { + _Clear_and_reserve_geometric(_Newsize); + _Mylast = uninitialized_move(_First, _Last, _Myfirst, _Al); + return; + } + + const auto _Oldsize = static_cast(_Mylast - _Myfirst); + if (_Newsize > _Oldsize) + { + const pointer _Mid = _First + _Oldsize; + move_unchecked(_First, _Mid, _Myfirst); + _Mylast = uninitialized_move(_Mid, _Last, _Mylast, _Al); + } + else + { + const pointer _Newlast = _Myfirst + _Newsize; + move_unchecked(_First, _Last, _Myfirst); + _Mylast = _Newlast; + } + } + + static void _Xlength() { _TLX __xlength_error("vector too long"); } + + static void _Xrange() { _TLX __xout_of_range("invalid vector subscript"); } + + constexpr _Alty& _Getal() noexcept { return _Mypair._Get_first(); } + + constexpr const _Alty& _Getal() const noexcept { return _Mypair._Get_first(); } + + __compressed_pair<_Alty, _Scary_val> _Mypair; +}; + +#pragma region operator== +template +inline constexpr bool operator==(const tlx::vector& lhs, const tlx::vector& rhs) +{ + if (lhs.size() != rhs.size()) + return false; + return std::equal(lhs.begin(), lhs.end(), rhs.begin()); +} + +template ::value && + !std::is_same>::value>> +inline constexpr bool operator==(const tlx::vector& lhs, const OtherContainer& rhs) +{ + if (lhs.size() != rhs.size()) + return false; + return std::equal(lhs.begin(), lhs.end(), rhs.begin()); +} + +template ::value && + !std::is_same>::value>> +inline constexpr bool operator==(const OtherContainer& lhs, const tlx::vector& rhs) +{ + if (lhs.size() != rhs.size()) + return false; + return std::equal(lhs.begin(), lhs.end(), rhs.begin()); +} +#pragma endregion + +#pragma region c++20 like std::erase +template +void erase(vector<_Ty, _Alloc, _Policy>& cont, const _Ty& val) +{ + cont.erase(std::remove(cont.begin(), cont.end(), val), cont.end()); +} +template +void erase_if(vector<_Ty, _Alloc, _Policy>& cont, _Pr pred) +{ + cont.erase(std::remove_if(cont.begin(), cont.end(), pred), cont.end()); +} +#pragma endregion + +#pragma region ordered insert, for flat container emulating +template +inline typename _Cont::iterator ordered_insert(_Cont& vec, typename _Cont::value_type const& val) +{ + return vec.insert(std::upper_bound(vec.begin(), vec.end(), val), val); +} + +template +inline typename _Cont::iterator ordered_insert(_Cont& vec, typename _Cont::value_type const& val, _Pred pred) +{ + return vec.insert(std::upper_bound(vec.begin(), vec.end(), val, pred), val); +} +#pragma endregion + +} // namespace tlx + +namespace std +{ +template +struct pointer_traits<_TLX sequence_const_iterator<_Myvec>> { + using pointer = _TLX sequence_const_iterator<_Myvec>; + using element_type = const typename pointer::value_type; + using difference_type = typename pointer::difference_type; + + static constexpr element_type* to_address(const pointer _Iter) noexcept { return std::to_address(_Iter._Ptr); } +}; +template +struct pointer_traits<_TLX sequence_iterator<_Myvec>> { + using pointer = _TLX sequence_iterator<_Myvec>; + using element_type = typename pointer::value_type; + using difference_type = typename pointer::difference_type; + + static constexpr element_type* to_address(const pointer _Iter) noexcept { return std::to_address(_Iter._Ptr); } +}; +} // namespace std diff --git a/yasio/wtimer_hres.hpp b/yasio/wtimer_hres.hpp index 0b10a1a7b..f7c6961d2 100644 --- a/yasio/wtimer_hres.hpp +++ b/yasio/wtimer_hres.hpp @@ -29,7 +29,6 @@ SOFTWARE. #ifdef _WIN32 # include "yasio/config.hpp" -# include "yasio/utils.hpp" # if defined(YASIO__USE_TIMEAPI) # include @@ -56,7 +55,7 @@ struct wtimer_hres { TIMECAPS tc; if (TIMERR_NOERROR == timeGetDevCaps(&tc, sizeof(TIMECAPS))) { - timer_res_ = yasio::clamp(static_cast(timer_res / _1ms_den), tc.wPeriodMin, tc.wPeriodMax); + timer_res_ = std::clamp(static_cast(timer_res / _1ms_den), tc.wPeriodMin, tc.wPeriodMax); timeBeginPeriod(timer_res_); } # else @@ -78,7 +77,7 @@ struct wtimer_hres { ULONG MinimumResolution, MaximumResolution, CurrentResolution; if (NtQueryTimerResolution(&MinimumResolution, &MaximumResolution, &CurrentResolution) != 0) break; - ZwSetTimerResolution(yasio::clamp(timer_res, MaximumResolution, MinimumResolution), TRUE, &timer_res); + ZwSetTimerResolution(std::clamp(timer_res, MaximumResolution, MinimumResolution), TRUE, &timer_res); } while (false); # endif } diff --git a/yasio/xxsocket.cpp b/yasio/xxsocket.cpp index 577fa6ff7..209c4f4d8 100644 --- a/yasio/xxsocket.cpp +++ b/yasio/xxsocket.cpp @@ -36,7 +36,7 @@ SOFTWARE. # include "yasio/xxsocket.hpp" #endif -#include "yasio/utils.hpp" +#include "yasio/tlx/chrono.hpp" #if !defined(_WIN32) # include "yasio/impl/ifaddrs.hpp" @@ -718,9 +718,9 @@ int xxsocket::send_n(socket_native_type s, const void* buf, int len, std::chrono if (n == -1 && xxsocket::not_send_error(error)) { // Wait upto for the blocking to subside. - auto start = yasio::highp_clock(); + auto start = tlx::highp_clock(); int const rtn = handle_write_ready(s, wtimeout); - wtimeout -= std::chrono::microseconds(yasio::highp_clock() - start); + wtimeout -= std::chrono::microseconds(tlx::highp_clock() - start); // Did select() succeed? if (rtn != -1 && wtimeout.count() > 0) @@ -768,9 +768,9 @@ int xxsocket::recv_n(socket_native_type s, void* buf, int len, std::chrono::micr if (n == -1 && xxsocket::not_recv_error(error)) { // Wait upto for the blocking to subside. - auto start = yasio::highp_clock(); + auto start = tlx::highp_clock(); int const rtn = handle_read_ready(s, wtimeout); - wtimeout -= std::chrono::microseconds(yasio::highp_clock() - start); + wtimeout -= std::chrono::microseconds(tlx::highp_clock() - start); // Did select() succeed? if (rtn != -1 && wtimeout.count() > 0) @@ -834,9 +834,9 @@ int xxsocket::select(socket_native_type s, fd_set* readfds, fd_set* writefds, fd timeval waitd_tv = {static_cast(wtimeout.count() / std::micro::den), static_cast(wtimeout.count() % std::micro::den)}; - long long start = highp_clock(); + long long start = tlx::highp_clock(); n = ::select(static_cast(s + 1), readfds, writefds, exceptfds, &waitd_tv); - wtimeout -= std::chrono::microseconds(highp_clock() - start); + wtimeout -= std::chrono::microseconds(tlx::highp_clock() - start); if (n < 0 && xxsocket::get_last_errno() == EINTR) { diff --git a/yasio/xxsocket.hpp b/yasio/xxsocket.hpp index d4339e6a0..f44051d9c 100644 --- a/yasio/xxsocket.hpp +++ b/yasio/xxsocket.hpp @@ -40,7 +40,7 @@ SOFTWARE. #include #include "yasio/impl/socket.hpp" #include "yasio/logging.hpp" -#include "yasio/string_view.hpp" +#include "yasio/tlx/string_view.hpp" namespace yasio { @@ -1176,7 +1176,7 @@ template <> struct hash { std::size_t operator()(const yasio::ip::endpoint& ep) const YASIO__NOEXCEPT { - return std::hash()(cxx17::string_view{reinterpret_cast(&ep), static_cast(ep.len())}); + return std::hash()(std::string_view{reinterpret_cast(&ep), static_cast(ep.len())}); } }; } // namespace std diff --git a/yasio/yasio.natvis b/yasio/yasio.natvis index e49aba7dd..ff12db8a3 100644 --- a/yasio/yasio.natvis +++ b/yasio/yasio.natvis @@ -1,82 +1,82 @@ - - - {{ size={_Mysize} capacity={_Myres} }} + + + {{ size={_Mypair._Myval2._Mylast - _Mypair._Myval2._Myfirst} capacity={_Mypair._Myval2._Myend - _Mypair._Myval2._Myfirst} }} - _Mysize - _Myres + _Mypair._Myval2._Mylast - _Mypair._Myval2._Myfirst + _Mypair._Myval2._Myend - _Mypair._Myval2._Myfirst - _Mysize - _Myfirst + _Mypair._Myval2._Mylast - _Mypair._Myval2._Myfirst + _Mypair._Myval2._Myfirst - - {{ size={_Mysize} {_Myfirst,[_Mysize]s8} }} + + {{ size={_Mysize} {_Mypair._Myval2._Myfirst,[_Mysize]s8} }} - _Mysize - _Myres + _Mypair._Myval2._Mylast - _Mypair._Myval2._Myfirst + _Mypair._Myval2._Myend - _Mypair._Myval2._Myfirst - _Mysize - _Myfirst + _Mypair._Myval2._Mylast - _Mypair._Myval2._Myfirst + _Mypair._Myval2._Myfirst - - - {_Myfirst,s8} - {&_Myfirst,s8} - _Myfirst,s8 - &_Myfirst,s8 + + + {_Mypair._Myval2._Myfirst,s8} + {&_Mypair._Myval2._Myfirst,s8} + _Mypair._Myval2._Myfirst,s8 + &_Mypair._Myval2._Myfirst,s8 - _Mysize - _Myres - - _Mysize - _Myfirst + _Mypair._Myval2._Mylast - _Mypair._Myval2._Myfirst + _Mypair._Myval2._Myend - _Mypair._Myval2._Myfirst + + _Mypair._Myval2._Mylast - _Mypair._Myval2._Myfirst + _Mypair._Myval2._Myfirst - - _Mysize - &_Myfirst + + _Mypair._Myval2._Mylast - _Mypair._Myval2._Myfirst + &_Mypair._Myval2._Myfirst - - - - {_Myfirst,su} - {&_Myfirst,su} - _Myfirst,su - &_Myfirst,su + + + + {_Mypair._Myval2._Myfirst,su} + {&_Mypair._Myval2._Myfirst,su} + _Mypair._Myval2._Myfirst,su + &_Mypair._Myval2._Myfirst,su - _Mysize - _Myres - - _Mysize - _Myfirst + _Mypair._Myval2._Mylast - _Mypair._Myval2._Myfirst + _Mypair._Myval2._Myend - _Mypair._Myval2._Myfirst + + _Mypair._Myval2._Mylast - _Mypair._Myval2._Myfirst + _Mypair._Myval2._Myfirst - - _Mysize - &_Myfirst + + _Mypair._Myval2._Mylast - _Mypair._Myval2._Myfirst + &_Mypair._Myval2._Myfirst - - {_Myfirst,s32} - {&_Myfirst,s32} - _Myfirst,s32 - &_Myfirst,s32 + + {_Mypair._Myval2._Myfirst,s32} + {&_Mypair._Myval2._Myfirst,s32} + _Mypair._Myval2._Myfirst,s32 + &_Mypair._Myval2._Myfirst,s32 - _Mysize - _Myres - - _Mysize - _Myfirst + _Mypair._Myval2._Mylast - _Mypair._Myval2._Myfirst + _Mypair._Myval2._Myend - _Mypair._Myval2._Myfirst + + _Mypair._Myval2._Mylast - _Mypair._Myval2._Myfirst + _Mypair._Myval2._Myfirst - - _Mysize - &_Myfirst + + _Mypair._Myval2._Mylast - _Mypair._Myval2._Myfirst + &_Mypair._Myval2._Myfirst