Skip to content

Commit

Permalink
MOD: Improve FlagSet in C++
Browse files Browse the repository at this point in the history
  • Loading branch information
threecgreen committed Apr 26, 2024
1 parent bde3e96 commit 57314c4
Show file tree
Hide file tree
Showing 8 changed files with 175 additions and 121 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
## 0.18.0 - TBD

### Breaking changes
- Changed `FlagSet` to be more class-like:
- Added predicate methods and setters for each bit flag
- Improved string formatting
- Removed bitwise operators. Bitwise operations can be performed by first casting to a
`std::uint8_t` or calling the `Raw()` method
- Changed format of `display_factor` and `price_ratio` to a fixed-precision decimal for
`InstrumentDefMsg` and `InstrumentDefMsgV1` to match existing values and DBN crate
- Changed format of `unit_of_measure_qty` to a fixed-precision decimal for
Expand Down
33 changes: 17 additions & 16 deletions cmake/SourcesAndHeaders.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ set(headers
include/databento/dbn.hpp
include/databento/dbn_decoder.hpp
include/databento/dbn_file_store.hpp
include/databento/detail/file_stream.hpp
include/databento/detail/http_client.hpp
include/databento/detail/json_helpers.hpp
include/databento/detail/scoped_fd.hpp
include/databento/detail/scoped_thread.hpp
include/databento/detail/shared_channel.hpp
include/databento/detail/tcp_client.hpp
include/databento/detail/zstd_stream.hpp
include/databento/enums.hpp
include/databento/exceptions.hpp
include/databento/fixed_price.hpp
Expand All @@ -23,14 +31,6 @@ set(headers
include/databento/symbology.hpp
include/databento/timeseries.hpp
include/databento/with_ts_out.hpp
include/databento/detail/file_stream.hpp
include/databento/detail/http_client.hpp
include/databento/detail/json_helpers.hpp
include/databento/detail/scoped_fd.hpp
include/databento/detail/scoped_thread.hpp
include/databento/detail/shared_channel.hpp
include/databento/detail/tcp_client.hpp
include/databento/detail/zstd_stream.hpp
src/stream_op_helper.hpp
)

Expand All @@ -40,10 +40,18 @@ set(sources
src/datetime.cpp
src/dbn.cpp
src/dbn_decoder.cpp
src/dbn_file_store.cpp
src/detail/file_stream.cpp
src/detail/http_client.cpp
src/detail/json_helpers.cpp
src/detail/scoped_fd.cpp
src/detail/shared_channel.cpp
src/detail/tcp_client.cpp
src/detail/zstd_stream.cpp
src/enums.cpp
src/exceptions.cpp
src/dbn_file_store.cpp
src/fixed_price.cpp
src/flag_set.cpp
src/historical.cpp
src/live.cpp
src/live_blocking.cpp
Expand All @@ -54,11 +62,4 @@ set(sources
src/record.cpp
src/symbol_map.cpp
src/symbology.cpp
src/detail/file_stream.cpp
src/detail/http_client.cpp
src/detail/json_helpers.cpp
src/detail/scoped_fd.cpp
src/detail/shared_channel.cpp
src/detail/tcp_client.cpp
src/detail/zstd_stream.cpp
)
101 changes: 58 additions & 43 deletions include/databento/flag_set.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#pragma once

#include <bitset>
#include <cstdint>
#include <ostream>
#include <string>

namespace databento {
// Transparent wrapper around the bit flags used in several DBN record types.
Expand All @@ -11,77 +11,92 @@ class FlagSet {
using Repr = std::uint8_t;
// Indicates it's the last message in the packet from the venue for a given
// `instrument_id`.
static constexpr Repr kLast = 1 << 7;
static inline constexpr Repr kLast = 1 << 7;
// Indicates a top-of-book message, not an individual order.
static constexpr Repr kTob = 1 << 6;
static inline constexpr Repr kTob = 1 << 6;
// Indicates the message was sourced from a replay, such as a snapshot
// server.
static constexpr Repr kSnapshot = 1 << 5;
static inline constexpr Repr kSnapshot = 1 << 5;
// Indicates an aggregated price level message, not an individual order.
static constexpr Repr kMbp = 1 << 4;
static inline constexpr Repr kMbp = 1 << 4;
// Indicates the `ts_recv` value is inaccurate due to clock issues or packet
// reordering.
static constexpr Repr kBadTsRecv = 1 << 3;
static inline constexpr Repr kBadTsRecv = 1 << 3;
// Indicates an unrecoverable gap was detected in the channel.
static constexpr Repr kMaybeBadBook = 1 << 2;
static inline constexpr Repr kMaybeBadBook = 1 << 2;

friend std::ostream& operator<<(std::ostream&, FlagSet);

constexpr FlagSet() = default;
constexpr FlagSet() : repr_{0} {};

constexpr FlagSet( // cppcheck-suppress noExplicitConstructor
std::uint8_t repr)
: repr_{repr} {}
explicit constexpr FlagSet(Repr repr) : repr_{repr} {}

explicit constexpr operator std::uint8_t() const { return repr_; }

constexpr FlagSet operator~() const {
return FlagSet{static_cast<Repr>(~repr_)};
}
constexpr bool operator==(FlagSet rhs) const { return repr_ == rhs.repr_; }
constexpr bool operator!=(FlagSet rhs) const { return repr_ != rhs.repr_; }

constexpr FlagSet operator|(FlagSet rhs) const {
return FlagSet{static_cast<Repr>(repr_ | rhs.repr_)};
FlagSet Clear() {
repr_ = 0;
return *this;
}

constexpr FlagSet operator&(FlagSet rhs) const {
return FlagSet{static_cast<Repr>(repr_ & rhs.repr_)};
}
constexpr Repr Raw() const { return repr_; }
void SetRaw(Repr raw) { repr_ = raw; }

constexpr FlagSet operator^(FlagSet rhs) const {
return FlagSet{static_cast<Repr>(repr_ ^ rhs.repr_)};
// Checks if any flags are set.
constexpr bool Any() const { return repr_ != 0; }
constexpr bool IsEmpty() const { return repr_ == 0; }
constexpr bool IsLast() const { return bits_.last; }
FlagSet SetLast() {
bits_.last = true;
return *this;
}

FlagSet operator|=(FlagSet rhs) {
repr_ = repr_ | rhs.repr_;
constexpr bool IsTob() const { return bits_.tob; }
FlagSet SetTob() {
bits_.tob = true;
return *this;
}

FlagSet operator&=(FlagSet rhs) {
repr_ = repr_ & rhs.repr_;
constexpr bool IsSnapshot() const { return bits_.snapshot; }
FlagSet SetSnapshot() {
bits_.snapshot = true;
return *this;
}

FlagSet operator^=(FlagSet rhs) {
repr_ = repr_ ^ rhs.repr_;
constexpr bool IsMbp() const { return bits_.mbp; }
FlagSet SetMbp() {
bits_.mbp = true;
return *this;
}
constexpr bool IsBadTsRecv() const { return bits_.bad_ts_recv; }
FlagSet SetBadTsRecv() {
bits_.bad_ts_recv = true;
return *this;
}
constexpr bool IsMaybeBadBook() const { return bits_.maybe_bad_book; }
FlagSet SetMaybeBadBook() {
bits_.maybe_bad_book = true;
return *this;
}

constexpr bool operator==(FlagSet rhs) const { return repr_ == rhs.repr_; }

constexpr bool operator!=(FlagSet rhs) const { return repr_ != rhs.repr_; }

// Checks if any flags are set.
constexpr bool Any() const { return repr_ != 0; }

private:
Repr repr_{};
struct BitFlags {
bool reserved0 : 1;
bool reserved1 : 1;
bool maybe_bad_book : 1;
bool bad_ts_recv : 1;
bool mbp : 1;
bool snapshot : 1;
bool tob : 1;
bool last : 1;
};
union {
BitFlags bits_;
Repr repr_;
};
};

inline std::ostream& operator<<(std::ostream& stream, FlagSet flag) {
// print as binary
stream << "0b" << std::bitset<8>{flag.repr_};
return stream;
}
std::ostream& operator<<(std::ostream& stream, FlagSet flag_set);
std::string ToString(FlagSet flags);

static_assert(sizeof(FlagSet) == sizeof(std::uint8_t),
"FlagSet must be a transparent wrapper around std::uint8_t");
Expand Down
41 changes: 41 additions & 0 deletions src/flag_set.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include "databento/flag_set.hpp"

#include <array>

#include "stream_op_helper.hpp"

namespace databento {
std::ostream& operator<<(std::ostream& stream, FlagSet flag_set) {
constexpr std::array<std::pair<bool (FlagSet::*)() const, const char*>, 6>
kFlagsAndNames = {{
{&FlagSet::IsLast, "LAST"},
{&FlagSet::IsTob, "TOB"},
{&FlagSet::IsSnapshot, "SNAPSHOT"},
{&FlagSet::IsMbp, "MBP"},
{&FlagSet::IsBadTsRecv, "BAD_TS_RECV"},
{&FlagSet::IsMaybeBadBook, "MAYBE_BAD_BOOK"},
}};

bool has_written_flag = false;
for (const auto& pair : kFlagsAndNames) {
if ((flag_set.*pair.first)()) {
if (has_written_flag) {
stream << " | " << pair.second;
} else {
stream << pair.second;
has_written_flag = true;
}
}
}
// Cast to uint16_t to avoid being formatted as char
const auto raw = static_cast<std::uint16_t>(flag_set.Raw());
if (has_written_flag) {
stream << " (" << raw << ')';
} else {
stream << raw;
}
return stream;
}

std::string ToString(FlagSet flags) { return MakeString(flags); }
} // namespace databento
24 changes: 12 additions & 12 deletions test/src/dbn_decoder_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ TEST_P(DbnDecoderSchemaTests, TestDecodeMbo) {
EXPECT_EQ(ch_mbo1.order_id, 647784973705);
EXPECT_EQ(ch_mbo1.price, 3722750000000);
EXPECT_EQ(ch_mbo1.size, 1);
EXPECT_EQ(ch_mbo1.flags, 128);
EXPECT_EQ(ch_mbo1.flags.Raw(), 128);
EXPECT_EQ(ch_mbo1.channel_id, 0);
EXPECT_EQ(ch_mbo1.action, Action::Cancel);
EXPECT_EQ(ch_mbo1.side, Side::Ask);
Expand All @@ -280,7 +280,7 @@ TEST_P(DbnDecoderSchemaTests, TestDecodeMbo) {
EXPECT_EQ(ch_mbo2.order_id, 647784973631);
EXPECT_EQ(ch_mbo2.price, 3723000000000);
EXPECT_EQ(ch_mbo2.size, 1);
EXPECT_EQ(ch_mbo2.flags, 128);
EXPECT_EQ(ch_mbo2.flags.Raw(), 128);
EXPECT_EQ(ch_mbo2.channel_id, 0);
EXPECT_EQ(ch_mbo2.action, Action::Cancel);
EXPECT_EQ(ch_mbo2.side, Side::Ask);
Expand Down Expand Up @@ -327,7 +327,7 @@ TEST_P(DbnDecoderSchemaTests, TestDecodeMbp1) {
EXPECT_EQ(ch_mbp1.size, 1);
EXPECT_EQ(ch_mbp1.action, Action::Add);
EXPECT_EQ(ch_mbp1.side, Side::Ask);
EXPECT_EQ(ch_mbp1.flags, 128);
EXPECT_EQ(ch_mbp1.flags.Raw(), 128);
EXPECT_EQ(ch_mbp1.depth, 0);
EXPECT_EQ(ch_mbp1.ts_recv.time_since_epoch().count(), 1609160400006136329);
EXPECT_EQ(ch_mbp1.ts_in_delta.count(), 17214);
Expand Down Expand Up @@ -356,7 +356,7 @@ TEST_P(DbnDecoderSchemaTests, TestDecodeMbp1) {
EXPECT_EQ(ch_mbp2.size, 1);
EXPECT_EQ(ch_mbp2.action, Action::Add);
EXPECT_EQ(ch_mbp2.side, Side::Ask);
EXPECT_EQ(ch_mbp2.flags, 128);
EXPECT_EQ(ch_mbp2.flags.Raw(), 128);
EXPECT_EQ(ch_mbp2.depth, 0);
EXPECT_EQ(ch_mbp2.ts_recv.time_since_epoch().count(), 1609160400006246513);
EXPECT_EQ(ch_mbp2.ts_in_delta.count(), 18858);
Expand Down Expand Up @@ -407,7 +407,7 @@ TEST_P(DbnDecoderSchemaTests, TestDecodeMbp10) {
EXPECT_EQ(ch_mbp1.size, 1);
EXPECT_EQ(ch_mbp1.action, Action::Cancel);
EXPECT_EQ(ch_mbp1.side, Side::Ask);
EXPECT_EQ(ch_mbp1.flags, 128);
EXPECT_EQ(ch_mbp1.flags.Raw(), 128);
EXPECT_EQ(ch_mbp1.depth, 9);
EXPECT_EQ(ch_mbp1.ts_recv.time_since_epoch().count(), 1609160400000704060);
EXPECT_EQ(ch_mbp1.ts_in_delta.count(), 22993);
Expand Down Expand Up @@ -448,7 +448,7 @@ TEST_P(DbnDecoderSchemaTests, TestDecodeMbp10) {
EXPECT_EQ(ch_mbp2.size, 1);
EXPECT_EQ(ch_mbp2.action, Action::Cancel);
EXPECT_EQ(ch_mbp2.side, Side::Bid);
EXPECT_EQ(ch_mbp2.flags, 128);
EXPECT_EQ(ch_mbp2.flags.Raw(), 128);
EXPECT_EQ(ch_mbp2.depth, 1);
EXPECT_EQ(ch_mbp2.ts_recv.time_since_epoch().count(), 1609160400000750544);
EXPECT_EQ(ch_mbp2.ts_in_delta.count(), 20625);
Expand Down Expand Up @@ -511,7 +511,7 @@ TEST_P(DbnDecoderSchemaTests, TestDecodeCbbo) {
EXPECT_EQ(ch_cbbo1.size, 1);
EXPECT_EQ(ch_cbbo1.action, Action::Add);
EXPECT_EQ(ch_cbbo1.side, Side::Ask);
EXPECT_EQ(ch_cbbo1.flags, 128);
EXPECT_EQ(ch_cbbo1.flags.Raw(), 128);
EXPECT_EQ(ch_cbbo1.ts_recv.time_since_epoch().count(), 1609160400006136329);
EXPECT_EQ(ch_cbbo1.ts_in_delta.count(), 17214);
EXPECT_EQ(ch_cbbo1.sequence, 1170362);
Expand Down Expand Up @@ -539,7 +539,7 @@ TEST_P(DbnDecoderSchemaTests, TestDecodeCbbo) {
EXPECT_EQ(ch_cbbo2.size, 1);
EXPECT_EQ(ch_cbbo2.action, Action::Add);
EXPECT_EQ(ch_cbbo2.side, Side::Ask);
EXPECT_EQ(ch_cbbo2.flags, 128);
EXPECT_EQ(ch_cbbo2.flags.Raw(), 128);
EXPECT_EQ(ch_cbbo2.ts_recv.time_since_epoch().count(), 1609160400006246513);
EXPECT_EQ(ch_cbbo2.ts_in_delta.count(), 18858);
EXPECT_EQ(ch_cbbo2.sequence, 1170364);
Expand Down Expand Up @@ -589,7 +589,7 @@ TEST_P(DbnDecoderSchemaTests, TestDecodeTbbo) {
EXPECT_EQ(ch_tbbo1.size, 5);
EXPECT_EQ(ch_tbbo1.action, Action::Trade);
EXPECT_EQ(ch_tbbo1.side, Side::Ask);
EXPECT_EQ(ch_tbbo1.flags, 129);
EXPECT_EQ(ch_tbbo1.flags.Raw(), 129);
EXPECT_EQ(ch_tbbo1.depth, 0);
EXPECT_EQ(ch_tbbo1.ts_recv.time_since_epoch().count(), 1609160400099150057);
EXPECT_EQ(ch_tbbo1.ts_in_delta.count(), 19251);
Expand Down Expand Up @@ -618,7 +618,7 @@ TEST_P(DbnDecoderSchemaTests, TestDecodeTbbo) {
EXPECT_EQ(ch_tbbo2.size, 21);
EXPECT_EQ(ch_tbbo2.action, Action::Trade);
EXPECT_EQ(ch_tbbo2.side, Side::Ask);
EXPECT_EQ(ch_tbbo2.flags, 129);
EXPECT_EQ(ch_tbbo2.flags.Raw(), 129);
EXPECT_EQ(ch_tbbo2.depth, 0);
EXPECT_EQ(ch_tbbo2.ts_recv.time_since_epoch().count(), 1609160400108142648);
EXPECT_EQ(ch_tbbo2.ts_in_delta.count(), 20728);
Expand Down Expand Up @@ -669,7 +669,7 @@ TEST_P(DbnDecoderSchemaTests, TestDecodeTrades) {
EXPECT_EQ(ch_trade1.size, 5);
EXPECT_EQ(ch_trade1.action, Action::Trade);
EXPECT_EQ(ch_trade1.side, Side::Ask);
EXPECT_EQ(ch_trade1.flags, 129);
EXPECT_EQ(ch_trade1.flags.Raw(), 129);
EXPECT_EQ(ch_trade1.depth, 0);
EXPECT_EQ(ch_trade1.ts_recv.time_since_epoch().count(), 1609160400099150057);
EXPECT_EQ(ch_trade1.ts_in_delta.count(), 19251);
Expand All @@ -692,7 +692,7 @@ TEST_P(DbnDecoderSchemaTests, TestDecodeTrades) {
EXPECT_EQ(ch_trade2.size, 21);
EXPECT_EQ(ch_trade2.action, Action::Trade);
EXPECT_EQ(ch_trade2.side, Side::Ask);
EXPECT_EQ(ch_trade2.flags, 129);
EXPECT_EQ(ch_trade2.flags.Raw(), 129);
EXPECT_EQ(ch_trade2.depth, 0);
EXPECT_EQ(ch_trade2.ts_recv.time_since_epoch().count(), 1609160400108142648);
EXPECT_EQ(ch_trade2.ts_in_delta.count(), 20728);
Expand Down
Loading

0 comments on commit 57314c4

Please sign in to comment.