[+] Aurora::IO::Net::NetSocketConnectByHost
[+] Aurora::IO::FS::DirDeleterEx [+] Aurora::IO::Compress [+] Aurora::IO::Decompress [*] Aurora::Memory::ByteBuffer zero-alloc fixes [*] Aurora::Memory::ByteBuffer linear read of begin/end should return (`const AuUInt8 *`)'s [*] Changed NT file CREATE flags [*] Fix linux regression [*] Update logger sink DirLogArchive ... [+] DirectoryLogger::uMaxLogsOrZeroBeforeDelete ... [+] DirectoryLogger::uMaxCumulativeFileSizeInMiBOrZeroBeforeDelete ... [+] DirectoryLogger::uMaxCumulativeFileSizeInMiBOrZeroBeforeCompress ... [+] DirectoryLogger::uMaxFileTimeInDeltaMSOrZeroBeforeCompress ... [+] DirectoryLogger::uMaxFileTimeInDeltaMSOrZeroBeforeDelete [*] FIX: BufferedLineReader was taking the wrong end head (prep) LZMACompressor [*] Updated build-script for LZMA (when i can be bothered to impl it) (prep) FSOverlappedUtilities (prep) FSDefaultOverlappedWorkerThread | default worker pool / apc dispatcher / auasync dispatcher concept for higher level overlapped ops (stub) [+] Aurora::IO::FS::OverlappedForceDelegatedIO (stub) [+] Aurora::IO::FS::OverlappedCompress (stub) [+] Aurora::IO::FS::OverlappedDecompress (stub) [+] Aurora::IO::FS::OverlappedWrite (stub) [+] Aurora::IO::FS::OverlappedRead (stub) [+] Aurora::IO::FS::OverlappedStat (stub) [+] Aurora::IO::FS::OverlappedCopy (stub) [+] Aurora::IO::FS::OverlappedRelink (stub) [+] Aurora::IO::FS::OverlappedTrustFile (stub) [+] Aurora::IO::FS::OverlappedBlockFile (stub) [+] Aurora::IO::FS::OverlappedUnblockFile (stub) [+] Aurora::IO::FS::OverlappedDelete
This commit is contained in:
parent
bf8c1eb8c7
commit
04aca5fcf2
@ -20,6 +20,7 @@
|
|||||||
"wxwidgets",
|
"wxwidgets",
|
||||||
"glm",
|
"glm",
|
||||||
"bzip2",
|
"bzip2",
|
||||||
|
"liblzma",
|
||||||
"lz4"
|
"lz4"
|
||||||
],
|
],
|
||||||
"depends": [
|
"depends": [
|
||||||
|
@ -44,6 +44,14 @@ namespace Aurora::IO::FS
|
|||||||
*/
|
*/
|
||||||
AUKN_SYM bool DirDeleter(const AuString &string);
|
AUKN_SYM bool DirDeleter(const AuString &string);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
* @param string
|
||||||
|
* @param failingPaths
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
AUKN_SYM bool DirDeleterEx(const AuString &string, AuList<AuString> &failingPaths);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Writes a blob into a file chunk-by-chunk.
|
* @brief Writes a blob into a file chunk-by-chunk.
|
||||||
* The directory structure may or may not exist for the write operation to succeed.
|
* The directory structure may or may not exist for the write operation to succeed.
|
||||||
@ -143,19 +151,34 @@ namespace Aurora::IO::FS
|
|||||||
AUKN_SYM bool BlockFile(const AuString &path);
|
AUKN_SYM bool BlockFile(const AuString &path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Specifies generic level of trust
|
* @brief Specifies generic local-system/trusted level of trust
|
||||||
* @param path
|
* @param path
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
AUKN_SYM bool UnblockFile(const AuString &path);
|
AUKN_SYM bool UnblockFile(const AuString &path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Specifies user/internal level trust of a file
|
* @brief Specifies user/executable level trust of a file
|
||||||
|
* @warning This routine is intended to enable execution of files
|
||||||
|
* on both UNIX and NT based systems. UnblockFile
|
||||||
|
* will be enough for resources and some powershell scripts;
|
||||||
|
* however, this is required for unblocking / `mode |= 0111`ing
|
||||||
|
* executable files.
|
||||||
* @param path
|
* @param path
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
AUKN_SYM bool TrustFile(const AuString &path);
|
AUKN_SYM bool TrustFile(const AuString &path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Transfers the contents of the specified filepath through a
|
||||||
|
* zstandard compression pipe to an ending path + ".zst" file.
|
||||||
|
* @warning This file API does not relate to file-system level compression
|
||||||
|
* @param path = ur mother
|
||||||
|
*/
|
||||||
|
AUKN_SYM bool Compress(const AuString &path, AuInt8 level = 17);
|
||||||
|
|
||||||
|
AUKN_SYM bool Decompress(const AuString &path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Normalizes an arbitrary string of in
|
* @brief Normalizes an arbitrary string of in
|
||||||
* @param out
|
* @param out
|
||||||
@ -198,3 +221,4 @@ namespace Aurora::IO::FS
|
|||||||
#include "Async.hpp"
|
#include "Async.hpp"
|
||||||
#include "Watcher.hpp"
|
#include "Watcher.hpp"
|
||||||
#include "IReadDir.hpp"
|
#include "IReadDir.hpp"
|
||||||
|
#include "Overlapped.hpp"
|
69
Include/Aurora/IO/FS/Overlapped.hpp
Normal file
69
Include/Aurora/IO/FS/Overlapped.hpp
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/***
|
||||||
|
Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||||
|
|
||||||
|
File: Overlapped.hpp
|
||||||
|
Date: 2023-1-26
|
||||||
|
Author: Reece
|
||||||
|
Note: Defer to Async.hpp for creation of overlapped stream objects
|
||||||
|
This header defines FS.hpp-like APIs that run overlapped or on a
|
||||||
|
worker thread or two.
|
||||||
|
***/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Aurora::IO::Loop
|
||||||
|
{
|
||||||
|
struct ILoopSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Aurora::IO::FS
|
||||||
|
{
|
||||||
|
struct IOverlappedOperationBase;
|
||||||
|
|
||||||
|
AUKN_INTERFACE(IOverlappedCallback,
|
||||||
|
AUI_METHOD(void, OnSuccess, (AuSPtr<IOverlappedOperationBase>, pOriginator)),
|
||||||
|
AUI_METHOD(void, OnFailure, (AuSPtr<IOverlappedOperationBase>, pOriginator))
|
||||||
|
);
|
||||||
|
|
||||||
|
struct IOverlappedOperationBase
|
||||||
|
{
|
||||||
|
virtual AuSPtr<Loop::ILoopSource> ToWaitable() = 0;
|
||||||
|
|
||||||
|
virtual AuSPtr<IOverlappedCallback> SetCallback(AuSPtr<IOverlappedOperationBase> pCallback) = 0;
|
||||||
|
|
||||||
|
virtual bool IsOperationComplete() = 0;
|
||||||
|
virtual bool IsOperationSuccessful() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IOverlappedStatOperation : IOverlappedOperationBase
|
||||||
|
{
|
||||||
|
virtual AuSPtr<Stat> GetStats() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IOverlappedReadOperation : IOverlappedOperationBase
|
||||||
|
{
|
||||||
|
virtual AuSPtr<Aurora::Memory::ByteBuffer> GetByteBuffer() = 0;
|
||||||
|
virtual AuSPtr<Aurora::Memory::MemoryViewWrite> GetReadView() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief If running under AuAsync runner, we use the local thread for the entire IO operation.
|
||||||
|
* In the future, whence I can be bothered to finish the APC api, *all* overlapped operations
|
||||||
|
* will be processed on the local thread.
|
||||||
|
* Specifying true forces all IO to occour on a preallocated pool of IO workers
|
||||||
|
* @warning this api is thread-local
|
||||||
|
* @warning this api does not change the behaviour of overlapped file streamss
|
||||||
|
*/
|
||||||
|
AUKN_SYM bool OverlappedForceDelegatedIO(bool bForceIOWorkerThreads);
|
||||||
|
|
||||||
|
AUKN_SYM AuSPtr<IOverlappedOperationBase> OverlappedCompress(const AuString &path, AuInt8 iLevel = 17);
|
||||||
|
AUKN_SYM AuSPtr<IOverlappedOperationBase> OverlappedDecompress(const AuString &path);
|
||||||
|
AUKN_SYM AuSPtr<IOverlappedOperationBase> OverlappedWrite(const AuString &path, AuSPtr<Aurora::Memory::MemoryViewRead> pMemoryView);
|
||||||
|
AUKN_SYM AuSPtr<IOverlappedReadOperation> OverlappedRead(const AuString &path);
|
||||||
|
AUKN_SYM AuSPtr<IOverlappedStatOperation> OverlappedStat(const AuString &path);
|
||||||
|
AUKN_SYM AuSPtr<IOverlappedOperationBase> OverlappedCopy(const AuString &path, const AuString &dest);
|
||||||
|
AUKN_SYM AuSPtr<IOverlappedOperationBase> OverlappedRelink(const AuString &path, const AuString &dest);
|
||||||
|
AUKN_SYM AuSPtr<IOverlappedOperationBase> OverlappedTrustFile(const AuString &path);
|
||||||
|
AUKN_SYM AuSPtr<IOverlappedOperationBase> OverlappedBlockFile(const AuString &path);
|
||||||
|
AUKN_SYM AuSPtr<IOverlappedOperationBase> OverlappedUnblockFile(const AuString &path);
|
||||||
|
AUKN_SYM AuSPtr<IOverlappedOperationBase> OverlappedDelete(const AuString &path);
|
||||||
|
}
|
@ -11,9 +11,22 @@ namespace Aurora::IO::Net
|
|||||||
{
|
{
|
||||||
struct ISocketServer;
|
struct ISocketServer;
|
||||||
|
|
||||||
|
struct NetSocketConnectByHost
|
||||||
|
{
|
||||||
|
AuOptionalEx<NetHostname> netHostname;
|
||||||
|
AuOptionalEx<AuUInt16> uPort;
|
||||||
|
AuOptionalEx<ETransportProtocol> protocol;
|
||||||
|
};
|
||||||
|
|
||||||
struct NetSocketConnect
|
struct NetSocketConnect
|
||||||
{
|
{
|
||||||
NetEndpoint endpoint;
|
// connect by protocol, address:port
|
||||||
|
AuOptionalEx<NetEndpoint> endpoint;
|
||||||
|
|
||||||
|
// or connect by [string/ip family, ip address], port
|
||||||
|
NetSocketConnectByHost byHost;
|
||||||
|
|
||||||
|
//
|
||||||
AuSPtr<ISocketDriver> pDriver;
|
AuSPtr<ISocketDriver> pDriver;
|
||||||
AuUInt32 uMaxConnectTimeMs {};
|
AuUInt32 uMaxConnectTimeMs {};
|
||||||
};
|
};
|
||||||
|
@ -11,12 +11,16 @@ namespace Aurora::IO::Net
|
|||||||
{
|
{
|
||||||
struct AUKN_SYM NetHostname
|
struct AUKN_SYM NetHostname
|
||||||
{
|
{
|
||||||
|
NetHostname(const NetHostname &cpy);
|
||||||
|
NetHostname();
|
||||||
NetHostname(const AuString &hostname);
|
NetHostname(const AuString &hostname);
|
||||||
NetHostname(const IPAddress &ipAddress);
|
NetHostname(const IPAddress &ipAddress);
|
||||||
|
|
||||||
const EHostnameType type;
|
EHostnameType type;
|
||||||
const AuString hostname;
|
AuString hostname;
|
||||||
const IPAddress address;
|
IPAddress address;
|
||||||
|
|
||||||
|
NetHostname &operator =(const NetHostname &other);
|
||||||
|
|
||||||
bool operator ==(const NetHostname &other) const;
|
bool operator ==(const NetHostname &other) const;
|
||||||
const AuUInt HashCode() const;
|
const AuUInt HashCode() const;
|
||||||
|
@ -16,8 +16,11 @@ namespace Aurora::Logging
|
|||||||
{
|
{
|
||||||
struct DirectoryLogger
|
struct DirectoryLogger
|
||||||
{
|
{
|
||||||
AuUInt32 maxLogsOrZero {};
|
AuUInt32 uMaxLogsOrZeroBeforeDelete {};
|
||||||
AuUInt32 maxFileSizeOrZero {}; // MB
|
AuUInt32 uMaxCumulativeFileSizeInMiBOrZeroBeforeDelete {};
|
||||||
|
AuUInt32 uMaxCumulativeFileSizeInMiBOrZeroBeforeCompress {};
|
||||||
|
AuUInt32 uMaxFileTimeInDeltaMSOrZeroBeforeCompress {};
|
||||||
|
AuUInt32 uMaxFileTimeInDeltaMSOrZeroBeforeDelete {};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -116,7 +116,13 @@ namespace Aurora::Memory
|
|||||||
*/
|
*/
|
||||||
inline ByteBuffer(const ByteBuffer &buffer, bool preservePointers = true)
|
inline ByteBuffer(const ByteBuffer &buffer, bool preservePointers = true)
|
||||||
{
|
{
|
||||||
this->base = FAlloc<AuUInt8 *>(buffer.length);
|
if (buffer.length)
|
||||||
|
{
|
||||||
|
this->base = FAlloc<AuUInt8 *>(buffer.length);
|
||||||
|
}
|
||||||
|
this->scaleSize = buffer.scaleSize;
|
||||||
|
this->flagCircular = buffer.flagCircular;
|
||||||
|
this->flagExpandable = buffer.flagExpandable;
|
||||||
if (!this->base)
|
if (!this->base)
|
||||||
{
|
{
|
||||||
Reset();
|
Reset();
|
||||||
@ -135,9 +141,6 @@ namespace Aurora::Memory
|
|||||||
this->readPtr = this->base;
|
this->readPtr = this->base;
|
||||||
}
|
}
|
||||||
AuMemcpy(this->base, buffer.base, this->length);
|
AuMemcpy(this->base, buffer.base, this->length);
|
||||||
this->flagCircular = buffer.flagCircular;
|
|
||||||
this->flagExpandable = buffer.flagExpandable;
|
|
||||||
this->scaleSize = buffer.scaleSize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -149,7 +152,8 @@ namespace Aurora::Memory
|
|||||||
*/
|
*/
|
||||||
inline ByteBuffer(const void *in, AuUInt length, bool circular = false, bool expandable = false) : flagCircular(circular), flagExpandable(expandable), flagReadError(0), flagWriteError(0)
|
inline ByteBuffer(const void *in, AuUInt length, bool circular = false, bool expandable = false) : flagCircular(circular), flagExpandable(expandable), flagReadError(0), flagWriteError(0)
|
||||||
{
|
{
|
||||||
this->base = FAlloc<AuUInt8 *>(length);
|
this->scaleSize = kBufferInitialPower;
|
||||||
|
this->base = length ? FAlloc<AuUInt8 *>(length) : nullptr;
|
||||||
if (!this->base)
|
if (!this->base)
|
||||||
{
|
{
|
||||||
Reset();
|
Reset();
|
||||||
@ -160,12 +164,12 @@ namespace Aurora::Memory
|
|||||||
this->readPtr = this->base;
|
this->readPtr = this->base;
|
||||||
this->writePtr = this->readPtr + this->length;
|
this->writePtr = this->readPtr + this->length;
|
||||||
AuMemcpy(this->base, in, this->length);
|
AuMemcpy(this->base, in, this->length);
|
||||||
this->scaleSize = kBufferInitialPower;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline ByteBuffer(const AuList<AuUInt8> &vector, bool circular = false, bool expandable = false) : flagCircular(circular), flagExpandable(expandable), flagReadError(0), flagWriteError(0)
|
inline ByteBuffer(const AuList<AuUInt8> &vector, bool circular = false, bool expandable = false) : flagCircular(circular), flagExpandable(expandable), flagReadError(0), flagWriteError(0)
|
||||||
{
|
{
|
||||||
this->base = FAlloc<AuUInt8 *>(vector.size());
|
this->scaleSize = kBufferInitialPower;
|
||||||
|
this->base = vector.size() ? FAlloc<AuUInt8 *>(vector.size()) : nullptr;
|
||||||
if (!this->base)
|
if (!this->base)
|
||||||
{
|
{
|
||||||
Reset();
|
Reset();
|
||||||
@ -176,11 +180,11 @@ namespace Aurora::Memory
|
|||||||
this->readPtr = this->base;
|
this->readPtr = this->base;
|
||||||
this->writePtr = this->readPtr + this->length;
|
this->writePtr = this->readPtr + this->length;
|
||||||
AuMemcpy(this->base, vector.data(), this->length);
|
AuMemcpy(this->base, vector.data(), this->length);
|
||||||
this->scaleSize = kBufferInitialPower;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline ByteBuffer(AuUInt length, bool circular = false, bool expandable = false) : flagCircular(circular), flagExpandable(expandable), flagReadError(0), flagWriteError(0)
|
inline ByteBuffer(AuUInt length, bool circular = false, bool expandable = false) : flagCircular(circular), flagExpandable(expandable), flagReadError(0), flagWriteError(0)
|
||||||
{
|
{
|
||||||
|
this->scaleSize = kBufferInitialPower;
|
||||||
if (!length)
|
if (!length)
|
||||||
{
|
{
|
||||||
Reset();
|
Reset();
|
||||||
@ -196,7 +200,6 @@ namespace Aurora::Memory
|
|||||||
this->allocSize = length;
|
this->allocSize = length;
|
||||||
this->readPtr = this->base;
|
this->readPtr = this->base;
|
||||||
this->writePtr = this->base;
|
this->writePtr = this->base;
|
||||||
this->scaleSize = kBufferInitialPower;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline ByteBuffer(AuUInt length, AuUInt alignment, bool circular = false, bool expandable = false) : flagCircular(circular), flagExpandable(expandable), flagReadError(0), flagWriteError(0)
|
inline ByteBuffer(AuUInt length, AuUInt alignment, bool circular = false, bool expandable = false) : flagCircular(circular), flagExpandable(expandable), flagReadError(0), flagWriteError(0)
|
||||||
@ -206,6 +209,7 @@ namespace Aurora::Memory
|
|||||||
Reset();
|
Reset();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this->scaleSize = kBufferInitialPower;
|
||||||
this->base = ZAlloc<AuUInt8 *>(length, alignment);
|
this->base = ZAlloc<AuUInt8 *>(length, alignment);
|
||||||
if (!this->base)
|
if (!this->base)
|
||||||
{
|
{
|
||||||
@ -216,14 +220,14 @@ namespace Aurora::Memory
|
|||||||
this->allocSize = length;
|
this->allocSize = length;
|
||||||
this->readPtr = this->base;
|
this->readPtr = this->base;
|
||||||
this->writePtr = this->base;
|
this->writePtr = this->base;
|
||||||
this->scaleSize = kBufferInitialPower;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
ByteBuffer(T *base, T *end, bool circular = false, bool expandable = false) : flagCircular(circular), flagExpandable(expandable), flagReadError(0), flagWriteError(0)
|
ByteBuffer(T *base, T *end, bool circular = false, bool expandable = false) : flagCircular(circular), flagExpandable(expandable), flagReadError(0), flagWriteError(0)
|
||||||
{
|
{
|
||||||
auto length = static_cast<AuUInt>(end - base) * sizeof(T);
|
auto length = static_cast<AuUInt>(end - base) * sizeof(T);
|
||||||
this->base = ZAlloc<AuUInt8 *>(length);
|
this->base = length ? ZAlloc<AuUInt8 *>(length) : nullptr;
|
||||||
|
this->scaleSize = kBufferInitialPower;
|
||||||
if (!this->base)
|
if (!this->base)
|
||||||
{
|
{
|
||||||
Reset();
|
Reset();
|
||||||
@ -233,7 +237,6 @@ namespace Aurora::Memory
|
|||||||
this->allocSize = length;
|
this->allocSize = length;
|
||||||
this->readPtr = this->base;
|
this->readPtr = this->base;
|
||||||
this->writePtr = this->base + length;
|
this->writePtr = this->base + length;
|
||||||
this->scaleSize = kBufferInitialPower;
|
|
||||||
AuMemcpy(this->base, base, length);
|
AuMemcpy(this->base, base, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,14 +288,14 @@ namespace Aurora::Memory
|
|||||||
* @warning writers should use ::GetLinearWriteable(uDesiredLength) or ::GetOrAllocateLinearWriteable(...)
|
* @warning writers should use ::GetLinearWriteable(uDesiredLength) or ::GetOrAllocateLinearWriteable(...)
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
inline auline AuUInt8 * begin() const;
|
inline auline const AuUInt8 * begin() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief linear read end
|
* @brief linear read end
|
||||||
* @warning writers should use ::GetLinearWriteable(uDesiredLength) or ::GetOrAllocateLinearWriteable(...)
|
* @warning writers should use ::GetLinearWriteable(uDesiredLength) or ::GetOrAllocateLinearWriteable(...)
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
inline auline AuUInt8 * end() const;
|
inline auline const AuUInt8 * end() const;
|
||||||
|
|
||||||
inline auline bool empty() const;
|
inline auline bool empty() const;
|
||||||
|
|
||||||
|
@ -16,6 +16,16 @@ namespace Aurora::Memory
|
|||||||
Free(this->base);
|
Free(this->base);
|
||||||
this->base = nullptr;
|
this->base = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!length)
|
||||||
|
{
|
||||||
|
this->length = length;
|
||||||
|
this->allocSize = length;
|
||||||
|
this->readPtr = this->base;
|
||||||
|
this->writePtr = this->base;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
this->base = fast ? FAlloc<AuUInt8 *>(length) : ZAlloc<AuUInt8 *>(length);
|
this->base = fast ? FAlloc<AuUInt8 *>(length) : ZAlloc<AuUInt8 *>(length);
|
||||||
if (!this->base)
|
if (!this->base)
|
||||||
{
|
{
|
||||||
@ -35,11 +45,22 @@ namespace Aurora::Memory
|
|||||||
Free(this->base);
|
Free(this->base);
|
||||||
this->base = nullptr;
|
this->base = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!length)
|
||||||
|
{
|
||||||
|
this->length = length;
|
||||||
|
this->allocSize = length;
|
||||||
|
this->readPtr = this->base;
|
||||||
|
this->writePtr = this->base;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
this->base = fast ? FAlloc<AuUInt8 *>(length, alignment) : ZAlloc<AuUInt8 *>(length, alignment);
|
this->base = fast ? FAlloc<AuUInt8 *>(length, alignment) : ZAlloc<AuUInt8 *>(length, alignment);
|
||||||
if (!this->base)
|
if (!this->base)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->length = length;
|
this->length = length;
|
||||||
this->allocSize = length;
|
this->allocSize = length;
|
||||||
this->readPtr = this->base;
|
this->readPtr = this->base;
|
||||||
@ -119,9 +140,9 @@ namespace Aurora::Memory
|
|||||||
|
|
||||||
if (length == 0)
|
if (length == 0)
|
||||||
{
|
{
|
||||||
this->length = 0;
|
this->length = 0;
|
||||||
this->writePtr = this->base;
|
this->writePtr = this->base;
|
||||||
this->readPtr = this->base;
|
this->readPtr = this->base;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
|
|
||||||
namespace Aurora::Memory
|
namespace Aurora::Memory
|
||||||
{
|
{
|
||||||
|
static const auto kMaxSaneElementsForAuMemory = 0xFFFFF;
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
bool ByteBuffer::Read(T &out)
|
bool ByteBuffer::Read(T &out)
|
||||||
{
|
{
|
||||||
@ -22,10 +24,16 @@ namespace Aurora::Memory
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto len = Read<AuUInt32>();
|
auto uLength = Read<AuUInt32>();
|
||||||
out.resize(len);
|
if (uLength > kMaxSaneElementsForAuMemory)
|
||||||
|
{
|
||||||
|
this->flagReadError = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
for (auto i = 0u; i < len; i++)
|
out.resize(uLength);
|
||||||
|
|
||||||
|
for (auto i = 0u; i < uLength; i++)
|
||||||
{
|
{
|
||||||
Read<T::value_type>(out[i]);
|
Read<T::value_type>(out[i]);
|
||||||
}
|
}
|
||||||
@ -34,7 +42,13 @@ namespace Aurora::Memory
|
|||||||
}
|
}
|
||||||
else if constexpr (AuIsSame_v<AuRemoveReference_t<T>, AuString>)
|
else if constexpr (AuIsSame_v<AuRemoveReference_t<T>, AuString>)
|
||||||
{
|
{
|
||||||
out.resize(Read<AuUInt32>());
|
auto uLength = Read<AuUInt32>();
|
||||||
|
if (uLength > kMaxSaneElementsForAuMemory)
|
||||||
|
{
|
||||||
|
this->flagReadError = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out.resize(uLength);
|
||||||
Read(out.data(), out.size());
|
Read(out.data(), out.size());
|
||||||
return !this->flagReadError;
|
return !this->flagReadError;
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ namespace Aurora::Memory
|
|||||||
|
|
||||||
AuUInt8 &ByteBuffer::operator [](AuUInt idx) const
|
AuUInt8 &ByteBuffer::operator [](AuUInt idx) const
|
||||||
{
|
{
|
||||||
auto pBegin = this->begin();
|
auto pBegin = (AuUInt8 *)this->begin(); // intentionally returning a volatile reference
|
||||||
auto pEnd = this->end();
|
auto pEnd = this->end();
|
||||||
SysAssert(idx < (AuUInt)(pEnd - pBegin));
|
SysAssert(idx < (AuUInt)(pEnd - pBegin));
|
||||||
return *(pBegin + idx);
|
return *(pBegin + idx);
|
||||||
@ -62,7 +62,7 @@ namespace Aurora::Memory
|
|||||||
return IsEmpty();
|
return IsEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
AuUInt8 *ByteBuffer::begin() const
|
const AuUInt8 *ByteBuffer::begin() const
|
||||||
{
|
{
|
||||||
if (this->flagCircular)
|
if (this->flagCircular)
|
||||||
{
|
{
|
||||||
@ -75,7 +75,7 @@ namespace Aurora::Memory
|
|||||||
return this->readPtr;
|
return this->readPtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
AuUInt8 *ByteBuffer::end() const
|
const AuUInt8 *ByteBuffer::end() const
|
||||||
{
|
{
|
||||||
AuUInt8 *pBase {};
|
AuUInt8 *pBase {};
|
||||||
AuUInt uCount {};
|
AuUInt uCount {};
|
||||||
|
@ -270,7 +270,9 @@ namespace Aurora
|
|||||||
{
|
{
|
||||||
/// You can bypass branding by assigning an empty string to 'defaultBrand'
|
/// You can bypass branding by assigning an empty string to 'defaultBrand'
|
||||||
AuString defaultBrand = "Aurora SDK Sample";
|
AuString defaultBrand = "Aurora SDK Sample";
|
||||||
};
|
bool bForceOverlappedUtilsToDelegatedThreadPool { false };
|
||||||
|
AuUInt32 uOverlappedUtilsThreadPoolSize { 2 }; // note: this does not relate to the overlapped aio apis
|
||||||
|
}; // these threads are only spawned as a fallback for AuFS::Overlapped*** apis
|
||||||
|
|
||||||
struct DebugConfig
|
struct DebugConfig
|
||||||
{
|
{
|
||||||
|
0
Source/Compression/Compressors/LZMACompressor.hpp
Normal file
0
Source/Compression/Compressors/LZMACompressor.hpp
Normal file
0
Source/Compression/Compressors/LZMADecompressor.hpp
Normal file
0
Source/Compression/Compressors/LZMADecompressor.hpp
Normal file
@ -26,7 +26,7 @@ namespace Aurora::IO::Character
|
|||||||
}
|
}
|
||||||
|
|
||||||
AuUInt bytesRead;
|
AuUInt bytesRead;
|
||||||
if (this->inputStream_->Read(AuMemoryViewStreamWrite(AuMemoryViewWrite(this->buffer_.writePtr, this->buffer_.end()), bytesRead)) !=
|
if (this->inputStream_->Read(AuMemoryViewStreamWrite(AuMemoryViewWrite(this->buffer_.writePtr, this->buffer_.writePtr + this->buffer_.length), bytesRead)) !=
|
||||||
EStreamError::eErrorNone)
|
EStreamError::eErrorNone)
|
||||||
{
|
{
|
||||||
SysPushErrorIO();
|
SysPushErrorIO();
|
||||||
|
@ -17,18 +17,19 @@ namespace Aurora::IO::FS
|
|||||||
AuList<AuString> nextLevel2;
|
AuList<AuString> nextLevel2;
|
||||||
AuString curPath;
|
AuString curPath;
|
||||||
AuString curSubDir;
|
AuString curSubDir;
|
||||||
|
AuList<AuString> failedPaths;
|
||||||
|
|
||||||
bool OpenDir(const AuString &str)
|
bool OpenDir(const AuString &str)
|
||||||
{
|
{
|
||||||
curPath = str;
|
this->curPath = str;
|
||||||
pDir = ReadDir(str);
|
this->pDir = ReadDir(str);
|
||||||
return bool(pDir);
|
return bool(pDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenNext(const AuString &str)
|
bool OpenNext(const AuString &str)
|
||||||
{
|
{
|
||||||
curPath = str;
|
this->curPath = str;
|
||||||
pDir = ReadDir(str);
|
this->pDir = ReadDir(str);
|
||||||
return bool(pDir);
|
return bool(pDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,15 +37,15 @@ namespace Aurora::IO::FS
|
|||||||
{
|
{
|
||||||
this->pDir.reset();
|
this->pDir.reset();
|
||||||
|
|
||||||
if (!nextLevel.size())
|
if (!this->nextLevel.size())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto a = nextLevel[0];
|
auto a = this->nextLevel[0];
|
||||||
nextLevel.erase(nextLevel.begin());
|
this->nextLevel.erase(this->nextLevel.begin());
|
||||||
this->pDir = ReadDir(curPath + "/" + a);
|
this->pDir = ReadDir(this->curPath + "/" + a);
|
||||||
curSubDir = a;
|
this->curSubDir = a;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual StatEx *Next() override
|
virtual StatEx *Next() override
|
||||||
@ -79,7 +80,10 @@ namespace Aurora::IO::FS
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AuFS::Remove(pNext->path);
|
if (!AuFS::Remove(pNext->path))
|
||||||
|
{
|
||||||
|
this->failedPaths.push_back(pNext->fileName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return pNext;
|
return pNext;
|
||||||
@ -87,9 +91,13 @@ namespace Aurora::IO::FS
|
|||||||
|
|
||||||
void RemoveDirs()
|
void RemoveDirs()
|
||||||
{
|
{
|
||||||
for (auto itr = nextLevel2.rbegin(); itr != nextLevel2.rend(); itr++)
|
for (auto itr = this->nextLevel2.rbegin(); itr != this->nextLevel2.rend(); itr++)
|
||||||
{
|
{
|
||||||
AuFS::Remove(curPath + "/" + itr->c_str());
|
auto dir = this->curPath + "/" + itr->c_str();
|
||||||
|
if (!AuFS::Remove(dir))
|
||||||
|
{
|
||||||
|
this->failedPaths.push_back(itr->c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -118,4 +126,44 @@ namespace Aurora::IO::FS
|
|||||||
AuFS::Remove(string);
|
AuFS::Remove(string);
|
||||||
return !AuFS::DirExists(string);
|
return !AuFS::DirExists(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AUKN_SYM bool DirDeleterEx(const AuString &string, AuList<AuString> &failingPaths)
|
||||||
|
{
|
||||||
|
auto pObj = AuMakeShared<RecursiveDirDeleter>();
|
||||||
|
if (!pObj)
|
||||||
|
{
|
||||||
|
SysPushErrorMem();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pObj->OpenDir(string))
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
while (pObj->Next())
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pObj->RemoveDirs();
|
||||||
|
|
||||||
|
AuFS::Remove(string);
|
||||||
|
|
||||||
|
if (AuFS::DirExists(string))
|
||||||
|
{
|
||||||
|
for (const auto &str : pObj->failedPaths)
|
||||||
|
{
|
||||||
|
if (AuFS::FileExists(str) ||
|
||||||
|
AuFS::DirExists(str))
|
||||||
|
{
|
||||||
|
failingPaths.push_back(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
@ -192,7 +192,18 @@ namespace Aurora::IO::FS
|
|||||||
|
|
||||||
CreateDirectories(pathNormalized, true);
|
CreateDirectories(pathNormalized, true);
|
||||||
|
|
||||||
fileHandle = CreateFileW(win32Path.c_str(), GENERIC_WRITE, NULL, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
fileHandle = CreateFileW(win32Path.c_str(), GENERIC_WRITE | GENERIC_READ, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
|
if (fileHandle == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
fileHandle = CreateFileW(win32Path.c_str(), GENERIC_WRITE, NULL, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileHandle == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
AuThreading::ContextYield();
|
||||||
|
fileHandle = CreateFileW(win32Path.c_str(), GENERIC_WRITE | GENERIC_READ, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
if (fileHandle == INVALID_HANDLE_VALUE)
|
if (fileHandle == INVALID_HANDLE_VALUE)
|
||||||
{
|
{
|
||||||
SysPushErrorIO("Couldn't open handle: {}", path);
|
SysPushErrorIO("Couldn't open handle: {}", path);
|
||||||
|
@ -250,7 +250,7 @@ namespace Aurora::IO::FS
|
|||||||
|
|
||||||
// NOTE: Linux filesystems are such a cluster fuck of unimplemented interfaces and half-assed drivers
|
// NOTE: Linux filesystems are such a cluster fuck of unimplemented interfaces and half-assed drivers
|
||||||
// It's not unusual for these "files" to not support the required seek operations across NIX-like oses.
|
// It's not unusual for these "files" to not support the required seek operations across NIX-like oses.
|
||||||
if (len == 0)
|
if (qwLength == 0)
|
||||||
{
|
{
|
||||||
if (bIsStupidFD)
|
if (bIsStupidFD)
|
||||||
{
|
{
|
||||||
@ -291,7 +291,7 @@ namespace Aurora::IO::FS
|
|||||||
static bool UnixExists(const AuString &path, bool dir)
|
static bool UnixExists(const AuString &path, bool dir)
|
||||||
{
|
{
|
||||||
struct stat s;
|
struct stat s;
|
||||||
int err = stat(path.c_str(), &s);
|
int err = ::stat(path.c_str(), &s);
|
||||||
if (-1 == err)
|
if (-1 == err)
|
||||||
{
|
{
|
||||||
SysAssert(ENOENT == errno, "General File IO Error, path {}", path);
|
SysAssert(ENOENT == errno, "General File IO Error, path {}", path);
|
||||||
|
224
Source/IO/FS/FSCompress.cpp
Normal file
224
Source/IO/FS/FSCompress.cpp
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
/***
|
||||||
|
Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||||
|
|
||||||
|
File: FSCompress.cpp
|
||||||
|
Date: 2023-1-26
|
||||||
|
Author: Reece
|
||||||
|
***/
|
||||||
|
#include <Source/RuntimeInternal.hpp>
|
||||||
|
#include "FS.hpp"
|
||||||
|
#include "FS.Generic.hpp"
|
||||||
|
#include <Source/Time/Time.hpp>
|
||||||
|
|
||||||
|
namespace Aurora::IO::FS
|
||||||
|
{
|
||||||
|
static const AuUInt64 kFileCopyBlock = 0xFFFF * 4; // 64KiB
|
||||||
|
static auto const kCompressionType = AuCompression::ECompressionType::eZSTD;
|
||||||
|
static const AuString kStringSuffix = ".zst";
|
||||||
|
|
||||||
|
AUKN_SYM bool Compress(const AuString &path, AuInt8 level)
|
||||||
|
{
|
||||||
|
static const auto kCompressionReadChunks = kFileCopyBlock;
|
||||||
|
|
||||||
|
auto pFileSrc = OpenReadShared(path, EFileAdvisoryLockLevel::eBlockWrite);
|
||||||
|
if (!pFileSrc)
|
||||||
|
{
|
||||||
|
SysPushErrorIO("Couldn't open compression source path: {}", path);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pFileDest = OpenWriteShared(path + kStringSuffix, EFileAdvisoryLockLevel::eBlockReadWrite);
|
||||||
|
if (!pFileDest)
|
||||||
|
{
|
||||||
|
SysPushErrorIO("Couldn't open compression destination path: {}.{}", path, kStringSuffix);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pFileDest->GetLength())
|
||||||
|
{
|
||||||
|
SysPushErrorIO("File exists");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto qwLength = pFileSrc->GetLength();
|
||||||
|
|
||||||
|
auto pFileStream = AuMakeShared<AuIO::FS::FileReader>(pFileSrc);
|
||||||
|
if (!pFileStream)
|
||||||
|
{
|
||||||
|
SysPushErrorMemory();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pDestStream = AuMakeShared<AuIO::FS::FileWriter>(pFileDest);
|
||||||
|
if (!pDestStream)
|
||||||
|
{
|
||||||
|
SysPushErrorMemory();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
AuCompression::CompressInfo compress { kCompressionType };
|
||||||
|
compress.uCompressionLevel = level;
|
||||||
|
compress.uInternalStreamSize = kCompressionReadChunks * 2;
|
||||||
|
auto pCompressor = AuCompression::CompressorUnique(pFileStream, compress);
|
||||||
|
if (!pCompressor)
|
||||||
|
{
|
||||||
|
SysPushErrorMemory("no compressor");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
AuUInt64 qwTotalRead {};
|
||||||
|
{
|
||||||
|
AuByteBuffer tempMemory(kCompressionReadChunks);
|
||||||
|
if (!tempMemory)
|
||||||
|
{
|
||||||
|
SysPushErrorMemory();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (AuUInt64 i = 0; i < qwLength; i += 0 /*kCompressionReadChunks*/)
|
||||||
|
{
|
||||||
|
auto [read, written] = pCompressor->Ingest(kCompressionReadChunks);
|
||||||
|
i += read;
|
||||||
|
|
||||||
|
if (read == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
qwTotalRead += read;
|
||||||
|
|
||||||
|
bool bAnyWritten {};
|
||||||
|
while (auto uBytes = pCompressor->Read(tempMemory))
|
||||||
|
{
|
||||||
|
tempMemory.writePtr += uBytes;
|
||||||
|
bAnyWritten = true;
|
||||||
|
|
||||||
|
AuUInt idc {};
|
||||||
|
if (AuIO::WriteAll(pDestStream.get(), { tempMemory, idc }) != AuIO::EStreamError::eErrorNone)
|
||||||
|
{
|
||||||
|
SysPushErrorIO("AuIO::WriteAll failed");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
tempMemory.writePtr = tempMemory.base;
|
||||||
|
tempMemory.readPtr = tempMemory.base;
|
||||||
|
}
|
||||||
|
|
||||||
|
// zstd u ok?
|
||||||
|
// if i remove this, we get no data at all.
|
||||||
|
// even if i try to displace this to above `->Finish()` we still get nothing
|
||||||
|
// preemptively flushing fixes everything /shrug
|
||||||
|
pCompressor->Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
pCompressor->Finish();
|
||||||
|
|
||||||
|
while (auto uBytes = pCompressor->Read(tempMemory))
|
||||||
|
{
|
||||||
|
tempMemory.writePtr += uBytes;
|
||||||
|
|
||||||
|
AuUInt idc {};
|
||||||
|
if (AuIO::WriteAll(pDestStream.get(), { tempMemory, idc }) != AuIO::EStreamError::eErrorNone)
|
||||||
|
{
|
||||||
|
SysPushErrorIO("AuIO::WriteAll failed");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
tempMemory.writePtr = tempMemory.base;
|
||||||
|
tempMemory.readPtr = tempMemory.base;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return qwTotalRead == qwLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
AUKN_SYM bool Decompress(const AuString &path)
|
||||||
|
{
|
||||||
|
static const auto kCompressionReadChunks = kFileCopyBlock;
|
||||||
|
|
||||||
|
auto pFileSrc = OpenReadShared(path + kStringSuffix, EFileAdvisoryLockLevel::eBlockWrite);
|
||||||
|
if (!pFileSrc)
|
||||||
|
{
|
||||||
|
SysPushErrorIO("Couldn't open compression source path: {}.{}", path, kStringSuffix);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pFileDest = OpenWriteShared(path, EFileAdvisoryLockLevel::eBlockReadWrite);
|
||||||
|
if (!pFileDest)
|
||||||
|
{
|
||||||
|
SysPushErrorIO("Couldn't open decompression destination path: {}", path);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pFileDest->GetLength())
|
||||||
|
{
|
||||||
|
SysPushErrorIO("File exists");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto qwLength = pFileSrc->GetLength();
|
||||||
|
|
||||||
|
auto pFileStream = AuMakeShared<AuIO::FS::FileReader>(pFileSrc);
|
||||||
|
if (!pFileStream)
|
||||||
|
{
|
||||||
|
SysPushErrorMemory();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pDestStream = AuMakeShared<AuIO::FS::FileWriter>(pFileDest);
|
||||||
|
if (!pDestStream)
|
||||||
|
{
|
||||||
|
SysPushErrorMemory();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
AuCompression::DecompressInfo decompress{ kCompressionType };
|
||||||
|
decompress.uInternalStreamSize = kCompressionReadChunks * 5;
|
||||||
|
auto pDecompressor = AuCompression::DecompressorUnique(pFileStream, decompress);
|
||||||
|
if (!pDecompressor)
|
||||||
|
{
|
||||||
|
SysPushErrorMemory("no decompressor");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
AuUInt64 qwTotalRead {};
|
||||||
|
{
|
||||||
|
AuByteBuffer tempMemory(kCompressionReadChunks);
|
||||||
|
if (!tempMemory)
|
||||||
|
{
|
||||||
|
SysPushErrorMemory();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (AuUInt64 i = 0; i < qwLength; i += 0 /*kCompressionReadChunks*/)
|
||||||
|
{
|
||||||
|
auto [read, written] = pDecompressor->Ingest(kCompressionReadChunks);
|
||||||
|
i += read;
|
||||||
|
|
||||||
|
if (read == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
qwTotalRead += read;
|
||||||
|
|
||||||
|
while (auto uBytes = pDecompressor->Read(tempMemory))
|
||||||
|
{
|
||||||
|
tempMemory.writePtr += uBytes;
|
||||||
|
|
||||||
|
AuUInt idc {};
|
||||||
|
if (AuIO::WriteAll(pDestStream.get(), { tempMemory, idc }) != AuIO::EStreamError::eErrorNone)
|
||||||
|
{
|
||||||
|
SysPushErrorIO("AuIO::WriteAll failed");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
tempMemory.writePtr = tempMemory.base;
|
||||||
|
tempMemory.readPtr = tempMemory.base;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return qwTotalRead == qwLength;
|
||||||
|
}
|
||||||
|
}
|
13
Source/IO/FS/FSCompress.hpp
Normal file
13
Source/IO/FS/FSCompress.hpp
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/***
|
||||||
|
Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||||
|
|
||||||
|
File: FSCompress.hpp
|
||||||
|
Date: 2023-1-26
|
||||||
|
Author: Reece
|
||||||
|
***/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Aurora::IO::FS
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
0
Source/IO/FS/FSDefaultOverlappedWorkerThread.cpp
Normal file
0
Source/IO/FS/FSDefaultOverlappedWorkerThread.cpp
Normal file
0
Source/IO/FS/FSDefaultOverlappedWorkerThread.hpp
Normal file
0
Source/IO/FS/FSDefaultOverlappedWorkerThread.hpp
Normal file
93
Source/IO/FS/FSOverlappedUtilities.cpp
Normal file
93
Source/IO/FS/FSOverlappedUtilities.cpp
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
/***
|
||||||
|
Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||||
|
|
||||||
|
File: FSOverlappedUtilities.cpp
|
||||||
|
Date: 2023-1-26
|
||||||
|
Author: Reece
|
||||||
|
***/
|
||||||
|
#include <Source/RuntimeInternal.hpp>
|
||||||
|
#include "FS.hpp"
|
||||||
|
#include "FSOverlappedUtilities.hpp"
|
||||||
|
|
||||||
|
namespace Aurora::IO::FS
|
||||||
|
{
|
||||||
|
struct OverlappedStatOperation :
|
||||||
|
virtual BaseOverlappedOperation,
|
||||||
|
IOverlappedStatOperation
|
||||||
|
{
|
||||||
|
Stat stat;
|
||||||
|
|
||||||
|
virtual AuSPtr<Stat> GetStats() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OverlappedReadOperation :
|
||||||
|
virtual BaseOverlappedOperation,
|
||||||
|
IOverlappedReadOperation
|
||||||
|
{
|
||||||
|
AuMemoryViewWrite write;
|
||||||
|
AuByteBuffer buffer;
|
||||||
|
|
||||||
|
virtual AuSPtr<Aurora::Memory::ByteBuffer> GetByteBuffer() override;
|
||||||
|
virtual AuSPtr<Aurora::Memory::MemoryViewWrite> GetReadView() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
AUKN_SYM bool OverlappedForceDelegatedIO(bool bForceIOWorkerThreads)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
AUKN_SYM AuSPtr<IOverlappedOperationBase> OverlappedCompress(const AuString &path, AuInt8 iLevel)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
AUKN_SYM AuSPtr<IOverlappedOperationBase> OverlappedDecompress(const AuString &path)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
AUKN_SYM AuSPtr<IOverlappedOperationBase> OverlappedWrite(const AuString &path, AuSPtr<Aurora::Memory::MemoryViewRead> pMemoryView)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
AUKN_SYM AuSPtr<IOverlappedReadOperation> OverlappedRead(const AuString &path)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
AUKN_SYM AuSPtr<IOverlappedStatOperation> OverlappedStat(const AuString &path)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
AUKN_SYM AuSPtr<IOverlappedOperationBase> OverlappedCopy(const AuString &path, const AuString &dest)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
AUKN_SYM AuSPtr<IOverlappedOperationBase> OverlappedRelink(const AuString &path, const AuString &dest)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
AUKN_SYM AuSPtr<IOverlappedOperationBase> OverlappedTrustFile(const AuString &path)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
AUKN_SYM AuSPtr<IOverlappedOperationBase> OverlappedBlockFile(const AuString &path)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
AUKN_SYM AuSPtr<IOverlappedOperationBase> OverlappedUnblockFile(const AuString &path)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
AUKN_SYM AuSPtr<IOverlappedOperationBase> OverlappedDelete(const AuString &path)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
30
Source/IO/FS/FSOverlappedUtilities.hpp
Normal file
30
Source/IO/FS/FSOverlappedUtilities.hpp
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/***
|
||||||
|
Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||||
|
|
||||||
|
File: FSOverlappedUtilities.hpp
|
||||||
|
Date: 2023-1-26
|
||||||
|
Author: Reece
|
||||||
|
***/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Aurora::IO::FS
|
||||||
|
{
|
||||||
|
struct BaseOverlappedOperation : virtual IOverlappedOperationBase
|
||||||
|
{
|
||||||
|
BaseOverlappedOperation();
|
||||||
|
|
||||||
|
virtual AuSPtr<Loop::ILoopSource> ToWaitable() override;
|
||||||
|
|
||||||
|
virtual AuSPtr<IOverlappedCallback> SetCallback(AuSPtr<IOverlappedOperationBase> pCallback) override;
|
||||||
|
|
||||||
|
virtual bool IsOperationComplete() override;
|
||||||
|
virtual bool IsOperationSuccessful() override;
|
||||||
|
|
||||||
|
void Complete();
|
||||||
|
void Fail();
|
||||||
|
|
||||||
|
private:
|
||||||
|
AuSPtr<Loop::ILoopSource> pWaitable;
|
||||||
|
AuSPtr<IOverlappedOperationBase> pCallback;
|
||||||
|
};
|
||||||
|
}
|
@ -250,7 +250,7 @@ namespace Aurora::IO::FS
|
|||||||
fileHandle = CreateFileW(win32Path.c_str(), GENERIC_WRITE | GENERIC_READ, NtLockAdvisoryToShare(lock), NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
fileHandle = CreateFileW(win32Path.c_str(), GENERIC_WRITE | GENERIC_READ, NtLockAdvisoryToShare(lock), NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
if (fileHandle == INVALID_HANDLE_VALUE)
|
if (fileHandle == INVALID_HANDLE_VALUE)
|
||||||
{
|
{
|
||||||
fileHandle = CreateFileW(win32Path.c_str(), GENERIC_WRITE, NtLockAdvisoryToShare(lock), NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
fileHandle = CreateFileW(win32Path.c_str(), GENERIC_WRITE, NtLockAdvisoryToShare(lock), NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -260,7 +260,7 @@ namespace Aurora::IO::FS
|
|||||||
fileHandle = CreateFileW(win32Path.c_str(), GENERIC_WRITE | FILE_READ_ATTRIBUTES, NtLockAdvisoryToShare(lock), NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
fileHandle = CreateFileW(win32Path.c_str(), GENERIC_WRITE | FILE_READ_ATTRIBUTES, NtLockAdvisoryToShare(lock), NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
if (fileHandle == INVALID_HANDLE_VALUE)
|
if (fileHandle == INVALID_HANDLE_VALUE)
|
||||||
{
|
{
|
||||||
fileHandle = CreateFileW(win32Path.c_str(), GENERIC_WRITE, NtLockAdvisoryToShare(lock), NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
fileHandle = CreateFileW(win32Path.c_str(), GENERIC_WRITE, NtLockAdvisoryToShare(lock), NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,19 @@
|
|||||||
|
|
||||||
namespace Aurora::IO::Net
|
namespace Aurora::IO::Net
|
||||||
{
|
{
|
||||||
|
NetHostname::NetHostname()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
NetHostname::NetHostname(const NetHostname &cpy) :
|
||||||
|
type(cpy.type),
|
||||||
|
hostname(cpy.hostname),
|
||||||
|
address(cpy.address)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
NetHostname::NetHostname(const AuString &hostname) :
|
NetHostname::NetHostname(const AuString &hostname) :
|
||||||
type(EHostnameType::eHostByDns),
|
type(EHostnameType::eHostByDns),
|
||||||
hostname(hostname),
|
hostname(hostname),
|
||||||
@ -32,6 +45,14 @@ namespace Aurora::IO::Net
|
|||||||
this->address == other.address;
|
this->address == other.address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NetHostname &NetHostname::operator =(const NetHostname &other)
|
||||||
|
{
|
||||||
|
this->type = other.type;
|
||||||
|
this->hostname = other.hostname;
|
||||||
|
this->address = other.address;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
const AuUInt NetHostname::HashCode() const
|
const AuUInt NetHostname::HashCode() const
|
||||||
{
|
{
|
||||||
return AuHashCode(this->type) ^
|
return AuHashCode(this->type) ^
|
||||||
|
@ -34,7 +34,12 @@ namespace Aurora::IO::Net
|
|||||||
ADDRINFOEXW infoEx { 0 };
|
ADDRINFOEXW infoEx { 0 };
|
||||||
int iInfoEx { 0 };
|
int iInfoEx { 0 };
|
||||||
|
|
||||||
if (this->bA && !this->bAAAA)
|
if (this->bA && this->bAAAA)
|
||||||
|
{
|
||||||
|
infoEx.ai_family = AF_UNSPEC;
|
||||||
|
//infoEx.ai_flags = AI_ALL;
|
||||||
|
}
|
||||||
|
else if (this->bA && !this->bAAAA)
|
||||||
{
|
{
|
||||||
infoEx.ai_family = AF_INET;
|
infoEx.ai_family = AF_INET;
|
||||||
}
|
}
|
||||||
@ -43,11 +48,6 @@ namespace Aurora::IO::Net
|
|||||||
infoEx.ai_family = AF_INET6;
|
infoEx.ai_family = AF_INET6;
|
||||||
//infoEx.ai_flags = AI_V4MAPPED; // beats returning nothing...
|
//infoEx.ai_flags = AI_V4MAPPED; // beats returning nothing...
|
||||||
}
|
}
|
||||||
else if (this->bA && this->bAAAA)
|
|
||||||
{
|
|
||||||
infoEx.ai_family = AF_UNSPEC;
|
|
||||||
//infoEx.ai_flags = AI_ALL;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
@ -113,7 +113,11 @@ namespace Aurora::IO::Net
|
|||||||
{
|
{
|
||||||
int iInfoEx { 0 };
|
int iInfoEx { 0 };
|
||||||
|
|
||||||
if (this->bA && !this->bAAAA)
|
if (this->bA && this->bAAAA)
|
||||||
|
{
|
||||||
|
infoEx.ai_family = AF_UNSPEC;
|
||||||
|
}
|
||||||
|
else if (this->bA && !this->bAAAA)
|
||||||
{
|
{
|
||||||
infoEx.ai_family = AF_INET;
|
infoEx.ai_family = AF_INET;
|
||||||
}
|
}
|
||||||
@ -121,10 +125,6 @@ namespace Aurora::IO::Net
|
|||||||
{
|
{
|
||||||
infoEx.ai_family = AF_INET6;
|
infoEx.ai_family = AF_INET6;
|
||||||
}
|
}
|
||||||
else if (this->bA && this->bAAAA)
|
|
||||||
{
|
|
||||||
infoEx.ai_family = AF_UNSPEC;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
@ -31,11 +31,22 @@ namespace Aurora::IO::Net
|
|||||||
}
|
}
|
||||||
|
|
||||||
Socket::Socket(struct NetInterface *pInterface,
|
Socket::Socket(struct NetInterface *pInterface,
|
||||||
struct NetWorker *pWorker,
|
struct NetWorker *pWorker,
|
||||||
const AuSPtr<ISocketDriver> &pSocketDriver,
|
const AuSPtr<ISocketDriver> &pSocketDriver,
|
||||||
const NetEndpoint &endpoint) :
|
const NetEndpoint &endpoint) :
|
||||||
SocketBase(pInterface, pWorker, pSocketDriver, endpoint)
|
SocketBase(pInterface, pWorker, pSocketDriver, endpoint)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Socket::Socket(struct NetInterface *pInterface,
|
||||||
|
struct NetWorker *pWorker,
|
||||||
|
const AuSPtr<ISocketDriver> &pSocketDriver,
|
||||||
|
const AuPair<NetHostname, AuUInt16> &endpoint,
|
||||||
|
AuNet::ETransportProtocol eProtocol) :
|
||||||
|
SocketBase(pInterface, pWorker, pSocketDriver, endpoint, eProtocol)
|
||||||
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Socket::Socket(NetInterface *pInterface,
|
Socket::Socket(NetInterface *pInterface,
|
||||||
@ -62,7 +73,7 @@ namespace Aurora::IO::Net
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Socket::FinishConstructAsync()
|
void Socket::RenewSocket()
|
||||||
{
|
{
|
||||||
if (!this->SendPreestablish())
|
if (!this->SendPreestablish())
|
||||||
{
|
{
|
||||||
@ -70,6 +81,18 @@ namespace Aurora::IO::Net
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this->bHasRemoteMany_ && this->connectMany_.ips.size())
|
||||||
|
{
|
||||||
|
this->remoteEndpoint_ = this->connectMany_.ips[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->osHandle_ &&
|
||||||
|
this->osHandle_ != -1)
|
||||||
|
{
|
||||||
|
::closesocket(this->osHandle_);
|
||||||
|
this->osHandle_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
this->osHandle_ = ::WSASocketW(
|
this->osHandle_ = ::WSASocketW(
|
||||||
IPToDomain(this->remoteEndpoint_),
|
IPToDomain(this->remoteEndpoint_),
|
||||||
TransportToPlatformType(this->remoteEndpoint_),
|
TransportToPlatformType(this->remoteEndpoint_),
|
||||||
@ -79,7 +102,6 @@ namespace Aurora::IO::Net
|
|||||||
WSA_FLAG_OVERLAPPED
|
WSA_FLAG_OVERLAPPED
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
if (this->osHandle_ == -1)
|
if (this->osHandle_ == -1)
|
||||||
{
|
{
|
||||||
this->SendErrorNoStream(GetLastNetError());
|
this->SendErrorNoStream(GetLastNetError());
|
||||||
@ -93,10 +115,34 @@ namespace Aurora::IO::Net
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(AURORA_IS_MODERNNT_DERIVED)
|
#if !defined(AURORA_IS_MODERNNT_DERIVED)
|
||||||
|
|
||||||
|
this->osHandleOwner_ = AuMakeShared<AuFS::FileHandle>();
|
||||||
|
if (!this->osHandle_)
|
||||||
|
{
|
||||||
|
this->SendErrorNoStream(GetLastNetError());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this->osHandleOwner_->Init((int)this->osHandle_, (int)this->osHandle_);
|
this->osHandleOwner_->Init((int)this->osHandle_, (int)this->osHandle_);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Socket::FinishConstructAsync()
|
||||||
|
{
|
||||||
|
if (this->resolveLater.size() || this->bResolving_)
|
||||||
|
{
|
||||||
|
if (!this->TryStartResolve())
|
||||||
|
{
|
||||||
|
this->SendErrorNoStream(GetLastNetError());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RenewSocket();
|
||||||
|
}
|
||||||
|
|
||||||
bool Socket::PrepareConnectOperations()
|
bool Socket::PrepareConnectOperations()
|
||||||
{
|
{
|
||||||
::setsockopt(this->osHandle_,
|
::setsockopt(this->osHandle_,
|
||||||
|
@ -29,6 +29,12 @@ namespace Aurora::IO::Net
|
|||||||
const AuSPtr<ISocketDriver> &pSocketDriver,
|
const AuSPtr<ISocketDriver> &pSocketDriver,
|
||||||
AuUInt osHandle);
|
AuUInt osHandle);
|
||||||
|
|
||||||
|
Socket(struct NetInterface *pInterface,
|
||||||
|
struct NetWorker *pWorker,
|
||||||
|
const AuSPtr<ISocketDriver> &pSocketDriver,
|
||||||
|
const AuPair<NetHostname, AuUInt16> &endpoint,
|
||||||
|
AuNet::ETransportProtocol eProtocol);
|
||||||
|
|
||||||
Socket(struct NetInterface *pInterface,
|
Socket(struct NetInterface *pInterface,
|
||||||
struct NetWorker *pWorker,
|
struct NetWorker *pWorker,
|
||||||
const AuSPtr<ISocketDriver> &pSocketDriver,
|
const AuSPtr<ISocketDriver> &pSocketDriver,
|
||||||
@ -61,5 +67,6 @@ namespace Aurora::IO::Net
|
|||||||
virtual void Shutdown(bool bNow) override;
|
virtual void Shutdown(bool bNow) override;
|
||||||
|
|
||||||
virtual void CloseSocket() override;
|
virtual void CloseSocket() override;
|
||||||
|
virtual void RenewSocket() override;
|
||||||
};
|
};
|
||||||
}
|
}
|
@ -12,6 +12,7 @@
|
|||||||
#include "AuIPAddress.hpp"
|
#include "AuIPAddress.hpp"
|
||||||
#include "AuNetError.hpp"
|
#include "AuNetError.hpp"
|
||||||
#include "AuNetSocketServer.hpp"
|
#include "AuNetSocketServer.hpp"
|
||||||
|
#include "AuNetInterface.hpp"
|
||||||
|
|
||||||
#if defined(AURORA_IS_MODERNNT_DERIVED)
|
#if defined(AURORA_IS_MODERNNT_DERIVED)
|
||||||
#include "AuNetStream.NT.hpp"
|
#include "AuNetStream.NT.hpp"
|
||||||
@ -54,9 +55,9 @@ namespace Aurora::IO::Net
|
|||||||
}
|
}
|
||||||
|
|
||||||
SocketBase::SocketBase(struct NetInterface *pInterface,
|
SocketBase::SocketBase(struct NetInterface *pInterface,
|
||||||
struct NetWorker *pWorker,
|
struct NetWorker *pWorker,
|
||||||
const AuSPtr<ISocketDriver> &pSocketDriver,
|
const AuSPtr<ISocketDriver> &pSocketDriver,
|
||||||
const NetEndpoint &endpoint) :
|
const NetEndpoint &endpoint) :
|
||||||
connectOperation(this),
|
connectOperation(this),
|
||||||
socketChannel_(this),
|
socketChannel_(this),
|
||||||
pInterface_(pInterface),
|
pInterface_(pInterface),
|
||||||
@ -72,6 +73,45 @@ namespace Aurora::IO::Net
|
|||||||
this->pWorker_->AddSocket(this);
|
this->pWorker_->AddSocket(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SocketBase::SocketBase(struct NetInterface *pInterface,
|
||||||
|
struct NetWorker *pWorker,
|
||||||
|
const AuSPtr<ISocketDriver> &pSocketDriver,
|
||||||
|
const AuPair<NetHostname, AuUInt16> &endpoint,
|
||||||
|
AuNet::ETransportProtocol eProtocol) :
|
||||||
|
connectOperation(this),
|
||||||
|
socketChannel_(this),
|
||||||
|
pInterface_(pInterface),
|
||||||
|
pWorker_(pWorker),
|
||||||
|
pSocketDriver_(pSocketDriver)
|
||||||
|
{
|
||||||
|
auto &[host, uPort] = endpoint;
|
||||||
|
|
||||||
|
if (host.type == AuNet::EHostnameType::eHostByIp)
|
||||||
|
{
|
||||||
|
this->remoteEndpoint_.ip = host.address;
|
||||||
|
this->remoteEndpoint_.uPort = uPort;
|
||||||
|
this->remoteEndpoint_.transportProtocol = eProtocol;
|
||||||
|
OptimizeEndpoint(this->remoteEndpoint_);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->resolveLater = host.hostname;
|
||||||
|
this->remoteEndpoint_.uPort = uPort;
|
||||||
|
this->remoteEndpoint_.transportProtocol = eProtocol;
|
||||||
|
this->connectMany_.uPort = uPort;
|
||||||
|
this->connectMany_.protocol = eProtocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->osHandleOwner_ = AuMakeShared<AuFS::FileHandle>();
|
||||||
|
if (!this->osHandle_)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->pWorker_->AddSocket(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
SocketBase::SocketBase(NetInterface *pInterface,
|
SocketBase::SocketBase(NetInterface *pInterface,
|
||||||
NetWorker *pWorker,
|
NetWorker *pWorker,
|
||||||
const AuSPtr<ISocketDriver> &pSocketDriver,
|
const AuSPtr<ISocketDriver> &pSocketDriver,
|
||||||
@ -100,13 +140,48 @@ namespace Aurora::IO::Net
|
|||||||
|
|
||||||
bool SocketBase::IsValid()
|
bool SocketBase::IsValid()
|
||||||
{
|
{
|
||||||
return bool(this->osHandleOwner_) &&
|
return (this->resolveLater.size()) ||
|
||||||
|
bool(this->osHandleOwner_) &&
|
||||||
bool(this->connectOperation.IsValid()) &&
|
bool(this->connectOperation.IsValid()) &&
|
||||||
bool(this->osHandle_ != 0) &&
|
bool(this->osHandle_ != 0) &&
|
||||||
bool(this->osHandle_ != -1) &&
|
bool(this->osHandle_ != -1) &&
|
||||||
bool(!this->bForceFailConstruct_);
|
bool(!this->bForceFailConstruct_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SocketBase::TryStartResolve()
|
||||||
|
{
|
||||||
|
auto pThat = this->SharedFromThis();
|
||||||
|
|
||||||
|
if (this->bResolving_)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->bResolving_ = true;
|
||||||
|
auto address = this->resolveLater;
|
||||||
|
this->resolveLater.clear();
|
||||||
|
|
||||||
|
auto pResolver = this->pInterface_->GetResolveService()->SimpleAllResolve(address,
|
||||||
|
AuMakeSharedThrow<AuAsync::PromiseCallbackFunctional<AuList<AuNet::IPAddress>,
|
||||||
|
AuNet::NetError>>(
|
||||||
|
[=](const AuSPtr<AuList<AuNet::IPAddress>> &ips)
|
||||||
|
{
|
||||||
|
|
||||||
|
pThat->bResolving_ = false;
|
||||||
|
pThat->connectMany_.uPort = pThat->remoteEndpoint_.uPort;
|
||||||
|
pThat->connectMany_.ips.insert(pThat->connectMany_.ips.end(), ips->begin(), ips->end());
|
||||||
|
pThat->bHasRemoteMany_ = true;
|
||||||
|
pThat->RenewSocket();
|
||||||
|
pThat->ConnectNext();
|
||||||
|
},
|
||||||
|
[=](const AuSPtr<AuNet::NetError> &error)
|
||||||
|
{
|
||||||
|
pThat->SendErrorNoStream(error ? *error.get() : AuNet::NetError {});
|
||||||
|
}));
|
||||||
|
|
||||||
|
return bool(pResolver);
|
||||||
|
}
|
||||||
|
|
||||||
bool SocketBase::ConnectNext()
|
bool SocketBase::ConnectNext()
|
||||||
{
|
{
|
||||||
if (this->connectMany_.ips.empty())
|
if (this->connectMany_.ips.empty())
|
||||||
@ -126,6 +201,12 @@ namespace Aurora::IO::Net
|
|||||||
|
|
||||||
bool SocketBase::Connect(const NetEndpoint &endpoint)
|
bool SocketBase::Connect(const NetEndpoint &endpoint)
|
||||||
{
|
{
|
||||||
|
if (!this->IsValid())
|
||||||
|
{
|
||||||
|
this->SendErrorNoStream({});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
this->remoteEndpoint_ = endpoint;
|
this->remoteEndpoint_ = endpoint;
|
||||||
this->endpointSize_ = OptimizeEndpoint(this->remoteEndpoint_);
|
this->endpointSize_ = OptimizeEndpoint(this->remoteEndpoint_);
|
||||||
this->localEndpoint_.transportProtocol = this->remoteEndpoint_.transportProtocol;
|
this->localEndpoint_.transportProtocol = this->remoteEndpoint_.transportProtocol;
|
||||||
@ -272,7 +353,7 @@ namespace Aurora::IO::Net
|
|||||||
void SocketBase::SendOnData()
|
void SocketBase::SendOnData()
|
||||||
{
|
{
|
||||||
auto pReadableBuffer = this->socketChannel_.AsReadableByteBuffer();
|
auto pReadableBuffer = this->socketChannel_.AsReadableByteBuffer();
|
||||||
auto pStartOffset = pReadableBuffer->readPtr;
|
auto pStartOffset = pReadableBuffer ? pReadableBuffer->readPtr : nullptr;
|
||||||
|
|
||||||
if (this->bHasFinalized_)
|
if (this->bHasFinalized_)
|
||||||
{
|
{
|
||||||
@ -305,7 +386,7 @@ namespace Aurora::IO::Net
|
|||||||
|
|
||||||
this->ToChannel()->ScheduleOutOfFrameWrite();
|
this->ToChannel()->ScheduleOutOfFrameWrite();
|
||||||
|
|
||||||
auto uHeadDelta = pReadableBuffer->readPtr - pStartOffset;
|
auto uHeadDelta = pReadableBuffer ? (pReadableBuffer->readPtr - pStartOffset) : 0;
|
||||||
this->socketChannel_.GetRecvStatsEx().AddBytes(uHeadDelta);
|
this->socketChannel_.GetRecvStatsEx().AddBytes(uHeadDelta);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -344,6 +425,11 @@ namespace Aurora::IO::Net
|
|||||||
|
|
||||||
bool SocketBase::SendPreestablish(SocketServer *pServer)
|
bool SocketBase::SendPreestablish(SocketServer *pServer)
|
||||||
{
|
{
|
||||||
|
if (this->bHasPreestablished_)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (pServer && pServer->uDefaultInputStreamSize)
|
if (pServer && pServer->uDefaultInputStreamSize)
|
||||||
{
|
{
|
||||||
this->socketChannel_.uBytesInputBuffer = pServer->uDefaultInputStreamSize;
|
this->socketChannel_.uBytesInputBuffer = pServer->uDefaultInputStreamSize;
|
||||||
@ -365,7 +451,7 @@ namespace Aurora::IO::Net
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return this->bHasPreestablished_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SocketBase::SendEnd()
|
void SocketBase::SendEnd()
|
||||||
|
@ -37,6 +37,12 @@ namespace Aurora::IO::Net
|
|||||||
const AuSPtr<ISocketDriver> &pSocketDriver,
|
const AuSPtr<ISocketDriver> &pSocketDriver,
|
||||||
const NetEndpoint &endpoint);
|
const NetEndpoint &endpoint);
|
||||||
|
|
||||||
|
SocketBase(struct NetInterface *pInterface,
|
||||||
|
struct NetWorker *pWorker,
|
||||||
|
const AuSPtr<ISocketDriver> &pSocketDriver,
|
||||||
|
const AuPair<NetHostname, AuUInt16> &endpoint,
|
||||||
|
AuNet::ETransportProtocol eProtocol);
|
||||||
|
|
||||||
SocketBase(struct NetInterface *pInterface,
|
SocketBase(struct NetInterface *pInterface,
|
||||||
struct NetWorker *pWorker,
|
struct NetWorker *pWorker,
|
||||||
const AuSPtr<ISocketDriver> &pSocketDriver,
|
const AuSPtr<ISocketDriver> &pSocketDriver,
|
||||||
@ -44,6 +50,7 @@ namespace Aurora::IO::Net
|
|||||||
|
|
||||||
virtual ~SocketBase();
|
virtual ~SocketBase();
|
||||||
|
|
||||||
|
bool TryStartResolve();
|
||||||
|
|
||||||
bool ConnectNext();
|
bool ConnectNext();
|
||||||
bool Connect(const NetEndpoint &endpoint);
|
bool Connect(const NetEndpoint &endpoint);
|
||||||
@ -102,14 +109,16 @@ namespace Aurora::IO::Net
|
|||||||
virtual bool ConnectBlocking() = 0;
|
virtual bool ConnectBlocking() = 0;
|
||||||
|
|
||||||
virtual void CloseSocket() = 0;
|
virtual void CloseSocket() = 0;
|
||||||
|
virtual void RenewSocket() = 0;
|
||||||
|
|
||||||
AuUInt endpointSize_ {};
|
AuUInt endpointSize_ {};
|
||||||
|
|
||||||
bool bHasFinalized_ {};
|
bool bHasFinalized_ {};
|
||||||
bool bHasEnded {};
|
bool bHasEnded {};
|
||||||
|
bool bResolving_ {};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
AuUInt osHandle_;
|
AuUInt osHandle_ {};
|
||||||
|
|
||||||
NetEndpoint remoteEndpoint_;
|
NetEndpoint remoteEndpoint_;
|
||||||
NetEndpoint localEndpoint_;
|
NetEndpoint localEndpoint_;
|
||||||
@ -128,6 +137,8 @@ namespace Aurora::IO::Net
|
|||||||
|
|
||||||
bool bHasErrored_ {};
|
bool bHasErrored_ {};
|
||||||
bool bHasConnected_ {};
|
bool bHasConnected_ {};
|
||||||
|
bool bHasPreestablished_ {};
|
||||||
|
AuString resolveLater {};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,12 +25,6 @@ namespace Aurora::IO::Net
|
|||||||
|
|
||||||
AuSPtr<ISocket> NetSrvSockets::Connect(const NetSocketConnect &netConnect)
|
AuSPtr<ISocket> NetSrvSockets::Connect(const NetSocketConnect &netConnect)
|
||||||
{
|
{
|
||||||
if (netConnect.endpoint.transportProtocol != ETransportProtocol::eProtocolTCP)
|
|
||||||
{
|
|
||||||
SysPushErrorNet("Invalid transport protocol. Hint: Use ConnectManyEx for UDP.");
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto pWorker = this->pParent_->TryScheduleEx();
|
auto pWorker = this->pParent_->TryScheduleEx();
|
||||||
if (!pWorker)
|
if (!pWorker)
|
||||||
{
|
{
|
||||||
@ -38,10 +32,48 @@ namespace Aurora::IO::Net
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto pSocket = AuMakeShared<Socket>(this->pParent_,
|
AuSPtr<Socket> pSocket;
|
||||||
pWorker.get(),
|
|
||||||
netConnect.pDriver,
|
if (netConnect.endpoint)
|
||||||
netConnect.endpoint);
|
{
|
||||||
|
if (netConnect.endpoint.Value().transportProtocol != ETransportProtocol::eProtocolTCP)
|
||||||
|
{
|
||||||
|
SysPushErrorNet("Invalid transport protocol. Hint: Use ConnectManyEx for UDP.");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
pSocket = AuMakeShared<Socket>(this->pParent_,
|
||||||
|
pWorker.get(),
|
||||||
|
netConnect.pDriver,
|
||||||
|
netConnect.endpoint.value());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!netConnect.byHost.netHostname)
|
||||||
|
{
|
||||||
|
SysPushErrorArg("Missing hostname or endpoint");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!netConnect.byHost.protocol)
|
||||||
|
{
|
||||||
|
SysPushErrorArg("Missing protocol");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!netConnect.byHost.uPort)
|
||||||
|
{
|
||||||
|
SysPushErrorArg("Missing port");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
pSocket = AuMakeShared<Socket>(this->pParent_,
|
||||||
|
pWorker.get(),
|
||||||
|
netConnect.pDriver,
|
||||||
|
AuMakePair(netConnect.byHost.netHostname.value(), netConnect.byHost.uPort.value()),
|
||||||
|
netConnect.byHost.protocol.value());
|
||||||
|
}
|
||||||
|
|
||||||
if (!pSocket)
|
if (!pSocket)
|
||||||
{
|
{
|
||||||
SysPushErrorNet("No Memory");
|
SysPushErrorNet("No Memory");
|
||||||
@ -52,6 +84,11 @@ namespace Aurora::IO::Net
|
|||||||
{
|
{
|
||||||
pSocket->FinishConstructAsync();
|
pSocket->FinishConstructAsync();
|
||||||
|
|
||||||
|
if (pSocket->bResolving_)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!pSocket->IsValid())
|
if (!pSocket->IsValid())
|
||||||
{
|
{
|
||||||
pSocket->SendErrorNoStream({});
|
pSocket->SendErrorNoStream({});
|
||||||
|
@ -11,54 +11,153 @@
|
|||||||
|
|
||||||
namespace Aurora::Logging::Sinks
|
namespace Aurora::Logging::Sinks
|
||||||
{
|
{
|
||||||
static void EraseFilesByTimestamp(AuUInt32 maxLogs,
|
|
||||||
const AuString &path, /*const its not worth reallocation*/ AuList<AuString> &files)
|
|
||||||
{
|
|
||||||
if (files.size() <= maxLogs)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// our filenames are usually prefixed by an ISO 8601 timestamp where the most significant bits are last (YYYY-MM-DD?HH-MM-SS)
|
|
||||||
// a quick ghetto sort should be all we need. no need to waste time parsing timestamps
|
|
||||||
std::sort(files.begin(), files.end());
|
|
||||||
|
|
||||||
auto amount = files.size() - maxLogs;
|
|
||||||
for (auto x = 0, i = 0; ((x < amount) && (i > files.size())); i++)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (AuIOFS::Remove(path + "/" + files[i]))
|
|
||||||
{
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void CleanupOldLogs(DirectoryLogger logger, const AuString &baseLogPath)
|
static void CleanupOldLogs(DirectoryLogger logger, const AuString &baseLogPath)
|
||||||
{
|
{
|
||||||
AuList<AuString> files;
|
AuList<AuString> files;
|
||||||
AuBST<AuString, AuIOFS::Stat> fileMeta;
|
AuList<AuPair<AuString, AuIOFS::Stat>> fileMeta;
|
||||||
AuUInt32 size {};
|
AuUInt64 qwSize {};
|
||||||
|
|
||||||
AuIOFS::FilesInDirectory(baseLogPath, files);
|
auto doScan = [&]()
|
||||||
|
|
||||||
for (const auto &file : files)
|
|
||||||
{
|
{
|
||||||
AuIOFS::Stat stat;
|
files.clear();
|
||||||
AuIOFS::StatFile(baseLogPath + "/" + file, stat);
|
fileMeta.clear();
|
||||||
fileMeta[file] = stat;
|
qwSize = 0;
|
||||||
size += stat.uSize;
|
|
||||||
|
AuIOFS::FilesInDirectory(baseLogPath, files);
|
||||||
|
|
||||||
|
for (const auto &file : files)
|
||||||
|
{
|
||||||
|
AuIOFS::Stat stat;
|
||||||
|
AuIOFS::StatFile(baseLogPath + "/" + file, stat);
|
||||||
|
fileMeta.push_back(AuMakePair(file, stat));
|
||||||
|
qwSize += stat.uSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(fileMeta.begin(), fileMeta.end(), [](AuPair<AuString, AuIOFS::Stat> a, AuPair<AuString, AuIOFS::Stat> b)
|
||||||
|
{
|
||||||
|
return AuGet<1>(a).modified < AuGet<1>(b).modified;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
doScan();
|
||||||
|
|
||||||
|
if (logger.uMaxFileTimeInDeltaMSOrZeroBeforeCompress)
|
||||||
|
{
|
||||||
|
auto qwMaxTime = AuTime::CurrentClockMS() - logger.uMaxFileTimeInDeltaMSOrZeroBeforeCompress;
|
||||||
|
bool bRescan {};
|
||||||
|
|
||||||
|
for (AuUInt i = 0; i < fileMeta.size(); i++)
|
||||||
|
{
|
||||||
|
if (qwMaxTime > fileMeta[i].second.modified)
|
||||||
|
{
|
||||||
|
auto path = baseLogPath + "/" + fileMeta[i].first;
|
||||||
|
if (AuIOFS::Compress(path))
|
||||||
|
{
|
||||||
|
AuIOFS::Remove(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
bRescan = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bRescan)
|
||||||
|
{
|
||||||
|
doScan();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EraseFilesByTimestamp(logger.maxLogsOrZero, baseLogPath, files);
|
if (logger.uMaxCumulativeFileSizeInMiBOrZeroBeforeCompress)
|
||||||
// TODO: erase when size >= maxFileSizeOrZero * 1024
|
{
|
||||||
// Didn't auRuntime v1.0 have this?
|
bool bRescan {};
|
||||||
|
|
||||||
|
if (qwSize > logger.uMaxCumulativeFileSizeInMiBOrZeroBeforeCompress)
|
||||||
|
{
|
||||||
|
std::sort(fileMeta.begin(), fileMeta.end(), [](AuPair<AuString, AuIOFS::Stat> a, AuPair<AuString, AuIOFS::Stat> b)
|
||||||
|
{
|
||||||
|
return AuGet<1>(a).uSize > AuGet<1>(b).uSize;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (AuUInt i = 0; i < fileMeta.size(); i++)
|
||||||
|
{
|
||||||
|
if (qwSize <= logger.uMaxCumulativeFileSizeInMiBOrZeroBeforeCompress)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto path = baseLogPath + "/" + fileMeta[i].first;
|
||||||
|
if (AuIOFS::Compress(path))
|
||||||
|
{
|
||||||
|
AuIOFS::Remove(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
bRescan = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bRescan)
|
||||||
|
{
|
||||||
|
doScan();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logger.uMaxLogsOrZeroBeforeDelete)
|
||||||
|
{
|
||||||
|
if (fileMeta.size() > logger.uMaxLogsOrZeroBeforeDelete)
|
||||||
|
{
|
||||||
|
for (AuUInt i = logger.uMaxLogsOrZeroBeforeDelete; i < fileMeta.size(); i++)
|
||||||
|
{
|
||||||
|
AuIOFS::Remove(baseLogPath + "/" + fileMeta[i].first);
|
||||||
|
}
|
||||||
|
|
||||||
|
doScan();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logger.uMaxCumulativeFileSizeInMiBOrZeroBeforeDelete)
|
||||||
|
{
|
||||||
|
bool bRescan {};
|
||||||
|
|
||||||
|
if (qwSize > logger.uMaxCumulativeFileSizeInMiBOrZeroBeforeDelete)
|
||||||
|
{
|
||||||
|
std::sort(fileMeta.begin(), fileMeta.end(), [](AuPair<AuString, AuIOFS::Stat> a, AuPair<AuString, AuIOFS::Stat> b)
|
||||||
|
{
|
||||||
|
return AuGet<1>(a).uSize > AuGet<1>(b).uSize;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (AuUInt i = 0; i < fileMeta.size(); i++)
|
||||||
|
{
|
||||||
|
if (qwSize <= logger.uMaxCumulativeFileSizeInMiBOrZeroBeforeDelete)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AuIOFS::Remove(baseLogPath + "/" + fileMeta[i].first))
|
||||||
|
{
|
||||||
|
qwSize -= fileMeta[i].second.uSize;
|
||||||
|
bRescan = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bRescan)
|
||||||
|
{
|
||||||
|
doScan();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logger.uMaxFileTimeInDeltaMSOrZeroBeforeDelete)
|
||||||
|
{
|
||||||
|
auto qwMaxTime = AuTime::CurrentClockMS() - logger.uMaxFileTimeInDeltaMSOrZeroBeforeDelete;
|
||||||
|
|
||||||
|
for (AuUInt i = 0; i < fileMeta.size(); i++)
|
||||||
|
{
|
||||||
|
if (qwMaxTime > fileMeta[i].second.modified)
|
||||||
|
{
|
||||||
|
AuIOFS::Remove(baseLogPath + "/" + fileMeta[i].first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IBasicSink *NewDirectoryLoggerNew(const AuString &baseDirectory,
|
IBasicSink *NewDirectoryLoggerNew(const AuString &baseDirectory,
|
||||||
@ -68,10 +167,11 @@ namespace Aurora::Logging::Sinks
|
|||||||
AuString path;
|
AuString path;
|
||||||
|
|
||||||
auto tm = Time::ToCivilTime(Time::CurrentClockMS());
|
auto tm = Time::ToCivilTime(Time::CurrentClockMS());
|
||||||
path = fmt::format("{}/{:04}-{:02}-{:02}T{:02}-{:02}-{:02}Z.txt",
|
path = fmt::format("{}/{:04}-{:02}-{:02}T{:02}-{:02}-{:02}Z.{}",
|
||||||
baseDirectory,
|
baseDirectory,
|
||||||
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
|
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
|
||||||
tm.tm_hour, tm.tm_min, tm.tm_sec);
|
tm.tm_hour, tm.tm_min, tm.tm_sec,
|
||||||
|
bBinary ? ".log" : ".txt");
|
||||||
|
|
||||||
CleanupOldLogs(meta, baseDirectory);
|
CleanupOldLogs(meta, baseDirectory);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user