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
2 changes: 1 addition & 1 deletion .clang-tidy-minimal
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Checks: >
readability-string-compare,
readability-uniqueptr-delete-release,

ExtraArgs:
ExtraArgsBefore:
Comment thread
paulquiring marked this conversation as resolved.
- '-Wno-error=deprecated-declarations'

CheckOptions:
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/coverage_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ on:
workflow_call:
jobs:
coverage-report:
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
steps:
- name: Checkout Repository
uses: actions/checkout@v6
Expand All @@ -50,6 +50,7 @@ jobs:
genhtml "$(bazel info output_path)/_coverage/_coverage_report.dat" \
-o=cpp_coverage \
--show-details \
--source-directory="$(bazel info execution_root)" \
--legend \
--function-coverage \
--branch-coverage
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ compile_commands.json
# docs build artifacts
_build
docs/ubproject.toml

# coverage
cpp_coverage
4 changes: 3 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,6 @@ repos:
files: \.(c|cpp|h|hpp)$
- id: clang-tidy
args: ["--config-file=.clang-tidy-minimal", "-p=$(pwd)", "--warnings-as-errors=*"]
exclude: '_test\.cpp$'
# load_buffer_internal.hpp is a private header with no compile commands, so clang-tidy will fail on it.
# The header is still checked via load_buffer.cpp.
exclude: '(_test\.cpp|load_buffer_internal\.hpp)$'
46 changes: 44 additions & 2 deletions score/flatbuffers/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@
#
# SPDX-License-Identifier: Apache-2.0
# *******************************************************************************
load("@rules_cc//cc:cc_library.bzl", "cc_library")

load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")

