mirror of
https://github.com/DarkStore-3DS/Project_CTR.git
synced 2026-07-04 00:39:03 +00:00
Put everything back.
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
#include <tc/ByteData.h>
|
||||
|
||||
const std::string tc::ByteData::kClassName = "tc::ByteData";
|
||||
|
||||
tc::ByteData::ByteData() :
|
||||
ByteData(0)
|
||||
{}
|
||||
|
||||
tc::ByteData::ByteData(const tc::ByteData& other) :
|
||||
ByteData(other.mPtr.get(), other.mSize)
|
||||
{}
|
||||
|
||||
tc::ByteData::ByteData(tc::ByteData&& other) :
|
||||
mSize(other.mSize),
|
||||
mPtr(std::move(other.mPtr))
|
||||
{
|
||||
other.mSize = 0;
|
||||
}
|
||||
|
||||
tc::ByteData::ByteData(std::initializer_list<byte_t> l) :
|
||||
ByteData(l.size(), false)
|
||||
{
|
||||
size_t i = 0;
|
||||
for (auto itr = l.begin(); itr != l.end(); itr++, i++)
|
||||
{
|
||||
mPtr.get()[i] = *itr;
|
||||
}
|
||||
}
|
||||
|
||||
tc::ByteData::ByteData(size_t size, bool clear_memory)
|
||||
{
|
||||
if (size == 0)
|
||||
{
|
||||
mPtr.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
mPtr = std::unique_ptr<byte_t>(new byte_t[size]);
|
||||
}
|
||||
catch (const std::bad_alloc&)
|
||||
{
|
||||
throw tc::OutOfMemoryException(kClassName, "std::bad_alloc thrown");
|
||||
}
|
||||
|
||||
if (mPtr == nullptr)
|
||||
{
|
||||
throw tc::OutOfMemoryException(kClassName, "Failed to allocate memory");
|
||||
}
|
||||
}
|
||||
|
||||
mSize = size;
|
||||
|
||||
if (clear_memory == true)
|
||||
{
|
||||
memset(mPtr.get(), 0, mSize);
|
||||
}
|
||||
}
|
||||
|
||||
tc::ByteData::ByteData(const byte_t* data, size_t size) :
|
||||
ByteData(size, false)
|
||||
{
|
||||
memcpy(mPtr.get(), data, mSize);
|
||||
}
|
||||
|
||||
tc::ByteData& tc::ByteData::operator=(const ByteData& other)
|
||||
{
|
||||
*this = tc::ByteData(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
tc::ByteData& tc::ByteData::operator=(ByteData&& other)
|
||||
{
|
||||
this->mPtr = std::move(other.mPtr);
|
||||
this->mSize = other.mSize;
|
||||
other.mSize = 0;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
byte_t& tc::ByteData::operator[](size_t index)
|
||||
{
|
||||
return mPtr.get()[index];
|
||||
}
|
||||
|
||||
byte_t tc::ByteData::operator[](size_t index) const
|
||||
{
|
||||
return mPtr.get()[index];
|
||||
}
|
||||
|
||||
bool tc::ByteData::operator==(const ByteData& other) const
|
||||
{
|
||||
return (this->mSize == other.mSize && memcmp(this->mPtr.get(), other.mPtr.get(), this->mSize) == 0);
|
||||
}
|
||||
|
||||
bool tc::ByteData::operator!=(const ByteData& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
byte_t* tc::ByteData::data() const
|
||||
{
|
||||
return mPtr.get();
|
||||
}
|
||||
|
||||
size_t tc::ByteData::size() const
|
||||
{
|
||||
return mPtr == nullptr ? 0 : mSize;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#include <tc/Exception.h>
|
||||
|
||||
tc::Exception::Exception() noexcept :
|
||||
what_(""),
|
||||
module_(""),
|
||||
error_("")
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
tc::Exception::Exception(const std::string & what) noexcept :
|
||||
what_(what),
|
||||
module_(""),
|
||||
error_(what)
|
||||
{
|
||||
}
|
||||
|
||||
tc::Exception::Exception(const std::string & module, const std::string & what) noexcept :
|
||||
what_(""),
|
||||
module_(module),
|
||||
error_(what)
|
||||
{
|
||||
if (module_.length() > 0)
|
||||
{
|
||||
what_ = "[" + module_ + " ERROR] " + error_;
|
||||
}
|
||||
else
|
||||
{
|
||||
what_ = error_;
|
||||
}
|
||||
}
|
||||
|
||||
const char* tc::Exception::what() const noexcept
|
||||
{
|
||||
return what_.c_str();
|
||||
}
|
||||
|
||||
const char* tc::Exception::module() const noexcept
|
||||
{
|
||||
return module_.c_str();
|
||||
}
|
||||
|
||||
const char * tc::Exception::error() const noexcept
|
||||
{
|
||||
return error_.c_str();
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#include <tc/PlatformErrorHandlingUtil.h>
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef _WIN32
|
||||
std::string tc::PlatformErrorHandlingUtil::GetLastErrorString(DWORD error)
|
||||
{
|
||||
if (error)
|
||||
{
|
||||
LPVOID lpMsgBuf;
|
||||
DWORD bufLen = FormatMessage(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
error,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPTSTR) &lpMsgBuf,
|
||||
0, NULL );
|
||||
|
||||
if (bufLen)
|
||||
{
|
||||
LPCSTR lpMsgStr = (LPCSTR)lpMsgBuf;
|
||||
std::string result(lpMsgStr, lpMsgStr+bufLen);
|
||||
|
||||
LocalFree(lpMsgBuf);
|
||||
|
||||
// remove CR+LF from string
|
||||
result.erase(std::remove(result.begin(), result.end(), '\n'), result.end());
|
||||
result.erase(std::remove(result.begin(), result.end(), '\r'), result.end());
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return std::string();
|
||||
}
|
||||
#else
|
||||
std::string tc::PlatformErrorHandlingUtil::GetGnuErrorNumString(int errnum)
|
||||
{
|
||||
return std::string(strerror(errnum));
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,222 @@
|
||||
#include <tc/cli/FormatUtil.h>
|
||||
|
||||
#include <fmt/core.h>
|
||||
|
||||
inline int charToByte(char chr)
|
||||
{
|
||||
if (chr >= 'a' && chr <= 'f')
|
||||
return (chr - 'a') + 0xa;
|
||||
else if (chr >= 'A' && chr <= 'F')
|
||||
return (chr - 'A') + 0xa;
|
||||
else if (chr >= '0' && chr <= '9')
|
||||
return chr - '0';
|
||||
return -1;
|
||||
}
|
||||
|
||||
tc::ByteData tc::cli::FormatUtil::hexStringToBytes(const std::string& str)
|
||||
{
|
||||
size_t size = str.size();
|
||||
if ((size % 2))
|
||||
{
|
||||
return tc::ByteData();
|
||||
}
|
||||
|
||||
auto bytes = tc::ByteData(size/2);
|
||||
|
||||
for (size_t i = 0; i < bytes.size(); i++)
|
||||
{
|
||||
int byte = 0;
|
||||
|
||||
byte = charToByte(str[i * 2]);
|
||||
if (byte == -1)
|
||||
return tc::ByteData();
|
||||
|
||||
bytes.data()[i] = byte_t((byte & 0xf) << 4);
|
||||
|
||||
byte = charToByte(str[(i * 2) + 1]);
|
||||
if (byte == -1)
|
||||
return tc::ByteData();
|
||||
|
||||
bytes.data()[i] |= byte_t((byte & 0xf) << 0);
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
std::string tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(const byte_t* data, size_t len, bool upper_case, const std::string& delimiter, size_t row_len, size_t indent_len, bool print_first_indent)
|
||||
{
|
||||
// create indentation string
|
||||
std::string indent_str = "";
|
||||
for (size_t i = 0; i < indent_len; i++)
|
||||
{
|
||||
indent_str += " ";
|
||||
}
|
||||
|
||||
const byte_t* original_data = data;
|
||||
|
||||
// create output string
|
||||
std::string output_str = "";
|
||||
|
||||
for (size_t print_len = 0; len > 0; len -= print_len, data += print_len)
|
||||
{
|
||||
if (data != original_data || print_first_indent)
|
||||
{
|
||||
output_str += indent_str;
|
||||
}
|
||||
|
||||
print_len = std::min<size_t>(len, row_len);
|
||||
|
||||
output_str += formatBytesAsString(data, print_len, upper_case, delimiter);
|
||||
|
||||
output_str += fmt::format("\n");
|
||||
}
|
||||
|
||||
return output_str;
|
||||
}
|
||||
|
||||
std::string tc::cli::FormatUtil::formatBytesAsString(const byte_t* data, size_t size, bool upper_case, const std::string& delimiter)
|
||||
{
|
||||
// create output string
|
||||
std::string output_str;
|
||||
|
||||
for (size_t i = 0; i < size; i++)
|
||||
{
|
||||
output_str += fmt::format((upper_case ? "{:02X}" : "{:02x}"), data[i]);
|
||||
if (i+1 < size)
|
||||
{
|
||||
output_str += delimiter;
|
||||
}
|
||||
}
|
||||
|
||||
return output_str;
|
||||
}
|
||||
|
||||
std::string tc::cli::FormatUtil::formatBytesAsString(const tc::ByteData& data, bool upper_case, const std::string& delimiter)
|
||||
{
|
||||
return formatBytesAsString(data.data(), data.size(), upper_case, delimiter);
|
||||
}
|
||||
|
||||
|
||||
std::string tc::cli::FormatUtil::formatListWithLineLimit(const std::vector<std::string>& str_list, size_t row_len, size_t indent_len, bool print_first_indent)
|
||||
{
|
||||
if (str_list.size() == 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
// create output string
|
||||
std::string output_str = "";
|
||||
|
||||
// create indentation string
|
||||
std::string indent_str = "";
|
||||
for (size_t i = 0; i < indent_len; i++)
|
||||
{
|
||||
indent_str += " ";
|
||||
}
|
||||
|
||||
// create delimiter string
|
||||
std::string delimiter_str = ", ";
|
||||
|
||||
size_t printed_len = 0;
|
||||
for (auto itr = str_list.begin(); itr != str_list.end(); itr++)
|
||||
{
|
||||
// format the strings
|
||||
// wrap the line after row_len multples
|
||||
if (printed_len > row_len || printed_len == 0)
|
||||
{
|
||||
// don't print the new line if this is the first string
|
||||
if (itr != str_list.begin())
|
||||
{
|
||||
output_str += fmt::format("{:s}\n", delimiter_str);
|
||||
}
|
||||
|
||||
// print indent if this isn't the first string or the user has opted into printing the indent regardless
|
||||
if (itr != str_list.begin() || print_first_indent)
|
||||
{
|
||||
output_str += indent_str;
|
||||
}
|
||||
|
||||
// reset printed_len
|
||||
printed_len = 0;
|
||||
}
|
||||
// within a line we want to separate the next string from the last one with a comma and a space
|
||||
else
|
||||
{
|
||||
//ss << delimiter_str;
|
||||
output_str += delimiter_str;
|
||||
}
|
||||
|
||||
// print string
|
||||
output_str += *itr;
|
||||
|
||||
// note the length of the string printed
|
||||
printed_len += itr->size() + delimiter_str.size();
|
||||
}
|
||||
output_str += fmt::format("\n");
|
||||
|
||||
return output_str;
|
||||
}
|
||||
|
||||
std::string tc::cli::FormatUtil::formatBytesAsHxdHexString(const byte_t* data, size_t size, size_t bytes_per_row, size_t byte_group_size)
|
||||
{
|
||||
if (size == 0 || bytes_per_row == 0 || byte_group_size == 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
// create output string
|
||||
std::string output_str = "";
|
||||
|
||||
// iterate over blocks
|
||||
for (size_t i = 0; size > 0; i++)
|
||||
{
|
||||
size_t row_print_len = std::min<size_t>(size, bytes_per_row);
|
||||
|
||||
output_str += fmt::format("{:08x} | ", uint64_t(i) * uint64_t(bytes_per_row));
|
||||
|
||||
// for block i print each byte
|
||||
for (size_t j = 0; j < bytes_per_row; j++)
|
||||
{
|
||||
if (j < row_print_len)
|
||||
{
|
||||
output_str += fmt::format("{:02X}", data[(i * bytes_per_row) + j]);
|
||||
}
|
||||
else
|
||||
{
|
||||
output_str += " ";
|
||||
}
|
||||
|
||||
|
||||
if (((j+1) % byte_group_size) == 0)
|
||||
{
|
||||
output_str += " ";
|
||||
}
|
||||
}
|
||||
|
||||
output_str += " ";
|
||||
|
||||
for (size_t j = 0; j < bytes_per_row; j++)
|
||||
{
|
||||
if (j < row_print_len)
|
||||
{
|
||||
byte_t byte = data[(i * bytes_per_row) + j];
|
||||
output_str += fmt::format("{:c}", (iscntrl(byte) == 0 && byte < 0x7f) ? (char)byte : '.');
|
||||
}
|
||||
else
|
||||
{
|
||||
output_str += " ";
|
||||
}
|
||||
}
|
||||
|
||||
output_str += fmt::format("\n");
|
||||
|
||||
size -= row_print_len;
|
||||
}
|
||||
|
||||
return output_str;
|
||||
}
|
||||
|
||||
std::string tc::cli::FormatUtil::formatBytesAsHxdHexString(const byte_t* data, size_t size)
|
||||
{
|
||||
return formatBytesAsHxdHexString(data, size, 0x10, 1);
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
#include <tc/cli/OptionParser.h>
|
||||
|
||||
#include <fmt/core.h>
|
||||
|
||||
const std::string tc::cli::OptionParser::kClassName = "tc::cli::OptionParser";
|
||||
|
||||
tc::cli::OptionParser::OptionParser() :
|
||||
mOptionaAliasMap(),
|
||||
mUnkOptHandler(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void tc::cli::OptionParser::registerOptionHandler(const std::shared_ptr<IOptionHandler>& handler)
|
||||
{
|
||||
if (handler == nullptr)
|
||||
{
|
||||
// throw exception
|
||||
throw tc::ArgumentNullException(kClassName, "OptionHandler was null.");
|
||||
}
|
||||
|
||||
if (handler->getOptionStrings().empty() && handler->getOptionRegexPatterns().empty())
|
||||
{
|
||||
throw tc::ArgumentOutOfRangeException(kClassName, "OptionHandler had no option strings or regex patterns.");
|
||||
}
|
||||
|
||||
for (auto itr = handler->getOptionStrings().begin(); itr != handler->getOptionStrings().end(); itr++)
|
||||
{
|
||||
mOptionaAliasMap.insert(std::pair<std::string, std::shared_ptr<IOptionHandler>>(*itr, handler));
|
||||
}
|
||||
|
||||
for (auto itr = handler->getOptionRegexPatterns().begin(); itr != handler->getOptionRegexPatterns().end(); itr++)
|
||||
{
|
||||
mOptionRegexList.push_back(std::pair<std::regex, std::shared_ptr<IOptionHandler>>(std::regex(*itr), handler));
|
||||
}
|
||||
}
|
||||
|
||||
void tc::cli::OptionParser::registerUnrecognisedOptionHandler(const std::shared_ptr<IOptionHandler>& handler)
|
||||
{
|
||||
if (handler == nullptr)
|
||||
{
|
||||
// throw exception
|
||||
throw tc::ArgumentNullException(kClassName, "OptionHandler was null.");
|
||||
}
|
||||
|
||||
mUnkOptHandler = handler;
|
||||
}
|
||||
|
||||
void tc::cli::OptionParser::processOptions(const std::vector<std::string>& args)
|
||||
{
|
||||
/*
|
||||
fmt::print("OptionParser\n");
|
||||
fmt::print(" args:\n");
|
||||
for (auto itr = args.begin(); itr != args.end(); itr++)
|
||||
{
|
||||
fmt::print(" #{:s}#\n", *itr);
|
||||
}
|
||||
*/
|
||||
|
||||
//fmt::print("Begin parsing options\n");
|
||||
|
||||
std::string opt = std::string();
|
||||
std::vector<std::string> params = std::vector<std::string>();
|
||||
for (auto itr = args.begin(); itr != args.end(); itr++)
|
||||
{
|
||||
//fmt::print("itr={:s}\n", *itr);
|
||||
|
||||
// (1) parse the current string
|
||||
std::string tmp_opt = std::string();
|
||||
std::string tmp_param = std::string();
|
||||
bool is_compound = false;
|
||||
|
||||
// if the string begins with '-' then it is an option (which may be compound)
|
||||
if (itr->compare(0,1,"-") == 0)
|
||||
{
|
||||
//fmt::print("looks like an option\n");
|
||||
|
||||
// if there is an "=" in this, then this is a compound option & paramter
|
||||
size_t equalsign_pos = itr->find('=');
|
||||
if (equalsign_pos == std::string::npos)
|
||||
{
|
||||
//fmt::print("the option looks like a solo option\n");
|
||||
tmp_opt = *itr;
|
||||
}
|
||||
else
|
||||
{
|
||||
//fmt::print("the option looks like a compound opt=param\n");
|
||||
tmp_opt = itr->substr(0, equalsign_pos);
|
||||
tmp_param = itr->substr(equalsign_pos + 1, std::string::npos);
|
||||
//fmt::print(" > opt : {:s}\n", tmp_opt);
|
||||
//fmt::print(" > param : {:s}\n", tmp_param);
|
||||
// --path=here
|
||||
// 0123456789a
|
||||
// --path : pos=0, size = 6 = equalsign_pos
|
||||
// here : pos = 7 = eqialsign_pos + 1, size = 4 = 11 - 7 = itr->length() -(equalsign_pos+1)
|
||||
is_compound = true;
|
||||
}
|
||||
|
||||
}
|
||||
// otherwise it is a param
|
||||
else
|
||||
{
|
||||
tmp_param = *itr;
|
||||
}
|
||||
|
||||
// (2) interprete it in the context of the current state
|
||||
|
||||
// the user has indicated the end of the current option
|
||||
// hand off to option handler and clear state
|
||||
if (opt.empty() == false && tmp_opt.empty() == false)
|
||||
{
|
||||
handleOption(opt, params);
|
||||
|
||||
opt = std::string();
|
||||
params = std::vector<std::string>();
|
||||
}
|
||||
|
||||
// if tmp_opt isn't empty then make it the option
|
||||
if (tmp_opt.empty() == false)
|
||||
{
|
||||
opt = tmp_opt;
|
||||
}
|
||||
|
||||
// if tmp_param isn't empty then add it to the param list
|
||||
if (tmp_param.empty() == false)
|
||||
{
|
||||
// if there is no option set, then this is a head-less parameter, throw exception
|
||||
if (opt.empty() == true)
|
||||
{
|
||||
throw tc::ArgumentException(kClassName, "Option parameter was provided without an option.");
|
||||
}
|
||||
params.push_back(tmp_param);
|
||||
}
|
||||
|
||||
// compound options only accept one parameter
|
||||
if (is_compound)
|
||||
{
|
||||
handleOption(opt, params);
|
||||
|
||||
opt = std::string();
|
||||
params = std::vector<std::string>();
|
||||
}
|
||||
}
|
||||
|
||||
// process dangling opt/params
|
||||
if (opt.empty() == false)
|
||||
{
|
||||
handleOption(opt, params);
|
||||
}
|
||||
}
|
||||
|
||||
void tc::cli::OptionParser::processOptions(const std::vector<std::string>& args, size_t pos, size_t num)
|
||||
{
|
||||
processOptions({args.begin()+pos, args.begin()+pos+num});
|
||||
}
|
||||
|
||||
void tc::cli::OptionParser::handleOption(const std::string& opt, const std::vector<std::string>& params)
|
||||
{
|
||||
// attempt to locate a literal alias
|
||||
auto aliasItr = mOptionaAliasMap.find(opt);
|
||||
if (aliasItr != mOptionaAliasMap.end())
|
||||
{
|
||||
aliasItr->second->processOption(opt, params);
|
||||
return;
|
||||
}
|
||||
|
||||
// attempt to pattern match
|
||||
for (auto regexItr = mOptionRegexList.begin(); regexItr != mOptionRegexList.end(); regexItr++)
|
||||
{
|
||||
if (std::regex_match(opt, regexItr->first))
|
||||
{
|
||||
regexItr->second->processOption(opt, params);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// attempt to use unknown option handler
|
||||
if (mUnkOptHandler != nullptr)
|
||||
{
|
||||
mUnkOptHandler->processOption(opt, params);
|
||||
return;
|
||||
}
|
||||
|
||||
// if no handler is located, throw exception
|
||||
throw tc::ArgumentException(kClassName, fmt::format("Option \"{}\" is not recognised.", opt));
|
||||
}
|
||||
@@ -0,0 +1,328 @@
|
||||
#include <tc/crypto/Aes128CbcEncryptedStream.h>
|
||||
#include <tc/io/IOUtil.h>
|
||||
#include <tc/io/StreamUtil.h>
|
||||
|
||||
/*
|
||||
#include <fmt/core.h>
|
||||
#include <tc/cli/FormatUtil.h>
|
||||
*/
|
||||
|
||||
// inline utils
|
||||
inline uint64_t castInt64ToUint64(int64_t val) { return val < 0 ? 0 : uint64_t(val); }
|
||||
inline int64_t castUint64ToInt64(uint64_t val) { return (int64_t)std::min<uint64_t>(val, uint64_t(std::numeric_limits<int64_t>::max())); }
|
||||
|
||||
inline uint64_t offsetToBlockIndex(int64_t offset) { return castInt64ToUint64(offset / tc::io::IOUtil::castSizeToInt64(tc::crypto::Aes128CbcEncryptor::kBlockSize)); };
|
||||
inline int64_t blockIndexToOffset(uint64_t block_index) { return castUint64ToInt64(block_index) * tc::io::IOUtil::castSizeToInt64(tc::crypto::Aes128CbcEncryptor::kBlockSize); };
|
||||
|
||||
inline size_t lengthToBlockNum(int64_t length) { return tc::io::IOUtil::castInt64ToSize(length / tc::io::IOUtil::castSizeToInt64(tc::crypto::Aes128CbcEncryptor::kBlockSize)); };
|
||||
inline size_t offsetInBlock(int64_t offset) { return tc::io::IOUtil::castInt64ToSize(offset % tc::io::IOUtil::castSizeToInt64(tc::crypto::Aes128CbcEncryptor::kBlockSize)); };
|
||||
|
||||
|
||||
const std::string tc::crypto::Aes128CbcEncryptedStream::kClassName = "tc::crypto::Aes128CbcEncryptedStream";
|
||||
|
||||
tc::crypto::Aes128CbcEncryptedStream::Aes128CbcEncryptedStream() :
|
||||
mBaseStream(),
|
||||
mCryptor(std::shared_ptr<tc::crypto::Aes128CbcEncryptor>(new tc::crypto::Aes128CbcEncryptor()))
|
||||
{
|
||||
memset(mBaseIv.data(), 0, mBaseIv.size());
|
||||
}
|
||||
|
||||
tc::crypto::Aes128CbcEncryptedStream::Aes128CbcEncryptedStream(const std::shared_ptr<tc::io::IStream>& stream, const key_t& key, const iv_t& iv) :
|
||||
Aes128CbcEncryptedStream()
|
||||
{
|
||||
mBaseStream = stream;
|
||||
|
||||
// validate stream properties
|
||||
if (mBaseStream == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName, "stream is null.");
|
||||
}
|
||||
if (mBaseStream->canRead() == false)
|
||||
{
|
||||
throw tc::NotSupportedException(kClassName, "stream does not support reading.");
|
||||
}
|
||||
if (mBaseStream->canSeek() == false)
|
||||
{
|
||||
throw tc::NotSupportedException(kClassName, "stream does not support seeking.");
|
||||
}
|
||||
if ((mBaseStream->length() % sizeof(block_t)) != 0)
|
||||
{
|
||||
throw tc::NotSupportedException(kClassName, "stream does is not block aligned.");
|
||||
}
|
||||
|
||||
// initialize cryptor
|
||||
mCryptor->initialize(key.data(), key.size(), iv.data(), iv.size());
|
||||
mBaseIv = iv;
|
||||
}
|
||||
|
||||
bool tc::crypto::Aes128CbcEncryptedStream::canRead() const
|
||||
{
|
||||
return mBaseStream == nullptr ? false : mBaseStream->canRead();
|
||||
}
|
||||
|
||||
bool tc::crypto::Aes128CbcEncryptedStream::canWrite() const
|
||||
{
|
||||
return false; // always false this is a read-only stream
|
||||
}
|
||||
bool tc::crypto::Aes128CbcEncryptedStream::canSeek() const
|
||||
{
|
||||
return mBaseStream == nullptr ? false : mBaseStream->canSeek();
|
||||
}
|
||||
|
||||
int64_t tc::crypto::Aes128CbcEncryptedStream::length()
|
||||
{
|
||||
return mBaseStream == nullptr ? 0 : mBaseStream->length();
|
||||
}
|
||||
|
||||
int64_t tc::crypto::Aes128CbcEncryptedStream::position()
|
||||
{
|
||||
return mBaseStream == nullptr ? 0 : mBaseStream->position();
|
||||
}
|
||||
|
||||
size_t tc::crypto::Aes128CbcEncryptedStream::read(byte_t* ptr, size_t count)
|
||||
{
|
||||
if (mBaseStream == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::read()", "Failed to read from stream (stream is disposed)");
|
||||
}
|
||||
|
||||
// track read_count
|
||||
size_t data_read_count = 0;
|
||||
|
||||
// get predicted read count
|
||||
count = tc::io::IOUtil::getReadableCount(this->length(), this->position(), count);
|
||||
|
||||
// if count is 0 just return
|
||||
if (count == 0) return data_read_count;
|
||||
|
||||
// get current position
|
||||
int64_t current_pos = mBaseStream->position();
|
||||
if (current_pos < 0)
|
||||
{
|
||||
throw tc::InvalidOperationException(kClassName+"::read()", "Current stream position is negative.");
|
||||
}
|
||||
|
||||
// determine begin & end offsets
|
||||
int64_t begin_read_offset = current_pos;
|
||||
int64_t end_read_offset = begin_read_offset + tc::io::IOUtil::castSizeToInt64(count);
|
||||
int64_t begin_aligned_offset = begin_read_offset - tc::io::IOUtil::castSizeToInt64(offsetInBlock(begin_read_offset));
|
||||
int64_t end_aligned_offset = end_read_offset - tc::io::IOUtil::castSizeToInt64(offsetInBlock(end_read_offset)) + tc::io::IOUtil::castSizeToInt64(offsetInBlock(end_read_offset) ? sizeof(block_t) : 0x0);
|
||||
size_t block_num = lengthToBlockNum(end_aligned_offset - begin_aligned_offset);
|
||||
|
||||
bool read_partial_begin_block = false;
|
||||
uint64_t partial_begin_block_index = offsetToBlockIndex(begin_read_offset);
|
||||
size_t partial_begin_block_offset = 0;
|
||||
size_t partial_begin_block_size = sizeof(block_t);
|
||||
|
||||
bool read_partial_end_block = false;
|
||||
uint64_t partial_end_block_index = offsetToBlockIndex(end_read_offset);
|
||||
size_t partial_end_block_offset = 0;
|
||||
size_t partial_end_block_size = sizeof(block_t);
|
||||
|
||||
if (offsetInBlock(begin_read_offset) != 0)
|
||||
{
|
||||
read_partial_begin_block = true;
|
||||
partial_begin_block_offset += offsetInBlock(begin_read_offset);
|
||||
partial_begin_block_size -= partial_begin_block_offset;
|
||||
}
|
||||
if (offsetInBlock(end_read_offset) != 0)
|
||||
{
|
||||
if (partial_begin_block_index == partial_end_block_index)
|
||||
{
|
||||
read_partial_begin_block = true;
|
||||
partial_begin_block_size -= (sizeof(block_t) - offsetInBlock(end_read_offset));
|
||||
}
|
||||
else
|
||||
{
|
||||
read_partial_end_block = true;
|
||||
partial_end_block_size = offsetInBlock(end_read_offset);
|
||||
}
|
||||
}
|
||||
|
||||
size_t continuous_block_num = block_num - (size_t)read_partial_begin_block - (size_t)read_partial_end_block;
|
||||
uint64_t continuous_begin_block_index = (continuous_block_num == 0) ? 0 : (offsetToBlockIndex(begin_aligned_offset) + (uint64_t)read_partial_begin_block);
|
||||
|
||||
/*
|
||||
fmt::print("##############################################\n");
|
||||
fmt::print("count: 0x{:x}\n", count);
|
||||
fmt::print("begin_read_offset: 0x{:x}\n", begin_read_offset);
|
||||
fmt::print("end_read_offset: 0x{:x}\n", end_read_offset);
|
||||
fmt::print("begin_aligned_offset: 0x{:x}\n", begin_aligned_offset);
|
||||
fmt::print("end_aligned_offset: 0x{:x}\n", end_aligned_offset);
|
||||
fmt::print("block_num: 0x{:x}\n", block_num);
|
||||
|
||||
fmt::print("partial_begin:\n");
|
||||
fmt::print(" read_block: {}\n", read_partial_begin_block);
|
||||
fmt::print(" block_index: 0x{:x}\n", partial_begin_block_index);
|
||||
fmt::print(" offset: 0x{:x}\n", partial_begin_block_offset);
|
||||
fmt::print(" size: 0x{:x}\n", partial_begin_block_size);
|
||||
|
||||
fmt::print("partial_end:\n");
|
||||
fmt::print(" read_block: {}\n", read_partial_end_block);
|
||||
fmt::print(" block_index: 0x{:x}\n", partial_end_block_index);
|
||||
fmt::print(" offset: 0x{:x}\n", partial_end_block_offset);
|
||||
fmt::print(" size: 0x{:x}\n", partial_end_block_size);
|
||||
|
||||
fmt::print("continuous:\n");
|
||||
fmt::print(" block_index: 0x{:x}\n", continuous_begin_block_index);
|
||||
fmt::print(" block_num: 0x{:x}\n", continuous_block_num);
|
||||
*/
|
||||
|
||||
if (block_num == 0)
|
||||
{
|
||||
tc::InvalidOperationException(kClassName+"::read()", "Invalid block number (0 blocks, would have returned before now if count==0)");
|
||||
}
|
||||
|
||||
if (block_num < continuous_block_num)
|
||||
{
|
||||
tc::InvalidOperationException(kClassName+"::read()", "Invalid block number (underflow error)");
|
||||
}
|
||||
|
||||
// allocate memory for partial block
|
||||
tc::ByteData partial_block = tc::ByteData(sizeof(block_t));
|
||||
|
||||
// read un-aligned begin block
|
||||
if (read_partial_begin_block)
|
||||
{
|
||||
// read iv
|
||||
iv_t iv;
|
||||
if (partial_begin_block_index == 0)
|
||||
{
|
||||
iv = mBaseIv;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->seek(blockIndexToOffset(partial_begin_block_index-1), tc::io::SeekOrigin::Begin);
|
||||
mBaseStream->read(iv.data(), iv.size());
|
||||
}
|
||||
mCryptor->update_iv(iv.data(), iv.size());
|
||||
|
||||
// read block
|
||||
this->seek(blockIndexToOffset(partial_begin_block_index), tc::io::SeekOrigin::Begin);
|
||||
mBaseStream->read(partial_block.data(), partial_block.size());
|
||||
|
||||
// decrypt block
|
||||
mCryptor->decrypt(partial_block.data(), partial_block.data(), partial_block.size());
|
||||
|
||||
// copy out block carving
|
||||
memcpy(ptr + data_read_count, partial_block.data() + partial_begin_block_offset, partial_begin_block_size);
|
||||
|
||||
// increment data read count
|
||||
data_read_count += partial_begin_block_size;
|
||||
}
|
||||
|
||||
// read continous blocks
|
||||
if (continuous_block_num > 0)
|
||||
{
|
||||
// read iv
|
||||
iv_t iv;
|
||||
if (continuous_begin_block_index == 0)
|
||||
{
|
||||
iv = mBaseIv;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->seek(blockIndexToOffset(continuous_begin_block_index-1), tc::io::SeekOrigin::Begin);
|
||||
mBaseStream->read(iv.data(), iv.size());
|
||||
}
|
||||
mCryptor->update_iv(iv.data(), iv.size());
|
||||
|
||||
// read blocks
|
||||
this->seek(blockIndexToOffset(continuous_begin_block_index), tc::io::SeekOrigin::Begin);
|
||||
mBaseStream->read(ptr + data_read_count, continuous_block_num * sizeof(block_t));
|
||||
|
||||
// decrypt blocks
|
||||
mCryptor->decrypt(ptr + data_read_count, ptr + data_read_count, continuous_block_num * sizeof(block_t));
|
||||
|
||||
// increment data read count
|
||||
data_read_count += continuous_block_num * sizeof(block_t);
|
||||
}
|
||||
|
||||
// read un-aligned end block
|
||||
if (read_partial_end_block)
|
||||
{
|
||||
// read iv
|
||||
iv_t iv;
|
||||
if (partial_end_block_index == 0)
|
||||
{
|
||||
iv = mBaseIv;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->seek(blockIndexToOffset(partial_end_block_index-1), tc::io::SeekOrigin::Begin);
|
||||
mBaseStream->read(iv.data(), iv.size());
|
||||
}
|
||||
mCryptor->update_iv(iv.data(), iv.size());
|
||||
|
||||
// read block
|
||||
this->seek(blockIndexToOffset(partial_end_block_index), tc::io::SeekOrigin::Begin);
|
||||
mBaseStream->read(partial_block.data(), partial_block.size());
|
||||
|
||||
// decrypt block
|
||||
mCryptor->decrypt(partial_block.data(), partial_block.data(), partial_block.size());
|
||||
|
||||
// copy out block carving
|
||||
memcpy(ptr + data_read_count, partial_block.data() + partial_end_block_offset, partial_end_block_size);
|
||||
|
||||
// increment
|
||||
data_read_count += partial_end_block_size;
|
||||
}
|
||||
|
||||
// restore expected logical position
|
||||
this->seek(begin_read_offset + tc::io::IOUtil::castSizeToInt64(data_read_count), tc::io::SeekOrigin::Begin);
|
||||
|
||||
// return data read count
|
||||
return data_read_count;
|
||||
}
|
||||
|
||||
size_t tc::crypto::Aes128CbcEncryptedStream::write(const byte_t* ptr, size_t count)
|
||||
{
|
||||
if (mBaseStream == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::write()", "Failed to set stream position (stream is disposed)");
|
||||
}
|
||||
|
||||
throw tc::NotImplementedException(kClassName+"::write()", "write is not implemented for Aes128CbcEncryptedStream");
|
||||
}
|
||||
|
||||
int64_t tc::crypto::Aes128CbcEncryptedStream::seek(int64_t offset, tc::io::SeekOrigin origin)
|
||||
{
|
||||
if (mBaseStream == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::seek()", "Failed to set stream position (stream is disposed)");
|
||||
}
|
||||
|
||||
return mBaseStream->seek(offset, origin);
|
||||
}
|
||||
|
||||
void tc::crypto::Aes128CbcEncryptedStream::setLength(int64_t length)
|
||||
{
|
||||
if (mBaseStream == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::setLength()", "Failed to set stream length (stream is disposed)");
|
||||
}
|
||||
|
||||
throw tc::NotImplementedException(kClassName+"::setLength()", "setLength is not implemented for Aes128CbcEncryptedStream");
|
||||
}
|
||||
|
||||
void tc::crypto::Aes128CbcEncryptedStream::flush()
|
||||
{
|
||||
if (mBaseStream == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::seek()", "Failed to flush stream (stream is disposed)");
|
||||
}
|
||||
|
||||
mBaseStream->flush();
|
||||
}
|
||||
|
||||
void tc::crypto::Aes128CbcEncryptedStream::dispose()
|
||||
{
|
||||
if (mBaseStream.get() != nullptr)
|
||||
{
|
||||
// dispose base stream
|
||||
mBaseStream->dispose();
|
||||
|
||||
// release ptr
|
||||
mBaseStream.reset();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#include <tc/crypto/Aes128CbcEncryptor.h>
|
||||
|
||||
void tc::crypto::EncryptAes128Cbc(byte_t* dst, const byte_t* src, size_t size, const byte_t* key, size_t key_size, const byte_t* iv, size_t iv_size)
|
||||
{
|
||||
tc::crypto::Aes128CbcEncryptor crypt;
|
||||
crypt.initialize(key, key_size, iv, iv_size);
|
||||
crypt.encrypt(dst, src, size);
|
||||
}
|
||||
|
||||
void tc::crypto::DecryptAes128Cbc(byte_t* dst, const byte_t* src, size_t size, const byte_t* key, size_t key_size, const byte_t* iv, size_t iv_size)
|
||||
{
|
||||
tc::crypto::Aes128CbcEncryptor crypt;
|
||||
crypt.initialize(key, key_size, iv, iv_size);
|
||||
crypt.decrypt(dst, src, size);
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
#include <tc/crypto/Aes128CtrEncryptedStream.h>
|
||||
#include <tc/io/IOUtil.h>
|
||||
#include <tc/io/StreamUtil.h>
|
||||
|
||||
/*
|
||||
#include <fmt/core.h>
|
||||
#include <tc/cli/FormatUtil.h>
|
||||
*/
|
||||
|
||||
// inline utils
|
||||
inline uint64_t castInt64ToUint64(int64_t val) { return val < 0 ? 0 : uint64_t(val); }
|
||||
inline int64_t castUint64ToInt64(uint64_t val) { return (int64_t)std::min<uint64_t>(val, uint64_t(std::numeric_limits<int64_t>::max())); }
|
||||
|
||||
inline uint64_t offsetToBlockIndex(int64_t offset) { return castInt64ToUint64(offset / tc::io::IOUtil::castSizeToInt64(tc::crypto::Aes128CtrEncryptor::kBlockSize)); };
|
||||
inline int64_t blockIndexToOffset(uint64_t block_index) { return castUint64ToInt64(block_index) * tc::io::IOUtil::castSizeToInt64(tc::crypto::Aes128CtrEncryptor::kBlockSize); };
|
||||
|
||||
inline size_t lengthToBlockNum(int64_t length) { return tc::io::IOUtil::castInt64ToSize(length / tc::io::IOUtil::castSizeToInt64(tc::crypto::Aes128CtrEncryptor::kBlockSize)); };
|
||||
inline size_t offsetInBlock(int64_t offset) { return tc::io::IOUtil::castInt64ToSize(offset % tc::io::IOUtil::castSizeToInt64(tc::crypto::Aes128CtrEncryptor::kBlockSize)); };
|
||||
|
||||
const std::string tc::crypto::Aes128CtrEncryptedStream::kClassName = "tc::crypto::Aes128CtrEncryptedStream";
|
||||
|
||||
tc::crypto::Aes128CtrEncryptedStream::Aes128CtrEncryptedStream() :
|
||||
mBaseStream(),
|
||||
mCryptor(std::shared_ptr<tc::crypto::Aes128CtrEncryptor>(new tc::crypto::Aes128CtrEncryptor()))
|
||||
{
|
||||
}
|
||||
|
||||
tc::crypto::Aes128CtrEncryptedStream::Aes128CtrEncryptedStream(const std::shared_ptr<tc::io::IStream>& stream, const key_t& key, const counter_t& counter) :
|
||||
Aes128CtrEncryptedStream()
|
||||
{
|
||||
mBaseStream = stream;
|
||||
|
||||
// validate stream properties
|
||||
if (mBaseStream == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName, "stream is null.");
|
||||
}
|
||||
if (mBaseStream->canRead() == false)
|
||||
{
|
||||
throw tc::NotSupportedException(kClassName, "stream does not support reading.");
|
||||
}
|
||||
if (mBaseStream->canSeek() == false)
|
||||
{
|
||||
throw tc::NotSupportedException(kClassName, "stream does not support seeking.");
|
||||
}
|
||||
|
||||
// initialize cryptor
|
||||
mCryptor->initialize(key.data(), key.size(), counter.data(), counter.size());
|
||||
}
|
||||
|
||||
bool tc::crypto::Aes128CtrEncryptedStream::canRead() const
|
||||
{
|
||||
return mBaseStream == nullptr ? false : mBaseStream->canRead();
|
||||
}
|
||||
|
||||
bool tc::crypto::Aes128CtrEncryptedStream::canWrite() const
|
||||
{
|
||||
return false; // always false this is a read-only stream
|
||||
}
|
||||
bool tc::crypto::Aes128CtrEncryptedStream::canSeek() const
|
||||
{
|
||||
return mBaseStream == nullptr ? false : mBaseStream->canSeek();
|
||||
}
|
||||
|
||||
int64_t tc::crypto::Aes128CtrEncryptedStream::length()
|
||||
{
|
||||
return mBaseStream == nullptr ? 0 : mBaseStream->length();
|
||||
}
|
||||
|
||||
int64_t tc::crypto::Aes128CtrEncryptedStream::position()
|
||||
{
|
||||
return mBaseStream == nullptr ? 0 : mBaseStream->position();
|
||||
}
|
||||
|
||||
size_t tc::crypto::Aes128CtrEncryptedStream::read(byte_t* ptr, size_t count)
|
||||
{
|
||||
if (mBaseStream == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::read()", "Failed to read from stream (stream is disposed)");
|
||||
}
|
||||
|
||||
// track read_count
|
||||
size_t data_read_count = 0;
|
||||
|
||||
// get predicted read count
|
||||
count = tc::io::IOUtil::getReadableCount(this->length(), this->position(), count);
|
||||
|
||||
// if count is 0 just return
|
||||
if (count == 0) return data_read_count;
|
||||
|
||||
// get current position
|
||||
int64_t current_pos = mBaseStream->position();
|
||||
if (current_pos < 0)
|
||||
{
|
||||
throw tc::InvalidOperationException(kClassName+"::read()", "Current stream position is negative.");
|
||||
}
|
||||
|
||||
// determine begin & end offsets
|
||||
int64_t begin_read_offset = current_pos;
|
||||
int64_t end_read_offset = begin_read_offset + tc::io::IOUtil::castSizeToInt64(count);
|
||||
int64_t begin_aligned_offset = begin_read_offset - tc::io::IOUtil::castSizeToInt64(offsetInBlock(begin_read_offset));
|
||||
int64_t end_aligned_offset = end_read_offset - tc::io::IOUtil::castSizeToInt64(offsetInBlock(end_read_offset)) + tc::io::IOUtil::castSizeToInt64(offsetInBlock(end_read_offset) ? sizeof(block_t) : 0x0);
|
||||
size_t block_num = lengthToBlockNum(end_aligned_offset - begin_aligned_offset);
|
||||
|
||||
bool read_partial_begin_block = false;
|
||||
uint64_t partial_begin_block_index = offsetToBlockIndex(begin_read_offset);
|
||||
size_t partial_begin_block_offset = 0;
|
||||
size_t partial_begin_block_size = sizeof(block_t);
|
||||
|
||||
bool read_partial_end_block = false;
|
||||
uint64_t partial_end_block_index = offsetToBlockIndex(end_read_offset);
|
||||
size_t partial_end_block_offset = 0;
|
||||
size_t partial_end_block_size = sizeof(block_t);
|
||||
|
||||
if (offsetInBlock(begin_read_offset) != 0)
|
||||
{
|
||||
read_partial_begin_block = true;
|
||||
partial_begin_block_offset += offsetInBlock(begin_read_offset);
|
||||
partial_begin_block_size -= partial_begin_block_offset;
|
||||
}
|
||||
if (offsetInBlock(end_read_offset) != 0)
|
||||
{
|
||||
if (partial_begin_block_index == partial_end_block_index)
|
||||
{
|
||||
read_partial_begin_block = true;
|
||||
partial_begin_block_size -= (sizeof(block_t) - offsetInBlock(end_read_offset));
|
||||
}
|
||||
else
|
||||
{
|
||||
read_partial_end_block = true;
|
||||
partial_end_block_size = offsetInBlock(end_read_offset);
|
||||
}
|
||||
}
|
||||
|
||||
size_t continuous_block_num = block_num - (size_t)read_partial_begin_block - (size_t)read_partial_end_block;
|
||||
uint64_t continuous_begin_block_index = (continuous_block_num == 0) ? 0 : (offsetToBlockIndex(begin_aligned_offset) + (uint64_t)read_partial_begin_block);
|
||||
|
||||
/*
|
||||
fmt::print("##############################################\n");
|
||||
fmt::print("count: 0x{:x}\n", count);
|
||||
fmt::print("begin_read_offset: 0x{:x}\n", begin_read_offset);
|
||||
fmt::print("end_read_offset: 0x{:x}\n", end_read_offset);
|
||||
fmt::print("begin_aligned_offset: 0x{:x}\n", begin_aligned_offset);
|
||||
fmt::print("end_aligned_offset: 0x{:x}\n", end_aligned_offset);
|
||||
fmt::print("block_num: 0x{:x}\n", block_num);
|
||||
|
||||
fmt::print("partial_begin:\n");
|
||||
fmt::print(" read_block: {}\n", read_partial_begin_block);
|
||||
fmt::print(" block_index: 0x{:x}\n", partial_begin_block_index);
|
||||
fmt::print(" offset: 0x{:x}\n", partial_begin_block_offset);
|
||||
fmt::print(" size: 0x{:x}\n", partial_begin_block_size);
|
||||
|
||||
fmt::print("partial_end:\n");
|
||||
fmt::print(" read_block: {}\n", read_partial_end_block);
|
||||
fmt::print(" block_index: 0x{:x}\n", partial_end_block_index);
|
||||
fmt::print(" offset: 0x{:x}\n", partial_end_block_offset);
|
||||
fmt::print(" size: 0x{:x}\n", partial_end_block_size);
|
||||
|
||||
fmt::print("continuous:\n");
|
||||
fmt::print(" block_index: 0x{:x}\n", continuous_begin_block_index);
|
||||
fmt::print(" block_num: 0x{:x}\n", continuous_block_num);
|
||||
*/
|
||||
|
||||
if (block_num == 0)
|
||||
{
|
||||
tc::InvalidOperationException(kClassName+"::read()", "Invalid block number (0 blocks, would have returned before now if count==0)");
|
||||
}
|
||||
|
||||
if (block_num < continuous_block_num)
|
||||
{
|
||||
tc::InvalidOperationException(kClassName+"::read()", "Invalid block number (underflow error)");
|
||||
}
|
||||
|
||||
// allocate memory for partial block
|
||||
tc::ByteData partial_block = tc::ByteData(sizeof(block_t));
|
||||
|
||||
// read un-aligned begin block
|
||||
if (read_partial_begin_block)
|
||||
{
|
||||
// read block
|
||||
this->seek(blockIndexToOffset(partial_begin_block_index), tc::io::SeekOrigin::Begin);
|
||||
mBaseStream->read(partial_block.data(), partial_block.size());
|
||||
|
||||
// decrypt block
|
||||
mCryptor->decrypt(partial_block.data(), partial_block.data(), partial_block.size(), partial_begin_block_index);
|
||||
|
||||
// copy out block carving
|
||||
memcpy(ptr + data_read_count, partial_block.data() + partial_begin_block_offset, partial_begin_block_size);
|
||||
|
||||
// increment data read count
|
||||
data_read_count += partial_begin_block_size;
|
||||
}
|
||||
|
||||
// read continous blocks
|
||||
if (continuous_block_num > 0)
|
||||
{
|
||||
// read blocks
|
||||
this->seek(blockIndexToOffset(continuous_begin_block_index), tc::io::SeekOrigin::Begin);
|
||||
mBaseStream->read(ptr + data_read_count, continuous_block_num * sizeof(block_t));
|
||||
|
||||
// decrypt blocks
|
||||
mCryptor->decrypt(ptr + data_read_count, ptr + data_read_count, continuous_block_num * sizeof(block_t), continuous_begin_block_index);
|
||||
|
||||
// increment data read count
|
||||
data_read_count += continuous_block_num * sizeof(block_t);
|
||||
}
|
||||
|
||||
// read un-aligned end block
|
||||
if (read_partial_end_block)
|
||||
{
|
||||
// read block
|
||||
this->seek(blockIndexToOffset(partial_end_block_index), tc::io::SeekOrigin::Begin);
|
||||
mBaseStream->read(partial_block.data(), partial_block.size());
|
||||
|
||||
// decrypt block
|
||||
mCryptor->decrypt(partial_block.data(), partial_block.data(), partial_block.size(), partial_end_block_index);
|
||||
|
||||
// copy out block carving
|
||||
memcpy(ptr + data_read_count, partial_block.data() + partial_end_block_offset, partial_end_block_size);
|
||||
|
||||
// increment
|
||||
data_read_count += partial_end_block_size;
|
||||
}
|
||||
|
||||
// restore expected logical position
|
||||
this->seek(begin_read_offset + tc::io::IOUtil::castSizeToInt64(data_read_count), tc::io::SeekOrigin::Begin);
|
||||
|
||||
// return data read count
|
||||
return data_read_count;
|
||||
}
|
||||
|
||||
size_t tc::crypto::Aes128CtrEncryptedStream::write(const byte_t* ptr, size_t count)
|
||||
{
|
||||
if (mBaseStream == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::write()", "Failed to set stream position (stream is disposed)");
|
||||
}
|
||||
|
||||
throw tc::NotImplementedException(kClassName+"::write()", "write is not implemented for Aes128CtrEncryptedStream");
|
||||
}
|
||||
|
||||
int64_t tc::crypto::Aes128CtrEncryptedStream::seek(int64_t offset, tc::io::SeekOrigin origin)
|
||||
{
|
||||
if (mBaseStream == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::seek()", "Failed to set stream position (stream is disposed)");
|
||||
}
|
||||
|
||||
return mBaseStream->seek(offset, origin);
|
||||
}
|
||||
|
||||
void tc::crypto::Aes128CtrEncryptedStream::setLength(int64_t length)
|
||||
{
|
||||
if (mBaseStream == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::setLength()", "Failed to set stream length (stream is disposed)");
|
||||
}
|
||||
|
||||
throw tc::NotImplementedException(kClassName+"::setLength()", "setLength is not implemented for Aes128CtrEncryptedStream");
|
||||
}
|
||||
|
||||
void tc::crypto::Aes128CtrEncryptedStream::flush()
|
||||
{
|
||||
if (mBaseStream == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::seek()", "Failed to flush stream (stream is disposed)");
|
||||
}
|
||||
|
||||
mBaseStream->flush();
|
||||
}
|
||||
|
||||
void tc::crypto::Aes128CtrEncryptedStream::dispose()
|
||||
{
|
||||
if (mBaseStream != nullptr)
|
||||
{
|
||||
// dispose base stream
|
||||
mBaseStream->dispose();
|
||||
|
||||
// release ptr
|
||||
mBaseStream.reset();
|
||||
}
|
||||
if (mCryptor != nullptr)
|
||||
mCryptor.reset();
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#include <tc/crypto/Aes128CtrEncryptor.h>
|
||||
|
||||
void tc::crypto::EncryptAes128Ctr(byte_t* dst, const byte_t* src, size_t size, uint64_t block_number, const byte_t* key, size_t key_size, const byte_t* iv, size_t iv_size)
|
||||
{
|
||||
tc::crypto::Aes128CtrEncryptor crypt;
|
||||
crypt.initialize(key, key_size, iv, iv_size);
|
||||
crypt.encrypt(dst, src, size, block_number);
|
||||
}
|
||||
|
||||
void tc::crypto::DecryptAes128Ctr(byte_t* dst, const byte_t* src, size_t size, uint64_t block_number, const byte_t* key, size_t key_size, const byte_t* iv, size_t iv_size)
|
||||
{
|
||||
tc::crypto::Aes128CtrEncryptor crypt;
|
||||
crypt.initialize(key, key_size, iv, iv_size);
|
||||
crypt.decrypt(dst, src, size, block_number);
|
||||
}
|
||||
|
||||
void tc::crypto::IncrementCounterAes128Ctr(byte_t* counter, uint64_t incr)
|
||||
{
|
||||
if (counter == nullptr)
|
||||
{
|
||||
throw tc::ArgumentOutOfRangeException("counter was null.");
|
||||
}
|
||||
tc::crypto::detail::incr_counter<16>(counter, incr);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#include <tc/crypto/Aes128EcbEncryptor.h>
|
||||
|
||||
void tc::crypto::EncryptAes128Ecb(byte_t* dst, const byte_t* src, size_t size, const byte_t* key, size_t key_size)
|
||||
{
|
||||
tc::crypto::Aes128EcbEncryptor crypt;
|
||||
crypt.initialize(key, key_size);
|
||||
crypt.encrypt(dst, src, size);
|
||||
}
|
||||
|
||||
void tc::crypto::DecryptAes128Ecb(byte_t* dst, const byte_t* src, size_t size, const byte_t* key, size_t key_size)
|
||||
{
|
||||
tc::crypto::Aes128EcbEncryptor crypt;
|
||||
crypt.initialize(key, key_size);
|
||||
crypt.decrypt(dst, src, size);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#include <tc/crypto/Aes128XtsEncryptor.h>
|
||||
|
||||
void tc::crypto::EncryptAes128Xts(byte_t* dst, const byte_t* src, size_t size, uint64_t sector_number, const byte_t* key1, size_t key1_size, const byte_t* key2, size_t key2_size, size_t sector_size, bool tweak_word_order)
|
||||
{
|
||||
tc::crypto::Aes128XtsEncryptor crypt;
|
||||
crypt.initialize(key1, key1_size, key2, key2_size, sector_size, tweak_word_order);
|
||||
crypt.encrypt(dst, src, size, sector_number);
|
||||
}
|
||||
|
||||
void tc::crypto::DecryptAes128Xts(byte_t* dst, const byte_t* src, size_t size, uint64_t sector_number, const byte_t* key1, size_t key1_size, const byte_t* key2, size_t key2_size, size_t sector_size, bool tweak_word_order)
|
||||
{
|
||||
tc::crypto::Aes128XtsEncryptor crypt;
|
||||
crypt.initialize(key1, key1_size, key2, key2_size, sector_size, tweak_word_order);
|
||||
crypt.decrypt(dst, src, size, sector_number);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#include <tc/crypto/Aes192CbcEncryptor.h>
|
||||
|
||||
void tc::crypto::EncryptAes192Cbc(byte_t* dst, const byte_t* src, size_t size, const byte_t* key, size_t key_size, const byte_t* iv, size_t iv_size)
|
||||
{
|
||||
tc::crypto::Aes192CbcEncryptor crypt;
|
||||
crypt.initialize(key, key_size, iv, iv_size);
|
||||
crypt.encrypt(dst, src, size);
|
||||
}
|
||||
|
||||
void tc::crypto::DecryptAes192Cbc(byte_t* dst, const byte_t* src, size_t size, const byte_t* key, size_t key_size, const byte_t* iv, size_t iv_size)
|
||||
{
|
||||
tc::crypto::Aes192CbcEncryptor crypt;
|
||||
crypt.initialize(key, key_size, iv, iv_size);
|
||||
crypt.decrypt(dst, src, size);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#include <tc/crypto/Aes192CtrEncryptor.h>
|
||||
|
||||
void tc::crypto::EncryptAes192Ctr(byte_t* dst, const byte_t* src, size_t size, uint64_t block_number, const byte_t* key, size_t key_size, const byte_t* iv, size_t iv_size)
|
||||
{
|
||||
tc::crypto::Aes192CtrEncryptor crypt;
|
||||
crypt.initialize(key, key_size, iv, iv_size);
|
||||
crypt.encrypt(dst, src, size, block_number);
|
||||
}
|
||||
|
||||
void tc::crypto::DecryptAes192Ctr(byte_t* dst, const byte_t* src, size_t size, uint64_t block_number, const byte_t* key, size_t key_size, const byte_t* iv, size_t iv_size)
|
||||
{
|
||||
tc::crypto::Aes192CtrEncryptor crypt;
|
||||
crypt.initialize(key, key_size, iv, iv_size);
|
||||
crypt.decrypt(dst, src, size, block_number);
|
||||
}
|
||||
|
||||
void tc::crypto::IncrementCounterAes192Ctr(byte_t* counter, uint64_t incr)
|
||||
{
|
||||
if (counter == nullptr)
|
||||
{
|
||||
throw tc::ArgumentOutOfRangeException("counter was null.");
|
||||
}
|
||||
tc::crypto::detail::incr_counter<16>(counter, incr);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#include <tc/crypto/Aes192EcbEncryptor.h>
|
||||
|
||||
void tc::crypto::EncryptAes192Ecb(byte_t* dst, const byte_t* src, size_t size, const byte_t* key, size_t key_size)
|
||||
{
|
||||
tc::crypto::Aes192EcbEncryptor crypt;
|
||||
crypt.initialize(key, key_size);
|
||||
crypt.encrypt(dst, src, size);
|
||||
}
|
||||
|
||||
void tc::crypto::DecryptAes192Ecb(byte_t* dst, const byte_t* src, size_t size, const byte_t* key, size_t key_size)
|
||||
{
|
||||
tc::crypto::Aes192EcbEncryptor crypt;
|
||||
crypt.initialize(key, key_size);
|
||||
crypt.decrypt(dst, src, size);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#include <tc/crypto/Aes256CbcEncryptor.h>
|
||||
|
||||
void tc::crypto::EncryptAes256Cbc(byte_t* dst, const byte_t* src, size_t size, const byte_t* key, size_t key_size, const byte_t* iv, size_t iv_size)
|
||||
{
|
||||
tc::crypto::Aes256CbcEncryptor crypt;
|
||||
crypt.initialize(key, key_size, iv, iv_size);
|
||||
crypt.encrypt(dst, src, size);
|
||||
}
|
||||
|
||||
void tc::crypto::DecryptAes256Cbc(byte_t* dst, const byte_t* src, size_t size, const byte_t* key, size_t key_size, const byte_t* iv, size_t iv_size)
|
||||
{
|
||||
tc::crypto::Aes256CbcEncryptor crypt;
|
||||
crypt.initialize(key, key_size, iv, iv_size);
|
||||
crypt.decrypt(dst, src, size);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#include <tc/crypto/Aes256CtrEncryptor.h>
|
||||
|
||||
void tc::crypto::EncryptAes256Ctr(byte_t* dst, const byte_t* src, size_t size, uint64_t block_number, const byte_t* key, size_t key_size, const byte_t* iv, size_t iv_size)
|
||||
{
|
||||
tc::crypto::Aes256CtrEncryptor crypt;
|
||||
crypt.initialize(key, key_size, iv, iv_size);
|
||||
crypt.encrypt(dst, src, size, block_number);
|
||||
}
|
||||
|
||||
void tc::crypto::DecryptAes256Ctr(byte_t* dst, const byte_t* src, size_t size, uint64_t block_number, const byte_t* key, size_t key_size, const byte_t* iv, size_t iv_size)
|
||||
{
|
||||
tc::crypto::Aes256CtrEncryptor crypt;
|
||||
crypt.initialize(key, key_size, iv, iv_size);
|
||||
crypt.decrypt(dst, src, size, block_number);
|
||||
}
|
||||
|
||||
void tc::crypto::IncrementCounterAes256Ctr(byte_t* counter, uint64_t incr)
|
||||
{
|
||||
if (counter == nullptr)
|
||||
{
|
||||
throw tc::ArgumentOutOfRangeException("counter was null.");
|
||||
}
|
||||
tc::crypto::detail::incr_counter<16>(counter, incr);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#include <tc/crypto/Aes256EcbEncryptor.h>
|
||||
|
||||
void tc::crypto::EncryptAes256Ecb(byte_t* dst, const byte_t* src, size_t size, const byte_t* key, size_t key_size)
|
||||
{
|
||||
tc::crypto::Aes256EcbEncryptor crypt;
|
||||
crypt.initialize(key, key_size);
|
||||
crypt.encrypt(dst, src, size);
|
||||
}
|
||||
|
||||
void tc::crypto::DecryptAes256Ecb(byte_t* dst, const byte_t* src, size_t size, const byte_t* key, size_t key_size)
|
||||
{
|
||||
tc::crypto::Aes256EcbEncryptor crypt;
|
||||
crypt.initialize(key, key_size);
|
||||
crypt.decrypt(dst, src, size);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#include <tc/crypto/Aes256XtsEncryptor.h>
|
||||
|
||||
void tc::crypto::EncryptAes256Xts(byte_t* dst, const byte_t* src, size_t size, uint64_t sector_number, const byte_t* key1, size_t key1_size, const byte_t* key2, size_t key2_size, size_t sector_size, bool tweak_word_order)
|
||||
{
|
||||
tc::crypto::Aes256XtsEncryptor crypt;
|
||||
crypt.initialize(key1, key1_size, key2, key2_size, sector_size, tweak_word_order);
|
||||
crypt.encrypt(dst, src, size, sector_number);
|
||||
}
|
||||
|
||||
void tc::crypto::DecryptAes256Xts(byte_t* dst, const byte_t* src, size_t size, uint64_t sector_number, const byte_t* key1, size_t key1_size, const byte_t* key2, size_t key2_size, size_t sector_size, bool tweak_word_order)
|
||||
{
|
||||
tc::crypto::Aes256XtsEncryptor crypt;
|
||||
crypt.initialize(key1, key1_size, key2, key2_size, sector_size, tweak_word_order);
|
||||
crypt.decrypt(dst, src, size, sector_number);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#include <tc/crypto/HmacMd5Generator.h>
|
||||
|
||||
void tc::crypto::GenerateHmacMd5Mac(byte_t* mac, const byte_t* data, size_t data_size, const byte_t* key, size_t key_size)
|
||||
{
|
||||
tc::crypto::HmacMd5Generator impl;
|
||||
impl.initialize(key, key_size);
|
||||
impl.update(data, data_size);
|
||||
impl.getMac(mac);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#include <tc/crypto/HmacSha1Generator.h>
|
||||
|
||||
void tc::crypto::GenerateHmacSha1Mac(byte_t* mac, const byte_t* data, size_t data_size, const byte_t* key, size_t key_size)
|
||||
{
|
||||
tc::crypto::HmacSha1Generator impl;
|
||||
impl.initialize(key, key_size);
|
||||
impl.update(data, data_size);
|
||||
impl.getMac(mac);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#include <tc/crypto/HmacSha256Generator.h>
|
||||
|
||||
void tc::crypto::GenerateHmacSha256Mac(byte_t* mac, const byte_t* data, size_t data_size, const byte_t* key, size_t key_size)
|
||||
{
|
||||
tc::crypto::HmacSha256Generator impl;
|
||||
impl.initialize(key, key_size);
|
||||
impl.update(data, data_size);
|
||||
impl.getMac(mac);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#include <tc/crypto/HmacSha512Generator.h>
|
||||
|
||||
void tc::crypto::GenerateHmacSha512Mac(byte_t* mac, const byte_t* data, size_t data_size, const byte_t* key, size_t key_size)
|
||||
{
|
||||
tc::crypto::HmacSha512Generator impl;
|
||||
impl.initialize(key, key_size);
|
||||
impl.update(data, data_size);
|
||||
impl.getMac(mac);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#include <tc/crypto/Md5Generator.h>
|
||||
|
||||
const std::array<byte_t, tc::crypto::Md5Generator::kAsn1OidDataSize> tc::crypto::Md5Generator::kAsn1OidData = {0x30, 0x20, 0x30, 0x0C, 0x06, 0x08, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10};
|
||||
|
||||
void tc::crypto::GenerateMd5Hash(byte_t* hash, const byte_t* data, size_t data_size)
|
||||
{
|
||||
tc::crypto::Md5Generator impl;
|
||||
impl.initialize();
|
||||
impl.update(data, data_size);
|
||||
impl.getHash(hash);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#include <tc/crypto/Pbkdf1Md5KeyDeriver.h>
|
||||
|
||||
void tc::crypto::DeriveKeyPbkdf1Md5(byte_t* key, size_t key_size, const byte_t* password, size_t password_size, const byte_t* salt, size_t salt_size, size_t n_rounds)
|
||||
{
|
||||
tc::crypto::Pbkdf1Md5KeyDeriver impl;
|
||||
impl.initialize(password, password_size, salt, salt_size, n_rounds);
|
||||
impl.getBytes(key, key_size);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#include <tc/crypto/Pbkdf1Sha1KeyDeriver.h>
|
||||
|
||||
void tc::crypto::DeriveKeyPbkdf1Sha1(byte_t* key, size_t key_size, const byte_t* password, size_t password_size, const byte_t* salt, size_t salt_size, size_t n_rounds)
|
||||
{
|
||||
tc::crypto::Pbkdf1Sha1KeyDeriver impl;
|
||||
impl.initialize(password, password_size, salt, salt_size, n_rounds);
|
||||
impl.getBytes(key, key_size);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#include <tc/crypto/Pbkdf2Sha1KeyDeriver.h>
|
||||
|
||||
void tc::crypto::DeriveKeyPbkdf2Sha1(byte_t* key, size_t key_size, const byte_t* password, size_t password_size, const byte_t* salt, size_t salt_size, size_t n_rounds)
|
||||
{
|
||||
tc::crypto::Pbkdf2Sha1KeyDeriver impl;
|
||||
impl.initialize(password, password_size, salt, salt_size, n_rounds);
|
||||
impl.getBytes(key, key_size);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#include <tc/crypto/Pbkdf2Sha256KeyDeriver.h>
|
||||
|
||||
void tc::crypto::DeriveKeyPbkdf2Sha256(byte_t* key, size_t key_size, const byte_t* password, size_t password_size, const byte_t* salt, size_t salt_size, size_t n_rounds)
|
||||
{
|
||||
tc::crypto::Pbkdf2Sha256KeyDeriver impl;
|
||||
impl.initialize(password, password_size, salt, salt_size, n_rounds);
|
||||
impl.getBytes(key, key_size);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#include <tc/crypto/Pbkdf2Sha512KeyDeriver.h>
|
||||
|
||||
void tc::crypto::DeriveKeyPbkdf2Sha512(byte_t* key, size_t key_size, const byte_t* password, size_t password_size, const byte_t* salt, size_t salt_size, size_t n_rounds)
|
||||
{
|
||||
tc::crypto::Pbkdf2Sha512KeyDeriver impl;
|
||||
impl.initialize(password, password_size, salt, salt_size, n_rounds);
|
||||
impl.getBytes(key, key_size);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#include <tc/crypto/PseudoRandomByteGenerator.h>
|
||||
|
||||
void tc::crypto::GeneratePseudoRandomBytes(byte_t* data, size_t data_size)
|
||||
{
|
||||
tc::crypto::PseudoRandomByteGenerator impl;
|
||||
impl.getBytes(data, data_size);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#include <tc/crypto/RsaKey.h>
|
||||
|
||||
tc::crypto::RsaPublicKey::RsaPublicKey(const byte_t* modulus, size_t modulus_size)
|
||||
{
|
||||
static const byte_t kPublicExponent[3] = { 0x01, 0x00, 0x01 };
|
||||
|
||||
if (modulus != nullptr && modulus_size != 0)
|
||||
{
|
||||
this->n = tc::ByteData(modulus, modulus_size);
|
||||
this->e = tc::ByteData(kPublicExponent, sizeof(kPublicExponent));
|
||||
}
|
||||
}
|
||||
|
||||
tc::crypto::RsaPrivateKey::RsaPrivateKey(const byte_t* modulus, size_t modulus_size, const byte_t* private_exponent, size_t private_exponent_size)
|
||||
{
|
||||
static const byte_t kPublicExponent[3] = { 0x01, 0x00, 0x01 };
|
||||
|
||||
if (modulus != nullptr && modulus_size != 0 && private_exponent != nullptr && private_exponent_size != 0)
|
||||
{
|
||||
this->n = tc::ByteData(modulus, modulus_size);
|
||||
this->d = tc::ByteData(private_exponent, private_exponent_size);
|
||||
this->e = tc::ByteData(kPublicExponent, sizeof(kPublicExponent));
|
||||
}
|
||||
}
|
||||
|
||||
tc::crypto::RsaKey tc::crypto::RsaPrivateKey::getPublicKey()
|
||||
{
|
||||
return RsaPublicKey(this->n.data(), this->n.size());
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#include <tc/crypto/RsaKeyGenerator.h>
|
||||
|
||||
void tc::crypto::GenerateRsaKey(RsaKey& key, size_t key_bit_size)
|
||||
{
|
||||
tc::crypto::RsaKeyGenerator impl;
|
||||
impl.generateKey(key, key_bit_size);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#include <tc/crypto/RsaOaepSha256Encryptor.h>
|
||||
|
||||
bool tc::crypto::EncryptRsa1024OaepSha256(byte_t* block, const byte_t* message, size_t message_size, const RsaKey& key, const byte_t* label, size_t label_size, bool isLabelDigested)
|
||||
{
|
||||
tc::crypto::Rsa1024OaepSha256Encryptor impl;
|
||||
impl.initialize(key, label, label_size, isLabelDigested);
|
||||
return impl.encrypt(block, message, message_size);
|
||||
}
|
||||
|
||||
bool tc::crypto::DecryptRsa1024OaepSha256(byte_t* message, size_t& message_size, size_t message_capacity, const byte_t* block, const RsaKey& key, const byte_t* label, size_t label_size, bool isLabelDigested)
|
||||
{
|
||||
tc::crypto::Rsa1024OaepSha256Encryptor impl;
|
||||
impl.initialize(key, label, label_size, isLabelDigested);
|
||||
return impl.decrypt(message, message_size, message_capacity, block);
|
||||
}
|
||||
|
||||
bool tc::crypto::EncryptRsa2048OaepSha256(byte_t* block, const byte_t* message, size_t message_size, const RsaKey& key, const byte_t* label, size_t label_size, bool isLabelDigested)
|
||||
{
|
||||
tc::crypto::Rsa2048OaepSha256Encryptor impl;
|
||||
impl.initialize(key, label, label_size, isLabelDigested);
|
||||
return impl.encrypt(block, message, message_size);
|
||||
}
|
||||
|
||||
bool tc::crypto::DecryptRsa2048OaepSha256(byte_t* message, size_t& message_size, size_t message_capacity, const byte_t* block, const RsaKey& key, const byte_t* label, size_t label_size, bool isLabelDigested)
|
||||
{
|
||||
tc::crypto::Rsa2048OaepSha256Encryptor impl;
|
||||
impl.initialize(key, label, label_size, isLabelDigested);
|
||||
return impl.decrypt(message, message_size, message_capacity, block);
|
||||
}
|
||||
|
||||
bool tc::crypto::EncryptRsa4096OaepSha256(byte_t* block, const byte_t* message, size_t message_size, const RsaKey& key, const byte_t* label, size_t label_size, bool isLabelDigested)
|
||||
{
|
||||
tc::crypto::Rsa4096OaepSha256Encryptor impl;
|
||||
impl.initialize(key, label, label_size, isLabelDigested);
|
||||
return impl.encrypt(block, message, message_size);
|
||||
}
|
||||
|
||||
bool tc::crypto::DecryptRsa4096OaepSha256(byte_t* message, size_t& message_size, size_t message_capacity, const byte_t* block, const RsaKey& key, const byte_t* label, size_t label_size, bool isLabelDigested)
|
||||
{
|
||||
tc::crypto::Rsa4096OaepSha256Encryptor impl;
|
||||
impl.initialize(key, label, label_size, isLabelDigested);
|
||||
return impl.decrypt(message, message_size, message_capacity, block);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#include <tc/crypto/RsaOaepSha512Encryptor.h>
|
||||
|
||||
bool tc::crypto::EncryptRsa2048OaepSha512(byte_t* block, const byte_t* message, size_t message_size, const RsaKey& key, const byte_t* label, size_t label_size, bool isLabelDigested)
|
||||
{
|
||||
tc::crypto::Rsa2048OaepSha512Encryptor impl;
|
||||
impl.initialize(key, label, label_size, isLabelDigested);
|
||||
return impl.encrypt(block, message, message_size);
|
||||
}
|
||||
|
||||
bool tc::crypto::DecryptRsa2048OaepSha512(byte_t* message, size_t& message_size, size_t message_capacity, const byte_t* block, const RsaKey& key, const byte_t* label, size_t label_size, bool isLabelDigested)
|
||||
{
|
||||
tc::crypto::Rsa2048OaepSha512Encryptor impl;
|
||||
impl.initialize(key, label, label_size, isLabelDigested);
|
||||
return impl.decrypt(message, message_size, message_capacity, block);
|
||||
}
|
||||
|
||||
bool tc::crypto::EncryptRsa4096OaepSha512(byte_t* block, const byte_t* message, size_t message_size, const RsaKey& key, const byte_t* label, size_t label_size, bool isLabelDigested)
|
||||
{
|
||||
tc::crypto::Rsa4096OaepSha512Encryptor impl;
|
||||
impl.initialize(key, label, label_size, isLabelDigested);
|
||||
return impl.encrypt(block, message, message_size);
|
||||
}
|
||||
|
||||
bool tc::crypto::DecryptRsa4096OaepSha512(byte_t* message, size_t& message_size, size_t message_capacity, const byte_t* block, const RsaKey& key, const byte_t* label, size_t label_size, bool isLabelDigested)
|
||||
{
|
||||
tc::crypto::Rsa4096OaepSha512Encryptor impl;
|
||||
impl.initialize(key, label, label_size, isLabelDigested);
|
||||
return impl.decrypt(message, message_size, message_capacity, block);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#include <tc/crypto/RsaPkcs1Md5Signer.h>
|
||||
|
||||
bool tc::crypto::SignRsa1024Pkcs1Md5(byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa1024Pkcs1Md5Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.sign(signature, message_digest);
|
||||
}
|
||||
|
||||
bool tc::crypto::VerifyRsa1024Pkcs1Md5(const byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa1024Pkcs1Md5Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.verify(signature, message_digest);
|
||||
}
|
||||
|
||||
bool tc::crypto::SignRsa2048Pkcs1Md5(byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa2048Pkcs1Md5Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.sign(signature, message_digest);
|
||||
}
|
||||
|
||||
bool tc::crypto::VerifyRsa2048Pkcs1Md5(const byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa2048Pkcs1Md5Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.verify(signature, message_digest);
|
||||
}
|
||||
|
||||
bool tc::crypto::SignRsa4096Pkcs1Md5(byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa4096Pkcs1Md5Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.sign(signature, message_digest);
|
||||
}
|
||||
|
||||
bool tc::crypto::VerifyRsa4096Pkcs1Md5(const byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa4096Pkcs1Md5Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.verify(signature, message_digest);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#include <tc/crypto/RsaPkcs1Sha1Signer.h>
|
||||
|
||||
bool tc::crypto::SignRsa1024Pkcs1Sha1(byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa1024Pkcs1Sha1Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.sign(signature, message_digest);
|
||||
}
|
||||
|
||||
bool tc::crypto::VerifyRsa1024Pkcs1Sha1(const byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa1024Pkcs1Sha1Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.verify(signature, message_digest);
|
||||
}
|
||||
|
||||
bool tc::crypto::SignRsa2048Pkcs1Sha1(byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa2048Pkcs1Sha1Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.sign(signature, message_digest);
|
||||
}
|
||||
|
||||
bool tc::crypto::VerifyRsa2048Pkcs1Sha1(const byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa2048Pkcs1Sha1Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.verify(signature, message_digest);
|
||||
}
|
||||
|
||||
bool tc::crypto::SignRsa4096Pkcs1Sha1(byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa4096Pkcs1Sha1Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.sign(signature, message_digest);
|
||||
}
|
||||
|
||||
bool tc::crypto::VerifyRsa4096Pkcs1Sha1(const byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa4096Pkcs1Sha1Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.verify(signature, message_digest);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#include <tc/crypto/RsaPkcs1Sha256Signer.h>
|
||||
|
||||
bool tc::crypto::SignRsa1024Pkcs1Sha256(byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa1024Pkcs1Sha256Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.sign(signature, message_digest);
|
||||
}
|
||||
|
||||
bool tc::crypto::VerifyRsa1024Pkcs1Sha256(const byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa1024Pkcs1Sha256Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.verify(signature, message_digest);
|
||||
}
|
||||
|
||||
bool tc::crypto::SignRsa2048Pkcs1Sha256(byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa2048Pkcs1Sha256Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.sign(signature, message_digest);
|
||||
}
|
||||
|
||||
bool tc::crypto::VerifyRsa2048Pkcs1Sha256(const byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa2048Pkcs1Sha256Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.verify(signature, message_digest);
|
||||
}
|
||||
|
||||
bool tc::crypto::SignRsa4096Pkcs1Sha256(byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa4096Pkcs1Sha256Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.sign(signature, message_digest);
|
||||
}
|
||||
|
||||
bool tc::crypto::VerifyRsa4096Pkcs1Sha256(const byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa4096Pkcs1Sha256Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.verify(signature, message_digest);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#include <tc/crypto/RsaPkcs1Sha512Signer.h>
|
||||
|
||||
bool tc::crypto::SignRsa1024Pkcs1Sha512(byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa1024Pkcs1Sha512Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.sign(signature, message_digest);
|
||||
}
|
||||
|
||||
bool tc::crypto::VerifyRsa1024Pkcs1Sha512(const byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa1024Pkcs1Sha512Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.verify(signature, message_digest);
|
||||
}
|
||||
|
||||
bool tc::crypto::SignRsa2048Pkcs1Sha512(byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa2048Pkcs1Sha512Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.sign(signature, message_digest);
|
||||
}
|
||||
|
||||
bool tc::crypto::VerifyRsa2048Pkcs1Sha512(const byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa2048Pkcs1Sha512Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.verify(signature, message_digest);
|
||||
}
|
||||
|
||||
bool tc::crypto::SignRsa4096Pkcs1Sha512(byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa4096Pkcs1Sha512Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.sign(signature, message_digest);
|
||||
}
|
||||
|
||||
bool tc::crypto::VerifyRsa4096Pkcs1Sha512(const byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa4096Pkcs1Sha512Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.verify(signature, message_digest);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#include <tc/crypto/RsaPssSha256Signer.h>
|
||||
|
||||
bool tc::crypto::SignRsa1024PssSha256(byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa1024PssSha256Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.sign(signature, message_digest);
|
||||
}
|
||||
|
||||
bool tc::crypto::VerifyRsa1024PssSha256(const byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa1024PssSha256Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.verify(signature, message_digest);
|
||||
}
|
||||
|
||||
bool tc::crypto::SignRsa2048PssSha256(byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa2048PssSha256Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.sign(signature, message_digest);
|
||||
}
|
||||
|
||||
bool tc::crypto::VerifyRsa2048PssSha256(const byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa2048PssSha256Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.verify(signature, message_digest);
|
||||
}
|
||||
|
||||
bool tc::crypto::SignRsa4096PssSha256(byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa4096PssSha256Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.sign(signature, message_digest);
|
||||
}
|
||||
|
||||
bool tc::crypto::VerifyRsa4096PssSha256(const byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa4096PssSha256Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.verify(signature, message_digest);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#include <tc/crypto/RsaPssSha512Signer.h>
|
||||
|
||||
bool tc::crypto::SignRsa1024PssSha512(byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa1024PssSha512Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.sign(signature, message_digest);
|
||||
}
|
||||
|
||||
bool tc::crypto::VerifyRsa1024PssSha512(const byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa1024PssSha512Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.verify(signature, message_digest);
|
||||
}
|
||||
|
||||
bool tc::crypto::SignRsa2048PssSha512(byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa2048PssSha512Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.sign(signature, message_digest);
|
||||
}
|
||||
|
||||
bool tc::crypto::VerifyRsa2048PssSha512(const byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa2048PssSha512Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.verify(signature, message_digest);
|
||||
}
|
||||
|
||||
bool tc::crypto::SignRsa4096PssSha512(byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa4096PssSha512Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.sign(signature, message_digest);
|
||||
}
|
||||
|
||||
bool tc::crypto::VerifyRsa4096PssSha512(const byte_t* signature, const byte_t* message_digest, const RsaKey& key)
|
||||
{
|
||||
tc::crypto::Rsa4096PssSha512Signer impl;
|
||||
impl.initialize(key);
|
||||
return impl.verify(signature, message_digest);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#include <tc/crypto/Sha1Generator.h>
|
||||
|
||||
const std::array<byte_t, tc::crypto::Sha1Generator::kAsn1OidDataSize> tc::crypto::Sha1Generator::kAsn1OidData = {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00, 0x04, 0x14};
|
||||
|
||||
|
||||
void tc::crypto::GenerateSha1Hash(byte_t* hash, const byte_t* data, size_t data_size)
|
||||
{
|
||||
tc::crypto::Sha1Generator impl;
|
||||
impl.initialize();
|
||||
impl.update(data, data_size);
|
||||
impl.getHash(hash);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#include <tc/crypto/Sha256Generator.h>
|
||||
|
||||
const std::array<byte_t, tc::crypto::Sha256Generator::kAsn1OidDataSize> tc::crypto::Sha256Generator::kAsn1OidData = {0x30, 0x31, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20};
|
||||
|
||||
void tc::crypto::GenerateSha256Hash(byte_t* hash, const byte_t* data, size_t data_size)
|
||||
{
|
||||
tc::crypto::Sha256Generator impl;
|
||||
impl.initialize();
|
||||
impl.update(data, data_size);
|
||||
impl.getHash(hash);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#include <tc/crypto/Sha512Generator.h>
|
||||
|
||||
const std::array<byte_t, tc::crypto::Sha512Generator::kAsn1OidDataSize> tc::crypto::Sha512Generator::kAsn1OidData = {0x30, 0x51, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40};
|
||||
|
||||
void tc::crypto::GenerateSha512Hash(byte_t* hash, const byte_t* data, size_t data_size)
|
||||
{
|
||||
tc::crypto::Sha512Generator impl;
|
||||
impl.initialize();
|
||||
impl.update(data, data_size);
|
||||
impl.getHash(hash);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#include <tc/crypto/detail/AesImpl.h>
|
||||
#include <mbedtls/aes.h>
|
||||
|
||||
struct tc::crypto::detail::AesImpl::ImplCtx
|
||||
{
|
||||
mbedtls_aes_context mEncContext;
|
||||
mbedtls_aes_context mDecContext;
|
||||
};
|
||||
|
||||
tc::crypto::detail::AesImpl::AesImpl() :
|
||||
mState(State::None),
|
||||
mImplCtx(new ImplCtx())
|
||||
{
|
||||
mbedtls_aes_init(&(mImplCtx->mEncContext));
|
||||
mbedtls_aes_init(&(mImplCtx->mDecContext));
|
||||
}
|
||||
|
||||
tc::crypto::detail::AesImpl::~AesImpl()
|
||||
{
|
||||
mbedtls_aes_free(&(mImplCtx->mEncContext));
|
||||
mbedtls_aes_free(&(mImplCtx->mDecContext));
|
||||
}
|
||||
|
||||
void tc::crypto::detail::AesImpl::initialize(const byte_t* key, size_t key_size)
|
||||
{
|
||||
if (key == nullptr) { throw tc::ArgumentNullException("AesImpl::initialize()", "key was null."); }
|
||||
if (key_size != 16 && key_size != 24 && key_size != 32) { throw tc::ArgumentOutOfRangeException("AesImpl::initialize()", "key_size did not equal 16, 24 or 32."); }
|
||||
|
||||
mbedtls_aes_setkey_enc(&(mImplCtx->mEncContext), key, uint32_t(key_size) * 8);
|
||||
mbedtls_aes_setkey_dec(&(mImplCtx->mDecContext), key, uint32_t(key_size) * 8);
|
||||
|
||||
mState = State::Initialized;
|
||||
}
|
||||
|
||||
void tc::crypto::detail::AesImpl::encrypt(byte_t* dst, const byte_t* src)
|
||||
{
|
||||
if (mState != State::Initialized) { return; }
|
||||
if (dst == nullptr) { throw tc::ArgumentNullException("AesImpl::encrypt()", "dst was null."); }
|
||||
if (src == nullptr) { throw tc::ArgumentNullException("AesImpl::encrypt()", "src was null."); }
|
||||
|
||||
mbedtls_aes_crypt_ecb(&(mImplCtx->mEncContext), MBEDTLS_AES_ENCRYPT, src, dst);
|
||||
}
|
||||
|
||||
void tc::crypto::detail::AesImpl::decrypt(byte_t* dst, const byte_t* src)
|
||||
{
|
||||
if (mState != State::Initialized) { return; }
|
||||
if (dst == nullptr) { throw tc::ArgumentNullException("AesImpl::decrypt()", "dst was null."); }
|
||||
if (src == nullptr) { throw tc::ArgumentNullException("AesImpl::decrypt()", "src was null."); }
|
||||
|
||||
mbedtls_aes_crypt_ecb(&(mImplCtx->mDecContext), MBEDTLS_AES_DECRYPT, src, dst);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#include <tc/crypto/detail/Md5Impl.h>
|
||||
#include <mbedtls/md.h>
|
||||
|
||||
struct tc::crypto::detail::Md5Impl::ImplCtx
|
||||
{
|
||||
mbedtls_md_context_t mMdContext;
|
||||
};
|
||||
|
||||
tc::crypto::detail::Md5Impl::Md5Impl() :
|
||||
mState(State::None),
|
||||
mImplCtx(new ImplCtx())
|
||||
{
|
||||
mbedtls_md_init(&(mImplCtx->mMdContext));
|
||||
mbedtls_md_setup(&(mImplCtx->mMdContext), mbedtls_md_info_from_type(MBEDTLS_MD_MD5), 0);
|
||||
}
|
||||
|
||||
tc::crypto::detail::Md5Impl::~Md5Impl()
|
||||
{
|
||||
mbedtls_md_free(&(mImplCtx->mMdContext));
|
||||
}
|
||||
|
||||
void tc::crypto::detail::Md5Impl::initialize()
|
||||
{
|
||||
mbedtls_md_starts(&(mImplCtx->mMdContext));
|
||||
mState = State::Initialized;
|
||||
}
|
||||
|
||||
void tc::crypto::detail::Md5Impl::update(const byte_t* src, size_t src_size)
|
||||
{
|
||||
mbedtls_md_update(&(mImplCtx->mMdContext), src, src_size);
|
||||
}
|
||||
|
||||
void tc::crypto::detail::Md5Impl::getHash(byte_t* hash)
|
||||
{
|
||||
if (mState == State::Initialized)
|
||||
{
|
||||
mbedtls_md_finish(&(mImplCtx->mMdContext), mHash.data());
|
||||
mState = State::Done;
|
||||
}
|
||||
if (mState == State::Done)
|
||||
{
|
||||
memcpy(hash, mHash.data(), mHash.size());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
#include <tc/crypto/detail/PrbgImpl.h>
|
||||
#include <mbedtls/entropy.h>
|
||||
#include <mbedtls/ctr_drbg.h>
|
||||
|
||||
const std::string tc::crypto::detail::PrbgImpl::kClassName = "tc::crypto::detail::PrbgImpl";
|
||||
|
||||
struct tc::crypto::detail::PrbgImpl::ImplCtx
|
||||
{
|
||||
mbedtls_ctr_drbg_context ctr_drbg;
|
||||
mbedtls_entropy_context entropy;
|
||||
};
|
||||
|
||||
tc::crypto::detail::PrbgImpl::PrbgImpl() :
|
||||
mImplCtx(new ImplCtx())
|
||||
{
|
||||
mbedtls_ctr_drbg_init(&(mImplCtx->ctr_drbg));
|
||||
mbedtls_entropy_init(&(mImplCtx->entropy));
|
||||
int ret = mbedtls_ctr_drbg_seed(&(mImplCtx->ctr_drbg), mbedtls_entropy_func, &(mImplCtx->entropy), (const unsigned char *)kClassName.c_str(), kClassName.size());
|
||||
switch (ret)
|
||||
{
|
||||
case (0):
|
||||
break;
|
||||
case (MBEDTLS_ERR_ENTROPY_SOURCE_FAILED):
|
||||
throw tc::crypto::CryptoException(kClassName, "Entropy source failed");
|
||||
default:
|
||||
throw tc::crypto::CryptoException(kClassName, "An unexpected error occurred");
|
||||
}
|
||||
}
|
||||
|
||||
tc::crypto::detail::PrbgImpl::~PrbgImpl()
|
||||
{
|
||||
mbedtls_ctr_drbg_free(&(mImplCtx->ctr_drbg));
|
||||
mbedtls_entropy_free(&(mImplCtx->entropy));
|
||||
}
|
||||
|
||||
void tc::crypto::detail::PrbgImpl::getBytes(byte_t* data, size_t data_size)
|
||||
{
|
||||
int ret = mbedtls_ctr_drbg_random(&(mImplCtx->ctr_drbg), data, data_size);
|
||||
switch (ret)
|
||||
{
|
||||
case (0):
|
||||
break;
|
||||
case (MBEDTLS_ERR_CTR_DRBG_REQUEST_TOO_BIG):
|
||||
throw tc::crypto::CryptoException(kClassName, "Request too big");
|
||||
case (MBEDTLS_ERR_ENTROPY_SOURCE_FAILED):
|
||||
throw tc::crypto::CryptoException(kClassName, "Entropy source failed");
|
||||
default:
|
||||
throw tc::crypto::CryptoException(kClassName, "An unexpected error occurred");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
#include <tc/crypto/detail/RsaImpl.h>
|
||||
#include <mbedtls/rsa.h>
|
||||
|
||||
struct tc::crypto::detail::RsaImpl::ImplCtx
|
||||
{
|
||||
mbedtls_rsa_context mContext;
|
||||
};
|
||||
|
||||
tc::crypto::detail::RsaImpl::RsaImpl() :
|
||||
mState(State::None),
|
||||
mImplCtx(new ImplCtx())
|
||||
{
|
||||
mbedtls_rsa_init(&(mImplCtx->mContext), MBEDTLS_RSA_PKCS_V15, 0);
|
||||
}
|
||||
|
||||
tc::crypto::detail::RsaImpl::~RsaImpl()
|
||||
{
|
||||
mbedtls_rsa_free(&(mImplCtx->mContext));
|
||||
}
|
||||
|
||||
void tc::crypto::detail::RsaImpl::initialize(size_t key_bit_size, const byte_t* n, size_t n_size, const byte_t* p, size_t p_size, const byte_t* q, size_t q_size, const byte_t* d, size_t d_size, const byte_t* e, size_t e_size)
|
||||
{
|
||||
if ((key_bit_size % 8) != 0) { throw tc::ArgumentOutOfRangeException("RsaImpl::initialize()", "key_bit_size was not a multiple of 8 bits."); }
|
||||
if (n == nullptr && n_size != 0) { throw tc::ArgumentNullException("RsaImpl::initialize()", "n was null when n_size was not 0."); }
|
||||
if (p == nullptr && p_size != 0) { throw tc::ArgumentNullException("RsaImpl::initialize()", "p was null when p_size was not 0."); }
|
||||
if (q == nullptr && q_size != 0) { throw tc::ArgumentNullException("RsaImpl::initialize()", "q was null when q_size was not 0."); }
|
||||
if (d == nullptr && d_size != 0) { throw tc::ArgumentNullException("RsaImpl::initialize()", "d was null when d_size was not 0."); }
|
||||
if (e == nullptr && e_size != 0) { throw tc::ArgumentNullException("RsaImpl::initialize()", "e was null when e_size was not 0."); }
|
||||
if (n != nullptr && n_size == 0) { throw tc::ArgumentNullException("RsaImpl::initialize()", "n was not null but n_size was 0."); }
|
||||
if (p != nullptr && p_size == 0) { throw tc::ArgumentNullException("RsaImpl::initialize()", "p was not null but p_size was 0."); }
|
||||
if (q != nullptr && q_size == 0) { throw tc::ArgumentNullException("RsaImpl::initialize()", "q was not null but q_size was 0."); }
|
||||
if (d != nullptr && d_size == 0) { throw tc::ArgumentNullException("RsaImpl::initialize()", "d was not null but d_size was 0."); }
|
||||
if (e != nullptr && e_size == 0) { throw tc::ArgumentNullException("RsaImpl::initialize()", "e was not null but e_size was 0."); }
|
||||
if (n_size > 0 && n_size != (key_bit_size/8)) { throw tc::ArgumentNullException("RsaImpl::initialize()", "n_size was non-zero but not expected size."); }
|
||||
if (p_size > 0 && p_size != (key_bit_size/8)/2) { throw tc::ArgumentNullException("RsaImpl::initialize()", "p_size was non-zero but not expected size."); }
|
||||
if (q_size > 0 && q_size != (key_bit_size/8)/2) { throw tc::ArgumentNullException("RsaImpl::initialize()", "q_size was non-zero but not expected size."); }
|
||||
if (d_size > 0 && d_size != (key_bit_size/8)) { throw tc::ArgumentNullException("RsaImpl::initialize()", "d_size was non-zero but not expected size."); }
|
||||
if (e_size > 0 && e_size != 3 && e_size != 4) { throw tc::ArgumentNullException("RsaImpl::initialize()", "e_size was non-zero but not expected size."); }
|
||||
|
||||
mImplCtx->mContext.len = key_bit_size / 8;
|
||||
|
||||
int ret = mbedtls_rsa_import_raw(&(mImplCtx->mContext), n, n_size, p, p_size, q, q_size, d, d_size, e, e_size);
|
||||
|
||||
// TODO: Confirm these error codes
|
||||
if (ret != 0)
|
||||
{
|
||||
if (ret < MBEDTLS_ERR_RSA_BAD_INPUT_DATA) { throw tc::crypto::CryptoException("RsaImpl::initialize()", "Bad input parameters to function."); }
|
||||
else { throw tc::crypto::CryptoException("RsaImpl::initialize()", "An unexpected error occurred."); }
|
||||
}
|
||||
|
||||
mState = State::Initialized;
|
||||
}
|
||||
|
||||
void tc::crypto::detail::RsaImpl::publicTransform(byte_t* dst, const byte_t* src)
|
||||
{
|
||||
if (mState != State::Initialized) { return; }
|
||||
if (dst == nullptr) { throw tc::ArgumentNullException("RsaImpl::publicTransform()", "dst was null."); }
|
||||
if (src == nullptr) { throw tc::ArgumentNullException("RsaImpl::publicTransform()", "src was null."); }
|
||||
|
||||
int ret = mbedtls_rsa_public(&(mImplCtx->mContext), src, dst);
|
||||
|
||||
switch (ret)
|
||||
{
|
||||
case (0):
|
||||
break;
|
||||
case (MBEDTLS_ERR_RSA_BAD_INPUT_DATA):
|
||||
throw tc::crypto::CryptoException("RsaImpl::publicTransform()", "Bad input parameters to function.");
|
||||
break;
|
||||
case (MBEDTLS_ERR_RSA_INVALID_PADDING):
|
||||
throw tc::crypto::CryptoException("RsaImpl::publicTransform()", "Input data contains invalid padding and is rejected.");
|
||||
break;
|
||||
case (MBEDTLS_ERR_RSA_KEY_GEN_FAILED):
|
||||
throw tc::crypto::CryptoException("RsaImpl::publicTransform()", "Something failed during generation of a key.");
|
||||
break;
|
||||
case (MBEDTLS_ERR_RSA_KEY_CHECK_FAILED):
|
||||
throw tc::crypto::CryptoException("RsaImpl::publicTransform()", "Key failed to pass the validity check of the library.");
|
||||
break;
|
||||
case (MBEDTLS_ERR_RSA_PUBLIC_FAILED):
|
||||
throw tc::crypto::CryptoException("RsaImpl::publicTransform()", "The public key operation failed.");
|
||||
break;
|
||||
case (MBEDTLS_ERR_RSA_PRIVATE_FAILED):
|
||||
throw tc::crypto::CryptoException("RsaImpl::publicTransform()", "The private key operation failed.");
|
||||
break;
|
||||
case (MBEDTLS_ERR_RSA_VERIFY_FAILED):
|
||||
throw tc::crypto::CryptoException("RsaImpl::publicTransform()", "The PKCS#1 verification failed.");
|
||||
break;
|
||||
case (MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE):
|
||||
throw tc::crypto::CryptoException("RsaImpl::publicTransform()", "The output buffer for decryption is not large enough.");
|
||||
break;
|
||||
case (MBEDTLS_ERR_RSA_RNG_FAILED):
|
||||
throw tc::crypto::CryptoException("RsaImpl::publicTransform()", "The random generator failed to generate non-zeros.");
|
||||
break;
|
||||
default:
|
||||
throw tc::crypto::CryptoException("RsaImpl::publicTransform()", "An unexpected error occurred.");
|
||||
}
|
||||
}
|
||||
|
||||
void tc::crypto::detail::RsaImpl::privateTransform(byte_t* dst, const byte_t* src)
|
||||
{
|
||||
if (mState != State::Initialized) { return; }
|
||||
if (dst == nullptr) { throw tc::ArgumentNullException("RsaImpl::privateTransform()", "dst was null."); }
|
||||
if (src == nullptr) { throw tc::ArgumentNullException("RsaImpl::privateTransform()", "src was null."); }
|
||||
|
||||
int ret = mbedtls_rsa_private(&(mImplCtx->mContext), nullptr, nullptr, src, dst);
|
||||
|
||||
switch (ret)
|
||||
{
|
||||
case (0):
|
||||
break;
|
||||
case (MBEDTLS_ERR_RSA_BAD_INPUT_DATA):
|
||||
throw tc::crypto::CryptoException("RsaImpl::privateTransform()", "Bad input parameters to function.");
|
||||
break;
|
||||
case (MBEDTLS_ERR_RSA_INVALID_PADDING):
|
||||
throw tc::crypto::CryptoException("RsaImpl::privateTransform()", "Input data contains invalid padding and is rejected.");
|
||||
break;
|
||||
case (MBEDTLS_ERR_RSA_KEY_GEN_FAILED):
|
||||
throw tc::crypto::CryptoException("RsaImpl::privateTransform()", "Something failed during generation of a key.");
|
||||
break;
|
||||
case (MBEDTLS_ERR_RSA_KEY_CHECK_FAILED):
|
||||
throw tc::crypto::CryptoException("RsaImpl::privateTransform()", "Key failed to pass the validity check of the library.");
|
||||
break;
|
||||
case (MBEDTLS_ERR_RSA_PUBLIC_FAILED):
|
||||
throw tc::crypto::CryptoException("RsaImpl::privateTransform()", "The public key operation failed.");
|
||||
break;
|
||||
case (MBEDTLS_ERR_RSA_PRIVATE_FAILED):
|
||||
throw tc::crypto::CryptoException("RsaImpl::privateTransform()", "The private key operation failed.");
|
||||
break;
|
||||
case (MBEDTLS_ERR_RSA_VERIFY_FAILED):
|
||||
throw tc::crypto::CryptoException("RsaImpl::privateTransform()", "The PKCS#1 verification failed.");
|
||||
break;
|
||||
case (MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE):
|
||||
throw tc::crypto::CryptoException("RsaImpl::privateTransform()", "The output buffer for decryption is not large enough.");
|
||||
break;
|
||||
case (MBEDTLS_ERR_RSA_RNG_FAILED):
|
||||
throw tc::crypto::CryptoException("RsaImpl::privateTransform()", "The random generator failed to generate non-zeros.");
|
||||
break;
|
||||
default:
|
||||
throw tc::crypto::CryptoException("RsaImpl::privateTransform()", "An unexpected error occurred.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
#include <tc/crypto/detail/RsaKeyGeneratorImpl.h>
|
||||
#include <mbedtls/rsa.h>
|
||||
#include <mbedtls/ctr_drbg.h>
|
||||
#include <mbedtls/entropy.h>
|
||||
|
||||
const std::string tc::crypto::detail::RsaKeyGeneratorImpl::kClassName = "tc::crypto::detail::RsaKeyGeneratorImpl";
|
||||
|
||||
struct tc::crypto::detail::RsaKeyGeneratorImpl::ImplCtx
|
||||
{
|
||||
mbedtls_rsa_context rsa;
|
||||
mbedtls_ctr_drbg_context ctr_drbg;
|
||||
mbedtls_entropy_context entropy;
|
||||
};
|
||||
|
||||
tc::crypto::detail::RsaKeyGeneratorImpl::RsaKeyGeneratorImpl() :
|
||||
mImplCtx(new ImplCtx())
|
||||
{
|
||||
mbedtls_entropy_init( &(mImplCtx->entropy) );
|
||||
mbedtls_ctr_drbg_init( &(mImplCtx->ctr_drbg) );
|
||||
mbedtls_rsa_init( &(mImplCtx->rsa), 0, 0 );
|
||||
|
||||
int ret = mbedtls_ctr_drbg_seed(&(mImplCtx->ctr_drbg), mbedtls_entropy_func, &(mImplCtx->entropy), (const unsigned char *)kClassName.c_str(), kClassName.size());
|
||||
switch (ret)
|
||||
{
|
||||
case (0):
|
||||
break;
|
||||
case (MBEDTLS_ERR_ENTROPY_SOURCE_FAILED):
|
||||
throw tc::crypto::CryptoException(kClassName, "mbedtls_ctr_drbg_seed() Entropy source failed");
|
||||
default:
|
||||
throw tc::crypto::CryptoException(kClassName, "mbedtls_ctr_drbg_seed() An unexpected error occurred");
|
||||
}
|
||||
}
|
||||
|
||||
tc::crypto::detail::RsaKeyGeneratorImpl::~RsaKeyGeneratorImpl()
|
||||
{
|
||||
mbedtls_rsa_free( &(mImplCtx->rsa) );
|
||||
mbedtls_ctr_drbg_free( &(mImplCtx->ctr_drbg) );
|
||||
mbedtls_entropy_free( &(mImplCtx->entropy) );
|
||||
}
|
||||
|
||||
void tc::crypto::detail::RsaKeyGeneratorImpl::generateKey(size_t key_bit_size, byte_t* n, size_t n_size, byte_t* p, size_t p_size, byte_t* q, size_t q_size, byte_t* d, size_t d_size, byte_t* e, size_t e_size)
|
||||
{
|
||||
if ((key_bit_size % 8) != 0) { throw tc::ArgumentOutOfRangeException(kClassName, "key_bit_size was not a multiple of 8 bits."); }
|
||||
if (n != nullptr && n_size < (key_bit_size/8)) { throw tc::ArgumentNullException(kClassName, "n was not null, but n_size was not large enough"); }
|
||||
if (p != nullptr && p_size < (key_bit_size/8)/2) { throw tc::ArgumentNullException(kClassName, "p was not null, but p_size was not large enough"); }
|
||||
if (q != nullptr && q_size < (key_bit_size/8)/2) { throw tc::ArgumentNullException(kClassName, "q was not null, but q_size was not large enough"); }
|
||||
if (d != nullptr && d_size < (key_bit_size/8)) { throw tc::ArgumentNullException(kClassName, "d was not null, but d_size was not large enough"); }
|
||||
if (e != nullptr && e_size < 3) { throw tc::ArgumentNullException(kClassName, "e was not null, but e_size was not large enough"); }
|
||||
|
||||
int ret = 1;
|
||||
|
||||
// generate key
|
||||
ret = mbedtls_rsa_gen_key(&(mImplCtx->rsa), mbedtls_ctr_drbg_random, &(mImplCtx->ctr_drbg), uint32_t(key_bit_size), 0x10001);
|
||||
switch (ret)
|
||||
{
|
||||
case (0):
|
||||
break;
|
||||
case (MBEDTLS_ERR_RSA_KEY_GEN_FAILED):
|
||||
throw tc::crypto::CryptoException(kClassName, "mbedtls_rsa_gen_key() Something failed during generation of a key.");
|
||||
case (MBEDTLS_ERR_RSA_RNG_FAILED):
|
||||
throw tc::crypto::CryptoException(kClassName, "mbedtls_rsa_gen_key() The random generator failed to generate non-zeros.");
|
||||
default:
|
||||
throw tc::crypto::CryptoException(kClassName, "mbedtls_rsa_gen_key() An unexpected error occurred.");
|
||||
}
|
||||
|
||||
// export key from mbedtls context
|
||||
ret = mbedtls_rsa_export_raw(&(mImplCtx->rsa), \
|
||||
n, n_size, \
|
||||
p, p_size, \
|
||||
q, q_size, \
|
||||
d, d_size, \
|
||||
e, e_size \
|
||||
);
|
||||
|
||||
switch (ret)
|
||||
{
|
||||
case (0):
|
||||
break;
|
||||
default:
|
||||
throw tc::crypto::CryptoException(kClassName, "mbedtls_rsa_export_raw() An unexpected error occurred.");
|
||||
}
|
||||
|
||||
// clear key from mbedtls context
|
||||
mbedtls_rsa_init( &(mImplCtx->rsa), 0, 0 );
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#include <tc/crypto/detail/Sha1Impl.h>
|
||||
#include <mbedtls/md.h>
|
||||
|
||||
struct tc::crypto::detail::Sha1Impl::ImplCtx
|
||||
{
|
||||
mbedtls_md_context_t mMdContext;
|
||||
};
|
||||
|
||||
tc::crypto::detail::Sha1Impl::Sha1Impl() :
|
||||
mState(State::None),
|
||||
mImplCtx(new ImplCtx())
|
||||
{
|
||||
mbedtls_md_init(&(mImplCtx->mMdContext));
|
||||
mbedtls_md_setup(&(mImplCtx->mMdContext), mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), 0);
|
||||
}
|
||||
|
||||
tc::crypto::detail::Sha1Impl::~Sha1Impl()
|
||||
{
|
||||
mbedtls_md_free(&(mImplCtx->mMdContext));
|
||||
}
|
||||
|
||||
void tc::crypto::detail::Sha1Impl::initialize()
|
||||
{
|
||||
mbedtls_md_starts(&(mImplCtx->mMdContext));
|
||||
mState = State::Initialized;
|
||||
}
|
||||
|
||||
void tc::crypto::detail::Sha1Impl::update(const byte_t* src, size_t src_size)
|
||||
{
|
||||
mbedtls_md_update(&(mImplCtx->mMdContext), src, src_size);
|
||||
}
|
||||
|
||||
void tc::crypto::detail::Sha1Impl::getHash(byte_t* hash)
|
||||
{
|
||||
if (mState == State::Initialized)
|
||||
{
|
||||
mbedtls_md_finish(&(mImplCtx->mMdContext), mHash.data());
|
||||
mState = State::Done;
|
||||
}
|
||||
if (mState == State::Done)
|
||||
{
|
||||
memcpy(hash, mHash.data(), mHash.size());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
#include <tc/crypto/detail/Sha2Impl.h>
|
||||
#include <mbedtls/md.h>
|
||||
|
||||
struct tc::crypto::detail::Sha2Impl::ImplCtx
|
||||
{
|
||||
mbedtls_md_context_t mMdContext;
|
||||
};
|
||||
|
||||
tc::crypto::detail::Sha2Impl::Sha2Impl(SHA2BitSize algo) :
|
||||
mState(State::None),
|
||||
mHashSize(0),
|
||||
mImplCtx(new ImplCtx())
|
||||
{
|
||||
mbedtls_md_init(&(mImplCtx->mMdContext));
|
||||
switch(algo)
|
||||
{
|
||||
case (SHA2BitSize_256):
|
||||
mHashSize = kSha2_256_HashSize;
|
||||
mbedtls_md_setup(&(mImplCtx->mMdContext), mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 0);
|
||||
break;
|
||||
case (SHA2BitSize_512):
|
||||
mHashSize = kSha2_512_HashSize;
|
||||
mbedtls_md_setup(&(mImplCtx->mMdContext), mbedtls_md_info_from_type(MBEDTLS_MD_SHA512), 0);
|
||||
break;
|
||||
default:
|
||||
throw tc::crypto::CryptoException("tc::crypto::detail::Sha2Impl", "Invalid value for SHA2BitSize");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
tc::crypto::detail::Sha2Impl::~Sha2Impl()
|
||||
{
|
||||
mbedtls_md_free(&(mImplCtx->mMdContext));
|
||||
}
|
||||
|
||||
void tc::crypto::detail::Sha2Impl::initialize()
|
||||
{
|
||||
mbedtls_md_starts(&(mImplCtx->mMdContext));
|
||||
mState = State::Initialized;
|
||||
}
|
||||
|
||||
void tc::crypto::detail::Sha2Impl::update(const byte_t* src, size_t src_size)
|
||||
{
|
||||
mbedtls_md_update(&(mImplCtx->mMdContext), src, src_size);
|
||||
}
|
||||
|
||||
void tc::crypto::detail::Sha2Impl::getHash(byte_t* hash)
|
||||
{
|
||||
if (mState == State::Initialized)
|
||||
{
|
||||
mbedtls_md_finish(&(mImplCtx->mMdContext), mHash.data());
|
||||
mState = State::Done;
|
||||
}
|
||||
if (mState == State::Done)
|
||||
{
|
||||
memcpy(hash, mHash.data(), mHashSize);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
#include <tc/io/BasicPathResolver.h>
|
||||
|
||||
const std::string tc::io::BasicPathResolver::kClassName = "tc::io::BasicPathResolver";
|
||||
|
||||
tc::io::BasicPathResolver::BasicPathResolver() :
|
||||
BasicPathResolver(tc::io::Path("/"), {})
|
||||
{}
|
||||
|
||||
tc::io::BasicPathResolver::BasicPathResolver(const tc::io::Path& current_directory_path) :
|
||||
BasicPathResolver(current_directory_path, {})
|
||||
{}
|
||||
|
||||
tc::io::BasicPathResolver::BasicPathResolver(const tc::io::Path& current_directory_path, const std::vector<std::string>& root_names) :
|
||||
mCurrentDirPath(),
|
||||
mExplicitRootLabels(root_names)
|
||||
{
|
||||
setCurrentDirectory(current_directory_path);
|
||||
}
|
||||
|
||||
|
||||
void tc::io::BasicPathResolver::setCurrentDirectory(const tc::io::Path& path)
|
||||
{
|
||||
if (path.empty())
|
||||
{
|
||||
throw tc::ArgumentOutOfRangeException(kClassName, "path was empty.");
|
||||
}
|
||||
|
||||
mCurrentDirPath = path;
|
||||
}
|
||||
|
||||
const tc::io::Path& tc::io::BasicPathResolver::getCurrentDirectory() const
|
||||
{
|
||||
return mCurrentDirPath;
|
||||
}
|
||||
|
||||
void tc::io::BasicPathResolver::setExplicitRootLabels(const std::vector<std::string>& root_labels)
|
||||
{
|
||||
mExplicitRootLabels = root_labels;
|
||||
}
|
||||
|
||||
const std::vector<std::string>& tc::io::BasicPathResolver::getExplicitRootLabels() const
|
||||
{
|
||||
return mExplicitRootLabels;
|
||||
}
|
||||
|
||||
void tc::io::BasicPathResolver::resolveCanonicalPath(const tc::io::Path& path, tc::io::Path& canonical_path) const
|
||||
{
|
||||
canonical_path = resolveCanonicalPath(path);
|
||||
}
|
||||
|
||||
tc::io::Path tc::io::BasicPathResolver::resolveCanonicalPath(const tc::io::Path& path) const
|
||||
{
|
||||
// create output path
|
||||
tc::io::Path canonical_path;
|
||||
|
||||
// get iterator for input path
|
||||
auto path_itr = path.begin();
|
||||
|
||||
// if the begining of the path exists and is a valid root label, then the input path is an absolute (but not necessarily canonical) path
|
||||
if (path_itr != path.end() && (std::find(mExplicitRootLabels.begin(), mExplicitRootLabels.end(), *path_itr) != mExplicitRootLabels.end() || *path_itr == mCurrentDirPath.front()))
|
||||
{
|
||||
// the beginning of canonical_path is the path root name
|
||||
canonical_path = tc::io::Path(*path_itr + "/");
|
||||
|
||||
// increment path iterator
|
||||
path_itr++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// the beginning of the canonical_path is the current directory path
|
||||
canonical_path = mCurrentDirPath;
|
||||
}
|
||||
|
||||
// process relative elements of path, combining with the base canonical_path
|
||||
for (; path_itr != path.end(); path_itr++)
|
||||
{
|
||||
// ignore "current directory" alias
|
||||
if (*path_itr == ".")
|
||||
continue;
|
||||
// ignore empty path elements
|
||||
else if (*path_itr == "")
|
||||
continue;
|
||||
// navigate up for "parent directory" alias
|
||||
else if (*path_itr == "..")
|
||||
{
|
||||
// ".." is the parent directory, so if there are path elements then we remove from the back to "go to the parent directory"
|
||||
if (canonical_path.size() > 1)
|
||||
canonical_path.pop_back();
|
||||
else
|
||||
continue;
|
||||
}
|
||||
else
|
||||
canonical_path.push_back(*path_itr);
|
||||
}
|
||||
|
||||
return canonical_path;
|
||||
}
|
||||
@@ -0,0 +1,370 @@
|
||||
#include <tc/io/ConcatenatedStream.h>
|
||||
|
||||
#include <tc/io/StreamUtil.h>
|
||||
#include <tc/io/IOUtil.h>
|
||||
|
||||
const std::string tc::io::ConcatenatedStream::kClassName = "tc::io::ConcatenatedStream";
|
||||
|
||||
tc::io::ConcatenatedStream::ConcatenatedStream() :
|
||||
mStreamList(),
|
||||
mStreamListMap(),
|
||||
mCurrentStream(),
|
||||
mCanRead(false),
|
||||
mCanWrite(false),
|
||||
mCanSeek(false),
|
||||
mStreamLength(0)
|
||||
{}
|
||||
|
||||
tc::io::ConcatenatedStream::ConcatenatedStream(ConcatenatedStream&& other) :
|
||||
ConcatenatedStream()
|
||||
{
|
||||
*this = std::move(other);
|
||||
}
|
||||
|
||||
tc::io::ConcatenatedStream::ConcatenatedStream(const std::vector<std::shared_ptr<tc::io::IStream>>& stream_list) :
|
||||
ConcatenatedStream()
|
||||
{
|
||||
// track the overall stream properties
|
||||
bool can_read = true;
|
||||
bool can_write = true;
|
||||
bool can_seek = true;
|
||||
|
||||
// reset stream length
|
||||
mStreamLength = 0;
|
||||
|
||||
// process stream list
|
||||
for (auto itr = stream_list.begin(); itr != stream_list.end(); itr++)
|
||||
{
|
||||
// skip null streams
|
||||
if (*itr == nullptr)
|
||||
continue;
|
||||
// skip empty streams
|
||||
if ((*itr)->length() == 0)
|
||||
continue;
|
||||
// skip streams that can't be read or writen to (so it's useless)
|
||||
if ((*itr)->canRead() == false && (*itr)->canWrite() == false)
|
||||
continue;
|
||||
|
||||
// create stream info for the input stream
|
||||
StreamInfo info;
|
||||
// range is from the current concatenated stream position for the length of the input stream
|
||||
info.range = StreamRange(mStreamLength, (*itr)->length());
|
||||
info.stream = *itr;
|
||||
|
||||
// throw an exception if the input stream range overlaps with an existing range (which shouldn't be possible)
|
||||
if (mStreamListMap.find(info.range) != mStreamListMap.end())
|
||||
{
|
||||
throw tc::Exception(kClassName, "Poor state management detected.");
|
||||
}
|
||||
|
||||
if (info.stream->canRead() == false)
|
||||
can_read = false;
|
||||
if (info.stream->canWrite() == false)
|
||||
can_write = false;
|
||||
if (info.stream->canSeek() == false)
|
||||
can_seek = false;
|
||||
|
||||
mStreamListMap.insert(std::pair<StreamRange, size_t>(info.range, mStreamList.size()));
|
||||
mStreamList.push_back(info);
|
||||
|
||||
mStreamLength += info.range.length;
|
||||
}
|
||||
|
||||
// check the stream is usable
|
||||
if (!can_read && !can_write)
|
||||
{
|
||||
throw tc::NotSupportedException(kClassName, "Stream does not support read or write.");
|
||||
}
|
||||
// check the stream is usable
|
||||
if (mStreamLength == 0)
|
||||
{
|
||||
throw tc::NotSupportedException(kClassName, "Stream had no length.");
|
||||
}
|
||||
|
||||
// save iterator to current stream
|
||||
mCurrentStream = mStreamList.begin();
|
||||
|
||||
// save properties
|
||||
mCanRead = can_read;
|
||||
mCanWrite = can_write;
|
||||
mCanSeek = can_seek;
|
||||
}
|
||||
|
||||
tc::io::ConcatenatedStream::~ConcatenatedStream()
|
||||
{
|
||||
dispose();
|
||||
}
|
||||
|
||||
tc::io::ConcatenatedStream& tc::io::ConcatenatedStream::operator=(tc::io::ConcatenatedStream&& other)
|
||||
{
|
||||
mStreamList = std::move(other.mStreamList);
|
||||
mStreamListMap = std::move(other.mStreamListMap);
|
||||
mCurrentStream = std::move(other.mCurrentStream);
|
||||
mCanRead = other.mCanRead;
|
||||
mCanWrite = other.mCanWrite;
|
||||
mCanSeek = other.mCanSeek;
|
||||
mStreamLength = other.mStreamLength;
|
||||
|
||||
// clear other state
|
||||
other.mStreamList.clear();
|
||||
other.mStreamListMap.clear();
|
||||
other.mCurrentStream.makeNull();
|
||||
other.mCanRead = false;
|
||||
other.mCanWrite = false;
|
||||
other.mCanSeek = false;
|
||||
other.mStreamLength = 0;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool tc::io::ConcatenatedStream::canRead() const
|
||||
{
|
||||
return isStreamDisposed() ? false : mCanRead;
|
||||
}
|
||||
|
||||
bool tc::io::ConcatenatedStream::canWrite() const
|
||||
{
|
||||
return isStreamDisposed() ? false : mCanWrite;
|
||||
}
|
||||
|
||||
bool tc::io::ConcatenatedStream::canSeek() const
|
||||
{
|
||||
return isStreamDisposed() ? false : mCanSeek;
|
||||
}
|
||||
|
||||
int64_t tc::io::ConcatenatedStream::length()
|
||||
{
|
||||
return isStreamDisposed() ? 0 : mStreamLength;
|
||||
}
|
||||
|
||||
int64_t tc::io::ConcatenatedStream::position()
|
||||
{
|
||||
return isStreamDisposed() ? 0 : (mCurrentStream.get()->range.offset + mCurrentStream.get()->stream->position());
|
||||
}
|
||||
|
||||
size_t tc::io::ConcatenatedStream::read(byte_t* ptr, size_t count)
|
||||
{
|
||||
if (isStreamDisposed())
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"read()", "Stream wasd.");
|
||||
}
|
||||
if (mCanRead == false)
|
||||
{
|
||||
throw tc::NotSupportedException(kClassName+"read()", "Stream does not support reading.");
|
||||
}
|
||||
|
||||
// read
|
||||
size_t readable_count = IOUtil::getReadableCount(mStreamLength, position(), count);
|
||||
size_t remaining_readable_count = readable_count;
|
||||
do {
|
||||
// determine expected readable data count for the current stream
|
||||
size_t readable_count_for_current_stream = IOUtil::getReadableCount(mCurrentStream.get()->range.length, mCurrentStream.get()->stream->position(), remaining_readable_count);
|
||||
|
||||
if (readable_count_for_current_stream != 0)
|
||||
{
|
||||
// read data and throw exception if unexpected read count is returned
|
||||
if (readable_count_for_current_stream != mCurrentStream.get()->stream->read(ptr + (readable_count - remaining_readable_count), readable_count_for_current_stream))
|
||||
{
|
||||
throw tc::io::IOException(kClassName+"read()", "Reading from one of the base streams returned less data than expected.");
|
||||
}
|
||||
|
||||
// decrement the remaining readable count
|
||||
remaining_readable_count -= readable_count_for_current_stream;
|
||||
}
|
||||
|
||||
// if there is more data to be read, increment the current stream
|
||||
if (remaining_readable_count != 0)
|
||||
{
|
||||
updateCurrentStream(mCurrentStream.get() + 1);
|
||||
|
||||
// make sure we haven't somehow reached the end before we expected
|
||||
if (mCurrentStream.get() == mStreamList.end())
|
||||
{
|
||||
throw tc::io::IOException(kClassName+"read()", "More data was expected to be readable but end of stream list was reached.");
|
||||
}
|
||||
|
||||
// correct the position the 0x0 if not already
|
||||
if (mCurrentStream.get()->stream->position() != 0x0)
|
||||
{
|
||||
if (!mCanSeek)
|
||||
{
|
||||
throw tc::io::IOException(kClassName+"read()", "Tried to continue reading from the next stream but the position was not 0, and seek was not supported.");
|
||||
}
|
||||
|
||||
mCurrentStream.get()->stream->seek(0, tc::io::SeekOrigin::Begin);
|
||||
}
|
||||
}
|
||||
} while (remaining_readable_count > 0);
|
||||
|
||||
return readable_count;
|
||||
}
|
||||
|
||||
size_t tc::io::ConcatenatedStream::write(const byte_t* ptr, size_t count)
|
||||
{
|
||||
if (isStreamDisposed())
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"write()", "Stream was disposed.");
|
||||
}
|
||||
if (mCanWrite == false)
|
||||
{
|
||||
throw tc::NotSupportedException(kClassName+"write()", "Stream does not support writing.");
|
||||
}
|
||||
|
||||
// write
|
||||
size_t writable_count = IOUtil::getWritableCount(mStreamLength, position(), count);
|
||||
size_t remaining_writable_count = writable_count;
|
||||
do {
|
||||
// determine expected writable data count for the current stream
|
||||
size_t writable_count_for_current_stream = IOUtil::getWritableCount(mCurrentStream.get()->range.length, mCurrentStream.get()->stream->position(), remaining_writable_count);
|
||||
|
||||
if (writable_count_for_current_stream != 0)
|
||||
{
|
||||
// write data and throw exception if unexpected write count is returned
|
||||
if (writable_count_for_current_stream != mCurrentStream.get()->stream->write(ptr + (writable_count - remaining_writable_count), writable_count_for_current_stream))
|
||||
{
|
||||
throw tc::io::IOException(kClassName+"write()", "Writing from one of the base streams returned less data than expected.");
|
||||
}
|
||||
|
||||
// decrement the remaining writable count
|
||||
remaining_writable_count -= writable_count_for_current_stream;
|
||||
}
|
||||
|
||||
// if there is more data to be write, increment the current stream
|
||||
if (remaining_writable_count != 0)
|
||||
{
|
||||
updateCurrentStream(mCurrentStream.get() + 1);
|
||||
|
||||
// make sure we haven't somehow reached the end before we expected
|
||||
if (mCurrentStream.get() == mStreamList.end())
|
||||
{
|
||||
throw tc::io::IOException(kClassName+"write()", "More data was expected to be writable but end of stream list was reached.");
|
||||
}
|
||||
|
||||
// correct the position the 0x0 if not already
|
||||
if (mCurrentStream.get()->stream->position() != 0x0)
|
||||
{
|
||||
if (!mCanSeek)
|
||||
{
|
||||
throw tc::io::IOException(kClassName+"write()", "Tried to continue writing from the next stream but the position was not 0, and seek was not supported.");
|
||||
}
|
||||
|
||||
mCurrentStream.get()->stream->seek(0, tc::io::SeekOrigin::Begin);
|
||||
}
|
||||
}
|
||||
} while (remaining_writable_count > 0);
|
||||
|
||||
return writable_count;
|
||||
}
|
||||
|
||||
int64_t tc::io::ConcatenatedStream::seek(int64_t offset, SeekOrigin origin)
|
||||
{
|
||||
if (isStreamDisposed())
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"seek()", "Stream was disposed.");
|
||||
}
|
||||
if (mCanSeek == false)
|
||||
{
|
||||
throw tc::NotSupportedException(kClassName+"seek()", "Stream does not support seeking.");
|
||||
}
|
||||
|
||||
// seek
|
||||
int64_t absolute_seek_pos = StreamUtil::getSeekResult(offset, origin, position(), mStreamLength);
|
||||
|
||||
// seek is <= 0 : we use the first stream and set the position to 0
|
||||
if (absolute_seek_pos <= 0)
|
||||
{
|
||||
updateCurrentStream(mStreamList.begin());
|
||||
if (mCurrentStream.get() == mStreamList.end())
|
||||
{
|
||||
throw tc::io::IOException(kClassName+"seek()", "Failed to seek because underlying stream could not be determined.");
|
||||
}
|
||||
mCurrentStream.get()->stream->seek(0, tc::io::SeekOrigin::Begin);
|
||||
}
|
||||
// seek is < mStreamLength : we find the stream in the map and set the relative position
|
||||
else if (absolute_seek_pos < mStreamLength)
|
||||
{
|
||||
// before we do a map lookup, check if the current stream has the range the offset sits in
|
||||
if ((absolute_seek_pos >= mCurrentStream.get()->range.offset) && (absolute_seek_pos < (mCurrentStream.get()->range.offset + mCurrentStream.get()->range.length)))
|
||||
{
|
||||
mCurrentStream.get()->stream->seek(absolute_seek_pos - mCurrentStream.get()->range.offset, tc::io::SeekOrigin::Begin);
|
||||
}
|
||||
// look up the correct stream in the map
|
||||
else
|
||||
{
|
||||
auto rangeItr = mStreamListMap.find(StreamRange(absolute_seek_pos));
|
||||
if (rangeItr == mStreamListMap.end())
|
||||
{
|
||||
throw tc::io::IOException(kClassName+"seek()", "Failed to seek because underlying stream could not be determined.");
|
||||
}
|
||||
|
||||
if (rangeItr->second > mStreamList.size())
|
||||
{
|
||||
throw tc::io::IOException(kClassName+"seek()", "Failed to seek because underlying stream could not be determined.");
|
||||
}
|
||||
|
||||
updateCurrentStream(mStreamList.begin() + rangeItr->second);
|
||||
mCurrentStream.get()->stream->seek(absolute_seek_pos - mCurrentStream.get()->range.offset, tc::io::SeekOrigin::Begin);
|
||||
}
|
||||
}
|
||||
// seek is >= mStreamLength : we use the end stream and seek to the end of it
|
||||
else
|
||||
{
|
||||
updateCurrentStream(--mStreamList.end());
|
||||
if (mCurrentStream.get() == mStreamList.end())
|
||||
{
|
||||
throw tc::io::IOException(kClassName+"seek()", "Failed to seek because underlying stream could not be determined.");
|
||||
}
|
||||
mCurrentStream.get()->stream->seek(0, tc::io::SeekOrigin::End);
|
||||
}
|
||||
|
||||
return position();
|
||||
}
|
||||
|
||||
void tc::io::ConcatenatedStream::setLength(int64_t length)
|
||||
{
|
||||
if (isStreamDisposed())
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"setLength()", "Stream was disposed.");
|
||||
}
|
||||
|
||||
throw tc::NotImplementedException(kClassName+"setLength()", "setLength() is not implemented for tc::io::ConcatenatedStream.");
|
||||
}
|
||||
|
||||
void tc::io::ConcatenatedStream::flush()
|
||||
{
|
||||
if (isStreamDisposed())
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"flush()", "Stream was disposed.");
|
||||
}
|
||||
|
||||
mCurrentStream.get()->stream->flush();
|
||||
}
|
||||
|
||||
void tc::io::ConcatenatedStream::dispose()
|
||||
{
|
||||
if (isStreamDisposed() == false)
|
||||
mCurrentStream.get()->stream->flush();
|
||||
|
||||
mStreamList.clear();
|
||||
mCurrentStream.makeNull();
|
||||
|
||||
mCanRead = false;
|
||||
mCanWrite = false;
|
||||
mCanSeek = false;
|
||||
mStreamLength = 0;
|
||||
}
|
||||
|
||||
void tc::io::ConcatenatedStream::updateCurrentStream(std::vector<StreamInfo>::iterator stream_itr)
|
||||
{
|
||||
if (mCurrentStream.isNull())
|
||||
{
|
||||
mCurrentStream = stream_itr;
|
||||
}
|
||||
else if (mCurrentStream.get() != stream_itr)
|
||||
{
|
||||
// if stream itr != end() flush the stream
|
||||
if (mCurrentStream.get() != mStreamList.end())
|
||||
mCurrentStream.get()->stream->flush();
|
||||
mCurrentStream = stream_itr;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,752 @@
|
||||
#include <tc/io/FileStream.h>
|
||||
#include <tc/PlatformErrorHandlingUtil.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <direct.h>
|
||||
#include <cstdlib>
|
||||
#else
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h> /* For O_RDWR */
|
||||
#include <unistd.h> /* For open(), creat() */
|
||||
#endif
|
||||
|
||||
const std::string tc::io::FileStream::kClassName = "tc::io::FileStream";
|
||||
|
||||
tc::io::FileStream::FileHandle::~FileHandle()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
CloseHandle(handle);
|
||||
#else
|
||||
::close(handle);
|
||||
#endif
|
||||
}
|
||||
|
||||
tc::io::FileStream::FileStream() :
|
||||
mCanRead(false),
|
||||
mCanWrite(false),
|
||||
mCanSeek(false),
|
||||
mIsAppendRestrictSeekCall(false),
|
||||
mFileHandle()
|
||||
{}
|
||||
|
||||
tc::io::FileStream::FileStream(FileStream&& other) :
|
||||
FileStream()
|
||||
{
|
||||
*this = std::move(other);
|
||||
}
|
||||
|
||||
tc::io::FileStream::FileStream(const tc::io::Path& path, FileMode mode, FileAccess access) :
|
||||
FileStream()
|
||||
{
|
||||
// dispose stream before opening new stream
|
||||
dispose();
|
||||
|
||||
open_impl(path, mode, access);
|
||||
}
|
||||
|
||||
tc::io::FileStream& tc::io::FileStream::operator=(tc::io::FileStream&& other)
|
||||
{
|
||||
mCanRead = other.mCanRead;
|
||||
mCanWrite = other.mCanWrite;
|
||||
mCanSeek = other.mCanSeek;
|
||||
mIsAppendRestrictSeekCall = other.mIsAppendRestrictSeekCall;
|
||||
mFileHandle = std::move(other.mFileHandle);
|
||||
other.dispose();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool tc::io::FileStream::canRead() const
|
||||
{
|
||||
return mFileHandle == nullptr ? false : mCanRead;
|
||||
}
|
||||
|
||||
bool tc::io::FileStream::canWrite() const
|
||||
{
|
||||
return mFileHandle == nullptr ? false : mCanWrite;
|
||||
}
|
||||
bool tc::io::FileStream::canSeek() const
|
||||
{
|
||||
return mFileHandle == nullptr || mIsAppendRestrictSeekCall == true ? false : mCanSeek;
|
||||
}
|
||||
|
||||
int64_t tc::io::FileStream::length()
|
||||
{
|
||||
return mFileHandle == nullptr ? 0 : length_impl();
|
||||
}
|
||||
|
||||
int64_t tc::io::FileStream::position()
|
||||
{
|
||||
if (mFileHandle == nullptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mCanSeek == false)
|
||||
{
|
||||
throw tc::NotSupportedException(kClassName+"::position()", "This method is not supported for streams that do not support seeking");
|
||||
}
|
||||
|
||||
return seek_impl(0, SeekOrigin::Current);
|
||||
}
|
||||
|
||||
size_t tc::io::FileStream::read(byte_t* ptr, size_t count)
|
||||
{
|
||||
if (mFileHandle == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::read()", "Failed to read from stream (stream is disposed)");
|
||||
}
|
||||
|
||||
if (mCanRead == false)
|
||||
{
|
||||
throw tc::NotSupportedException(kClassName+"::read()", "Stream does not support reading");
|
||||
}
|
||||
|
||||
if (ptr == nullptr)
|
||||
{
|
||||
throw tc::ArgumentNullException(kClassName+"::read()", "ptr was null");
|
||||
}
|
||||
|
||||
if (count < 0)
|
||||
{
|
||||
throw tc::ArgumentOutOfRangeException(kClassName+"::read()", "count was negative");
|
||||
}
|
||||
|
||||
return read_impl(ptr, count);
|
||||
}
|
||||
|
||||
size_t tc::io::FileStream::write(const byte_t* ptr, size_t count)
|
||||
{
|
||||
if (mFileHandle == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::write()", "Failed to write to stream (no file open)");
|
||||
}
|
||||
|
||||
if (mCanWrite == false)
|
||||
{
|
||||
throw tc::NotSupportedException(kClassName+"::write()", "Stream does not support writing");
|
||||
}
|
||||
|
||||
if (ptr == nullptr)
|
||||
{
|
||||
throw tc::ArgumentNullException(kClassName+"::write()", "ptr was null");
|
||||
}
|
||||
|
||||
if (count < 0)
|
||||
{
|
||||
throw tc::ArgumentOutOfRangeException(kClassName+"::write()", "count was negative");
|
||||
}
|
||||
|
||||
return write_impl(ptr, count);
|
||||
}
|
||||
|
||||
int64_t tc::io::FileStream::seek(int64_t offset, SeekOrigin origin)
|
||||
{
|
||||
if (mFileHandle == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::seek()", "Failed to set stream position (stream is disposed)");
|
||||
}
|
||||
|
||||
if (mCanSeek == false)
|
||||
{
|
||||
throw tc::NotSupportedException(kClassName+"::seek()", "Stream does not support seeking");
|
||||
}
|
||||
|
||||
if (mIsAppendRestrictSeekCall == true)
|
||||
{
|
||||
throw tc::io::IOException(kClassName+"::seek()", "Streams opened in Append mode are not allowed to change file position.");
|
||||
}
|
||||
|
||||
return seek_impl(offset, origin);
|
||||
}
|
||||
|
||||
void tc::io::FileStream::setLength(int64_t length)
|
||||
{
|
||||
if (mFileHandle == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::setLength()", "Failed to set stream length (stream is disposed)");
|
||||
}
|
||||
|
||||
if (mCanWrite == false || mCanSeek == false)
|
||||
{
|
||||
throw tc::NotSupportedException(kClassName+"::setLength()", "Stream does not support both writing and seeking");
|
||||
}
|
||||
|
||||
setLength_impl(length);
|
||||
}
|
||||
|
||||
void tc::io::FileStream::flush()
|
||||
{
|
||||
if (mFileHandle != nullptr)
|
||||
{
|
||||
flush_impl();
|
||||
}
|
||||
}
|
||||
|
||||
void tc::io::FileStream::dispose()
|
||||
{
|
||||
if (mFileHandle.get() != nullptr)
|
||||
{
|
||||
flush();
|
||||
mFileHandle.reset();
|
||||
}
|
||||
mCanRead = false;
|
||||
mCanWrite = false;
|
||||
mCanSeek = false;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#pragma warning(disable : 4065) // disable warning for switch case with only default case
|
||||
|
||||
void tc::io::FileStream::open_impl(const tc::io::Path& path, FileMode mode, FileAccess access)
|
||||
{
|
||||
// convert Path to unicode string
|
||||
std::u16string unicode_path = path.to_u16string(tc::io::Path::Format::Win32);
|
||||
|
||||
DWORD access_flag = 0;
|
||||
DWORD share_mode_flag = 0;
|
||||
DWORD creation_flag = 0;
|
||||
|
||||
// process mode
|
||||
switch (mode)
|
||||
{
|
||||
case (FileMode::CreateNew):
|
||||
// create file if does not exist | return error if file does not exist
|
||||
creation_flag = CREATE_NEW;
|
||||
break;
|
||||
case (FileMode::Create):
|
||||
// create file if does not exist | truncate file if it exists
|
||||
creation_flag = CREATE_ALWAYS;
|
||||
break;
|
||||
case (FileMode::Open):
|
||||
// no flags
|
||||
creation_flag = OPEN_EXISTING;
|
||||
break;
|
||||
case (FileMode::OpenOrCreate):
|
||||
// create file if does not exist
|
||||
creation_flag = access == FileAccess::Read ? OPEN_EXISTING : OPEN_ALWAYS;
|
||||
break;
|
||||
case (FileMode::Truncate):
|
||||
// truncate file if file exists
|
||||
creation_flag = TRUNCATE_EXISTING;
|
||||
break;
|
||||
case (FileMode::Append):
|
||||
// open in append mode
|
||||
creation_flag = OPEN_ALWAYS;
|
||||
break;
|
||||
default:
|
||||
throw tc::ArgumentOutOfRangeException(kClassName+"::open()", "Illegal value for mode");
|
||||
}
|
||||
|
||||
// process access
|
||||
switch (access)
|
||||
{
|
||||
case (FileAccess::Read):
|
||||
// read access
|
||||
access_flag = GENERIC_READ;
|
||||
// shared read lock
|
||||
share_mode_flag = FILE_SHARE_READ;
|
||||
break;
|
||||
case (FileAccess::Write):
|
||||
// write access
|
||||
access_flag = GENERIC_WRITE;
|
||||
// exclusive lock
|
||||
share_mode_flag = 0;
|
||||
break;
|
||||
case (FileAccess::ReadWrite):
|
||||
// read/write access
|
||||
access_flag = GENERIC_READ | GENERIC_WRITE;
|
||||
// exclusive lock
|
||||
share_mode_flag = 0;
|
||||
break;
|
||||
default:
|
||||
throw tc::ArgumentOutOfRangeException(kClassName+"::open()", "Illegal value for access");
|
||||
}
|
||||
|
||||
// validate use of write dependent flags (open existing is the only one that supports no write flag)
|
||||
if (creation_flag != OPEN_EXISTING && !(access_flag & GENERIC_WRITE))
|
||||
{
|
||||
throw tc::ArgumentException(kClassName + "::open()", "Stream open mode requires write access, but write access was not allowed");
|
||||
}
|
||||
|
||||
// append can only open in write only mode
|
||||
if (mode == tc::io::FileMode::Append && (access_flag & GENERIC_READ | GENERIC_WRITE) != GENERIC_WRITE)
|
||||
{
|
||||
throw tc::ArgumentException(kClassName + "::open()", "Stream opened in Append mode can only work with Write access. ReadWrite is not permitted");
|
||||
}
|
||||
|
||||
// open file
|
||||
HANDLE file_handle = CreateFileW((LPCWSTR)unicode_path.c_str(),
|
||||
access_flag,
|
||||
share_mode_flag,
|
||||
0,
|
||||
creation_flag,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
|
||||
// check file handle
|
||||
if (file_handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
DWORD error = GetLastError();
|
||||
switch (error)
|
||||
{
|
||||
case (ERROR_FILE_NOT_FOUND):
|
||||
case (ERROR_PATH_NOT_FOUND):
|
||||
throw tc::io::FileNotFoundException(kClassName+"::open()", PlatformErrorHandlingUtil::GetLastErrorString(error));
|
||||
case (ERROR_FILE_EXISTS):
|
||||
throw tc::io::FileExistsException(kClassName+"::open()", PlatformErrorHandlingUtil::GetLastErrorString(error));
|
||||
case (ERROR_INVALID_PARAMETER):
|
||||
throw tc::ArgumentException(kClassName + "::open()", PlatformErrorHandlingUtil::GetLastErrorString(error));
|
||||
case (ERROR_ACCESS_DENIED):
|
||||
throw tc::UnauthorisedAccessException(kClassName+"::open()", PlatformErrorHandlingUtil::GetLastErrorString(error));
|
||||
default:
|
||||
throw tc::io::IOException(kClassName+"::open()", "Failed to open file stream (" + PlatformErrorHandlingUtil::GetLastErrorString(error) + ")");
|
||||
}
|
||||
}
|
||||
|
||||
// store file handle
|
||||
mFileHandle = std::unique_ptr<tc::io::FileStream::FileHandle>(new tc::io::FileStream::FileHandle(file_handle));
|
||||
|
||||
// seek to end of file if in append mode
|
||||
if (mode == FileMode::Append)
|
||||
{
|
||||
seek_impl(0, SeekOrigin::End);
|
||||
mIsAppendRestrictSeekCall = true;
|
||||
}
|
||||
|
||||
|
||||
// set state flags
|
||||
mCanRead = (access_flag & GENERIC_READ) ? true : false;
|
||||
mCanWrite = (access_flag & GENERIC_WRITE) ? true : false;
|
||||
mCanSeek = GetFileType(mFileHandle->handle) == FILE_TYPE_DISK ? true : false;
|
||||
}
|
||||
|
||||
int64_t tc::io::FileStream::length_impl()
|
||||
{
|
||||
LARGE_INTEGER stream_length;
|
||||
|
||||
if (GetFileSizeEx(mFileHandle->handle, &stream_length) == false)
|
||||
{
|
||||
DWORD error = GetLastError();
|
||||
switch (error)
|
||||
{
|
||||
// TODO: Directly handle usual errors for custom exceptions
|
||||
default:
|
||||
throw tc::io::IOException(kClassName+"::length()", "Failed to get stream length (" + PlatformErrorHandlingUtil::GetLastErrorString(error) + ")");
|
||||
}
|
||||
}
|
||||
|
||||
return (int64_t) stream_length.QuadPart;
|
||||
}
|
||||
|
||||
size_t tc::io::FileStream::read_impl(byte_t* ptr, size_t count)
|
||||
{
|
||||
DWORD bytes_read;
|
||||
|
||||
if (ReadFile(mFileHandle->handle, ptr, (DWORD)count, &bytes_read, NULL) == false)
|
||||
{
|
||||
DWORD error = GetLastError();
|
||||
switch (error)
|
||||
{
|
||||
// TODO: Directly handle usual errors for custom exceptions
|
||||
default:
|
||||
throw tc::io::IOException(kClassName+"::read()", "Failed to read from stream (" + PlatformErrorHandlingUtil::GetLastErrorString(error) + ")");
|
||||
}
|
||||
}
|
||||
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
size_t tc::io::FileStream::write_impl(const byte_t* ptr, size_t count)
|
||||
{
|
||||
DWORD bytes_written;
|
||||
|
||||
if (WriteFile(mFileHandle->handle, ptr, (DWORD)count, &bytes_written, NULL) == false)
|
||||
{
|
||||
DWORD error = GetLastError();
|
||||
switch (error)
|
||||
{
|
||||
// TODO: Directly handle usual errors for custom exceptions
|
||||
default:
|
||||
throw tc::io::IOException(kClassName+"::write()", "Failed to write to stream (" + PlatformErrorHandlingUtil::GetLastErrorString(error) + ")");
|
||||
}
|
||||
}
|
||||
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
int64_t tc::io::FileStream::seek_impl(int64_t offset, SeekOrigin origin)
|
||||
{
|
||||
DWORD seek_flag = 0;
|
||||
switch(origin)
|
||||
{
|
||||
case (SeekOrigin::Begin):
|
||||
seek_flag = FILE_BEGIN;
|
||||
break;
|
||||
case (SeekOrigin::Current):
|
||||
seek_flag = FILE_CURRENT;
|
||||
break;
|
||||
case (SeekOrigin::End):
|
||||
seek_flag = FILE_END;
|
||||
break;
|
||||
default:
|
||||
throw tc::ArgumentOutOfRangeException(kClassName+"::seek()", "Unknown SeekOrigin value");
|
||||
}
|
||||
|
||||
LARGE_INTEGER win_pos, out;
|
||||
win_pos.QuadPart = offset;
|
||||
if (SetFilePointerEx(
|
||||
mFileHandle->handle,
|
||||
win_pos,
|
||||
&out,
|
||||
seek_flag
|
||||
) == false)
|
||||
{
|
||||
DWORD error = GetLastError();
|
||||
switch (error)
|
||||
{
|
||||
case (ERROR_NEGATIVE_SEEK):
|
||||
throw tc::ArgumentOutOfRangeException(kClassName+"::seek()", PlatformErrorHandlingUtil::GetLastErrorString(error));
|
||||
default:
|
||||
throw tc::io::IOException(kClassName+"::seek()", "Failed to set stream position (" + PlatformErrorHandlingUtil::GetLastErrorString(error) + ")");
|
||||
}
|
||||
}
|
||||
|
||||
return out.QuadPart;
|
||||
}
|
||||
|
||||
void tc::io::FileStream::setLength_impl(int64_t length)
|
||||
{
|
||||
seek(length, tc::io::SeekOrigin::Begin);
|
||||
|
||||
if (SetEndOfFile(
|
||||
mFileHandle->handle
|
||||
) == false)
|
||||
{
|
||||
DWORD error = GetLastError();
|
||||
switch (error)
|
||||
{
|
||||
default:
|
||||
throw tc::io::IOException(kClassName+"::setLength()", "Failed to set end of file (" + PlatformErrorHandlingUtil::GetLastErrorString(error) + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tc::io::FileStream::flush_impl()
|
||||
{
|
||||
if (mCanWrite)
|
||||
{
|
||||
// flush buffers only applies to written data
|
||||
FlushFileBuffers(mFileHandle->handle);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning(default : 4065) // reenable warning for switch case with only default case
|
||||
|
||||
#else
|
||||
void tc::io::FileStream::open_impl(const tc::io::Path& path, FileMode mode, FileAccess access)
|
||||
{
|
||||
// convert Path to unicode string
|
||||
std::string unicode_path = path.to_string(tc::io::Path::Format::POSIX);
|
||||
|
||||
// open file
|
||||
int open_flag = 0;
|
||||
|
||||
// process mode
|
||||
switch (mode)
|
||||
{
|
||||
case (FileMode::CreateNew):
|
||||
// create file if does not exist | return error if file does not exist
|
||||
open_flag |= O_CREAT | O_EXCL;
|
||||
break;
|
||||
case (FileMode::Create):
|
||||
// create file if does not exist | truncate file if it exists
|
||||
open_flag |= O_CREAT | O_TRUNC;
|
||||
break;
|
||||
case (FileMode::Open):
|
||||
// no flags
|
||||
open_flag |= 0;
|
||||
break;
|
||||
case (FileMode::OpenOrCreate):
|
||||
// create file if does not exist (however only enable create flag if write access is enabled)
|
||||
open_flag |= (access == FileAccess::ReadWrite || access == FileAccess::Write) ? O_CREAT : 0;
|
||||
break;
|
||||
case (FileMode::Truncate):
|
||||
// truncate file if file exists
|
||||
open_flag |= O_TRUNC;
|
||||
break;
|
||||
case (FileMode::Append):
|
||||
// open in append mode (create file if doesn't exist)
|
||||
open_flag |= O_APPEND | O_CREAT;
|
||||
break;
|
||||
default:
|
||||
throw tc::ArgumentOutOfRangeException(kClassName+"::open()", "Illegal value for mode");
|
||||
}
|
||||
|
||||
// process access
|
||||
switch (access)
|
||||
{
|
||||
case (FileAccess::Read):
|
||||
// read access
|
||||
open_flag |= O_RDONLY;
|
||||
#ifdef O_SHLOCK
|
||||
// shared lock
|
||||
open_flag |= O_SHLOCK;
|
||||
#endif
|
||||
break;
|
||||
case (FileAccess::Write):
|
||||
// write access
|
||||
open_flag |= O_WRONLY;
|
||||
#ifdef O_EXLOCK
|
||||
// exclusive lock
|
||||
open_flag |= O_EXLOCK;
|
||||
#endif
|
||||
break;
|
||||
case (FileAccess::ReadWrite):
|
||||
// read/write access
|
||||
open_flag |= O_RDWR;
|
||||
#ifdef O_EXLOCK
|
||||
// exclusive lock
|
||||
open_flag |= O_EXLOCK;
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
throw tc::ArgumentOutOfRangeException(kClassName+"::open()", "Illegal value for access");
|
||||
}
|
||||
|
||||
// validate use of write dependent flags
|
||||
if ((open_flag & (O_APPEND | O_TRUNC | O_CREAT)) && !(open_flag & (O_WRONLY|O_RDWR)))
|
||||
{
|
||||
throw tc::ArgumentException(kClassName+"::open()", "Stream open mode requires write access, but write access was not allowed");
|
||||
}
|
||||
// explicitly check APPEND as being write only
|
||||
if ((open_flag & (O_APPEND)) && (open_flag & (O_RDWR)))
|
||||
{
|
||||
throw tc::ArgumentException(kClassName+"::open()", "Stream opened in Append mode can only work with Write access. ReadWrite is not permitted");
|
||||
}
|
||||
|
||||
// open file handle with Read/Write for User, Read for Group, nothing for others
|
||||
int file_handle = ::open(unicode_path.c_str(), open_flag, S_IRUSR | S_IWUSR | S_IRGRP);
|
||||
|
||||
// handle error
|
||||
if (file_handle == -1)
|
||||
{
|
||||
switch (errno)
|
||||
{
|
||||
case (EACCES):
|
||||
case (EROFS):
|
||||
throw tc::UnauthorisedAccessException(kClassName+"::open()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
||||
case (ENAMETOOLONG):
|
||||
throw tc::io::PathTooLongException(kClassName+"::open()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
||||
case (ENOENT):
|
||||
throw tc::io::FileNotFoundException(kClassName+"::open()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
||||
case (EEXIST):
|
||||
throw tc::io::FileExistsException(kClassName+"::open()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
||||
case (EINVAL):
|
||||
throw tc::ArgumentOutOfRangeException(kClassName+"::open()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
||||
case (EFAULT):
|
||||
throw tc::AccessViolationException(kClassName+"::open()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
||||
case (EISDIR):
|
||||
throw tc::io::FileNotFoundException(kClassName+"::open()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
||||
case (EDQUOT):
|
||||
case (EFBIG):
|
||||
case (EINTR):
|
||||
case (ELOOP):
|
||||
case (EMFILE):
|
||||
case (ENFILE):
|
||||
case (ENOMEM):
|
||||
case (ENOSPC):
|
||||
case (ENXIO):
|
||||
case (EOVERFLOW):
|
||||
case (EPERM):
|
||||
case (ETXTBSY):
|
||||
case (EWOULDBLOCK):
|
||||
default:
|
||||
throw tc::io::IOException(kClassName+"::open()", "Failed to open file stream (" + PlatformErrorHandlingUtil::GetGnuErrorNumString(errno) + ")");
|
||||
}
|
||||
}
|
||||
|
||||
// store file handle
|
||||
mFileHandle = std::unique_ptr<tc::io::FileStream::FileHandle>(new tc::io::FileStream::FileHandle(file_handle));
|
||||
|
||||
// get stat info on file
|
||||
struct stat stat_buf;
|
||||
if (fstat(mFileHandle->handle, &stat_buf) == -1)
|
||||
{
|
||||
throw tc::io::IOException(kClassName+"::open()", "Failed to check stream properties using fstat() (" + PlatformErrorHandlingUtil::GetGnuErrorNumString(errno) + ")");
|
||||
}
|
||||
|
||||
// if this is a directory throw an exception
|
||||
if (S_ISDIR(stat_buf.st_mode))
|
||||
{
|
||||
throw tc::io::FileNotFoundException(kClassName+"::open()", "Path refers to a directory not a file");
|
||||
}
|
||||
|
||||
// set state flags
|
||||
// would check O_RDONLY but that resolves to 0 so it can't be bitmask checked
|
||||
mCanRead = (open_flag & O_RDWR) || !(open_flag & O_WRONLY) ? true : false;
|
||||
mCanWrite = (open_flag & (O_WRONLY|O_RDWR)) ? true : false;
|
||||
mCanSeek = S_ISREG(stat_buf.st_mode) ? true : false;
|
||||
|
||||
// seek to end of file if in append mode
|
||||
if (mode == FileMode::Append)
|
||||
{
|
||||
seek_impl(0, SeekOrigin::End);
|
||||
mIsAppendRestrictSeekCall = true;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t tc::io::FileStream::length_impl()
|
||||
{
|
||||
int64_t length;
|
||||
|
||||
// get stat info on file
|
||||
struct stat stat_buf;
|
||||
if (fstat(mFileHandle->handle, &stat_buf) == -1)
|
||||
{
|
||||
throw tc::io::IOException(kClassName+"::length()", "Failed to check stream properties using fstat() (" + PlatformErrorHandlingUtil::GetGnuErrorNumString(errno) + ")");
|
||||
}
|
||||
|
||||
if (S_ISREG(stat_buf.st_mode))
|
||||
{
|
||||
length = stat_buf.st_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw tc::NotSupportedException(kClassName+"::length()", "length() cannot be used with device-files or pipes");
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
size_t tc::io::FileStream::read_impl(byte_t* ptr, size_t count)
|
||||
{
|
||||
int64_t read_len = ::read(mFileHandle->handle, ptr, count);
|
||||
|
||||
// handle error
|
||||
if (read_len == -1)
|
||||
{
|
||||
switch (errno)
|
||||
{
|
||||
case (EINVAL):
|
||||
throw tc::ArgumentOutOfRangeException(kClassName+"::read()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
||||
case (EFAULT):
|
||||
throw tc::AccessViolationException(kClassName+"::read()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
||||
case (EISDIR):
|
||||
case (EBADF):
|
||||
case (EAGAIN):
|
||||
case (EINTR):
|
||||
case (EIO):
|
||||
default:
|
||||
throw tc::io::IOException(kClassName+"::read()", "Failed to read from stream (" + PlatformErrorHandlingUtil::GetGnuErrorNumString(errno) + ")");
|
||||
}
|
||||
}
|
||||
|
||||
return size_t(read_len);
|
||||
}
|
||||
|
||||
size_t tc::io::FileStream::write_impl(const byte_t* ptr, size_t count)
|
||||
{
|
||||
int64_t write_len = ::write(mFileHandle->handle, ptr, count);
|
||||
|
||||
// handle error
|
||||
if (write_len == -1)
|
||||
{
|
||||
switch (errno)
|
||||
{
|
||||
case (EINVAL):
|
||||
throw tc::ArgumentOutOfRangeException(kClassName+"::write()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
||||
case (EFAULT):
|
||||
throw tc::AccessViolationException(kClassName+"::write()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
||||
case (EFBIG):
|
||||
case (EAGAIN):
|
||||
case (EDESTADDRREQ):
|
||||
case (EDQUOT):
|
||||
case (EINTR):
|
||||
case (EIO):
|
||||
case (ENOSPC):
|
||||
case (EPERM):
|
||||
case (EPIPE):
|
||||
default:
|
||||
throw tc::io::IOException(kClassName+"::write()", "Failed to write to stream (" + PlatformErrorHandlingUtil::GetGnuErrorNumString(errno) + ")");
|
||||
}
|
||||
}
|
||||
|
||||
return size_t(write_len);
|
||||
}
|
||||
|
||||
int64_t tc::io::FileStream::seek_impl(int64_t offset, SeekOrigin origin)
|
||||
{
|
||||
int seek_flag = 0;
|
||||
switch(origin)
|
||||
{
|
||||
case (SeekOrigin::Begin):
|
||||
seek_flag = SEEK_SET;
|
||||
break;
|
||||
case (SeekOrigin::Current):
|
||||
seek_flag = SEEK_CUR;
|
||||
break;
|
||||
case (SeekOrigin::End):
|
||||
seek_flag = SEEK_END;
|
||||
break;
|
||||
default:
|
||||
throw tc::ArgumentOutOfRangeException(kClassName+"::seek()", "Unknown SeekOrigin value");
|
||||
}
|
||||
|
||||
#ifdef _LARGEFILE64_SOURCE
|
||||
int64_t fpos = lseek64(mFileHandle->handle, offset, seek_flag);
|
||||
#else
|
||||
int64_t fpos = lseek(mFileHandle->handle, offset, seek_flag);
|
||||
#endif
|
||||
|
||||
// handle error
|
||||
if (fpos == -1)
|
||||
{
|
||||
switch (errno)
|
||||
{
|
||||
case (EINVAL):
|
||||
throw tc::ArgumentOutOfRangeException(kClassName+"::seek()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
||||
case (EOVERFLOW):
|
||||
throw tc::OverflowException(kClassName+"::seek()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
||||
case (EBADF):
|
||||
case (ESPIPE):
|
||||
case (ENXIO):
|
||||
default:
|
||||
throw tc::io::IOException(kClassName+"::seek()", "Failed to set stream position (" + PlatformErrorHandlingUtil::GetGnuErrorNumString(errno) + ")");
|
||||
}
|
||||
}
|
||||
|
||||
return fpos;
|
||||
}
|
||||
|
||||
void tc::io::FileStream::setLength_impl(int64_t length)
|
||||
{
|
||||
#ifdef _LARGEFILE64_SOURCE
|
||||
int trun_res = ftruncate64(mFileHandle->handle, length);
|
||||
#else
|
||||
int trun_res = ftruncate(mFileHandle->handle, length);
|
||||
#endif
|
||||
|
||||
if (trun_res == -1)
|
||||
{
|
||||
switch (errno)
|
||||
{
|
||||
case (EINTR):
|
||||
case (EINVAL):
|
||||
case (EFBIG):
|
||||
case (EIO):
|
||||
case (EBADF):
|
||||
default:
|
||||
throw tc::io::IOException(kClassName+"::seek()", "Failed to set stream position (" + PlatformErrorHandlingUtil::GetGnuErrorNumString(errno) + ")");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void tc::io::FileStream::flush_impl()
|
||||
{
|
||||
// open/read/write are non-buffered
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,40 @@
|
||||
#include <tc/io/IOUtil.h>
|
||||
|
||||
int64_t tc::io::IOUtil::castSizeToInt64(size_t size)
|
||||
{
|
||||
if (std::numeric_limits<size_t>::digits > std::numeric_limits<int64_t>::digits)
|
||||
size = std::min<size_t>(size, size_t(std::numeric_limits<int64_t>::max()));
|
||||
|
||||
return int64_t(size);
|
||||
}
|
||||
|
||||
size_t tc::io::IOUtil::castInt64ToSize(int64_t length)
|
||||
{
|
||||
if (length < 0)
|
||||
return 0;
|
||||
|
||||
if (std::numeric_limits<size_t>::digits < std::numeric_limits<int64_t>::digits)
|
||||
length = std::min<int64_t>(length, int64_t(std::numeric_limits<size_t>::max()));
|
||||
|
||||
return size_t(length);
|
||||
}
|
||||
|
||||
size_t tc::io::IOUtil::getAvailableSize(int64_t data_length, int64_t data_offset)
|
||||
{
|
||||
if (data_length < 0 || data_offset < 0)
|
||||
return 0;
|
||||
|
||||
int64_t readable_length = (data_offset < data_length) ? (data_length - data_offset) : 0;
|
||||
|
||||
return castInt64ToSize(readable_length);
|
||||
}
|
||||
|
||||
size_t tc::io::IOUtil::getReadableCount(int64_t data_length, int64_t data_offset, size_t requested_read_count)
|
||||
{
|
||||
return std::min<size_t>(getAvailableSize(data_length, data_offset), requested_read_count);
|
||||
}
|
||||
|
||||
size_t tc::io::IOUtil::getWritableCount(int64_t data_length, int64_t data_offset, size_t requested_write_count)
|
||||
{
|
||||
return getReadableCount(data_length, data_offset, requested_write_count);
|
||||
}
|
||||
@@ -0,0 +1,419 @@
|
||||
#include <tc/io/LocalFileSystem.h>
|
||||
#include <tc/io/FileStream.h>
|
||||
#include <tc/PlatformErrorHandlingUtil.h>
|
||||
#include <tc/Exception.h>
|
||||
#include <tc/string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <direct.h>
|
||||
#include <cstdlib>
|
||||
|
||||
#pragma warning(disable : 4065) // disable warning for switch case with only default case
|
||||
|
||||
#else
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#endif
|
||||
|
||||
const std::string tc::io::LocalFileSystem::kClassName = "tc::io::LocalFileSystem";
|
||||
|
||||
tc::io::LocalFileSystem::LocalFileSystem() :
|
||||
mState(1 << tc::RESFLAG_READY)
|
||||
{
|
||||
}
|
||||
|
||||
tc::ResourceStatus tc::io::LocalFileSystem::state()
|
||||
{
|
||||
return mState;
|
||||
}
|
||||
|
||||
void tc::io::LocalFileSystem::dispose()
|
||||
{
|
||||
mState = (1 << tc::RESFLAG_NOINIT);
|
||||
}
|
||||
|
||||
void tc::io::LocalFileSystem::createFile(const tc::io::Path& path)
|
||||
{
|
||||
tc::io::FileStream file(path, FileMode::Create, FileAccess::Write);
|
||||
}
|
||||
|
||||
void tc::io::LocalFileSystem::removeFile(const tc::io::Path& path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// convert Path to unicode string
|
||||
std::u16string unicode_path = path.to_u16string(tc::io::Path::Format::Win32);
|
||||
|
||||
// delete file
|
||||
if (DeleteFileW((LPCWSTR)unicode_path.c_str()) == false)
|
||||
{
|
||||
DWORD error = GetLastError();
|
||||
switch (error)
|
||||
{
|
||||
default:
|
||||
throw tc::io::IOException(kClassName+"::removeFile()", "Failed to remove file (" + PlatformErrorHandlingUtil::GetLastErrorString(error) + ")");
|
||||
}
|
||||
}
|
||||
#else
|
||||
// convert Path to unicode string
|
||||
std::string unicode_path = path.to_string(tc::io::Path::Format::POSIX);
|
||||
|
||||
if (unlink(unicode_path.c_str()) == -1)
|
||||
{
|
||||
switch (errno)
|
||||
{
|
||||
case (EACCES): // Search permission is denied for a component of the path prefix. -OR- Write permission is denied on the directory containing the link to be removed.
|
||||
case (EROFS): // The named file resides on a read-only file system.
|
||||
case (EPERM): // The named file is a directory and the effective user ID of the process is not the super-user. -OR- The directory containing the file is marked sticky, and neither the containing directory nor the file to be removed are owned by the effective user ID.
|
||||
case (EBUSY): // The entry to be unlinked is the mount point for a mounted file system. -OR- The file named by the path argument cannot be unlinked because it is being used by the system or by another process.
|
||||
throw tc::UnauthorisedAccessException(kClassName+"::removeFile()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
||||
case (ENAMETOOLONG): // A component of a pathname exceeds {NAME_MAX} characters, or an entire path name exceeds {PATH_MAX} characters (possibly as a result of expanding a symlink).
|
||||
throw tc::io::PathTooLongException(kClassName+"::removeFile()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
||||
case (ENOENT): // The named file does not exist.
|
||||
throw tc::io::FileNotFoundException(kClassName+"::removeFile()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
||||
case (ENOTDIR): // A component of the path prefix is not a directory.
|
||||
throw tc::io::DirectoryNotFoundException(kClassName+"::removeFile()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
||||
case (EFAULT): // Path points outside the process's allocated address space.
|
||||
case (EIO): // An I/O error occurs while deleting the directory entry or deallocating the inode.
|
||||
case (ELOOP): // Too many symbolic links are encountered in translating the pathname. This is taken to be indicative of a looping symbolic link.
|
||||
default:
|
||||
throw tc::io::IOException(kClassName+"::removeFile()", "Failed to remove file (" + PlatformErrorHandlingUtil::GetGnuErrorNumString(errno) + ")");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void tc::io::LocalFileSystem::openFile(const tc::io::Path& path, tc::io::FileMode mode, tc::io::FileAccess access, std::shared_ptr<tc::io::IStream>& stream)
|
||||
{
|
||||
stream = std::shared_ptr<tc::io::FileStream>(new tc::io::FileStream(path, mode, access));
|
||||
}
|
||||
|
||||
void tc::io::LocalFileSystem::createDirectory(const tc::io::Path& path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// convert Path to unicode string
|
||||
std::u16string unicode_path = path.to_u16string(tc::io::Path::Format::Win32);
|
||||
|
||||
// create directory
|
||||
if (CreateDirectoryW((LPCWSTR)unicode_path.c_str(), nullptr) == false && GetLastError() != ERROR_ALREADY_EXISTS)
|
||||
{
|
||||
DWORD error = GetLastError();
|
||||
switch (error)
|
||||
{
|
||||
default:
|
||||
throw tc::io::IOException(kClassName+"::createDirectory()", "Failed to create directory (" + PlatformErrorHandlingUtil::GetLastErrorString(error) + ")");
|
||||
}
|
||||
}
|
||||
#else
|
||||
// convert Path to unicode string
|
||||
std::string unicode_path = path.to_string(tc::io::Path::Format::POSIX);
|
||||
|
||||
if (mkdir(unicode_path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == -1 && errno != EEXIST)
|
||||
{
|
||||
switch (errno)
|
||||
{
|
||||
case (EACCES): // Search permission is denied for a component of the path prefix. -OR- Write permission is denied for the parent directory.
|
||||
case (EROFS): // The parent directory resides on a read-only file system.
|
||||
throw tc::UnauthorisedAccessException(kClassName+"::createDirectory()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
||||
case (ENOTDIR): // A component of the path prefix is not a directory.
|
||||
case (ENOENT): // A component of the path prefix does not exist or path is an empty string.
|
||||
throw tc::io::DirectoryNotFoundException(kClassName+"::removeFile()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
||||
case (ENAMETOOLONG): // A component of a pathname exceeded {NAME_MAX} characters, or an entire path name exceeded {PATH_MAX} characters.
|
||||
throw tc::io::PathTooLongException(kClassName+"::removeFile()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
||||
case (EISDIR): // The named file is the root directory.
|
||||
case (EDQUOT): // The new directory cannot be created because the user's quota of disk blocks on the file system that will contain the directory has been exhausted. -OR- The user's quota of inodes on the file system on which the directory is being created has been exhausted.
|
||||
//case (EEXIST): // The named file exists
|
||||
case (EFAULT): // Path points outside the process's allocated address space.
|
||||
case (EIO): // An I/O error occurred while reading from or writing to the file system. -OR- An I/O error occurred while making the directory entry or allocating the inode.
|
||||
case (ELOOP): // Too many symbolic links were encountered in translating the pathname. This is taken to be indicative of a looping symbolic link.
|
||||
case (EMLINK): // The parent directory already has {LINK_MAX} links.
|
||||
case (ENOSPC): // The new directory cannot be created because there is no space left on the file system that would contain it. -OR- There are no free inodes on the file system on which the directory is being created.
|
||||
default:
|
||||
throw tc::io::IOException(kClassName+"::createDirectory()", "Failed to create directory (" + PlatformErrorHandlingUtil::GetGnuErrorNumString(errno) + ")");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void tc::io::LocalFileSystem::removeDirectory(const tc::io::Path& path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// convert Path to unicode string
|
||||
std::u16string unicode_path = path.to_u16string(tc::io::Path::Format::Win32);
|
||||
|
||||
if (RemoveDirectoryW((wchar_t*)unicode_path.c_str()) == false)
|
||||
{
|
||||
DWORD error = GetLastError();
|
||||
switch (error)
|
||||
{
|
||||
case (ERROR_DIR_NOT_EMPTY):
|
||||
case (ERROR_DIRECTORY):
|
||||
default:
|
||||
throw tc::io::IOException(kClassName+"::removeDirectory()", "Failed to remove directory (" + PlatformErrorHandlingUtil::GetLastErrorString(error) + ")");
|
||||
}
|
||||
}
|
||||
#else
|
||||
// convert Path to unicode string
|
||||
std::string unicode_path = path.to_string(tc::io::Path::Format::POSIX);
|
||||
|
||||
if (rmdir(unicode_path.c_str()) == -1)
|
||||
{
|
||||
switch (errno)
|
||||
{
|
||||
case (EACCES): // Search permission is denied for a component of the path prefix. -OR- Write permission is denied on the directory containing the link to be removed.
|
||||
case (EROFS): // The directory entry to be removed resides on a read-only file system.
|
||||
case (EPERM): // The directory containing the directory to be removed is marked sticky, and neither the containing directory nor the directory to be removed are owned by the effective user ID.
|
||||
case (EBUSY): // The directory to be removed is the mount point for a mounted file system.
|
||||
throw tc::UnauthorisedAccessException(kClassName+"::removeDirectory()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
||||
case (ENAMETOOLONG):
|
||||
throw tc::io::PathTooLongException(kClassName+"::removeDirectory()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
||||
case (ENOENT): // The named directory does not exist.
|
||||
case (ENOTDIR): // A component of the path prefix is not a directory.
|
||||
throw tc::io::DirectoryNotFoundException(kClassName+"::removeDirectory()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
||||
case (ENOTEMPTY): // The named directory contains files other than `.' and `..' in it.
|
||||
throw tc::io::DirectoryNotEmptyException(kClassName+"::removeDirectory()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
||||
case (EFAULT): // Path points outside the process's allocated address space.
|
||||
case (EIO): // An I/O error occurred while reading from or writing to the file system.
|
||||
case (ELOOP): // Too many symbolic links are encountered in translating the pathname. This is taken to be indicative of a looping symbolic link.
|
||||
default:
|
||||
throw tc::io::IOException(kClassName+"::removeDirectory()", "Failed to remove directory (" + PlatformErrorHandlingUtil::GetGnuErrorNumString(errno) + ")");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void tc::io::LocalFileSystem::getWorkingDirectory(tc::io::Path& path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
std::shared_ptr<char16_t> raw_char16_path(new char16_t[MAX_PATH]);
|
||||
|
||||
// get current directory
|
||||
if (GetCurrentDirectoryW(MAX_PATH, (LPWSTR)(raw_char16_path.get())) == false)
|
||||
{
|
||||
DWORD error = GetLastError();
|
||||
switch (error)
|
||||
{
|
||||
default:
|
||||
throw tc::io::IOException(kClassName+"::getWorkingDirectory()", "Failed to get current working directory (" + PlatformErrorHandlingUtil::GetLastErrorString(error) + ")");
|
||||
}
|
||||
}
|
||||
|
||||
path = Path(raw_char16_path.get());
|
||||
#else
|
||||
setWorkingDirectory(Path("."));
|
||||
|
||||
std::shared_ptr<char> raw_current_working_directory(new char[PATH_MAX]);
|
||||
|
||||
if (getcwd(raw_current_working_directory.get(), PATH_MAX) == nullptr)
|
||||
{
|
||||
switch (errno)
|
||||
{
|
||||
case (EACCES): // Read or search permission was denied for a component of the pathname. This is only checked in limited cases, depending on implementation details.
|
||||
throw tc::UnauthorisedAccessException(kClassName+"::getWorkingDirectory()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
||||
case (EINVAL): // The size argument is zero.
|
||||
case (ENOENT): // A component of the pathname no longer exists.
|
||||
case (ENOMEM): // Insufficient memory is available.
|
||||
case (ERANGE): // The size argument is greater than zero but smaller than the length of the pathname plus 1.
|
||||
default:
|
||||
throw tc::io::IOException(kClassName+"::getWorkingDirectory()", "Failed to get current working directory (getcwd) (" + PlatformErrorHandlingUtil::GetGnuErrorNumString(errno) + ")");
|
||||
}
|
||||
}
|
||||
|
||||
path = Path(raw_current_working_directory.get());
|
||||
#endif
|
||||
}
|
||||
|
||||
void tc::io::LocalFileSystem::setWorkingDirectory(const tc::io::Path& path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// convert Path to unicode string
|
||||
std::u16string unicode_path = path.to_u16string(tc::io::Path::Format::Win32);
|
||||
|
||||
// delete file
|
||||
if (SetCurrentDirectoryW((LPCWSTR)unicode_path.c_str()) == false)
|
||||
{
|
||||
DWORD error = GetLastError();
|
||||
switch (error)
|
||||
{
|
||||
default:
|
||||
throw tc::io::IOException(kClassName+"::setWorkingDirectory()", "Failed to set current working directory (" + PlatformErrorHandlingUtil::GetLastErrorString(error) + ")");
|
||||
}
|
||||
}
|
||||
#else
|
||||
// convert Path to unicode string
|
||||
std::string unicode_path = path.to_string(tc::io::Path::Format::POSIX);
|
||||
|
||||
// get full path to directory
|
||||
if (chdir(unicode_path.c_str()) != 0)
|
||||
{
|
||||
switch (errno)
|
||||
{
|
||||
case (EACCES): // Search permission is denied for any component of the path name.
|
||||
throw tc::UnauthorisedAccessException(kClassName+"::setWorkingDirectory()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
||||
case (ENAMETOOLONG): // A component of a pathname exceeded {NAME_MAX} characters, or an entire path name exceeded {PATH_MAX} characters.
|
||||
throw tc::io::PathTooLongException(kClassName+"::setWorkingDirectory()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
||||
case (ENOENT): // The named directory does not exist.
|
||||
case (ENOTDIR): // A component of the path prefix is not a directory.
|
||||
throw tc::io::DirectoryNotFoundException(kClassName+"::setWorkingDirectory()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
||||
case (EFAULT): // Path points outside the process's allocated address space.
|
||||
case (EIO): // An I/O error occurred while reading from or writing to the file system.
|
||||
case (ELOOP): // Too many symbolic links were encountered in translating the pathname. This is taken to be indicative of a looping symbolic link.
|
||||
default:
|
||||
throw tc::io::IOException(kClassName+"::setWorkingDirectory()", "Failed to get directory info (chdir)(" + PlatformErrorHandlingUtil::GetGnuErrorNumString(errno) + ")");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void tc::io::LocalFileSystem::getDirectoryListing(const tc::io::Path& path, sDirectoryListing& info)
|
||||
{
|
||||
std::vector<std::string> child_dir_name_list;
|
||||
std::vector<std::string> child_file_name_list;
|
||||
Path current_directory_path;
|
||||
#ifdef _WIN32
|
||||
Path wildcard_path = path + tc::io::Path("*");
|
||||
|
||||
// convert Path to unicode string
|
||||
std::u16string unicode_path = wildcard_path.to_u16string(tc::io::Path::Format::Win32);
|
||||
|
||||
HANDLE dir_handle = INVALID_HANDLE_VALUE;
|
||||
WIN32_FIND_DATAW dir_entry;
|
||||
|
||||
dir_handle = FindFirstFileW((LPCWSTR)unicode_path.c_str(), &dir_entry);
|
||||
if (dir_handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
DWORD error = GetLastError();
|
||||
switch (error)
|
||||
{
|
||||
default:
|
||||
throw tc::io::IOException(kClassName+"::getDirectoryListing()", "Failed to open directory (" + PlatformErrorHandlingUtil::GetLastErrorString(error) + ")");
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
std::string utf8_name;
|
||||
tc::string::TranscodeUtil::UTF16ToUTF8((char16_t*)dir_entry.cFileName, utf8_name);
|
||||
|
||||
if (dir_entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
{
|
||||
child_dir_name_list.push_back(utf8_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
child_file_name_list.push_back(utf8_name);
|
||||
}
|
||||
} while (FindNextFileW(dir_handle, &dir_entry) != 0);
|
||||
|
||||
// throw error where GetLastError() isn't just that there were no more files
|
||||
if (GetLastError() != ERROR_NO_MORE_FILES)
|
||||
{
|
||||
FindClose(dir_handle);
|
||||
|
||||
DWORD error = GetLastError();
|
||||
switch (error)
|
||||
{
|
||||
default:
|
||||
throw tc::io::IOException(kClassName+"::getDirectoryListing()", "Failed to open directory (" + PlatformErrorHandlingUtil::GetLastErrorString(error) + ")");
|
||||
}
|
||||
}
|
||||
|
||||
FindClose(dir_handle);
|
||||
|
||||
|
||||
// save current dir for later
|
||||
Path prev_current_dir;
|
||||
getWorkingDirectory(prev_current_dir);
|
||||
|
||||
// change the directory
|
||||
setWorkingDirectory(path);
|
||||
|
||||
// save the path
|
||||
getWorkingDirectory(current_directory_path);
|
||||
|
||||
// restore current directory
|
||||
setWorkingDirectory(prev_current_dir);
|
||||
#else
|
||||
// convert Path to unicode string
|
||||
std::string unicode_path = path.to_string(tc::io::Path::Format::POSIX);
|
||||
|
||||
// open directory
|
||||
DIR *dp;
|
||||
dp = opendir(unicode_path.c_str());
|
||||
if (dp == nullptr)
|
||||
{
|
||||
switch (errno)
|
||||
{
|
||||
case (EACCES): // Permission denied.
|
||||
throw tc::UnauthorisedAccessException(kClassName+"::getDirectoryListing()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
||||
case (ENOTDIR): // A component of the path prefix is not a directory. // name is not a directory.
|
||||
case (ENOENT): // Directory does not exist, or name is an empty string.
|
||||
throw tc::io::DirectoryNotFoundException(kClassName+"::getDirectoryListing()", PlatformErrorHandlingUtil::GetGnuErrorNumString(errno));
|
||||
case (EBADF): // fd is not a valid file descriptor open for reading.
|
||||
case (EMFILE):
|
||||
case (ENFILE):
|
||||
case (ENOMEM):
|
||||
default:
|
||||
throw tc::io::IOException(kClassName+"::getDirectoryListing()", "Failed to get directory info (opendir)(" + PlatformErrorHandlingUtil::GetGnuErrorNumString(errno) + ")");
|
||||
}
|
||||
}
|
||||
|
||||
// get file and directory names
|
||||
child_dir_name_list.clear();
|
||||
child_file_name_list.clear();
|
||||
|
||||
// since errno can be set by external sources it will be cleared, since the conditions for checking errno being set aren't specific to failure
|
||||
errno = 0;
|
||||
for (struct dirent *ep = readdir(dp); ep != nullptr && errno == 0; ep = readdir(dp))
|
||||
{
|
||||
if (ep->d_type == DT_DIR)
|
||||
{
|
||||
child_dir_name_list.push_back(std::string(ep->d_name));
|
||||
}
|
||||
else if (ep->d_type == DT_REG)
|
||||
{
|
||||
child_file_name_list.push_back(std::string(ep->d_name));
|
||||
}
|
||||
}
|
||||
|
||||
// throw an error if necessary
|
||||
if (errno != 0)
|
||||
{
|
||||
switch (errno)
|
||||
{
|
||||
case (EBADF): // fd is not a valid file descriptor open for reading.
|
||||
case (EIO): // An I/O error occurred while reading from or writing to the file system.
|
||||
default:
|
||||
throw tc::io::IOException(kClassName+"::getDirectoryListing()", "Failed to get directory info (readdir)(" + PlatformErrorHandlingUtil::GetGnuErrorNumString(errno) + ")");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// close dp
|
||||
closedir(dp);
|
||||
|
||||
// save current dir for later
|
||||
Path prev_current_dir;
|
||||
getWorkingDirectory(prev_current_dir);
|
||||
|
||||
// change the directory
|
||||
setWorkingDirectory(path);
|
||||
|
||||
// save the path
|
||||
getWorkingDirectory(current_directory_path);
|
||||
|
||||
// restore current directory
|
||||
setWorkingDirectory(prev_current_dir);
|
||||
#endif
|
||||
info.abs_path = current_directory_path;
|
||||
info.dir_list = child_dir_name_list;
|
||||
info.file_list = child_file_name_list;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#pragma warning(default : 4065) // reenable warning for switch case with only default case
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,49 @@
|
||||
#include <tc/io/MemorySource.h>
|
||||
#include <tc/io/IOUtil.h>
|
||||
|
||||
const std::string tc::io::MemorySource::kClassName = "tc::io::MemorySource";
|
||||
|
||||
tc::io::MemorySource::MemorySource() :
|
||||
mData()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
tc::io::MemorySource::MemorySource(const tc::ByteData& byte_data) :
|
||||
mData(byte_data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
tc::io::MemorySource::MemorySource(tc::ByteData&& byte_data) :
|
||||
mData(std::move(byte_data))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
tc::io::MemorySource::MemorySource(const byte_t* data, size_t len) :
|
||||
mData(data, len)
|
||||
{
|
||||
}
|
||||
|
||||
int64_t tc::io::MemorySource::length()
|
||||
{
|
||||
return int64_t(mData.size());
|
||||
}
|
||||
|
||||
tc::ByteData tc::io::MemorySource::pullData(int64_t offset, size_t count)
|
||||
{
|
||||
size_t read_len = IOUtil::getReadableCount(this->length(), offset, count);
|
||||
|
||||
// if the read length is zero then return now.
|
||||
if (read_len == 0)
|
||||
return tc::ByteData();
|
||||
|
||||
|
||||
tc::ByteData out(read_len);
|
||||
|
||||
memcpy(out.data(), mData.data() + offset, read_len);
|
||||
|
||||
return out;
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
#include <tc/io/MemoryStream.h>
|
||||
|
||||
#include <limits>
|
||||
#include <tc/io/StreamUtil.h>
|
||||
#include <tc/io/IOUtil.h>
|
||||
|
||||
const std::string tc::io::MemoryStream::kClassName = "tc::io::MemoryStream";
|
||||
|
||||
tc::io::MemoryStream::MemoryStream() :
|
||||
MemoryStream(0)
|
||||
{}
|
||||
|
||||
tc::io::MemoryStream::MemoryStream(size_t length) :
|
||||
mData(),
|
||||
mPosition(0)
|
||||
{
|
||||
setLength(length);
|
||||
}
|
||||
|
||||
tc::io::MemoryStream::MemoryStream(const tc::ByteData& byte_data) :
|
||||
mData(byte_data),
|
||||
mPosition(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
tc::io::MemoryStream::MemoryStream(tc::ByteData&& byte_data) :
|
||||
mData(std::move(byte_data)),
|
||||
mPosition(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
tc::io::MemoryStream::MemoryStream(const byte_t* data, size_t len) :
|
||||
mData(data, len),
|
||||
mPosition(0)
|
||||
{
|
||||
}
|
||||
|
||||
bool tc::io::MemoryStream::canRead() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tc::io::MemoryStream::canWrite() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tc::io::MemoryStream::canSeek() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int64_t tc::io::MemoryStream::length()
|
||||
{
|
||||
return mData.size();
|
||||
}
|
||||
|
||||
int64_t tc::io::MemoryStream::position()
|
||||
{
|
||||
return mPosition;
|
||||
}
|
||||
|
||||
size_t tc::io::MemoryStream::read(byte_t* ptr, size_t count)
|
||||
{
|
||||
if (ptr == nullptr)
|
||||
{
|
||||
throw tc::ArgumentNullException(kClassName+"::read()", "ptr is null.");
|
||||
}
|
||||
|
||||
count = IOUtil::getReadableCount(IOUtil::castSizeToInt64(mData.size()), mPosition, count);
|
||||
|
||||
memcpy(ptr, mData.data() + mPosition, count);
|
||||
|
||||
mPosition += IOUtil::castSizeToInt64(count);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t tc::io::MemoryStream::write(const byte_t* ptr, size_t count)
|
||||
{
|
||||
if (ptr == nullptr)
|
||||
{
|
||||
throw tc::ArgumentNullException(kClassName+"::write()", "ptr is null.");
|
||||
}
|
||||
|
||||
// check if the position is past the end of stream, enlarge stream in this case
|
||||
if ((IOUtil::castInt64ToSize(mPosition) + count) > mData.size())
|
||||
{
|
||||
setLength(mPosition + IOUtil::castSizeToInt64(count));
|
||||
}
|
||||
|
||||
count = IOUtil::getWritableCount(IOUtil::castSizeToInt64(mData.size()), mPosition, count);
|
||||
|
||||
memcpy(mData.data() + mPosition, ptr, count);
|
||||
|
||||
mPosition += IOUtil::castSizeToInt64(count);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int64_t tc::io::MemoryStream::seek(int64_t offset, SeekOrigin origin)
|
||||
{
|
||||
int64_t new_pos = StreamUtil::getSeekResult(offset, origin, mPosition, (int64_t)mData.size());
|
||||
|
||||
if (new_pos < 0)
|
||||
{
|
||||
throw tc::ArgumentOutOfRangeException(kClassName+"::seek()", "New position is negative.");
|
||||
}
|
||||
|
||||
mPosition = new_pos;
|
||||
|
||||
return mPosition;
|
||||
}
|
||||
|
||||
void tc::io::MemoryStream::setLength(int64_t length)
|
||||
{
|
||||
// check length isn't too large (int64_t could be larger than size_t)
|
||||
if (IOUtil::castInt64ToSize(length) > std::numeric_limits<size_t>::max())
|
||||
{
|
||||
throw tc::ArgumentOutOfRangeException(kClassName+"::setLength()", "Length greater than maxium possible length for MemoryStream");
|
||||
}
|
||||
|
||||
// check length isn't negative
|
||||
if (length < 0)
|
||||
{
|
||||
throw tc::ArgumentOutOfRangeException(kClassName+"::setLength()", "Length is negative.");
|
||||
}
|
||||
|
||||
// create new ByteData
|
||||
ByteData data(IOUtil::castInt64ToSize(length));
|
||||
|
||||
// determine copy length (between old and new ByteData)
|
||||
size_t copy_len = std::min<size_t>(data.size(), mData.size());
|
||||
|
||||
// copy from old to new ByteData
|
||||
memcpy(data.data(), mData.data(), copy_len);
|
||||
|
||||
// re-assign mData (this frees the old mData)
|
||||
mData = data;
|
||||
|
||||
// reduce position if shrunk
|
||||
mPosition = std::min<int64_t>(mPosition, int64_t(mData.size()));
|
||||
}
|
||||
|
||||
void tc::io::MemoryStream::flush()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void tc::io::MemoryStream::dispose()
|
||||
{
|
||||
mData = ByteData();
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
#include <tc/io/OverlayedSource.h>
|
||||
#include <tc/io/IOUtil.h>
|
||||
|
||||
const std::string tc::io::OverlayedSource::kClassName = "tc::io::OverlayedSource";
|
||||
|
||||
tc::io::OverlayedSource::OverlayedSource() :
|
||||
mBaseSource(),
|
||||
mOverlaySourceInfos()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
tc::io::OverlayedSource::OverlayedSource(const std::shared_ptr<tc::io::ISource>& base_source, const std::shared_ptr<tc::io::ISource>& overlay_source, int64_t offset, int64_t length) :
|
||||
OverlayedSource(base_source, {OverlaySourceInfo{overlay_source, offset, length}})
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
tc::io::OverlayedSource::OverlayedSource(const std::shared_ptr<tc::io::ISource>& base_source, const std::vector<OverlaySourceInfo>& overlay_source_infos)
|
||||
{
|
||||
// throw exception if the base source is null
|
||||
if (base_source == nullptr)
|
||||
{
|
||||
throw tc::ArgumentNullException(kClassName+"::OverlayedSource()", "base_source was null.");
|
||||
}
|
||||
|
||||
// copy base source ptr
|
||||
mBaseSource = base_source;
|
||||
|
||||
// check/import overlay sources
|
||||
for (auto itr = overlay_source_infos.begin(); itr != overlay_source_infos.end(); itr++)
|
||||
{
|
||||
// skip regions with no length
|
||||
if (itr->length == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// throw exception if a overlay source is null
|
||||
if (itr->overlay_source == nullptr)
|
||||
{
|
||||
throw tc::ArgumentNullException(kClassName+"::OverlayedSource()", "overlay_source was null.");
|
||||
}
|
||||
|
||||
// throw exception if overly region offset is negative
|
||||
if (itr->offset < 0)
|
||||
{
|
||||
throw tc::ArgumentOutOfRangeException(kClassName+"::OverlayedSource()", "Invalid overlay region. Overlay offset is negative.");
|
||||
}
|
||||
|
||||
// throw exception if the overlay region offset is beyond the length of the base source
|
||||
if (itr->offset > mBaseSource->length())
|
||||
{
|
||||
throw tc::ArgumentOutOfRangeException(kClassName+"::OverlayedSource()", "Invalid overlay region. Overlay offset beyond length of base_source.");
|
||||
}
|
||||
|
||||
// throw exception if the overlay region exceeds the length of the base source
|
||||
if ((itr->offset + itr->length) > mBaseSource->length())
|
||||
{
|
||||
throw tc::ArgumentOutOfRangeException(kClassName+"::OverlayedSource()", "Invalid overlay region. Overlay region exceeds the length of base_source.");
|
||||
}
|
||||
|
||||
// throw exception if the overlay region exceeds the length of the overlay source
|
||||
if (itr->length > itr->overlay_source->length())
|
||||
{
|
||||
throw tc::ArgumentOutOfRangeException(kClassName+"::OverlayedSource()", "Invalid overlay region. Overlay region exceeds the length of overlay_source.");
|
||||
}
|
||||
|
||||
// save overlay source info
|
||||
mOverlaySourceInfos.push_back(*itr);
|
||||
}
|
||||
}
|
||||
|
||||
int64_t tc::io::OverlayedSource::length()
|
||||
{
|
||||
// return 0 if mBaseSource is null, otherwise deref the pointer and get the length
|
||||
return mBaseSource == nullptr ? 0 : mBaseSource->length();
|
||||
}
|
||||
|
||||
tc::ByteData tc::io::OverlayedSource::pullData(int64_t offset, size_t count)
|
||||
{
|
||||
// return empty byte_data if the base is empty
|
||||
if (mBaseSource == nullptr)
|
||||
return tc::ByteData();
|
||||
|
||||
size_t read_len = IOUtil::getReadableCount(this->length(), offset, count);
|
||||
|
||||
// if the read length is zero then return now.
|
||||
if (read_len == 0)
|
||||
return tc::ByteData();
|
||||
|
||||
// get base source byte_data, this will be overwritten with regions
|
||||
tc::ByteData out = mBaseSource->pullData(offset, count);
|
||||
|
||||
// iterate thru the overlays
|
||||
for (auto itr = mOverlaySourceInfos.begin(); itr != mOverlaySourceInfos.end(); itr++)
|
||||
{
|
||||
int64_t overlay_pull_offset = 0;
|
||||
size_t overlay_pull_count = 0;
|
||||
|
||||
// skip overlay if the pullable count is 0
|
||||
getOverlaySourcePullableRegion(offset, count, *itr, overlay_pull_offset, overlay_pull_count);
|
||||
if (overlay_pull_count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// pull data from overlay
|
||||
ByteData overlay_pull = itr->overlay_source->pullData(overlay_pull_offset, overlay_pull_count);
|
||||
|
||||
// adjust the outsize to be the minimum of the ByteData & attempted pull_count
|
||||
overlay_pull_count = std::min<size_t>(overlay_pull.size(), overlay_pull_count);
|
||||
|
||||
// copy into out buffer
|
||||
int64_t overlay_offset_in_out = (overlay_pull_offset + itr->offset) - offset;
|
||||
memcpy(out.data() + overlay_offset_in_out, overlay_pull.data(), overlay_pull_count);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void tc::io::OverlayedSource::getOverlaySourcePullableRegion(int64_t base_pull_offset, size_t base_pull_count, const OverlaySourceInfo& overlay_info, int64_t& overlay_pull_offset, size_t& overlay_pull_count)
|
||||
{
|
||||
int64_t overlay_relative_start_offset = base_pull_offset - overlay_info.offset;
|
||||
int64_t overlay_relative_end_offset = overlay_relative_start_offset + int64_t(base_pull_count);
|
||||
|
||||
// if the start offset > overlay length: then the data starts after the overlay ends
|
||||
// if the end offset < 0: then the data ends before the overlay begins
|
||||
if (overlay_relative_start_offset > overlay_info.length || overlay_relative_end_offset < 0)
|
||||
{
|
||||
overlay_pull_offset = 0;
|
||||
overlay_pull_count = 0;
|
||||
}
|
||||
// otherwise some or all of the overlay can be used
|
||||
else
|
||||
{
|
||||
// the offset must be reset to zero if it is negative
|
||||
overlay_pull_offset = overlay_relative_start_offset > 0 ? overlay_relative_start_offset : 0;
|
||||
|
||||
// getReadableSize will cap the amount read if it exceeds the base_length
|
||||
overlay_pull_count = IOUtil::getReadableCount(overlay_info.length, overlay_pull_offset, size_t(overlay_relative_end_offset - overlay_pull_offset));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#include <tc/io/PaddingSource.h>
|
||||
#include <tc/io/IOUtil.h>
|
||||
|
||||
const std::string tc::io::PaddingSource::kClassName = "tc::io::PaddingSource";
|
||||
|
||||
tc::io::PaddingSource::PaddingSource() :
|
||||
mSourceLength(0),
|
||||
mPaddingByte(0)
|
||||
{
|
||||
}
|
||||
|
||||
tc::io::PaddingSource::PaddingSource(byte_t padding_byte, int64_t size) :
|
||||
mSourceLength(size),
|
||||
mPaddingByte(padding_byte)
|
||||
{
|
||||
if (size < 0)
|
||||
{
|
||||
throw tc::ArgumentOutOfRangeException(kClassName, "length is negative");
|
||||
}
|
||||
}
|
||||
|
||||
int64_t tc::io::PaddingSource::length()
|
||||
{
|
||||
return mSourceLength;
|
||||
}
|
||||
|
||||
tc::ByteData tc::io::PaddingSource::pullData(int64_t offset, size_t count)
|
||||
{
|
||||
tc::ByteData data(IOUtil::getReadableCount(mSourceLength, offset, count));
|
||||
|
||||
memset(data.data(), mPaddingByte, data.size());
|
||||
|
||||
return data;
|
||||
}
|
||||
@@ -0,0 +1,331 @@
|
||||
|
||||
#include <tc/io/Path.h>
|
||||
#include <tc/string.h>
|
||||
#include <tc/Exception.h>
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
|
||||
static const char kWindowsPathSeparator = '\\'; /**< Path separator used on Microsoft Windows based systems */
|
||||
static const char kPosixPathSeparator = '/'; /**< Path separator used on POSIX based systems */
|
||||
#ifdef _WIN32
|
||||
static const char kNativePathSeparator = kWindowsPathSeparator; /**< Path separator for the native environment */
|
||||
#else
|
||||
static const char kNativePathSeparator = kPosixPathSeparator; /**< Path separator for the native environment */
|
||||
#endif
|
||||
|
||||
const std::string tc::io::Path::kClassName = "tc::io::Path";
|
||||
|
||||
tc::io::Path::Path()
|
||||
{}
|
||||
|
||||
tc::io::Path::Path(std::initializer_list<std::string> list) :
|
||||
mUnicodePath(list)
|
||||
{
|
||||
}
|
||||
|
||||
tc::io::Path::Path(const std::string& path)
|
||||
{
|
||||
initializePath(path);
|
||||
}
|
||||
|
||||
tc::io::Path::Path(const std::u16string& path)
|
||||
{
|
||||
std::string utf8_path;
|
||||
string::TranscodeUtil::UTF16ToUTF8(path, utf8_path);
|
||||
initializePath(utf8_path);
|
||||
}
|
||||
|
||||
tc::io::Path::Path(const std::u32string& path)
|
||||
{
|
||||
std::string utf8_path;
|
||||
string::TranscodeUtil::UTF32ToUTF8(path, utf8_path);
|
||||
initializePath(utf8_path);
|
||||
}
|
||||
|
||||
tc::io::Path tc::io::Path::operator+(const Path& other) const
|
||||
{
|
||||
Path new_path = *this;
|
||||
new_path.appendPath(other.mUnicodePath);
|
||||
return new_path;
|
||||
}
|
||||
|
||||
void tc::io::Path::operator+=(const Path& other)
|
||||
{
|
||||
appendPath(other.mUnicodePath);
|
||||
}
|
||||
|
||||
bool tc::io::Path::operator==(const Path& other) const
|
||||
{
|
||||
return mUnicodePath == other.mUnicodePath;
|
||||
}
|
||||
|
||||
bool tc::io::Path::operator!=(const Path& other) const
|
||||
{
|
||||
return !(this->operator==(other));
|
||||
}
|
||||
|
||||
bool tc::io::Path::operator<(const Path& other) const
|
||||
{
|
||||
int cmp_score = 0;
|
||||
|
||||
auto self_itr = this->begin();
|
||||
auto other_itr = other.begin();
|
||||
|
||||
// in this loop for as long as both path has an itr, it'll compare them
|
||||
for (; self_itr != this->end() && other_itr != other.end(); self_itr++, other_itr++)
|
||||
{
|
||||
cmp_score = self_itr->compare(*other_itr);
|
||||
if (cmp_score != 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// if one of the itrs isn't the end, then that one is "larger"
|
||||
// it can't be both or the prior loop won't have ended
|
||||
if (cmp_score == 0 && (self_itr != this->end() || other_itr != other.end()))
|
||||
{
|
||||
cmp_score = self_itr == this->end() ? -1 : 1;
|
||||
}
|
||||
|
||||
return cmp_score < 0;
|
||||
}
|
||||
|
||||
tc::io::Path::iterator tc::io::Path::begin()
|
||||
{
|
||||
return mUnicodePath.begin();
|
||||
}
|
||||
|
||||
std::string& tc::io::Path::front()
|
||||
{
|
||||
return mUnicodePath.front();
|
||||
}
|
||||
|
||||
const std::string& tc::io::Path::front() const
|
||||
{
|
||||
return mUnicodePath.front();
|
||||
}
|
||||
|
||||
std::string& tc::io::Path::back()
|
||||
{
|
||||
return mUnicodePath.back();
|
||||
}
|
||||
|
||||
const std::string& tc::io::Path::back() const
|
||||
{
|
||||
return mUnicodePath.back();
|
||||
}
|
||||
|
||||
tc::io::Path::const_iterator tc::io::Path::begin() const
|
||||
{
|
||||
return mUnicodePath.begin();
|
||||
}
|
||||
|
||||
tc::io::Path::iterator tc::io::Path::end()
|
||||
{
|
||||
return mUnicodePath.end();
|
||||
}
|
||||
|
||||
tc::io::Path::const_iterator tc::io::Path::end() const
|
||||
{
|
||||
return mUnicodePath.end();
|
||||
}
|
||||
|
||||
void tc::io::Path::pop_front()
|
||||
{
|
||||
mUnicodePath.pop_front();
|
||||
}
|
||||
|
||||
void tc::io::Path::pop_back()
|
||||
{
|
||||
mUnicodePath.pop_back();
|
||||
}
|
||||
|
||||
void tc::io::Path::push_front(const std::string& str)
|
||||
{
|
||||
mUnicodePath.push_front(str);
|
||||
}
|
||||
|
||||
void tc::io::Path::push_back(const std::string& str)
|
||||
{
|
||||
mUnicodePath.push_back(str);
|
||||
}
|
||||
|
||||
void tc::io::Path::clear()
|
||||
{
|
||||
mUnicodePath.clear();
|
||||
}
|
||||
|
||||
size_t tc::io::Path::size() const
|
||||
{
|
||||
return mUnicodePath.size();
|
||||
}
|
||||
|
||||
bool tc::io::Path::empty() const
|
||||
{
|
||||
return mUnicodePath.empty();
|
||||
}
|
||||
|
||||
tc::io::Path tc::io::Path::subpath(size_t pos, size_t len) const
|
||||
{
|
||||
tc::io::Path out_path;
|
||||
|
||||
auto itr = begin();
|
||||
size_t index = 0;
|
||||
|
||||
// while the out_path size is less than len and the iterator hasn't ended
|
||||
while ( out_path.size() < len && itr != end() )
|
||||
{
|
||||
// provided the index >= pos save the element
|
||||
if (index >= pos)
|
||||
{
|
||||
out_path.push_back(*itr);
|
||||
}
|
||||
|
||||
itr++;
|
||||
index++;
|
||||
}
|
||||
|
||||
return out_path;
|
||||
}
|
||||
|
||||
tc::io::Path tc::io::Path::subpath(const_iterator begin, const_iterator end) const
|
||||
{
|
||||
tc::io::Path out_path;
|
||||
|
||||
for (auto itr = begin; itr != end && itr != this->end(); itr++)
|
||||
{
|
||||
out_path.push_back(*itr);
|
||||
}
|
||||
|
||||
return out_path;
|
||||
}
|
||||
|
||||
std::string tc::io::Path::to_string(Format format) const
|
||||
{
|
||||
std::string path_str = "";
|
||||
|
||||
if (format == Path::Format::Native)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
format = Path::Format::Win32;
|
||||
#else
|
||||
format = Path::Format::POSIX;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string path_separator_str;
|
||||
switch (format)
|
||||
{
|
||||
case (Path::Format::POSIX):
|
||||
path_separator_str = fmt::format("{:c}", kPosixPathSeparator);
|
||||
break;
|
||||
case (Path::Format::Win32):
|
||||
path_separator_str = fmt::format("{:c}", kWindowsPathSeparator);
|
||||
break;
|
||||
default:
|
||||
throw tc::ArgumentException(kClassName, "Invalid Format type.");
|
||||
}
|
||||
|
||||
// special case where the path has one element and its the root path
|
||||
if (this->size() == 1)
|
||||
{
|
||||
// for POSIX style this is the empty string ("/")
|
||||
if (format == Path::Format::POSIX && this->front() == "")
|
||||
return path_separator_str;
|
||||
// for Win32 style this is a drive letter followed by ':' e.g. ("C:\")
|
||||
else if (format == Path::Format::Win32 && std::regex_match(this->front(), std::regex("^([A-Z,a-z]):")))
|
||||
return fmt::format("{}{}", this->front(), path_separator_str);
|
||||
}
|
||||
|
||||
for (const_iterator itr = this->begin(); itr != this->end(); itr++)
|
||||
{
|
||||
path_str += *itr;
|
||||
// don't print path separator where it would be trailing character
|
||||
if (itr != --(this->end()))
|
||||
path_str += path_separator_str;
|
||||
}
|
||||
|
||||
return path_str;
|
||||
}
|
||||
|
||||
std::u16string tc::io::Path::to_u16string(Format format) const
|
||||
{
|
||||
std::string u8string = to_string(format);
|
||||
std::u16string u16string;
|
||||
|
||||
// convert
|
||||
string::TranscodeUtil::UTF8ToUTF16(u8string, u16string);
|
||||
|
||||
// return
|
||||
return u16string;
|
||||
}
|
||||
|
||||
std::u32string tc::io::Path::to_u32string(Format format) const
|
||||
{
|
||||
std::string u8string = to_string(format);
|
||||
std::u32string u32string;
|
||||
|
||||
// convert
|
||||
string::TranscodeUtil::UTF8ToUTF32(u8string, u32string);
|
||||
|
||||
// return
|
||||
return u32string;
|
||||
}
|
||||
|
||||
tc::io::Path::operator std::string() const
|
||||
{
|
||||
return to_string(Format::Native);
|
||||
}
|
||||
|
||||
tc::io::Path::operator std::u16string() const
|
||||
{
|
||||
return to_u16string(Format::Native);
|
||||
}
|
||||
|
||||
tc::io::Path::operator std::u32string() const
|
||||
{
|
||||
return to_u32string(Format::Native);
|
||||
}
|
||||
|
||||
void tc::io::Path::initializePath(const std::string& src)
|
||||
{
|
||||
size_t windows_slash_count = 0;
|
||||
size_t posix_slash_count = 0;
|
||||
for (size_t i = 0; i < src.size(); i++)
|
||||
{
|
||||
if (src[i] == kWindowsPathSeparator)
|
||||
windows_slash_count += 1;
|
||||
else if (src[i] == kPosixPathSeparator)
|
||||
posix_slash_count += 1;
|
||||
}
|
||||
|
||||
if (windows_slash_count != 0 && posix_slash_count != 0)
|
||||
{
|
||||
throw tc::ArgumentException(kClassName, "Path literal has both forward ('/') and backward ('\\') path separators.");
|
||||
}
|
||||
|
||||
char path_delimiter = kNativePathSeparator;
|
||||
if (windows_slash_count > 0)
|
||||
path_delimiter = kWindowsPathSeparator;
|
||||
else if (posix_slash_count > 0)
|
||||
path_delimiter = kPosixPathSeparator;
|
||||
|
||||
|
||||
std::stringstream src_stream(src);
|
||||
|
||||
std::string element;
|
||||
while (std::getline(src_stream, element, path_delimiter))
|
||||
{
|
||||
mUnicodePath.push_back(element);
|
||||
}
|
||||
}
|
||||
|
||||
void tc::io::Path::appendPath(const std::list<std::string>& other)
|
||||
{
|
||||
for (std::list<std::string>::const_iterator itr = other.begin(); itr != other.end(); itr++)
|
||||
{
|
||||
mUnicodePath.push_back(*itr);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#include <tc/io/PathUtil.h>
|
||||
#include <tc/string.h>
|
||||
|
||||
void tc::io::PathUtil::pathToWindowsUTF16(const tc::io::Path& path, std::u16string& out)
|
||||
{
|
||||
out = path.to_u16string(tc::io::Path::Format::Win32);
|
||||
}
|
||||
|
||||
void tc::io::PathUtil::pathToUnixUTF8(const tc::io::Path& path, std::string& out)
|
||||
{
|
||||
out = path.to_string(tc::io::Path::Format::POSIX);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#include <tc/io/StreamSink.h>
|
||||
|
||||
const std::string tc::io::StreamSink::kClassName = "tc::io::StreamSink";
|
||||
|
||||
tc::io::StreamSink::StreamSink() :
|
||||
mBaseStream(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
tc::io::StreamSink::StreamSink(const std::shared_ptr<tc::io::IStream>& stream) :
|
||||
mBaseStream(stream)
|
||||
{
|
||||
if (mBaseStream == nullptr)
|
||||
{
|
||||
throw tc::ArgumentNullException(kClassName, "The base stream is null.");
|
||||
}
|
||||
|
||||
if (mBaseStream->canWrite() == false)
|
||||
{
|
||||
throw tc::NotSupportedException(kClassName, "The base stream does not support writing.");
|
||||
}
|
||||
|
||||
if (mBaseStream->canSeek() == false)
|
||||
{
|
||||
throw tc::NotSupportedException(kClassName, "The base stream does not support seeking.");
|
||||
}
|
||||
}
|
||||
|
||||
int64_t tc::io::StreamSink::length()
|
||||
{
|
||||
return mBaseStream == nullptr ? 0 :mBaseStream->length();
|
||||
}
|
||||
|
||||
void tc::io::StreamSink::setLength(int64_t length)
|
||||
{
|
||||
if (mBaseStream == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::setLength()", "The base stream was not initialized.");
|
||||
}
|
||||
|
||||
mBaseStream->setLength(length);
|
||||
}
|
||||
|
||||
size_t tc::io::StreamSink::pushData(const tc::ByteData& data, int64_t offset)
|
||||
{
|
||||
if (mBaseStream == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::pushData()", "The base stream was not initialized.");
|
||||
}
|
||||
|
||||
mBaseStream->seek(offset, tc::io::SeekOrigin::Begin);
|
||||
|
||||
return mBaseStream->write(data.data(), data.size());
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
#include <tc/io/StreamSource.h>
|
||||
#include <tc/io/IOUtil.h>
|
||||
|
||||
const std::string tc::io::StreamSource::kClassName = "tc::io::StreamSource";
|
||||
|
||||
tc::io::StreamSource::StreamSource() :
|
||||
mBaseStream(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
tc::io::StreamSource::StreamSource(const std::shared_ptr<tc::io::IStream>& stream) :
|
||||
mBaseStream(stream)
|
||||
{
|
||||
if (mBaseStream == nullptr)
|
||||
{
|
||||
throw tc::ArgumentNullException(kClassName, "The base stream is null.");
|
||||
}
|
||||
|
||||
if (mBaseStream->canRead() == false)
|
||||
{
|
||||
throw tc::NotSupportedException(kClassName, "The base stream does not support reading.");
|
||||
}
|
||||
|
||||
if (mBaseStream->canSeek() == false)
|
||||
{
|
||||
throw tc::NotSupportedException(kClassName, "The base stream does not support seeking.");
|
||||
}
|
||||
}
|
||||
|
||||
int64_t tc::io::StreamSource::length()
|
||||
{
|
||||
return mBaseStream == nullptr ? 0 : mBaseStream->length();
|
||||
}
|
||||
|
||||
tc::ByteData tc::io::StreamSource::pullData(int64_t offset, size_t count)
|
||||
{
|
||||
// get readable count
|
||||
size_t read_count = IOUtil::getReadableCount(this->length(), offset, count);
|
||||
|
||||
// return if nothing is to be read
|
||||
if (read_count == 0)
|
||||
{
|
||||
return tc::ByteData();
|
||||
}
|
||||
|
||||
// allocate ByteData
|
||||
ByteData data(read_count);
|
||||
|
||||
// read from stream (note this will not be called if mBaseStream is null, as in that case read_count == 0, and this code won't be reached)
|
||||
mBaseStream->seek(offset, tc::io::SeekOrigin::Begin);
|
||||
mBaseStream->read(data.data(), data.size());
|
||||
|
||||
// return populated ByteData
|
||||
return data;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#include <tc/io/StreamUtil.h>
|
||||
|
||||
int64_t tc::io::StreamUtil::getSeekResult(int64_t offset, tc::io::SeekOrigin origin, int64_t current_position, int64_t stream_length)
|
||||
{
|
||||
int64_t new_pos = 0;
|
||||
switch (origin)
|
||||
{
|
||||
case (SeekOrigin::Begin):
|
||||
new_pos = offset;
|
||||
break;
|
||||
case (SeekOrigin::Current):
|
||||
new_pos = current_position + offset;
|
||||
break;
|
||||
case (SeekOrigin::End):
|
||||
new_pos = stream_length + offset;
|
||||
break;
|
||||
default:
|
||||
throw tc::ArgumentOutOfRangeException("Illegal value for origin.");
|
||||
}
|
||||
|
||||
return new_pos;
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
#include <tc/io/SubFileSystem.h>
|
||||
|
||||
const std::string tc::io::SubFileSystem::kClassName = "tc::io::SubFileSystem";
|
||||
|
||||
tc::io::SubFileSystem::SubFileSystem() :
|
||||
mBaseFileSystem(),
|
||||
mBasePathResolver(),
|
||||
mSubPathResolver()
|
||||
{
|
||||
}
|
||||
|
||||
tc::io::SubFileSystem::SubFileSystem(const std::shared_ptr<tc::io::IFileSystem>& file_system, const tc::io::Path& base_path) :
|
||||
SubFileSystem()
|
||||
{
|
||||
// copy IFileSystem ptr
|
||||
mBaseFileSystem = file_system;
|
||||
|
||||
if (mBaseFileSystem == nullptr)
|
||||
{
|
||||
throw tc::ArgumentNullException(kClassName, "file_system is null");
|
||||
}
|
||||
else if (mBaseFileSystem->state().test(RESFLAG_READY) == false)
|
||||
{
|
||||
throw tc::InvalidOperationException(kClassName, "file_system is not ready");
|
||||
}
|
||||
|
||||
// save current path
|
||||
tc::io::Path prev_canonical_base_path;
|
||||
mBaseFileSystem->getWorkingDirectory(prev_canonical_base_path);
|
||||
|
||||
// get full path of root
|
||||
tc::io::Path canonical_base_path;
|
||||
mBaseFileSystem->setWorkingDirectory(base_path);
|
||||
mBaseFileSystem->getWorkingDirectory(canonical_base_path);
|
||||
|
||||
// restore current path
|
||||
mBaseFileSystem->setWorkingDirectory(prev_canonical_base_path);
|
||||
|
||||
// set state for path resolvers
|
||||
mBasePathResolver.setCurrentDirectory(canonical_base_path);
|
||||
mSubPathResolver.setCurrentDirectory(tc::io::Path("/"));
|
||||
}
|
||||
|
||||
tc::ResourceStatus tc::io::SubFileSystem::state()
|
||||
{
|
||||
return mBaseFileSystem.get() ? mBaseFileSystem->state() : tc::ResourceStatus(1 << tc::RESFLAG_NOINIT);
|
||||
}
|
||||
|
||||
void tc::io::SubFileSystem::dispose()
|
||||
{
|
||||
if (mBaseFileSystem.get() != nullptr)
|
||||
mBaseFileSystem->dispose();
|
||||
|
||||
mBasePathResolver = tc::io::BasicPathResolver();
|
||||
mSubPathResolver = tc::io::BasicPathResolver();
|
||||
}
|
||||
|
||||
void tc::io::SubFileSystem::createFile(const tc::io::Path& path)
|
||||
{
|
||||
if (mBaseFileSystem == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::createFile()", "Failed to create file (no base file system)");
|
||||
}
|
||||
|
||||
// convert sub filesystem path to real path
|
||||
tc::io::Path real_path;
|
||||
subPathToRealPath(path, real_path);
|
||||
|
||||
// create file
|
||||
mBaseFileSystem->createFile(real_path);
|
||||
}
|
||||
|
||||
void tc::io::SubFileSystem::removeFile(const tc::io::Path& path)
|
||||
{
|
||||
if (mBaseFileSystem == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::removeFile()", "Failed to remove file (no base file system)");
|
||||
}
|
||||
|
||||
// convert sub filesystem path to real path
|
||||
tc::io::Path real_path;
|
||||
subPathToRealPath(path, real_path);
|
||||
|
||||
// delete file
|
||||
mBaseFileSystem->removeFile(real_path);
|
||||
}
|
||||
|
||||
void tc::io::SubFileSystem::openFile(const tc::io::Path& path, tc::io::FileMode mode, tc::io::FileAccess access, std::shared_ptr<tc::io::IStream>& stream)
|
||||
{
|
||||
if (mBaseFileSystem == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::openFile()", "Failed to open file (no base file system)");
|
||||
}
|
||||
|
||||
// convert sub filesystem path to real path
|
||||
tc::io::Path real_path;
|
||||
subPathToRealPath(path, real_path);
|
||||
|
||||
// open file
|
||||
return mBaseFileSystem->openFile(real_path, mode, access, stream);
|
||||
}
|
||||
|
||||
void tc::io::SubFileSystem::createDirectory(const tc::io::Path& path)
|
||||
{
|
||||
if (mBaseFileSystem == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::createDirectory()", "Failed to create directory (no base file system)");
|
||||
}
|
||||
|
||||
// convert sub filesystem path to real path
|
||||
tc::io::Path real_path;
|
||||
subPathToRealPath(path, real_path);
|
||||
|
||||
// create directory
|
||||
mBaseFileSystem->createDirectory(real_path);
|
||||
}
|
||||
|
||||
void tc::io::SubFileSystem::removeDirectory(const tc::io::Path& path)
|
||||
{
|
||||
if (mBaseFileSystem == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::removeDirectory()", "Failed to remove directory (no base file system)");
|
||||
}
|
||||
|
||||
// convert sub filesystem path to real path
|
||||
tc::io::Path real_path;
|
||||
subPathToRealPath(path, real_path);
|
||||
|
||||
// remove directory
|
||||
mBaseFileSystem->removeDirectory(real_path);
|
||||
}
|
||||
|
||||
void tc::io::SubFileSystem::getWorkingDirectory(tc::io::Path& path)
|
||||
{
|
||||
if (mBaseFileSystem == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::getWorkingDirectory()", "Failed to get current working directory (no base file system)");
|
||||
}
|
||||
|
||||
path = mSubPathResolver.getCurrentDirectory();
|
||||
}
|
||||
|
||||
void tc::io::SubFileSystem::setWorkingDirectory(const tc::io::Path& path)
|
||||
{
|
||||
if (mBaseFileSystem == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::setWorkingDirectory()", "Failed to set current working directory (no base file system)");
|
||||
}
|
||||
|
||||
// convert sub filesystem path to real path
|
||||
tc::io::Path canonical_base_path;
|
||||
subPathToRealPath(path, canonical_base_path);
|
||||
|
||||
// save previous basefs working directory path
|
||||
tc::io::Path prev_canonical_base_path;
|
||||
mBaseFileSystem->getWorkingDirectory(prev_canonical_base_path);
|
||||
|
||||
// set and get working directory path so that canonical_base_path is populated with the full real path
|
||||
mBaseFileSystem->setWorkingDirectory(canonical_base_path);
|
||||
mBaseFileSystem->getWorkingDirectory(canonical_base_path);
|
||||
|
||||
// restore previous basefs working directory path
|
||||
mBaseFileSystem->setWorkingDirectory(prev_canonical_base_path);
|
||||
|
||||
// save current directory
|
||||
tc::io::Path canonical_sub_path;
|
||||
realPathToSubPath(canonical_base_path, canonical_sub_path);
|
||||
mSubPathResolver.setCurrentDirectory(canonical_sub_path);
|
||||
}
|
||||
|
||||
void tc::io::SubFileSystem::getDirectoryListing(const tc::io::Path& path, sDirectoryListing& info)
|
||||
{
|
||||
if (mBaseFileSystem == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::getDirectoryListing()", "Failed to get directory listing (no base file system)");
|
||||
}
|
||||
|
||||
// convert sub filesystem path to real path
|
||||
tc::io::Path canonical_base_path;
|
||||
subPathToRealPath(path, canonical_base_path);
|
||||
|
||||
// get real directory info
|
||||
tc::io::sDirectoryListing dir_info;
|
||||
mBaseFileSystem->getDirectoryListing(canonical_base_path, dir_info);
|
||||
|
||||
// convert directory absolute path
|
||||
tc::io::Path canonical_sub_path;
|
||||
realPathToSubPath(dir_info.abs_path, canonical_sub_path);
|
||||
|
||||
// update info with sub filesystem path
|
||||
dir_info.abs_path = canonical_sub_path;
|
||||
|
||||
// write object to output
|
||||
info = dir_info;
|
||||
}
|
||||
|
||||
void tc::io::SubFileSystem::subPathToRealPath(const tc::io::Path& sub_path, tc::io::Path& real_path)
|
||||
{
|
||||
// get canonical sub path
|
||||
tc::io::Path canonical_sub_path = mSubPathResolver.resolveCanonicalPath(sub_path);
|
||||
|
||||
// get canonical base path
|
||||
real_path = mBasePathResolver.resolveCanonicalPath(canonical_sub_path.subpath(1, tc::io::Path::npos));
|
||||
}
|
||||
|
||||
void tc::io::SubFileSystem::realPathToSubPath(const tc::io::Path& real_path, tc::io::Path& sub_path)
|
||||
{
|
||||
tc::io::Path canonical_base_path = mBasePathResolver.getCurrentDirectory();
|
||||
|
||||
if (real_path.subpath(0, canonical_base_path.size()) != canonical_base_path)
|
||||
{
|
||||
throw tc::UnauthorisedAccessException(kClassName, "Sub filesystem escape detected");
|
||||
}
|
||||
|
||||
sub_path = tc::io::Path("/") + real_path.subpath(canonical_base_path.size(), tc::io::Path::npos);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
#include <tc/io/SubSink.h>
|
||||
#include <tc/io/IOUtil.h>
|
||||
|
||||
const std::string tc::io::SubSink::kClassName = "tc::io::SubSink";
|
||||
|
||||
tc::io::SubSink::SubSink() :
|
||||
mBaseSink(nullptr),
|
||||
mBaseSinkOffset(0),
|
||||
mSubSinkLength(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
tc::io::SubSink::SubSink(const std::shared_ptr<tc::io::ISink>& sink, int64_t offset, int64_t length) :
|
||||
SubSink()
|
||||
{
|
||||
mBaseSink = sink;
|
||||
|
||||
// validate arguments
|
||||
if (mBaseSink == nullptr)
|
||||
{
|
||||
throw tc::ArgumentNullException(kClassName, "The base sink is null.");
|
||||
}
|
||||
|
||||
if (offset < 0)
|
||||
{
|
||||
throw tc::ArgumentOutOfRangeException(kClassName, "offset is negative");
|
||||
}
|
||||
if (length < 0)
|
||||
{
|
||||
throw tc::ArgumentOutOfRangeException(kClassName, "length is negative");
|
||||
}
|
||||
|
||||
int64_t base_length = mBaseSink->length();
|
||||
|
||||
// validate arguments against sink length
|
||||
// sub sink length should not be greater than the base sink length
|
||||
if (length > base_length)
|
||||
{
|
||||
throw tc::ArgumentOutOfRangeException(kClassName, "The sub sink length is greater than base sink length.");
|
||||
}
|
||||
// Base length - length is the maximum possible offset for the sub sink
|
||||
if (offset > (base_length - length))
|
||||
{
|
||||
throw tc::ArgumentOutOfRangeException(kClassName, "The sub sink offset is greater than the maximum possible offset given the base sink length and sub sink length.");
|
||||
}
|
||||
|
||||
// set class state
|
||||
mBaseSinkOffset = offset;
|
||||
mSubSinkLength = length;
|
||||
}
|
||||
|
||||
int64_t tc::io::SubSink::length()
|
||||
{
|
||||
return mBaseSink == nullptr ? 0 : mSubSinkLength;
|
||||
}
|
||||
|
||||
void tc::io::SubSink::setLength(int64_t length)
|
||||
{
|
||||
throw tc::NotImplementedException(kClassName+"::setLength()", "setLength is not implemented for SubSink.");
|
||||
}
|
||||
|
||||
size_t tc::io::SubSink::pushData(const tc::ByteData& data, int64_t offset)
|
||||
{
|
||||
if (mBaseSink == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::pushData()", "Failed to push data (no base sink)");
|
||||
}
|
||||
|
||||
auto new_data = tc::ByteData(data.data(), IOUtil::getWritableCount(mSubSinkLength, offset, data.size()));
|
||||
|
||||
return mBaseSink->pushData(new_data, mBaseSinkOffset + offset);
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
#include <tc/io/SubSource.h>
|
||||
#include <tc/io/IOUtil.h>
|
||||
|
||||
const std::string tc::io::SubSource::kClassName = "tc::io::SubSource";
|
||||
|
||||
tc::io::SubSource::SubSource() :
|
||||
mBaseSource(nullptr),
|
||||
mBaseSourceOffset(0),
|
||||
mSubSourceLength(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
tc::io::SubSource::SubSource(const std::shared_ptr<tc::io::ISource>& source, int64_t offset, int64_t length) :
|
||||
SubSource()
|
||||
{
|
||||
mBaseSource = source;
|
||||
|
||||
// validate arguments
|
||||
if (mBaseSource == nullptr)
|
||||
{
|
||||
throw tc::ArgumentNullException(kClassName, "source is null");
|
||||
}
|
||||
|
||||
if (offset < 0)
|
||||
{
|
||||
throw tc::ArgumentOutOfRangeException(kClassName, "offset is negative");
|
||||
}
|
||||
if (length < 0)
|
||||
{
|
||||
throw tc::ArgumentOutOfRangeException(kClassName, "length is negative");
|
||||
}
|
||||
|
||||
int64_t base_length = mBaseSource->length();
|
||||
|
||||
// validate arguments against source length
|
||||
// sub source length should not be greater than the base source length
|
||||
if (length > base_length)
|
||||
{
|
||||
throw tc::ArgumentOutOfRangeException(kClassName, "sub source length is greater than base source length");
|
||||
}
|
||||
// Base length - length is the maximum possible offset for the sub source
|
||||
if (offset > (base_length - length))
|
||||
{
|
||||
throw tc::ArgumentOutOfRangeException(kClassName, "sub source offset is greater than the maximum possible offset given the base source size and sub source size");
|
||||
}
|
||||
|
||||
// set class state
|
||||
mBaseSourceOffset = offset;
|
||||
mSubSourceLength = length;
|
||||
}
|
||||
|
||||
int64_t tc::io::SubSource::length()
|
||||
{
|
||||
return mBaseSource == nullptr ? 0 : mSubSourceLength;
|
||||
}
|
||||
|
||||
tc::ByteData tc::io::SubSource::pullData(int64_t offset, size_t count)
|
||||
{
|
||||
size_t pull_count = IOUtil::getReadableCount(length(), offset, count);
|
||||
|
||||
if (pull_count == 0)
|
||||
return tc::ByteData();
|
||||
|
||||
return mBaseSource->pullData(mBaseSourceOffset + offset, pull_count);
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
#include <tc/io/SubStream.h>
|
||||
#include <tc/io/IOUtil.h>
|
||||
#include <tc/io/StreamUtil.h>
|
||||
#include <algorithm>
|
||||
|
||||
const std::string tc::io::SubStream::kClassName = "tc::io::SubStream";
|
||||
|
||||
tc::io::SubStream::SubStream() :
|
||||
mBaseStream(),
|
||||
mBaseStreamOffset(0),
|
||||
mSubStreamLength(0),
|
||||
mSubStreamPosition(0)
|
||||
{}
|
||||
|
||||
|
||||
tc::io::SubStream::SubStream(const std::shared_ptr<tc::io::IStream>& stream, int64_t offset, int64_t length) :
|
||||
SubStream()
|
||||
{
|
||||
// copy stream
|
||||
mBaseStream = stream;
|
||||
|
||||
// validate the stream exists
|
||||
if (mBaseStream == nullptr)
|
||||
{
|
||||
throw tc::ArgumentNullException(kClassName, "stream is null");
|
||||
}
|
||||
|
||||
// check if the stream supports seeking
|
||||
if (mBaseStream->canSeek() == false)
|
||||
{
|
||||
tc::NotSupportedException(kClassName, "Streams that do not support seeking are not supported");
|
||||
}
|
||||
|
||||
|
||||
// validate arguments
|
||||
if (offset < 0)
|
||||
{
|
||||
throw tc::ArgumentOutOfRangeException(kClassName, "offset is negative");
|
||||
}
|
||||
if (length < 0)
|
||||
{
|
||||
throw tc::ArgumentOutOfRangeException(kClassName, "length is negative");
|
||||
}
|
||||
|
||||
int64_t base_length = mBaseStream->length();
|
||||
|
||||
// validate arguments against stream length
|
||||
// substream length should not be greater than the base stream length
|
||||
if (length > base_length)
|
||||
{
|
||||
throw tc::ArgumentOutOfRangeException(kClassName, "SubStream length is greater than base stream length");
|
||||
}
|
||||
// Base length - length is the maximum possible offset for the substream
|
||||
if (offset > (base_length - length))
|
||||
{
|
||||
throw tc::ArgumentOutOfRangeException(kClassName, "SubStream offset is greater than the maximum possible offset given the base stream size and SubStream size");
|
||||
}
|
||||
|
||||
// set class state
|
||||
mBaseStreamOffset = offset;
|
||||
mSubStreamLength = length;
|
||||
mSubStreamPosition = 0;
|
||||
}
|
||||
|
||||
bool tc::io::SubStream::canRead() const
|
||||
{
|
||||
return mBaseStream == nullptr ? false : mBaseStream->canRead();
|
||||
}
|
||||
|
||||
bool tc::io::SubStream::canWrite() const
|
||||
{
|
||||
return mBaseStream == nullptr ? false : mBaseStream->canWrite();
|
||||
}
|
||||
bool tc::io::SubStream::canSeek() const
|
||||
{
|
||||
return mBaseStream == nullptr ? false : mBaseStream->canSeek();
|
||||
}
|
||||
|
||||
int64_t tc::io::SubStream::length()
|
||||
{
|
||||
return mBaseStream == nullptr ? 0 : mSubStreamLength;
|
||||
}
|
||||
|
||||
int64_t tc::io::SubStream::position()
|
||||
{
|
||||
return mBaseStream == nullptr ? 0 : mSubStreamPosition;
|
||||
}
|
||||
|
||||
size_t tc::io::SubStream::read(byte_t* ptr, size_t count)
|
||||
{
|
||||
if (mBaseStream == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::read()", "Failed to read from stream (stream is disposed)");
|
||||
}
|
||||
|
||||
count = IOUtil::getReadableCount(mSubStreamLength, mSubStreamPosition, count);
|
||||
|
||||
// assert proper position in file
|
||||
mBaseStream->seek(mBaseStreamOffset + mSubStreamPosition, SeekOrigin::Begin);
|
||||
|
||||
// read data
|
||||
size_t data_read_size = mBaseStream->read(ptr, count);
|
||||
|
||||
// update sub stream position
|
||||
seek(data_read_size, SeekOrigin::Current);
|
||||
|
||||
return data_read_size;
|
||||
}
|
||||
|
||||
size_t tc::io::SubStream::write(const byte_t* ptr, size_t count)
|
||||
{
|
||||
if (mBaseStream == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::write()", "Failed to write to stream (stream is disposed)");
|
||||
}
|
||||
|
||||
count = IOUtil::getWritableCount(mSubStreamLength, mSubStreamPosition, count);
|
||||
|
||||
// assert proper position in file
|
||||
mBaseStream->seek(mBaseStreamOffset + mSubStreamPosition, SeekOrigin::Begin);
|
||||
|
||||
// write data
|
||||
size_t data_written_size = mBaseStream->write(ptr, count);
|
||||
|
||||
// update sub stream position
|
||||
seek(data_written_size, SeekOrigin::Current);
|
||||
|
||||
return data_written_size;
|
||||
}
|
||||
|
||||
int64_t tc::io::SubStream::seek(int64_t offset, SeekOrigin origin)
|
||||
{
|
||||
if (mBaseStream == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::seek()", "Failed to set stream position (stream is disposed)");
|
||||
}
|
||||
|
||||
mSubStreamPosition = StreamUtil::getSeekResult(offset, origin, mSubStreamPosition, mSubStreamLength);
|
||||
|
||||
if (mSubStreamPosition < 0)
|
||||
{
|
||||
throw tc::InvalidOperationException(kClassName+"::seek()", "Negative seek result determined");
|
||||
}
|
||||
|
||||
return mSubStreamPosition;
|
||||
}
|
||||
|
||||
void tc::io::SubStream::setLength(int64_t length)
|
||||
{
|
||||
if (mBaseStream == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::setLength()", "Failed to set stream length (stream is disposed)");
|
||||
}
|
||||
|
||||
throw tc::NotImplementedException(kClassName+"::setLength()", "setLength is not implemented for SubStream");
|
||||
}
|
||||
|
||||
void tc::io::SubStream::flush()
|
||||
{
|
||||
if (mBaseStream == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::seek()", "Failed to flush stream (stream is disposed)");
|
||||
}
|
||||
|
||||
mBaseStream->flush();
|
||||
}
|
||||
|
||||
void tc::io::SubStream::dispose()
|
||||
{
|
||||
if (mBaseStream.get() != nullptr)
|
||||
{
|
||||
// dispose base stream
|
||||
mBaseStream->dispose();
|
||||
|
||||
// release ptr
|
||||
mBaseStream.reset();
|
||||
}
|
||||
|
||||
// clear state
|
||||
mBaseStreamOffset = 0;
|
||||
mSubStreamLength = 0;
|
||||
mSubStreamPosition = 0;
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
#include <tc/io/VirtualFileSystem.h>
|
||||
|
||||
const std::string tc::io::VirtualFileSystem::kClassName = "tc::io::VirtualFileSystem";
|
||||
|
||||
tc::io::VirtualFileSystem::VirtualFileSystem() :
|
||||
mCurDir(nullptr),
|
||||
mFsSnapshot(),
|
||||
mPathResolver()
|
||||
{
|
||||
}
|
||||
|
||||
tc::io::VirtualFileSystem::VirtualFileSystem(const FileSystemSnapshot& fs_snapshot, const std::shared_ptr<tc::io::IPortablePathResolver>& path_resolver) :
|
||||
VirtualFileSystem()
|
||||
{
|
||||
mFsSnapshot = fs_snapshot;
|
||||
mPathResolver = path_resolver;
|
||||
|
||||
// Use default path resolver if none was provided
|
||||
if (mPathResolver == nullptr)
|
||||
{
|
||||
mPathResolver = std::shared_ptr<tc::io::BasicPathResolver>(new tc::io::BasicPathResolver());
|
||||
}
|
||||
|
||||
// get root directory
|
||||
tc::io::Path canonical_root_path = mPathResolver->resolveCanonicalPath(tc::io::Path("/"));
|
||||
|
||||
auto root_itr = mFsSnapshot.dir_entry_path_map.find(canonical_root_path);
|
||||
// if the path was not found in the map, throw exception
|
||||
if (root_itr == mFsSnapshot.dir_entry_path_map.end())
|
||||
{
|
||||
throw tc::InvalidOperationException(kClassName, "Failed to located root directory");
|
||||
}
|
||||
// if the dir_entry index isn't valid, throw exception
|
||||
if (root_itr->second >= mFsSnapshot.dir_entries.size())
|
||||
{
|
||||
throw tc::InvalidOperationException(kClassName, "Failed to located root directory");
|
||||
}
|
||||
|
||||
mCurDir = &mFsSnapshot.dir_entries.at(root_itr->second);
|
||||
}
|
||||
|
||||
tc::ResourceStatus tc::io::VirtualFileSystem::state()
|
||||
{
|
||||
return mCurDir == nullptr? tc::ResourceStatus(1 << tc::RESFLAG_NOINIT) : tc::ResourceStatus(1 << tc::RESFLAG_READY);
|
||||
}
|
||||
|
||||
void tc::io::VirtualFileSystem::dispose()
|
||||
{
|
||||
mCurDir = nullptr;
|
||||
mFsSnapshot.dir_entries.clear();
|
||||
mFsSnapshot.file_entries.clear();
|
||||
mFsSnapshot.dir_entry_path_map.clear();
|
||||
mFsSnapshot.file_entry_path_map.clear();
|
||||
mPathResolver.reset();
|
||||
}
|
||||
|
||||
void tc::io::VirtualFileSystem::createFile(const tc::io::Path& path)
|
||||
{
|
||||
if (mCurDir == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::createFile()", "VirtualFileSystem not initialized");
|
||||
}
|
||||
|
||||
throw tc::NotImplementedException(kClassName+"::createFile()", "createFile is not supported for VirtualFileSystem");
|
||||
}
|
||||
|
||||
void tc::io::VirtualFileSystem::removeFile(const tc::io::Path& path)
|
||||
{
|
||||
if (mCurDir == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::removeFile()", "VirtualFileSystem not initialized");
|
||||
}
|
||||
|
||||
throw tc::NotImplementedException(kClassName+"::removeFile()", "removeFile is not supported for VirtualFileSystem");
|
||||
}
|
||||
|
||||
void tc::io::VirtualFileSystem::openFile(const tc::io::Path& path, tc::io::FileMode mode, tc::io::FileAccess access, std::shared_ptr<tc::io::IStream>& stream)
|
||||
{
|
||||
if (mCurDir == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::openFile()", "VirtualFileSystem not initialized");
|
||||
}
|
||||
|
||||
tc::io::Path resolved_path = mPathResolver->resolveCanonicalPath(path);
|
||||
|
||||
if (mode != tc::io::FileMode::Open)
|
||||
{
|
||||
throw tc::NotSupportedException(kClassName+"::openFile()", "This file-system is read-only, only FileMode::Open is supported.");
|
||||
}
|
||||
if (access != tc::io::FileAccess::Read)
|
||||
{
|
||||
throw tc::NotSupportedException(kClassName+"::openFile()", "This file-system is read-only, only FileAccess::Read is supported.");
|
||||
}
|
||||
|
||||
auto file_itr = mFsSnapshot.file_entry_path_map.find(resolved_path);
|
||||
// if resolved_path does not exist in the map, throw exception
|
||||
if (file_itr == mFsSnapshot.file_entry_path_map.end())
|
||||
{
|
||||
throw tc::io::FileNotFoundException(kClassName+"::openFile()", "File does not exist.");
|
||||
}
|
||||
// if the file_entry index isn't valid or leads to a null IStream pointer, throw exception
|
||||
if (file_itr->second >= mFsSnapshot.file_entries.size() || mFsSnapshot.file_entries.at(file_itr->second).stream == nullptr)
|
||||
{
|
||||
throw tc::io::FileNotFoundException(kClassName+"::openFile()", "File does not exist.");
|
||||
}
|
||||
// if the stream has invalid properties, throw exception
|
||||
if ( !(mFsSnapshot.file_entries.at(file_itr->second).stream->canRead() == true && mFsSnapshot.file_entries.at(file_itr->second).stream->canWrite() == false) )
|
||||
{
|
||||
throw tc::io::FileNotFoundException(kClassName+"::openFile()", "File does not exist.");
|
||||
}
|
||||
|
||||
stream = mFsSnapshot.file_entries.at(file_itr->second).stream;
|
||||
}
|
||||
|
||||
void tc::io::VirtualFileSystem::createDirectory(const tc::io::Path& path)
|
||||
{
|
||||
if (mCurDir == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::createDirectory()", "VirtualFileSystem not initialized");
|
||||
}
|
||||
|
||||
throw tc::NotImplementedException(kClassName+"::createDirectory()", "createDirectory is not supported for VirtualFileSystem");
|
||||
}
|
||||
|
||||
void tc::io::VirtualFileSystem::removeDirectory(const tc::io::Path& path)
|
||||
{
|
||||
if (mCurDir == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::removeDirectory()", "VirtualFileSystem not initialized");
|
||||
}
|
||||
|
||||
throw tc::NotImplementedException(kClassName+"::removeDirectory()", "removeDirectory is not supported for VirtualFileSystem");
|
||||
}
|
||||
|
||||
void tc::io::VirtualFileSystem::getWorkingDirectory(tc::io::Path& path)
|
||||
{
|
||||
if (mCurDir == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::getWorkingDirectory()", "VirtualFileSystem not initialized");
|
||||
}
|
||||
|
||||
path = mCurDir->dir_listing.abs_path;
|
||||
}
|
||||
|
||||
void tc::io::VirtualFileSystem::setWorkingDirectory(const tc::io::Path& path)
|
||||
{
|
||||
if (mCurDir == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::setWorkingDirectory()", "VirtualFileSystem not initialized");
|
||||
}
|
||||
|
||||
tc::io::Path resolved_path = mPathResolver->resolveCanonicalPath(path);
|
||||
|
||||
auto dir_itr = mFsSnapshot.dir_entry_path_map.find(resolved_path);
|
||||
// if the path was not found in the map, throw exception
|
||||
if (dir_itr == mFsSnapshot.dir_entry_path_map.end())
|
||||
{
|
||||
throw tc::io::DirectoryNotFoundException(kClassName+"::setWorkingDirectory()", "Directory does not exist.");
|
||||
}
|
||||
// if the dir_entry index isn't valid, throw exception
|
||||
if (dir_itr->second >= mFsSnapshot.dir_entries.size())
|
||||
{
|
||||
throw tc::io::DirectoryNotFoundException(kClassName+"::setWorkingDirectory()", "Directory does not exist.");
|
||||
}
|
||||
|
||||
mCurDir = &mFsSnapshot.dir_entries.at(dir_itr->second);
|
||||
mPathResolver->setCurrentDirectory(mCurDir->dir_listing.abs_path);
|
||||
}
|
||||
|
||||
void tc::io::VirtualFileSystem::getDirectoryListing(const tc::io::Path& path, tc::io::sDirectoryListing& info)
|
||||
{
|
||||
if (mCurDir == nullptr)
|
||||
{
|
||||
throw tc::ObjectDisposedException(kClassName+"::getDirectoryListing()", "VirtualFileSystem not initialized");
|
||||
}
|
||||
|
||||
tc::io::Path resolved_path = mPathResolver->resolveCanonicalPath(path);
|
||||
|
||||
auto dir_itr = mFsSnapshot.dir_entry_path_map.find(resolved_path);
|
||||
// if the path was not found in the map, throw exception
|
||||
if (dir_itr == mFsSnapshot.dir_entry_path_map.end())
|
||||
{
|
||||
throw tc::io::DirectoryNotFoundException(kClassName+"::getDirectoryListing()", "Directory does not exist.");
|
||||
}
|
||||
// if the dir_entry index isn't valid, throw exception
|
||||
if (dir_itr->second >= mFsSnapshot.dir_entries.size())
|
||||
{
|
||||
throw tc::io::DirectoryNotFoundException(kClassName+"::getDirectoryListing()", "Directory does not exist.");
|
||||
}
|
||||
|
||||
info = mFsSnapshot.dir_entries.at(dir_itr->second).dir_listing;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#include <tc/os/Environment.h>
|
||||
#include <tc/string.h>
|
||||
|
||||
bool tc::os::getEnvVar(const std::string& name, std::string& value)
|
||||
{
|
||||
bool did_find_variable = false;
|
||||
#ifdef _WIN32
|
||||
// convert utf-8 to utf-16 for wide char functions
|
||||
std::u16string utf16_name;
|
||||
tc::string::TranscodeUtil::UTF8ToUTF16(name, utf16_name);
|
||||
|
||||
// get size of environment variable
|
||||
size_t required_size = 0;
|
||||
_wgetenv_s(&required_size, nullptr, 0, (wchar_t*)utf16_name.c_str());
|
||||
|
||||
// set output if variable was found
|
||||
if (required_size != 0)
|
||||
{
|
||||
// get environment variable
|
||||
std::shared_ptr<wchar_t> utf16_value(new wchar_t[required_size]);
|
||||
_wgetenv_s(&required_size, utf16_value.get(), required_size, (wchar_t*)utf16_name.c_str());
|
||||
|
||||
// transcode back to utf-8
|
||||
tc::string::TranscodeUtil::UTF16ToUTF8((char16_t*)utf16_value.get(), value);
|
||||
|
||||
did_find_variable = true;
|
||||
}
|
||||
#else
|
||||
// get ptr to env variable
|
||||
char* env_ptr = getenv(name.c_str());
|
||||
|
||||
// set output if variable was found
|
||||
if (env_ptr != nullptr)
|
||||
{
|
||||
value = std::string(env_ptr);
|
||||
did_find_variable = true;
|
||||
}
|
||||
#endif
|
||||
return did_find_variable;
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
#include <tc/string/TranscodeUtil.h>
|
||||
#include <tc/ArgumentException.h>
|
||||
|
||||
#include <tc/string/detail/utf8.h>
|
||||
#include <tc/string/detail/utf16.h>
|
||||
|
||||
void tc::string::TranscodeUtil::UTF8ToUTF32(const std::string& src, std::u32string& dst)
|
||||
{
|
||||
size_t done = 0;
|
||||
dst.clear();
|
||||
for (size_t i = 0; i < src.length(); i += done)
|
||||
{
|
||||
// get number of leading high bits in first byte
|
||||
uint8_t prefix = detail::get_utf8_prefix(src[i]);
|
||||
if (prefix == 1 || prefix > 4) // 1 is reserved for trailer bytes
|
||||
{
|
||||
throw tc::ArgumentException("not a UTF-8 string");
|
||||
}
|
||||
|
||||
// if there are no prefix bits, this is ASCII
|
||||
if (prefix == 0)
|
||||
{
|
||||
dst.push_back(src[i]);
|
||||
done = 1;
|
||||
}
|
||||
// otherwise this is a multibyte character
|
||||
else
|
||||
{
|
||||
// there must be enough characters
|
||||
if ((i + prefix) > src.length())
|
||||
{
|
||||
throw tc::ArgumentException("not a UTF-8 string");
|
||||
}
|
||||
|
||||
char32_t uni = detail::get_utf8_data(prefix, src[i]);
|
||||
|
||||
for (uint8_t j = 1; j < prefix; j++)
|
||||
{
|
||||
if (detail::utf8_has_prefix(1, src[i + j]) == false)
|
||||
{
|
||||
throw tc::ArgumentException("not a UTF-8 string");
|
||||
}
|
||||
|
||||
uni <<= 6;
|
||||
uni |= detail::get_utf8_data(1, src[i + j]);
|
||||
}
|
||||
|
||||
if (uni >= detail::kUtf16HighSurrogateStart && uni <= detail::kUtf16LowSurrogateEnd)
|
||||
{
|
||||
throw tc::ArgumentException("not a UTF-8 string");
|
||||
}
|
||||
|
||||
if (uni > detail::kUtf16EncodeMax)
|
||||
{
|
||||
throw tc::ArgumentException("not a UTF-8 string");
|
||||
}
|
||||
|
||||
dst.push_back(uni);
|
||||
done = prefix;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void tc::string::TranscodeUtil::UTF16ToUTF32(const std::u16string& src, std::u32string& dst)
|
||||
{
|
||||
size_t done = 0;
|
||||
dst.clear();
|
||||
for (size_t i = 0; i < src.length(); i+=done)
|
||||
{
|
||||
// this isn't a utf16 reserved character, so just add to unicode string
|
||||
if (src[i] < detail::kUtf16HighSurrogateStart || src[i] > detail::kUtf16LowSurrogateEnd)
|
||||
{
|
||||
dst.push_back(src[i]);
|
||||
done = 1;
|
||||
}
|
||||
// otherwise we need to decode it
|
||||
else
|
||||
{
|
||||
// check that the high surrogate char exists first
|
||||
if (src[i] < detail::kUtf16HighSurrogateStart || src[i] > detail::kUtf16HighSurrogateEnd)
|
||||
{
|
||||
throw tc::ArgumentException("not a UTF-16 string");
|
||||
}
|
||||
// check that the low surrogate char exists next
|
||||
if (i >= src.length() - 1 || src[i + 1] < detail::kUtf16LowSurrogateStart || src[i + 1] > detail::kUtf16LowSurrogateEnd)
|
||||
{
|
||||
throw tc::ArgumentException("not a UTF-16 string");
|
||||
}
|
||||
|
||||
char32_t uni = ((src[i] & detail::kUtf16SurrogateMask) << detail::kUtf16SurrogateBits) | (src[i + 1] & detail::kUtf16SurrogateMask) | 0x10000;
|
||||
|
||||
dst.push_back(uni);
|
||||
done = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tc::string::TranscodeUtil::UTF32ToUTF8(const std::u32string& src, std::string& dst)
|
||||
{
|
||||
dst.clear();
|
||||
for (size_t i = 0; i < src.length(); i++)
|
||||
{
|
||||
if (src[i] <= detail::kUtf8AsciiEnd)
|
||||
{
|
||||
dst.push_back((char)src[i]);
|
||||
}
|
||||
else if (src[i] <= detail::kUtf82ByteEnd)
|
||||
{
|
||||
dst.push_back(detail::make_utf8(2, (uint8_t)(src[i] >> 6)));
|
||||
dst.push_back(detail::make_utf8(1, (uint8_t)(src[i] >> 0)));
|
||||
}
|
||||
else if (src[i] <= detail::kUtf83ByteEnd)
|
||||
{
|
||||
dst.push_back(detail::make_utf8(3, (uint8_t)(src[i] >> 12)));
|
||||
dst.push_back(detail::make_utf8(1, (uint8_t)(src[i] >> 6)));
|
||||
dst.push_back(detail::make_utf8(1, (uint8_t)(src[i] >> 0)));
|
||||
}
|
||||
else if (src[i] <= detail::kUtf84ByteEnd)
|
||||
{
|
||||
dst.push_back(detail::make_utf8(4, (uint8_t)(src[i] >> 18)));
|
||||
dst.push_back(detail::make_utf8(1, (uint8_t)(src[i] >> 12)));
|
||||
dst.push_back(detail::make_utf8(1, (uint8_t)(src[i] >> 6)));
|
||||
dst.push_back(detail::make_utf8(1, (uint8_t)(src[i] >> 0)));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw tc::ArgumentException("not a UTF-16 string");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tc::string::TranscodeUtil::UTF32ToUTF16(const std::u32string& src, std::u16string& dst)
|
||||
{
|
||||
dst.clear();
|
||||
for (size_t i = 0; i < src.size(); i++)
|
||||
{
|
||||
char32_t uni = src[i];
|
||||
if (uni < detail::kUtf16NonNativeStart)
|
||||
{
|
||||
dst.push_back((char16_t)uni);
|
||||
}
|
||||
else
|
||||
{
|
||||
uni -= detail::kUtf16NonNativeStart;
|
||||
dst.push_back(((uni >> detail::kUtf16SurrogateBits) & detail::kUtf16SurrogateMask) + detail::kUtf16HighSurrogateStart);
|
||||
dst.push_back((uni & detail::kUtf16SurrogateMask) + detail::kUtf16LowSurrogateStart);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void tc::string::TranscodeUtil::UTF8ToUTF16(const std::string& src, std::u16string& dst)
|
||||
{
|
||||
std::u32string unicode;
|
||||
TranscodeUtil::UTF8ToUTF32(src, unicode);
|
||||
TranscodeUtil::UTF32ToUTF16(unicode, dst);
|
||||
}
|
||||
|
||||
void tc::string::TranscodeUtil::UTF16ToUTF8(const std::u16string& src, std::string& dst)
|
||||
{
|
||||
std::u32string unicode;
|
||||
TranscodeUtil::UTF16ToUTF32(src, unicode);
|
||||
TranscodeUtil::UTF32ToUTF8(unicode, dst);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#include <tc/types.h>
|
||||
|
||||
bool tc::is_size_t_not_64bit()
|
||||
{
|
||||
return uint64_t(std::numeric_limits<size_t>::max()) < std::numeric_limits<uint64_t>::max();
|
||||
}
|
||||
|
||||
bool tc::is_size_t_too_large_for_int64_t(size_t val)
|
||||
{
|
||||
return uint64_t(std::numeric_limits<int64_t>::max()) < uint64_t(val);
|
||||
}
|
||||
|
||||
bool tc::is_uint64_t_too_large_for_int64_t(uint64_t val)
|
||||
{
|
||||
return uint64_t(std::numeric_limits<int64_t>::max()) < val;
|
||||
}
|
||||
|
||||
bool tc::is_int64_t_too_large_for_size_t(int64_t val)
|
||||
{
|
||||
return uint64_t(std::numeric_limits<size_t>::max()) < uint64_t(val) || val < 0;
|
||||
}
|
||||
|
||||
bool tc::is_uint64_t_too_large_for_size_t(uint64_t val)
|
||||
{
|
||||
return uint64_t(std::numeric_limits<size_t>::max()) < val;
|
||||
}
|
||||
Reference in New Issue
Block a user