Skip to content

Commit c36f6c9

Browse files
committed
FIX: Fix Zstd decoding and batch download in C++
1 parent cd28657 commit c36f6c9

File tree

13 files changed

+82
-69
lines changed

13 files changed

+82
-69
lines changed

.github/workflows/build.yaml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
- name: Install dependencies
2323
run: |
2424
sudo apt-get update
25-
sudo apt-get install libzstd-dev ninja-build
25+
sudo apt-get install cppcheck libzstd-dev ninja-build
2626
- name: Install gtest
2727
uses: MarkusJx/googletest-installer@v1.1
2828
- name: CMake configure
@@ -33,6 +33,7 @@ jobs:
3333
-DDATABENTO_ENABLE_UNIT_TESTING=1 \
3434
-DDATABENTO_ENABLE_EXAMPLES=1 \
3535
-DDATABENTO_ENABLE_CLANG_TIDY=1 \
36+
-DDATABENTO_ENABLE_CPPCHECK=1 \
3637
-DDATABENTO_ENABLE_ASAN=1 \
3738
-DDATABENTO_ENABLE_UBSAN=1
3839
- name: CMake build
@@ -48,16 +49,16 @@ jobs:
4849
- name: Checkout repository
4950
uses: actions/checkout@v3
5051
- name: Install dependencies
51-
run: brew install cmake googletest openssl@3 ninja zstd
52+
run: brew install cmake cppcheck googletest openssl@3 ninja zstd
5253
# Don't enable clang-tidy on macOS because it's incredibly slow
5354
- name: CMake configure
5455
run: |
5556
cmake -S . -B build \
5657
-GNinja \
5758
-DDATABENTO_ENABLE_UNIT_TESTING=1 \
5859
-DDATABENTO_ENABLE_EXAMPLES=1 \
59-
-DDATABENTO_ENABLE_ASAN=1 \
60-
-DDATABENTO_ENABLE_UBSAN=1 \
60+
-DDATABENTO_ENABLE_CPPCHECK=1 \
61+
-DDATABENTO_ENABLE_TSAN=1 \
6162
-DOPENSSL_ROOT_DIR=$(brew --prefix openssl@3)
6263
- name: CMake build
6364
run: cmake --build build

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,13 @@
44

55
#### Enhancements
66
- Added constants for dataset codes for Databento Equity Basic and OPRA Pillar
7+
- Added `const char*` getters to records for fixed-length `char` arrays
8+
- Added `RType` getter to `Record`
79

810
#### Bug fixes
911
- Batch live subscriptions to avoid hitting max message length
12+
- Fix bug in Zstd decompression
13+
- Fix `Historical::BatchDownload` truncating file before writing each chunk
1014

1115
## 0.9.0 - 2023-06-13
1216

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ else()
135135
)
136136
endif()
137137
FetchContent_MakeAvailable(json)
138+
# Ignore compiler warnings in headers
139+
add_system_include_property(nlohmann_json)
138140
endif()
139141
# cpp-httplib
140142
set(httplib_version 0.11.4)

cmake/StaticAnalyzers.cmake

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,13 @@ if(${PROJECT_NAME_UPPERCASE}_ENABLE_CPPCHECK)
1717
set(
1818
CMAKE_CXX_CPPCHECK ${CPPCHECK}
1919
--enable=all
20-
--suppress=missingIncludeSystem
20+
--suppress=missingIncludeSystem # False positives
2121
--suppress=preprocessorErrorDirective
22-
--suppress=unusedFunction
23-
--suppress=unmatchedSuppression
22+
--suppress=unusedFunction # False positives
23+
--suppress=unmatchedSuppression # Support different cppcheck versions
2424
--inline-suppr
2525
--relative-paths=${CMAKE_SOURCE_DIR}
2626
--quiet
27-
-I${CMAKE_SOURCE_DIR}/include
28-
-I${CMAKE_SOURCE_DIR}/src
29-
# Ignore tests
30-
-i${CMAKE_SOURCE_DIR}/test
31-
# Ignore third-party dependencies
32-
-i${CMAKE_BINARY_DIR}
3327
)
3428
message(STATUS "Cppcheck finished setting up.")
3529
else()

