master
h 225 lines 6.44 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 message.h
8
9 Abstract:
10
11 This file contains a utility class to write serialized messages.
12
13 --*/
14
15 #pragma once
16
17 #include <vector>
18 #include <string>
19 #include <string_view>
20 #include <type_traits>
21 #include <gsl/gsl>
22 #include <cassert>
23
24 namespace wsl::shared {
25
26 template <typename TMessage>
27 class MessageWriter
28 {
29 using THeader = decltype(TMessage::Header);
30 using TMessageType = decltype(THeader::MessageType);
31
32 public:
33 MessageWriter(TMessageType type)
34 {
35 // Note: this is required because the structure might be padded. For instance:
36 // struct a
37 // {
38 // char c;
39 // char buffer[0];
40 // };
41 // Would have a.buffer at byte 1, but sizeof(a) can be > 1 depending on padding.
42
43 const auto bufferOffset = reinterpret_cast<size_t>(&reinterpret_cast<TMessage*>(0)->Buffer);
44 m_buffer.resize(bufferOffset);
45
46 (*this)->Header.MessageType = type;
47 Size() = static_cast<unsigned long>(bufferOffset);
48
49 // Validate that 'Buffer' has a char type.
50 static_assert(std::is_same_v<std::remove_reference_t<decltype((*this)->Buffer[0])>, char>);
51 }
52
53 MessageWriter() : MessageWriter(TMessage::Type)
54 {
55 }
56
57 TMessage* operator->()
58 {
59 return reinterpret_cast<TMessage*>(m_buffer.data());
60 }
61
62 void WriteString(std::string_view String)
63 {
64 std::transform(String.begin(), String.end(), std::back_inserter(m_buffer), [](auto c) { return static_cast<std::byte>(c); });
65 m_buffer.push_back(std::byte{0}); // zero terminator for the string
66
67 Size() = static_cast<unsigned int>(m_buffer.size());
68 }
69
70 void WriteString(const char* String)
71 {
72 WriteString(String ? std::string_view{String} : std::string_view{});
73 }
74
75 void WriteString(unsigned int& Index, std::string_view String)
76 {
77 // Don't write directly to Index since resizing the buffer might invalidate it.
78 // Instead save its relative offset to the buffer so we can write there after the resize.
79 const auto IndexOffset = GetRelativeIndex(Index);
80 const auto IndexValue = Size();
81
82 WriteString(String);
83 WriteRelativeIndex(IndexOffset, IndexValue);
84 }
85
86 void WriteString(unsigned int& Index, const char* String)
87 {
88 WriteString(Index, String ? std::string_view{String} : std::string_view{});
89 }
90
91 void WriteSpan(const gsl::span<gsl::byte>& Span)
92 {
93 gsl::copy(Span, InsertBuffer(Span.size()));
94 }
95
96 template <typename T>
97 gsl::span<T> InsertArray(unsigned int& Index, unsigned int& SizeInMessage, unsigned int ArraySize)
98 {
99 SizeInMessage = ArraySize;
100 return InsertBuffer(Index, ArraySize * sizeof(T));
101 }
102
103 gsl::span<std::byte> InsertBuffer(unsigned int& Index, size_t BufferSize, unsigned int& Size)
104 {
105 Size = BufferSize;
106 return InsertBuffer(Index, BufferSize);
107 }
108
109 gsl::span<std::byte> InsertBuffer(unsigned int& Index, size_t BufferSize)
110 {
111 const auto IndexOffset = GetRelativeIndex(Index);
112 const auto IndexValue = Size();
113
114 m_buffer.resize(m_buffer.size() + BufferSize);
115 WriteRelativeIndex(IndexOffset, IndexValue);
116 Size() = static_cast<unsigned long>(m_buffer.size());
117
118 return Span().subspan(IndexValue, BufferSize);
119 }
120
121 gsl::span<std::byte> InsertBuffer(size_t BufferSize)
122 {
123 m_buffer.resize(m_buffer.size() + BufferSize);
124 const auto Index = Size();
125 Size() = static_cast<unsigned long>(m_buffer.size());
126
127 return Span().subspan(Index, BufferSize);
128 }
129
130 void WriteString(const std::wstring& String)
131 {
132 WriteString(wsl::shared::string::WideToMultiByte(String));
133 }
134
135 void WriteString(const wchar_t* String)
136 {
137 WriteString(wsl::shared::string::WideToMultiByte(String));
138 }
139
140 void WriteString(unsigned int& Index, const std::wstring& String)
141 {
142 WriteString(Index, wsl::shared::string::WideToMultiByte(String));
143 }
144
145 void WriteString(unsigned int& Index, const wchar_t* String)
146 {
147 WriteString(Index, wsl::shared::string::WideToMultiByte(String));
148 }
149
150 // Write an array of strings.
151 // Each field is prefixed with its size as int32_t, and the array ends with a -1 terminator.
152 void WriteStringArray(unsigned int& Index, const char* const* String, size_t Count)
153 {
154 size_t totalSize = sizeof(int32_t); // The array ends with a '-1' terminator.
155 for (size_t i = 0; i < Count; i++)
156 {
157 totalSize += strlen(String[i]) + sizeof(int32_t);
158 }
159
160 auto span = InsertBuffer(Index, totalSize);
161 auto it = span.begin();
162
163 auto insertSize = [&](int32_t size) {
164 it = std::copy(reinterpret_cast<const std::byte*>(&size), reinterpret_cast<const std::byte*>(&size) + sizeof(size), it);
165 };
166
167 for (size_t i = 0; i < Count; i++)
168 {
169 auto size = strlen(String[i]);
170 THROW_INVALID_ARG_IF(size > std::numeric_limits<int32_t>::max());
171
172 insertSize(static_cast<int32_t>(size));
173
174 it = std::copy(reinterpret_cast<const std::byte*>(String[i]), reinterpret_cast<const std::byte*>(String[i] + size), it);
175 }
176
177 insertSize(-1);
178
179 assert(it == span.end());
180 }
181
182 gsl::span<std::byte> Span()
183 {
184 // In case the structure is padded,
185 // make sure that the message is at least the size of the structure.
186
187 const int64_t diff = sizeof(TMessage) - m_buffer.size();
188 if (diff > 0)
189 {
190 InsertBuffer(diff);
191 }
192
193 return gsl::make_span(m_buffer);
194 }
195
196 std::vector<std::byte> MoveBuffer()
197 {
198 return std::vector<std::byte>(std::move(m_buffer));
199 }
200
201 private:
202 unsigned int& Size()
203 {
204 return (*this)->Header.MessageSize;
205 }
206
207 size_t GetRelativeIndex(unsigned int& Index)
208 {
209 const auto* indexPtr = reinterpret_cast<char*>(&Index);
210 const auto* bufferStart = reinterpret_cast<char*>(m_buffer.data());
211
212 // Validate that 'Index' is actually within the bounds of our buffer
213 assert(indexPtr >= bufferStart && indexPtr + sizeof(Index) <= bufferStart + m_buffer.size());
214
215 return static_cast<size_t>(indexPtr - bufferStart);
216 }
217
218 void WriteRelativeIndex(size_t Offset, unsigned int Value)
219 {
220 *reinterpret_cast<unsigned int*>(&m_buffer[Offset]) = Value;
221 }
222
223 std::vector<std::byte> m_buffer;
224 };
225 } // namespace wsl::shared