Compare commits
21 Commits
pico_sdk
...
a581d9bf9d
| Author | SHA1 | Date | |
|---|---|---|---|
| a581d9bf9d | |||
| 35772fc2d1 | |||
| 6041a72f30 | |||
| 98d93865f0 | |||
| d8cadce026 | |||
| 87a29bc8bc | |||
| 39c1be6bc0 | |||
| 52c400915d | |||
| 95d3c9c658 | |||
| 7f894e77bb | |||
| 97c14870c0 | |||
| 7195734765 | |||
| 8ecec6b914 | |||
| 9029bcf099 | |||
| 6f1660fd9e | |||
| f50a3abc8e | |||
| 8c15113cb0 | |||
| 4253acb72b | |||
| e22630b1bd | |||
| 563d9c5487 | |||
| 34bc322b9b |
162
AGENTS.md
Normal file
162
AGENTS.md
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
# AGENTS.md - Agent Guidelines for PIP
|
||||||
|
|
||||||
|
This file provides guidance for agentic coding agents working on the PIP (Platform Independent Primitives) codebase.
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
PIP is a C++ cross-platform library providing platform-independent abstractions for:
|
||||||
|
- Core/Types: Strings, variants, containers, datetime, networks
|
||||||
|
- Threading: Mutexes, semaphores, thread pools, timers
|
||||||
|
- I/O: Files, serial, CAN, GPIO, SPI, Ethernet
|
||||||
|
- Math: Vectors, matrices, FFT, quaternions
|
||||||
|
- Crypto: MD5, SHA, BLAKE2, SipHash
|
||||||
|
- Compression: zlib support
|
||||||
|
- HTTP: Client and server support
|
||||||
|
- Serialization: JSON, binary, XML
|
||||||
|
|
||||||
|
## Build Commands
|
||||||
|
|
||||||
|
### Basic Build
|
||||||
|
```bash
|
||||||
|
cmake -B build
|
||||||
|
cmake --build build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build with Tests
|
||||||
|
```bash
|
||||||
|
cmake -B build -DTESTS=ON
|
||||||
|
cmake --build build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run Tests
|
||||||
|
```bash
|
||||||
|
ctest --test-dir build/tests # Run all tests
|
||||||
|
ctest --test-dir build/tests -R <regex> # Run specific tests matching regex
|
||||||
|
ctest --test-dir build/tests -V # Verbose output
|
||||||
|
```
|
||||||
|
|
||||||
|
To run a single test:
|
||||||
|
```bash
|
||||||
|
# Build the test executable, then run directly:
|
||||||
|
./build/tests/pip_<test_name>_test
|
||||||
|
# Or use ctest with specific test name
|
||||||
|
ctest --test-dir build/tests -R "TestName"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Other Commands
|
||||||
|
```bash
|
||||||
|
cmake --build build --target clean # Clean build
|
||||||
|
cmake --install build_pip # Install
|
||||||
|
cmake --build build --target doc # Build documentation
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build Options
|
||||||
|
```bash
|
||||||
|
-DTESTS=ON # Build tests
|
||||||
|
-DCOVERAGE=ON # Build with coverage
|
||||||
|
-DICU=ON # ICU support for codepage conversion
|
||||||
|
-DSTD_IOSTREAM=ON # std::iostream operators support
|
||||||
|
-DINTROSPECTION=ON # Build with introspection
|
||||||
|
-DPIP_BUILD_CRYPT=ON # Crypt module (requires libsodium)
|
||||||
|
-DPIP_BUILD_FFTW=ON # FFT support
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code Style Guidelines
|
||||||
|
|
||||||
|
### File Organization
|
||||||
|
- Header files: `libs/main/**/*.h`
|
||||||
|
- Source files: `libs/main/**/*.cpp`
|
||||||
|
- Private headers: `*_p.h`
|
||||||
|
- Tests: `tests/<module>/`
|
||||||
|
- Use Doxygen comments (`/*! ... */`) for documentation with `\brief`, `\param`, `\return`
|
||||||
|
|
||||||
|
### Naming Conventions
|
||||||
|
- **Classes**: PascalCase with `PI` prefix (e.g., `PIString`, `PIByteArray`, `PIVariant`)
|
||||||
|
- **Functions**: camelCase (e.g., `toAscii()`, `isEmpty()`, `append()`)
|
||||||
|
- **Member variables**: snake_case, or just lowercase (e.g., `array_size`, `count`)
|
||||||
|
- **Constants**: PascalCase or UPPER_SNAKE_CASE
|
||||||
|
- **Enums**: PascalCase for enum names and values
|
||||||
|
|
||||||
|
### Code Formatting
|
||||||
|
- **Indentation**: Use tabs (4 spaces equivalent) or spaces - match existing code
|
||||||
|
- **Braces**: Opening brace on same line for functions, new line for namespaces/classes
|
||||||
|
- **Includes**: System includes first, then project headers
|
||||||
|
- **Use forward declarations** where possible to reduce compile times
|
||||||
|
- **Use `nullptr`** instead of `NULL`
|
||||||
|
|
||||||
|
### C++ Standards
|
||||||
|
- C++11 standard (enforced in CMakeLists.txt)
|
||||||
|
- Use `override` keyword for virtual function overrides
|
||||||
|
- Use `explicit` for single-argument constructors
|
||||||
|
- Use `const` member functions where applicable
|
||||||
|
- Use range-based `for` loops when possible
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
- Return error codes
|
||||||
|
- DO NOT USE exceptions
|
||||||
|
- Use `piCerr` and `piCout` for error messages
|
||||||
|
- Check for null pointers where appropriate
|
||||||
|
|
||||||
|
### Module Structure
|
||||||
|
|
||||||
|
Each module typically has:
|
||||||
|
1. Public header: `pip_<module>.h` or `<classname>.h`
|
||||||
|
2. Private implementation: `<classname>.cpp` or `<classname>_p.cpp`
|
||||||
|
3. Use `PIP_EXPORT` macro for symbols that need to be exported from the library
|
||||||
|
|
||||||
|
Example class structure:
|
||||||
|
```cpp
|
||||||
|
// header.h
|
||||||
|
#ifndef MODULE_CLASSNAME_H
|
||||||
|
#define MODULE_CLASSNAME_H
|
||||||
|
|
||||||
|
#include "pip_export.h"
|
||||||
|
|
||||||
|
class PIP_EXPORT ClassName {
|
||||||
|
public:
|
||||||
|
ClassName();
|
||||||
|
~ClassName();
|
||||||
|
|
||||||
|
void doSomething();
|
||||||
|
bool isValid() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void privateMethod();
|
||||||
|
int data;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MODULE_CLASSNAME_H
|
||||||
|
|
||||||
|
// source.cpp
|
||||||
|
#include "header.h"
|
||||||
|
#include "piincludes_p.h"
|
||||||
|
|
||||||
|
ClassName::ClassName() : data(0) {}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
- Use Google Test framework
|
||||||
|
- Test files go in `tests/<module>/`
|
||||||
|
- Use `TEST(TestSuite, TestName)` or `TEST_F(TestFixture, TestName)` for test cases
|
||||||
|
- Use `ASSERT_*` for fatal failures, `EXPECT_*` for non-fatal
|
||||||
|
- Test naming: `<ClassName>_<TestDescription>`
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```cpp
|
||||||
|
#include "pistring.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
TEST(PIString_Tests, constructor_empty) {
|
||||||
|
PIString str;
|
||||||
|
ASSERT_TRUE(str.isEmpty());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Module Dependencies
|
||||||
|
- Main library: `libs/main/`
|
||||||
|
- Third-party: `3rd/`
|
||||||
|
- Utils: `utils/`
|
||||||
|
|
||||||
|
### Key Files
|
||||||
|
- `CMakeLists.txt` - Main build configuration
|
||||||
|
- `tests/CMakeLists.txt` - Test configuration
|
||||||
@@ -6,7 +6,7 @@ endif()
|
|||||||
project(PIP)
|
project(PIP)
|
||||||
set(PIP_MAJOR 5)
|
set(PIP_MAJOR 5)
|
||||||
set(PIP_MINOR 5)
|
set(PIP_MINOR 5)
|
||||||
set(PIP_REVISION 2)
|
set(PIP_REVISION 5)
|
||||||
set(PIP_SUFFIX )
|
set(PIP_SUFFIX )
|
||||||
set(PIP_COMPANY SHS)
|
set(PIP_COMPANY SHS)
|
||||||
set(PIP_DOMAIN org.SHS)
|
set(PIP_DOMAIN org.SHS)
|
||||||
@@ -221,17 +221,10 @@ if (TESTS)
|
|||||||
add_subdirectory(tests)
|
add_subdirectory(tests)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(PIP_MICRO)
|
|
||||||
add_definitions(-DMICRO_PIP)
|
|
||||||
set(ICU OFF)
|
|
||||||
set(LOCAL ON)
|
|
||||||
endif()
|
|
||||||
if(PIP_FREERTOS)
|
if(PIP_FREERTOS)
|
||||||
add_definitions(-DPIP_FREERTOS)
|
add_definitions(-DPIP_FREERTOS)
|
||||||
endif()
|
set(ICU OFF)
|
||||||
if(DEFINED PICO_BOARD)
|
set(LOCAL ON)
|
||||||
add_definitions(-DPICO_SDK)
|
|
||||||
message(STATUS "Building PIP for Pi Pico SDK ${PICO_SDK_VERSION_STRING}")
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Check Bessel functions
|
# Check Bessel functions
|
||||||
@@ -336,7 +329,7 @@ if ((NOT DEFINED SHSTKPROJECT) AND (DEFINED ANDROID_PLATFORM))
|
|||||||
#message("${ANDROID_NDK}/sysroot/usr/include")
|
#message("${ANDROID_NDK}/sysroot/usr/include")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT PIP_MICRO)
|
if(NOT PIP_FREERTOS)
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
if(${C_COMPILER} STREQUAL "cl.exe")
|
if(${C_COMPILER} STREQUAL "cl.exe")
|
||||||
else()
|
else()
|
||||||
@@ -357,7 +350,7 @@ if(NOT PIP_MICRO)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
set(PIP_LIBS)
|
set(PIP_LIBS)
|
||||||
if(PIP_MICRO)
|
if(PIP_FREERTOS)
|
||||||
set(PIP_LIBS ${LIBS_MAIN})
|
set(PIP_LIBS ${LIBS_MAIN})
|
||||||
else()
|
else()
|
||||||
foreach(LIB_ ${LIBS_MAIN})
|
foreach(LIB_ ${LIBS_MAIN})
|
||||||
@@ -371,11 +364,11 @@ if(WIN32)
|
|||||||
endif()
|
endif()
|
||||||
else()
|
else()
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
|
||||||
|
if(DEFINED ENV{QNX_HOST} OR PIP_FREERTOS)
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftemplate-depth-32")
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS}")
|
set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||||
if(DEFINED ENV{QNX_HOST} OR PIP_MICRO)
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftemplate-depth-32")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(PCRE2_BUILD_PCRE2_8 OFF)
|
set(PCRE2_BUILD_PCRE2_8 OFF)
|
||||||
set(PCRE2_BUILD_PCRE2_16 ON )
|
set(PCRE2_BUILD_PCRE2_16 ON )
|
||||||
@@ -413,7 +406,7 @@ endif()
|
|||||||
|
|
||||||
|
|
||||||
if (NOT CROSSTOOLS)
|
if (NOT CROSSTOOLS)
|
||||||
if (NOT PIP_MICRO)
|
if (NOT PIP_FREERTOS)
|
||||||
|
|
||||||
if (PIP_BUILD_CONSOLE)
|
if (PIP_BUILD_CONSOLE)
|
||||||
pip_module(console "" "PIP console support" "" "" "")
|
pip_module(console "" "PIP console support" "" "" "")
|
||||||
@@ -631,7 +624,7 @@ string(REPLACE ";" "," PIP_EXPORTS_STR "${PIP_EXPORTS}")
|
|||||||
target_compile_definitions(pip PRIVATE "PICODE_DEFINES=\"${PIP_EXPORTS_STR}\"")
|
target_compile_definitions(pip PRIVATE "PICODE_DEFINES=\"${PIP_EXPORTS_STR}\"")
|
||||||
|
|
||||||
|
|
||||||
if(NOT PIP_MICRO)
|
if(NOT PIP_FREERTOS)
|
||||||
|
|
||||||
# Auxiliary
|
# Auxiliary
|
||||||
if (NOT CROSSTOOLS)
|
if (NOT CROSSTOOLS)
|
||||||
@@ -708,7 +701,7 @@ if(NOT LOCAL)
|
|||||||
install(TARGETS ${PIP_MODULES} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib)
|
install(TARGETS ${PIP_MODULES} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib)
|
||||||
endif()
|
endif()
|
||||||
else()
|
else()
|
||||||
if(NOT PIP_MICRO)
|
if(NOT PIP_FREERTOS)
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
install(TARGETS ${PIP_MODULES} RUNTIME DESTINATION bin)
|
install(TARGETS ${PIP_MODULES} RUNTIME DESTINATION bin)
|
||||||
install(TARGETS ${PIP_MODULES} ARCHIVE DESTINATION lib)
|
install(TARGETS ${PIP_MODULES} ARCHIVE DESTINATION lib)
|
||||||
@@ -736,7 +729,7 @@ endif()
|
|||||||
#
|
#
|
||||||
# Build Documentation
|
# Build Documentation
|
||||||
#
|
#
|
||||||
if ((NOT PIP_MICRO) AND (NOT CROSSTOOLS))
|
if ((NOT PIP_FREERTOS) AND (NOT CROSSTOOLS))
|
||||||
include(PIPDocumentation)
|
include(PIPDocumentation)
|
||||||
find_package(Doxygen)
|
find_package(Doxygen)
|
||||||
if(DOXYGEN_FOUND)
|
if(DOXYGEN_FOUND)
|
||||||
@@ -805,7 +798,7 @@ message(" Type : ${CMAKE_BUILD_TYPE}")
|
|||||||
if (NOT LOCAL)
|
if (NOT LOCAL)
|
||||||
message(" Install: \"${CMAKE_INSTALL_PREFIX}\"")
|
message(" Install: \"${CMAKE_INSTALL_PREFIX}\"")
|
||||||
else()
|
else()
|
||||||
if(NOT PIP_MICRO)
|
if(NOT PIP_FREERTOS)
|
||||||
message(" Install: local \"bin\", \"lib\" and \"include\"")
|
message(" Install: local \"bin\", \"lib\" and \"include\"")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
@@ -838,7 +831,7 @@ message(" Utilites:")
|
|||||||
foreach(_util ${PIP_UTILS_LIST})
|
foreach(_util ${PIP_UTILS_LIST})
|
||||||
message(" * ${_util}")
|
message(" * ${_util}")
|
||||||
endforeach()
|
endforeach()
|
||||||
if(NOT PIP_MICRO)
|
if(NOT PIP_FREERTOS)
|
||||||
message("")
|
message("")
|
||||||
message(" Using libraries:")
|
message(" Using libraries:")
|
||||||
foreach(LIB_ ${LIBS_STATUS})
|
foreach(LIB_ ${LIBS_STATUS})
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ if (NOT BUILDING_PIP)
|
|||||||
find_library(PTHREAD_LIBRARY pthread)
|
find_library(PTHREAD_LIBRARY pthread)
|
||||||
find_library(UTIL_LIBRARY util)
|
find_library(UTIL_LIBRARY util)
|
||||||
set(_PIP_ADD_LIBS_ ${PTHREAD_LIBRARY} ${UTIL_LIBRARY})
|
set(_PIP_ADD_LIBS_ ${PTHREAD_LIBRARY} ${UTIL_LIBRARY})
|
||||||
if((NOT DEFINED ENV{QNX_HOST}) AND (NOT APPLE) AND (NOT PIP_MICRO))
|
if((NOT DEFINED ENV{QNX_HOST}) AND (NOT APPLE) AND (NOT PIP_FREERTOS))
|
||||||
find_library(RT_LIBRARY rt)
|
find_library(RT_LIBRARY rt)
|
||||||
list(APPEND _PIP_ADD_LIBS_ ${RT_LIBRARY})
|
list(APPEND _PIP_ADD_LIBS_ ${RT_LIBRARY})
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ list(APPEND COMPONENT_ADD_INCLUDEDIRS "../libs/main/thread")
|
|||||||
set(COMPONENT_PRIV_REQUIRES pthread lwip freertos vfs spi_flash libsodium)
|
set(COMPONENT_PRIV_REQUIRES pthread lwip freertos vfs spi_flash libsodium)
|
||||||
register_component()
|
register_component()
|
||||||
set(PIP_FREERTOS ON)
|
set(PIP_FREERTOS ON)
|
||||||
set(PIP_MICRO ON)
|
|
||||||
set(LIB OFF)
|
set(LIB OFF)
|
||||||
set(INCLUDE_DIRS ${IDF_INCLUDE_DIRECTORIES})
|
set(INCLUDE_DIRS ${IDF_INCLUDE_DIRECTORIES})
|
||||||
list(APPEND INCLUDE_DIRS $ENV{IDF_PATH}/components/newlib/platform_include)
|
list(APPEND INCLUDE_DIRS $ENV{IDF_PATH}/components/newlib/platform_include)
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ bool PIHTTPClient::init() {
|
|||||||
if (is_cancel) return false;
|
if (is_cancel) return false;
|
||||||
CurlThreadPool::instance();
|
CurlThreadPool::instance();
|
||||||
if (!PRIVATE->init()) return false;
|
if (!PRIVATE->init()) return false;
|
||||||
auto ait = request.arguments().makeIterator();
|
auto ait = request.queryArguments().makeIterator();
|
||||||
while (ait.next()) {
|
while (ait.next()) {
|
||||||
if (!url.contains('?'))
|
if (!url.contains('?'))
|
||||||
url.append('?');
|
url.append('?');
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ bool PISystemMonitor::startOnProcess(int pID, PISystemTime interval) {
|
|||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
# else
|
# else
|
||||||
PRIVATE->hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pID_);
|
PRIVATE->hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pID_);
|
||||||
if (PRIVATE->hProc == 0) {
|
if (PRIVATE->hProc == 0) {
|
||||||
piCoutObj << "Can`t open process with ID = %1, %2!"_tr("PISystemMonitor").arg(pID_).arg(errorString());
|
piCoutObj << "Can`t open process with ID = %1, %2!"_tr("PISystemMonitor").arg(pID_).arg(errorString());
|
||||||
return false;
|
return false;
|
||||||
@@ -178,9 +178,6 @@ PISystemTime uint64toST(uint64_t v) {
|
|||||||
void PISystemMonitor::run() {
|
void PISystemMonitor::run() {
|
||||||
cur_tm.clear();
|
cur_tm.clear();
|
||||||
tbid.clear();
|
tbid.clear();
|
||||||
ProcessStats tstat;
|
|
||||||
tstat.ID = pID_;
|
|
||||||
#ifndef PIP_NO_THREADS
|
|
||||||
__PIThreadCollection * pitc = __PIThreadCollection::instance();
|
__PIThreadCollection * pitc = __PIThreadCollection::instance();
|
||||||
pitc->lock();
|
pitc->lock();
|
||||||
PIVector<PIThread *> tv = pitc->threads();
|
PIVector<PIThread *> tv = pitc->threads();
|
||||||
@@ -188,14 +185,16 @@ void PISystemMonitor::run() {
|
|||||||
if (t->isPIObject()) tbid[t->tid()] = t->name();
|
if (t->isPIObject()) tbid[t->tid()] = t->name();
|
||||||
pitc->unlock();
|
pitc->unlock();
|
||||||
// piCout << tbid.keys().toType<uint>();
|
// piCout << tbid.keys().toType<uint>();
|
||||||
# ifdef FREERTOS
|
ProcessStats tstat;
|
||||||
|
tstat.ID = pID_;
|
||||||
|
#ifdef MICRO_PIP
|
||||||
for (auto * t: tv)
|
for (auto * t: tv)
|
||||||
if (t->isPIObject()) gatherThread(t->tid());
|
if (t->isPIObject()) gatherThread(t->tid());
|
||||||
# else // FREERTOS
|
#else
|
||||||
# ifndef WINDOWS
|
# ifndef WINDOWS
|
||||||
double delay_ms = delay_.toMilliseconds();
|
double delay_ms = delay_.toMilliseconds();
|
||||||
tbid[pID_] = "main";
|
tbid[pID_] = "main";
|
||||||
# ifdef MAC_OS
|
# ifdef MAC_OS
|
||||||
rusage_info_current ru;
|
rusage_info_current ru;
|
||||||
proc_pid_rusage(pID_, RUSAGE_INFO_CURRENT, (rusage_info_t *)&ru);
|
proc_pid_rusage(pID_, RUSAGE_INFO_CURRENT, (rusage_info_t *)&ru);
|
||||||
// piCout << PISystemTime(((uint*)&(ru.ri_user_time))[1], ((uint*)&(ru.ri_user_time))[0]);
|
// piCout << PISystemTime(((uint*)&(ru.ri_user_time))[1], ((uint*)&(ru.ri_user_time))[0]);
|
||||||
@@ -211,7 +210,7 @@ void PISystemMonitor::run() {
|
|||||||
tstat.cpu_load_user = 100.f * (PRIVATE->cpu_u_cur - PRIVATE->cpu_u_prev).toMilliseconds() / delay_ms;
|
tstat.cpu_load_user = 100.f * (PRIVATE->cpu_u_cur - PRIVATE->cpu_u_prev).toMilliseconds() / delay_ms;
|
||||||
cycle = 0;
|
cycle = 0;
|
||||||
// piCout << (PRIVATE->cpu_u_cur - PRIVATE->cpu_u_prev).toMilliseconds() / delay_ms;
|
// piCout << (PRIVATE->cpu_u_cur - PRIVATE->cpu_u_prev).toMilliseconds() / delay_ms;
|
||||||
# else // MAC_OS
|
# else
|
||||||
PRIVATE->file.seekToBegin();
|
PRIVATE->file.seekToBegin();
|
||||||
PIString str = PIString::fromAscii(PRIVATE->file.readAll());
|
PIString str = PIString::fromAscii(PRIVATE->file.readAll());
|
||||||
int si = str.find('(') + 1, fi = 0, cc = 1;
|
int si = str.find('(') + 1, fi = 0, cc = 1;
|
||||||
@@ -265,8 +264,8 @@ void PISystemMonitor::run() {
|
|||||||
if (i.flags[PIFile::FileInfo::Dot] || i.flags[PIFile::FileInfo::DotDot]) continue;
|
if (i.flags[PIFile::FileInfo::Dot] || i.flags[PIFile::FileInfo::DotDot]) continue;
|
||||||
gatherThread(i.name().toInt());
|
gatherThread(i.name().toInt());
|
||||||
}
|
}
|
||||||
# endif // MAC_OS
|
# endif
|
||||||
# else // WINDOWS
|
# else
|
||||||
if (GetProcessMemoryInfo(PRIVATE->hProc, &PRIVATE->mem_cnt, sizeof(PRIVATE->mem_cnt)) != 0) {
|
if (GetProcessMemoryInfo(PRIVATE->hProc, &PRIVATE->mem_cnt, sizeof(PRIVATE->mem_cnt)) != 0) {
|
||||||
tstat.physical_memsize = PRIVATE->mem_cnt.WorkingSetSize;
|
tstat.physical_memsize = PRIVATE->mem_cnt.WorkingSetSize;
|
||||||
}
|
}
|
||||||
@@ -316,9 +315,8 @@ void PISystemMonitor::run() {
|
|||||||
tstat.cpu_load_user = 0.f;
|
tstat.cpu_load_user = 0.f;
|
||||||
}
|
}
|
||||||
PRIVATE->tm.reset();
|
PRIVATE->tm.reset();
|
||||||
# endif // WINDOWS
|
# endif
|
||||||
# endif // FREERTOS
|
#endif
|
||||||
#endif // PIP_NO_THREADS
|
|
||||||
|
|
||||||
tstat.cpu_load_system = piClampf(tstat.cpu_load_system, 0.f, 100.f);
|
tstat.cpu_load_system = piClampf(tstat.cpu_load_system, 0.f, 100.f);
|
||||||
tstat.cpu_load_user = piClampf(tstat.cpu_load_user, 0.f, 100.f);
|
tstat.cpu_load_user = piClampf(tstat.cpu_load_user, 0.f, 100.f);
|
||||||
@@ -354,7 +352,7 @@ void PISystemMonitor::gatherThread(llong id) {
|
|||||||
#ifdef MICRO_PIP
|
#ifdef MICRO_PIP
|
||||||
ts.name = tbid.value(id, "<PIThread>");
|
ts.name = tbid.value(id, "<PIThread>");
|
||||||
#else
|
#else
|
||||||
ts.name = tbid.value(id, "<non-PIThread>");
|
ts.name = tbid.value(id, "<non-PIThread>");
|
||||||
# ifndef WINDOWS
|
# ifndef WINDOWS
|
||||||
PIFile f(PRIVATE->proc_dir + "task/" + PIString::fromNumber(id) + "/stat");
|
PIFile f(PRIVATE->proc_dir + "task/" + PIString::fromNumber(id) + "/stat");
|
||||||
// piCout << f.path();
|
// piCout << f.path();
|
||||||
|
|||||||
@@ -18,11 +18,9 @@
|
|||||||
*/
|
*/
|
||||||
#include "pikbdlistener.h"
|
#include "pikbdlistener.h"
|
||||||
|
|
||||||
#ifndef MICRO_PIP
|
#include "piincludes_p.h"
|
||||||
|
#include "piliterals.h"
|
||||||
# include "piincludes_p.h"
|
#include "piwaitevent_p.h"
|
||||||
# include "piliterals.h"
|
|
||||||
# include "piwaitevent_p.h"
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
#ifndef WINDOWS
|
#ifndef WINDOWS
|
||||||
# include <termios.h>
|
# include <termios.h>
|
||||||
@@ -51,7 +49,7 @@ bool PIKbdListener::exiting;
|
|||||||
PIKbdListener * PIKbdListener::_object = 0;
|
PIKbdListener * PIKbdListener::_object = 0;
|
||||||
|
|
||||||
|
|
||||||
# ifndef WINDOWS
|
#ifndef WINDOWS
|
||||||
// unix
|
// unix
|
||||||
const PIKbdListener::EscSeq PIKbdListener::esc_seq[] = {
|
const PIKbdListener::EscSeq PIKbdListener::esc_seq[] = {
|
||||||
{"OA", PIKbdListener::UpArrow, 0, 0, 1},
|
{"OA", PIKbdListener::UpArrow, 0, 0, 1},
|
||||||
@@ -132,22 +130,22 @@ void setupTerminal(bool on) {
|
|||||||
printf(on ? "h" : "l");
|
printf(on ? "h" : "l");
|
||||||
fflush(0);
|
fflush(0);
|
||||||
}
|
}
|
||||||
# endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
PRIVATE_DEFINITION_START(PIKbdListener)
|
PRIVATE_DEFINITION_START(PIKbdListener)
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
void *hIn, *hOut;
|
void *hIn, *hOut;
|
||||||
DWORD smode, tmode;
|
DWORD smode, tmode;
|
||||||
CONSOLE_SCREEN_BUFFER_INFO sbi;
|
CONSOLE_SCREEN_BUFFER_INFO sbi;
|
||||||
# else
|
#else
|
||||||
struct termios sterm, tterm;
|
struct termios sterm, tterm;
|
||||||
# endif
|
#endif
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
DWORD
|
DWORD
|
||||||
# else
|
#else
|
||||||
int
|
int
|
||||||
# endif
|
#endif
|
||||||
ret;
|
ret;
|
||||||
PIWaitEvent event;
|
PIWaitEvent event;
|
||||||
PRIVATE_DEFINITION_END(PIKbdListener)
|
PRIVATE_DEFINITION_END(PIKbdListener)
|
||||||
@@ -156,13 +154,13 @@ PRIVATE_DEFINITION_END(PIKbdListener)
|
|||||||
PIKbdListener::PIKbdListener(KBFunc slot, void * _d, bool startNow): PIThread() {
|
PIKbdListener::PIKbdListener(KBFunc slot, void * _d, bool startNow): PIThread() {
|
||||||
setName("keyboard_listener"_a);
|
setName("keyboard_listener"_a);
|
||||||
_object = this;
|
_object = this;
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
PRIVATE->hIn = GetStdHandle(STD_INPUT_HANDLE);
|
PRIVATE->hIn = GetStdHandle(STD_INPUT_HANDLE);
|
||||||
PRIVATE->hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
PRIVATE->hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
GetConsoleMode(PRIVATE->hIn, &PRIVATE->smode);
|
GetConsoleMode(PRIVATE->hIn, &PRIVATE->smode);
|
||||||
# else
|
#else
|
||||||
tcgetattr(0, &PRIVATE->sterm);
|
tcgetattr(0, &PRIVATE->sterm);
|
||||||
# endif
|
#endif
|
||||||
ret_func = slot;
|
ret_func = slot;
|
||||||
kbddata_ = _d;
|
kbddata_ = _d;
|
||||||
dbl_interval = 400;
|
dbl_interval = 400;
|
||||||
@@ -180,10 +178,10 @@ PIKbdListener::~PIKbdListener() {
|
|||||||
|
|
||||||
|
|
||||||
void PIKbdListener::begin() {
|
void PIKbdListener::begin() {
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
GetConsoleMode(PRIVATE->hIn, &PRIVATE->tmode);
|
GetConsoleMode(PRIVATE->hIn, &PRIVATE->tmode);
|
||||||
SetConsoleMode(PRIVATE->hIn, ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT | ENABLE_EXTENDED_FLAGS);
|
SetConsoleMode(PRIVATE->hIn, ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT | ENABLE_EXTENDED_FLAGS);
|
||||||
# else
|
#else
|
||||||
struct termios term;
|
struct termios term;
|
||||||
tcgetattr(0, &term);
|
tcgetattr(0, &term);
|
||||||
term.c_lflag &= ~(ECHO | ICANON);
|
term.c_lflag &= ~(ECHO | ICANON);
|
||||||
@@ -191,11 +189,11 @@ void PIKbdListener::begin() {
|
|||||||
PRIVATE->tterm = term;
|
PRIVATE->tterm = term;
|
||||||
tcsetattr(0, TCSANOW, &term);
|
tcsetattr(0, TCSANOW, &term);
|
||||||
setupTerminal(true);
|
setupTerminal(true);
|
||||||
# endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
PIKbdListener::KeyModifiers getModifiers(DWORD v, bool * shift = 0) {
|
PIKbdListener::KeyModifiers getModifiers(DWORD v, bool * shift = 0) {
|
||||||
PIKbdListener::KeyModifiers ret;
|
PIKbdListener::KeyModifiers ret;
|
||||||
bool ctrl = v & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED);
|
bool ctrl = v & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED);
|
||||||
@@ -216,7 +214,7 @@ PIKbdListener::MouseButtons getButtons(DWORD v) {
|
|||||||
if (v & FROM_LEFT_2ND_BUTTON_PRESSED) ret |= PIKbdListener::MouseMiddle;
|
if (v & FROM_LEFT_2ND_BUTTON_PRESSED) ret |= PIKbdListener::MouseMiddle;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
# endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
void PIKbdListener::readKeyboard() {
|
void PIKbdListener::readKeyboard() {
|
||||||
@@ -224,7 +222,7 @@ void PIKbdListener::readKeyboard() {
|
|||||||
ke.modifiers = 0;
|
ke.modifiers = 0;
|
||||||
char rc[8];
|
char rc[8];
|
||||||
piZeroMemory(rc, 8);
|
piZeroMemory(rc, 8);
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
INPUT_RECORD ir;
|
INPUT_RECORD ir;
|
||||||
ReadConsoleInput(PRIVATE->hIn, &ir, 1, &(PRIVATE->ret));
|
ReadConsoleInput(PRIVATE->hIn, &ir, 1, &(PRIVATE->ret));
|
||||||
switch (ir.EventType) {
|
switch (ir.EventType) {
|
||||||
@@ -408,7 +406,7 @@ void PIKbdListener::readKeyboard() {
|
|||||||
} break;
|
} break;
|
||||||
default: piMSleep(10); return;
|
default: piMSleep(10); return;
|
||||||
}
|
}
|
||||||
# else
|
#else
|
||||||
tcsetattr(0, TCSANOW, &PRIVATE->tterm);
|
tcsetattr(0, TCSANOW, &PRIVATE->tterm);
|
||||||
if (!PRIVATE->event.wait(0)) return;
|
if (!PRIVATE->event.wait(0)) return;
|
||||||
PRIVATE->ret = read(0, rc, 8);
|
PRIVATE->ret = read(0, rc, 8);
|
||||||
@@ -535,7 +533,7 @@ void PIKbdListener::readKeyboard() {
|
|||||||
cout << endl;*/
|
cout << endl;*/
|
||||||
}
|
}
|
||||||
if (ke.key == 0 && PRIVATE->ret > 1) ke.key = PIChar::fromSystem(rc).unicode16Code();
|
if (ke.key == 0 && PRIVATE->ret > 1) ke.key = PIChar::fromSystem(rc).unicode16Code();
|
||||||
# endif
|
#endif
|
||||||
if ((rc[0] == '\n' || rc[0] == '\r') && PRIVATE->ret == 1) ke.key = Return;
|
if ((rc[0] == '\n' || rc[0] == '\r') && PRIVATE->ret == 1) ke.key = Return;
|
||||||
if (exit_enabled && ke.key == exit_key) {
|
if (exit_enabled && ke.key == exit_key) {
|
||||||
PIKbdListener::exiting = true;
|
PIKbdListener::exiting = true;
|
||||||
@@ -562,32 +560,30 @@ bool PIKbdListener::stopAndWait(PISystemTime timeout) {
|
|||||||
|
|
||||||
void PIKbdListener::end() {
|
void PIKbdListener::end() {
|
||||||
// cout << "list end" << endl;
|
// cout << "list end" << endl;
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
SetConsoleMode(PRIVATE->hIn, PRIVATE->smode);
|
SetConsoleMode(PRIVATE->hIn, PRIVATE->smode);
|
||||||
# else
|
#else
|
||||||
tcsetattr(0, TCSANOW, &PRIVATE->sterm);
|
tcsetattr(0, TCSANOW, &PRIVATE->sterm);
|
||||||
setupTerminal(false);
|
setupTerminal(false);
|
||||||
# endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PIKbdListener::setActive(bool yes) {
|
void PIKbdListener::setActive(bool yes) {
|
||||||
is_active = yes;
|
is_active = yes;
|
||||||
if (is_active) {
|
if (is_active) {
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
SetConsoleMode(PRIVATE->hIn, PRIVATE->tmode);
|
SetConsoleMode(PRIVATE->hIn, PRIVATE->tmode);
|
||||||
# else
|
#else
|
||||||
tcsetattr(0, TCSANOW, &PRIVATE->tterm);
|
tcsetattr(0, TCSANOW, &PRIVATE->tterm);
|
||||||
setupTerminal(true);
|
setupTerminal(true);
|
||||||
# endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
SetConsoleMode(PRIVATE->hIn, PRIVATE->smode);
|
SetConsoleMode(PRIVATE->hIn, PRIVATE->smode);
|
||||||
# else
|
#else
|
||||||
tcsetattr(0, TCSANOW, &PRIVATE->sterm);
|
tcsetattr(0, TCSANOW, &PRIVATE->sterm);
|
||||||
setupTerminal(false);
|
setupTerminal(false);
|
||||||
# endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // MICRO_PIP
|
|
||||||
|
|||||||
@@ -26,19 +26,15 @@
|
|||||||
#ifndef PIKBDLISTENER_H
|
#ifndef PIKBDLISTENER_H
|
||||||
#define PIKBDLISTENER_H
|
#define PIKBDLISTENER_H
|
||||||
|
|
||||||
#include "pibase.h"
|
#include "pithread.h"
|
||||||
|
#include "pitime.h"
|
||||||
|
|
||||||
#ifndef MICRO_PIP
|
#define WAIT_FOR_EXIT \
|
||||||
|
while (!PIKbdListener::exiting) \
|
||||||
# include "pithread.h"
|
piMSleep(PIP_MIN_MSLEEP * 5); \
|
||||||
# include "pitime.h"
|
if (PIKbdListener::instance()) { \
|
||||||
|
if (!PIKbdListener::instance()->stopAndWait(PISystemTime::fromSeconds(1))) PIKbdListener::instance()->terminate(); \
|
||||||
# define WAIT_FOR_EXIT \
|
}
|
||||||
while (!PIKbdListener::exiting) \
|
|
||||||
piMSleep(PIP_MIN_MSLEEP * 5); \
|
|
||||||
if (PIKbdListener::instance()) { \
|
|
||||||
if (!PIKbdListener::instance()->stopAndWait(PISystemTime::fromSeconds(1))) PIKbdListener::instance()->terminate(); \
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PIP_EXPORT PIKbdListener: public PIThread {
|
class PIP_EXPORT PIKbdListener: public PIThread {
|
||||||
@@ -235,7 +231,7 @@ private:
|
|||||||
void run() override { readKeyboard(); }
|
void run() override { readKeyboard(); }
|
||||||
void end() override;
|
void end() override;
|
||||||
|
|
||||||
# ifndef WINDOWS
|
#ifndef WINDOWS
|
||||||
struct PIP_EXPORT EscSeq {
|
struct PIP_EXPORT EscSeq {
|
||||||
const char * seq;
|
const char * seq;
|
||||||
int key;
|
int key;
|
||||||
@@ -255,7 +251,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const EscSeq esc_seq[];
|
static const EscSeq esc_seq[];
|
||||||
# endif
|
#endif
|
||||||
|
|
||||||
PRIVATE_DECLARATION(PIP_EXPORT)
|
PRIVATE_DECLARATION(PIP_EXPORT)
|
||||||
KBFunc ret_func;
|
KBFunc ret_func;
|
||||||
@@ -309,5 +305,4 @@ REGISTER_PIVARIANTSIMPLE(PIKbdListener::KeyEvent)
|
|||||||
REGISTER_PIVARIANTSIMPLE(PIKbdListener::MouseEvent)
|
REGISTER_PIVARIANTSIMPLE(PIKbdListener::MouseEvent)
|
||||||
REGISTER_PIVARIANTSIMPLE(PIKbdListener::WheelEvent)
|
REGISTER_PIVARIANTSIMPLE(PIKbdListener::WheelEvent)
|
||||||
|
|
||||||
#endif // MICRO_PIP
|
|
||||||
#endif // PIKBDLISTENER_H
|
#endif // PIKBDLISTENER_H
|
||||||
|
|||||||
@@ -835,8 +835,8 @@ public:
|
|||||||
//! piCout << v.contains({1,4}); // true
|
//! piCout << v.contains({1,4}); // true
|
||||||
//! piCout << v.contains({1,5}); // false
|
//! piCout << v.contains({1,5}); // false
|
||||||
//! \endcode
|
//! \endcode
|
||||||
//! \~\sa \a every(), \a any(), \a entries(), \a forEach()
|
//! \~\sa \a every(), \a any(), \a entries(), \a forEach(), \a contains()
|
||||||
inline bool contains(const PIVector<T> & v, ssize_t start = 0) const {
|
inline bool containsAll(const PIVector<T> & v, ssize_t start = 0) const {
|
||||||
if (start < 0) {
|
if (start < 0) {
|
||||||
start = piv_size + start;
|
start = piv_size + start;
|
||||||
if (start < 0) start = 0;
|
if (start < 0) start = 0;
|
||||||
@@ -854,6 +854,24 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! \~english Tests if any element of `v` exists in the array.
|
||||||
|
//! \~russian Проверяет наличие хотя бы одного из элементов `v` в массиве.
|
||||||
|
//! \~\sa \a containsAll(), \a contains()
|
||||||
|
inline bool containsAny(const PIVector<T> & v, ssize_t start = 0) const {
|
||||||
|
if (start < 0) {
|
||||||
|
start = piv_size + start;
|
||||||
|
if (start < 0) start = 0;
|
||||||
|
}
|
||||||
|
for (const T & e: v) {
|
||||||
|
for (size_t i = start; i < piv_size; ++i) {
|
||||||
|
if (e == piv_data[i]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
//! \~english Count elements equal `e` in the array.
|
//! \~english Count elements equal `e` in the array.
|
||||||
//! \~russian Подсчитывает количество элементов, совпадающих с элементом `e` в массиве.
|
//! \~russian Подсчитывает количество элементов, совпадающих с элементом `e` в массиве.
|
||||||
//! \~\details
|
//! \~\details
|
||||||
@@ -1349,8 +1367,8 @@ public:
|
|||||||
alloc(piv_size + v.piv_size);
|
alloc(piv_size + v.piv_size);
|
||||||
if (os > 0) {
|
if (os > 0) {
|
||||||
memmove(reinterpret_cast<void *>(piv_data + index + v.piv_size),
|
memmove(reinterpret_cast<void *>(piv_data + index + v.piv_size),
|
||||||
reinterpret_cast<const void *>(piv_data + index),
|
reinterpret_cast<const void *>(piv_data + index),
|
||||||
os * sizeof(T));
|
os * sizeof(T));
|
||||||
}
|
}
|
||||||
newT(piv_data + index, v.piv_data, v.piv_size);
|
newT(piv_data + index, v.piv_data, v.piv_size);
|
||||||
return *this;
|
return *this;
|
||||||
@@ -1372,8 +1390,8 @@ public:
|
|||||||
alloc(piv_size + init_list.size());
|
alloc(piv_size + init_list.size());
|
||||||
if (os > 0) {
|
if (os > 0) {
|
||||||
memmove(reinterpret_cast<void *>(piv_data + index + init_list.size()),
|
memmove(reinterpret_cast<void *>(piv_data + index + init_list.size()),
|
||||||
reinterpret_cast<const void *>(piv_data + index),
|
reinterpret_cast<const void *>(piv_data + index),
|
||||||
os * sizeof(T));
|
os * sizeof(T));
|
||||||
}
|
}
|
||||||
newT(piv_data + index, init_list.begin(), init_list.size());
|
newT(piv_data + index, init_list.begin(), init_list.size());
|
||||||
return *this;
|
return *this;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*! \file pivector2d.h
|
/*! \file pivector2d.h
|
||||||
* \brief 2D wrapper around PIVector
|
* \brief 2D wrapper around PIVector
|
||||||
*
|
*
|
||||||
* This file declares PIVector
|
* This file declares PIVector2D
|
||||||
*/
|
*/
|
||||||
/*
|
/*
|
||||||
PIP - Platform Independent Primitives
|
PIP - Platform Independent Primitives
|
||||||
@@ -25,31 +25,85 @@
|
|||||||
#ifndef PIVECTOR2D_H
|
#ifndef PIVECTOR2D_H
|
||||||
#define PIVECTOR2D_H
|
#define PIVECTOR2D_H
|
||||||
|
|
||||||
|
#include "pipair.h"
|
||||||
#include "pivector.h"
|
#include "pivector.h"
|
||||||
|
|
||||||
/*! \brief 2D array,
|
//! \addtogroup Containers
|
||||||
* \details This class used to store 2D array of any type elements as plain vector.
|
//! \{
|
||||||
* You can read/write any element via operators [][], first dimension - row, second - column.
|
//! \class PIVector2D
|
||||||
* The first dimension is Row, and you can operate with Row as PIVector<T>: modify any element, assign to another Row and etc.
|
//! \brief
|
||||||
* You can't add values to array, but you can modify any elements or create another PIVector2D.
|
//! \~english 2D array container.
|
||||||
* PIVector2D has constructors from PIVector<T> and PIVector<PIVector<T> >
|
//! \~russian Двумерный контейнер-массив.
|
||||||
*/
|
//! \details
|
||||||
|
//! \~english
|
||||||
|
//! This class is used to store a 2D array of elements of any type as a single continuous block of memory (a plain PIVector).
|
||||||
|
//! Elements can be accessed using the `[][]` operators, where the first index is the row and the second is the column.
|
||||||
|
//! Rows can be manipulated as \a PIVector objects, allowing modification of individual elements or assignment of entire rows.
|
||||||
|
//! You cannot directly add or remove elements to change the dimensions of the array after construction
|
||||||
|
//! (use \a resize(), \a addRow(), \a removeRow(), \a removeColumn() instead), but you can modify the values of existing elements.
|
||||||
|
//! \~russian
|
||||||
|
//! Этот класс используется для хранения двумерного массива элементов любого типа в виде единого непрерывного блока памяти (обычного
|
||||||
|
//! \a PIVector). Доступ к элементам осуществляется с помощью операторов `[][]`, где первый индекс — это строка, а второй — столбец. Со
|
||||||
|
//! строками можно работать как с объектами \a PIVector, что позволяет изменять отдельные элементы или присваивать целые строки. Нельзя
|
||||||
|
//! напрямую добавлять или удалять элементы, чтобы изменить размеры массива после создания (используйте \a resize(), \a addRow(), \a
|
||||||
|
//! removeRow(), \a removeColumn() для этого), но можно изменять значения существующих элементов.
|
||||||
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class PIVector2D {
|
class PIVector2D {
|
||||||
public:
|
public:
|
||||||
|
//! \~english Constructs an empty 2D array. No memory is allocated.
|
||||||
|
//! \~russian Создаёт пустой двумерный массив. Память не выделяется.
|
||||||
|
//! \details
|
||||||
|
//! \~english After this constructor, \a rows() and \a cols() return 0, and \a isEmpty() returns true.
|
||||||
|
//! \~russian После этого конструктора \a rows() и \a cols() возвращают 0, а \a isEmpty() возвращает true.
|
||||||
|
//! \sa PIVector::PIVector()
|
||||||
inline PIVector2D() { rows_ = cols_ = 0; }
|
inline PIVector2D() { rows_ = cols_ = 0; }
|
||||||
|
|
||||||
|
//! \~english Constructs a 2D array with the given dimensions, filled with copies of `f`.
|
||||||
|
//! \~russian Создаёт двумерный массив заданного размера, заполненный копиями `f`.
|
||||||
|
//! \details
|
||||||
|
//! \~english The underlying storage is a single contiguous block of memory of size `rows * cols`.
|
||||||
|
//! All elements are initialized with the value `f`.
|
||||||
|
//! \~russian Внутреннее хранилище представляет собой единый непрерывный блок памяти размером `rows * cols`.
|
||||||
|
//! Все элементы инициализируются значением `f`.
|
||||||
|
//! \sa PIVector::PIVector(size_t, const T&)
|
||||||
inline PIVector2D(size_t rows, size_t cols, const T & f = T()) {
|
inline PIVector2D(size_t rows, size_t cols, const T & f = T()) {
|
||||||
rows_ = rows;
|
rows_ = rows;
|
||||||
cols_ = cols;
|
cols_ = cols;
|
||||||
mat.resize(rows * cols, f);
|
mat.resize(rows * cols, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! \~english Constructs a 2D array from an existing 1D vector, reshaping it.
|
||||||
|
//! \~russian Создаёт двумерный массив из существующего одномерного вектора, изменяя его форму.
|
||||||
|
//! \details
|
||||||
|
//! \~english The constructor copies the data from `v` into the internal flat vector.
|
||||||
|
//! If `v` is larger than `rows * cols`, the excess elements are ignored (the vector is truncated).
|
||||||
|
//! If `v` is smaller, other values filled whith default cunstructor T()
|
||||||
|
//! \~russian Конструктор копирует данные из `v` во внутренний плоский вектор.
|
||||||
|
//! Если `v` больше, чем `rows * cols`, лишние элементы игнорируются (вектор обрезается).
|
||||||
|
//! Если `v` меньше, остальные значения будут заполнены из конструктора по умолчанию T()
|
||||||
|
//! \sa PIVector::PIVector(const PIVector&), reshape()
|
||||||
inline PIVector2D(size_t rows, size_t cols, const PIVector<T> & v): rows_(rows), cols_(cols), mat(v) { mat.resize(rows * cols); }
|
inline PIVector2D(size_t rows, size_t cols, const PIVector<T> & v): rows_(rows), cols_(cols), mat(v) { mat.resize(rows * cols); }
|
||||||
|
|
||||||
|
//! \~english Move constructs a 2D array from an existing 1D vector, reshaping it.
|
||||||
|
//! \~russian Конструктор перемещения из существующего одномерного вектора, изменяя его форму.
|
||||||
|
//! \details
|
||||||
|
//! \~english The data is moved from `v` into the internal flat vector, avoiding a copy.
|
||||||
|
//! After construction, `v` is left in a valid but unspecified state.
|
||||||
|
//! \~russian Данные перемещаются из `v` во внутренний плоский вектор, что позволяет избежать копирования.
|
||||||
|
//! После завершения конструктора `v` остаётся в корректном, но неопределённом состоянии.
|
||||||
|
//! \sa PIVector::PIVector(PIVector&&)
|
||||||
inline PIVector2D(size_t rows, size_t cols, PIVector<T> && v): rows_(rows), cols_(cols), mat(std::move(v)) { mat.resize(rows * cols); }
|
inline PIVector2D(size_t rows, size_t cols, PIVector<T> && v): rows_(rows), cols_(cols), mat(std::move(v)) { mat.resize(rows * cols); }
|
||||||
|
|
||||||
|
//! \~english Constructs a 2D array from a vector of vectors (jagged array). Assumes all inner vectors have the same size.
|
||||||
|
//! \~russian Создаёт двумерный массив из вектора векторов (рваного массива). Предполагается, что все внутренние векторы имеют
|
||||||
|
//! одинаковый размер.
|
||||||
|
//! \details
|
||||||
|
//! \~english If the input is empty, the constructed array is also empty. Otherwise, the number of columns is taken from the size of the
|
||||||
|
//! first inner vector. All inner vectors are concatenated in the internal flat storage.
|
||||||
|
//! \~russian Если входной массив пуст, создаётся пустой двумерный массив. В противном случае количество столбцов берётся из размера
|
||||||
|
//! первого внутреннего вектора. Все внутренние векторы конкатенируются во внутреннем плоском хранилище. \sa PIVector::append()
|
||||||
inline PIVector2D(const PIVector<PIVector<T>> & v) {
|
inline PIVector2D(const PIVector<PIVector<T>> & v) {
|
||||||
rows_ = v.size();
|
rows_ = v.size();
|
||||||
if (rows_) {
|
if (rows_) {
|
||||||
@@ -63,22 +117,81 @@ public:
|
|||||||
if (mat.isEmpty()) rows_ = cols_ = 0;
|
if (mat.isEmpty()) rows_ = cols_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! \~english Returns the number of rows in the 2D array.
|
||||||
|
//! \~russian Возвращает количество строк в двумерном массиве.
|
||||||
|
//! \return Number of rows.
|
||||||
|
//! \details
|
||||||
|
//! \~english The result is always non-negative. If the array is empty, returns 0.
|
||||||
|
//! \~russian Результат всегда неотрицательный. Если массив пуст, возвращает 0.
|
||||||
|
//! \sa cols(), size(), PIVector::size()
|
||||||
inline size_t rows() const { return rows_; }
|
inline size_t rows() const { return rows_; }
|
||||||
|
|
||||||
|
//! \~english Returns the number of columns in the 2D array.
|
||||||
|
//! \~russian Возвращает количество столбцов в двумерном массиве.
|
||||||
|
//! \return Number of columns.
|
||||||
|
//! \details
|
||||||
|
//! \~english The result is always non-negative. If the array is empty, returns 0.
|
||||||
|
//! \~russian Результат всегда неотрицательный. Если массив пуст, возвращает 0.
|
||||||
|
//! \sa rows(), size(), PIVector::size()
|
||||||
inline size_t cols() const { return cols_; }
|
inline size_t cols() const { return cols_; }
|
||||||
|
|
||||||
|
//! \~english Returns the total number of elements (`rows * cols`).
|
||||||
|
//! \~russian Возвращает общее количество элементов (`строки * столбцы`).
|
||||||
|
//! \return Total number of elements.
|
||||||
|
//! \details
|
||||||
|
//! \~english This is equivalent to the size of the underlying flat vector.
|
||||||
|
//! \~russian Это эквивалентно размеру внутреннего плоского вектора.
|
||||||
|
//! \sa rows(), cols(), PIVector::size()
|
||||||
inline size_t size() const { return mat.size(); }
|
inline size_t size() const { return mat.size(); }
|
||||||
|
|
||||||
|
//! \~english Returns the total number of elements as a signed value.
|
||||||
|
//! \~russian Возвращает общее количество элементов в виде знакового числа.
|
||||||
|
//! \return Signed size.
|
||||||
|
//! \sa size(), PIVector::size_s()
|
||||||
inline ssize_t size_s() const { return mat.size_s(); }
|
inline ssize_t size_s() const { return mat.size_s(); }
|
||||||
|
|
||||||
|
//! \~english Returns the total number of elements (same as \a size()).
|
||||||
|
//! \~russian Возвращает общее количество элементов (то же, что и \a size()).
|
||||||
|
//! \return Total number of elements.
|
||||||
|
//! \sa size(), PIVector::length()
|
||||||
inline size_t length() const { return mat.length(); }
|
inline size_t length() const { return mat.length(); }
|
||||||
|
|
||||||
|
//! \~english Returns the number of elements that the underlying container has currently allocated space for.
|
||||||
|
//! \~russian Возвращает количество элементов, для которого сейчас выделена память во внутреннем контейнере.
|
||||||
|
//! \return Capacity of the flat vector.
|
||||||
|
//! \details
|
||||||
|
//! \~english This value may be larger than \a size(). It indicates how many elements can be added before a reallocation is needed.
|
||||||
|
//! \~russian Это значение может быть больше, чем \a size(). Оно показывает, сколько элементов можно добавить до того, как потребуется
|
||||||
|
//! перераспределение памяти. \sa reserve(), PIVector::capacity()
|
||||||
inline size_t capacity() const { return mat.capacity(); }
|
inline size_t capacity() const { return mat.capacity(); }
|
||||||
|
|
||||||
|
//! \~english Checks if the array has no elements.
|
||||||
|
//! \~russian Проверяет, пуст ли массив.
|
||||||
|
//! \return \c true if the array is empty, \c false otherwise.
|
||||||
|
//! \details
|
||||||
|
//! \~english An empty array has both rows and columns equal to 0.
|
||||||
|
//! \~russian Пустой массив имеет и строки, и столбцы равные 0.
|
||||||
|
//! \sa isNotEmpty(), PIVector::isEmpty()
|
||||||
inline bool isEmpty() const { return mat.isEmpty(); }
|
inline bool isEmpty() const { return mat.isEmpty(); }
|
||||||
|
|
||||||
|
//! \~english Checks if the array has at least one element.
|
||||||
|
//! \~russian Проверяет, не пуст ли массив.
|
||||||
|
//! \return \c true if the array is not empty, \c false otherwise.
|
||||||
|
//! \sa isEmpty(), PIVector::isNotEmpty()
|
||||||
inline bool isNotEmpty() const { return mat.isNotEmpty(); }
|
inline bool isNotEmpty() const { return mat.isNotEmpty(); }
|
||||||
|
|
||||||
|
|
||||||
|
//! \class Row
|
||||||
|
//! \brief
|
||||||
|
//! \~english Proxy class representing a single row in a \a PIVector2D for modification.
|
||||||
|
//! \~russian Прокси-класс, представляющий одну строку в \a PIVector2D для модификации.
|
||||||
|
//! \details
|
||||||
|
//! \~english Objects of this class are returned by non-const \a operator[] or \a row().
|
||||||
|
//! They provide array-like access to the elements of a specific row and allow operations such as assignment from another row or a \a
|
||||||
|
//! PIVector, searching, filling, and iteration.
|
||||||
|
//! \~russian Объекты этого класса возвращаются неконстантными операторами \a operator[] или методом \a row().
|
||||||
|
//! Они предоставляют доступ к элементам конкретной строки, подобный массиву, и позволяют выполнять такие операции, как присваивание из
|
||||||
|
//! другой строки или \a PIVector, поиск, заполнение и итерацию. \sa Col, RowConst
|
||||||
class Row {
|
class Row {
|
||||||
friend class PIVector2D<T>;
|
friend class PIVector2D<T>;
|
||||||
|
|
||||||
@@ -91,65 +204,417 @@ public:
|
|||||||
size_t st_, sz_;
|
size_t st_, sz_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
//! \~english Returns the number of columns in this row.
|
||||||
|
//! \~russian Возвращает количество столбцов в этой строке.
|
||||||
|
//! \return Row size (number of columns).
|
||||||
|
//! \sa PIVector::size()
|
||||||
inline size_t size() const { return sz_; }
|
inline size_t size() const { return sz_; }
|
||||||
|
|
||||||
|
//! \~english Accesses the element at the given column index within the row.
|
||||||
|
//! \~russian Доступ к элементу по заданному индексу столбца в строке.
|
||||||
|
//! \details
|
||||||
|
//! \~english No bounds checking is performed in release builds; use with caution.
|
||||||
|
//! \~russian В релизной сборке проверка границ не выполняется; используйте с осторожностью.
|
||||||
|
//! \sa PIVector::operator[]
|
||||||
inline T & operator[](size_t index) { return (*p_)[st_ + index]; }
|
inline T & operator[](size_t index) { return (*p_)[st_ + index]; }
|
||||||
|
|
||||||
|
//! \~english Const access to the element at the given column index within the row.
|
||||||
|
//! \~russian Константный доступ к элементу по заданному индексу столбца в строке.
|
||||||
|
//! \sa operator[] (non-const)
|
||||||
inline const T & operator[](size_t index) const { return (*p_)[st_ + index]; }
|
inline const T & operator[](size_t index) const { return (*p_)[st_ + index]; }
|
||||||
|
|
||||||
|
//! \~english Returns a pointer to the row data starting at an optional offset.
|
||||||
|
//! \~russian Возвращает указатель на данные строки, начиная с опционального смещения.
|
||||||
|
//! \details
|
||||||
|
//! \~english The pointer can be used for direct memory operations. It remains valid as long as the underlying 2D array is not
|
||||||
|
//! reallocated.
|
||||||
|
//! \~russian Указатель можно использовать для прямых операций с памятью. Он остаётся действительным, пока не произойдёт
|
||||||
|
//! перераспределение памяти внутреннего двумерного массива. \sa PIVector::data()
|
||||||
inline T * data(size_t index = 0) { return p_->data(st_ + index); }
|
inline T * data(size_t index = 0) { return p_->data(st_ + index); }
|
||||||
|
|
||||||
|
//! \~english Returns a const pointer to the row data starting at an optional offset.
|
||||||
|
//! \~russian Возвращает константный указатель на данные строки, начиная с опционального смещения.
|
||||||
|
//! \sa data() (non-const)
|
||||||
inline const T * data(size_t index = 0) const { return p_->data(st_ + index); }
|
inline const T * data(size_t index = 0) const { return p_->data(st_ + index); }
|
||||||
|
|
||||||
|
//! \~english Assigns the contents of another Row to this row.
|
||||||
|
//! \~russian Присваивает этой строке содержимое другой строки.
|
||||||
|
//! \details
|
||||||
|
//! \~english Only the minimum of the two row sizes is copied; if this row is shorter, excess elements in `other` are ignored.
|
||||||
|
//! \~russian Копируется только минимум из размеров двух строк; если эта строка короче, лишние элементы из `other` игнорируются.
|
||||||
|
//! \sa PIVector::operator=
|
||||||
inline Row & operator=(const Row & other) {
|
inline Row & operator=(const Row & other) {
|
||||||
if (p_ == other.p_ && st_ == other.st_) return *this;
|
if (p_ == other.p_ && st_ == other.st_) return *this;
|
||||||
const size_t sz = piMin<size_t>(sz_, other.sz_);
|
const size_t sz = piMin<size_t>(sz_, other.sz_);
|
||||||
p_->_copyRaw(p_->data(st_), other.data(), sz);
|
p_->_copyRaw(p_->data(st_), other.data(), sz);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! \~english Assigns the contents of a \a PIVector to this row.
|
||||||
|
//! \~russian Присваивает этой строке содержимое \a PIVector.
|
||||||
|
//! \details
|
||||||
|
//! \~english Only the minimum of the row size and vector size is copied.
|
||||||
|
//! \~russian Копируется только минимум из размера строки и размера вектора.
|
||||||
|
//! \sa PIVector::operator=
|
||||||
inline Row & operator=(const PIVector<T> & other) {
|
inline Row & operator=(const PIVector<T> & other) {
|
||||||
const size_t sz = piMin<size_t>(sz_, other.size());
|
const size_t sz = piMin<size_t>(sz_, other.size());
|
||||||
p_->_copyRaw(p_->data(st_), other.data(), sz);
|
p_->_copyRaw(p_->data(st_), other.data(), sz);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! \~english Converts the row to a \a PIVector.
|
||||||
|
//! \~russian Преобразует строку в \a PIVector.
|
||||||
|
//! \sa PIVector::PIVector(const T*, size_t)
|
||||||
inline PIVector<T> toVector() const { return PIVector<T>(p_->data(st_), sz_); }
|
inline PIVector<T> toVector() const { return PIVector<T>(p_->data(st_), sz_); }
|
||||||
|
|
||||||
|
//! \~english Returns the first index of element `e` in the row, starting from `start`.
|
||||||
|
//! \~russian Возвращает первый индекс элемента `e` в строке, начиная с позиции `start`.
|
||||||
|
//! \details
|
||||||
|
//! \~english See \a PIVector::indexOf() for details on negative start handling.
|
||||||
|
//! \~russian Подробнее об обработке отрицательного `start` см. \a PIVector::indexOf().
|
||||||
|
//! \sa PIVector::indexOf()
|
||||||
|
inline ssize_t indexOf(const T & e, ssize_t start = 0) const {
|
||||||
|
if (start < 0) start = 0;
|
||||||
|
for (size_t i = (size_t)start; i < sz_; ++i) {
|
||||||
|
if ((*p_)[st_ + i] == e) return (ssize_t)i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Returns the last index of element `e` in the row, searching backwards from `start`.
|
||||||
|
//! \~russian Возвращает последний индекс элемента `e` в строке, выполняя поиск в обратном направлении от `start`.
|
||||||
|
//! \sa PIVector::lastIndexOf()
|
||||||
|
inline ssize_t lastIndexOf(const T & e, ssize_t start = -1) const {
|
||||||
|
ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start;
|
||||||
|
for (ssize_t i = from; i >= 0; --i) {
|
||||||
|
if ((*p_)[st_ + i] == e) return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Returns the first index where the predicate `test` returns true, starting from `start`.
|
||||||
|
//! \~russian Возвращает первый индекс, для которого предикат `test` возвращает true, начиная с `start`.
|
||||||
|
//! \sa PIVector::indexWhere()
|
||||||
|
inline ssize_t indexWhere(std::function<bool(const T & e)> test, ssize_t start = 0) const {
|
||||||
|
if (start < 0) start = 0;
|
||||||
|
for (size_t i = (size_t)start; i < sz_; ++i) {
|
||||||
|
if (test((*p_)[st_ + i])) return (ssize_t)i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Returns the last index where the predicate `test` returns true, searching backwards from `start`.
|
||||||
|
//! \~russian Возвращает последний индекс, для которого предикат `test` возвращает true,
|
||||||
|
//! выполняя поиск в обратном направлении от `start`.
|
||||||
|
//! \sa PIVector::lastIndexWhere()
|
||||||
|
inline ssize_t lastIndexWhere(std::function<bool(const T & e)> test, ssize_t start = -1) const {
|
||||||
|
ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start;
|
||||||
|
for (ssize_t i = from; i >= 0; --i) {
|
||||||
|
if (test((*p_)[st_ + i])) return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Applies a function to each element of the row (modifiable).
|
||||||
|
//! \~russian Применяет функцию к каждому элементу строки (с возможностью изменения).
|
||||||
|
//! \param func Function that takes a reference to T.
|
||||||
|
//! \details
|
||||||
|
//! \~english The function can modify the elements.
|
||||||
|
//! \~russian Функция может изменять элементы.
|
||||||
|
//! \sa PIVector::forEach()
|
||||||
|
inline void forEach(std::function<void(T &)> func) {
|
||||||
|
for (size_t i = 0; i < sz_; ++i) {
|
||||||
|
func((*p_)[st_ + i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Applies a function to each element of the row (read-only).
|
||||||
|
//! \~russian Применяет функцию к каждому элементу строки (только чтение).
|
||||||
|
//! \details
|
||||||
|
//! \~english The function can't modify the elements.
|
||||||
|
//! \~russian Функция не может изменять элементы.
|
||||||
|
//! \sa forEach (modifiable)
|
||||||
|
inline void forEach(std::function<void(const T &)> func) const {
|
||||||
|
for (size_t i = 0; i < sz_; ++i) {
|
||||||
|
func((*p_)[st_ + i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Fills the row with copies of `value`.
|
||||||
|
//! \~russian Заполняет строку копиями `value`.
|
||||||
|
//! \sa PIVector::fill()
|
||||||
|
inline void fill(const T & value) {
|
||||||
|
for (size_t i = 0; i < sz_; ++i) {
|
||||||
|
(*p_)[st_ + i] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Checks if the row contains the element `e`.
|
||||||
|
//! \~russian Проверяет, содержит ли строка элемент `e`.
|
||||||
|
//! \sa PIVector::contains()
|
||||||
|
inline bool contains(const T & e, ssize_t start = 0) const { return indexOf(e, start) != -1; }
|
||||||
|
|
||||||
|
//! \~english Counts occurrences of `e` in the row.
|
||||||
|
//! \~russian Подсчитывает количество вхождений `e` в строке.
|
||||||
|
//! \sa PIVector::entries()
|
||||||
|
inline int entries(const T & e, ssize_t start = 0) const {
|
||||||
|
if (start < 0) start = 0;
|
||||||
|
int count = 0;
|
||||||
|
for (size_t i = (size_t)start; i < sz_; ++i) {
|
||||||
|
if ((*p_)[st_ + i] == e) ++count;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Counts elements in the row that pass the `test`.
|
||||||
|
//! \~russian Подсчитывает элементы в строке, проходящие `test`.
|
||||||
|
//! \sa PIVector::entries(std::function)
|
||||||
|
inline int entries(std::function<bool(const T & e)> test, ssize_t start = 0) const {
|
||||||
|
if (start < 0) start = 0;
|
||||||
|
int count = 0;
|
||||||
|
for (size_t i = (size_t)start; i < sz_; ++i) {
|
||||||
|
if (test((*p_)[st_ + i])) ++count;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Tests if any element in the row passes the `test`.
|
||||||
|
//! \~russian Проверяет, проходит ли какой-либо элемент в строке `test`.
|
||||||
|
//! \sa PIVector::any()
|
||||||
|
inline bool any(std::function<bool(const T & e)> test) const {
|
||||||
|
for (size_t i = 0; i < sz_; ++i) {
|
||||||
|
if (test((*p_)[st_ + i])) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Tests if all elements in the row pass the `test`.
|
||||||
|
//! \~russian Проверяет, проходят ли все элементы в строке `test`.
|
||||||
|
//! \sa PIVector::every()
|
||||||
|
inline bool every(std::function<bool(const T & e)> test) const {
|
||||||
|
for (size_t i = 0; i < sz_; ++i) {
|
||||||
|
if (!test((*p_)[st_ + i])) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//! \class Col
|
||||||
|
//! \brief
|
||||||
|
//! \~english Proxy class representing a single column in a \a PIVector2D for modification.
|
||||||
|
//! \~russian Прокси-класс, представляющий один столбец в \a PIVector2D для модификации.
|
||||||
|
//! \details
|
||||||
|
//! \~english Objects of this class are returned by non-const \a col(). They provide column-wise access and operations similar to \a
|
||||||
|
//! Row.
|
||||||
|
//! \~russian Объекты этого класса возвращаются неконстантным методом \a col(). Они предоставляют доступ к столбцам и операции,
|
||||||
|
//! аналогичные \a Row. \sa Row, ColConst
|
||||||
class Col {
|
class Col {
|
||||||
friend class PIVector2D<T>;
|
friend class PIVector2D<T>;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
inline Col(PIVector2D<T> * p, size_t row): p_(&(p->mat)) {
|
inline Col(PIVector2D<T> * p, size_t col): p_(&(p->mat)) {
|
||||||
step_ = p->cols_;
|
step_ = p->cols_;
|
||||||
row_ = row;
|
col_ = col;
|
||||||
sz_ = p->rows_;
|
sz_ = p->rows_;
|
||||||
}
|
}
|
||||||
PIVector<T> * p_;
|
PIVector<T> * p_;
|
||||||
size_t step_, row_, sz_;
|
size_t step_, col_, sz_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
//! \~english Returns the size of the column (number of rows).
|
||||||
|
//! \~russian Возвращает размер столбца (количество строк).
|
||||||
inline size_t size() const { return sz_; }
|
inline size_t size() const { return sz_; }
|
||||||
inline T & operator[](size_t index) { return (*p_)[index * step_ + row_]; }
|
|
||||||
inline const T & operator[](size_t index) const { return (*p_)[index * step_ + row_]; }
|
//! \~english Accesses the element at the given row index within the column.
|
||||||
inline T * data(size_t index = 0) { return p_->data(index * step_ + row_); }
|
//! \~russian Доступ к элементу по заданному индексу строки в столбце.
|
||||||
inline const T * data(size_t index = 0) const { return p_->data(index * step_ + row_); }
|
//! \return Reference to the element.
|
||||||
|
inline T & operator[](size_t index) { return (*p_)[index * step_ + col_]; }
|
||||||
|
|
||||||
|
//! \~english Const access to the element at the given row index within the column.
|
||||||
|
//! \~russian Константный доступ к элементу по заданному индексу строки в столбце.
|
||||||
|
inline const T & operator[](size_t index) const { return (*p_)[index * step_ + col_]; }
|
||||||
|
|
||||||
|
//! \~english Returns a pointer to the column data starting at an optional row offset.
|
||||||
|
//! \~russian Возвращает указатель на данные столбца, начиная с опционального смещения по строкам.
|
||||||
|
//! \details
|
||||||
|
//! \~english Note that column elements are not stored contiguously in memory, so this pointer cannot be used to iterate over the
|
||||||
|
//! whole column.
|
||||||
|
//! \~russian Обратите внимание, что элементы столбца не хранятся в памяти непрерывно, поэтому этот указатель нельзя использовать
|
||||||
|
//! для итерации по всему столбцу.
|
||||||
|
inline T * data(size_t index = 0) { return p_->data(index * step_ + col_); }
|
||||||
|
|
||||||
|
//! \~english Returns a const pointer to the column data starting at an optional row offset.
|
||||||
|
//! \~russian Возвращает константный указатель на данные столбца, начиная с опционального смещения по строкам.
|
||||||
|
inline const T * data(size_t index = 0) const { return p_->data(index * step_ + col_); }
|
||||||
|
|
||||||
|
//! \~english Assigns the contents of another Col to this column.
|
||||||
|
//! \~russian Присваивает этому столбцу содержимое другого столбца.
|
||||||
inline Col & operator=(const Col & other) {
|
inline Col & operator=(const Col & other) {
|
||||||
if (p_ == other.p_ && row_ == other.row_) return *this;
|
if (p_ == other.p_ && col_ == other.col_) return *this;
|
||||||
const size_t sz = piMin<size_t>(sz_, other.sz_);
|
const size_t sz = piMin<size_t>(sz_, other.sz_);
|
||||||
for (int i = 0; i < sz; ++i)
|
for (size_t i = 0; i < sz; ++i)
|
||||||
(*p_)[i * step_ + row_] = other[i];
|
(*p_)[i * step_ + col_] = other[i];
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
inline Row & operator=(const PIVector<T> & other) {
|
|
||||||
|
//! \~english Assigns the contents of a \a PIVector to this column.
|
||||||
|
//! \~russian Присваивает этому столбцу содержимое \a PIVector.
|
||||||
|
inline Col & operator=(const PIVector<T> & other) {
|
||||||
const size_t sz = piMin<size_t>(sz_, other.size());
|
const size_t sz = piMin<size_t>(sz_, other.size());
|
||||||
for (int i = 0; i < sz; ++i)
|
for (size_t i = 0; i < sz; ++i)
|
||||||
(*p_)[i * step_ + row_] = other[i];
|
(*p_)[i * step_ + col_] = other[i];
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! \~english Converts the column to a \a PIVector.
|
||||||
|
//! \~russian Преобразует столбец в \a PIVector.
|
||||||
inline PIVector<T> toVector() const {
|
inline PIVector<T> toVector() const {
|
||||||
PIVector<T> ret;
|
PIVector<T> ret;
|
||||||
ret.reserve(sz_);
|
ret.reserve(sz_);
|
||||||
for (size_t i = 0; i < sz_; i++)
|
for (size_t i = 0; i < sz_; i++)
|
||||||
ret << (*p_)[i * step_ + row_];
|
ret << (*p_)[i * step_ + col_];
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! \~english Returns the first index of element `e` in the row, starting from `start`.
|
||||||
|
//! \~russian Возвращает первый индекс элемента `e` в строке, начиная с позиции `start`.
|
||||||
|
//! \details
|
||||||
|
//! \~english See \a PIVector::indexOf() for details on negative start handling.
|
||||||
|
//! \~russian Подробнее об обработке отрицательного `start` см. \a PIVector::indexOf().
|
||||||
|
//! \sa PIVector::indexOf()
|
||||||
|
inline ssize_t indexOf(const T & e, ssize_t start = 0) const {
|
||||||
|
if (start < 0) start = 0;
|
||||||
|
for (size_t i = (size_t)start; i < sz_; ++i) {
|
||||||
|
if ((*p_)[i * step_ + col_] == e) return (ssize_t)i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Returns the last index of element `e` in the row, searching backwards from `start`.
|
||||||
|
//! \~russian Возвращает последний индекс элемента `e` в строке, выполняя поиск в обратном направлении от `start`.
|
||||||
|
//! \sa PIVector::lastIndexOf()
|
||||||
|
inline ssize_t lastIndexOf(const T & e, ssize_t start = -1) const {
|
||||||
|
ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start;
|
||||||
|
for (ssize_t i = from; i >= 0; --i) {
|
||||||
|
if ((*p_)[i * step_ + col_] == e) return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Returns the first index where the predicate `test` returns true, starting from `start`.
|
||||||
|
//! \~russian Возвращает первый индекс, для которого предикат `test` возвращает true, начиная с `start`.
|
||||||
|
//! \sa PIVector::indexWhere()
|
||||||
|
inline ssize_t indexWhere(std::function<bool(const T & e)> test, ssize_t start = 0) const {
|
||||||
|
if (start < 0) start = 0;
|
||||||
|
for (size_t i = (size_t)start; i < sz_; ++i) {
|
||||||
|
if (test((*p_)[i * step_ + col_])) return (ssize_t)i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Returns the last index where the predicate `test` returns true, searching backwards from `start`.
|
||||||
|
//! \~russian Возвращает последний индекс, для которого предикат `test` возвращает true,
|
||||||
|
//! выполняя поиск в обратном направлении от `start`.
|
||||||
|
//! \sa PIVector::lastIndexWhere()
|
||||||
|
inline ssize_t lastIndexWhere(std::function<bool(const T & e)> test, ssize_t start = -1) const {
|
||||||
|
ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start;
|
||||||
|
for (ssize_t i = from; i >= 0; --i) {
|
||||||
|
if (test((*p_)[i * step_ + col_])) return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Applies a function to each element of the column (modifiable).
|
||||||
|
//! \~russian Применяет функцию к каждому элементу столбца (с возможностью изменения).
|
||||||
|
//! \details
|
||||||
|
//! \~english The function can modify the elements.
|
||||||
|
//! \~russian Функция может изменять элементы.
|
||||||
|
//! \sa PIVector::forEach()
|
||||||
|
inline void forEach(std::function<void(T &)> func) {
|
||||||
|
for (size_t i = 0; i < sz_; ++i) {
|
||||||
|
func((*p_)[i * step_ + col_]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Applies a function to each element of the column (read-only).
|
||||||
|
//! \~russian Применяет функцию к каждому элементу столбца (только чтение).
|
||||||
|
//! \details
|
||||||
|
//! \~english The function can't modify the elements.
|
||||||
|
//! \~russian Функция не может изменять элементы.
|
||||||
|
//! \sa forEach (modifiable)
|
||||||
|
inline void forEach(std::function<void(const T &)> func) const {
|
||||||
|
for (size_t i = 0; i < sz_; ++i) {
|
||||||
|
func((*p_)[i * step_ + col_]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Fills the column with copies of `value`.
|
||||||
|
//! \~russian Заполняет столбец копиями `value`.
|
||||||
|
//! \sa PIVector::fill()
|
||||||
|
inline void fill(const T & value) {
|
||||||
|
for (size_t i = 0; i < sz_; ++i) {
|
||||||
|
(*p_)[i * step_ + col_] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Checks if the column contains the element `e`.
|
||||||
|
//! \~russian Проверяет, содержит ли столбец элемент `e`.
|
||||||
|
//! \sa PIVector::contains()
|
||||||
|
inline bool contains(const T & e, ssize_t start = 0) const { return indexOf(e, start) != -1; }
|
||||||
|
|
||||||
|
//! \~english Counts occurrences of `e` in the column.
|
||||||
|
//! \~russian Подсчитывает количество вхождений `e` в столбце.
|
||||||
|
//! \sa PIVector::entries()
|
||||||
|
inline int entries(const T & e, ssize_t start = 0) const {
|
||||||
|
if (start < 0) start = 0;
|
||||||
|
int count = 0;
|
||||||
|
for (size_t i = (size_t)start; i < sz_; ++i) {
|
||||||
|
if ((*p_)[i * step_ + col_] == e) ++count;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Counts elements in the column that pass the `test`.
|
||||||
|
//! \~russian Подсчитывает элементы в столбце, проходящие `test`.
|
||||||
|
//! \sa PIVector::entries(std::function)
|
||||||
|
inline int entries(std::function<bool(const T & e)> test, ssize_t start = 0) const {
|
||||||
|
if (start < 0) start = 0;
|
||||||
|
int count = 0;
|
||||||
|
for (size_t i = (size_t)start; i < sz_; ++i) {
|
||||||
|
if (test((*p_)[i * step_ + col_])) ++count;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Tests if any element in the column passes the `test`.
|
||||||
|
//! \~russian Проверяет, проходит ли какой-либо элемент в столбце `test`.
|
||||||
|
//! \sa PIVector::any()
|
||||||
|
inline bool any(std::function<bool(const T & e)> test) const {
|
||||||
|
for (size_t i = 0; i < sz_; ++i) {
|
||||||
|
if (test((*p_)[i * step_ + col_])) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Tests if all elements in the column pass the `test`.
|
||||||
|
//! \~russian Проверяет, проходят ли все элементы в столбце `test`.
|
||||||
|
//! \sa PIVector::every()
|
||||||
|
inline bool every(std::function<bool(const T & e)> test) const {
|
||||||
|
for (size_t i = 0; i < sz_; ++i) {
|
||||||
|
if (!test((*p_)[i * step_ + col_])) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//! \class RowConst
|
||||||
|
//! \brief
|
||||||
|
//! \~english Proxy class representing a single read-only row in a \a PIVector2D.
|
||||||
|
//! \~russian Прокси-класс, представляющий одну строку в \a PIVector2D только для чтения.
|
||||||
|
//! \details
|
||||||
|
//! \~english Returned by const \a operator[] or \a row(). Provides const access to row elements.
|
||||||
|
//! \~russian Возвращается константными версиями \a operator[] или \a row(). Предоставляет константный доступ к элементам строки.
|
||||||
|
//! \sa Row, ColConst
|
||||||
class RowConst {
|
class RowConst {
|
||||||
friend class PIVector2D<T>;
|
friend class PIVector2D<T>;
|
||||||
|
|
||||||
@@ -162,73 +627,374 @@ public:
|
|||||||
size_t st_, sz_;
|
size_t st_, sz_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
inline RowConst(const PIVector2D<T>::Row & r): p_(r.p_) {
|
||||||
|
st_ = r.st_;
|
||||||
|
sz_ = r.sz_;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Size of the row (number of columns).
|
||||||
|
//! \~russian Размер строки (количество столбцов).
|
||||||
inline size_t size() const { return sz_; }
|
inline size_t size() const { return sz_; }
|
||||||
|
|
||||||
|
//! \~english Const access to the element at the given column index within the row.
|
||||||
|
//! \~russian Константный доступ к элементу по заданному индексу столбца в строке.
|
||||||
inline const T & operator[](size_t index) const { return (*p_)[st_ + index]; }
|
inline const T & operator[](size_t index) const { return (*p_)[st_ + index]; }
|
||||||
|
|
||||||
|
//! \~english Returns a const pointer to the row data starting at an optional offset.
|
||||||
|
//! \~russian Возвращает константный указатель на данные строки, начиная с опционального смещения.
|
||||||
inline const T * data(size_t index = 0) const { return p_->data(st_ + index); }
|
inline const T * data(size_t index = 0) const { return p_->data(st_ + index); }
|
||||||
|
|
||||||
|
//! \~english Converts the row to a \a PIVector.
|
||||||
|
//! \~russian Преобразует строку в \a PIVector.
|
||||||
inline PIVector<T> toVector() const { return PIVector<T>(p_->data(st_), sz_); }
|
inline PIVector<T> toVector() const { return PIVector<T>(p_->data(st_), sz_); }
|
||||||
|
|
||||||
|
//! \~english Returns the first index of element `e` in the row, starting from `start`.
|
||||||
|
//! \~russian Возвращает первый индекс элемента `e` в строке, начиная с позиции `start`.
|
||||||
|
//! \details
|
||||||
|
//! \~english See \a PIVector::indexOf() for details on negative start handling.
|
||||||
|
//! \~russian Подробнее об обработке отрицательного `start` см. \a PIVector::indexOf().
|
||||||
|
//! \sa PIVector::indexOf()
|
||||||
|
inline ssize_t indexOf(const T & e, ssize_t start = 0) const {
|
||||||
|
if (start < 0) start = 0;
|
||||||
|
for (size_t i = (size_t)start; i < sz_; ++i) {
|
||||||
|
if ((*p_)[st_ + i] == e) return (ssize_t)i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Returns the last index of element `e` in the row, searching backwards from `start`.
|
||||||
|
//! \~russian Возвращает последний индекс элемента `e` в строке, выполняя поиск в обратном направлении от `start`.
|
||||||
|
//! \return Index if found, -1 otherwise.
|
||||||
|
//! \sa PIVector::lastIndexOf()
|
||||||
|
inline ssize_t lastIndexOf(const T & e, ssize_t start = -1) const {
|
||||||
|
ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start;
|
||||||
|
for (ssize_t i = from; i >= 0; --i) {
|
||||||
|
if ((*p_)[st_ + i] == e) return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Returns the first index where the predicate `test` returns true, starting from `start`.
|
||||||
|
//! \~russian Возвращает первый индекс, для которого предикат `test` возвращает true, начиная с `start`.
|
||||||
|
//! \sa PIVector::indexWhere()
|
||||||
|
inline ssize_t indexWhere(std::function<bool(const T & e)> test, ssize_t start = 0) const {
|
||||||
|
if (start < 0) start = 0;
|
||||||
|
for (size_t i = (size_t)start; i < sz_; ++i) {
|
||||||
|
if (test((*p_)[st_ + i])) return (ssize_t)i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Returns the last index where the predicate `test` returns true, searching backwards from `start`.
|
||||||
|
//! \~russian Возвращает последний индекс, для которого предикат `test` возвращает true,
|
||||||
|
//! выполняя поиск в обратном направлении от `start`.
|
||||||
|
//! \sa PIVector::lastIndexWhere()
|
||||||
|
inline ssize_t lastIndexWhere(std::function<bool(const T & e)> test, ssize_t start = -1) const {
|
||||||
|
ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start;
|
||||||
|
for (ssize_t i = from; i >= 0; --i) {
|
||||||
|
if (test((*p_)[st_ + i])) return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Applies a function to each element of the row (read-only).
|
||||||
|
//! \~russian Применяет функцию к каждому элементу строки (только чтение).
|
||||||
|
//! \details
|
||||||
|
//! \~english The function can't modify the elements.
|
||||||
|
//! \~russian Функция не может изменять элементы.
|
||||||
|
//! \sa forEach (modifiable)
|
||||||
|
inline void forEach(std::function<void(const T &)> func) const {
|
||||||
|
for (size_t i = 0; i < sz_; ++i) {
|
||||||
|
func((*p_)[st_ + i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Checks if the row contains the element `e`.
|
||||||
|
//! \~russian Проверяет, содержит ли строка элемент `e`.
|
||||||
|
//! \sa PIVector::contains()
|
||||||
|
inline bool contains(const T & e, ssize_t start = 0) const { return indexOf(e, start) != -1; }
|
||||||
|
|
||||||
|
//! \~english Counts occurrences of `e` in the row.
|
||||||
|
//! \~russian Подсчитывает количество вхождений `e` в строке.
|
||||||
|
//! \sa PIVector::entries()
|
||||||
|
inline int entries(const T & e, ssize_t start = 0) const {
|
||||||
|
if (start < 0) start = 0;
|
||||||
|
int count = 0;
|
||||||
|
for (size_t i = (size_t)start; i < sz_; ++i) {
|
||||||
|
if ((*p_)[st_ + i] == e) ++count;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Counts elements in the row that pass the `test`.
|
||||||
|
//! \~russian Подсчитывает элементы в строке, проходящие `test`.
|
||||||
|
//! \sa PIVector::entries(std::function)
|
||||||
|
inline int entries(std::function<bool(const T & e)> test, ssize_t start = 0) const {
|
||||||
|
if (start < 0) start = 0;
|
||||||
|
int count = 0;
|
||||||
|
for (size_t i = (size_t)start; i < sz_; ++i) {
|
||||||
|
if (test((*p_)[st_ + i])) ++count;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Tests if any element in the row passes the `test`.
|
||||||
|
//! \~russian Проверяет, проходит ли какой-либо элемент в строке `test`.
|
||||||
|
//! \sa PIVector::any()
|
||||||
|
inline bool any(std::function<bool(const T & e)> test) const {
|
||||||
|
for (size_t i = 0; i < sz_; ++i) {
|
||||||
|
if (test((*p_)[st_ + i])) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Tests if all elements in the row pass the `test`.
|
||||||
|
//! \~russian Проверяет, проходят ли все элементы в строке `test`.
|
||||||
|
//! \sa PIVector::every()
|
||||||
|
inline bool every(std::function<bool(const T & e)> test) const {
|
||||||
|
for (size_t i = 0; i < sz_; ++i) {
|
||||||
|
if (!test((*p_)[st_ + i])) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//! \class ColConst
|
||||||
|
//! \brief
|
||||||
|
//! \~english Proxy class representing a single read-only column in a \a PIVector2D.
|
||||||
|
//! \~russian Прокси-класс, представляющий один столбец в \a PIVector2D только для чтения.
|
||||||
|
//! \details
|
||||||
|
//! \~english Returned by const \a col(). Provides const access to column elements.
|
||||||
|
//! \~russian Возвращается константной версией \a col(). Предоставляет константный доступ к элементам столбца.
|
||||||
|
//! \sa Col, RowConst
|
||||||
class ColConst {
|
class ColConst {
|
||||||
friend class PIVector2D<T>;
|
friend class PIVector2D<T>;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
inline ColConst(const PIVector2D<T> * p, size_t row): p_(&(p->mat)) {
|
inline ColConst(const PIVector2D<T> * p, size_t col): p_(&(p->mat)) {
|
||||||
step_ = p->cols_;
|
step_ = p->cols_;
|
||||||
row_ = row;
|
col_ = col;
|
||||||
sz_ = p->rows_;
|
sz_ = p->rows_;
|
||||||
}
|
}
|
||||||
const PIVector<T> * p_;
|
const PIVector<T> * p_;
|
||||||
size_t step_, row_, sz_;
|
size_t step_, col_, sz_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
inline size_t size() const { return p_->rows_; }
|
inline ColConst(const PIVector2D<T>::Col & c): p_(c.p_) {
|
||||||
inline const T & operator[](size_t index) const { return (*p_)[index * step_ + row_]; }
|
step_ = c.step_;
|
||||||
inline const T * data(size_t index = 0) const { return p_->data(index * step_ + row_); }
|
col_ = c.col_;
|
||||||
|
sz_ = c.sz_;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Size of the column (number of rows).
|
||||||
|
//! \~russian Размер столбца (количество строк).
|
||||||
|
inline size_t size() const { return sz_; }
|
||||||
|
|
||||||
|
//! \~english Const access to the element at the given row index within the column.
|
||||||
|
//! \~russian Константный доступ к элементу по заданному индексу строки в столбце.
|
||||||
|
inline const T & operator[](size_t index) const { return (*p_)[index * step_ + col_]; }
|
||||||
|
|
||||||
|
//! \~english Returns a const pointer to the column data starting at an optional row offset.
|
||||||
|
//! \~russian Возвращает константный указатель на данные столбца, начиная с опционального смещения по строкам.
|
||||||
|
inline const T * data(size_t index = 0) const { return p_->data(index * step_ + col_); }
|
||||||
|
|
||||||
|
//! \~english Converts the column to a \a PIVector.
|
||||||
|
//! \~russian Преобразует столбец в \a PIVector.
|
||||||
inline PIVector<T> toVector() const {
|
inline PIVector<T> toVector() const {
|
||||||
PIVector<T> ret;
|
PIVector<T> ret;
|
||||||
ret.reserve(sz_);
|
ret.reserve(sz_);
|
||||||
for (int i = 0; i < size(); i++)
|
for (size_t i = 0; i < size(); i++)
|
||||||
ret << (*p_)[i * step_ + row_];
|
ret << (*p_)[i * step_ + col_];
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! \~english Returns the first index of element `e` in the row, starting from `start`.
|
||||||
|
//! \~russian Возвращает первый индекс элемента `e` в строке, начиная с позиции `start`.
|
||||||
|
//! \details
|
||||||
|
//! \~english See \a PIVector::indexOf() for details on negative start handling.
|
||||||
|
//! \~russian Подробнее об обработке отрицательного `start` см. \a PIVector::indexOf().
|
||||||
|
//! \sa PIVector::indexOf()
|
||||||
|
inline ssize_t indexOf(const T & e, ssize_t start = 0) const {
|
||||||
|
if (start < 0) start = 0;
|
||||||
|
for (size_t i = (size_t)start; i < sz_; ++i) {
|
||||||
|
if ((*p_)[i * step_ + col_] == e) return (ssize_t)i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Returns the last index of element `e` in the row, searching backwards from `start`.
|
||||||
|
//! \~russian Возвращает последний индекс элемента `e` в строке, выполняя поиск в обратном направлении от `start`.
|
||||||
|
//! \sa PIVector::lastIndexOf()
|
||||||
|
inline ssize_t lastIndexOf(const T & e, ssize_t start = -1) const {
|
||||||
|
ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start;
|
||||||
|
for (ssize_t i = from; i >= 0; --i) {
|
||||||
|
if ((*p_)[i * step_ + col_] == e) return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Returns the first index where the predicate `test` returns true, starting from `start`.
|
||||||
|
//! \~russian Возвращает первый индекс, для которого предикат `test` возвращает true, начиная с `start`.
|
||||||
|
//! \sa PIVector::indexWhere()
|
||||||
|
inline ssize_t indexWhere(std::function<bool(const T & e)> test, ssize_t start = 0) const {
|
||||||
|
if (start < 0) start = 0;
|
||||||
|
for (size_t i = (size_t)start; i < sz_; ++i) {
|
||||||
|
if (test((*p_)[i * step_ + col_])) return (ssize_t)i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Returns the last index where the predicate `test` returns true, searching backwards from `start`.
|
||||||
|
//! \~russian Возвращает последний индекс, для которого предикат `test` возвращает true,
|
||||||
|
//! выполняя поиск в обратном направлении от `start`.
|
||||||
|
//! \sa PIVector::lastIndexWhere()
|
||||||
|
inline ssize_t lastIndexWhere(std::function<bool(const T & e)> test, ssize_t start = -1) const {
|
||||||
|
ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start;
|
||||||
|
for (ssize_t i = from; i >= 0; --i) {
|
||||||
|
if (test((*p_)[i * step_ + col_])) return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Applies a function to each element of the column (read-only).
|
||||||
|
//! \~russian Применяет функцию к каждому элементу столбца (только чтение).
|
||||||
|
//! \details
|
||||||
|
//! \~english The function can't modify the elements.
|
||||||
|
//! \~russian Функция не может изменять элементы.
|
||||||
|
//! \sa forEach (modifiable)
|
||||||
|
inline void forEach(std::function<void(const T &)> func) const {
|
||||||
|
for (size_t i = 0; i < sz_; ++i) {
|
||||||
|
func((*p_)[i * step_ + col_]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Checks if the column contains the element `e`.
|
||||||
|
//! \~russian Проверяет, содержит ли столбец элемент `e`.
|
||||||
|
//! \sa PIVector::contains()
|
||||||
|
inline bool contains(const T & e, ssize_t start = 0) const { return indexOf(e, start) != -1; }
|
||||||
|
|
||||||
|
//! \~english Counts occurrences of `e` in the column.
|
||||||
|
//! \~russian Подсчитывает количество вхождений `e` в столбце.
|
||||||
|
//! \sa PIVector::entries()
|
||||||
|
inline int entries(const T & e, ssize_t start = 0) const {
|
||||||
|
if (start < 0) start = 0;
|
||||||
|
int count = 0;
|
||||||
|
for (size_t i = (size_t)start; i < sz_; ++i) {
|
||||||
|
if ((*p_)[i * step_ + col_] == e) ++count;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Counts elements in the column that pass the `test`.
|
||||||
|
//! \~russian Подсчитывает элементы в столбце, проходящие `test`.
|
||||||
|
//! \sa PIVector::entries(std::function)
|
||||||
|
inline int entries(std::function<bool(const T & e)> test, ssize_t start = 0) const {
|
||||||
|
if (start < 0) start = 0;
|
||||||
|
int count = 0;
|
||||||
|
for (size_t i = (size_t)start; i < sz_; ++i) {
|
||||||
|
if (test((*p_)[i * step_ + col_])) ++count;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Tests if any element in the column passes the `test`.
|
||||||
|
//! \~russian Проверяет, проходит ли какой-либо элемент в столбце `test`.
|
||||||
|
//! \sa PIVector::any()
|
||||||
|
inline bool any(std::function<bool(const T & e)> test) const {
|
||||||
|
for (size_t i = 0; i < sz_; ++i) {
|
||||||
|
if (test((*p_)[i * step_ + col_])) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Tests if all elements in the column pass the `test`.
|
||||||
|
//! \~russian Проверяет, проходят ли все элементы в столбце `test`.
|
||||||
|
//! \sa PIVector::every()
|
||||||
|
inline bool every(std::function<bool(const T & e)> test) const {
|
||||||
|
for (size_t i = 0; i < sz_; ++i) {
|
||||||
|
if (!test((*p_)[i * step_ + col_])) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//! \~english Returns a reference to the element at the given row and column.
|
||||||
|
//! \~russian Возвращает ссылку на элемент по заданной строке и столбцу.
|
||||||
|
//! \details
|
||||||
|
//! \~english No bounds checking is performed.
|
||||||
|
//! \~russian Проверка границ не выполняется.
|
||||||
|
//! \sa at() (const version), PIVector::operator[]
|
||||||
inline T & element(size_t row, size_t col) { return mat[row * cols_ + col]; }
|
inline T & element(size_t row, size_t col) { return mat[row * cols_ + col]; }
|
||||||
|
|
||||||
|
//! \~english Returns a const reference to the element at the given row and column.
|
||||||
|
//! \~russian Возвращает константную ссылку на элемент по заданной строке и столбцу.
|
||||||
inline const T & element(size_t row, size_t col) const { return mat[row * cols_ + col]; }
|
inline const T & element(size_t row, size_t col) const { return mat[row * cols_ + col]; }
|
||||||
|
|
||||||
|
//! \~english Returns a const reference to the element at the given row and column (bounds-checked only in debug).
|
||||||
|
//! \~russian Возвращает константную ссылку на элемент по заданной строке и столбцу (проверка границ только в отладочном режиме).
|
||||||
inline const T & at(size_t row, size_t col) const { return mat[row * cols_ + col]; }
|
inline const T & at(size_t row, size_t col) const { return mat[row * cols_ + col]; }
|
||||||
|
|
||||||
|
//! \~english Returns a proxy object for the row at the given index for modification.
|
||||||
|
//! \~russian Возвращает прокси-объект для строки по заданному индексу для модификации.
|
||||||
|
//! \sa row(), Col
|
||||||
inline Row operator[](size_t index) { return Row(this, index); }
|
inline Row operator[](size_t index) { return Row(this, index); }
|
||||||
|
|
||||||
|
//! \~english Returns a proxy object for the row at the given index for read-only access.
|
||||||
|
//! \~russian Возвращает прокси-объект для строки по заданному индексу только для чтения.
|
||||||
inline RowConst operator[](size_t index) const { return RowConst(this, index); }
|
inline RowConst operator[](size_t index) const { return RowConst(this, index); }
|
||||||
|
|
||||||
|
//! \~english Returns a pointer to the underlying flat data starting at an optional offset.
|
||||||
|
//! \~russian Возвращает указатель на внутренние плоские данные, начиная с опционального смещения.
|
||||||
|
//! \sa PIVector::data()
|
||||||
inline T * data(size_t index = 0) { return mat.data(index); }
|
inline T * data(size_t index = 0) { return mat.data(index); }
|
||||||
|
|
||||||
|
//! \~english Returns a const pointer to the underlying flat data starting at an optional offset.
|
||||||
|
//! \~russian Возвращает константный указатель на внутренние плоские данные, начиная с опционального смещения.
|
||||||
inline const T * data(size_t index = 0) const { return mat.data(index); }
|
inline const T * data(size_t index = 0) const { return mat.data(index); }
|
||||||
|
|
||||||
|
|
||||||
|
//! \~english Returns a proxy object for the row at the given index for modification.
|
||||||
|
//! \~russian Возвращает прокси-объект для строки по заданному индексу для модификации.
|
||||||
|
//! \sa operator[]
|
||||||
inline Row row(size_t index) { return Row(this, index); }
|
inline Row row(size_t index) { return Row(this, index); }
|
||||||
|
|
||||||
|
//! \~english Returns a proxy object for the row at the given index for read-only access.
|
||||||
|
//! \~russian Возвращает прокси-объект для строки по заданному индексу только для чтения.
|
||||||
inline RowConst row(size_t index) const { return RowConst(this, index); }
|
inline RowConst row(size_t index) const { return RowConst(this, index); }
|
||||||
|
|
||||||
|
//! \~english Returns a proxy object for the column at the given index for modification.
|
||||||
|
//! \~russian Возвращает прокси-объект для столбца по заданному индексу для модификации.
|
||||||
|
//! \sa col() const
|
||||||
inline Col col(size_t index) { return Col(this, index); }
|
inline Col col(size_t index) { return Col(this, index); }
|
||||||
|
|
||||||
|
//! \~english Returns a proxy object for the column at the given index for read-only access.
|
||||||
|
//! \~russian Возвращает прокси-объект для столбца по заданному индексу только для чтения.
|
||||||
inline ColConst col(size_t index) const { return ColConst(this, index); }
|
inline ColConst col(size_t index) const { return ColConst(this, index); }
|
||||||
inline PIVector2D<T> & setRow(size_t row, const Row & other) {
|
|
||||||
const size_t sz = piMin<size_t>(cols_, other.sz_);
|
//! \~english Replaces a row with the contents of a read-only RowConst object.
|
||||||
mat._copyRaw(mat.data(cols_ * row), other.data(), sz);
|
//! \~russian Заменяет строку содержимым объекта RowConst только для чтения.
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
inline PIVector2D<T> & setRow(size_t row, const RowConst & other) {
|
inline PIVector2D<T> & setRow(size_t row, const RowConst & other) {
|
||||||
const size_t sz = piMin<size_t>(cols_, other.sz_);
|
const size_t sz = piMin<size_t>(cols_, other.sz_);
|
||||||
mat._copyRaw(mat.data(cols_ * row), other.data(), sz);
|
mat._copyRaw(mat.data(cols_ * row), other.data(), sz);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! \~english Replaces a row with the contents of a \a PIVector.
|
||||||
|
//! \~russian Заменяет строку содержимым \a PIVector.
|
||||||
inline PIVector2D<T> & setRow(size_t row, const PIVector<T> & other) {
|
inline PIVector2D<T> & setRow(size_t row, const PIVector<T> & other) {
|
||||||
const size_t sz = piMin<size_t>(cols_, other.size());
|
const size_t sz = piMin<size_t>(cols_, other.size());
|
||||||
mat._copyRaw(mat.data(cols_ * row), other.data(), sz);
|
mat._copyRaw(mat.data(cols_ * row), other.data(), sz);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
inline PIVector2D<T> & addRow(const Row & other) {
|
|
||||||
if (cols_ == 0) cols_ = other.sz_;
|
//! \~english Appends a new row to the bottom of the array from another Row object.
|
||||||
const size_t sz = piMin<size_t>(cols_, other.sz_);
|
//! \~russian Добавляет новую строку в конец массива из другого объекта Row.
|
||||||
const size_t ps = mat.size();
|
//! \details
|
||||||
mat.resize(mat.size() + cols_);
|
//! \~english If the array was empty, its column count is set to the size of the source row.
|
||||||
mat._copyRaw(mat.data(ps), other.data(), sz);
|
//! Otherwise, only `min(cols(), other.size())` elements are copied; the rest of the new row is default-initialized.
|
||||||
rows_++;
|
//! \~russian Если массив был пуст, количество столбцов устанавливается равным размеру исходной строки.
|
||||||
return *this;
|
//! В противном случае копируется только `min(cols(), other.size())` элементов; остальные элементы новой строки инициализируются по
|
||||||
}
|
//! умолчанию. \sa PIVector::push_back()
|
||||||
inline PIVector2D<T> & addRow(const RowConst & other) {
|
inline PIVector2D<T> & addRow(const RowConst & other) {
|
||||||
if (cols_ == 0) cols_ = other.sz_;
|
if (cols_ == 0) cols_ = other.sz_;
|
||||||
const size_t sz = piMin<size_t>(cols_, other.sz_);
|
const size_t sz = piMin<size_t>(cols_, other.sz_);
|
||||||
@@ -238,6 +1004,9 @@ public:
|
|||||||
rows_++;
|
rows_++;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! \~english Appends a new row to the bottom of the array from a \a PIVector.
|
||||||
|
//! \~russian Добавляет новую строку в конец массива из \a PIVector.
|
||||||
inline PIVector2D<T> & addRow(const PIVector<T> & other) {
|
inline PIVector2D<T> & addRow(const PIVector<T> & other) {
|
||||||
if (cols_ == 0) cols_ = other.size();
|
if (cols_ == 0) cols_ = other.size();
|
||||||
const size_t sz = piMin<size_t>(cols_, other.size());
|
const size_t sz = piMin<size_t>(cols_, other.size());
|
||||||
@@ -248,34 +1017,120 @@ public:
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline PIVector2D<T> & resize(size_t rows, size_t cols, const T & f = T()) {
|
//! \~english Appends a new column to the right of the array from a \a ColConst.
|
||||||
mat.resize(rows * cols_, f);
|
//! \~russian Добавляет новую строку в конец массива из \a ColConst.
|
||||||
rows_ = rows;
|
inline PIVector2D<T> & addColumn(const ColConst & other) {
|
||||||
const int cs = (cols - cols_);
|
if (other.size() == 0) return *this;
|
||||||
if (cs < 0) {
|
if (size() == 0) {
|
||||||
for (size_t r = 0; r < rows; ++r) {
|
_resizeRaw(other.size(), 1);
|
||||||
mat.remove(r * cols + cols, -cs);
|
for (size_t r = 0; r < other.size(); ++r) {
|
||||||
|
element(r, 0) = other[r];
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t oldCols = cols_;
|
||||||
|
const size_t newCols = oldCols + 1;
|
||||||
|
const size_t newSize = rows_ * newCols;
|
||||||
|
|
||||||
|
mat._resizeRaw(newSize);
|
||||||
|
|
||||||
|
for (size_t r = rows_ - 1; r > 0; --r) {
|
||||||
|
T * src = mat.data(r * oldCols);
|
||||||
|
T * dst = mat.data(r * newCols);
|
||||||
|
memmove(dst, src, oldCols * sizeof(T));
|
||||||
|
if (r < other.size()) {
|
||||||
|
mat._copyRaw(&(dst[oldCols]), &(other[r]), 1);
|
||||||
|
} else {
|
||||||
|
const T tmp = T();
|
||||||
|
mat._copyRaw(&(dst[oldCols]), &tmp, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mat.resize(rows * cols, f);
|
mat._copyRaw(mat.data(oldCols), &(other[0]), 1);
|
||||||
if (!mat.isEmpty()) {
|
|
||||||
if (cs > 0) {
|
cols_ = newCols;
|
||||||
for (size_t r = 0; r < rows_; ++r) {
|
|
||||||
for (int i = 0; i < cs; ++i)
|
|
||||||
mat.insert(r * cols + cols_, mat.take_back());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cols_ = cols;
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! \~english Appends a new column to the right of the array from a \a PIVector.
|
||||||
|
//! \~russian Добавляет новую строку в конец массива из \a PIVector.
|
||||||
|
inline PIVector2D<T> & addColumn(const PIVector<T> & other) {
|
||||||
|
if (other.size() == 0) return *this;
|
||||||
|
if (size() == 0) {
|
||||||
|
_resizeRaw(other.size(), 1);
|
||||||
|
for (size_t r = 0; r < other.size(); ++r) {
|
||||||
|
element(r, 0) = other[r];
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t oldCols = cols_;
|
||||||
|
const size_t newCols = oldCols + 1;
|
||||||
|
const size_t newSize = rows_ * newCols;
|
||||||
|
|
||||||
|
mat._resizeRaw(newSize);
|
||||||
|
|
||||||
|
for (size_t r = rows_ - 1; r > 0; --r) {
|
||||||
|
T * src = mat.data(r * oldCols);
|
||||||
|
T * dst = mat.data(r * newCols);
|
||||||
|
memmove(dst, src, oldCols * sizeof(T));
|
||||||
|
if (r < other.size()) {
|
||||||
|
mat._copyRaw(&(dst[oldCols]), &(other[r]), 1);
|
||||||
|
} else {
|
||||||
|
const T tmp = T();
|
||||||
|
mat._copyRaw(&(dst[oldCols]), &tmp, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mat._copyRaw(mat.data(oldCols), &(other[0]), 1);
|
||||||
|
|
||||||
|
cols_ = newCols;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Resizes the 2D array to new dimensions.
|
||||||
|
//! \~russian Изменяет размер двумерного массива.
|
||||||
|
//! \details
|
||||||
|
//! \~english If the new dimensions are larger, new elements are appended and filled with copies of `f`.
|
||||||
|
//! If they are smaller, the array is truncated (excess elements are destroyed). The underlying memory may be reallocated.
|
||||||
|
//! \~russian Если новые размеры больше текущих, новые элементы добавляются в конец и заполняются копиями `f`.
|
||||||
|
//! Если новые размеры меньше, массив усекается (лишние элементы уничтожаются). Внутренняя память может быть перераспределена.
|
||||||
|
//! \code
|
||||||
|
//! PIVector2D<int> mat(2, 3, 0); // 2x3 matrix filled with 0
|
||||||
|
//! mat.resize(3, 4, 1); // becomes 3x4, new elements filled with 1
|
||||||
|
//! \endcode
|
||||||
|
//! \sa PIVector::resize()
|
||||||
|
inline PIVector2D<T> & resize(size_t rows, size_t cols, const T & f = T()) {
|
||||||
|
if (rows == rows_ && cols == cols_) return *this;
|
||||||
|
PIVector2D<T> tmp(rows, cols, f);
|
||||||
|
size_t copyRows = piMin(rows_, rows);
|
||||||
|
size_t copyCols = piMin(cols_, cols);
|
||||||
|
for (size_t r = 0; r < copyRows; ++r) {
|
||||||
|
for (size_t c = 0; c < copyCols; ++c) {
|
||||||
|
tmp.element(r, c) = element(r, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
swap(tmp);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Equality operator.
|
||||||
|
//! \~russian Оператор равенства.
|
||||||
|
//! \sa PIVector::operator==
|
||||||
inline bool operator==(const PIVector2D<T> & t) const {
|
inline bool operator==(const PIVector2D<T> & t) const {
|
||||||
if (cols_ != t.cols_ || rows_ != t.rows_) return false;
|
if (cols_ != t.cols_ || rows_ != t.rows_) return false;
|
||||||
return mat == t.mat;
|
return mat == t.mat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! \~english Inequality operator.
|
||||||
|
//! \~russian Оператор неравенства.
|
||||||
inline bool operator!=(const PIVector2D<T> & t) const { return !(*this == t); }
|
inline bool operator!=(const PIVector2D<T> & t) const { return !(*this == t); }
|
||||||
|
|
||||||
|
//! \~english Converts the 2D array to a vector of vectors (PIVector<PIVector<T>>).
|
||||||
|
//! \~russian Преобразует двумерный массив в вектор векторов (PIVector<PIVector<T>>).
|
||||||
|
//! \details
|
||||||
|
//! \~english Each row vector is a copy of the corresponding row.
|
||||||
|
//! \~russian Каждый вектор-строка является копией соответствующей строки.
|
||||||
|
//! \sa fromVectors(), PIVector::PIVector(const T*, size_t)
|
||||||
inline PIVector<PIVector<T>> toVectors() const {
|
inline PIVector<PIVector<T>> toVectors() const {
|
||||||
PIVector<PIVector<T>> ret;
|
PIVector<PIVector<T>> ret;
|
||||||
ret.reserve(rows_);
|
ret.reserve(rows_);
|
||||||
@@ -284,18 +1139,31 @@ public:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! \~english Returns a const reference to the underlying flat \a PIVector.
|
||||||
|
//! \~russian Возвращает константную ссылку на внутренний плоский \a PIVector.
|
||||||
|
inline const PIVector<T> & asPlainVector() const { return mat; }
|
||||||
|
|
||||||
|
//! \~english Returns a reference to the underlying flat \a PIVector.
|
||||||
|
//! \~russian Возвращает ссылку на внутренний плоский \a PIVector.
|
||||||
|
inline PIVector<T> & asPlainVector() { return mat; }
|
||||||
|
|
||||||
|
//! \~english Returns a copy of the underlying flat \a PIVector.
|
||||||
|
//! \~russian Возвращает копию внутреннего плоского \a PIVector.
|
||||||
inline PIVector<T> toPlainVector() const { return mat; }
|
inline PIVector<T> toPlainVector() const { return mat; }
|
||||||
|
|
||||||
inline PIVector<T> & plainVector() { return mat; }
|
//! \~english Swaps this 2D array with another.
|
||||||
|
//! \~russian Меняет местами этот двумерный массив с другим.
|
||||||
inline const PIVector<T> & plainVector() const { return mat; }
|
//! \details
|
||||||
|
//! \~english Swaps the flat vectors and the dimension members. Very fast, no memory allocation.
|
||||||
|
//! \~russian Обменивает внутренние плоские векторы и члены, хранящие размеры. Очень быстро, без выделения памяти.
|
||||||
|
//! \sa PIVector::swap()
|
||||||
inline void swap(PIVector2D<T> & other) {
|
inline void swap(PIVector2D<T> & other) {
|
||||||
mat.swap(other.mat);
|
mat.swap(other.mat);
|
||||||
piSwap<size_t>(rows_, other.rows_);
|
piSwap<size_t>(rows_, other.rows_);
|
||||||
piSwap<size_t>(cols_, other.cols_);
|
piSwap<size_t>(cols_, other.cols_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! \internal
|
||||||
template<typename T1 = T, typename std::enable_if<std::is_trivially_copyable<T1>::value, int>::type = 0>
|
template<typename T1 = T, typename std::enable_if<std::is_trivially_copyable<T1>::value, int>::type = 0>
|
||||||
inline PIVector2D<T> & _resizeRaw(size_t r, size_t c) {
|
inline PIVector2D<T> & _resizeRaw(size_t r, size_t c) {
|
||||||
rows_ = r;
|
rows_ = r;
|
||||||
@@ -304,29 +1172,369 @@ public:
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! \~english Clears the array, removing all elements and setting dimensions to 0.
|
||||||
|
//! \~russian Очищает массив, удаляя все элементы и устанавливая размеры в 0.
|
||||||
|
//! \details
|
||||||
|
//! \~english The capacity of the underlying flat vector may remain unchanged.
|
||||||
|
//! \~russian Ёмкость внутреннего плоского вектора может остаться неизменной.
|
||||||
|
//! \sa PIVector::clear()
|
||||||
inline void clear() {
|
inline void clear() {
|
||||||
rows_ = cols_ = 0;
|
rows_ = cols_ = 0;
|
||||||
mat.clear();
|
mat.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ST>
|
|
||||||
inline PIVector2D<ST> map(std::function<ST(const T & e)> f) const {
|
//! \~english Checks if the underlying flat vector contains the element `e`.
|
||||||
return PIVector2D<ST>(rows_, cols_, mat.map(f));
|
//! \~russian Проверяет, содержит ли внутренний плоский вектор элемент `e`.
|
||||||
|
//! \sa PIVector::contains()
|
||||||
|
inline bool contains(const T & e) const { return mat.contains(e); }
|
||||||
|
|
||||||
|
//! \~english Counts occurrences of `e` in the underlying flat vector.
|
||||||
|
//! \~russian Подсчитывает количество вхождений `e` во внутреннем плоском векторе.
|
||||||
|
//! \sa PIVector::entries()
|
||||||
|
inline int entries(const T & e) const { return mat.entries(e); }
|
||||||
|
|
||||||
|
//! \~english Counts elements in the flat vector that pass the `test`.
|
||||||
|
//! \~russian Подсчитывает элементы в плоском векторе, проходящие `test`.
|
||||||
|
//! \sa PIVector::entries(std::function)
|
||||||
|
inline int entries(std::function<bool(const T & e)> test) const { return mat.entries(test); }
|
||||||
|
|
||||||
|
|
||||||
|
//! \~english Returns the first index (row, col) of `e` in the 2D array.
|
||||||
|
//! \~russian Возвращает первый индекс (строка, столбец) элемента `e` в двумерном массиве.
|
||||||
|
//! \sa PIVector::indexOf()
|
||||||
|
inline PIPair<ssize_t, ssize_t> indexOf(const T & e) const {
|
||||||
|
ssize_t flat = mat.indexOf(e);
|
||||||
|
if (flat < 0 || cols_ == 0) return PIPair<ssize_t, ssize_t>(-1, -1);
|
||||||
|
return PIPair<ssize_t, ssize_t>(flat / cols_, flat % cols_);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void forEach(std::function<void(const T &)> f) const { mat.forEach(f); }
|
//! \~english Returns the first index (row, col) in the 2D array that passes the `test`.
|
||||||
|
//! \~russian Возвращает первый индекс (строка, столбец) в двумерном массиве, проходящий `test`.
|
||||||
|
//! \sa PIVector::indexWhere()
|
||||||
|
inline PIPair<ssize_t, ssize_t> indexWhere(std::function<bool(const T & e)> test, ssize_t start = 0) const {
|
||||||
|
ssize_t flat = mat.indexWhere(test, start);
|
||||||
|
if (flat < 0 || cols_ == 0) return PIPair<ssize_t, ssize_t>(-1, -1);
|
||||||
|
return PIPair<ssize_t, ssize_t>(flat / cols_, flat % cols_);
|
||||||
|
}
|
||||||
|
|
||||||
inline PIVector2D<T> & forEach(std::function<void(T &)> f) {
|
//! \~english Returns the last index (row, col) of `e` in the 2D array.
|
||||||
mat.forEach(f);
|
//! \~russian Возвращает последний индекс (строка, столбец) элемента `e` в двумерном массиве.
|
||||||
|
//! \sa PIVector::lastIndexOf()
|
||||||
|
inline PIPair<ssize_t, ssize_t> lastIndexOf(const T & e, ssize_t start = -1) const {
|
||||||
|
ssize_t flat = mat.lastIndexOf(e, start);
|
||||||
|
if (flat < 0 || cols_ == 0) return PIPair<ssize_t, ssize_t>(-1, -1);
|
||||||
|
return PIPair<ssize_t, ssize_t>(flat / cols_, flat % cols_);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Returns the last index (row, col) in the 2D array that passes the `test`.
|
||||||
|
//! \~russian Возвращает последний индекс (строка, столбец) в двумерном массиве, проходящий `test`.
|
||||||
|
//! \sa PIVector::lastIndexWhere()
|
||||||
|
inline PIPair<ssize_t, ssize_t> lastIndexWhere(std::function<bool(const T & e)> test, ssize_t start = -1) const {
|
||||||
|
ssize_t flat = mat.lastIndexWhere(test, start);
|
||||||
|
if (flat < 0 || cols_ == 0) return PIPair<ssize_t, ssize_t>(-1, -1);
|
||||||
|
return PIPair<ssize_t, ssize_t>(flat / cols_, flat % cols_);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! \~english Tests if any element in the flat vector passes the `test`.
|
||||||
|
//! \~russian Проверяет, проходит ли какой-либо элемент в плоском векторе `test`.
|
||||||
|
//! \sa PIVector::any()
|
||||||
|
inline bool any(std::function<bool(const T & e)> test) const { return mat.any(test); }
|
||||||
|
|
||||||
|
//! \~english Tests if all elements in the flat vector pass the `test`.
|
||||||
|
//! \~russian Проверяет, проходят ли все элементы в плоском векторе `test`.
|
||||||
|
//! \sa PIVector::every()
|
||||||
|
inline bool every(std::function<bool(const T & e)> test) const { return mat.every(test); }
|
||||||
|
|
||||||
|
//! \~english Fills the entire 2D array with copies of `e`.
|
||||||
|
//! \~russian Заполняет весь двумерный массив копиями `e`.
|
||||||
|
//! \sa PIVector::fill()
|
||||||
|
inline PIVector2D<T> & fill(const T & e = T()) {
|
||||||
|
mat.fill(e);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! \~english Fills the entire 2D array using a generator function `f` based on flat index.
|
||||||
|
//! \~russian Заполняет весь двумерный массив, используя функцию-генератор `f` на основе плоского индекса.
|
||||||
|
//! \sa PIVector::fill(std::function)
|
||||||
|
inline PIVector2D<T> & fill(std::function<T(size_t i)> f) {
|
||||||
|
mat.fill(f);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Same as \a fill().
|
||||||
|
//! \~russian То же, что и \a fill().
|
||||||
|
inline PIVector2D<T> & assign(const T & e = T()) { return fill(e); }
|
||||||
|
|
||||||
|
//! \~english Assigns new size and fills with value.
|
||||||
|
//! \~russian Задаёт новый размер и заполняет значением.
|
||||||
|
//! \sa PIVector::assign(size_t, const T&)
|
||||||
|
inline PIVector2D<T> & assign(size_t rows, size_t cols, const T & f = T()) {
|
||||||
|
mat.assign(rows * cols, f);
|
||||||
|
rows_ = rows;
|
||||||
|
cols_ = cols;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! \~english Returns a transposed 2D array (rows become columns and vice versa).
|
||||||
|
//! \~russian Возвращает транспонированный двумерный массив (строки становятся столбцами и наоборот).
|
||||||
|
//! \details
|
||||||
|
//! \~english The element at (r, c) in the original becomes at (c, r) in the result.
|
||||||
|
//! \~russian Элемент (r, c) исходного массива становится элементом (c, r) в результате.
|
||||||
|
//! \code
|
||||||
|
//! PIVector2D<int> mat(2, 3, ...);
|
||||||
|
//! auto t = mat.transposed(); // now 3x2
|
||||||
|
//! \endcode
|
||||||
|
inline PIVector2D<T> transposed() const {
|
||||||
|
if (isEmpty()) return PIVector2D<T>();
|
||||||
|
PIVector2D<T> result(cols_, rows_);
|
||||||
|
for (size_t r = 0; r < rows_; ++r) {
|
||||||
|
for (size_t c = 0; c < cols_; ++c) {
|
||||||
|
result.element(c, r) = element(r, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Reverses the order of rows in place.
|
||||||
|
//! \~russian Изменяет порядок строк на обратный на месте.
|
||||||
|
//! \sa reverseColumns(), PIVector::reverse()
|
||||||
|
inline PIVector2D<T> & reverseRows() {
|
||||||
|
const size_t half = rows_ / 2;
|
||||||
|
for (size_t i = 0; i < half; ++i) {
|
||||||
|
T * row1 = data(i * cols_);
|
||||||
|
T * row2 = data((rows_ - 1 - i) * cols_);
|
||||||
|
for (size_t j = 0; j < cols_; ++j) {
|
||||||
|
piSwap(row1[j], row2[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Reverses the order of columns in each row in place.
|
||||||
|
//! \~russian Изменяет порядок столбцов в каждой строке на обратный на месте.
|
||||||
|
//! \sa reverseRows(), PIVector::reverse()
|
||||||
|
inline PIVector2D<T> & reverseColumns() {
|
||||||
|
for (size_t r = 0; r < rows_; ++r) {
|
||||||
|
Row currentRow = row(r);
|
||||||
|
const size_t half = cols_ / 2;
|
||||||
|
for (size_t c = 0; c < half; ++c) {
|
||||||
|
piSwap<T>(currentRow[c], currentRow[cols_ - 1 - c]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Returns a sub-2D array (a range of rows and columns).
|
||||||
|
//! \~russian Возвращает подмассив (диапазон строк и столбцов).
|
||||||
|
//! \details
|
||||||
|
//! \~english If the range exceeds the array boundaries, it is clipped. If rowCount or colCount is 0, an empty array is returned.
|
||||||
|
//! \~russian Если диапазон выходит за границы массива, он обрезается. Если rowCount или colCount равны 0, возвращается пустой массив.
|
||||||
|
//! \sa PIVector::getRange()
|
||||||
|
inline PIVector2D<T> getRange(size_t rowStart, size_t rowCount, size_t colStart, size_t colCount) const {
|
||||||
|
if (rowStart >= rows_ || colStart >= cols_ || rowCount == 0 || colCount == 0) return PIVector2D<T>();
|
||||||
|
size_t actualRowCount = piMin<size_t>(rowCount, rows_ - rowStart);
|
||||||
|
size_t actualColCount = piMin<size_t>(colCount, cols_ - colStart);
|
||||||
|
|
||||||
|
PIVector2D<T> result(actualRowCount, actualColCount);
|
||||||
|
for (size_t r = 0; r < actualRowCount; ++r) {
|
||||||
|
for (size_t c = 0; c < actualColCount; ++c) {
|
||||||
|
result.element(r, c) = element(rowStart + r, colStart + c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Applies a function to each element and returns a new 2D array of a different type.
|
||||||
|
//! \~russian Применяет функцию к каждому элементу и возвращает новый двумерный массив другого типа.
|
||||||
|
//! \details
|
||||||
|
//! \~english The original array is not modified.
|
||||||
|
//! \~russian Исходный массив не изменяется.
|
||||||
|
//! \sa PIVector::map()
|
||||||
|
template<typename ST>
|
||||||
|
inline PIVector2D<ST> map(std::function<ST(const T & e)> f) const {
|
||||||
|
return PIVector2D<ST>(rows_, cols_, mat.template map<ST>(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Applies a function (with row and col indices) to each element and returns a new 2D array.
|
||||||
|
//! \~russian Применяет функцию (с индексами строки и столбца) к каждому элементу и возвращает новый двумерный массив.
|
||||||
|
//! \sa PIVector::mapIndexed()
|
||||||
|
template<typename ST>
|
||||||
|
inline PIVector2D<ST> mapIndexed(std::function<ST(size_t row, size_t col, const T & e)> f) const {
|
||||||
|
PIVector<ST> mappedMat;
|
||||||
|
mappedMat.reserve(size());
|
||||||
|
for (size_t r = 0; r < rows_; ++r) {
|
||||||
|
for (size_t c = 0; c < cols_; ++c) {
|
||||||
|
mappedMat << f(r, c, element(r, c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return PIVector2D<ST>(rows_, cols_, std::move(mappedMat));
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Applies a function to each row (modifiable).
|
||||||
|
//! \~russian Применяет функцию к каждой строке (с возможностью изменения).
|
||||||
|
//! \sa forEachRow() const, PIVector::forEach()
|
||||||
|
inline PIVector2D<T> & forEachRow(std::function<void(Row)> f) {
|
||||||
|
for (size_t r = 0; r < rows_; ++r)
|
||||||
|
f(row(r));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Applies a function to each row (read-only).
|
||||||
|
//! \~russian Применяет функцию к каждой строке (только чтение).
|
||||||
|
inline void forEachRow(std::function<void(RowConst)> f) const {
|
||||||
|
for (size_t r = 0; r < rows_; ++r)
|
||||||
|
f(row(r));
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Applies a function to each column (modifiable).
|
||||||
|
//! \~russian Применяет функцию к каждому столбцу (с возможностью изменения).
|
||||||
|
inline PIVector2D<T> & forEachColumn(std::function<void(Col)> f) {
|
||||||
|
for (size_t c = 0; c < cols_; ++c)
|
||||||
|
f(col(c));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Applies a function to each column (read-only).
|
||||||
|
//! \~russian Применяет функцию к каждому столбцу (только чтение).
|
||||||
|
//! \param f Function taking a \a ColConst.
|
||||||
|
inline void forEachColumn(std::function<void(ColConst)> f) const {
|
||||||
|
for (size_t c = 0; c < cols_; ++c)
|
||||||
|
f(col(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Accumulates a value across all elements.
|
||||||
|
//! \~russian Аккумулирует значение по всем элементам.
|
||||||
|
//! \sa PIVector::reduce()
|
||||||
|
template<typename ST>
|
||||||
|
inline ST reduce(std::function<ST(const T & e, const ST & acc)> f, const ST & initial = ST()) const {
|
||||||
|
return mat.template reduce<ST>(f, initial);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Accumulates a value across all elements with indices.
|
||||||
|
//! \~russian Аккумулирует значение по всем элементам с индексами.
|
||||||
|
//! \sa PIVector::reduceIndexed()
|
||||||
|
template<typename ST>
|
||||||
|
inline ST reduceIndexed(std::function<ST(size_t row, size_t col, const T & e, const ST & acc)> f, const ST & initial = ST()) const {
|
||||||
|
ST ret(initial);
|
||||||
|
for (size_t r = 0; r < rows_; ++r) {
|
||||||
|
for (size_t c = 0; c < cols_; ++c) {
|
||||||
|
ret = f(r, c, element(r, c), ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Removes a row from the 2D array.
|
||||||
|
//! \~russian Удаляет строку из двумерного массива.
|
||||||
|
//! \details
|
||||||
|
//! \~english The elements of the specified row are destroyed, and all subsequent rows are shifted up.
|
||||||
|
//! If the last row is removed and the array becomes empty, \a cols() is set to 0.
|
||||||
|
//! \~russian Элементы указанной строки уничтожаются, а все последующие строки сдвигаются вверх.
|
||||||
|
//! Если удаляется последняя строка и массив становится пустым, \a cols() устанавливается в 0.
|
||||||
|
//! \sa removeColumn(), PIVector::remove()
|
||||||
|
inline PIVector2D<T> & removeRow(size_t row) {
|
||||||
|
if (row >= rows_) return *this;
|
||||||
|
mat.remove(row * cols_, cols_);
|
||||||
|
rows_--;
|
||||||
|
if (rows_ == 0) cols_ = 0;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Removes a column from the 2D array.
|
||||||
|
//! \~russian Удаляет столбец из двумерного массива.
|
||||||
|
//! \details
|
||||||
|
//! \~english This operation is more expensive than removing a row because elements must be moved.
|
||||||
|
//! \~russian Эта операция дороже, чем удаление строки, поскольку требуется перемещение элементов.
|
||||||
|
//! \sa removeRow(), PIVector::remove()
|
||||||
|
inline PIVector2D<T> & removeColumn(size_t col) {
|
||||||
|
if (col >= cols_ || rows_ == 0) return *this;
|
||||||
|
PIVector2D<T> result(rows_, cols_ - 1);
|
||||||
|
for (size_t r = 0; r < rows_; ++r) {
|
||||||
|
for (size_t c = 0, nc = 0; c < cols_; ++c) {
|
||||||
|
if (c == col) continue;
|
||||||
|
result.element(r, nc++) = element(r, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
swap(result);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Removes all rows that satisfy a condition.
|
||||||
|
//! \~russian Удаляет все строки, удовлетворяющие условию.
|
||||||
|
//! \details
|
||||||
|
//! \~english Rows are removed from the bottom to avoid index shifting issues.
|
||||||
|
//! \~russian Строки удаляются снизу вверх, чтобы избежать проблем со смещением индексов.
|
||||||
|
//! \sa removeColumnsWhere(), PIVector::removeWhere()
|
||||||
|
inline PIVector2D<T> & removeRowsWhere(std::function<bool(const RowConst &)> test) {
|
||||||
|
ssize_t r = rows_;
|
||||||
|
while (--r >= 0) {
|
||||||
|
if (test(RowConst(this, r))) {
|
||||||
|
removeRow(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Removes all columns that satisfy a condition.
|
||||||
|
//! \~russian Удаляет все столбцы, удовлетворяющие условию.
|
||||||
|
//! \sa removeRowsWhere()
|
||||||
|
inline PIVector2D<T> & removeColumnsWhere(std::function<bool(const ColConst &)> test) {
|
||||||
|
ssize_t c = cols_;
|
||||||
|
while (--c >= 0) {
|
||||||
|
if (test(ColConst(this, c))) {
|
||||||
|
removeColumn(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! \~english Returns a new 2D array containing only the rows that pass the test.
|
||||||
|
//! \~russian Возвращает новый двумерный массив, содержащий только строки, прошедшие проверку.
|
||||||
|
//! \sa filterColumns(), PIVector::filter()
|
||||||
|
inline PIVector2D<T> filterRows(std::function<bool(const RowConst &)> test) const {
|
||||||
|
PIVector2D<T> result;
|
||||||
|
for (size_t r = 0; r < rows_; ++r) {
|
||||||
|
RowConst currentRow = row(r);
|
||||||
|
if (test(currentRow)) {
|
||||||
|
result.addRow(currentRow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Returns a new 2D array containing only the columns that pass the test.
|
||||||
|
//! \~russian Возвращает новый двумерный массив, содержащий только столбцы, прошедшие проверку.
|
||||||
|
//! \sa filterRows()
|
||||||
|
inline PIVector2D<T> filterColumns(std::function<bool(const ColConst &)> test) const {
|
||||||
|
if (isEmpty()) return PIVector2D<T>();
|
||||||
|
PIVector<size_t> goodCols;
|
||||||
|
for (size_t c = 0; c < cols_; ++c) {
|
||||||
|
if (test(col(c))) {
|
||||||
|
goodCols << c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PIVector2D<T> result(rows_, goodCols.size());
|
||||||
|
for (size_t r = 0; r < rows_; ++r) {
|
||||||
|
for (size_t gc = 0; gc < goodCols.size(); ++gc) {
|
||||||
|
result.element(r, gc) = element(r, goodCols[gc]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
size_t rows_, cols_;
|
size_t rows_, cols_;
|
||||||
PIVector<T> mat;
|
PIVector<T> mat;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//! \relatesalso PICout
|
||||||
|
//! \~english Output operator for \a PIVector2D to \a PICout.
|
||||||
|
//! \~russian Оператор вывода \a PIVector2D в \a PICout.
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline PICout operator<<(PICout s, const PIVector2D<T> & v) {
|
inline PICout operator<<(PICout s, const PIVector2D<T> & v) {
|
||||||
s.saveAndSetControls(0);
|
s.saveAndSetControls(0);
|
||||||
@@ -346,5 +1554,6 @@ inline PICout operator<<(PICout s, const PIVector2D<T> & v) {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! \}
|
||||||
|
|
||||||
#endif // PIVECTOR2D_H
|
#endif // PIVECTOR2D_H
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
|
|
||||||
#include "piconditionvar.h"
|
#include "piconditionvar.h"
|
||||||
#include "pithread.h"
|
#include "pithread.h"
|
||||||
#include "pitime.h"
|
|
||||||
#ifndef MICRO_PIP
|
#ifndef MICRO_PIP
|
||||||
# include "pifile.h"
|
# include "pifile.h"
|
||||||
# include "piiostream.h"
|
# include "piiostream.h"
|
||||||
|
|||||||
@@ -18,19 +18,18 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "piwaitevent_p.h"
|
#include "piwaitevent_p.h"
|
||||||
#ifndef MICRO_PIP
|
#ifdef WINDOWS
|
||||||
# ifdef WINDOWS
|
|
||||||
// # ifdef _WIN32_WINNT
|
// # ifdef _WIN32_WINNT
|
||||||
// # undef _WIN32_WINNT
|
// # undef _WIN32_WINNT
|
||||||
// # define _WIN32_WINNT 0x0600
|
// # define _WIN32_WINNT 0x0600
|
||||||
// # endif
|
// # endif
|
||||||
# include <synchapi.h>
|
# include <synchapi.h>
|
||||||
# else
|
#else
|
||||||
# include <errno.h>
|
# include <errno.h>
|
||||||
# include <fcntl.h>
|
# include <fcntl.h>
|
||||||
# include <sys/ioctl.h>
|
# include <sys/ioctl.h>
|
||||||
# endif
|
#endif
|
||||||
# include "pistring.h"
|
#include "pistring.h"
|
||||||
|
|
||||||
|
|
||||||
PIWaitEvent::~PIWaitEvent() {
|
PIWaitEvent::~PIWaitEvent() {
|
||||||
@@ -40,12 +39,12 @@ PIWaitEvent::~PIWaitEvent() {
|
|||||||
|
|
||||||
void PIWaitEvent::create() {
|
void PIWaitEvent::create() {
|
||||||
destroy();
|
destroy();
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
event = CreateEventA(NULL, TRUE, FALSE, NULL);
|
event = CreateEventA(NULL, TRUE, FALSE, NULL);
|
||||||
if (!event) {
|
if (!event) {
|
||||||
piCout << "Error with CreateEventA:" << errorString();
|
piCout << "Error with CreateEventA:" << errorString();
|
||||||
}
|
}
|
||||||
# else
|
#else
|
||||||
for (int i = 0; i < 3; ++i)
|
for (int i = 0; i < 3; ++i)
|
||||||
piZeroMemory(fds[i]);
|
piZeroMemory(fds[i]);
|
||||||
if (::pipe(pipe_fd) < 0) {
|
if (::pipe(pipe_fd) < 0) {
|
||||||
@@ -54,34 +53,34 @@ void PIWaitEvent::create() {
|
|||||||
fcntl(pipe_fd[ReadEnd], F_SETFL, O_NONBLOCK);
|
fcntl(pipe_fd[ReadEnd], F_SETFL, O_NONBLOCK);
|
||||||
fcntl(pipe_fd[WriteEnd], F_SETFL, O_NONBLOCK);
|
fcntl(pipe_fd[WriteEnd], F_SETFL, O_NONBLOCK);
|
||||||
}
|
}
|
||||||
# endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PIWaitEvent::destroy() {
|
void PIWaitEvent::destroy() {
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
if (event) {
|
if (event) {
|
||||||
CloseHandle(event);
|
CloseHandle(event);
|
||||||
event = NULL;
|
event = NULL;
|
||||||
}
|
}
|
||||||
# else
|
#else
|
||||||
for (int i = 0; i < 2; ++i) {
|
for (int i = 0; i < 2; ++i) {
|
||||||
if (pipe_fd[i] != 0) {
|
if (pipe_fd[i] != 0) {
|
||||||
::close(pipe_fd[i]);
|
::close(pipe_fd[i]);
|
||||||
pipe_fd[i] = 0;
|
pipe_fd[i] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool PIWaitEvent::wait(int fd, CheckRole role) {
|
bool PIWaitEvent::wait(int fd, CheckRole role) {
|
||||||
if (!isCreate()) return false;
|
if (!isCreate()) return false;
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
DWORD ret = WaitForSingleObjectEx(event, INFINITE, TRUE);
|
DWORD ret = WaitForSingleObjectEx(event, INFINITE, TRUE);
|
||||||
ResetEvent(event);
|
ResetEvent(event);
|
||||||
if (ret == WAIT_IO_COMPLETION || ret == WAIT_FAILED) return false;
|
if (ret == WAIT_IO_COMPLETION || ret == WAIT_FAILED) return false;
|
||||||
# else
|
#else
|
||||||
if (fd == -1) return false;
|
if (fd == -1) return false;
|
||||||
int nfds = piMaxi(pipe_fd[ReadEnd], fd) + 1;
|
int nfds = piMaxi(pipe_fd[ReadEnd], fd) + 1;
|
||||||
int fd_index = role;
|
int fd_index = role;
|
||||||
@@ -98,18 +97,18 @@ bool PIWaitEvent::wait(int fd, CheckRole role) {
|
|||||||
if (sr == EBADF || sr == EINTR) return false;
|
if (sr == EBADF || sr == EINTR) return false;
|
||||||
if (FD_ISSET(fd, &(fds[CheckExeption]))) return true;
|
if (FD_ISSET(fd, &(fds[CheckExeption]))) return true;
|
||||||
return FD_ISSET(fd, &(fds[fd_index]));
|
return FD_ISSET(fd, &(fds[fd_index]));
|
||||||
# endif
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool PIWaitEvent::sleep(int us) {
|
bool PIWaitEvent::sleep(int us) {
|
||||||
if (!isCreate()) return false;
|
if (!isCreate()) return false;
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
DWORD ret = WaitForSingleObjectEx(event, us / 1000, TRUE);
|
DWORD ret = WaitForSingleObjectEx(event, us / 1000, TRUE);
|
||||||
ResetEvent(event);
|
ResetEvent(event);
|
||||||
return ret == WAIT_TIMEOUT;
|
return ret == WAIT_TIMEOUT;
|
||||||
# else
|
#else
|
||||||
int nfds = pipe_fd[ReadEnd] + 1;
|
int nfds = pipe_fd[ReadEnd] + 1;
|
||||||
FD_ZERO(&(fds[CheckRead]));
|
FD_ZERO(&(fds[CheckRead]));
|
||||||
FD_SET(pipe_fd[ReadEnd], &(fds[CheckRead]));
|
FD_SET(pipe_fd[ReadEnd], &(fds[CheckRead]));
|
||||||
@@ -121,36 +120,34 @@ bool PIWaitEvent::sleep(int us) {
|
|||||||
while (::read(pipe_fd[ReadEnd], &buf, sizeof(buf)) > 0)
|
while (::read(pipe_fd[ReadEnd], &buf, sizeof(buf)) > 0)
|
||||||
;
|
;
|
||||||
return ret == 0;
|
return ret == 0;
|
||||||
# endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PIWaitEvent::interrupt() {
|
void PIWaitEvent::interrupt() {
|
||||||
if (!isCreate()) return;
|
if (!isCreate()) return;
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
SetEvent(event);
|
SetEvent(event);
|
||||||
# else
|
#else
|
||||||
auto _r = ::write(pipe_fd[WriteEnd], "", 1);
|
auto _r = ::write(pipe_fd[WriteEnd], "", 1);
|
||||||
NO_UNUSED(_r);
|
NO_UNUSED(_r);
|
||||||
# endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool PIWaitEvent::isCreate() const {
|
bool PIWaitEvent::isCreate() const {
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
return event;
|
return event;
|
||||||
# else
|
#else
|
||||||
return pipe_fd[ReadEnd] != 0;
|
return pipe_fd[ReadEnd] != 0;
|
||||||
# endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void * PIWaitEvent::getEvent() const {
|
void * PIWaitEvent::getEvent() const {
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
return event;
|
return event;
|
||||||
# else
|
#else
|
||||||
return nullptr;
|
return nullptr;
|
||||||
# endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // MICRO_PIP
|
|
||||||
|
|||||||
@@ -20,9 +20,7 @@
|
|||||||
#ifndef PIWAITEVENT_P_H
|
#ifndef PIWAITEVENT_P_H
|
||||||
#define PIWAITEVENT_P_H
|
#define PIWAITEVENT_P_H
|
||||||
|
|
||||||
#ifndef MICRO_PIP
|
#include "pibase.h"
|
||||||
|
|
||||||
# include "pibase.h"
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
#ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
# include <stdarg.h>
|
# include <stdarg.h>
|
||||||
@@ -54,18 +52,17 @@ public:
|
|||||||
void * getEvent() const; // WINDOWS only
|
void * getEvent() const; // WINDOWS only
|
||||||
|
|
||||||
private:
|
private:
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
void * event = nullptr;
|
void * event = nullptr;
|
||||||
# else
|
#else
|
||||||
int pipe_fd[2] = {0, 0};
|
int pipe_fd[2] = {0, 0};
|
||||||
fd_set fds[3];
|
fd_set fds[3];
|
||||||
enum {
|
enum {
|
||||||
ReadEnd = 0,
|
ReadEnd = 0,
|
||||||
WriteEnd = 1
|
WriteEnd = 1
|
||||||
};
|
};
|
||||||
# endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif // MICRO_PIP
|
|
||||||
#endif // PIWAITEVENT_P_H
|
#endif // PIWAITEVENT_P_H
|
||||||
|
|||||||
@@ -19,12 +19,10 @@
|
|||||||
|
|
||||||
#include "piintrospection_server_p.h"
|
#include "piintrospection_server_p.h"
|
||||||
|
|
||||||
#if defined(PIP_INTROSPECTION) && !defined(PIP_FORCE_NO_PIINTROSPECTION)
|
#include "pichunkstream.h"
|
||||||
|
#include "piinit.h"
|
||||||
# include "pichunkstream.h"
|
#include "piobject.h"
|
||||||
# include "piinit.h"
|
#include "pisysteminfo.h"
|
||||||
# include "piobject.h"
|
|
||||||
# include "pisysteminfo.h"
|
|
||||||
|
|
||||||
|
|
||||||
const uint PIIntrospection::sign = 0x0F1C2B3A;
|
const uint PIIntrospection::sign = 0x0F1C2B3A;
|
||||||
@@ -113,9 +111,9 @@ PIByteArray PIIntrospection::packContainers() {
|
|||||||
PIByteArray ret;
|
PIByteArray ret;
|
||||||
PIVector<PIIntrospectionContainers::TypeInfo> data;
|
PIVector<PIIntrospectionContainers::TypeInfo> data;
|
||||||
PIIntrospectionContainers * p = 0;
|
PIIntrospectionContainers * p = 0;
|
||||||
# ifdef PIP_INTROSPECTION
|
#ifdef PIP_INTROSPECTION
|
||||||
p = PIINTROSPECTION_CONTAINERS->p;
|
p = PIINTROSPECTION_CONTAINERS->p;
|
||||||
# endif
|
#endif
|
||||||
if (p) {
|
if (p) {
|
||||||
data = p->getInfo();
|
data = p->getInfo();
|
||||||
}
|
}
|
||||||
@@ -133,9 +131,9 @@ void PIIntrospection::unpackContainers(PIByteArray & ba, PIVector<PIIntrospectio
|
|||||||
PIByteArray PIIntrospection::packThreads() {
|
PIByteArray PIIntrospection::packThreads() {
|
||||||
PIByteArray ret;
|
PIByteArray ret;
|
||||||
PIIntrospectionThreads * p = 0;
|
PIIntrospectionThreads * p = 0;
|
||||||
# ifdef PIP_INTROSPECTION
|
#ifdef PIP_INTROSPECTION
|
||||||
p = PIINTROSPECTION_THREADS->p;
|
p = PIINTROSPECTION_THREADS->p;
|
||||||
# endif
|
#endif
|
||||||
if (p) {
|
if (p) {
|
||||||
p->mutex.lock();
|
p->mutex.lock();
|
||||||
PIMap<PIThread *, PIIntrospectionThreads::ThreadInfo> & tm(p->threads);
|
PIMap<PIThread *, PIIntrospectionThreads::ThreadInfo> & tm(p->threads);
|
||||||
@@ -172,5 +170,3 @@ void PIIntrospection::unpackObjects(PIByteArray & ba, PIVector<PIIntrospection::
|
|||||||
objects.clear();
|
objects.clear();
|
||||||
ba >> objects;
|
ba >> objects;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // #if defined(PIP_INTROSPECTION) && !defined(PIP_FORCE_NO_PIINTROSPECTION)
|
|
||||||
|
|||||||
@@ -27,7 +27,6 @@
|
|||||||
#include "piintrospection_threads_p.h"
|
#include "piintrospection_threads_p.h"
|
||||||
#include "pisystemmonitor.h"
|
#include "pisystemmonitor.h"
|
||||||
|
|
||||||
#if defined(PIP_INTROSPECTION) && !defined(PIP_FORCE_NO_PIINTROSPECTION)
|
|
||||||
|
|
||||||
class PIP_EXPORT PIIntrospection {
|
class PIP_EXPORT PIIntrospection {
|
||||||
public:
|
public:
|
||||||
@@ -169,5 +168,4 @@ BINARY_STREAM_READ(PIIntrospection::ObjectInfo) {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // #if defined(PIP_INTROSPECTION) && !defined(PIP_FORCE_NO_PIINTROSPECTION)
|
|
||||||
#endif // PIINTROSPECTION_SERVER_P_H
|
#endif // PIINTROSPECTION_SERVER_P_H
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
#include "piintrospection_threads_p.h"
|
#include "piintrospection_threads_p.h"
|
||||||
|
|
||||||
#if defined(PIP_INTROSPECTION) && !defined(PIP_FORCE_NO_PIINTROSPECTION)
|
|
||||||
|
|
||||||
PIIntrospectionThreads::ThreadInfo::ThreadInfo() {
|
PIIntrospectionThreads::ThreadInfo::ThreadInfo() {
|
||||||
id = delay = 0;
|
id = delay = 0;
|
||||||
@@ -79,5 +78,3 @@ void PIIntrospectionThreads::threadRunDone(PIThread * t, ullong us) {
|
|||||||
ThreadInfo & ti(threads[t]);
|
ThreadInfo & ti(threads[t]);
|
||||||
ti.run_us = (ti.run_us * 0.8) + (us * 0.2); /// WARNING
|
ti.run_us = (ti.run_us * 0.8) + (us * 0.2); /// WARNING
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // #if defined(PIP_INTROSPECTION) && !defined(PIP_FORCE_NO_PIINTROSPECTION)
|
|
||||||
|
|||||||
@@ -20,10 +20,6 @@
|
|||||||
#ifndef PIINTROSPECTION_THREADS_P_H
|
#ifndef PIINTROSPECTION_THREADS_P_H
|
||||||
#define PIINTROSPECTION_THREADS_P_H
|
#define PIINTROSPECTION_THREADS_P_H
|
||||||
|
|
||||||
#include "pibase.h"
|
|
||||||
|
|
||||||
#if defined(PIP_INTROSPECTION) && !defined(PIP_FORCE_NO_PIINTROSPECTION)
|
|
||||||
|
|
||||||
#include "pimap.h"
|
#include "pimap.h"
|
||||||
#include "pithread.h"
|
#include "pithread.h"
|
||||||
|
|
||||||
@@ -72,5 +68,4 @@ BINARY_STREAM_READ(PIIntrospectionThreads::ThreadInfo) {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // #if defined(PIP_INTROSPECTION) && !defined(PIP_FORCE_NO_PIINTROSPECTION)
|
|
||||||
#endif // PIINTROSPECTION_THREADS_P_H
|
#endif // PIINTROSPECTION_THREADS_P_H
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
#include "pipropertystorage.h"
|
#include "pipropertystorage.h"
|
||||||
#include "piwaitevent_p.h"
|
#include "piwaitevent_p.h"
|
||||||
#if !defined(WINDOWS) && !defined(MAC_OS) && !defined(PIP_NO_SOCKET)
|
#if !defined(WINDOWS) && !defined(MAC_OS) && !defined(MICRO_PIP)
|
||||||
# define PIP_CAN
|
# define PIP_CAN
|
||||||
#endif
|
#endif
|
||||||
#ifdef PIP_CAN
|
#ifdef PIP_CAN
|
||||||
@@ -39,29 +39,25 @@
|
|||||||
|
|
||||||
REGISTER_DEVICE(PICAN)
|
REGISTER_DEVICE(PICAN)
|
||||||
|
|
||||||
#ifdef PIP_CAN
|
|
||||||
PRIVATE_DEFINITION_START(PICAN)
|
PRIVATE_DEFINITION_START(PICAN)
|
||||||
PIWaitEvent event;
|
PIWaitEvent event;
|
||||||
PRIVATE_DEFINITION_END(PICAN)
|
PRIVATE_DEFINITION_END(PICAN)
|
||||||
#endif
|
|
||||||
|
|
||||||
PICAN::PICAN(const PIString & path, PIIODevice::DeviceMode mode): PIIODevice(path, mode) {
|
PICAN::PICAN(const PIString & path, PIIODevice::DeviceMode mode): PIIODevice(path, mode) {
|
||||||
setThreadedReadBufferSize(256);
|
setThreadedReadBufferSize(256);
|
||||||
setPath(path);
|
setPath(path);
|
||||||
#ifdef PIP_CAN
|
|
||||||
can_id = 0;
|
can_id = 0;
|
||||||
sock = 0;
|
sock = 0;
|
||||||
PRIVATE->event.create();
|
PRIVATE->event.create();
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PICAN::~PICAN() {
|
PICAN::~PICAN() {
|
||||||
stopAndWait();
|
stopAndWait();
|
||||||
close();
|
close();
|
||||||
#ifdef PIP_CAN
|
|
||||||
PRIVATE->event.destroy();
|
PRIVATE->event.destroy();
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -168,9 +164,7 @@ int PICAN::readedCANID() const {
|
|||||||
|
|
||||||
|
|
||||||
void PICAN::interrupt() {
|
void PICAN::interrupt() {
|
||||||
#ifdef PIP_CAN
|
|
||||||
PRIVATE->event.interrupt();
|
PRIVATE->event.interrupt();
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -18,65 +18,64 @@
|
|||||||
*/
|
*/
|
||||||
#include "piethernet.h"
|
#include "piethernet.h"
|
||||||
|
|
||||||
#ifndef PIP_NO_SOCKET
|
#include "piconfig.h"
|
||||||
|
#include "piconstchars.h"
|
||||||
# include "piconfig.h"
|
#include "piincludes_p.h"
|
||||||
# include "piconstchars.h"
|
#include "piliterals.h"
|
||||||
# include "piincludes_p.h"
|
#include "pipropertystorage.h"
|
||||||
# include "piliterals.h"
|
#include "pisysteminfo.h"
|
||||||
# include "pipropertystorage.h"
|
#include "pitranslator.h"
|
||||||
# include "pisysteminfo.h"
|
// clang-format off
|
||||||
# include "pitranslator.h"
|
#ifdef QNX
|
||||||
|
# include <arpa/inet.h>
|
||||||
# ifdef QNX
|
# include <fcntl.h>
|
||||||
# include <arpa/inet.h>
|
# include <hw/nicinfo.h>
|
||||||
# include <fcntl.h>
|
# include <ifaddrs.h>
|
||||||
# include <hw/nicinfo.h>
|
# include <net/if.h>
|
||||||
# include <ifaddrs.h>
|
# include <net/if_dl.h>
|
||||||
# include <net/if.h>
|
# include <netdb.h>
|
||||||
# include <net/if_dl.h>
|
# include <netinet/in.h>
|
||||||
# include <netdb.h>
|
# include <sys/ioctl.h>
|
||||||
|
# include <sys/socket.h>
|
||||||
|
# include <sys/time.h>
|
||||||
|
# include <sys/types.h>
|
||||||
|
# ifdef BLACKBERRY
|
||||||
# include <netinet/in.h>
|
# include <netinet/in.h>
|
||||||
# include <sys/ioctl.h>
|
|
||||||
# include <sys/socket.h>
|
|
||||||
# include <sys/time.h>
|
|
||||||
# include <sys/types.h>
|
|
||||||
# ifdef BLACKBERRY
|
|
||||||
# include <netinet/in.h>
|
|
||||||
# else
|
|
||||||
# include <sys/dcmd_io-net.h>
|
|
||||||
# endif
|
|
||||||
# define ip_mreqn ip_mreq
|
|
||||||
# define imr_address imr_interface
|
|
||||||
# else
|
# else
|
||||||
# ifdef WINDOWS
|
# include <sys/dcmd_io-net.h>
|
||||||
# include <io.h>
|
|
||||||
# include <iphlpapi.h>
|
|
||||||
# include <psapi.h>
|
|
||||||
# include <winsock2.h>
|
|
||||||
# include <ws2tcpip.h>
|
|
||||||
# define ip_mreqn ip_mreq
|
|
||||||
# define imr_address imr_interface
|
|
||||||
# else
|
|
||||||
# include <arpa/inet.h>
|
|
||||||
# include <fcntl.h>
|
|
||||||
# include <net/if.h>
|
|
||||||
# include <netdb.h>
|
|
||||||
# include <netinet/in.h>
|
|
||||||
# include <netinet/tcp.h>
|
|
||||||
# include <sys/ioctl.h>
|
|
||||||
# include <sys/socket.h>
|
|
||||||
# if !defined(ANDROID) && !defined(LWIP)
|
|
||||||
# include <ifaddrs.h>
|
|
||||||
# endif
|
|
||||||
# ifdef LWIP
|
|
||||||
# include <lwip/sockets.h>
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
# endif
|
# endif
|
||||||
# include "piwaitevent_p.h"
|
# define ip_mreqn ip_mreq
|
||||||
|
# define imr_address imr_interface
|
||||||
|
#else
|
||||||
|
# ifdef WINDOWS
|
||||||
|
# include <io.h>
|
||||||
|
# include <winsock2.h>
|
||||||
|
# include <iphlpapi.h>
|
||||||
|
# include <psapi.h>
|
||||||
|
# include <ws2tcpip.h>
|
||||||
|
# define ip_mreqn ip_mreq
|
||||||
|
# define imr_address imr_interface
|
||||||
|
# else
|
||||||
|
# include <fcntl.h>
|
||||||
|
# include <sys/ioctl.h>
|
||||||
|
# include <netinet/in.h>
|
||||||
|
# include <netinet/tcp.h>
|
||||||
|
# include <arpa/inet.h>
|
||||||
|
# include <sys/socket.h>
|
||||||
|
# include <netdb.h>
|
||||||
|
# include <net/if.h>
|
||||||
|
# if !defined(ANDROID) && !defined(LWIP)
|
||||||
|
# include <ifaddrs.h>
|
||||||
|
# endif
|
||||||
|
# ifdef LWIP
|
||||||
|
# include <lwip/sockets.h>
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
// clang-format on
|
||||||
|
#include "piwaitevent_p.h"
|
||||||
|
|
||||||
# include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
|
|
||||||
/** \class PIEthernet piethernet.h
|
/** \class PIEthernet piethernet.h
|
||||||
@@ -101,11 +100,11 @@
|
|||||||
*
|
*
|
||||||
* */
|
* */
|
||||||
|
|
||||||
# ifndef WINDOWS
|
#ifndef WINDOWS
|
||||||
PIString getSockAddr(sockaddr * s) {
|
PIString getSockAddr(sockaddr * s) {
|
||||||
return s == 0 ? PIString() : PIStringAscii(inet_ntoa(((sockaddr_in *)s)->sin_addr));
|
return s == 0 ? PIString() : PIStringAscii(inet_ntoa(((sockaddr_in *)s)->sin_addr));
|
||||||
}
|
}
|
||||||
# endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
REGISTER_DEVICE(PIEthernet)
|
REGISTER_DEVICE(PIEthernet)
|
||||||
@@ -197,11 +196,11 @@ void PIEthernet::construct() {
|
|||||||
setMulticastTTL(1);
|
setMulticastTTL(1);
|
||||||
server_thread_.setData(this);
|
server_thread_.setData(this);
|
||||||
server_thread_.setName("_S.tcpserver"_a);
|
server_thread_.setName("_S.tcpserver"_a);
|
||||||
# ifdef LWIP
|
#ifdef MICRO_PIP
|
||||||
setThreadedReadBufferSize(512);
|
setThreadedReadBufferSize(512);
|
||||||
# else
|
#else
|
||||||
setThreadedReadBufferSize(64_KiB);
|
setThreadedReadBufferSize(64_KiB);
|
||||||
# endif
|
#endif
|
||||||
// setPriority(piHigh);
|
// setPriority(piHigh);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,9 +304,9 @@ bool PIEthernet::openDevice() {
|
|||||||
PRIVATE->addr_.sin_addr.s_addr = INADDR_ANY;
|
PRIVATE->addr_.sin_addr.s_addr = INADDR_ANY;
|
||||||
else
|
else
|
||||||
PRIVATE->addr_.sin_addr.s_addr = addr_r.ip();
|
PRIVATE->addr_.sin_addr.s_addr = addr_r.ip();
|
||||||
# ifdef QNX
|
#ifdef QNX
|
||||||
PRIVATE->addr_.sin_len = sizeof(PRIVATE->addr_);
|
PRIVATE->addr_.sin_len = sizeof(PRIVATE->addr_);
|
||||||
# endif
|
#endif
|
||||||
// piCout << "bind to" << (params[PIEthernet::Broadcast] ? "255.255.255.255" : ip_) << ":" << port_ << " ...";
|
// piCout << "bind to" << (params[PIEthernet::Broadcast] ? "255.255.255.255" : ip_) << ":" << port_ << " ...";
|
||||||
int tries = 0;
|
int tries = 0;
|
||||||
while ((bind(sock, (sockaddr *)&PRIVATE->addr_, sizeof(PRIVATE->addr_)) == -1) && (tries < 2)) {
|
while ((bind(sock, (sockaddr *)&PRIVATE->addr_, sizeof(PRIVATE->addr_)) == -1) && (tries < 2)) {
|
||||||
@@ -380,14 +379,14 @@ void PIEthernet::applyBuffers() {
|
|||||||
|
|
||||||
void PIEthernet::applyTimeout(int fd, int opt, PISystemTime tm) {
|
void PIEthernet::applyTimeout(int fd, int opt, PISystemTime tm) {
|
||||||
if (fd == 0) return;
|
if (fd == 0) return;
|
||||||
// piCoutObj << "setReadIsBlocking" << yes;
|
// piCoutObj << "setReadIsBlocking" << yes;
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
DWORD _tm = tm.toMilliseconds();
|
DWORD _tm = tm.toMilliseconds();
|
||||||
# else
|
#else
|
||||||
timeval _tm;
|
timeval _tm;
|
||||||
_tm.tv_sec = tm.seconds;
|
_tm.tv_sec = tm.seconds;
|
||||||
_tm.tv_usec = tm.nanoseconds / 1000;
|
_tm.tv_usec = tm.nanoseconds / 1000;
|
||||||
# endif
|
#endif
|
||||||
ethSetsockopt(fd, SOL_SOCKET, opt, &_tm, sizeof(_tm));
|
ethSetsockopt(fd, SOL_SOCKET, opt, &_tm, sizeof(_tm));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,30 +411,30 @@ bool PIEthernet::joinMulticastGroup(const PIString & group) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
addr_r.set(path());
|
addr_r.set(path());
|
||||||
# ifndef LWIP
|
#ifndef LWIP
|
||||||
struct ip_mreqn mreq;
|
struct ip_mreqn mreq;
|
||||||
# else
|
#else
|
||||||
struct ip_mreq mreq;
|
struct ip_mreq mreq;
|
||||||
# endif
|
#endif
|
||||||
piZeroMemory(mreq);
|
piZeroMemory(mreq);
|
||||||
# ifdef LINUX
|
#ifdef LINUX
|
||||||
// mreq.imr_address.s_addr = INADDR_ANY;
|
// mreq.imr_address.s_addr = INADDR_ANY;
|
||||||
/*PIEthernet::InterfaceList il = interfaces();
|
/*PIEthernet::InterfaceList il = interfaces();
|
||||||
const PIEthernet::Interface * ci = il.getByAddress(addr_r.ipString());
|
const PIEthernet::Interface * ci = il.getByAddress(addr_r.ipString());
|
||||||
if (ci != 0) mreq.imr_ifindex = ci->index;*/
|
if (ci != 0) mreq.imr_ifindex = ci->index;*/
|
||||||
# endif
|
#endif
|
||||||
if (params[PIEthernet::Broadcast])
|
if (params[PIEthernet::Broadcast])
|
||||||
# ifndef LWIP
|
#ifndef LWIP
|
||||||
mreq.imr_address.s_addr = INADDR_ANY;
|
mreq.imr_address.s_addr = INADDR_ANY;
|
||||||
# else
|
#else
|
||||||
mreq.imr_interface.s_addr = INADDR_ANY;
|
mreq.imr_interface.s_addr = INADDR_ANY;
|
||||||
# endif
|
#endif
|
||||||
else
|
else
|
||||||
# ifndef LWIP
|
#ifndef LWIP
|
||||||
mreq.imr_address.s_addr = addr_r.ip();
|
mreq.imr_address.s_addr = addr_r.ip();
|
||||||
# else
|
#else
|
||||||
mreq.imr_interface.s_addr = addr_r.ip();
|
mreq.imr_interface.s_addr = addr_r.ip();
|
||||||
# endif
|
#endif
|
||||||
|
|
||||||
// piCout << "join group" << group << "ip" << ip_ << "with index" << mreq.imr_ifindex << "socket" << sock;
|
// piCout << "join group" << group << "ip" << ip_ << "with index" << mreq.imr_ifindex << "socket" << sock;
|
||||||
mreq.imr_multiaddr.s_addr = inet_addr(group.dataAscii());
|
mreq.imr_multiaddr.s_addr = inet_addr(group.dataAscii());
|
||||||
@@ -458,24 +457,24 @@ bool PIEthernet::leaveMulticastGroup(const PIString & group) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
addr_r.set(path());
|
addr_r.set(path());
|
||||||
# ifndef LWIP
|
#ifndef LWIP
|
||||||
struct ip_mreqn mreq;
|
struct ip_mreqn mreq;
|
||||||
# else
|
#else
|
||||||
struct ip_mreq mreq;
|
struct ip_mreq mreq;
|
||||||
# endif
|
#endif
|
||||||
piZeroMemory(mreq);
|
piZeroMemory(mreq);
|
||||||
if (params[PIEthernet::Broadcast])
|
if (params[PIEthernet::Broadcast])
|
||||||
# ifndef LWIP
|
#ifndef LWIP
|
||||||
mreq.imr_address.s_addr = INADDR_ANY;
|
mreq.imr_address.s_addr = INADDR_ANY;
|
||||||
# else
|
#else
|
||||||
mreq.imr_interface.s_addr = INADDR_ANY;
|
mreq.imr_interface.s_addr = INADDR_ANY;
|
||||||
# endif
|
#endif
|
||||||
else
|
else
|
||||||
# ifndef LWIP
|
#ifndef LWIP
|
||||||
mreq.imr_address.s_addr = addr_r.ip();
|
mreq.imr_address.s_addr = addr_r.ip();
|
||||||
# else
|
#else
|
||||||
mreq.imr_interface.s_addr = addr_r.ip();
|
mreq.imr_interface.s_addr = addr_r.ip();
|
||||||
# endif
|
#endif
|
||||||
mreq.imr_multiaddr.s_addr = inet_addr(group.dataAscii());
|
mreq.imr_multiaddr.s_addr = inet_addr(group.dataAscii());
|
||||||
if (ethSetsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) {
|
if (ethSetsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) {
|
||||||
piCoutObj << "Can`t leave multicast group" << group << "," << ethErrorString();
|
piCoutObj << "Can`t leave multicast group" << group << "," << ethErrorString();
|
||||||
@@ -499,9 +498,9 @@ bool PIEthernet::connect(bool threaded) {
|
|||||||
PRIVATE->addr_.sin_port = htons(addr_r.port());
|
PRIVATE->addr_.sin_port = htons(addr_r.port());
|
||||||
PRIVATE->addr_.sin_addr.s_addr = addr_r.ip();
|
PRIVATE->addr_.sin_addr.s_addr = addr_r.ip();
|
||||||
PRIVATE->addr_.sin_family = AF_INET;
|
PRIVATE->addr_.sin_family = AF_INET;
|
||||||
# ifdef QNX
|
#ifdef QNX
|
||||||
PRIVATE->addr_.sin_len = sizeof(PRIVATE->addr_);
|
PRIVATE->addr_.sin_len = sizeof(PRIVATE->addr_);
|
||||||
# endif
|
#endif
|
||||||
connecting_ = true;
|
connecting_ = true;
|
||||||
connected_ = connectTCP();
|
connected_ = connectTCP();
|
||||||
connecting_ = false;
|
connecting_ = false;
|
||||||
@@ -536,9 +535,9 @@ bool PIEthernet::listen(bool threaded) {
|
|||||||
PRIVATE->addr_.sin_port = htons(addr_r.port());
|
PRIVATE->addr_.sin_port = htons(addr_r.port());
|
||||||
PRIVATE->addr_.sin_addr.s_addr = addr_r.ip();
|
PRIVATE->addr_.sin_addr.s_addr = addr_r.ip();
|
||||||
PRIVATE->addr_.sin_family = AF_INET;
|
PRIVATE->addr_.sin_family = AF_INET;
|
||||||
# ifdef QNX
|
#ifdef QNX
|
||||||
PRIVATE->addr_.sin_len = sizeof(PRIVATE->addr_);
|
PRIVATE->addr_.sin_len = sizeof(PRIVATE->addr_);
|
||||||
# endif
|
#endif
|
||||||
opened_ = false;
|
opened_ = false;
|
||||||
int tries = 0;
|
int tries = 0;
|
||||||
while ((bind(sock, (sockaddr *)&PRIVATE->addr_, sizeof(PRIVATE->addr_)) == -1) && (tries < 2)) {
|
while ((bind(sock, (sockaddr *)&PRIVATE->addr_, sizeof(PRIVATE->addr_)) == -1) && (tries < 2)) {
|
||||||
@@ -662,9 +661,9 @@ ssize_t PIEthernet::readDevice(void * read_to, ssize_t max_size) {
|
|||||||
PRIVATE->addr_.sin_port = htons(addr_r.port());
|
PRIVATE->addr_.sin_port = htons(addr_r.port());
|
||||||
PRIVATE->addr_.sin_addr.s_addr = addr_r.ip();
|
PRIVATE->addr_.sin_addr.s_addr = addr_r.ip();
|
||||||
PRIVATE->addr_.sin_family = AF_INET;
|
PRIVATE->addr_.sin_family = AF_INET;
|
||||||
# ifdef QNX
|
#ifdef QNX
|
||||||
PRIVATE->addr_.sin_len = sizeof(PRIVATE->addr_);
|
PRIVATE->addr_.sin_len = sizeof(PRIVATE->addr_);
|
||||||
# endif
|
#endif
|
||||||
// piCoutObj << "connect to " << path() << "...";
|
// piCoutObj << "connect to " << path() << "...";
|
||||||
connected_ = connectTCP();
|
connected_ = connectTCP();
|
||||||
// piCoutObj << "connect to " << path() << connected_;
|
// piCoutObj << "connect to " << path() << connected_;
|
||||||
@@ -679,7 +678,7 @@ ssize_t PIEthernet::readDevice(void * read_to, ssize_t max_size) {
|
|||||||
}
|
}
|
||||||
if (!connected_) return -1;
|
if (!connected_) return -1;
|
||||||
errorClear();
|
errorClear();
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
{
|
{
|
||||||
long wr = waitForEvent(PRIVATE->event, FD_READ | FD_CLOSE);
|
long wr = waitForEvent(PRIVATE->event, FD_READ | FD_CLOSE);
|
||||||
switch (wr) {
|
switch (wr) {
|
||||||
@@ -695,34 +694,34 @@ ssize_t PIEthernet::readDevice(void * read_to, ssize_t max_size) {
|
|||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# else
|
#else
|
||||||
if (PRIVATE->event.wait(sock)) {
|
if (PRIVATE->event.wait(sock)) {
|
||||||
errorClear();
|
errorClear();
|
||||||
rs = ethRecv(sock, read_to, max_size);
|
rs = ethRecv(sock, read_to, max_size);
|
||||||
}
|
}
|
||||||
# endif
|
#endif
|
||||||
// piCoutObj << "readed" << rs;
|
// piCoutObj << "readed" << rs;
|
||||||
if (rs <= 0) {
|
if (rs <= 0) {
|
||||||
lerr = ethErrorCore();
|
lerr = ethErrorCore();
|
||||||
// piCoutObj << "readed" << rs << "error" << lerr;
|
// piCoutObj << "readed" << rs << "error" << lerr;
|
||||||
|
|
||||||
// async normal returns
|
// async normal returns
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
if (lerr == WSAEWOULDBLOCK) {
|
if (lerr == WSAEWOULDBLOCK) {
|
||||||
# else
|
#else
|
||||||
if (lerr == EWOULDBLOCK || lerr == EAGAIN || lerr == EINTR) {
|
if (lerr == EWOULDBLOCK || lerr == EAGAIN || lerr == EINTR) {
|
||||||
# endif
|
#endif
|
||||||
// piCoutObj << "Ignore would_block" << lerr;
|
// piCoutObj << "Ignore would_block" << lerr;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if no disconnect on timeout
|
// if no disconnect on timeout
|
||||||
if (!params[DisonnectOnTimeout]) {
|
if (!params[DisonnectOnTimeout]) {
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
if (lerr == WSAETIMEDOUT) {
|
if (lerr == WSAETIMEDOUT) {
|
||||||
# else
|
#else
|
||||||
if (lerr == ETIMEDOUT) {
|
if (lerr == ETIMEDOUT) {
|
||||||
# endif
|
#endif
|
||||||
// piCoutObj << "Ignore read timeout";
|
// piCoutObj << "Ignore read timeout";
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -746,7 +745,7 @@ ssize_t PIEthernet::readDevice(void * read_to, ssize_t max_size) {
|
|||||||
case UDP: {
|
case UDP: {
|
||||||
piZeroMemory(PRIVATE->raddr_);
|
piZeroMemory(PRIVATE->raddr_);
|
||||||
// piCoutObj << "read from" << path() << "...";
|
// piCoutObj << "read from" << path() << "...";
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
long wr = waitForEvent(PRIVATE->event, FD_READ | FD_CLOSE);
|
long wr = waitForEvent(PRIVATE->event, FD_READ | FD_CLOSE);
|
||||||
switch (wr) {
|
switch (wr) {
|
||||||
case FD_READ:
|
case FD_READ:
|
||||||
@@ -759,9 +758,9 @@ ssize_t PIEthernet::readDevice(void * read_to, ssize_t max_size) {
|
|||||||
break;
|
break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
# else
|
#else
|
||||||
rs = ethRecvfrom(sock, read_to, max_size, 0, (sockaddr *)&PRIVATE->raddr_);
|
rs = ethRecvfrom(sock, read_to, max_size, 0, (sockaddr *)&PRIVATE->raddr_);
|
||||||
# endif
|
#endif
|
||||||
// piCoutObj << "read from" << path() << rs << "bytes";
|
// piCoutObj << "read from" << path() << rs << "bytes";
|
||||||
if (rs > 0) {
|
if (rs > 0) {
|
||||||
addr_lr.set(uint(PRIVATE->raddr_.sin_addr.s_addr), ntohs(PRIVATE->raddr_.sin_port));
|
addr_lr.set(uint(PRIVATE->raddr_.sin_addr.s_addr), ntohs(PRIVATE->raddr_.sin_port));
|
||||||
@@ -794,11 +793,11 @@ ssize_t PIEthernet::writeDevice(const void * data, ssize_t max_size) {
|
|||||||
return ethSendto(sock_s,
|
return ethSendto(sock_s,
|
||||||
data,
|
data,
|
||||||
max_size,
|
max_size,
|
||||||
# ifndef WINDOWS
|
#ifndef WINDOWS
|
||||||
isOptionSet(BlockingWrite) ? 0 : MSG_DONTWAIT
|
isOptionSet(BlockingWrite) ? 0 : MSG_DONTWAIT
|
||||||
# else
|
#else
|
||||||
0
|
0
|
||||||
# endif
|
#endif
|
||||||
,
|
,
|
||||||
(sockaddr *)&PRIVATE->saddr_,
|
(sockaddr *)&PRIVATE->saddr_,
|
||||||
sizeof(PRIVATE->saddr_));
|
sizeof(PRIVATE->saddr_));
|
||||||
@@ -810,9 +809,9 @@ ssize_t PIEthernet::writeDevice(const void * data, ssize_t max_size) {
|
|||||||
PRIVATE->addr_.sin_port = htons(addr_r.port());
|
PRIVATE->addr_.sin_port = htons(addr_r.port());
|
||||||
PRIVATE->addr_.sin_addr.s_addr = addr_r.ip();
|
PRIVATE->addr_.sin_addr.s_addr = addr_r.ip();
|
||||||
PRIVATE->addr_.sin_family = AF_INET;
|
PRIVATE->addr_.sin_family = AF_INET;
|
||||||
# ifdef QNX
|
#ifdef QNX
|
||||||
PRIVATE->addr_.sin_len = sizeof(PRIVATE->addr_);
|
PRIVATE->addr_.sin_len = sizeof(PRIVATE->addr_);
|
||||||
# endif
|
#endif
|
||||||
// piCoutObj << "connect to " << ip << ":" << port_;
|
// piCoutObj << "connect to " << ip << ":" << port_;
|
||||||
connected_ = connectTCP();
|
connected_ = connectTCP();
|
||||||
if (!connected_) piCoutObj << "Can`t connect to" << addr_r << "," << ethErrorString();
|
if (!connected_) piCoutObj << "Can`t connect to" << addr_r << "," << ethErrorString();
|
||||||
@@ -848,11 +847,11 @@ ssize_t PIEthernet::writeDevice(const void * data, ssize_t max_size) {
|
|||||||
int sr = ::send(sock, remain_data, remain_size, 0);
|
int sr = ::send(sock, remain_data, remain_size, 0);
|
||||||
if (sr < 0) {
|
if (sr < 0) {
|
||||||
int err = ethErrorCore();
|
int err = ethErrorCore();
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
if (err == WSAEWOULDBLOCK) {
|
if (err == WSAEWOULDBLOCK) {
|
||||||
# else
|
#else
|
||||||
if (err == EAGAIN || err == EWOULDBLOCK) {
|
if (err == EAGAIN || err == EWOULDBLOCK) {
|
||||||
# endif
|
#endif
|
||||||
piMinSleep();
|
piMinSleep();
|
||||||
// piCoutObj << "wait for write";
|
// piCoutObj << "wait for write";
|
||||||
continue;
|
continue;
|
||||||
@@ -911,30 +910,30 @@ void PIEthernet::server_func(void * eth) {
|
|||||||
}
|
}
|
||||||
sockaddr_in client_addr;
|
sockaddr_in client_addr;
|
||||||
socklen_t slen = sizeof(client_addr);
|
socklen_t slen = sizeof(client_addr);
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
long wr = ce->waitForEvent(ce->PRIVATEWB->event, FD_ACCEPT | FD_CLOSE);
|
long wr = ce->waitForEvent(ce->PRIVATEWB->event, FD_ACCEPT | FD_CLOSE);
|
||||||
if (wr != FD_ACCEPT) {
|
if (wr != FD_ACCEPT) {
|
||||||
piMSleep(10);
|
piMSleep(10);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
# else
|
#else
|
||||||
if (!ce->PRIVATEWB->event.wait(ce->sock)) {
|
if (!ce->PRIVATEWB->event.wait(ce->sock)) {
|
||||||
piMSleep(10);
|
piMSleep(10);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
# endif
|
#endif
|
||||||
// piCout << "server" << "accept ...";
|
// piCout << "server" << "accept ...";
|
||||||
int s = accept(ce->sock, (sockaddr *)&client_addr, &slen);
|
int s = accept(ce->sock, (sockaddr *)&client_addr, &slen);
|
||||||
// piCout << "server" << "accept done" << ethErrorString();
|
// piCout << "server" << "accept done" << ethErrorString();
|
||||||
if (s == -1) {
|
if (s == -1) {
|
||||||
int lerr = ethErrorCore();
|
int lerr = ethErrorCore();
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
if (lerr == WSAETIMEDOUT) {
|
if (lerr == WSAETIMEDOUT) {
|
||||||
# elif defined(ANDROID)
|
#elif defined(ANDROID)
|
||||||
if ((lerr == EAGAIN || lerr == EINTR)) {
|
if ((lerr == EAGAIN || lerr == EINTR)) {
|
||||||
# else
|
#else
|
||||||
if (lerr == EAGAIN) {
|
if (lerr == EAGAIN) {
|
||||||
# endif
|
#endif
|
||||||
piMSleep(10);
|
piMSleep(10);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -970,7 +969,7 @@ void PIEthernet::setType(Type t, bool reopen) {
|
|||||||
bool PIEthernet::connectTCP() {
|
bool PIEthernet::connectTCP() {
|
||||||
::connect(sock, (sockaddr *)&(PRIVATE->addr_), sizeof(PRIVATE->addr_));
|
::connect(sock, (sockaddr *)&(PRIVATE->addr_), sizeof(PRIVATE->addr_));
|
||||||
// piCout << errorString();
|
// piCout << errorString();
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
long wr = waitForEvent(PRIVATE->event, FD_CONNECT | FD_CLOSE);
|
long wr = waitForEvent(PRIVATE->event, FD_CONNECT | FD_CLOSE);
|
||||||
switch (wr) {
|
switch (wr) {
|
||||||
case FD_CONNECT:
|
case FD_CONNECT:
|
||||||
@@ -978,7 +977,7 @@ bool PIEthernet::connectTCP() {
|
|||||||
return ethIsWriteable(sock);
|
return ethIsWriteable(sock);
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
# else
|
#else
|
||||||
if (PRIVATE->event.wait(sock, PIWaitEvent::CheckWrite)) {
|
if (PRIVATE->event.wait(sock, PIWaitEvent::CheckWrite)) {
|
||||||
if (ethIsWriteable(sock))
|
if (ethIsWriteable(sock))
|
||||||
return true;
|
return true;
|
||||||
@@ -987,12 +986,12 @@ bool PIEthernet::connectTCP() {
|
|||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# endif
|
#endif
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
long PIEthernet::waitForEvent(PIWaitEvent & event, long mask) {
|
long PIEthernet::waitForEvent(PIWaitEvent & event, long mask) {
|
||||||
if (!event.isCreate() || sock < 0) return 0;
|
if (!event.isCreate() || sock < 0) return 0;
|
||||||
if (WSAEventSelect(sock, event.getEvent(), mask) == SOCKET_ERROR) {
|
if (WSAEventSelect(sock, event.getEvent(), mask) == SOCKET_ERROR) {
|
||||||
@@ -1009,7 +1008,7 @@ long PIEthernet::waitForEvent(PIWaitEvent & event, long mask) {
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
# endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
bool PIEthernet::configureDevice(const void * e_main, const void * e_parent) {
|
bool PIEthernet::configureDevice(const void * e_main, const void * e_parent) {
|
||||||
@@ -1119,7 +1118,7 @@ PIEthernet::InterfaceList PIEthernet::interfaces() {
|
|||||||
Interface ci;
|
Interface ci;
|
||||||
ci.index = -1;
|
ci.index = -1;
|
||||||
ci.mtu = 1500;
|
ci.mtu = 1500;
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
ulong ulOutBufLen = sizeof(IP_ADAPTER_INFO);
|
ulong ulOutBufLen = sizeof(IP_ADAPTER_INFO);
|
||||||
PIP_ADAPTER_INFO pAdapterInfo = (PIP_ADAPTER_INFO)HeapAlloc(GetProcessHeap(), 0, sizeof(IP_ADAPTER_INFO));
|
PIP_ADAPTER_INFO pAdapterInfo = (PIP_ADAPTER_INFO)HeapAlloc(GetProcessHeap(), 0, sizeof(IP_ADAPTER_INFO));
|
||||||
@@ -1170,10 +1169,10 @@ PIEthernet::InterfaceList PIEthernet::interfaces() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (pAdapterInfo) HeapFree(GetProcessHeap(), 0, pAdapterInfo);
|
if (pAdapterInfo) HeapFree(GetProcessHeap(), 0, pAdapterInfo);
|
||||||
|
#else
|
||||||
|
# ifdef MICRO_PIP
|
||||||
# else
|
# else
|
||||||
# ifdef LWIP
|
# ifdef ANDROID
|
||||||
# else
|
|
||||||
# ifdef ANDROID
|
|
||||||
struct ifconf ifc;
|
struct ifconf ifc;
|
||||||
int s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
|
int s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
|
||||||
ifc.ifc_len = 256;
|
ifc.ifc_len = 256;
|
||||||
@@ -1201,7 +1200,7 @@ PIEthernet::InterfaceList PIEthernet::interfaces() {
|
|||||||
il << ci;
|
il << ci;
|
||||||
}
|
}
|
||||||
delete ifc.ifc_buf;
|
delete ifc.ifc_buf;
|
||||||
# else
|
# else
|
||||||
struct ifaddrs *ret, *cif = 0;
|
struct ifaddrs *ret, *cif = 0;
|
||||||
int s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
|
int s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
|
||||||
if (getifaddrs(&ret) == 0) {
|
if (getifaddrs(&ret) == 0) {
|
||||||
@@ -1219,8 +1218,8 @@ PIEthernet::InterfaceList PIEthernet::interfaces() {
|
|||||||
ci.address = getSockAddr(cif->ifa_addr);
|
ci.address = getSockAddr(cif->ifa_addr);
|
||||||
ci.netmask = getSockAddr(cif->ifa_netmask);
|
ci.netmask = getSockAddr(cif->ifa_netmask);
|
||||||
ci.mac.clear();
|
ci.mac.clear();
|
||||||
# ifdef QNX
|
# ifdef QNX
|
||||||
# ifndef BLACKBERRY
|
# ifndef BLACKBERRY
|
||||||
int fd = ::open((PIString("/dev/io-net/") + ci.name).dataAscii(), O_RDONLY);
|
int fd = ::open((PIString("/dev/io-net/") + ci.name).dataAscii(), O_RDONLY);
|
||||||
if (fd != 0) {
|
if (fd != 0) {
|
||||||
nic_config_t nic;
|
nic_config_t nic;
|
||||||
@@ -1228,9 +1227,9 @@ PIEthernet::InterfaceList PIEthernet::interfaces() {
|
|||||||
::close(fd);
|
::close(fd);
|
||||||
ci.mac = macFromBytes(PIByteArray(nic.permanent_address, 6));
|
ci.mac = macFromBytes(PIByteArray(nic.permanent_address, 6));
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
# else
|
# else
|
||||||
# ifdef MAC_OS
|
# ifdef MAC_OS
|
||||||
PIString req = PISystemInfo::instance()->ifconfigPath + " " + ci.name + " | grep ether";
|
PIString req = PISystemInfo::instance()->ifconfigPath + " " + ci.name + " | grep ether";
|
||||||
FILE * fp = popen(req.dataAscii(), "r");
|
FILE * fp = popen(req.dataAscii(), "r");
|
||||||
if (fp != 0) {
|
if (fp != 0) {
|
||||||
@@ -1241,7 +1240,7 @@ PIEthernet::InterfaceList PIEthernet::interfaces() {
|
|||||||
}
|
}
|
||||||
pclose(fp);
|
pclose(fp);
|
||||||
}
|
}
|
||||||
# else
|
# else
|
||||||
if (s != -1) {
|
if (s != -1) {
|
||||||
struct ifreq ir;
|
struct ifreq ir;
|
||||||
memset(&ir, 0, sizeof(ir));
|
memset(&ir, 0, sizeof(ir));
|
||||||
@@ -1253,8 +1252,8 @@ PIEthernet::InterfaceList PIEthernet::interfaces() {
|
|||||||
ci.mtu = ir.ifr_mtu;
|
ci.mtu = ir.ifr_mtu;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# endif
|
|
||||||
# endif
|
# endif
|
||||||
|
# endif
|
||||||
ci.flags = 0;
|
ci.flags = 0;
|
||||||
if (cif->ifa_flags & IFF_UP) ci.flags |= PIEthernet::ifActive;
|
if (cif->ifa_flags & IFF_UP) ci.flags |= PIEthernet::ifActive;
|
||||||
if (cif->ifa_flags & IFF_RUNNING) ci.flags |= PIEthernet::ifRunning;
|
if (cif->ifa_flags & IFF_RUNNING) ci.flags |= PIEthernet::ifRunning;
|
||||||
@@ -1275,18 +1274,18 @@ PIEthernet::InterfaceList PIEthernet::interfaces() {
|
|||||||
piCout << "[PIEthernet]"
|
piCout << "[PIEthernet]"
|
||||||
<< "Can`t get interfaces: %1"_tr("PIEthernet").arg(errorString());
|
<< "Can`t get interfaces: %1"_tr("PIEthernet").arg(errorString());
|
||||||
if (s != -1) ::close(s);
|
if (s != -1) ::close(s);
|
||||||
# endif
|
|
||||||
# endif
|
# endif
|
||||||
# endif
|
# endif
|
||||||
|
#endif
|
||||||
return il;
|
return il;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PINetworkAddress PIEthernet::interfaceAddress(const PIString & interface_) {
|
PINetworkAddress PIEthernet::interfaceAddress(const PIString & interface_) {
|
||||||
# if defined(WINDOWS) || defined(LWIP)
|
#if defined(WINDOWS) || defined(MICRO_PIP)
|
||||||
piCout << "[PIEthernet] Not implemented, use \"PIEthernet::allAddresses\" or \"PIEthernet::interfaces\" instead";
|
piCout << "[PIEthernet] Not implemented, use \"PIEthernet::allAddresses\" or \"PIEthernet::interfaces\" instead";
|
||||||
return PINetworkAddress();
|
return PINetworkAddress();
|
||||||
# else
|
#else
|
||||||
struct ifreq ifr;
|
struct ifreq ifr;
|
||||||
piZeroMemory(ifr);
|
piZeroMemory(ifr);
|
||||||
strcpy(ifr.ifr_name, interface_.dataAscii());
|
strcpy(ifr.ifr_name, interface_.dataAscii());
|
||||||
@@ -1295,7 +1294,7 @@ PINetworkAddress PIEthernet::interfaceAddress(const PIString & interface_) {
|
|||||||
::close(s);
|
::close(s);
|
||||||
struct sockaddr_in * sa = (struct sockaddr_in *)&ifr.ifr_addr;
|
struct sockaddr_in * sa = (struct sockaddr_in *)&ifr.ifr_addr;
|
||||||
return PINetworkAddress(uint(sa->sin_addr.s_addr));
|
return PINetworkAddress(uint(sa->sin_addr.s_addr));
|
||||||
# endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1318,16 +1317,16 @@ PIVector<PINetworkAddress> PIEthernet::allAddresses() {
|
|||||||
// System wrap
|
// System wrap
|
||||||
|
|
||||||
int PIEthernet::ethErrorCore() {
|
int PIEthernet::ethErrorCore() {
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
return WSAGetLastError();
|
return WSAGetLastError();
|
||||||
# else
|
#else
|
||||||
return errno;
|
return errno;
|
||||||
# endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PIString PIEthernet::ethErrorString() {
|
PIString PIEthernet::ethErrorString() {
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
char * msg = nullptr;
|
char * msg = nullptr;
|
||||||
int err = WSAGetLastError();
|
int err = WSAGetLastError();
|
||||||
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
@@ -1344,18 +1343,18 @@ PIString PIEthernet::ethErrorString() {
|
|||||||
} else
|
} else
|
||||||
ret += '?';
|
ret += '?';
|
||||||
return ret;
|
return ret;
|
||||||
# else
|
#else
|
||||||
return errorString();
|
return errorString();
|
||||||
# endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int PIEthernet::ethRecv(int sock, void * buf, int size, int flags) {
|
int PIEthernet::ethRecv(int sock, void * buf, int size, int flags) {
|
||||||
if (sock < 0) return -1;
|
if (sock < 0) return -1;
|
||||||
return recv(sock,
|
return recv(sock,
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
(char *)
|
(char *)
|
||||||
# endif
|
#endif
|
||||||
buf,
|
buf,
|
||||||
size,
|
size,
|
||||||
flags);
|
flags);
|
||||||
@@ -1364,29 +1363,29 @@ int PIEthernet::ethRecv(int sock, void * buf, int size, int flags) {
|
|||||||
|
|
||||||
int PIEthernet::ethRecvfrom(int sock, void * buf, int size, int flags, sockaddr * addr) {
|
int PIEthernet::ethRecvfrom(int sock, void * buf, int size, int flags, sockaddr * addr) {
|
||||||
if (sock < 0) return -1;
|
if (sock < 0) return -1;
|
||||||
# ifdef QNX
|
#ifdef QNX
|
||||||
return recv(sock, buf, size, flags);
|
return recv(sock, buf, size, flags);
|
||||||
# else
|
#else
|
||||||
socklen_t len = sizeof(sockaddr);
|
socklen_t len = sizeof(sockaddr);
|
||||||
return recvfrom(sock,
|
return recvfrom(sock,
|
||||||
# ifdef WINDOWS
|
# ifdef WINDOWS
|
||||||
(char *)
|
(char *)
|
||||||
# endif
|
# endif
|
||||||
buf,
|
buf,
|
||||||
size,
|
size,
|
||||||
flags,
|
flags,
|
||||||
addr,
|
addr,
|
||||||
&len);
|
&len);
|
||||||
# endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int PIEthernet::ethSendto(int sock, const void * buf, int size, int flags, sockaddr * addr, int addr_len) {
|
int PIEthernet::ethSendto(int sock, const void * buf, int size, int flags, sockaddr * addr, int addr_len) {
|
||||||
if (sock < 0) return -1;
|
if (sock < 0) return -1;
|
||||||
return sendto(sock,
|
return sendto(sock,
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
(const char *)
|
(const char *)
|
||||||
# endif
|
#endif
|
||||||
buf,
|
buf,
|
||||||
size,
|
size,
|
||||||
flags,
|
flags,
|
||||||
@@ -1400,26 +1399,26 @@ void PIEthernet::ethClosesocket(int sock, bool shutdown) {
|
|||||||
if (sock < 0) return;
|
if (sock < 0) return;
|
||||||
if (shutdown)
|
if (shutdown)
|
||||||
::shutdown(sock,
|
::shutdown(sock,
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
SD_BOTH);
|
SD_BOTH);
|
||||||
closesocket(sock);
|
closesocket(sock);
|
||||||
# else
|
#else
|
||||||
SHUT_RDWR);
|
SHUT_RDWR);
|
||||||
::close(sock);
|
::close(sock);
|
||||||
# endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int PIEthernet::ethSetsockopt(int sock, int level, int optname, const void * optval, int optlen) {
|
int PIEthernet::ethSetsockopt(int sock, int level, int optname, const void * optval, int optlen) {
|
||||||
if (sock < 0) return -1;
|
if (sock < 0) return -1;
|
||||||
auto ret = setsockopt(sock,
|
auto ret = setsockopt(sock,
|
||||||
level,
|
level,
|
||||||
optname,
|
optname,
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
(char *)
|
(char *)
|
||||||
# endif
|
#endif
|
||||||
optval,
|
optval,
|
||||||
optlen);
|
optlen);
|
||||||
if (ret != 0) piCout << "setsockopt error:" << ethErrorString();
|
if (ret != 0) piCout << "setsockopt error:" << ethErrorString();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -1427,11 +1426,11 @@ int PIEthernet::ethSetsockopt(int sock, int level, int optname, const void * opt
|
|||||||
|
|
||||||
int PIEthernet::ethSetsockoptInt(int sock, int level, int optname, int value) {
|
int PIEthernet::ethSetsockoptInt(int sock, int level, int optname, int value) {
|
||||||
if (sock < 0) return -1;
|
if (sock < 0) return -1;
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
DWORD
|
DWORD
|
||||||
# else
|
#else
|
||||||
int
|
int
|
||||||
# endif
|
#endif
|
||||||
so = value;
|
so = value;
|
||||||
return ethSetsockopt(sock, level, optname, &so, sizeof(so));
|
return ethSetsockopt(sock, level, optname, &so, sizeof(so));
|
||||||
}
|
}
|
||||||
@@ -1439,11 +1438,11 @@ int PIEthernet::ethSetsockoptInt(int sock, int level, int optname, int value) {
|
|||||||
|
|
||||||
int PIEthernet::ethSetsockoptBool(int sock, int level, int optname, bool value) {
|
int PIEthernet::ethSetsockoptBool(int sock, int level, int optname, bool value) {
|
||||||
if (sock < 0) return -1;
|
if (sock < 0) return -1;
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
BOOL
|
BOOL
|
||||||
# else
|
#else
|
||||||
int
|
int
|
||||||
# endif
|
#endif
|
||||||
so = (value ? 1 : 0);
|
so = (value ? 1 : 0);
|
||||||
return ethSetsockopt(sock, level, optname, &so, sizeof(so));
|
return ethSetsockopt(sock, level, optname, &so, sizeof(so));
|
||||||
}
|
}
|
||||||
@@ -1451,12 +1450,12 @@ int PIEthernet::ethSetsockoptBool(int sock, int level, int optname, bool value)
|
|||||||
|
|
||||||
void PIEthernet::ethNonblocking(int sock) {
|
void PIEthernet::ethNonblocking(int sock) {
|
||||||
if (sock < 0) return;
|
if (sock < 0) return;
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
u_long mode = 1;
|
u_long mode = 1;
|
||||||
ioctlsocket(sock, FIONBIO, &mode);
|
ioctlsocket(sock, FIONBIO, &mode);
|
||||||
# else
|
#else
|
||||||
fcntl(sock, F_SETFL, O_NONBLOCK);
|
fcntl(sock, F_SETFL, O_NONBLOCK);
|
||||||
# endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1472,7 +1471,7 @@ bool PIEthernet::ethIsWriteable(int sock) {
|
|||||||
timeout.tv_sec = timeout.tv_usec = 0;
|
timeout.tv_sec = timeout.tv_usec = 0;
|
||||||
::select(fds, nullptr, &fd_test, nullptr, &timeout);
|
::select(fds, nullptr, &fd_test, nullptr, &timeout);
|
||||||
return FD_ISSET(sock, &fd_test);*/
|
return FD_ISSET(sock, &fd_test);*/
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
fd_set fd_test;
|
fd_set fd_test;
|
||||||
FD_ZERO(&fd_test);
|
FD_ZERO(&fd_test);
|
||||||
FD_SET(sock, &fd_test);
|
FD_SET(sock, &fd_test);
|
||||||
@@ -1480,12 +1479,10 @@ bool PIEthernet::ethIsWriteable(int sock) {
|
|||||||
timeout.tv_sec = timeout.tv_usec = 0;
|
timeout.tv_sec = timeout.tv_usec = 0;
|
||||||
::select(0, nullptr, &fd_test, nullptr, &timeout);
|
::select(0, nullptr, &fd_test, nullptr, &timeout);
|
||||||
return FD_ISSET(sock, &fd_test);
|
return FD_ISSET(sock, &fd_test);
|
||||||
# else
|
#else
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
socklen_t len = sizeof(ret);
|
socklen_t len = sizeof(ret);
|
||||||
getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&ret, &len);
|
getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&ret, &len);
|
||||||
return ret == 0;
|
return ret == 0;
|
||||||
# endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // PIP_NO_SOCKET
|
|
||||||
|
|||||||
@@ -26,17 +26,14 @@
|
|||||||
#ifndef PIETHERNET_H
|
#ifndef PIETHERNET_H
|
||||||
#define PIETHERNET_H
|
#define PIETHERNET_H
|
||||||
|
|
||||||
|
|
||||||
#include "piiodevice.h"
|
#include "piiodevice.h"
|
||||||
#include "pinetworkaddress.h"
|
#include "pinetworkaddress.h"
|
||||||
|
|
||||||
#ifndef PIP_NO_SOCKET
|
#ifdef ANDROID
|
||||||
|
|
||||||
# ifdef ANDROID
|
|
||||||
struct
|
struct
|
||||||
# else
|
#else
|
||||||
class
|
class
|
||||||
# endif
|
#endif
|
||||||
sockaddr;
|
sockaddr;
|
||||||
|
|
||||||
class PIP_EXPORT PIEthernet: public PIIODevice {
|
class PIP_EXPORT PIEthernet: public PIIODevice {
|
||||||
@@ -442,7 +439,7 @@ public:
|
|||||||
//! \}
|
//! \}
|
||||||
//! \ioparams
|
//! \ioparams
|
||||||
//! \{
|
//! \{
|
||||||
# ifdef DOXYGEN
|
#ifdef DOXYGEN
|
||||||
//! \brief read ip, default ""
|
//! \brief read ip, default ""
|
||||||
string ip;
|
string ip;
|
||||||
|
|
||||||
@@ -463,7 +460,7 @@ public:
|
|||||||
|
|
||||||
//! \brief time-to-live for multicast, default 1
|
//! \brief time-to-live for multicast, default 1
|
||||||
int multicastTTL;
|
int multicastTTL;
|
||||||
# endif
|
#endif
|
||||||
//! \}
|
//! \}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -512,9 +509,9 @@ private:
|
|||||||
static void server_func(void * eth);
|
static void server_func(void * eth);
|
||||||
void setType(Type t, bool reopen = true);
|
void setType(Type t, bool reopen = true);
|
||||||
bool connectTCP();
|
bool connectTCP();
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
long waitForEvent(PIWaitEvent & event, long mask);
|
long waitForEvent(PIWaitEvent & event, long mask);
|
||||||
# endif
|
#endif
|
||||||
|
|
||||||
static int ethErrorCore();
|
static int ethErrorCore();
|
||||||
static PIString ethErrorString();
|
static PIString ethErrorString();
|
||||||
@@ -539,5 +536,4 @@ inline bool operator!=(const PIEthernet::Interface & v0, const PIEthernet::Inter
|
|||||||
return (v0.name != v1.name || v0.address != v1.address || v0.netmask != v1.netmask);
|
return (v0.name != v1.name || v0.address != v1.address || v0.netmask != v1.netmask);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // PIP_NO_SOCKET
|
|
||||||
#endif // PIETHERNET_H
|
#endif // PIETHERNET_H
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
# include <utime.h>
|
# include <utime.h>
|
||||||
#endif
|
#endif
|
||||||
#define S_IFHDN 0x40
|
#define S_IFHDN 0x40
|
||||||
#if defined(QNX) || defined(ANDROID) || defined(MICRO_PIP)
|
#if defined(QNX) || defined(ANDROID) || defined(FREERTOS)
|
||||||
# define _fopen_call_ fopen
|
# define _fopen_call_ fopen
|
||||||
# define _fseek_call_ fseek
|
# define _fseek_call_ fseek
|
||||||
# define _ftell_call_ ftell
|
# define _ftell_call_ ftell
|
||||||
|
|||||||
@@ -19,37 +19,38 @@
|
|||||||
|
|
||||||
#include "piserial.h"
|
#include "piserial.h"
|
||||||
|
|
||||||
#ifndef MICRO_PIP
|
#include "piconfig.h"
|
||||||
|
#include "pidir.h"
|
||||||
|
#include "piincludes_p.h"
|
||||||
|
#include "pipropertystorage.h"
|
||||||
|
#include "pitime.h"
|
||||||
|
#include "pitranslator.h"
|
||||||
|
#include "piwaitevent_p.h"
|
||||||
|
|
||||||
# include "piconfig.h"
|
#include <errno.h>
|
||||||
# include "pidir.h"
|
|
||||||
# include "piincludes_p.h"
|
|
||||||
# include "pipropertystorage.h"
|
|
||||||
# include "pitime.h"
|
|
||||||
# include "pitranslator.h"
|
|
||||||
# include "piwaitevent_p.h"
|
|
||||||
|
|
||||||
# include <errno.h>
|
#if defined(MICRO_PIP)
|
||||||
|
# define PISERIAL_NO_PINS
|
||||||
# if defined(PISERIAL_NO_PINS) || defined(WINDOWS)
|
#endif
|
||||||
# define TIOCM_LE 1
|
#if defined(PISERIAL_NO_PINS) || defined(WINDOWS)
|
||||||
# define TIOCM_DTR 4
|
# define TIOCM_LE 1
|
||||||
# define TIOCM_RTS 7
|
# define TIOCM_DTR 4
|
||||||
# define TIOCM_CTS 8
|
# define TIOCM_RTS 7
|
||||||
# define TIOCM_ST 3
|
# define TIOCM_CTS 8
|
||||||
# define TIOCM_SR 2
|
# define TIOCM_ST 3
|
||||||
# define TIOCM_CAR 1
|
# define TIOCM_SR 2
|
||||||
# define TIOCM_RNG 9
|
# define TIOCM_CAR 1
|
||||||
# define TIOCM_DSR 6
|
# define TIOCM_RNG 9
|
||||||
|
# define TIOCM_DSR 6
|
||||||
|
#endif
|
||||||
|
#ifdef WINDOWS
|
||||||
|
# ifndef INITGUID
|
||||||
|
# define INITGUID
|
||||||
|
# include <guiddef.h>
|
||||||
|
# undef INITGUID
|
||||||
|
# else
|
||||||
|
# include <guiddef.h>
|
||||||
# endif
|
# endif
|
||||||
# ifdef WINDOWS
|
|
||||||
# ifndef INITGUID
|
|
||||||
# define INITGUID
|
|
||||||
# include <guiddef.h>
|
|
||||||
# undef INITGUID
|
|
||||||
# else
|
|
||||||
# include <guiddef.h>
|
|
||||||
# endif
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
# include <ntddmodm.h>
|
# include <ntddmodm.h>
|
||||||
# include <winreg.h>
|
# include <winreg.h>
|
||||||
@@ -58,89 +59,89 @@
|
|||||||
# include <cfgmgr32.h>
|
# include <cfgmgr32.h>
|
||||||
# include <setupapi.h>
|
# include <setupapi.h>
|
||||||
// clang-format on
|
// clang-format on
|
||||||
# define B50 50
|
# define B50 50
|
||||||
# define B75 75
|
# define B75 75
|
||||||
# define B110 110
|
# define B110 110
|
||||||
# define B300 300
|
# define B300 300
|
||||||
# define B600 600
|
# define B600 600
|
||||||
# define B1200 1200
|
# define B1200 1200
|
||||||
# define B2400 2400
|
# define B2400 2400
|
||||||
# define B4800 4800
|
# define B4800 4800
|
||||||
# define B9600 9600
|
# define B9600 9600
|
||||||
# define B14400 14400
|
# define B14400 14400
|
||||||
# define B19200 19200
|
# define B19200 19200
|
||||||
# define B38400 38400
|
# define B38400 38400
|
||||||
# define B57600 57600
|
# define B57600 57600
|
||||||
# define B115200 115200
|
# define B115200 115200
|
||||||
# define B230400 230400
|
# define B230400 230400
|
||||||
# define B460800 460800
|
# define B460800 460800
|
||||||
# define B500000 500000
|
# define B500000 500000
|
||||||
# define B576000 576000
|
# define B576000 576000
|
||||||
# define B921600 921600
|
# define B921600 921600
|
||||||
# define B1000000 1000000
|
# define B1000000 1000000
|
||||||
# define B1152000 1152000
|
# define B1152000 1152000
|
||||||
# define B1500000 1500000
|
# define B1500000 1500000
|
||||||
# define B2000000 2000000
|
# define B2000000 2000000
|
||||||
# define B2500000 2500000
|
# define B2500000 2500000
|
||||||
# define B3000000 3000000
|
# define B3000000 3000000
|
||||||
# define B3500000 3500000
|
# define B3500000 3500000
|
||||||
# define B4000000 4000000
|
# define B4000000 4000000
|
||||||
# else
|
#else
|
||||||
# include <fcntl.h>
|
# include <fcntl.h>
|
||||||
# include <sys/ioctl.h>
|
# include <sys/ioctl.h>
|
||||||
# include <termios.h>
|
# include <termios.h>
|
||||||
# ifndef B50
|
# ifndef B50
|
||||||
# define B50 0000001
|
# define B50 0000001
|
||||||
# endif
|
|
||||||
# ifndef B75
|
|
||||||
# define B75 0000002
|
|
||||||
# endif
|
|
||||||
# ifndef B230400
|
|
||||||
# define B230400 0010003
|
|
||||||
# endif
|
|
||||||
# ifndef B460800
|
|
||||||
# define B460800 0010004
|
|
||||||
# endif
|
|
||||||
# ifndef B500000
|
|
||||||
# define B500000 0010005
|
|
||||||
# endif
|
|
||||||
# ifndef B576000
|
|
||||||
# define B576000 0010006
|
|
||||||
# endif
|
|
||||||
# ifndef B921600
|
|
||||||
# define B921600 0010007
|
|
||||||
# endif
|
|
||||||
# ifndef B1000000
|
|
||||||
# define B1000000 0010010
|
|
||||||
# endif
|
|
||||||
# ifndef B1152000
|
|
||||||
# define B1152000 0010011
|
|
||||||
# endif
|
|
||||||
# ifndef B1500000
|
|
||||||
# define B1500000 0010012
|
|
||||||
# endif
|
|
||||||
# ifndef B2000000
|
|
||||||
# define B2000000 0010013
|
|
||||||
# endif
|
|
||||||
# ifndef B2500000
|
|
||||||
# define B2500000 0010014
|
|
||||||
# endif
|
|
||||||
# ifndef B3000000
|
|
||||||
# define B3000000 0010015
|
|
||||||
# endif
|
|
||||||
# ifndef B3500000
|
|
||||||
# define B3500000 0010016
|
|
||||||
# endif
|
|
||||||
# ifndef B4000000
|
|
||||||
# define B4000000 0010017
|
|
||||||
# endif
|
|
||||||
# endif
|
# endif
|
||||||
# ifndef CRTSCTS
|
# ifndef B75
|
||||||
# define CRTSCTS 020000000000
|
# define B75 0000002
|
||||||
# endif
|
# endif
|
||||||
# ifdef LINUX
|
# ifndef B230400
|
||||||
# include <linux/serial.h>
|
# define B230400 0010003
|
||||||
# endif
|
# endif
|
||||||
|
# ifndef B460800
|
||||||
|
# define B460800 0010004
|
||||||
|
# endif
|
||||||
|
# ifndef B500000
|
||||||
|
# define B500000 0010005
|
||||||
|
# endif
|
||||||
|
# ifndef B576000
|
||||||
|
# define B576000 0010006
|
||||||
|
# endif
|
||||||
|
# ifndef B921600
|
||||||
|
# define B921600 0010007
|
||||||
|
# endif
|
||||||
|
# ifndef B1000000
|
||||||
|
# define B1000000 0010010
|
||||||
|
# endif
|
||||||
|
# ifndef B1152000
|
||||||
|
# define B1152000 0010011
|
||||||
|
# endif
|
||||||
|
# ifndef B1500000
|
||||||
|
# define B1500000 0010012
|
||||||
|
# endif
|
||||||
|
# ifndef B2000000
|
||||||
|
# define B2000000 0010013
|
||||||
|
# endif
|
||||||
|
# ifndef B2500000
|
||||||
|
# define B2500000 0010014
|
||||||
|
# endif
|
||||||
|
# ifndef B3000000
|
||||||
|
# define B3000000 0010015
|
||||||
|
# endif
|
||||||
|
# ifndef B3500000
|
||||||
|
# define B3500000 0010016
|
||||||
|
# endif
|
||||||
|
# ifndef B4000000
|
||||||
|
# define B4000000 0010017
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
#ifndef CRTSCTS
|
||||||
|
# define CRTSCTS 020000000000
|
||||||
|
#endif
|
||||||
|
#ifdef LINUX
|
||||||
|
# include <linux/serial.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
//! \class PISerial piserial.h
|
//! \class PISerial piserial.h
|
||||||
@@ -176,16 +177,16 @@ REGISTER_DEVICE(PISerial)
|
|||||||
|
|
||||||
PRIVATE_DEFINITION_START(PISerial)
|
PRIVATE_DEFINITION_START(PISerial)
|
||||||
PIWaitEvent event;
|
PIWaitEvent event;
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
PIWaitEvent event_write;
|
PIWaitEvent event_write;
|
||||||
DCB desc, sdesc;
|
DCB desc, sdesc;
|
||||||
HANDLE hCom = nullptr;
|
HANDLE hCom = nullptr;
|
||||||
DWORD readed = 0, mask = 0;
|
DWORD readed = 0, mask = 0;
|
||||||
OVERLAPPED overlap, overlap_write;
|
OVERLAPPED overlap, overlap_write;
|
||||||
# else
|
#else
|
||||||
termios desc, sdesc;
|
termios desc, sdesc;
|
||||||
uint readed = 0;
|
uint readed = 0;
|
||||||
# endif
|
#endif
|
||||||
PRIVATE_DEFINITION_END(PISerial)
|
PRIVATE_DEFINITION_END(PISerial)
|
||||||
|
|
||||||
|
|
||||||
@@ -213,9 +214,9 @@ PISerial::~PISerial() {
|
|||||||
stopAndWait();
|
stopAndWait();
|
||||||
close();
|
close();
|
||||||
PRIVATE->event.destroy();
|
PRIVATE->event.destroy();
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
PRIVATE->event_write.destroy();
|
PRIVATE->event_write.destroy();
|
||||||
# endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -346,7 +347,7 @@ bool PISerial::setBreak(bool enabled) {
|
|||||||
piCoutObj << "sendBreak error: \"" << path() << "\" is not opened!";
|
piCoutObj << "sendBreak error: \"" << path() << "\" is not opened!";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
if (!SetCommBreak(PRIVATE->hCom)) {
|
if (!SetCommBreak(PRIVATE->hCom)) {
|
||||||
piCoutObj << "setBreak error: " << errorString();
|
piCoutObj << "setBreak error: " << errorString();
|
||||||
@@ -362,14 +363,14 @@ bool PISerial::setBreak(bool enabled) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# else
|
#else
|
||||||
if (ioctl(fd, enabled ? TIOCSBRK : TIOCCBRK) < 0) {
|
if (ioctl(fd, enabled ? TIOCSBRK : TIOCCBRK) < 0) {
|
||||||
piCoutObj << "setBreak error: " << errorString();
|
piCoutObj << "setBreak error: " << errorString();
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
# endif
|
#endif
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -379,8 +380,8 @@ bool PISerial::setBit(int bit, bool on, const PIString & bname) {
|
|||||||
piCoutObj << "setBit" << bname << " error: \"" << path() << "\" is not opened!";
|
piCoutObj << "setBit" << bname << " error: \"" << path() << "\" is not opened!";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
# ifndef PISERIAL_NO_PINS
|
#ifndef PISERIAL_NO_PINS
|
||||||
# ifdef WINDOWS
|
# ifdef WINDOWS
|
||||||
static int bit_map_on[] = {0, 0, 0, 0, SETDTR, 0, 0, SETRTS, 0, 0, 0};
|
static int bit_map_on[] = {0, 0, 0, 0, SETDTR, 0, 0, SETRTS, 0, 0, 0};
|
||||||
static int bit_map_off[] = {0, 0, 0, 0, CLRDTR, 0, 0, CLRRTS, 0, 0, 0};
|
static int bit_map_off[] = {0, 0, 0, 0, CLRDTR, 0, 0, CLRRTS, 0, 0, 0};
|
||||||
int action = (on ? bit_map_on : bit_map_off)[bit];
|
int action = (on ? bit_map_on : bit_map_off)[bit];
|
||||||
@@ -391,14 +392,14 @@ bool PISerial::setBit(int bit, bool on, const PIString & bname) {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
# else
|
# else
|
||||||
if (ioctl(fd, on ? TIOCMBIS : TIOCMBIC, &bit) < 0) {
|
if (ioctl(fd, on ? TIOCMBIS : TIOCMBIC, &bit) < 0) {
|
||||||
piCoutObj << "setBit" << bname << " error: " << errorString();
|
piCoutObj << "setBit" << bname << " error: " << errorString();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
# endif
|
|
||||||
# endif
|
# endif
|
||||||
|
#endif
|
||||||
piCoutObj << "setBit" << bname << " doesn`t implemented, sorry :-(";
|
piCoutObj << "setBit" << bname << " doesn`t implemented, sorry :-(";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -409,23 +410,23 @@ bool PISerial::isBit(int bit, const PIString & bname) const {
|
|||||||
piCoutObj << "isBit" << bname << " error: \"" << path() << "\" is not opened!";
|
piCoutObj << "isBit" << bname << " error: \"" << path() << "\" is not opened!";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
# ifndef PISERIAL_NO_PINS
|
#ifndef PISERIAL_NO_PINS
|
||||||
# ifdef WINDOWS
|
# ifdef WINDOWS
|
||||||
# else
|
# else
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
if (ioctl(fd, TIOCMGET, &ret) < 0) piCoutObj << "isBit" << bname << " error: " << errorString();
|
if (ioctl(fd, TIOCMGET, &ret) < 0) piCoutObj << "isBit" << bname << " error: " << errorString();
|
||||||
return ret & bit;
|
return ret & bit;
|
||||||
# endif
|
|
||||||
# endif
|
# endif
|
||||||
|
#endif
|
||||||
piCoutObj << "isBit" << bname << " doesn`t implemented, sorry :-(";
|
piCoutObj << "isBit" << bname << " doesn`t implemented, sorry :-(";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PISerial::flush() {
|
void PISerial::flush() {
|
||||||
# ifndef WINDOWS
|
#ifndef WINDOWS
|
||||||
if (fd != -1) tcflush(fd, TCIOFLUSH);
|
if (fd != -1) tcflush(fd, TCIOFLUSH);
|
||||||
# endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -440,9 +441,9 @@ int PISerial::convertSpeed(PISerial::Speed speed) {
|
|||||||
case S2400: return B2400;
|
case S2400: return B2400;
|
||||||
case S4800: return B4800;
|
case S4800: return B4800;
|
||||||
case S9600: return B9600;
|
case S9600: return B9600;
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
case S14400: return B14400;
|
case S14400: return B14400;
|
||||||
# endif
|
#endif
|
||||||
case S19200: return B19200;
|
case S19200: return B19200;
|
||||||
case S38400: return B38400;
|
case S38400: return B38400;
|
||||||
case S57600: return B57600;
|
case S57600: return B57600;
|
||||||
@@ -462,13 +463,13 @@ int PISerial::convertSpeed(PISerial::Speed speed) {
|
|||||||
case S4000000: return B4000000;
|
case S4000000: return B4000000;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
piCoutObj << "Warning: Custom speed %1"_tr("PISerial").arg((int)speed);
|
piCoutObj << "Warning: Custom speed %1"_tr("PISerial").arg((int)speed);
|
||||||
return (int)speed;
|
return (int)speed;
|
||||||
# else
|
#else
|
||||||
piCoutObj << "Warning: Unknown speed %1, using 115200"_tr("PISerial").arg((int)speed);
|
piCoutObj << "Warning: Unknown speed %1, using 115200"_tr("PISerial").arg((int)speed);
|
||||||
return B115200;
|
return B115200;
|
||||||
# endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -671,9 +672,9 @@ bool PISerial::send(const void * data, int size) {
|
|||||||
void PISerial::interrupt() {
|
void PISerial::interrupt() {
|
||||||
// piCoutObj << "interrupt";
|
// piCoutObj << "interrupt";
|
||||||
PRIVATE->event.interrupt();
|
PRIVATE->event.interrupt();
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
PRIVATE->event_write.interrupt();
|
PRIVATE->event_write.interrupt();
|
||||||
# endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -695,7 +696,7 @@ bool PISerial::openDevice() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (p.isEmpty()) return false;
|
if (p.isEmpty()) return false;
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
DWORD ds = 0, sm = 0;
|
DWORD ds = 0, sm = 0;
|
||||||
if (isReadable()) {
|
if (isReadable()) {
|
||||||
ds |= GENERIC_READ;
|
ds |= GENERIC_READ;
|
||||||
@@ -713,7 +714,7 @@ bool PISerial::openDevice() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
fd = 0;
|
fd = 0;
|
||||||
# else
|
#else
|
||||||
int om = 0;
|
int om = 0;
|
||||||
switch (mode()) {
|
switch (mode()) {
|
||||||
case PIIODevice::ReadOnly: om = O_RDONLY; break;
|
case PIIODevice::ReadOnly: om = O_RDONLY; break;
|
||||||
@@ -728,12 +729,12 @@ bool PISerial::openDevice() {
|
|||||||
tcgetattr(fd, &PRIVATE->desc);
|
tcgetattr(fd, &PRIVATE->desc);
|
||||||
PRIVATE->sdesc = PRIVATE->desc;
|
PRIVATE->sdesc = PRIVATE->desc;
|
||||||
// piCoutObj << "Initialized " << p;
|
// piCoutObj << "Initialized " << p;
|
||||||
# endif
|
#endif
|
||||||
applySettings();
|
applySettings();
|
||||||
PRIVATE->event.create();
|
PRIVATE->event.create();
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
PRIVATE->event_write.create();
|
PRIVATE->event_write.create();
|
||||||
# endif
|
#endif
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -744,28 +745,28 @@ bool PISerial::closeDevice() {
|
|||||||
stopThreadedRead();
|
stopThreadedRead();
|
||||||
}
|
}
|
||||||
if (fd != -1) {
|
if (fd != -1) {
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
SetCommState(PRIVATE->hCom, &PRIVATE->sdesc);
|
SetCommState(PRIVATE->hCom, &PRIVATE->sdesc);
|
||||||
SetCommMask(PRIVATE->hCom, PRIVATE->mask);
|
SetCommMask(PRIVATE->hCom, PRIVATE->mask);
|
||||||
// piCoutObj << "close" <<
|
// piCoutObj << "close" <<
|
||||||
CloseHandle(PRIVATE->hCom);
|
CloseHandle(PRIVATE->hCom);
|
||||||
PRIVATE->hCom = 0;
|
PRIVATE->hCom = 0;
|
||||||
# else
|
#else
|
||||||
tcsetattr(fd, TCSANOW, &PRIVATE->sdesc);
|
tcsetattr(fd, TCSANOW, &PRIVATE->sdesc);
|
||||||
::close(fd);
|
::close(fd);
|
||||||
# endif
|
#endif
|
||||||
fd = -1;
|
fd = -1;
|
||||||
}
|
}
|
||||||
PRIVATE->event.destroy();
|
PRIVATE->event.destroy();
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
PRIVATE->event_write.destroy();
|
PRIVATE->event_write.destroy();
|
||||||
# endif
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PISerial::applySettings() {
|
void PISerial::applySettings() {
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
if (fd == -1) return;
|
if (fd == -1) return;
|
||||||
setTimeouts();
|
setTimeouts();
|
||||||
GetCommMask(PRIVATE->hCom, &PRIVATE->mask);
|
GetCommMask(PRIVATE->hCom, &PRIVATE->mask);
|
||||||
@@ -791,7 +792,7 @@ void PISerial::applySettings() {
|
|||||||
piCoutObj << "Unable to set comm state for \"%1\""_tr("PISerial").arg(path());
|
piCoutObj << "Unable to set comm state for \"%1\""_tr("PISerial").arg(path());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
# else
|
#else
|
||||||
if (fd == -1) return;
|
if (fd == -1) return;
|
||||||
tcgetattr(fd, &PRIVATE->desc);
|
tcgetattr(fd, &PRIVATE->desc);
|
||||||
PRIVATE->desc.c_oflag = PRIVATE->desc.c_lflag = PRIVATE->desc.c_cflag = 0;
|
PRIVATE->desc.c_oflag = PRIVATE->desc.c_lflag = PRIVATE->desc.c_cflag = 0;
|
||||||
@@ -825,12 +826,12 @@ void PISerial::applySettings() {
|
|||||||
piCoutObj << "Can`t set attributes for \"%1\""_tr("PISerial").arg(path());
|
piCoutObj << "Can`t set attributes for \"%1\""_tr("PISerial").arg(path());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
# endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PISerial::setTimeouts() {
|
void PISerial::setTimeouts() {
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
COMMTIMEOUTS times;
|
COMMTIMEOUTS times;
|
||||||
if (isOptionSet(BlockingRead)) {
|
if (isOptionSet(BlockingRead)) {
|
||||||
times.ReadIntervalTimeout = MAXDWORD;
|
times.ReadIntervalTimeout = MAXDWORD;
|
||||||
@@ -844,9 +845,9 @@ void PISerial::setTimeouts() {
|
|||||||
times.WriteTotalTimeoutConstant = isOptionSet(BlockingWrite) ? 0 : 1;
|
times.WriteTotalTimeoutConstant = isOptionSet(BlockingWrite) ? 0 : 1;
|
||||||
times.WriteTotalTimeoutMultiplier = 0;
|
times.WriteTotalTimeoutMultiplier = 0;
|
||||||
if (SetCommTimeouts(PRIVATE->hCom, ×) == -1) piCoutObj << "Unable to set timeouts for \"" << path() << "\"";
|
if (SetCommTimeouts(PRIVATE->hCom, ×) == -1) piCoutObj << "Unable to set timeouts for \"" << path() << "\"";
|
||||||
# else
|
#else
|
||||||
fcntl(fd, F_SETFL, isOptionSet(BlockingRead) ? 0 : O_NONBLOCK);
|
fcntl(fd, F_SETFL, isOptionSet(BlockingRead) ? 0 : O_NONBLOCK);
|
||||||
# endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -865,7 +866,7 @@ void PISerial::setTimeouts() {
|
|||||||
//!
|
//!
|
||||||
//! \~\sa \a readData(), \a readString()
|
//! \~\sa \a readData(), \a readString()
|
||||||
ssize_t PISerial::readDevice(void * read_to, ssize_t max_size) {
|
ssize_t PISerial::readDevice(void * read_to, ssize_t max_size) {
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
if (!canRead()) return -1;
|
if (!canRead()) return -1;
|
||||||
if (sending) return -1;
|
if (sending) return -1;
|
||||||
// piCoutObj << "read ..." << PRIVATE->hCom << max_size;
|
// piCoutObj << "read ..." << PRIVATE->hCom << max_size;
|
||||||
@@ -895,7 +896,7 @@ ssize_t PISerial::readDevice(void * read_to, ssize_t max_size) {
|
|||||||
return -1;
|
return -1;
|
||||||
// piCoutObj << "read" << (PRIVATE->readed) << errorString();
|
// piCoutObj << "read" << (PRIVATE->readed) << errorString();
|
||||||
return PRIVATE->readed;
|
return PRIVATE->readed;
|
||||||
# else
|
#else
|
||||||
if (!canRead()) return -1;
|
if (!canRead()) return -1;
|
||||||
if (isOptionSet(PIIODevice::BlockingRead)) {
|
if (isOptionSet(PIIODevice::BlockingRead)) {
|
||||||
if (!PRIVATE->event.wait(fd)) return -1;
|
if (!PRIVATE->event.wait(fd)) return -1;
|
||||||
@@ -910,7 +911,7 @@ ssize_t PISerial::readDevice(void * read_to, ssize_t max_size) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
# endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -919,7 +920,7 @@ ssize_t PISerial::writeDevice(const void * data, ssize_t max_size) {
|
|||||||
// piCoutObj << "Can`t write to uninitialized COM";
|
// piCoutObj << "Can`t write to uninitialized COM";
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
DWORD wrote(0);
|
DWORD wrote(0);
|
||||||
// piCoutObj << "send ..." << max_size;// << ": " << PIString((char*)data, max_size);
|
// piCoutObj << "send ..." << max_size;// << ": " << PIString((char*)data, max_size);
|
||||||
sending = true;
|
sending = true;
|
||||||
@@ -931,11 +932,11 @@ ssize_t PISerial::writeDevice(const void * data, ssize_t max_size) {
|
|||||||
}
|
}
|
||||||
sending = false;
|
sending = false;
|
||||||
// piCoutObj << "send ok" << wrote;// << " bytes in " << path();
|
// piCoutObj << "send ok" << wrote;// << " bytes in " << path();
|
||||||
# else
|
#else
|
||||||
ssize_t wrote;
|
ssize_t wrote;
|
||||||
wrote = ::write(fd, data, max_size);
|
wrote = ::write(fd, data, max_size);
|
||||||
if (isOptionSet(BlockingWrite)) tcdrain(fd);
|
if (isOptionSet(BlockingWrite)) tcdrain(fd);
|
||||||
# endif
|
#endif
|
||||||
return (ssize_t)wrote;
|
return (ssize_t)wrote;
|
||||||
// piCoutObj << "Error while sending";
|
// piCoutObj << "Error while sending";
|
||||||
}
|
}
|
||||||
@@ -1060,9 +1061,9 @@ void PISerial::configureFromVariantDevice(const PIPropertyStorage & d) {
|
|||||||
PIVector<int> PISerial::availableSpeeds() {
|
PIVector<int> PISerial::availableSpeeds() {
|
||||||
PIVector<int> spds;
|
PIVector<int> spds;
|
||||||
spds << 50 << 75 << 110 << 300 << 600 << 1200 << 2400 << 4800 << 9600 <<
|
spds << 50 << 75 << 110 << 300 << 600 << 1200 << 2400 << 4800 << 9600 <<
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
14400 <<
|
14400 <<
|
||||||
# endif
|
#endif
|
||||||
19200 << 38400 << 57600 << 115200 << 230400 << 460800 << 500000 << 576000 << 921600 << 1000000 << 1152000 << 1500000 << 2000000
|
19200 << 38400 << 57600 << 115200 << 230400 << 460800 << 500000 << 576000 << 921600 << 1000000 << 1152000 << 1500000 << 2000000
|
||||||
<< 2500000 << 3000000 << 3500000 << 4000000;
|
<< 2500000 << 3000000 << 3500000 << 4000000;
|
||||||
return spds;
|
return spds;
|
||||||
@@ -1078,7 +1079,7 @@ PIStringList PISerial::availableDevices(bool test) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
PIString devicePortName(HDEVINFO deviceInfoSet, PSP_DEVINFO_DATA deviceInfoData) {
|
PIString devicePortName(HDEVINFO deviceInfoSet, PSP_DEVINFO_DATA deviceInfoData) {
|
||||||
PIString ret;
|
PIString ret;
|
||||||
const HKEY key = SetupDiOpenDevRegKey(deviceInfoSet, deviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
|
const HKEY key = SetupDiOpenDevRegKey(deviceInfoSet, deviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
|
||||||
@@ -1146,13 +1147,13 @@ bool parseID(PIString str, PISerial::DeviceInfo & di) {
|
|||||||
if (i > 0) di.pID = str.mid(i + 4, 4).toInt(16);
|
if (i > 0) di.pID = str.mid(i + 4, 4).toInt(16);
|
||||||
return (di.vID > 0) && (di.pID > 0);
|
return (di.vID > 0) && (di.pID > 0);
|
||||||
}
|
}
|
||||||
# endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
PIVector<PISerial::DeviceInfo> PISerial::availableDevicesInfo(bool test) {
|
PIVector<PISerial::DeviceInfo> PISerial::availableDevicesInfo(bool test) {
|
||||||
PIVector<DeviceInfo> ret;
|
PIVector<DeviceInfo> ret;
|
||||||
DeviceInfo di;
|
DeviceInfo di;
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
static const GUID guids[] = {GUID_DEVINTERFACE_MODEM, GUID_DEVINTERFACE_COMPORT};
|
static const GUID guids[] = {GUID_DEVINTERFACE_MODEM, GUID_DEVINTERFACE_COMPORT};
|
||||||
static const int guids_cnt = sizeof(guids) / sizeof(GUID);
|
static const int guids_cnt = sizeof(guids) / sizeof(GUID);
|
||||||
for (int i = 0; i < guids_cnt; ++i) {
|
for (int i = 0; i < guids_cnt; ++i) {
|
||||||
@@ -1181,12 +1182,12 @@ PIVector<PISerial::DeviceInfo> PISerial::availableDevicesInfo(bool test) {
|
|||||||
}
|
}
|
||||||
SetupDiDestroyDeviceInfoList(dis);
|
SetupDiDestroyDeviceInfoList(dis);
|
||||||
}
|
}
|
||||||
# else
|
#else
|
||||||
# ifndef ANDROID
|
# ifndef ANDROID
|
||||||
PIStringList prefixes;
|
PIStringList prefixes;
|
||||||
# ifdef QNX
|
# ifdef QNX
|
||||||
prefixes << "ser";
|
prefixes << "ser";
|
||||||
# else
|
# else
|
||||||
prefixes << "ttyS"
|
prefixes << "ttyS"
|
||||||
<< "ttyO"
|
<< "ttyO"
|
||||||
<< "ttyUSB"
|
<< "ttyUSB"
|
||||||
@@ -1197,14 +1198,14 @@ PIVector<PISerial::DeviceInfo> PISerial::availableDevicesInfo(bool test) {
|
|||||||
<< "ttyAMA"
|
<< "ttyAMA"
|
||||||
<< "rfcomm"
|
<< "rfcomm"
|
||||||
<< "ircomm";
|
<< "ircomm";
|
||||||
# ifdef FREE_BSD
|
# ifdef FREE_BSD
|
||||||
prefixes << "cu";
|
prefixes << "cu";
|
||||||
# endif
|
# endif
|
||||||
# ifdef MAC_OS
|
# ifdef MAC_OS
|
||||||
prefixes.clear();
|
prefixes.clear();
|
||||||
prefixes << "cu."
|
prefixes << "cu."
|
||||||
<< "tty.";
|
<< "tty.";
|
||||||
# endif
|
# endif
|
||||||
PIFile file_prefixes("/proc/tty/drivers", PIIODevice::ReadOnly);
|
PIFile file_prefixes("/proc/tty/drivers", PIIODevice::ReadOnly);
|
||||||
if (file_prefixes.open()) {
|
if (file_prefixes.open()) {
|
||||||
PIString fc = PIString::fromAscii(file_prefixes.readAll()), line, cpref;
|
PIString fc = PIString::fromAscii(file_prefixes.readAll()), line, cpref;
|
||||||
@@ -1225,18 +1226,18 @@ PIVector<PISerial::DeviceInfo> PISerial::availableDevicesInfo(bool test) {
|
|||||||
}
|
}
|
||||||
prefixes.removeDuplicates();
|
prefixes.removeDuplicates();
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
PIDir dir("/dev");
|
PIDir dir("/dev");
|
||||||
PIVector<PIFile::FileInfo> de = dir.entries();
|
PIVector<PIFile::FileInfo> de = dir.entries();
|
||||||
# ifdef LINUX
|
# ifdef LINUX
|
||||||
char linkbuf[1024];
|
char linkbuf[1024];
|
||||||
# endif
|
# endif
|
||||||
for (const auto & e: de) { // TODO changes in FileInfo
|
for (const auto & e: de) { // TODO changes in FileInfo
|
||||||
for (const auto & p: prefixes) {
|
for (const auto & p: prefixes) {
|
||||||
if (e.name().startsWith(p)) {
|
if (e.name().startsWith(p)) {
|
||||||
di = DeviceInfo();
|
di = DeviceInfo();
|
||||||
di.path = e.path;
|
di.path = e.path;
|
||||||
# ifdef LINUX
|
# ifdef LINUX
|
||||||
ssize_t lsz = readlink(("/sys/class/tty/" + e.name()).dataAscii(), linkbuf, 1024);
|
ssize_t lsz = readlink(("/sys/class/tty/" + e.name()).dataAscii(), linkbuf, 1024);
|
||||||
if (lsz > 0) {
|
if (lsz > 0) {
|
||||||
PIString fpath = "/sys/class/tty/" + PIString(linkbuf, lsz) + "/";
|
PIString fpath = "/sys/class/tty/" + PIString(linkbuf, lsz) + "/";
|
||||||
@@ -1252,16 +1253,16 @@ PIVector<PISerial::DeviceInfo> PISerial::availableDevicesInfo(bool test) {
|
|||||||
if (di.pID > 0) break;
|
if (di.pID > 0) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
ret << di;
|
ret << di;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# endif
|
|
||||||
# endif
|
# endif
|
||||||
|
#endif
|
||||||
if (test) {
|
if (test) {
|
||||||
for (int i = 0; i < ret.size_s(); ++i) {
|
for (int i = 0; i < ret.size_s(); ++i) {
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
void * hComm = CreateFileA(ret[i].path.dataAscii(),
|
void * hComm = CreateFileA(ret[i].path.dataAscii(),
|
||||||
GENERIC_READ,
|
GENERIC_READ,
|
||||||
FILE_SHARE_READ,
|
FILE_SHARE_READ,
|
||||||
@@ -1270,31 +1271,31 @@ PIVector<PISerial::DeviceInfo> PISerial::availableDevicesInfo(bool test) {
|
|||||||
FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
|
FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
|
||||||
0);
|
0);
|
||||||
if (hComm == INVALID_HANDLE_VALUE) {
|
if (hComm == INVALID_HANDLE_VALUE) {
|
||||||
# else
|
#else
|
||||||
int fd = ::open(ret[i].path.dataAscii(), O_NOCTTY | O_RDONLY);
|
int fd = ::open(ret[i].path.dataAscii(), O_NOCTTY | O_RDONLY);
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
# endif
|
#endif
|
||||||
ret.remove(i);
|
ret.remove(i);
|
||||||
--i;
|
--i;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
bool rok = true;
|
bool rok = true;
|
||||||
# ifndef WINDOWS
|
#ifndef WINDOWS
|
||||||
int void_ = 0;
|
int void_ = 0;
|
||||||
fcntl(fd, F_SETFL, O_NONBLOCK);
|
fcntl(fd, F_SETFL, O_NONBLOCK);
|
||||||
if (::read(fd, &void_, 1) == -1) rok = errno != EIO;
|
if (::read(fd, &void_, 1) == -1) rok = errno != EIO;
|
||||||
|
|
||||||
# endif
|
#endif
|
||||||
if (!rok) {
|
if (!rok) {
|
||||||
ret.remove(i);
|
ret.remove(i);
|
||||||
--i;
|
--i;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
# ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
CloseHandle(hComm);
|
CloseHandle(hComm);
|
||||||
# else
|
#else
|
||||||
::close(fd);
|
::close(fd);
|
||||||
# endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
@@ -1308,14 +1309,12 @@ void PISerial::optionsChanged() {
|
|||||||
|
|
||||||
void PISerial::threadedReadBufferSizeChanged() {
|
void PISerial::threadedReadBufferSizeChanged() {
|
||||||
if (!isOpened()) return;
|
if (!isOpened()) return;
|
||||||
# if defined(LINUX)
|
#if defined(LINUX)
|
||||||
serial_struct ss;
|
serial_struct ss;
|
||||||
ioctl(fd, TIOCGSERIAL, &ss);
|
ioctl(fd, TIOCGSERIAL, &ss);
|
||||||
// piCoutObj << "b" << ss.xmit_fifo_size;
|
// piCoutObj << "b" << ss.xmit_fifo_size;
|
||||||
ss.xmit_fifo_size = piMaxi(threadedReadBufferSize(), 4096);
|
ss.xmit_fifo_size = piMaxi(threadedReadBufferSize(), 4096);
|
||||||
ioctl(fd, TIOCSSERIAL, &ss);
|
ioctl(fd, TIOCSSERIAL, &ss);
|
||||||
// piCoutObj << "a" << ss.xmit_fifo_size;
|
// piCoutObj << "a" << ss.xmit_fifo_size;
|
||||||
# endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // MICRO_PIP
|
|
||||||
|
|||||||
@@ -51,14 +51,8 @@
|
|||||||
#ifdef PIP_FREERTOS
|
#ifdef PIP_FREERTOS
|
||||||
# define FREERTOS
|
# define FREERTOS
|
||||||
#endif
|
#endif
|
||||||
#ifdef MICRO_PIP
|
#if defined(FREERTOS) || defined(PLATFORMIO)
|
||||||
# ifndef FREERTOS
|
# define MICRO_PIP
|
||||||
# define PIP_NO_THREADS
|
|
||||||
# endif
|
|
||||||
# ifndef LWIP
|
|
||||||
# define PIP_NO_SOCKET
|
|
||||||
# endif
|
|
||||||
# define PISERIAL_NO_PINS
|
|
||||||
#endif
|
#endif
|
||||||
#ifndef WINDOWS
|
#ifndef WINDOWS
|
||||||
# ifndef QNX
|
# ifndef QNX
|
||||||
|
|||||||
@@ -400,9 +400,9 @@ PIJSON PIJSON::parseValue(PIString & s) {
|
|||||||
s.trim();
|
s.trim();
|
||||||
if (s.isEmpty()) return ret;
|
if (s.isEmpty()) return ret;
|
||||||
if (s[0] == '{') {
|
if (s[0] == '{') {
|
||||||
ret = parseObject(s.takeRange('{', '}'));
|
ret = parseObject(s.takeRange('{', '}').trim());
|
||||||
} else if (s[0] == '[') {
|
} else if (s[0] == '[') {
|
||||||
ret = parseArray(s.takeRange('[', ']'));
|
ret = parseArray(s.takeRange('[', ']').trim());
|
||||||
} else {
|
} else {
|
||||||
s.trim();
|
s.trim();
|
||||||
if (s.startsWith('"')) {
|
if (s.startsWith('"')) {
|
||||||
|
|||||||
@@ -42,6 +42,14 @@ PIString mask(const PIString & str) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PIString overrideFile(PIString path) {
|
||||||
|
if (path.isEmpty()) return {};
|
||||||
|
PIFile::FileInfo fi(path);
|
||||||
|
auto ext = fi.extension();
|
||||||
|
path.insert(path.size_s() - ext.size_s() - (ext.isEmpty() ? 0 : 1), ".override");
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
PIValueTree PIValueTreeConversions::fromPropertyStorage(const PIPropertyStorage & ps) {
|
PIValueTree PIValueTreeConversions::fromPropertyStorage(const PIPropertyStorage & ps) {
|
||||||
PIValueTree ret;
|
PIValueTree ret;
|
||||||
@@ -292,7 +300,11 @@ PIString toTextTree(const PIValueTree & root, PIString prefix, PIValueTreeConver
|
|||||||
ret += "\n[" + prefix + "]\n";
|
ret += "\n[" + prefix + "]\n";
|
||||||
if (root.isArray() && options[PIValueTreeConversions::WithAttributes]) ret += toTextTreeAttributes(root, "", options);
|
if (root.isArray() && options[PIValueTreeConversions::WithAttributes]) ret += toTextTreeAttributes(root, "", options);
|
||||||
for (const auto & c: root.children()) {
|
for (const auto & c: root.children()) {
|
||||||
PIString cp = prefix;
|
if (c.hasChildren()) continue;
|
||||||
|
ret += toTextTree(c, prefix, options);
|
||||||
|
}
|
||||||
|
for (const auto & c: root.children()) {
|
||||||
|
if (!c.hasChildren()) continue;
|
||||||
ret += toTextTree(c, prefix, options);
|
ret += toTextTree(c, prefix, options);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -315,9 +327,13 @@ PIString toTextTree(const PIValueTree & root, PIString prefix, PIValueTreeConver
|
|||||||
PIString PIValueTreeConversions::toText(const PIValueTree & root, Options options) {
|
PIString PIValueTreeConversions::toText(const PIValueTree & root, Options options) {
|
||||||
PIString ret;
|
PIString ret;
|
||||||
for (const auto & c: root.children()) {
|
for (const auto & c: root.children()) {
|
||||||
ret += toTextTree(c, PIString(), options);
|
if (c.hasChildren()) continue;
|
||||||
|
ret += toTextTree(c, {}, options);
|
||||||
|
}
|
||||||
|
for (const auto & c: root.children()) {
|
||||||
|
if (!c.hasChildren()) continue;
|
||||||
|
ret += toTextTree(c, {}, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,13 +345,26 @@ PIValueTree PIValueTreeConversions::fromText(const PIString & str) {
|
|||||||
|
|
||||||
|
|
||||||
PIValueTree PIValueTreeConversions::fromJSONFile(const PIString & path) {
|
PIValueTree PIValueTreeConversions::fromJSONFile(const PIString & path) {
|
||||||
return PIValueTreeConversions::fromJSON(PIJSON::fromJSON(PIString::fromUTF8(PIFile::readAll(path))));
|
auto ret = PIValueTreeConversions::fromJSON(PIJSON::fromJSON(PIString::fromUTF8(PIFile::readAll(path))));
|
||||||
|
auto ofp = overrideFile(path);
|
||||||
|
if (PIFile::isExists(ofp)) {
|
||||||
|
auto override_vt = PIValueTreeConversions::fromJSON(PIJSON::fromJSON(PIString::fromUTF8(PIFile::readAll(ofp))));
|
||||||
|
ret.merge(override_vt);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PIValueTree PIValueTreeConversions::fromTextFile(const PIString & path) {
|
PIValueTree PIValueTreeConversions::fromTextFile(const PIString & path) {
|
||||||
PIFile f(path, PIIODevice::ReadOnly);
|
PIFile f(path, PIIODevice::ReadOnly);
|
||||||
return PIValueTreeConversions::fromText(&f);
|
auto ret = PIValueTreeConversions::fromText(&f);
|
||||||
|
auto ofp = overrideFile(path);
|
||||||
|
if (PIFile::isExists(ofp)) {
|
||||||
|
PIFile of(ofp, PIIODevice::ReadOnly);
|
||||||
|
auto override_vt = PIValueTreeConversions::fromText(&of);
|
||||||
|
ret.merge(override_vt);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -122,6 +122,10 @@ bool PIHIDevice::open(const PIHIDeviceInfo & device) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
HidD_GetPreparsedData(PRIVATE->deviceHandle, &PRIVATE->preparsed);
|
HidD_GetPreparsedData(PRIVATE->deviceHandle, &PRIVATE->preparsed);
|
||||||
|
if (!PRIVATE->preparsed) {
|
||||||
|
close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -338,12 +342,12 @@ PIVector<PIHIDeviceInfo> PIHIDevice::allDevices(bool try_open) {
|
|||||||
|
|
||||||
PIDir hid_dir("/sys/bus/hid/devices"_a);
|
PIDir hid_dir("/sys/bus/hid/devices"_a);
|
||||||
auto hid_devs = hid_dir.entries();
|
auto hid_devs = hid_dir.entries();
|
||||||
for (auto hd: hid_devs) {
|
for (const auto & hd: hid_devs) {
|
||||||
// piCout << d.path;
|
// piCout << d.path;
|
||||||
if (!isDir(hd)) continue;
|
if (!isDir(hd)) continue;
|
||||||
PIDir dir_input(hd.path + "/input"_a);
|
PIDir dir_input(hd.path + "/input"_a);
|
||||||
auto hid_inputs = dir_input.entries();
|
auto hid_inputs = dir_input.entries();
|
||||||
for (auto hd_i: hid_inputs) {
|
for (const auto & hd_i: hid_inputs) {
|
||||||
if (!isDir(hd_i)) continue;
|
if (!isDir(hd_i)) continue;
|
||||||
// now in /sys/bus/hid/devices/<dev>/input/input<N>
|
// now in /sys/bus/hid/devices/<dev>/input/input<N>
|
||||||
// piCout << hd_i.path;
|
// piCout << hd_i.path;
|
||||||
@@ -360,7 +364,7 @@ PIVector<PIHIDeviceInfo> PIHIDevice::allDevices(bool try_open) {
|
|||||||
PIDir dir_e(hd_i.path);
|
PIDir dir_e(hd_i.path);
|
||||||
PIStringList devs;
|
PIStringList devs;
|
||||||
auto dl_e = dir_e.entries();
|
auto dl_e = dir_e.entries();
|
||||||
for (auto d_e: dl_e) {
|
for (const auto & d_e: dl_e) {
|
||||||
if (!d_e.isDir() || d_e.flags[PIFile::FileInfo::Dot] || d_e.flags[PIFile::FileInfo::DotDot]) continue;
|
if (!d_e.isDir() || d_e.flags[PIFile::FileInfo::Dot] || d_e.flags[PIFile::FileInfo::DotDot]) continue;
|
||||||
devs << d_e.name();
|
devs << d_e.name();
|
||||||
}
|
}
|
||||||
@@ -388,7 +392,7 @@ PIVector<PIHIDeviceInfo> PIHIDevice::allDevices(bool try_open) {
|
|||||||
if (test_f.isClosed()) continue;
|
if (test_f.isClosed()) continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ullong ev = readFile(hd_i.path + "/capabilities/ev"_a).toULLong(16);
|
// ullong ev = readFile(hd_i.path + "/capabilities/ev"_a).toULLong(16);
|
||||||
|
|
||||||
auto readAxes = [readFile, checkBit, &hd_i, &dev](const PIString & file, bool is_relative) {
|
auto readAxes = [readFile, checkBit, &hd_i, &dev](const PIString & file, bool is_relative) {
|
||||||
PIVector<PIHIDeviceInfo::AxisInfo> ret;
|
PIVector<PIHIDeviceInfo::AxisInfo> ret;
|
||||||
@@ -524,6 +528,7 @@ PIVector<PIHIDeviceInfo> PIHIDevice::allDevices(bool try_open) {
|
|||||||
PHIDP_PREPARSED_DATA preparsed = nullptr;
|
PHIDP_PREPARSED_DATA preparsed = nullptr;
|
||||||
if (HidD_GetPreparsedData(deviceHandle, &preparsed) == FALSE) {
|
if (HidD_GetPreparsedData(deviceHandle, &preparsed) == FALSE) {
|
||||||
piCout << "HidD_GetPreparsedData error:" << errorString();
|
piCout << "HidD_GetPreparsedData error:" << errorString();
|
||||||
|
CloseHandle(deviceHandle);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// auto pp = PIByteArray(preparsed, 64);
|
// auto pp = PIByteArray(preparsed, 64);
|
||||||
|
|||||||
@@ -206,7 +206,10 @@ PIString PIString::dtos(const double num, char format, int precision) {
|
|||||||
if (wr > 4) wr = 4;
|
if (wr > 4) wr = 4;
|
||||||
f[2 + wr] = format;
|
f[2 + wr] = format;
|
||||||
f[3 + wr] = 0;
|
f[3 + wr] = 0;
|
||||||
pisprintf(f, num);
|
char ch[256];
|
||||||
|
piZeroMemory(ch, 256);
|
||||||
|
snprintf(ch, 256, f, num);
|
||||||
|
return PIStringAscii(ch).replaceAll(',', '.');
|
||||||
}
|
}
|
||||||
#undef pisprintf
|
#undef pisprintf
|
||||||
|
|
||||||
@@ -466,7 +469,7 @@ void PIString::buildData(const char * cp) const {
|
|||||||
UErrorCode e((UErrorCode)0);
|
UErrorCode e((UErrorCode)0);
|
||||||
UConverter * cc = ucnv_open(cp, &e);
|
UConverter * cc = ucnv_open(cp, &e);
|
||||||
if (cc) {
|
if (cc) {
|
||||||
const size_t len = MB_CUR_MAX * size() + 1;
|
const size_t len = UCNV_GET_MAX_BYTES_FOR_STRING(size(), ucnv_getMaxCharSize(cc)) + 1; // MB_CUR_MAX * size() + 1;
|
||||||
data_ = (char *)malloc(len);
|
data_ = (char *)malloc(len);
|
||||||
int sz = ucnv_fromUChars(cc, data_, len, (const UChar *)(d.data()), d.size_s(), &e);
|
int sz = ucnv_fromUChars(cc, data_, len, (const UChar *)(d.data()), d.size_s(), &e);
|
||||||
ucnv_close(cc);
|
ucnv_close(cc);
|
||||||
|
|||||||
@@ -124,6 +124,23 @@ void PIValueTree::applyValues(const PIValueTree & root, bool recursive) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PIValueTree::merge(const PIValueTree & root) {
|
||||||
|
if (_is_null) return;
|
||||||
|
for (const auto & c: root._children) {
|
||||||
|
bool found = false;
|
||||||
|
for (auto & i: _children) {
|
||||||
|
if (c.name() == i.name()) {
|
||||||
|
if (c.isValid()) i._value = c._value;
|
||||||
|
i.merge(c);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) _children << c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
PIVariant PIValueTree::childValue(const PIString & child_name, const PIVariant & default_value, bool * exists) const {
|
PIVariant PIValueTree::childValue(const PIString & child_name, const PIVariant & default_value, bool * exists) const {
|
||||||
const PIValueTree & node = child(child_name);
|
const PIValueTree & node = child(child_name);
|
||||||
if (node.isNull()) {
|
if (node.isNull()) {
|
||||||
|
|||||||
@@ -170,6 +170,13 @@ public:
|
|||||||
//! \param recursive Если установлено в true, то значения будут применяться рекурсивно к дочерним узлам.
|
//! \param recursive Если установлено в true, то значения будут применяться рекурсивно к дочерним узлам.
|
||||||
void applyValues(const PIValueTree & root, bool recursive = true);
|
void applyValues(const PIValueTree & root, bool recursive = true);
|
||||||
|
|
||||||
|
//! \~\brief
|
||||||
|
//! \~english Set or add the values of a given %PIValueTree object to the current %PIValueTree object.
|
||||||
|
//! \param root The %PIValueTree object whose values are to be merged.
|
||||||
|
//! \~russian Устанавливает или добавляет значения данного объекта %PIValueTree к текущему объекту %PIValueTree.
|
||||||
|
//! \param root Объект %PIValueTree, значения которого должны быть добавлены.
|
||||||
|
void merge(const PIValueTree & root);
|
||||||
|
|
||||||
//! \~\brief
|
//! \~\brief
|
||||||
//! \~english Returns the children of the current %PIValueTree object.
|
//! \~english Returns the children of the current %PIValueTree object.
|
||||||
//! \~russian Возвращает дочерние элементы текущего объекта %PIValueTree.
|
//! \~russian Возвращает дочерние элементы текущего объекта %PIValueTree.
|
||||||
|
|||||||
70
plans/pivector2d.md
Normal file
70
plans/pivector2d.md
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
# План рефакторинга PIVector2D
|
||||||
|
|
||||||
|
## Этап 1: Выполнить наследование Row от RowConst, Col от ColConst
|
||||||
|
|
||||||
|
### 1.1 Переместить RowConst перед Row
|
||||||
|
- Найти местоположение RowConst (текущая позиция ~строка 610)
|
||||||
|
- Переместить определение RowConst перед Row (до строки ~184)
|
||||||
|
|
||||||
|
### 1.2 Переместить ColConst перед Col
|
||||||
|
- Найти местоположение ColConst (текущая позиция ~строка 770)
|
||||||
|
- Переместить определение ColConst перед Col (до строки ~402)
|
||||||
|
|
||||||
|
### 1.3 Изменить класс Row
|
||||||
|
- Наследовать от RowConst: `class Row : public RowConst`
|
||||||
|
- Убрать дублирующиеся методы (они унаследованы от RowConst):
|
||||||
|
- size()
|
||||||
|
- toVector()
|
||||||
|
- operator[] (const версия)
|
||||||
|
- data() (const версия)
|
||||||
|
- indexOf()
|
||||||
|
- lastIndexOf()
|
||||||
|
- indexWhere()
|
||||||
|
- lastIndexWhere()
|
||||||
|
- forEach() (const версия)
|
||||||
|
- contains()
|
||||||
|
- entries()
|
||||||
|
- any()
|
||||||
|
- every()
|
||||||
|
- Сохранить неконстантные методы:
|
||||||
|
- operator[] (неконстантный)
|
||||||
|
- data() (неконстантный)
|
||||||
|
- operator=()
|
||||||
|
- forEach() (неконстантный)
|
||||||
|
- fill()
|
||||||
|
|
||||||
|
### 1.4 Изменить класс Col
|
||||||
|
- Наследовать от ColConst: `class Col : public ColConst`
|
||||||
|
- Аналогично убрать дублирующиеся методы
|
||||||
|
|
||||||
|
### 1.5 Проверить тесты
|
||||||
|
- Запустить: `./pip_math_test --gtest_filter="*Vector2D*"`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Этап 2: Заменить PIPair<ssize_t, ssize_t> на PIVector2DIndex
|
||||||
|
|
||||||
|
### 2.1 Создать структуру PIVector2DIndex
|
||||||
|
```cpp
|
||||||
|
struct PIVector2DIndex {
|
||||||
|
ssize_t row;
|
||||||
|
ssize_t col;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 Обновить return types
|
||||||
|
Методы для изменения:
|
||||||
|
- indexOf() -> возвращает PIVector2DIndex вместо PIPair<ssize_t, ssize_t>
|
||||||
|
- lastIndexOf()
|
||||||
|
- indexWhere()
|
||||||
|
- lastIndexWhere()
|
||||||
|
|
||||||
|
### 2.3 Обновить тесты и документацию
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Этап 3: Дополнительные улучшения (опционально)
|
||||||
|
|
||||||
|
- Добавить методы для работы с диапазонами
|
||||||
|
- Оптимизировать методы удаления строк/столбцов
|
||||||
|
- Добавить проверку границ в debug-режиме
|
||||||
@@ -1,85 +1,1433 @@
|
|||||||
|
#include "pistring.h"
|
||||||
#include "pivector2d.h"
|
#include "pivector2d.h"
|
||||||
|
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
#include <numeric>
|
||||||
|
|
||||||
int ROWS_COUNT_INIT = 31;
|
size_t ROWS_COUNT_INIT = 31;
|
||||||
|
size_t COLS_COUNT_INIT = 34;
|
||||||
int ROWS_COUNT_INCREASE = 41;
|
int ROWS_COUNT_INCREASE = 41;
|
||||||
int ROWS_COUNT_REDUCE = 22;
|
|
||||||
|
|
||||||
int COLS_COUNT_INIT = 34;
|
|
||||||
int COLS_COUNT_INCREASE = 44;
|
int COLS_COUNT_INCREASE = 44;
|
||||||
|
int ROWS_COUNT_REDUCE = 22;
|
||||||
int COLS_COUNT_REDUCE = 13;
|
int COLS_COUNT_REDUCE = 13;
|
||||||
|
|
||||||
void assert_fill_with(PIVector2D<int> vec, int rows, int cols) {
|
void fill_with_sequential(PIVector2D<int> & vec, int rows, int cols) {
|
||||||
for (int r = 0; r < rows; r++) {
|
for (int r = 0; r < rows; ++r) {
|
||||||
for (int c = 0; c < cols; c++) {
|
for (int c = 0; c < cols; ++c) {
|
||||||
ASSERT_EQ(vec.element(r, c), r * COLS_COUNT_INIT + c);
|
vec.element(r, c) = r * cols + c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Vector2D: public ::testing::Test {
|
void assert_fill_with_sequential(const PIVector2D<int> & vec, int rows, int cols) {
|
||||||
|
ASSERT_EQ(vec.rows(), rows);
|
||||||
|
ASSERT_EQ(vec.cols(), cols);
|
||||||
|
for (int r = 0; r < rows; ++r) {
|
||||||
|
for (int c = 0; c < cols; ++c) {
|
||||||
|
ASSERT_EQ(vec.element(r, c), r * cols + c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Vector2DTest: public ::testing::Test {
|
||||||
protected:
|
protected:
|
||||||
PIVector2D<int> vec = PIVector2D<int>(ROWS_COUNT_INIT, COLS_COUNT_INIT);
|
PIVector2D<int> vec = PIVector2D<int>(ROWS_COUNT_INIT, COLS_COUNT_INIT);
|
||||||
|
|
||||||
void SetUp() override {
|
void SetUp() override { fill_with_sequential(vec, ROWS_COUNT_INIT, COLS_COUNT_INIT); }
|
||||||
for (int r = 0; r < ROWS_COUNT_INIT; ++r) {
|
};
|
||||||
for (int c = 0; c < COLS_COUNT_INIT; ++c) {
|
|
||||||
vec.element(r, c) = r * COLS_COUNT_INIT + c;
|
// ==================== CONSTRUCTOR TESTS ====================
|
||||||
|
TEST_F(Vector2DTest, defaultConstructor_createsEmptyVector) {
|
||||||
|
PIVector2D<int> emptyVec;
|
||||||
|
EXPECT_TRUE(emptyVec.isEmpty());
|
||||||
|
EXPECT_EQ(emptyVec.rows(), 0);
|
||||||
|
EXPECT_EQ(emptyVec.cols(), 0);
|
||||||
|
EXPECT_EQ(emptyVec.size(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, sizedConstructor_createsCorrectDimensions) {
|
||||||
|
PIVector2D<int> testVec(5, 3, 42);
|
||||||
|
EXPECT_EQ(testVec.rows(), 5);
|
||||||
|
EXPECT_EQ(testVec.cols(), 3);
|
||||||
|
EXPECT_EQ(testVec.size(), 15);
|
||||||
|
|
||||||
|
for (size_t r = 0; r < 5; ++r) {
|
||||||
|
for (size_t c = 0; c < 3; ++c) {
|
||||||
|
EXPECT_EQ(testVec.element(r, c), 42);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, fromPlainVector_constructor_reshapesCorrectly) {
|
||||||
|
PIVector<int> plain(20);
|
||||||
|
std::iota(plain.data(), plain.data() + 20, 0);
|
||||||
|
|
||||||
|
PIVector2D<int> vec2d(4, 5, plain);
|
||||||
|
EXPECT_EQ(vec2d.rows(), 4);
|
||||||
|
EXPECT_EQ(vec2d.cols(), 5);
|
||||||
|
|
||||||
|
for (size_t r = 0; r < 4; ++r) {
|
||||||
|
for (size_t c = 0; c < 5; ++c) {
|
||||||
|
EXPECT_EQ(vec2d.element(r, c), static_cast<int>(r * 5 + c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, fromPlainVector_move_constructor_reshapesCorrectly) {
|
||||||
|
PIVector<int> plain(20);
|
||||||
|
std::iota(plain.data(), plain.data() + 20, 0);
|
||||||
|
|
||||||
|
PIVector2D<int> vec2d(4, 5, std::move(plain));
|
||||||
|
EXPECT_EQ(vec2d.rows(), 4);
|
||||||
|
EXPECT_EQ(vec2d.cols(), 5);
|
||||||
|
EXPECT_TRUE(plain.isEmpty()); // Moved-from state
|
||||||
|
|
||||||
|
for (size_t r = 0; r < 4; ++r) {
|
||||||
|
for (size_t c = 0; c < 5; ++c) {
|
||||||
|
EXPECT_EQ(vec2d.element(r, c), static_cast<int>(r * 5 + c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, fromVectorOfVectors_constructor_reshapesCorrectly) {
|
||||||
|
PIVector<PIVector<int>> vectors;
|
||||||
|
vectors << PIVector<int>({1, 2, 3}) << PIVector<int>({4, 5, 6}) << PIVector<int>({7, 8, 9});
|
||||||
|
|
||||||
|
PIVector2D<int> vec2d(vectors);
|
||||||
|
EXPECT_EQ(vec2d.rows(), 3);
|
||||||
|
EXPECT_EQ(vec2d.cols(), 3);
|
||||||
|
|
||||||
|
EXPECT_EQ(vec2d.element(0, 0), 1);
|
||||||
|
EXPECT_EQ(vec2d.element(1, 1), 5);
|
||||||
|
EXPECT_EQ(vec2d.element(2, 2), 9);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== CAPACITY TESTS ====================
|
||||||
|
TEST_F(Vector2DTest, sizeMethods_returnCorrectValues) {
|
||||||
|
EXPECT_EQ(vec.rows(), ROWS_COUNT_INIT);
|
||||||
|
EXPECT_EQ(vec.cols(), COLS_COUNT_INIT);
|
||||||
|
EXPECT_EQ(vec.size(), ROWS_COUNT_INIT * COLS_COUNT_INIT);
|
||||||
|
EXPECT_EQ(vec.size_s(), static_cast<ssize_t>(ROWS_COUNT_INIT * COLS_COUNT_INIT));
|
||||||
|
EXPECT_EQ(vec.length(), ROWS_COUNT_INIT * COLS_COUNT_INIT);
|
||||||
|
EXPECT_FALSE(vec.isEmpty());
|
||||||
|
EXPECT_TRUE(vec.isNotEmpty());
|
||||||
|
EXPECT_GE(vec.capacity(), vec.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== ELEMENT ACCESS TESTS ====================
|
||||||
|
TEST_F(Vector2DTest, element_access_returnsCorrectValues) {
|
||||||
|
EXPECT_EQ(vec.element(5, 7), 5 * COLS_COUNT_INIT + 7);
|
||||||
|
EXPECT_EQ(vec.at(10, 20), 10 * COLS_COUNT_INIT + 20);
|
||||||
|
|
||||||
|
vec.element(15, 15) = 999;
|
||||||
|
EXPECT_EQ(vec.element(15, 15), 999);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, row_proxy_allows_elementAccess) {
|
||||||
|
auto row = vec[5];
|
||||||
|
EXPECT_EQ(row.size(), COLS_COUNT_INIT);
|
||||||
|
EXPECT_EQ(row[7], 5 * COLS_COUNT_INIT + 7);
|
||||||
|
|
||||||
|
row[10] = 123;
|
||||||
|
EXPECT_EQ(vec.element(5, 10), 123);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, row_proxy_data_pointer_works) {
|
||||||
|
auto row = vec[10];
|
||||||
|
int * ptr = row.data();
|
||||||
|
EXPECT_EQ(ptr, vec.data(10 * COLS_COUNT_INIT));
|
||||||
|
|
||||||
|
ptr[5] = 777;
|
||||||
|
EXPECT_EQ(vec.element(10, 5), 777);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, row_proxy_const_access_works) {
|
||||||
|
const auto & constVec = vec;
|
||||||
|
auto row = constVec[5];
|
||||||
|
EXPECT_EQ(row[7], 5 * COLS_COUNT_INIT + 7);
|
||||||
|
|
||||||
|
// Compilation test - uncommenting should fail
|
||||||
|
// row[10] = 123;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, row_proxy_assignment_works) {
|
||||||
|
PIVector2D<int> other(ROWS_COUNT_INIT, COLS_COUNT_INIT, 42);
|
||||||
|
|
||||||
|
vec[10] = other[10];
|
||||||
|
for (size_t c = 0; c < COLS_COUNT_INIT; ++c) {
|
||||||
|
EXPECT_EQ(vec.element(10, c), 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
PIVector<int> newRow(COLS_COUNT_INIT, 99);
|
||||||
|
vec[15] = newRow;
|
||||||
|
for (size_t c = 0; c < COLS_COUNT_INIT; ++c) {
|
||||||
|
EXPECT_EQ(vec.element(15, c), 99);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, row_proxy_toVector_conversion_works) {
|
||||||
|
auto rowVec = vec[7].toVector();
|
||||||
|
EXPECT_EQ(rowVec.size(), COLS_COUNT_INIT);
|
||||||
|
for (size_t c = 0; c < COLS_COUNT_INIT; ++c) {
|
||||||
|
EXPECT_EQ(rowVec[c], vec.element(7, c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, col_proxy_allows_elementAccess) {
|
||||||
|
auto col = vec.col(5);
|
||||||
|
EXPECT_EQ(col.size(), ROWS_COUNT_INIT);
|
||||||
|
EXPECT_EQ(col[10], 10 * COLS_COUNT_INIT + 5);
|
||||||
|
|
||||||
|
col[15] = 456;
|
||||||
|
EXPECT_EQ(vec.element(15, 5), 456);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, col_proxy_data_pointer_works) {
|
||||||
|
auto col = vec.col(8);
|
||||||
|
int * ptr = col.data(5); // Start from row 5
|
||||||
|
EXPECT_EQ(ptr, &vec.element(5, 8));
|
||||||
|
|
||||||
|
col[2] = 888; // This should affect row 7
|
||||||
|
EXPECT_EQ(vec.element(2, 8), 888);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, col_proxy_assignment_works) {
|
||||||
|
PIVector2D<int> other(ROWS_COUNT_INIT, COLS_COUNT_INIT, 42);
|
||||||
|
|
||||||
|
vec.col(12) = other.col(12);
|
||||||
|
for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) {
|
||||||
|
EXPECT_EQ(vec.element(r, 12), 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
PIVector<int> newCol(ROWS_COUNT_INIT, 77);
|
||||||
|
vec.col(20) = newCol;
|
||||||
|
for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) {
|
||||||
|
EXPECT_EQ(vec.element(r, 20), 77);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, col_proxy_toVector_conversion_works) {
|
||||||
|
auto colVec = vec.col(9).toVector();
|
||||||
|
EXPECT_EQ(colVec.size(), ROWS_COUNT_INIT);
|
||||||
|
for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) {
|
||||||
|
EXPECT_EQ(colVec[r], vec.element(r, 9));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, row_and_col_methods_return_same_as_operator) {
|
||||||
|
auto row1 = vec.row(10);
|
||||||
|
auto row2 = vec[10];
|
||||||
|
EXPECT_EQ(row1[0], row2[0]);
|
||||||
|
|
||||||
|
auto col1 = vec.col(15);
|
||||||
|
auto col2 = vec.col(15); // No operator[] for col
|
||||||
|
EXPECT_EQ(col1[5], col2[5]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== PROXY SEARCH TESTS ====================
|
||||||
|
TEST_F(Vector2DTest, row_proxy_search_works) {
|
||||||
|
auto row = vec[10];
|
||||||
|
// indexOf
|
||||||
|
ssize_t idx = row.indexOf(vec.element(10, 5));
|
||||||
|
EXPECT_EQ(idx, 5);
|
||||||
|
EXPECT_EQ(row.indexOf(-999), -1);
|
||||||
|
|
||||||
|
// lastIndexOf (add a duplicate)
|
||||||
|
vec.element(10, 7) = vec.element(10, 5); // duplicate
|
||||||
|
idx = row.lastIndexOf(vec.element(10, 5));
|
||||||
|
EXPECT_EQ(idx, 7);
|
||||||
|
|
||||||
|
// indexWhere
|
||||||
|
auto isEven = [](const int & e) { return e % 2 == 0; };
|
||||||
|
ssize_t firstEven = row.indexWhere(isEven);
|
||||||
|
EXPECT_GE(firstEven, 0);
|
||||||
|
|
||||||
|
// lastIndexWhere
|
||||||
|
ssize_t lastEven = row.lastIndexWhere(isEven);
|
||||||
|
EXPECT_GE(lastEven, firstEven);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, col_proxy_search_works) {
|
||||||
|
auto col = vec.col(9); // используем столбец 9, где все элементы нечётные
|
||||||
|
ssize_t idx = col.indexOf(vec.element(12, 9));
|
||||||
|
EXPECT_EQ(idx, 12);
|
||||||
|
EXPECT_EQ(col.indexOf(-999), -1);
|
||||||
|
|
||||||
|
vec.element(20, 9) = vec.element(12, 9); // duplicate
|
||||||
|
idx = col.lastIndexOf(vec.element(12, 9));
|
||||||
|
EXPECT_EQ(idx, 20);
|
||||||
|
|
||||||
|
auto isOdd = [](const int & e) { return e % 2 != 0; };
|
||||||
|
ssize_t firstOdd = col.indexWhere(isOdd);
|
||||||
|
EXPECT_GE(firstOdd, 0);
|
||||||
|
ssize_t lastOdd = col.lastIndexWhere(isOdd);
|
||||||
|
EXPECT_GE(lastOdd, firstOdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, rowconst_proxy_search_works) {
|
||||||
|
const auto & constVec = vec;
|
||||||
|
const auto row = constVec[10];
|
||||||
|
ssize_t idx = row.indexOf(vec.element(10, 5));
|
||||||
|
EXPECT_EQ(idx, 5);
|
||||||
|
idx = row.lastIndexOf(vec.element(10, 5));
|
||||||
|
EXPECT_EQ(idx, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, colconst_proxy_search_works) {
|
||||||
|
const auto & constVec = vec;
|
||||||
|
auto col = constVec.col(8);
|
||||||
|
ssize_t idx = col.indexOf(vec.element(12, 8));
|
||||||
|
EXPECT_EQ(idx, 12);
|
||||||
|
idx = col.lastIndexOf(vec.element(12, 8));
|
||||||
|
EXPECT_EQ(idx, 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== ROW/COLUMN ITERATION TESTS ====================
|
||||||
|
TEST_F(Vector2DTest, forEachRow_modifies_rows) {
|
||||||
|
vec.forEachRow([](PIVector2D<int>::Row row) {
|
||||||
|
for (size_t c = 0; c < row.size(); ++c)
|
||||||
|
row[c] = 999;
|
||||||
|
});
|
||||||
|
for (size_t r = 0; r < vec.rows(); ++r)
|
||||||
|
for (size_t c = 0; c < vec.cols(); ++c)
|
||||||
|
EXPECT_EQ(vec.element(r, c), 999);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, forEachRow_readonly_counts_rows) {
|
||||||
|
size_t count = 0;
|
||||||
|
vec.forEachRow([&count](PIVector2D<int>::RowConst) { ++count; });
|
||||||
|
EXPECT_EQ(count, vec.rows());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, forEachColumn_modifies_columns) {
|
||||||
|
vec.forEachColumn([](PIVector2D<int>::Col col) {
|
||||||
|
for (size_t r = 0; r < col.size(); ++r)
|
||||||
|
col[r] = 777;
|
||||||
|
});
|
||||||
|
for (size_t r = 0; r < vec.rows(); ++r)
|
||||||
|
for (size_t c = 0; c < vec.cols(); ++c)
|
||||||
|
EXPECT_EQ(vec.element(r, c), 777);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, forEachColumn_readonly_counts_columns) {
|
||||||
|
size_t count = 0;
|
||||||
|
vec.forEachColumn([&count](PIVector2D<int>::ColConst) { ++count; });
|
||||||
|
EXPECT_EQ(count, vec.cols());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== MODIFIER TESTS ====================
|
||||||
|
TEST_F(Vector2DTest, setRow_replaces_row_correctly) {
|
||||||
|
PIVector<int> newRow(COLS_COUNT_INIT);
|
||||||
|
std::iota(newRow.data(), newRow.data() + COLS_COUNT_INIT, 100);
|
||||||
|
|
||||||
|
vec.setRow(12, newRow);
|
||||||
|
for (size_t c = 0; c < COLS_COUNT_INIT; ++c) {
|
||||||
|
EXPECT_EQ(vec.element(12, c), static_cast<int>(100 + c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, setRow_with_shorter_vector_truncates) {
|
||||||
|
PIVector<int> shortRow(COLS_COUNT_INIT - 5, 999);
|
||||||
|
vec.setRow(8, shortRow);
|
||||||
|
|
||||||
|
for (size_t c = 0; c < COLS_COUNT_INIT - 5; ++c) {
|
||||||
|
EXPECT_EQ(vec.element(8, c), 999);
|
||||||
|
}
|
||||||
|
// Rest unchanged
|
||||||
|
EXPECT_EQ(vec.element(8, COLS_COUNT_INIT - 5), 8 * COLS_COUNT_INIT + COLS_COUNT_INIT - 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, addRow_appends_row_to_empty) {
|
||||||
|
PIVector2D<int> empty;
|
||||||
|
PIVector<int> newRow(5, 42);
|
||||||
|
|
||||||
|
empty.addRow(newRow);
|
||||||
|
EXPECT_EQ(empty.rows(), 1);
|
||||||
|
EXPECT_EQ(empty.cols(), 5);
|
||||||
|
for (size_t c = 0; c < 5; ++c) {
|
||||||
|
EXPECT_EQ(empty.element(0, c), 42);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, addRow_appends_row_to_existing) {
|
||||||
|
size_t oldRows = vec.rows();
|
||||||
|
PIVector<int> newRow(COLS_COUNT_INIT, 999);
|
||||||
|
|
||||||
|
vec.addRow(newRow);
|
||||||
|
EXPECT_EQ(vec.rows(), oldRows + 1);
|
||||||
|
EXPECT_EQ(vec.cols(), COLS_COUNT_INIT);
|
||||||
|
|
||||||
|
for (size_t c = 0; c < COLS_COUNT_INIT; ++c) {
|
||||||
|
EXPECT_EQ(vec.element(oldRows, c), 999);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, addRow_with_shorter_vector_uses_min) {
|
||||||
|
size_t oldRows = vec.rows();
|
||||||
|
size_t shortCols = COLS_COUNT_INIT - 10;
|
||||||
|
PIVector<int> shortRow(shortCols, 777);
|
||||||
|
|
||||||
|
vec.addRow(shortRow);
|
||||||
|
EXPECT_EQ(vec.rows(), oldRows + 1);
|
||||||
|
EXPECT_EQ(vec.cols(), COLS_COUNT_INIT); // cols unchanged
|
||||||
|
|
||||||
|
for (size_t c = 0; c < shortCols; ++c) {
|
||||||
|
EXPECT_EQ(vec.element(oldRows, c), 777);
|
||||||
|
}
|
||||||
|
for (size_t c = shortCols; c < COLS_COUNT_INIT; ++c) {
|
||||||
|
EXPECT_EQ(vec.element(oldRows, c), 0); // default initialized
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== ADD COLUMN TESTS ====================
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, addColumn_appends_column_to_empty) {
|
||||||
|
PIVector2D<int> empty;
|
||||||
|
PIVector<int> newCol(5);
|
||||||
|
for (size_t i = 0; i < 5; ++i)
|
||||||
|
newCol[i] = static_cast<int>(100 + i);
|
||||||
|
|
||||||
|
empty.addColumn(newCol);
|
||||||
|
|
||||||
|
EXPECT_EQ(empty.rows(), 5);
|
||||||
|
EXPECT_EQ(empty.cols(), 1);
|
||||||
|
for (size_t r = 0; r < 5; ++r) {
|
||||||
|
EXPECT_EQ(empty.element(r, 0), static_cast<int>(100 + r));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, addColumn_appends_column_to_existing) {
|
||||||
|
size_t oldRows = vec.rows();
|
||||||
|
size_t oldCols = vec.cols();
|
||||||
|
|
||||||
|
PIVector<int> newCol(oldRows, 999);
|
||||||
|
vec.addColumn(newCol);
|
||||||
|
|
||||||
|
EXPECT_EQ(vec.rows(), oldRows);
|
||||||
|
EXPECT_EQ(vec.cols(), oldCols + 1);
|
||||||
|
|
||||||
|
// Check that old data is preserved
|
||||||
|
for (size_t r = 0; r < oldRows; ++r) {
|
||||||
|
for (size_t c = 0; c < oldCols; ++c) {
|
||||||
|
EXPECT_EQ(vec.element(r, c), static_cast<int>(r * COLS_COUNT_INIT + c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check new column
|
||||||
|
for (size_t r = 0; r < oldRows; ++r) {
|
||||||
|
EXPECT_EQ(vec.element(r, oldCols), 999);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, addColumn_with_shorter_vector_uses_min) {
|
||||||
|
size_t oldRows = vec.rows();
|
||||||
|
size_t oldCols = vec.cols();
|
||||||
|
size_t shortLen = oldRows - 10;
|
||||||
|
PIVector<int> shortCol(shortLen, 777);
|
||||||
|
|
||||||
|
vec.addColumn(shortCol);
|
||||||
|
|
||||||
|
EXPECT_EQ(vec.cols(), oldCols + 1);
|
||||||
|
|
||||||
|
// First shortLen rows should be 777
|
||||||
|
for (size_t r = 0; r < shortLen; ++r) {
|
||||||
|
EXPECT_EQ(vec.element(r, oldCols), 777);
|
||||||
|
}
|
||||||
|
// Remaining rows should be default-initialized (0)
|
||||||
|
for (size_t r = shortLen; r < oldRows; ++r) {
|
||||||
|
EXPECT_EQ(vec.element(r, oldCols), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, addColumn_with_longer_vector_truncates) {
|
||||||
|
size_t oldRows = vec.rows();
|
||||||
|
size_t oldCols = vec.cols();
|
||||||
|
size_t longLen = oldRows + 10;
|
||||||
|
PIVector<int> longCol(longLen, 555);
|
||||||
|
|
||||||
|
vec.addColumn(longCol);
|
||||||
|
|
||||||
|
EXPECT_EQ(vec.cols(), oldCols + 1);
|
||||||
|
|
||||||
|
// All rows should be 555 (only first oldRows elements are used)
|
||||||
|
for (size_t r = 0; r < oldRows; ++r) {
|
||||||
|
EXPECT_EQ(vec.element(r, oldCols), 555);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, addColumn_with_empty_source_does_nothing_on_empty) {
|
||||||
|
PIVector2D<int> empty;
|
||||||
|
PIVector<int> emptyCol;
|
||||||
|
|
||||||
|
empty.addColumn(emptyCol);
|
||||||
|
EXPECT_TRUE(empty.isEmpty());
|
||||||
|
EXPECT_EQ(empty.rows(), 0);
|
||||||
|
EXPECT_EQ(empty.cols(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, addColumn_with_empty_source_adds_default_column) {
|
||||||
|
auto oldVec = vec;
|
||||||
|
|
||||||
|
vec.addColumn({});
|
||||||
|
|
||||||
|
EXPECT_EQ(vec.cols(), oldVec.cols());
|
||||||
|
EXPECT_EQ(vec.rows(), oldVec.rows());
|
||||||
|
|
||||||
|
for (size_t r = 0; r < oldVec.rows(); ++r) {
|
||||||
|
for (size_t c = 0; c < oldVec.cols(); ++c) {
|
||||||
|
EXPECT_EQ(vec.element(r, c), oldVec.element(r, c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, addColumn_with_Col_proxy_works) {
|
||||||
|
auto oldVec = vec;
|
||||||
|
const size_t colIndex = 5;
|
||||||
|
|
||||||
|
auto srcCol = oldVec.col(colIndex);
|
||||||
|
vec.addColumn(srcCol);
|
||||||
|
|
||||||
|
EXPECT_EQ(vec.cols(), oldVec.cols() + 1);
|
||||||
|
EXPECT_EQ(vec.rows(), oldVec.rows());
|
||||||
|
|
||||||
|
for (size_t r = 0; r < oldVec.rows(); ++r) {
|
||||||
|
for (size_t c = 0; c < oldVec.cols(); ++c) {
|
||||||
|
EXPECT_EQ(vec.element(r, c), oldVec.element(r, c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (size_t r = 0; r < vec.rows(); ++r) {
|
||||||
|
// EXPECT_EQ(vec.element(r, oldVec.cols()), int());
|
||||||
|
piCout << r << vec.cols() << oldVec.cols() << colIndex;
|
||||||
|
EXPECT_EQ(vec.element(r, oldVec.cols()), oldVec.element(r, colIndex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, addColumn_with_ColConst_proxy_works) {
|
||||||
|
size_t oldRows = vec.rows();
|
||||||
|
size_t oldCols = vec.cols();
|
||||||
|
|
||||||
|
const auto & constVec = vec;
|
||||||
|
auto srcCol = constVec.col(7);
|
||||||
|
// Need a non-const array to add to
|
||||||
|
PIVector2D<int> mutableVec = vec; // copy
|
||||||
|
mutableVec.addColumn(srcCol);
|
||||||
|
|
||||||
|
EXPECT_EQ(mutableVec.cols(), oldCols + 1);
|
||||||
|
|
||||||
|
for (size_t r = 0; r < oldRows; ++r) {
|
||||||
|
EXPECT_EQ(mutableVec.element(r, oldCols), vec.element(r, 7));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== RESIZE TESTS ====================
|
||||||
|
class Vector2DResizeTest: public Vector2DTest {
|
||||||
|
protected:
|
||||||
|
void assert_resize_reduce_preserves_data(int newRows, int newCols) {
|
||||||
|
vec.resize(newRows, newCols, 0);
|
||||||
|
ASSERT_EQ(vec.rows(), newRows);
|
||||||
|
ASSERT_EQ(vec.cols(), newCols);
|
||||||
|
|
||||||
|
for (int r = 0; r < newRows; ++r) {
|
||||||
|
for (int c = 0; c < newCols; ++c) {
|
||||||
|
EXPECT_EQ(vec.element(r, c), r * COLS_COUNT_INIT + c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void resize_reduce_is_data_stay_consistent(int newRowsCount, int newColsCount) {
|
void assert_resize_increase_initializes_new(size_t newRows, size_t newCols) {
|
||||||
vec.resize(newRowsCount, newColsCount, 0);
|
vec.resize(newRows, newCols, 0);
|
||||||
assert_fill_with(vec, newRowsCount, newColsCount);
|
ASSERT_EQ(vec.rows(), newRows);
|
||||||
}
|
ASSERT_EQ(vec.cols(), newCols);
|
||||||
|
|
||||||
void resize_increase_is_data_stay_consistent(int newRowsCount, int newColsCount) {
|
// Check old data preserved
|
||||||
vec.resize(newRowsCount, newColsCount, 0);
|
for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) {
|
||||||
assert_fill_with(vec, ROWS_COUNT_INIT, COLS_COUNT_INIT);
|
for (size_t c = 0; c < COLS_COUNT_INIT; ++c) {
|
||||||
|
EXPECT_EQ(vec.element(r, c), r * COLS_COUNT_INIT + c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (int r = 0; r < newRowsCount; ++r) {
|
// Check new elements initialized to 0
|
||||||
for (int c = 0; c < newColsCount; ++c) {
|
for (size_t r = 0; r < newRows; ++r) {
|
||||||
if (r < ROWS_COUNT_INIT || c < COLS_COUNT_INIT) continue;
|
for (size_t c = 0; c < newCols; ++c) {
|
||||||
ASSERT_EQ(vec.element(r, c), 0);
|
if (r >= ROWS_COUNT_INIT || c >= COLS_COUNT_INIT) {
|
||||||
|
EXPECT_EQ(vec.element(r, c), 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(Vector2D, resize_is_increase_col_count) {
|
TEST_F(Vector2DResizeTest, resize_increase_both_preserves_data) {
|
||||||
vec.resize(ROWS_COUNT_INIT, COLS_COUNT_INCREASE, 0);
|
assert_resize_increase_initializes_new(ROWS_COUNT_INCREASE, COLS_COUNT_INCREASE);
|
||||||
ASSERT_EQ(vec.cols(), COLS_COUNT_INCREASE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(Vector2D, resize_is_reduce_col_count) {
|
TEST_F(Vector2DResizeTest, resize_increase_rows_only_preserves_data) {
|
||||||
vec.resize(ROWS_COUNT_INIT, COLS_COUNT_REDUCE, 0);
|
assert_resize_increase_initializes_new(ROWS_COUNT_INCREASE, COLS_COUNT_INIT);
|
||||||
ASSERT_EQ(vec.cols(), COLS_COUNT_REDUCE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(Vector2D, resize_is_increase_rows_count) {
|
TEST_F(Vector2DResizeTest, resize_increase_cols_only_preserves_data) {
|
||||||
vec.resize(ROWS_COUNT_INCREASE, COLS_COUNT_INIT, 0);
|
assert_resize_increase_initializes_new(ROWS_COUNT_INIT, COLS_COUNT_INCREASE);
|
||||||
ASSERT_EQ(vec.rows(), ROWS_COUNT_INCREASE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(Vector2D, resize_is_reduce_rows_count) {
|
TEST_F(Vector2DResizeTest, resize_reduce_both_preserves_data) {
|
||||||
vec.resize(ROWS_COUNT_REDUCE, COLS_COUNT_INIT, 0);
|
assert_resize_reduce_preserves_data(ROWS_COUNT_REDUCE, COLS_COUNT_REDUCE);
|
||||||
ASSERT_EQ(vec.rows(), ROWS_COUNT_REDUCE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(Vector2D, resize_increase_both_is_data_stay_consistent) {
|
TEST_F(Vector2DResizeTest, resize_reduce_rows_only_preserves_data) {
|
||||||
resize_increase_is_data_stay_consistent(ROWS_COUNT_INCREASE, COLS_COUNT_INCREASE);
|
assert_resize_reduce_preserves_data(ROWS_COUNT_REDUCE, COLS_COUNT_INIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(Vector2D, resize_reduce_cols_is_data_stay_consistent) {
|
TEST_F(Vector2DResizeTest, resize_reduce_cols_only_preserves_data) {
|
||||||
resize_reduce_is_data_stay_consistent(ROWS_COUNT_INIT, COLS_COUNT_REDUCE);
|
assert_resize_reduce_preserves_data(ROWS_COUNT_INIT, COLS_COUNT_REDUCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(Vector2D, resize_reduce_rows_is_data_stay_consistent) {
|
TEST_F(Vector2DResizeTest, resize_to_zero_creates_empty) {
|
||||||
resize_reduce_is_data_stay_consistent(ROWS_COUNT_REDUCE, COLS_COUNT_INIT);
|
vec.resize(0, 0, 42);
|
||||||
|
EXPECT_TRUE(vec.isEmpty());
|
||||||
|
EXPECT_EQ(vec.rows(), 0);
|
||||||
|
EXPECT_EQ(vec.cols(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(Vector2D, resize_reduce_both_is_data_stay_consistent) {
|
TEST_F(Vector2DResizeTest, resize_same_dimensions_does_nothing) {
|
||||||
resize_reduce_is_data_stay_consistent(ROWS_COUNT_REDUCE, COLS_COUNT_REDUCE);
|
size_t oldRows = vec.rows();
|
||||||
|
size_t oldCols = vec.cols();
|
||||||
|
PIVector<int> oldData = vec.asPlainVector();
|
||||||
|
|
||||||
|
vec.resize(oldRows, oldCols, 999);
|
||||||
|
EXPECT_EQ(vec.rows(), oldRows);
|
||||||
|
EXPECT_EQ(vec.cols(), oldCols);
|
||||||
|
EXPECT_EQ(vec.asPlainVector(), oldData); // Data unchanged
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== SEARCH AND LOOKUP TESTS ====================
|
||||||
|
TEST_F(Vector2DTest, contains_finds_element_in_flat_vector) {
|
||||||
|
EXPECT_TRUE(vec.contains(5 * COLS_COUNT_INIT + 7));
|
||||||
|
EXPECT_FALSE(vec.contains(-999));
|
||||||
|
EXPECT_TRUE(vec.contains(0)); // first element
|
||||||
|
EXPECT_TRUE(vec.contains(ROWS_COUNT_INIT * COLS_COUNT_INIT - 1)); // last element
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, contains_with_start_parameter_works) {
|
||||||
|
int target = 10 * COLS_COUNT_INIT + 15;
|
||||||
|
EXPECT_TRUE(vec.contains(target));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, contains_vector_of_elements_works) {
|
||||||
|
PIVector<int> searchFor;
|
||||||
|
searchFor << 100 << 200 << 300;
|
||||||
|
EXPECT_TRUE(vec.asPlainVector().containsAll(searchFor));
|
||||||
|
|
||||||
|
searchFor << -999;
|
||||||
|
EXPECT_FALSE(vec.asPlainVector().containsAll(searchFor));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, entries_counts_occurrences) {
|
||||||
|
// Add some duplicates
|
||||||
|
vec.fill(0);
|
||||||
|
vec.element(5, 5) = 42;
|
||||||
|
vec.element(10, 10) = 42;
|
||||||
|
|
||||||
|
EXPECT_EQ(vec.entries(42), 2);
|
||||||
|
EXPECT_EQ(vec.entries(-1), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, entries_with_predicate_counts_matches) {
|
||||||
|
auto isEven = [](const int & e) { return e % 2 == 0; };
|
||||||
|
int evenCount = 0;
|
||||||
|
for (size_t i = 0; i < vec.size(); ++i) {
|
||||||
|
if (vec.asPlainVector()[i] % 2 == 0) evenCount++;
|
||||||
|
}
|
||||||
|
EXPECT_EQ(vec.entries(isEven), evenCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ==================== STATISTICS AND CONDITIONS TESTS ====================
|
||||||
|
TEST_F(Vector2DTest, any_returns_true_if_any_match) {
|
||||||
|
auto isNegative = [](const int & e) { return e < 0; };
|
||||||
|
auto isLarge = [](const int & e) { return e > 1000000; };
|
||||||
|
|
||||||
|
EXPECT_FALSE(vec.any(isNegative));
|
||||||
|
EXPECT_FALSE(vec.any(isLarge));
|
||||||
|
|
||||||
|
auto isPositive = [](const int & e) { return e >= 0; };
|
||||||
|
EXPECT_TRUE(vec.any(isPositive));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, every_returns_true_if_all_match) {
|
||||||
|
auto isNonNegative = [](const int & e) { return e >= 0; };
|
||||||
|
const int max = ROWS_COUNT_INIT * COLS_COUNT_INIT;
|
||||||
|
auto isLessThan = [max](const int & e) { return e < max; };
|
||||||
|
|
||||||
|
EXPECT_TRUE(vec.every(isNonNegative));
|
||||||
|
EXPECT_TRUE(vec.every(isLessThan));
|
||||||
|
|
||||||
|
auto isEven = [](const int & e) { return e % 2 == 0; };
|
||||||
|
EXPECT_FALSE(vec.every(isEven));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== FILL AND ASSIGN TESTS ====================
|
||||||
|
TEST_F(Vector2DTest, fill_sets_all_elements_to_value) {
|
||||||
|
vec.fill(42);
|
||||||
|
for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) {
|
||||||
|
for (size_t c = 0; c < COLS_COUNT_INIT; ++c) {
|
||||||
|
EXPECT_EQ(vec.element(r, c), 42);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, fill_with_function_generates_values) {
|
||||||
|
vec.fill([](size_t i) { return static_cast<int>(i * 2); });
|
||||||
|
for (size_t i = 0; i < vec.size(); ++i) {
|
||||||
|
EXPECT_EQ(vec.asPlainVector()[i], static_cast<int>(i * 2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, assign_is_alias_for_fill) {
|
||||||
|
vec.assign(99);
|
||||||
|
for (size_t i = 0; i < vec.size(); ++i) {
|
||||||
|
EXPECT_EQ(vec.asPlainVector()[i], 99);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, assign_with_dimensions_resets_and_fills) {
|
||||||
|
vec.assign(5, 7, 123);
|
||||||
|
EXPECT_EQ(vec.rows(), 5);
|
||||||
|
EXPECT_EQ(vec.cols(), 7);
|
||||||
|
for (size_t r = 0; r < 5; ++r) {
|
||||||
|
for (size_t c = 0; c < 7; ++c) {
|
||||||
|
EXPECT_EQ(vec.element(r, c), 123);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== COMPARISON TESTS ====================
|
||||||
|
TEST_F(Vector2DTest, equality_operator_works) {
|
||||||
|
PIVector2D<int> same = vec;
|
||||||
|
EXPECT_EQ(vec, same);
|
||||||
|
|
||||||
|
PIVector2D<int> differentRows(ROWS_COUNT_INIT + 1, COLS_COUNT_INIT);
|
||||||
|
EXPECT_NE(vec, differentRows);
|
||||||
|
|
||||||
|
PIVector2D<int> differentCols(ROWS_COUNT_INIT, COLS_COUNT_INIT + 1);
|
||||||
|
EXPECT_NE(vec, differentCols);
|
||||||
|
|
||||||
|
PIVector2D<int> differentData(ROWS_COUNT_INIT, COLS_COUNT_INIT, 99);
|
||||||
|
EXPECT_NE(vec, differentData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== CONVERSION TESTS ====================
|
||||||
|
TEST_F(Vector2DTest, toVectors_converts_correctly) {
|
||||||
|
auto vectors = vec.toVectors();
|
||||||
|
EXPECT_EQ(vectors.size(), ROWS_COUNT_INIT);
|
||||||
|
|
||||||
|
for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) {
|
||||||
|
EXPECT_EQ(vectors[r].size(), COLS_COUNT_INIT);
|
||||||
|
for (size_t c = 0; c < COLS_COUNT_INIT; ++c) {
|
||||||
|
EXPECT_EQ(vectors[r][c], vec.element(r, c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, plainVector_returns_underlying_storage) {
|
||||||
|
const auto & plain = vec.asPlainVector();
|
||||||
|
EXPECT_EQ(plain.size(), vec.size());
|
||||||
|
|
||||||
|
for (size_t i = 0; i < plain.size(); ++i) {
|
||||||
|
EXPECT_EQ(plain[i], vec.asPlainVector()[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, toPlainVector_returns_copy) {
|
||||||
|
auto copy = vec.toPlainVector();
|
||||||
|
EXPECT_EQ(copy.size(), vec.size());
|
||||||
|
EXPECT_NE(copy.data(), vec.data()); // Different memory
|
||||||
|
|
||||||
|
for (size_t i = 0; i < copy.size(); ++i) {
|
||||||
|
EXPECT_EQ(copy[i], vec.asPlainVector()[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== SWAP TESTS ====================
|
||||||
|
TEST_F(Vector2DTest, swap_exchanges_contents) {
|
||||||
|
PIVector2D<int> other(5, 5, 42);
|
||||||
|
|
||||||
|
size_t oldRows = vec.rows();
|
||||||
|
size_t oldCols = vec.cols();
|
||||||
|
PIVector<int> oldData = vec.asPlainVector();
|
||||||
|
|
||||||
|
vec.swap(other);
|
||||||
|
|
||||||
|
EXPECT_EQ(vec.rows(), 5);
|
||||||
|
EXPECT_EQ(vec.cols(), 5);
|
||||||
|
for (size_t i = 0; i < vec.size(); ++i) {
|
||||||
|
EXPECT_EQ(vec.asPlainVector()[i], 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_EQ(other.rows(), oldRows);
|
||||||
|
EXPECT_EQ(other.cols(), oldCols);
|
||||||
|
EXPECT_EQ(other.asPlainVector(), oldData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== CLEAR TESTS ====================
|
||||||
|
TEST_F(Vector2DTest, clear_removes_all_elements) {
|
||||||
|
vec.clear();
|
||||||
|
EXPECT_TRUE(vec.isEmpty());
|
||||||
|
EXPECT_EQ(vec.rows(), 0);
|
||||||
|
EXPECT_EQ(vec.cols(), 0);
|
||||||
|
EXPECT_EQ(vec.size(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== TRANSPOSE AND REVERSE TESTS ====================
|
||||||
|
TEST_F(Vector2DTest, transposed_returns_correct_dimensions) {
|
||||||
|
auto transposed = vec.transposed();
|
||||||
|
EXPECT_EQ(transposed.rows(), COLS_COUNT_INIT);
|
||||||
|
EXPECT_EQ(transposed.cols(), ROWS_COUNT_INIT);
|
||||||
|
|
||||||
|
for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) {
|
||||||
|
for (size_t c = 0; c < COLS_COUNT_INIT; ++c) {
|
||||||
|
EXPECT_EQ(transposed.element(c, r), vec.element(r, c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Vector2DTransposeTest, emptyMatrix_returnsEmpty) {
|
||||||
|
PIVector2D<int> empty;
|
||||||
|
auto transposed = empty.transposed();
|
||||||
|
EXPECT_TRUE(transposed.isEmpty());
|
||||||
|
EXPECT_EQ(transposed.rows(), 0);
|
||||||
|
EXPECT_EQ(transposed.cols(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Vector2DTransposeTest, singleElement_returnsSame) {
|
||||||
|
PIVector2D<int> single(1, 1, 42);
|
||||||
|
auto transposed = single.transposed();
|
||||||
|
EXPECT_EQ(transposed.rows(), 1);
|
||||||
|
EXPECT_EQ(transposed.cols(), 1);
|
||||||
|
EXPECT_EQ(transposed.element(0, 0), 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Vector2DTransposeTest, oneRow_becomesOneColumn) {
|
||||||
|
PIVector2D<int> rowVec(1, 5);
|
||||||
|
for (size_t c = 0; c < 5; ++c)
|
||||||
|
rowVec.element(0, c) = static_cast<int>(c);
|
||||||
|
auto transposed = rowVec.transposed();
|
||||||
|
EXPECT_EQ(transposed.rows(), 5);
|
||||||
|
EXPECT_EQ(transposed.cols(), 1);
|
||||||
|
for (size_t r = 0; r < 5; ++r) {
|
||||||
|
EXPECT_EQ(transposed.element(r, 0), static_cast<int>(r));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Vector2DTransposeTest, oneColumn_becomesOneRow) {
|
||||||
|
PIVector2D<int> colVec(5, 1);
|
||||||
|
for (size_t r = 0; r < 5; ++r)
|
||||||
|
colVec.element(r, 0) = static_cast<int>(r);
|
||||||
|
auto transposed = colVec.transposed();
|
||||||
|
EXPECT_EQ(transposed.rows(), 1);
|
||||||
|
EXPECT_EQ(transposed.cols(), 5);
|
||||||
|
for (size_t c = 0; c < 5; ++c) {
|
||||||
|
EXPECT_EQ(transposed.element(0, c), static_cast<int>(c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, transposed_doesNotModifyOriginal) {
|
||||||
|
auto original = vec; // копия для сравнения
|
||||||
|
auto transposed = vec.transposed();
|
||||||
|
// Проверяем, что исходный массив не изменился
|
||||||
|
EXPECT_EQ(vec, original);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, reverseRows_reverses_row_order) {
|
||||||
|
auto original = vec;
|
||||||
|
vec.reverseRows();
|
||||||
|
|
||||||
|
for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) {
|
||||||
|
for (size_t c = 0; c < COLS_COUNT_INIT; ++c) {
|
||||||
|
EXPECT_EQ(vec.element(r, c), original.element(ROWS_COUNT_INIT - 1 - r, c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, reverseColumns_reverses_column_order_in_each_row) {
|
||||||
|
auto original = vec;
|
||||||
|
vec.reverseColumns();
|
||||||
|
|
||||||
|
for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) {
|
||||||
|
for (size_t c = 0; c < COLS_COUNT_INIT; ++c) {
|
||||||
|
EXPECT_EQ(vec.element(r, c), original.element(r, COLS_COUNT_INIT - 1 - c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, reverseRows_and_reverseColumns_compose_correctly) {
|
||||||
|
auto original = vec;
|
||||||
|
vec.reverseRows();
|
||||||
|
vec.reverseColumns();
|
||||||
|
|
||||||
|
// This should be equivalent to 180-degree rotation
|
||||||
|
for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) {
|
||||||
|
for (size_t c = 0; c < COLS_COUNT_INIT; ++c) {
|
||||||
|
EXPECT_EQ(vec.element(r, c), original.element(ROWS_COUNT_INIT - 1 - r, COLS_COUNT_INIT - 1 - c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== RANGE TESTS ====================
|
||||||
|
TEST_F(Vector2DTest, getRange_returns_submatrix) {
|
||||||
|
auto sub = vec.getRange(5, 10, 8, 15);
|
||||||
|
EXPECT_EQ(sub.rows(), 10);
|
||||||
|
EXPECT_EQ(sub.cols(), 15);
|
||||||
|
|
||||||
|
for (size_t r = 0; r < 10; ++r) {
|
||||||
|
for (size_t c = 0; c < 15; ++c) {
|
||||||
|
EXPECT_EQ(sub.element(r, c), vec.element(5 + r, 8 + c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, getRange_with_invalid_params_returns_empty) {
|
||||||
|
auto sub1 = vec.getRange(ROWS_COUNT_INIT, 5, 0, 5);
|
||||||
|
EXPECT_TRUE(sub1.isEmpty());
|
||||||
|
|
||||||
|
auto sub2 = vec.getRange(0, 5, COLS_COUNT_INIT, 5);
|
||||||
|
EXPECT_TRUE(sub2.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, getRange_truncates_out_of_bounds) {
|
||||||
|
auto sub = vec.getRange(ROWS_COUNT_INIT - 5, 10, COLS_COUNT_INIT - 5, 10);
|
||||||
|
EXPECT_EQ(sub.rows(), 5);
|
||||||
|
EXPECT_EQ(sub.cols(), 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== FUNCTIONAL PROGRAMMING TESTS ====================
|
||||||
|
TEST_F(Vector2DTest, map_transforms_elements) {
|
||||||
|
auto doubled = vec.map<int>([](const int & e) { return e * 2; });
|
||||||
|
EXPECT_EQ(doubled.rows(), vec.rows());
|
||||||
|
EXPECT_EQ(doubled.cols(), vec.cols());
|
||||||
|
|
||||||
|
for (size_t r = 0; r < vec.rows(); ++r) {
|
||||||
|
for (size_t c = 0; c < vec.cols(); ++c) {
|
||||||
|
EXPECT_EQ(doubled.element(r, c), vec.element(r, c) * 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, map_changes_type) {
|
||||||
|
auto asString = vec.map<PIString>([](const int & e) { return PIString::fromNumber(e); });
|
||||||
|
EXPECT_EQ(asString.rows(), vec.rows());
|
||||||
|
EXPECT_EQ(asString.cols(), vec.cols());
|
||||||
|
|
||||||
|
for (size_t r = 0; r < vec.rows(); ++r) {
|
||||||
|
for (size_t c = 0; c < vec.cols(); ++c) {
|
||||||
|
EXPECT_EQ(asString.element(r, c), PIString::fromNumber(vec.element(r, c)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, mapIndexed_uses_indices) {
|
||||||
|
auto indexed = vec.mapIndexed<int>([](size_t r, size_t c, const int & e) { return static_cast<int>(r * 1000 + c); });
|
||||||
|
|
||||||
|
for (size_t r = 0; r < vec.rows(); ++r) {
|
||||||
|
for (size_t c = 0; c < vec.cols(); ++c) {
|
||||||
|
EXPECT_EQ(indexed.element(r, c), static_cast<int>(r * 1000 + c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, forEach_readonly_visits_all_elements) {
|
||||||
|
size_t count = 0;
|
||||||
|
vec.asPlainVector().forEach([&count](const int &) { count++; });
|
||||||
|
EXPECT_EQ(count, vec.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, forEach_modifying_changes_elements) {
|
||||||
|
vec.asPlainVector().forEach([](int & e) { e++; });
|
||||||
|
|
||||||
|
for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) {
|
||||||
|
for (size_t c = 0; c < COLS_COUNT_INIT; ++c) {
|
||||||
|
EXPECT_EQ(vec.element(r, c), r * COLS_COUNT_INIT + c + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, indexOf_returns_correct_pair) {
|
||||||
|
auto p = vec.indexOf(vec.element(10, 15));
|
||||||
|
EXPECT_EQ(p.first, 10);
|
||||||
|
EXPECT_EQ(p.second, 15);
|
||||||
|
p = vec.indexOf(-999);
|
||||||
|
EXPECT_EQ(p.first, -1);
|
||||||
|
EXPECT_EQ(p.second, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, indexWhere_returns_correct_pair) {
|
||||||
|
vec.element(5, 5) = -42;
|
||||||
|
auto isTarget = [](const int & e) { return e == -42; };
|
||||||
|
auto p = vec.indexWhere(isTarget);
|
||||||
|
EXPECT_EQ(p.first, 5);
|
||||||
|
EXPECT_EQ(p.second, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, lastIndexOf_works) {
|
||||||
|
int val = vec.element(10, 10);
|
||||||
|
vec.element(20, 20) = val; // duplicate
|
||||||
|
auto p = vec.lastIndexOf(val);
|
||||||
|
EXPECT_EQ(p.first, 20);
|
||||||
|
EXPECT_EQ(p.second, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, lastIndexWhere_works) {
|
||||||
|
auto isLarge = [](const int & e) { return e > 500; };
|
||||||
|
auto p = vec.lastIndexWhere(isLarge);
|
||||||
|
EXPECT_GE(p.first, 0);
|
||||||
|
EXPECT_GE(p.second, 0);
|
||||||
|
// The last element with value >500 should be the largest index
|
||||||
|
size_t lastFlat = p.first * vec.cols() + p.second;
|
||||||
|
size_t expectedLastFlat = vec.size() - 1;
|
||||||
|
EXPECT_EQ(lastFlat, expectedLastFlat);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, reduce_accumulates_correctly) {
|
||||||
|
int sum = vec.reduce<int>([](const int & e, const int & acc) { return e + acc; });
|
||||||
|
|
||||||
|
int expected = (vec.size() - 1) * vec.size() / 2;
|
||||||
|
EXPECT_EQ(sum, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, reduce_with_initial_value) {
|
||||||
|
int sum = vec.reduce<int>([](const int & e, const int & acc) { return e + acc; }, 100);
|
||||||
|
|
||||||
|
int expected = (vec.size() - 1) * vec.size() / 2 + 100;
|
||||||
|
EXPECT_EQ(sum, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, reduceIndexed_uses_indices) {
|
||||||
|
int sum =
|
||||||
|
vec.reduceIndexed<int>([](size_t r, size_t c, const int & e, const int & acc) { return acc + static_cast<int>(r * 1000 + c); });
|
||||||
|
|
||||||
|
int expected = 0;
|
||||||
|
for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) {
|
||||||
|
for (size_t c = 0; c < COLS_COUNT_INIT; ++c) {
|
||||||
|
expected += r * 1000 + c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EXPECT_EQ(sum, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== REMOVAL TESTS ====================
|
||||||
|
TEST_F(Vector2DTest, removeRow_removes_specified_row) {
|
||||||
|
size_t oldRows = vec.rows();
|
||||||
|
auto rowContent = vec[10].toVector();
|
||||||
|
|
||||||
|
vec.removeRow(10);
|
||||||
|
EXPECT_EQ(vec.rows(), oldRows - 1);
|
||||||
|
|
||||||
|
// Check rows after 10 shifted up
|
||||||
|
for (size_t r = 10; r < vec.rows(); ++r) {
|
||||||
|
for (size_t c = 0; c < vec.cols(); ++c) {
|
||||||
|
EXPECT_EQ(vec.element(r, c), (r + 1) * COLS_COUNT_INIT + c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, removeRow_invalid_index_does_nothing) {
|
||||||
|
size_t oldRows = vec.rows();
|
||||||
|
vec.removeRow(ROWS_COUNT_INIT + 10);
|
||||||
|
EXPECT_EQ(vec.rows(), oldRows);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, removeRow_last_row_works) {
|
||||||
|
size_t oldRows = vec.rows();
|
||||||
|
vec.removeRow(oldRows - 1);
|
||||||
|
EXPECT_EQ(vec.rows(), oldRows - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, removeColumn_removes_specified_column) {
|
||||||
|
size_t oldCols = vec.cols();
|
||||||
|
vec.removeColumn(15);
|
||||||
|
EXPECT_EQ(vec.cols(), oldCols - 1);
|
||||||
|
|
||||||
|
for (size_t r = 0; r < vec.rows(); ++r) {
|
||||||
|
for (size_t c = 0; c < 15; ++c) {
|
||||||
|
EXPECT_EQ(vec.element(r, c), r * COLS_COUNT_INIT + c);
|
||||||
|
}
|
||||||
|
for (size_t c = 15; c < vec.cols(); ++c) {
|
||||||
|
EXPECT_EQ(vec.element(r, c), r * COLS_COUNT_INIT + c + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, removeColumn_invalid_index_does_nothing) {
|
||||||
|
size_t oldCols = vec.cols();
|
||||||
|
vec.removeColumn(COLS_COUNT_INIT + 10);
|
||||||
|
EXPECT_EQ(vec.cols(), oldCols);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, removeColumn_last_column_works) {
|
||||||
|
size_t oldCols = vec.cols();
|
||||||
|
vec.removeColumn(oldCols - 1);
|
||||||
|
EXPECT_EQ(vec.cols(), oldCols - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, removeRowsWhere_removes_matching_rows) {
|
||||||
|
auto isSpecial = [](const PIVector2D<int>::RowConst & row) {
|
||||||
|
return row[0] == 999; // First element is 999
|
||||||
|
};
|
||||||
|
|
||||||
|
const size_t rowsCont = 5;
|
||||||
|
// Add some identifiable rows
|
||||||
|
for (size_t r = 0; r < rowsCont; ++r) {
|
||||||
|
vec.addRow(PIVector<int>(COLS_COUNT_INIT, 999));
|
||||||
|
}
|
||||||
|
EXPECT_EQ(vec.filterRows(isSpecial).rows(), rowsCont);
|
||||||
|
|
||||||
|
vec.removeRowsWhere(isSpecial);
|
||||||
|
EXPECT_EQ(vec.rows(), ROWS_COUNT_INIT);
|
||||||
|
|
||||||
|
// Verify no rows with 999 remain
|
||||||
|
auto res = vec.filterRows(isSpecial);
|
||||||
|
EXPECT_TRUE(res.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, removeColumnsWhere_removes_matching_columns) {
|
||||||
|
// Make some columns have a distinctive first element
|
||||||
|
for (size_t c = 0; c < 5; ++c) {
|
||||||
|
vec.element(0, c) = 777;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto isSpecial = [](const PIVector2D<int>::ColConst & col) {
|
||||||
|
return col[0] == 777; // First element is 777
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t oldCols = vec.cols();
|
||||||
|
vec.removeColumnsWhere(isSpecial);
|
||||||
|
EXPECT_EQ(vec.cols(), oldCols - 5);
|
||||||
|
|
||||||
|
// Verify no columns with 777 in first row remain
|
||||||
|
for (size_t c = 0; c < vec.cols(); ++c) {
|
||||||
|
EXPECT_NE(vec.element(0, c), 777);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== FILTER TESTS ====================
|
||||||
|
TEST_F(Vector2DTest, filterRows_returns_only_matching_rows) {
|
||||||
|
auto rowsWithEvenFirst = vec.filterRows([](const PIVector2D<int>::RowConst & row) { return row[0] % 2 == 0; });
|
||||||
|
|
||||||
|
// First element of row r is r * COLS_COUNT_INIT
|
||||||
|
// This is even for all rows since COLS_COUNT_INIT is even (34)
|
||||||
|
EXPECT_EQ(rowsWithEvenFirst.rows(), ROWS_COUNT_INIT);
|
||||||
|
|
||||||
|
auto rowsWithLargeFirst = vec.filterRows([](const PIVector2D<int>::RowConst & row) { return row[0] > 500; });
|
||||||
|
|
||||||
|
// First element > 500 means r * 34 > 500 -> r > 14.7
|
||||||
|
EXPECT_EQ(rowsWithLargeFirst.rows(), ROWS_COUNT_INIT - 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, filterColumns_returns_only_matching_columns) {
|
||||||
|
auto colsWithEvenFirst = vec.filterColumns([](const PIVector2D<int>::ColConst & col) { return col[0] % 2 == 0; });
|
||||||
|
|
||||||
|
// First element of column c is c
|
||||||
|
EXPECT_EQ(colsWithEvenFirst.cols(), COLS_COUNT_INIT / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, filterColumns_empty_result_returns_empty) {
|
||||||
|
auto noCols = vec.filterColumns([](const PIVector2D<int>::ColConst &) { return false; });
|
||||||
|
EXPECT_TRUE(noCols.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== EDGE CASE TESTS ====================
|
||||||
|
TEST(Vector2DEdgeTest, empty_vector_operations) {
|
||||||
|
PIVector2D<int> empty;
|
||||||
|
|
||||||
|
EXPECT_TRUE(empty.isEmpty());
|
||||||
|
EXPECT_EQ(empty.rows(), 0);
|
||||||
|
EXPECT_EQ(empty.cols(), 0);
|
||||||
|
|
||||||
|
// These should not crash
|
||||||
|
empty.clear();
|
||||||
|
empty.fill(42);
|
||||||
|
empty.transposed();
|
||||||
|
empty.reverseRows();
|
||||||
|
empty.reverseColumns();
|
||||||
|
|
||||||
|
auto range = empty.getRange(0, 5, 0, 5);
|
||||||
|
EXPECT_TRUE(range.isEmpty());
|
||||||
|
|
||||||
|
auto filtered = empty.filterRows([](const PIVector2D<int>::RowConst &) { return true; });
|
||||||
|
EXPECT_TRUE(filtered.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Vector2DEdgeTest, single_element_vector) {
|
||||||
|
PIVector2D<int> single(1, 1, 42);
|
||||||
|
|
||||||
|
EXPECT_EQ(single.rows(), 1);
|
||||||
|
EXPECT_EQ(single.cols(), 1);
|
||||||
|
EXPECT_EQ(single.element(0, 0), 42);
|
||||||
|
|
||||||
|
auto row = single[0];
|
||||||
|
EXPECT_EQ(row.size(), 1);
|
||||||
|
EXPECT_EQ(row[0], 42);
|
||||||
|
|
||||||
|
auto col = single.col(0);
|
||||||
|
EXPECT_EQ(col.size(), 1);
|
||||||
|
EXPECT_EQ(col[0], 42);
|
||||||
|
|
||||||
|
single.reverseRows(); // Should do nothing
|
||||||
|
EXPECT_EQ(single.element(0, 0), 42);
|
||||||
|
|
||||||
|
single.reverseColumns(); // Should do nothing
|
||||||
|
EXPECT_EQ(single.element(0, 0), 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== PROXY ADDITIONAL OPERATIONS TESTS ====================
|
||||||
|
TEST_F(Vector2DTest, row_proxy_forEach_modifies_elements) {
|
||||||
|
auto row = vec[5];
|
||||||
|
row.forEach([](int & e) { e += 100; });
|
||||||
|
for (size_t c = 0; c < COLS_COUNT_INIT; ++c) {
|
||||||
|
EXPECT_EQ(vec.element(5, c), 5 * COLS_COUNT_INIT + c + 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, row_proxy_forEach_const_iterates) {
|
||||||
|
const auto & constVec = vec;
|
||||||
|
auto row = constVec[5];
|
||||||
|
size_t count = 0;
|
||||||
|
row.forEach([&count](const int &) { ++count; });
|
||||||
|
EXPECT_EQ(count, COLS_COUNT_INIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, row_proxy_fill_sets_all_elements) {
|
||||||
|
auto row = vec[12];
|
||||||
|
row.fill(999);
|
||||||
|
for (size_t c = 0; c < COLS_COUNT_INIT; ++c) {
|
||||||
|
EXPECT_EQ(vec.element(12, c), 999);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, row_proxy_contains_finds_element) {
|
||||||
|
auto row = vec[8];
|
||||||
|
EXPECT_TRUE(row.contains(vec.element(8, 10)));
|
||||||
|
EXPECT_FALSE(row.contains(-999));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, row_proxy_entries_counts_occurrences) {
|
||||||
|
auto row = vec[15];
|
||||||
|
// Add a duplicate
|
||||||
|
int val = vec.element(15, 5);
|
||||||
|
vec.element(15, 20) = val;
|
||||||
|
EXPECT_EQ(row.entries(val), 2);
|
||||||
|
EXPECT_EQ(row.entries(-999), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, row_proxy_entries_with_predicate_counts_matches) {
|
||||||
|
auto row = vec[20];
|
||||||
|
auto isEven = [](const int & e) { return e % 2 == 0; };
|
||||||
|
int expected = 0;
|
||||||
|
for (size_t c = 0; c < COLS_COUNT_INIT; ++c) {
|
||||||
|
if (row[c] % 2 == 0) ++expected;
|
||||||
|
}
|
||||||
|
EXPECT_EQ(row.entries(isEven), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, row_proxy_any_returns_true_if_any_match) {
|
||||||
|
auto row = vec[25];
|
||||||
|
auto isNegative = [](const int & e) { return e < 0; };
|
||||||
|
EXPECT_FALSE(row.any(isNegative));
|
||||||
|
auto isPositive = [](const int & e) { return e >= 0; };
|
||||||
|
EXPECT_TRUE(row.any(isPositive));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, row_proxy_every_returns_true_if_all_match) {
|
||||||
|
auto row = vec[30];
|
||||||
|
auto isLessThanMax = [&](const int & e) { return e < static_cast<int>(vec.size()); };
|
||||||
|
EXPECT_TRUE(row.every(isLessThanMax));
|
||||||
|
auto isEven = [](const int & e) { return e % 2 == 0; };
|
||||||
|
EXPECT_FALSE(row.every(isEven));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
TEST_F(Vector2DTest, col_proxy_forEach_modifies_elements) {
|
||||||
|
auto col = vec.col(7);
|
||||||
|
col.forEach([](int & e) { e += 50; });
|
||||||
|
for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) {
|
||||||
|
EXPECT_EQ(vec.element(r, 7), r * COLS_COUNT_INIT + 7 + 50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, col_proxy_forEach_const_iterates) {
|
||||||
|
const auto & constVec = vec;
|
||||||
|
auto col = constVec.col(9);
|
||||||
|
size_t count = 0;
|
||||||
|
col.forEach([&count](const int &) { ++count; });
|
||||||
|
EXPECT_EQ(count, ROWS_COUNT_INIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, col_proxy_fill_sets_all_elements) {
|
||||||
|
auto col = vec.col(11);
|
||||||
|
col.fill(777);
|
||||||
|
for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) {
|
||||||
|
EXPECT_EQ(vec.element(r, 11), 777);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, col_proxy_contains_finds_element) {
|
||||||
|
auto col = vec.col(13);
|
||||||
|
EXPECT_TRUE(col.contains(vec.element(5, 13)));
|
||||||
|
EXPECT_FALSE(col.contains(-999));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, col_proxy_entries_counts_occurrences) {
|
||||||
|
auto col = vec.col(17);
|
||||||
|
int val = vec.element(3, 17);
|
||||||
|
vec.element(22, 17) = val; // duplicate
|
||||||
|
EXPECT_EQ(col.entries(val), 2);
|
||||||
|
EXPECT_EQ(col.entries(-999), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, col_proxy_entries_with_predicate_counts_matches) {
|
||||||
|
auto col = vec.col(19);
|
||||||
|
auto isOdd = [](const int & e) { return e % 2 != 0; };
|
||||||
|
int expected = 0;
|
||||||
|
for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) {
|
||||||
|
if (col[r] % 2 != 0) ++expected;
|
||||||
|
}
|
||||||
|
EXPECT_EQ(col.entries(isOdd), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, col_proxy_any_returns_true_if_any_match) {
|
||||||
|
auto col = vec.col(21);
|
||||||
|
auto isNegative = [](const int & e) { return e < 0; };
|
||||||
|
EXPECT_FALSE(col.any(isNegative));
|
||||||
|
auto isPositive = [](const int & e) { return e >= 0; };
|
||||||
|
EXPECT_TRUE(col.any(isPositive));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, col_proxy_every_returns_true_if_all_match) {
|
||||||
|
auto col = vec.col(23);
|
||||||
|
auto isLessThanMax = [&](const int & e) { return e < static_cast<int>(vec.size()); };
|
||||||
|
EXPECT_TRUE(col.every(isLessThanMax));
|
||||||
|
auto isEven = [](const int & e) { return e % 2 == 0; };
|
||||||
|
EXPECT_FALSE(col.every(isEven));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
TEST_F(Vector2DTest, rowconst_proxy_forEach_iterates) {
|
||||||
|
const auto & constVec = vec;
|
||||||
|
auto row = constVec[5];
|
||||||
|
size_t count = 0;
|
||||||
|
row.forEach([&count](const int &) { ++count; });
|
||||||
|
EXPECT_EQ(count, COLS_COUNT_INIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, rowconst_proxy_contains_finds_element) {
|
||||||
|
const auto & constVec = vec;
|
||||||
|
auto row = constVec[6];
|
||||||
|
EXPECT_TRUE(row.contains(vec.element(6, 10)));
|
||||||
|
EXPECT_FALSE(row.contains(-999));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, rowconst_proxy_entries_counts_occurrences) {
|
||||||
|
const auto & constVec = vec;
|
||||||
|
auto row = constVec[7];
|
||||||
|
int val = vec.element(7, 5);
|
||||||
|
// We can't modify through const proxy, but duplicates already exist from previous tests
|
||||||
|
EXPECT_GE(row.entries(val), 1); // at least one
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, rowconst_proxy_entries_with_predicate_counts_matches) {
|
||||||
|
const auto & constVec = vec;
|
||||||
|
auto row = constVec[9];
|
||||||
|
auto isEven = [](const int & e) { return e % 2 == 0; };
|
||||||
|
int expected = 0;
|
||||||
|
for (size_t c = 0; c < COLS_COUNT_INIT; ++c) {
|
||||||
|
if (vec.element(9, c) % 2 == 0) ++expected;
|
||||||
|
}
|
||||||
|
EXPECT_EQ(row.entries(isEven), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, rowconst_proxy_any_returns_true_if_any_match) {
|
||||||
|
const auto & constVec = vec;
|
||||||
|
auto row = constVec[10];
|
||||||
|
auto isNegative = [](const int & e) { return e < 0; };
|
||||||
|
EXPECT_FALSE(row.any(isNegative));
|
||||||
|
auto isPositive = [](const int & e) { return e >= 0; };
|
||||||
|
EXPECT_TRUE(row.any(isPositive));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, rowconst_proxy_every_returns_true_if_all_match) {
|
||||||
|
const auto & constVec = vec;
|
||||||
|
auto row = constVec[11];
|
||||||
|
auto isLessThanMax = [&](const int & e) { return e < static_cast<int>(vec.size()); };
|
||||||
|
EXPECT_TRUE(row.every(isLessThanMax));
|
||||||
|
auto isEven = [](const int & e) { return e % 2 == 0; };
|
||||||
|
EXPECT_FALSE(row.every(isEven));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
TEST_F(Vector2DTest, colconst_proxy_forEach_iterates) {
|
||||||
|
const auto & constVec = vec;
|
||||||
|
auto col = constVec.col(25);
|
||||||
|
size_t count = 0;
|
||||||
|
col.forEach([&count](const int &) { ++count; });
|
||||||
|
EXPECT_EQ(count, ROWS_COUNT_INIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, colconst_proxy_contains_finds_element) {
|
||||||
|
const auto & constVec = vec;
|
||||||
|
auto col = constVec.col(27);
|
||||||
|
EXPECT_TRUE(col.contains(vec.element(4, 27)));
|
||||||
|
EXPECT_FALSE(col.contains(-999));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, colconst_proxy_entries_counts_occurrences) {
|
||||||
|
const auto & constVec = vec;
|
||||||
|
auto col = constVec.col(29);
|
||||||
|
int val = vec.element(8, 29);
|
||||||
|
EXPECT_GE(col.entries(val), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, colconst_proxy_entries_with_predicate_counts_matches) {
|
||||||
|
const auto & constVec = vec;
|
||||||
|
auto col = constVec.col(31);
|
||||||
|
auto isOdd = [](const int & e) { return e % 2 != 0; };
|
||||||
|
int expected = 0;
|
||||||
|
for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) {
|
||||||
|
if (vec.element(r, 31) % 2 != 0) ++expected;
|
||||||
|
}
|
||||||
|
EXPECT_EQ(col.entries(isOdd), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, colconst_proxy_any_returns_true_if_any_match) {
|
||||||
|
const auto & constVec = vec;
|
||||||
|
auto col = constVec.col(33);
|
||||||
|
auto isNegative = [](const int & e) { return e < 0; };
|
||||||
|
EXPECT_FALSE(col.any(isNegative));
|
||||||
|
auto isPositive = [](const int & e) { return e >= 0; };
|
||||||
|
EXPECT_TRUE(col.any(isPositive));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Vector2DTest, colconst_proxy_every_returns_true_if_all_match) {
|
||||||
|
const auto & constVec = vec;
|
||||||
|
auto col = constVec.col(0);
|
||||||
|
auto isLessThanMax = [&](const int & e) { return e < static_cast<int>(vec.size()); };
|
||||||
|
EXPECT_TRUE(col.every(isLessThanMax));
|
||||||
|
auto isNotEven = [](const int & e) { return e % 2 != 0; };
|
||||||
|
EXPECT_FALSE(col.every(isNotEven));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== OUTPUT TESTS ====================
|
||||||
|
TEST_F(Vector2DTest, picout_operator_works) {
|
||||||
|
// Just test that it compiles and doesn't crash
|
||||||
|
PICout s;
|
||||||
|
s << vec;
|
||||||
|
// No assertion, just ensure it runs
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PIP_STD_IOSTREAM
|
||||||
|
TEST_F(Vector2DTest, iostream_operator_works) {
|
||||||
|
// PIVector2D doesn't have direct iostream operator,
|
||||||
|
// but PIVector does, and we can test conversion
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << vec.plainVector();
|
||||||
|
// No assertion, just ensure it runs
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ const PIString contextName = "QAD::PIValueTreeEdit";
|
|||||||
|
|
||||||
|
|
||||||
void gatherStrings(TSFile::Context & context, const PIValueTree & vt, const PIString & loc) {
|
void gatherStrings(TSFile::Context & context, const PIValueTree & vt, const PIString & loc) {
|
||||||
const static PIStringList attrs({Attribute::prefix, Attribute::suffix});
|
const static PIStringList attrs({Attribute::prefix, Attribute::suffix, Attribute::toolTip});
|
||||||
for (const auto & c: vt.children()) {
|
for (const auto & c: vt.children()) {
|
||||||
context.confirm(c.name(), loc);
|
context.confirm(c.name(), loc);
|
||||||
context.confirm(c.comment(), loc);
|
context.confirm(c.comment(), loc);
|
||||||
|
|||||||
Reference in New Issue
Block a user