cc_library(
name = "flatbufferscpp",
srcs = [
"src/idl_parser.cpp",
"details/idl_parser.cpp",
],
hdrs = [
"@flatbuffers//:include/flatbuffers/allocator.h",
Expand All @@ -38,3 +39,44 @@ cc_library(
strip_include_prefix = "/include",
visibility = ["//visibility:public"],
)

cc_library(
name = "flatbufferutils",
srcs = [
"details/load_buffer.cpp",
"details/load_buffer_internal.hpp",
],
hdrs = [
"load_buffer.hpp",
],
visibility = ["//visibility:public"],
deps = [
"@score_baselibs//score/filesystem",
"@score_baselibs//score/os:fcntl",
"@score_baselibs//score/os:stat",
"@score_baselibs//score/os:unistd",
"@score_baselibs//score/result",
],
)

cc_test(
name = "load_buffer_unit_test",
srcs = ["details/load_buffer_test.cpp"],
tags = ["unit"],
Comment thread
paulquiring marked this conversation as resolved.
deps = [
":flatbufferutils",
"@googletest//:gtest_main",
"@score_baselibs//score/os/mocklib:fcntl_mock",
"@score_baselibs//score/os/mocklib:stat_mock",
"@score_baselibs//score/os/mocklib:unistd_mock",
],
)

cc_test(
name = "load_buffer_test",
Comment thread
paulquiring marked this conversation as resolved.
srcs = ["test/load_buffer_test.cpp"],
deps = [
":flatbufferutils",
"@googletest//:gtest_main",
],
)
41 changes: 41 additions & 0 deletions score/flatbuffers/details/load_buffer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/********************************************************************************
* Copyright (c) 2026 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
********************************************************************************/

#include "score/flatbuffers/load_buffer.hpp"
#include "score/flatbuffers/details/load_buffer_internal.hpp"

namespace score
{

namespace flatbuffers
{

score::os::Result<std::vector<uint8_t>> LoadBuffer(const score::filesystem::Path& path) noexcept
{
std::vector<uint8_t> data;
const auto read_result = detail::LoadBufferImpl(detail::OS{}, path, data);
if (read_result.has_value())
{
return std::move(data);
}
return score::cpp::make_unexpected(read_result.error());
}

score::os::Result<score::cpp::blank> LoadBuffer(const score::filesystem::Path& path,
std::pmr::vector<uint8_t>& data) noexcept
{
return detail::LoadBufferImpl(detail::OS{}, path, data);
}

} // namespace flatbuffers
} // namespace score
145 changes: 145 additions & 0 deletions score/flatbuffers/details/load_buffer_internal.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/********************************************************************************
* Copyright (c) 2026 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
********************************************************************************/

#ifndef SCORE_LIB_FLATBUFFERS_LOAD_BUFFER_INTERNAL_HPP
#define SCORE_LIB_FLATBUFFERS_LOAD_BUFFER_INTERNAL_HPP

#include "score/filesystem/path.h"
#include "score/os/errno.h"

#include "score/os/fcntl_impl.h"
#include "score/os/stat_impl.h"
#include "score/os/unistd.h"

#include <cerrno>
#include <cstddef>
#include <cstdint>
Comment thread
paulquiring marked this conversation as resolved.
#include <iterator>
#include <new>

namespace score
{

namespace flatbuffers
{

namespace detail
Comment thread
paulquiring marked this conversation as resolved.
{

struct OS
{
score::os::FcntlImpl fcntl{};
score::os::StatImpl stat{};
score::os::internal::UnistdImpl unistd{};
};

// OST must expose three members: fcntl (open), stat (fstat), unistd (close/read).
// Container must provide resize() and data().
template <class OST = OS, class Container>
score::os::Result<score::cpp::blank> LoadBufferImpl(const OST& os,
const score::filesystem::Path& path,
Container& data) noexcept
{
const auto fd_result = os.fcntl.open(path.CStr(), score::os::Fcntl::Open::kReadOnly);
if (!fd_result.has_value())
{
return score::cpp::make_unexpected(fd_result.error());
}
const std::int32_t file_desc = fd_result.value();

// Helper to ensure the file descriptor in an error case is always closed.
// Any close error is only reported when no prior error occurred.
auto close_fd = [&os,
file_desc](const score::os::Error* prior_error) noexcept -> score::os::Result<score::cpp::blank> {
const auto close_result = os.unistd.close(file_desc);
if (prior_error != nullptr)
{
return score::cpp::make_unexpected(*prior_error);
}
return close_result;
};

// Obtain the file size via fstat
score::os::StatBuffer stat_buf{};
const auto stat_result = os.stat.fstat(file_desc, stat_buf);
if (!stat_result.has_value())
{ // defensive error handling
// fstat failure is impossible on a just-opened valid fd. Mocks are used to
// cover this line in unit tests.
return close_fd(&stat_result.error());
}

if (stat_buf.st_size < 0)
{ // defensive error handling
// No real Linux filesystem reports negative sizes. Mocks are used to cover
// this line in unit tests.
const auto err = score::os::Error::createFromErrno(EINVAL);
return close_fd(&err);
}

const auto file_size = static_cast<std::size_t>(stat_buf.st_size);

try
{
data.resize(file_size);
}
Comment thread
paulquiring marked this conversation as resolved.
catch (const std::bad_alloc&)
{
const auto err = score::os::Error::createFromErrno(ENOMEM);
return close_fd(&err);
}
catch (...)
{ // potential custom exception
const auto err = score::os::Error::createUnspecifiedError();
return close_fd(&err);
}

// Read the entire file, handling partial reads
std::size_t total_bytes_read = 0U;
while (total_bytes_read < file_size)
{
const auto read_result = os.unistd.read(file_desc,
std::next(data.data(), static_cast<std::ptrdiff_t>(total_bytes_read)),
file_size - total_bytes_read);
if (!read_result.has_value())
{
const auto& read_error = read_result.error();
// Retry on EINTR (interrupted system call)
if (read_error == score::os::Error::Code::kOperationWasInterruptedBySignal)
{ // The EINTR retry requires a signal to arrive during a regular-file
// read syscall, which is non-deterministic. Mocks are used to cover
// this line in unit tests.
continue;
}
return close_fd(&read_error);
}

const auto bytes_read = read_result.value();
if (bytes_read == 0)
{
// Unexpected EOF before reading the full file
const auto err = score::os::Error::createFromErrno(EIO);
return close_fd(&err);
}

total_bytes_read += static_cast<std::size_t>(bytes_read);
}

return close_fd(nullptr);
}

} // namespace detail
} // namespace flatbuffers
} // namespace score

#endif // SCORE_LIB_FLATBUFFERS_LOAD_BUFFER_INTERNAL_HPP
Loading
Loading