include/databento/historical.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,8 @@ class Historical {
237237
using HttplibParams = std::multimap<std::string, std::string>;
238238

239239
BatchJob BatchSubmitJob(const HttplibParams& params);
240+
void StreamToFile(const std::string& url_path, const HttplibParams& params,
241+
const std::string& file_path);
240242
void DownloadFile(const std::string& url, const std::string& output_path);
241243
std::vector<BatchJob> BatchListJobs(const HttplibParams& params);
242244
std::vector<DatasetConditionDetail> MetadataGetDatasetCondition(

include/databento/record.hpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class Record {
3636
explicit Record(RecordHeader* record) : record_{record} {}
3737

3838
const RecordHeader& Header() const { return *record_; }
39+
::databento::RType RType() const { return record_->rtype; }
3940

4041
template <typename T>
4142
bool Holds() const {
@@ -53,7 +54,7 @@ class Record {
5354

5455
std::size_t Size() const;
5556
static std::size_t SizeOfSchema(Schema schema);
56-
static RType RTypeFromSchema(Schema schema);
57+
static ::databento::RType RTypeFromSchema(Schema schema);
5758

5859
private:
5960
RecordHeader* record_;
@@ -168,6 +169,21 @@ static_assert(sizeof(OhlcvMsg) == 56, "OhlcvMsg size must match C");
168169
struct InstrumentDefMsg {
169170
static bool HasRType(RType rtype) { return rtype == RType::InstrumentDef; }
170171

172+
const char* Currency() const { return currency.data(); }
173+
const char* SettlCurrency() const { return settl_currency.data(); }
174+
const char* SecSubType() const { return secsubtype.data(); }
175+
const char* RawSymbol() const { return raw_symbol.data(); }
176+
const char* Group() const { return group.data(); }
177+
const char* Exchange() const { return exchange.data(); }
178+
const char* Asset() const { return asset.data(); }
179+
const char* Cfi() const { return cfi.data(); }
180+
const char* SecurityType() const { return security_type.data(); }
181+
const char* UnitOfMeasure() const { return unit_of_measure.data(); }
182+
const char* Underlying() const { return underlying.data(); }
183+
const char* StrikePriceCurrency() const {
184+
return strike_price_currency.data();
185+
}
186+
171187
RecordHeader hd;
172188
UnixNanos ts_recv;
173189
std::int64_t min_price_increment;
@@ -308,6 +324,9 @@ struct ErrorMsg {
308324
struct SymbolMappingMsg {
309325
static bool HasRType(RType rtype) { return rtype == RType::SymbolMapping; }
310326

327+
const char* STypeInSymbol() const { return stype_in_symbol.data(); }
328+
const char* STypeOutSymbol() const { return stype_out_symbol.data(); }
329+
311330
RecordHeader hd;
312331
std::array<char, 22> stype_in_symbol;
313332
std::array<char, 22> stype_out_symbol;

src/dbn_decoder.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ databento::Metadata DbnDecoder::DecodeMetadata() {
151151
const auto version_and_size =
152152
DbnDecoder::DecodeMetadataVersionAndSize(buffer_.data(), 8);
153153
buffer_.resize(version_and_size.second);
154-
input_->ReadExact(buffer_.data(), version_and_size.second);
154+
input_->ReadExact(buffer_.data(), buffer_.size());
155155
buffer_idx_ = buffer_.size();
156156
return DbnDecoder::DecodeMetadataFields(version_and_size.first, buffer_);
157157
}

src/detail/json_helpers.cpp

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,7 @@ std::vector<std::string> ParseAt(const std::string& endpoint,
9696
if (!symbols_json.is_array()) {
9797
throw JsonResponseError::TypeMismatch(endpoint, key + " array", json);
9898
}
99-
std::vector<std::string> res;
100-
res.reserve(symbols_json.size());
101-
for (const auto& item : symbols_json.items()) {
102-
res.emplace_back(item.value());
103-
}
104-
return res;
99+
return {symbols_json.begin(), symbols_json.end()};
105100
}
106101

107102
} // namespace detail

src/detail/zstd_stream.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ ZstdStream::ZstdStream(std::unique_ptr<IReadable> input,
2121
void ZstdStream::ReadExact(std::uint8_t* buffer, std::size_t length) {
2222
std::size_t size{};
2323
do {
24-
size += ReadSome(buffer, length - size);
24+
size += ReadSome(&buffer[size], length - size);
2525
} while (size < length && read_suggestion_ != 0);
2626
// check for end of stream without obtaining `length` bytes
2727
if (size < length) {

src/historical.cpp

Lines changed: 29 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <exception> // exception, exception_ptr
1111
#include <fstream> // ofstream
1212
#include <ios> // ios::binary
13+
#include <iterator> // back_inserter
1314
#include <memory> // unique_ptr
1415
#include <string>
1516
#include <utility> // move
@@ -245,10 +246,8 @@ std::vector<databento::BatchJob> Historical::BatchListJobs(
245246
throw JsonResponseError::TypeMismatch(kEndpoint, "array", json);
246247
}
247248
std::vector<BatchJob> jobs;
248-
jobs.reserve(json.size());
249-
for (const auto& job_json : json.items()) {
250-
jobs.emplace_back(::Parse(kEndpoint, job_json.value()));
251-
}
249+
std::transform(json.begin(), json.end(), std::back_inserter(jobs),
250+
[](const auto& item) { return ::Parse(kEndpoint, item); });
252251
return jobs;
253252
}
254253

@@ -316,6 +315,21 @@ std::string Historical::BatchDownload(const std::string& output_dir,
316315
return output_path;
317316
}
318317

318+
void Historical::StreamToFile(const std::string& url_path,
319+
const HttplibParams& params,
320+
const std::string& file_path) {
321+
std::ofstream out_file{file_path, std::ios::binary};
322+
if (out_file.fail()) {
323+
throw InvalidArgumentError{"Historical::StreamToFile", "file_path",
324+
"Failed to open file"};
325+
}
326+
this->client_.GetRawStream(
327+
url_path, params, [&out_file](const char* data, std::size_t length) {
328+
out_file.write(data, static_cast<std::streamsize>(length));
329+
return true;
330+
});
331+
}
332+
319333
void Historical::DownloadFile(const std::string& url,
320334
const std::string& output_path) {
321335
static const std::string kEndpoint = "Historical::BatchDownload";
@@ -338,12 +352,7 @@ void Historical::DownloadFile(const std::string& url,
338352
path = url.substr(slash);
339353
}
340354

341-
client_.GetRawStream(
342-
path, {}, [&output_path](const char* data, std::size_t length) {
343-
std::ofstream out_file{output_path};
344-
out_file.write(data, static_cast<std::streamsize>(length));
345-
return KeepGoing::Continue;
346-
});
355+
StreamToFile(path, {}, output_path);
347356
}
348357

349358
std::map<std::string, std::int32_t> Historical::MetadataListPublishers() {
@@ -848,14 +857,15 @@ databento::SymbologyResolution Historical::SymbologyResolve(
848857
mapping_json);
849858
}
850859
std::vector<StrMappingInterval> mapping_intervals;
851-
mapping_intervals.reserve(mapping_json.size());
852-
for (const auto& interval_json : mapping_json.items()) {
853-
mapping_intervals.emplace_back(StrMappingInterval{
854-
detail::CheckedAt(kEndpoint, interval_json.value(), "d0"),
855-
detail::CheckedAt(kEndpoint, interval_json.value(), "d1"),
856-
detail::CheckedAt(kEndpoint, interval_json.value(), "s"),
857-
});
858-
}
860+
std::transform(mapping_json.begin(), mapping_json.end(),
861+
std::back_inserter(mapping_intervals),
862+
[](const auto& interval_json) {
863+
return StrMappingInterval{
864+
detail::CheckedAt(kEndpoint, interval_json, "d0"),
865+
detail::CheckedAt(kEndpoint, interval_json, "d1"),
866+
detail::CheckedAt(kEndpoint, interval_json, "s"),
867+
};
868+
});
859869
res.mappings.emplace(mapping.key(), std::move(mapping_intervals));
860870
}
861871
if (!partial_json.is_array()) {
@@ -1053,19 +1063,7 @@ databento::DbnFileStore Historical::TimeseriesGetRangeToFile(
10531063
}
10541064
databento::DbnFileStore Historical::TimeseriesGetRangeToFile(
10551065
const HttplibParams& params, const std::string& file_path) {
1056-
{
1057-
std::ofstream out_file{file_path, std::ios::binary};
1058-
if (out_file.fail()) {
1059-
throw InvalidArgumentError{kTimeseriesGetRangeEndpoint, "file_path",
1060-
"Non-existent or invalid file"};
1061-
}
1062-
this->client_.GetRawStream(
1063-
kTimeseriesGetRangePath, params,
1064-
[&out_file](const char* data, std::size_t length) {
1065-
out_file.write(data, static_cast<std::streamsize>(length));
1066-
return true;
1067-
});
1068-
} // close out_file
1066+
StreamToFile(kTimeseriesGetRangePath, params, file_path);
10691067
return DbnFileStore{file_path};
10701068
}
10711069

0 commit comments

Comments
 (0)