Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .clang-tidy
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Checks: >
bugprone-*,
-bugprone-easily-swappable-parameters,
clang-analyzer-*,
-clang-analyzer-optin.cplusplus.VirtualCall,
clang-diagnostic-*,
cppcore-guidelines-*,
google-*,
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## 0.45.0 - 2025-12-10

### Enhancements
- Added download retry, resumption, and checksum verification to
`Historical::BatchDownload`
- Added new venue, dataset, and publisher for Cboe Futures Exchange (`XCBF.PITCH`)
- Upgraded default `httplib` version to 0.28.0

## 0.44.0 - 2025-11-18

### Enhancements
Expand Down
11 changes: 9 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.24..4.0)

project(
databento
VERSION 0.44.0
VERSION 0.45.0
LANGUAGES CXX
DESCRIPTION "Official Databento client library"
)
Expand Down Expand Up @@ -128,6 +128,10 @@ endif()

find_package(OpenSSL REQUIRED)
find_package(zstd REQUIRED)
if (APPLE)
find_library(CORE_FOUNDATION_LIB CoreFoundation REQUIRED)
find_library(CFNETWORK_LIB CFNetwork REQUIRED)
endif()
if(NOT TARGET zstd::libzstd)
if(TARGET zstd::libzstd_shared)
add_library(zstd::libzstd ALIAS zstd::libzstd_shared)
Expand Down Expand Up @@ -178,7 +182,7 @@ if(${PROJECT_NAME_UPPERCASE}_USE_EXTERNAL_HTTPLIB)
find_package(httplib REQUIRED)
endif()
else()
set(httplib_version 0.20.0)
set(httplib_version 0.28.0)
FetchContent_Declare(
httplib
URL https://github.com/yhirose/cpp-httplib/archive/refs/tags/v${httplib_version}.tar.gz
Expand Down Expand Up @@ -223,6 +227,9 @@ target_link_libraries(
OpenSSL::SSL
Threads::Threads
zstd::libzstd
# macOS-specific libraries required by httplib
$<$<PLATFORM_ID:Darwin>:${CFNETWORK_LIB}>
$<$<PLATFORM_ID:Darwin>:${CORE_FRAMEWORK_LIB}>
)

target_compile_definitions(
Expand Down
2 changes: 2 additions & 0 deletions cmake/SourcesAndHeaders.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ set(headers
include/databento/detail/json_helpers.hpp
include/databento/detail/scoped_fd.hpp
include/databento/detail/scoped_thread.hpp
include/databento/detail/sha256_hasher.hpp
include/databento/detail/tcp_client.hpp
include/databento/detail/zstd_stream.hpp
include/databento/enums.hpp
Expand Down Expand Up @@ -54,6 +55,7 @@ set(sources
src/detail/http_client.cpp
src/detail/json_helpers.cpp
src/detail/scoped_fd.cpp
src/detail/sha256_hasher.cpp
src/detail/tcp_client.cpp
src/detail/zstd_stream.cpp
src/enums.cpp
Expand Down
1 change: 1 addition & 0 deletions cmake/StandardSettings.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# Project settings
#

option(${PROJECT_NAME_UPPERCASE}_USE_EXTERNAL_DATE "Use an external date library" OFF)
option(${PROJECT_NAME_UPPERCASE}_USE_EXTERNAL_JSON "Use an external JSON library" OFF)
option(${PROJECT_NAME_UPPERCASE}_USE_EXTERNAL_HTTPLIB "Use an external httplib library" OFF)
option(${PROJECT_NAME_UPPERCASE}_USE_EXTERNAL_GTEST "Use an external google test (gtest) library" ON)
Expand Down
2 changes: 1 addition & 1 deletion include/databento/detail/http_client.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class HttpClient {

nlohmann::json GetJson(const std::string& path, const httplib::Params& params);
nlohmann::json PostJson(const std::string& path, const httplib::Params& form_params);
void GetRawStream(const std::string& path, const httplib::Params& params,
void GetRawStream(const std::string& path, const httplib::Headers& headers,
const httplib::ContentReceiver& callback);
void PostRawStream(const std::string& path, const httplib::Params& form_params,
const httplib::ContentReceiver& callback);
Expand Down
26 changes: 26 additions & 0 deletions include/databento/detail/sha256_hasher.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#pragma once

#include <cstddef> // byte, size_t
#include <memory> // unique_ptr
#include <string>
#include <string_view>

// Forward declaration
struct evp_md_ctx_st;
using EVP_MD_CTX = evp_md_ctx_st;

namespace databento::detail {
// One-off hash
std::string Sha256Hash(std::string_view data);

class Sha256Hasher {
public:
Sha256Hasher();

void Update(const std::byte* buffer, std::size_t length);
std::string Finalize();

private:
std::unique_ptr<::EVP_MD_CTX, void (*)(::EVP_MD_CTX*)> ctx_;
};
} // namespace databento::detail
1 change: 1 addition & 0 deletions include/databento/file_stream.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class InFileStream : public IReadable {
class OutFileStream : public IWritable {
public:
explicit OutFileStream(const std::filesystem::path& file_path);
OutFileStream(const std::filesystem::path& file_path, std::ios_base::openmode mode);

void WriteAll(const std::byte* buffer, std::size_t length) override;

Expand Down
4 changes: 3 additions & 1 deletion include/databento/historical.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <filesystem>
#include <map> // multimap
#include <string>
#include <string_view>
#include <vector>

#include "databento/batch.hpp" // BatchJob
Expand Down Expand Up @@ -225,7 +226,8 @@ class Historical {
std::string user_agent_ext);

BatchJob BatchSubmitJob(const HttplibParams& params);
void DownloadFile(const std::string& url, const std::filesystem::path& output_path);
void DownloadFile(const std::string& url, const std::filesystem::path& output_path,
std::string_view hash, std::uint64_t exp_size);
std::vector<BatchJob> BatchListJobs(const HttplibParams& params);
std::vector<DatasetConditionDetail> MetadataGetDatasetCondition(
const HttplibParams& params);
Expand Down
6 changes: 6 additions & 0 deletions include/databento/publishers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ enum class Venue : std::uint16_t {
Xeur = 50,
// European Energy Exchange
Xeee = 51,
// Cboe Futures Exchange
Xcbf = 52,
};

// A source of data.
Expand Down Expand Up @@ -191,6 +193,8 @@ enum class Dataset : std::uint16_t {
XeurEobi = 38,
// European Energy Exchange EOBI
XeeeEobi = 39,
// Cboe Futures Exchange PITCH
XcbfPitch = 40,
};

// A specific Venue from a specific data source.
Expand Down Expand Up @@ -403,6 +407,8 @@ enum class Publisher : std::uint16_t {
XeurEobiXoff = 103,
// European Energy Exchange EOBI - Off-Market Trades
XeeeEobiXoff = 104,
// Cboe Futures Exchange
XcbfPitchXcbf = 105,
};

// Get a Publisher's Venue.
Expand Down
2 changes: 1 addition & 1 deletion pkg/PKGBUILD
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Maintainer: Databento <support@databento.com>
_pkgname=databento-cpp
pkgname=databento-cpp-git
pkgver=0.44.0
pkgver=0.45.0
pkgrel=1
pkgdesc="Official C++ client for Databento"
arch=('any')
Expand Down
5 changes: 2 additions & 3 deletions src/detail/http_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,12 @@ nlohmann::json HttpClient::PostJson(const std::string& path,
return HttpClient::CheckAndParseResponse(path, std::move(res));
}

void HttpClient::GetRawStream(const std::string& path, const httplib::Params& params,
void HttpClient::GetRawStream(const std::string& path, const httplib::Headers& headers,
const httplib::ContentReceiver& callback) {
const std::string full_path = httplib::append_query_params(path, params);
std::string err_body{};
int err_status{};
const httplib::Result res = client_.Get(
full_path, MakeStreamResponseHandler(err_status),
path, headers, MakeStreamResponseHandler(err_status),
[&callback, &err_body, &err_status](const char* data, std::size_t length) {
// if an error response was received, read all content into
// err_body
Expand Down
40 changes: 40 additions & 0 deletions src/detail/sha256_hasher.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#include "databento/detail/sha256_hasher.hpp"

#include <openssl/evp.h>

#include <ios> // hex, setw, setfill
#include <sstream>

#include "databento/exceptions.hpp"

std::string databento::detail::Sha256Hash(std::string_view data) {
Sha256Hasher hasher{};
hasher.Update(reinterpret_cast<const std::byte*>(data.data()), data.length());
return hasher.Finalize();
}

using databento::detail::Sha256Hasher;

Sha256Hasher::Sha256Hasher() : ctx_{::EVP_MD_CTX_new(), &::EVP_MD_CTX_free} {
::EVP_DigestInit_ex(ctx_.get(), ::EVP_sha256(), NULL);
}

void Sha256Hasher::Update(const std::byte* buffer, std::size_t length) {
if (!::EVP_DigestUpdate(ctx_.get(), buffer, length)) {
throw databento::Exception{"Failed to update SHA256 digest"};
}
}

std::string Sha256Hasher::Finalize() {
std::array<unsigned char, EVP_MAX_MD_SIZE> hash{};
unsigned int hash_length = 0;
if (!::EVP_DigestFinal_ex(ctx_.get(), hash.data(), &hash_length)) {
throw databento::Exception{"Failed to finalize SHA256 digest"};
}

std::ostringstream hash_hex_stream;
for (size_t i = 0; i < hash_length; ++i) {
hash_hex_stream << std::hex << std::setw(2) << std::setfill('0') << +hash[i];
}
return hash_hex_stream.str();
}
13 changes: 9 additions & 4 deletions src/file_stream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ InFileStream::InFileStream(const std::filesystem::path& file_path)
: stream_{file_path, std::ios::binary} {
if (stream_.fail()) {
throw InvalidArgumentError{"InFileStream", "file_path",
"Non-existent or invalid file at " + file_path.string()};
"Non-existent or invalid file: " + file_path.string()};
}
}

Expand All @@ -33,10 +33,15 @@ std::size_t InFileStream::ReadSome(std::byte* buffer, std::size_t max_length) {
using databento::OutFileStream;

OutFileStream::OutFileStream(const std::filesystem::path& file_path)
: stream_{file_path, std::ios::binary} {
: OutFileStream{file_path, std::ios::binary} {}

OutFileStream::OutFileStream(const std::filesystem::path& file_path,
std::ios_base::openmode mode)
: stream_{file_path, mode} {
if (stream_.fail()) {
throw InvalidArgumentError{"OutFileStream", "file_path",
"Non-existent or invalid file"};
throw InvalidArgumentError{
"OutFileStream", "file_path",
"Can't open file for writing at path: " + file_path.string()};
}
}

Expand Down
Loading
Loading