Skip to content

Commit 8e1e13f

Browse files
authored
Merge pull request #48 from finger563/size_t_fix
Update of #45 (replace size_t with uint32_t for length encoding and add new option for aligned memory access)
2 parents 576c28e + 7d4de8f commit 8e1e13f

20 files changed

+632
-53
lines changed

CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ target_include_directories(alpaca INTERFACE
2424

2525

2626
if(ALPACA_BUILD_TESTS)
27-
add_subdirectory(test)
27+
add_subdirectory(test)
2828
endif()
2929

3030
if(ALPACA_BUILD_SAMPLES)

README.md

+10
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ The source for the above example can be found [here](https://github.com/p-ranav/
8888
* [Data Structure Versioning](#data-structure-versioning)
8989
* [Integrity Checking with Checksums](#integrity-checking-with-checksums)
9090
* [Macros to Exclude STL Data Structures](#macros-to-exclude-stl-data-structures)
91+
* [Aligned Memory Access](#aligned-memory-access)
9192
* [Python Interoperability](#python-interoperability)
9293
* [Usage](#usage)
9394
* [Format String Specification](#format-string-specification)
@@ -1161,6 +1162,15 @@ int main() {
11611162
auto bytes_written = serialize<options::fixed_length_encoding>(s, bytes);
11621163
}
11631164
```
1165+
### Aligned Memory Access
1166+
1167+
The Alpaca library, by default, utilizes unaligned memory access, as this is permitted on the x86_64 architecture.
1168+
However, certain architectures, such as the Arm Cortex-M3, M4, and M33, require aligned memory access for 32-bit
1169+
and 64-bit data types.
1170+
1171+
For architectures where aligned memory access is necessary, the Alpaca library includes the
1172+
```options::force_aligned_access``` option.
1173+
When this option is enabled, the library will not perform unaligned accesses and will use ```memcpy``` instead.
11641174

11651175
## Python Interoperability
11661176

include/alpaca/detail/field_type.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ enum class field_type : uint8_t {
3636
list,
3737
deque,
3838
filesystem_path,
39-
bitset
39+
bitset,
4040
};
4141

4242
template <field_type value> constexpr uint8_t to_byte() {

include/alpaca/detail/from_bytes.h

+31-8
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <alpaca/detail/options.h>
44
#include <alpaca/detail/variable_length_encoding.h>
55
#include <cstdint>
6+
#include <cstring>
67
#include <fstream>
78
#include <iostream>
89
#include <system_error>
@@ -12,6 +13,21 @@ namespace alpaca {
1213

1314
namespace detail {
1415

16+
template <options O, typename T>
17+
void get_aligned(T& value, const uint8_t* bytes, size_t current_index)
18+
{
19+
if (force_aligned_access<O>() &&
20+
reinterpret_cast<uintptr_t>(bytes + current_index) % alignof(T) != 0)
21+
{
22+
// non-aligned access --> byte-byte copy
23+
std::memcpy(&value, bytes + current_index, sizeof(T));
24+
} else {
25+
// aligned access, directly assign the value
26+
value = *(reinterpret_cast<const T *>(bytes + current_index));
27+
}
28+
}
29+
30+
1531
template <options O, typename Container>
1632
typename std::enable_if<!std::is_array_v<Container>, bool>::type
1733
from_bytes_crc32(uint32_t &value, Container &bytes, std::size_t &current_index,
@@ -21,7 +37,9 @@ from_bytes_crc32(uint32_t &value, Container &bytes, std::size_t &current_index,
2137
if (end_index < num_bytes_to_read) {
2238
return false;
2339
}
24-
value = *(reinterpret_cast<const uint32_t *>(bytes.data() + current_index));
40+
41+
get_aligned<O>(value, &bytes[0], current_index);
42+
2543
update_value_based_on_alpaca_endian_rules<O, uint32_t>(value);
2644
current_index += num_bytes_to_read;
2745
return true;
@@ -36,7 +54,9 @@ from_bytes_crc32(uint32_t &value, Container &bytes, std::size_t &current_index,
3654
if (end_index < num_bytes_to_read) {
3755
return false;
3856
}
39-
value = *(reinterpret_cast<const uint32_t *>(bytes + current_index));
57+
58+
get_aligned<O>(value, &bytes[0], current_index);
59+
4060
update_value_based_on_alpaca_endian_rules<O, uint32_t>(value);
4161
current_index += num_bytes_to_read;
4262
return true;
@@ -87,7 +107,8 @@ from_bytes(T &value, Container &bytes, std::size_t &current_index,
87107
/// TODO: report error
88108
return false;
89109
}
90-
value = *(reinterpret_cast<const T *>(bytes.data() + current_index));
110+
111+
get_aligned<O>(value, &bytes[0], current_index);
91112
current_index += num_bytes_to_read;
92113
update_value_based_on_alpaca_endian_rules<O, T>(value);
93114
return true;
@@ -139,7 +160,8 @@ from_bytes(T &value, Container &bytes, std::size_t &current_index,
139160
/// TODO: report error
140161
return false;
141162
}
142-
value = *(reinterpret_cast<const T *>(bytes + current_index));
163+
164+
get_aligned<O>(value, &bytes[0], current_index);
143165
current_index += num_bytes_to_read;
144166
update_value_based_on_alpaca_endian_rules<O, T>(value);
145167
return true;
@@ -194,7 +216,8 @@ from_bytes(T &value, Container &bytes, std::size_t &current_index,
194216
char value_bytes[num_bytes_to_read];
195217
bytes.read(&value_bytes[0], num_bytes_to_read);
196218
current_index += num_bytes_to_read;
197-
value = *(reinterpret_cast<const T *>(value_bytes));
219+
get_aligned<O>(value, (uint8_t*) &value_bytes[0], 0);
220+
198221
update_value_based_on_alpaca_endian_rules<O, T>(value);
199222
return true;
200223
}
@@ -237,7 +260,7 @@ from_bytes(T &value, Container &bytes, std::size_t &current_index,
237260
/// TODO: report error
238261
return false;
239262
}
240-
value = *(reinterpret_cast<const T *>(bytes.data() + current_index));
263+
get_aligned<O>(value, &bytes[0], current_index);
241264
current_index += num_bytes_to_read;
242265
} else {
243266
value = decode_varint<T>(bytes, current_index);
@@ -286,7 +309,7 @@ from_bytes(T &value, Container &bytes, std::size_t &current_index,
286309
/// TODO: report error
287310
return false;
288311
}
289-
value = *(reinterpret_cast<const T *>(bytes + current_index));
312+
get_aligned<O>(value, &bytes[0], current_index);
290313
current_index += num_bytes_to_read;
291314
} else {
292315
value = decode_varint<T>(bytes, current_index);
@@ -338,7 +361,7 @@ from_bytes(T &value, Container &bytes, std::size_t &current_index,
338361
char value_bytes[num_bytes_to_read];
339362
bytes.read(&value_bytes[0], num_bytes_to_read);
340363
current_index += num_bytes_to_read;
341-
value = *(reinterpret_cast<const T *>(value_bytes));
364+
get_aligned<O>(value, (uint8_t*) &value_bytes[0], 0);
342365
} else {
343366
value = decode_varint<T>(bytes, current_index);
344367
}

include/alpaca/detail/options.h

+10-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ enum class options {
88
big_endian = 1,
99
fixed_length_encoding = 2,
1010
with_version = 4,
11-
with_checksum = 8
11+
with_checksum = 8,
12+
force_aligned_access = 16,
1213
};
1314

1415
template <typename E> struct enable_bitmask_operators {
@@ -25,6 +26,9 @@ operator|(E lhs, E rhs) {
2526

2627
namespace detail {
2728

29+
using size_t_serialized_type = uint32_t;
30+
31+
2832
template <typename T, T value, T flag> constexpr bool enum_has_flag() {
2933
using underlying = typename std::underlying_type<T>::type;
3034
return (static_cast<underlying>(value) & static_cast<underlying>(flag)) ==
@@ -47,6 +51,11 @@ template <options O> constexpr bool with_version() {
4751

4852
template <options O> constexpr bool with_checksum() {
4953
return enum_has_flag<options, O, options::with_checksum>();
54+
55+
}
56+
57+
template <options O> constexpr bool force_aligned_access() {
58+
return enum_has_flag<options, O, options::force_aligned_access>();
5059
}
5160

5261
} // namespace detail

include/alpaca/detail/to_bytes.h

+6-3
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,15 @@ to_bytes(T &bytes, std::size_t &byte_index, const U &original_value) {
4747
// encode as variable-length
4848
template <options O, typename T, typename U>
4949
typename std::enable_if<
50-
std::is_same_v<U, uint32_t> || std::is_same_v<U, uint64_t> ||
51-
std::is_same_v<U, int32_t> || std::is_same_v<U, long> || std::is_same_v<U, int64_t> ||
52-
std::is_same_v<U, std::size_t>,
50+
std::is_same_v<U, uint32_t> || std::is_same_v<U, uint64_t> ||
51+
std::is_same_v<U, int32_t> || std::is_same_v<U, int64_t> ||
52+
std::is_same_v<U, unsigned long> || std::is_same_v<U, long> ||
53+
std::is_same_v<U, unsigned long long> || std::is_same_v<U, long long>,
5354
void>::type
5455
to_bytes(T &bytes, std::size_t &byte_index, const U &original_value) {
5556

57+
static_assert(!std::is_same_v<T, size_t>, "Unable to directly serialize size_t type");
58+
5659
U value = original_value;
5760
update_value_based_on_alpaca_endian_rules<O, U>(value);
5861

include/alpaca/detail/types/array.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ typename std::enable_if<is_array_type<T>::value, void>::type type_info(
1414
std::vector<uint8_t> &typeids,
1515
std::unordered_map<std::string_view, std::size_t> &struct_visitor_map) {
1616
typeids.push_back(to_byte<field_type::array>());
17-
typeids.push_back(std::tuple_size_v<T>);
17+
typeids.push_back((size_t_serialized_type) std::tuple_size_v<T>);
1818
using value_type = typename T::value_type;
1919
type_info<value_type>(typeids, struct_visitor_map);
2020
}
@@ -41,7 +41,7 @@ void from_bytes_to_array(T &value, Container &bytes, std::size_t &current_index,
4141

4242
using decayed_value_type = typename std::decay<typename T::value_type>::type;
4343

44-
constexpr auto size = std::tuple_size<T>::value;
44+
constexpr auto size = (size_t_serialized_type) std::tuple_size<T>::value;
4545

4646
if (size > end_index - current_index) {
4747
// size is greater than the number of bytes remaining
@@ -52,7 +52,7 @@ void from_bytes_to_array(T &value, Container &bytes, std::size_t &current_index,
5252
}
5353

5454
// read `size` bytes and save to value
55-
for (std::size_t i = 0; i < size; ++i) {
55+
for (size_t_serialized_type i = 0; i < size; ++i) {
5656
decayed_value_type v{};
5757
from_bytes_router<O>(v, bytes, current_index, end_index, error_code);
5858
value[i] = v;

include/alpaca/detail/types/bitset.h

+4-4
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ template <options O, std::size_t N, typename Container>
2626
void to_bytes_from_bitset_type(const std::bitset<N> &input, Container &bytes,
2727
std::size_t &byte_index) {
2828
// save bitset size
29-
to_bytes_router<O, std::size_t>(input.size(), bytes, byte_index);
29+
to_bytes_router<O, size_t_serialized_type>((size_t_serialized_type) input.size(), bytes, byte_index);
3030

3131
// serialize the bitset itself into (bits/8 + 1) bytes
3232
int num_bytes = input.size() / 8 + 1;
@@ -63,8 +63,8 @@ bool from_bytes_to_bitset(std::bitset<N> &value, Container &bytes,
6363
}
6464

6565
// current byte is the size of the vector
66-
std::size_t size = 0;
67-
detail::from_bytes<O, std::size_t>(size, bytes, current_index, end_index,
66+
size_t_serialized_type size = 0;
67+
detail::from_bytes<O, size_t_serialized_type>(size, bytes, current_index, end_index,
6868
error_code);
6969

7070
if (size != N) {
@@ -90,7 +90,7 @@ bool from_bytes_to_bitset(std::bitset<N> &value, Container &bytes,
9090
// reset the value to 0
9191
value.reset();
9292

93-
for (std::size_t i = 0; i < num_serialized_bytes; ++i) {
93+
for (size_t_serialized_type i = 0; i < num_serialized_bytes; ++i) {
9494
uint8_t byte{};
9595
from_bytes_router<O>(byte, bytes, current_index, end_index, error_code);
9696
if (error_code) {

include/alpaca/detail/types/deque.h

+4-4
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ template <options O, typename T, typename Container>
2626
void to_bytes_from_deque_type(const T &input, Container &bytes,
2727
std::size_t &byte_index) {
2828
// save deque size
29-
to_bytes_router<O, std::size_t>(input.size(), bytes, byte_index);
29+
to_bytes_router<O, size_t_serialized_type>(input.size(), bytes, byte_index);
3030

3131
// value of each element in deque
3232
for (const auto &v : input) {
@@ -57,8 +57,8 @@ bool from_bytes_to_deque(std::deque<T> &value, Container &bytes,
5757
}
5858

5959
// current byte is the size of the vector
60-
std::size_t size = 0;
61-
detail::from_bytes<O, std::size_t>(size, bytes, current_index, end_index,
60+
size_t_serialized_type size = 0;
61+
detail::from_bytes<O, size_t_serialized_type>(size, bytes, current_index, end_index,
6262
error_code);
6363

6464
if (size > end_index - current_index) {
@@ -70,7 +70,7 @@ bool from_bytes_to_deque(std::deque<T> &value, Container &bytes,
7070
}
7171

7272
// read `size` bytes and save to value
73-
for (std::size_t i = 0; i < size; ++i) {
73+
for (size_t_serialized_type i = 0; i < size; ++i) {
7474
T v{};
7575
from_bytes_router<O>(v, bytes, current_index, end_index, error_code);
7676
if (error_code) {

include/alpaca/detail/types/filesystem_path.h

+7-6
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ template <options O, typename Container>
2626
void to_bytes(Container &bytes, std::size_t &byte_index,
2727
const std::filesystem::path &input) {
2828
// save string length
29-
to_bytes_router<O>(input.native().size(), bytes, byte_index);
29+
to_bytes_router<O>((size_t_serialized_type)input.native().size(), bytes,
30+
byte_index);
3031

3132
for (const auto &c : input.native()) {
3233
to_bytes<O>(bytes, byte_index, c);
@@ -35,8 +36,8 @@ void to_bytes(Container &bytes, std::size_t &byte_index,
3536

3637
template <options O, typename Container>
3738
bool from_bytes(std::filesystem::path &value, Container &bytes,
38-
std::size_t &current_index, std::size_t &end_index,
39-
std::error_code &error_code) {
39+
std::size_t &current_index, std::size_t &end_index,
40+
std::error_code &error_code) {
4041

4142
if (current_index >= end_index) {
4243
// end of input
@@ -45,9 +46,9 @@ bool from_bytes(std::filesystem::path &value, Container &bytes,
4546
}
4647

4748
// current byte is the length of the string
48-
std::size_t size = 0;
49-
detail::from_bytes<O, std::size_t>(size, bytes, current_index, end_index,
50-
error_code);
49+
size_t_serialized_type size = 0;
50+
detail::from_bytes<O, size_t_serialized_type>(size, bytes, current_index,
51+
end_index, error_code);
5152

5253
if (size > end_index - current_index) {
5354
// size is greater than the number of bytes remaining

include/alpaca/detail/types/list.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ template <options O, typename T, typename Container>
2626
void to_bytes_from_list_type(const T &input, Container &bytes,
2727
std::size_t &byte_index) {
2828
// save list size
29-
to_bytes_router<O, std::size_t>(input.size(), bytes, byte_index);
29+
to_bytes_router<O, size_t_serialized_type>(input.size(), bytes, byte_index);
3030

3131
// value of each element in list
3232
for (const auto &v : input) {
@@ -57,8 +57,8 @@ bool from_bytes_to_list(std::list<T> &value, Container &bytes,
5757
}
5858

5959
// current byte is the size of the vector
60-
std::size_t size = 0;
61-
detail::from_bytes<O, std::size_t>(size, bytes, current_index, end_index,
60+
size_t_serialized_type size = 0;
61+
detail::from_bytes<O, size_t_serialized_type>(size, bytes, current_index, end_index,
6262
error_code);
6363

6464
if (size > end_index - current_index) {

include/alpaca/detail/types/map.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ template <options O, typename T, typename Container>
5151
void to_bytes_from_map_type(const T &input, Container &bytes,
5252
std::size_t &byte_index) {
5353
// save map size
54-
to_bytes_router<O, std::size_t, Container>(input.size(), bytes, byte_index);
54+
to_bytes_router<O, size_t_serialized_type, Container>((size_t_serialized_type) input.size(), bytes, byte_index);
5555

5656
// save key,value pairs in map
5757
for (const auto &[key, value] : input) {
@@ -84,8 +84,8 @@ template <options O, typename T, typename Container>
8484
void from_bytes_to_map(T &map, Container &bytes, std::size_t &current_index,
8585
std::size_t &end_index, std::error_code &error_code) {
8686
// current byte is the size of the map
87-
std::size_t size = 0;
88-
detail::from_bytes<O, std::size_t>(size, bytes, current_index, end_index,
87+
size_t_serialized_type size = 0;
88+
detail::from_bytes<O, size_t_serialized_type>(size, bytes, current_index, end_index,
8989
error_code);
9090

9191
if (size > end_index - current_index) {

include/alpaca/detail/types/set.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ template <options O, typename T, typename Container>
5050
void to_bytes_from_set_type(const T &input, Container &bytes,
5151
std::size_t &byte_index) {
5252
// save set size
53-
to_bytes_router<O, std::size_t, Container>(input.size(), bytes, byte_index);
53+
to_bytes_router<O, size_t_serialized_type, Container>((size_t_serialized_type) input.size(), bytes, byte_index);
5454

5555
// save values in set
5656
for (const auto &value : input) {
@@ -82,8 +82,8 @@ template <options O, typename T, typename Container>
8282
void from_bytes_to_set(T &set, Container &bytes, std::size_t &current_index,
8383
std::size_t &end_index, std::error_code &error_code) {
8484
// current byte is the size of the set
85-
std::size_t size = 0;
86-
detail::from_bytes<O, std::size_t>(size, bytes, current_index, end_index,
85+
size_t_serialized_type size = 0;
86+
detail::from_bytes<O, size_t_serialized_type>(size, bytes, current_index, end_index,
8787
error_code);
8888

8989
if (size > end_index - current_index) {

0 commit comments

Comments
 (0)