Skip to content

Commit

Permalink
windows/serialInterface: Implemented read buffering to reduce the num…
Browse files Browse the repository at this point in the history
…ber of ReadFile() syscalls being made
  • Loading branch information
dragonmux committed Jan 14, 2024
1 parent b6d81e7 commit 753c2a0
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 7 deletions.
4 changes: 4 additions & 0 deletions src/include/windows/serialInterface.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@ struct serialInterface_t
{
private:
HANDLE device{INVALID_HANDLE_VALUE};
mutable std::array<uint8_t, 4096U> readBuffer{};
mutable size_t readBufferFullness{0U};
mutable size_t readBufferOffset{0U};

void handleDeviceError(std::string_view operation) noexcept;
void refillBuffer() const;

public:
serialInterface_t() noexcept = default;
Expand Down
41 changes: 34 additions & 7 deletions src/windows/serialInterface.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -268,22 +268,49 @@ void serialInterface_t::writePacket(const std::string_view &packet) const
}
}

// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
void serialInterface_t::refillBuffer() const
{
DWORD bytesReceived = 0;
// Try to fill the read buffer, and if that fails, bail
if (!ReadFile(device, readBuffer.data(), static_cast<size_t>(readBuffer.size()), &bytesReceived, nullptr))
{
console.error("Read from device failed ("sv, GetLastError(), ")"sv);
throw bmpCommsError_t{};
}
/* We now have more data, so update the read buffer counters */
readBufferFullness = bytesReceived;
readBufferOffset = 0U;
}

// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
std::string serialInterface_t::readPacket() const
{
std::array<char, bmp_t::maxPacketSize + 1U> packet{};
DWORD read = 0;
size_t length{0};
for (; length < packet.size(); length += read)
// Try gathering a '#' terminated response
while (length < packet.size())
{
// Due to the protocol's structure, best we can do is reading a single byte at a time.
if (!ReadFile(device, packet.data() + length, 1U, &read, nullptr))
// Check if we need more data or should use what's in the buffer already
if (readBufferOffset == readBufferFullness)
refillBuffer();

// Look for an end of message marker
size_t responseLength{0U};
while (readBufferOffset + responseLength < readBufferFullness && length + responseLength < readBuffer.size())
{
console.error("Read from device failed ("sv, GetLastError(), "), read "sv, length, " bytes");
throw bmpCommsError_t{};
// If we've found one then stop scanning
if (readBuffer[readBufferOffset + responseLength++] == '#')
break;
}
if (read && packet[length] == '#')
// We now either have a remote end of message marker, or need all the data from the buffer
memcpy(packet.data() + length, readBuffer.data() + readBufferOffset, responseLength);
readBufferOffset += responseLength;
length += responseLength - 1U;
// If it's a remote end of message marker, break out the loop
if (packet[length] == '#')
break;
++length;
}

// Adjust the length to remove the beginning '&' (the ending '#' is already taken care of in the read loop)
Expand Down

0 comments on commit 753c2a0

Please sign in to comment.