Add better HexDump & make safe binary-to-UTF8 conversions

This commit is contained in:
Alex Spataru 2025-01-07 12:14:01 -05:00
parent 9c0f2b6c13
commit 430abb247b
4 changed files with 145 additions and 70 deletions

View File

@ -149,6 +149,7 @@ set(HEADERS
src/IO/CircularBuffer.h
src/IO/FileTransmission.h
src/IO/FrameReader.h
src/IO/HexDump.h
src/JSON/FrameParser.h
src/JSON/ProjectModel.h
src/JSON/Frame.h

View File

@ -100,9 +100,37 @@ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
## hexdump
Copyright (c) 2014, Zac Bergquist
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
## QSimpleUpdater
Copyright (c) 2014-2021 Alex Spataru <https://aspatru.com>.
Copyright (c) 2014-2025 Alex Spataru <https://aspatru.com>.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -24,52 +24,9 @@
#include "IO/Manager.h"
#include "IO/Console.h"
#include "IO/HexDump.h"
#include "Misc/Translator.h"
/**
* Generates a hexdump of the given data
*/
static QString HexDump(const char *data, const size_t size)
{
QString result;
char ascii[17];
ascii[16] = '\0';
for (size_t i = 0; i < size; ++i)
{
char hexBuffer[4];
snprintf(hexBuffer, sizeof(hexBuffer), "%02X ",
static_cast<quint8>(data[i]));
result += hexBuffer;
if (data[i] >= ' ' && data[i] <= '~')
ascii[i % 16] = data[i];
else
ascii[i % 16] = '.';
if ((i + 1) % 8 == 0 || i + 1 == size)
{
result += ' ';
if ((i + 1) % 16 == 0)
result += QString("| %1 \n").arg(ascii);
else if (i + 1 == size)
{
ascii[(i + 1) % 16] = '\0';
if ((i + 1) % 16 <= 8)
result += ' ';
for (size_t j = (i + 1) % 16; j < 16; ++j)
result += " ";
result += QString("| %1 \n").arg(ascii);
}
}
}
return result;
}
/**
* Constructor function
*/
@ -516,10 +473,10 @@ void IO::Console::append(const QString &string, const bool addTimestamp)
// Process lines
while (!tokens.isEmpty())
{
if (m_isStartingLine)
auto token = tokens.first();
if (m_isStartingLine && !token.simplified().isEmpty())
processedString.append(timestamp);
auto token = tokens.first();
processedString.append(token);
m_isStartingLine = (token == QStringLiteral("\n"));
tokens.removeFirst();
@ -589,16 +546,30 @@ QString IO::Console::dataToString(const QByteArray &data)
}
/**
* Converts the given @a data into an UTF-8 string
* @brief Converts a QByteArray to a QString, preserving printable characters
* and line breaks.
*
* Non-printable characters are replaced with a dot ('.') for readability.
*
* @param data The QByteArray to convert.
* @return QString A human-readable string representation of the input data.
*/
QString IO::Console::plainTextStr(const QByteArray &data)
{
QString str = QString::fromUtf8(data);
// Filter out non-printable characters, but keep line breaks
QByteArray filteredData;
filteredData.reserve(data.size());
for (int i = 0; i < data.size(); ++i)
{
unsigned char c = static_cast<unsigned char>(data[i]);
if (std::isprint(c) || std::isspace(c) || c == '\r' || c == '\n')
filteredData.append(c);
else
filteredData.append('.');
}
if (str.toUtf8() != data)
str = QString::fromLatin1(data);
return str;
// Convert the filtered data to QString using UTF-8 as default
return QString::fromUtf8(filteredData);
}
/**
@ -606,21 +577,7 @@ QString IO::Console::plainTextStr(const QByteArray &data)
*/
QString IO::Console::hexadecimalStr(const QByteArray &data)
{
// Remove line breaks from data
QByteArray copy = data;
// Convert data to string with dump every ~80 chars
QString str;
const int characters = 80;
for (int i = 0; i < copy.length(); i += characters)
{
QByteArray line;
for (int j = 0; j < qMin(characters, copy.length() - i); ++j)
line.append(copy.at(i + j));
str.append(HexDump(line.data(), line.size()));
}
// Return string
return str;
std::ostringstream oss;
oss << Hexdump(data.data(), data.size()) << "\n";
return QString::fromStdString(oss.str());
}

89
app/src/IO/HexDump.h Normal file
View File

@ -0,0 +1,89 @@
/*
* Copyright (c) 2014, Zac Bergquist
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <cctype>
#include <iomanip>
#include <ostream>
template<unsigned RowSize, bool ShowAscii>
struct CustomHexdump
{
CustomHexdump(const void *data, unsigned length)
: mData(static_cast<const unsigned char *>(data))
, mLength(length)
{
}
const unsigned char *mData;
const unsigned mLength;
};
template<unsigned RowSize, bool ShowAscii>
std::ostream &operator<<(std::ostream &out,
const CustomHexdump<RowSize, ShowAscii> &dump)
{
out.fill('0');
for (int i = 0; i < dump.mLength; i += RowSize)
{
out << "0x" << std::setw(6) << std::hex << i << ": ";
for (int j = 0; j < RowSize; ++j)
{
if (i + j < dump.mLength)
{
out << std::hex << std::setw(2) << static_cast<int>(dump.mData[i + j])
<< " ";
}
else
out << " ";
}
out << " ";
if (ShowAscii)
{
for (int j = 0; j < RowSize; ++j)
{
if (i + j < dump.mLength)
{
if (std::isprint(dump.mData[i + j]))
out << static_cast<char>(dump.mData[i + j]);
else
out << ".";
}
}
}
out << std::endl;
}
return out;
}
typedef CustomHexdump<16, true> Hexdump;