forked from sccn/liblsl
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsync_serialization.h
More file actions
94 lines (85 loc) · 3.86 KB
/
Copy pathsync_serialization.h
File metadata and controls
94 lines (85 loc) · 3.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#ifndef SYNC_SERIALIZATION_H
#define SYNC_SERIALIZATION_H
#include "sample.h"
#include <algorithm>
#include <asio/buffer.hpp>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <vector>
namespace lsl {
/**
* Byte-swap a synchronous gather-write buffer sequence for a reverse-endianness client.
*
* The input is the on-the-wire layout produced by the sync outlet, one group per sample:
* [tag:1] ( [timestamp:8] iff tag == TAG_TRANSMITTED_TIMESTAMP ) [sample:sample_bytes]
*
* The sequence is walked by tag rather than by guessing from buffer sizes, so it stays
* correct even when a sample happens to be 8 bytes (e.g. 2x int32 or 4x int16), which a
* size-based classifier would mistake for a timestamp. Tag bytes pass through unchanged,
* timestamps are reversed as a single 8-byte value, and sample payloads are reversed per
* channel value (@p value_size bytes each; @p value_size == 1 is a no-op).
*
* Swapped bytes are written into @p storage, which is cleared and reserved to the exact
* required size up front so that the returned const_buffers referencing it remain valid
* for the lifetime of the returned vector (a growing vector would otherwise reallocate and
* dangle earlier pointers).
*
* @param bufs Gather buffers for one or more samples, in the layout described above.
* @param storage Scratch storage that backs the swapped buffers; must outlive the result.
* @param sample_bytes Total bytes per sample (channel_count * value_size).
* @param value_size Bytes per channel value (1, 2, 4 or 8).
* @param num_channels Number of channels per sample.
* @return const_buffers describing the byte-swapped stream; tag buffers alias @p bufs, the
* rest point into @p storage.
*/
inline std::vector<asio::const_buffer> sync_swap_buffers(
const std::vector<asio::const_buffer> &bufs, std::vector<char> &storage,
std::size_t sample_bytes, std::size_t value_size, uint32_t num_channels) {
(void)sample_bytes; // implied by num_channels * value_size; kept for call-site clarity
// Reserve the exact swapped byte count (everything except the 1-byte tags) before
// appending so that storage never reallocates and the returned pointers stay valid.
std::size_t needed = 0;
for (const auto &b : bufs)
if (b.size() != 1) needed += b.size();
storage.clear();
storage.reserve(needed);
std::vector<asio::const_buffer> result;
result.reserve(bufs.size());
const auto swap_into = [&](const asio::const_buffer &buf, std::size_t width) {
const std::size_t offset = storage.size();
const std::size_t n = buf.size();
storage.resize(offset + n); // within reserved capacity -> no reallocation
std::memcpy(storage.data() + offset, buf.data(), n);
// Reverse each width-sized value in place using byte operations only. storage is a
// vector<char> and values are packed at arbitrary offsets, so it may be unaligned for
// the typed dereferences in sample::convert_endian() -- undefined behavior on
// strict-alignment targets. Swapping bytes directly avoids that entirely.
if (width > 1)
for (char *p = storage.data() + offset, *end = p + n; p < end; p += width)
std::reverse(p, p + width);
result.push_back(asio::const_buffer(storage.data() + offset, n));
};
std::size_t i = 0;
while (i < bufs.size()) {
// Each sample group starts with a 1-byte tag, which is never byte-swapped.
const asio::const_buffer &tagbuf = bufs[i];
const uint8_t tag = *static_cast<const uint8_t *>(tagbuf.data());
result.push_back(tagbuf);
++i;
// A transmitted-timestamp tag is followed by an 8-byte timestamp value.
if (tag == TAG_TRANSMITTED_TIMESTAMP && i < bufs.size()) {
swap_into(bufs[i], sizeof(double));
++i;
}
// Then the fixed-size sample payload, swapped per channel value.
if (i < bufs.size()) {
swap_into(bufs[i], value_size);
++i;
}
}
(void)num_channels;
return result;
}
} // namespace lsl
#endif // SYNC_SERIALIZATION_H