stcp: ed25519 pubkey exchange in handshake, fix instance destroy segfault, fix chat_tcp_test
- u_async.c: process immediate_queue BEFORE epoll_wait in uasync_poll - stcp_link.c: defensive NULL etcp_conn/etcp_link in stcp_link_close - utun_instance.c: move stcp_server_list_destroy_all to Phase J (before ETCP), add deferred drain in both Phase J and L - stcp.h/c: STCP_HS_ENC_CLIENT 6→38, STCP_HS_ENC_SERVER 7→39, exchange ed25519_pubkey in both handshake directions - etcp_connections.c: copy peer_ed25519 from STCP to ETCP_CONN in etcp_link_enter_ready_tcp; guard link->etcp in tcp_link_close_cb - chat_sync.c: fix lk->conn NULL dereference for TCP links - test_stcp.c: update stcp_server_create/stcp_client_connect callsites - tools/chat_tcp_test: change transport udp→tcp
This commit is contained in:
1 parent
a3783b5f55
commit
38c9f229bc
1272 files changed
+968388
-1
No files matched your search
@@ -0,0 +1,290 @@
|
||||
#include "media_blocks.h"
|
||||
|
||||
#include "../../lib/debug_config.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QDateTime>
|
||||
#include <QRandomGenerator>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
/* ─── MediaBlockReader ─── */
|
||||
|
||||
MediaBlockReader::MediaBlockReader() {}
|
||||
|
||||
void MediaBlockReader::initFromAttrs(const QString& mediaDir,
|
||||
const QString& dt, const QString& basename,
|
||||
const QString& suffix, const QString& ext,
|
||||
const QJsonObject& localAttrs,
|
||||
qint64 blockSize, qint64 totalSize, int totalBlocks)
|
||||
{
|
||||
m_mediaDir = mediaDir; m_dt = dt; m_basename = basename;
|
||||
m_suffix = suffix; m_ext = ext;
|
||||
m_blockSize = blockSize; m_totalSize = totalSize; m_totalBlocks = totalBlocks;
|
||||
m_pos = 0; m_valid = true;
|
||||
|
||||
QString st = localAttrs.value("st").toString();
|
||||
if (st == "fl") {
|
||||
m_mode = SINGLE_FILE;
|
||||
QString fp = localAttrs.value("fp").toString();
|
||||
m_filePath = mediaDir + "/" + fp;
|
||||
m_fileSize = totalSize;
|
||||
QFileInfo fi(m_filePath);
|
||||
m_complete = fi.exists() && fi.size() == totalSize;
|
||||
} else {
|
||||
m_mode = FRAGMENTED;
|
||||
QJsonArray bl = localAttrs.value("bl").toArray();
|
||||
for (const auto& v : bl) m_blocks.append(v.toInt());
|
||||
m_complete = (m_blocks.size() == totalBlocks);
|
||||
}
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "MediaBlockReader: mode=%s complete=%d path=%s blocks=%d/%d",
|
||||
m_mode == SINGLE_FILE ? "single" : "frag", m_complete,
|
||||
qPrintable(debugPath()), m_blocks.size(), totalBlocks);
|
||||
}
|
||||
|
||||
MediaBlockReader MediaBlockReader::fromLegacyFile(const QString& filePath) {
|
||||
MediaBlockReader r;
|
||||
r.m_mode = SINGLE_FILE;
|
||||
r.m_filePath = filePath;
|
||||
r.m_valid = true;
|
||||
QFileInfo fi(filePath);
|
||||
r.m_fileSize = fi.size();
|
||||
r.m_complete = fi.exists() && r.m_fileSize > 0;
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "MediaBlockReader: legacy file=%s size=%lld valid=%d",
|
||||
qPrintable(filePath), (long long)r.m_fileSize, r.m_valid);
|
||||
return r;
|
||||
}
|
||||
|
||||
qint64 MediaBlockReader::blockSizeForN(int n) const {
|
||||
if (n < m_totalBlocks - 1) return m_blockSize;
|
||||
qint64 last = m_totalSize - (m_totalBlocks - 1) * m_blockSize;
|
||||
if (last <= 0) last = m_totalSize;
|
||||
return last;
|
||||
}
|
||||
|
||||
QString MediaBlockReader::blockFilePath(int n) const {
|
||||
return QString("%1/%2_%3_%4_%5.%6")
|
||||
.arg(m_mediaDir, m_dt).arg(n).arg(m_basename, m_suffix, m_ext);
|
||||
}
|
||||
|
||||
QByteArray MediaBlockReader::readAll() {
|
||||
if (!m_valid || !m_complete || m_mode == NONE) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "MediaBlockReader::readAll: invalid/incomplete");
|
||||
return {};
|
||||
}
|
||||
|
||||
if (m_mode == SINGLE_FILE) {
|
||||
QFile f(m_filePath);
|
||||
if (!f.open(QIODevice::ReadOnly)) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "MediaBlockReader::readAll: cannot open %s", qPrintable(m_filePath));
|
||||
return {};
|
||||
}
|
||||
QByteArray data = f.readAll();
|
||||
m_pos = data.size();
|
||||
return data;
|
||||
}
|
||||
|
||||
QByteArray data; data.resize(m_totalSize);
|
||||
qint64 offset = 0;
|
||||
for (int i = 0; i < m_blocks.size(); i++) {
|
||||
int n = m_blocks[i];
|
||||
QString path = blockFilePath(n);
|
||||
QFile f(path);
|
||||
if (!f.open(QIODevice::ReadOnly)) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "MediaBlockReader::readAll: cannot open block %d: %s", n, qPrintable(path));
|
||||
return {};
|
||||
}
|
||||
qint64 sz = blockSizeForN(n);
|
||||
f.read(data.data() + offset, sz);
|
||||
offset += sz;
|
||||
}
|
||||
m_pos = data.size();
|
||||
return data;
|
||||
}
|
||||
|
||||
QByteArray MediaBlockReader::readRange(qint64 offset, qint64 size) {
|
||||
if (!m_valid || offset < 0 || size <= 0 || m_mode == NONE) return {};
|
||||
if (offset >= m_totalSize) return {};
|
||||
|
||||
if (offset + size > m_totalSize) size = m_totalSize - offset;
|
||||
|
||||
if (m_mode == SINGLE_FILE) {
|
||||
QFile f(m_filePath);
|
||||
if (!f.open(QIODevice::ReadOnly)) return {};
|
||||
f.seek(offset);
|
||||
QByteArray data = f.read(size);
|
||||
m_pos = offset + data.size();
|
||||
return data;
|
||||
}
|
||||
|
||||
int startBlock = offset / m_blockSize;
|
||||
QByteArray data; data.resize(size);
|
||||
qint64 copied = 0;
|
||||
qint64 remaining = size;
|
||||
int currentN = startBlock;
|
||||
|
||||
while (remaining > 0 && currentN < m_totalBlocks && m_blocks.contains(currentN)) {
|
||||
qint64 blockStart = (qint64)currentN * m_blockSize;
|
||||
qint64 blockSize = blockSizeForN(currentN);
|
||||
qint64 inBlockOffset = offset + copied - blockStart;
|
||||
if (inBlockOffset < 0) inBlockOffset = 0;
|
||||
qint64 toRead = qMin(remaining, blockSize - inBlockOffset);
|
||||
if (toRead <= 0) break;
|
||||
|
||||
QString path = blockFilePath(currentN);
|
||||
QFile f(path);
|
||||
if (f.open(QIODevice::ReadOnly)) {
|
||||
f.seek(inBlockOffset);
|
||||
f.read(data.data() + copied, toRead);
|
||||
}
|
||||
copied += toRead;
|
||||
remaining -= toRead;
|
||||
currentN++;
|
||||
}
|
||||
m_pos = offset + copied;
|
||||
if (copied < size) data.resize(copied);
|
||||
return data;
|
||||
}
|
||||
|
||||
bool MediaBlockReader::seek(qint64 pos) {
|
||||
if (!m_valid || pos < 0 || pos > m_totalSize) return false;
|
||||
m_pos = pos;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MediaBlockReader::atEnd() const {
|
||||
return m_pos >= m_totalSize;
|
||||
}
|
||||
|
||||
bool MediaBlockReader::saveToFile(const QString& destPath) {
|
||||
if (!m_valid || !m_complete) return false;
|
||||
|
||||
QByteArray data = readAll();
|
||||
if (data.isEmpty()) return false;
|
||||
|
||||
QFile f(destPath);
|
||||
if (!f.open(QIODevice::WriteOnly)) return false;
|
||||
f.write(data);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "MediaBlockReader: saved to %s (%lld bytes)", qPrintable(destPath), (long long)data.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
QString MediaBlockReader::assembleAndCleanup() {
|
||||
if (!m_valid || m_mode != FRAGMENTED || !m_complete) return {};
|
||||
|
||||
QString assembledPath = QString("%1/%2_%3_%4.%5")
|
||||
.arg(m_mediaDir, m_dt, m_basename, m_suffix, m_ext);
|
||||
|
||||
QFile af(assembledPath);
|
||||
if (!af.open(QIODevice::WriteOnly)) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "MediaBlockReader::assemble: cannot create %s", qPrintable(assembledPath));
|
||||
return {};
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_blocks.size(); i++) {
|
||||
int n = m_blocks[i];
|
||||
QString path = blockFilePath(n);
|
||||
QFile bf(path);
|
||||
if (bf.open(QIODevice::ReadOnly)) {
|
||||
af.write(bf.readAll());
|
||||
bf.close();
|
||||
QFile::remove(path);
|
||||
}
|
||||
}
|
||||
af.close();
|
||||
|
||||
m_mode = SINGLE_FILE;
|
||||
m_filePath = assembledPath;
|
||||
m_fileSize = m_totalSize;
|
||||
m_blocks.clear();
|
||||
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "MediaBlockReader: assembled %s", qPrintable(assembledPath));
|
||||
return assembledPath;
|
||||
}
|
||||
|
||||
QJsonObject MediaBlockReader::localAttrsAfterAssembly() const {
|
||||
QJsonObject obj;
|
||||
obj["st"] = "fl";
|
||||
obj["fp"] = QString("%1_%2_%3.%4").arg(m_dt, m_basename, m_suffix, m_ext);
|
||||
QJsonArray bl;
|
||||
for (int i = 0; i < m_totalBlocks; i++) bl.append(i);
|
||||
obj["bl"] = bl;
|
||||
return obj;
|
||||
}
|
||||
|
||||
QString MediaBlockReader::debugPath() const {
|
||||
if (m_mode == SINGLE_FILE) return m_filePath;
|
||||
if (m_mode == FRAGMENTED) return QString("%1/%2_*_%3_%4.%5")
|
||||
.arg(m_mediaDir, m_dt, m_basename, m_suffix, m_ext);
|
||||
return "none";
|
||||
}
|
||||
|
||||
/* ─── media_split_to_blocks ─── */
|
||||
|
||||
qint64 media_calc_block_size(qint64 fileSize) {
|
||||
if (fileSize <= 0) return 0;
|
||||
qint64 target = (fileSize + 29) / 30; /* ceil(file_size / 30) */
|
||||
if (target < 10 * 1024 * 1024) target = 10 * 1024 * 1024;
|
||||
if (target > 25 * 1024 * 1024) target = 25 * 1024 * 1024;
|
||||
return target;
|
||||
}
|
||||
|
||||
MediaBlockInfo media_split_to_blocks(const QString& srcFile,
|
||||
const QString& mediaDir,
|
||||
bool removeSrc)
|
||||
{
|
||||
MediaBlockInfo info;
|
||||
QFileInfo fi(srcFile);
|
||||
if (!fi.exists()) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "media_split_to_blocks: src not found: %s", qPrintable(srcFile));
|
||||
return info;
|
||||
}
|
||||
|
||||
info.fileSize = fi.size();
|
||||
if (info.fileSize == 0) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "media_split_to_blocks: empty file: %s", qPrintable(srcFile));
|
||||
return info;
|
||||
}
|
||||
|
||||
info.blockSize = media_calc_block_size(info.fileSize);
|
||||
info.numBlocks = (int)((info.fileSize + info.blockSize - 1) / info.blockSize);
|
||||
info.ext = fi.suffix();
|
||||
info.basename = fi.completeBaseName();
|
||||
|
||||
QDateTime now = QDateTime::currentDateTimeUtc();
|
||||
info.dt = now.toString("yyyyMMdd-HHmmss");
|
||||
uint16_t rnd = QRandomGenerator::global()->generate() & 0xFFFF;
|
||||
info.suffix = QString::number(rnd, 16).rightJustified(4, '0');
|
||||
|
||||
QDir().mkpath(mediaDir);
|
||||
|
||||
QFile src(srcFile);
|
||||
if (!src.open(QIODevice::ReadOnly)) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "media_split_to_blocks: cannot open src: %s", qPrintable(srcFile));
|
||||
return info;
|
||||
}
|
||||
|
||||
qint64 remaining = info.fileSize;
|
||||
for (int n = 0; n < info.numBlocks; n++) {
|
||||
qint64 chunk = qMin(remaining, info.blockSize);
|
||||
QString path = QString("%1/%2_%3_%4_%5.%6")
|
||||
.arg(mediaDir, info.dt).arg(n).arg(info.basename, info.suffix, info.ext);
|
||||
QFile dst(path);
|
||||
if (!dst.open(QIODevice::WriteOnly)) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "media_split_to_blocks: cannot create block %d: %s", n, qPrintable(path));
|
||||
src.close(); return info;
|
||||
}
|
||||
dst.write(src.read(chunk));
|
||||
dst.close();
|
||||
remaining -= chunk;
|
||||
}
|
||||
src.close();
|
||||
|
||||
if (removeSrc) QFile::remove(srcFile);
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "media_split_to_blocks: %s -> %d blocks (%lld bytes each) in %s",
|
||||
qPrintable(fi.fileName()), info.numBlocks, (long long)info.blockSize, qPrintable(mediaDir));
|
||||
return info;
|
||||
}
|
||||
Reference in new issue
Block a user