CIRCT 22.0.0git
Loading...
Searching...
No Matches
Ports.cpp
Go to the documentation of this file.
1//===- Ports.cpp - ESI communication channels -------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// DO NOT EDIT!
10// This file is distributed as part of an ESI package. The source for this file
11// should always be modified within CIRCT (lib/dialect/ESI/runtime/cpp/).
12//
13//===----------------------------------------------------------------------===//
14
15#include "esi/Ports.h"
16#include "esi/Types.h"
17
18#include <algorithm>
19#include <chrono>
20#include <cstring>
21#include <map>
22#include <stdexcept>
23
24using namespace esi;
25
27 if (auto chanType = dynamic_cast<const ChannelType *>(type))
28 type = chanType->getInner();
29 auto translationType = dynamic_cast<const WindowType *>(type);
30 if (translationType) {
31 this->type = translationType->getIntoType();
32 this->translationInfo = std::make_unique<TranslationInfo>(translationType);
33 } else {
34 this->type = type;
35 this->translationInfo = nullptr;
36 }
37}
40 : id(id), type(type), channels(channels) {}
41
42WriteChannelPort &BundlePort::getRawWrite(const std::string &name) const {
43 auto f = channels.find(name);
44 if (f == channels.end())
45 throw std::runtime_error("Channel '" + name + "' not found");
46 auto *write = dynamic_cast<WriteChannelPort *>(&f->second);
47 if (!write)
48 throw std::runtime_error("Channel '" + name + "' is not a write channel");
49 return *write;
50}
51
52ReadChannelPort &BundlePort::getRawRead(const std::string &name) const {
53 auto f = channels.find(name);
54 if (f == channels.end())
55 throw std::runtime_error("Channel '" + name + "' not found");
56 auto *read = dynamic_cast<ReadChannelPort *>(&f->second);
57 if (!read)
58 throw std::runtime_error("Channel '" + name + "' is not a read channel");
59 return *read;
60}
61
68
69void ReadChannelPort::connect(std::function<bool(MessageData)> callback,
70 const ConnectOptions &options) {
72 throw std::runtime_error("Channel already connected");
73
75
76 if (options.translateMessage && translationInfo) {
77 translationInfo->precomputeFrameInfo();
78 this->callback = [this, cb = std::move(callback)](MessageData data) {
79 if (translateIncoming(data))
80 return cb(MessageData(std::move(translationBuffer)));
81 return true;
82 };
83 } else {
84 this->callback = callback;
85 }
86 connectImpl(options);
88}
89
92
94
95 bool translate = options.translateMessage && translationInfo;
96 if (translate)
97 translationInfo->precomputeFrameInfo();
98 this->callback = [this, translate](MessageData data) {
99 if (translate) {
100 if (!translateIncoming(data))
101 return true;
102 data = MessageData(std::move(translationBuffer));
103 }
104
105 std::scoped_lock<std::mutex> lock(pollingM);
106 assert(!(!promiseQueue.empty() && !dataQueue.empty()) &&
107 "Both queues are in use.");
108
109 if (!promiseQueue.empty()) {
110 // If there are promises waiting, fulfill the first one.
111 std::promise<MessageData> p = std::move(promiseQueue.front());
112 promiseQueue.pop();
113 p.set_value(std::move(data));
114 } else {
115 // If not, add it to the data queue, unless the queue is full.
116 if (dataQueue.size() >= maxDataQueueMsgs && maxDataQueueMsgs != 0)
117 return false;
118 dataQueue.push(std::move(data));
119 }
120 return true;
121 };
122 connectImpl(options);
124}
125
126std::future<MessageData> ReadChannelPort::readAsync() {
127 if (mode == Mode::Callback)
128 throw std::runtime_error(
129 "Cannot read from a callback channel. `connect()` without a callback "
130 "specified to use polling mode.");
131
132 std::scoped_lock<std::mutex> lock(pollingM);
133 assert(!(!promiseQueue.empty() && !dataQueue.empty()) &&
134 "Both queues are in use.");
135
136 if (!dataQueue.empty()) {
137 // If there's data available, fulfill the promise immediately.
138 std::promise<MessageData> p;
139 std::future<MessageData> f = p.get_future();
140 p.set_value(std::move(dataQueue.front()));
141 dataQueue.pop();
142 return f;
143 } else {
144 // Otherwise, add a promise to the queue and return the future.
145 promiseQueue.emplace();
146 return promiseQueue.back().get_future();
147 }
148}
149
150//===----------------------------------------------------------------------===//
151// Window translation support
152//===----------------------------------------------------------------------===//
153
155 const Type *intoType = windowType->getIntoType();
156
157 const StructType *intoStruct = dynamic_cast<const StructType *>(intoType);
158 if (!intoStruct)
159 throw std::runtime_error(
160 "Window intoType must be a struct for translation");
161
162 const auto &intoFields = intoStruct->getFields();
163
164 // Build a map from field name to (offset, type, isListField).
165 // For list fields, the offset is where the list_length field will be stored,
166 // and we also track the element type.
167 struct FieldInfo {
168 size_t offset;
169 const Type *type;
170 bool isList;
171 const Type *listElementType; // Only valid if isList is true
172 size_t listElementSize; // Only valid if isList is true
173 };
174 std::map<std::string, FieldInfo> fieldMap;
175 size_t currentOffset = 0;
176 hasListField = false;
177
178 auto processField = [&](const std::string &name, const Type *fieldType) {
179 auto *listType = dynamic_cast<const ListType *>(fieldType);
180 if (listType) {
181 hasListField = true;
182 // For list fields in the intoType:
183 // - We store a list_length (size_t / 8 bytes) followed by the list data
184 // - The offset here is where the list_length goes
185 const Type *elemType = listType->getElementType();
186 std::ptrdiff_t elemBits = elemType->getBitWidth();
187 if (elemBits < 0)
188 throw std::runtime_error(
189 "Cannot translate list with dynamically-sized element type: " +
190 name);
191 if (elemBits % 8 != 0)
192 throw std::runtime_error(
193 "Cannot translate list element with non-byte-aligned size: " +
194 name);
195 size_t elemBytes = (static_cast<size_t>(elemBits) + 7) / 8;
196 fieldMap[name] = {currentOffset, fieldType, true, elemType, elemBytes};
197 // Reserve space for list_length. We use size_t (8 bytes on 64-bit
198 // platforms) for consistency with standard C/C++ container sizes.
199 // Note: This means the translated message format is platform-dependent.
200 currentOffset += sizeof(size_t);
201 // List data will be appended dynamically after the fixed header
202 } else {
203 std::ptrdiff_t fieldBits = fieldType->getBitWidth();
204 if (fieldBits < 0)
205 throw std::runtime_error("Cannot translate field with dynamic size: " +
206 name);
207 if (fieldBits % 8 != 0)
208 throw std::runtime_error(
209 "Cannot translate field with non-byte-aligned size: " + name);
210 size_t fieldBytes = (static_cast<size_t>(fieldBits) + 7) / 8;
211 fieldMap[name] = {currentOffset, fieldType, false, nullptr, 0};
212 currentOffset += fieldBytes;
213 }
214 };
215
216 if (intoStruct->isReverse()) {
217 for (auto it = intoFields.rbegin(); it != intoFields.rend(); ++it)
218 processField(it->first, it->second);
219 } else {
220 for (const auto &[name, fieldType] : intoFields)
221 processField(name, fieldType);
222 }
223
224 // intoTypeBytes is now the size of the fixed header portion
225 // (for types with lists, this excludes the variable-length list data)
226 intoTypeBytes = currentOffset;
227
228 const auto &windowFrames = windowType->getFrames();
229 frames.clear();
230 frames.reserve(windowFrames.size());
231
232 for (const auto &frame : windowFrames) {
233 FrameInfo frameInfo;
234 size_t frameOffset = 0;
235
236 // Calculate frame layout in SV memory order.
237 // SV structs are laid out with the last declared field at the lowest
238 // address. So when we iterate fields in reverse order (rbegin to rend),
239 // we get the memory layout order.
240 //
241 // For list fields, the lowered struct in CIRCT adds fields in this order:
242 // 1. list_data[numItems] - array of list elements
243 // 2. list_size - (if numItems > 1) count of valid items
244 // 3. last - indicates end of list
245 //
246 // In SV memory layout (reversed), this becomes:
247 // offset 0: last (1 byte)
248 // offset 1: list_size (if present)
249 // offset after size: list_data[numItems]
250 // offset after list: header fields...
251 struct FrameFieldLayout {
252 std::string name;
253 size_t frameOffset;
254 size_t size;
255 bool isList;
256 size_t numItems; // From window spec
257 size_t listElementSize; // Size of each list element
258 size_t bufferOffset; // Offset in translation buffer
259 const Type *listElementType; // Element type for list fields
260 };
261 std::vector<FrameFieldLayout> fieldLayouts;
262
263 for (auto fieldIt = frame.fields.rbegin(); fieldIt != frame.fields.rend();
264 ++fieldIt) {
265 const WindowType::Field &field = *fieldIt;
266 auto it = fieldMap.find(field.name);
267 if (it == fieldMap.end())
268 throw std::runtime_error("Frame field '" + field.name +
269 "' not found in intoType");
270
271 const FieldInfo &fieldInfo = it->second;
272 FrameFieldLayout layout;
273 layout.name = field.name;
274 layout.bufferOffset = fieldInfo.offset;
275 layout.isList = fieldInfo.isList;
276 layout.numItems = field.numItems;
277 layout.listElementType = fieldInfo.listElementType;
278 layout.listElementSize = fieldInfo.listElementSize;
279
280 if (fieldInfo.isList) {
281 // For list fields, the frame layout is (in memory order):
282 // 1. 'last' field (1 byte)
283 // 2. list_data (one element per frame)
284 // Note: numItems > 1 is not yet supported.
285 size_t numItems = field.numItems > 0 ? field.numItems : 1;
286 if (numItems != 1)
287 throw std::runtime_error(
288 "List translation with numItems > 1 is not yet supported. "
289 "Field '" +
290 field.name + "' has numItems=" + std::to_string(numItems));
291
292 // 'last' field comes first in memory
293 size_t lastOffset = frameOffset;
294 frameOffset += 1;
295
296 // List data comes after 'last'
297 layout.frameOffset = frameOffset;
298 layout.size = fieldInfo.listElementSize;
299 fieldLayouts.push_back(layout);
300 frameOffset += layout.size;
301
302 // Create ListFieldInfo for this list field
303 ListFieldInfo listInfo;
304 listInfo.fieldName = layout.name;
305 listInfo.dataOffset = layout.frameOffset;
306 listInfo.elementSize = fieldInfo.listElementSize;
307 listInfo.listLengthBufferOffset = layout.bufferOffset;
309 listInfo.lastFieldOffset = lastOffset;
310 frameInfo.listField = listInfo;
311 } else {
312 // Regular (non-list) field
313 std::ptrdiff_t fieldBits = fieldInfo.type->getBitWidth();
314 layout.frameOffset = frameOffset;
315 layout.size = (static_cast<size_t>(fieldBits) + 7) / 8;
316 fieldLayouts.push_back(layout);
317 frameOffset += layout.size;
318 }
319 }
320
321 frameInfo.expectedSize = frameOffset;
322
323 // Second pass: build copy ops for non-list fields
324 for (const auto &layout : fieldLayouts) {
325 if (!layout.isList) {
326 // Regular field - add copy op
327 frameInfo.copyOps.push_back(
328 {layout.frameOffset, layout.bufferOffset, layout.size});
329 }
330 // List fields were already handled in the first pass
331 }
332
333 // Sort copy ops by frameOffset to ensure processing order matches frame
334 // layout.
335 std::sort(frameInfo.copyOps.begin(), frameInfo.copyOps.end(),
336 [](const CopyOp &a, const CopyOp &b) {
337 return a.frameOffset < b.frameOffset;
338 });
339
340 // Merge adjacent copy ops
341 if (!frameInfo.copyOps.empty()) {
342 std::vector<CopyOp> mergedOps;
343 mergedOps.push_back(frameInfo.copyOps[0]);
344
345 for (size_t i = 1; i < frameInfo.copyOps.size(); ++i) {
346 CopyOp &last = mergedOps.back();
347 CopyOp current = frameInfo.copyOps[i];
348
349 if (last.frameOffset + last.size == current.frameOffset &&
350 last.bufferOffset + last.size == current.bufferOffset) {
351 last.size += current.size;
352 } else {
353 mergedOps.push_back(current);
354 }
355 }
356
357 frameInfo.copyOps = std::move(mergedOps);
358 }
359
360 frames.push_back(std::move(frameInfo));
361 }
362}
363
366 "Translation type must be set for window translation.");
367
368 // Get the frame data directly.
369 const uint8_t *frameData = data.getBytes();
370 size_t frameDataSize = data.size();
371
372 // Get the frame metadata for the current expected frame.
373 const auto &frameInfo = translationInfo->frames[nextFrameIndex];
374
375 // Check size
376 if (frameDataSize < frameInfo.expectedSize)
377 throw std::runtime_error("Frame data too small: expected at least " +
378 std::to_string(frameInfo.expectedSize) +
379 " bytes, got " + std::to_string(frameDataSize) +
380 " bytes");
381
382 // Check if this is the first frame of a new message.
383 // For list frames, we use accumulatingListData to track whether we're
384 // continuing to accumulate list items.
385 bool isFirstFrame = (nextFrameIndex == 0) && !accumulatingListData;
386
387 if (isFirstFrame) {
388 // Initialize the translation buffer to hold the fixed header portion.
389 translationBuffer.resize(translationInfo->intoTypeBytes, 0);
390 // Clear list data buffer for types with lists
391 if (translationInfo->hasListField)
392 listDataBuffer.clear();
393 }
394
395 // Execute copy ops for non-list fields
396 for (const auto &op : frameInfo.copyOps)
397 std::memcpy(translationBuffer.data() + op.bufferOffset,
398 frameData + op.frameOffset, op.size);
399
400 // Handle list field if present in this frame
401 if (frameInfo.listField.has_value()) {
402 const auto &listInfo = frameInfo.listField.value();
403
404 // With numItems == 1, each frame contains exactly one list element
405 size_t bytesToCopy = listInfo.elementSize;
406 // Additional check: dataOffset must not be beyond frameDataSize
407 if (listInfo.dataOffset > frameDataSize)
408 throw std::runtime_error("List data offset is beyond frame bounds");
409 // Bounds check to prevent buffer overflow from corrupted _size field
410 if (listInfo.dataOffset + bytesToCopy > frameDataSize)
411 throw std::runtime_error("List data extends beyond frame bounds");
412 size_t oldSize = listDataBuffer.size();
413 listDataBuffer.resize(oldSize + bytesToCopy);
414 std::memcpy(listDataBuffer.data() + oldSize,
415 frameData + listInfo.dataOffset, bytesToCopy);
416
417 // Check if this is the last frame of the list
418 uint8_t lastFlag = frameData[listInfo.lastFieldOffset];
419 if (lastFlag) {
420 // List is complete. Build the final message:
421 // [fixed header with list_length (size_t)][list data...]
422 // Write the actual length to the listLengthBufferOffset position.
423 size_t listLength = listDataBuffer.size() / listInfo.elementSize;
424 size_t *listLengthPtr = reinterpret_cast<size_t *>(
425 translationBuffer.data() + listInfo.listLengthBufferOffset);
426 *listLengthPtr = listLength;
427
428 // Append list data to translation buffer
429 size_t headerSize = translationBuffer.size();
430 translationBuffer.resize(headerSize + listDataBuffer.size());
431 std::memcpy(translationBuffer.data() + headerSize, listDataBuffer.data(),
432 listDataBuffer.size());
433
434 // Reset for next message
435 nextFrameIndex = 0;
436 listDataBuffer.clear();
437 accumulatingListData = false;
438 return true;
439 }
440
441 // Not the last frame - stay on this frame index for list accumulation
442 // (list frames repeat until last=true)
443 accumulatingListData = true;
444 return false;
445 }
446
447 // No list field in this frame - advance to next frame
448 nextFrameIndex++;
449 size_t numFrames = translationInfo->frames.size();
450
451 // Check if all frames have been received.
452 if (nextFrameIndex >= numFrames) {
453 // Reset for the next message.
454 nextFrameIndex = 0;
455 return true;
456 }
457
458 return false;
459}
460
463 "Translation type must be set for window translation.");
464
465 const uint8_t *srcData = data.getBytes();
466 size_t srcDataSize = data.size();
467
468 if (srcDataSize < translationInfo->intoTypeBytes)
469 throw std::runtime_error("Source data too small: expected at least " +
470 std::to_string(translationInfo->intoTypeBytes) +
471 " bytes, got " + std::to_string(srcDataSize) +
472 " bytes");
473
474 // Check if we have list fields
475 if (!translationInfo->hasListField) {
476 // No list fields - simple fixed-size translation
477 for (const auto &frameInfo : translationInfo->frames) {
478 std::vector<uint8_t> frameBuffer(frameInfo.expectedSize, 0);
479 for (const auto &op : frameInfo.copyOps)
480 std::memcpy(frameBuffer.data() + op.frameOffset,
481 srcData + op.bufferOffset, op.size);
482 translationBuffer.emplace_back(std::move(frameBuffer));
483 }
484 return;
485 }
486
487 // Handle list fields - need to split list data into multiple frames
488 for (const auto &frameInfo : translationInfo->frames) {
489 if (!frameInfo.listField.has_value()) {
490 // Non-list frame - copy as normal
491 std::vector<uint8_t> frameBuffer(frameInfo.expectedSize, 0);
492 for (const auto &op : frameInfo.copyOps)
493 std::memcpy(frameBuffer.data() + op.frameOffset,
494 srcData + op.bufferOffset, op.size);
495 translationBuffer.emplace_back(std::move(frameBuffer));
496 } else {
497 // List frame - need to generate multiple frames
498 const auto &listInfo = frameInfo.listField.value();
499
500 // Read the list length from the source data
501 size_t listLength = 0;
502 std::memcpy(&listLength, srcData + listInfo.listLengthBufferOffset,
503 sizeof(size_t));
504
505 // Check that the buffer is large enough for the list data
506 if (translationInfo->intoTypeBytes + (listLength * listInfo.elementSize) >
507 srcDataSize) {
508 throw std::runtime_error(
509 "Source buffer too small for list data: possible corrupted or "
510 "inconsistent list length field");
511 }
512 // Get pointer to list data (after the fixed header)
513 const uint8_t *listData = srcData + translationInfo->intoTypeBytes;
514
515 // Generate frames for the list
516 size_t itemsRemaining = listLength;
517 size_t listDataOffset = 0;
518
519 // Handle empty list case - still need to send one frame with last=true
520 if (listLength == 0)
521 throw std::runtime_error(
522 "Cannot send empty lists - parallel ESI list encoding requires at "
523 "least one frame to be sent, and each frame must contain at least "
524 "one element.");
525
526 while (itemsRemaining > 0) {
527 std::vector<uint8_t> frameBuffer(frameInfo.expectedSize, 0);
528
529 // Copy non-list fields (header data) to each frame
530 for (const auto &op : frameInfo.copyOps)
531 std::memcpy(frameBuffer.data() + op.frameOffset,
532 srcData + op.bufferOffset, op.size);
533
534 // With numItems == 1, each frame contains exactly one list element
535 size_t bytesInThisFrame = listInfo.elementSize;
536
537 // Copy list data
538 std::memcpy(frameBuffer.data() + listInfo.dataOffset,
539 listData + listDataOffset, bytesInThisFrame);
540
541 // Update remaining count
542 itemsRemaining -= 1;
543 listDataOffset += bytesInThisFrame;
544
545 // Set last field
546 frameBuffer[listInfo.lastFieldOffset] = (itemsRemaining == 0) ? 1 : 0;
547
548 translationBuffer.emplace_back(std::move(frameBuffer));
549 }
550 }
551 }
552}
assert(baseType &&"element must be base type")
PortMap channels
Definition Ports.h:477
ReadChannelPort & getRawRead(const std::string &name) const
Definition Ports.cpp:52
WriteChannelPort & getRawWrite(const std::string &name) const
Get access to the raw byte streams of a channel.
Definition Ports.cpp:42
BundlePort(AppID id, const BundleType *type, PortMap channels)
Construct a port.
Definition Ports.cpp:39
Bundles represent a collection of channels.
Definition Types.h:97
const Type * type
Definition Ports.h:133
virtual void connectImpl(const ConnectOptions &options)
Called by all connect methods to let backends initiate the underlying connections.
Definition Ports.h:202
ChannelPort(const Type *type)
Definition Ports.cpp:26
std::unique_ptr< TranslationInfo > translationInfo
Definition Ports.h:194
Channels are the basic communication primitives.
Definition Types.h:118
Lists represent variable-length sequences of elements of a single type.
Definition Types.h:319
A logical chunk of data representing serialized data.
Definition Common.h:113
A ChannelPort which reads data from the accelerator.
Definition Ports.h:318
volatile Mode mode
Definition Ports.h:375
virtual std::future< MessageData > readAsync()
Asynchronous read.
Definition Ports.cpp:126
size_t nextFrameIndex
Index of the next expected frame (for multi-frame windows).
Definition Ports.h:383
virtual void connect(std::function< bool(MessageData)> callback, const ConnectOptions &options={})
Definition Ports.cpp:69
std::vector< uint8_t > translationBuffer
Window translation support.
Definition Ports.h:381
std::mutex pollingM
Mutex to protect the two queues used for polling.
Definition Ports.h:399
bool accumulatingListData
Flag to track whether we're in the middle of accumulating list data.
Definition Ports.h:387
void resetTranslationState()
Reset translation state buffers and indices.
Definition Ports.cpp:62
std::function< bool(MessageData)> callback
Backends call this callback when new data is available.
Definition Ports.h:378
std::queue< MessageData > dataQueue
Store incoming data here if there are no outstanding promises to be fulfilled.
Definition Ports.h:402
static constexpr uint64_t DefaultMaxDataQueueMsgs
Default max data queue size set at connect time.
Definition Ports.h:348
bool translateIncoming(MessageData &data)
Translate incoming data if the port type is a window type.
Definition Ports.cpp:364
std::queue< std::promise< MessageData > > promiseQueue
Promises to be fulfilled when data is available.
Definition Ports.h:406
uint64_t maxDataQueueMsgs
Maximum number of messages to store in dataQueue. 0 means no limit.
Definition Ports.h:404
std::vector< uint8_t > listDataBuffer
For list fields: accumulated list data across frames.
Definition Ports.h:385
Structs are an ordered collection of fields, each with a name and a type.
Definition Types.h:210
bool isReverse() const
Definition Types.h:239
const FieldVector & getFields() const
Definition Types.h:218
Root class of the ESI type system.
Definition Types.h:34
virtual std::ptrdiff_t getBitWidth() const
Definition Types.h:41
Windows represent a fixed-size sliding window over a stream of data.
Definition Types.h:280
const std::vector< Frame > & getFrames() const
Definition Types.h:304
const Type * getIntoType() const
Definition Types.h:302
A ChannelPort which sends data to the accelerator.
Definition Ports.h:206
void translateOutgoing(const MessageData &data)
Translate outgoing data if the port type is a window type.
Definition Ports.cpp:461
Definition esi.py:1
std::map< std::string, ChannelPort & > PortMap
Definition Ports.h:29
bool translateMessage
If the type of this port is a window, translate the incoming/outgoing data into its underlying ('into...
Definition Ports.h:104
A copy operation for translating between frame data and the translation.
Definition Ports.h:148
size_t bufferOffset
Offset in the translation buffer.
Definition Ports.h:152
size_t frameOffset
Offset in the incoming/outgoing frame data.
Definition Ports.h:150
size_t size
Number of bytes to copy.
Definition Ports.h:154
Information about each frame in the windowed type.
Definition Ports.h:176
std::vector< CopyOp > copyOps
Precomputed copy operations for translating this frame (non-list fields).
Definition Ports.h:181
std::optional< ListFieldInfo > listField
Information about list fields in this frame (parallel encoding).
Definition Ports.h:184
size_t expectedSize
The total size of a frame in bytes.
Definition Ports.h:178
Information about a list field within a frame (for parallel encoding).
Definition Ports.h:160
size_t listLengthBufferOffset
Offset in the translation buffer where list length is stored.
Definition Ports.h:170
size_t dataOffset
Offset of the list data array in the frame.
Definition Ports.h:164
std::string fieldName
Name of the list field.
Definition Ports.h:162
size_t lastFieldOffset
Offset of the 'last' field in the frame.
Definition Ports.h:168
size_t elementSize
Size of each list element in bytes.
Definition Ports.h:166
size_t listDataBufferOffset
Offset in the translation buffer where list data starts.
Definition Ports.h:172
void precomputeFrameInfo()
Precompute and optimize the copy operations for translating frames.
Definition Ports.cpp:154
bool hasListField
True if the window contains a list field (variable-size message).
Definition Ports.h:192
const WindowType * windowType
The window type being translated.
Definition Ports.h:144
size_t intoTypeBytes
Size of the 'into' type in bytes (for fixed-size types).
Definition Ports.h:190
std::vector< FrameInfo > frames
Precomputed information about each frame.
Definition Ports.h:187
Field information describing a field within a frame.
Definition Types.h:283
std::string name
Definition Types.h:284