Compare commits
5 Commits
5b066cbc27
...
ee137b2820
| Author | SHA1 | Date | |
|---|---|---|---|
| ee137b2820 | |||
| cdde340efe | |||
| ee34e8a72e | |||
| 0840d807a0 | |||
| 4655d72554 |
@@ -97,7 +97,7 @@ set(PIP_UTILS_LIST)
|
||||
set(PIP_TESTS_LIST)
|
||||
set(PIP_EXPORTS)
|
||||
|
||||
set(PIP_SRC_MODULES "console;crypt;compress;usb;fftw;opencl;io_utils;client_server;cloud;lua")
|
||||
set(PIP_SRC_MODULES "console;crypt;compress;usb;fftw;opencl;io_utils;client_server;cloud;lua;http_server")
|
||||
foreach(_m ${PIP_SRC_MODULES})
|
||||
set(PIP_MSG_${_m} "no")
|
||||
endforeach()
|
||||
@@ -481,6 +481,33 @@ if (NOT CROSSTOOLS)
|
||||
list(APPEND HDR_DIRS "${_lua_bri_dir}/LuaBridge")
|
||||
list(APPEND HDRS ${_lua_src_hdr})
|
||||
|
||||
# libmicrohttpd
|
||||
find_library(microhttpd_LIBRARIES microhttpd HINTS "${MINGW_LIB}")
|
||||
if (microhttpd_LIBRARIES)
|
||||
set(_microhttpd_add_libs)
|
||||
if(WIN32)
|
||||
if("${C_COMPILER}" STREQUAL "cl.exe")
|
||||
else()
|
||||
list(APPEND _microhttpd_add_libs ws2_32)
|
||||
endif()
|
||||
else()
|
||||
list(APPEND _microhttpd_add_libs dl)
|
||||
find_library(tls_lib gnutls)
|
||||
if (tls_lib)
|
||||
list(APPEND _microhttpd_add_libs ${tls_lib})
|
||||
endif()
|
||||
if(DEFINED ENV{QNX_HOST})
|
||||
list(APPEND _microhttpd_add_libs socket)
|
||||
else()
|
||||
if (NOT DEFINED ANDROID_PLATFORM)
|
||||
list(APPEND _microhttpd_add_libs pthread util)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
list(APPEND microhttpd_LIBRARIES "${_microhttpd_add_libs}")
|
||||
pip_module(http_server "${microhttpd_LIBRARIES}" "PIP HTTP server" "" "" "")
|
||||
endif()
|
||||
|
||||
# Test program
|
||||
if(PIP_UTILS)
|
||||
|
||||
@@ -488,7 +515,7 @@ if (NOT CROSSTOOLS)
|
||||
#target_link_libraries(pip_plugin pip)
|
||||
|
||||
add_executable(pip_test "main.cpp")
|
||||
target_link_libraries(pip_test pip pip_io_utils pip_client_server)
|
||||
target_link_libraries(pip_test pip pip_io_utils pip_client_server pip_http_server)
|
||||
if(sodium_FOUND)
|
||||
add_executable(pip_cloud_test "main_picloud_test.cpp")
|
||||
target_link_libraries(pip_cloud_test pip_cloud)
|
||||
|
||||
@@ -12,6 +12,7 @@ Create imported targets:
|
||||
* PIP::ClientServer
|
||||
* PIP::Cloud
|
||||
* PIP::Lua
|
||||
* PIP::HTTPServer
|
||||
|
||||
These targets include directories and depends on
|
||||
main library
|
||||
@@ -23,10 +24,10 @@ include(SHSTKMacros)
|
||||
|
||||
shstk_set_find_dirs(PIP)
|
||||
|
||||
set(__libs "usb;crypt;console;fftw;compress;opencl;io_utils;client_server;cloud;lua")
|
||||
set(__libs "usb;crypt;console;fftw;compress;opencl;io_utils;client_server;cloud;lua;http_server")
|
||||
|
||||
if (BUILDING_PIP)
|
||||
#set(_libs "pip;pip_usb;pip_console;pip_crypt;pip_fftw;pip_compress;pip_opencl;pip_io_utils;pip_cloud;pip_lua")
|
||||
#set(_libs "pip;pip_usb;pip_console;pip_crypt;pip_fftw;pip_compress;pip_opencl;pip_io_utils;pip_cloud;pip_lua;pip_http_server")
|
||||
#set(_bins "pip_cmg;pip_rc;deploy_tool")
|
||||
#get_target_property(_path pip BINARY_DIR)
|
||||
#get_target_property(_path pip LIBRARY_OUTPUT_NAME)
|
||||
@@ -96,6 +97,7 @@ set(__module_io_utils IOUtils )
|
||||
set(__module_client_server ClientServer)
|
||||
set(__module_cloud Cloud )
|
||||
set(__module_lua Lua )
|
||||
set(__module_http_server HTTPServer )
|
||||
|
||||
foreach (_l ${__libs})
|
||||
set( __inc_${_l} "")
|
||||
|
||||
@@ -3,19 +3,19 @@
|
||||
|
||||
\~english
|
||||
|
||||
* direct output to console (\a PICout)
|
||||
* containers (\a PIVector, \a PIDeque, \a PIVector2D, \a PIStack, \a PIQueue, \a PIMap, \a PISet)
|
||||
* byte array (\a PIByteArray)
|
||||
* serialization (\a PIBinaryStream, \a PITextStream, \a PIIOBinaryStream, \a PIIOTextStream, \a PIChunkStream, \a PIJSON)
|
||||
* string (\a PIConstChars, \a PIString, \a PIStringList)
|
||||
* base object (events and handlers) (\a PIObject)
|
||||
* multithreading
|
||||
* Direct output to console (\a PICout)
|
||||
* Containers (\a PIVector, \a PIDeque, \a PIVector2D, \a PIStack, \a PIQueue, \a PIMap, \a PISet)
|
||||
* Byte array (\a PIByteArray)
|
||||
* Serialization (\a PIBinaryStream, \a PITextStream, \a PIIOBinaryStream, \a PIIOTextStream, \a PIChunkStream, \a PIJSON)
|
||||
* String (\a PIConstChars, \a PIString, \a PIStringList)
|
||||
* Base object (events and handlers) (\a PIObject)
|
||||
* Multithreading
|
||||
* thread (\a PIThread)
|
||||
* blocking (\a PIMutex, \a PISpinlock)
|
||||
* executor (\a PIThreadPoolExecutor)
|
||||
* blocking (\a PIMutex, \a PISpinlock, \a PIConditionVariable, \a PISemaphore, \a PIReadWriteLock)
|
||||
* executor (\a PIThreadPoolExecutor, \a PIThreadPoolLoop)
|
||||
* blocking dequeue (\a PIBlockingDequeue)
|
||||
* timer (\a PITimer)
|
||||
* tiling console (with widgets) (\a PIScreen)
|
||||
* timer (\a PITimer)
|
||||
* Tiling console (with widgets) (\a PIScreen)
|
||||
* simple text rows
|
||||
* scroll bar
|
||||
* list
|
||||
@@ -37,7 +37,8 @@
|
||||
* peering net node (\a PIPeer)
|
||||
* connection quality diagnotic (\a PIDiagnostics)
|
||||
* Run-time libraries
|
||||
* abstract (\a PILibrary)
|
||||
* external process (\a PIProcess)
|
||||
* external library (\a PILibrary)
|
||||
* plugin (\a PIPluginLoader)
|
||||
* Mathematics
|
||||
* complex numbers
|
||||
@@ -49,24 +50,33 @@
|
||||
* CRC checksum (\a PICRC)
|
||||
* Fourier transform (\a PIFFTW, \a PIFFT)
|
||||
* expression evaluator (\a PIEvaluator)
|
||||
* command-line arguments parser (\a PICLI)
|
||||
* process (\a PIProcess)
|
||||
* Application-level
|
||||
* command-line arguments parser (\a PICLI)
|
||||
* system resources monitoring (\a PISystemMonitor)
|
||||
* single-instance application control (\a PISingleApplication)
|
||||
* high-level log (\a PILog)
|
||||
* translation support (\a PITranslator)
|
||||
* State machine ([By stantard](https://www.w3.org/TR/scxml/)) (\a PIStateMachine)
|
||||
* High-level TCP client-server
|
||||
* server (\a PIClientServer::Server, \a PIClientServer::ServerClient)
|
||||
* client (\a PIClientServer::Client)
|
||||
* Crypt support (\a PICrypt, \a PIAuth)
|
||||
|
||||
\~russian
|
||||
|
||||
* общение с консолью (\a PICout)
|
||||
* контейнеры (\a PIVector, \a PIDeque, \a PIVector2D, \a PIStack, \a PIQueue, \a PIMap, \a PISet)
|
||||
* байтовый массив (\a PIByteArray)
|
||||
* сериализация (\a PIBinaryStream, \a PITextStream, \a PIIOBinaryStream, \a PIIOTextStream, \a PIChunkStream)
|
||||
* строка (\a PIConstChars, \a PIString, \a PIStringList)
|
||||
* базовый объект (события и обработчики) (\a PIObject)
|
||||
* многопоточность
|
||||
* Общение с консолью (\a PICout)
|
||||
* Контейнеры (\a PIVector, \a PIDeque, \a PIVector2D, \a PIStack, \a PIQueue, \a PIMap, \a PISet)
|
||||
* Байтовый массив (\a PIByteArray)
|
||||
* Сериализация (\a PIBinaryStream, \a PITextStream, \a PIIOBinaryStream, \a PIIOTextStream, \a PIChunkStream, \a PIJSON)
|
||||
* Строка (\a PIConstChars, \a PIString, \a PIStringList)
|
||||
* Базовый объект (события и обработчики) (\a PIObject)
|
||||
* Многопоточность
|
||||
* поток (\a PIThread)
|
||||
* блокировки (\a PIMutex, \a PISpinlock)
|
||||
* исполнитель (\a PIThreadPoolExecutor)
|
||||
* блокировки (\a PIMutex, \a PISpinlock, \a PIConditionVariable, \a PISemaphore, \a PIReadWriteLock)
|
||||
* исполнитель (\a PIThreadPoolExecutor, \a PIThreadPoolLoop)
|
||||
* блокирующая очередь (\a PIBlockingDequeue)
|
||||
* таймер (\a PITimer)
|
||||
* тайлинговая консоль (с виджетами) (\a PIScreen)
|
||||
* таймер (\a PITimer)
|
||||
* Тайлинговая консоль (с виджетами) (\a PIScreen)
|
||||
* простой вывод строк
|
||||
* скроллбар
|
||||
* лист
|
||||
@@ -76,7 +86,7 @@
|
||||
* прогрессбар
|
||||
* вывод PICout
|
||||
* текстовый ввод
|
||||
* устройства ввода/вывода
|
||||
* Устройства ввода/вывода
|
||||
* базовый класс (\a PIIODevice)
|
||||
* файл (\a PIFile)
|
||||
* последовательный порт (\a PISerial)
|
||||
@@ -87,8 +97,9 @@
|
||||
* сложное составное устройство (\a PIConnection)
|
||||
* пиринговая сеть (\a PIPeer)
|
||||
* диагностика качества связи (\a PIDiagnostics)
|
||||
* поддержка библиотек времени выполнения
|
||||
* базовая функциональность (\a PILibrary)
|
||||
* Поддержка библиотек времени выполнения
|
||||
* внешний процесс (\a PIProcess)
|
||||
* внешняя библиотека (\a PILibrary)
|
||||
* плагин (\a PIPluginLoader)
|
||||
* Математика
|
||||
* комплексные числа
|
||||
@@ -100,5 +111,14 @@
|
||||
* CRC контрольная сумма (\a PICRC)
|
||||
* преобразования Фурье (\a PIFFTW, \a PIFFT)
|
||||
* вычислитель выражений (\a PIEvaluator)
|
||||
* парсер аргументов командной строки (\a PICLI)
|
||||
* процесс (\a PIProcess)
|
||||
* Уровень приложения
|
||||
* парсер аргументов командной строки (\a PICLI)
|
||||
* мониторинг ресурсов системы (\a PISystemMonitor)
|
||||
* контроль одного экземпляра приложения (\a PISingleApplication)
|
||||
* высокоуровневый лог (\a PILog)
|
||||
* поддержка перевода (\a PITranslator)
|
||||
* Машина состояний ([По стандарту](https://www.w3.org/TR/scxml/)) (\a PIStateMachine)
|
||||
* Высокоуровневый TCP клиент-сервер
|
||||
* сервер (\a PIClientServer::Server, \a PIClientServer::ServerClient)
|
||||
* клиент (\a PIClientServer::Client)
|
||||
* Поддержка шифрования (\a PICrypt, \a PIAuth)
|
||||
|
||||
319
libs/http_server/microhttpd_server_p.cpp
Normal file
319
libs/http_server/microhttpd_server_p.cpp
Normal file
@@ -0,0 +1,319 @@
|
||||
#include "microhttpd_server_p.h"
|
||||
|
||||
#include "piliterals_string.h"
|
||||
#include "piliterals_time.h"
|
||||
|
||||
#include <microhttpd.h>
|
||||
|
||||
|
||||
struct MicrohttpdServerConnection {
|
||||
bool ready();
|
||||
int send_reply(const MicrohttpdServer::Reply & r);
|
||||
int send_error();
|
||||
|
||||
bool done = false;
|
||||
MicrohttpdServer::Method method = MicrohttpdServer::Method::Unknown;
|
||||
PIString path;
|
||||
PIByteArray body;
|
||||
PIMap<PIString, PIString> headers, args, post;
|
||||
MHD_Connection * connection = nullptr;
|
||||
MicrohttpdServer * server = nullptr;
|
||||
MHD_PostProcessor * postprocessor = nullptr;
|
||||
};
|
||||
|
||||
|
||||
bool MicrohttpdServerConnection::ready() {
|
||||
if (!server) return false;
|
||||
if (done) return true;
|
||||
done = true;
|
||||
MicrohttpdServer::Reply rep;
|
||||
if (method == MicrohttpdServer::Method::Get) {
|
||||
if (path == "/favicon.ico"_a) {
|
||||
// piCout << "send favicon" << server->favicon.size() << "bytes";
|
||||
rep.setBody(server->favicon);
|
||||
send_reply(rep);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// piCout << "ready" << (int)method << path;
|
||||
MicrohttpdServer::Request req;
|
||||
req.method = method;
|
||||
req.path = path;
|
||||
req.body = body;
|
||||
req.headers = headers;
|
||||
req.args = args;
|
||||
rep.setCode(MHD_HTTP_BAD_REQUEST);
|
||||
if (server->callback) rep = server->callback(req);
|
||||
rep.addFixedHeaders();
|
||||
send_reply(rep);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int MicrohttpdServerConnection::send_reply(const MicrohttpdServer::Reply & r) {
|
||||
MHD_Response * response = MHD_create_response_from_buffer(r.body.size(), (void *)r.body.data(), MHD_RESPMEM_MUST_COPY);
|
||||
if (!response) {
|
||||
// piCout << "null response" << r.body.size() << (void *)r.body.data();
|
||||
return MHD_NO;
|
||||
}
|
||||
auto it = r.headers.makeIterator();
|
||||
while (it.next())
|
||||
MHD_add_response_header(response, it.key().dataAscii(), it.value().dataUTF8());
|
||||
// piCout << "status" << r.code;
|
||||
int ret = MHD_queue_response(connection, r.code, response);
|
||||
MHD_destroy_response(response);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int MicrohttpdServerConnection::send_error() {
|
||||
return MHD_queue_response(connection, MHD_HTTP_BAD_REQUEST, MHD_create_response_from_buffer(0, nullptr, MHD_RESPMEM_MUST_COPY));
|
||||
}
|
||||
|
||||
|
||||
void log_callback(void * cls, const char * fm, va_list ap) {
|
||||
MicrohttpdServer * server = (MicrohttpdServer *)cls;
|
||||
piCout << "log" << server;
|
||||
if (!server) return;
|
||||
piCout << server << fm << ap;
|
||||
}
|
||||
|
||||
|
||||
int iterate_post(void * conn_cls,
|
||||
MHD_ValueKind kind,
|
||||
const char * key,
|
||||
const char * filename,
|
||||
const char * content_type,
|
||||
const char * transfer_encoding,
|
||||
const char * data,
|
||||
uint64_t off,
|
||||
size_t size) {
|
||||
MicrohttpdServerConnection * conn = (MicrohttpdServerConnection *)conn_cls;
|
||||
if (!conn) return MHD_NO;
|
||||
conn->post[PIString::fromUTF8(key)] = PIString::fromUTF8(data);
|
||||
return MHD_YES;
|
||||
}
|
||||
|
||||
|
||||
void request_completed(void * cls, MHD_Connection * connection, void ** con_cls, MHD_RequestTerminationCode toe) {
|
||||
MicrohttpdServerConnection *& conn((MicrohttpdServerConnection *&)(*con_cls));
|
||||
// piCout << "request_completed" << conn << conn->headers << conn->post << '"' << conn->body << '"';
|
||||
if (!conn) return;
|
||||
if (conn->method == MicrohttpdServer::Method::Post && conn->postprocessor) MHD_destroy_post_processor(conn->postprocessor);
|
||||
conn->ready();
|
||||
piDeleteSafety(conn);
|
||||
}
|
||||
|
||||
|
||||
int header_iterate(void * cls, MHD_ValueKind kind, const char * key, const char * value) {
|
||||
MicrohttpdServerConnection * conn = (MicrohttpdServerConnection *)cls;
|
||||
if (!conn) return MHD_NO;
|
||||
conn->headers[PIString::fromUTF8(key)] = PIString::fromUTF8(value);
|
||||
return MHD_YES;
|
||||
}
|
||||
|
||||
|
||||
int args_iterate(void * cls, MHD_ValueKind kind, const char * key, const char * value) {
|
||||
MicrohttpdServerConnection * conn = (MicrohttpdServerConnection *)cls;
|
||||
if (!conn) return MHD_NO;
|
||||
conn->args[PIString::fromUTF8(key)] = PIString::fromUTF8(value);
|
||||
return MHD_YES;
|
||||
}
|
||||
|
||||
|
||||
int answer_to_connection(void * cls,
|
||||
MHD_Connection * connection,
|
||||
const char * url,
|
||||
const char * method,
|
||||
const char * version,
|
||||
const char * upload_data,
|
||||
size_t * upload_data_size,
|
||||
void ** con_cls) {
|
||||
MicrohttpdServer * server = (MicrohttpdServer *)cls;
|
||||
|
||||
MicrohttpdServer::Method m = MicrohttpdServer::Method::Unknown;
|
||||
|
||||
if (0 == strcmp(method, "GET"))
|
||||
m = MicrohttpdServer::Method::Get;
|
||||
else if (0 == strcmp(method, "POST"))
|
||||
m = MicrohttpdServer::Method::Post;
|
||||
else if (0 == strcmp(method, "HEAD"))
|
||||
m = MicrohttpdServer::Method::Head;
|
||||
else if (0 == strcmp(method, "PUT"))
|
||||
m = MicrohttpdServer::Method::Put;
|
||||
else if (0 == strcmp(method, "DELETE"))
|
||||
m = MicrohttpdServer::Method::Delete;
|
||||
else if (0 == strcmp(method, "CONNECT"))
|
||||
m = MicrohttpdServer::Method::Connect;
|
||||
else if (0 == strcmp(method, "OPTIONS"))
|
||||
m = MicrohttpdServer::Method::Options;
|
||||
else if (0 == strcmp(method, "TRACE"))
|
||||
m = MicrohttpdServer::Method::Trace;
|
||||
else if (0 == strcmp(method, "PATCH"))
|
||||
m = MicrohttpdServer::Method::Patch;
|
||||
|
||||
if (m == MicrohttpdServer::Method::Unknown) {
|
||||
piCout << "[MicrohttpdServer]"
|
||||
<< "Warning:"
|
||||
<< "Unknown method!";
|
||||
return MHD_NO;
|
||||
}
|
||||
|
||||
// piCout << "answer" << url << method << server;
|
||||
MicrohttpdServerConnection *& conn((MicrohttpdServerConnection *&)(*con_cls));
|
||||
if (!conn) {
|
||||
conn = new MicrohttpdServerConnection();
|
||||
conn->connection = connection;
|
||||
conn->server = server;
|
||||
conn->path = PIString::fromUTF8(url);
|
||||
conn->method = m;
|
||||
MHD_get_connection_values(connection, MHD_HEADER_KIND, (MHD_KeyValueIterator)header_iterate, *con_cls);
|
||||
MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, (MHD_KeyValueIterator)args_iterate, *con_cls);
|
||||
|
||||
if (m == MicrohttpdServer::Method::Post) {
|
||||
// qDebug() << "new POST" << *upload_data_size;
|
||||
conn->postprocessor = MHD_create_post_processor(connection, 65536, (MHD_PostDataIterator)iterate_post, (void *)conn);
|
||||
}
|
||||
return MHD_YES;
|
||||
}
|
||||
|
||||
switch (m) {
|
||||
case MicrohttpdServer::Method::Get:
|
||||
if (!conn->ready()) return conn->send_error();
|
||||
return MHD_YES;
|
||||
case MicrohttpdServer::Method::Post:
|
||||
// qDebug() << "add POST" << *upload_data_size << PIString::fromUtf8(upload_data, *upload_data_size);
|
||||
if (*upload_data_size) {
|
||||
conn->body.append(upload_data, *upload_data_size);
|
||||
if (conn->postprocessor) MHD_post_process(conn->postprocessor, upload_data, *upload_data_size);
|
||||
*upload_data_size = 0;
|
||||
return MHD_YES;
|
||||
} else {
|
||||
// qDebug() << "answer ok";
|
||||
if (!conn->ready()) return conn->send_error();
|
||||
return MHD_YES;
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
return conn->send_error();
|
||||
}
|
||||
|
||||
|
||||
PRIVATE_DEFINITION_START(MicrohttpdServer)
|
||||
MHD_Daemon * daemon;
|
||||
PRIVATE_DEFINITION_END(MicrohttpdServer)
|
||||
|
||||
|
||||
MicrohttpdServer::MicrohttpdServer() {
|
||||
PRIVATE->daemon = nullptr;
|
||||
opts[Option::ConnectionLimit] = FD_SETSIZE - 4;
|
||||
opts[Option::ConnectionTimeout] = 0_s;
|
||||
}
|
||||
|
||||
|
||||
MicrohttpdServer::~MicrohttpdServer() {
|
||||
stop();
|
||||
}
|
||||
|
||||
|
||||
void MicrohttpdServer::setOption(Option o, PIVariant v) {
|
||||
opts[o] = std::move(v);
|
||||
}
|
||||
|
||||
|
||||
void MicrohttpdServer::setFavicon(const PIByteArray & im) {
|
||||
favicon = im;
|
||||
}
|
||||
|
||||
|
||||
bool MicrohttpdServer::listen(PINetworkAddress addr) {
|
||||
stop();
|
||||
uint flags = 0;
|
||||
#if MHD_VERSION <= 0x00095100
|
||||
flags |= MHD_USE_POLL_INTERNALLY;
|
||||
#else
|
||||
flags |= MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD;
|
||||
#endif
|
||||
if (opts.value(Option::HTTPSEnabled).toBool()) flags |= MHD_USE_TLS;
|
||||
mem_key = opts.value(Option::HTTPSMemKey).toByteArray();
|
||||
if (mem_key.isNotEmpty()) mem_key.append(0);
|
||||
mem_cert = opts.value(Option::HTTPSMemCert).toByteArray();
|
||||
if (mem_cert.isNotEmpty()) mem_cert.append(0);
|
||||
key_pass = opts.value(Option::HTTPSKeyPassword).toByteArray();
|
||||
if (key_pass.isNotEmpty()) key_pass.append(0);
|
||||
sockaddr_in sa_addr;
|
||||
memset(&sa_addr, 0, sizeof(sa_addr));
|
||||
sa_addr.sin_port = htons(addr.port());
|
||||
sa_addr.sin_addr.s_addr = addr.ip();
|
||||
sa_addr.sin_family = AF_INET;
|
||||
PIVector<MHD_OptionItem> options;
|
||||
options.append({MHD_OPTION_EXTERNAL_LOGGER, (intptr_t)log_callback, this});
|
||||
options.append({MHD_OPTION_NOTIFY_COMPLETED, (intptr_t)request_completed, nullptr});
|
||||
options.append({MHD_OPTION_CONNECTION_LIMIT, opts.value(Option::ConnectionLimit).toInt(), nullptr});
|
||||
options.append({MHD_OPTION_CONNECTION_TIMEOUT, piRound(opts.value(Option::ConnectionTimeout).toSystemTime().toSeconds()), nullptr});
|
||||
options.append({MHD_OPTION_SOCK_ADDR, 0, &sa_addr});
|
||||
if (opts.value(Option::HTTPSEnabled).toBool()) {
|
||||
options.append({MHD_OPTION_HTTPS_MEM_KEY, 0, mem_key.data()});
|
||||
options.append({MHD_OPTION_HTTPS_MEM_CERT, 0, mem_cert.data()});
|
||||
options.append({MHD_OPTION_HTTPS_KEY_PASSWORD, 0, key_pass.data()});
|
||||
}
|
||||
options.append({MHD_OPTION_END, 0, nullptr});
|
||||
PRIVATE->daemon = MHD_start_daemon(flags,
|
||||
addr.port(),
|
||||
nullptr,
|
||||
nullptr,
|
||||
(MHD_AccessHandlerCallback)answer_to_connection,
|
||||
this,
|
||||
MHD_OPTION_ARRAY,
|
||||
options.data(),
|
||||
MHD_OPTION_END);
|
||||
return isListen();
|
||||
}
|
||||
|
||||
|
||||
bool MicrohttpdServer::isListen() const {
|
||||
return PRIVATE->daemon;
|
||||
}
|
||||
|
||||
|
||||
void MicrohttpdServer::stop() {
|
||||
if (PRIVATE->daemon) {
|
||||
MHD_stop_daemon(PRIVATE->daemon);
|
||||
PRIVATE->daemon = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MicrohttpdServer::Reply::addHeader(const PIString & header, const PIString & value) {
|
||||
headers[header] = value;
|
||||
}
|
||||
|
||||
|
||||
void MicrohttpdServer::Reply::removeHeader(const PIString & header) {
|
||||
headers.remove(header);
|
||||
}
|
||||
|
||||
|
||||
void MicrohttpdServer::Reply::setBody(const PIByteArray & b) {
|
||||
body = b;
|
||||
}
|
||||
|
||||
|
||||
void MicrohttpdServer::Reply::setCode(int c) {
|
||||
code = c;
|
||||
}
|
||||
|
||||
|
||||
void MicrohttpdServer::Reply::addFixedHeaders() {
|
||||
if (!headers.contains(MHD_HTTP_HEADER_CONTENT_TYPE)) {
|
||||
if (body.isNotEmpty()) {
|
||||
if (body.startsWith(PIByteArray::fromAscii("<!DOCTYPE html>")))
|
||||
addHeader(MHD_HTTP_HEADER_CONTENT_TYPE, "text/html; charset=utf-8");
|
||||
else if (body[0] == '[' || body[0] == '{')
|
||||
addHeader(MHD_HTTP_HEADER_CONTENT_TYPE, "application/json; charset=utf-8");
|
||||
}
|
||||
}
|
||||
addHeader(MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, "*");
|
||||
}
|
||||
49
libs/http_server/pihttpserver.cpp
Normal file
49
libs/http_server/pihttpserver.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#include "pihttpserver.h"
|
||||
|
||||
#include "piliterals_string.h"
|
||||
|
||||
|
||||
PIHTTPServer::PIHTTPServer() {
|
||||
setRequestCallback([this](const MicrohttpdServer::Request & r) -> MicrohttpdServer::Reply {
|
||||
MicrohttpdServer::Reply rep;
|
||||
rep.setCode(404);
|
||||
auto in_path = r.path.split("/");
|
||||
in_path.removeAll("");
|
||||
auto it = functions.makeReverseIterator();
|
||||
while (it.next()) {
|
||||
if (it.value().function) {
|
||||
if (it.value().match(in_path)) {
|
||||
rep = it.value().function(r);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
auto hit = reply_headers.makeIterator();
|
||||
while (hit.next())
|
||||
rep.addHeader(hit.key(), hit.value());
|
||||
return rep;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
PIHTTPServer::~PIHTTPServer() {
|
||||
stop();
|
||||
}
|
||||
|
||||
|
||||
void PIHTTPServer::registerPath(const PIString & path, RequestFunction functor) {
|
||||
auto & ep(functions[path]);
|
||||
ep.path = path.split("/");
|
||||
ep.function = functor;
|
||||
ep.path.removeAll("");
|
||||
}
|
||||
|
||||
|
||||
bool PIHTTPServer::Endpoint::match(const PIStringList & in_path) const {
|
||||
if (in_path.size() != path.size()) return false;
|
||||
for (int i = 0; i < path.size_s(); ++i) {
|
||||
if (path[i] == "*"_a) continue;
|
||||
if (path[i] != in_path[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -132,9 +132,7 @@ inline void piSwapBinary(const void *& f, const void *& s) {
|
||||
//! \~english Example:\n \snippet piincludes.cpp compareBinary
|
||||
//! \~russian Пример:\n \snippet piincludes.cpp compareBinary
|
||||
inline bool piCompareBinary(const void * f, const void * s, size_t size) {
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
if (((const uchar *)f)[i] != ((const uchar *)s)[i]) return false;
|
||||
return true;
|
||||
return 0 == memcmp(f, s, size);
|
||||
}
|
||||
|
||||
//! \~\brief
|
||||
|
||||
83
libs/main/http_server/microhttpd_server_p.h
Normal file
83
libs/main/http_server/microhttpd_server_p.h
Normal file
@@ -0,0 +1,83 @@
|
||||
#ifndef MICROHTTPD_SERVER_P_H
|
||||
#define MICROHTTPD_SERVER_P_H
|
||||
|
||||
#include "pibase.h"
|
||||
#include "piobject.h"
|
||||
#include "pip_http_server_export.h"
|
||||
|
||||
struct MicrohttpdServerConnection;
|
||||
|
||||
class PIP_HTTP_SERVER_EXPORT MicrohttpdServer: public PIObject {
|
||||
PIOBJECT(MicrohttpdServer)
|
||||
friend struct MicrohttpdServerConnection;
|
||||
|
||||
public:
|
||||
MicrohttpdServer();
|
||||
virtual ~MicrohttpdServer();
|
||||
|
||||
enum class Method {
|
||||
Unknown,
|
||||
Get,
|
||||
Head,
|
||||
Post,
|
||||
Put,
|
||||
Delete,
|
||||
Connect,
|
||||
Options,
|
||||
Trace,
|
||||
Patch
|
||||
};
|
||||
enum class Option {
|
||||
ConnectionLimit, // uint
|
||||
ConnectionTimeout, // uint, sec
|
||||
HTTPSEnabled, // bool
|
||||
HTTPSMemKey, // const char * to key.pem data
|
||||
HTTPSMemCert, // const char * to cert.pem data
|
||||
HTTPSKeyPassword // const char * to passwd for key.pem
|
||||
};
|
||||
|
||||
struct PIP_HTTP_SERVER_EXPORT Request {
|
||||
MicrohttpdServer::Method method;
|
||||
PIString path;
|
||||
PIByteArray body;
|
||||
PIMap<PIString, PIString> headers;
|
||||
PIMap<PIString, PIString> args;
|
||||
};
|
||||
|
||||
class PIP_HTTP_SERVER_EXPORT Reply {
|
||||
friend struct MicrohttpdServerConnection;
|
||||
|
||||
public:
|
||||
void addHeader(const PIString & header, const PIString & value);
|
||||
void removeHeader(const PIString & header);
|
||||
void setBody(const PIByteArray & b);
|
||||
void setCode(int c);
|
||||
|
||||
private:
|
||||
void addFixedHeaders();
|
||||
int code = 200;
|
||||
PIByteArray body;
|
||||
PIMap<PIString, PIString> headers;
|
||||
};
|
||||
|
||||
void setOption(Option o, PIVariant v);
|
||||
void setFavicon(const PIByteArray & im);
|
||||
|
||||
bool listen(PINetworkAddress addr);
|
||||
bool listenAll(ushort port) { return listen({0, port}); }
|
||||
bool isListen() const;
|
||||
void stop();
|
||||
|
||||
void setRequestCallback(std::function<Reply(Request)> c) { callback = c; }
|
||||
|
||||
private:
|
||||
PRIVATE_DECLARATION(PIP_HTTP_SERVER_EXPORT)
|
||||
|
||||
PIByteArray favicon;
|
||||
PIMap<Option, PIVariant> opts;
|
||||
std::function<Reply(Request)> callback;
|
||||
PIByteArray mem_key, mem_cert, key_pass;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
33
libs/main/http_server/pihttpserver.h
Normal file
33
libs/main/http_server/pihttpserver.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#ifndef PIHTTPSERVER_H
|
||||
#define PIHTTPSERVER_H
|
||||
|
||||
#include "microhttpd_server_p.h"
|
||||
#include "pip_http_server_export.h"
|
||||
|
||||
class PIP_HTTP_SERVER_EXPORT PIHTTPServer: public MicrohttpdServer {
|
||||
PIOBJECT_SUBCLASS(PIHTTPServer, MicrohttpdServer)
|
||||
|
||||
public:
|
||||
PIHTTPServer();
|
||||
virtual ~PIHTTPServer();
|
||||
|
||||
using RequestFunction = std::function<MicrohttpdServer::Reply(const MicrohttpdServer::Request &)>;
|
||||
|
||||
void registerPath(const PIString & path, RequestFunction functor);
|
||||
|
||||
void addReplyHeader(const PIString & name, const PIString & value) { reply_headers[name] = value; }
|
||||
void removeReplyHeader(const PIString & name) { reply_headers.remove(name); }
|
||||
void clearReplyHeaders() { reply_headers.clear(); }
|
||||
|
||||
private:
|
||||
struct Endpoint {
|
||||
bool match(const PIStringList & in_path) const;
|
||||
PIStringList path;
|
||||
RequestFunction function;
|
||||
};
|
||||
PIMap<PIString, PIString> reply_headers;
|
||||
PIMap<PIString, Endpoint> functions;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@@ -553,6 +553,17 @@ uint PIString::hash() const {
|
||||
}
|
||||
|
||||
|
||||
PIByteArray PIString::toAscii() const {
|
||||
if (isEmpty()) return PIByteArray();
|
||||
PIByteArray ret;
|
||||
ret.resize(size());
|
||||
for (int i = 0; i < size_s(); ++i) {
|
||||
ret[i] = uchar(at(i).ch);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PIByteArray PIString::toSystem() const {
|
||||
if (isEmpty()) return PIByteArray();
|
||||
buildData(__syslocname__);
|
||||
|
||||
@@ -1107,6 +1107,10 @@ public:
|
||||
//! \~russian Тоже самое, что \a toUTF8().
|
||||
PIByteArray toByteArray() const { return toUTF8(); }
|
||||
|
||||
//! \~english Returns \a PIByteArray contains \a dataAscii() of this string without terminating null-char.
|
||||
//! \~russian Возвращает \a PIByteArray содержащий \a dataAscii() строки без завершающего нулевого байта.
|
||||
PIByteArray toAscii() const;
|
||||
|
||||
//! \~english Returns \a PIByteArray contains \a data() of this string without terminating null-char.
|
||||
//! \~russian Возвращает \a PIByteArray содержащий \a data() строки без завершающего нулевого байта.
|
||||
PIByteArray toSystem() const;
|
||||
|
||||
@@ -32,24 +32,38 @@
|
||||
template<typename T>
|
||||
class PIP_EXPORT PIProtectedVariable {
|
||||
public:
|
||||
void set(const T & v) {
|
||||
PIMutexLocker _ml(mutex);
|
||||
var = v;
|
||||
}
|
||||
void set(T && v) {
|
||||
//! \~english Sets value to \"v\"
|
||||
//! \~russian Устанавливает значение как \"v\"
|
||||
void set(T v) {
|
||||
PIMutexLocker _ml(mutex);
|
||||
var = std::move(v);
|
||||
}
|
||||
|
||||
//! \~english Returns copy of value
|
||||
//! \~russian Возвращает копию значения
|
||||
T get() const {
|
||||
PIMutexLocker _ml(mutex);
|
||||
return var;
|
||||
}
|
||||
|
||||
//! \~english Lock mutex and returns reference of value
|
||||
//! \~russian Блокирует мьютекс и возвращает ссылку на значение
|
||||
T & lock() {
|
||||
mutex.lock();
|
||||
return var;
|
||||
}
|
||||
|
||||
//! \~english Unlock mutex
|
||||
//! \~russian Разблокирует мьютекс
|
||||
void unlock() { mutex.unlock(); }
|
||||
|
||||
//! \~english Sets value to \"v\"
|
||||
//! \~russian Устанавливает значение как \"v\"
|
||||
PIProtectedVariable<T> & operator=(T v) {
|
||||
set(std::move(v));
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
mutable PIMutex mutex;
|
||||
|
||||
234
libs/main/thread/pireadwritelock.cpp
Normal file
234
libs/main/thread/pireadwritelock.cpp
Normal file
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
PIReadWriteLock, PIReadLocker, PIWriteLocker
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//! \addtogroup Thread
|
||||
//! \{
|
||||
//! \class PIReadWriteLock pireadwritelock.h
|
||||
//!
|
||||
//! \~\brief
|
||||
//! \~english Read/Write lock
|
||||
//! \~russian Блокировка чтения/записи
|
||||
//!
|
||||
//!
|
||||
//! \~\details
|
||||
//! \~english \section PIReadWriteLock_sec0 Synopsis
|
||||
//! \~russian \section PIReadWriteLock_sec0 Краткий обзор
|
||||
//!
|
||||
//! \~english
|
||||
//! %PIReadWriteLock provides more complex critical code section defence between several threads.
|
||||
//! %PIReadWriteLock permit only one thread to modify data, besides multiple thread can read data
|
||||
//! simultaneously. Conatains methods:
|
||||
//! \a lockWrite(), \a tryLockWrite(), \a unlockWrite(), \a lockRead(), \a tryLockRead() and \a unlockRead().
|
||||
//!
|
||||
//! For automatic read and write locks use \a PIReadLocker and \a PIWriteLocker.
|
||||
//!
|
||||
//! \~russian
|
||||
//! %PIReadWriteLock предоставляет более сложную межпотоковую защиту критических секций кода.
|
||||
//! %PIReadWriteLock разрешает только одному потоку изменять данные, в то время как читать данные
|
||||
//! могут одновременно несколько потоков.
|
||||
//! Содержит методы:
|
||||
//! \a lockWrite(), \a tryLockWrite(), \a unlockWrite(), \a lockRead(), \a tryLockRead() и \a unlockRead().
|
||||
//!
|
||||
//! Для автоматической блокировки на чтение и запись используйте \a PIReadLocker и \a PIWriteLocker.
|
||||
//!
|
||||
//! \~english \section PIReadWriteLock_sec1 Usage
|
||||
//! \~russian \section PIReadWriteLock_sec1 Использование
|
||||
//!
|
||||
//! \~english
|
||||
//! When one thread lock for write with \a lockWrite(), all other threads (read and write) can`t access data until this thread
|
||||
//! call \a unlockWrite(). In this way, write lock works similar to mutex.
|
||||
//!
|
||||
//! On the other hand, several threads can simultaneously read data. In other words, \a lockRead() increase read threads counter
|
||||
//! and \a unlockRead() decrease. But thread can`t read data while other thread locked for write.
|
||||
//!
|
||||
//! \~russian
|
||||
//! Когда один поток блокирует запись с помощью функции \a lockWrite(), все остальные потоки (чтение и запись) не смогут получить
|
||||
//! доступ к данным, пока этот поток не вызовет \a unlockWrite(). Таким образом, блокировка записи работает аналогично мьютексу.
|
||||
//!
|
||||
//! С другой стороны, несколько потоков могут одновременно считывать данные. Другими словами, функция \a lockRead() увеличивает счетчик
|
||||
//! потоков чтения, а функция \a unlockRead() уменьшает. Но поток не может считывать данные, пока другой поток заблокирован для записи.
|
||||
//!
|
||||
//! \~\code
|
||||
//! \endcode
|
||||
//! \}
|
||||
|
||||
|
||||
//! \addtogroup Thread
|
||||
//! \{
|
||||
//! \class PIReadLocker pireadwritelock.h
|
||||
//!
|
||||
//! \~\brief
|
||||
//! \~english %PIReadWriteLock read autolocker
|
||||
//! \~russian Автоблокировщик на чтение %PIReadWriteLock
|
||||
//!
|
||||
//!
|
||||
//! \~\details
|
||||
//!
|
||||
//! \~english
|
||||
//! When a %PIReadLocker object is created, it lock %PIReadWriteLock for read, if "condition" \c true.
|
||||
//! When control leaves the scope in which the %PIReadLocker object was created,
|
||||
//! the %PIReadLocker is destructed and unlock for read, if "condition" was \c true.
|
||||
//!
|
||||
//! If "condition" \c false this class do nothing.
|
||||
//!
|
||||
//! The %PIReadLocker class is non-copyable.
|
||||
//!
|
||||
//! \~russian
|
||||
//! При создании экземпляра %PIReadLocker он блокирует %PIReadWriteLock на чтение, если "condition" \c true.
|
||||
//! Когда выполнение покидает область жизни объекта, вызывается его деструктор и освобождается блокировка на чтение,
|
||||
//! если "condition" был \c true.
|
||||
//!
|
||||
//! Если "condition" \c false, то этот объект ничего не делает.
|
||||
//!
|
||||
//! Класс %PIReadLocker некопируемый.
|
||||
//!
|
||||
//! \~\code
|
||||
//! // critical section start
|
||||
//! {
|
||||
//! PIReadLocker locker(rw_lock);
|
||||
//! // ... your read code here
|
||||
//! }
|
||||
//! // critical section end
|
||||
//! \endcode
|
||||
//! \}
|
||||
|
||||
|
||||
//! \addtogroup Thread
|
||||
//! \{
|
||||
//! \class PIWriteLocker pireadwritelock.h
|
||||
//!
|
||||
//! \~\brief
|
||||
//! \~english %PIReadWriteLock write autolocker
|
||||
//! \~russian Автоблокировщик на запись %PIReadWriteLock
|
||||
//!
|
||||
//!
|
||||
//! \~\details
|
||||
//!
|
||||
//! \~english
|
||||
//! When a %PIWriteLocker object is created, it lock %PIReadWriteLock for write, if "condition" \c true.
|
||||
//! When control leaves the scope in which the %PIWriteLocker object was created,
|
||||
//! the %PIWriteLocker is destructed and unlock for write, if "condition" was \c true.
|
||||
//!
|
||||
//! If "condition" \c false this class do nothing.
|
||||
//!
|
||||
//! The %PIWriteLocker class is non-copyable.
|
||||
//!
|
||||
//! \~russian
|
||||
//! При создании экземпляра %PIWriteLocker он блокирует %PIReadWriteLock на запись, если "condition" \c true.
|
||||
//! Когда выполнение покидает область жизни объекта, вызывается его деструктор и освобождается блокировка на запись,
|
||||
//! если "condition" был \c true.
|
||||
//!
|
||||
//! Если "condition" \c false, то этот объект ничего не делает.
|
||||
//!
|
||||
//! Класс %PIWriteLocker некопируемый.
|
||||
//!
|
||||
//! \~\code
|
||||
//! // critical section start
|
||||
//! {
|
||||
//! PIWriteLocker locker(rw_lock);
|
||||
//! // ... your write code here
|
||||
//! }
|
||||
//! // critical section end
|
||||
//! \endcode
|
||||
//! \}
|
||||
|
||||
|
||||
#include "pireadwritelock.h"
|
||||
|
||||
|
||||
PIReadWriteLock::PIReadWriteLock() {}
|
||||
|
||||
|
||||
PIReadWriteLock::~PIReadWriteLock() {
|
||||
var.notifyAll();
|
||||
}
|
||||
|
||||
|
||||
void PIReadWriteLock::lockWrite() {
|
||||
PIMutexLocker _ml(mutex);
|
||||
while (reading > 0 || writing) {
|
||||
var.wait(mutex);
|
||||
}
|
||||
writing = true;
|
||||
}
|
||||
|
||||
|
||||
bool PIReadWriteLock::tryLockWrite() {
|
||||
PIMutexLocker _ml(mutex);
|
||||
if (reading > 0 || writing) return false;
|
||||
writing = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool PIReadWriteLock::tryLockWrite(PISystemTime timeout) {
|
||||
PITimeMeasurer tm;
|
||||
PIMutexLocker _ml(mutex);
|
||||
while (reading > 0 || writing) {
|
||||
PISystemTime remain = timeout - tm.elapsed();
|
||||
if (remain.isNegative()) return false;
|
||||
var.waitFor(mutex, remain);
|
||||
}
|
||||
writing = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void PIReadWriteLock::unlockWrite() {
|
||||
PIMutexLocker _ml(mutex);
|
||||
writing = false;
|
||||
var.notifyAll();
|
||||
}
|
||||
|
||||
|
||||
void PIReadWriteLock::lockRead() {
|
||||
PIMutexLocker _ml(mutex);
|
||||
while (writing) {
|
||||
var.wait(mutex);
|
||||
}
|
||||
++reading;
|
||||
}
|
||||
|
||||
|
||||
bool PIReadWriteLock::tryLockRead() {
|
||||
PIMutexLocker _ml(mutex);
|
||||
if (writing) return false;
|
||||
++reading;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool PIReadWriteLock::tryLockRead(PISystemTime timeout) {
|
||||
PITimeMeasurer tm;
|
||||
PIMutexLocker _ml(mutex);
|
||||
while (writing) {
|
||||
PISystemTime remain = timeout - tm.elapsed();
|
||||
if (remain.isNegative()) return false;
|
||||
var.waitFor(mutex, remain);
|
||||
}
|
||||
++reading;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void PIReadWriteLock::unlockRead() {
|
||||
PIMutexLocker _ml(mutex);
|
||||
--reading;
|
||||
var.notifyAll();
|
||||
}
|
||||
130
libs/main/thread/pireadwritelock.h
Normal file
130
libs/main/thread/pireadwritelock.h
Normal file
@@ -0,0 +1,130 @@
|
||||
/*! \file pireadwritelock.h
|
||||
* \ingroup Read/Write lock
|
||||
* \~\brief
|
||||
* \~english Read/Write lock
|
||||
* \~russian Блокировка чтения/записи
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
PIReadWriteLock, PIReadLocker, PIWriteLocker
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIREADWRITELOCK_H
|
||||
#define PIREADWRITELOCK_H
|
||||
|
||||
#include "piconditionvar.h"
|
||||
|
||||
|
||||
class PIP_EXPORT PIReadWriteLock {
|
||||
public:
|
||||
NO_COPY_CLASS(PIReadWriteLock)
|
||||
|
||||
//! \~english Constructs %PIReadWriteLock.
|
||||
//! \~russian Создает %PIReadWriteLock.
|
||||
PIReadWriteLock();
|
||||
|
||||
//! \~english Destroy %PIReadWriteLock.
|
||||
//! \~russian Деструктор %PIReadWriteLock.
|
||||
~PIReadWriteLock();
|
||||
|
||||
|
||||
//! \~english Lock for write. If already locked for write or read, than wait until all locks released.
|
||||
//! \~russian Заблокировать на запись. Если уже заблокировано на запись или чтение, то ждёт освобождения блокировок.
|
||||
void lockWrite();
|
||||
|
||||
//! \~english Try to lock for write. Returns if operation was successfull.
|
||||
//! \~russian Пробует заблокировать на запись. Возвращает успех операции.
|
||||
bool tryLockWrite();
|
||||
|
||||
//! \~english Try to lock for write for \"timeout\". Returns if operation was successfull (timeout has not expired).
|
||||
//! \~russian Пробует заблокировать на запись в течении \"timeout\". Возвращает успех операции (не истек ли тайм-аут).
|
||||
bool tryLockWrite(PISystemTime timeout);
|
||||
|
||||
//! \~english Release lock for write.
|
||||
//! \~russian Освобождает блокировку на запись.
|
||||
void unlockWrite();
|
||||
|
||||
|
||||
//! \~english Lock for read. If already locked for write, than wait until write lock released.
|
||||
//! \~russian Заблокировать на чтение. Если уже заблокировано на запись, то ждёт освобождения записывающей блокировки.
|
||||
void lockRead();
|
||||
|
||||
//! \~english Try to lock for read. Returns if operation was successfull.
|
||||
//! \~russian Пробует заблокировать на чтение. Возвращает успех операции.
|
||||
bool tryLockRead();
|
||||
|
||||
//! \~english Try to lock for read for \"timeout\". Returns if operation was successfull (timeout has not expired).
|
||||
//! \~russian Пробует заблокировать на чтение в течении \"timeout\". Возвращает успех операции (не истек ли тайм-аут).
|
||||
bool tryLockRead(PISystemTime timeout);
|
||||
|
||||
//! \~english Release lock for read.
|
||||
//! \~russian Освобождает блокировку на чтение.
|
||||
void unlockRead();
|
||||
|
||||
private:
|
||||
int reading = 0;
|
||||
bool writing = false;
|
||||
PIMutex mutex;
|
||||
PIConditionVariable var;
|
||||
};
|
||||
|
||||
|
||||
class PIP_EXPORT PIReadLocker {
|
||||
public:
|
||||
NO_COPY_CLASS(PIReadLocker);
|
||||
|
||||
//! \~english Constructs and lock for read %PIReadWriteLock "l" if "condition" is \c true.
|
||||
//! \~russian Создается и блокирует на чтение %PIReadWriteLock "l" если "condition" \c true.
|
||||
PIReadLocker(PIReadWriteLock & l, bool condition = true): rwl(l), cond(condition) {
|
||||
if (cond) rwl.lockRead();
|
||||
}
|
||||
|
||||
//! \~english Release read lock on %PIReadWriteLock if "condition" was \c true.
|
||||
//! \~russian Освобождает блокировку на чтение %PIReadWriteLock если "condition" был \c true.
|
||||
~PIReadLocker() {
|
||||
if (cond) rwl.unlockRead();
|
||||
}
|
||||
|
||||
private:
|
||||
PIReadWriteLock & rwl;
|
||||
bool cond = true;
|
||||
};
|
||||
|
||||
|
||||
class PIP_EXPORT PIWriteLocker {
|
||||
public:
|
||||
NO_COPY_CLASS(PIWriteLocker);
|
||||
|
||||
//! \~english Constructs and lock for write %PIReadWriteLock "l" if "condition" is \c true.
|
||||
//! \~russian Создается и блокирует на запись %PIReadWriteLock "l" если "condition" \c true.
|
||||
PIWriteLocker(PIReadWriteLock & l, bool condition = true): rwl(l), cond(condition) {
|
||||
if (cond) rwl.lockWrite();
|
||||
}
|
||||
|
||||
//! \~english Release write lock on %PIReadWriteLock if "condition" was \c true.
|
||||
//! \~russian Освобождает блокировку на запись %PIReadWriteLock если "condition" был \c true.
|
||||
~PIWriteLocker() {
|
||||
if (cond) rwl.unlockWrite();
|
||||
}
|
||||
|
||||
private:
|
||||
PIReadWriteLock & rwl;
|
||||
bool cond = true;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
152
libs/main/thread/pisemaphore.cpp
Normal file
152
libs/main/thread/pisemaphore.cpp
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
PISemaphore, PISemaphoreLocker
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//! \addtogroup Thread
|
||||
//! \{
|
||||
//! \class PISemaphore pisemaphore.h
|
||||
//!
|
||||
//! \~\brief
|
||||
//! \~english Basic semaphore
|
||||
//! \~russian Простой семафор
|
||||
//!
|
||||
//!
|
||||
//! \~\details
|
||||
//! \~english \section PISemaphore_sec0 Synopsis
|
||||
//! \~russian \section PISemaphore_sec0 Краткий обзор
|
||||
//!
|
||||
//! \~english
|
||||
//! %PISemaphore provides critical code section defence between several threads with resource counting.
|
||||
//! Semaphore contains logic counter and functions to change it:
|
||||
//! \a release(), \a acquire() and \a tryAcquire().
|
||||
//!
|
||||
//! For automatic acquire-release use \a PISemaphoreLocker.
|
||||
//!
|
||||
//! \~russian
|
||||
//! %PISemaphore предоставляет межпотоковую защиту критических секций кода с подсчетом ресурсов.
|
||||
//! Семафор состоит из логического счетчика и методов для его изменения:
|
||||
//! \a release(), \a acquire() and \a tryAcquire().
|
||||
//!
|
||||
//! Для автоматического захвата-освобождения используйте \a PISemaphoreLocker.
|
||||
//!
|
||||
//! \~english \section PISemaphore_sec1 Usage
|
||||
//! \~russian \section PISemaphore_sec1 Использование
|
||||
//!
|
||||
//! \~english
|
||||
//!
|
||||
//! \~russian
|
||||
//!
|
||||
//! \~\code
|
||||
//! \endcode
|
||||
//! \}
|
||||
|
||||
|
||||
//! \addtogroup Thread
|
||||
//! \{
|
||||
//! \class PISemaphoreLocker pisemaphore.h
|
||||
//!
|
||||
//! \~\brief
|
||||
//! \~english %PISemaphore autolocker
|
||||
//! \~russian Автоблокировщик %PISemaphore
|
||||
//!
|
||||
//!
|
||||
//! \~\details
|
||||
//!
|
||||
//! \~english
|
||||
//! When a %PISemaphoreLocker object is created, it attempts to acquire the semaphore resources, if "condition" \c true.
|
||||
//! When control leaves the scope in which the %PISemaphoreLocker object was created,
|
||||
//! the %PISemaphoreLocker is destructed and the resources are released, if "condition" was \c true.
|
||||
//!
|
||||
//! If "condition" \c false this class do nothing.
|
||||
//!
|
||||
//! The %PISemaphoreLocker class is non-copyable.
|
||||
//!
|
||||
//! \~russian
|
||||
//! При создании экземпляра %PISemaphoreLocker захватываются ресурсы семафора, если "condition" \c true.
|
||||
//! Когда выполнение покидает область жизни объекта, вызывается его деструктор и ресурсы
|
||||
//! освобождаются, если "condition" был \c true.
|
||||
//!
|
||||
//! Если "condition" \c false, то этот объект ничего не делает.
|
||||
//!
|
||||
//! Класс %PISemaphoreLocker некопируемый.
|
||||
//!
|
||||
//! \~\code
|
||||
//! // critical section start
|
||||
//! {
|
||||
//! PISemaphoreLocker locker(mutex, 5);
|
||||
//! // ... your code here
|
||||
//! }
|
||||
//! // critical section end
|
||||
//! \endcode
|
||||
//! \}
|
||||
|
||||
|
||||
#include "pisemaphore.h"
|
||||
|
||||
|
||||
PISemaphore::PISemaphore(int initial) {
|
||||
count = initial;
|
||||
}
|
||||
|
||||
|
||||
PISemaphore::~PISemaphore() {
|
||||
var.notifyAll();
|
||||
}
|
||||
|
||||
|
||||
void PISemaphore::acquire(int cnt) {
|
||||
PIMutexLocker _ml(mutex);
|
||||
while (count < cnt) {
|
||||
var.wait(mutex);
|
||||
}
|
||||
count -= cnt;
|
||||
}
|
||||
|
||||
|
||||
bool PISemaphore::tryAcquire(int cnt) {
|
||||
PIMutexLocker _ml(mutex);
|
||||
if (count < cnt) return false;
|
||||
count -= cnt;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool PISemaphore::tryAcquire(int cnt, PISystemTime timeout) {
|
||||
PITimeMeasurer tm;
|
||||
PIMutexLocker _ml(mutex);
|
||||
while (count < cnt) {
|
||||
PISystemTime remain = timeout - tm.elapsed();
|
||||
if (remain.isNegative()) return false;
|
||||
var.waitFor(mutex, remain);
|
||||
}
|
||||
count -= cnt;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void PISemaphore::release(int cnt) {
|
||||
PIMutexLocker _ml(mutex);
|
||||
count += cnt;
|
||||
var.notifyAll();
|
||||
}
|
||||
|
||||
|
||||
int PISemaphore::available() const {
|
||||
PIMutexLocker _ml(mutex);
|
||||
return count;
|
||||
}
|
||||
95
libs/main/thread/pisemaphore.h
Normal file
95
libs/main/thread/pisemaphore.h
Normal file
@@ -0,0 +1,95 @@
|
||||
/*! \file pisemaphore.h
|
||||
* \ingroup Semaphore
|
||||
* \~\brief
|
||||
* \~english Basic semaphore
|
||||
* \~russian Простой семафор
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
PISemaphore, PISemaphoreLocker
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PISEMAPHORE_H
|
||||
#define PISEMAPHORE_H
|
||||
|
||||
#include "piconditionvar.h"
|
||||
|
||||
|
||||
class PIP_EXPORT PISemaphore {
|
||||
public:
|
||||
NO_COPY_CLASS(PISemaphore)
|
||||
|
||||
//! \~english Constructs semaphore with \"initial\" available resources.
|
||||
//! \~russian Создает семафор с \"initial\" начальными свободными ресурсами.
|
||||
PISemaphore(int initial = 0);
|
||||
|
||||
//! \~english Destroy semaphore.
|
||||
//! \~russian Деструктор семафора.
|
||||
~PISemaphore();
|
||||
|
||||
|
||||
//! \~english Acquire \"cnt\" resources. If no available resources, than blocks until they freed.
|
||||
//! \~russian Захватывает \"cnt\" ресурсов. Если свободных ресурсов недостаточно, то блокирует до их появления.
|
||||
void acquire(int cnt = 1);
|
||||
|
||||
//! \~english Try to acquire \"cnt\" resources. Returns if operation was successfull.
|
||||
//! \~russian Пробует захватывает \"cnt\" ресурсов. Возвращает успех захвата.
|
||||
bool tryAcquire(int cnt = 1);
|
||||
|
||||
//! \~english Try to acquire \"cnt\" resources for \"timeout\". Returns if operation was successfull (timeout has not expired).
|
||||
//! \~russian Пробует захватывает \"cnt\" ресурсов в течении \"timeout\". Возвращает успех захвата (не истек ли тайм-аут).
|
||||
bool tryAcquire(int cnt, PISystemTime timeout);
|
||||
|
||||
//! \~english Release \"cnt\" resources.
|
||||
//! \~russian Освобождает \"cnt\" ресурсов.
|
||||
void release(int cnt = 1);
|
||||
|
||||
//! \~english Returns available resources count.
|
||||
//! \~russian Возвращает количество свободных ресурсов.
|
||||
int available() const;
|
||||
|
||||
private:
|
||||
int count = 0;
|
||||
mutable PIMutex mutex;
|
||||
PIConditionVariable var;
|
||||
};
|
||||
|
||||
|
||||
class PIP_EXPORT PISemaphoreLocker {
|
||||
public:
|
||||
NO_COPY_CLASS(PISemaphoreLocker);
|
||||
|
||||
//! \~english Constructs and acquire \"cnt\" resources on semaphore "s" if "condition" is \c true.
|
||||
//! \~russian Создается и захватывает \"cnt\" ресурсов у семафора "s" если "condition" \c true.
|
||||
PISemaphoreLocker(PISemaphore & s, int cnt = 1, bool condition = true): sem(s), count(cnt), cond(condition) {
|
||||
if (cond) sem.acquire(count);
|
||||
}
|
||||
|
||||
//! \~english Release "cnt" resources on semaphore if "condition" was \c true.
|
||||
//! \~russian Освобождает "cnt" ресурсов у семафора если "condition" был \c true.
|
||||
~PISemaphoreLocker() {
|
||||
if (cond) sem.release(count);
|
||||
}
|
||||
|
||||
private:
|
||||
PISemaphore & sem;
|
||||
int count = 1;
|
||||
bool cond = true;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@@ -56,6 +56,8 @@
|
||||
#include "pimutex.h"
|
||||
#include "pipipelinethread.h"
|
||||
#include "piprotectedvariable.h"
|
||||
#include "pireadwritelock.h"
|
||||
#include "pisemaphore.h"
|
||||
#include "pispinlock.h"
|
||||
#include "pithread.h"
|
||||
#include "pithreadnotifier.h"
|
||||
|
||||
@@ -129,6 +129,20 @@ struct base64HelpStruct {
|
||||
// clang-format on
|
||||
|
||||
|
||||
bool PIByteArray::startsWith(const PIByteArray & o) const {
|
||||
if (o.isEmpty()) return false;
|
||||
if (size() < o.size()) return false;
|
||||
return piCompareBinary(data(), o.data(), o.size());
|
||||
}
|
||||
|
||||
|
||||
bool PIByteArray::endsWith(const PIByteArray & o) const {
|
||||
if (o.isEmpty()) return false;
|
||||
if (size() < o.size()) return false;
|
||||
return piCompareBinary(data(size() - o.size()), o.data(), o.size());
|
||||
}
|
||||
|
||||
|
||||
PIByteArray & PIByteArray::convertToBase64() {
|
||||
return *this = toBase64();
|
||||
}
|
||||
@@ -396,6 +410,17 @@ PIByteArray PIByteArray::fromHex(PIString str) {
|
||||
}
|
||||
|
||||
|
||||
PIByteArray PIByteArray::fromAscii(const char * str) {
|
||||
PIByteArray ret;
|
||||
int ind = 0;
|
||||
while (str[ind] != 0) {
|
||||
ret.append(str[ind]);
|
||||
++ind;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PICout operator<<(PICout s, const PIByteArray & ba) {
|
||||
s.space();
|
||||
s.saveAndSetControls(0);
|
||||
|
||||
@@ -340,6 +340,10 @@ public:
|
||||
//! \~\sa \a every(), \a any(), \a contains(), \a indexWhere()
|
||||
inline int entries(std::function<bool(uchar e)> test, ssize_t start = 0) const { return d.entries(test, start); }
|
||||
|
||||
bool startsWith(const PIByteArray & o) const;
|
||||
|
||||
bool endsWith(const PIByteArray & o) const;
|
||||
|
||||
//! \~english Returns the first index at which a given element `e`
|
||||
//! can be found in the array, or `-1` if it is not present.
|
||||
//! \~russian Возвращает первый индекс, по которому данный элемент `e`
|
||||
@@ -1165,6 +1169,8 @@ public:
|
||||
|
||||
static PIByteArray fromHex(PIString str);
|
||||
|
||||
static PIByteArray fromAscii(const char * str);
|
||||
|
||||
//! \~english Return converted from Base 64 data
|
||||
//! \~russian Возвращает массив из Base 64 представления
|
||||
static PIByteArray fromBase64(const PIByteArray & base64);
|
||||
|
||||
163
main.cpp
163
main.cpp
@@ -1,123 +1,66 @@
|
||||
#include "pibytearray.h"
|
||||
#include "piclientserver_client.h"
|
||||
#include "piclientserver_server.h"
|
||||
#include "picodeparser.h"
|
||||
#include "piintrospection_server.h"
|
||||
#include "piiostream.h"
|
||||
#include "pijson.h"
|
||||
#include "pilog.h"
|
||||
#include "pimathbase.h"
|
||||
#include "pihttpserver.h"
|
||||
#include "pip.h"
|
||||
#include "piprotectedvariable.h"
|
||||
#include "pitranslator_p.h"
|
||||
#include "pivaluetree_conversions.h"
|
||||
|
||||
using namespace PICoutManipulators;
|
||||
|
||||
class MyStr: public PIString {
|
||||
public:
|
||||
MyStr(): PIString() {}
|
||||
MyStr(const char * o): PIString(o) { piCout << "MyStr *"; }
|
||||
MyStr(const MyStr & o): PIString(o) { piCout << "MyStr &"; }
|
||||
// MyStr(const MyStr & o): PIString(o) { piCout << "MyStr &"; }
|
||||
MyStr(MyStr && o): PIString(o) { piCout << "MyStr &&"; }
|
||||
MyStr & operator=(const MyStr & o) {
|
||||
*this += o;
|
||||
piCout << "MyStr =&";
|
||||
return *this;
|
||||
}
|
||||
MyStr & operator=(MyStr && o) {
|
||||
*this += o;
|
||||
piCout << "MyStr =&&";
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
PIKbdListener kbd;
|
||||
const char * pageTitle = "<!DOCTYPE html>"
|
||||
"<html>"
|
||||
"<body>"
|
||||
"<h1>Title</h1>"
|
||||
"</body>"
|
||||
"</html>";
|
||||
|
||||
|
||||
int main(int argc, char * argv[]) {
|
||||
PIProtectedVariable<MyStr> var;
|
||||
// MyStr s("hello");
|
||||
var.set("hello");
|
||||
auto & v = var.lock();
|
||||
// v = "world";
|
||||
var.unlock();
|
||||
piCout << var.get();
|
||||
kbd.enableExitCapture();
|
||||
|
||||
/*PICodeParser parser;
|
||||
parser.parseFile("client_server.h");
|
||||
for (auto m: parser.enums) {
|
||||
PIHTTPServer server;
|
||||
|
||||
server.setFavicon(PIFile::readAll("logo.png", false));
|
||||
// server.setOption(MicrohttpdServer::Option::HTTPSEnabled, true);
|
||||
server.listen({"127.0.0.1", 7777});
|
||||
// server.listen({"192.168.1.10", 7778});
|
||||
|
||||
server.registerPath("/", [](const MicrohttpdServer::Request & r) -> MicrohttpdServer::Reply {
|
||||
MicrohttpdServer::Reply ret;
|
||||
ret.setBody(PIByteArray::fromAscii(pageTitle));
|
||||
return ret;
|
||||
});
|
||||
|
||||
server.registerPath("/html", [](const MicrohttpdServer::Request & r) -> MicrohttpdServer::Reply {
|
||||
MicrohttpdServer::Reply ret;
|
||||
ret.setBody("<!DOCTYPE html><html><body><p>arg=%1</p></body></html>"_a.arg(r.args.value("a0")).toUTF8());
|
||||
return ret;
|
||||
});
|
||||
|
||||
server.registerPath("/api", [](const MicrohttpdServer::Request & r) -> MicrohttpdServer::Reply {
|
||||
MicrohttpdServer::Reply ret;
|
||||
ret.setBody(PIByteArray::fromAscii("<!DOCTYPE html><html><body>API</body></html>"));
|
||||
return ret;
|
||||
});
|
||||
|
||||
server.registerPath("/api/*", [](const MicrohttpdServer::Request & r) -> MicrohttpdServer::Reply {
|
||||
MicrohttpdServer::Reply ret;
|
||||
ret.setBody("<!DOCTYPE html><html><body>API etry %1</body></html>"_a.arg(r.path).toUTF8());
|
||||
ret.setCode(405);
|
||||
return ret;
|
||||
});
|
||||
|
||||
/*server.setRequestCallback([](MicrohttpdServer::Request r) -> MicrohttpdServer::Reply {
|
||||
MicrohttpdServer::Reply rep;
|
||||
piCout << "request" << r.path;
|
||||
piCout << " header" << r.headers;
|
||||
piCout << " args" << r.args;
|
||||
piCout << " body" << r.body;
|
||||
piCout << "";
|
||||
piCout << m.name; // << m.args << m.value;
|
||||
// piCout << m.expand({"hello"});
|
||||
}*/
|
||||
rep.setBody(PIByteArray::fromAscii("[{\"value1\": true, \"value2\": \"ыекштп\"}]"));
|
||||
return rep;
|
||||
});*/
|
||||
piCout << "start" << server.isListen();
|
||||
|
||||
return 0;
|
||||
PITranslator::loadLang("ru");
|
||||
PISerial f("COM123");
|
||||
f.open();
|
||||
|
||||
/*auto test = [](PIString s, PIString v) {
|
||||
piCout << " in:" << s;
|
||||
piCout << "arg:" << minArgPlaceholder(s);
|
||||
piCout << "out:" << arg(s, v);
|
||||
piCout << "";
|
||||
};
|
||||
|
||||
test(" %", "asd");
|
||||
test("%", "asd");
|
||||
test("1", "asd");
|
||||
test(" %11", "asd");
|
||||
test("%1%2 %0f%0g", "asd");
|
||||
test("%01 ", "asd");*/
|
||||
|
||||
/*piCout << PIString::readableSize(50_KiB);
|
||||
piCout << PIString::readableSize(1_GB);
|
||||
PITranslator::loadLang("ru");
|
||||
piCout << PIString::readableSize(50_KiB);
|
||||
piCout << PIString::readableSize(1_GB);
|
||||
piCout << "test\nstring"_tr;
|
||||
PITranslator::clear();
|
||||
piCout << PIString::readableSize(50_KiB);
|
||||
piCout << PIString::readableSize(1_GB);
|
||||
piCout << "test\nstring"_tr;
|
||||
piCout << "hello!"_tr;
|
||||
PITranslator::loadConfig("[]\nhello!=привет!\n[Co]\nhello!=привет CO!\n"_u8);
|
||||
piCout << "hello!"_tr("Co") << "hello!"_tr;*/
|
||||
// piCout << "hello!"_trNoOp;
|
||||
|
||||
|
||||
/*PISet<int> set;
|
||||
piCout << set << piSerialize(set) << piDeserialize<PISet<int>>(piSerialize(set));
|
||||
set << 1 << 2 << 3;
|
||||
piCout << set << piSerialize(set) << piDeserialize<PISet<int>>(piSerialize(set));
|
||||
set << 1 << -2 << 50 << -100;
|
||||
piCout << set << piSerialize(set) << piDeserialize<PISet<int>>(piSerialize(set));*/
|
||||
return 0;
|
||||
|
||||
std::numeric_limits<complexf>::epsilon();
|
||||
using cmlp = complexf;
|
||||
PIMathMatrixT<3, 3, double> v0;
|
||||
PIMathMatrixT<3, 3, cmlp> v1;
|
||||
v0[0][1] = 1;
|
||||
v0[1][1] = 2;
|
||||
v0[2][1] = 3;
|
||||
v1[0][1] = cmlp(1., 0);
|
||||
v1[1][1] = cmlp(2., -1);
|
||||
v1[2][1] = cmlp(3., 2);
|
||||
piCout << v0 << v1;
|
||||
piCout << (v0 * 2.) << (v1 * cmlp(2., 2.));
|
||||
piCout << (v0 + 2.) << (v1 + cmlp(2.));
|
||||
piCout << (v0 - 2.) << (v1 - cmlp(2.));
|
||||
piCout << (v0 / 2.) << (v1 / cmlp(2., 1.));
|
||||
// piCout << (v0.length()) << (v1.length());
|
||||
// piCout << (v0.lengthSqr()) << (v1.lengthSqr());
|
||||
// piCout << (v0.manhattanLength()) << (v1.manhattanLength());
|
||||
|
||||
/*foo<int>();
|
||||
foo<double>();
|
||||
foo<complexf>();
|
||||
foo<complexd>();
|
||||
return 0;*/
|
||||
WAIT_FOR_EXIT
|
||||
|
||||
server.stop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user