diff --git a/CMakeLists.txt b/CMakeLists.txt index b32040a1..fbb4fe38 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,7 +41,7 @@ option(DXFCXX_INSTALL_LIB "Prepare install the libraries" ON) option(DXFCXX_INSTALL_SAMPLES "Prepare install the samples" ${DXFCXX_IS_ROOT_PROJECT}) option(DXFCXX_INSTALL_TOOLS "Prepare install the tools" ${DXFCXX_IS_ROOT_PROJECT}) option(DXFCXX_USE_DXFEED_GRAAL_NATIVE_SDK_JFROG "" ON) -set(DXFEED_GRAAL_NATIVE_SDK_VERSION "1.0.2" CACHE STRING "") +set(DXFEED_GRAAL_NATIVE_SDK_VERSION "1.0.4" CACHE STRING "") set(DXFEED_GRAAL_NATIVE_SDK_JFROG_BASE_URL "https://dxfeed.jfrog.io/artifactory/maven-open/com/dxfeed/graal-native-sdk/" CACHE STRING "") option(DXFCXX_ENABLE_ASAN_UBSAN "Enable address, UB sanitizers etc" OFF) @@ -62,8 +62,13 @@ elseif (APPLE) set(DXFCXX_TARGET_PLATFORM "x86_64-osx") endif () elseif (UNIX) - set(DXFCXX_GRAAL_TARGET_PLATFORM "amd64-linux") - set(DXFCXX_TARGET_PLATFORM "x86_64-linux") + if (${CMAKE_HOST_SYSTEM_PROCESSOR} STREQUAL "x86_64") + set(DXFCXX_GRAAL_TARGET_PLATFORM "amd64-linux") + set(DXFCXX_TARGET_PLATFORM "x86_64-linux") + else () + set(DXFCXX_GRAAL_TARGET_PLATFORM "unknown") + set(DXFCXX_TARGET_PLATFORM "unknown") + endif () elseif () set(DXFCXX_GRAAL_TARGET_PLATFORM "unknown") set(DXFCXX_TARGET_PLATFORM "unknown") @@ -77,7 +82,7 @@ else () endif () if (DXFCXX_GRAAL_TARGET_PLATFORM STREQUAL "unknown") - message(ERROR "Unknown platform!") + message(ERROR "Unsupported platform!") else () set(DXFEED_GRAAL_NATIVE_SDK_URL ${DXFEED_GRAAL_NATIVE_SDK_URL}-${DXFCXX_GRAAL_TARGET_PLATFORM}.zip) endif () @@ -88,6 +93,9 @@ endif () FetchContent_MakeAvailable(DxFeedGraalNativeSdk) # DxFeedGraalNativeSdk_SOURCE_DIR +FetchContent_Declare(Process GIT_REPOSITORY "https://github.com/ttldtor/Process.git" GIT_TAG default) +FetchContent_MakeAvailable(Process) + add_subdirectory(third_party/utfcpp-3.2.3) set(FMT_INSTALL OFF) add_subdirectory(third_party/fmt-10.0.0) @@ -105,6 +113,8 @@ set(dxFeedGraalCxxApi_Internal_Sources src/internal/EventClassList.cpp src/internal/SymbolList.cpp src/internal/Common.cpp + src/internal/Platform.cpp + src/internal/Console.cpp ) set(dxFeedGraalCxxApi_InternalUtils_Sources @@ -319,6 +329,9 @@ add_custom_command(TARGET ${PROJECT_NAME}_static POST_BUILD COMMAND ${CMAKE_COMM add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $) +target_compile_definitions(${PROJECT_NAME} PUBLIC DXFCXX_VERSION="${DXFCXX_VERSION}") +target_compile_definitions(${PROJECT_NAME}_static PUBLIC DXFCXX_VERSION="${DXFCXX_VERSION}") + if (DXFCXX_BUILD_UNIT_TESTS) include(CTest) add_subdirectory(tests) diff --git a/DEPENDENCIES.md b/DEPENDENCIES.md index 0bcad066..0918315b 100644 --- a/DEPENDENCIES.md +++ b/DEPENDENCIES.md @@ -7,7 +7,8 @@ - [fmt](https://github.com/fmtlib/fmt) v10.0.0 - [doctest](https://github.com/doctest/doctest) v2.4.11 (Tests) - [range-v3](https://github.com/ericniebler/range-v3) v0.12 -- [date] (https://github.com/HowardHinnant/date) v3.0.1 +- [date](https://github.com/HowardHinnant/date) v3.0.1 +- [Process](https://github.com/ttldtor/Process) v1.0.0 (Tools) ## Run-time diff --git a/README.md b/README.md index 1782b3ad..e16a3053 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ the [Overview](#overview) section.
## Table of Contents - [Overview](#overview) - * [Reasons for the New C/C++ API Repository](#reasons-for-the-new-net-api-repository) + * [Reasons for the New C/C++ API Repository](#reasons-for-the-new-cxx-api-repository) * [Benefits of the New Version](#benefits-of-the-new-version) * [Milestones](#milestones) * [Future Development](#future-development) @@ -40,9 +40,10 @@ the [Overview](#overview) section.
## Overview -### Reasons for the New C/C++ API Repository +### Reasons for the New CXX API Repository -The old version of [dxFeed C API](https://github.com/dxFeed/dxfeed-c-api), has several [architectural restrictions](#architectural-restrictions-and-other-limitations-of-the-old-version) +The old version of [dxFeed C API](https://github.com/dxFeed/dxfeed-c-api), has +several [architectural restrictions](#architectural-restrictions-and-other-limitations-of-the-old-version) that prevent us from providing a state-of-the-art technological solution. ### Benefits of the New Version @@ -95,13 +96,18 @@ ready to answer any questions and help with the transition. #### Sample Mapping -| # | Sample | Old Version | New Version | -|:---:|:----------------------------------------------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------| -| 1 | How to subscribe to `Quote`, `Trade`, `TradeETH`, `Order`, `SpreadOrder`, `AnalyticOrder`, `TimeAndSale` events | [CommandLineSample](https://github.com/dxFeed/dxfeed-c-api/tree/master/samples/CommandLineSample) | EventSample | -| 2 | How to subscribe to `Candle` event | [CandleSample](https://github.com/dxFeed/dxfeed-c-api/tree/master/samples/CandleSample) | *Q2’2023*, please see [TBD](#future-development) section | -| 3 | How to subscribe to `Order`, `SpreadOrder`, `Candle`, `TimeAndSale`, `Greeks`, `Series` snapshots | [SnapshotConsoleSample](https://github.com/dxFeed/dxfeed-c-api/tree/master/samples/SnapshotConsoleSample) | *Q2’2023*, please see [TBD](#future-development) section | -| 4 | How to subscribe to depth of market | [PriceLevelBookSample](https://github.com/dxFeed/dxfeed-c-api/tree/master/samples/PriceLevelBookSample) | *Q2’2023*, please see [TBD](#future-development) section | -| 5 | How to subscribe to order snapshot with incremental updates | [IncSnapshotConsoleSample](https://github.com/dxFeed/dxfeed-c-api/tree/master/samples/IncSnapshotConsoleSample) | *Q2’2023*, please see [TBD](#future-development) section | +| # | Sample | Old Version | New Version | +|:--:|:-------------------------------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------|:--------------------------------------------------------------| +| 1 | How to subscribe to `Quote`, `Trade`, `TradeETH`, `Order`, `SpreadOrder`, `AnalyticOrder`, `TimeAndSale` events | [CommandLineSample](https://github.com/dxFeed/dxfeed-c-api/tree/master/samples/CommandLineSample) | [DxFeedConnect](samples/cpp/DxFeedConnect/src/main.cpp) | +| 2 | How to subscribe to `Candle` event | [CandleSample](https://github.com/dxFeed/dxfeed-c-api/tree/master/samples/CandleSample) | *Q2’2023*, please see [TBD](#future-development) section | +| 3 | How to subscribe to `Order`, `SpreadOrder`, `Candle`, `TimeAndSale`, `Greeks`, `Series` snapshots | [SnapshotConsoleSample](https://github.com/dxFeed/dxfeed-c-api/tree/master/samples/SnapshotConsoleSample) | *Q2’2023*, please see [TBD](#future-development) section | +| 4 | How to subscribe to depth of market | [PriceLevelBookSample](https://github.com/dxFeed/dxfeed-c-api/tree/master/samples/PriceLevelBookSample) | *Q2’2023*, please see [TBD](#future-development) section | +| 5 | How to subscribe to order snapshot with incremental updates | [IncSnapshotConsoleSample](https://github.com/dxFeed/dxfeed-c-api/tree/master/samples/IncSnapshotConsoleSample) | *Q2’2023*, please see [TBD](#future-development) section | +| 6 | How to write tape files | | [WriteTapeFile](samples/cpp/WriteTapeFile/src/main.cpp) | +| 7 | How to convert tape file | | [ConvertTapeFile](samples/cpp/ConvertTapeFile/src/main.cpp) | +| 8 | How to create multiple event listeners and subscribe to `Quote` and `Trade` events (using the "dxfeed.properties" file). | | [DxFeedSample](samples/cpp/DxFeedSample/src/main.cpp) | +| 9 | How to parse files | | [DxFeedFileParser](samples/cpp/DxFeedFileParser/src/main.cpp) | +| 10 | A simple sample that shows how to subscribe to quotes for one instrument and print all received quotes to the console. | | [PrintQuoteEvents](samples/cpp/PrintQuoteEvents/src/main.cpp) | ### Implementation Details @@ -127,17 +133,17 @@ Below is a scheme of this process: ### Architectural Restrictions and Other Limitations of the Old Version -| # | Limitation | How It’s Solved in the New Version | -|:---:|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| 1 | Single-threaded architecture limiting throughput. | Based on the Java API, each subscription object ([DXFeedSubscription](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/api/DXFeedSubscription.html)) *can* run on its own thread. | -| 2 | User code in event callbacks (for example, [dxf_event_listener_t](https://docs.dxfeed.com/c-api/group__event-data-structures-event-subscription-stuff.html#gac8bcb70cd4c8857f286f4be65e9522c6)) is executed in the socket read thread, which can significantly reduce throughput. | Socket processing threads and callback threads are separated. | -| 3 | In event callbacks, one market event type and one data portion always arrive (excluding snapshot subscription), which increases the load on the CPU with a large amount of incoming data. | Event callbacks can receive different market event types, and more than one by batch. | -| 4 | It’s impossible to subscribe to data without getting [regionals](https://kb.dxfeed.com/en/data-model/exchange-codes.html) (if it is available for the market event) or only for a certain regional. | ```subscription->addSymbols({"AAPL"});``` - [composite](https://kb.dxfeed.com/en/data-model/qd-model-of-market-events.html#quote-47603)
```subscription->addSymbols({"AAPL&Q"});``` - [regional](https://kb.dxfeed.com/en/data-model/qd-model-of-market-events.html#quote-x--regional-quote-). | -| 5 | It’s impossible to subscribe to Order event (excluding snapshot subscription) without getting: all [sources](https://kb.dxfeed.com/en/data-model/qd-model-of-market-events.html#order-x), Order by Quote (including regionals), Order by [MarketMaker](https://kb.dxfeed.com/en/data-model/qd-model-of-market-events.html#marketmaker-47603). | ```subscription->addSymbols(IndexedEventSubscriptionSymbol::create("AAPL", OrderSource::NTV));``` - [OrderSource](https://github.com/dxFeed/dxfeed-graal-cxx-api/blob/main/include/dxfeed_graal_native_cpp_api/event/market/OrderSource.hpp) determines which data is being subscribed to. | -| 6 | Data is mixed up when creating two subscriptions (regular and time series) for the same market event type. Both regular and time series data go to both subscriptions. | Each subscription instance receives only the data requested. | -| 7 | Each subsequent request for the same symbol set in a subscription instance overwrites the existing one in another subscription instance. | Subscription instances and the data they receive are independent of each other. | -| 8 | Removing a symbol from one subscription instance caused it to be removed from all others. | Subscription instances and the data they receive are independent of each other. | -| 9 | Incorrect behavior when reading from a file (if a market event in the file hasn’t been subscribed to). Reading from a file always occurs at maximum speed. The supported format is binary only. | ```endpoint->connect("file:tape.txt[format=text]");``` - processing a text file with at it's "real" speed by timestamps
```endpoint->connect("file:tape.bin[format=binary,speed=max]");``` - processing a binary file with max speed. | +| # | Limitation | How It’s Solved in the New Version | +|:-:|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| 1 | Single-threaded architecture limiting throughput. | Based on the Java API, each subscription object ([DXFeedSubscription](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/api/DXFeedSubscription.html)) *can* run on its own thread. | +| 2 | User code in event callbacks (for example, [dxf_event_listener_t](https://docs.dxfeed.com/c-api/group__event-data-structures-event-subscription-stuff.html#gac8bcb70cd4c8857f286f4be65e9522c6)) is executed in the socket read thread, which can significantly reduce throughput. | Socket processing threads and callback threads are separated. | +| 3 | In event callbacks, one market event type and one data portion always arrive (excluding snapshot subscription), which increases the load on the CPU with a large amount of incoming data. | Event callbacks can receive different market event types, and more than one by batch. | +| 4 | It’s impossible to subscribe to data without getting [regionals](https://kb.dxfeed.com/en/data-model/exchange-codes.html) (if it is available for the market event) or only for a certain regional. | ```subscription->addSymbols({"AAPL"});``` - [composite](https://kb.dxfeed.com/en/data-model/qd-model-of-market-events.html#quote-47603)
```subscription->addSymbols({"AAPL&Q"});``` - [regional](https://kb.dxfeed.com/en/data-model/qd-model-of-market-events.html#quote-x--regional-quote-). | +| 5 | It’s impossible to subscribe to Order event (excluding snapshot subscription) without getting: all [sources](https://kb.dxfeed.com/en/data-model/qd-model-of-market-events.html#order-x), Order by Quote (including regionals), Order by [MarketMaker](https://kb.dxfeed.com/en/data-model/qd-model-of-market-events.html#marketmaker-47603). | ```subscription->addSymbols(IndexedEventSubscriptionSymbol::create("AAPL", OrderSource::NTV));``` - [OrderSource](https://github.com/dxFeed/dxfeed-graal-cxx-api/blob/main/include/dxfeed_graal_native_cpp_api/event/market/OrderSource.hpp) determines which data is being subscribed to. | +| 6 | Data is mixed up when creating two subscriptions (regular and time series) for the same market event type. Both regular and time series data go to both subscriptions. | Each subscription instance receives only the data requested. | +| 7 | Each subsequent request for the same symbol set in a subscription instance overwrites the existing one in another subscription instance. | Subscription instances and the data they receive are independent of each other. | +| 8 | Removing a symbol from one subscription instance caused it to be removed from all others. | Subscription instances and the data they receive are independent of each other. | +| 9 | Incorrect behavior when reading from a file (if a market event in the file hasn’t been subscribed to). Reading from a file always occurs at maximum speed. The supported format is binary only. | ```endpoint->connect("file:tape.txt[format=text]");``` - processing a text file with at it's "real" speed by timestamps
```endpoint->connect("file:tape.bin[format=binary,speed=max]");``` - processing a binary file with max speed. | ## Documentation @@ -173,7 +179,8 @@ Xcode 14.3+ Examples of how to install the library can be found [here](https://github.com/dxFeed/dxfeed-graal-cxx-api-samples). -The API is delivered as a dynamic library (`dxFeedGraalCxxApi.so|dll|dylib`) and a static one (`dxFeedGraalCxxApi.a|lib`). +The API is delivered as a dynamic library (`dxFeedGraalCxxApi.so|dll|dylib`) and a static +one (`dxFeedGraalCxxApi.a|lib`). Also, it depends on `DxFeedGraalNativeSdk.so|dll|dylib`. Please place it nearby or available on `PATH`. ## Usage @@ -227,10 +234,11 @@ Quote{AAPL, eventTime=0, time=20221219-223312.000, timeNanoPart=0, sequence=0, b ## Tools - +* [LatencyTest](https://github.com/dxFeed/dxfeed-graal-cxx-api/blob/main/tools/Tools/src/LatencyTest/LatencyTestTool.cpp) + connects to the specified address(es) and calculates latency. ## Samples + * [ConvertTapeFile](https://github.com/dxFeed/dxfeed-graal-cxx-api/blob/main/samples/cpp/ConvertTapeFile/src/main.cpp) demonstrates how to convert one tape file to another tape file with optional intermediate processing or filtering * [DxFeedConnect](https://github.com/dxFeed/dxfeed-graal-cxx-api/blob/main/samples/cpp/DxFeedConnect/src/main.cpp) @@ -254,6 +264,7 @@ be downloaded from [Release](https://github.com/dxFeed/dxfeed-graal-cxx-api/rele and `dxfeed.properties` file * [WriteTapeFile](https://github.com/dxFeed/dxfeed-graal-cxx-api/blob/main/samples/cpp/WriteTapeFile/src/main.cpp) is a simple demonstration of how to write events to a tape file +* [Other samples (external project)](https://github.com/dxFeed/dxfeed-graal-cxx-api-samples) ## Current State @@ -261,211 +272,212 @@ be downloaded from [Release](https://github.com/dxFeed/dxfeed-graal-cxx-api/rele - [Order](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/event/market/Order.html) is a snapshot of the full available market depth for a symbol - - [ ] dxFeed Graal C API - - [x] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [x] dxFeed Graal C++ API - [SpreadOrder](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/event/market/SpreadOrder.html) is a snapshot of the full available market depth for all spreads - - [ ] dxFeed Graal C API - - [x] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [x] dxFeed Graal C++ API - [AnalyticOrder](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/event/market/AnalyticOrder.html) represents an extension of Order introducing analytic information, e.g., adding iceberg-related information to this order - - [ ] dxFeed Graal C API - - [x] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [x] dxFeed Graal C++ API - [Trade](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/event/market/Trade.html) is a snapshot of the price and size of the last trade during regular trading hours and an overall day volume and day turnover - - [ ] dxFeed Graal C API - - [x] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [x] dxFeed Graal C++ API - [TradeETH](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/event/market/TradeETH.html) is a snapshot of the price and size of the last trade during extended trading hours and the extended trading hours day volume and day turnover - - [ ] dxFeed Graal C API - - [x] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [x] dxFeed Graal C++ API - [Candle](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/event/candle/Candle.html) - event with open, high, low, and close prices and other information for a specific period - - [ ] dxFeed Graal C API - - [x] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [x] dxFeed Graal C++ API - [Quote](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/event/market/Quote.html) is a snapshot of the best bid and ask prices and other fields that change with each quote - - [ ] dxFeed Graal C API - - [x] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [x] dxFeed Graal C++ API - [Profile](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/event/market/Profile.html) is a snapshot that contains the security instrument description - - [ ] dxFeed Graal C API - - [x] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [x] dxFeed Graal C++ API - [Summary](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/event/market/Summary.html) is a snapshot of the trading session, including session highs, lows, etc. - - [ ] dxFeed Graal C API - - [x] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [x] dxFeed Graal C++ API - [TimeAndSale](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/event/market/TimeAndSale.html) - represents a trade or other market event with price, like market open/close price, etc. - - [ ] dxFeed Graal C API - - [x] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [x] dxFeed Graal C++ API - [Greeks](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/event/option/Greeks.html) is a snapshot of the option price, Black-Scholes volatility, and Greeks - - [ ] dxFeed Graal C API - - [x] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [x] dxFeed Graal C++ API - [Series](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/event/option/Series.html) is a snapshot of computed values available for all options series for a given underlying symbol based on options market prices - - [ ] dxFeed Graal C API - - [x] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [x] dxFeed Graal C++ API - [TheoPrice](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/event/option/TheoPrice.html) is a snapshot of the - theoretical option price computation that is periodically performed by [dxPrice](http://www.devexperts.com/en/products/price.html) model-free computation - - [ ] dxFeed Graal C API - - [x] dxFeed Graal C++ API + theoretical option price computation that is periodically performed + by [dxPrice](http://www.devexperts.com/en/products/price.html) model-free computation + - [ ] dxFeed Graal C API + - [x] dxFeed Graal C++ API - [Underlying](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/event/option/Underlying.html) is a snapshot of computed values available for an option underlying symbol based on the market’s option prices - - [ ] dxFeed Graal C API - - [x] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [x] dxFeed Graal C++ API - [Configuration](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/event/misc/Configuration.html) is an event with an application-specific attachment - - [ ] dxFeed Graal C API - - [ ] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [ ] dxFeed Graal C++ API - [Message](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/event/misc/Message.html) is an event with an application-specific attachment - - [ ] dxFeed Graal C API - - [ ] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [ ] dxFeed Graal C++ API ### Subscription Symbols - String - - [ ] dxFeed Graal C API - - [x] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [x] dxFeed Graal C++ API - [TimeSeriesSubscriptionSymbol](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/api/osub/TimeSeriesSubscriptionSymbol.html) - represents subscription to time-series events - - [ ] dxFeed Graal C API - - [x] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [x] dxFeed Graal C++ API - [IndexedSubscriptionSymbol](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/api/osub/IndexedEventSubscriptionSymbol.html) - represents subscription to a specific source of indexed events - - [ ] dxFeed Graal C API - - [x] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [x] dxFeed Graal C++ API - [WildcardSymbol.ALL](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/api/osub/WildcardSymbol.html) - represents a *wildcard* subscription to all events of the specific event type - - [ ] dxFeed Graal C API - - [x] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [x] dxFeed Graal C++ API - [CandleSymbol](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/event/candle/CandleSymbol.html) - symbol used with [DXFeedSubscription](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/api/DXFeedSubscription.html) class to subscribe for [Candle](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/event/candle/Candle.html) events - - [ ] dxFeed Graal C API - - [x] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [x] dxFeed Graal C++ API ### Subscriptions & Models - [CreateSubscription](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/api/DXFeedSubscription.html) creates a new subscription for multiple event types *attached* to a specified feed - - [ ] dxFeed Graal C API - - [x] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [x] dxFeed Graal C++ API - [CreateTimeSeriesSubscription](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/api/DXFeedTimeSeriesSubscription.html) extends DXFeedSubscription to conveniently subscribe to time series of events for a set of symbols and event types ([Java API sample](https://github.com/devexperts/QD/blob/master/dxfeed-samples/src/main/java/com/dxfeed/sample/api/DXFeedConnect.java)) - - [ ] dxFeed Graal C API - - [ ] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [ ] dxFeed Graal C++ API - [GetLastEvent](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/api/DXFeed.html#getLastEvent-E-) returns the last event for the specified event instance ([Java API sample](https://github.com/devexperts/QD/blob/master/dxfeed-samples/src/main/java/com/dxfeed/sample/api/DXFeedSample.java)) - - [ ] dxFeed Graal C API - - [ ] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [ ] dxFeed Graal C++ API - [GetLastEvents](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/api/DXFeed.html#getLastEvents-java.util.Collection-) returns the last events for the specified event instances list - - [ ] dxFeed Graal C API - - [ ] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [ ] dxFeed Graal C++ API - [GetLastEventPromise](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/api/DXFeed.html#getLastEventPromise-java.lang.Class-java.lang.Object-) requests the last event for the specified event type and symbol ([Java API sample](https://github.com/devexperts/QD/blob/master/dxfeed-samples/src/main/java/com/dxfeed/sample/console/LastEventsConsole.java)) - - [ ] dxFeed Graal C API - - [ ] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [ ] dxFeed Graal C++ API - [GetLastEventsPromises](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/api/DXFeed.html#getLastEventsPromises-java.lang.Class-java.util.Collection-) requests the last events for the specified event type and symbol collection - - [ ] dxFeed Graal C API - - [ ] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [ ] dxFeed Graal C++ API - [GetLastEventIfSubscribed](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/api/DXFeed.html#getLastEventIfSubscribed-java.lang.Class-java.lang.Object-) returns the last event for the specified event type and symbol if there’s a subscription for it - - [ ] dxFeed Graal C API - - [ ] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [ ] dxFeed Graal C++ API - [GetIndexedEventsPromise](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/api/DXFeed.html#getIndexedEventsPromise-java.lang.Class-java.lang.Object-com.dxfeed.event.IndexedEventSource-) requests an indexed events list for the specified event type, symbol, and source - - [ ] dxFeed Graal C API - - [ ] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [ ] dxFeed Graal C++ API - [GetIndexedEventsIfSubscribed](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/api/DXFeed.html#getIndexedEventsIfSubscribed-java.lang.Class-java.lang.Object-com.dxfeed.event.IndexedEventSource-) requests an indexed events list for the specified event type, symbol, and source if there’s a subscription for it - - [ ] dxFeed Graal C API - - [ ] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [ ] dxFeed Graal C++ API - [GetTimeSeriesPromise](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/api/DXFeed.html#getTimeSeriesPromise-java.lang.Class-java.lang.Object-long-long-) requests time series of events for the specified event type, symbol, and time range ([Java API sample](https://github.com/devexperts/QD/blob/master/dxfeed-samples/src/main/java/com/dxfeed/sample/_simple_/FetchDailyCandles.java)) - - [ ] dxFeed Graal C API - - [ ] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [ ] dxFeed Graal C++ API - [GetTimeSeriesIfSubscribed](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/api/DXFeed.html#getTimeSeriesIfSubscribed-java.lang.Class-java.lang.Object-long-long-) requests time series of events for the specified event type, symbol, and time range if there’s a subscription for it - - [ ] dxFeed Graal C API - - [ ] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [ ] dxFeed Graal C++ API - [TimeSeriesEventModel](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/model/TimeSeriesEventModel.html) - is a model of a list of time series events ([Java API sample](https://github.com/devexperts/QD/blob/master/dxfeed-samples/src/main/java/com/dxfeed/sample/ui/swing/DXFeedCandleChart.java)) - - [ ] dxFeed Graal C API - - [ ] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [ ] dxFeed Graal C++ API - [IndexedEventModel](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/model/IndexedEventModel.html) is a model of a list of indexed events ([Java API sample](https://github.com/devexperts/QD/blob/master/dxfeed-samples/src/main/java/com/dxfeed/sample/ui/swing/DXFeedTimeAndSales.java)) - - [ ] dxFeed Graal C API - - [ ] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [ ] dxFeed Graal C++ API - [OrderBookModel](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/model/market/OrderBookModel.html) is a model of convenient Order Book management ([Java API sample](https://github.com/devexperts/QD/blob/master/dxfeed-samples/src/main/java/com/dxfeed/sample/ui/swing/DXFeedMarketDepth.java)) - - [ ] dxFeed Graal C API - - [ ] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [ ] dxFeed Graal C++ API ### IPF & Schedule - [InstrumentProfile](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/ipf/InstrumentProfile.html) represents basic profile information about a market instrument ([Java API sample](https://github.com/devexperts/QD/blob/master/dxfeed-samples/src/main/java/com/dxfeed/sample/ipf/DXFeedIpfConnect.java)) - - [ ] dxFeed Graal C API - - [ ] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [ ] dxFeed Graal C++ API - [InstrumentProfileCollector](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/ipf/live/InstrumentProfileCollector.html) collects instrument profile updates and provides the live instrument profiles list ([Java API sample](https://github.com/devexperts/QD/blob/master/dxfeed-samples/src/main/java/com/dxfeed/sample/ipf/DXFeedLiveIpfSample.java)) - - [ ] dxFeed Graal C API - - [ ] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [ ] dxFeed Graal C++ API - [Schedule](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/schedule/Schedule.html) provides API to retrieve and explore various exchanges’ trading schedules and different financial instrument classes ([Java API sample](https://github.com/devexperts/QD/blob/master/dxfeed-samples/src/main/java/com/dxfeed/sample/schedule/ScheduleSample.java)) - - [ ] dxFeed Graal C API - - [ ] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [ ] dxFeed Graal C++ API ### Services - [OnDemandService](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/ondemand/OnDemandService.html) provides on-demand historical tick data replay controls ([Java API sample](https://github.com/devexperts/QD/blob/master/dxfeed-samples/src/main/java/com/dxfeed/sample/ondemand/OnDemandSample.java)) - - [ ] dxFeed Graal C API - - [ ] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [ ] dxFeed Graal C++ API ### Endpoint Roles - [FEED](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/api/DXEndpoint.Role.html#FEED) connects to the remote data feed provider and is optimized for real-time or delayed data processing (**this is a default role**) - - [ ] dxFeed Graal C API - - [x] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [x] dxFeed Graal C++ API - [STREAM_FEED](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/api/DXEndpoint.Role.html#STREAM_FEED) is similar to FEED and also connects to the remote data feed provider but is designed for bulk data parsing from files - - [ ] dxFeed Graal C API - - [x] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [x] dxFeed Graal C++ API - [LOCAL_HUB](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/api/DXEndpoint.Role.html#LOCAL_HUB) is a local hub without the ability to establish network connections. Events published via publisher are delivered to local feed only. - - [ ] dxFeed Graal C API - - [x] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [x] dxFeed Graal C++ API - [PUBLISHER](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/api/DXEndpoint.Role.html#PUBLISHER) connects to the remote publisher hub (also known as multiplexor) or creates a publisher on the local host ([Java API sample](https://github.com/devexperts/QD/blob/master/dxfeed-samples/src/main/java/com/dxfeed/sample/_simple_/WriteTapeFile.java)) - - [ ] dxFeed Graal C API - - [x] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [x] dxFeed Graal C++ API - [STREAM_PUBLISHER](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/api/DXEndpoint.Role.html#STREAM_PUBLISHER) is similar to PUBLISHER and also connects to the remote publisher hub, but is designed for bulk data publishing - - [ ] dxFeed Graal C API - - [x] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [x] dxFeed Graal C++ API - [ON_DEMAND_FEED](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/api/DXEndpoint.Role.html#ON_DEMAND_FEED) is similar to FEED, but it is designed to be used with OnDemandService for historical data replay only - - [ ] dxFeed Graal C API - - [ ] dxFeed Graal C++ API + - [ ] dxFeed Graal C API + - [ ] dxFeed Graal C++ API diff --git a/THIRD_PARTY_LICENSES.md b/THIRD_PARTY_LICENSES.md index 8103b81d..58499dbf 100644 --- a/THIRD_PARTY_LICENSES.md +++ b/THIRD_PARTY_LICENSES.md @@ -6,8 +6,10 @@ SPDX-License-Identifier: MIT 3. doctest - https://github.com/doctest/doctest/blob/master/LICENSE.txt SPDX-License-Identifier: MIT -4. range-v3 - https://github.com/ericniebler/range-v3/blob/master/LICENSE.txt +4. range-v3 - https://github.com/ericniebler/range-v3/blob/master/LICENSE.txt SPDX-License-Identifier: BSL-1.0 -5. date - https://github.com/HowardHinnant/date/blob/master/LICENSE.txt +5. date - https://github.com/HowardHinnant/date/blob/master/LICENSE.txt SPDX-License-Identifier: MIT +6. Process - https://github.com/ttldtor/Process/blob/default/LICENSE + SPDX-License-Identifier: BSL-1.0 \ No newline at end of file diff --git a/include/dxfeed_graal_cpp_api/api.hpp b/include/dxfeed_graal_cpp_api/api.hpp index 1d4aa441..0a8b62d6 100644 --- a/include/dxfeed_graal_cpp_api/api.hpp +++ b/include/dxfeed_graal_cpp_api/api.hpp @@ -7,22 +7,26 @@ #include "internal/CEntryPointErrors.hpp" #include "internal/Common.hpp" +#include "internal/Console.hpp" #include "internal/Enum.hpp" #include "internal/EventClassList.hpp" #include "internal/Id.hpp" #include "internal/Isolate.hpp" #include "internal/JavaObjectHandler.hpp" #include "internal/NonCopyable.hpp" +#include "internal/Platform.hpp" #include "internal/RawListWrapper.hpp" +#include "internal/StopWatch.hpp" #include "internal/SymbolList.hpp" +#include "internal/Timer.hpp" #include "internal/context/ApiContext.hpp" #include "internal/managers/DXEndpointManager.hpp" #include "internal/managers/DXFeedSubscriptionManager.hpp" #include "internal/managers/EntityManager.hpp" #include "internal/managers/ErrorHandlingManager.hpp" -#include "internal/utils/StringUtils.hpp" #include "internal/utils/CmdArgsUtils.hpp" #include "internal/utils/EnumUtils.hpp" +#include "internal/utils/StringUtils.hpp" #include "internal/utils/TimeFormat.hpp" #include "internal/utils/debug/Debug.hpp" diff --git a/include/dxfeed_graal_cpp_api/api/DXEndpoint.hpp b/include/dxfeed_graal_cpp_api/api/DXEndpoint.hpp index d39e896b..ed3b59ef 100644 --- a/include/dxfeed_graal_cpp_api/api/DXEndpoint.hpp +++ b/include/dxfeed_graal_cpp_api/api/DXEndpoint.hpp @@ -174,6 +174,12 @@ struct DXFeed; * [Javadoc.](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/api/DXEndpoint.html) */ struct DXFCPP_EXPORT DXEndpoint : SharedEntity { + /// The alias to a type of shared pointer to the DXEndpoint object + using Ptr = std::shared_ptr; + + /// The alias to a type of unique pointer to the DXEndpoint object + using Unique = std::unique_ptr; + /** * `"name"` * diff --git a/include/dxfeed_graal_cpp_api/api/DXFeed.hpp b/include/dxfeed_graal_cpp_api/api/DXFeed.hpp index 72474792..b157d91b 100644 --- a/include/dxfeed_graal_cpp_api/api/DXFeed.hpp +++ b/include/dxfeed_graal_cpp_api/api/DXFeed.hpp @@ -110,6 +110,12 @@ class EventTypeEnum; * This class is thread-safe and can be used concurrently from multiple threads without external synchronization. */ struct DXFCPP_EXPORT DXFeed : SharedEntity { + /// The alias to a type of shared pointer to the DXFeed object + using Ptr = std::shared_ptr; + + /// The alias to a type of unique pointer to the DXFeed object + using Unique = std::unique_ptr; + friend struct DXEndpoint; private: diff --git a/include/dxfeed_graal_cpp_api/api/DXFeedSubscription.hpp b/include/dxfeed_graal_cpp_api/api/DXFeedSubscription.hpp index 54943962..3b1a8f09 100644 --- a/include/dxfeed_graal_cpp_api/api/DXFeedSubscription.hpp +++ b/include/dxfeed_graal_cpp_api/api/DXFeedSubscription.hpp @@ -21,6 +21,7 @@ #include #include +#include namespace dxfcpp { @@ -49,7 +50,7 @@ class DXFCPP_EXPORT DXFeedSubscription : public SharedEntity { template #if __cpp_concepts requires requires(EventTypeIt iter) { - { *iter } -> std::convertible_to; + { *iter } -> dxfcpp::ConvertibleTo; } #endif DXFeedSubscription(EventTypeIt begin, EventTypeIt end) noexcept @@ -106,6 +107,12 @@ class DXFCPP_EXPORT DXFeedSubscription : public SharedEntity { std::vector getDecoratedSymbolsImpl() const noexcept; public: + /// The alias to a type of shared pointer to the DXFeedSubscription object + using Ptr = std::shared_ptr; + + /// The alias to a type of unique pointer to the DXFeedSubscription object + using Unique = std::unique_ptr; + /// std::string toString() const noexcept override; @@ -166,7 +173,7 @@ class DXFCPP_EXPORT DXFeedSubscription : public SharedEntity { template #if __cpp_concepts requires requires(EventTypeIt iter) { - { *iter } -> std::convertible_to; + { *iter } -> dxfcpp::ConvertibleTo; } #endif static std::shared_ptr create(EventTypeIt begin, EventTypeIt end) noexcept { @@ -341,7 +348,7 @@ class DXFCPP_EXPORT DXFeedSubscription : public SharedEntity { std::size_t addEventListener(std::function> &)> &&listener) noexcept #if __cpp_concepts requires std::is_base_of_v && requires { - { EventT::TYPE } -> std::convertible_to; + { EventT::TYPE } -> dxfcpp::ConvertibleTo; } #endif { diff --git a/include/dxfeed_graal_cpp_api/api/DXPublisher.hpp b/include/dxfeed_graal_cpp_api/api/DXPublisher.hpp index 3a283e5d..4288e0ba 100644 --- a/include/dxfeed_graal_cpp_api/api/DXPublisher.hpp +++ b/include/dxfeed_graal_cpp_api/api/DXPublisher.hpp @@ -58,6 +58,12 @@ class ObservableSubscription; * This class is thread-safe and can be used concurrently from multiple threads without external synchronization. */ struct DXFCPP_EXPORT DXPublisher : SharedEntity { + /// The alias to a type of shared pointer to the DXPublisher object + using Ptr = std::shared_ptr; + + /// The alias to a type of unique pointer to the DXPublisher object + using Unique = std::unique_ptr; + friend struct DXEndpoint; private: diff --git a/include/dxfeed_graal_cpp_api/entity/SharedEntity.hpp b/include/dxfeed_graal_cpp_api/entity/SharedEntity.hpp index 226efd40..15087391 100644 --- a/include/dxfeed_graal_cpp_api/entity/SharedEntity.hpp +++ b/include/dxfeed_graal_cpp_api/entity/SharedEntity.hpp @@ -13,7 +13,7 @@ namespace dxfcpp { /// Base abstract "shared entity" class. Has some helpers for dynamic polymorphism struct DXFCPP_EXPORT SharedEntity : public Entity, std::enable_shared_from_this { - /// The simple type synonym for the SharedEntity type + /// The alias to a type of shared pointer to the SharedEntity object. using Ptr = std::shared_ptr; /** diff --git a/include/dxfeed_graal_cpp_api/event/EventType.hpp b/include/dxfeed_graal_cpp_api/event/EventType.hpp index 347c1174..cfa25327 100644 --- a/include/dxfeed_graal_cpp_api/event/EventType.hpp +++ b/include/dxfeed_graal_cpp_api/event/EventType.hpp @@ -27,7 +27,7 @@ namespace dxfcpp { * @see DXFeed */ struct DXFCPP_EXPORT EventType : public SharedEntity { - /// The alias to a type of shared pointer to the EventType's child object. + /// The alias to a type of shared pointer to the EventType object. using Ptr = std::shared_ptr; /** diff --git a/include/dxfeed_graal_cpp_api/event/EventTypeEnum.hpp b/include/dxfeed_graal_cpp_api/event/EventTypeEnum.hpp index b6387750..38725f39 100644 --- a/include/dxfeed_graal_cpp_api/event/EventTypeEnum.hpp +++ b/include/dxfeed_graal_cpp_api/event/EventTypeEnum.hpp @@ -44,6 +44,11 @@ class DXFCPP_EXPORT EventTypeEnum { } public: + using RefSetType = + std::unordered_set, decltype([](auto &&eventTypeRef) { + return static_cast(eventTypeRef.get().getId()); + })>; + static const EventTypeEnum QUOTE; static const EventTypeEnum PROFILE; static const EventTypeEnum SUMMARY; @@ -74,9 +79,11 @@ class DXFCPP_EXPORT EventTypeEnum { static const std::unordered_map> ALL_BY_CLASS_NAME; - explicit EventTypeEnum() noexcept : EventTypeEnum{static_cast(-1), "INVALID", "Invalid", false} { + EventTypeEnum() noexcept : EventTypeEnum{static_cast(-1), "INVALID", "Invalid", false} { } + virtual ~EventTypeEnum() noexcept = default; + /** * @return The dxFeed Graal Native C-API event class id */ diff --git a/include/dxfeed_graal_cpp_api/event/candle/Candle.hpp b/include/dxfeed_graal_cpp_api/event/candle/Candle.hpp index 877d4310..f454dd66 100644 --- a/include/dxfeed_graal_cpp_api/event/candle/Candle.hpp +++ b/include/dxfeed_graal_cpp_api/event/candle/Candle.hpp @@ -160,14 +160,72 @@ class DXFCPP_EXPORT Candle final : public EventTypeWithSymbol, eventSymbol_ = eventSymbol; } + /** + * Changes event's symbol and returns the current candle. + * + * @param eventSymbol The symbol of this event. + * @return The current candle. + */ + Candle &withEventSymbol(const CandleSymbol &eventSymbol) noexcept { + Candle::setEventSymbol(eventSymbol); + + return *this; + } + + /** + * Changes event's symbol and returns a shared pointer to the current candle. + * + * @warning Please do not use this method unless the object was created with `std::shared_ptr(new + * Candle(...))` or `std::make_shared(...)` + * + * @param eventSymbol The symbol of this event. + * @return A shared pointer to the current candle. + */ + Candle::Ptr withEventSymbolShared(const CandleSymbol &eventSymbol) noexcept { + Candle::setEventSymbol(eventSymbol); + + return shared_from_this()->sharedAs(); + } + + /// std::int64_t getEventTime() const noexcept override { return data_.eventTime; } + /// void setEventTime(std::int64_t eventTime) noexcept override { data_.eventTime = eventTime; } + /** + * Changes event's creation time and returns the current candle. + * + * @param eventTime the difference, measured in milliseconds, between the event creation time and + * midnight, January 1, 1970 UTC. + * @return The current candle. + */ + Candle &withEventTime(std::int64_t eventTime) noexcept { + Candle::setEventTime(eventTime); + + return *this; + } + + /** + * Changes event's creation time and returns a shared pointer to the current candle. + * + * @warning Please do not use this method unless the object was created with `std::shared_ptr(new + * Candle(...))` or `std::make_shared(...)` + * + * @param eventTime the difference, measured in milliseconds, between the event creation time and + * midnight, January 1, 1970 UTC. + * @return A shared pointer to the current candle. + */ + Candle::Ptr withEventTimeShared(std::int64_t eventTime) noexcept { + Candle::setEventTime(eventTime); + + return shared_from_this()->sharedAs(); + } + std::string toString() const noexcept override; /// @@ -185,16 +243,103 @@ class DXFCPP_EXPORT Candle final : public EventTypeWithSymbol, data_.eventFlags = eventFlags; } + /** + * Changes transactional event flags and returns the current candle. + * See EventFlag "Event Flags" section. + * + * @param eventFlags transactional event flags. + * @return The current candle. + */ + Candle &withEventFlags(std::int32_t eventFlags) noexcept { + Candle::setEventFlags(eventFlags); + + return *this; + } + + /** + * Changes transactional event flags and returns a shared pointer to the current candle. + * See EventFlag "Event Flags" section. + * + * @warning Please do not use this method unless the object was created with `std::shared_ptr(new + * Candle(...))` or `std::make_shared(...)` + * + * @param eventFlags transactional event flags. + * @return A shared pointer to the current candle. + */ + Candle::Ptr withEventFlagsShared(std::int32_t eventFlags) noexcept { + Candle::setEventFlags(eventFlags); + + return shared_from_this()->sharedAs(); + } + /// void setEventFlags(const EventFlagsMask &eventFlags) noexcept override { data_.eventFlags = static_cast(eventFlags.getMask()); } + /** + * Changes transactional event flags and returns the current candle. + * See EventFlag "Event Flags" section. + * + * @param eventFlags transactional event flags' mask. + * @return The current candle. + */ + Candle &withEventFlags(const EventFlagsMask &eventFlags) noexcept { + Candle::setEventFlags(eventFlags); + + return *this; + } + + /** + * Changes transactional event flags and returns a shared pointer to the current candle. + * See EventFlag "Event Flags" section. + * + * @warning Please do not use this method unless the object was created with `std::shared_ptr(new + * Candle(...))` or `std::make_shared(...)` + * + * @param eventFlags transactional event flags' mask. + * @return A shared pointer to the current candle. + */ + Candle::Ptr withEventFlagsShared(const EventFlagsMask &eventFlags) noexcept { + Candle::setEventFlags(eventFlags); + + return shared_from_this()->sharedAs(); + } + /// void setIndex(std::int64_t index) noexcept override { data_.index = index; } + /** + * Changes unique per-symbol index of this event. + * Returns the current candle. + * + * @param index unique per-symbol index of this candle. + * @return The current candle. + */ + Candle &withIndex(std::int64_t index) noexcept { + Candle::setIndex(index); + + return *this; + } + + /** + * Changes unique per-symbol index of this event. + * Returns a shared pointer to the current candle. + * + * @warning Please do not use this method unless the object was created with `std::shared_ptr(new + * Candle(...))` or `std::make_shared(...)` + * + * @param index unique per-symbol index of this candle. + * @return A shared pointer to the current candle. + */ + Candle::Ptr withIndexShared(std::int64_t index) noexcept { + Candle::setIndex(index); + + return shared_from_this()->sharedAs(); + } + /// std::int64_t getIndex() const noexcept override { return data_.index; @@ -221,6 +366,35 @@ class DXFCPP_EXPORT Candle final : public EventTypeWithSymbol, getSequence()); } + /** + * Changes timestamp of the event in milliseconds. + * Returns the current candle. + * + * @param time timestamp of the event in milliseconds. + * @return The current candle. + */ + Candle &withTime(std::int64_t time) noexcept { + Candle::setTime(time); + + return *this; + } + + /** + * Changes timestamp of the event in milliseconds. + * Returns a shared pointer to the current candle. + * + * @warning Please do not use this method unless the object was created with `std::shared_ptr(new + * Candle(...))` or `std::make_shared(...)` + * + * @param time timestamp of the event in milliseconds. + * @return A shared pointer to the current candle. + */ + Candle::Ptr withTimeShared(std::int64_t time) noexcept { + Candle::setTime(time); + + return shared_from_this()->sharedAs(); + } + /** * Returns the sequence number of this event to distinguish events that have the same @ref Candle::getTime() "time". * This sequence number does not have to be unique and does not need to be sequential. @@ -245,9 +419,40 @@ class DXFCPP_EXPORT Candle final : public EventTypeWithSymbol, data_.index = orOp(andOp(data_.index, ~MAX_SEQUENCE), sequence); } + /** + * Changes @ref Candle::getSequence() "sequence number" of this event. + * Returns the current candle. + * + * @param sequence the sequence. + * @return The current candle. + * @see Candle::getSequence() + */ + Candle &withSequence(std::int32_t sequence) noexcept { + Candle::setSequence(sequence); + + return *this; + } + + /** + * Changes @ref Candle::getSequence() "sequence number" of this event. + * Returns a shared pointer to the current candle. + * + * @warning Please do not use this method unless the object was created with `std::shared_ptr(new + * Candle(...))` or `std::make_shared(...)` + * + * @param sequence the sequence. + * @return A shared pointer to the current candle. + * @see Candle::getSequence() + */ + Candle::Ptr withSequenceShared(std::int32_t sequence) noexcept { + Candle::setSequence(sequence); + + return shared_from_this()->sharedAs(); + } + /** * Returns total number of original trade (or quote) events in this candle. - * @return total number of original trade (or quote) events in this candle. + * @return Total number of original trade (or quote) events in this candle. */ std::int64_t getCount() const noexcept { return data_.count; @@ -255,15 +460,44 @@ class DXFCPP_EXPORT Candle final : public EventTypeWithSymbol, /** * Changes total number of original trade (or quote) events in this candle. - * @param count total number of original trade (or quote) events in this candle. + * @param count Total number of original trade (or quote) events in this candle. */ void setCount(std::int64_t count) noexcept { data_.count = count; } + /** + * Changes total number of original trade (or quote) events in this candle. + * Returns the current candle. + * + * @param count Total number of original trade (or quote) events in this candle. + * @return The current candle. + */ + Candle &withCount(std::int64_t count) noexcept { + Candle::setCount(count); + + return *this; + } + + /** + * Changes total number of original trade (or quote) events in this candle. + * Returns a shared pointer to the current candle. + * + * @warning Please do not use this method unless the object was created with `std::shared_ptr(new + * Candle(...))` or `std::make_shared(...)` + * + * @param count Total number of original trade (or quote) events in this candle. + * @return A shared pointer to the current candle. + */ + Candle::Ptr withCountShared(std::int64_t count) noexcept { + Candle::setCount(count); + + return shared_from_this()->sharedAs(); + } + /** * Returns the first (open) price of this candle. - * @return the first (open) price of this candle. + * @return The first (open) price of this candle. */ double getOpen() const noexcept { return data_.open; @@ -271,15 +505,44 @@ class DXFCPP_EXPORT Candle final : public EventTypeWithSymbol, /** * Changes the first (open) price of this candle. - * @param open the first (open) price of this candle. + * @param open The first (open) price of this candle. */ void setOpen(double open) noexcept { data_.open = open; } + /** + * Changes the first (open) price of this candle. + * Returns the current candle. + * + * @param open The first (open) price of this candle. + * @return The current candle. + */ + Candle &withOpen(double open) noexcept { + Candle::setOpen(open); + + return *this; + } + + /** + * Changes the first (open) price of this candle. + * Returns a shared pointer to the current candle. + * + * @warning Please do not use this method unless the object was created with `std::shared_ptr(new + * Candle(...))` or `std::make_shared(...)` + * + * @param open The first (open) price of this candle. + * @return A shared pointer to the current candle. + */ + Candle::Ptr withOpenShared(double open) noexcept { + Candle::setOpen(open); + + return shared_from_this()->sharedAs(); + } + /** * Returns the maximal (high) price of this candle. - * @return the maximal (high) price of this candle. + * @return The maximal (high) price of this candle. */ double getHigh() const noexcept { return data_.high; @@ -287,15 +550,44 @@ class DXFCPP_EXPORT Candle final : public EventTypeWithSymbol, /** * Changes the maximal (high) price of this candle. - * @param high the maximal (high) price of this candle. + * @param high The maximal (high) price of this candle. */ void setHigh(double high) noexcept { data_.high = high; } + /** + * Changes the maximal (high) price of this candle. + * Returns the current candle. + * + * @param high The maximal (high) price of this candle. + * @return The current candle. + */ + Candle &withHigh(double high) noexcept { + Candle::setHigh(high); + + return *this; + } + + /** + * Changes the maximal (high) price of this candle. + * Returns a shared pointer to the current candle. + * + * @warning Please do not use this method unless the object was created with `std::shared_ptr(new + * Candle(...))` or `std::make_shared(...)` + * + * @param high The maximal (high) price of this candle. + * @return A shared pointer to the current candle. + */ + Candle::Ptr withHighShared(double high) noexcept { + Candle::setHigh(high); + + return shared_from_this()->sharedAs(); + } + /** * Returns the minimal (low) price of this candle. - * @return the minimal (low) price of this candle. + * @return The minimal (low) price of this candle. */ double getLow() const noexcept { return data_.low; @@ -303,15 +595,44 @@ class DXFCPP_EXPORT Candle final : public EventTypeWithSymbol, /** * Changes the minimal (low) price of this candle. - * @param low the minimal (low) price of this candle. + * @param low The minimal (low) price of this candle. */ void setLow(double low) noexcept { data_.low = low; } + /** + * Changes the minimal (low) price of this candle. + * Returns the current candle. + * + * @param low The minimal (low) price of this candle. + * @return The current candle. + */ + Candle &withLow(double low) noexcept { + Candle::setLow(low); + + return *this; + } + + /** + * Changes the minimal (low) price of this candle. + * Returns a shared pointer to the current candle. + * + * @warning Please do not use this method unless the object was created with `std::shared_ptr(new + * Candle(...))` or `std::make_shared(...)` + * + * @param low The minimal (low) price of this candle. + * @return A shared pointer to the current candle. + */ + Candle::Ptr withLowShared(double low) noexcept { + Candle::setLow(low); + + return shared_from_this()->sharedAs(); + } + /** * Returns the last (close) price of this candle. - * @return the last (close) price of this candle. + * @return The last (close) price of this candle. */ double getClose() const noexcept { return data_.close; @@ -319,15 +640,44 @@ class DXFCPP_EXPORT Candle final : public EventTypeWithSymbol, /** * Changes the last (close) price of this candle. - * @param close the last (close) price of this candle. + * @param close The last (close) price of this candle. */ void setClose(double close) noexcept { data_.close = close; } + /** + * Changes the last (close) price of this candle. + * Returns the current candle. + * + * @param close The last (close) price of this candle. + * @return The current candle. + */ + Candle &withClose(double close) noexcept { + Candle::setClose(close); + + return *this; + } + + /** + * Changes the last (close) price of this candle. + * Returns a shared pointer to the current candle. + * + * @warning Please do not use this method unless the object was created with `std::shared_ptr(new + * Candle(...))` or `std::make_shared(...)` + * + * @param close The last (close) price of this candle. + * @return A shared pointer to the current candle. + */ + Candle::Ptr withCloseShared(double close) noexcept { + Candle::setClose(close); + + return shared_from_this()->sharedAs(); + } + /** * Returns total volume in this candle. - * @return total volume in this candle. + * @return Total volume in this candle. */ double getVolume() const noexcept { return data_.volume; @@ -335,17 +685,46 @@ class DXFCPP_EXPORT Candle final : public EventTypeWithSymbol, /** * Changes total volume in this candle. - * @param volume total volume in this candle. + * @param volume Total volume in this candle. */ void setVolume(double volume) noexcept { data_.volume = volume; } + /** + * Changes total volume in this candle. + * Returns the current candle. + * + * @param volume Total volume in this candle. + * @return The current candle. + */ + Candle &withVolume(double volume) noexcept { + Candle::setVolume(volume); + + return *this; + } + + /** + * Changes total volume in this candle. + * Returns a shared pointer to the current candle. + * + * @warning Please do not use this method unless the object was created with `std::shared_ptr(new + * Candle(...))` or `std::make_shared(...)` + * + * @param volume Total volume in this candle. + * @return A shared pointer to the current candle. + */ + Candle::Ptr withVolumeShared(double volume) noexcept { + Candle::setVolume(volume); + + return shared_from_this()->sharedAs(); + } + /** * Returns volume-weighted average price (VWAP) in this candle. * Total turnover in this candle can be computed with getVWAP() * @ref Candle::getVolume() * "getVolume"(). - * @return volume-weighted average price (VWAP) in this candle. + * @return Volume-weighted average price (VWAP) in this candle. */ double getVWAP() const noexcept { return data_.vwap; @@ -353,15 +732,44 @@ class DXFCPP_EXPORT Candle final : public EventTypeWithSymbol, /** * Changes volume-weighted average price (VWAP) in this candle. - * @param vwap volume-weighted average price (VWAP) in this candle. + * @param vwap Volume-weighted average price (VWAP) in this candle. */ void setVWAP(double vwap) noexcept { data_.vwap = vwap; } + /** + * Changes volume-weighted average price (VWAP) in this candle. + * Returns the current candle. + * + * @param vwap Volume-weighted average price (VWAP) in this candle. + * @return The current candle. + */ + Candle &withVWAP(double vwap) noexcept { + Candle::setVWAP(vwap); + + return *this; + } + + /** + * Changes volume-weighted average price (VWAP) in this candle. + * Returns a shared pointer to the current candle. + * + * @warning Please do not use this method unless the object was created with `std::shared_ptr(new + * Candle(...))` or `std::make_shared(...)` + * + * @param vwap Volume-weighted average price (VWAP) in this candle. + * @return A shared pointer to the current candle. + */ + Candle::Ptr withVWAPShared(double vwap) noexcept { + Candle::setVWAP(vwap); + + return shared_from_this()->sharedAs(); + } + /** * Returns bid volume in this candle. - * @return bid volume in this candle. + * @return Bid volume in this candle. */ double getBidVolume() const noexcept { return data_.bidVolume; @@ -369,15 +777,44 @@ class DXFCPP_EXPORT Candle final : public EventTypeWithSymbol, /** * Changes bid volume in this candle. - * @param bidVolume bid volume in this candle. + * @param bidVolume Bid volume in this candle. */ void setBidVolume(double bidVolume) noexcept { data_.bidVolume = bidVolume; } + /** + * Changes bid volume in this candle. + * Returns the current candle. + * + * @param bidVolume Bid volume in this candle. + * @return The current candle. + */ + Candle &withBidVolume(double bidVolume) noexcept { + Candle::setBidVolume(bidVolume); + + return *this; + } + + /** + * Changes bid volume in this candle. + * Returns a shared pointer to the current candle. + * + * @warning Please do not use this method unless the object was created with `std::shared_ptr(new + * Candle(...))` or `std::make_shared(...)` + * + * @param bidVolume Bid volume in this candle. + * @return A shared pointer to the current candle. + */ + Candle::Ptr withBidVolumeShared(double bidVolume) noexcept { + Candle::setBidVolume(bidVolume); + + return shared_from_this()->sharedAs(); + } + /** * Returns ask volume in this candle. - * @return ask volume in this candle. + * @return Ask volume in this candle. */ double getAskVolume() const noexcept { return data_.askVolume; @@ -385,43 +822,130 @@ class DXFCPP_EXPORT Candle final : public EventTypeWithSymbol, /** * Changes ask volume in this candle. - * @param askVolume ask volume in this candle. + * @param askVolume Ask volume in this candle. */ void setAskVolume(double askVolume) noexcept { data_.askVolume = askVolume; } /** - * Returns implied volatility. - * @return implied volatility. + * Changes ask volume in this candle. + * Returns the current candle. + * + * @param askVolume Ask volume in this candle. + * @return The current candle. + */ + Candle &withAskVolume(double askVolume) noexcept { + Candle::setAskVolume(askVolume); + + return *this; + } + + /** + * Changes ask volume in this candle. + * Returns a shared pointer to the current candle. + * + * @warning Please do not use this method unless the object was created with `std::shared_ptr(new + * Candle(...))` or `std::make_shared(...)` + * + * @param askVolume Ask volume in this candle. + * @return A shared pointer to the current candle. + */ + Candle::Ptr withAskVolumeShared(double askVolume) noexcept { + Candle::setAskVolume(askVolume); + + return shared_from_this()->sharedAs(); + } + + /** + * Returns the implied volatility. + * @return The implied volatility. */ double getImpVolatility() const noexcept { return data_.impVolatility; } /** - * Changes implied volatility. - * @param impVolatility implied volatility. + * Changes the implied volatility. + * @param impVolatility The implied volatility. */ void setImpVolatility(double impVolatility) { data_.impVolatility = impVolatility; } /** - * Returns open interest. - * @return open interest. + * Changes implied volatility. + * Returns the current candle. + * + * @param impVolatility The implied volatility. + * @return The current candle. + */ + Candle &withImpVolatility(double impVolatility) noexcept { + Candle::setImpVolatility(impVolatility); + + return *this; + } + + /** + * Changes implied volatility. + * Returns a shared pointer to the current candle. + * + * @warning Please do not use this method unless the object was created with `std::shared_ptr(new + * Candle(...))` or `std::make_shared(...)` + * + * @param impVolatility The implied volatility. + * @return A shared pointer to the current candle. + */ + Candle::Ptr withImpVolatilityShared(double impVolatility) noexcept { + Candle::setImpVolatility(impVolatility); + + return shared_from_this()->sharedAs(); + } + + /** + * Returns the open interest. + * @return The open interest. */ double getOpenInterest() const noexcept { return data_.openInterest; } /** - * Changes open interest. - * @param openInterest open interest. + * Changes the open interest. + * @param openInterest The open interest. */ void setOpenInterest(double openInterest) noexcept { data_.openInterest = openInterest; } + + /** + * Changes the open interest. + * Returns the current candle. + * + * @param openInterest The open interest. + * @return The current candle. + */ + Candle &withOpenInterest(double openInterest) noexcept { + Candle::setOpenInterest(openInterest); + + return *this; + } + + /** + * Changes the open interest. + * Returns a shared pointer to the current candle. + * + * @warning Please do not use this method unless the object was created with `std::shared_ptr(new + * Candle(...))` or `std::make_shared(...)` + * + * @param openInterest The open interest. + * @return A shared pointer to the current candle. + */ + Candle::Ptr withOpenInterestShared(double openInterest) noexcept { + Candle::setOpenInterest(openInterest); + + return shared_from_this()->sharedAs(); + } }; } // namespace dxfcpp \ No newline at end of file diff --git a/include/dxfeed_graal_cpp_api/event/market/AnalyticOrder.hpp b/include/dxfeed_graal_cpp_api/event/market/AnalyticOrder.hpp index 901fec2f..ef5d4908 100644 --- a/include/dxfeed_graal_cpp_api/event/market/AnalyticOrder.hpp +++ b/include/dxfeed_graal_cpp_api/event/market/AnalyticOrder.hpp @@ -112,6 +112,33 @@ class DXFCPP_EXPORT AnalyticOrder final : public Order { explicit AnalyticOrder(std::string eventSymbol) noexcept : Order(std::move(eventSymbol)) { } + /** + * Changes event's symbol and returns the current analytic order. + * + * @param eventSymbol The symbol of this event. + * @return The current analytic order. + */ + AnalyticOrder &withEventSymbol(const std::string &eventSymbol) noexcept override { + MarketEvent::setEventSymbol(eventSymbol); + + return *this; + } + + /** + * Changes event's symbol and returns a shared pointer to the current analytic order. + * + * @warning Please do not use this method unless the object was created with `std::shared_ptr(new + * Order(...))` or `std::make_shared(...)` + * + * @param eventSymbol The symbol of this event. + * @return A shared pointer to the current order. + */ + AnalyticOrder::Ptr withEventSymbolShared(const std::string &eventSymbol) noexcept { + MarketEvent::setEventSymbol(eventSymbol); + + return shared_from_this()->sharedAs(); + } + /** * Returns iceberg peak size of this analytic order. * diff --git a/include/dxfeed_graal_cpp_api/event/market/Order.hpp b/include/dxfeed_graal_cpp_api/event/market/Order.hpp index b1e17ea3..2866a7bd 100644 --- a/include/dxfeed_graal_cpp_api/event/market/Order.hpp +++ b/include/dxfeed_graal_cpp_api/event/market/Order.hpp @@ -149,7 +149,7 @@ class DXFCPP_EXPORT Order : public OrderBase { * @param eventSymbol The symbol of this event. * @return The current order. */ - Order &withEventSymbol(const std::string &eventSymbol) noexcept { + virtual Order &withEventSymbol(const std::string &eventSymbol) noexcept { MarketEvent::setEventSymbol(eventSymbol); return *this; diff --git a/include/dxfeed_graal_cpp_api/internal/CEntryPointErrors.hpp b/include/dxfeed_graal_cpp_api/internal/CEntryPointErrors.hpp index e117d624..75aa9b27 100644 --- a/include/dxfeed_graal_cpp_api/internal/CEntryPointErrors.hpp +++ b/include/dxfeed_graal_cpp_api/internal/CEntryPointErrors.hpp @@ -19,13 +19,95 @@ namespace dxfcpp { +/** + * Enum of the possible error codes returned by internal GraalVM functions + * + * [Graal:CEntryPointErrors](https://github.com/oracle/graal/blob/96a1a66347bd4e5e00ae4e8e79812ebaf8cd5e33/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/function/CEntryPointErrors.java#L43) + */ +enum class CEntryPointErrorsEnum : uint32_t { + /// 0 - No error occurred. + NO_ERROR, + + /// 1 - An unspecified error occurred. + UNSPECIFIED = 0, + + /// 2 - An argument was NULL (nullptr). + NULL_ARGUMENT = 2, + + /// 4 - The specified thread is not attached to the isolate. + UNATTACHED_THREAD = 4, + + /// 5 - The specified isolate is unknown. + UNINITIALIZED_ISOLATE = 5, + + /// 6 - Locating the image file failed. + LOCATE_IMAGE_FAILED = 6, + + /// 7 - Opening the located image file failed. + OPEN_IMAGE_FAILED = 7, + + /// 8 - Mapping the heap from the image file into memory failed. + MAP_HEAP_FAILED = 8, + + /// 801 - Reserving address space for the new isolate failed. + RESERVE_ADDRESS_SPACE_FAILED = 801, + + /// 802 - The image heap does not fit in the available address space. + INSUFFICIENT_ADDRESS_SPACE = 802, + + /// 9 - Setting the protection of the heap memory failed. + PROTECT_HEAP_FAILED = 9, + + /// 10 - The version of the specified isolate parameters is unsupported. + UNSUPPORTED_ISOLATE_PARAMETERS_VERSION = 10, + + /// 11 - Initialization of threading in the isolate failed. + THREADING_INITIALIZATION_FAILED = 11, + + /// 12 - Some exception is not caught. + UNCAUGHT_EXCEPTION = 12, + + /// 13 - Initialization the isolate failed. + ISOLATE_INITIALIZATION_FAILED = 13, + + /// 14 - Opening the located auxiliary image file failed. + OPEN_AUX_IMAGE_FAILED = 14, + + /// 15 - Reading the opened auxiliary image file failed. + READ_AUX_IMAGE_META_FAILED = 15, + + /// 16 - Mapping the auxiliary image file into memory failed. + MAP_AUX_IMAGE_FAILED = 16, + + /// 17 - Insufficient memory for the auxiliary image. + INSUFFICIENT_AUX_IMAGE_MEMORY = 17, + + /// 18 - Auxiliary images are not supported on this platform or edition. + AUX_IMAGE_UNSUPPORTED = 18, + + /// 19 - Releasing the isolate's address space failed. + FREE_ADDRESS_SPACE_FAILED = 19, + + /// 20 - Releasing the isolate's image heap memory failed. + FREE_IMAGE_HEAP_FAILED = 20, + + /// 21 - The auxiliary image was built from a different primary image. + AUX_IMAGE_PRIMARY_IMAGE_MISMATCH = 21, + + /// 22 - The isolate arguments could not be parsed. + ARGUMENT_PARSING_FAILED = 22, + + /// 23 - Current target does not support the following CPU features that are required by the image. + CPU_FEATURE_CHECK_FAILED = 23, +}; + /** * Possible error codes returned by internal GraalVM functions * * [Graal:CEntryPointErrors](https://github.com/oracle/graal/blob/96a1a66347bd4e5e00ae4e8e79812ebaf8cd5e33/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/function/CEntryPointErrors.java#L43) */ struct DXFCPP_EXPORT CEntryPointErrors { - using CodeType = std::uint32_t; + using CodeType = CEntryPointErrorsEnum; private: CodeType code_{}; @@ -36,6 +118,11 @@ struct DXFCPP_EXPORT CEntryPointErrors { : code_{static_cast(code)}, description_{std::move(description)} { } + template + CEntryPointErrors(Code code, std::string description) noexcept + : code_{static_cast(code)}, description_{std::move(description)} { + } + public: /// 0 - No error occurred. static const CEntryPointErrors NO_ERROR; @@ -122,6 +209,14 @@ struct DXFCPP_EXPORT CEntryPointErrors { return UNSPECIFIED; } + template static const CEntryPointErrors &valueOf(Code code) { + if (auto found = ALL.find(static_cast(code)); found != ALL.end()) { + return found->second; + } + + return UNSPECIFIED; + } + /// Returns the code [[nodiscard]] CodeType getCode() const { return code_; diff --git a/include/dxfeed_graal_cpp_api/internal/Common.hpp b/include/dxfeed_graal_cpp_api/internal/Common.hpp index 097fc6ea..baae089e 100644 --- a/include/dxfeed_graal_cpp_api/internal/Common.hpp +++ b/include/dxfeed_graal_cpp_api/internal/Common.hpp @@ -26,6 +26,12 @@ namespace dxfcpp { template concept Integral = std::is_integral_v; +template +concept EnumConcept = std::is_enum_v; + +template +concept ConvertibleTo = std::is_convertible_v && requires { static_cast(std::declval()); }; + #include namespace detail { diff --git a/include/dxfeed_graal_cpp_api/internal/Conf.hpp b/include/dxfeed_graal_cpp_api/internal/Conf.hpp index 9f9ca54c..42b0b450 100644 --- a/include/dxfeed_graal_cpp_api/internal/Conf.hpp +++ b/include/dxfeed_graal_cpp_api/internal/Conf.hpp @@ -7,6 +7,10 @@ # error Please, include windows.h or winnt.h after dxFeed Graal CXX API headers #endif +#ifndef DXFCXX_VERSION +#define DXFCXX_VERSION "0.0.0" +#endif + #ifdef DXFCPP_EXPORT # error DXFCPP_EXPORT was previously defined #endif diff --git a/include/dxfeed_graal_cpp_api/internal/Console.hpp b/include/dxfeed_graal_cpp_api/internal/Console.hpp new file mode 100644 index 00000000..119a29bc --- /dev/null +++ b/include/dxfeed_graal_cpp_api/internal/Console.hpp @@ -0,0 +1,16 @@ +// Copyright (c) 2023 Devexperts LLC. +// SPDX-License-Identifier: MPL-2.0 + +#pragma once + +#include "Common.hpp" +#include "Conf.hpp" + +#include +#include + +namespace dxfcpp { +struct DXFCPP_EXPORT Console final { + static std::pair getSize() noexcept; +}; +} // namespace dxfcpp \ No newline at end of file diff --git a/include/dxfeed_graal_cpp_api/internal/Enum.hpp b/include/dxfeed_graal_cpp_api/internal/Enum.hpp index 16ff607d..6736e21e 100644 --- a/include/dxfeed_graal_cpp_api/internal/Enum.hpp +++ b/include/dxfeed_graal_cpp_api/internal/Enum.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include "Common.hpp" @@ -71,7 +72,7 @@ template struct Enum { * * @return `true` if the elements are the same */ - template > E> friend bool operator==(const E &e1, const E &e2) { + template > E> friend bool operator==(const E &e1, const E &e2) { return e1.getCode() == e2.getCode(); } @@ -85,7 +86,7 @@ template struct Enum { * * @return The output stream */ - template > E> + template > E> friend OStream &operator<<(OStream &os, const E &e) { return os << e.toString(); } diff --git a/include/dxfeed_graal_cpp_api/internal/Handler.hpp b/include/dxfeed_graal_cpp_api/internal/Handler.hpp index a163bb26..bdcf83bb 100644 --- a/include/dxfeed_graal_cpp_api/internal/Handler.hpp +++ b/include/dxfeed_graal_cpp_api/internal/Handler.hpp @@ -79,7 +79,7 @@ template struct Handler final { * * @param mainFuturesSize The size of the circular buffer of futures */ - explicit Handler(std::size_t mainFuturesSize = MAIN_FUTURES_DEFAULT_SIZE) + explicit Handler(std::size_t mainFuturesSize = MAIN_FUTURES_DEFAULT_SIZE) noexcept : mainFuturesCurrentIndex_{0ULL}, mainFuturesSize_{mainFuturesSize} { mainFutures_.reserve(mainFuturesSize); } diff --git a/include/dxfeed_graal_cpp_api/internal/Isolate.hpp b/include/dxfeed_graal_cpp_api/internal/Isolate.hpp index 561c91e3..e7627e97 100644 --- a/include/dxfeed_graal_cpp_api/internal/Isolate.hpp +++ b/include/dxfeed_graal_cpp_api/internal/Isolate.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #ifdef NO_ERROR # undef NO_ERROR @@ -43,9 +44,9 @@ class Isolate final { } } - CEntryPointErrors detach() noexcept; + CEntryPointErrorsEnum detach() noexcept; - CEntryPointErrors detachAllThreadsAndTearDownIsolate() noexcept; + CEntryPointErrorsEnum detachAllThreadsAndTearDownIsolate() noexcept; ~IsolateThread() noexcept { if constexpr (Debugger::traceIsolates) { @@ -89,7 +90,7 @@ class Isolate final { static std::shared_ptr create() noexcept; - CEntryPointErrors attach() noexcept; + CEntryPointErrorsEnum attach() noexcept; GraalIsolateThreadHandle get() noexcept; @@ -113,7 +114,7 @@ class Isolate final { } template - auto runIsolated(F &&f) -> std::variant> { + auto runIsolated(F &&f) -> std::variant> { if constexpr (Debugger::traceIsolates) { Debugger::trace(toString() + "::runIsolated(" + typeid(f).name() + ")"); } @@ -123,10 +124,10 @@ class Isolate final { return std::invoke(std::forward(f), currentThreadHandle); } - if (auto result = attach(); result != CEntryPointErrors::NO_ERROR) { + if (auto result = attach(); result != CEntryPointErrorsEnum::NO_ERROR) { if constexpr (Debugger::traceIsolates) { Debugger::trace(toString() + "::runIsolated(" + typeid(f).name() + - "): result != CEntryPointErrors::NO_ERROR -> " + result.getDescription()); + "): result != CEntryPointErrorsEnum::NO_ERROR -> " + CEntryPointErrors::valueOf(result).getDescription()); } return result; @@ -137,13 +138,13 @@ class Isolate final { template #if __cpp_concepts - requires std::convertible_to> + requires dxfcpp::ConvertibleTo> #endif auto runIsolatedOrElse(F &&f, R defaultValue) { return std::visit( [defaultValue = std::move(defaultValue)](T &&arg) -> std::invoke_result_t { - if constexpr (std::is_same_v) { + if constexpr (std::is_same_v) { return defaultValue; } else { return arg; @@ -158,9 +159,7 @@ class Isolate final { } } - std::string toString() const { - std::lock_guard lock(mtx_); - + std::string toString() const noexcept { return std::string("Isolate{") + dxfcpp::toString(bit_cast(handle_)) + ", main = " + mainIsolateThread_.toString() + ", current = " + currentIsolateThread_.toString() + "}"; } @@ -172,7 +171,7 @@ template auto runIsolated(F &&f) { template #if __cpp_concepts - requires std::convertible_to> + requires dxfcpp::ConvertibleTo> #endif auto runIsolatedOrElse(F &&f, R defaultValue) { return Isolate::getInstance()->runIsolatedOrElse(std::forward(f), std::move(defaultValue)); diff --git a/include/dxfeed_graal_cpp_api/internal/Platform.hpp b/include/dxfeed_graal_cpp_api/internal/Platform.hpp new file mode 100644 index 00000000..3fcaf45d --- /dev/null +++ b/include/dxfeed_graal_cpp_api/internal/Platform.hpp @@ -0,0 +1,20 @@ +// Copyright (c) 2023 Devexperts LLC. +// SPDX-License-Identifier: MPL-2.0 + +#pragma once + +#include "Common.hpp" +#include "Conf.hpp" + +#include +#include + +namespace dxfcpp { +struct DXFCPP_EXPORT Platform final { + static std::size_t getLogicalCoresCount() noexcept { + return std::thread::hardware_concurrency(); + } + + static std::string getPlatformInfo() noexcept; +}; +} // namespace dxfcpp \ No newline at end of file diff --git a/include/dxfeed_graal_cpp_api/internal/StopWatch.hpp b/include/dxfeed_graal_cpp_api/internal/StopWatch.hpp new file mode 100644 index 00000000..7f6764da --- /dev/null +++ b/include/dxfeed_graal_cpp_api/internal/StopWatch.hpp @@ -0,0 +1,87 @@ +// Copyright (c) 2023 Devexperts LLC. +// SPDX-License-Identifier: MPL-2.0 + +#pragma once + +#include "Conf.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "Common.hpp" + +namespace dxfcpp { + +struct StopWatch final { + private: + mutable std::mutex mutex_{}; + std::chrono::milliseconds elapsed_{}; + std::chrono::steady_clock::time_point startTimeStamp_{}; + std::atomic isRunning_{}; + + public: + StopWatch() noexcept { + reset(); + } + + void start() noexcept { + if (!isRunning_) { + std::lock_guard lock{mutex_}; + startTimeStamp_ = std::chrono::steady_clock::now(); + isRunning_ = true; + } + } + + void stop() noexcept { + if (isRunning_) { + auto endTimestamp = std::chrono::steady_clock::now(); + + std::lock_guard lock{mutex_}; + auto elapsedThisPeriod = endTimestamp - startTimeStamp_; + elapsed_ += std::chrono::duration_cast(elapsedThisPeriod); + isRunning_ = false; + } + } + + void reset() noexcept { + std::lock_guard lock{mutex_}; + + elapsed_ = std::chrono::milliseconds::zero(); + isRunning_ = false; + startTimeStamp_ = std::chrono::steady_clock::time_point{}; + } + + void restart() noexcept { + std::lock_guard lock{mutex_}; + + elapsed_ = std::chrono::milliseconds::zero(); + isRunning_ = true; + startTimeStamp_ = std::chrono::steady_clock::now(); + } + + bool isRunning() const noexcept { + return isRunning_; + } + + std::chrono::milliseconds elapsed() const noexcept { + std::lock_guard lock{mutex_}; + + auto elapsed = elapsed_; + + if (isRunning_) { + auto currentTimestamp = std::chrono::steady_clock::now(); + auto elapsedUntilNow = currentTimestamp - startTimeStamp_; + + elapsed += std::chrono::duration_cast(elapsedUntilNow); + } + + return elapsed; + } +}; + +} // namespace dxfcpp \ No newline at end of file diff --git a/include/dxfeed_graal_cpp_api/internal/Timer.hpp b/include/dxfeed_graal_cpp_api/internal/Timer.hpp new file mode 100644 index 00000000..07d8fc15 --- /dev/null +++ b/include/dxfeed_graal_cpp_api/internal/Timer.hpp @@ -0,0 +1,54 @@ +// Copyright (c) 2023 Devexperts LLC. +// SPDX-License-Identifier: MPL-2.0 + +#pragma once + +#include "Conf.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "Common.hpp" + +namespace dxfcpp { + +struct Timer { + private: + std::unique_ptr> future_; + + std::atomic isRunning_{}; + + Timer() noexcept = default; + + public: + template + static std::shared_ptr schedule(F &&f, Delay &&delay, Period &&period) noexcept { + auto t = std::shared_ptr(new Timer()); + + t->future_ = std::make_unique>(std::async( + std::launch::async, + [self = t](auto &&f, auto &&d, auto &&p) { + self->isRunning_ = true; + std::this_thread::sleep_for(d); + + while (self->isRunning_) { + f(); + std::this_thread::sleep_for(p); + } + }, + std::forward(f), std::forward(delay), std::forward(period))); + + return t; + } + + void stop() { + isRunning_ = false; + } +}; + +} // namespace dxfcpp \ No newline at end of file diff --git a/include/dxfeed_graal_cpp_api/internal/utils/CmdArgsUtils.hpp b/include/dxfeed_graal_cpp_api/internal/utils/CmdArgsUtils.hpp index 3a5aeb3c..77742290 100644 --- a/include/dxfeed_graal_cpp_api/internal/utils/CmdArgsUtils.hpp +++ b/include/dxfeed_graal_cpp_api/internal/utils/CmdArgsUtils.hpp @@ -5,39 +5,244 @@ #include "../Conf.hpp" +#include "../../event/EventTypeEnum.hpp" + #include #include +#include #include -#include #include +#include namespace dxfcpp { -class EventTypeEnum; - struct DXFCPP_EXPORT CmdArgsUtils final { /** * Parses an input string and returns a set of symbols. * + * The symbol "all" or "*" can be passed. This will add WildcardSymbol (all symbols). + * + * @param symbols The coma-separated list of symbols. + * @return The created set of parsed symbols + */ + static std::unordered_set parseSymbols(const std::string &symbols) noexcept; + + /** + * Parses an input string and returns a set of symbols. + * + * The symbol "all" or "*" can be passed. This will add WildcardSymbol (all symbols).. + * + * @param symbols The coma-separated list of symbols. + * @return The created set of parsed symbols + */ + static std::unordered_set parseSymbols(const char *symbols) noexcept { + return parseSymbols(std::string(symbols)); + } + + /** + * Parses an input string and returns a set of symbols. + * + * The symbol "all" or "*" can be passed. This will add WildcardSymbol (all symbols). + * + * @param symbols The coma-separated list of symbols. + * @return The created set of parsed symbols + */ + static std::unordered_set parseSymbols(std::string_view symbols) noexcept { + return parseSymbols(symbols.data()); + } + + /** + * Parses an input string and returns a set of symbols. + * + * The symbol "all" or "*" can be passed. This will add WildcardSymbol (all symbols). + * + * @param symbols The coma-separated list of symbols. + * @return The created set of parsed symbols + */ + static std::unordered_set parseSymbols(std::optional symbols) noexcept { + if (symbols.has_value()) { + return parseSymbols(symbols.value()); + } + + return {}; + } + + /** + * Parses an input string and returns a set of candle symbols. + * + * @param symbols The coma-separated list of symbols. + * @return The created set of parsed candle symbols + */ + static std::unordered_set parseCandleSymbols(const std::string &symbols) noexcept; + + /** + * Parses an input string and returns a set of candle symbols. + * * @param symbols The coma-separated list of symbols. - * @return The created set of parsed symbols. + * @return The created set of parsed candle symbols */ - static std::unordered_set parseSymbols(const std::string &symbols); + static std::unordered_set parseCandleSymbols(const char *symbols) noexcept { + return parseCandleSymbols(std::string(symbols)); + } + + /** + * Parses an input string and returns a set of candle symbols. + * + * @param symbols The coma-separated list of symbols. + * @return The created set of parsed candle symbols + */ + static std::unordered_set parseCandleSymbols(std::string_view symbols) noexcept { + return parseCandleSymbols(symbols.data()); + } + + /** + * Parses an input string and returns a set of candle symbols. + * + * @param symbols The coma-separated list of symbols. + * @return The created set of parsed candle symbols + */ + static std::unordered_set parseCandleSymbols(std::optional symbols) noexcept { + if (symbols.has_value()) { + return parseCandleSymbols(symbols.value()); + } + + return {}; + } /** * Parses an input string and returns a set of event types. * + * "all" or "*" will be converted to all types. + * * @param types The comma-separated list of event types. * @return The created set of parsed types. */ - static std::unordered_set> parseTypes(const std::string &types); + static std::unordered_set> + parseTypes(const std::string &types) noexcept; + + /** + * Parses an input string and returns a set of event types. + * + * "all" or "*" will be converted to all types. + * + * @param types The comma-separated list of event types. + * @return The created set of parsed types. + */ + static std::unordered_set> parseTypes(const char *types) noexcept { + return parseTypes(std::string(types)); + } + + /** + * Parses an input string and returns a set of event types. + * + * "all" or "*" will be converted to all types. + * + * @param types The comma-separated list of event types. + * @return The created set of parsed types. + */ + static std::unordered_set> parseTypes(std::string_view types) noexcept { + return parseTypes(types.data()); + } + + /** + * Parses an input string and returns a set of event types. + * + * "all" or "*" will be converted to all types. + * + * @param types The comma-separated list of event types. + * @return The created set of parsed types. + */ + static std::unordered_set> + parseTypes(std::optional types) noexcept; + + /** + * Parses the input collection of strings and returns a collection of key-value properties. + * The input strings should look like comma-separated: "key=value". + * + * @param properties The input comma-separated key-value pairs. + * @return The collection of key-value properties. + */ + static std::unordered_map parseProperties(const std::string &properties) noexcept; + + /** + * Parses the input collection of strings and returns a collection of key-value properties. + * The input strings should look like comma-separated: "key=value". + * + * @param properties The input comma-separated key-value pairs. + * @return The collection of key-value properties. + */ + static std::unordered_map parseProperties(const char *properties) noexcept { + return parseProperties(std::string(properties)); + } + + /** + * Parses the input collection of strings and returns a collection of key-value properties. + * The input strings should look like comma-separated: "key=value". + * + * @param properties The input comma-separated key-value pairs. + * @return The collection of key-value properties. + */ + static std::unordered_map parseProperties(std::string_view properties) noexcept { + return parseProperties(properties.data()); + } + + /** + * Parses the input collection of strings and returns a collection of key-value properties. + * The input strings should look like comma-separated: "key=value". + * + * @param properties The input comma-separated key-value pairs. + * @return The collection of key-value properties. + */ + static std::unordered_map + parseProperties(std::optional properties) noexcept { + if (properties.has_value()) { + return parseProperties(properties.value()); + } + + return {}; + } + + /** + * Parses Date+Time string and converts to timestamp + * + * @param string Date+Time string + * @return UTC timestamp + */ + static std::int64_t parseDateTime(const std::string &string) noexcept; + + /** + * Parses Date+Time string and converts to timestamp + * + * @param string Date+Time string + * @return UTC timestamp + */ + static std::int64_t parseDateTime(const char *string) noexcept { + return parseDateTime(std::string(string)); + } + + /** + * Parses Date+Time string and converts to timestamp + * + * @param string Date+Time string + * @return UTC timestamp + */ + static std::int64_t parseDateTime(std::string_view string) noexcept { + return parseDateTime(string.data()); + } + + /** + * Parses Date+Time string and converts to timestamp + * + * @param string Date+Time string + * @return UTC timestamp + */ + static std::int64_t parseDateTime(std::optional string) noexcept { + if (string.has_value()) { + return parseDateTime(string.value()); + } + + return -1; + } }; -/** - * Parses Date+Time string and converts to timestamp - * - * @param string Date+Time string - * @return UTC timestamp - */ -DXFCPP_EXPORT std::int64_t parseDateTime(const std::string& string); } // namespace dxfcpp \ No newline at end of file diff --git a/include/dxfeed_graal_cpp_api/internal/utils/StringUtils.hpp b/include/dxfeed_graal_cpp_api/internal/utils/StringUtils.hpp index 06a2e9e8..d78bb01d 100644 --- a/include/dxfeed_graal_cpp_api/internal/utils/StringUtils.hpp +++ b/include/dxfeed_graal_cpp_api/internal/utils/StringUtils.hpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -27,11 +28,17 @@ DXFCPP_EXPORT std::string toString(void *ptr); DXFCPP_EXPORT std::string toString(double d); +template std::string toStringAny(T &&t); + +template std::string toString(const std::pair &p) { + return "{" + toStringAny(p.first) + ", " + toStringAny(p.second) + "}"; +} + template std::string toStringAny(T &&t) { if constexpr (requires { t.toString(); }) { return t.toString(); - } else if constexpr (requires { *t.toString(); }) { - return *t.toString(); + } else if constexpr (requires { t->toString(); }) { + return t->toString(); } else if constexpr (requires { toString(t); }) { return toString(t); } else if constexpr (requires { std::to_string(t); }) { @@ -88,9 +95,7 @@ DXFCPP_EXPORT std::string formatTimeStampWithMillisWithTimeZone(std::int64_t tim DXFCPP_EXPORT char *createCString(const std::string &s) noexcept; template -#if __cpp_concepts requires requires { std::is_same_v getName())>, std::string>; } -#endif std::string namesToString(It begin, It end) { std::string result{"["}; @@ -101,11 +106,23 @@ std::string namesToString(It begin, It end) { return result + "]"; } +template + requires requires { std::is_same_v get().getName())>, std::string>; } +std::string namesToString(It begin, It end) { + std::string result{"["}; + + for (auto it = begin; it != end; it++) { + result += String::EMPTY + "'" + it->get().getName() + "'" + (std::next(it) == end ? "" : ", "); + } + + return result + "]"; +} + template std::string elementsToString(It begin, It end) { std::string result{"["}; for (auto it = begin; it != end; it++) { - result += String::EMPTY + "'" + toStringAny(*it) + "'" + (std::next(it) == end ? "" : ", "); + result += String::EMPTY + toStringAny(*it) + (std::next(it) == end ? "" : ", "); } return result + "]"; @@ -138,7 +155,7 @@ inline bool equals(const Range1 &first, const Range2 &second, Predicate cmp) { auto firstIt = std::begin(first); auto secondIt = std::begin(second); - for (; firstIt != std::end(first) && secondIt != std::end(second); ++firstIt, secondIt++) { + for (; firstIt != std::end(first) && secondIt != std::end(second); ++firstIt, ++secondIt) { if (!cmp(*firstIt, *secondIt)) { return false; } @@ -147,11 +164,23 @@ inline bool equals(const Range1 &first, const Range2 &second, Predicate cmp) { return (secondIt == std::end(second)) && (firstIt == std::end(first)); } -template -inline bool iEquals(const Range1 &first, const Range2 &second, const std::locale &locale = std::locale()) { +DXFCPP_EXPORT inline bool iEquals(const std::string &first, const std::string &second) noexcept { + const std::locale &locale = std::locale(); + return equals(first, second, detail::IsIEqual(locale)); } +DXFCPP_EXPORT inline std::size_t icHash(const std::string &s) noexcept { + const std::locale &locale = std::locale(); + std::string result{}; + + for (auto c : s) { + result += std::tolower(c, locale); + } + + return std::hash()(result); +} + DXFCPP_EXPORT std::string trimStr(const std::string &s) noexcept; } // namespace dxfcpp \ No newline at end of file diff --git a/include/dxfeed_graal_cpp_api/internal/utils/TimeFormat.hpp b/include/dxfeed_graal_cpp_api/internal/utils/TimeFormat.hpp index d620d189..a31075e1 100644 --- a/include/dxfeed_graal_cpp_api/internal/utils/TimeFormat.hpp +++ b/include/dxfeed_graal_cpp_api/internal/utils/TimeFormat.hpp @@ -16,6 +16,7 @@ #include #include #include +#include namespace dxfcpp { @@ -28,9 +29,9 @@ struct DXFCPP_EXPORT TimeFormat { template requires requires(Entry e) { { Entry{typename Entry::KeyType{}, typename Entry::ValueType{}} }; - { Entry::STUB } -> std::convertible_to; - { e.getKey() } -> std::convertible_to; - { e.getValue() } -> std::convertible_to; + { Entry::STUB } -> dxfcpp::ConvertibleTo; + { e.getKey() } -> dxfcpp::ConvertibleTo; + { e.getValue() } -> dxfcpp::ConvertibleTo; } class Cache { std::vector data_{maxSize, Entry::STUB}; diff --git a/include/dxfeed_graal_cpp_api/symbols/SymbolWrapper.hpp b/include/dxfeed_graal_cpp_api/symbols/SymbolWrapper.hpp index 41b26e1e..d41e1831 100644 --- a/include/dxfeed_graal_cpp_api/symbols/SymbolWrapper.hpp +++ b/include/dxfeed_graal_cpp_api/symbols/SymbolWrapper.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "../api/osub/IndexedEventSubscriptionSymbol.hpp" #include "../api/osub/TimeSeriesSubscriptionSymbol.hpp" @@ -44,7 +45,7 @@ concept ConvertibleToSymbolWrapperCollection = } && ( requires(Collection c) { - { *std::begin(c) } -> std::convertible_to; + { *std::begin(c) } -> dxfcpp::ConvertibleTo; } || requires(Collection c) { { *std::begin(c) } -> ConvertibleToSymbolWrapper; diff --git a/samples/cpp/DxFeedConnect/src/main.cpp b/samples/cpp/DxFeedConnect/src/main.cpp index 333e262b..9e74911e 100644 --- a/samples/cpp/DxFeedConnect/src/main.cpp +++ b/samples/cpp/DxFeedConnect/src/main.cpp @@ -63,7 +63,7 @@ int main(int argc, char *argv[]) { auto time = -1ULL; if (argc >= 5) { - time = parseDateTime(argv[4]); + time = CmdArgsUtils::parseDateTime(argv[4]); } // Create an endpoint and connect to specified address. diff --git a/samples/cpp/PrintQuoteEvents/src/main.cpp b/samples/cpp/PrintQuoteEvents/src/main.cpp index f8cf0e9a..87912ae5 100644 --- a/samples/cpp/PrintQuoteEvents/src/main.cpp +++ b/samples/cpp/PrintQuoteEvents/src/main.cpp @@ -6,7 +6,7 @@ #include /* - * A simple sample that shows how to subscribe to quotes for one instruments, + * A simple sample that shows how to subscribe to quotes for one instrument, * and print all received quotes to the console. * Use default DXFeed instance for that data feed address is defined by "dxfeed.properties" file. * The properties file is copied to the build output directory from the project directory. diff --git a/src/api.cpp b/src/api.cpp index 30d590d3..2b03d063 100644 --- a/src/api.cpp +++ b/src/api.cpp @@ -14,45 +14,65 @@ namespace dxfcpp { -const CEntryPointErrors CEntryPointErrors::NO_ERROR(0, "No error occurred."); -const CEntryPointErrors CEntryPointErrors::UNSPECIFIED(1, "An unspecified error occurred."); -const CEntryPointErrors CEntryPointErrors::NULL_ARGUMENT(2, "An argument was NULL (nullptr)."); -const CEntryPointErrors CEntryPointErrors::UNATTACHED_THREAD(4, "The specified thread is not attached to the isolate."); -const CEntryPointErrors CEntryPointErrors::UNINITIALIZED_ISOLATE(5, "The specified isolate is unknown."); -const CEntryPointErrors CEntryPointErrors::LOCATE_IMAGE_FAILED(6, "Locating the image file failed."); -const CEntryPointErrors CEntryPointErrors::OPEN_IMAGE_FAILED(7, "Opening the located image file failed."); -const CEntryPointErrors CEntryPointErrors::MAP_HEAP_FAILED(8, +const CEntryPointErrors CEntryPointErrors::NO_ERROR(CEntryPointErrorsEnum::NO_ERROR, "No error occurred."); +const CEntryPointErrors CEntryPointErrors::UNSPECIFIED(CEntryPointErrorsEnum::UNSPECIFIED, + "An unspecified error occurred."); +const CEntryPointErrors CEntryPointErrors::NULL_ARGUMENT(CEntryPointErrorsEnum::NULL_ARGUMENT, + "An argument was NULL (nullptr)."); +const CEntryPointErrors CEntryPointErrors::UNATTACHED_THREAD(CEntryPointErrorsEnum::UNATTACHED_THREAD, + "The specified thread is not attached to the isolate."); +const CEntryPointErrors CEntryPointErrors::UNINITIALIZED_ISOLATE(CEntryPointErrorsEnum::UNINITIALIZED_ISOLATE, + "The specified isolate is unknown."); +const CEntryPointErrors CEntryPointErrors::LOCATE_IMAGE_FAILED(CEntryPointErrorsEnum::LOCATE_IMAGE_FAILED, + "Locating the image file failed."); +const CEntryPointErrors CEntryPointErrors::OPEN_IMAGE_FAILED(CEntryPointErrorsEnum::OPEN_IMAGE_FAILED, + "Opening the located image file failed."); +const CEntryPointErrors CEntryPointErrors::MAP_HEAP_FAILED(CEntryPointErrorsEnum::MAP_HEAP_FAILED, "Mapping the heap from the image file into memory failed."); const CEntryPointErrors - CEntryPointErrors::RESERVE_ADDRESS_SPACE_FAILED(801, "Reserving address space for the new isolate failed."); + CEntryPointErrors::RESERVE_ADDRESS_SPACE_FAILED(CEntryPointErrorsEnum::RESERVE_ADDRESS_SPACE_FAILED, + "Reserving address space for the new isolate failed."); const CEntryPointErrors - CEntryPointErrors::INSUFFICIENT_ADDRESS_SPACE(802, "The image heap does not fit in the available address space."); -const CEntryPointErrors CEntryPointErrors::PROTECT_HEAP_FAILED(9, "Setting the protection of the heap memory failed."); + CEntryPointErrors::INSUFFICIENT_ADDRESS_SPACE(CEntryPointErrorsEnum::INSUFFICIENT_ADDRESS_SPACE, + "The image heap does not fit in the available address space."); +const CEntryPointErrors CEntryPointErrors::PROTECT_HEAP_FAILED(CEntryPointErrorsEnum::PROTECT_HEAP_FAILED, + "Setting the protection of the heap memory failed."); const CEntryPointErrors CEntryPointErrors::UNSUPPORTED_ISOLATE_PARAMETERS_VERSION( - 10, "The version of the specified isolate parameters is unsupported."); + CEntryPointErrorsEnum::UNSUPPORTED_ISOLATE_PARAMETERS_VERSION, + "The version of the specified isolate parameters is unsupported."); const CEntryPointErrors - CEntryPointErrors::THREADING_INITIALIZATION_FAILED(11, "Initialization of threading in the isolate failed."); -const CEntryPointErrors CEntryPointErrors::UNCAUGHT_EXCEPTION(12, "Some exception is not caught."); -const CEntryPointErrors CEntryPointErrors::ISOLATE_INITIALIZATION_FAILED(13, "Initialization the isolate failed."); -const CEntryPointErrors CEntryPointErrors::OPEN_AUX_IMAGE_FAILED(14, + CEntryPointErrors::THREADING_INITIALIZATION_FAILED(CEntryPointErrorsEnum::THREADING_INITIALIZATION_FAILED, + "Initialization of threading in the isolate failed."); +const CEntryPointErrors CEntryPointErrors::UNCAUGHT_EXCEPTION(CEntryPointErrorsEnum::UNCAUGHT_EXCEPTION, + "Some exception is not caught."); +const CEntryPointErrors + CEntryPointErrors::ISOLATE_INITIALIZATION_FAILED(CEntryPointErrorsEnum::ISOLATE_INITIALIZATION_FAILED, + "Initialization the isolate failed."); +const CEntryPointErrors CEntryPointErrors::OPEN_AUX_IMAGE_FAILED(CEntryPointErrorsEnum::OPEN_AUX_IMAGE_FAILED, "Opening the located auxiliary image file failed."); const CEntryPointErrors - CEntryPointErrors::READ_AUX_IMAGE_META_FAILED(15, "Reading the opened auxiliary image file failed."); -const CEntryPointErrors CEntryPointErrors::MAP_AUX_IMAGE_FAILED(16, + CEntryPointErrors::READ_AUX_IMAGE_META_FAILED(CEntryPointErrorsEnum::READ_AUX_IMAGE_META_FAILED, + "Reading the opened auxiliary image file failed."); +const CEntryPointErrors CEntryPointErrors::MAP_AUX_IMAGE_FAILED(CEntryPointErrorsEnum::MAP_AUX_IMAGE_FAILED, "Mapping the auxiliary image file into memory failed."); const CEntryPointErrors - CEntryPointErrors::INSUFFICIENT_AUX_IMAGE_MEMORY(17, "Insufficient memory for the auxiliary image."); + CEntryPointErrors::INSUFFICIENT_AUX_IMAGE_MEMORY(CEntryPointErrorsEnum::INSUFFICIENT_AUX_IMAGE_MEMORY, + "Insufficient memory for the auxiliary image."); const CEntryPointErrors - CEntryPointErrors::AUX_IMAGE_UNSUPPORTED(18, "Auxiliary images are not supported on this platform or edition."); -const CEntryPointErrors CEntryPointErrors::FREE_ADDRESS_SPACE_FAILED(19, + CEntryPointErrors::AUX_IMAGE_UNSUPPORTED(CEntryPointErrorsEnum::AUX_IMAGE_UNSUPPORTED, + "Auxiliary images are not supported on this platform or edition."); +const CEntryPointErrors CEntryPointErrors::FREE_ADDRESS_SPACE_FAILED(CEntryPointErrorsEnum::FREE_ADDRESS_SPACE_FAILED, "Releasing the isolate's address space failed."); -const CEntryPointErrors CEntryPointErrors::FREE_IMAGE_HEAP_FAILED(20, +const CEntryPointErrors CEntryPointErrors::FREE_IMAGE_HEAP_FAILED(CEntryPointErrorsEnum::FREE_IMAGE_HEAP_FAILED, "Releasing the isolate's image heap memory failed."); const CEntryPointErrors CEntryPointErrors::AUX_IMAGE_PRIMARY_IMAGE_MISMATCH( - 21, "The auxiliary image was built from a different primary image."); -const CEntryPointErrors CEntryPointErrors::ARGUMENT_PARSING_FAILED(22, "The isolate arguments could not be parsed."); + CEntryPointErrorsEnum::AUX_IMAGE_PRIMARY_IMAGE_MISMATCH, + "The auxiliary image was built from a different primary image."); +const CEntryPointErrors CEntryPointErrors::ARGUMENT_PARSING_FAILED(CEntryPointErrorsEnum::ARGUMENT_PARSING_FAILED, + "The isolate arguments could not be parsed."); const CEntryPointErrors CEntryPointErrors::CPU_FEATURE_CHECK_FAILED( - 23, "Current target does not support the following CPU features that are required by the image."); + CEntryPointErrorsEnum::CPU_FEATURE_CHECK_FAILED, + "Current target does not support the following CPU features that are required by the image."); const std::unordered_map> CEntryPointErrors::ALL{ diff --git a/src/internal/Console.cpp b/src/internal/Console.cpp new file mode 100644 index 00000000..dcc6b449 --- /dev/null +++ b/src/internal/Console.cpp @@ -0,0 +1,74 @@ +// Copyright (c) 2023 Devexperts LLC. +// SPDX-License-Identifier: MPL-2.0 + +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) +# include + +namespace dxfcpp { +std::pair Console::getSize() noexcept { + CONSOLE_SCREEN_BUFFER_INFO consoleScreenBufferInfo{}; + std::size_t columns{}, rows{}; + + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &consoleScreenBufferInfo); + columns = + static_cast(consoleScreenBufferInfo.srWindow.Right - consoleScreenBufferInfo.srWindow.Left + 1); + rows = static_cast(consoleScreenBufferInfo.srWindow.Bottom - consoleScreenBufferInfo.srWindow.Top + 1); + + return {columns, rows}; +} +} // namespace dxfcpp + +#elif defined(__CYGWIN__) || defined(__ANDROID__) || defined(__linux__) || defined(__APPLE__) +# include // open(), O_EVTONLY, O_NONBLOCK +# include // ioctl() +# include // close() + +namespace dxfcpp { +std::pair Console::getSize() noexcept { +# if defined(__APPLE__) + auto ttyFileDescriptor = open("/dev/tty", O_EVTONLY | O_NONBLOCK); +# else + auto ttyFileDescriptor = open("/dev/tty", O_RDWR); +# endif + + if (ttyFileDescriptor == -1) { + return {80, 25}; + } + + winsize winSize{}; + + auto result = ioctl(ttyFileDescriptor, TIOCGWINSZ, &winSize); + + close(ttyFileDescriptor); + + if (result == -1) { + return {80, 25}; + } + + return {static_cast(winSize.ws_col), static_cast(winSize.ws_row)}; +} +} // namespace dxfcpp + +#else + +namespace dxfcpp { +std::pair Console::getSize() noexcept { + return {80, 25}; +} +} // namespace dxfcpp + +#endif diff --git a/src/internal/Isolate.cpp b/src/internal/Isolate.cpp index bfdd4330..fed4db67 100644 --- a/src/internal/Isolate.cpp +++ b/src/internal/Isolate.cpp @@ -7,7 +7,7 @@ namespace dxfcpp { -CEntryPointErrors Isolate::IsolateThread::detach() noexcept { +CEntryPointErrorsEnum Isolate::IsolateThread::detach() noexcept { if constexpr (Debugger::traceIsolates) { Debugger::trace(toString() + "::detach()"); } @@ -18,14 +18,15 @@ CEntryPointErrors Isolate::IsolateThread::detach() noexcept { Debugger::trace(toString() + "::detach(): !handle => Not attached"); } - return CEntryPointErrors::NO_ERROR; + return CEntryPointErrorsEnum::NO_ERROR; } - auto result = CEntryPointErrors::valueOf(graal_detach_thread(dxfcpp::bit_cast(handle))); + auto result = + static_cast(graal_detach_thread(dxfcpp::bit_cast(handle))); - if (result == CEntryPointErrors::NO_ERROR) { + if (result == CEntryPointErrorsEnum::NO_ERROR) { if constexpr (Debugger::traceIsolates) { - Debugger::trace(toString() + "::detach(): result == CEntryPointErrors::NO_ERROR => Detached"); + Debugger::trace(toString() + "::detach(): result == CEntryPointErrorsEnum::NO_ERROR => Detached"); } handle = nullptr; @@ -34,7 +35,7 @@ CEntryPointErrors Isolate::IsolateThread::detach() noexcept { return result; } -CEntryPointErrors Isolate::IsolateThread::detachAllThreadsAndTearDownIsolate() noexcept { +CEntryPointErrorsEnum Isolate::IsolateThread::detachAllThreadsAndTearDownIsolate() noexcept { if constexpr (Debugger::traceIsolates) { Debugger::trace(toString() + "::detachAllThreadsAndTearDownIsolate()"); } @@ -44,16 +45,17 @@ CEntryPointErrors Isolate::IsolateThread::detachAllThreadsAndTearDownIsolate() n Debugger::trace(toString() + "::detachAllThreadsAndTearDownIsolate(): !handle => Not attached"); } - return CEntryPointErrors::NO_ERROR; + return CEntryPointErrorsEnum::NO_ERROR; } - auto result = CEntryPointErrors::valueOf( + auto result = static_cast( graal_detach_all_threads_and_tear_down_isolate(dxfcpp::bit_cast(handle))); - if (result == CEntryPointErrors::NO_ERROR) { + if (result == CEntryPointErrorsEnum::NO_ERROR) { if constexpr (Debugger::traceIsolates) { - Debugger::trace(toString() + "::detachAllThreadsAndTearDownIsolate(): CEntryPointErrors::NO_ERROR => All " - "threads have been detached. The isolate has been teared down."); + Debugger::trace(toString() + + "::detachAllThreadsAndTearDownIsolate(): CEntryPointErrorsEnum::NO_ERROR => All " + "threads have been detached. The isolate has been teared down."); } handle = nullptr; @@ -70,8 +72,8 @@ std::shared_ptr Isolate::create() noexcept { graal_isolate_t *graalIsolateHandle{}; graal_isolatethread_t *graalIsolateThreadHandle{}; - if (CEntryPointErrors::valueOf(graal_create_isolate(nullptr, &graalIsolateHandle, &graalIsolateThreadHandle)) == - CEntryPointErrors::NO_ERROR) { + if (static_cast(graal_create_isolate( + nullptr, &graalIsolateHandle, &graalIsolateThreadHandle)) == CEntryPointErrorsEnum::NO_ERROR) { auto result = std::shared_ptr{new (std::nothrow) Isolate{graalIsolateHandle, graalIsolateThreadHandle}}; @@ -90,7 +92,7 @@ std::shared_ptr Isolate::create() noexcept { return nullptr; } -CEntryPointErrors Isolate::attach() noexcept { +CEntryPointErrorsEnum Isolate::attach() noexcept { if constexpr (Debugger::traceIsolates) { Debugger::trace(toString() + "::attach()"); } @@ -103,13 +105,14 @@ CEntryPointErrors Isolate::attach() noexcept { graal_isolatethread_t *newIsolateThreadHandle{}; - if (auto result = CEntryPointErrors::valueOf( + if (auto result = static_cast( graal_attach_thread(dxfcpp::bit_cast(handle_), &newIsolateThreadHandle)); - result != CEntryPointErrors::NO_ERROR) { + result != CEntryPointErrorsEnum::NO_ERROR) { if constexpr (Debugger::traceIsolates) { - Debugger::trace(toString() + "::attach(): result != CEntryPointErrors::NO_ERROR [" + - std::to_string(result.getCode()) + "] " + result.getDescription()); + Debugger::trace(toString() + "::attach(): result != CEntryPointErrorsEnum::NO_ERROR [" + + std::to_string(static_cast>(result)) + + "] " + CEntryPointErrors::valueOf(result).getDescription()); } return result; @@ -127,7 +130,7 @@ CEntryPointErrors Isolate::attach() noexcept { } } - return CEntryPointErrors::NO_ERROR; + return CEntryPointErrorsEnum::NO_ERROR; } GraalIsolateThreadHandle Isolate::get() noexcept { diff --git a/src/internal/Platform.cpp b/src/internal/Platform.cpp new file mode 100644 index 00000000..e6cade7a --- /dev/null +++ b/src/internal/Platform.cpp @@ -0,0 +1,115 @@ +// Copyright (c) 2023 Devexperts LLC. +// SPDX-License-Identifier: MPL-2.0 + +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#if defined(__APPLE__) +# include +#endif + +namespace dxfcpp { + +std::string getOSName() noexcept { +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || defined(__CYGWIN__) + return "Windows"; +#elif defined(__ANDROID__) + return "Android"; +#elif defined(__linux__) + return "Linux"; +#elif defined(__sun__) + return "Solaris"; +#elif defined(__APPLE__) +# if defined(TARGET_IPHONE_SIMULATOR) + return "iOS|tvOS|watchOS-Simulator"; +# elif defined(TARGET_OS_MACCATALYST) + return "MacOS-Catalyst"; +# elif defined(TARGET_OS_IPHONE) + return "iOS|tvOS|watchOS"; +# elif defined(TARGET_OS_MAC) + return "MacOS"; +# else +# return "UnknownApple"; +# endif +#elif defined(__FreeBSD__) + return "FreeBSD"; +#elif defined(__DragonFly__) + return "DragonFly"; +#elif defined(__NetBSD__) + return "NetBSD"; +#elif defined(__OpenBSD__) + return "OpenBSD"; +#elif defined(BSD) + return "BSD-like"; +#elif defined(__hpux) + return "HP-UX"; +#elif defined(_AIX) || defined(__TOS_AIX__) + return "AIX"; +#elif defined(__HAIKU__) + return "Haiku"; +#elif defined(__QNX__) + return "QNX"; +#elif defined(unix) || defined(__unix__) || defined(__unix) + return "UNIX-like"; +#elif defined(_POSIX_VERSION) + return "POSIX-like"; +#else + return "Unknown"; +#endif +} + +std::string getOSArchitecture() noexcept { +#if defined(__alpha__) || defined(__alpha) + return "alpha"; +#elif defined(__arm64) || defined(__aarch64__) + return "aarch64"; +#elif defined(__arm__) || defined(__arm) || defined(_M_ARM) + return "arm"; +#elif defined(__amd64__) || defined(__x86_64__) || defined(__x86_64__) || defined(__x86_64) || defined(_M_AMD64) + return "x86_64"; +#elif defined(i386) || defined(__i386__) || defined(_M_IX86) + return "x86_32"; +#elif defined(__ia64__) || defined(__ia64) || defined(_M_IA64) || defined(__itanium__) + return "itanium"; +#elif defined(__mips__) || defined(mips) || defined(__mips) || defined(__MIPS__) + return "mips"; +#elif defined(__powerpc64__) || defined(__ppc64__) || defined(__PPC64__) || defined(_ARCH_PPC64) + return "ppc64"; +#elif defined(__powerpc) || defined(__powerpc__) || defined(__POWERPC__) || defined(__PPC__) + return "powerpc"; +#elif defined(__sparc__) || defined(__sparc) + return "spark"; +#elif defined(__370__) || defined(__s390__) || defined(__zarch__) || defined(__SYSC_ZARCH__) + return "systemz"; +#elif defined(__hppa__) || defined(__HPPA__) || defined(__hppa) + return "hppa"; +#elif defined(__convex__) + return "convex"; +#elif defined(__e2k__) + return "e2k"; +#elif defined(__riscv) + return "riscv"; +#elif defined(_TMS320C2XX) || defined(_TMS320C5X) || defined(_TMS320C6X) + return "tms320"; +#else + return "unknown"; +#endif +} + +std::string Platform::getPlatformInfo() noexcept { + const auto cores = getLogicalCoresCount(); + + return fmt::format("{} {}({} core{})", getOSName(), getOSArchitecture(), cores, (cores > 1 ? "s" : "")); +} + +} // namespace dxfcpp \ No newline at end of file diff --git a/src/internal/utils/CmdArgsUtils.cpp b/src/internal/utils/CmdArgsUtils.cpp index 63760101..ea5a839c 100644 --- a/src/internal/utils/CmdArgsUtils.cpp +++ b/src/internal/utils/CmdArgsUtils.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -38,22 +39,38 @@ auto getUtcOffset() { return tzDiff * 60 * 1000; } -auto splitAndTrim = [](auto &&symbols) noexcept { - return symbols | ranges::views::split(',') | ranges::views::filter([](const auto &s) { - return !s.empty(); - }) | - ranges::views::transform([](auto &&s) { - return s | ranges::to(); - }) | - ranges::views::transform([](const std::string &s) { - return trimStr(s); - }) | - ranges::views::filter([](const auto &s) { - return !s.empty(); - }); +decltype(ranges::views::filter([](const auto &s) { + return !s.empty(); +})) filterNonEmpty{}; + +decltype(ranges::views::transform([](auto &&s) { + return s | ranges::to(); +})) transformToString{}; + +decltype(ranges::views::transform([](const std::string &s) { + return trimStr(s); +})) trim{}; + +auto splitAndTrim = [](auto &&symbols, char sep = ',') noexcept { + return symbols | ranges::views::split(sep) | filterNonEmpty | transformToString | trim; }; -std::unordered_set CmdArgsUtils::parseSymbols(const std::string &symbols) { +decltype(ranges::views::transform([](auto &&s) { + auto locale = std::locale{}; + + return s | ranges::views::transform([&locale](auto c) { + return std::toupper(c, locale); + }) | + ranges::to(); +})) transformToUpper{}; + +std::unordered_set parseStringSymbols(const std::string &symbols) noexcept { + auto trimmedSymbols = trimStr(symbols); + + if (trimmedSymbols.empty()) { + return {}; + } + std::unordered_set result{}; auto addSymbol = [&result](auto &&symbol) { @@ -107,15 +124,45 @@ std::unordered_set CmdArgsUtils::parseSymbols(const std::string &sy return result; } -std::unordered_set> CmdArgsUtils::parseTypes(const std::string &types) { - auto split = splitAndTrim(types); - auto allByName = split | ranges::views::transform([](auto &&s) { - return s | ranges::views::transform([](unsigned char c) { - return std::toupper(c); - }) | - ranges::to(); - }) | - ranges::views::filter([](const auto &s) { +std::unordered_set CmdArgsUtils::parseSymbols(const std::string &symbols) noexcept { + auto trimmedSymbols = trimStr(symbols); + + if (trimmedSymbols.empty()) { + return {}; + } + + auto parsed = parseStringSymbols(trimmedSymbols); + + if (parsed.contains("*") || parsed.contains("all") || parsed.contains("All") || parsed.contains("ALL")) { + return {WildcardSymbol::ALL}; + } + + return parsed | ranges::to>(); +} + +std::unordered_set CmdArgsUtils::parseCandleSymbols(const std::string &symbols) noexcept { + auto parsed = parseStringSymbols(symbols); + + return parsed | ranges::views::transform([](auto &&s) { + return CandleSymbol::valueOf(s); + }) | + ranges::to>(); +} + +std::unordered_set> +CmdArgsUtils::parseTypes(const std::string &types) noexcept { + auto trimmedTypes = trimStr(types); + + if (trimmedTypes.empty()) { + return {}; + } + + if (trimmedTypes == "*" || trimmedTypes == "all" || trimmedTypes == "All" || trimmedTypes == "ALL") { + return EventTypeEnum::ALL | ranges::to>>(); + } + + auto split = splitAndTrim(trimmedTypes) | filterNonEmpty; + auto allByName = split | transformToUpper | ranges::views::filter([](const auto &s) { return EventTypeEnum::ALL_BY_NAME.contains(s); }) | ranges::views::transform([](const auto &s) { @@ -132,7 +179,35 @@ std::unordered_set> CmdArgsUtils::pa ranges::to>>(); } -std::int64_t parseDateTime(const std::string &string) { +std::unordered_set> +CmdArgsUtils::parseTypes(std::optional types) noexcept { + if (types.has_value()) { + return parseTypes(types.value()); + } + + return std::unordered_set>{}; +} + +std::unordered_map CmdArgsUtils::parseProperties(const std::string &properties) noexcept { + auto p = trimStr(properties); + + if (p.empty()) { + return {}; + } + + return splitAndTrim(p) | filterNonEmpty | ranges::views::transform([](const std::string &kv) { + return splitAndTrim(kv, '=') | ranges::to>(); + }) | + ranges::views::filter([](auto &&kv) { + return kv.size() == 2; + }) | + ranges::views::transform([](auto &&kv) { + return std::make_pair(kv[0], kv[1]); + }) | + ranges::to>(); +} + +std::int64_t CmdArgsUtils::parseDateTime(const std::string &string) noexcept { const static auto dateFormats = { "%Y%m%d", "%Y-%m-%d", // "%Y\\%m\\%d", @@ -203,7 +278,6 @@ std::int64_t parseDateTime(const std::string &string) { std::chrono::from_stream(in, format.c_str(), tp, &abbrev, &offset); #endif - if (in.fail()) { return -1LL; } diff --git a/src/system/System.cpp b/src/system/System.cpp index ed8f56de..f6c029d5 100644 --- a/src/system/System.cpp +++ b/src/system/System.cpp @@ -23,9 +23,9 @@ bool System::setProperty(const std::string &key, const std::string &value) { auto result = runIsolatedOrElse( [key = key, value = value](auto threadHandle) { - return CEntryPointErrors::valueOf(dxfg_system_set_property( + return static_cast(dxfg_system_set_property( dxfcpp::bit_cast(threadHandle), key.c_str(), value.c_str())) == - CEntryPointErrors::NO_ERROR; + CEntryPointErrorsEnum::NO_ERROR; }, false); diff --git a/third_party/date-3.0.1/CMakeLists.txt b/third_party/date-3.0.1/CMakeLists.txt index 012512ae..eb528d54 100644 --- a/third_party/date-3.0.1/CMakeLists.txt +++ b/third_party/date-3.0.1/CMakeLists.txt @@ -36,6 +36,8 @@ option( DISABLE_STRING_VIEW "Disable string view" OFF ) option( COMPILE_WITH_C_LOCALE "define ONLY_C_LOCALE=1" OFF ) option( BUILD_TZ_LIB "build/install of TZ library" OFF ) +option(DATE_INSTALL "Prepare install" OFF) + if( ENABLE_DATE_TESTING AND NOT BUILD_TZ_LIB ) message(WARNING "Testing requested, bug BUILD_TZ_LIB not ON - forcing the latter") set (BUILD_TZ_LIB ON CACHE BOOL "required for testing" FORCE) @@ -161,46 +163,49 @@ endif( ) #[===================================================================[ installation #]===================================================================] -set( version_config "${CMAKE_CURRENT_BINARY_DIR}/dateConfigVersion.cmake" ) - -include( CMakePackageConfigHelpers ) -write_basic_package_version_file( "${version_config}" - VERSION ${PROJECT_VERSION} - COMPATIBILITY SameMajorVersion ) - -install( TARGETS date - EXPORT dateConfig - PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/date ) -export( TARGETS date NAMESPACE date:: FILE dateTargets.cmake ) -if (CMAKE_VERSION VERSION_LESS 3.15) - install( - FILES include/date/date.h - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/date ) -endif () +if (DATE_INSTALL) -if( BUILD_TZ_LIB ) - install( TARGETS date-tz - EXPORT dateConfig - PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/date - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) # This is for Windows - export( TARGETS date-tz NAMESPACE date:: APPEND FILE dateTargets.cmake ) -endif( ) + set( version_config "${CMAKE_CURRENT_BINARY_DIR}/dateConfigVersion.cmake" ) -if( WIN32 AND NOT CYGWIN) - set( CONFIG_LOC CMake ) -else( ) - set( CONFIG_LOC "${CMAKE_INSTALL_LIBDIR}/cmake/date" ) -endif( ) -install( EXPORT dateConfig - FILE dateTargets.cmake - NAMESPACE date:: - DESTINATION ${CONFIG_LOC} ) -install ( - FILES cmake/dateConfig.cmake "${version_config}" - DESTINATION ${CONFIG_LOC}) + include( CMakePackageConfigHelpers ) + write_basic_package_version_file( "${version_config}" + VERSION ${PROJECT_VERSION} + COMPATIBILITY SameMajorVersion ) + install( TARGETS date + EXPORT dateConfig + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/date ) + export( TARGETS date NAMESPACE date:: FILE dateTargets.cmake ) + if (CMAKE_VERSION VERSION_LESS 3.15) + install( + FILES include/date/date.h + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/date ) + endif () + + if( BUILD_TZ_LIB ) + install( TARGETS date-tz + EXPORT dateConfig + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/date + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) # This is for Windows + export( TARGETS date-tz NAMESPACE date:: APPEND FILE dateTargets.cmake ) + endif( ) + + if( WIN32 AND NOT CYGWIN) + set( CONFIG_LOC CMake ) + else( ) + set( CONFIG_LOC "${CMAKE_INSTALL_LIBDIR}/cmake/date" ) + endif( ) + install( EXPORT dateConfig + FILE dateTargets.cmake + NAMESPACE date:: + DESTINATION ${CONFIG_LOC} ) + install ( + FILES cmake/dateConfig.cmake "${version_config}" + DESTINATION ${CONFIG_LOC}) + +endif (DATE_INSTALL) #[===================================================================[ testing #]===================================================================] diff --git a/tools/Tools/CMakeLists.txt b/tools/Tools/CMakeLists.txt index 7e006d06..1e09976e 100644 --- a/tools/Tools/CMakeLists.txt +++ b/tools/Tools/CMakeLists.txt @@ -18,9 +18,44 @@ set(CMAKE_C_STANDARD 11) set(CXX_EXTENSIONS OFF) set(C_EXTENSIONS OFF) -add_executable(${PROJECT_NAME} src/main.cpp) -target_include_directories(${PROJECT_NAME} PRIVATE ../../include) -target_link_libraries(${PROJECT_NAME} PRIVATE dxfcxx::static) +if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + set(CMAKE_MACOSX_RPATH ON) + set(CMAKE_SKIP_BUILD_RPATH ON) + set(CMAKE_BUILD_WITH_INSTALL_RPATH ON) + set(CMAKE_INSTALL_RPATH_USE_LINK_PATH OFF) + set(CMAKE_BUILD_RPATH_USE_ORIGIN ON) + set(CMAKE_INSTALL_RPATH "@loader_path/../${CMAKE_INSTALL_LIBDIR};@loader_path;@executable_path;@executable_path/../Frameworks") +elseif (UNIX) + set(CMAKE_SKIP_BUILD_RPATH ON) + set(CMAKE_BUILD_WITH_INSTALL_RPATH ON) + set(CMAKE_INSTALL_RPATH_USE_LINK_PATH OFF) + set(CMAKE_BUILD_RPATH_USE_ORIGIN ON) + set(CMAKE_INSTALL_RPATH "$ORIGIN/../${CMAKE_INSTALL_LIBDIR}:$ORIGIN/../lib64:$ORIGIN/../lib:$ORIGIN") +endif () + +add_executable(${PROJECT_NAME} + src/main.cpp + src/Tools.cpp + src/Args/Args.cpp +) + +target_include_directories(${PROJECT_NAME} PRIVATE ../../third_party/range-v3-0.12/include) +target_link_libraries(${PROJECT_NAME} PRIVATE dxfcxx::static process::process fmt::fmt-header-only) add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $) + +#target_link_libraries(${PROJECT_NAME} PRIVATE dxfcxx process::process fmt::fmt-header-only) +#target_compile_definitions(${PROJECT_NAME} PRIVATE DXFCPP_USE_DLLS) +#add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different +# $ +# $ +# $) + +if (DXFCXX_INSTALL AND DXFCXX_INSTALL_TOOLS) + install(TARGETS ${PROJECT_NAME}) + + if (WIN32) + install(FILES $ DESTINATION ${CMAKE_INSTALL_BINDIR}) + endif () +endif () \ No newline at end of file diff --git a/tools/Tools/src/Args/Args.cpp b/tools/Tools/src/Args/Args.cpp new file mode 100644 index 00000000..7ba4d8da --- /dev/null +++ b/tools/Tools/src/Args/Args.cpp @@ -0,0 +1,106 @@ +// Copyright (c) 2023 Devexperts LLC. +// SPDX-License-Identifier: MPL-2.0 + +#include "Args.hpp" + +namespace dxfcpp::tools { + +const std::string AddressArg::NAME{"address"}; +const std::size_t AddressArg::POSITION{0}; +const std::string AddressArg::HELP_TEXT{R"( +The address(es) to connect to retrieve data (see "Help address"). +For Token-Based Authorization, use the following format: "
:[login=entitle:]". +)"}; + +const std::string TypesArg::NAME{"types"}; +const std::size_t TypesArg::POSITION{1}; +const std::string TypesArg::HELP_TEXT{R"( +Comma-separated list of dxfeed event types (e.g. Quote, TimeAndSale). +Use "all" for all available event types. +)"}; + +const std::string SymbolsArg::NAME{"symbols"}; +const std::size_t SymbolsArg::POSITION{2}; +const std::string SymbolsArg::HELP_TEXT{R"( +Comma-separated list of symbol names to get events for (e.g. "IBM, AAPL, MSFT"). +Use "all" for wildcard subscription. +The "dxfeed.wildcard.enable" property must be set to true to enable wildcard subscription. +)"}; + +const std::string PropertiesArg::NAME{"properties"}; +const std::string PropertiesArg::SHORT_NAME{"p"}; +const std::string PropertiesArg::LONG_NAME{"properties"}; +const std::string PropertiesArg::HELP_TEXT{R"( +Comma-separated list of properties (key-value pair separated by an equals sign). +)"}; + +const std::string FromTimeArg::NAME{"fromTime"}; +const std::string FromTimeArg::SHORT_NAME{"f"}; +const std::string FromTimeArg::LONG_NAME{"from-time"}; +const std::string FromTimeArg::HELP_TEXT{R"( +From-time for the history subscription in standard formats (see "Help Time Format"). +)"}; + +const std::string SourceArg::NAME{"source"}; +const std::string SourceArg::SHORT_NAME{"s"}; +const std::string SourceArg::LONG_NAME{"source"}; +const std::string SourceArg::HELP_TEXT{R"( +Order source for the indexed subscription (e.g. NTV, ntv). +)"}; + +const std::string TapeArg::NAME{"tape"}; +const std::string TapeArg::SHORT_NAME{"t"}; +const std::string TapeArg::LONG_NAME{"tape"}; +const std::string TapeArg::HELP_TEXT{R"( +Tape all incoming data into the specified file (see "Help Tape"). +)"}; + +const std::string QuiteArg::NAME{"quite"}; +const std::string QuiteArg::SHORT_NAME{"q"}; +const std::string QuiteArg::LONG_NAME{"quite"}; +const std::string QuiteArg::HELP_TEXT{R"( +Be quiet, event printing is disabled. +)"}; + +const std::string ForceStreamArg::NAME{"forceStream"}; +const std::string ForceStreamArg::SHORT_NAME{}; +const std::string ForceStreamArg::LONG_NAME{"force-stream"}; +const std::string ForceStreamArg::HELP_TEXT{R"( +Enforces a streaming contract for subscription. The StreamFeed role is used instead of Feed. +)"}; + +const std::string CPUUsageByCoreArg::NAME{"cpuUsageByCore"}; +const std::string CPUUsageByCoreArg::SHORT_NAME{}; +const std::string CPUUsageByCoreArg::LONG_NAME{"cpu-usage-by-core"}; +const std::string CPUUsageByCoreArg::HELP_TEXT{R"( +Show CPU usage by core (where 1 core = 100%). +)"}; + +const std::string DetachListenerArg::NAME{"tape"}; +const std::string DetachListenerArg::SHORT_NAME{}; +const std::string DetachListenerArg::LONG_NAME{"detach-listener"}; +const std::string DetachListenerArg::HELP_TEXT{R"( +Don't attach a listener. Used for debugging purposes. +)"}; + +const std::string IntervalArg::NAME{"interval"}; +const std::string IntervalArg::SHORT_NAME{}; +const std::string IntervalArg::LONG_NAME{"interval"}; +const std::string IntervalArg::HELP_TEXT{R"( +Measurement interval in seconds. +)"}; + +const std::string HelpArg::NAME{"help"}; +const std::string HelpArg::SHORT_NAME{"h"}; +const std::string HelpArg::LONG_NAME{"help"}; +const std::string HelpArg::HELP_TEXT{R"( +Display this help screen. +)"}; + +const std::string ArticleArgRequired::NAME{"article"}; +const std::size_t ArticleArgRequired::POSITION{0}; +const std::string ArticleArgRequired::HELP_TEXT{R"( +Help article to show. +)"}; + +} \ No newline at end of file diff --git a/tools/Tools/src/Args/Args.hpp b/tools/Tools/src/Args/Args.hpp new file mode 100644 index 00000000..9fef2147 --- /dev/null +++ b/tools/Tools/src/Args/Args.hpp @@ -0,0 +1,635 @@ +// Copyright (c) 2023 Devexperts LLC. +// SPDX-License-Identifier: MPL-2.0 + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +namespace dxfcpp::tools { + +decltype(ranges::views::filter([](const auto &s) { + return !s.empty(); +})) filterNonEmpty{}; + +decltype(ranges::views::transform([](auto &&s) { + return s | ranges::to(); +})) transformToString{}; + +decltype(ranges::views::transform([](const std::string &s) { + return trimStr(s); +})) trim{}; + +inline auto splitAndTrim = [](const std::string &s, char sep = ',') noexcept { + return s | ranges::views::split(sep) | transformToString | trim; +}; + +template struct ParseResult { + static const std::size_t LAST_INDEX{static_cast(-1)}; + R result{}; + std::string errorString{}; + bool isError{}; + bool isHelp{}; + std::size_t nextIndex{LAST_INDEX}; + + static ParseResult ok(R &&r, std::size_t nextIndex = LAST_INDEX) noexcept { + return {r, "", false, false, nextIndex}; + } + + static ParseResult ok(const R &r, std::size_t nextIndex = LAST_INDEX) noexcept { + return {r, "", false, false, nextIndex}; + } + + static ParseResult error(const std::string &errorString) noexcept { + return {{}, errorString, true, false, LAST_INDEX}; + } + + static ParseResult help() noexcept { + return {{}, {}, false, true, LAST_INDEX}; + } +}; + +struct Arg { + template + [[nodiscard]] static std::string prepareHelp(std::size_t namePadding, + std::size_t nameFieldSize /* padding + name + padding */, + std::size_t windowSize) noexcept { + auto nameField = fmt::format("{:{}}{:<{}}{:{}}", "", namePadding, A::getFullName(), + nameFieldSize - 2 * namePadding, "", namePadding); + + auto helpTextSize = windowSize - nameFieldSize - 1; + auto fullHelpTextLines = splitAndTrim(A::getFullHelpText(), '\n') | + ranges::views::transform([helpTextSize](auto &&line) { + if (line.size() > helpTextSize) { + return line | ranges::views::chunk(helpTextSize) | transformToString | + ranges::to>(); + } + + return std::vector{{line}}; + }) | + ranges::views::join | ranges::to>(); + + if (fullHelpTextLines.empty()) { + return nameField + "\n"; + } + + std::string result{}; + + for (std::size_t i = 0; i < fullHelpTextLines.size(); i++) { + if (i == 0) { + result += fmt::format("{}{}\n", nameField, fullHelpTextLines[i]); + } else if (fullHelpTextLines[i].empty()) { + result += "\n"; + } else { + result += fmt::format("{:{}}{}\n", "", nameFieldSize, fullHelpTextLines[i]); + } + } + + return result; + } +}; + +struct PositionalArg : Arg { + template + static ParseResult> parse(const std::vector &args) noexcept { + return ParseResult>::ok(args.size() > A::POSITION ? std::optional{args[A::POSITION]} + : std::nullopt); + } +}; + +struct RequiredMixin { + template + static ParseResult defaultParseImpl(const std::vector &args) noexcept { + return ParseResult::ok(args[A::POSITION]); + } + + template > + static ParseResult parse(const std::vector &args) noexcept { + if (args.size() > A::POSITION) { + return parseImpl(args); + } + + return ParseResult::error("\"" + A::NAME + "\" argument parsing error. Insufficient parameters."); + } +}; + +struct NamedArg : Arg { + template static bool canParse(const std::vector &args, std::size_t index) noexcept { + return args.size() > index + 1 && ((!A::SHORT_NAME.empty() && args[index] == "-" + A::SHORT_NAME) || + (!A::LONG_NAME.empty() && args[index] == "--" + A::LONG_NAME)); + } + + template + static ParseResult> parse(const std::vector &args, + std::size_t index) noexcept { + if (args.size() <= index + 1) { + return ParseResult>::ok(std::nullopt, index); + } + + if ((!A::SHORT_NAME.empty() && args[index] == "-" + A::SHORT_NAME) || + (!A::LONG_NAME.empty() && args[index] == "--" + A::LONG_NAME)) { + return ParseResult>::ok(args[index + 1], index + 2); + } + + return ParseResult>::ok(std::nullopt, index); + } + + template [[nodiscard]] static std::string getFullName() noexcept { + std::string result{}; + + if (!A::SHORT_NAME.empty()) { + result += "-" + A::SHORT_NAME; + } + + if (!A::LONG_NAME.empty()) { + result += (!A::SHORT_NAME.empty() ? ", " : String::EMPTY) + "--" + A::LONG_NAME; + } + + return result; + } +}; + +struct NamedUnsignedIntArg : NamedArg { + template + static ParseResult> parse(const std::vector &args, + std::size_t index) noexcept { + if (args.size() <= index + 1) { + return ParseResult>::ok(std::nullopt, index); + } + + if ((!A::SHORT_NAME.empty() && args[index] == "-" + A::SHORT_NAME) || + (!A::LONG_NAME.empty() && args[index] == "--" + A::LONG_NAME)) { + try { + return ParseResult>::ok(std::stoull(args[index + 1]), index + 2); + } catch (...) { + return ParseResult>::ok(std::nullopt, index); + } + } + + return ParseResult>::ok(std::nullopt, index); + } +}; + +struct FlagArg : NamedArg { + template + static ParseResult parse(const std::vector &args, std::size_t index) noexcept { + auto r = ParseResult::ok(args.size() > index && + ((!A::SHORT_NAME.empty() && args[index] == "-" + A::SHORT_NAME) || + (!A::LONG_NAME.empty() && args[index] == "--" + A::LONG_NAME)), + index); + + if (r.result) { + r.nextIndex = index + 1; + } + + return r; + } +}; + +struct TailArg : PositionalArg, RequiredMixin { + template static ParseResult parse(const std::vector &args) noexcept { + return RequiredMixin::parse &args) noexcept { + return ParseResult::ok(args | ranges::views::join(std::string(" ")) | ranges::to, + args.size() - 1); + }>(args); + } +}; + +struct AddressArg : PositionalArg { + const static std::string NAME; + const static std::size_t POSITION; + const static std::string HELP_TEXT; + + [[nodiscard]] static std::string prepareHelp(std::size_t namePadding, + std::size_t nameFieldSize /* padding + name + padding */, + std::size_t windowSize) noexcept { + return Arg::prepareHelp(namePadding, nameFieldSize, windowSize); + } + + [[nodiscard]] static std::string getFullName() noexcept { + return fmt::format("{} (pos. {})", NAME, POSITION); + } + + [[nodiscard]] static std::string getFullHelpText() noexcept { + return trimStr(HELP_TEXT); + } + + static ParseResult> parse(const std::vector &args) { + return PositionalArg::parse(args); + } +}; + +struct AddressArgRequired : AddressArg, RequiredMixin { + [[nodiscard]] static std::string prepareHelp(std::size_t namePadding, + std::size_t nameFieldSize /* padding + name + padding */, + std::size_t windowSize) noexcept { + return Arg::prepareHelp(namePadding, nameFieldSize, windowSize); + } + + [[nodiscard]] static std::string getFullHelpText() noexcept { + return fmt::format("Required. {}", trimStr(HELP_TEXT)); + } + + static ParseResult parse(const std::vector &args) { + return RequiredMixin::parse(args); + } +}; + +struct TypesArg : PositionalArg { + const static std::string NAME; + const static std::size_t POSITION; + const static std::string HELP_TEXT; + + [[nodiscard]] static std::string prepareHelp(std::size_t namePadding, + std::size_t nameFieldSize /* padding + name + padding */, + std::size_t windowSize) noexcept { + return Arg::prepareHelp(namePadding, nameFieldSize, windowSize); + } + + [[nodiscard]] static std::string getFullName() noexcept { + return fmt::format("{} (pos. {})", NAME, POSITION); + } + + [[nodiscard]] static std::string getFullHelpText() noexcept { + return trimStr(HELP_TEXT); + } + + static ParseResult> parse(const std::vector &args) { + return PositionalArg::parse(args); + } +}; + +struct TypesArgRequired : TypesArg, RequiredMixin { + [[nodiscard]] static std::string prepareHelp(std::size_t namePadding, + std::size_t nameFieldSize /* padding + name + padding */, + std::size_t windowSize) noexcept { + return Arg::prepareHelp(namePadding, nameFieldSize, windowSize); + } + + [[nodiscard]] static std::string getFullHelpText() noexcept { + return fmt::format("Required. {}", trimStr(HELP_TEXT)); + } + + static ParseResult parse(const std::vector &args) { + return RequiredMixin::parse(args); + } +}; + +struct SymbolsArg : PositionalArg { + const static std::string NAME; + const static std::size_t POSITION; + const static std::string HELP_TEXT; + + [[nodiscard]] static std::string prepareHelp(std::size_t namePadding, + std::size_t nameFieldSize /* padding + name + padding */, + std::size_t windowSize) noexcept { + return Arg::prepareHelp(namePadding, nameFieldSize, windowSize); + } + + [[nodiscard]] static std::string getFullName() noexcept { + return fmt::format("{} (pos. {})", NAME, POSITION); + } + + [[nodiscard]] static std::string getFullHelpText() noexcept { + return trimStr(HELP_TEXT); + } + + static ParseResult> parse(const std::vector &args) { + return PositionalArg::parse(args); + } +}; + +struct SymbolsArgRequired : SymbolsArg, RequiredMixin { + [[nodiscard]] static std::string prepareHelp(std::size_t namePadding, + std::size_t nameFieldSize /* padding + name + padding */, + std::size_t windowSize) noexcept { + return Arg::prepareHelp(namePadding, nameFieldSize, windowSize); + } + + [[nodiscard]] static std::string getFullHelpText() noexcept { + return fmt::format("Required. {}", trimStr(HELP_TEXT)); + } + + static ParseResult parse(const std::vector &args) { + if (args.size() > POSITION) { + return ParseResult::ok(args[POSITION]); + } + + return ParseResult::error("Symbols parsing error. Insufficient parameters."); + } +}; + +struct PropertiesArg : NamedArg { + const static std::string NAME; + const static std::string SHORT_NAME; + const static std::string LONG_NAME; + const static std::string HELP_TEXT; + + [[nodiscard]] static std::string prepareHelp(std::size_t namePadding, + std::size_t nameFieldSize /* padding + name + padding */, + std::size_t windowSize) noexcept { + return Arg::prepareHelp(namePadding, nameFieldSize, windowSize); + } + + [[nodiscard]] static std::string getFullName() noexcept { + return NamedArg::getFullName(); + } + + [[nodiscard]] static std::string getFullHelpText() noexcept { + return trimStr(HELP_TEXT); + } + + static bool canParse(const std::vector &args, std::size_t index) { + return NamedArg::canParse(args, index); + } + + static ParseResult> parse(const std::vector &args, std::size_t index) { + return NamedArg::parse(args, index); + } +}; + +struct FromTimeArg : NamedArg { + const static std::string NAME; + const static std::string SHORT_NAME; + const static std::string LONG_NAME; + const static std::string HELP_TEXT; + + [[nodiscard]] static std::string prepareHelp(std::size_t namePadding, + std::size_t nameFieldSize /* padding + name + padding */, + std::size_t windowSize) noexcept { + return Arg::prepareHelp(namePadding, nameFieldSize, windowSize); + } + + [[nodiscard]] static std::string getFullName() noexcept { + return NamedArg::getFullName(); + } + + [[nodiscard]] static std::string getFullHelpText() noexcept { + return trimStr(HELP_TEXT); + } + + static bool canParse(const std::vector &args, std::size_t index) { + return NamedArg::canParse(args, index); + } + + static ParseResult> parse(const std::vector &args, std::size_t index) { + return NamedArg::parse(args, index); + } +}; + +struct SourceArg : NamedArg { + const static std::string NAME; + const static std::string SHORT_NAME; + const static std::string LONG_NAME; + const static std::string HELP_TEXT; + + [[nodiscard]] static std::string prepareHelp(std::size_t namePadding, + std::size_t nameFieldSize /* padding + name + padding */, + std::size_t windowSize) noexcept { + return Arg::prepareHelp(namePadding, nameFieldSize, windowSize); + } + + [[nodiscard]] static std::string getFullName() noexcept { + return NamedArg::getFullName(); + } + + [[nodiscard]] static std::string getFullHelpText() noexcept { + return trimStr(HELP_TEXT); + } + + static bool canParse(const std::vector &args, std::size_t index) { + return NamedArg::canParse(args, index); + } + + static ParseResult> parse(const std::vector &args, std::size_t index) { + return NamedArg::parse(args, index); + } +}; + +struct TapeArg : NamedArg { + const static std::string NAME; + const static std::string SHORT_NAME; + const static std::string LONG_NAME; + const static std::string HELP_TEXT; + + [[nodiscard]] static std::string prepareHelp(std::size_t namePadding, + std::size_t nameFieldSize /* padding + name + padding */, + std::size_t windowSize) noexcept { + return Arg::prepareHelp(namePadding, nameFieldSize, windowSize); + } + + [[nodiscard]] static std::string getFullName() noexcept { + return NamedArg::getFullName(); + } + + [[nodiscard]] static std::string getFullHelpText() noexcept { + return trimStr(HELP_TEXT); + } + + static bool canParse(const std::vector &args, std::size_t index) { + return NamedArg::canParse(args, index); + } + + static ParseResult> parse(const std::vector &args, std::size_t index) { + return NamedArg::parse(args, index); + } +}; + +struct QuiteArg : FlagArg { + const static std::string NAME; + const static std::string SHORT_NAME; + const static std::string LONG_NAME; + const static std::string HELP_TEXT; + + [[nodiscard]] static std::string prepareHelp(std::size_t namePadding, + std::size_t nameFieldSize /* padding + name + padding */, + std::size_t windowSize) noexcept { + return Arg::prepareHelp(namePadding, nameFieldSize, windowSize); + } + + [[nodiscard]] static std::string getFullName() noexcept { + return NamedArg::getFullName(); + } + + [[nodiscard]] static std::string getFullHelpText() noexcept { + return trimStr(HELP_TEXT); + } + + static ParseResult parse(const std::vector &args, std::size_t index) { + return FlagArg::parse(args, index); + } +}; + +struct ForceStreamArg : FlagArg { + const static std::string NAME; + const static std::string SHORT_NAME; + const static std::string LONG_NAME; + const static std::string HELP_TEXT; + + [[nodiscard]] static std::string prepareHelp(std::size_t namePadding, + std::size_t nameFieldSize /* padding + name + padding */, + std::size_t windowSize) noexcept { + return Arg::prepareHelp(namePadding, nameFieldSize, windowSize); + } + + [[nodiscard]] static std::string getFullName() noexcept { + return NamedArg::getFullName(); + } + + [[nodiscard]] static std::string getFullHelpText() noexcept { + return trimStr(HELP_TEXT); + } + + static ParseResult parse(const std::vector &args, std::size_t index) { + return FlagArg::parse(args, index); + } +}; + +struct CPUUsageByCoreArg : FlagArg { + const static std::string NAME; + const static std::string SHORT_NAME; + const static std::string LONG_NAME; + const static std::string HELP_TEXT; + + [[nodiscard]] static std::string prepareHelp(std::size_t namePadding, + std::size_t nameFieldSize /* padding + name + padding */, + std::size_t windowSize) noexcept { + return Arg::prepareHelp(namePadding, nameFieldSize, windowSize); + } + + [[nodiscard]] static std::string getFullName() noexcept { + return NamedArg::getFullName(); + } + + [[nodiscard]] static std::string getFullHelpText() noexcept { + return trimStr(HELP_TEXT); + } + + static ParseResult parse(const std::vector &args, std::size_t index) { + return FlagArg::parse(args, index); + } +}; + +struct DetachListenerArg : FlagArg { + const static std::string NAME; + const static std::string SHORT_NAME; + const static std::string LONG_NAME; + const static std::string HELP_TEXT; + + [[nodiscard]] static std::string prepareHelp(std::size_t namePadding, + std::size_t nameFieldSize /* padding + name + padding */, + std::size_t windowSize) noexcept { + return Arg::prepareHelp(namePadding, nameFieldSize, windowSize); + } + + [[nodiscard]] static std::string getFullName() noexcept { + return NamedArg::getFullName(); + } + + [[nodiscard]] static std::string getFullHelpText() noexcept { + return trimStr(HELP_TEXT); + } + + static ParseResult parse(const std::vector &args, std::size_t index) { + return FlagArg::parse(args, index); + } +}; + +struct IntervalArg : NamedUnsignedIntArg { + const static std::string NAME; + const static std::string SHORT_NAME; + const static std::string LONG_NAME; + const static std::string HELP_TEXT; + + [[nodiscard]] static std::string prepareHelp(std::size_t namePadding, + std::size_t nameFieldSize /* padding + name + padding */, + std::size_t windowSize) noexcept { + return Arg::prepareHelp(namePadding, nameFieldSize, windowSize); + } + + [[nodiscard]] static std::string getFullName() noexcept { + return NamedArg::getFullName(); + } + + [[nodiscard]] static std::string getFullHelpText() noexcept { + return trimStr(HELP_TEXT); + } + + static bool canParse(const std::vector &args, std::size_t index) { + return NamedArg::canParse(args, index); + } + + static ParseResult> parse(const std::vector &args, std::size_t index) { + return NamedUnsignedIntArg::parse(args, index); + } +}; + +struct HelpArg : FlagArg { + const static std::string NAME; + const static std::string SHORT_NAME; + const static std::string LONG_NAME; + const static std::string HELP_TEXT; + + [[nodiscard]] static std::string prepareHelp(std::size_t namePadding, + std::size_t nameFieldSize /* padding + name + padding */, + std::size_t windowSize) noexcept { + return Arg::prepareHelp(namePadding, nameFieldSize, windowSize); + } + + [[nodiscard]] static std::string getFullName() noexcept { + return NamedArg::getFullName(); + } + + [[nodiscard]] static std::string getFullHelpText() noexcept { + return trimStr(HELP_TEXT); + } + + static ParseResult parse(const std::vector &args, std::size_t index) { + return FlagArg::parse(args, index); + } +}; + +struct ArticleArgRequired : TailArg { + const static std::string NAME; + const static std::size_t POSITION; + const static std::string HELP_TEXT; + + [[nodiscard]] static std::string prepareHelp(std::size_t namePadding, + std::size_t nameFieldSize /* padding + name + padding */, + std::size_t windowSize) noexcept { + return Arg::prepareHelp(namePadding, nameFieldSize, windowSize); + } + + [[nodiscard]] static std::string getFullName() noexcept { + return fmt::format("{} (pos. {})", NAME, POSITION); + } + + [[nodiscard]] static std::string getFullHelpText() noexcept { + return fmt::format("Required. {}", trimStr(HELP_TEXT)); + } + + static ParseResult parse(const std::vector &args) { + return TailArg::parse(args); + } +}; + +using ArgType = + std::variant; + +} // namespace dxfcpp::tools \ No newline at end of file diff --git a/tools/Tools/src/Connect/ConnectTool.cpp b/tools/Tools/src/Connect/ConnectTool.cpp deleted file mode 100644 index b2057e9d..00000000 --- a/tools/Tools/src/Connect/ConnectTool.cpp +++ /dev/null @@ -1,2 +0,0 @@ -// Copyright (c) 2023 Devexperts LLC. -// SPDX-License-Identifier: MPL-2.0 \ No newline at end of file diff --git a/tools/Tools/src/Connect/ConnectTool.hpp b/tools/Tools/src/Connect/ConnectTool.hpp new file mode 100644 index 00000000..dde89d8c --- /dev/null +++ b/tools/Tools/src/Connect/ConnectTool.hpp @@ -0,0 +1,202 @@ +// Copyright (c) 2023 Devexperts LLC. +// SPDX-License-Identifier: MPL-2.0 + +#pragma once + +#include + +#include "../Args/Args.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +namespace dxfcpp::tools { + +struct ConnectTool { + static const std::string NAME; + static const std::string SHORT_DESCRIPTION; + static const std::string DESCRIPTION; + static const std::vector USAGE; + static const std::vector ADDITIONAL_INFO; + static const std::vector ARGS; + + [[nodiscard]] static std::string getName() noexcept { + return NAME; + } + + [[nodiscard]] static std::string getFullName() noexcept { + return NAME; + } + + [[nodiscard]] static std::string prepareHelp(std::size_t namePadding, + std::size_t nameFieldSize /* padding + name + padding */, + std::size_t) noexcept { + return fmt::format("{:{}}{:<{}}{:{}}{}\n", "", namePadding, getFullName(), nameFieldSize - 2 * namePadding, "", + namePadding, SHORT_DESCRIPTION); + } + + struct Args { + std::string address; + std::string types; + std::string symbols; + std::optional fromTime; + std::optional source; + std::optional properties; + std::optional tape; + bool isQuite; + + static ParseResult parse(const std::vector &args) noexcept { + std::size_t index = 0; + + if (HelpArg::parse(args, index).result) { + return ParseResult::help(); + } + + auto parsedAddress = AddressArgRequired::parse(args); + + if (parsedAddress.isError) { + return ParseResult::error(parsedAddress.errorString); + } + + index++; + + auto parsedTypes = TypesArgRequired::parse(args); + + if (parsedTypes.isError) { + return ParseResult::error(parsedTypes.errorString); + } + + index++; + + auto parsedSymbols = SymbolsArgRequired::parse(args); + + if (parsedSymbols.isError) { + return ParseResult::error(parsedSymbols.errorString); + } + + index++; + + bool fromTimeIsParsed{}; + std::optional fromTime{}; + bool sourceIsParsed{}; + std::optional source{}; + bool propertiesIsParsed{}; + std::optional properties{}; + bool tapeIsParsed{}; + std::optional tape{}; + bool isQuite{}; + + for (; index < args.size();) { + if (!fromTimeIsParsed && FromTimeArg::canParse(args, index)) { + auto parseResult = FromTimeArg::parse(args, index); + + fromTime = parseResult.result; + fromTimeIsParsed = true; + index = parseResult.nextIndex; + } else if (!sourceIsParsed && SourceArg::canParse(args, index)) { + auto parseResult = SourceArg::parse(args, index); + + source = parseResult.result; + sourceIsParsed = true; + index = parseResult.nextIndex; + } else if (!propertiesIsParsed && PropertiesArg::canParse(args, index)) { + auto parseResult = PropertiesArg::parse(args, index); + + properties = parseResult.result; + propertiesIsParsed = true; + index = parseResult.nextIndex; + } else if (!tapeIsParsed && TapeArg::canParse(args, index)) { + auto parseResult = TapeArg::parse(args, index); + + tape = parseResult.result; + tapeIsParsed = true; + index = parseResult.nextIndex; + } else { + if (!isQuite && (isQuite = QuiteArg::parse(args, index).result)) { + index++; + continue; + } + + index++; + } + } + + return ParseResult::ok({parsedAddress.result, parsedTypes.result, parsedSymbols.result, fromTime, + source, properties, tape, isQuite}); + } + }; + + static void run(const Args &args) noexcept { + using namespace std::literals; + + auto endpoint = DXEndpoint::newBuilder() + ->withRole(DXEndpoint::Role::FEED) + ->withProperties(CmdArgsUtils::parseProperties(args.properties)) + ->withName(NAME + "Tool") + ->build(); + + std::shared_ptr sub = + endpoint->getFeed()->createSubscription(CmdArgsUtils::parseTypes(args.types)); + + if (!args.isQuite) { + sub->addEventListener([](auto &&events) { + for (auto &&e : events) { + std::cout << e << "\n"; + } + + std::cout.flush(); + }); + } + + auto symbols = CmdArgsUtils::parseSymbols(args.symbols); + + if (args.fromTime.has_value()) { + auto fromTime = CmdArgsUtils::parseDateTime(args.fromTime.value()); + + symbols = symbols | ranges::views::transform([fromTime](const auto &sw) { + return TimeSeriesSubscriptionSymbol{sw, fromTime}; + }) | + ranges::to>; + } else if (args.source.has_value()) { + auto source = OrderSource::valueOf(args.source.value()); + + symbols = symbols | ranges::views::transform([source](const auto &sw) { + return IndexedEventSubscriptionSymbol{sw, source}; + }) | + ranges::to>; + } + + if (args.tape.has_value()) { + std::string tape = args.tape.value(); + + auto pub = DXEndpoint::newBuilder() + ->withRole(DXEndpoint::Role::STREAM_PUBLISHER) + ->withProperty(DXEndpoint::DXFEED_WILDCARD_ENABLE_PROPERTY, "true") // Enabled by default + ->withName(NAME + "Tool") + ->build() + ->connect(tape.starts_with("tape:") ? tape : "tape:" + tape) + ->getPublisher(); + + sub->addEventListener([pub](auto &&events) { + pub->publishEvents(events); + }); + } + + sub->addSymbols(symbols); + endpoint->connect(args.address); + std::this_thread::sleep_for(std::chrono::days(365)); + } +}; + +} // namespace dxfcpp::tools \ No newline at end of file diff --git a/tools/Tools/src/Dump/DumpTool.cpp b/tools/Tools/src/Dump/DumpTool.cpp deleted file mode 100644 index b2057e9d..00000000 --- a/tools/Tools/src/Dump/DumpTool.cpp +++ /dev/null @@ -1,2 +0,0 @@ -// Copyright (c) 2023 Devexperts LLC. -// SPDX-License-Identifier: MPL-2.0 \ No newline at end of file diff --git a/tools/Tools/src/Dump/DumpTool.hpp b/tools/Tools/src/Dump/DumpTool.hpp new file mode 100644 index 00000000..a795ac64 --- /dev/null +++ b/tools/Tools/src/Dump/DumpTool.hpp @@ -0,0 +1,177 @@ +// Copyright (c) 2023 Devexperts LLC. +// SPDX-License-Identifier: MPL-2.0 + +#pragma once + +#include + +#include "../Args/Args.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +namespace dxfcpp::tools { + +struct DumpTool { + static const std::string NAME; + static const std::string SHORT_DESCRIPTION; + static const std::string DESCRIPTION; + static const std::vector USAGE; + static const std::vector ADDITIONAL_INFO; + static const std::vector ARGS; + + [[nodiscard]] static std::string getName() noexcept { + return NAME; + } + + [[nodiscard]] static std::string getFullName() noexcept { + return NAME; + } + + [[nodiscard]] static std::string prepareHelp(std::size_t namePadding, + std::size_t nameFieldSize /* padding + name + padding */, + std::size_t) noexcept { + return fmt::format("{:{}}{:<{}}{:{}}{}\n", "", namePadding, getFullName(), nameFieldSize - 2 * namePadding, "", + namePadding, SHORT_DESCRIPTION); + } + + struct Args { + std::string address; + std::optional types; + std::optional symbols; + std::optional properties; + std::optional tape; + bool isQuite; + + static ParseResult parse(const std::vector &args) noexcept { + std::size_t index = 0; + + if (HelpArg::parse(args, index).result) { + return ParseResult::help(); + } + + auto parsedAddress = AddressArgRequired::parse(args); + + if (parsedAddress.isError) { + return ParseResult::error(parsedAddress.errorString); + } + + index++; + + auto parsedTypes = TypesArg::parse(args); + + if (parsedTypes.result.has_value()) { + index++; + } + + auto parsedSymbols = SymbolsArg::parse(args); + + if (parsedSymbols.result.has_value()) { + index++; + } + + bool propertiesIsParsed{}; + std::optional properties{}; + bool tapeIsParsed{}; + std::optional tape{}; + bool isQuite{}; + + for (; index < args.size();) { + if (!propertiesIsParsed && PropertiesArg::canParse(args, index)) { + auto parseResult = PropertiesArg::parse(args, index); + + properties = parseResult.result; + propertiesIsParsed = true; + index = parseResult.nextIndex; + } else if (!tapeIsParsed && TapeArg::canParse(args, index)) { + auto parseResult = TapeArg::parse(args, index); + + tape = parseResult.result; + tapeIsParsed = true; + index = parseResult.nextIndex; + } else { + if (!isQuite && (isQuite = QuiteArg::parse(args, index).result)) { + index++; + continue; + } + + index++; + } + } + + return ParseResult::ok( + {parsedAddress.result, parsedTypes.result, parsedSymbols.result, properties, tape, isQuite}); + } + }; + + static void run(const Args &args) noexcept { + using namespace std::literals; + + auto properties = CmdArgsUtils::parseProperties(args.properties); + + auto inputEndpoint = + DXEndpoint::newBuilder() + ->withRole(DXEndpoint::Role::STREAM_FEED) + ->withProperty(DXEndpoint::DXFEED_WILDCARD_ENABLE_PROPERTY, "true") // Enabled by default + ->withProperties(properties) + ->withName(NAME + "Tool") + ->build(); + + auto sub = inputEndpoint->getFeed()->createSubscription( + !args.types.has_value() ? CmdArgsUtils::parseTypes("all") : CmdArgsUtils::parseTypes(args.types.value())); + + if (!args.isQuite) { + sub->addEventListener([](auto &&events) { + for (auto &&e : events) { + std::cout << e << "\n"; + } + + std::cout.flush(); + }); + } + + std::optional outputEndpoint{}; + + if (args.tape.has_value()) { + std::string tape = args.tape.value(); + + outputEndpoint = + DXEndpoint::newBuilder() + ->withRole(DXEndpoint::Role::STREAM_PUBLISHER) + ->withProperty(DXEndpoint::DXFEED_WILDCARD_ENABLE_PROPERTY, "true") // Enabled by default + ->withProperties(properties) + ->withName(NAME + "Tool") + ->build() + ->connect(tape.starts_with("tape:") ? tape : "tape:" + tape); + + sub->addEventListener([endpoint = outputEndpoint.value()](auto &&events) { + endpoint->getPublisher()->publishEvents(events); + }); + } + + sub->addSymbols(!args.symbols.has_value() ? CmdArgsUtils::parseSymbols("all") + : CmdArgsUtils::parseSymbols(args.symbols.value())); + + inputEndpoint->connect(args.address); + inputEndpoint->awaitNotConnected(); + inputEndpoint->closeAndAwaitTermination(); + + if (outputEndpoint.has_value()) { + outputEndpoint.value()->awaitProcessed(); + outputEndpoint.value()->closeAndAwaitTermination(); + } + } +}; + +} // namespace dxfcpp::tools \ No newline at end of file diff --git a/tools/Tools/src/Help/HelpTool.hpp b/tools/Tools/src/Help/HelpTool.hpp new file mode 100644 index 00000000..d2da03d0 --- /dev/null +++ b/tools/Tools/src/Help/HelpTool.hpp @@ -0,0 +1,266 @@ +// Copyright (c) 2023 Devexperts LLC. +// SPDX-License-Identifier: MPL-2.0 + +#pragma once + +#include + +#include "../Args/Args.hpp" + +#include "../Connect/ConnectTool.hpp" +#include "../Dump/DumpTool.hpp" +#include "../LatencyTest/LatencyTestTool.hpp" +#include "../PerfTest/PerfTestTool.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +namespace dxfcpp::tools { + +struct HelpTool { + using Tool = + std::variant; + + static const std::unordered_map EMBEDDED_ARTICLES; + static const std::string NAME; + static const std::string SHORT_DESCRIPTION; + static const std::string DESCRIPTION; + static const std::vector USAGE; + static const std::vector ADDITIONAL_INFO; + static const std::vector ARGS; + + [[nodiscard]] static std::string getName() noexcept { + return NAME; + } + + [[nodiscard]] static std::string getFullName() noexcept { + return NAME; + } + + [[nodiscard]] static std::string prepareHelp(std::size_t namePadding, + std::size_t nameFieldSize /* padding + name + padding */, + std::size_t) noexcept { + return fmt::format("{:{}}{:<{}}{:{}}{}\n", "", namePadding, getFullName(), + nameFieldSize - 2 * namePadding, "", namePadding, SHORT_DESCRIPTION); + } + + static const std::unordered_map ALL_TOOLS; + static const std::vector ALL_TOOL_NAMES; + static const std::vector ALL_ARTICLE_NAMES; + static const std::set ALL_NAMES; + + static constexpr std::size_t PADDING{2}; + + struct Args { + std::string article; + + static ParseResult parse(const std::vector &args) noexcept { + std::size_t index = 0; + + if (HelpArg::parse(args, index).result) { + return ParseResult::help(); + } + + auto parsedArticle = ArticleArgRequired::parse(args); + + if (parsedArticle.isError) { + return ParseResult::error(parsedArticle.errorString); + } + + return ParseResult::ok({parsedArticle.result}); + } + }; + + static void run(const Args &args) noexcept { + auto [width, height] = Console::getSize(); + using namespace std::literals; + + if (iEquals(args.article, "all")) { + fmt::print("{0:-^{1}}\n", "", width - 1); + for (auto&& name : ALL_NAMES) { + std::cout << generateScreen(name) << std::endl; + fmt::print("{0:-^{1}}\n", "", width - 1); + } + } else if (iEquals(args.article, "contents")) { + fmt::print("\nHelp articles:\n"); + + for (auto&& name : ALL_NAMES) { + fmt::print("{:{}}{}\n", "", PADDING, name); + } + } else { + auto screen = generateScreen(args.article); + + if (screen.empty()) { + fmt::print("\n{:{}}No help article found for \"{}\".\n", "", PADDING, args.article); + } else { + fmt::print("{}\n", screen); + } + } + } + + static std::pair getArticle(const std::string &article) noexcept { + for (auto &&[key, value] : EMBEDDED_ARTICLES) { + if (iEquals(key, article)) { + return {key, value}; + } + } + + return {String::EMPTY, String::EMPTY}; + } + + template + static std::string generateHeader(std::size_t width, const std::string &parseResult = "") noexcept { + auto toolName = Tool::getName(); + + std::string result{}; + + result = fmt::format("\n{}{}", toolName, ((toolName.size() <= width - 1) ? "\n" : "")) + + fmt::format("{0:=^{1}}\n", "", (toolName.size() <= width - 1 ? toolName.size() : width - 1)); + + if (!parseResult.empty()) { + result += fmt::format("\n{:{}}{}\n", "", PADDING, parseResult); + } else if (!Tool::DESCRIPTION.empty()) { + result += Tool::DESCRIPTION; + } else if (!Tool::SHORT_DESCRIPTION.empty()) { + result += "\n" + Tool::SHORT_DESCRIPTION + "\n"; + } + + return result; + } + + template static std::string generateArgsInfo(std::size_t width) noexcept { + std::string result{}; + + auto maxFullNameSize = ranges::max(Tool::ARGS | ranges::views::transform([](auto &&arg) { + return std::visit( + [](Arg &&) { + return std::decay_t::getFullName().size(); + }, + arg); + })); + + for (auto &&arg : Tool::ARGS) { + result += std::visit( + [maxFullNameSize, width](Arg &&) { + return fmt::format( + "{}", std::decay_t::prepareHelp(PADDING, PADDING + maxFullNameSize + PADDING, width)); + }, + arg); + } + + return result; + } + + template static std::string generateUsage() noexcept { + std::string result = "Usage:\n"; + + for (auto &&u : Tool::USAGE) { + result += fmt::format("{:{}}{}\n", "", PADDING, u); + } + + return result; + } + + template static std::string generateAdditionalInfo() noexcept { + std::string result{}; + + if (!Tool::ADDITIONAL_INFO.empty()) { + result += "\n"; + + for (auto &&ai : Tool::ADDITIONAL_INFO) { + result += ai + "\n"; + } + } + + return result; + } + + template static std::string generateArticleInfo() noexcept { + std::string result{}; + auto [article, content] = HelpTool::getArticle(Tool::getName()); + + if (!content.empty()) { + result += "\n" + content + "\n"; + } + + return result; + } + + template static std::string generateToolHelpScreen(const std::string &parseResult = "") noexcept { + auto [width, height] = Console::getSize(); + + std::string result{}; + + result += generateHeader(width, parseResult) + "\n"; + result += generateUsage(); + result += "\nWhere:\n\n"; + result += generateArgsInfo(width); + result += generateAdditionalInfo(); + result += generateArticleInfo(); + + return result; + } + + static std::string generateArticleHeader(std::size_t width, const std::string &article) noexcept { + std::string result{}; + + result = fmt::format("\n{}{}", article, ((article.size() <= width - 1) ? "\n" : "")) + + fmt::format("{0:=^{1}}\n", "", (article.size() <= width - 1 ? article.size() : width - 1)); + + return result; + } + + static std::string generateArticleScreen(const std::string &article, const std::string& content) noexcept { + auto [width, height] = Console::getSize(); + std::string result{}; + + result += generateArticleHeader(width, article) + "\n"; + result += content + "\n"; + + return result; + } + + static std::string generateScreen(const std::string& article) noexcept { + std::string screen{}; + + for (auto&& [name, tool] : ALL_TOOLS) { + if (iEquals(article, name)) { + screen = std::visit( + [](Tool &&) { + using T = std::decay_t; + + return tools::HelpTool::generateToolHelpScreen(); + }, + tool); + + break; + } + } + + if (!screen.empty()) { + return screen; + } + + auto [name, content] = getArticle(article); + + if (!content.empty()) { + return generateArticleScreen(name, content); + } + + return screen; + } +}; + +} // namespace dxfcpp::tools \ No newline at end of file diff --git a/tools/Tools/src/LatencyTest/LatencyTestTool.hpp b/tools/Tools/src/LatencyTest/LatencyTestTool.hpp new file mode 100644 index 00000000..386746e8 --- /dev/null +++ b/tools/Tools/src/LatencyTest/LatencyTestTool.hpp @@ -0,0 +1,360 @@ +// Copyright (c) 2023 Devexperts LLC. +// SPDX-License-Identifier: MPL-2.0 + +#pragma once + +#include + +#include "../Args/Args.hpp" + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +namespace dxfcpp::tools { + +struct LatencyTest { + static const std::string NAME; + static const std::string SHORT_DESCRIPTION; + static const std::string DESCRIPTION; + static const std::vector USAGE; + static const std::vector ADDITIONAL_INFO; + static const std::vector ARGS; + + [[nodiscard]] static std::string getName() noexcept { + return NAME; + } + + [[nodiscard]] static std::string getFullName() noexcept { + return NAME; + } + + [[nodiscard]] static std::string prepareHelp(std::size_t namePadding, + std::size_t nameFieldSize /* padding + name + padding */, + std::size_t) noexcept { + return fmt::format("{:{}}{:<{}}{:{}}{}\n", "", namePadding, getFullName(), + nameFieldSize - 2 * namePadding, "", namePadding, SHORT_DESCRIPTION); + } + + struct Diagnostic final { + private: + std::atomic eventsCounter_{}; + std::atomic listenerCallsCounter_{}; + + std::shared_ptr timer_{}; + StopWatch timerDiff_{}; + StopWatch runningDiff_{}; + + double min_ = math::NaN; + double mean_ = math::NaN; + double max_ = math::NaN; + double percentile_ = math::NaN; + double stdDev_ = math::NaN; + double stdErr_ = math::NaN; + + std::size_t sampleSize_{}; + + mutable std::mutex mtx_{}; + std::unordered_set symbols_{}; + std::list deltaTimes_{}; + std::chrono::seconds measurementPeriod_{}; + + explicit Diagnostic(std::chrono::seconds measurementPeriod) noexcept : measurementPeriod_{measurementPeriod} { + timerDiff_.restart(); + runningDiff_.restart(); + } + + static std::string formatDouble(double value) noexcept { + if (std::isnan(value)) { + return "---"; + } + + return fmt::format("{:.2f}", value); + } + + static double calcPercentile(std::vector sequence, double excelPercentile) noexcept { + std::sort(std::begin(sequence), std::end(sequence)); + + auto n = ((static_cast(sequence.size()) - 1.0) * excelPercentile) + 1.0; + + if (std::abs(n - 1.0) < std::numeric_limits::epsilon()) { + return static_cast(sequence[0]); + } + + if (std::abs(static_cast(sequence.size()) - 1.0) < std::numeric_limits::epsilon()) { + return static_cast(sequence[sequence.size() - 1]); + } + + auto k = static_cast(n); + auto d = n - static_cast(k); + + return static_cast(sequence[k - 1]) + + (d * (static_cast(sequence[k]) - static_cast(sequence[k - 1]))); + } + + static double calcMin(auto &&values) noexcept { + return *std::min_element(std::begin(values), std::end(values)); + } + + static double calcMean(auto &&values) noexcept { + return std::accumulate(std::begin(values), std::end(values), 0.0) / values.size(); + } + + static double calcMax(auto &&values) noexcept { + return *std::max_element(std::begin(values), std::end(values)); + } + + static double calcStdDev(auto &&values) noexcept { + double stdDev = 0.0; + + if (values.size() <= 1) { + return stdDev; + } + + double avg = calcMean(values); + double sum = std::accumulate(std::begin(values), std::end(values), 0.0, [avg](double a, std::int64_t b) { + return a + (static_cast(b) - avg) * (static_cast(b) - avg); + }); + + stdDev = std::sqrt(sum / (static_cast(values.size()) - 1.0)); + + return stdDev; + } + + static double calcStdErr(auto &&values, double stdDev) noexcept { + return stdDev / std::sqrt(static_cast(values.size())); + } + + void onTimer() noexcept { + auto eventsPerSecond = getEventsPerSecond(); + auto listenerCallsPerSecond = getListenerCallsPerSecond(); + + std::lock_guard lock{mtx_}; + + if (!deltaTimes_.empty()) { + min_ = calcMin(deltaTimes_); + mean_ = calcMean(deltaTimes_); + max_ = calcMax(deltaTimes_); + percentile_ = calcPercentile(std::vector{deltaTimes_.begin(), deltaTimes_.end()}, 0.99); + stdDev_ = calcStdDev(deltaTimes_); + stdErr_ = calcStdErr(deltaTimes_, stdDev_); + sampleSize_ = deltaTimes_.size(); + + deltaTimes_.clear(); + } + + fmt::print("\n{}\n", Platform::getPlatformInfo()); + std::cout << "----------------------------------------------------\n"; + fmt::print(" Rate of events (avg) : {} (events/s)\n", formatDouble(eventsPerSecond)); + fmt::print(" Rate of unique symbols : {} (symbols/interval)\n", symbols_.size()); + fmt::print(" Min : {} (ms)\n", formatDouble(min_)); + fmt::print(" Max : {} (ms)\n", formatDouble(max_)); + fmt::print(" 99th percentile : {} (ms)\n", formatDouble(percentile_)); + fmt::print(" Mean : {} (ms)\n", formatDouble(mean_)); + fmt::print(" StdDev : {} (ms)\n", formatDouble(stdDev_)); + fmt::print(" Error : {} (ms)\n", formatDouble(stdErr_)); + fmt::print(" Sample size (N) : {} (events)\n", sampleSize_); + fmt::print(" Measurement interval : {} (s)\n", measurementPeriod_.count()); + fmt::print(" Running time : {:%H:%M:%S}\n", runningDiff_.elapsed()); + + symbols_.clear(); + + min_ = math::NaN; + mean_ = math::NaN; + max_ = math::NaN; + percentile_ = math::NaN; + stdDev_ = math::NaN; + stdErr_ = math::NaN; + sampleSize_ = 0; + + timerDiff_.restart(); + } + + std::size_t getAndResetEventsCounter() noexcept { + return eventsCounter_.exchange(0); + } + + std::size_t getAndResetListenerCallsCounter() noexcept { + return listenerCallsCounter_.exchange(0); + } + + double getEventsPerSecond() noexcept { + return static_cast(getAndResetEventsCounter()) / + static_cast(std::chrono::duration_cast(timerDiff_.elapsed()).count()); + } + + double getListenerCallsPerSecond() noexcept { + return static_cast(getAndResetListenerCallsCounter()) / + static_cast(std::chrono::duration_cast(timerDiff_.elapsed()).count()); + } + + public: + static std::shared_ptr create(std::chrono::seconds measurementPeriod) noexcept { + auto d = std::shared_ptr(new Diagnostic(measurementPeriod)); + + d->timer_ = Timer::schedule( + [self = d] { + self->onTimer(); + }, + measurementPeriod, measurementPeriod); + + return d; + } + + void addEventsCounter(std::size_t value) noexcept { + eventsCounter_ += value; + } + + void addListenerCallsCounter(std::size_t value) noexcept { + listenerCallsCounter_ += value; + } + + void handleEvents(const std::vector> &events) { + auto time = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + std::size_t validEvents = 0; + + std::lock_guard lock{mtx_}; + + for (auto &&e : events) { + std::int64_t deltaTime = 0; + + if (std::shared_ptr quote = e->sharedAs(); quote) { + deltaTime = time - quote->getTime(); + validEvents++; + deltaTimes_.emplace_back(deltaTime); + symbols_.emplace(quote->getEventSymbol()); + } else if (std::shared_ptr trade = e->sharedAs(); trade) { + deltaTime = time - trade->getTime(); + validEvents++; + deltaTimes_.emplace_back(deltaTime); + symbols_.emplace(trade->getEventSymbol()); + } else if (std::shared_ptr tradeEth = e->sharedAs(); tradeEth) { + deltaTime = time - tradeEth->getTime(); + validEvents++; + deltaTimes_.emplace_back(deltaTime); + symbols_.emplace(tradeEth->getEventSymbol()); + } else if (std::shared_ptr timeAndSale = e->sharedAs(); timeAndSale) { + if (!timeAndSale->isNew() || !timeAndSale->isValidTick()) { + continue; + } + + deltaTime = time - timeAndSale->getTime(); + validEvents++; + deltaTimes_.emplace_back(deltaTime); + symbols_.emplace(timeAndSale->getEventSymbol()); + } + } + + addEventsCounter(validEvents); + } + }; + + struct Args { + std::string address{}; + std::optional types{}; + std::optional symbols{}; + std::optional properties{}; + bool forceStream{}; + std::size_t interval{2}; + + static ParseResult parse(const std::vector &args) noexcept { + std::size_t index = 0; + + if (HelpArg::parse(args, index).result) { + return ParseResult::help(); + } + + auto parsedAddress = AddressArgRequired::parse(args); + + if (parsedAddress.isError) { + return ParseResult::error(parsedAddress.errorString); + } + + index++; + + auto parsedTypes = TypesArg::parse(args); + + if (parsedTypes.result.has_value()) { + index++; + } + + auto parsedSymbols = SymbolsArg::parse(args); + + if (parsedSymbols.result.has_value()) { + index++; + } + + bool propertiesIsParsed{}; + std::optional properties{}; + bool intervalIsParsed{}; + std::optional interval{}; + bool forceStream{}; + + for (; index < args.size();) { + if (!propertiesIsParsed && PropertiesArg::canParse(args, index)) { + auto parseResult = PropertiesArg::parse(args, index); + + properties = parseResult.result; + propertiesIsParsed = true; + index = parseResult.nextIndex; + } else if (!intervalIsParsed && IntervalArg::canParse(args, index)) { + auto parseResult = IntervalArg::parse(args, index); + + interval = parseResult.result; + intervalIsParsed = true; + index = parseResult.nextIndex; + } else { + if (!forceStream && (forceStream = ForceStreamArg::parse(args, index).result)) { + index++; + continue; + } + + index++; + } + } + + return ParseResult::ok({parsedAddress.result, parsedTypes.result, parsedSymbols.result, properties, forceStream, interval.value_or(2)}); + } + }; + + static void run(const Args &args) noexcept { + using namespace std::literals; + + auto endpoint = DXEndpoint::newBuilder() + ->withRole(args.forceStream ? DXEndpoint::Role::STREAM_FEED : DXEndpoint::Role::FEED) + ->withProperty(DXEndpoint::DXFEED_WILDCARD_ENABLE_PROPERTY, "true") // Enabled by default. + ->withProperties(CmdArgsUtils::parseProperties(args.properties)) + ->withName(NAME + "Tool") + ->build(); + + auto sub = endpoint->getFeed()->createSubscription( + CmdArgsUtils::parseTypes(args.types.has_value() ? args.types.value() : "all")); + auto diagnostic = Diagnostic::create(std::chrono::seconds(args.interval)); + + sub->addEventListener([d = diagnostic](auto &&events) { + d->addListenerCallsCounter(1); + d->handleEvents(events); + }); + + sub->addSymbols(CmdArgsUtils::parseSymbols(args.symbols.has_value() ? args.symbols.value() : "all")); + endpoint->connect(args.address); + endpoint->awaitNotConnected(); + endpoint->closeAndAwaitTermination(); + } +}; + +} // namespace dxfcpp::tools \ No newline at end of file diff --git a/tools/Tools/src/PerfTest/PerfTestTool.cpp b/tools/Tools/src/PerfTest/PerfTestTool.cpp deleted file mode 100644 index b2057e9d..00000000 --- a/tools/Tools/src/PerfTest/PerfTestTool.cpp +++ /dev/null @@ -1,2 +0,0 @@ -// Copyright (c) 2023 Devexperts LLC. -// SPDX-License-Identifier: MPL-2.0 \ No newline at end of file diff --git a/tools/Tools/src/PerfTest/PerfTestTool.hpp b/tools/Tools/src/PerfTest/PerfTestTool.hpp new file mode 100644 index 00000000..ec0c57d1 --- /dev/null +++ b/tools/Tools/src/PerfTest/PerfTestTool.hpp @@ -0,0 +1,271 @@ +// Copyright (c) 2023 Devexperts LLC. +// SPDX-License-Identifier: MPL-2.0 + +#pragma once + +#include + +#include "../Args/Args.hpp" + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +namespace dxfcpp::tools { + +struct PerfTestTool { + static const std::string NAME; + static const std::string SHORT_DESCRIPTION; + static const std::string DESCRIPTION; + static const std::vector USAGE; + static const std::vector ADDITIONAL_INFO; + static const std::vector ARGS; + + [[nodiscard]] static std::string getName() noexcept { + return NAME; + } + + [[nodiscard]] static std::string getFullName() noexcept { + return NAME; + } + + [[nodiscard]] static std::string prepareHelp(std::size_t namePadding, + std::size_t nameFieldSize /* padding + name + padding */, + std::size_t) noexcept { + return fmt::format("{:{}}{:<{}}{:{}}{}\n", "", namePadding, getFullName(), nameFieldSize - 2 * namePadding, "", + namePadding, SHORT_DESCRIPTION); + } + + struct Diagnostic final { + private: + bool showCpuUsageByCore_{}; + std::chrono::milliseconds cpuStartTime_{}; + std::atomic eventsCounter_{}; + std::atomic listenerCallsCounter_{}; + + mutable std::mutex mtx_{}; + double peakMemoryUsage_{}; + double peakCpuUsage_{}; + + std::shared_ptr timer_{}; + StopWatch timerDiff_{}; + StopWatch runningDiff_{}; + + explicit Diagnostic(bool showCpuUsageByCore) noexcept : showCpuUsageByCore_{showCpuUsageByCore} { + timerDiff_.restart(); + runningDiff_.restart(); + cpuStartTime_ = ttldtor::process::Process::getTotalProcessorTime(); + } + + static std::string formatDouble(double value) noexcept { + return fmt::format("{:.2f}", std::isnan(value) ? 0.0 : value); + } + + void onTimer() noexcept { + auto eventsPerSecond = getEventsPerSecond(); + auto listenerCallsPerSecond = getListenerCallsPerSecond(); + + std::lock_guard lock{mtx_}; + + auto currentMemoryUsage = getMemoryUsage(); + peakMemoryUsage_ = currentMemoryUsage > peakMemoryUsage_ ? currentMemoryUsage : peakMemoryUsage_; + + auto currentCpuUsage = getCpuUsage(); + peakCpuUsage_ = currentCpuUsage > peakCpuUsage_ ? currentCpuUsage : peakCpuUsage_; + + fmt::print("\n{}\n", Platform::getPlatformInfo()); + std::cout << "----------------------------------------------------\n"; + fmt::print(" Rate of events (avg) : {} (events/s)\n", formatDouble(eventsPerSecond)); + fmt::print(" Rate of listener calls : {} (calls/s)\n", formatDouble(listenerCallsPerSecond)); + fmt::print(" Number of events in call (avg) : {} (events)\n", + formatDouble(eventsPerSecond / listenerCallsPerSecond)); + fmt::print(" Current memory usage : {:.3f} (Mbyte)\n", currentMemoryUsage); + fmt::print(" Peak memory usage : {:.3f} (Mbyte)\n", peakMemoryUsage_); + fmt::print(" Current CPU usage : {:.2f} %\n", currentCpuUsage * 100.0); + fmt::print(" Peak CPU usage : {:.2f} %\n", peakCpuUsage_ * 100.0); + fmt::print(" Running time : {:%H:%M:%S}\n", runningDiff_.elapsed()); + + timerDiff_.restart(); + } + + std::size_t getAndResetEventsCounter() noexcept { + return eventsCounter_.exchange(0); + } + + std::size_t getAndResetListenerCallsCounter() noexcept { + return listenerCallsCounter_.exchange(0); + } + + double getEventsPerSecond() noexcept { + return static_cast(getAndResetEventsCounter()) / + static_cast(std::chrono::duration_cast(timerDiff_.elapsed()).count()); + } + + double getListenerCallsPerSecond() noexcept { + return static_cast(getAndResetListenerCallsCounter()) / + static_cast(std::chrono::duration_cast(timerDiff_.elapsed()).count()); + } + + static double getMemoryUsage() noexcept { + return static_cast(ttldtor::process::Process::getWorkingSetSize()) / 1024.0 / 1024.0; + } + + double getCpuUsage() noexcept { + auto cpuEndTime = ttldtor::process::Process::getTotalProcessorTime(); + auto cpuDiff = cpuEndTime - cpuStartTime_; + + cpuStartTime_ = cpuEndTime; + + return static_cast(cpuDiff.count()) / + (static_cast(timerDiff_.elapsed().count()) * + static_cast(!showCpuUsageByCore_ ? std::thread::hardware_concurrency() : 1)); + } + + public: + static std::shared_ptr create(std::chrono::seconds measurementPeriod, + bool showCpuUsageByCore) noexcept { + auto d = std::shared_ptr(new Diagnostic(showCpuUsageByCore)); + + d->timer_ = Timer::schedule( + [self = d] { + self->onTimer(); + }, + measurementPeriod, measurementPeriod); + + return d; + } + + void addEventsCounter(std::size_t value) noexcept { + eventsCounter_ += value; + } + + void addListenerCallsCounter(std::size_t value) noexcept { + listenerCallsCounter_ += value; + } + }; + + struct Args { + std::string address{}; + std::string types{}; + std::string symbols{}; + std::optional properties{}; + bool forceStream{}; + bool showCpuUsageByCore{}; + bool detachListener{}; + + static ParseResult parse(const std::vector &args) noexcept { + std::size_t index = 0; + + if (HelpArg::parse(args, index).result) { + return ParseResult::help(); + } + + auto parsedAddress = AddressArgRequired::parse(args); + + if (parsedAddress.isError) { + return ParseResult::error(parsedAddress.errorString); + } + + index++; + + auto parsedTypes = TypesArgRequired::parse(args); + + if (parsedTypes.isError) { + return ParseResult::error(parsedTypes.errorString); + } + + index++; + + auto parsedSymbols = SymbolsArgRequired::parse(args); + + if (parsedSymbols.isError) { + return ParseResult::error(parsedSymbols.errorString); + } + + index++; + + bool propertiesIsParsed{}; + std::optional properties{}; + bool forceStream{}; + bool showCpuUsageByCore{}; + bool detachListener{}; + + for (; index < args.size();) { + if (!propertiesIsParsed && PropertiesArg::canParse(args, index)) { + auto parseResult = PropertiesArg::parse(args, index); + + properties = parseResult.result; + propertiesIsParsed = true; + index = parseResult.nextIndex; + } else { + if (!forceStream && (forceStream = ForceStreamArg::parse(args, index).result)) { + index++; + continue; + } + + if (!showCpuUsageByCore && (showCpuUsageByCore = CPUUsageByCoreArg::parse(args, index).result)) { + index++; + continue; + } + + if (!detachListener && (detachListener = DetachListenerArg::parse(args, index).result)) { + index++; + continue; + } + + index++; + } + } + + return ParseResult::ok({parsedAddress.result, parsedTypes.result, parsedSymbols.result, properties, + forceStream, showCpuUsageByCore, detachListener}); + } + }; + + static void run(const Args &args) noexcept { + using namespace std::literals; + + auto endpoint = DXEndpoint::newBuilder() + ->withRole(args.forceStream ? DXEndpoint::Role::STREAM_FEED : DXEndpoint::Role::FEED) + ->withProperty(DXEndpoint::DXFEED_WILDCARD_ENABLE_PROPERTY, "true") // Enabled by default. + ->withProperties(CmdArgsUtils::parseProperties(args.properties)) + ->withName(NAME + "Tool") + ->build(); + + auto sub = endpoint->getFeed()->createSubscription(CmdArgsUtils::parseTypes(args.types)); + auto diagnostic = Diagnostic::create(2s, args.showCpuUsageByCore); + + std::atomic hash{}; + + if (!args.detachListener) { + sub->addEventListener([d = diagnostic, &hash](auto &&events) { + d->addListenerCallsCounter(1); + d->addEventsCounter(events.size()); + + for (auto &&e : events) { + hash += std::hash>{}(e); + } + }); + } + + sub->addSymbols(CmdArgsUtils::parseSymbols(args.symbols)); + endpoint->connect(args.address); + endpoint->awaitNotConnected(); + endpoint->closeAndAwaitTermination(); + + std::cout << hash << std::endl; + } +}; + +} // namespace dxfcpp::tools \ No newline at end of file diff --git a/tools/Tools/src/Tools.cpp b/tools/Tools/src/Tools.cpp new file mode 100644 index 00000000..0738dcad --- /dev/null +++ b/tools/Tools/src/Tools.cpp @@ -0,0 +1,389 @@ +// Copyright (c) 2023 Devexperts LLC. +// SPDX-License-Identifier: MPL-2.0 + +#include "Tools.hpp" + +namespace dxfcpp::tools { + +const std::string ConnectTool::NAME{"Connect"}; +const std::string ConnectTool::SHORT_DESCRIPTION{"Connects to specified address(es)."}; +const std::string ConnectTool::DESCRIPTION{R"( +Connects to the specified address(es) and subscribes to the specified symbols. +)"}; +const std::vector ConnectTool::USAGE{ + NAME + "
[]", +}; +const std::vector ConnectTool::ADDITIONAL_INFO{}; + +const std::vector ConnectTool::ARGS{ + AddressArgRequired{}, TypesArgRequired{}, SymbolsArgRequired{}, FromTimeArg{}, SourceArg{}, + PropertiesArg{}, TapeArg{}, QuiteArg{}, HelpArg{}}; + +const std::string DumpTool::NAME{"Dump"}; +const std::string DumpTool::SHORT_DESCRIPTION{"Dumps all events received from address."}; +const std::string DumpTool::DESCRIPTION{R"( +Dumps all events received from address. +Enforces a streaming contract for subscription. A wildcard enabled by default. +This was designed to receive data from a file. +If is not specified, creates a subscription for all available event types. +If is not specified, the wildcard symbol is used. +)"}; +const std::vector DumpTool::USAGE{ + NAME + "
[]", + NAME + "
[]", + NAME + "
[]", +}; +const std::vector DumpTool::ADDITIONAL_INFO{}; + +const std::vector DumpTool::ARGS{AddressArgRequired{}, TypesArg{}, SymbolsArg{}, PropertiesArg{}, TapeArg{}, + QuiteArg{}, HelpArg{}}; + +const std::string LatencyTest::NAME{"LatencyTest"}; +const std::string LatencyTest::SHORT_DESCRIPTION{"Connects to the specified address(es) and calculates latency."}; +const std::string LatencyTest::DESCRIPTION{R"( +Connects to the specified address(es) and calculates latency. +)"}; +const std::vector LatencyTest::USAGE{ + NAME + "
[]", +}; +const std::vector LatencyTest::ADDITIONAL_INFO{}; + +const std::vector LatencyTest::ARGS{AddressArgRequired{}, TypesArg{}, SymbolsArg{}, PropertiesArg{}, + ForceStreamArg{}, IntervalArg{}, HelpArg{}}; + +const std::string PerfTestTool::NAME{"PerfTest"}; +const std::string PerfTestTool::SHORT_DESCRIPTION{"Connects to specified address and calculates performance counters."}; +const std::string PerfTestTool::DESCRIPTION{R"( +Connects to the specified address(es) and calculates performance counters (events per second, cpu usage, etc). +)"}; +const std::vector PerfTestTool::USAGE{ + NAME + "
[]", +}; +const std::vector PerfTestTool::ADDITIONAL_INFO{}; + +const std::vector PerfTestTool::ARGS{AddressArgRequired{}, TypesArgRequired{}, SymbolsArgRequired{}, + PropertiesArg{}, ForceStreamArg{}, CPUUsageByCoreArg{}, + DetachListenerArg{}, HelpArg{}}; + +const std::unordered_map HelpTool::EMBEDDED_ARTICLES{ + {"Connect", + R"(This tool is used to connect to some address with specified subscription, and log or tape received data. By default it +just logs all received records to a screen in text format. + +The main extra-functionality of the connect tool is taping data into file, which can be performed using --tape option. +(see "Help Tape" for its detailed description and samples). + +Examples: + +Connects to the demo-endpoint and subscribes to Quote event for AAPL,IBM,MSFT symbols: + connect demo.dxfeed.com:7300 Quote AAPL,IBM,MSFT + +Connects to the demo-endpoint and subscribes to Quote,Trade events for AAPL,MSFT symbols with aggregation period 5s: + connect demo.dxfeed.com:7300 Quote,Trade AAPL,MSFT -p dxfeed.aggregationPeriod=5s + +Connects to the demo-endpoint and subscribes to Order event for AAPL symbol with order source NTV (NASDAQ Total View): + connect demo.dxfeed.com:7300 Order AAPL -s NTV + +Connects to the demo-endpoint and subscribes to TimeAndSale event for AAPL symbol from starting January 1, 1970: + connect demo.dxfeed.com:7300 TimeAndSale AAPL --from-time 0 + +Connects to the tape file and subscribes for all events and all symbols: + connect file:tape.csv all all -p dxfeed.wildcard.enable=true)"}, + {"Dump", + R"(This tool dumps all received data records and subscription items to a console. It can connect to any address including +plain file. + +Examples: + +Dump all events and all symbols from the specified file: + dump tape.csv + +Dump all events and all symbols from the specified file at maximum speed: + dump tape.csv[speed=max] + +Dump all events and all symbols from the specified file at maximum speed, cyclically: + dump tape.csv[speed=max,cycle] + +Dump only Quote event for all symbols from the specified file: + dump tape.csv Quote + +Dump only Quote event for AAPL symbol from the specified file: + dump tape.csv Quote AAPL + +Dump only Quote event for AAPL symbol from the specified address in a stream contract: + dump demo.dxfeed.com:7300 Quote AAPL + +Tape only Quote event for AAPL symbol from the specified address in a stream contract into the specified tape file: + dump demo.dxfeed.com:7300 Quote AAPL -q -t tape.bin)"}, + {"PerfTest", R"(Examples: + +Connects to the local endpoint, subscribes to TimeAndSale event for ETH/USD:GDAX symbol and print performance counters: + perftest 127.0.0.1:7777 TimeAndSale ETH/USD:GDAX + +Connects to the tape file (on max speed and cyclically), subscribes for all symbols and print performance counters: + perftest file:tape.csv[speed=max,cycle] all all --force-stream)"}, + {"Address", + R"(To create a connection using any tool one must specify an address. Depending on an address format different message +connectors are used to establish connection. + +To establish multiple simultaneous connections multiple addresses can be specified like: +()()...() + +Every single address has the following format: +[@][]. + + is used to configure data or subscription filter in message adapter factory. + + is the list of initial properties passed to a connector. They have a form of comma-separated list +of pairs = enclosed in square [...] or round (...) brackets. Multiple pairs of brackets are also allowed, so +[key1=val1,key2=val2] has the same meaning as [key1=val1][key2=val2]. + +For Token-Based Authorization use "login=entitle:" property: +
[login=entitle:] + + format depends on particular connector. + +Available connectors are: +[name] [address format] [description] +ClientSocket : Connects to some host using TCP/IP client socket. +ServerSocket : Creates server TCP/IP socket connection. +NioServer nio: TCP/IP server socket connector with scalable non-blocking API. +File file: Connects to a file. +Tape tape: Writes data to tape files. + +To get detailed information about address format and available properties use "Help ".)"}, + {"ClientSocket", R"(Connects to some host using TCP/IP client socket. + +Address format: : + +Properties: +[type] [name] [description] +ConnectOrder connectOrder Order of considering specified server addresses during connect/reconnect: "shuffle" + (default), "random", "ordered", "priority" +String fieldReplacer Field Replacers for input connection +String name Name of this connector +String password User password +String proxyHost HTTP proxy host name +int proxyPort HTTP proxy port +long reconnectDelay Delay between reconnection attempts in milliseconds +int threadPriority Priority for threads associated with this connector +String user User login name)"}, + {"ServerSocket", R"(Creates server TCP/IP socket connection. + +Address format: : + +Properties: +[type] [name] [description] +String bindAddr Network interface address to bind socket to +String fieldReplacer Field Replacers for input connection +int maxConnections Max number of connections allowed for connector +String name Name of this connector +String password User password +long reconnectDelay Delay between reconnection attempts in milliseconds +int threadPriority Priority for threads associated with this connector +String user User login name)"}, + {"NioServer", R"(TCP/IP server socket connector with scalable non-blocking API. + +Address format: nio: + +Properties: +[type] [name] [description] +String bindAddr Network interface address to bind socket to +String fieldReplacer Field Replacers for input connection +int maxConnections Max number of connections allowed for connector +String name Name of this connector +String password User password +int readerThreads Number of reader threads in the pool +long reconnectDelay Delay between reconnection attempts in milliseconds +int socketTimeout SO_TIMEOUT option value +int threadPriority Priority for threads associated with this connector +String user User login name +int writerThreads Number of writer threads in a pool)"}, + {"File", R"(Connects to a file. + +Address format: file: + +Properties: +[type] [name] [description] +StreamCompression compression File compression (one of "none", "gzip", or "zip"), autodetect by default from + file header +boolean cycle Enables cycle playback +TimePeriod delayed Delay relatively to current time +String fieldReplacer Field Replacers for input connection +FileFormat format File format (one of "binary", "text", "csv", or "blob::"), + autodetect by default from file header +boolean ignoreTime Ignores ".time" files even if they present +String name Name of this connector +String password User password +MessageType readAs Overrides the type of read messages (one of "ticker_data", "stream_data", + "history_data", or "raw_data", works for binary tape files only) +long reconnectDelay Delay between reconnection attempts in milliseconds +MessageType resyncOn Message type to resync partial binary stream captured with tcpdump +boolean schemeKnown Enables parsing of files without record descriptions +double speed Replay speed vs real time, use "max" to read file as fast as possible, defaults to 1 +Date start Time to start playing from, use [YYYYMMDD-]HHMMSS[.sss][tz] format +Date stop Time to stop playing, use [YYYYMMDD-]HHMMSS[.sss][tz] format +int threadPriority Priority for threads associated with this connector +TimestampsType time Time format (one of "none", "long", "text", "field", or "message"), autodetect by + default +String user User login name + +File connector receives a URL of a file as an address and connects to it. It supports the same file formats as --tape +option of Connect tool (see "Help Tape") and they are usually combined. + +It can read and give out all the data presented in a file at once, or it can also use ".time" files to reproduce +original delays. By default it uses these files if they present, but this can be disabled by setting "ignoretime=true". + +File connector also supports reading multiple timestamped files. If filename contains special '~' marker, then it is +replaced with a timestamp. For example, address "file:records~.dat" means that we want to seek for files like +records20080320-154914+0030.dat and connect to them consecutively. Corresponding ".time" files are also used in that +case (if they exist).)"}, + {"Tape", R"(The --tape (-t) option of tools is used to tape all incoming data into the specified file. + +This option has several parameters: + +"format" parameter defines format of stored data. Its value can be one of "text", "binary" or +"blob::records>" (binary format is used by default). Blob is a special format that is +used for compact store of a single-record, single-symbol data stream. + +"saveas" parameter overrides the type of stored messages. Data messages can be stored as "ticker_data", "stream_data", +"history_data", or "raw_data". + +"split" parameter can be used to create multiple files with timestamped names. Its value is time period (see "Help time +format") determining how often must new files be created. When this parameter is defined a special '~' marker in file +name must be used (it will be replaced with file timestamp). + +"time" parameter defines whether to create ".time" files. These files store information about when each piece of data +was recorded and are used to read data from file in a realtime mode (keeping original time delays). ".time" file format +is simple: it contains lines with records of kind ":". The "time" parameter can be set into +one of "none", "long" or "text". In the first case no ".time" files are created. Otherwise the value defines whether to +write timestamps in ".time" files in old-style format (single long value in milliseconds) or in human-readable text +format (see "Help time format"). By default ".time" files with old-style timestamps are created only if the file has +".data" extension. + +Two special parameters "storagesize" and "storagetime" are used to drop out-of-date taped files. + +"storagetime" parameter value is a time-period (see "Help Time format"). If this parameter is defined then all taped +files which have timestamps less than current time minus "storagetime" value are deleted. + +"storagesize" parameter is a number followed by one-character suffix 'b' (bytes), 'k' (kilobytes), 'm' (megabytes), 'g' +(gigabytes). If the suffix is omitted then value in bytes is supposed. If this parameter is defined then the total size +of all existing taped files (not counting corresponding ".time" files) is kept at value not much greater than +"storagesize" value (also by means of deleting older files). + +Both these parameters guarantee that at least specified amount of data is always stored, i.e. they define low boundary +of stored data size. These two parameters can be useful if we are implementing data delay by taping data on disk. + +Note, that all files that match specified timestamped-file pattern are searched to be deleted when using these +parameters (i.e. even if these files weren't actually taped by this execution of connect tool). + +Examples: + +--tape quotes.log +will tape data into file "quotes.log" in binary format. No ".time" files will be created. + +--tape quotes.log[saveas=ticker_data] +will tape data into file "quotes.log" with "ticker_data" messages regardless of the orignal type of the incoming data +messages. + +--tape log[time=long] +will tape data into file "log" and timestamps into file "log.time" as long values (old-style). + +--tape log_~.txt[format=text,split=2m,time=text] +will tape data in text format. It will create files like +log_20071114-154214+0030.txt +log_20071114-154214+0030.time +log_20071114-154400+0300.txt +log_20071114-154400+0300.time +log_20071114-154600+0300.txt +log_20071114-154600+0300.time +... +creating new pair of ".txt" and ".time" files every 2 minutes. + +--tape log_~.dat[split=2m,time=text,storagesize=100k,storagetime=30m] +will act similar to a previous sample (except it will create ".dat" files and store data in binary format). Moreover it +will keep only files taped during last 30 minutes and total size of these files will be not much more than 100 kb.)"}, + {"Time Format", + R"(Many tools handle dates, times and periods as values of their arguments, options or some input or output values. A +common flexible format exists for parsing and formatting date-times and time-periods and it is used almost everywhere. + +Dates and times can be parsed from one of the following forms: + + + This is a standart java representation of time as a single long number. Value of msecs is measured from 1970-01-01. + Here the value should be positive and have at least 9 digits (otherwise it could not be distinguished from date in + format 'yyyyMMdd'). Each date since 1970-01-03 can be represented in this form. + +[