barretenberg
Loading...
Searching...
No Matches
check_memory_span.hpp
1#pragma once
2// Note: heavy header due to serialization logic, don't include outside of tests
3#include <barretenberg/serialize/msgpack_impl/msgpack_impl.hpp>
4
5#include "barretenberg/common/throw_or_abort.hpp"
6#include "schema_name.hpp"
7#include <algorithm>
8#include <cstddef>
9#include <cstdint>
10#include <iostream>
11#include <vector>
12
13namespace msgpack {
14template <typename T> uintptr_t __aligned_for(uintptr_t ptr)
15{
16 // Round to next alignment, (ptr % alignof(T)) == 0 after
17 return ptr + (alignof(T) - (ptr % alignof(T))) % alignof(T);
18}
19template <typename T, typename... Args> std::string check_memory_span(T* obj, Args*... args)
20{
21 // We need to handle alignment. Thankfully, we have a tool here.
22 // Convert the variadic template arguments to a vector of pairs.
23 // Each pair contains a pointer (as uintptr_t) and its size.
24 std::vector<std::pair<uintptr_t, size_t>> pointers{ { (uintptr_t)(args), sizeof(Args) }... };
25 // Sort the vector based on the pointer values.
26 std::sort(pointers.begin(), pointers.end(), [](const auto& a, const auto& b) { return a.first < b.first; });
27
28 for (size_t i = 1; i < pointers.size(); ++i) {
29 // Check if any of the Args* pointers overlap.
30 auto last_end = pointers[i - 1].first + pointers[i - 1].second;
31 if (last_end > pointers[i].first) {
32 return "Overlap in " + msgpack_schema_name(*obj) + " MSGPACK_FIELDS() params detected!";
33 }
34 // Check if gap is too large.
35 // Give some fuzzy room in case of 64 byte alignment restrictions.
36 if (__aligned_for<T>(last_end) < pointers[i].first) {
37 return "Gap in " + msgpack_schema_name(*obj) + " MSGPACK_FIELDS() params detected before member #" +
38 std::to_string(i) + " !";
39 }
40 }
41
42 // Check if all Args* pointers exist in T* memory.
43 uintptr_t t_start = reinterpret_cast<uintptr_t>(obj);
44 uintptr_t t_end = t_start + sizeof(T);
45 if (pointers.front().first < t_start || pointers.back().first + pointers.back().second > t_end) {
46 return "Some " + msgpack_schema_name(*obj) + " MSGPACK_FIELDS() params don't exist in object!";
47 }
48
49 // Check if all of T* memory is used by the Args* pointers.
50 size_t start = (size_t)obj;
51 size_t end = (size_t)obj;
52 for (auto [ptr, size] : pointers) {
53 end = std::max(end, ptr + size);
54 }
55 size_t total_size = end - start;
56 if (__aligned_for<T>(total_size) < sizeof(T)) {
57 return "Incomplete " + msgpack_schema_name(*obj) + " MSGPACK_FIELDS() params! Not all of object specified.";
58 }
59 return {};
60}
61
62template <msgpack_concepts::HasMsgPack T> std::string check_msgpack_method(const T& object)
63{
64 std::string result;
65 auto checker = [&](auto&... values) { result = check_memory_span(&object, &values...); };
66 const_cast<T&>(object).msgpack( // NOLINT
67 [&](auto&... keys_and_values) { std::apply(checker, drop_keys(std::tie(keys_and_values...))); });
68 return result;
69}
70void check_msgpack_usage(const auto& object)
71{
72 std::string result = check_msgpack_method(object);
73 if (!result.empty()) {
74 throw_or_abort(result);
75 }
76}
77} // namespace msgpack