CIRCT 23.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 // Set frameBytes from the lowered type's bit width. Fall back to the
364 // maximum computed frame expectedSize if the type system can't report width
365 // (e.g. opaque union types).
366 std::ptrdiff_t loweredBits = windowType->getLoweredType()->getBitWidth();
367 if (loweredBits > 0) {
368 frameBytes = (loweredBits + 7) / 8;
369 } else {
370 frameBytes = 0;
371 for (const auto &f : frames)
372 frameBytes = std::max(frameBytes, f.expectedSize);
373 }
374}
375
378 "Translation type must be set for window translation.");
379
380 // Get the frame data directly.
381 const uint8_t *frameData = data.getBytes();
382 size_t frameDataSize = data.size();
383
384 // Get the frame metadata for the current expected frame.
385 const auto &frameInfo = translationInfo->frames[nextFrameIndex];
386
387 // Check size
388 if (frameDataSize < frameInfo.expectedSize)
389 throw std::runtime_error("Frame data too small: expected at least " +
390 std::to_string(frameInfo.expectedSize) +
391 " bytes, got " + std::to_string(frameDataSize) +
392 " bytes");
393
394 // Check if this is the first frame of a new message.
395 // For list frames, we use accumulatingListData to track whether we're
396 // continuing to accumulate list items.
397 bool isFirstFrame = (nextFrameIndex == 0) && !accumulatingListData;
398
399 if (isFirstFrame) {
400 // Initialize the translation buffer to hold the fixed header portion.
401 translationBuffer.resize(translationInfo->intoTypeBytes, 0);
402 // Clear list data buffer for types with lists
403 if (translationInfo->hasListField)
404 listDataBuffer.clear();
405 }
406
407 // Execute copy ops for non-list fields
408 for (const auto &op : frameInfo.copyOps)
409 std::memcpy(translationBuffer.data() + op.bufferOffset,
410 frameData + op.frameOffset, op.size);
411
412 // Handle list field if present in this frame
413 if (frameInfo.listField.has_value()) {
414 const auto &listInfo = frameInfo.listField.value();
415
416 // With numItems == 1, each frame contains exactly one list element
417 size_t bytesToCopy = listInfo.elementSize;
418 // Additional check: dataOffset must not be beyond frameDataSize
419 if (listInfo.dataOffset > frameDataSize)
420 throw std::runtime_error("List data offset is beyond frame bounds");
421 // Bounds check to prevent buffer overflow from corrupted _size field
422 if (listInfo.dataOffset + bytesToCopy > frameDataSize)
423 throw std::runtime_error("List data extends beyond frame bounds");
424 size_t oldSize = listDataBuffer.size();
425 listDataBuffer.resize(oldSize + bytesToCopy);
426 std::memcpy(listDataBuffer.data() + oldSize,
427 frameData + listInfo.dataOffset, bytesToCopy);
428
429 // Check if this is the last frame of the list
430 uint8_t lastFlag = frameData[listInfo.lastFieldOffset];
431 if (lastFlag) {
432 // List is complete. Build the final message:
433 // [fixed header with list_length (size_t)][list data...]
434 // Write the actual length to the listLengthBufferOffset position.
435 size_t listLength = listDataBuffer.size() / listInfo.elementSize;
436 size_t *listLengthPtr = reinterpret_cast<size_t *>(
437 translationBuffer.data() + listInfo.listLengthBufferOffset);
438 *listLengthPtr = listLength;
439
440 // Append list data to translation buffer
441 size_t headerSize = translationBuffer.size();
442 translationBuffer.resize(headerSize + listDataBuffer.size());
443 std::memcpy(translationBuffer.data() + headerSize, listDataBuffer.data(),
444 listDataBuffer.size());
445
446 // Reset for next message
447 nextFrameIndex = 0;
448 listDataBuffer.clear();
449 accumulatingListData = false;
450 return true;
451 }
452
453 // Not the last frame - stay on this frame index for list accumulation
454 // (list frames repeat until last=true)
455 accumulatingListData = true;
456 return false;
457 }
458
459 // No list field in this frame - advance to next frame
460 nextFrameIndex++;
461 size_t numFrames = translationInfo->frames.size();
462
463 // Check if all frames have been received.
464 if (nextFrameIndex >= numFrames) {
465 // Reset for the next message.
466 nextFrameIndex = 0;
467 return true;
468 }
469
470 return false;
471}
472
475 "Translation type must be set for window translation.");
476
477 const uint8_t *srcData = data.getBytes();
478 size_t srcDataSize = data.size();
479
480 if (srcDataSize < translationInfo->intoTypeBytes)
481 throw std::runtime_error("Source data too small: expected at least " +
482 std::to_string(translationInfo->intoTypeBytes) +
483 " bytes, got " + std::to_string(srcDataSize) +
484 " bytes");
485
486 // Check if we have list fields
487 if (!translationInfo->hasListField) {
488 // No list fields - simple fixed-size translation
489 for (const auto &frameInfo : translationInfo->frames) {
490 std::vector<uint8_t> frameBuffer(frameInfo.expectedSize, 0);
491 for (const auto &op : frameInfo.copyOps)
492 std::memcpy(frameBuffer.data() + op.frameOffset,
493 srcData + op.bufferOffset, op.size);
494 translationBuffer.emplace_back(std::move(frameBuffer));
495 }
496 return;
497 }
498
499 // Handle list fields - need to split list data into multiple frames
500 for (const auto &frameInfo : translationInfo->frames) {
501 if (!frameInfo.listField.has_value()) {
502 // Non-list frame - copy as normal
503 std::vector<uint8_t> frameBuffer(frameInfo.expectedSize, 0);
504 for (const auto &op : frameInfo.copyOps)
505 std::memcpy(frameBuffer.data() + op.frameOffset,
506 srcData + op.bufferOffset, op.size);
507 translationBuffer.emplace_back(std::move(frameBuffer));
508 } else {
509 // List frame - need to generate multiple frames
510 const auto &listInfo = frameInfo.listField.value();
511
512 // Read the list length from the source data
513 size_t listLength = 0;
514 std::memcpy(&listLength, srcData + listInfo.listLengthBufferOffset,
515 sizeof(size_t));
516
517 // Check that the buffer is large enough for the list data
518 if (translationInfo->intoTypeBytes + (listLength * listInfo.elementSize) >
519 srcDataSize) {
520 throw std::runtime_error(
521 "Source buffer too small for list data: possible corrupted or "
522 "inconsistent list length field");
523 }
524 // Get pointer to list data (after the fixed header)
525 const uint8_t *listData = srcData + translationInfo->intoTypeBytes;
526
527 // Generate frames for the list
528 size_t itemsRemaining = listLength;
529 size_t listDataOffset = 0;
530
531 // Handle empty list case - still need to send one frame with last=true
532 if (listLength == 0)
533 throw std::runtime_error(
534 "Cannot send empty lists - parallel ESI list encoding requires at "
535 "least one frame to be sent, and each frame must contain at least "
536 "one element.");
537
538 while (itemsRemaining > 0) {
539 std::vector<uint8_t> frameBuffer(frameInfo.expectedSize, 0);
540
541 // Copy non-list fields (header data) to each frame
542 for (const auto &op : frameInfo.copyOps)
543 std::memcpy(frameBuffer.data() + op.frameOffset,
544 srcData + op.bufferOffset, op.size);
545
546 // With numItems == 1, each frame contains exactly one list element
547 size_t bytesInThisFrame = listInfo.elementSize;
548
549 // Copy list data
550 std::memcpy(frameBuffer.data() + listInfo.dataOffset,
551 listData + listDataOffset, bytesInThisFrame);
552
553 // Update remaining count
554 itemsRemaining -= 1;
555 listDataOffset += bytesInThisFrame;
556
557 // Set last field
558 frameBuffer[listInfo.lastFieldOffset] = (itemsRemaining == 0) ? 1 : 0;
559
560 translationBuffer.emplace_back(std::move(frameBuffer));
561 }
562 }
563 }
564}
565
566std::vector<MessageData>
568 size_t frameBytes = getFrameSizeBytes();
569 assert(frameBytes > 0 && "Frame size must be greater than 0");
570 if (data.getSize() % frameBytes != 0)
571 throw std::runtime_error(
572 "write message size (" + std::to_string(data.getSize()) +
573 ") is not a multiple of frame size (" + std::to_string(frameBytes) +
574 ") on type " + type->toString());
575 std::vector<MessageData> frames;
576 size_t numFrames = data.getSize() / frameBytes;
577 const uint8_t *ptr = data.getBytes();
578 for (size_t i = 0; i < numFrames; ++i) {
579 frames.emplace_back(ptr, frameBytes);
580 ptr += frameBytes;
581 }
582 return frames;
583}
assert(baseType &&"element must be base type")
PortMap channels
Definition Ports.h:500
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:104
const Type * type
Definition Ports.h:140
size_t getFrameSizeBytes() const
Get the size of each frame in bytes.
Definition Ports.h:132
virtual void connectImpl(const ConnectOptions &options)
Called by all connect methods to let backends initiate the underlying connections.
Definition Ports.h:211
ChannelPort(const Type *type)
Definition Ports.cpp:26
std::unique_ptr< TranslationInfo > translationInfo
Definition Ports.h:203
Channels are the basic communication primitives.
Definition Types.h:125
Lists represent variable-length sequences of elements of a single type.
Definition Types.h:384
A logical chunk of data representing serialized data.
Definition Common.h:113
A ChannelPort which reads data from the accelerator.
Definition Ports.h:341
volatile Mode mode
Definition Ports.h:398
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:406
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:404
std::mutex pollingM
Mutex to protect the two queues used for polling.
Definition Ports.h:422
bool accumulatingListData
Flag to track whether we're in the middle of accumulating list data.
Definition Ports.h:410
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:401
std::queue< MessageData > dataQueue
Store incoming data here if there are no outstanding promises to be fulfilled.
Definition Ports.h:425
static constexpr uint64_t DefaultMaxDataQueueMsgs
Default max data queue size set at connect time.
Definition Ports.h:371
bool translateIncoming(MessageData &data)
Translate incoming data if the port type is a window type.
Definition Ports.cpp:376
std::queue< std::promise< MessageData > > promiseQueue
Promises to be fulfilled when data is available.
Definition Ports.h:429
uint64_t maxDataQueueMsgs
Maximum number of messages to store in dataQueue. 0 means no limit.
Definition Ports.h:427
std::vector< uint8_t > listDataBuffer
For list fields: accumulated list data across frames.
Definition Ports.h:408
Structs are an ordered collection of fields, each with a name and a type.
Definition Types.h:244
bool isReverse() const
Definition Types.h:273
const FieldVector & getFields() const
Definition Types.h:252
Root class of the ESI type system.
Definition Types.h:36
std::string toString(bool oneLine=false) const
Definition Types.cpp:120
virtual std::ptrdiff_t getBitWidth() const
Definition Types.h:43
Windows represent a fixed-size sliding window over a stream of data.
Definition Types.h:314
const std::vector< Frame > & getFrames() const
Definition Types.h:338
const Type * getLoweredType() const
Definition Types.h:337
const Type * getIntoType() const
Definition Types.h:336
A ChannelPort which sends data to the accelerator.
Definition Ports.h:215
void translateOutgoing(const MessageData &data)
Translate outgoing data if the port type is a window type.
Definition Ports.cpp:473
std::vector< MessageData > getMessageFrames(const MessageData &data)
Break a message into its frames.
Definition Ports.cpp:567
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:155
size_t bufferOffset
Offset in the translation buffer.
Definition Ports.h:159
size_t frameOffset
Offset in the incoming/outgoing frame data.
Definition Ports.h:157
size_t size
Number of bytes to copy.
Definition Ports.h:161
Information about each frame in the windowed type.
Definition Ports.h:183
std::vector< CopyOp > copyOps
Precomputed copy operations for translating this frame (non-list fields).
Definition Ports.h:188
std::optional< ListFieldInfo > listField
Information about list fields in this frame (parallel encoding).
Definition Ports.h:191
size_t expectedSize
The total size of a frame in bytes.
Definition Ports.h:185
Information about a list field within a frame (for parallel encoding).
Definition Ports.h:167
size_t listLengthBufferOffset
Offset in the translation buffer where list length is stored.
Definition Ports.h:177
size_t dataOffset
Offset of the list data array in the frame.
Definition Ports.h:171
std::string fieldName
Name of the list field.
Definition Ports.h:169
size_t lastFieldOffset
Offset of the 'last' field in the frame.
Definition Ports.h:175
size_t elementSize
Size of each list element in bytes.
Definition Ports.h:173
size_t listDataBufferOffset
Offset in the translation buffer where list data starts.
Definition Ports.h:179
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:201
const WindowType * windowType
The window type being translated.
Definition Ports.h:151
size_t intoTypeBytes
Size of the 'into' type in bytes (for fixed-size types).
Definition Ports.h:197
size_t frameBytes
Number of bytes per wire frame (from the lowered type's bit width).
Definition Ports.h:199
std::vector< FrameInfo > frames
Precomputed information about each frame.
Definition Ports.h:194
Field information describing a field within a frame.
Definition Types.h:317
std::string name
Definition Types.h:318