mirror of
https://git.tesses.org/tesses50/tessesframework.git
synced 2026-08-03 09:05:32 +00:00
Overhaul cmake configuration, add console api, fix http code that caused issues with cgi-bin
This commit is contained in:
118
src/Args.cpp
118
src/Args.cpp
@@ -1,60 +1,72 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/TessesFramework.hpp"
|
||||
|
||||
namespace Tesses::Framework {
|
||||
Args::Args(std::vector<std::string> args)
|
||||
{
|
||||
if(args.size() < 1) return;
|
||||
filename = args[0];
|
||||
bool onlyPos=false;
|
||||
Args::Args(std::vector<std::string> args) {
|
||||
if (args.size() < 1)
|
||||
return;
|
||||
filename = args[0];
|
||||
bool onlyPos = false;
|
||||
|
||||
for(size_t i = 1; i < args.size(); i++)
|
||||
{
|
||||
std::string& arg = args[i];
|
||||
if(arg == "--")
|
||||
{
|
||||
onlyPos=true;
|
||||
continue;
|
||||
}
|
||||
if(!onlyPos && arg.size() > 2 && arg[0] == '-' && arg[1] == '-')
|
||||
{
|
||||
auto p = Tesses::Framework::Http::HttpUtils::SplitString(arg.substr(2),"=",2);
|
||||
if(p.size() == 1)
|
||||
flags.push_back(p[0]);
|
||||
else if(p.size() == 2)
|
||||
options.push_back(std::pair<std::string,std::string>(p[0],p[1]));
|
||||
}
|
||||
else {
|
||||
positional.push_back(arg);
|
||||
}
|
||||
for (size_t i = 1; i < args.size(); i++) {
|
||||
std::string &arg = args[i];
|
||||
if (arg == "--") {
|
||||
onlyPos = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
Args::Args(int argc, char** argv)
|
||||
{
|
||||
if(argc < 1) return;
|
||||
filename = argv[0];
|
||||
bool onlyPos=false;
|
||||
|
||||
for(int i = 1; i < argc; i++)
|
||||
{
|
||||
std::string_view arg = argv[i];
|
||||
if(arg == "--")
|
||||
{
|
||||
onlyPos=true;
|
||||
continue;
|
||||
}
|
||||
if(!onlyPos && arg.size() > 2 && arg[0] == '-' && arg[1] == '-')
|
||||
{
|
||||
auto p = Tesses::Framework::Http::HttpUtils::SplitString((std::string)arg.substr(2),"=",2);
|
||||
if(p.size() == 1)
|
||||
flags.push_back(p[0]);
|
||||
else if(p.size() == 2)
|
||||
options.push_back(std::pair<std::string,std::string>(p[0],p[1]));
|
||||
}
|
||||
else {
|
||||
positional.push_back((std::string)arg);
|
||||
}
|
||||
if (!onlyPos && arg.size() > 2 && arg[0] == '-' && arg[1] == '-') {
|
||||
auto p = Tesses::Framework::Http::HttpUtils::SplitString(
|
||||
arg.substr(2), "=", 2);
|
||||
if (p.size() == 1)
|
||||
flags.push_back(p[0]);
|
||||
else if (p.size() == 2)
|
||||
options.push_back(
|
||||
std::pair<std::string, std::string>(p[0], p[1]));
|
||||
} else {
|
||||
positional.push_back(arg);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Args::Args(int argc, char **argv) {
|
||||
if (argc < 1)
|
||||
return;
|
||||
filename = argv[0];
|
||||
bool onlyPos = false;
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
std::string_view arg = argv[i];
|
||||
if (arg == "--") {
|
||||
onlyPos = true;
|
||||
continue;
|
||||
}
|
||||
if (!onlyPos && arg.size() > 2 && arg[0] == '-' && arg[1] == '-') {
|
||||
auto p = Tesses::Framework::Http::HttpUtils::SplitString(
|
||||
(std::string)arg.substr(2), "=", 2);
|
||||
if (p.size() == 1)
|
||||
flags.push_back(p[0]);
|
||||
else if (p.size() == 2)
|
||||
options.push_back(
|
||||
std::pair<std::string, std::string>(p[0], p[1]));
|
||||
} else {
|
||||
positional.push_back((std::string)arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace Tesses::Framework
|
||||
@@ -1,379 +1,374 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/BitTorrent/TorrentFile.hpp"
|
||||
#include "TessesFramework/Streams/MemoryStream.hpp"
|
||||
#include "TessesFramework/Crypto/Crypto.hpp"
|
||||
#include "TessesFramework/Http/HttpUtils.hpp"
|
||||
namespace Tesses::Framework::BitTorrent
|
||||
{
|
||||
|
||||
TorrentFileInfo::TorrentFileInfo()
|
||||
{
|
||||
this->fileListOrLength = 0;
|
||||
}
|
||||
TorrentFileInfo::TorrentFileInfo(const Serialization::Bencode::BeDictionary& tkn)
|
||||
{
|
||||
Load(tkn);
|
||||
}
|
||||
int64_t TorrentFileInfo::GetTorrentFileSize()
|
||||
{
|
||||
if(std::holds_alternative<int64_t>(this->fileListOrLength))
|
||||
return std::get<int64_t>(fileListOrLength);
|
||||
else if(std::holds_alternative<std::vector<TorrentFileEntry>>(this->fileListOrLength)) {
|
||||
auto& files= std::get<std::vector<TorrentFileEntry>>(this->fileListOrLength);
|
||||
int64_t no=0;
|
||||
for(auto itm : files)
|
||||
{
|
||||
no += itm.length;
|
||||
}
|
||||
return no;
|
||||
#include "TessesFramework/Streams/MemoryStream.hpp"
|
||||
namespace Tesses::Framework::BitTorrent {
|
||||
|
||||
TorrentFileInfo::TorrentFileInfo() { this->fileListOrLength = 0; }
|
||||
TorrentFileInfo::TorrentFileInfo(
|
||||
const Serialization::Bencode::BeDictionary &tkn) {
|
||||
Load(tkn);
|
||||
}
|
||||
int64_t TorrentFileInfo::GetTorrentFileSize() {
|
||||
if (std::holds_alternative<int64_t>(this->fileListOrLength))
|
||||
return std::get<int64_t>(fileListOrLength);
|
||||
else if (std::holds_alternative<std::vector<TorrentFileEntry>>(
|
||||
this->fileListOrLength)) {
|
||||
auto &files =
|
||||
std::get<std::vector<TorrentFileEntry>>(this->fileListOrLength);
|
||||
int64_t no = 0;
|
||||
for (auto itm : files) {
|
||||
no += itm.length;
|
||||
}
|
||||
return 0;
|
||||
return no;
|
||||
}
|
||||
Serialization::Bencode::BeDictionary& TorrentFileInfo::GenerateInfoDictionary()
|
||||
{
|
||||
this->info = {};
|
||||
this->info.tokens.emplace_back("name",this->name);
|
||||
this->info.tokens.emplace_back("piece length",this->piece_length);
|
||||
this->info.tokens.emplace_back("pieces",this->pieces);
|
||||
if(this->isPrivate)
|
||||
this->info.tokens.emplace_back("private",1);
|
||||
return 0;
|
||||
}
|
||||
Serialization::Bencode::BeDictionary &
|
||||
TorrentFileInfo::GenerateInfoDictionary() {
|
||||
this->info = {};
|
||||
this->info.tokens.emplace_back("name", this->name);
|
||||
this->info.tokens.emplace_back("piece length", this->piece_length);
|
||||
this->info.tokens.emplace_back("pieces", this->pieces);
|
||||
if (this->isPrivate)
|
||||
this->info.tokens.emplace_back("private", 1);
|
||||
|
||||
if(std::holds_alternative<int64_t>(this->fileListOrLength))
|
||||
{
|
||||
this->info.tokens.emplace_back("length",std::get<int64_t>(this->fileListOrLength));
|
||||
if (std::holds_alternative<int64_t>(this->fileListOrLength)) {
|
||||
this->info.tokens.emplace_back(
|
||||
"length", std::get<int64_t>(this->fileListOrLength));
|
||||
} else if (std::holds_alternative<std::vector<TorrentFileEntry>>(
|
||||
this->fileListOrLength)) {
|
||||
Serialization::Bencode::BeArray a;
|
||||
for (auto &item :
|
||||
std::get<std::vector<TorrentFileEntry>>(this->fileListOrLength)) {
|
||||
Serialization::Bencode::BeDictionary dict;
|
||||
dict.tokens.emplace_back("length", item.length);
|
||||
Serialization::Bencode::BeArray path;
|
||||
for (auto &p : item.path.path)
|
||||
path.tokens.push_back(p);
|
||||
dict.tokens.emplace_back("path", path);
|
||||
|
||||
a.tokens.push_back(dict);
|
||||
}
|
||||
else if(std::holds_alternative<std::vector<TorrentFileEntry>>(this->fileListOrLength))
|
||||
{
|
||||
Serialization::Bencode::BeArray a;
|
||||
for(auto& item : std::get<std::vector<TorrentFileEntry>>(this->fileListOrLength))
|
||||
{
|
||||
Serialization::Bencode::BeDictionary dict;
|
||||
dict.tokens.emplace_back("length",item.length);
|
||||
Serialization::Bencode::BeArray path;
|
||||
for(auto& p : item.path.path)
|
||||
path.tokens.push_back(p);
|
||||
dict.tokens.emplace_back("path",path);
|
||||
|
||||
a.tokens.push_back(dict);
|
||||
}
|
||||
this->info.tokens.emplace_back("files", a);
|
||||
}
|
||||
return this->info;
|
||||
}
|
||||
Serialization::Bencode::BeDictionary& TorrentFileInfo::GetInfoDictionary()
|
||||
{
|
||||
return this->info;
|
||||
this->info.tokens.emplace_back("files", a);
|
||||
}
|
||||
return this->info;
|
||||
}
|
||||
Serialization::Bencode::BeDictionary &TorrentFileInfo::GetInfoDictionary() {
|
||||
return this->info;
|
||||
}
|
||||
|
||||
void TorrentFileInfo::Load(const Serialization::Bencode::BeDictionary& dict)
|
||||
{
|
||||
this->info = dict;
|
||||
auto o=dict.GetValue("name");
|
||||
if(std::holds_alternative<Serialization::Bencode::BeString>(o))
|
||||
this->name = std::get<Serialization::Bencode::BeString>(o);
|
||||
else
|
||||
this->name = {};
|
||||
|
||||
o=dict.GetValue("piece length");
|
||||
if(std::holds_alternative<int64_t>(o))
|
||||
this->piece_length = std::get<int64_t>(o);
|
||||
else
|
||||
this->piece_length = DEFAULT_PIECE_LENGTH;
|
||||
|
||||
o=dict.GetValue("private");
|
||||
if(std::holds_alternative<int64_t>(o))
|
||||
this->isPrivate = std::get<int64_t>(o);
|
||||
else
|
||||
this->isPrivate = 0;
|
||||
|
||||
o=dict.GetValue("pieces");
|
||||
if(std::holds_alternative<Serialization::Bencode::BeString>(o))
|
||||
this->pieces = std::get<Serialization::Bencode::BeString>(o);
|
||||
else
|
||||
this->pieces = {};
|
||||
|
||||
|
||||
|
||||
o=dict.GetValue("files");
|
||||
if(std::holds_alternative<Serialization::Bencode::BeArray>(o))
|
||||
{
|
||||
std::vector<TorrentFileEntry> ents;
|
||||
void TorrentFileInfo::Load(const Serialization::Bencode::BeDictionary &dict) {
|
||||
this->info = dict;
|
||||
auto o = dict.GetValue("name");
|
||||
if (std::holds_alternative<Serialization::Bencode::BeString>(o))
|
||||
this->name = std::get<Serialization::Bencode::BeString>(o);
|
||||
else
|
||||
this->name = {};
|
||||
|
||||
for(auto& item : std::get<Serialization::Bencode::BeArray>(o).tokens)
|
||||
{
|
||||
if(std::holds_alternative<Serialization::Bencode::BeDictionary>(item))
|
||||
{
|
||||
TorrentFileEntry fe;
|
||||
fe.path.relative=true;
|
||||
|
||||
auto& d2=std::get<Serialization::Bencode::BeDictionary>(item);
|
||||
auto o2=d2.GetValue("length");
|
||||
if(std::holds_alternative<int64_t>(o2))
|
||||
{
|
||||
fe.length = std::get<int64_t>(o2);
|
||||
}
|
||||
o2=d2.GetValue("path");
|
||||
if(std::holds_alternative<Serialization::Bencode::BeArray>(o2))
|
||||
{
|
||||
auto& arr=std::get<Serialization::Bencode::BeArray>(o2);
|
||||
for(auto& itm : arr.tokens)
|
||||
{
|
||||
if(std::holds_alternative<Serialization::Bencode::BeString>(itm))
|
||||
{
|
||||
fe.path.path.push_back(std::get<Serialization::Bencode::BeString>(itm));
|
||||
}
|
||||
o = dict.GetValue("piece length");
|
||||
if (std::holds_alternative<int64_t>(o))
|
||||
this->piece_length = std::get<int64_t>(o);
|
||||
else
|
||||
this->piece_length = DEFAULT_PIECE_LENGTH;
|
||||
|
||||
o = dict.GetValue("private");
|
||||
if (std::holds_alternative<int64_t>(o))
|
||||
this->isPrivate = std::get<int64_t>(o);
|
||||
else
|
||||
this->isPrivate = 0;
|
||||
|
||||
o = dict.GetValue("pieces");
|
||||
if (std::holds_alternative<Serialization::Bencode::BeString>(o))
|
||||
this->pieces = std::get<Serialization::Bencode::BeString>(o);
|
||||
else
|
||||
this->pieces = {};
|
||||
|
||||
o = dict.GetValue("files");
|
||||
if (std::holds_alternative<Serialization::Bencode::BeArray>(o)) {
|
||||
std::vector<TorrentFileEntry> ents;
|
||||
|
||||
for (auto &item : std::get<Serialization::Bencode::BeArray>(o).tokens) {
|
||||
if (std::holds_alternative<Serialization::Bencode::BeDictionary>(
|
||||
item)) {
|
||||
TorrentFileEntry fe;
|
||||
fe.path.relative = true;
|
||||
|
||||
auto &d2 = std::get<Serialization::Bencode::BeDictionary>(item);
|
||||
auto o2 = d2.GetValue("length");
|
||||
if (std::holds_alternative<int64_t>(o2)) {
|
||||
fe.length = std::get<int64_t>(o2);
|
||||
}
|
||||
o2 = d2.GetValue("path");
|
||||
if (std::holds_alternative<Serialization::Bencode::BeArray>(
|
||||
o2)) {
|
||||
auto &arr = std::get<Serialization::Bencode::BeArray>(o2);
|
||||
for (auto &itm : arr.tokens) {
|
||||
if (std::holds_alternative<
|
||||
Serialization::Bencode::BeString>(itm)) {
|
||||
fe.path.path.push_back(
|
||||
std::get<Serialization::Bencode::BeString>(
|
||||
itm));
|
||||
}
|
||||
}
|
||||
ents.push_back(fe);
|
||||
}
|
||||
}
|
||||
this->fileListOrLength = ents;
|
||||
}
|
||||
else {
|
||||
o = dict.GetValue("length");
|
||||
if(std::holds_alternative<int64_t>(o))
|
||||
{
|
||||
this->fileListOrLength = std::get<int64_t>(o);
|
||||
}
|
||||
else {
|
||||
this->fileListOrLength = (int64_t)0;
|
||||
ents.push_back(fe);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Serialization::Bencode::BeString TorrentFileInfo::GetInfoHash()
|
||||
{
|
||||
auto strm = std::make_shared<Streams::MemoryStream>(true);
|
||||
Serialization::Bencode::Bencode::Save(strm,this->info);
|
||||
strm->Seek(0L,Streams::SeekOrigin::Begin);
|
||||
return Crypto::Sha1::ComputeHash(strm);
|
||||
}
|
||||
|
||||
|
||||
TorrentFile::TorrentFile()
|
||||
{
|
||||
this->created_by = "TessesFrameworkTorrent";
|
||||
this->creation_date = (int64_t)time(NULL);
|
||||
}
|
||||
TorrentFile::TorrentFile(const Serialization::Bencode::BeDictionary& tkn)
|
||||
{
|
||||
auto o=tkn.GetValue("info");
|
||||
if(std::holds_alternative<Serialization::Bencode::BeDictionary>(o))
|
||||
{
|
||||
this->info.Load(std::get<Serialization::Bencode::BeDictionary>(o));
|
||||
this->fileListOrLength = ents;
|
||||
} else {
|
||||
o = dict.GetValue("length");
|
||||
if (std::holds_alternative<int64_t>(o)) {
|
||||
this->fileListOrLength = std::get<int64_t>(o);
|
||||
} else {
|
||||
this->fileListOrLength = (int64_t)0;
|
||||
}
|
||||
o=tkn.GetValue("announce");
|
||||
if(std::holds_alternative<Serialization::Bencode::BeString>(o))
|
||||
{
|
||||
this->announce = std::get<Serialization::Bencode::BeString>(o);
|
||||
}
|
||||
o=tkn.GetValue("announce-list");
|
||||
if(std::holds_alternative<Serialization::Bencode::BeArray>(o))
|
||||
{
|
||||
auto& ls = std::get<Serialization::Bencode::BeArray>(o);
|
||||
for(auto& item : ls.tokens)
|
||||
{
|
||||
if(std::holds_alternative<Serialization::Bencode::BeArray>(item))
|
||||
{
|
||||
auto ls2 = std::get<Serialization::Bencode::BeArray>(item);
|
||||
auto item2=ls2.tokens.at(0);
|
||||
if(std::holds_alternative<Serialization::Bencode::BeString>(item2))
|
||||
{
|
||||
this->announce_list.push_back(std::get<Serialization::Bencode::BeString>(item2));
|
||||
}
|
||||
}
|
||||
}
|
||||
Serialization::Bencode::BeString TorrentFileInfo::GetInfoHash() {
|
||||
auto strm = std::make_shared<Streams::MemoryStream>(true);
|
||||
Serialization::Bencode::Bencode::Save(strm, this->info);
|
||||
strm->Seek(0L, Streams::SeekOrigin::Begin);
|
||||
return Crypto::Sha1::ComputeHash(strm);
|
||||
}
|
||||
|
||||
TorrentFile::TorrentFile() {
|
||||
this->created_by = "TessesFrameworkTorrent";
|
||||
this->creation_date = (int64_t)time(NULL);
|
||||
}
|
||||
TorrentFile::TorrentFile(const Serialization::Bencode::BeDictionary &tkn) {
|
||||
auto o = tkn.GetValue("info");
|
||||
if (std::holds_alternative<Serialization::Bencode::BeDictionary>(o)) {
|
||||
this->info.Load(std::get<Serialization::Bencode::BeDictionary>(o));
|
||||
}
|
||||
o = tkn.GetValue("announce");
|
||||
if (std::holds_alternative<Serialization::Bencode::BeString>(o)) {
|
||||
this->announce = std::get<Serialization::Bencode::BeString>(o);
|
||||
}
|
||||
o = tkn.GetValue("announce-list");
|
||||
if (std::holds_alternative<Serialization::Bencode::BeArray>(o)) {
|
||||
auto &ls = std::get<Serialization::Bencode::BeArray>(o);
|
||||
for (auto &item : ls.tokens) {
|
||||
if (std::holds_alternative<Serialization::Bencode::BeArray>(item)) {
|
||||
auto ls2 = std::get<Serialization::Bencode::BeArray>(item);
|
||||
auto item2 = ls2.tokens.at(0);
|
||||
if (std::holds_alternative<Serialization::Bencode::BeString>(
|
||||
item2)) {
|
||||
this->announce_list.push_back(
|
||||
std::get<Serialization::Bencode::BeString>(item2));
|
||||
}
|
||||
}
|
||||
}
|
||||
o=tkn.GetValue("creation date");
|
||||
if(std::holds_alternative<int64_t>(o))
|
||||
{
|
||||
this->creation_date = std::get<int64_t>(o);
|
||||
}
|
||||
o=tkn.GetValue("comment");
|
||||
if(std::holds_alternative<Serialization::Bencode::BeString>(o))
|
||||
{
|
||||
this->comment = std::get<Serialization::Bencode::BeString>(o);
|
||||
}
|
||||
o=tkn.GetValue("created by");
|
||||
if(std::holds_alternative<Serialization::Bencode::BeString>(o))
|
||||
{
|
||||
this->created_by = std::get<Serialization::Bencode::BeString>(o);
|
||||
}
|
||||
o=tkn.GetValue("url-list");
|
||||
if(std::holds_alternative<Serialization::Bencode::BeArray>(o))
|
||||
{
|
||||
auto& li =std::get<Serialization::Bencode::BeArray>(o);
|
||||
for(auto& itm : li.tokens)
|
||||
{
|
||||
if(std::holds_alternative<Serialization::Bencode::BeString>(itm))
|
||||
this->url_list.push_back(std::get<Serialization::Bencode::BeString>(itm));
|
||||
}
|
||||
}
|
||||
o = tkn.GetValue("creation date");
|
||||
if (std::holds_alternative<int64_t>(o)) {
|
||||
this->creation_date = std::get<int64_t>(o);
|
||||
}
|
||||
o = tkn.GetValue("comment");
|
||||
if (std::holds_alternative<Serialization::Bencode::BeString>(o)) {
|
||||
this->comment = std::get<Serialization::Bencode::BeString>(o);
|
||||
}
|
||||
o = tkn.GetValue("created by");
|
||||
if (std::holds_alternative<Serialization::Bencode::BeString>(o)) {
|
||||
this->created_by = std::get<Serialization::Bencode::BeString>(o);
|
||||
}
|
||||
o = tkn.GetValue("url-list");
|
||||
if (std::holds_alternative<Serialization::Bencode::BeArray>(o)) {
|
||||
auto &li = std::get<Serialization::Bencode::BeArray>(o);
|
||||
for (auto &itm : li.tokens) {
|
||||
if (std::holds_alternative<Serialization::Bencode::BeString>(itm))
|
||||
this->url_list.push_back(
|
||||
std::get<Serialization::Bencode::BeString>(itm));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Serialization::Bencode::BeDictionary TorrentFile::ToDictionary()
|
||||
{
|
||||
Serialization::Bencode::BeDictionary dict;
|
||||
dict.SetValue("info",this->info.GetInfoDictionary());
|
||||
dict.SetValue("announce",this->announce);
|
||||
if(!this->announce_list.empty())
|
||||
{
|
||||
Serialization::Bencode::BeArray a;
|
||||
for(auto& item : this->announce_list)
|
||||
{
|
||||
Serialization::Bencode::BeArray a2;
|
||||
a2.tokens.push_back(item);
|
||||
a.tokens.push_back(a2);
|
||||
}
|
||||
dict.SetValue("announce-list",a);
|
||||
Serialization::Bencode::BeDictionary TorrentFile::ToDictionary() {
|
||||
Serialization::Bencode::BeDictionary dict;
|
||||
dict.SetValue("info", this->info.GetInfoDictionary());
|
||||
dict.SetValue("announce", this->announce);
|
||||
if (!this->announce_list.empty()) {
|
||||
Serialization::Bencode::BeArray a;
|
||||
for (auto &item : this->announce_list) {
|
||||
Serialization::Bencode::BeArray a2;
|
||||
a2.tokens.push_back(item);
|
||||
a.tokens.push_back(a2);
|
||||
}
|
||||
if(!this->url_list.empty())
|
||||
{
|
||||
Serialization::Bencode::BeArray ls;
|
||||
for(auto& itm : this->url_list)
|
||||
{
|
||||
ls.tokens.push_back(itm);
|
||||
}
|
||||
dict.SetValue("url-list",ls);
|
||||
dict.SetValue("announce-list", a);
|
||||
}
|
||||
if (!this->url_list.empty()) {
|
||||
Serialization::Bencode::BeArray ls;
|
||||
for (auto &itm : this->url_list) {
|
||||
ls.tokens.push_back(itm);
|
||||
}
|
||||
|
||||
dict.SetValue("created by", this->created_by);
|
||||
dict.SetValue("creation date", this->creation_date);
|
||||
dict.SetValue("comment",this->comment);
|
||||
return dict;
|
||||
dict.SetValue("url-list", ls);
|
||||
}
|
||||
|
||||
void TorrentFile::Print(std::shared_ptr<TextStreams::TextWriter> writer)
|
||||
{
|
||||
writer->WriteLine("Announce: " + (std::string)this->announce);
|
||||
writer->WriteLine("Announce List:");
|
||||
for(auto& item : this->announce_list)
|
||||
{
|
||||
writer->WriteLine("\t" + (std::string)item);
|
||||
}
|
||||
writer->WriteLine("Comment: " + (std::string)this->comment);
|
||||
writer->WriteLine("Created By: " + (std::string)this->created_by);
|
||||
Date::DateTime dt(this->creation_date);
|
||||
dt.SetToLocal();
|
||||
writer->WriteLine("Creation Date: " + dt.ToString());
|
||||
writer->WriteLine("Info Hash: " + Http::HttpUtils::BytesToHex(this->info.GetInfoHash().data));
|
||||
writer->WriteLine("Info:");
|
||||
writer->WriteLine("\tName: " + (std::string)this->info.name);
|
||||
writer->WriteLine(this->info.isPrivate ? "\tPrivate: true" : "\tPrivate: false");
|
||||
writer->WriteLine("\tPiece Length: " + TF_FileSize(this->info.piece_length));
|
||||
if(std::holds_alternative<int64_t>(this->info.fileListOrLength))
|
||||
{
|
||||
writer->WriteLine("\tIs Single File: true");
|
||||
writer->WriteLine("\tFile length: " + TF_FileSize((uint64_t)std::get<int64_t>(this->info.fileListOrLength)));
|
||||
}
|
||||
else if(std::holds_alternative<std::vector<TorrentFileEntry>>(this->info.fileListOrLength))
|
||||
{
|
||||
writer->WriteLine("\tIs Single File: false");
|
||||
writer->WriteLine("\tFiles:");
|
||||
auto& files = std::get<std::vector<TorrentFileEntry>>(this->info.fileListOrLength);
|
||||
for(auto& file : files)
|
||||
{
|
||||
writer->WriteLine("\t\tPath: " + file.path.ToString());
|
||||
writer->WriteLine("\t\tLength: " + TF_FileSize(file.length));
|
||||
writer->WriteLine();
|
||||
}
|
||||
dict.SetValue("created by", this->created_by);
|
||||
dict.SetValue("creation date", this->creation_date);
|
||||
dict.SetValue("comment", this->comment);
|
||||
return dict;
|
||||
}
|
||||
|
||||
void TorrentFile::Print(std::shared_ptr<TextStreams::TextWriter> writer) {
|
||||
writer->WriteLine("Announce: " + (std::string)this->announce);
|
||||
writer->WriteLine("Announce List:");
|
||||
for (auto &item : this->announce_list) {
|
||||
writer->WriteLine("\t" + (std::string)item);
|
||||
}
|
||||
writer->WriteLine("Comment: " + (std::string)this->comment);
|
||||
writer->WriteLine("Created By: " + (std::string)this->created_by);
|
||||
Date::DateTime dt(this->creation_date);
|
||||
dt.SetToLocal();
|
||||
writer->WriteLine("Creation Date: " + dt.ToString());
|
||||
writer->WriteLine("Info Hash: " + Http::HttpUtils::BytesToHex(
|
||||
this->info.GetInfoHash().data));
|
||||
writer->WriteLine("Info:");
|
||||
writer->WriteLine("\tName: " + (std::string)this->info.name);
|
||||
writer->WriteLine(this->info.isPrivate ? "\tPrivate: true"
|
||||
: "\tPrivate: false");
|
||||
writer->WriteLine("\tPiece Length: " +
|
||||
TF_FileSize(this->info.piece_length));
|
||||
if (std::holds_alternative<int64_t>(this->info.fileListOrLength)) {
|
||||
writer->WriteLine("\tIs Single File: true");
|
||||
writer->WriteLine("\tFile length: " +
|
||||
TF_FileSize((uint64_t)std::get<int64_t>(
|
||||
this->info.fileListOrLength)));
|
||||
} else if (std::holds_alternative<std::vector<TorrentFileEntry>>(
|
||||
this->info.fileListOrLength)) {
|
||||
writer->WriteLine("\tIs Single File: false");
|
||||
writer->WriteLine("\tFiles:");
|
||||
auto &files = std::get<std::vector<TorrentFileEntry>>(
|
||||
this->info.fileListOrLength);
|
||||
for (auto &file : files) {
|
||||
writer->WriteLine("\t\tPath: " + file.path.ToString());
|
||||
writer->WriteLine("\t\tLength: " + TF_FileSize(file.length));
|
||||
writer->WriteLine();
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<ReadWriteAt> TorrentFileInfo::GetStreamFromFilesystem(std::shared_ptr<Tesses::Framework::Filesystem::VFS> vfs, const Tesses::Framework::Filesystem::VFSPath& path)
|
||||
{
|
||||
if(std::holds_alternative<std::vector<TorrentFileEntry>>(this->fileListOrLength))
|
||||
{
|
||||
return std::make_shared<TorrentDirectoryStream>(vfs,path / this->name, std::get<std::vector<TorrentFileEntry>>(this->fileListOrLength));
|
||||
}
|
||||
else if(std::holds_alternative<int64_t>(this->fileListOrLength)) {
|
||||
return std::make_shared<TorrentFileStream>(vfs,path / this->name, (uint64_t)std::get<int64_t>(this->fileListOrLength));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<ReadWriteAt> TorrentFileInfo::GetStreamFromFilesystem(
|
||||
std::shared_ptr<Tesses::Framework::Filesystem::VFS> vfs,
|
||||
const Tesses::Framework::Filesystem::VFSPath &path) {
|
||||
if (std::holds_alternative<std::vector<TorrentFileEntry>>(
|
||||
this->fileListOrLength)) {
|
||||
return std::make_shared<TorrentDirectoryStream>(
|
||||
vfs, path / this->name,
|
||||
std::get<std::vector<TorrentFileEntry>>(this->fileListOrLength));
|
||||
} else if (std::holds_alternative<int64_t>(this->fileListOrLength)) {
|
||||
return std::make_shared<TorrentFileStream>(
|
||||
vfs, path / this->name,
|
||||
(uint64_t)std::get<int64_t>(this->fileListOrLength));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void ParsePieces(TorrentFileInfo* fi,std::shared_ptr<ReadWriteAt> rwa, int64_t flength)
|
||||
{
|
||||
int64_t pieces = fi->pieces.data.size()/20;
|
||||
std::vector<uint8_t> buffer;
|
||||
buffer.resize((size_t)fi->piece_length);
|
||||
size_t lastPieceSize = (size_t)(flength % (int64_t)buffer.size());
|
||||
for(int64_t i = 0; i < pieces; i++)
|
||||
{
|
||||
size_t len = buffer.size();
|
||||
if(i == pieces-1 && lastPieceSize != 0) len = std::min(len,lastPieceSize);
|
||||
rwa->ReadBlockAt(i*buffer.size(), buffer.data(), len);
|
||||
auto hsh=Crypto::Sha1::ComputeHash(buffer.data(),len);
|
||||
std::copy(hsh.begin(),hsh.end(),fi->pieces.data.begin()+(i*20));
|
||||
}
|
||||
static void ParsePieces(TorrentFileInfo *fi, std::shared_ptr<ReadWriteAt> rwa,
|
||||
int64_t flength) {
|
||||
int64_t pieces = fi->pieces.data.size() / 20;
|
||||
std::vector<uint8_t> buffer;
|
||||
buffer.resize((size_t)fi->piece_length);
|
||||
size_t lastPieceSize = (size_t)(flength % (int64_t)buffer.size());
|
||||
for (int64_t i = 0; i < pieces; i++) {
|
||||
size_t len = buffer.size();
|
||||
if (i == pieces - 1 && lastPieceSize != 0)
|
||||
len = std::min(len, lastPieceSize);
|
||||
rwa->ReadBlockAt(i * buffer.size(), buffer.data(), len);
|
||||
auto hsh = Crypto::Sha1::ComputeHash(buffer.data(), len);
|
||||
std::copy(hsh.begin(), hsh.end(), fi->pieces.data.begin() + (i * 20));
|
||||
}
|
||||
void TorrentFileInfo::CreateFromFilesystem(std::shared_ptr<Tesses::Framework::Filesystem::VFS> vfs, const Tesses::Framework::Filesystem::VFSPath& path, bool isPrivate, int64_t pieceLength)
|
||||
{
|
||||
this->isPrivate=isPrivate;
|
||||
this->piece_length = pieceLength;
|
||||
this->pieces.data.clear();
|
||||
this->name = path.GetFileName();
|
||||
int64_t len=0;
|
||||
if(vfs->FileExists(path))
|
||||
{
|
||||
auto strm = vfs->OpenFile(path,"rb");
|
||||
len = strm->GetLength();
|
||||
this->fileListOrLength= len;
|
||||
int64_t pieces = len / piece_length;
|
||||
if((len % piece_length) != 0) pieces++;
|
||||
}
|
||||
void TorrentFileInfo::CreateFromFilesystem(
|
||||
std::shared_ptr<Tesses::Framework::Filesystem::VFS> vfs,
|
||||
const Tesses::Framework::Filesystem::VFSPath &path, bool isPrivate,
|
||||
int64_t pieceLength) {
|
||||
this->isPrivate = isPrivate;
|
||||
this->piece_length = pieceLength;
|
||||
this->pieces.data.clear();
|
||||
this->name = path.GetFileName();
|
||||
int64_t len = 0;
|
||||
if (vfs->FileExists(path)) {
|
||||
auto strm = vfs->OpenFile(path, "rb");
|
||||
len = strm->GetLength();
|
||||
this->fileListOrLength = len;
|
||||
int64_t pieces = len / piece_length;
|
||||
if ((len % piece_length) != 0)
|
||||
pieces++;
|
||||
|
||||
this->pieces.data.resize(pieces*20);
|
||||
}
|
||||
else if(vfs->DirectoryExists(path))
|
||||
{
|
||||
std::vector<TorrentFileEntry> ents;
|
||||
std::function<void(Tesses::Framework::Filesystem::VFSPath,Tesses::Framework::Filesystem::VFSPath)> crawl;
|
||||
crawl= [&](Tesses::Framework::Filesystem::VFSPath inFS, Tesses::Framework::Filesystem::VFSPath inTorrent)->void{
|
||||
for(auto ent : vfs->EnumeratePaths(inFS))
|
||||
{
|
||||
if(vfs->FileExists(ent))
|
||||
{
|
||||
auto strm = vfs->OpenFile(ent,"rb");
|
||||
auto flen = strm->GetLength();
|
||||
ents.emplace_back(inTorrent / ent.GetFileName(),flen);
|
||||
this->pieces.data.resize(pieces * 20);
|
||||
} else if (vfs->DirectoryExists(path)) {
|
||||
std::vector<TorrentFileEntry> ents;
|
||||
std::function<void(Tesses::Framework::Filesystem::VFSPath,
|
||||
Tesses::Framework::Filesystem::VFSPath)>
|
||||
crawl;
|
||||
crawl = [&](Tesses::Framework::Filesystem::VFSPath inFS,
|
||||
Tesses::Framework::Filesystem::VFSPath inTorrent) -> void {
|
||||
for (auto ent : vfs->EnumeratePaths(inFS)) {
|
||||
if (vfs->FileExists(ent)) {
|
||||
auto strm = vfs->OpenFile(ent, "rb");
|
||||
auto flen = strm->GetLength();
|
||||
ents.emplace_back(inTorrent / ent.GetFileName(), flen);
|
||||
|
||||
len += flen;
|
||||
}
|
||||
else if(vfs->DirectoryExists(ent))
|
||||
{
|
||||
crawl(ent,inTorrent / ent.GetFileName());
|
||||
}
|
||||
len += flen;
|
||||
} else if (vfs->DirectoryExists(ent)) {
|
||||
crawl(ent, inTorrent / ent.GetFileName());
|
||||
}
|
||||
};
|
||||
Tesses::Framework::Filesystem::VFSPath p2;
|
||||
p2.relative=true;
|
||||
crawl(path, p2);
|
||||
}
|
||||
};
|
||||
Tesses::Framework::Filesystem::VFSPath p2;
|
||||
p2.relative = true;
|
||||
crawl(path, p2);
|
||||
|
||||
this->fileListOrLength = ents;
|
||||
int64_t pieces = len / piece_length;
|
||||
if((len % piece_length) != 0) pieces++;
|
||||
this->fileListOrLength = ents;
|
||||
int64_t pieces = len / piece_length;
|
||||
if ((len % piece_length) != 0)
|
||||
pieces++;
|
||||
|
||||
this->pieces.data.resize(pieces*20);
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("File or directory does not exist");
|
||||
}
|
||||
ParsePieces(this,this->GetStreamFromFilesystem(vfs,path.GetParent()),len);
|
||||
|
||||
|
||||
this->GenerateInfoDictionary();
|
||||
this->pieces.data.resize(pieces * 20);
|
||||
} else {
|
||||
throw std::runtime_error("File or directory does not exist");
|
||||
}
|
||||
ParsePieces(this, this->GetStreamFromFilesystem(vfs, path.GetParent()),
|
||||
len);
|
||||
|
||||
void TorrentFile::CreateTorrent(std::shared_ptr<Tesses::Framework::Streams::Stream> torrent_file_stream,const std::vector<Serialization::Bencode::BeString>& trackers,const std::vector<Serialization::Bencode::BeString>& webseeds, std::shared_ptr<Tesses::Framework::Filesystem::VFS> vfs, const Tesses::Framework::Filesystem::VFSPath& path, bool isPrivate, int64_t pieceLength, Serialization::Bencode::BeString comment, Serialization::Bencode::BeString created_by)
|
||||
{
|
||||
TorrentFile file;
|
||||
file.announce = trackers.at(0);
|
||||
file.announce_list = trackers;
|
||||
file.url_list = webseeds;
|
||||
file.comment = comment;
|
||||
file.created_by = created_by;
|
||||
file.info.CreateFromFilesystem(vfs,path,isPrivate,pieceLength);
|
||||
auto dict=file.ToDictionary();
|
||||
Serialization::Bencode::Bencode::Save(torrent_file_stream,dict);
|
||||
}
|
||||
}
|
||||
this->GenerateInfoDictionary();
|
||||
}
|
||||
|
||||
void TorrentFile::CreateTorrent(
|
||||
std::shared_ptr<Tesses::Framework::Streams::Stream> torrent_file_stream,
|
||||
const std::vector<Serialization::Bencode::BeString> &trackers,
|
||||
const std::vector<Serialization::Bencode::BeString> &webseeds,
|
||||
std::shared_ptr<Tesses::Framework::Filesystem::VFS> vfs,
|
||||
const Tesses::Framework::Filesystem::VFSPath &path, bool isPrivate,
|
||||
int64_t pieceLength, Serialization::Bencode::BeString comment,
|
||||
Serialization::Bencode::BeString created_by) {
|
||||
TorrentFile file;
|
||||
file.announce = trackers.at(0);
|
||||
file.announce_list = trackers;
|
||||
file.url_list = webseeds;
|
||||
file.comment = comment;
|
||||
file.created_by = created_by;
|
||||
file.info.CreateFromFilesystem(vfs, path, isPrivate, pieceLength);
|
||||
auto dict = file.ToDictionary();
|
||||
Serialization::Bencode::Bencode::Save(torrent_file_stream, dict);
|
||||
}
|
||||
} // namespace Tesses::Framework::BitTorrent
|
||||
@@ -1,122 +1,146 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/BitTorrent/TorrentStream.hpp"
|
||||
|
||||
namespace Tesses::Framework::BitTorrent
|
||||
{
|
||||
TorrentFileEntry::TorrentFileEntry()
|
||||
{
|
||||
namespace Tesses::Framework::BitTorrent {
|
||||
TorrentFileEntry::TorrentFileEntry() {}
|
||||
TorrentFileEntry::TorrentFileEntry(Tesses::Framework::Filesystem::VFSPath path,
|
||||
int64_t length)
|
||||
: path(path), length(length) {}
|
||||
|
||||
ReadWriteAt::~ReadWriteAt() {}
|
||||
|
||||
TorrentFileStream::TorrentFileStream(
|
||||
std::shared_ptr<Tesses::Framework::Filesystem::VFS> vfs,
|
||||
Tesses::Framework::Filesystem::VFSPath path, uint64_t length)
|
||||
: vfs(vfs), path(path), length(length) {}
|
||||
bool TorrentFileStream::ReadBlockAt(uint64_t offset, uint8_t *data,
|
||||
size_t len) {
|
||||
if (!vfs->FileExists(path))
|
||||
return false;
|
||||
auto strm = vfs->OpenFile(path, "rb");
|
||||
strm->Seek((int64_t)offset, Streams::SeekOrigin::Begin);
|
||||
strm->ReadBlock(data, len);
|
||||
return true;
|
||||
}
|
||||
void TorrentFileStream::WriteBlockAt(uint64_t offset, const uint8_t *data,
|
||||
size_t len) {
|
||||
len =
|
||||
(size_t)std::min((int64_t)len, (int64_t)this->length - (int64_t)offset);
|
||||
if (len == 0)
|
||||
return;
|
||||
|
||||
mtx.Lock();
|
||||
auto strm = vfs->OpenFile(path, "wb");
|
||||
strm->Seek((int64_t)offset, Streams::SeekOrigin::Begin);
|
||||
strm->WriteBlock(data, len);
|
||||
mtx.Unlock();
|
||||
}
|
||||
|
||||
TorrentDirectoryStream::TorrentDirectoryStream(
|
||||
std::shared_ptr<Tesses::Framework::Filesystem::VFS> vfs,
|
||||
Tesses::Framework::Filesystem::VFSPath path,
|
||||
std::vector<TorrentFileEntry> entries)
|
||||
: vfs(vfs), path(path), entries(entries) {
|
||||
if (!vfs->DirectoryExists(path))
|
||||
this->vfs->CreateDirectory(path);
|
||||
this->mtxs.resize(entries.size());
|
||||
}
|
||||
|
||||
// From https://www.seanjoflynn.com/research/bittorrent.html , which is licensed
|
||||
// under MIT based on code repo
|
||||
bool TorrentDirectoryStream::ReadBlockAt(uint64_t offset, uint8_t *data,
|
||||
size_t len) {
|
||||
uint64_t currentOffset = 0;
|
||||
uint64_t end = offset + len;
|
||||
for (size_t i = 0; i < this->entries.size(); i++) {
|
||||
if (offset < currentOffset && end < currentOffset) {
|
||||
currentOffset += this->entries[i].length;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (offset > currentOffset + this->entries[i].length &&
|
||||
end > currentOffset + this->entries[i].length) {
|
||||
currentOffset += this->entries[i].length;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto path = this->path / this->entries[i].path;
|
||||
if (!vfs->FileExists(path))
|
||||
return false;
|
||||
|
||||
int64_t fstart =
|
||||
std::max((int64_t)0, (int64_t)offset - (int64_t)currentOffset);
|
||||
int64_t fend = std::min((int64_t)end - (int64_t)currentOffset,
|
||||
(int64_t)this->entries[i].length);
|
||||
int flength = (int)(fend - fstart);
|
||||
int bstart =
|
||||
std::max((int)0, (int)((int64_t)currentOffset - (int64_t)offset));
|
||||
auto strm = vfs->OpenFile(path, "rb");
|
||||
strm->Seek(fstart, Streams::SeekOrigin::Begin);
|
||||
strm->ReadBlock(data + bstart, flength);
|
||||
|
||||
currentOffset += this->entries[i].length;
|
||||
}
|
||||
TorrentFileEntry::TorrentFileEntry(Tesses::Framework::Filesystem::VFSPath path, int64_t length): path(path), length(length)
|
||||
{}
|
||||
return true;
|
||||
}
|
||||
|
||||
ReadWriteAt::~ReadWriteAt(){}
|
||||
// From https://www.seanjoflynn.com/research/bittorrent.html , which is licensed
|
||||
// under MIT based on code repo
|
||||
void TorrentDirectoryStream::WriteBlockAt(uint64_t offset, const uint8_t *data,
|
||||
size_t len) {
|
||||
uint64_t currentOffset = 0;
|
||||
uint64_t end = offset + len;
|
||||
for (size_t i = 0; i < this->entries.size(); i++) {
|
||||
if (offset < currentOffset && end < currentOffset) {
|
||||
currentOffset += this->entries[i].length;
|
||||
continue;
|
||||
}
|
||||
|
||||
TorrentFileStream::TorrentFileStream(std::shared_ptr<Tesses::Framework::Filesystem::VFS> vfs, Tesses::Framework::Filesystem::VFSPath path,uint64_t length) : vfs(vfs), path(path), length(length)
|
||||
{
|
||||
}
|
||||
bool TorrentFileStream::ReadBlockAt(uint64_t offset, uint8_t* data, size_t len)
|
||||
{
|
||||
if(!vfs->FileExists(path)) return false;
|
||||
auto strm = vfs->OpenFile(path,"rb");
|
||||
strm->Seek((int64_t)offset,Streams::SeekOrigin::Begin);
|
||||
strm->ReadBlock(data,len);
|
||||
return true;
|
||||
}
|
||||
void TorrentFileStream::WriteBlockAt(uint64_t offset, const uint8_t* data, size_t len)
|
||||
{
|
||||
len = (size_t)std::min((int64_t)len, (int64_t)this->length-(int64_t)offset);
|
||||
if(len == 0) return;
|
||||
if (offset > currentOffset + this->entries[i].length &&
|
||||
end > currentOffset + this->entries[i].length) {
|
||||
currentOffset += this->entries[i].length;
|
||||
continue;
|
||||
}
|
||||
|
||||
mtx.Lock();
|
||||
auto strm = vfs->OpenFile(path,"wb");
|
||||
strm->Seek((int64_t)offset,Streams::SeekOrigin::Begin);
|
||||
strm->WriteBlock(data,len);
|
||||
mtx.Unlock();
|
||||
}
|
||||
auto path = this->path / this->entries[i].path;
|
||||
auto parent = path.GetParent();
|
||||
if (!vfs->DirectoryExists(parent))
|
||||
vfs->CreateDirectory(parent);
|
||||
|
||||
TorrentDirectoryStream::TorrentDirectoryStream(std::shared_ptr<Tesses::Framework::Filesystem::VFS> vfs, Tesses::Framework::Filesystem::VFSPath path,std::vector<TorrentFileEntry> entries) : vfs(vfs), path(path), entries(entries)
|
||||
{
|
||||
if(!vfs->DirectoryExists(path))
|
||||
this->vfs->CreateDirectory(path);
|
||||
this->mtxs.resize(entries.size());
|
||||
}
|
||||
int64_t fstart =
|
||||
std::max((int64_t)0, (int64_t)offset - (int64_t)currentOffset);
|
||||
int64_t fend = std::min((int64_t)end - (int64_t)currentOffset,
|
||||
(int64_t)this->entries[i].length);
|
||||
int flength = (int)(fend - fstart);
|
||||
int bstart =
|
||||
std::max((int)0, (int)((int64_t)currentOffset - (int64_t)offset));
|
||||
|
||||
// From https://www.seanjoflynn.com/research/bittorrent.html , which is licensed under MIT based on code repo
|
||||
bool TorrentDirectoryStream::ReadBlockAt(uint64_t offset, uint8_t* data, size_t len)
|
||||
{
|
||||
uint64_t currentOffset = 0;
|
||||
uint64_t end = offset + len;
|
||||
for(size_t i = 0; i < this->entries.size(); i++)
|
||||
this->mtxs[i].Lock();
|
||||
{
|
||||
if(offset < currentOffset && end < currentOffset) {
|
||||
currentOffset += this->entries[i].length;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(offset > currentOffset + this->entries[i].length && end > currentOffset + this->entries[i].length ){
|
||||
currentOffset += this->entries[i].length;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto path = this->path / this->entries[i].path;
|
||||
if(!vfs->FileExists(path)) return false;
|
||||
|
||||
int64_t fstart = std::max((int64_t)0,(int64_t)offset - (int64_t)currentOffset);
|
||||
int64_t fend = std::min((int64_t)end - (int64_t)currentOffset, (int64_t)this->entries[i].length);
|
||||
int flength = (int)(fend - fstart);
|
||||
int bstart = std::max((int)0,(int)((int64_t)currentOffset - (int64_t)offset));
|
||||
auto strm = vfs->OpenFile(path,"rb");
|
||||
auto strm = vfs->OpenFile(path, "wb");
|
||||
strm->Seek(fstart, Streams::SeekOrigin::Begin);
|
||||
strm->ReadBlock(data+bstart,flength);
|
||||
|
||||
|
||||
|
||||
currentOffset += this->entries[i].length;
|
||||
|
||||
|
||||
strm->WriteBlock(data + bstart, flength);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
this->mtxs[i].Unlock();
|
||||
|
||||
// From https://www.seanjoflynn.com/research/bittorrent.html , which is licensed under MIT based on code repo
|
||||
void TorrentDirectoryStream::WriteBlockAt(uint64_t offset, const uint8_t* data, size_t len)
|
||||
{
|
||||
uint64_t currentOffset = 0;
|
||||
uint64_t end = offset + len;
|
||||
for(size_t i = 0; i < this->entries.size(); i++)
|
||||
{
|
||||
if(offset < currentOffset && end < currentOffset) {
|
||||
currentOffset += this->entries[i].length;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(offset > currentOffset + this->entries[i].length && end > currentOffset + this->entries[i].length ){
|
||||
currentOffset += this->entries[i].length;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto path = this->path / this->entries[i].path;
|
||||
auto parent = path.GetParent();
|
||||
if(!vfs->DirectoryExists(parent))
|
||||
vfs->CreateDirectory(parent);
|
||||
|
||||
int64_t fstart = std::max((int64_t)0,(int64_t)offset - (int64_t)currentOffset);
|
||||
int64_t fend = std::min((int64_t)end - (int64_t)currentOffset, (int64_t)this->entries[i].length);
|
||||
int flength = (int)(fend - fstart);
|
||||
int bstart = std::max((int)0,(int)((int64_t)currentOffset - (int64_t)offset));
|
||||
|
||||
this->mtxs[i].Lock();
|
||||
{
|
||||
auto strm = vfs->OpenFile(path,"wb");
|
||||
strm->Seek(fstart, Streams::SeekOrigin::Begin);
|
||||
strm->WriteBlock(data+bstart,flength);
|
||||
}
|
||||
this->mtxs[i].Unlock();
|
||||
|
||||
|
||||
currentOffset += this->entries[i].length;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
currentOffset += this->entries[i].length;
|
||||
}
|
||||
}
|
||||
} // namespace Tesses::Framework::BitTorrent
|
||||
869
src/Console.cpp
Normal file
869
src/Console.cpp
Normal file
@@ -0,0 +1,869 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/Console.hpp"
|
||||
#include "TessesFramework/Http/HttpUtils.hpp"
|
||||
#include "TessesFramework/Text/StringConverter.hpp"
|
||||
|
||||
#if __has_include(<termios.h>)
|
||||
#include <sys/ioctl.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#if defined(_WIN32)
|
||||
#include <cstdlib>
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace Tesses::Framework {
|
||||
|
||||
size_t Console::List(std::vector<std::string> &strs) {
|
||||
if (!IsTTY())
|
||||
return std::string::npos;
|
||||
|
||||
auto echo = Console::GetEcho();
|
||||
auto canonical = Console::GetCanonical();
|
||||
Console::SetEcho(false);
|
||||
Console::SetCanonical(false);
|
||||
size_t i = 0;
|
||||
while (true) {
|
||||
Console::Clear();
|
||||
auto size = Console::GetSize();
|
||||
// Console::SetBackgroundColor(ConsoleColor::CC_WHITE,true);
|
||||
// Console::SetForegroundColor(ConsoleColor::CC_BLACK,true);
|
||||
|
||||
size_t page = size.second == 0 ? 0 : (i / (size.second));
|
||||
size_t offsetInPage = size.second == 0 ? 0 : (i % (size.second));
|
||||
|
||||
for (int item = 0; item < size.second; ++item) {
|
||||
if (item == offsetInPage) {
|
||||
Console::SetBackgroundColor(ConsoleColor::CC_WHITE, true);
|
||||
Console::SetForegroundColor(ConsoleColor::CC_BLACK, false);
|
||||
} else {
|
||||
Console::SetBackgroundColor(ConsoleColor::CC_BLACK, false);
|
||||
Console::SetForegroundColor(ConsoleColor::CC_WHITE, true);
|
||||
}
|
||||
|
||||
size_t myOffset = (size_t)item + page * (size.second);
|
||||
|
||||
if (myOffset < strs.size()) {
|
||||
if (item == size.second - 1) {
|
||||
Console::Write(strs[myOffset]);
|
||||
} else {
|
||||
Console::WriteLine(strs[myOffset]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Console::SetBackgroundColor(ConsoleColor::CC_BLACK, false);
|
||||
Console::SetForegroundColor(ConsoleColor::CC_WHITE, true);
|
||||
|
||||
int code = Console::Read();
|
||||
|
||||
if (code == 10)
|
||||
break;
|
||||
if (code == -1)
|
||||
break;
|
||||
|
||||
if (code == 27) {
|
||||
code = Console::Read();
|
||||
if (code == 91) {
|
||||
code = Console::Read();
|
||||
if (code == 65) {
|
||||
i--;
|
||||
if (i >= strs.size())
|
||||
i = strs.size() - 1;
|
||||
} else if (code == 66) {
|
||||
i++;
|
||||
if (i >= strs.size())
|
||||
i = 0;
|
||||
} else if (code == -1)
|
||||
break;
|
||||
} else if (code == -1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
Console::SetEcho(echo);
|
||||
Console::SetCanonical(canonical);
|
||||
Console::SetBackgroundColor(ConsoleColor::CC_BLACK, false);
|
||||
Console::SetForegroundColor(ConsoleColor::CC_WHITE, true);
|
||||
return i;
|
||||
}
|
||||
|
||||
void Console::SetForegroundColor(ConsoleColor col, bool alt) {
|
||||
|
||||
if (!IsTTY())
|
||||
return;
|
||||
#if __has_include(<termios.h>)
|
||||
if (alt) {
|
||||
printf("\x1b[%im", ((int)col) + 90); // this should be Write
|
||||
} else {
|
||||
printf("\x1b[%im", ((int)col) + 30); // this too
|
||||
}
|
||||
#elif defined(_WIN32)
|
||||
|
||||
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (hConsole == INVALID_HANDLE_VALUE)
|
||||
return;
|
||||
CONSOLE_SCREEN_BUFFER_INFO bi;
|
||||
|
||||
GetConsoleScreenBufferInfo(hConsole, &bi);
|
||||
|
||||
bi.wAttributes &= ~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE |
|
||||
FOREGROUND_INTENSITY);
|
||||
|
||||
if (col == ConsoleColor::CC_RED || col == ConsoleColor::CC_YELLOW ||
|
||||
col == ConsoleColor::CC_MAGENTA || col == ConsoleColor::CC_WHITE)
|
||||
bi.wAttributes |= FOREGROUND_RED;
|
||||
|
||||
if (col == ConsoleColor::CC_GREEN || col == ConsoleColor::CC_YELLOW ||
|
||||
col == ConsoleColor::CC_CYAN || col == ConsoleColor::CC_WHITE)
|
||||
bi.wAttributes |= FOREGROUND_GREEN;
|
||||
|
||||
if (col == ConsoleColor::CC_BLUE || col == ConsoleColor::CC_CYAN ||
|
||||
col == ConsoleColor::CC_MAGENTA || col == ConsoleColor::CC_WHITE)
|
||||
bi.wAttributes |= FOREGROUND_BLUE;
|
||||
|
||||
if (alt)
|
||||
bi.wAttributes |= FOREGROUND_INTENSITY;
|
||||
|
||||
SetConsoleTextAttribute(hConsole, bi.wAttributes);
|
||||
|
||||
#else
|
||||
if (alt) {
|
||||
printf("\x1b[%im", ((int)col) + 90); // this should be Write
|
||||
} else {
|
||||
printf("\x1b[%im", ((int)col) + 30); // this too
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Console::SetBackgroundColor(ConsoleColor col, bool alt) {
|
||||
if (!IsTTY())
|
||||
return;
|
||||
#if __has_include(<termios.h>)
|
||||
if (alt) {
|
||||
printf("\x1b[%im", ((int)col) + 100); // this should be Write
|
||||
} else {
|
||||
printf("\x1b[%im", ((int)col) + 40); // this too
|
||||
}
|
||||
#elif defined(_WIN32)
|
||||
|
||||
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (hConsole == INVALID_HANDLE_VALUE)
|
||||
return;
|
||||
CONSOLE_SCREEN_BUFFER_INFO bi;
|
||||
|
||||
GetConsoleScreenBufferInfo(hConsole, &bi);
|
||||
|
||||
bi.wAttributes &= ~(BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE |
|
||||
BACKGROUND_INTENSITY);
|
||||
|
||||
if (col == ConsoleColor::CC_RED || col == ConsoleColor::CC_YELLOW ||
|
||||
col == ConsoleColor::CC_MAGENTA || col == ConsoleColor::CC_WHITE)
|
||||
bi.wAttributes |= BACKGROUND_RED;
|
||||
|
||||
if (col == ConsoleColor::CC_GREEN || col == ConsoleColor::CC_YELLOW ||
|
||||
col == ConsoleColor::CC_CYAN || col == ConsoleColor::CC_WHITE)
|
||||
bi.wAttributes |= BACKGROUND_GREEN;
|
||||
|
||||
if (col == ConsoleColor::CC_BLUE || col == ConsoleColor::CC_CYAN ||
|
||||
col == ConsoleColor::CC_MAGENTA || col == ConsoleColor::CC_WHITE)
|
||||
bi.wAttributes |= BACKGROUND_BLUE;
|
||||
|
||||
if (alt)
|
||||
bi.wAttributes |= BACKGROUND_INTENSITY;
|
||||
|
||||
SetConsoleTextAttribute(hConsole, bi.wAttributes);
|
||||
#else
|
||||
if (alt) {
|
||||
printf("\x1b[%im", ((int)col) + 100); // this should be Write
|
||||
} else {
|
||||
printf("\x1b[%im", ((int)col) + 40); // this too
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Console::SetEcho(bool echo) {
|
||||
if (!IsTTY())
|
||||
return false;
|
||||
#if __has_include(<termios.h>)
|
||||
struct termios raw;
|
||||
if (tcgetattr(0, &raw) != 0)
|
||||
return false;
|
||||
if (echo) {
|
||||
raw.c_lflag |= ECHO;
|
||||
} else {
|
||||
raw.c_lflag &= ~(ECHO);
|
||||
}
|
||||
|
||||
if (tcsetattr(0, TCSAFLUSH, &raw) != 0)
|
||||
return false;
|
||||
|
||||
#elif defined(_WIN32)
|
||||
HANDLE hConsole = GetStdHandle(STD_INPUT_HANDLE);
|
||||
if (hConsole == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
DWORD dwMode;
|
||||
|
||||
if (!GetConsoleMode(hConsole, &dwMode))
|
||||
return false;
|
||||
if (echo)
|
||||
dwMode |= ENABLE_ECHO_INPUT;
|
||||
else
|
||||
dwMode &= ~(ENABLE_ECHO_INPUT);
|
||||
if (SetConsoleMode(hConsole, dwMode))
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
bool Console::GetEcho() {
|
||||
if (!IsTTY())
|
||||
return false;
|
||||
#if __has_include(<termios.h>)
|
||||
|
||||
struct termios raw;
|
||||
if (tcgetattr(0, &raw) != 0)
|
||||
return true;
|
||||
return (raw.c_lflag & ECHO) > 0;
|
||||
#elif defined(_WIN32)
|
||||
HANDLE hConsole = GetStdHandle(STD_INPUT_HANDLE);
|
||||
if (hConsole == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
DWORD dwMode;
|
||||
|
||||
if (GetConsoleMode(hConsole, &dwMode)) {
|
||||
return (dwMode & ENABLE_ECHO_INPUT) != 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
bool Console::GetCanonical() {
|
||||
if (!IsTTY())
|
||||
return false;
|
||||
#if __has_include(<termios.h>)
|
||||
struct termios raw;
|
||||
if (tcgetattr(0, &raw) != 0)
|
||||
return false;
|
||||
return (raw.c_lflag & ICANON) > 0;
|
||||
#elif defined(_WIN32)
|
||||
HANDLE hConsole = GetStdHandle(STD_INPUT_HANDLE);
|
||||
if (hConsole == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
DWORD dwMode;
|
||||
|
||||
if (GetConsoleMode(hConsole, &dwMode)) {
|
||||
return (dwMode & ENABLE_LINE_INPUT) != 0;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
bool Console::SetCanonical(bool can) {
|
||||
if (!IsTTY())
|
||||
return false;
|
||||
#if __has_include(<termios.h>)
|
||||
struct termios raw;
|
||||
if (tcgetattr(0, &raw) != 0)
|
||||
return false;
|
||||
if (can) {
|
||||
raw.c_lflag |= ICANON;
|
||||
} else {
|
||||
raw.c_lflag &= ~(ICANON);
|
||||
}
|
||||
|
||||
if (tcsetattr(0, TCSAFLUSH, &raw) != 0)
|
||||
return false;
|
||||
|
||||
#elif defined(_WIN32)
|
||||
HANDLE hConsole = GetStdHandle(STD_INPUT_HANDLE);
|
||||
if (hConsole == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
DWORD dwMode;
|
||||
|
||||
if (!GetConsoleMode(hConsole, &dwMode))
|
||||
return false;
|
||||
if (can)
|
||||
dwMode |= ENABLE_LINE_INPUT;
|
||||
else
|
||||
dwMode &= ~(ENABLE_LINE_INPUT);
|
||||
if (SetConsoleMode(hConsole, dwMode))
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Console::GetSignals() {
|
||||
if (!IsTTY())
|
||||
return true;
|
||||
#if __has_include(<termios.h>)
|
||||
struct termios raw;
|
||||
if (tcgetattr(0, &raw) != 0)
|
||||
return false;
|
||||
return (raw.c_lflag & ISIG) > 0;
|
||||
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
bool Console::SetSignals(bool sig) {
|
||||
if (!IsTTY())
|
||||
return false;
|
||||
#if __has_include(<termios.h>)
|
||||
struct termios raw;
|
||||
if (tcgetattr(0, &raw) != 0)
|
||||
return false;
|
||||
if (sig) {
|
||||
raw.c_lflag |= ISIG;
|
||||
} else {
|
||||
raw.c_lflag &= ~(ISIG);
|
||||
}
|
||||
|
||||
if (tcsetattr(0, TCSAFLUSH, &raw) != 0)
|
||||
return false;
|
||||
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
// ISIG
|
||||
|
||||
std::string Console::ReadPassword() {
|
||||
bool echo = GetEcho();
|
||||
if (!SetEcho(false)) {
|
||||
Write("\nWARN: the password will be visible: ");
|
||||
}
|
||||
std::string text = ReadLine();
|
||||
SetEcho(echo);
|
||||
Write("\n");
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
thread_local std::string key;
|
||||
thread_local int keyOffset = -1;
|
||||
#endif
|
||||
|
||||
int Console::Read() {
|
||||
#if defined(WIN32)
|
||||
if (keyOffset >= 0 && keyOffset < key.size()) {
|
||||
return (int)key[keyOffset++];
|
||||
} else {
|
||||
keyOffset = -1;
|
||||
}
|
||||
if (!Console::GetCanonical()) {
|
||||
|
||||
HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
|
||||
if (hIn == INVALID_HANDLE_VALUE)
|
||||
return -1;
|
||||
|
||||
DWORD nRead;
|
||||
|
||||
INPUT_RECORD ir;
|
||||
|
||||
while (ReadConsoleInputW(hIn, &ir, 1, &nRead)) {
|
||||
if (nRead == 0)
|
||||
return -1;
|
||||
if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown) {
|
||||
if (ir.Event.KeyEvent.wVirtualKeyCode == VK_UP) {
|
||||
key = {27, 91, 65};
|
||||
keyOffset = 1;
|
||||
return key[0];
|
||||
} else if (ir.Event.KeyEvent.wVirtualKeyCode == VK_DOWN) {
|
||||
key = {27, 91, 66};
|
||||
keyOffset = 1;
|
||||
return key[0];
|
||||
} else if (ir.Event.KeyEvent.wVirtualKeyCode == VK_LEFT) {
|
||||
key = {27, 91, 68};
|
||||
keyOffset = 1;
|
||||
return key[0];
|
||||
} else if (ir.Event.KeyEvent.wVirtualKeyCode == VK_RIGHT) {
|
||||
key = {27, 91, 67};
|
||||
keyOffset = 1;
|
||||
return key[0];
|
||||
} else if (ir.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE) {
|
||||
return 27;
|
||||
} else if (ir.Event.KeyEvent.wVirtualKeyCode == VK_DELETE) {
|
||||
key = {27, 91, 51, 126};
|
||||
keyOffset = 1;
|
||||
return key[0];
|
||||
} else if (ir.Event.KeyEvent.wVirtualKeyCode == VK_RETURN) {
|
||||
return 10;
|
||||
}
|
||||
|
||||
else if (ir.Event.KeyEvent.uChar.UnicodeChar != 0) {
|
||||
if (ir.Event.KeyEvent.uChar.UnicodeChar <= 127) {
|
||||
return (int)ir.Event.KeyEvent.uChar.UnicodeChar;
|
||||
} else if (ir.Event.KeyEvent.uChar.UnicodeChar >= 0xD800 &&
|
||||
ir.Event.KeyEvent.uChar.UnicodeChar <= 0xDBFF) {
|
||||
|
||||
std::u16string str = {
|
||||
{(char16_t)ir.Event.KeyEvent.uChar.UnicodeChar, 0}};
|
||||
while (ReadConsoleInputW(hIn, &ir, 1, &nRead)) {
|
||||
if (ir.EventType == KEY_EVENT &&
|
||||
ir.Event.KeyEvent.bKeyDown) {
|
||||
|
||||
if (nRead == 0)
|
||||
return -1;
|
||||
if (ir.Event.KeyEvent.uChar.UnicodeChar >=
|
||||
0xDC00 &&
|
||||
ir.Event.KeyEvent.uChar.UnicodeChar <=
|
||||
0xDFFF) {
|
||||
str[1] =
|
||||
(char16_t)
|
||||
ir.Event.KeyEvent.uChar.UnicodeChar;
|
||||
break;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
key.clear();
|
||||
keyOffset = 1;
|
||||
Tesses::Framework::Text::StringConverter::UTF8::
|
||||
FromUTF16(key, str);
|
||||
if (!key.empty())
|
||||
return key[0];
|
||||
return -1;
|
||||
|
||||
} else if (ir.Event.KeyEvent.uChar.UnicodeChar > 127) {
|
||||
// normal unicode char
|
||||
key.clear();
|
||||
keyOffset = 1;
|
||||
|
||||
std::u16string str = {
|
||||
{(char16_t)ir.Event.KeyEvent.uChar.UnicodeChar}};
|
||||
|
||||
Tesses::Framework::Text::StringConverter::UTF8::
|
||||
FromUTF16(key, str);
|
||||
|
||||
if (!key.empty())
|
||||
return key[0];
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
} else {
|
||||
WCHAR chr;
|
||||
DWORD chrRead;
|
||||
HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
|
||||
if (hIn == INVALID_HANDLE_VALUE)
|
||||
return -1;
|
||||
if (ReadConsoleW(hIn, &chr, 1, &chrRead, NULL)) {
|
||||
if (chrRead == 0)
|
||||
return -1;
|
||||
|
||||
if (chr <= 127)
|
||||
return (int)chr;
|
||||
else if (chr >= 0xD800 && chr <= 0xDBFF) {
|
||||
std::u16string str = {{(char16_t)chr, 0}};
|
||||
|
||||
if (ReadConsoleW(hIn, &chr, 1, &chrRead, NULL)) {
|
||||
if (chrRead == 0)
|
||||
return -1;
|
||||
if (chr >= 0xDC00 && chr <= 0xDFFF) {
|
||||
str[1] = (char16_t)chr;
|
||||
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
key.clear();
|
||||
keyOffset = 1;
|
||||
Tesses::Framework::Text::StringConverter::UTF8::FromUTF16(key,
|
||||
str);
|
||||
if (!key.empty())
|
||||
return key[0];
|
||||
return -1;
|
||||
} else if (chr > 127) {
|
||||
// normal unicode char
|
||||
key.clear();
|
||||
keyOffset = 1;
|
||||
|
||||
std::u16string str = {{(char16_t)chr}};
|
||||
|
||||
Tesses::Framework::Text::StringConverter::UTF8::FromUTF16(key,
|
||||
str);
|
||||
|
||||
if (!key.empty())
|
||||
return key[0];
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
int res = fgetc(stdin);
|
||||
if (res == EOF)
|
||||
return -1; // make sure it's -1
|
||||
return res;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string Console::ReadLine() {
|
||||
std::string text;
|
||||
int c = Read();
|
||||
while (c != '\n' && c != -1) {
|
||||
text += (char)c;
|
||||
|
||||
c = Read();
|
||||
}
|
||||
if (!text.empty() && text.back() == '\r')
|
||||
text.resize(text.size() - 1);
|
||||
return text;
|
||||
}
|
||||
#if defined(_WIN32)
|
||||
void writeTextWin32(HANDLE hndl, const std::string &text) {
|
||||
std::u16string str = {};
|
||||
|
||||
Text::StringConverter::UTF16::FromUTF8(str, text);
|
||||
|
||||
auto buff = str.data();
|
||||
DWORD left = (DWORD)str.size();
|
||||
|
||||
DWORD read = 0;
|
||||
do {
|
||||
|
||||
if (WriteConsoleW(hndl, buff, left, &read, NULL)) {
|
||||
buff += read;
|
||||
left -= read;
|
||||
}
|
||||
} while (read > 0);
|
||||
}
|
||||
#else
|
||||
void writeTextUnix(FILE *f, std::string_view view) {
|
||||
size_t left = view.size();
|
||||
const char *ptr = view.data();
|
||||
size_t read = 0;
|
||||
do {
|
||||
read = fwrite(ptr, 1, left, f);
|
||||
ptr += read;
|
||||
left -= read;
|
||||
} while (read != 0);
|
||||
}
|
||||
#endif
|
||||
void Console::WriteToStream(std::string_view view, bool isStderr) {
|
||||
#if defined(_WIN32)
|
||||
HANDLE hOut = GetStdHandle(isStderr ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE);
|
||||
if (hOut == INVALID_HANDLE_VALUE)
|
||||
return;
|
||||
std::string text;
|
||||
|
||||
for (size_t i = 0; i < view.size(); ++i) {
|
||||
if (view[i] == '\x1b') {
|
||||
if (!text.empty()) {
|
||||
writeTextWin32(hOut, text);
|
||||
// fwrite(text.c_str(), 1, text.size(), f);
|
||||
text.clear();
|
||||
}
|
||||
i++;
|
||||
if (i < view.size()) {
|
||||
if (view[i] == '[') {
|
||||
i++;
|
||||
for (; i < view.size(); ++i) {
|
||||
text += view[i];
|
||||
if (view[i] >= 0x40 && view[i] <= 0x7e)
|
||||
break;
|
||||
}
|
||||
if (text.size() > 0 && text.back() == 'H') {
|
||||
MoveToHome();
|
||||
}
|
||||
if (text.size() > 0 && text.back() == 'J') {
|
||||
if (text[0] == '2') {
|
||||
ClearRetainPosition(
|
||||
ClearBehaviour::CB_ENTIRESCREEN);
|
||||
}
|
||||
|
||||
if (text[0] == '1') {
|
||||
ClearRetainPosition(
|
||||
ClearBehaviour::CB_CURSORANDABOVE);
|
||||
}
|
||||
if (text[0] == '0') {
|
||||
ClearRetainPosition(
|
||||
ClearBehaviour::CB_CURSORANDBELOW);
|
||||
}
|
||||
}
|
||||
|
||||
if (text.size() > 0 && text.back() == 'm') {
|
||||
|
||||
try {
|
||||
auto num =
|
||||
std::stol(text.substr(0, text.size() - 1));
|
||||
if (num >= 30 && num <= 37) {
|
||||
SetForegroundColor((ConsoleColor)(num - 30),
|
||||
false);
|
||||
} else if (num >= 40 && num <= 47) {
|
||||
SetBackgroundColor((ConsoleColor)(num - 40),
|
||||
false);
|
||||
} else if (num >= 90 && num <= 97) {
|
||||
SetForegroundColor((ConsoleColor)(num - 90),
|
||||
true);
|
||||
} else if (num >= 100 && num <= 107) {
|
||||
SetBackgroundColor((ConsoleColor)(num - 100),
|
||||
true);
|
||||
}
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
|
||||
text.clear();
|
||||
|
||||
} else {
|
||||
text += "\x1B";
|
||||
text += view[i];
|
||||
}
|
||||
}
|
||||
|
||||
} else if (view[i] == '\n') {
|
||||
text += "\r\n";
|
||||
|
||||
writeTextWin32(hOut, text);
|
||||
text.clear();
|
||||
} else {
|
||||
text += view[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (!text.empty()) {
|
||||
|
||||
writeTextWin32(hOut, text);
|
||||
text.clear();
|
||||
}
|
||||
|
||||
#else
|
||||
writeTextUnix(isStderr ? stderr : stdout, view);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Console::Write(std::string text) { WriteToStream(text, false); }
|
||||
void Console::WriteLine(std::string text) {
|
||||
Write(text);
|
||||
Write("\n");
|
||||
}
|
||||
|
||||
void Console::WriteLineView(std::string_view view) {
|
||||
std::string lf = "\n";
|
||||
WriteToStream(view, false);
|
||||
WriteToStream(lf, false);
|
||||
}
|
||||
void Console::WriteView(std::string_view view) { WriteToStream(view, false); }
|
||||
void Console::Error(std::string text) { WriteToStream(text, true); }
|
||||
void Console::ErrorLine(std::string text) {
|
||||
Error(text);
|
||||
Error("\n");
|
||||
}
|
||||
|
||||
void Console::ErrorLineView(std::string_view view) {
|
||||
std::string lf = "\n";
|
||||
WriteToStream(view, true);
|
||||
WriteToStream(lf, true);
|
||||
}
|
||||
void Console::ErrorView(std::string_view view) { WriteToStream(view, true); }
|
||||
bool Console::IsTTY() {
|
||||
#if defined(_WIN32)
|
||||
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (hOut == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
DWORD tmp;
|
||||
if (GetConsoleMode(hOut, &tmp))
|
||||
return true;
|
||||
#elif __has_include(<termios.h>)
|
||||
if (isatty(STDOUT_FILENO))
|
||||
return true;
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Console::ProgressBar(double pdbl) {
|
||||
bool showBar = IsTTY();
|
||||
if (pdbl < 0)
|
||||
pdbl = 0;
|
||||
if (pdbl > 1)
|
||||
pdbl = 1;
|
||||
|
||||
WriteView("\r");
|
||||
if (showBar) {
|
||||
auto sz = GetSize();
|
||||
|
||||
int totalBlocks = sz.first - 10;
|
||||
if (totalBlocks > 0) {
|
||||
WriteView("[\033[0;32m");
|
||||
int i;
|
||||
int off = pdbl * totalBlocks;
|
||||
for (int i = 0; i < totalBlocks; i++) {
|
||||
if (i < off)
|
||||
WriteView("=");
|
||||
else
|
||||
WriteView(" ");
|
||||
}
|
||||
|
||||
WriteView("\033[0m] ");
|
||||
}
|
||||
}
|
||||
|
||||
std::string mesg = Http::HttpUtils::LeftPad(
|
||||
std::to_string((int)(pdbl * 100)) + "%", 4, ' ');
|
||||
WriteView(mesg);
|
||||
Flush();
|
||||
}
|
||||
void Console::ProgressBar(int v) { ProgressBar((double)v / 100.0); }
|
||||
|
||||
std::pair<int, int> Console::GetSize() {
|
||||
if (!Console::IsTTY())
|
||||
return std::pair<int, int>(0, 0);
|
||||
|
||||
#if __has_include(<termios.h>)
|
||||
struct winsize ws;
|
||||
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0) {
|
||||
return std::make_pair<int, int>(ws.ws_col, ws.ws_row);
|
||||
}
|
||||
#elif defined(_WIN32)
|
||||
|
||||
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (hConsole == INVALID_HANDLE_VALUE)
|
||||
return std::make_pair<int, int>(0, 0);
|
||||
CONSOLE_SCREEN_BUFFER_INFO bi;
|
||||
|
||||
GetConsoleScreenBufferInfo(hConsole, &bi);
|
||||
|
||||
return std::make_pair<int, int>(bi.srWindow.Right - bi.srWindow.Left + 1,
|
||||
bi.srWindow.Bottom - bi.srWindow.Top + 1);
|
||||
#endif
|
||||
|
||||
return std::make_pair<int, int>(0, 0);
|
||||
}
|
||||
|
||||
void Console::SetPosition(int x, int y) {
|
||||
if (!IsTTY())
|
||||
return;
|
||||
#if __has_include(<termios.h>)
|
||||
printf("\x1B[%i;%iH", y + 1, x + 1);
|
||||
#elif defined(_WIN32)
|
||||
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (hOut == INVALID_HANDLE_VALUE)
|
||||
return;
|
||||
COORD coord = {(SHORT)x, (SHORT)y};
|
||||
SetConsoleCursorPosition(hOut, coord);
|
||||
#else
|
||||
printf("\x1B[%i;%iH", y + 1, x + 1);
|
||||
Flush();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Console::Flush() { fflush(stdout); }
|
||||
|
||||
void Console::Clear() {
|
||||
ClearRetainPosition(ClearBehaviour::CB_ENTIRESCREEN);
|
||||
MoveToHome();
|
||||
}
|
||||
|
||||
void Console::ClearRetainPosition(ClearBehaviour cb) {
|
||||
#if __has_include(<termios.h>)
|
||||
if (isatty(STDOUT_FILENO)) {
|
||||
printf("\x1b[%iJ", (int)cb);
|
||||
Flush();
|
||||
}
|
||||
#elif defined(_WIN32)
|
||||
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (hOut == INVALID_HANDLE_VALUE)
|
||||
return;
|
||||
CONSOLE_SCREEN_BUFFER_INFO bi;
|
||||
|
||||
GetConsoleScreenBufferInfo(hOut, &bi);
|
||||
|
||||
switch (cb) {
|
||||
case ClearBehaviour::CB_CURSORANDABOVE: {
|
||||
COORD home = {0, 0};
|
||||
|
||||
DWORD cells =
|
||||
((bi.dwCursorPosition.Y) * bi.dwSize.X) + bi.dwCursorPosition.X + 1;
|
||||
DWORD written;
|
||||
|
||||
FillConsoleOutputCharacter(hOut, ' ', cells, home, &written);
|
||||
FillConsoleOutputAttribute(hOut, bi.wAttributes, cells, home, &written);
|
||||
|
||||
} break;
|
||||
case ClearBehaviour::CB_CURSORANDBELOW: {
|
||||
if (bi.dwCursorPosition.X == 0) {
|
||||
COORD home = bi.dwCursorPosition;
|
||||
DWORD cells = bi.dwSize.X * (bi.dwSize.Y - bi.dwCursorPosition.Y);
|
||||
DWORD written;
|
||||
|
||||
FillConsoleOutputCharacter(hOut, ' ', cells, home, &written);
|
||||
FillConsoleOutputAttribute(hOut, bi.wAttributes, cells, home,
|
||||
&written);
|
||||
} else {
|
||||
COORD home = bi.dwCursorPosition;
|
||||
DWORD cells = bi.dwSize.X - home.X;
|
||||
DWORD written;
|
||||
|
||||
FillConsoleOutputCharacter(hOut, ' ', cells, home, &written);
|
||||
FillConsoleOutputAttribute(hOut, bi.wAttributes, cells, home,
|
||||
&written);
|
||||
|
||||
if (home.Y + 1 < bi.dwSize.Y) {
|
||||
home = {0, (SHORT)(bi.dwCursorPosition.Y + 1)};
|
||||
cells = bi.dwSize.X * (bi.dwSize.Y - home.Y);
|
||||
|
||||
FillConsoleOutputCharacter(hOut, ' ', cells, home, &written);
|
||||
FillConsoleOutputAttribute(hOut, bi.wAttributes, cells, home,
|
||||
&written);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case ClearBehaviour::CB_ENTIRESCREEN: {
|
||||
COORD home = {0, 0};
|
||||
|
||||
DWORD cells = bi.dwSize.X * bi.dwSize.Y;
|
||||
DWORD written;
|
||||
|
||||
FillConsoleOutputCharacter(hOut, ' ', cells, home, &written);
|
||||
FillConsoleOutputAttribute(hOut, bi.wAttributes, cells, home, &written);
|
||||
} break;
|
||||
}
|
||||
|
||||
#else
|
||||
printf("\x1b[%iJ", (int)cb);
|
||||
Flush();
|
||||
#endif
|
||||
}
|
||||
void Console::MoveToHome() {
|
||||
#if __has_include(<termios.h>)
|
||||
if (isatty(STDOUT_FILENO)) {
|
||||
Write("\x1b[H");
|
||||
Flush();
|
||||
}
|
||||
#elif defined(_WIN32)
|
||||
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (hOut == INVALID_HANDLE_VALUE)
|
||||
return;
|
||||
SetConsoleCursorPosition(hOut, {0, 0});
|
||||
#else
|
||||
Write("\x1b[H");
|
||||
Flush();
|
||||
#endif
|
||||
}
|
||||
} // namespace Tesses::Framework
|
||||
@@ -1,3 +1,21 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/Crypto/ClientTLSStream.hpp"
|
||||
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
@@ -8,241 +26,220 @@
|
||||
using StreamReader = Tesses::Framework::TextStreams::StreamReader;
|
||||
#endif
|
||||
|
||||
#include <mbedtls/entropy.h>
|
||||
#include <mbedtls/ctr_drbg.h>
|
||||
#include <mbedtls/x509.h>
|
||||
#include <mbedtls/ssl.h>
|
||||
#include <mbedtls/net_sockets.h>
|
||||
#include <mbedtls/entropy.h>
|
||||
#include <mbedtls/error.h>
|
||||
#include <mbedtls/net_sockets.h>
|
||||
#include <mbedtls/ssl.h>
|
||||
#include <mbedtls/x509.h>
|
||||
#endif
|
||||
#include <cstring>
|
||||
using namespace Tesses::Framework::Streams;
|
||||
|
||||
namespace Tesses::Framework::Crypto {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
class ClientTLSPrivateData {
|
||||
public:
|
||||
bool eos;
|
||||
bool success;
|
||||
std::shared_ptr<Stream> strm;
|
||||
mbedtls_entropy_context entropy;
|
||||
mbedtls_ctr_drbg_context ctr_drbg;
|
||||
mbedtls_ssl_context ssl;
|
||||
mbedtls_ssl_config conf;
|
||||
mbedtls_x509_crt cachain;
|
||||
~ClientTLSPrivateData() {
|
||||
mbedtls_x509_crt_free(&cachain);
|
||||
mbedtls_ctr_drbg_free(&ctr_drbg);
|
||||
mbedtls_entropy_free(&entropy);
|
||||
mbedtls_ssl_config_free(&conf);
|
||||
mbedtls_ssl_free(&ssl);
|
||||
}
|
||||
};
|
||||
static int strm_send(void *ctx, const unsigned char *buf, size_t len) {
|
||||
auto priv = static_cast<ClientTLSPrivateData *>(ctx);
|
||||
return (int)priv->strm->Write(buf, len);
|
||||
}
|
||||
static int strm_recv(void *ctx, unsigned char *buf, size_t len) {
|
||||
auto priv = static_cast<ClientTLSPrivateData *>(ctx);
|
||||
return (int)priv->strm->Read(buf, len);
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace Tesses::Framework::Crypto
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
class ClientTLSPrivateData {
|
||||
public:
|
||||
bool eos;
|
||||
bool success;
|
||||
std::shared_ptr<Stream> strm;
|
||||
mbedtls_entropy_context entropy;
|
||||
mbedtls_ctr_drbg_context ctr_drbg;
|
||||
mbedtls_ssl_context ssl;
|
||||
mbedtls_ssl_config conf;
|
||||
mbedtls_x509_crt cachain;
|
||||
~ClientTLSPrivateData()
|
||||
{
|
||||
mbedtls_x509_crt_free(&cachain);
|
||||
mbedtls_ctr_drbg_free(&ctr_drbg);
|
||||
mbedtls_entropy_free(&entropy);
|
||||
mbedtls_ssl_config_free(&conf);
|
||||
mbedtls_ssl_free(&ssl);
|
||||
}
|
||||
};
|
||||
static int strm_send(void* ctx,const unsigned char* buf,size_t len)
|
||||
{
|
||||
auto priv = static_cast<ClientTLSPrivateData*>(ctx);
|
||||
return (int)priv->strm->Write(buf, len);
|
||||
|
||||
}
|
||||
static int strm_recv(void* ctx,unsigned char* buf,size_t len)
|
||||
{
|
||||
auto priv = static_cast<ClientTLSPrivateData*>(ctx);
|
||||
return (int)priv->strm->Read(buf, len);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string ClientTLSStream::GetCertChain()
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
#if defined(TESSESFRAMEWORK_EMBED_CERT_BUNDLE)
|
||||
return std::string((const char*)CERTIFICATECHAIN,CERTIFICATECHAIN_SIZE);
|
||||
#else
|
||||
#if defined(TESSESFRAMEWORK_CERT_BUNDLE_FILE)
|
||||
StreamReader sr(TESSESFRAMEWORK_CERT_BUNDLE_FILE);
|
||||
return sr.ReadToEnd();
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
return "";
|
||||
std::string ClientTLSStream::GetCertChain() {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
#if defined(TESSESFRAMEWORK_EMBED_CERT_BUNDLE)
|
||||
return std::string((const char *)CERTIFICATECHAIN, CERTIFICATECHAIN_SIZE);
|
||||
#else
|
||||
#if defined(TESSESFRAMEWORK_CERT_BUNDLE_FILE)
|
||||
StreamReader sr(TESSESFRAMEWORK_CERT_BUNDLE_FILE);
|
||||
return sr.ReadToEnd();
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
return "";
|
||||
}
|
||||
|
||||
ClientTLSStream::ClientTLSStream(
|
||||
std::shared_ptr<Tesses::Framework::Streams::Stream> innerStream,
|
||||
bool verify, std::string domain)
|
||||
: ClientTLSStream(innerStream, verify, domain, "") {}
|
||||
|
||||
ClientTLSStream::ClientTLSStream(
|
||||
std::shared_ptr<Tesses::Framework::Streams::Stream> innerStream,
|
||||
bool verify, std::string domain, std::string cert) {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
if (cert.empty()) {
|
||||
cert = GetCertChain();
|
||||
}
|
||||
|
||||
ClientTLSStream::ClientTLSStream(std::shared_ptr<Tesses::Framework::Streams::Stream> innerStream, bool verify, std::string domain) : ClientTLSStream(innerStream,verify,domain,"")
|
||||
{
|
||||
|
||||
ClientTLSPrivateData *data = new ClientTLSPrivateData();
|
||||
this->privateData = static_cast<void *>(data);
|
||||
data->eos = false;
|
||||
data->success = false;
|
||||
data->strm = innerStream;
|
||||
|
||||
mbedtls_ssl_init(&data->ssl);
|
||||
mbedtls_ssl_config_init(&data->conf);
|
||||
mbedtls_x509_crt_init(&data->cachain);
|
||||
mbedtls_ctr_drbg_init(&data->ctr_drbg);
|
||||
mbedtls_entropy_init(&data->entropy);
|
||||
|
||||
const char *pers = "TessesFramework";
|
||||
|
||||
int ret = 0;
|
||||
|
||||
if ((ret = mbedtls_ctr_drbg_seed(
|
||||
&data->ctr_drbg, mbedtls_entropy_func, &data->entropy,
|
||||
(const unsigned char *)pers, strlen(pers))) != 0) {
|
||||
printf("FAILED mbedtls_ctr_drbg_seed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ClientTLSStream::ClientTLSStream(std::shared_ptr<Tesses::Framework::Streams::Stream> innerStream, bool verify, std::string domain, std::string cert)
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
if(cert.empty())
|
||||
{
|
||||
cert = GetCertChain();
|
||||
}
|
||||
if (ret != 0) {
|
||||
printf("FAILED mbedtls_x509_crt_parse cert %i\n", ret);
|
||||
return;
|
||||
}
|
||||
ret = mbedtls_x509_crt_parse(
|
||||
&data->cachain, (const unsigned char *)cert.c_str(), cert.size() + 1);
|
||||
|
||||
ClientTLSPrivateData* data = new ClientTLSPrivateData();
|
||||
this->privateData = static_cast<void*>(data);
|
||||
data->eos=false;
|
||||
data->success=false;
|
||||
data->strm = innerStream;
|
||||
|
||||
if (ret != 0) {
|
||||
printf("FAILED mbedtls_x509_crt_parse chain %i\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
mbedtls_ssl_init(&data->ssl);
|
||||
mbedtls_ssl_config_init(&data->conf);
|
||||
mbedtls_x509_crt_init(&data->cachain);
|
||||
mbedtls_ctr_drbg_init(&data->ctr_drbg);
|
||||
mbedtls_entropy_init(&data->entropy);
|
||||
|
||||
const char* pers = "TessesFramework";
|
||||
|
||||
int ret=0;
|
||||
|
||||
|
||||
|
||||
if ((ret = mbedtls_ctr_drbg_seed(&data->ctr_drbg, mbedtls_entropy_func, &data->entropy,
|
||||
(const unsigned char *) pers,
|
||||
strlen(pers))) != 0)
|
||||
{
|
||||
printf("FAILED mbedtls_ctr_drbg_seed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(ret != 0) { printf("FAILED mbedtls_x509_crt_parse cert %i\n",ret); return;}
|
||||
ret = mbedtls_x509_crt_parse(&data->cachain, (const unsigned char *) cert.c_str(),
|
||||
cert.size()+1);
|
||||
|
||||
if(ret != 0) {printf("FAILED mbedtls_x509_crt_parse chain %i\n",ret); return;}
|
||||
|
||||
|
||||
|
||||
if((ret = mbedtls_ssl_config_defaults(&data->conf,
|
||||
MBEDTLS_SSL_IS_CLIENT,
|
||||
if ((ret = mbedtls_ssl_config_defaults(&data->conf, MBEDTLS_SSL_IS_CLIENT,
|
||||
MBEDTLS_SSL_TRANSPORT_STREAM,
|
||||
MBEDTLS_SSL_PRESET_DEFAULT)) != 0)
|
||||
{
|
||||
char buffer[100];
|
||||
mbedtls_strerror(ret,buffer,sizeof(buffer));
|
||||
printf("FAILED mbedtls_ssl_conf_defaults %s\n",buffer);
|
||||
return;
|
||||
}
|
||||
MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
|
||||
char buffer[100];
|
||||
mbedtls_strerror(ret, buffer, sizeof(buffer));
|
||||
printf("FAILED mbedtls_ssl_conf_defaults %s\n", buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
mbedtls_ssl_conf_rng(&data->conf, mbedtls_ctr_drbg_random, &data->ctr_drbg);
|
||||
mbedtls_ssl_conf_rng(&data->conf, mbedtls_ctr_drbg_random, &data->ctr_drbg);
|
||||
|
||||
/* #if defined(MBEDTLS_SSL_CACHE_C)
|
||||
mbedtls_ssl_conf_session_cache(&conf, &cache,
|
||||
mbedtls_ssl_cache_get,
|
||||
mbedtls_ssl_cache_set);
|
||||
/* #if defined(MBEDTLS_SSL_CACHE_C)
|
||||
mbedtls_ssl_conf_session_cache(&conf, &cache,
|
||||
mbedtls_ssl_cache_get,
|
||||
mbedtls_ssl_cache_set);
|
||||
#endif*/
|
||||
mbedtls_ssl_conf_authmode(&data->conf, verify ? MBEDTLS_SSL_VERIFY_REQUIRED: MBEDTLS_SSL_VERIFY_NONE);
|
||||
mbedtls_ssl_conf_ca_chain(&data->conf, &data->cachain, NULL);
|
||||
mbedtls_ssl_conf_authmode(&data->conf, verify ? MBEDTLS_SSL_VERIFY_REQUIRED
|
||||
: MBEDTLS_SSL_VERIFY_NONE);
|
||||
mbedtls_ssl_conf_ca_chain(&data->conf, &data->cachain, NULL);
|
||||
|
||||
|
||||
mbedtls_ssl_set_bio(&data->ssl, static_cast<void*>(data),strm_send,strm_recv, NULL);
|
||||
if((ret=mbedtls_ssl_setup(&data->ssl,&data->conf) != 0))
|
||||
{
|
||||
printf("FAILED mbedtls_ssl_setup %i\n",ret);
|
||||
return;
|
||||
}
|
||||
if((ret=mbedtls_ssl_set_hostname(&data->ssl,domain.c_str()) != 0))
|
||||
{
|
||||
printf("FAILED mbedtls_ssl_set_hostname %i\n",ret);
|
||||
return;
|
||||
}
|
||||
if((ret = mbedtls_ssl_handshake(&data->ssl)) != 0)
|
||||
{
|
||||
char buffer[100];
|
||||
mbedtls_strerror(ret,buffer,sizeof(buffer));
|
||||
printf("FAILED mbedtls_ssl_handshake %s\n",buffer);
|
||||
return;
|
||||
}
|
||||
uint32_t flags;
|
||||
if ((flags = mbedtls_ssl_get_verify_result(&data->ssl)) != 0) {
|
||||
mbedtls_ssl_set_bio(&data->ssl, static_cast<void *>(data), strm_send,
|
||||
strm_recv, NULL);
|
||||
if ((ret = mbedtls_ssl_setup(&data->ssl, &data->conf) != 0)) {
|
||||
printf("FAILED mbedtls_ssl_setup %i\n", ret);
|
||||
return;
|
||||
}
|
||||
if ((ret = mbedtls_ssl_set_hostname(&data->ssl, domain.c_str()) != 0)) {
|
||||
printf("FAILED mbedtls_ssl_set_hostname %i\n", ret);
|
||||
return;
|
||||
}
|
||||
if ((ret = mbedtls_ssl_handshake(&data->ssl)) != 0) {
|
||||
char buffer[100];
|
||||
mbedtls_strerror(ret, buffer, sizeof(buffer));
|
||||
printf("FAILED mbedtls_ssl_handshake %s\n", buffer);
|
||||
return;
|
||||
}
|
||||
uint32_t flags;
|
||||
if ((flags = mbedtls_ssl_get_verify_result(&data->ssl)) != 0) {
|
||||
#if !defined(MBEDTLS_X509_REMOVE_INFO)
|
||||
char vrfy_buf[512];
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if !defined(MBEDTLS_X509_REMOVE_INFO)
|
||||
mbedtls_x509_crt_verify_info(vrfy_buf, sizeof(vrfy_buf), " ! ", flags);
|
||||
|
||||
|
||||
#endif
|
||||
if(verify)
|
||||
return;
|
||||
if (verify)
|
||||
return;
|
||||
}
|
||||
|
||||
data->success=true;
|
||||
data->success = true;
|
||||
|
||||
#endif
|
||||
}
|
||||
size_t ClientTLSStream::Read(uint8_t* buffer, size_t len)
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
auto priv = static_cast<ClientTLSPrivateData*>(this->privateData);
|
||||
if(!priv->success) return 0;
|
||||
if(priv->eos) return 0;
|
||||
int r = mbedtls_ssl_read(&priv->ssl,buffer,len);
|
||||
|
||||
#endif
|
||||
}
|
||||
size_t ClientTLSStream::Read(uint8_t *buffer, size_t len) {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
auto priv = static_cast<ClientTLSPrivateData *>(this->privateData);
|
||||
if (!priv->success)
|
||||
return 0;
|
||||
if (priv->eos)
|
||||
return 0;
|
||||
int r = mbedtls_ssl_read(&priv->ssl, buffer, len);
|
||||
|
||||
if (r == -30848) {
|
||||
priv->eos = true;
|
||||
return 0;
|
||||
}
|
||||
return (size_t)r;
|
||||
#else
|
||||
return (size_t)0;
|
||||
#endif
|
||||
}
|
||||
size_t ClientTLSStream::Write(const uint8_t *buffer, size_t len) {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
auto priv = static_cast<ClientTLSPrivateData *>(this->privateData);
|
||||
if (!priv->success)
|
||||
return 0;
|
||||
int r = mbedtls_ssl_write(&priv->ssl, buffer, len);
|
||||
return (size_t)r;
|
||||
#else
|
||||
return (size_t)0;
|
||||
#endif
|
||||
}
|
||||
|
||||
if(r == -30848)
|
||||
{
|
||||
priv->eos = true;
|
||||
return 0;
|
||||
}
|
||||
return (size_t)r;
|
||||
#else
|
||||
return (size_t)0;
|
||||
#endif
|
||||
}
|
||||
size_t ClientTLSStream::Write(const uint8_t* buffer, size_t len)
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
auto priv = static_cast<ClientTLSPrivateData*>(this->privateData);
|
||||
if(!priv->success) return 0;
|
||||
int r = mbedtls_ssl_write(&priv->ssl,buffer,len);
|
||||
return (size_t)r;
|
||||
#else
|
||||
return (size_t)0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ClientTLSStream::CanRead()
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
return !(!static_cast<ClientTLSPrivateData*>(this->privateData)->success || static_cast<ClientTLSPrivateData*>(this->privateData)->eos);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
bool ClientTLSStream::CanWrite()
|
||||
{
|
||||
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
return !(!static_cast<ClientTLSPrivateData*>(this->privateData)->success || static_cast<ClientTLSPrivateData*>(this->privateData)->eos);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
bool ClientTLSStream::EndOfStream()
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
return !static_cast<ClientTLSPrivateData*>(this->privateData)->success || static_cast<ClientTLSPrivateData*>(this->privateData)->eos;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
ClientTLSStream::~ClientTLSStream()
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
delete static_cast<ClientTLSPrivateData*>(this->privateData);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
bool ClientTLSStream::CanRead() {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
return !(!static_cast<ClientTLSPrivateData *>(this->privateData)->success ||
|
||||
static_cast<ClientTLSPrivateData *>(this->privateData)->eos);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
bool ClientTLSStream::CanWrite() {
|
||||
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
return !(!static_cast<ClientTLSPrivateData *>(this->privateData)->success ||
|
||||
static_cast<ClientTLSPrivateData *>(this->privateData)->eos);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
bool ClientTLSStream::EndOfStream() {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
return !static_cast<ClientTLSPrivateData *>(this->privateData)->success ||
|
||||
static_cast<ClientTLSPrivateData *>(this->privateData)->eos;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
ClientTLSStream::~ClientTLSStream() {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
delete static_cast<ClientTLSPrivateData *>(this->privateData);
|
||||
#endif
|
||||
}
|
||||
} // namespace Tesses::Framework::Crypto
|
||||
@@ -1,375 +1,377 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/Crypto/Crypto.hpp"
|
||||
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
#include <mbedtls/base64.h>
|
||||
#include <mbedtls/sha1.h>
|
||||
#include <mbedtls/sha256.h>
|
||||
#include <mbedtls/sha512.h>
|
||||
#include <mbedtls/base64.h>
|
||||
|
||||
#include <mbedtls/pkcs5.h>
|
||||
#include <mbedtls/entropy.h>
|
||||
#include <mbedtls/ctr_drbg.h>
|
||||
#include <mbedtls/entropy.h>
|
||||
#include <mbedtls/pkcs5.h>
|
||||
#endif
|
||||
#include <iostream>
|
||||
namespace Tesses::Framework::Crypto
|
||||
{
|
||||
bool HaveCrypto()
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
std::string Base64_Encode(std::vector<uint8_t> data)
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
std::string str={};
|
||||
size_t olen=0;
|
||||
mbedtls_base64_encode((uint8_t*)str.data(), 0, &olen, data.data(),data.size());
|
||||
str.resize(olen-1);
|
||||
|
||||
|
||||
if(mbedtls_base64_encode((uint8_t*)str.data(), olen, &olen, data.data(),data.size())==0)
|
||||
{
|
||||
return str;
|
||||
}
|
||||
|
||||
#endif
|
||||
return "";
|
||||
}
|
||||
std::vector<uint8_t> Base64_Decode(std::string str)
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
size_t olen=0;
|
||||
|
||||
std::vector<uint8_t> data;
|
||||
|
||||
mbedtls_base64_decode(data.data(), 0, &olen, (const uint8_t*)str.data(),str.size());
|
||||
|
||||
data.resize(olen);
|
||||
|
||||
|
||||
if(mbedtls_base64_decode(data.data(), olen, &olen, (const uint8_t*)str.data(),str.size())==0)
|
||||
{
|
||||
return data;
|
||||
}
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
Sha1::Sha1()
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
mbedtls_sha1_context* ctx = new mbedtls_sha1_context();
|
||||
this->inner = ctx;
|
||||
mbedtls_sha1_init(ctx);
|
||||
|
||||
#endif
|
||||
}
|
||||
bool Sha1::Start()
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
auto ctx = static_cast<mbedtls_sha1_context*>(this->inner);
|
||||
mbedtls_sha1_starts(ctx);
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
bool Sha1::Update(const uint8_t* buffer, size_t sz)
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
auto ctx = static_cast<mbedtls_sha1_context*>(this->inner);
|
||||
mbedtls_sha1_update(ctx,buffer,sz);
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
bool Sha1::Update(std::shared_ptr<Tesses::Framework::Streams::Stream> strm)
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
if(strm == nullptr) return false;
|
||||
uint8_t buffer[1024];
|
||||
size_t read;
|
||||
do {
|
||||
read = strm->Read(buffer,sizeof(buffer));
|
||||
if(!Update(buffer,read)) return false;
|
||||
} while(read != 0);
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Sha1::Finish()
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
auto ctx = static_cast<mbedtls_sha1_context*>(this->inner);
|
||||
std::vector<uint8_t> hash;
|
||||
hash.resize(20);
|
||||
mbedtls_sha1_finish(ctx,hash.data());
|
||||
return hash;
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
Sha1::~Sha1()
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
auto ctx = static_cast<mbedtls_sha1_context*>(this->inner);
|
||||
mbedtls_sha1_free(ctx);
|
||||
delete ctx;
|
||||
#endif
|
||||
}
|
||||
std::vector<uint8_t> Sha1::ComputeHash(const uint8_t* buffer, size_t len)
|
||||
{
|
||||
Sha1 sha1;
|
||||
if(!sha1.Start()) return {};
|
||||
if(!sha1.Update(buffer,len)) return {};
|
||||
return sha1.Finish();
|
||||
}
|
||||
std::vector<uint8_t> Sha1::ComputeHash(std::shared_ptr<Tesses::Framework::Streams::Stream> strm)
|
||||
{
|
||||
Sha1 sha1;
|
||||
if(!sha1.Start()) return {};
|
||||
if(!sha1.Update(strm)) return {};
|
||||
return sha1.Finish();
|
||||
}
|
||||
|
||||
|
||||
Sha256::Sha256()
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
mbedtls_sha256_context* ctx = new mbedtls_sha256_context();
|
||||
this->inner = ctx;
|
||||
mbedtls_sha256_init(ctx);
|
||||
|
||||
#endif
|
||||
}
|
||||
bool Sha256::Start(bool is224)
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
auto ctx = static_cast<mbedtls_sha256_context*>(this->inner);
|
||||
this->is224=is224;
|
||||
mbedtls_sha256_starts(ctx,is224);
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
bool Sha256::Is224()
|
||||
{
|
||||
return this->is224;
|
||||
}
|
||||
bool Sha256::Update(const uint8_t* buffer, size_t sz)
|
||||
{
|
||||
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
auto ctx = static_cast<mbedtls_sha256_context*>(this->inner);
|
||||
mbedtls_sha256_update(ctx,buffer,sz);
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
bool Sha256::Update(std::shared_ptr<Tesses::Framework::Streams::Stream> strm)
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
if(strm == nullptr) return false;
|
||||
uint8_t buffer[1024];
|
||||
size_t read;
|
||||
do {
|
||||
read = strm->Read(buffer,sizeof(buffer));
|
||||
if(!Update(buffer,read)) return false;
|
||||
} while(read != 0);
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Sha256::Finish()
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
auto ctx = static_cast<mbedtls_sha256_context*>(this->inner);
|
||||
std::vector<uint8_t> hash;
|
||||
hash.resize(32);
|
||||
mbedtls_sha256_finish(ctx,hash.data());
|
||||
return hash;
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
Sha256::~Sha256()
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
auto ctx = static_cast<mbedtls_sha256_context*>(this->inner);
|
||||
mbedtls_sha256_free(ctx);
|
||||
delete ctx;
|
||||
#endif
|
||||
}
|
||||
std::vector<uint8_t> Sha256::ComputeHash(const uint8_t* buffer, size_t len,bool is224)
|
||||
{
|
||||
Sha256 sha256;
|
||||
if(!sha256.Start(is224)) return {};
|
||||
if(!sha256.Update(buffer,len)) return {};
|
||||
return sha256.Finish();
|
||||
}
|
||||
std::vector<uint8_t> Sha256::ComputeHash(std::shared_ptr<Tesses::Framework::Streams::Stream> strm,bool is224)
|
||||
{
|
||||
Sha256 sha256;
|
||||
if(!sha256.Start(is224)) return {};
|
||||
if(!sha256.Update(strm)) return {};
|
||||
return sha256.Finish();
|
||||
}
|
||||
|
||||
|
||||
Sha512::Sha512()
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
mbedtls_sha512_context* ctx = new mbedtls_sha512_context();
|
||||
this->inner = ctx;
|
||||
mbedtls_sha512_init(ctx);
|
||||
#endif
|
||||
}
|
||||
bool Sha512::Start(bool is384)
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
auto ctx = static_cast<mbedtls_sha512_context*>(this->inner);
|
||||
this->is384=is384;
|
||||
mbedtls_sha512_starts(ctx,is384);
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
bool Sha512::Is384()
|
||||
{
|
||||
return this->is384;
|
||||
}
|
||||
bool Sha512::Update(const uint8_t* buffer, size_t sz)
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
auto ctx = static_cast<mbedtls_sha512_context*>(this->inner);
|
||||
mbedtls_sha512_update(ctx,buffer,sz);
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
bool Sha512::Update(std::shared_ptr<Tesses::Framework::Streams::Stream> strm)
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
if(strm == nullptr) return false;
|
||||
uint8_t buffer[1024];
|
||||
size_t read;
|
||||
do {
|
||||
read = strm->Read(buffer,sizeof(buffer));
|
||||
if(!Update(buffer,read)) return false;
|
||||
} while(read != 0);
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Sha512::Finish()
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
auto ctx = static_cast<mbedtls_sha512_context*>(this->inner);
|
||||
std::vector<uint8_t> hash;
|
||||
hash.resize(64);
|
||||
mbedtls_sha512_finish(ctx,hash.data());
|
||||
return hash;
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
Sha512::~Sha512()
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
auto ctx = static_cast<mbedtls_sha512_context*>(this->inner);
|
||||
mbedtls_sha512_free(ctx);
|
||||
delete ctx;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Sha512::ComputeHash(const uint8_t* buffer, size_t len,bool is384)
|
||||
{
|
||||
Sha512 sha512;
|
||||
if(!sha512.Start(is384)) return {};
|
||||
if(!sha512.Update(buffer,len)) return {};
|
||||
return sha512.Finish();
|
||||
}
|
||||
std::vector<uint8_t> Sha512::ComputeHash(std::shared_ptr<Tesses::Framework::Streams::Stream> strm,bool is384)
|
||||
{
|
||||
Sha512 sha512;
|
||||
if(!sha512.Start(is384)) return {};
|
||||
if(!sha512.Update(strm)) return {};
|
||||
return sha512.Finish();
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool PBKDF2(std::vector<uint8_t>& output,std::string pass, std::vector<uint8_t>& salt, long itterations, ShaVersion version)
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
|
||||
mbedtls_md_context_t ctx;
|
||||
mbedtls_md_init(&ctx);
|
||||
const mbedtls_md_info_t* info = NULL;
|
||||
switch(version)
|
||||
{
|
||||
case ShaVersion::VERSION_SHA1:
|
||||
info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
|
||||
break;
|
||||
case ShaVersion::VERSION_SHA224:
|
||||
info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA224);
|
||||
break;
|
||||
case ShaVersion::VERSION_SHA256:
|
||||
info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
|
||||
break;
|
||||
default:
|
||||
case ShaVersion::VERSION_SHA384:
|
||||
info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA384);
|
||||
break;
|
||||
case ShaVersion::VERSION_SHA512:
|
||||
info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
mbedtls_md_setup(&ctx, info, 1);
|
||||
|
||||
|
||||
|
||||
if(mbedtls_pkcs5_pbkdf2_hmac(&ctx, (const unsigned char*)pass.c_str(), pass.size(), salt.data(), salt.size(), (uint32_t)itterations,(uint32_t)output.size(),output.data()) == 0)
|
||||
{
|
||||
mbedtls_md_free(&ctx);
|
||||
return true;
|
||||
}
|
||||
mbedtls_md_free(&ctx);
|
||||
return false;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
bool RandomBytes(std::vector<uint8_t>& output, std::string personal_str)
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
mbedtls_entropy_context entropy={0};
|
||||
mbedtls_ctr_drbg_context ctr_drbg={0};
|
||||
|
||||
mbedtls_entropy_init(&entropy);
|
||||
mbedtls_ctr_drbg_init(&ctr_drbg);
|
||||
|
||||
int ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char *) personal_str.c_str(), personal_str.size());
|
||||
if(ret != 0)
|
||||
{
|
||||
mbedtls_ctr_drbg_free(&ctr_drbg);
|
||||
mbedtls_entropy_free(&entropy);
|
||||
return false;
|
||||
}
|
||||
ret = mbedtls_ctr_drbg_random(&ctr_drbg, output.data(),output.size());
|
||||
if (ret != 0)
|
||||
{
|
||||
mbedtls_ctr_drbg_free(&ctr_drbg);
|
||||
mbedtls_entropy_free(&entropy);
|
||||
return false;
|
||||
}
|
||||
mbedtls_ctr_drbg_free(&ctr_drbg);
|
||||
mbedtls_entropy_free(&entropy);
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
namespace Tesses::Framework::Crypto {
|
||||
bool HaveCrypto() {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
std::string Base64_Encode(std::vector<uint8_t> data) {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
std::string str = {};
|
||||
size_t olen = 0;
|
||||
mbedtls_base64_encode((uint8_t *)str.data(), 0, &olen, data.data(),
|
||||
data.size());
|
||||
str.resize(olen - 1);
|
||||
|
||||
if (mbedtls_base64_encode((uint8_t *)str.data(), olen, &olen, data.data(),
|
||||
data.size()) == 0) {
|
||||
return str;
|
||||
}
|
||||
|
||||
#endif
|
||||
return "";
|
||||
}
|
||||
std::vector<uint8_t> Base64_Decode(std::string str) {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
size_t olen = 0;
|
||||
|
||||
std::vector<uint8_t> data;
|
||||
|
||||
mbedtls_base64_decode(data.data(), 0, &olen, (const uint8_t *)str.data(),
|
||||
str.size());
|
||||
|
||||
data.resize(olen);
|
||||
|
||||
if (mbedtls_base64_decode(data.data(), olen, &olen,
|
||||
(const uint8_t *)str.data(), str.size()) == 0) {
|
||||
return data;
|
||||
}
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
Sha1::Sha1() {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
mbedtls_sha1_context *ctx = new mbedtls_sha1_context();
|
||||
this->inner = ctx;
|
||||
mbedtls_sha1_init(ctx);
|
||||
|
||||
#endif
|
||||
}
|
||||
bool Sha1::Start() {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
auto ctx = static_cast<mbedtls_sha1_context *>(this->inner);
|
||||
mbedtls_sha1_starts(ctx);
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
bool Sha1::Update(const uint8_t *buffer, size_t sz) {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
auto ctx = static_cast<mbedtls_sha1_context *>(this->inner);
|
||||
mbedtls_sha1_update(ctx, buffer, sz);
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
bool Sha1::Update(std::shared_ptr<Tesses::Framework::Streams::Stream> strm) {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
if (strm == nullptr)
|
||||
return false;
|
||||
uint8_t buffer[1024];
|
||||
size_t read;
|
||||
do {
|
||||
read = strm->Read(buffer, sizeof(buffer));
|
||||
if (!Update(buffer, read))
|
||||
return false;
|
||||
} while (read != 0);
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Sha1::Finish() {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
auto ctx = static_cast<mbedtls_sha1_context *>(this->inner);
|
||||
std::vector<uint8_t> hash;
|
||||
hash.resize(20);
|
||||
mbedtls_sha1_finish(ctx, hash.data());
|
||||
return hash;
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
Sha1::~Sha1() {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
auto ctx = static_cast<mbedtls_sha1_context *>(this->inner);
|
||||
mbedtls_sha1_free(ctx);
|
||||
delete ctx;
|
||||
#endif
|
||||
}
|
||||
std::vector<uint8_t> Sha1::ComputeHash(const uint8_t *buffer, size_t len) {
|
||||
Sha1 sha1;
|
||||
if (!sha1.Start())
|
||||
return {};
|
||||
if (!sha1.Update(buffer, len))
|
||||
return {};
|
||||
return sha1.Finish();
|
||||
}
|
||||
std::vector<uint8_t>
|
||||
Sha1::ComputeHash(std::shared_ptr<Tesses::Framework::Streams::Stream> strm) {
|
||||
Sha1 sha1;
|
||||
if (!sha1.Start())
|
||||
return {};
|
||||
if (!sha1.Update(strm))
|
||||
return {};
|
||||
return sha1.Finish();
|
||||
}
|
||||
|
||||
Sha256::Sha256() {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
mbedtls_sha256_context *ctx = new mbedtls_sha256_context();
|
||||
this->inner = ctx;
|
||||
mbedtls_sha256_init(ctx);
|
||||
|
||||
#endif
|
||||
}
|
||||
bool Sha256::Start(bool is224) {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
auto ctx = static_cast<mbedtls_sha256_context *>(this->inner);
|
||||
this->is224 = is224;
|
||||
mbedtls_sha256_starts(ctx, is224);
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
bool Sha256::Is224() { return this->is224; }
|
||||
bool Sha256::Update(const uint8_t *buffer, size_t sz) {
|
||||
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
auto ctx = static_cast<mbedtls_sha256_context *>(this->inner);
|
||||
mbedtls_sha256_update(ctx, buffer, sz);
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
bool Sha256::Update(std::shared_ptr<Tesses::Framework::Streams::Stream> strm) {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
if (strm == nullptr)
|
||||
return false;
|
||||
uint8_t buffer[1024];
|
||||
size_t read;
|
||||
do {
|
||||
read = strm->Read(buffer, sizeof(buffer));
|
||||
if (!Update(buffer, read))
|
||||
return false;
|
||||
} while (read != 0);
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Sha256::Finish() {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
auto ctx = static_cast<mbedtls_sha256_context *>(this->inner);
|
||||
std::vector<uint8_t> hash;
|
||||
hash.resize(32);
|
||||
mbedtls_sha256_finish(ctx, hash.data());
|
||||
return hash;
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
Sha256::~Sha256() {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
auto ctx = static_cast<mbedtls_sha256_context *>(this->inner);
|
||||
mbedtls_sha256_free(ctx);
|
||||
delete ctx;
|
||||
#endif
|
||||
}
|
||||
std::vector<uint8_t> Sha256::ComputeHash(const uint8_t *buffer, size_t len,
|
||||
bool is224) {
|
||||
Sha256 sha256;
|
||||
if (!sha256.Start(is224))
|
||||
return {};
|
||||
if (!sha256.Update(buffer, len))
|
||||
return {};
|
||||
return sha256.Finish();
|
||||
}
|
||||
std::vector<uint8_t>
|
||||
Sha256::ComputeHash(std::shared_ptr<Tesses::Framework::Streams::Stream> strm,
|
||||
bool is224) {
|
||||
Sha256 sha256;
|
||||
if (!sha256.Start(is224))
|
||||
return {};
|
||||
if (!sha256.Update(strm))
|
||||
return {};
|
||||
return sha256.Finish();
|
||||
}
|
||||
|
||||
Sha512::Sha512() {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
mbedtls_sha512_context *ctx = new mbedtls_sha512_context();
|
||||
this->inner = ctx;
|
||||
mbedtls_sha512_init(ctx);
|
||||
#endif
|
||||
}
|
||||
bool Sha512::Start(bool is384) {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
auto ctx = static_cast<mbedtls_sha512_context *>(this->inner);
|
||||
this->is384 = is384;
|
||||
mbedtls_sha512_starts(ctx, is384);
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
bool Sha512::Is384() { return this->is384; }
|
||||
bool Sha512::Update(const uint8_t *buffer, size_t sz) {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
auto ctx = static_cast<mbedtls_sha512_context *>(this->inner);
|
||||
mbedtls_sha512_update(ctx, buffer, sz);
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
bool Sha512::Update(std::shared_ptr<Tesses::Framework::Streams::Stream> strm) {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
if (strm == nullptr)
|
||||
return false;
|
||||
uint8_t buffer[1024];
|
||||
size_t read;
|
||||
do {
|
||||
read = strm->Read(buffer, sizeof(buffer));
|
||||
if (!Update(buffer, read))
|
||||
return false;
|
||||
} while (read != 0);
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Sha512::Finish() {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
auto ctx = static_cast<mbedtls_sha512_context *>(this->inner);
|
||||
std::vector<uint8_t> hash;
|
||||
hash.resize(64);
|
||||
mbedtls_sha512_finish(ctx, hash.data());
|
||||
return hash;
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
Sha512::~Sha512() {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
auto ctx = static_cast<mbedtls_sha512_context *>(this->inner);
|
||||
mbedtls_sha512_free(ctx);
|
||||
delete ctx;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Sha512::ComputeHash(const uint8_t *buffer, size_t len,
|
||||
bool is384) {
|
||||
Sha512 sha512;
|
||||
if (!sha512.Start(is384))
|
||||
return {};
|
||||
if (!sha512.Update(buffer, len))
|
||||
return {};
|
||||
return sha512.Finish();
|
||||
}
|
||||
std::vector<uint8_t>
|
||||
Sha512::ComputeHash(std::shared_ptr<Tesses::Framework::Streams::Stream> strm,
|
||||
bool is384) {
|
||||
Sha512 sha512;
|
||||
if (!sha512.Start(is384))
|
||||
return {};
|
||||
if (!sha512.Update(strm))
|
||||
return {};
|
||||
return sha512.Finish();
|
||||
}
|
||||
|
||||
bool PBKDF2(std::vector<uint8_t> &output, std::string pass,
|
||||
std::vector<uint8_t> &salt, long itterations, ShaVersion version) {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
|
||||
mbedtls_md_context_t ctx;
|
||||
mbedtls_md_init(&ctx);
|
||||
const mbedtls_md_info_t *info = NULL;
|
||||
switch (version) {
|
||||
case ShaVersion::VERSION_SHA1:
|
||||
info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
|
||||
break;
|
||||
case ShaVersion::VERSION_SHA224:
|
||||
info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA224);
|
||||
break;
|
||||
case ShaVersion::VERSION_SHA256:
|
||||
info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
|
||||
break;
|
||||
default:
|
||||
case ShaVersion::VERSION_SHA384:
|
||||
info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA384);
|
||||
break;
|
||||
case ShaVersion::VERSION_SHA512:
|
||||
info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
|
||||
break;
|
||||
}
|
||||
|
||||
mbedtls_md_setup(&ctx, info, 1);
|
||||
|
||||
if (mbedtls_pkcs5_pbkdf2_hmac(
|
||||
&ctx, (const unsigned char *)pass.c_str(), pass.size(), salt.data(),
|
||||
salt.size(), (uint32_t)itterations, (uint32_t)output.size(),
|
||||
output.data()) == 0) {
|
||||
mbedtls_md_free(&ctx);
|
||||
return true;
|
||||
}
|
||||
mbedtls_md_free(&ctx);
|
||||
return false;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
bool RandomBytes(std::vector<uint8_t> &output, std::string personal_str) {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_MBED)
|
||||
mbedtls_entropy_context entropy = {0};
|
||||
mbedtls_ctr_drbg_context ctr_drbg = {0};
|
||||
|
||||
mbedtls_entropy_init(&entropy);
|
||||
mbedtls_ctr_drbg_init(&ctr_drbg);
|
||||
|
||||
int ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
|
||||
(const unsigned char *)personal_str.c_str(),
|
||||
personal_str.size());
|
||||
if (ret != 0) {
|
||||
mbedtls_ctr_drbg_free(&ctr_drbg);
|
||||
mbedtls_entropy_free(&entropy);
|
||||
return false;
|
||||
}
|
||||
ret = mbedtls_ctr_drbg_random(&ctr_drbg, output.data(), output.size());
|
||||
if (ret != 0) {
|
||||
mbedtls_ctr_drbg_free(&ctr_drbg);
|
||||
mbedtls_entropy_free(&entropy);
|
||||
return false;
|
||||
}
|
||||
mbedtls_ctr_drbg_free(&ctr_drbg);
|
||||
mbedtls_entropy_free(&entropy);
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
} // namespace Tesses::Framework::Crypto
|
||||
|
||||
1608
src/Date/Date.cpp
1608
src/Date/Date.cpp
File diff suppressed because it is too large
Load Diff
@@ -1,184 +1,197 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/Filesystem/FSHelpers.hpp"
|
||||
#include "TessesFramework/TextStreams/StreamReader.hpp"
|
||||
#include "TessesFramework/TextStreams/StreamWriter.hpp"
|
||||
|
||||
namespace Tesses::Framework::Filesystem::Helpers
|
||||
{
|
||||
void ReadAllText(std::shared_ptr<VFS> vfs, VFSPath path, std::string& text)
|
||||
{
|
||||
auto file = vfs->OpenFile(path,"rb");
|
||||
if(file->CanRead())
|
||||
{
|
||||
TextStreams::StreamReader reader(file);
|
||||
reader.ReadToEnd(text);
|
||||
}
|
||||
namespace Tesses::Framework::Filesystem::Helpers {
|
||||
void ReadAllText(std::shared_ptr<VFS> vfs, VFSPath path, std::string &text) {
|
||||
auto file = vfs->OpenFile(path, "rb");
|
||||
if (file->CanRead()) {
|
||||
TextStreams::StreamReader reader(file);
|
||||
reader.ReadToEnd(text);
|
||||
}
|
||||
void ReadAllLines(std::shared_ptr<VFS> vfs, VFSPath path, std::vector<std::string>& lines)
|
||||
{
|
||||
auto file = vfs->OpenFile(path,"rb");
|
||||
if(file->CanRead())
|
||||
{
|
||||
TextStreams::StreamReader reader(file);
|
||||
reader.ReadAllLines(lines);
|
||||
}
|
||||
}
|
||||
void ReadAllLines(std::shared_ptr<VFS> vfs, VFSPath path,
|
||||
std::vector<std::string> &lines) {
|
||||
auto file = vfs->OpenFile(path, "rb");
|
||||
if (file->CanRead()) {
|
||||
TextStreams::StreamReader reader(file);
|
||||
reader.ReadAllLines(lines);
|
||||
}
|
||||
void ReadAllBytes(std::shared_ptr<VFS> vfs, VFSPath path, std::vector<uint8_t>& array)
|
||||
{
|
||||
auto file = vfs->OpenFile(path,"rb");
|
||||
if(file->CanRead())
|
||||
{
|
||||
if(file->CanSeek())
|
||||
{
|
||||
size_t length = (size_t)file->GetLength();
|
||||
array.resize(length);
|
||||
file->ReadBlock(array.data(), array.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t totalSize = 0;
|
||||
size_t read = 0;
|
||||
do {
|
||||
array.resize(totalSize+1024);
|
||||
read = file->ReadBlock(array.data()+totalSize,1024);
|
||||
totalSize += read;
|
||||
} while(read != 0);
|
||||
}
|
||||
void ReadAllBytes(std::shared_ptr<VFS> vfs, VFSPath path,
|
||||
std::vector<uint8_t> &array) {
|
||||
auto file = vfs->OpenFile(path, "rb");
|
||||
if (file->CanRead()) {
|
||||
if (file->CanSeek()) {
|
||||
size_t length = (size_t)file->GetLength();
|
||||
array.resize(length);
|
||||
file->ReadBlock(array.data(), array.size());
|
||||
} else {
|
||||
size_t totalSize = 0;
|
||||
size_t read = 0;
|
||||
do {
|
||||
array.resize(totalSize + 1024);
|
||||
read = file->ReadBlock(array.data() + totalSize, 1024);
|
||||
totalSize += read;
|
||||
} while (read != 0);
|
||||
|
||||
array.resize(totalSize);
|
||||
}
|
||||
array.resize(totalSize);
|
||||
}
|
||||
}
|
||||
std::string ReadAllText(std::shared_ptr<VFS> vfs, VFSPath path)
|
||||
{
|
||||
std::string text;
|
||||
ReadAllText(vfs,path,text);
|
||||
return text;
|
||||
}
|
||||
std::string ReadAllText(std::shared_ptr<VFS> vfs, VFSPath path) {
|
||||
std::string text;
|
||||
ReadAllText(vfs, path, text);
|
||||
return text;
|
||||
}
|
||||
std::vector<std::string> ReadAllLines(std::shared_ptr<VFS> vfs, VFSPath path) {
|
||||
std::vector<std::string> lines;
|
||||
ReadAllLines(vfs, path, lines);
|
||||
return lines;
|
||||
}
|
||||
std::vector<uint8_t> ReadAllBytes(std::shared_ptr<VFS> vfs, VFSPath path) {
|
||||
std::vector<uint8_t> bytes;
|
||||
ReadAllBytes(vfs, path, bytes);
|
||||
return bytes;
|
||||
}
|
||||
void WriteAllText(std::shared_ptr<VFS> vfs, VFSPath path,
|
||||
const std::string &text) {
|
||||
auto file = vfs->OpenFile(path, "wb");
|
||||
if (file->CanWrite()) {
|
||||
TextStreams::StreamWriter writer(file);
|
||||
writer.Write(text);
|
||||
}
|
||||
std::vector<std::string> ReadAllLines(std::shared_ptr<VFS> vfs, VFSPath path)
|
||||
{
|
||||
std::vector<std::string> lines;
|
||||
ReadAllLines(vfs,path,lines);
|
||||
return lines;
|
||||
}
|
||||
std::vector<uint8_t> ReadAllBytes(std::shared_ptr<VFS> vfs, VFSPath path)
|
||||
{
|
||||
std::vector<uint8_t> bytes;
|
||||
ReadAllBytes(vfs,path,bytes);
|
||||
return bytes;
|
||||
}
|
||||
void WriteAllText(std::shared_ptr<VFS> vfs, VFSPath path, const std::string& text)
|
||||
{
|
||||
auto file = vfs->OpenFile(path,"wb");
|
||||
if(file->CanWrite())
|
||||
{
|
||||
TextStreams::StreamWriter writer(file);
|
||||
writer.Write(text);
|
||||
}
|
||||
void WriteAllLines(std::shared_ptr<VFS> vfs, VFSPath path,
|
||||
const std::vector<std::string> &parts) {
|
||||
auto file = vfs->OpenFile(path, "wb");
|
||||
if (file->CanWrite()) {
|
||||
TextStreams::StreamWriter writer(file);
|
||||
for (auto &line : parts) {
|
||||
writer.WriteLine(line);
|
||||
}
|
||||
}
|
||||
void WriteAllLines(std::shared_ptr<VFS> vfs, VFSPath path, const std::vector<std::string>& parts)
|
||||
{
|
||||
auto file = vfs->OpenFile(path,"wb");
|
||||
if(file->CanWrite())
|
||||
{
|
||||
TextStreams::StreamWriter writer(file);
|
||||
for(auto& line : parts)
|
||||
{
|
||||
writer.WriteLine(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
void WriteAllBytes(std::shared_ptr<VFS> vfs, VFSPath path, const std::vector<uint8_t>& bytes)
|
||||
{
|
||||
auto file = vfs->OpenFile(path,"wb");
|
||||
if(file->CanWrite())
|
||||
{
|
||||
file->WriteBlock(bytes.data(),bytes.size());
|
||||
}
|
||||
}
|
||||
void WriteAllBytes(std::shared_ptr<VFS> vfs, VFSPath path,
|
||||
const std::vector<uint8_t> &bytes) {
|
||||
auto file = vfs->OpenFile(path, "wb");
|
||||
if (file->CanWrite()) {
|
||||
file->WriteBlock(bytes.data(), bytes.size());
|
||||
}
|
||||
}
|
||||
|
||||
void CopyFile(std::shared_ptr<VFS> vfsSrc, VFSPath pathSrc, std::shared_ptr<VFS> vfsDest, VFSPath pathDest, bool overwrite)
|
||||
{
|
||||
if(!overwrite && vfsDest->FileExists(pathDest)) return;
|
||||
if(!vfsSrc->FileExists(pathSrc)) return;
|
||||
auto src=vfsSrc->OpenFile(pathSrc,"wb");
|
||||
auto dest = vfsDest->OpenFile(pathDest,"wb");
|
||||
if(src->CanRead() && dest->CanWrite())
|
||||
void CopyFile(std::shared_ptr<VFS> vfsSrc, VFSPath pathSrc,
|
||||
std::shared_ptr<VFS> vfsDest, VFSPath pathDest, bool overwrite) {
|
||||
if (!overwrite && vfsDest->FileExists(pathDest))
|
||||
return;
|
||||
if (!vfsSrc->FileExists(pathSrc))
|
||||
return;
|
||||
auto src = vfsSrc->OpenFile(pathSrc, "wb");
|
||||
auto dest = vfsDest->OpenFile(pathDest, "wb");
|
||||
if (src->CanRead() && dest->CanWrite())
|
||||
src->CopyTo(dest);
|
||||
}
|
||||
|
||||
void CopyStreamProgress(
|
||||
std::shared_ptr<Streams::Stream> src, std::shared_ptr<Streams::Stream> dest,
|
||||
std::function<void(int64_t offset, int64_t length)> progress) {
|
||||
int64_t length = 0;
|
||||
try {
|
||||
length = src->GetLength();
|
||||
} catch (...) {
|
||||
length = 0;
|
||||
}
|
||||
if (length == 0)
|
||||
length = (int64_t)1 << 62; // a big number so its always 0% if the
|
||||
// stream does not have a length
|
||||
|
||||
void CopyStreamProgress(std::shared_ptr<Streams::Stream> src,std::shared_ptr<Streams::Stream> dest, std::function<void(int64_t offset, int64_t length)> progress)
|
||||
{
|
||||
int64_t length=0;
|
||||
try {
|
||||
length = src->GetLength();
|
||||
} catch(...) {
|
||||
length=0;
|
||||
}
|
||||
if(length == 0) length = (int64_t)1<<62; // a big number so its always 0% if the stream does not have a length
|
||||
int64_t offset = 0;
|
||||
size_t read = 0;
|
||||
std::vector<uint8_t> data(4096);
|
||||
|
||||
int64_t offset = 0;
|
||||
size_t read = 0;
|
||||
std::vector<uint8_t> data(4096);
|
||||
|
||||
|
||||
do {
|
||||
read = src->ReadBlock(data.data(),data.size());
|
||||
dest->WriteBlock(data.data(),read);
|
||||
offset += (int64_t)read;
|
||||
if(read != 0)
|
||||
progress(offset,length);
|
||||
} while(read != 0);
|
||||
do {
|
||||
read = src->ReadBlock(data.data(), data.size());
|
||||
dest->WriteBlock(data.data(), read);
|
||||
offset += (int64_t)read;
|
||||
if (read != 0)
|
||||
progress(offset, length);
|
||||
} while (read != 0);
|
||||
|
||||
if(offset > 0)
|
||||
progress(offset,offset);
|
||||
}
|
||||
void CopyFile(std::shared_ptr<VFS> vfsSrc, VFSPath pathSrc, std::shared_ptr<VFS> vfsDest, VFSPath pathDest, std::function<void(int64_t offset, int64_t length)> progress, bool overwrite)
|
||||
{
|
||||
if(!overwrite && vfsDest->FileExists(pathDest)) return;
|
||||
if(!vfsSrc->FileExists(pathSrc)) return;
|
||||
auto src=vfsSrc->OpenFile(pathSrc,"wb");
|
||||
auto dest = vfsDest->OpenFile(pathDest,"wb");
|
||||
if(src->CanRead() && dest->CanWrite())
|
||||
CopyStreamProgress(src,dest,progress);
|
||||
}
|
||||
void CopyDirectory(std::shared_ptr<VFS> vfsSrc, VFSPath pathSrc, std::shared_ptr<VFS> vfsDest, VFSPath pathDest,bool overwrite)
|
||||
{
|
||||
if (offset > 0)
|
||||
progress(offset, offset);
|
||||
}
|
||||
void CopyFile(std::shared_ptr<VFS> vfsSrc, VFSPath pathSrc,
|
||||
std::shared_ptr<VFS> vfsDest, VFSPath pathDest,
|
||||
std::function<void(int64_t offset, int64_t length)> progress,
|
||||
bool overwrite) {
|
||||
if (!overwrite && vfsDest->FileExists(pathDest))
|
||||
return;
|
||||
if (!vfsSrc->FileExists(pathSrc))
|
||||
return;
|
||||
auto src = vfsSrc->OpenFile(pathSrc, "wb");
|
||||
auto dest = vfsDest->OpenFile(pathDest, "wb");
|
||||
if (src->CanRead() && dest->CanWrite())
|
||||
CopyStreamProgress(src, dest, progress);
|
||||
}
|
||||
void CopyDirectory(std::shared_ptr<VFS> vfsSrc, VFSPath pathSrc,
|
||||
std::shared_ptr<VFS> vfsDest, VFSPath pathDest,
|
||||
bool overwrite) {
|
||||
|
||||
|
||||
if(vfsSrc->DirectoryExists(pathSrc))
|
||||
{
|
||||
vfsDest->CreateDirectory(pathDest);
|
||||
for(auto& srcPath : vfsSrc->EnumeratePaths(pathSrc))
|
||||
{
|
||||
if(vfsSrc->DirectoryExists(srcPath))
|
||||
{
|
||||
CopyDirectory(vfsSrc,srcPath,vfsDest,pathDest / srcPath.GetFileName());
|
||||
}
|
||||
if(vfsSrc->FileExists(srcPath))
|
||||
{
|
||||
CopyFile(vfsSrc,srcPath,vfsDest,pathDest / srcPath.GetFileName(),overwrite);
|
||||
}
|
||||
if (vfsSrc->DirectoryExists(pathSrc)) {
|
||||
vfsDest->CreateDirectory(pathDest);
|
||||
for (auto &srcPath : vfsSrc->EnumeratePaths(pathSrc)) {
|
||||
if (vfsSrc->DirectoryExists(srcPath)) {
|
||||
CopyDirectory(vfsSrc, srcPath, vfsDest,
|
||||
pathDest / srcPath.GetFileName());
|
||||
}
|
||||
if (vfsSrc->FileExists(srcPath)) {
|
||||
CopyFile(vfsSrc, srcPath, vfsDest,
|
||||
pathDest / srcPath.GetFileName(), overwrite);
|
||||
}
|
||||
}
|
||||
}
|
||||
void CopyDirectory(std::shared_ptr<VFS> vfsSrc, VFSPath pathSrc, std::shared_ptr<VFS> vfsDest, VFSPath pathDest, std::function<void(int64_t offset, int64_t length, VFSPath currentFile)> progress, bool overwrite)
|
||||
{
|
||||
if(vfsSrc->DirectoryExists(pathSrc))
|
||||
{
|
||||
vfsDest->CreateDirectory(pathDest);
|
||||
for(auto& srcPath : vfsSrc->EnumeratePaths(pathSrc))
|
||||
{
|
||||
if(vfsSrc->DirectoryExists(srcPath))
|
||||
{
|
||||
CopyDirectory(vfsSrc,srcPath,vfsDest,pathDest / srcPath.GetFileName(),progress,overwrite);
|
||||
}
|
||||
if(vfsSrc->FileExists(srcPath))
|
||||
{
|
||||
CopyFile(vfsSrc,srcPath,vfsDest,pathDest / srcPath.GetFileName(),[progress,srcPath](int64_t offset, int64_t length)->void {
|
||||
progress(offset,length,srcPath);
|
||||
},overwrite);
|
||||
}
|
||||
}
|
||||
void CopyDirectory(
|
||||
std::shared_ptr<VFS> vfsSrc, VFSPath pathSrc, std::shared_ptr<VFS> vfsDest,
|
||||
VFSPath pathDest,
|
||||
std::function<void(int64_t offset, int64_t length, VFSPath currentFile)>
|
||||
progress,
|
||||
bool overwrite) {
|
||||
if (vfsSrc->DirectoryExists(pathSrc)) {
|
||||
vfsDest->CreateDirectory(pathDest);
|
||||
for (auto &srcPath : vfsSrc->EnumeratePaths(pathSrc)) {
|
||||
if (vfsSrc->DirectoryExists(srcPath)) {
|
||||
CopyDirectory(vfsSrc, srcPath, vfsDest,
|
||||
pathDest / srcPath.GetFileName(), progress,
|
||||
overwrite);
|
||||
}
|
||||
if (vfsSrc->FileExists(srcPath)) {
|
||||
CopyFile(
|
||||
vfsSrc, srcPath, vfsDest, pathDest / srcPath.GetFileName(),
|
||||
[progress, srcPath](int64_t offset, int64_t length)
|
||||
-> void { progress(offset, length, srcPath); },
|
||||
overwrite);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace Tesses::Framework::Filesystem::Helpers
|
||||
@@ -1,3 +1,21 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/Filesystem/LocalFS.hpp"
|
||||
#include "TessesFramework/Streams/FileStream.hpp"
|
||||
#include <cerrno>
|
||||
@@ -6,13 +24,14 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
|
||||
#include "TessesFramework/Filesystem/VFSFix.hpp"
|
||||
#include <windows.h>
|
||||
#undef min
|
||||
#else
|
||||
#include <utime.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <unistd.h>
|
||||
#include <utime.h>
|
||||
#endif
|
||||
|
||||
#include "TessesFramework/Threading/Thread.hpp"
|
||||
@@ -21,465 +40,484 @@
|
||||
#include <sys/inotify.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
namespace Tesses::Framework::Filesystem
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
static void TimetToFileTime(time_t t, LPFILETIME pft) {
|
||||
ULARGE_INTEGER time_value;
|
||||
time_value.QuadPart = (t * 10000000LL) + 116444736000000000LL;
|
||||
pft->dwLowDateTime = time_value.LowPart;
|
||||
pft->dwHighDateTime = time_value.HighPart;
|
||||
}
|
||||
#endif
|
||||
bool LocalFilesystem::Stat(VFSPath path, StatData& sfs)
|
||||
namespace Tesses::Framework::Filesystem {
|
||||
#if defined(_WIN32)
|
||||
static void TimetToFileTime(time_t t, LPFILETIME pft) {
|
||||
ULARGE_INTEGER time_value;
|
||||
time_value.QuadPart = (t * 10000000LL) + 116444736000000000LL;
|
||||
pft->dwLowDateTime = time_value.LowPart;
|
||||
pft->dwHighDateTime = time_value.HighPart;
|
||||
}
|
||||
#endif
|
||||
bool LocalFilesystem::Stat(VFSPath path, StatData &sfs) {
|
||||
|
||||
std::string s = VFSPathToSystem(path);
|
||||
#if defined(_WIN32)
|
||||
|
||||
struct __stat64 st;
|
||||
if (_stat64(s.c_str(), &st) == 0)
|
||||
#else
|
||||
struct stat st;
|
||||
if (stat(s.c_str(), &st) == 0)
|
||||
#endif
|
||||
|
||||
{
|
||||
|
||||
std::string s = VFSPathToSystem(path);
|
||||
#if defined(_WIN32)
|
||||
sfs.Device = (uint64_t)st.st_dev;
|
||||
sfs.Inode = (uint64_t)st.st_ino;
|
||||
sfs.Mode = (uint32_t)st.st_mode;
|
||||
sfs.HardLinks = (uint64_t)st.st_nlink;
|
||||
sfs.UserId = (uint32_t)st.st_uid;
|
||||
sfs.GroupId = (uint32_t)st.st_gid;
|
||||
sfs.DeviceId = (uint64_t)st.st_rdev;
|
||||
sfs.Size = (uint64_t)st.st_size;
|
||||
#if defined(_WIN32)
|
||||
sfs.BlockSize = 512;
|
||||
sfs.BlockCount = sfs.Size / sfs.BlockSize;
|
||||
#else
|
||||
sfs.BlockSize = (uint64_t)st.st_blksize;
|
||||
sfs.BlockCount = (uint64_t)st.st_blocks;
|
||||
#endif
|
||||
sfs.LastAccess = Date::DateTime((int64_t)st.st_atime);
|
||||
sfs.LastModified = Date::DateTime((int64_t)st.st_mtime);
|
||||
sfs.LastStatus = Date::DateTime((int64_t)st.st_ctime);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
struct __stat64 st;
|
||||
if(_stat64(s.c_str(),&st) == 0)
|
||||
#else
|
||||
struct stat st;
|
||||
if(stat(s.c_str(),&st) == 0)
|
||||
#endif
|
||||
|
||||
{
|
||||
void LocalFilesystem::SetDate(VFSPath path, Date::DateTime lastWrite,
|
||||
Date::DateTime lastAccess) {
|
||||
std::string s = VFSPathToSystem(path);
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_SETDATE)
|
||||
#if defined(_WIN32)
|
||||
FILETIME lastWriteF;
|
||||
FILETIME lastAccessF;
|
||||
|
||||
sfs.Device = (uint64_t)st.st_dev;
|
||||
sfs.Inode = (uint64_t)st.st_ino;
|
||||
sfs.Mode = (uint32_t)st.st_mode;
|
||||
sfs.HardLinks = (uint64_t)st.st_nlink;
|
||||
sfs.UserId = (uint32_t)st.st_uid;
|
||||
sfs.GroupId = (uint32_t)st.st_gid;
|
||||
sfs.DeviceId = (uint64_t)st.st_rdev;
|
||||
sfs.Size = (uint64_t)st.st_size;
|
||||
#if defined(_WIN32)
|
||||
sfs.BlockSize = 512;
|
||||
sfs.BlockCount = sfs.Size / sfs.BlockSize;
|
||||
#else
|
||||
sfs.BlockSize = (uint64_t)st.st_blksize;
|
||||
sfs.BlockCount = (uint64_t)st.st_blocks;
|
||||
#endif
|
||||
sfs.LastAccess = Date::DateTime((int64_t)st.st_atime);
|
||||
sfs.LastModified = Date::DateTime((int64_t)st.st_mtime);
|
||||
sfs.LastStatus = Date::DateTime((int64_t)st.st_ctime);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
TimetToFileTime((time_t)lastWrite.ToEpoch(), &lastWriteF);
|
||||
TimetToFileTime((time_t)lastAccess.ToEpoch(), &lastAccessF);
|
||||
HANDLE hFile =
|
||||
CreateFileA(s.c_str(), FILE_WRITE_ATTRIBUTES,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS, // For directories
|
||||
NULL);
|
||||
if (hFile != INVALID_HANDLE_VALUE) {
|
||||
SetFileTime(hFile, NULL, &lastAccessF, &lastWriteF);
|
||||
CloseHandle(hFile);
|
||||
}
|
||||
#else
|
||||
struct utimbuf utim;
|
||||
utim.actime = (time_t)lastAccess.ToEpoch();
|
||||
utim.modtime = (time_t)lastWrite.ToEpoch();
|
||||
utime(s.c_str(), &utim);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
VFSPath LocalFilesystem::ReadLink(VFSPath path) {
|
||||
auto res =
|
||||
std::filesystem::read_symlink(this->VFSPathToSystem(path)).string();
|
||||
return this->SystemToVFSPath(res.c_str());
|
||||
}
|
||||
std::shared_ptr<Tesses::Framework::Streams::Stream>
|
||||
LocalFilesystem::OpenFile(VFSPath path, std::string mode) {
|
||||
return std::make_shared<Tesses::Framework::Streams::FileStream>(
|
||||
VFSPathToSystem(path), mode);
|
||||
}
|
||||
|
||||
void LocalFilesystem::SetDate(VFSPath path, Date::DateTime lastWrite, Date::DateTime lastAccess)
|
||||
{
|
||||
std::string s = VFSPathToSystem(path);
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_SETDATE)
|
||||
#if defined(_WIN32)
|
||||
FILETIME lastWriteF;
|
||||
FILETIME lastAccessF;
|
||||
void LocalFilesystem::DeleteDirectory(VFSPath path) {
|
||||
std::filesystem::remove(VFSPathToSystem(path));
|
||||
}
|
||||
void LocalFilesystem::DeleteFile(VFSPath path) {
|
||||
std::filesystem::remove(VFSPathToSystem(path));
|
||||
}
|
||||
void LocalFilesystem::CreateDirectory(VFSPath path) {
|
||||
std::filesystem::create_directories(VFSPathToSystem(path));
|
||||
}
|
||||
void LocalFilesystem::CreateSymlink(VFSPath existingFile, VFSPath symlinkFile) {
|
||||
if (std::filesystem::is_directory(VFSPathToSystem(existingFile))) {
|
||||
std::filesystem::create_directory_symlink(VFSPathToSystem(existingFile),
|
||||
VFSPathToSystem(symlinkFile));
|
||||
} else {
|
||||
std::filesystem::create_symlink(VFSPathToSystem(existingFile),
|
||||
VFSPathToSystem(symlinkFile));
|
||||
}
|
||||
}
|
||||
void LocalFilesystem::CreateHardlink(VFSPath existingFile, VFSPath newName) {
|
||||
std::filesystem::create_hard_link(VFSPathToSystem(existingFile),
|
||||
VFSPathToSystem(newName));
|
||||
}
|
||||
void LocalFilesystem::MoveFile(VFSPath src, VFSPath dest) {
|
||||
std::filesystem::rename(VFSPathToSystem(src), VFSPathToSystem(dest));
|
||||
}
|
||||
void LocalFilesystem::MoveDirectory(VFSPath src, VFSPath dest) {
|
||||
std::filesystem::rename(VFSPathToSystem(src), VFSPathToSystem(dest));
|
||||
}
|
||||
std::string LocalFilesystem::VFSPathToSystem(VFSPath path) {
|
||||
#if defined(_WIN32)
|
||||
bool first = true;
|
||||
std::string p = {};
|
||||
for (auto item : path.path) {
|
||||
if (!(first && !item.empty() && item.back() == ':') &&
|
||||
!(first && path.relative))
|
||||
p.push_back('\\');
|
||||
p.append(item);
|
||||
first = false;
|
||||
}
|
||||
return p;
|
||||
|
||||
TimetToFileTime((time_t)lastWrite.ToEpoch(),&lastWriteF);
|
||||
TimetToFileTime((time_t)lastAccess.ToEpoch(),&lastAccessF);
|
||||
HANDLE hFile = CreateFileA(
|
||||
s.c_str(),
|
||||
FILE_WRITE_ATTRIBUTES,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS, // For directories
|
||||
NULL
|
||||
);
|
||||
if(hFile != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
SetFileTime(
|
||||
hFile,
|
||||
NULL,
|
||||
&lastAccessF,
|
||||
&lastWriteF
|
||||
);
|
||||
CloseHandle(hFile);
|
||||
}
|
||||
#else
|
||||
struct utimbuf utim;
|
||||
utim.actime = (time_t)lastAccess.ToEpoch();
|
||||
utim.modtime = (time_t)lastWrite.ToEpoch();
|
||||
utime(s.c_str(),&utim);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
VFSPath LocalFilesystem::ReadLink(VFSPath path)
|
||||
{
|
||||
auto res = std::filesystem::read_symlink(this->VFSPathToSystem(path)).string();
|
||||
return this->SystemToVFSPath(res.c_str());
|
||||
}
|
||||
std::shared_ptr<Tesses::Framework::Streams::Stream> LocalFilesystem::OpenFile(VFSPath path, std::string mode)
|
||||
{
|
||||
return std::make_shared<Tesses::Framework::Streams::FileStream>(VFSPathToSystem(path), mode);
|
||||
}
|
||||
#else
|
||||
return path.ToString();
|
||||
#endif
|
||||
}
|
||||
VFSPath LocalFilesystem::SystemToVFSPath(std::string path) {
|
||||
VFSPath p;
|
||||
p.path = VFSPath::SplitPath(path);
|
||||
p.relative = true;
|
||||
if (!path.empty()) {
|
||||
if (path.front() == '/')
|
||||
p.relative = false;
|
||||
if (!p.path.empty()) {
|
||||
auto firstPartPath = p.path.front();
|
||||
|
||||
void LocalFilesystem::DeleteDirectory(VFSPath path)
|
||||
{
|
||||
std::filesystem::remove(VFSPathToSystem(path));
|
||||
}
|
||||
void LocalFilesystem::DeleteFile(VFSPath path)
|
||||
{
|
||||
std::filesystem::remove(VFSPathToSystem(path));
|
||||
}
|
||||
void LocalFilesystem::CreateDirectory(VFSPath path)
|
||||
{
|
||||
std::filesystem::create_directories(VFSPathToSystem(path));
|
||||
}
|
||||
void LocalFilesystem::CreateSymlink(VFSPath existingFile, VFSPath symlinkFile)
|
||||
{
|
||||
if(std::filesystem::is_directory(VFSPathToSystem(existingFile)))
|
||||
{
|
||||
std::filesystem::create_directory_symlink(VFSPathToSystem(existingFile),VFSPathToSystem(symlinkFile));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::filesystem::create_symlink(VFSPathToSystem(existingFile),VFSPathToSystem(symlinkFile));
|
||||
if (!firstPartPath.empty() && firstPartPath.back() == ':')
|
||||
p.relative = false;
|
||||
}
|
||||
}
|
||||
void LocalFilesystem::CreateHardlink(VFSPath existingFile, VFSPath newName)
|
||||
{
|
||||
std::filesystem::create_hard_link(VFSPathToSystem(existingFile),VFSPathToSystem(newName));
|
||||
}
|
||||
void LocalFilesystem::MoveFile(VFSPath src, VFSPath dest)
|
||||
{
|
||||
std::filesystem::rename(VFSPathToSystem(src),VFSPathToSystem(dest));
|
||||
}
|
||||
void LocalFilesystem::MoveDirectory(VFSPath src, VFSPath dest)
|
||||
{
|
||||
std::filesystem::rename(VFSPathToSystem(src),VFSPathToSystem(dest));
|
||||
}
|
||||
std::string LocalFilesystem::VFSPathToSystem(VFSPath path)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
bool first=true;
|
||||
std::string p = {};
|
||||
for(auto item : path.path)
|
||||
{
|
||||
if(!(first && !item.empty() && item.back()==':') && !(first && path.relative))
|
||||
p.push_back('\\');
|
||||
p.append(item);
|
||||
first=false;
|
||||
}
|
||||
return p;
|
||||
return p;
|
||||
}
|
||||
|
||||
#else
|
||||
return path.ToString();
|
||||
#endif
|
||||
}
|
||||
VFSPath LocalFilesystem::SystemToVFSPath(std::string path)
|
||||
{
|
||||
VFSPath p;
|
||||
p.path = VFSPath::SplitPath(path);
|
||||
p.relative=true;
|
||||
if(!path.empty())
|
||||
{
|
||||
if(path.front() == '/') p.relative=false;
|
||||
if(!p.path.empty())
|
||||
{
|
||||
auto firstPartPath = p.path.front();
|
||||
VFSPathEnumerator LocalFilesystem::EnumeratePaths(VFSPath path) {
|
||||
std::filesystem::path sysPath = VFSPathToSystem(path);
|
||||
if (!std::filesystem::is_directory(sysPath))
|
||||
return VFSPathEnumerator();
|
||||
|
||||
if(!firstPartPath.empty() && firstPartPath.back() == ':') p.relative=false;
|
||||
}
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
VFSPathEnumerator LocalFilesystem::EnumeratePaths(VFSPath path)
|
||||
{
|
||||
std::filesystem::path sysPath = VFSPathToSystem(path);
|
||||
if(!std::filesystem::is_directory(sysPath)) return VFSPathEnumerator();
|
||||
|
||||
auto dir = new std::filesystem::directory_iterator(sysPath);
|
||||
return VFSPathEnumerator([dir,path](VFSPath& path0)->bool {
|
||||
std::filesystem::directory_iterator& ittr = *dir;
|
||||
if(ittr != std::filesystem::directory_iterator())
|
||||
{
|
||||
auto dir = new std::filesystem::directory_iterator(sysPath);
|
||||
return VFSPathEnumerator(
|
||||
[dir, path](VFSPath &path0) -> bool {
|
||||
std::filesystem::directory_iterator &ittr = *dir;
|
||||
if (ittr != std::filesystem::directory_iterator()) {
|
||||
path0 = VFSPath(path, ittr->path().filename().string());
|
||||
ittr++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},[dir]()->void{
|
||||
delete dir;
|
||||
});
|
||||
}
|
||||
bool LocalFilesystem::StatVFS(VFSPath path, StatVFSData& data)
|
||||
{
|
||||
auto pathStr = this->VFSPathToSystem(path);
|
||||
#if defined(_WIN32)
|
||||
//not supporting windows yet
|
||||
VFS::StatVFS(path, data);
|
||||
},
|
||||
[dir]() -> void { delete dir; });
|
||||
}
|
||||
bool LocalFilesystem::StatVFS(VFSPath path, StatVFSData &data) {
|
||||
auto pathStr = this->VFSPathToSystem(path);
|
||||
#if defined(_WIN32)
|
||||
// not supporting windows yet
|
||||
VFS::StatVFS(path, data);
|
||||
return true;
|
||||
#else
|
||||
struct statvfs vfs;
|
||||
if (statvfs(pathStr.c_str(), &vfs) == 0) {
|
||||
data.BlockSize = vfs.f_bsize;
|
||||
data.FragmentSize = vfs.f_frsize;
|
||||
data.Blocks = vfs.f_blocks;
|
||||
data.BlocksFree = vfs.f_bfree;
|
||||
data.BlocksAvailable = vfs.f_bavail;
|
||||
data.TotalInodes = vfs.f_files;
|
||||
data.FreeInodes = vfs.f_ffree;
|
||||
data.AvailableInodes = vfs.f_favail;
|
||||
data.Id = vfs.f_fsid;
|
||||
data.Flags = vfs.f_flag;
|
||||
data.MaxNameLength = vfs.f_namemax;
|
||||
return true;
|
||||
#else
|
||||
struct statvfs vfs;
|
||||
if(statvfs(pathStr.c_str(), &vfs) == 0)
|
||||
{
|
||||
data.BlockSize = vfs.f_bsize;
|
||||
data.FragmentSize = vfs.f_frsize;
|
||||
data.Blocks = vfs.f_blocks;
|
||||
data.BlocksFree = vfs.f_bfree;
|
||||
data.BlocksAvailable = vfs.f_bavail;
|
||||
data.TotalInodes = vfs.f_files;
|
||||
data.FreeInodes = vfs.f_ffree;
|
||||
data.AvailableInodes = vfs.f_favail;
|
||||
data.Id = vfs.f_fsid;
|
||||
data.Flags = vfs.f_flag;
|
||||
data.MaxNameLength = vfs.f_namemax;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void LocalFilesystem::Chmod(VFSPath path, uint32_t mode)
|
||||
{
|
||||
auto pathStr = this->VFSPathToSystem(path);
|
||||
#if defined(_WIN32)
|
||||
void LocalFilesystem::Chmod(VFSPath path, uint32_t mode) {
|
||||
auto pathStr = this->VFSPathToSystem(path);
|
||||
#if defined(_WIN32)
|
||||
|
||||
#else
|
||||
chmod(pathStr.c_str(), (mode_t)mode);
|
||||
#endif
|
||||
}
|
||||
void LocalFilesystem::Chown(VFSPath path, uint32_t uid, uint32_t gid)
|
||||
{
|
||||
auto pathStr = this->VFSPathToSystem(path);
|
||||
#if defined(_WIN32)
|
||||
#else
|
||||
chmod(pathStr.c_str(), (mode_t)mode);
|
||||
#endif
|
||||
}
|
||||
void LocalFilesystem::Chown(VFSPath path, uint32_t uid, uint32_t gid) {
|
||||
auto pathStr = this->VFSPathToSystem(path);
|
||||
#if defined(_WIN32)
|
||||
|
||||
#else
|
||||
chown(pathStr.c_str(), (uid_t)uid, (gid_t)gid);
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
chown(pathStr.c_str(), (uid_t)uid, (gid_t)gid);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
FIFOCreationResult LocalFilesystem::CreateFIFO(VFSPath path, uint32_t mod)
|
||||
{
|
||||
auto pathStr = this->VFSPathToSystem(path);
|
||||
#if defined(_WIN32)
|
||||
FIFOCreationResult LocalFilesystem::CreateFIFO(VFSPath path, uint32_t mod) {
|
||||
auto pathStr = this->VFSPathToSystem(path);
|
||||
#if defined(_WIN32)
|
||||
return FIFOCreationResult::Unsupported;
|
||||
#else
|
||||
int res = mkfifo(pathStr.c_str(), (mode_t)mod);
|
||||
if (res == 0)
|
||||
return FIFOCreationResult::Success;
|
||||
else if (res == -1) {
|
||||
switch (res) {
|
||||
case EEXIST:
|
||||
return FIFOCreationResult::Exists;
|
||||
case ENOTSUP:
|
||||
return FIFOCreationResult::Unsupported;
|
||||
#else
|
||||
int res = mkfifo(pathStr.c_str(), (mode_t)mod);
|
||||
if(res == 0) return FIFOCreationResult::Success;
|
||||
else if(res == -1)
|
||||
{
|
||||
switch(res)
|
||||
{
|
||||
case EEXIST:
|
||||
return FIFOCreationResult::Exists;
|
||||
case ENOTSUP:
|
||||
return FIFOCreationResult::Unsupported;
|
||||
case EACCES:
|
||||
return FIFOCreationResult::Denied;
|
||||
case ENOSPC:
|
||||
return FIFOCreationResult::OutOfInodes;
|
||||
case EROFS:
|
||||
return FIFOCreationResult::ReadOnlyFS;
|
||||
}
|
||||
}
|
||||
|
||||
return FIFOCreationResult::UnknownError;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void LocalFilesystem::Lock(VFSPath path)
|
||||
{
|
||||
auto p2 = VFSPathToSystem(path);
|
||||
const char* fopenPath = p2.c_str();
|
||||
while(true)
|
||||
{
|
||||
FILE* fp = fopen(fopenPath,"wx");
|
||||
if(fp) {
|
||||
fclose(fp);
|
||||
break;
|
||||
}
|
||||
case EACCES:
|
||||
return FIFOCreationResult::Denied;
|
||||
case ENOSPC:
|
||||
return FIFOCreationResult::OutOfInodes;
|
||||
case EROFS:
|
||||
return FIFOCreationResult::ReadOnlyFS;
|
||||
}
|
||||
}
|
||||
void LocalFilesystem::Unlock(VFSPath path)
|
||||
{
|
||||
std::error_code error;
|
||||
std::filesystem::remove(VFSPathToSystem(path),error);
|
||||
|
||||
return FIFOCreationResult::UnknownError;
|
||||
#endif
|
||||
}
|
||||
|
||||
void LocalFilesystem::Lock(VFSPath path) {
|
||||
auto p2 = VFSPathToSystem(path);
|
||||
const char *fopenPath = p2.c_str();
|
||||
while (true) {
|
||||
FILE *fp = fopen(fopenPath, "wx");
|
||||
if (fp) {
|
||||
fclose(fp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#if defined(__linux__)
|
||||
}
|
||||
void LocalFilesystem::Unlock(VFSPath path) {
|
||||
std::error_code error;
|
||||
std::filesystem::remove(VFSPathToSystem(path), error);
|
||||
}
|
||||
#if defined(__linux__)
|
||||
|
||||
class INotifyWatcher : public FSWatcher {
|
||||
std::shared_ptr<Threading::Thread> thrd;
|
||||
static uint32_t to_linux_mask(FSWatcherEventType flags)
|
||||
{
|
||||
uint32_t lflags = 0;
|
||||
lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::Accessed) != 0) ? IN_ACCESS : 0;
|
||||
lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::AttributeChanged) != 0) ? IN_ATTRIB : 0;
|
||||
lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::Writen) != 0) ? IN_CLOSE_WRITE : 0;
|
||||
lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::Read) != 0) ? IN_CLOSE_NOWRITE : 0;
|
||||
lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::Created) != 0) ? IN_CREATE : 0;
|
||||
lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::Deleted) != 0) ? IN_DELETE : 0;
|
||||
lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::WatchEntryDeleted) != 0) ? IN_DELETE_SELF : 0;
|
||||
lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::Modified) != 0) ? IN_MODIFY : 0;
|
||||
lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::WatchEntryMoved) != 0) ? IN_MOVE_SELF : 0;
|
||||
lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::MoveOld) != 0) ? IN_MOVED_FROM : 0;
|
||||
lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::MoveNew) != 0) ? IN_MOVED_TO : 0;
|
||||
lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::Opened) != 0) ? IN_OPEN : 0;
|
||||
class INotifyWatcher : public FSWatcher {
|
||||
std::shared_ptr<Threading::Thread> thrd;
|
||||
static uint32_t to_linux_mask(FSWatcherEventType flags) {
|
||||
uint32_t lflags = 0;
|
||||
lflags |=
|
||||
(((uint32_t)flags & (uint32_t)FSWatcherEventType::Accessed) != 0)
|
||||
? IN_ACCESS
|
||||
: 0;
|
||||
lflags |= (((uint32_t)flags &
|
||||
(uint32_t)FSWatcherEventType::AttributeChanged) != 0)
|
||||
? IN_ATTRIB
|
||||
: 0;
|
||||
lflags |=
|
||||
(((uint32_t)flags & (uint32_t)FSWatcherEventType::Writen) != 0)
|
||||
? IN_CLOSE_WRITE
|
||||
: 0;
|
||||
lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::Read) != 0)
|
||||
? IN_CLOSE_NOWRITE
|
||||
: 0;
|
||||
lflags |=
|
||||
(((uint32_t)flags & (uint32_t)FSWatcherEventType::Created) != 0)
|
||||
? IN_CREATE
|
||||
: 0;
|
||||
lflags |=
|
||||
(((uint32_t)flags & (uint32_t)FSWatcherEventType::Deleted) != 0)
|
||||
? IN_DELETE
|
||||
: 0;
|
||||
lflags |= (((uint32_t)flags &
|
||||
(uint32_t)FSWatcherEventType::WatchEntryDeleted) != 0)
|
||||
? IN_DELETE_SELF
|
||||
: 0;
|
||||
lflags |=
|
||||
(((uint32_t)flags & (uint32_t)FSWatcherEventType::Modified) != 0)
|
||||
? IN_MODIFY
|
||||
: 0;
|
||||
lflags |= (((uint32_t)flags &
|
||||
(uint32_t)FSWatcherEventType::WatchEntryMoved) != 0)
|
||||
? IN_MOVE_SELF
|
||||
: 0;
|
||||
lflags |=
|
||||
(((uint32_t)flags & (uint32_t)FSWatcherEventType::MoveOld) != 0)
|
||||
? IN_MOVED_FROM
|
||||
: 0;
|
||||
lflags |=
|
||||
(((uint32_t)flags & (uint32_t)FSWatcherEventType::MoveNew) != 0)
|
||||
? IN_MOVED_TO
|
||||
: 0;
|
||||
lflags |=
|
||||
(((uint32_t)flags & (uint32_t)FSWatcherEventType::Opened) != 0)
|
||||
? IN_OPEN
|
||||
: 0;
|
||||
|
||||
return lflags;
|
||||
}
|
||||
static FSWatcherEventType from_linux_mask(uint32_t lflags)
|
||||
{
|
||||
uint32_t flags = 0;
|
||||
flags |= ((lflags & IN_ACCESS) != 0) ? (uint32_t)FSWatcherEventType::Accessed : 0;
|
||||
flags |= ((lflags & IN_ATTRIB) != 0) ? (uint32_t)FSWatcherEventType::AttributeChanged : 0;
|
||||
flags |= ((lflags & IN_CLOSE_WRITE) != 0) ? (uint32_t)FSWatcherEventType::Writen : 0;
|
||||
flags |= ((lflags & IN_CLOSE_NOWRITE) != 0) ? (uint32_t)FSWatcherEventType::Read : 0;
|
||||
flags |= ((lflags & IN_CREATE) != 0) ? (uint32_t)FSWatcherEventType::Created : 0;
|
||||
flags |= ((lflags & IN_DELETE) != 0) ? (uint32_t)FSWatcherEventType::Deleted : 0;
|
||||
flags |= ((lflags & IN_DELETE_SELF) != 0) ? (uint32_t)FSWatcherEventType::WatchEntryDeleted : 0;
|
||||
flags |= ((lflags & IN_MODIFY) != 0) ? (uint32_t)FSWatcherEventType::Modified : 0;
|
||||
flags |= ((lflags & IN_MOVE_SELF) != 0) ? (uint32_t)FSWatcherEventType::WatchEntryMoved : 0;
|
||||
flags |= ((lflags & IN_MOVED_FROM) != 0) ? (uint32_t)FSWatcherEventType::MoveOld : 0;
|
||||
flags |= ((lflags & IN_MOVED_TO) != 0) ? (uint32_t)FSWatcherEventType::MoveNew : 0;
|
||||
flags |= ((lflags & IN_OPEN) != 0) ? (uint32_t)FSWatcherEventType::Opened : 0;
|
||||
return lflags;
|
||||
}
|
||||
static FSWatcherEventType from_linux_mask(uint32_t lflags) {
|
||||
uint32_t flags = 0;
|
||||
flags |= ((lflags & IN_ACCESS) != 0)
|
||||
? (uint32_t)FSWatcherEventType::Accessed
|
||||
: 0;
|
||||
flags |= ((lflags & IN_ATTRIB) != 0)
|
||||
? (uint32_t)FSWatcherEventType::AttributeChanged
|
||||
: 0;
|
||||
flags |= ((lflags & IN_CLOSE_WRITE) != 0)
|
||||
? (uint32_t)FSWatcherEventType::Writen
|
||||
: 0;
|
||||
flags |= ((lflags & IN_CLOSE_NOWRITE) != 0)
|
||||
? (uint32_t)FSWatcherEventType::Read
|
||||
: 0;
|
||||
flags |= ((lflags & IN_CREATE) != 0)
|
||||
? (uint32_t)FSWatcherEventType::Created
|
||||
: 0;
|
||||
flags |= ((lflags & IN_DELETE) != 0)
|
||||
? (uint32_t)FSWatcherEventType::Deleted
|
||||
: 0;
|
||||
flags |= ((lflags & IN_DELETE_SELF) != 0)
|
||||
? (uint32_t)FSWatcherEventType::WatchEntryDeleted
|
||||
: 0;
|
||||
flags |= ((lflags & IN_MODIFY) != 0)
|
||||
? (uint32_t)FSWatcherEventType::Modified
|
||||
: 0;
|
||||
flags |= ((lflags & IN_MOVE_SELF) != 0)
|
||||
? (uint32_t)FSWatcherEventType::WatchEntryMoved
|
||||
: 0;
|
||||
flags |= ((lflags & IN_MOVED_FROM) != 0)
|
||||
? (uint32_t)FSWatcherEventType::MoveOld
|
||||
: 0;
|
||||
flags |= ((lflags & IN_MOVED_TO) != 0)
|
||||
? (uint32_t)FSWatcherEventType::MoveNew
|
||||
: 0;
|
||||
flags |= ((lflags & IN_OPEN) != 0)
|
||||
? (uint32_t)FSWatcherEventType::Opened
|
||||
: 0;
|
||||
|
||||
return (FSWatcherEventType)flags;
|
||||
}
|
||||
public:
|
||||
INotifyWatcher(std::shared_ptr<VFS> vfs, VFSPath path) : FSWatcher(vfs,path)
|
||||
{
|
||||
return (FSWatcherEventType)flags;
|
||||
}
|
||||
|
||||
public:
|
||||
INotifyWatcher(std::shared_ptr<VFS> vfs, VFSPath path)
|
||||
: FSWatcher(vfs, path) {}
|
||||
|
||||
protected:
|
||||
void SetEnabledImpl(bool enabled) {
|
||||
if (enabled) {
|
||||
int fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
|
||||
if (fd == -1) {
|
||||
throw std::runtime_error("Cannot init inotify");
|
||||
}
|
||||
auto str = this->GetFilesystem()->VFSPathToSystem(this->GetPath());
|
||||
|
||||
protected:
|
||||
int watch =
|
||||
inotify_add_watch(fd, str.c_str(), to_linux_mask(this->events));
|
||||
|
||||
void SetEnabledImpl(bool enabled)
|
||||
{
|
||||
if(enabled)
|
||||
{
|
||||
int fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
|
||||
if (fd == -1)
|
||||
{
|
||||
throw std::runtime_error("Cannot init inotify");
|
||||
}
|
||||
auto str = this->GetFilesystem()->VFSPathToSystem(this->GetPath());
|
||||
thrd = std::make_shared<Threading::Thread>([this, watch,
|
||||
fd]() -> void {
|
||||
int cnt = 0;
|
||||
struct pollfd pfd = {.fd = fd, .events = POLLIN};
|
||||
std::vector<std::pair<VFSPath, uint32_t>> mvFroms;
|
||||
char buf[4096]
|
||||
__attribute__((aligned(__alignof__(struct inotify_event))));
|
||||
const struct inotify_event *event;
|
||||
ssize_t size;
|
||||
|
||||
int watch = inotify_add_watch(fd, str.c_str(),to_linux_mask(this->events));
|
||||
bool fail = false;
|
||||
|
||||
thrd = std::make_shared<Threading::Thread>([this,watch,fd]()-> void {
|
||||
int cnt = 0;
|
||||
struct pollfd pfd = {.fd = fd, .events = POLLIN};
|
||||
std::vector<std::pair<VFSPath,uint32_t>> mvFroms;
|
||||
char buf[4096]
|
||||
__attribute__ ((aligned(__alignof__(struct inotify_event))));
|
||||
const struct inotify_event *event;
|
||||
ssize_t size;
|
||||
FSWatcherEvent evt;
|
||||
evt.dest = this->GetPath();
|
||||
while (!fail && this->enabled) {
|
||||
cnt = poll(&pfd, 1, -1);
|
||||
if (cnt == -1)
|
||||
break;
|
||||
|
||||
bool fail=false;
|
||||
if (cnt > 0) {
|
||||
if (pfd.revents & POLLIN) {
|
||||
for (;;) {
|
||||
size = read(fd, buf, sizeof(buf));
|
||||
if (size == -1 && errno != EAGAIN) {
|
||||
fail = true;
|
||||
break;
|
||||
}
|
||||
|
||||
FSWatcherEvent evt;
|
||||
evt.dest = this->GetPath();
|
||||
while(!fail && this->enabled)
|
||||
{
|
||||
cnt = poll(&pfd,1,-1);
|
||||
if(cnt == -1) break;
|
||||
if (size <= 0)
|
||||
break;
|
||||
|
||||
if(cnt > 0)
|
||||
{
|
||||
if(pfd.revents & POLLIN)
|
||||
{
|
||||
for (;;) {
|
||||
size = read(fd, buf, sizeof(buf));
|
||||
if (size == -1 && errno != EAGAIN) {
|
||||
fail=true;
|
||||
break;
|
||||
}
|
||||
for (char *ptr = buf; ptr < buf + size;
|
||||
ptr += sizeof(struct inotify_event) +
|
||||
event->len) {
|
||||
|
||||
if (size <= 0)
|
||||
break;
|
||||
event = (const struct inotify_event *)ptr;
|
||||
VFSPath path = this->GetPath();
|
||||
|
||||
for (char *ptr = buf; ptr < buf + size;
|
||||
ptr += sizeof(struct inotify_event) + event->len) {
|
||||
if (event->len)
|
||||
path = path /
|
||||
std::string(event->name,
|
||||
(size_t)event->len);
|
||||
|
||||
event = (const struct inotify_event *) ptr;
|
||||
VFSPath path = this->GetPath();
|
||||
|
||||
if(event->len)
|
||||
path = path / std::string(event->name, (size_t)event->len);
|
||||
|
||||
if(((uint32_t)this->events & (uint32_t)FSWatcherEventType::Moved) == (uint32_t)FSWatcherEventType::Moved && event->mask & IN_MOVED_FROM)
|
||||
{
|
||||
mvFroms.emplace_back(path,event->cookie);
|
||||
}
|
||||
else if(((uint32_t)this->events & (uint32_t)FSWatcherEventType::Moved) == (uint32_t)FSWatcherEventType::Moved && event->mask & IN_MOVED_TO)
|
||||
{
|
||||
for(auto ittr = mvFroms.begin(); ittr != mvFroms.end(); ittr++)
|
||||
{
|
||||
if(ittr->second == event->cookie)
|
||||
{
|
||||
evt.src = ittr->first;
|
||||
mvFroms.erase(ittr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
evt.isDir = (event->mask & IN_ISDIR);
|
||||
evt.dest = path;
|
||||
evt.type = FSWatcherEventType::Moved;
|
||||
if(this->event)
|
||||
this->event(evt);
|
||||
}
|
||||
else {
|
||||
|
||||
evt.isDir = (event->mask & IN_ISDIR);
|
||||
evt.src = path;
|
||||
evt.type = from_linux_mask(event->mask);;
|
||||
if(this->event)
|
||||
this->event(evt);
|
||||
}
|
||||
if(event->mask & IN_MOVE_SELF)
|
||||
{
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
if(event->mask & IN_DELETE_SELF)
|
||||
{
|
||||
close(fd);
|
||||
return;
|
||||
if (((uint32_t)this->events &
|
||||
(uint32_t)FSWatcherEventType::Moved) ==
|
||||
(uint32_t)
|
||||
FSWatcherEventType::Moved &&
|
||||
event->mask & IN_MOVED_FROM) {
|
||||
mvFroms.emplace_back(path,
|
||||
event->cookie);
|
||||
} else if (
|
||||
((uint32_t)this->events &
|
||||
(uint32_t)FSWatcherEventType::Moved) ==
|
||||
(uint32_t)
|
||||
FSWatcherEventType::Moved &&
|
||||
event->mask & IN_MOVED_TO) {
|
||||
for (auto ittr = mvFroms.begin();
|
||||
ittr != mvFroms.end(); ittr++) {
|
||||
if (ittr->second == event->cookie) {
|
||||
evt.src = ittr->first;
|
||||
mvFroms.erase(ittr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
evt.isDir = (event->mask & IN_ISDIR);
|
||||
evt.dest = path;
|
||||
evt.type = FSWatcherEventType::Moved;
|
||||
if (this->event)
|
||||
this->event(evt);
|
||||
} else {
|
||||
|
||||
evt.isDir = (event->mask & IN_ISDIR);
|
||||
evt.src = path;
|
||||
evt.type = from_linux_mask(event->mask);
|
||||
;
|
||||
if (this->event)
|
||||
this->event(evt);
|
||||
}
|
||||
if (event->mask & IN_MOVE_SELF) {
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
if (event->mask & IN_DELETE_SELF) {
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
thrd = nullptr;
|
||||
}
|
||||
}
|
||||
public:
|
||||
~INotifyWatcher()
|
||||
{
|
||||
this->enabled = false;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
close(fd);
|
||||
});
|
||||
|
||||
|
||||
std::shared_ptr<FSWatcher> LocalFilesystem::CreateWatcher(std::shared_ptr<VFS> vfs, VFSPath path)
|
||||
{
|
||||
#if defined(__linux__)
|
||||
return std::make_shared<INotifyWatcher>(vfs, path);
|
||||
#endif
|
||||
return VFS::CreateWatcher(vfs,path);
|
||||
} else {
|
||||
thrd = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<LocalFilesystem> LocalFS = std::make_shared<LocalFilesystem>();
|
||||
|
||||
public:
|
||||
~INotifyWatcher() { this->enabled = false; }
|
||||
};
|
||||
#endif
|
||||
|
||||
std::shared_ptr<FSWatcher>
|
||||
LocalFilesystem::CreateWatcher(std::shared_ptr<VFS> vfs, VFSPath path) {
|
||||
#if defined(__linux__)
|
||||
return std::make_shared<INotifyWatcher>(vfs, path);
|
||||
#endif
|
||||
return VFS::CreateWatcher(vfs, path);
|
||||
}
|
||||
|
||||
std::shared_ptr<LocalFilesystem> LocalFS = std::make_shared<LocalFilesystem>();
|
||||
|
||||
} // namespace Tesses::Framework::Filesystem
|
||||
|
||||
// C:/Users/Jim/Joel
|
||||
|
||||
@@ -1,552 +1,491 @@
|
||||
#include "TessesFramework/Filesystem/MountableFilesystem.hpp"
|
||||
#include "TessesFramework/Filesystem/NullFilesystem.hpp"
|
||||
#include <iostream>
|
||||
namespace Tesses::Framework::Filesystem
|
||||
{
|
||||
MountableFilesystem::MountableFilesystem() : MountableFilesystem(std::make_shared<NullFilesystem>())
|
||||
{
|
||||
namespace Tesses::Framework::Filesystem {
|
||||
MountableFilesystem::MountableFilesystem()
|
||||
: MountableFilesystem(std::make_shared<NullFilesystem>()) {}
|
||||
MountableFilesystem::MountableFilesystem(std::shared_ptr<VFS> root) {
|
||||
this->root = root;
|
||||
}
|
||||
MountableDirectory::~MountableDirectory() {
|
||||
for (auto dir : this->dirs)
|
||||
delete dir;
|
||||
}
|
||||
|
||||
}
|
||||
MountableFilesystem::MountableFilesystem(std::shared_ptr<VFS> root)
|
||||
{
|
||||
this->root = root;
|
||||
}
|
||||
MountableDirectory::~MountableDirectory()
|
||||
{
|
||||
for(auto dir : this->dirs) delete dir;
|
||||
}
|
||||
MountableFilesystem::~MountableFilesystem() {
|
||||
for (auto item : this->directories)
|
||||
delete item;
|
||||
}
|
||||
|
||||
MountableFilesystem::~MountableFilesystem()
|
||||
{
|
||||
for(auto item : this->directories) delete item;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void MountableFilesystem::GetFS(VFSPath srcPath, VFSPath& destRoot, VFSPath& destPath, std::shared_ptr<VFS>& vfs)
|
||||
{
|
||||
if(srcPath.path.empty()) return;
|
||||
for(auto item : this->directories)
|
||||
{
|
||||
if(srcPath.path.front() == item->name)
|
||||
{
|
||||
if(item->vfs != nullptr)
|
||||
{
|
||||
vfs = item->vfs;
|
||||
VFSPath srcPath1(std::vector(srcPath.path.begin()+1,srcPath.path.end()));
|
||||
srcPath1.relative=false;
|
||||
destPath = srcPath1;
|
||||
destRoot = VFSPath(VFSPath(),item->name);
|
||||
|
||||
|
||||
}
|
||||
VFSPath srcPath2(std::vector(srcPath.path.begin()+1,srcPath.path.end()));
|
||||
srcPath2.relative=false;
|
||||
item->GetFS(srcPath2,VFSPath(VFSPath(),item->name), destRoot,destPath,vfs);
|
||||
return;
|
||||
void MountableFilesystem::GetFS(VFSPath srcPath, VFSPath &destRoot,
|
||||
VFSPath &destPath, std::shared_ptr<VFS> &vfs) {
|
||||
if (srcPath.path.empty())
|
||||
return;
|
||||
for (auto item : this->directories) {
|
||||
if (srcPath.path.front() == item->name) {
|
||||
if (item->vfs != nullptr) {
|
||||
vfs = item->vfs;
|
||||
VFSPath srcPath1(
|
||||
std::vector(srcPath.path.begin() + 1, srcPath.path.end()));
|
||||
srcPath1.relative = false;
|
||||
destPath = srcPath1;
|
||||
destRoot = VFSPath(VFSPath(), item->name);
|
||||
}
|
||||
VFSPath srcPath2(
|
||||
std::vector(srcPath.path.begin() + 1, srcPath.path.end()));
|
||||
srcPath2.relative = false;
|
||||
item->GetFS(srcPath2, VFSPath(VFSPath(), item->name), destRoot,
|
||||
destPath, vfs);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MountableDirectory::GetFS(VFSPath srcPath, VFSPath curDir,
|
||||
VFSPath &destRoot, VFSPath &destPath,
|
||||
std::shared_ptr<VFS> &vfs) {
|
||||
if (srcPath.path.empty())
|
||||
return;
|
||||
for (auto item : this->dirs) {
|
||||
if (!srcPath.path.empty() && srcPath.path.front() == item->name) {
|
||||
if (item->vfs != nullptr) {
|
||||
vfs = item->vfs;
|
||||
|
||||
VFSPath srcPath1(
|
||||
std::vector(srcPath.path.begin() + 1, srcPath.path.end()));
|
||||
|
||||
void MountableDirectory::GetFS(VFSPath srcPath, VFSPath curDir, VFSPath& destRoot, VFSPath& destPath, std::shared_ptr<VFS>& vfs)
|
||||
{
|
||||
if(srcPath.path.empty()) return;
|
||||
for(auto item : this->dirs)
|
||||
{
|
||||
if(!srcPath.path.empty() && srcPath.path.front() == item->name)
|
||||
{
|
||||
if(item->vfs != nullptr)
|
||||
{
|
||||
vfs = item->vfs;
|
||||
|
||||
VFSPath srcPath1(std::vector(srcPath.path.begin()+1,srcPath.path.end()));
|
||||
|
||||
srcPath1.relative=false;
|
||||
destPath = srcPath1;
|
||||
destRoot = curDir;
|
||||
|
||||
}
|
||||
VFSPath srcPath2(std::vector(srcPath.path.begin()+1,srcPath.path.end()));
|
||||
srcPath2.relative=false;
|
||||
item->GetFS(srcPath2,VFSPath(curDir,item->name), destRoot,destPath,vfs);
|
||||
return;
|
||||
srcPath1.relative = false;
|
||||
destPath = srcPath1;
|
||||
destRoot = curDir;
|
||||
}
|
||||
VFSPath srcPath2(
|
||||
std::vector(srcPath.path.begin() + 1, srcPath.path.end()));
|
||||
srcPath2.relative = false;
|
||||
item->GetFS(srcPath2, VFSPath(curDir, item->name), destRoot,
|
||||
destPath, vfs);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
VFSPath MountableFilesystem::ReadLink(VFSPath path)
|
||||
{
|
||||
path = path.CollapseRelativeParents();
|
||||
VFSPath destRoot;
|
||||
VFSPath destPath = path;
|
||||
std::shared_ptr<VFS> vfs = root;
|
||||
|
||||
GetFS(path, destRoot, destPath, vfs);
|
||||
if(vfs != nullptr)
|
||||
return VFSPath(destRoot,vfs->ReadLink(destPath));
|
||||
return VFSPath();
|
||||
}
|
||||
|
||||
bool MountableFilesystem::StatVFS(VFSPath path, StatVFSData& data)
|
||||
{
|
||||
path = path.CollapseRelativeParents();
|
||||
VFSPath destRoot;
|
||||
VFSPath destPath = path;
|
||||
std::shared_ptr<VFS> vfs = root;
|
||||
|
||||
GetFS(path, destRoot, destPath, vfs);
|
||||
if(vfs != nullptr)
|
||||
return vfs->StatVFS(destPath,data);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MountableFilesystem::Stat(VFSPath path, StatData& data)
|
||||
{
|
||||
path = path.CollapseRelativeParents();
|
||||
VFSPath destRoot;
|
||||
VFSPath destPath = path;
|
||||
std::shared_ptr<VFS> vfs = root;
|
||||
|
||||
GetFS(path, destRoot, destPath, vfs);
|
||||
if(vfs != nullptr)
|
||||
return vfs->Stat(destPath,data);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void MountableFilesystem::Chmod(VFSPath path, uint32_t mode)
|
||||
{
|
||||
path = path.CollapseRelativeParents();
|
||||
VFSPath destRoot;
|
||||
VFSPath destPath = path;
|
||||
std::shared_ptr<VFS> vfs = root;
|
||||
|
||||
GetFS(path, destRoot, destPath, vfs);
|
||||
if(vfs != nullptr)
|
||||
vfs->Chmod(destPath,mode);
|
||||
}
|
||||
|
||||
void MountableFilesystem::Chown(VFSPath path, uint32_t uid, uint32_t gid)
|
||||
{
|
||||
path = path.CollapseRelativeParents();
|
||||
VFSPath destRoot;
|
||||
VFSPath destPath = path;
|
||||
std::shared_ptr<VFS> vfs = root;
|
||||
|
||||
GetFS(path, destRoot, destPath, vfs);
|
||||
if(vfs != nullptr)
|
||||
vfs->Chown(destPath,uid, gid);
|
||||
}
|
||||
|
||||
|
||||
FIFOCreationResult MountableFilesystem::CreateFIFO(VFSPath path, uint32_t mod)
|
||||
{
|
||||
path = path.CollapseRelativeParents();
|
||||
VFSPath destRoot;
|
||||
VFSPath destPath = path;
|
||||
std::shared_ptr<VFS> vfs = root;
|
||||
|
||||
GetFS(path, destRoot, destPath, vfs);
|
||||
if(vfs != nullptr)
|
||||
return vfs->CreateFIFO(destPath,mod);
|
||||
return FIFOCreationResult::UnknownError;
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<Tesses::Framework::Streams::Stream> MountableFilesystem::OpenFile(VFSPath path, std::string mode)
|
||||
{
|
||||
path = path.CollapseRelativeParents();
|
||||
VFSPath destRoot;
|
||||
VFSPath destPath = path;
|
||||
std::shared_ptr<VFS> vfs = root;
|
||||
|
||||
GetFS(path, destRoot, destPath, vfs);
|
||||
|
||||
if(vfs != nullptr)
|
||||
return vfs->OpenFile(destPath,mode);
|
||||
return nullptr;
|
||||
}
|
||||
void MountableFilesystem::CreateDirectory(VFSPath path)
|
||||
{
|
||||
path = path.CollapseRelativeParents();
|
||||
|
||||
VFSPath destRoot;
|
||||
VFSPath destPath = path;
|
||||
std::shared_ptr<VFS> vfs = root;
|
||||
|
||||
GetFS(path, destRoot, destPath, vfs);
|
||||
|
||||
if(destPath.path.empty()) return;
|
||||
|
||||
if(vfs != nullptr)
|
||||
vfs->CreateDirectory(destPath);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void MountableFilesystem::DeleteDirectory(VFSPath path)
|
||||
{
|
||||
path = path.CollapseRelativeParents();
|
||||
|
||||
VFSPath destRoot;
|
||||
VFSPath destPath = path;
|
||||
std::shared_ptr<VFS> vfs = root;
|
||||
|
||||
GetFS(path, destRoot, destPath, vfs);
|
||||
|
||||
if(destPath.path.empty()) return;
|
||||
|
||||
if(vfs != nullptr)
|
||||
vfs->DeleteDirectory(destPath);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void MountableFilesystem::DeleteFile(VFSPath path)
|
||||
{
|
||||
path = path.CollapseRelativeParents();
|
||||
|
||||
VFSPath destRoot;
|
||||
VFSPath destPath = path;
|
||||
std::shared_ptr<VFS> vfs = root;
|
||||
|
||||
GetFS(path, destRoot, destPath, vfs);
|
||||
|
||||
if(vfs != nullptr)
|
||||
vfs->DeleteFile(destPath);
|
||||
|
||||
}
|
||||
void MountableFilesystem::Lock(VFSPath path)
|
||||
{
|
||||
path = path.CollapseRelativeParents();
|
||||
|
||||
VFSPath destRoot;
|
||||
VFSPath destPath = path;
|
||||
std::shared_ptr<VFS> vfs = root;
|
||||
|
||||
GetFS(path, destRoot, destPath, vfs);
|
||||
|
||||
if(vfs != nullptr)
|
||||
vfs->Lock(destPath);
|
||||
|
||||
}
|
||||
void MountableFilesystem::Unlock(VFSPath path)
|
||||
{
|
||||
path = path.CollapseRelativeParents();
|
||||
|
||||
VFSPath destRoot;
|
||||
VFSPath destPath = path;
|
||||
std::shared_ptr<VFS> vfs = root;
|
||||
|
||||
GetFS(path, destRoot, destPath, vfs);
|
||||
|
||||
if(vfs != nullptr)
|
||||
vfs->Unlock(destPath);
|
||||
|
||||
}
|
||||
void MountableFilesystem::SetDate(VFSPath path, Date::DateTime lastWrite, Date::DateTime lastAccess)
|
||||
{
|
||||
|
||||
path = path.CollapseRelativeParents();
|
||||
|
||||
VFSPath destRoot;
|
||||
VFSPath destPath = path;
|
||||
std::shared_ptr<VFS> vfs = root;
|
||||
|
||||
GetFS(path, destRoot, destPath, vfs);
|
||||
|
||||
if(vfs != nullptr)
|
||||
vfs->SetDate(destPath,lastWrite,lastAccess);
|
||||
}
|
||||
void MountableFilesystem::CreateSymlink(VFSPath existingFile, VFSPath symlinkFile)
|
||||
{
|
||||
existingFile = existingFile.CollapseRelativeParents();
|
||||
symlinkFile = existingFile.CollapseRelativeParents();
|
||||
|
||||
VFSPath existingDestRoot;
|
||||
VFSPath existingDestPath = existingFile;
|
||||
std::shared_ptr<VFS> existingVFS = root;
|
||||
VFSPath symlinkDestRoot;
|
||||
VFSPath symlinkDestPath = symlinkFile;
|
||||
std::shared_ptr<VFS> symlinkVFS = root;
|
||||
|
||||
GetFS(existingFile, existingDestRoot, existingDestPath, existingVFS);
|
||||
GetFS(symlinkFile, symlinkDestRoot, symlinkDestPath, symlinkVFS);
|
||||
|
||||
if(existingVFS != nullptr && existingVFS == symlinkVFS)
|
||||
existingVFS->CreateSymlink(existingDestPath, symlinkDestPath);
|
||||
}
|
||||
|
||||
void MountableFilesystem::MoveDirectory(VFSPath src, VFSPath dest)
|
||||
{
|
||||
src = src.CollapseRelativeParents();
|
||||
dest = dest.CollapseRelativeParents();
|
||||
|
||||
VFSPath srcDestRoot;
|
||||
VFSPath srcDestPath = src;
|
||||
std::shared_ptr<VFS> srcVFS = root;
|
||||
VFSPath destDestRoot;
|
||||
VFSPath destDestPath = dest;
|
||||
std::shared_ptr<VFS> destVFS = root;
|
||||
|
||||
GetFS(src, srcDestRoot, srcDestPath, srcVFS);
|
||||
GetFS(dest, destDestRoot, destDestPath, destVFS);
|
||||
|
||||
if(srcVFS != nullptr && srcVFS == destVFS)
|
||||
srcVFS->MoveDirectory(srcDestPath, destDestPath);
|
||||
}
|
||||
void MountableFilesystem::MoveFile(VFSPath src, VFSPath dest)
|
||||
{
|
||||
src = src.CollapseRelativeParents();
|
||||
dest = dest.CollapseRelativeParents();
|
||||
|
||||
VFSPath srcDestRoot;
|
||||
VFSPath srcDestPath = src;
|
||||
std::shared_ptr<VFS> srcVFS = root;
|
||||
VFSPath destDestRoot;
|
||||
VFSPath destDestPath = dest;
|
||||
std::shared_ptr<VFS> destVFS = root;
|
||||
|
||||
GetFS(src, srcDestRoot, srcDestPath, srcVFS);
|
||||
GetFS(dest, destDestRoot, destDestPath, destVFS);
|
||||
|
||||
if(srcVFS != nullptr && srcVFS == destVFS)
|
||||
srcVFS->MoveFile(srcDestPath, destDestPath);
|
||||
}
|
||||
void MountableFilesystem::CreateHardlink(VFSPath existingFile, VFSPath newName)
|
||||
{
|
||||
existingFile = existingFile.CollapseRelativeParents();
|
||||
newName = existingFile.CollapseRelativeParents();
|
||||
|
||||
VFSPath existingDestRoot;
|
||||
VFSPath existingDestPath = existingFile;
|
||||
std::shared_ptr<VFS> existingVFS = root;
|
||||
VFSPath newNameRoot;
|
||||
VFSPath newNamePath = newName;
|
||||
std::shared_ptr<VFS> newNameVFS = root;
|
||||
|
||||
GetFS(existingFile, existingDestRoot, existingDestPath, existingVFS);
|
||||
GetFS(newName, newNameRoot, newNamePath, newNameVFS);
|
||||
|
||||
if(existingVFS != nullptr && existingVFS == newNameVFS)
|
||||
existingVFS->CreateHardlink(existingDestPath, newNamePath);
|
||||
}
|
||||
class MountableEnumerationState {
|
||||
public:
|
||||
VFSPathEnumerator* enumerator;
|
||||
std::vector<MountableDirectory*> dirs;
|
||||
size_t index;
|
||||
};
|
||||
VFSPathEnumerator MountableFilesystem::EnumeratePaths(VFSPath path)
|
||||
{
|
||||
|
||||
path = path.CollapseRelativeParents();
|
||||
bool mydirs = path.path.empty();
|
||||
std::vector<MountableDirectory*>* dirs = &this->directories;
|
||||
|
||||
if(!path.path.empty())
|
||||
for(auto p : path.path)
|
||||
{
|
||||
mydirs=true;
|
||||
bool hasSet=false;
|
||||
|
||||
for(auto itm : *dirs)
|
||||
{
|
||||
if(itm->name == p)
|
||||
{
|
||||
hasSet=true;
|
||||
}
|
||||
|
||||
VFSPath MountableFilesystem::ReadLink(VFSPath path) {
|
||||
path = path.CollapseRelativeParents();
|
||||
VFSPath destRoot;
|
||||
VFSPath destPath = path;
|
||||
std::shared_ptr<VFS> vfs = root;
|
||||
|
||||
GetFS(path, destRoot, destPath, vfs);
|
||||
if (vfs != nullptr)
|
||||
return VFSPath(destRoot, vfs->ReadLink(destPath));
|
||||
return VFSPath();
|
||||
}
|
||||
|
||||
bool MountableFilesystem::StatVFS(VFSPath path, StatVFSData &data) {
|
||||
path = path.CollapseRelativeParents();
|
||||
VFSPath destRoot;
|
||||
VFSPath destPath = path;
|
||||
std::shared_ptr<VFS> vfs = root;
|
||||
|
||||
GetFS(path, destRoot, destPath, vfs);
|
||||
if (vfs != nullptr)
|
||||
return vfs->StatVFS(destPath, data);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MountableFilesystem::Stat(VFSPath path, StatData &data) {
|
||||
path = path.CollapseRelativeParents();
|
||||
VFSPath destRoot;
|
||||
VFSPath destPath = path;
|
||||
std::shared_ptr<VFS> vfs = root;
|
||||
|
||||
GetFS(path, destRoot, destPath, vfs);
|
||||
if (vfs != nullptr)
|
||||
return vfs->Stat(destPath, data);
|
||||
return false;
|
||||
}
|
||||
|
||||
void MountableFilesystem::Chmod(VFSPath path, uint32_t mode) {
|
||||
path = path.CollapseRelativeParents();
|
||||
VFSPath destRoot;
|
||||
VFSPath destPath = path;
|
||||
std::shared_ptr<VFS> vfs = root;
|
||||
|
||||
GetFS(path, destRoot, destPath, vfs);
|
||||
if (vfs != nullptr)
|
||||
vfs->Chmod(destPath, mode);
|
||||
}
|
||||
|
||||
void MountableFilesystem::Chown(VFSPath path, uint32_t uid, uint32_t gid) {
|
||||
path = path.CollapseRelativeParents();
|
||||
VFSPath destRoot;
|
||||
VFSPath destPath = path;
|
||||
std::shared_ptr<VFS> vfs = root;
|
||||
|
||||
GetFS(path, destRoot, destPath, vfs);
|
||||
if (vfs != nullptr)
|
||||
vfs->Chown(destPath, uid, gid);
|
||||
}
|
||||
|
||||
FIFOCreationResult MountableFilesystem::CreateFIFO(VFSPath path, uint32_t mod) {
|
||||
path = path.CollapseRelativeParents();
|
||||
VFSPath destRoot;
|
||||
VFSPath destPath = path;
|
||||
std::shared_ptr<VFS> vfs = root;
|
||||
|
||||
GetFS(path, destRoot, destPath, vfs);
|
||||
if (vfs != nullptr)
|
||||
return vfs->CreateFIFO(destPath, mod);
|
||||
return FIFOCreationResult::UnknownError;
|
||||
}
|
||||
|
||||
std::shared_ptr<Tesses::Framework::Streams::Stream>
|
||||
MountableFilesystem::OpenFile(VFSPath path, std::string mode) {
|
||||
path = path.CollapseRelativeParents();
|
||||
VFSPath destRoot;
|
||||
VFSPath destPath = path;
|
||||
std::shared_ptr<VFS> vfs = root;
|
||||
|
||||
GetFS(path, destRoot, destPath, vfs);
|
||||
|
||||
if (vfs != nullptr)
|
||||
return vfs->OpenFile(destPath, mode);
|
||||
return nullptr;
|
||||
}
|
||||
void MountableFilesystem::CreateDirectory(VFSPath path) {
|
||||
path = path.CollapseRelativeParents();
|
||||
|
||||
VFSPath destRoot;
|
||||
VFSPath destPath = path;
|
||||
std::shared_ptr<VFS> vfs = root;
|
||||
|
||||
GetFS(path, destRoot, destPath, vfs);
|
||||
|
||||
if (destPath.path.empty())
|
||||
return;
|
||||
|
||||
if (vfs != nullptr)
|
||||
vfs->CreateDirectory(destPath);
|
||||
}
|
||||
|
||||
void MountableFilesystem::DeleteDirectory(VFSPath path) {
|
||||
path = path.CollapseRelativeParents();
|
||||
|
||||
VFSPath destRoot;
|
||||
VFSPath destPath = path;
|
||||
std::shared_ptr<VFS> vfs = root;
|
||||
|
||||
GetFS(path, destRoot, destPath, vfs);
|
||||
|
||||
if (destPath.path.empty())
|
||||
return;
|
||||
|
||||
if (vfs != nullptr)
|
||||
vfs->DeleteDirectory(destPath);
|
||||
}
|
||||
|
||||
void MountableFilesystem::DeleteFile(VFSPath path) {
|
||||
path = path.CollapseRelativeParents();
|
||||
|
||||
VFSPath destRoot;
|
||||
VFSPath destPath = path;
|
||||
std::shared_ptr<VFS> vfs = root;
|
||||
|
||||
GetFS(path, destRoot, destPath, vfs);
|
||||
|
||||
if (vfs != nullptr)
|
||||
vfs->DeleteFile(destPath);
|
||||
}
|
||||
void MountableFilesystem::Lock(VFSPath path) {
|
||||
path = path.CollapseRelativeParents();
|
||||
|
||||
VFSPath destRoot;
|
||||
VFSPath destPath = path;
|
||||
std::shared_ptr<VFS> vfs = root;
|
||||
|
||||
GetFS(path, destRoot, destPath, vfs);
|
||||
|
||||
if (vfs != nullptr)
|
||||
vfs->Lock(destPath);
|
||||
}
|
||||
void MountableFilesystem::Unlock(VFSPath path) {
|
||||
path = path.CollapseRelativeParents();
|
||||
|
||||
VFSPath destRoot;
|
||||
VFSPath destPath = path;
|
||||
std::shared_ptr<VFS> vfs = root;
|
||||
|
||||
GetFS(path, destRoot, destPath, vfs);
|
||||
|
||||
if (vfs != nullptr)
|
||||
vfs->Unlock(destPath);
|
||||
}
|
||||
void MountableFilesystem::SetDate(VFSPath path, Date::DateTime lastWrite,
|
||||
Date::DateTime lastAccess) {
|
||||
|
||||
path = path.CollapseRelativeParents();
|
||||
|
||||
VFSPath destRoot;
|
||||
VFSPath destPath = path;
|
||||
std::shared_ptr<VFS> vfs = root;
|
||||
|
||||
GetFS(path, destRoot, destPath, vfs);
|
||||
|
||||
if (vfs != nullptr)
|
||||
vfs->SetDate(destPath, lastWrite, lastAccess);
|
||||
}
|
||||
void MountableFilesystem::CreateSymlink(VFSPath existingFile,
|
||||
VFSPath symlinkFile) {
|
||||
existingFile = existingFile.CollapseRelativeParents();
|
||||
symlinkFile = existingFile.CollapseRelativeParents();
|
||||
|
||||
VFSPath existingDestRoot;
|
||||
VFSPath existingDestPath = existingFile;
|
||||
std::shared_ptr<VFS> existingVFS = root;
|
||||
VFSPath symlinkDestRoot;
|
||||
VFSPath symlinkDestPath = symlinkFile;
|
||||
std::shared_ptr<VFS> symlinkVFS = root;
|
||||
|
||||
GetFS(existingFile, existingDestRoot, existingDestPath, existingVFS);
|
||||
GetFS(symlinkFile, symlinkDestRoot, symlinkDestPath, symlinkVFS);
|
||||
|
||||
if (existingVFS != nullptr && existingVFS == symlinkVFS)
|
||||
existingVFS->CreateSymlink(existingDestPath, symlinkDestPath);
|
||||
}
|
||||
|
||||
void MountableFilesystem::MoveDirectory(VFSPath src, VFSPath dest) {
|
||||
src = src.CollapseRelativeParents();
|
||||
dest = dest.CollapseRelativeParents();
|
||||
|
||||
VFSPath srcDestRoot;
|
||||
VFSPath srcDestPath = src;
|
||||
std::shared_ptr<VFS> srcVFS = root;
|
||||
VFSPath destDestRoot;
|
||||
VFSPath destDestPath = dest;
|
||||
std::shared_ptr<VFS> destVFS = root;
|
||||
|
||||
GetFS(src, srcDestRoot, srcDestPath, srcVFS);
|
||||
GetFS(dest, destDestRoot, destDestPath, destVFS);
|
||||
|
||||
if (srcVFS != nullptr && srcVFS == destVFS)
|
||||
srcVFS->MoveDirectory(srcDestPath, destDestPath);
|
||||
}
|
||||
void MountableFilesystem::MoveFile(VFSPath src, VFSPath dest) {
|
||||
src = src.CollapseRelativeParents();
|
||||
dest = dest.CollapseRelativeParents();
|
||||
|
||||
VFSPath srcDestRoot;
|
||||
VFSPath srcDestPath = src;
|
||||
std::shared_ptr<VFS> srcVFS = root;
|
||||
VFSPath destDestRoot;
|
||||
VFSPath destDestPath = dest;
|
||||
std::shared_ptr<VFS> destVFS = root;
|
||||
|
||||
GetFS(src, srcDestRoot, srcDestPath, srcVFS);
|
||||
GetFS(dest, destDestRoot, destDestPath, destVFS);
|
||||
|
||||
if (srcVFS != nullptr && srcVFS == destVFS)
|
||||
srcVFS->MoveFile(srcDestPath, destDestPath);
|
||||
}
|
||||
void MountableFilesystem::CreateHardlink(VFSPath existingFile,
|
||||
VFSPath newName) {
|
||||
existingFile = existingFile.CollapseRelativeParents();
|
||||
newName = existingFile.CollapseRelativeParents();
|
||||
|
||||
VFSPath existingDestRoot;
|
||||
VFSPath existingDestPath = existingFile;
|
||||
std::shared_ptr<VFS> existingVFS = root;
|
||||
VFSPath newNameRoot;
|
||||
VFSPath newNamePath = newName;
|
||||
std::shared_ptr<VFS> newNameVFS = root;
|
||||
|
||||
GetFS(existingFile, existingDestRoot, existingDestPath, existingVFS);
|
||||
GetFS(newName, newNameRoot, newNamePath, newNameVFS);
|
||||
|
||||
if (existingVFS != nullptr && existingVFS == newNameVFS)
|
||||
existingVFS->CreateHardlink(existingDestPath, newNamePath);
|
||||
}
|
||||
class MountableEnumerationState {
|
||||
public:
|
||||
VFSPathEnumerator *enumerator;
|
||||
std::vector<MountableDirectory *> dirs;
|
||||
size_t index;
|
||||
};
|
||||
VFSPathEnumerator MountableFilesystem::EnumeratePaths(VFSPath path) {
|
||||
|
||||
path = path.CollapseRelativeParents();
|
||||
bool mydirs = path.path.empty();
|
||||
std::vector<MountableDirectory *> *dirs = &this->directories;
|
||||
|
||||
if (!path.path.empty())
|
||||
for (auto p : path.path) {
|
||||
mydirs = true;
|
||||
bool hasSet = false;
|
||||
|
||||
for (auto itm : *dirs) {
|
||||
if (itm->name == p) {
|
||||
hasSet = true;
|
||||
dirs = &itm->dirs;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!hasSet)
|
||||
{
|
||||
mydirs=false;
|
||||
if (!hasSet) {
|
||||
mydirs = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
VFSPath destRoot;
|
||||
VFSPath destPath = path;
|
||||
std::shared_ptr<VFS> vfs = root;
|
||||
VFSPath destRoot;
|
||||
VFSPath destPath = path;
|
||||
std::shared_ptr<VFS> vfs = root;
|
||||
|
||||
GetFS(path, destRoot, destPath, vfs);
|
||||
GetFS(path, destRoot, destPath, vfs);
|
||||
|
||||
|
||||
MountableEnumerationState* state = new MountableEnumerationState();
|
||||
state->dirs = *dirs;
|
||||
state->index = 0;
|
||||
if(vfs->DirectoryExists(destPath) || !mydirs)
|
||||
MountableEnumerationState *state = new MountableEnumerationState();
|
||||
state->dirs = *dirs;
|
||||
state->index = 0;
|
||||
if (vfs->DirectoryExists(destPath) || !mydirs)
|
||||
state->enumerator = vfs->EnumeratePaths(destPath).MakePointer();
|
||||
else
|
||||
else
|
||||
state->enumerator = nullptr;
|
||||
|
||||
return VFSPathEnumerator([state,path](VFSPath& path0)->bool{
|
||||
|
||||
while(state->enumerator != nullptr && state->enumerator->MoveNext())
|
||||
{
|
||||
return VFSPathEnumerator(
|
||||
[state, path](VFSPath &path0) -> bool {
|
||||
while (state->enumerator != nullptr &&
|
||||
state->enumerator->MoveNext()) {
|
||||
auto fname = state->enumerator->Current.GetFileName();
|
||||
|
||||
bool mustContinue=false;
|
||||
for(auto item : state->dirs)
|
||||
{
|
||||
if(item->name == fname)
|
||||
{
|
||||
mustContinue=true;
|
||||
bool mustContinue = false;
|
||||
for (auto item : state->dirs) {
|
||||
if (item->name == fname) {
|
||||
mustContinue = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(mustContinue) continue;
|
||||
if (mustContinue)
|
||||
continue;
|
||||
path0 = path / fname;
|
||||
return true;
|
||||
}
|
||||
if(state->enumerator != nullptr)
|
||||
{
|
||||
if (state->enumerator != nullptr) {
|
||||
delete state->enumerator;
|
||||
state->enumerator = nullptr;
|
||||
}
|
||||
if(state->index < state->dirs.size())
|
||||
{
|
||||
path0 = path / state->dirs[state->index++]->name;
|
||||
if (state->index < state->dirs.size()) {
|
||||
path0 = path / state->dirs[state->index++]->name;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},[state]()->void{
|
||||
if(state->enumerator) delete state->enumerator;
|
||||
},
|
||||
[state]() -> void {
|
||||
if (state->enumerator)
|
||||
delete state->enumerator;
|
||||
delete state;
|
||||
});
|
||||
}
|
||||
|
||||
void MountableFilesystem::Mount(VFSPath path, std::shared_ptr<VFS> fs) {
|
||||
path = path.CollapseRelativeParents();
|
||||
|
||||
if (path.path.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
void MountableFilesystem::Mount(VFSPath path, std::shared_ptr<VFS> fs)
|
||||
{
|
||||
path = path.CollapseRelativeParents();
|
||||
|
||||
if(path.path.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto* fsLs = &this->directories;
|
||||
bool needToCreate=true;
|
||||
for(auto index = path.path.begin(); index < path.path.end()-1; index++)
|
||||
{
|
||||
needToCreate=true;
|
||||
for(auto item : *fsLs)
|
||||
{
|
||||
if(item->name == *index)
|
||||
{
|
||||
needToCreate=false;
|
||||
fsLs = &(item->dirs);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(needToCreate)
|
||||
{
|
||||
MountableDirectory* dir = new MountableDirectory();
|
||||
dir->name = *index;
|
||||
dir->owns=false;
|
||||
dir->vfs=NULL;
|
||||
|
||||
fsLs->push_back(dir);
|
||||
fsLs = &(dir->dirs);
|
||||
}
|
||||
}
|
||||
|
||||
needToCreate=true;
|
||||
std::string lastDir = path.GetFileName();
|
||||
|
||||
for(auto item : *fsLs)
|
||||
{
|
||||
if(item->name == lastDir)
|
||||
{
|
||||
needToCreate=false;
|
||||
|
||||
item->vfs = fs;
|
||||
auto *fsLs = &this->directories;
|
||||
bool needToCreate = true;
|
||||
for (auto index = path.path.begin(); index < path.path.end() - 1; index++) {
|
||||
needToCreate = true;
|
||||
for (auto item : *fsLs) {
|
||||
if (item->name == *index) {
|
||||
needToCreate = false;
|
||||
fsLs = &(item->dirs);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (needToCreate) {
|
||||
MountableDirectory *dir = new MountableDirectory();
|
||||
dir->name = *index;
|
||||
dir->owns = false;
|
||||
dir->vfs = NULL;
|
||||
|
||||
if(needToCreate)
|
||||
{
|
||||
MountableDirectory* dir = new MountableDirectory();
|
||||
dir->name = lastDir;
|
||||
|
||||
dir->vfs=fs;
|
||||
fsLs->push_back(dir);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static bool myumount(MountableDirectory* dir,VFSPath path)
|
||||
{
|
||||
if(path.path.empty())
|
||||
{
|
||||
dir->vfs = nullptr;
|
||||
}
|
||||
|
||||
if(dir->dirs.empty())
|
||||
{
|
||||
delete dir;
|
||||
return true;
|
||||
}
|
||||
|
||||
for(auto index = dir->dirs.begin(); index < dir->dirs.end(); index++)
|
||||
{
|
||||
auto item = *index;
|
||||
if(!path.path.empty() && path.path.front() == item->name)
|
||||
{
|
||||
VFSPath srcPath2(std::vector(path.path.begin()+1,path.path.end()));
|
||||
|
||||
|
||||
|
||||
if(myumount(item,srcPath2))
|
||||
{
|
||||
dir->dirs.erase(index);
|
||||
}
|
||||
|
||||
if(dir->dirs.empty())
|
||||
{
|
||||
delete dir;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MountableFilesystem::Unmount(VFSPath path)
|
||||
{
|
||||
path = path.CollapseRelativeParents();
|
||||
|
||||
for(auto index = this->directories.begin(); index < this->directories.end(); index++)
|
||||
{
|
||||
auto item = *index;
|
||||
if(!path.path.empty() && path.path.front() == item->name)
|
||||
{
|
||||
VFSPath srcPath2(std::vector(path.path.begin()+1,path.path.end()));
|
||||
|
||||
if(myumount(item,srcPath2))
|
||||
{
|
||||
this->directories.erase(index);
|
||||
}
|
||||
return;
|
||||
}
|
||||
fsLs->push_back(dir);
|
||||
fsLs = &(dir->dirs);
|
||||
}
|
||||
}
|
||||
|
||||
std::string MountableFilesystem::VFSPathToSystem(VFSPath path)
|
||||
{
|
||||
return path.ToString();
|
||||
needToCreate = true;
|
||||
std::string lastDir = path.GetFileName();
|
||||
|
||||
for (auto item : *fsLs) {
|
||||
if (item->name == lastDir) {
|
||||
needToCreate = false;
|
||||
|
||||
item->vfs = fs;
|
||||
break;
|
||||
}
|
||||
}
|
||||
VFSPath MountableFilesystem::SystemToVFSPath(std::string path)
|
||||
{
|
||||
return VFSPath(path);
|
||||
|
||||
if (needToCreate) {
|
||||
MountableDirectory *dir = new MountableDirectory();
|
||||
dir->name = lastDir;
|
||||
|
||||
dir->vfs = fs;
|
||||
fsLs->push_back(dir);
|
||||
}
|
||||
}
|
||||
|
||||
static bool myumount(MountableDirectory *dir, VFSPath path) {
|
||||
if (path.path.empty()) {
|
||||
dir->vfs = nullptr;
|
||||
}
|
||||
|
||||
if (dir->dirs.empty()) {
|
||||
delete dir;
|
||||
return true;
|
||||
}
|
||||
|
||||
for (auto index = dir->dirs.begin(); index < dir->dirs.end(); index++) {
|
||||
auto item = *index;
|
||||
if (!path.path.empty() && path.path.front() == item->name) {
|
||||
VFSPath srcPath2(
|
||||
std::vector(path.path.begin() + 1, path.path.end()));
|
||||
|
||||
if (myumount(item, srcPath2)) {
|
||||
dir->dirs.erase(index);
|
||||
}
|
||||
|
||||
if (dir->dirs.empty()) {
|
||||
delete dir;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MountableFilesystem::Unmount(VFSPath path) {
|
||||
path = path.CollapseRelativeParents();
|
||||
|
||||
for (auto index = this->directories.begin();
|
||||
index < this->directories.end(); index++) {
|
||||
auto item = *index;
|
||||
if (!path.path.empty() && path.path.front() == item->name) {
|
||||
VFSPath srcPath2(
|
||||
std::vector(path.path.begin() + 1, path.path.end()));
|
||||
|
||||
if (myumount(item, srcPath2)) {
|
||||
this->directories.erase(index);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string MountableFilesystem::VFSPathToSystem(VFSPath path) {
|
||||
return path.ToString();
|
||||
}
|
||||
VFSPath MountableFilesystem::SystemToVFSPath(std::string path) {
|
||||
return VFSPath(path);
|
||||
}
|
||||
} // namespace Tesses::Framework::Filesystem
|
||||
|
||||
@@ -1,28 +1,20 @@
|
||||
#include "TessesFramework/Filesystem/NullFilesystem.hpp"
|
||||
|
||||
namespace Tesses::Framework::Filesystem
|
||||
{
|
||||
std::shared_ptr<Tesses::Framework::Streams::Stream> NullFilesystem::OpenFile(VFSPath path, std::string mode)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
VFSPathEnumerator NullFilesystem::EnumeratePaths(VFSPath path)
|
||||
{
|
||||
return VFSPathEnumerator();
|
||||
}
|
||||
std::string NullFilesystem::VFSPathToSystem(VFSPath path)
|
||||
{
|
||||
return path.ToString();
|
||||
}
|
||||
VFSPath NullFilesystem::SystemToVFSPath(std::string path)
|
||||
{
|
||||
return VFSPath(path);
|
||||
}
|
||||
|
||||
bool NullFilesystem::Stat(VFSPath path, StatData& data)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
namespace Tesses::Framework::Filesystem {
|
||||
std::shared_ptr<Tesses::Framework::Streams::Stream>
|
||||
NullFilesystem::OpenFile(VFSPath path, std::string mode) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
VFSPathEnumerator NullFilesystem::EnumeratePaths(VFSPath path) {
|
||||
return VFSPathEnumerator();
|
||||
}
|
||||
std::string NullFilesystem::VFSPathToSystem(VFSPath path) {
|
||||
return path.ToString();
|
||||
}
|
||||
VFSPath NullFilesystem::SystemToVFSPath(std::string path) {
|
||||
return VFSPath(path);
|
||||
}
|
||||
|
||||
bool NullFilesystem::Stat(VFSPath path, StatData &data) { return false; }
|
||||
} // namespace Tesses::Framework::Filesystem
|
||||
|
||||
@@ -1,206 +1,163 @@
|
||||
#include "TessesFramework/Filesystem/RelativeFilesystem.hpp"
|
||||
|
||||
namespace Tesses::Framework::Filesystem
|
||||
{
|
||||
VFSPath RelativeFilesystem::ToParent(VFSPath path)
|
||||
{
|
||||
if(path.relative)
|
||||
{
|
||||
return path.MakeAbsolute(GetWorking());
|
||||
}
|
||||
else
|
||||
{
|
||||
return path;
|
||||
}
|
||||
namespace Tesses::Framework::Filesystem {
|
||||
VFSPath RelativeFilesystem::ToParent(VFSPath path) {
|
||||
if (path.relative) {
|
||||
return path.MakeAbsolute(GetWorking());
|
||||
} else {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
RelativeFilesystem::RelativeFilesystem(std::shared_ptr<VFS> vfs, VFSPath working) : vfs(vfs), working(working)
|
||||
{
|
||||
RelativeFilesystem::RelativeFilesystem(std::shared_ptr<VFS> vfs,
|
||||
VFSPath working)
|
||||
: vfs(vfs), working(working) {}
|
||||
VFSPath RelativeFilesystem::GetWorking() {
|
||||
mtx.Lock();
|
||||
auto p = this->working;
|
||||
mtx.Unlock();
|
||||
return p;
|
||||
}
|
||||
void RelativeFilesystem::SetWorking(VFSPath path) {
|
||||
mtx.Lock();
|
||||
this->working = path;
|
||||
mtx.Unlock();
|
||||
}
|
||||
std::shared_ptr<VFS> RelativeFilesystem::GetVFS() { return vfs; }
|
||||
|
||||
}
|
||||
VFSPath RelativeFilesystem::GetWorking()
|
||||
{
|
||||
mtx.Lock();
|
||||
auto p = this->working;
|
||||
mtx.Unlock();
|
||||
return p;
|
||||
}
|
||||
void RelativeFilesystem::SetWorking(VFSPath path)
|
||||
{
|
||||
mtx.Lock();
|
||||
this->working=path;
|
||||
mtx.Unlock();
|
||||
}
|
||||
std::shared_ptr<VFS> RelativeFilesystem::GetVFS()
|
||||
{
|
||||
return vfs;
|
||||
}
|
||||
std::shared_ptr<Tesses::Framework::Streams::Stream>
|
||||
RelativeFilesystem::OpenFile(VFSPath path, std::string mode) {
|
||||
return this->vfs->OpenFile(ToParent(path), mode);
|
||||
}
|
||||
void RelativeFilesystem::CreateDirectory(VFSPath path) {
|
||||
this->vfs->CreateDirectory(ToParent(path));
|
||||
}
|
||||
void RelativeFilesystem::DeleteDirectory(VFSPath path) {
|
||||
this->vfs->DeleteDirectory(ToParent(path));
|
||||
}
|
||||
void RelativeFilesystem::DeleteFile(VFSPath path) {
|
||||
this->vfs->DeleteFile(ToParent(path));
|
||||
}
|
||||
void RelativeFilesystem::CreateSymlink(VFSPath existingFile,
|
||||
VFSPath symlinkFile) {
|
||||
this->vfs->CreateSymlink(existingFile, ToParent(symlinkFile));
|
||||
}
|
||||
VFSPathEnumerator RelativeFilesystem::EnumeratePaths(VFSPath path) {
|
||||
VFSPathEnumerator *enumerator =
|
||||
this->vfs->EnumeratePaths(ToParent(path)).MakePointer();
|
||||
|
||||
std::shared_ptr<Tesses::Framework::Streams::Stream> RelativeFilesystem::OpenFile(VFSPath path, std::string mode)
|
||||
{
|
||||
return this->vfs->OpenFile(ToParent(path),mode);
|
||||
}
|
||||
void RelativeFilesystem::CreateDirectory(VFSPath path)
|
||||
{
|
||||
this->vfs->CreateDirectory(ToParent(path));
|
||||
}
|
||||
void RelativeFilesystem::DeleteDirectory(VFSPath path)
|
||||
{
|
||||
this->vfs->DeleteDirectory(ToParent(path));
|
||||
}
|
||||
void RelativeFilesystem::DeleteFile(VFSPath path)
|
||||
{
|
||||
this->vfs->DeleteFile(ToParent(path));
|
||||
}
|
||||
void RelativeFilesystem::CreateSymlink(VFSPath existingFile, VFSPath symlinkFile)
|
||||
{
|
||||
this->vfs->CreateSymlink(existingFile, ToParent(symlinkFile));
|
||||
}
|
||||
VFSPathEnumerator RelativeFilesystem::EnumeratePaths(VFSPath path)
|
||||
{
|
||||
VFSPathEnumerator* enumerator = this->vfs->EnumeratePaths(ToParent(path)).MakePointer();
|
||||
|
||||
return VFSPathEnumerator([enumerator,path,this](VFSPath& path0)->bool{
|
||||
if(enumerator->MoveNext())
|
||||
{
|
||||
return VFSPathEnumerator(
|
||||
[enumerator, path, this](VFSPath &path0) -> bool {
|
||||
if (enumerator->MoveNext()) {
|
||||
path0 = path / enumerator->Current.GetFileName();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},[enumerator]()->void{
|
||||
delete enumerator;
|
||||
});
|
||||
}
|
||||
void RelativeFilesystem::CreateHardlink(VFSPath existingFile, VFSPath newName)
|
||||
{
|
||||
if(existingFile.relative || newName.relative)
|
||||
{
|
||||
auto working = GetWorking();
|
||||
if(existingFile.relative)
|
||||
existingFile = existingFile.MakeAbsolute(working);
|
||||
|
||||
if(newName.relative)
|
||||
newName = newName.MakeAbsolute(working);
|
||||
}
|
||||
vfs->CreateHardlink(existingFile, newName);
|
||||
}
|
||||
void RelativeFilesystem::MoveFile(VFSPath src, VFSPath dest)
|
||||
{
|
||||
if(src.relative || dest.relative)
|
||||
{
|
||||
auto working = GetWorking();
|
||||
if(src.relative)
|
||||
src = src.MakeAbsolute(working);
|
||||
|
||||
if(dest.relative)
|
||||
dest = dest.MakeAbsolute(working);
|
||||
}
|
||||
vfs->MoveFile(src, dest);
|
||||
}
|
||||
void RelativeFilesystem::MoveDirectory(VFSPath src, VFSPath dest)
|
||||
{
|
||||
if(src.relative || dest.relative)
|
||||
{
|
||||
auto working = GetWorking();
|
||||
if(src.relative)
|
||||
src = src.MakeAbsolute(working);
|
||||
|
||||
if(dest.relative)
|
||||
dest = dest.MakeAbsolute(working);
|
||||
}
|
||||
vfs->MoveDirectory(src, dest);
|
||||
}
|
||||
void RelativeFilesystem::DeleteDirectoryRecurse(VFSPath path)
|
||||
{
|
||||
vfs->DeleteDirectoryRecurse(ToParent(path));
|
||||
}
|
||||
VFSPath RelativeFilesystem::ReadLink(VFSPath path)
|
||||
{
|
||||
return vfs->ReadLink(ToParent(path));
|
||||
}
|
||||
std::string RelativeFilesystem::VFSPathToSystem(VFSPath path)
|
||||
{
|
||||
return vfs->VFSPathToSystem(path);
|
||||
}
|
||||
VFSPath RelativeFilesystem::SystemToVFSPath(std::string path)
|
||||
{
|
||||
return vfs->SystemToVFSPath(path);
|
||||
}
|
||||
void RelativeFilesystem::SetDate(VFSPath path, Date::DateTime lastWrite, Date::DateTime lastAccess)
|
||||
{
|
||||
vfs->SetDate(ToParent(path),lastWrite,lastAccess);
|
||||
}
|
||||
bool RelativeFilesystem::StatVFS(VFSPath path, StatVFSData& vfsData)
|
||||
{
|
||||
return vfs->StatVFS(ToParent(path), vfsData);
|
||||
}
|
||||
bool RelativeFilesystem::Stat(VFSPath path, StatData& data)
|
||||
{
|
||||
return vfs->Stat(ToParent(path),data);
|
||||
}
|
||||
},
|
||||
[enumerator]() -> void { delete enumerator; });
|
||||
}
|
||||
void RelativeFilesystem::CreateHardlink(VFSPath existingFile, VFSPath newName) {
|
||||
if (existingFile.relative || newName.relative) {
|
||||
auto working = GetWorking();
|
||||
if (existingFile.relative)
|
||||
existingFile = existingFile.MakeAbsolute(working);
|
||||
|
||||
void RelativeFilesystem::Chown(VFSPath path, uint32_t uid, uint32_t gid)
|
||||
{
|
||||
vfs->Chown(ToParent(path),uid,gid);
|
||||
if (newName.relative)
|
||||
newName = newName.MakeAbsolute(working);
|
||||
}
|
||||
void RelativeFilesystem::Chmod(VFSPath path, uint32_t mode)
|
||||
{
|
||||
vfs->Chmod(ToParent(path), mode);
|
||||
}
|
||||
FIFOCreationResult RelativeFilesystem::CreateFIFO(VFSPath path, uint32_t mode)
|
||||
{
|
||||
return vfs->CreateFIFO(ToParent(path),mode);
|
||||
}
|
||||
void RelativeFilesystem::Lock(VFSPath path)
|
||||
{
|
||||
vfs->Lock(ToParent(path));
|
||||
}
|
||||
void RelativeFilesystem::Unlock(VFSPath path)
|
||||
{
|
||||
vfs->Unlock(ToParent(path));
|
||||
}
|
||||
|
||||
vfs->CreateHardlink(existingFile, newName);
|
||||
}
|
||||
void RelativeFilesystem::MoveFile(VFSPath src, VFSPath dest) {
|
||||
if (src.relative || dest.relative) {
|
||||
auto working = GetWorking();
|
||||
if (src.relative)
|
||||
src = src.MakeAbsolute(working);
|
||||
|
||||
RelativeFilesystem::Watcher::Watcher(std::shared_ptr<RelativeFilesystem> vfs, VFSPath path) : FSWatcher(vfs, path)
|
||||
{
|
||||
this->watcher = FSWatcher::Create(vfs->vfs, vfs->ToParent(path));
|
||||
this->watcher->event = [vfs,this,path](FSWatcherEvent & evt)-> void{
|
||||
if(path.relative)
|
||||
{
|
||||
auto working = vfs->GetWorking();
|
||||
FSWatcherEvent e2=evt;
|
||||
if(evt.IsEvent(FSWatcherEventType::Moved))
|
||||
{
|
||||
e2.dest = e2.dest.MakeRelative(working);
|
||||
}
|
||||
e2.src = e2.src.MakeRelative(working);
|
||||
if (dest.relative)
|
||||
dest = dest.MakeAbsolute(working);
|
||||
}
|
||||
vfs->MoveFile(src, dest);
|
||||
}
|
||||
void RelativeFilesystem::MoveDirectory(VFSPath src, VFSPath dest) {
|
||||
if (src.relative || dest.relative) {
|
||||
auto working = GetWorking();
|
||||
if (src.relative)
|
||||
src = src.MakeAbsolute(working);
|
||||
|
||||
if(this->event) this->event(e2);
|
||||
if (dest.relative)
|
||||
dest = dest.MakeAbsolute(working);
|
||||
}
|
||||
vfs->MoveDirectory(src, dest);
|
||||
}
|
||||
void RelativeFilesystem::DeleteDirectoryRecurse(VFSPath path) {
|
||||
vfs->DeleteDirectoryRecurse(ToParent(path));
|
||||
}
|
||||
VFSPath RelativeFilesystem::ReadLink(VFSPath path) {
|
||||
return vfs->ReadLink(ToParent(path));
|
||||
}
|
||||
std::string RelativeFilesystem::VFSPathToSystem(VFSPath path) {
|
||||
return vfs->VFSPathToSystem(path);
|
||||
}
|
||||
VFSPath RelativeFilesystem::SystemToVFSPath(std::string path) {
|
||||
return vfs->SystemToVFSPath(path);
|
||||
}
|
||||
void RelativeFilesystem::SetDate(VFSPath path, Date::DateTime lastWrite,
|
||||
Date::DateTime lastAccess) {
|
||||
vfs->SetDate(ToParent(path), lastWrite, lastAccess);
|
||||
}
|
||||
bool RelativeFilesystem::StatVFS(VFSPath path, StatVFSData &vfsData) {
|
||||
return vfs->StatVFS(ToParent(path), vfsData);
|
||||
}
|
||||
bool RelativeFilesystem::Stat(VFSPath path, StatData &data) {
|
||||
return vfs->Stat(ToParent(path), data);
|
||||
}
|
||||
|
||||
void RelativeFilesystem::Chown(VFSPath path, uint32_t uid, uint32_t gid) {
|
||||
vfs->Chown(ToParent(path), uid, gid);
|
||||
}
|
||||
void RelativeFilesystem::Chmod(VFSPath path, uint32_t mode) {
|
||||
vfs->Chmod(ToParent(path), mode);
|
||||
}
|
||||
FIFOCreationResult RelativeFilesystem::CreateFIFO(VFSPath path, uint32_t mode) {
|
||||
return vfs->CreateFIFO(ToParent(path), mode);
|
||||
}
|
||||
void RelativeFilesystem::Lock(VFSPath path) { vfs->Lock(ToParent(path)); }
|
||||
void RelativeFilesystem::Unlock(VFSPath path) { vfs->Unlock(ToParent(path)); }
|
||||
|
||||
RelativeFilesystem::Watcher::Watcher(std::shared_ptr<RelativeFilesystem> vfs,
|
||||
VFSPath path)
|
||||
: FSWatcher(vfs, path) {
|
||||
this->watcher = FSWatcher::Create(vfs->vfs, vfs->ToParent(path));
|
||||
this->watcher->event = [vfs, this, path](FSWatcherEvent &evt) -> void {
|
||||
if (path.relative) {
|
||||
auto working = vfs->GetWorking();
|
||||
FSWatcherEvent e2 = evt;
|
||||
if (evt.IsEvent(FSWatcherEventType::Moved)) {
|
||||
e2.dest = e2.dest.MakeRelative(working);
|
||||
}
|
||||
else {
|
||||
if(this->event)
|
||||
this->event(evt);
|
||||
}
|
||||
};
|
||||
}
|
||||
e2.src = e2.src.MakeRelative(working);
|
||||
|
||||
void RelativeFilesystem::Watcher::SetEnabledImpl(bool enabled)
|
||||
{
|
||||
this->enabled = enabled;
|
||||
this->watcher->events = this->events;
|
||||
this->watcher->SetEnabled(enabled);
|
||||
}
|
||||
RelativeFilesystem::Watcher::~Watcher()
|
||||
{
|
||||
this->watcher->SetEnabled(false);
|
||||
}
|
||||
std::shared_ptr<FSWatcher> RelativeFilesystem::CreateWatcher(std::shared_ptr<VFS> vfs, VFSPath path)
|
||||
{
|
||||
auto sdfs = std::dynamic_pointer_cast<RelativeFilesystem>(vfs);
|
||||
if(sdfs)
|
||||
{
|
||||
return std::make_shared<Watcher>(sdfs,path);
|
||||
if (this->event)
|
||||
this->event(e2);
|
||||
} else {
|
||||
if (this->event)
|
||||
this->event(evt);
|
||||
}
|
||||
return VFS::CreateWatcher(vfs,path);
|
||||
};
|
||||
}
|
||||
|
||||
void RelativeFilesystem::Watcher::SetEnabledImpl(bool enabled) {
|
||||
this->enabled = enabled;
|
||||
this->watcher->events = this->events;
|
||||
this->watcher->SetEnabled(enabled);
|
||||
}
|
||||
RelativeFilesystem::Watcher::~Watcher() { this->watcher->SetEnabled(false); }
|
||||
std::shared_ptr<FSWatcher>
|
||||
RelativeFilesystem::CreateWatcher(std::shared_ptr<VFS> vfs, VFSPath path) {
|
||||
auto sdfs = std::dynamic_pointer_cast<RelativeFilesystem>(vfs);
|
||||
if (sdfs) {
|
||||
return std::make_shared<Watcher>(sdfs, path);
|
||||
}
|
||||
}
|
||||
return VFS::CreateWatcher(vfs, path);
|
||||
}
|
||||
} // namespace Tesses::Framework::Filesystem
|
||||
@@ -1,185 +1,150 @@
|
||||
#include "TessesFramework/Filesystem/SubdirFilesystem.hpp"
|
||||
#include "TessesFramework/Filesystem/LocalFS.hpp"
|
||||
#include <iostream>
|
||||
namespace Tesses::Framework::Filesystem
|
||||
{
|
||||
VFSPath SubdirFilesystem::ReadLink(VFSPath path)
|
||||
{
|
||||
return FromParent(this->parent->ReadLink(ToParent(path)));
|
||||
}
|
||||
VFSPath SubdirFilesystem::FromParent(VFSPath path)
|
||||
{
|
||||
// /a/b/c
|
||||
// /a/b/c
|
||||
VFSPath newPath;
|
||||
newPath.relative=false;
|
||||
namespace Tesses::Framework::Filesystem {
|
||||
VFSPath SubdirFilesystem::ReadLink(VFSPath path) {
|
||||
return FromParent(this->parent->ReadLink(ToParent(path)));
|
||||
}
|
||||
VFSPath SubdirFilesystem::FromParent(VFSPath path) {
|
||||
// /a/b/c
|
||||
// /a/b/c
|
||||
VFSPath newPath;
|
||||
newPath.relative = false;
|
||||
|
||||
if(path.path.size() >= this->path.path.size())
|
||||
{
|
||||
newPath.path.reserve(path.path.size()-this->path.path.size());
|
||||
for(size_t i = this->path.path.size(); i < path.path.size();i++)
|
||||
{
|
||||
newPath.path.push_back(path.path[i]);
|
||||
}
|
||||
if (path.path.size() >= this->path.path.size()) {
|
||||
newPath.path.reserve(path.path.size() - this->path.path.size());
|
||||
for (size_t i = this->path.path.size(); i < path.path.size(); i++) {
|
||||
newPath.path.push_back(path.path[i]);
|
||||
}
|
||||
return newPath;
|
||||
}
|
||||
return newPath;
|
||||
}
|
||||
|
||||
VFSPath SubdirFilesystem::ToParent(VFSPath path)
|
||||
{
|
||||
return this->path / path.CollapseRelativeParents();
|
||||
}
|
||||
SubdirFilesystem::SubdirFilesystem(std::shared_ptr<VFS> parent, VFSPath path)
|
||||
{
|
||||
this->parent = parent;
|
||||
if(path.relative && std::dynamic_pointer_cast<LocalFilesystem>(parent) != nullptr)
|
||||
{
|
||||
Tesses::Framework::Filesystem::LocalFilesystem lfs;
|
||||
auto curDir = std::filesystem::current_path();
|
||||
auto myPath = lfs.SystemToVFSPath(curDir.string()) / path;
|
||||
this->path = myPath.CollapseRelativeParents();
|
||||
}
|
||||
else
|
||||
VFSPath SubdirFilesystem::ToParent(VFSPath path) {
|
||||
return this->path / path.CollapseRelativeParents();
|
||||
}
|
||||
SubdirFilesystem::SubdirFilesystem(std::shared_ptr<VFS> parent, VFSPath path) {
|
||||
this->parent = parent;
|
||||
if (path.relative &&
|
||||
std::dynamic_pointer_cast<LocalFilesystem>(parent) != nullptr) {
|
||||
Tesses::Framework::Filesystem::LocalFilesystem lfs;
|
||||
auto curDir = std::filesystem::current_path();
|
||||
auto myPath = lfs.SystemToVFSPath(curDir.string()) / path;
|
||||
this->path = myPath.CollapseRelativeParents();
|
||||
} else
|
||||
this->path = path;
|
||||
}
|
||||
std::shared_ptr<Tesses::Framework::Streams::Stream>
|
||||
SubdirFilesystem::OpenFile(VFSPath path, std::string mode) {
|
||||
return this->parent->OpenFile(ToParent(path), mode);
|
||||
}
|
||||
void SubdirFilesystem::CreateDirectory(VFSPath path) {
|
||||
this->parent->CreateDirectory(ToParent(path));
|
||||
}
|
||||
void SubdirFilesystem::DeleteDirectory(VFSPath path) {
|
||||
this->parent->DeleteDirectory(ToParent(path));
|
||||
}
|
||||
void SubdirFilesystem::DeleteFile(VFSPath path) {
|
||||
this->parent->DeleteFile(ToParent(path));
|
||||
}
|
||||
void SubdirFilesystem::Lock(VFSPath path) {
|
||||
this->parent->Lock(ToParent(path));
|
||||
}
|
||||
|
||||
}
|
||||
std::shared_ptr<Tesses::Framework::Streams::Stream> SubdirFilesystem::OpenFile(VFSPath path, std::string mode)
|
||||
{
|
||||
return this->parent->OpenFile(ToParent(path),mode);
|
||||
}
|
||||
void SubdirFilesystem::CreateDirectory(VFSPath path)
|
||||
{
|
||||
this->parent->CreateDirectory(ToParent(path));
|
||||
}
|
||||
void SubdirFilesystem::DeleteDirectory(VFSPath path)
|
||||
{
|
||||
this->parent->DeleteDirectory(ToParent(path));
|
||||
}
|
||||
void SubdirFilesystem::DeleteFile(VFSPath path)
|
||||
{
|
||||
this->parent->DeleteFile(ToParent(path));
|
||||
}
|
||||
void SubdirFilesystem::Lock(VFSPath path)
|
||||
{
|
||||
this->parent->Lock(ToParent(path));
|
||||
}
|
||||
void SubdirFilesystem::Unlock(VFSPath path) {
|
||||
this->parent->Unlock(ToParent(path));
|
||||
}
|
||||
void SubdirFilesystem::CreateSymlink(VFSPath existingFile,
|
||||
VFSPath symlinkFile) {
|
||||
this->parent->CreateSymlink(ToParent(existingFile), ToParent(symlinkFile));
|
||||
}
|
||||
|
||||
void SubdirFilesystem::Unlock(VFSPath path)
|
||||
{
|
||||
this->parent->Unlock(ToParent(path));
|
||||
}
|
||||
void SubdirFilesystem::CreateSymlink(VFSPath existingFile, VFSPath symlinkFile)
|
||||
{
|
||||
this->parent->CreateSymlink(ToParent(existingFile),ToParent(symlinkFile));
|
||||
}
|
||||
VFSPathEnumerator SubdirFilesystem::EnumeratePaths(VFSPath path) {
|
||||
VFSPathEnumerator *enumerator =
|
||||
this->parent->EnumeratePaths(ToParent(path)).MakePointer();
|
||||
|
||||
VFSPathEnumerator SubdirFilesystem::EnumeratePaths(VFSPath path)
|
||||
{
|
||||
VFSPathEnumerator* enumerator = this->parent->EnumeratePaths(ToParent(path)).MakePointer();
|
||||
|
||||
return VFSPathEnumerator([enumerator,this](VFSPath& path0)->bool{
|
||||
if(enumerator->MoveNext())
|
||||
{
|
||||
return VFSPathEnumerator(
|
||||
[enumerator, this](VFSPath &path0) -> bool {
|
||||
if (enumerator->MoveNext()) {
|
||||
path0 = FromParent(enumerator->Current);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},[enumerator]()->void{
|
||||
delete enumerator;
|
||||
});
|
||||
}
|
||||
|
||||
void SubdirFilesystem::SetDate(VFSPath path, Date::DateTime lastWrite, Date::DateTime lastAccess)
|
||||
{
|
||||
this->parent->SetDate(ToParent(path),lastWrite,lastAccess);
|
||||
}
|
||||
|
||||
void SubdirFilesystem::CreateHardlink(VFSPath existingFile, VFSPath newName)
|
||||
{
|
||||
this->parent->CreateHardlink(ToParent(existingFile),ToParent(newName));
|
||||
}
|
||||
void SubdirFilesystem::MoveFile(VFSPath src, VFSPath dest)
|
||||
{
|
||||
this->parent->MoveFile(ToParent(src),ToParent(dest));
|
||||
}
|
||||
void SubdirFilesystem::MoveDirectory(VFSPath src, VFSPath dest)
|
||||
{
|
||||
this->parent->MoveDirectory(ToParent(src),ToParent(dest));
|
||||
}
|
||||
std::string SubdirFilesystem::VFSPathToSystem(VFSPath path)
|
||||
{
|
||||
return this->parent->VFSPathToSystem(ToParent(path));
|
||||
}
|
||||
VFSPath SubdirFilesystem::SystemToVFSPath(std::string path)
|
||||
{
|
||||
return FromParent(this->parent->SystemToVFSPath(path));
|
||||
}
|
||||
void SubdirFilesystem::DeleteDirectoryRecurse(VFSPath path)
|
||||
{
|
||||
this->parent->DeleteDirectoryRecurse(ToParent(path));
|
||||
}
|
||||
|
||||
|
||||
SubdirFilesystem::~SubdirFilesystem()
|
||||
{
|
||||
|
||||
}
|
||||
bool SubdirFilesystem::StatVFS(VFSPath path, StatVFSData& vfsData)
|
||||
{
|
||||
return this->parent->StatVFS(ToParent(path), vfsData);
|
||||
}
|
||||
bool SubdirFilesystem::Stat(VFSPath path, StatData& data)
|
||||
{
|
||||
return this->parent->Stat(ToParent(path), data);
|
||||
}
|
||||
void SubdirFilesystem::Chmod(VFSPath path, uint32_t mode)
|
||||
{
|
||||
return this->parent->Chmod(ToParent(path), mode);
|
||||
}
|
||||
void SubdirFilesystem::Chown(VFSPath path, uint32_t uid, uint32_t gid)
|
||||
{
|
||||
return this->parent->Chown(ToParent(path), uid, gid);
|
||||
}
|
||||
|
||||
|
||||
FIFOCreationResult SubdirFilesystem::CreateFIFO(VFSPath path, uint32_t mod)
|
||||
{
|
||||
return this->parent->CreateFIFO(path, mod);
|
||||
}
|
||||
|
||||
SubdirFilesystem::Watcher::Watcher(std::shared_ptr<SubdirFilesystem> vfs, VFSPath path) : FSWatcher(vfs, path)
|
||||
{
|
||||
this->watcher = FSWatcher::Create(vfs->parent, vfs->ToParent(path));
|
||||
this->watcher->event = [vfs,this](FSWatcherEvent & evt)-> void{
|
||||
FSWatcherEvent e2=evt;
|
||||
if(evt.IsEvent(FSWatcherEventType::Moved))
|
||||
{
|
||||
e2.dest = vfs->FromParent(e2.dest);
|
||||
}
|
||||
e2.src = vfs->FromParent(e2.src);
|
||||
|
||||
if(this->event) this->event(e2);
|
||||
};
|
||||
}
|
||||
|
||||
void SubdirFilesystem::Watcher::SetEnabledImpl(bool enabled)
|
||||
{
|
||||
this->enabled = enabled;
|
||||
this->watcher->events = this->events;
|
||||
this->watcher->SetEnabled(enabled);
|
||||
}
|
||||
SubdirFilesystem::Watcher::~Watcher()
|
||||
{
|
||||
this->watcher->SetEnabled(false);
|
||||
}
|
||||
std::shared_ptr<FSWatcher> SubdirFilesystem::CreateWatcher(std::shared_ptr<VFS> vfs, VFSPath path)
|
||||
{
|
||||
auto sdfs = std::dynamic_pointer_cast<SubdirFilesystem>(vfs);
|
||||
if(sdfs)
|
||||
{
|
||||
return std::make_shared<Watcher>(sdfs,path);
|
||||
}
|
||||
return VFS::CreateWatcher(vfs,path);
|
||||
}
|
||||
|
||||
},
|
||||
[enumerator]() -> void { delete enumerator; });
|
||||
}
|
||||
|
||||
void SubdirFilesystem::SetDate(VFSPath path, Date::DateTime lastWrite,
|
||||
Date::DateTime lastAccess) {
|
||||
this->parent->SetDate(ToParent(path), lastWrite, lastAccess);
|
||||
}
|
||||
|
||||
void SubdirFilesystem::CreateHardlink(VFSPath existingFile, VFSPath newName) {
|
||||
this->parent->CreateHardlink(ToParent(existingFile), ToParent(newName));
|
||||
}
|
||||
void SubdirFilesystem::MoveFile(VFSPath src, VFSPath dest) {
|
||||
this->parent->MoveFile(ToParent(src), ToParent(dest));
|
||||
}
|
||||
void SubdirFilesystem::MoveDirectory(VFSPath src, VFSPath dest) {
|
||||
this->parent->MoveDirectory(ToParent(src), ToParent(dest));
|
||||
}
|
||||
std::string SubdirFilesystem::VFSPathToSystem(VFSPath path) {
|
||||
return this->parent->VFSPathToSystem(ToParent(path));
|
||||
}
|
||||
VFSPath SubdirFilesystem::SystemToVFSPath(std::string path) {
|
||||
return FromParent(this->parent->SystemToVFSPath(path));
|
||||
}
|
||||
void SubdirFilesystem::DeleteDirectoryRecurse(VFSPath path) {
|
||||
this->parent->DeleteDirectoryRecurse(ToParent(path));
|
||||
}
|
||||
|
||||
SubdirFilesystem::~SubdirFilesystem() {}
|
||||
bool SubdirFilesystem::StatVFS(VFSPath path, StatVFSData &vfsData) {
|
||||
return this->parent->StatVFS(ToParent(path), vfsData);
|
||||
}
|
||||
bool SubdirFilesystem::Stat(VFSPath path, StatData &data) {
|
||||
return this->parent->Stat(ToParent(path), data);
|
||||
}
|
||||
void SubdirFilesystem::Chmod(VFSPath path, uint32_t mode) {
|
||||
return this->parent->Chmod(ToParent(path), mode);
|
||||
}
|
||||
void SubdirFilesystem::Chown(VFSPath path, uint32_t uid, uint32_t gid) {
|
||||
return this->parent->Chown(ToParent(path), uid, gid);
|
||||
}
|
||||
|
||||
FIFOCreationResult SubdirFilesystem::CreateFIFO(VFSPath path, uint32_t mod) {
|
||||
return this->parent->CreateFIFO(path, mod);
|
||||
}
|
||||
|
||||
SubdirFilesystem::Watcher::Watcher(std::shared_ptr<SubdirFilesystem> vfs,
|
||||
VFSPath path)
|
||||
: FSWatcher(vfs, path) {
|
||||
this->watcher = FSWatcher::Create(vfs->parent, vfs->ToParent(path));
|
||||
this->watcher->event = [vfs, this](FSWatcherEvent &evt) -> void {
|
||||
FSWatcherEvent e2 = evt;
|
||||
if (evt.IsEvent(FSWatcherEventType::Moved)) {
|
||||
e2.dest = vfs->FromParent(e2.dest);
|
||||
}
|
||||
e2.src = vfs->FromParent(e2.src);
|
||||
|
||||
if (this->event)
|
||||
this->event(e2);
|
||||
};
|
||||
}
|
||||
|
||||
void SubdirFilesystem::Watcher::SetEnabledImpl(bool enabled) {
|
||||
this->enabled = enabled;
|
||||
this->watcher->events = this->events;
|
||||
this->watcher->SetEnabled(enabled);
|
||||
}
|
||||
SubdirFilesystem::Watcher::~Watcher() { this->watcher->SetEnabled(false); }
|
||||
std::shared_ptr<FSWatcher>
|
||||
SubdirFilesystem::CreateWatcher(std::shared_ptr<VFS> vfs, VFSPath path) {
|
||||
auto sdfs = std::dynamic_pointer_cast<SubdirFilesystem>(vfs);
|
||||
if (sdfs) {
|
||||
return std::make_shared<Watcher>(sdfs, path);
|
||||
}
|
||||
return VFS::CreateWatcher(vfs, path);
|
||||
}
|
||||
|
||||
} // namespace Tesses::Framework::Filesystem
|
||||
|
||||
@@ -1,189 +1,183 @@
|
||||
#include "TessesFramework/Threading/Mutex.hpp"
|
||||
#include "TessesFramework/Filesystem/TempFS.hpp"
|
||||
#include "TessesFramework/Filesystem/LocalFS.hpp"
|
||||
#include "TessesFramework/Filesystem/SubdirFilesystem.hpp"
|
||||
#include "TessesFramework/Platform/Environment.hpp"
|
||||
#include "TessesFramework/Threading/Mutex.hpp"
|
||||
namespace Tesses::Framework::Filesystem {
|
||||
Tesses::Framework::Threading::Mutex umtx;
|
||||
int64_t uidx=0;
|
||||
void UniqueString(std::string& text)
|
||||
{
|
||||
umtx.Lock();
|
||||
text += std::to_string((int64_t)time(NULL));
|
||||
text += "_";
|
||||
text += std::to_string(uidx);
|
||||
Tesses::Framework::Threading::Mutex umtx;
|
||||
int64_t uidx = 0;
|
||||
void UniqueString(std::string &text) {
|
||||
umtx.Lock();
|
||||
text += std::to_string((int64_t)time(NULL));
|
||||
text += "_";
|
||||
text += std::to_string(uidx);
|
||||
|
||||
uidx++;
|
||||
|
||||
umtx.Unlock();
|
||||
}
|
||||
|
||||
TempFS::TempFS(bool deleteOnDestroy) : TempFS(std::make_shared<SubdirFilesystem>(LocalFS, Platform::Environment::SpecialFolders::GetTemp()), deleteOnDestroy)
|
||||
{
|
||||
|
||||
}
|
||||
TempFS::TempFS(std::shared_ptr<VFS> vfs,bool deleteOnDestroy)
|
||||
{
|
||||
this->parent = vfs;
|
||||
this->deleteOnDestroy=deleteOnDestroy;
|
||||
this->tmp_str = "tf_tmp_";
|
||||
UniqueString(this->tmp_str);
|
||||
VFSPath p;
|
||||
p.relative = false;
|
||||
p.path.push_back(this->tmp_str);
|
||||
this->parent->CreateDirectory(p);
|
||||
this->vfs = std::make_shared<SubdirFilesystem>(this->parent,p);
|
||||
}
|
||||
|
||||
std::string TempFS::TempDirectoryName()
|
||||
{
|
||||
return this->tmp_str;
|
||||
}
|
||||
std::shared_ptr<Tesses::Framework::Streams::Stream> TempFS::OpenFile(VFSPath path, std::string mode)
|
||||
{
|
||||
if(this->vfs == nullptr) return nullptr;
|
||||
return this->vfs->OpenFile(path,mode);
|
||||
}
|
||||
void TempFS::CreateDirectory(VFSPath path)
|
||||
{
|
||||
if(this->vfs == nullptr) return;
|
||||
this->vfs->CreateDirectory(path);
|
||||
}
|
||||
void TempFS::DeleteDirectory(VFSPath path)
|
||||
{
|
||||
if(this->vfs == nullptr) return;
|
||||
this->vfs->DeleteDirectory(path);
|
||||
}
|
||||
|
||||
void TempFS::DeleteFile(VFSPath path)
|
||||
{
|
||||
if(this->vfs == nullptr) return;
|
||||
this->vfs->DeleteFile(path);
|
||||
}
|
||||
void TempFS::Lock(VFSPath path)
|
||||
{
|
||||
if(this->vfs == nullptr) return;
|
||||
this->vfs->Lock(path);
|
||||
}
|
||||
void TempFS::Unlock(VFSPath path)
|
||||
{
|
||||
if(this->vfs == nullptr) return;
|
||||
this->vfs->Unlock(path);
|
||||
}
|
||||
void TempFS::CreateSymlink(VFSPath existingFile, VFSPath symlinkFile)
|
||||
{
|
||||
if(this->vfs == nullptr) return;
|
||||
this->vfs->CreateSymlink(existingFile, symlinkFile);
|
||||
}
|
||||
VFSPathEnumerator TempFS::EnumeratePaths(VFSPath path)
|
||||
{
|
||||
|
||||
if(this->vfs == nullptr) return VFSPathEnumerator();
|
||||
|
||||
return this->vfs->EnumeratePaths(path);
|
||||
}
|
||||
void TempFS::CreateHardlink(VFSPath existingFile, VFSPath newName)
|
||||
{
|
||||
|
||||
if(this->vfs == nullptr) return;
|
||||
this->vfs->CreateHardlink(existingFile,newName);
|
||||
}
|
||||
void TempFS::MoveFile(VFSPath src, VFSPath dest)
|
||||
{
|
||||
|
||||
if(this->vfs == nullptr) return;
|
||||
this->vfs->MoveFile(src,dest);
|
||||
}
|
||||
void TempFS::MoveDirectory(VFSPath src, VFSPath dest)
|
||||
{
|
||||
if(this->vfs == nullptr) return;
|
||||
this->vfs->MoveDirectory(src,dest);
|
||||
}
|
||||
void TempFS::DeleteDirectoryRecurse(VFSPath path)
|
||||
{
|
||||
|
||||
if(this->vfs == nullptr) return;
|
||||
this->vfs->DeleteDirectoryRecurse(path);
|
||||
}
|
||||
VFSPath TempFS::ReadLink(VFSPath path)
|
||||
{
|
||||
|
||||
if(this->vfs == nullptr) return VFSPath();
|
||||
return this->vfs->ReadLink(path);
|
||||
}
|
||||
std::string TempFS::VFSPathToSystem(VFSPath path)
|
||||
{
|
||||
|
||||
if(this->vfs == nullptr) return "";
|
||||
return this->vfs->VFSPathToSystem(path);
|
||||
}
|
||||
VFSPath TempFS::SystemToVFSPath(std::string path)
|
||||
{
|
||||
|
||||
if(this->vfs == nullptr) return VFSPath();
|
||||
return this->vfs->SystemToVFSPath(path);
|
||||
}
|
||||
|
||||
void TempFS::SetDate(VFSPath path, Date::DateTime lastWrite, Date::DateTime lastAccess)
|
||||
{
|
||||
|
||||
if(this->vfs == nullptr) return;
|
||||
this->vfs->SetDate(path,lastWrite,lastAccess);
|
||||
}
|
||||
bool TempFS::StatVFS(VFSPath path, StatVFSData& vfsData)
|
||||
{
|
||||
|
||||
if(this->vfs == nullptr) return false;
|
||||
return this->vfs->StatVFS(path, vfsData);
|
||||
}
|
||||
bool TempFS::Stat(VFSPath path, StatData& data)
|
||||
{
|
||||
|
||||
if(this->vfs == nullptr) return false;
|
||||
return this->vfs->Stat(path, data);
|
||||
}
|
||||
|
||||
void TempFS::Chmod(VFSPath path, uint32_t mode)
|
||||
{
|
||||
|
||||
if(this->vfs == nullptr) return;
|
||||
this->vfs->Chmod(path,mode);
|
||||
}
|
||||
void TempFS::Chown(VFSPath path, uint32_t uid, uint32_t gid)
|
||||
{
|
||||
|
||||
if(this->vfs == nullptr) return;
|
||||
this->vfs->Chown(path,uid, gid);
|
||||
}
|
||||
FIFOCreationResult TempFS::CreateFIFO(VFSPath path, uint32_t mod)
|
||||
{
|
||||
if(this->vfs == nullptr) return FIFOCreationResult::UnknownError;
|
||||
return this->vfs->CreateFIFO(path, mod);
|
||||
}
|
||||
void TempFS::Close()
|
||||
{
|
||||
|
||||
VFSPath p;
|
||||
p.relative = false;
|
||||
p.path.push_back(this->tmp_str);
|
||||
this->vfs = nullptr;
|
||||
if(this->deleteOnDestroy && this->parent->DirectoryExists(p))
|
||||
this->parent->DeleteDirectoryRecurse(p);
|
||||
}
|
||||
std::shared_ptr<FSWatcher> TempFS::CreateWatcher(std::shared_ptr<VFS> vfs, VFSPath path)
|
||||
{
|
||||
return FSWatcher::Create(vfs,path);
|
||||
}
|
||||
|
||||
TempFS::~TempFS()
|
||||
{
|
||||
VFSPath p;
|
||||
p.relative = false;
|
||||
p.path.push_back(this->tmp_str);
|
||||
this->vfs = nullptr;
|
||||
if(this->deleteOnDestroy && this->parent->DirectoryExists(p))
|
||||
this->parent->DeleteDirectoryRecurse(p);
|
||||
|
||||
}
|
||||
uidx++;
|
||||
|
||||
umtx.Unlock();
|
||||
}
|
||||
|
||||
TempFS::TempFS(bool deleteOnDestroy)
|
||||
: TempFS(std::make_shared<SubdirFilesystem>(
|
||||
LocalFS, Platform::Environment::SpecialFolders::GetTemp()),
|
||||
deleteOnDestroy) {}
|
||||
TempFS::TempFS(std::shared_ptr<VFS> vfs, bool deleteOnDestroy) {
|
||||
this->parent = vfs;
|
||||
this->deleteOnDestroy = deleteOnDestroy;
|
||||
this->tmp_str = "tf_tmp_";
|
||||
UniqueString(this->tmp_str);
|
||||
VFSPath p;
|
||||
p.relative = false;
|
||||
p.path.push_back(this->tmp_str);
|
||||
this->parent->CreateDirectory(p);
|
||||
this->vfs = std::make_shared<SubdirFilesystem>(this->parent, p);
|
||||
}
|
||||
|
||||
std::string TempFS::TempDirectoryName() { return this->tmp_str; }
|
||||
std::shared_ptr<Tesses::Framework::Streams::Stream>
|
||||
TempFS::OpenFile(VFSPath path, std::string mode) {
|
||||
if (this->vfs == nullptr)
|
||||
return nullptr;
|
||||
return this->vfs->OpenFile(path, mode);
|
||||
}
|
||||
void TempFS::CreateDirectory(VFSPath path) {
|
||||
if (this->vfs == nullptr)
|
||||
return;
|
||||
this->vfs->CreateDirectory(path);
|
||||
}
|
||||
void TempFS::DeleteDirectory(VFSPath path) {
|
||||
if (this->vfs == nullptr)
|
||||
return;
|
||||
this->vfs->DeleteDirectory(path);
|
||||
}
|
||||
|
||||
void TempFS::DeleteFile(VFSPath path) {
|
||||
if (this->vfs == nullptr)
|
||||
return;
|
||||
this->vfs->DeleteFile(path);
|
||||
}
|
||||
void TempFS::Lock(VFSPath path) {
|
||||
if (this->vfs == nullptr)
|
||||
return;
|
||||
this->vfs->Lock(path);
|
||||
}
|
||||
void TempFS::Unlock(VFSPath path) {
|
||||
if (this->vfs == nullptr)
|
||||
return;
|
||||
this->vfs->Unlock(path);
|
||||
}
|
||||
void TempFS::CreateSymlink(VFSPath existingFile, VFSPath symlinkFile) {
|
||||
if (this->vfs == nullptr)
|
||||
return;
|
||||
this->vfs->CreateSymlink(existingFile, symlinkFile);
|
||||
}
|
||||
VFSPathEnumerator TempFS::EnumeratePaths(VFSPath path) {
|
||||
|
||||
if (this->vfs == nullptr)
|
||||
return VFSPathEnumerator();
|
||||
|
||||
return this->vfs->EnumeratePaths(path);
|
||||
}
|
||||
void TempFS::CreateHardlink(VFSPath existingFile, VFSPath newName) {
|
||||
|
||||
if (this->vfs == nullptr)
|
||||
return;
|
||||
this->vfs->CreateHardlink(existingFile, newName);
|
||||
}
|
||||
void TempFS::MoveFile(VFSPath src, VFSPath dest) {
|
||||
|
||||
if (this->vfs == nullptr)
|
||||
return;
|
||||
this->vfs->MoveFile(src, dest);
|
||||
}
|
||||
void TempFS::MoveDirectory(VFSPath src, VFSPath dest) {
|
||||
if (this->vfs == nullptr)
|
||||
return;
|
||||
this->vfs->MoveDirectory(src, dest);
|
||||
}
|
||||
void TempFS::DeleteDirectoryRecurse(VFSPath path) {
|
||||
|
||||
if (this->vfs == nullptr)
|
||||
return;
|
||||
this->vfs->DeleteDirectoryRecurse(path);
|
||||
}
|
||||
VFSPath TempFS::ReadLink(VFSPath path) {
|
||||
|
||||
if (this->vfs == nullptr)
|
||||
return VFSPath();
|
||||
return this->vfs->ReadLink(path);
|
||||
}
|
||||
std::string TempFS::VFSPathToSystem(VFSPath path) {
|
||||
|
||||
if (this->vfs == nullptr)
|
||||
return "";
|
||||
return this->vfs->VFSPathToSystem(path);
|
||||
}
|
||||
VFSPath TempFS::SystemToVFSPath(std::string path) {
|
||||
|
||||
if (this->vfs == nullptr)
|
||||
return VFSPath();
|
||||
return this->vfs->SystemToVFSPath(path);
|
||||
}
|
||||
|
||||
void TempFS::SetDate(VFSPath path, Date::DateTime lastWrite,
|
||||
Date::DateTime lastAccess) {
|
||||
|
||||
if (this->vfs == nullptr)
|
||||
return;
|
||||
this->vfs->SetDate(path, lastWrite, lastAccess);
|
||||
}
|
||||
bool TempFS::StatVFS(VFSPath path, StatVFSData &vfsData) {
|
||||
|
||||
if (this->vfs == nullptr)
|
||||
return false;
|
||||
return this->vfs->StatVFS(path, vfsData);
|
||||
}
|
||||
bool TempFS::Stat(VFSPath path, StatData &data) {
|
||||
|
||||
if (this->vfs == nullptr)
|
||||
return false;
|
||||
return this->vfs->Stat(path, data);
|
||||
}
|
||||
|
||||
void TempFS::Chmod(VFSPath path, uint32_t mode) {
|
||||
|
||||
if (this->vfs == nullptr)
|
||||
return;
|
||||
this->vfs->Chmod(path, mode);
|
||||
}
|
||||
void TempFS::Chown(VFSPath path, uint32_t uid, uint32_t gid) {
|
||||
|
||||
if (this->vfs == nullptr)
|
||||
return;
|
||||
this->vfs->Chown(path, uid, gid);
|
||||
}
|
||||
FIFOCreationResult TempFS::CreateFIFO(VFSPath path, uint32_t mod) {
|
||||
if (this->vfs == nullptr)
|
||||
return FIFOCreationResult::UnknownError;
|
||||
return this->vfs->CreateFIFO(path, mod);
|
||||
}
|
||||
void TempFS::Close() {
|
||||
|
||||
VFSPath p;
|
||||
p.relative = false;
|
||||
p.path.push_back(this->tmp_str);
|
||||
this->vfs = nullptr;
|
||||
if (this->deleteOnDestroy && this->parent->DirectoryExists(p))
|
||||
this->parent->DeleteDirectoryRecurse(p);
|
||||
}
|
||||
std::shared_ptr<FSWatcher> TempFS::CreateWatcher(std::shared_ptr<VFS> vfs,
|
||||
VFSPath path) {
|
||||
return FSWatcher::Create(vfs, path);
|
||||
}
|
||||
|
||||
TempFS::~TempFS() {
|
||||
VFSPath p;
|
||||
p.relative = false;
|
||||
p.path.push_back(this->tmp_str);
|
||||
this->vfs = nullptr;
|
||||
if (this->deleteOnDestroy && this->parent->DirectoryExists(p))
|
||||
this->parent->DeleteDirectoryRecurse(p);
|
||||
}
|
||||
|
||||
} // namespace Tesses::Framework::Filesystem
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,30 +1,36 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/HiddenField.hpp"
|
||||
|
||||
namespace Tesses::Framework
|
||||
{
|
||||
namespace Tesses::Framework {
|
||||
|
||||
HiddenFieldData::~HiddenFieldData()
|
||||
{
|
||||
|
||||
}
|
||||
HiddenFieldData::~HiddenFieldData() {}
|
||||
|
||||
|
||||
|
||||
HiddenField::HiddenField()
|
||||
{
|
||||
this->ptr = nullptr;
|
||||
}
|
||||
HiddenField::HiddenField(HiddenFieldData* data)
|
||||
{
|
||||
HiddenField::HiddenField() { this->ptr = nullptr; }
|
||||
HiddenField::HiddenField(HiddenFieldData *data) { this->ptr = data; }
|
||||
void HiddenField::SetField(HiddenFieldData *data) {
|
||||
if (this->ptr != nullptr)
|
||||
delete this->ptr;
|
||||
this->ptr = data;
|
||||
}
|
||||
void HiddenField::SetField(HiddenFieldData* data)
|
||||
{
|
||||
if(this->ptr != nullptr) delete this->ptr;
|
||||
this->ptr = data;
|
||||
HiddenField::~HiddenField() {
|
||||
if (this->ptr != nullptr)
|
||||
delete this->ptr;
|
||||
}
|
||||
HiddenField::~HiddenField()
|
||||
{
|
||||
if(this->ptr != nullptr) delete this->ptr;
|
||||
}
|
||||
}
|
||||
} // namespace Tesses::Framework
|
||||
@@ -1,53 +1,73 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/Http/BasicAuthServer.hpp"
|
||||
#include "TessesFramework/Crypto/Crypto.hpp"
|
||||
#include <iostream>
|
||||
namespace Tesses::Framework::Http {
|
||||
|
||||
BasicAuthServer::BasicAuthServer()
|
||||
{
|
||||
|
||||
}
|
||||
BasicAuthServer::BasicAuthServer(std::shared_ptr<IHttpServer> server, std::function<bool(std::string username, std::string password)> auth,std::string realm) : server(server), authorization(auth), realm(realm)
|
||||
{
|
||||
BasicAuthServer::BasicAuthServer() {}
|
||||
BasicAuthServer::BasicAuthServer(
|
||||
std::shared_ptr<IHttpServer> server,
|
||||
std::function<bool(std::string username, std::string password)> auth,
|
||||
std::string realm)
|
||||
: server(server), authorization(auth), realm(realm) {}
|
||||
bool BasicAuthServer::Handle(ServerContext &ctx) {
|
||||
std::string www_authenticate =
|
||||
"Basic realm=\"" + this->realm + "\", charset=\"UTF-8\"";
|
||||
std::string user;
|
||||
std::string pass;
|
||||
if (!GetCreds(ctx, user, pass) || !this->authorization(user, pass)) {
|
||||
ctx.responseHeaders.SetValue("WWW-Authenticate", www_authenticate);
|
||||
ctx.statusCode = Unauthorized;
|
||||
ctx.WithMimeType("text/html").SendText("<h1>Unauthorized</h1>");
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
bool BasicAuthServer::Handle(ServerContext& ctx)
|
||||
{
|
||||
std::string www_authenticate = "Basic realm=\"" + this->realm + "\", charset=\"UTF-8\"";
|
||||
std::string user;
|
||||
std::string pass;
|
||||
if(!GetCreds(ctx,user,pass) || !this->authorization(user,pass)) {
|
||||
ctx.responseHeaders.SetValue("WWW-Authenticate",www_authenticate);
|
||||
ctx.statusCode = Unauthorized;
|
||||
ctx.WithMimeType("text/html").SendText("<h1>Unauthorized</h1>");
|
||||
return true;
|
||||
}
|
||||
if (this->server)
|
||||
return this->server->Handle(ctx);
|
||||
ctx.statusCode = InternalServerError;
|
||||
ctx.WithMimeType("text/html")
|
||||
.SendText("<h1>Internal Server Error</h1>\r\n<h3>REASON: Internal "
|
||||
"server not set on Basic Auth</h3>");
|
||||
return true;
|
||||
}
|
||||
|
||||
if(this->server)
|
||||
return this->server->Handle(ctx);
|
||||
ctx.statusCode = InternalServerError;
|
||||
ctx.WithMimeType("text/html").SendText("<h1>Internal Server Error</h1>\r\n<h3>REASON: Internal server not set on Basic Auth</h3>");
|
||||
return true;
|
||||
|
||||
}
|
||||
bool BasicAuthServer::GetCreds(ServerContext &ctx, std::string &user,
|
||||
std::string &pass) {
|
||||
std::string auth;
|
||||
if (!ctx.requestHeaders.TryGetFirst("Authorization", auth))
|
||||
return false;
|
||||
|
||||
auto security = HttpUtils::SplitString(auth, " ", 2);
|
||||
if (security.size() < 2)
|
||||
return false;
|
||||
if (security[0] != "Basic")
|
||||
return false;
|
||||
|
||||
bool BasicAuthServer::GetCreds(ServerContext& ctx, std::string& user, std::string& pass)
|
||||
{
|
||||
std::string auth;
|
||||
if(!ctx.requestHeaders.TryGetFirst("Authorization", auth)) return false;
|
||||
|
||||
auto security = HttpUtils::SplitString(auth," ",2);
|
||||
if(security.size() < 2) return false;
|
||||
if(security[0] != "Basic") return false;
|
||||
auto decoded = Crypto::Base64_Decode(security[1]);
|
||||
|
||||
auto decoded = Crypto::Base64_Decode(security[1]);
|
||||
|
||||
std::string decoded_str(decoded.begin(),decoded.end());
|
||||
security = HttpUtils::SplitString(decoded_str,":",2);
|
||||
if(security.size() < 2) return false;
|
||||
user = security[0];
|
||||
pass = security[1];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
std::string decoded_str(decoded.begin(), decoded.end());
|
||||
security = HttpUtils::SplitString(decoded_str, ":", 2);
|
||||
if (security.size() < 2)
|
||||
return false;
|
||||
user = security[0];
|
||||
pass = security[1];
|
||||
return true;
|
||||
}
|
||||
} // namespace Tesses::Framework::Http
|
||||
@@ -1,135 +1,139 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/Http/CGIServer.hpp"
|
||||
#include "TessesFramework/Filesystem/LocalFS.hpp"
|
||||
#include "TessesFramework/Platform/Process.hpp"
|
||||
#include "TessesFramework/Http/BasicAuthServer.hpp"
|
||||
#include "TessesFramework/Platform/Process.hpp"
|
||||
#include "TessesFramework/TextStreams/StreamReader.hpp"
|
||||
#include <iostream>
|
||||
namespace Tesses::Framework::Http {
|
||||
CGIServer::CGIServer(Tesses::Framework::Filesystem::VFSPath dir)
|
||||
{
|
||||
this->dir = dir;
|
||||
}
|
||||
bool CGIServer::Handle(ServerContext& ctx)
|
||||
{
|
||||
Tesses::Framework::Filesystem::VFSPath execPath = ctx.path;
|
||||
execPath.relative=true;
|
||||
CGIParams params;
|
||||
params.document_root = this->document_root ? *this->document_root : this->dir;
|
||||
params.adminEmail = this->adminEmail;
|
||||
params.workingDirectory = this->workingDirectory;
|
||||
params.program = this->dir / execPath.CollapseRelativeParents();
|
||||
|
||||
return ServeCGIRequest(ctx,params);
|
||||
}
|
||||
bool CGIServer::ServeCGIRequest(ServerContext& ctx, CGIParams& params)
|
||||
{
|
||||
using namespace Tesses::Framework::Filesystem;
|
||||
auto program = params.program.MakeAbsolute();
|
||||
if(!LocalFS->FileExists(program)) return false;
|
||||
Tesses::Framework::Platform::Process p;
|
||||
CGIServer::CGIServer(Tesses::Framework::Filesystem::VFSPath dir) {
|
||||
this->dir = dir;
|
||||
}
|
||||
bool CGIServer::Handle(ServerContext &ctx) {
|
||||
Tesses::Framework::Filesystem::VFSPath execPath = ctx.path;
|
||||
execPath.relative = true;
|
||||
CGIParams params;
|
||||
params.document_root =
|
||||
this->document_root ? *this->document_root : this->dir;
|
||||
params.adminEmail = this->adminEmail;
|
||||
params.workingDirectory = this->workingDirectory;
|
||||
params.program = this->dir / execPath.CollapseRelativeParents();
|
||||
|
||||
Tesses::Framework::Filesystem::VFSPath p0=ctx.originalPath;
|
||||
|
||||
p.env.emplace_back("SCRIPT_FILENAME",LocalFS->VFSPathToSystem(program));
|
||||
p.env.emplace_back("SCRIPT_NAME",p0.CollapseRelativeParents().ToString());
|
||||
if(ctx.encrypted)
|
||||
p.env.emplace_back("HTTPS","on");
|
||||
|
||||
std::string query;
|
||||
for(auto& item : ctx.queryParams.kvp)
|
||||
{
|
||||
for(auto& val : item.second)
|
||||
{
|
||||
if(!query.empty()) query += "&";
|
||||
|
||||
query += HttpUtils::UrlEncode(item.first);
|
||||
query += "=";
|
||||
query += HttpUtils::UrlEncode(val);
|
||||
}
|
||||
}
|
||||
p.env.emplace_back("QUERY_STRING",query);
|
||||
p.env.emplace_back("REQUEST_URI",ctx.GetOriginalPathWithQuery());
|
||||
p.env.emplace_back("REQUEST_METHOD",ctx.method);
|
||||
p.env.emplace_back("REMOTE_HOST",ctx.ip);
|
||||
p.env.emplace_back("REMOTE_ADDR",ctx.ip);
|
||||
p.env.emplace_back("REMOTE_PORT",std::to_string(ctx.port));
|
||||
std::string user;
|
||||
std::string pass;
|
||||
if(BasicAuthServer::GetCreds(ctx,user,pass))
|
||||
p.env.emplace_back("REMOTE_USER",user);
|
||||
p.env.emplace_back("SERVER_SOFTWARE","TessesFrameworkWebServer");
|
||||
p.env.emplace_back("SERVER_PORT",std::to_string(ctx.serverPort));
|
||||
p.env.emplace_back("GATEWAY_INTERFACE","CGI/1.1");
|
||||
p.env.emplace_back("SERVER_PROTOCOL",ctx.version);
|
||||
|
||||
if(params.document_root)
|
||||
p.env.emplace_back("DOCUMENT_ROOT",params.document_root->ToString());
|
||||
if(params.adminEmail)
|
||||
p.env.emplace_back("SERVER_ADMIN",*params.adminEmail);
|
||||
|
||||
for(auto& hdr : ctx.requestHeaders.kvp)
|
||||
{
|
||||
std::string hdr_name = HttpUtils::ToUpper(hdr.first);
|
||||
if(hdr_name == "CONTENT-LENGTH")
|
||||
{
|
||||
if(!hdr.second.empty())
|
||||
p.env.emplace_back("CONTENT_LENGTH",hdr.second.front());
|
||||
}
|
||||
else if(hdr_name == "CONTENT-TYPE")
|
||||
{
|
||||
if(!hdr.second.empty())
|
||||
p.env.emplace_back("CONTENT_LENGTH",hdr.second.front());
|
||||
}
|
||||
else {
|
||||
|
||||
if(!hdr.second.empty())
|
||||
p.env.emplace_back("HTTP_"+hdr.first,hdr.second.front());
|
||||
}
|
||||
}
|
||||
p.redirectStdIn=true;
|
||||
p.redirectStdOut=true;
|
||||
p.name = program.ToString();
|
||||
|
||||
|
||||
if(params.workingDirectory)
|
||||
{
|
||||
p.workingDirectory = params.workingDirectory->MakeAbsolute().ToString();
|
||||
}
|
||||
|
||||
if(p.Start())
|
||||
{
|
||||
auto strm = p.GetStdinStream();
|
||||
if(ctx.method != "GET") ctx.ReadStream(strm);
|
||||
p.CloseStdInNow();
|
||||
auto stout = p.GetStdoutStream();
|
||||
Tesses::Framework::TextStreams::StreamReader reader(stout);
|
||||
std::string line;
|
||||
while(reader.ReadLineHttp(line))
|
||||
{
|
||||
auto v = HttpUtils::SplitString(line,": ", 2);
|
||||
if(v.size() == 2)
|
||||
{
|
||||
if(HttpUtils::ToLower(v[0]) == "status")
|
||||
{
|
||||
auto v2 = HttpUtils::SplitString(v[1]," ",2);
|
||||
if(v2.empty())
|
||||
{
|
||||
ctx.statusCode = StatusCode::InternalServerError;
|
||||
throw std::runtime_error("Status response is empty");
|
||||
}
|
||||
ctx.statusCode= (StatusCode)std::stoi(v2[0]);
|
||||
}
|
||||
else {
|
||||
ctx.responseHeaders.AddValue(v[0],v[1]);
|
||||
}
|
||||
}
|
||||
else throw std::runtime_error("Corrupted header: " + line);
|
||||
line.clear();
|
||||
}
|
||||
|
||||
ctx.SendStream(stout);
|
||||
return true;
|
||||
}
|
||||
return ServeCGIRequest(ctx, params);
|
||||
}
|
||||
bool CGIServer::ServeCGIRequest(ServerContext &ctx, CGIParams ¶ms) {
|
||||
using namespace Tesses::Framework::Filesystem;
|
||||
auto program = params.program.MakeAbsolute();
|
||||
if (!LocalFS->FileExists(program))
|
||||
return false;
|
||||
Tesses::Framework::Platform::Process p;
|
||||
|
||||
Tesses::Framework::Filesystem::VFSPath p0 = ctx.originalPath;
|
||||
|
||||
p.env.emplace_back("SCRIPT_FILENAME", LocalFS->VFSPathToSystem(program));
|
||||
p.env.emplace_back("SCRIPT_NAME", p0.CollapseRelativeParents().ToString());
|
||||
if (ctx.encrypted)
|
||||
p.env.emplace_back("HTTPS", "on");
|
||||
|
||||
std::string query;
|
||||
for (auto &item : ctx.queryParams.kvp) {
|
||||
for (auto &val : item.second) {
|
||||
if (!query.empty())
|
||||
query += "&";
|
||||
|
||||
query += HttpUtils::UrlEncode(item.first);
|
||||
query += "=";
|
||||
query += HttpUtils::UrlEncode(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
p.env.emplace_back("QUERY_STRING", query);
|
||||
p.env.emplace_back("REQUEST_URI", ctx.GetOriginalPathWithQuery());
|
||||
p.env.emplace_back("REQUEST_METHOD", ctx.method);
|
||||
p.env.emplace_back("REMOTE_HOST", ctx.ip);
|
||||
p.env.emplace_back("REMOTE_ADDR", ctx.ip);
|
||||
p.env.emplace_back("REMOTE_PORT", std::to_string(ctx.port));
|
||||
std::string user;
|
||||
std::string pass;
|
||||
if (BasicAuthServer::GetCreds(ctx, user, pass))
|
||||
p.env.emplace_back("REMOTE_USER", user);
|
||||
p.env.emplace_back("SERVER_SOFTWARE", "TessesFrameworkWebServer");
|
||||
p.env.emplace_back("SERVER_PORT", std::to_string(ctx.serverPort));
|
||||
p.env.emplace_back("GATEWAY_INTERFACE", "CGI/1.1");
|
||||
p.env.emplace_back("SERVER_PROTOCOL", ctx.version);
|
||||
|
||||
if (params.document_root)
|
||||
p.env.emplace_back("DOCUMENT_ROOT", params.document_root->ToString());
|
||||
if (params.adminEmail)
|
||||
p.env.emplace_back("SERVER_ADMIN", *params.adminEmail);
|
||||
|
||||
for (auto &hdr : ctx.requestHeaders.kvp) {
|
||||
std::string hdr_name = HttpUtils::ToUpper(hdr.first);
|
||||
if (hdr_name == "CONTENT-LENGTH") {
|
||||
if (!hdr.second.empty())
|
||||
p.env.emplace_back("CONTENT_LENGTH", hdr.second.front());
|
||||
} else if (hdr_name == "CONTENT-TYPE") {
|
||||
if (!hdr.second.empty())
|
||||
p.env.emplace_back("CONTENT_LENGTH", hdr.second.front());
|
||||
} else {
|
||||
|
||||
if (!hdr.second.empty())
|
||||
p.env.emplace_back("HTTP_" + hdr.first, hdr.second.front());
|
||||
}
|
||||
}
|
||||
p.redirectStdIn = true;
|
||||
p.redirectStdOut = true;
|
||||
p.name = program.ToString();
|
||||
|
||||
if (params.workingDirectory) {
|
||||
p.workingDirectory = params.workingDirectory->MakeAbsolute().ToString();
|
||||
}
|
||||
|
||||
if (p.Start()) {
|
||||
auto strm = p.GetStdinStream();
|
||||
if (ctx.method != "GET")
|
||||
ctx.ReadStream(strm);
|
||||
p.CloseStdInNow();
|
||||
auto stout = p.GetStdoutStream();
|
||||
Tesses::Framework::TextStreams::StreamReader reader(stout);
|
||||
std::string line;
|
||||
while (reader.ReadLineHttp(line)) {
|
||||
auto v = HttpUtils::SplitString(line, ": ", 2);
|
||||
if (v.size() == 2) {
|
||||
if (HttpUtils::ToLower(v[0]) == "status") {
|
||||
auto v2 = HttpUtils::SplitString(v[1], " ", 2);
|
||||
if (v2.empty()) {
|
||||
ctx.statusCode = StatusCode::InternalServerError;
|
||||
throw std::runtime_error("Status response is empty");
|
||||
}
|
||||
ctx.statusCode = (StatusCode)std::stoi(v2[0]);
|
||||
} else {
|
||||
ctx.responseHeaders.AddValue(v[0], v[1]);
|
||||
}
|
||||
} else
|
||||
throw std::runtime_error("Corrupted header: " + line);
|
||||
line.clear();
|
||||
}
|
||||
|
||||
ctx.SendStream(stout);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} // namespace Tesses::Framework::Http
|
||||
@@ -1,22 +1,31 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/Http/CallbackServer.hpp"
|
||||
|
||||
namespace Tesses::Framework::Http
|
||||
{
|
||||
CallbackServer::CallbackServer(std::function<bool(ServerContext&)> cb) : CallbackServer(cb,[]()->void{})
|
||||
{
|
||||
|
||||
}
|
||||
CallbackServer::CallbackServer(std::function<bool(ServerContext&)> cb,std::function<void()> destroy)
|
||||
{
|
||||
this->cb = cb;
|
||||
this->destroy=destroy;
|
||||
}
|
||||
bool CallbackServer::Handle(ServerContext& ctx)
|
||||
{
|
||||
return this->cb(ctx);
|
||||
}
|
||||
CallbackServer::~CallbackServer()
|
||||
{
|
||||
this->destroy();
|
||||
}
|
||||
}
|
||||
namespace Tesses::Framework::Http {
|
||||
CallbackServer::CallbackServer(std::function<bool(ServerContext &)> cb)
|
||||
: CallbackServer(cb, []() -> void {}) {}
|
||||
CallbackServer::CallbackServer(std::function<bool(ServerContext &)> cb,
|
||||
std::function<void()> destroy) {
|
||||
this->cb = cb;
|
||||
this->destroy = destroy;
|
||||
}
|
||||
bool CallbackServer::Handle(ServerContext &ctx) { return this->cb(ctx); }
|
||||
CallbackServer::~CallbackServer() { this->destroy(); }
|
||||
} // namespace Tesses::Framework::Http
|
||||
@@ -1,22 +1,33 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/Http/ChangeableServer.hpp"
|
||||
|
||||
namespace Tesses::Framework::Http {
|
||||
ChangeableServer::ChangeableServer() : ChangeableServer(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
ChangeableServer::ChangeableServer(std::shared_ptr<IHttpServer> original)
|
||||
{
|
||||
ChangeableServer::ChangeableServer() : ChangeableServer(nullptr) {}
|
||||
ChangeableServer::ChangeableServer(std::shared_ptr<IHttpServer> original) {
|
||||
this->server = original;
|
||||
}
|
||||
|
||||
bool ChangeableServer::Handle(ServerContext& ctx)
|
||||
{
|
||||
if(this->server) this->server->Handle(ctx);
|
||||
bool ChangeableServer::Handle(ServerContext &ctx) {
|
||||
if (this->server)
|
||||
this->server->Handle(ctx);
|
||||
return false;
|
||||
}
|
||||
ChangeableServer::~ChangeableServer()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
ChangeableServer::~ChangeableServer() {}
|
||||
} // namespace Tesses::Framework::Http
|
||||
@@ -1,81 +1,81 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/Http/ContentDisposition.hpp"
|
||||
#include "TessesFramework/Http/HttpUtils.hpp"
|
||||
#include <iostream>
|
||||
namespace Tesses::Framework::Http
|
||||
{
|
||||
bool ContentDisposition::TryParse(std::string str, ContentDisposition& cd)
|
||||
{
|
||||
auto res = HttpUtils::SplitString(str,"; ");
|
||||
|
||||
|
||||
if(res.empty()) return false;
|
||||
cd.type = res[0];
|
||||
bool hasFileNameStar = false;
|
||||
for(size_t i = 1; i < res.size(); i++)
|
||||
{
|
||||
auto res2 = HttpUtils::SplitString(res[i],"=",2);
|
||||
if(res2.size() == 2)
|
||||
{
|
||||
if(res2[0] == "filename*")
|
||||
{
|
||||
//cd.filename = res2[1];
|
||||
//UTF-8''
|
||||
std::string p = res2[1];
|
||||
if(p.find("UTF-8''") == 0)
|
||||
{
|
||||
hasFileNameStar = true;
|
||||
p = HttpUtils::UrlPathDecode(p.substr(7));
|
||||
cd.filename = p;
|
||||
}
|
||||
}
|
||||
else if(res2[0] == "filename" && !hasFileNameStar)
|
||||
{
|
||||
std::string p = res2[1];
|
||||
if(!p.empty() && p[0] == '\"')
|
||||
{
|
||||
p = p.substr(1, p.size()-2);
|
||||
}
|
||||
|
||||
|
||||
p = HttpUtils::UrlPathDecode(p);
|
||||
|
||||
|
||||
namespace Tesses::Framework::Http {
|
||||
bool ContentDisposition::TryParse(std::string str, ContentDisposition &cd) {
|
||||
auto res = HttpUtils::SplitString(str, "; ");
|
||||
|
||||
if (res.empty())
|
||||
return false;
|
||||
cd.type = res[0];
|
||||
bool hasFileNameStar = false;
|
||||
for (size_t i = 1; i < res.size(); i++) {
|
||||
auto res2 = HttpUtils::SplitString(res[i], "=", 2);
|
||||
if (res2.size() == 2) {
|
||||
if (res2[0] == "filename*") {
|
||||
// cd.filename = res2[1];
|
||||
// UTF-8''
|
||||
std::string p = res2[1];
|
||||
if (p.find("UTF-8''") == 0) {
|
||||
hasFileNameStar = true;
|
||||
p = HttpUtils::UrlPathDecode(p.substr(7));
|
||||
cd.filename = p;
|
||||
}
|
||||
else if(res2[0] == "name")
|
||||
{
|
||||
std::string p = res2[1];
|
||||
if(!p.empty() && p[0] == '\"')
|
||||
{
|
||||
p = p.substr(1, p.size()-2);
|
||||
}
|
||||
|
||||
cd.fieldName = HttpUtils::UrlPathDecode(p);
|
||||
|
||||
} else if (res2[0] == "filename" && !hasFileNameStar) {
|
||||
std::string p = res2[1];
|
||||
if (!p.empty() && p[0] == '\"') {
|
||||
p = p.substr(1, p.size() - 2);
|
||||
}
|
||||
|
||||
p = HttpUtils::UrlPathDecode(p);
|
||||
|
||||
cd.filename = p;
|
||||
} else if (res2[0] == "name") {
|
||||
std::string p = res2[1];
|
||||
if (!p.empty() && p[0] == '\"') {
|
||||
p = p.substr(1, p.size() - 2);
|
||||
}
|
||||
|
||||
cd.fieldName = HttpUtils::UrlPathDecode(p);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string ContentDisposition::ToString()
|
||||
{
|
||||
std::vector<std::string> parts;
|
||||
std::string ContentDisposition::ToString() {
|
||||
std::vector<std::string> parts;
|
||||
|
||||
parts.push_back(this->type);
|
||||
parts.push_back(this->type);
|
||||
|
||||
if(!this->fieldName.empty())
|
||||
{
|
||||
parts.push_back("name=\"" + HttpUtils::UrlPathEncode(this->fieldName,true) + "\"");
|
||||
}
|
||||
|
||||
if(!this->filename.empty())
|
||||
{
|
||||
parts.push_back("filename=\"" + HttpUtils::UrlPathEncode(this->filename,true) + "\"");
|
||||
}
|
||||
|
||||
return HttpUtils::Join("; ", parts);
|
||||
if (!this->fieldName.empty()) {
|
||||
parts.push_back("name=\"" +
|
||||
HttpUtils::UrlPathEncode(this->fieldName, true) + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
if (!this->filename.empty()) {
|
||||
parts.push_back("filename=\"" +
|
||||
HttpUtils::UrlPathEncode(this->filename, true) + "\"");
|
||||
}
|
||||
|
||||
return HttpUtils::Join("; ", parts);
|
||||
}
|
||||
} // namespace Tesses::Framework::Http
|
||||
@@ -1,131 +1,135 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/Http/FileServer.hpp"
|
||||
#include "TessesFramework/Common.hpp"
|
||||
#include "TessesFramework/Filesystem/LocalFS.hpp"
|
||||
#include "TessesFramework/Filesystem/SubdirFilesystem.hpp"
|
||||
#include <iostream>
|
||||
#include "TessesFramework/Common.hpp"
|
||||
using LocalFilesystem = Tesses::Framework::Filesystem::LocalFilesystem;
|
||||
using SubdirFilesystem = Tesses::Framework::Filesystem::SubdirFilesystem;
|
||||
using VFSPath = Tesses::Framework::Filesystem::VFSPath;
|
||||
using VFS = Tesses::Framework::Filesystem::VFS;
|
||||
namespace Tesses::Framework::Http
|
||||
{
|
||||
FileServer::FileServer(std::filesystem::path path,bool allowListing,bool spa) : FileServer(path,allowListing,spa,{"index.html","default.html","index.htm","default.htm"})
|
||||
{
|
||||
namespace Tesses::Framework::Http {
|
||||
FileServer::FileServer(std::filesystem::path path, bool allowListing, bool spa)
|
||||
: FileServer(path, allowListing, spa,
|
||||
{"index.html", "default.html", "index.htm", "default.htm"}) {}
|
||||
FileServer::FileServer(std::filesystem::path path, bool allowListing, bool spa,
|
||||
std::vector<std::string> defaultNames) {
|
||||
std::shared_ptr<SubdirFilesystem> sdfs = std::make_shared<SubdirFilesystem>(
|
||||
Tesses::Framework::Filesystem::LocalFS,
|
||||
Tesses::Framework::Filesystem::LocalFS->SystemToVFSPath(path.string()));
|
||||
this->vfs = sdfs;
|
||||
this->spa = spa;
|
||||
|
||||
}
|
||||
FileServer::FileServer(std::filesystem::path path,bool allowListing, bool spa, std::vector<std::string> defaultNames)
|
||||
{
|
||||
std::shared_ptr<SubdirFilesystem> sdfs=std::make_shared<SubdirFilesystem>(Tesses::Framework::Filesystem::LocalFS,Tesses::Framework::Filesystem::LocalFS->SystemToVFSPath(path.string()));
|
||||
this->vfs = sdfs;
|
||||
this->spa = spa;
|
||||
|
||||
this->allowListing = allowListing;
|
||||
this->defaultNames = defaultNames;
|
||||
}
|
||||
FileServer::FileServer(std::shared_ptr<Tesses::Framework::Filesystem::VFS> fs, bool allowListing,bool spa) : FileServer(fs,allowListing,spa,{"index.html","default.html","index.htm","default.htm"})
|
||||
{
|
||||
|
||||
}
|
||||
FileServer::FileServer(std::shared_ptr<Tesses::Framework::Filesystem::VFS> fs, bool allowListing, bool spa, std::vector<std::string> defaultNames)
|
||||
{
|
||||
this->vfs = fs;
|
||||
this->allowListing = allowListing;
|
||||
this->defaultNames = defaultNames;
|
||||
this->spa = spa;
|
||||
}
|
||||
bool FileServer::SendFile(ServerContext& ctx,VFSPath path)
|
||||
{
|
||||
TF_LOG("File: " + path.ToString());
|
||||
auto strm = this->vfs->OpenFile(path,"rb");
|
||||
bool retVal = false;
|
||||
if(strm != nullptr)
|
||||
{
|
||||
Date::DateTime lw,la;
|
||||
this->vfs->GetDate(path,lw,la);
|
||||
ctx.WithLastModified(lw).WithMimeType(HttpUtils::MimeType(path.GetFileName())).SendStream(strm);
|
||||
retVal = true;
|
||||
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
bool FileServer::Handle(ServerContext& ctx)
|
||||
{
|
||||
auto path = ((VFSPath)HttpUtils::UrlPathDecode(ctx.path)).CollapseRelativeParents();
|
||||
|
||||
|
||||
if(this->vfs->DirectoryExists(path))
|
||||
{
|
||||
TF_LOG("Directory exists");
|
||||
for(auto f : defaultNames)
|
||||
{
|
||||
VFSPath p=path;
|
||||
p = p / f;
|
||||
TF_LOG("Trying " + p.ToString());
|
||||
TF_LOG("Before file exists");
|
||||
TF_LOG(this->vfs->FileExists(p)?"File Exists" : "File Does Not Exist");
|
||||
TF_LOG("After file exists");
|
||||
if(this->vfs->FileExists(p))
|
||||
return SendFile(ctx,p);
|
||||
}
|
||||
if(this->allowListing)
|
||||
{
|
||||
std::string p = HttpUtils::HtmlEncode(ctx.originalPath);
|
||||
std::string html = "<!DOCTYPE html><html><head><meta charset=\"UTF-8\"><title>Index of ";
|
||||
html.append(p);
|
||||
html.append("</title><meta name=\"color-scheme\" content=\"dark light\"></head><body><h1>Index of ");
|
||||
html.append(p);
|
||||
html.append("</h1><hr><pre><a href=\"../\">../</a>\r\n");
|
||||
|
||||
|
||||
for(auto item : vfs->EnumeratePaths(path))
|
||||
{
|
||||
if(vfs->DirectoryExists(item))
|
||||
{
|
||||
//is dir
|
||||
std::string path = item.GetFileName();
|
||||
html.append("<a href=\"");
|
||||
html.append(HttpUtils::UrlPathEncode(path) + "/");
|
||||
html.append("\">");
|
||||
html.append(HttpUtils::HtmlEncode(path) + "/");
|
||||
html.append("</a>\r\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
//is file
|
||||
std::string path = item.GetFileName();
|
||||
html.append("<a href=\"");
|
||||
html.append(HttpUtils::UrlPathEncode(path));
|
||||
html.append("\">");
|
||||
html.append(HttpUtils::HtmlEncode(path));
|
||||
html.append("</a>\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
html.append("</pre><hr></body></html>");
|
||||
|
||||
ctx.WithMimeType("text/html").SendText(html);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if(this->vfs->FileExists(path))
|
||||
{
|
||||
return SendFile(ctx,path);
|
||||
}
|
||||
else if(this->spa)
|
||||
{
|
||||
for(auto f : defaultNames)
|
||||
{
|
||||
VFSPath p(f);
|
||||
p.relative=false;
|
||||
if(this->vfs->FileExists(p))
|
||||
return SendFile(ctx,p);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
FileServer::~FileServer()
|
||||
{
|
||||
|
||||
}
|
||||
this->allowListing = allowListing;
|
||||
this->defaultNames = defaultNames;
|
||||
}
|
||||
FileServer::FileServer(std::shared_ptr<Tesses::Framework::Filesystem::VFS> fs,
|
||||
bool allowListing, bool spa)
|
||||
: FileServer(fs, allowListing, spa,
|
||||
{"index.html", "default.html", "index.htm", "default.htm"}) {}
|
||||
FileServer::FileServer(std::shared_ptr<Tesses::Framework::Filesystem::VFS> fs,
|
||||
bool allowListing, bool spa,
|
||||
std::vector<std::string> defaultNames) {
|
||||
this->vfs = fs;
|
||||
this->allowListing = allowListing;
|
||||
this->defaultNames = defaultNames;
|
||||
this->spa = spa;
|
||||
}
|
||||
bool FileServer::SendFile(ServerContext &ctx, VFSPath path) {
|
||||
TF_LOG("File: " + path.ToString());
|
||||
auto strm = this->vfs->OpenFile(path, "rb");
|
||||
bool retVal = false;
|
||||
if (strm != nullptr) {
|
||||
Date::DateTime lw, la;
|
||||
this->vfs->GetDate(path, lw, la);
|
||||
ctx.WithLastModified(lw)
|
||||
.WithMimeType(HttpUtils::MimeType(path.GetFileName()))
|
||||
.SendStream(strm);
|
||||
retVal = true;
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
bool FileServer::Handle(ServerContext &ctx) {
|
||||
auto path =
|
||||
((VFSPath)HttpUtils::UrlPathDecode(ctx.path)).CollapseRelativeParents();
|
||||
|
||||
if (this->vfs->DirectoryExists(path)) {
|
||||
TF_LOG("Directory exists");
|
||||
for (auto f : defaultNames) {
|
||||
VFSPath p = path;
|
||||
p = p / f;
|
||||
TF_LOG("Trying " + p.ToString());
|
||||
TF_LOG("Before file exists");
|
||||
TF_LOG(this->vfs->FileExists(p) ? "File Exists"
|
||||
: "File Does Not Exist");
|
||||
TF_LOG("After file exists");
|
||||
if (this->vfs->FileExists(p))
|
||||
return SendFile(ctx, p);
|
||||
}
|
||||
if (this->allowListing) {
|
||||
std::string p = HttpUtils::HtmlEncode(ctx.originalPath);
|
||||
std::string html = "<!DOCTYPE html><html><head><meta "
|
||||
"charset=\"UTF-8\"><title>Index of ";
|
||||
html.append(p);
|
||||
html.append("</title><meta name=\"color-scheme\" content=\"dark "
|
||||
"light\"></head><body><h1>Index of ");
|
||||
html.append(p);
|
||||
html.append("</h1><hr><pre><a href=\"../\">../</a>\r\n");
|
||||
|
||||
for (auto item : vfs->EnumeratePaths(path)) {
|
||||
if (vfs->DirectoryExists(item)) {
|
||||
// is dir
|
||||
std::string path = item.GetFileName();
|
||||
html.append("<a href=\"");
|
||||
html.append(HttpUtils::UrlPathEncode(path) + "/");
|
||||
html.append("\">");
|
||||
html.append(HttpUtils::HtmlEncode(path) + "/");
|
||||
html.append("</a>\r\n");
|
||||
} else {
|
||||
// is file
|
||||
std::string path = item.GetFileName();
|
||||
html.append("<a href=\"");
|
||||
html.append(HttpUtils::UrlPathEncode(path));
|
||||
html.append("\">");
|
||||
html.append(HttpUtils::HtmlEncode(path));
|
||||
html.append("</a>\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
html.append("</pre><hr></body></html>");
|
||||
|
||||
ctx.WithMimeType("text/html").SendText(html);
|
||||
return true;
|
||||
}
|
||||
} else if (this->vfs->FileExists(path)) {
|
||||
return SendFile(ctx, path);
|
||||
} else if (this->spa) {
|
||||
for (auto f : defaultNames) {
|
||||
VFSPath p(f);
|
||||
p.relative = false;
|
||||
if (this->vfs->FileExists(p))
|
||||
return SendFile(ctx, p);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
FileServer::~FileServer() {}
|
||||
} // namespace Tesses::Framework::Http
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,189 +1,182 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/Http/HttpStream.hpp"
|
||||
#include "TessesFramework/TextStreams/StreamWriter.hpp"
|
||||
#include "TessesFramework/TextStreams/StreamReader.hpp"
|
||||
#include <sstream>
|
||||
#include "TessesFramework/TextStreams/StreamWriter.hpp"
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
using StreamWriter = Tesses::Framework::TextStreams::StreamWriter;
|
||||
using StreamReader = Tesses::Framework::TextStreams::StreamReader;
|
||||
namespace Tesses::Framework::Http
|
||||
{
|
||||
HttpStream::HttpStream(std::shared_ptr<Tesses::Framework::Streams::Stream> strm, int64_t length, bool recv, bool http1_1)
|
||||
{
|
||||
this->strm = strm;
|
||||
this->length = length;
|
||||
this->recv = recv;
|
||||
this->http1_1 = http1_1;
|
||||
this->offset = 0;
|
||||
this->read = 0;
|
||||
this->position = 0;
|
||||
this->done=false;
|
||||
}
|
||||
bool HttpStream::CanRead()
|
||||
{
|
||||
if(this->done) return false;
|
||||
if(!this->recv) return false;
|
||||
if(this->offset < this->read) return true;
|
||||
return this->strm->CanRead();
|
||||
}
|
||||
bool HttpStream::CanWrite()
|
||||
{
|
||||
if(this->done) return false;
|
||||
if(this->recv) return false;
|
||||
return this->strm->CanWrite();
|
||||
}
|
||||
bool HttpStream::EndOfStream()
|
||||
{
|
||||
if(this->done) return true;
|
||||
if(!this->recv) return true;
|
||||
if(this->offset < this->read) return false;
|
||||
return this->strm->EndOfStream();
|
||||
}
|
||||
int64_t HttpStream::GetLength()
|
||||
{
|
||||
return this->length;
|
||||
}
|
||||
int64_t HttpStream::GetPosition()
|
||||
{
|
||||
return this->position;
|
||||
}
|
||||
size_t HttpStream::Read(uint8_t* buff, size_t len)
|
||||
{
|
||||
if(this->done) return 0;
|
||||
if(!this->recv) return 0;
|
||||
if(this->length == 0) return 0;
|
||||
if(this->length > 0)
|
||||
{
|
||||
|
||||
len = std::min((size_t)(this->length - this->position), len);
|
||||
|
||||
if(len > 0)
|
||||
len = this->strm->Read(buff,len);
|
||||
this->position += len;
|
||||
return len;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(this->http1_1)
|
||||
{
|
||||
if(this->offset < this->read)
|
||||
{
|
||||
|
||||
len = std::min((size_t)(this->read - this->offset), len);
|
||||
if(len > 0)
|
||||
len = this->strm->Read(buff,len);
|
||||
this->offset += len;
|
||||
this->position += len;
|
||||
if(this->offset >= this->read)
|
||||
{
|
||||
StreamReader reader(this->strm);
|
||||
reader.ReadLine();
|
||||
}
|
||||
return len;
|
||||
}
|
||||
else
|
||||
{
|
||||
namespace Tesses::Framework::Http {
|
||||
HttpStream::HttpStream(std::shared_ptr<Tesses::Framework::Streams::Stream> strm,
|
||||
int64_t length, bool recv, bool http1_1) {
|
||||
this->strm = strm;
|
||||
this->length = length;
|
||||
this->recv = recv;
|
||||
this->http1_1 = http1_1;
|
||||
this->offset = 0;
|
||||
this->read = 0;
|
||||
this->position = 0;
|
||||
this->done = false;
|
||||
}
|
||||
bool HttpStream::CanRead() {
|
||||
if (this->done)
|
||||
return false;
|
||||
if (!this->recv)
|
||||
return false;
|
||||
if (this->offset < this->read)
|
||||
return true;
|
||||
return this->strm->CanRead();
|
||||
}
|
||||
bool HttpStream::CanWrite() {
|
||||
if (this->done)
|
||||
return false;
|
||||
if (this->recv)
|
||||
return false;
|
||||
return this->strm->CanWrite();
|
||||
}
|
||||
bool HttpStream::EndOfStream() {
|
||||
if (this->done)
|
||||
return true;
|
||||
if (this->offset < this->read)
|
||||
return false;
|
||||
return this->strm->EndOfStream();
|
||||
}
|
||||
int64_t HttpStream::GetLength() { return this->length; }
|
||||
int64_t HttpStream::GetPosition() { return this->position; }
|
||||
size_t HttpStream::Read(uint8_t *buff, size_t len) {
|
||||
if (this->done)
|
||||
return 0;
|
||||
if (!this->recv)
|
||||
return 0;
|
||||
if (this->length == 0)
|
||||
return 0;
|
||||
if (this->length > 0) {
|
||||
|
||||
len = std::min((size_t)(this->length - this->position), len);
|
||||
|
||||
if (len > 0)
|
||||
len = this->strm->Read(buff, len);
|
||||
this->position += len;
|
||||
return len;
|
||||
} else {
|
||||
if (this->http1_1) {
|
||||
if (this->offset < this->read) {
|
||||
|
||||
len = std::min((size_t)(this->read - this->offset), len);
|
||||
if (len > 0)
|
||||
len = this->strm->Read(buff, len);
|
||||
this->offset += len;
|
||||
this->position += len;
|
||||
if (this->offset >= this->read) {
|
||||
StreamReader reader(this->strm);
|
||||
std::string line = reader.ReadLine();
|
||||
if(!line.empty())
|
||||
{
|
||||
this->read = std::stoull(line, NULL, 16);
|
||||
|
||||
|
||||
if(this->read == 0)
|
||||
{
|
||||
reader.ReadLine();
|
||||
this->done=true;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->offset=0;
|
||||
|
||||
len = std::min((size_t)(this->read - this->offset), len);
|
||||
if(len > 0)
|
||||
len = this->strm->Read(buff,len);
|
||||
this->offset += len;
|
||||
this->position += len;
|
||||
return len;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
reader.ReadLine();
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
return this->strm->Read(buff,len);
|
||||
}
|
||||
}
|
||||
}
|
||||
size_t HttpStream::Write(const uint8_t* buff, size_t len)
|
||||
{
|
||||
if(this->done) return 0;
|
||||
if(this->recv) return 0;
|
||||
if(this->length == 0) return 0;
|
||||
if(this->length > 0)
|
||||
{
|
||||
|
||||
len = std::min((size_t)(this->length - this->position), len);
|
||||
|
||||
if(len > 0)
|
||||
len = this->strm->Write(buff,len);
|
||||
this->position += len;
|
||||
return len;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(len == 0) return 0;
|
||||
if(this->http1_1)
|
||||
{
|
||||
std::stringstream strm;
|
||||
strm << std::hex << len;
|
||||
|
||||
StreamWriter writer(this->strm);
|
||||
writer.newline = "\r\n";
|
||||
writer.WriteLine(strm.str());
|
||||
|
||||
this->strm->WriteBlock(buff, len);
|
||||
|
||||
writer.WriteLine();
|
||||
return len;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this->strm->Write(buff,len);
|
||||
}
|
||||
}
|
||||
}
|
||||
void HttpStream::Close()
|
||||
{
|
||||
if(this->length == -1 && this->http1_1 && !done && !this->recv)
|
||||
{
|
||||
this->done=true;
|
||||
try {
|
||||
|
||||
StreamWriter writer(this->strm);
|
||||
writer.newline = "\r\n";
|
||||
writer.WriteLine("0");
|
||||
writer.WriteLine();
|
||||
}catch(...){
|
||||
} else {
|
||||
StreamReader reader(this->strm);
|
||||
std::string line = reader.ReadLine();
|
||||
if (!line.empty()) {
|
||||
this->read = std::stoull(line, NULL, 16);
|
||||
|
||||
if (this->read == 0) {
|
||||
reader.ReadLine();
|
||||
this->done = true;
|
||||
return 0;
|
||||
} else {
|
||||
this->offset = 0;
|
||||
|
||||
len =
|
||||
std::min((size_t)(this->read - this->offset), len);
|
||||
if (len > 0)
|
||||
len = this->strm->Read(buff, len);
|
||||
this->offset += len;
|
||||
this->position += len;
|
||||
return len;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} else {
|
||||
return this->strm->Read(buff, len);
|
||||
}
|
||||
}
|
||||
HttpStream::~HttpStream()
|
||||
{
|
||||
if(this->length == -1 && this->http1_1 && !done && !this->recv)
|
||||
{
|
||||
try {
|
||||
|
||||
}
|
||||
size_t HttpStream::Write(const uint8_t *buff, size_t len) {
|
||||
if (this->done)
|
||||
return 0;
|
||||
if (this->recv)
|
||||
return 0;
|
||||
if (this->length == 0)
|
||||
return 0;
|
||||
if (this->length > 0) {
|
||||
|
||||
len = std::min((size_t)(this->length - this->position), len);
|
||||
|
||||
if (len > 0)
|
||||
len = this->strm->Write(buff, len);
|
||||
this->position += len;
|
||||
return len;
|
||||
} else {
|
||||
if (len == 0)
|
||||
return 0;
|
||||
if (this->http1_1) {
|
||||
std::stringstream strm;
|
||||
strm << std::hex << len;
|
||||
|
||||
StreamWriter writer(this->strm);
|
||||
writer.newline = "\r\n";
|
||||
writer.WriteLine(strm.str());
|
||||
|
||||
this->strm->WriteBlock(buff, len);
|
||||
|
||||
writer.WriteLine();
|
||||
return len;
|
||||
} else {
|
||||
return this->strm->Write(buff, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
void HttpStream::Close() {
|
||||
if (this->length == -1 && this->http1_1 && !done && !this->recv) {
|
||||
this->done = true;
|
||||
try {
|
||||
|
||||
StreamWriter writer(this->strm);
|
||||
writer.newline = "\r\n";
|
||||
writer.WriteLine("0");
|
||||
writer.WriteLine();
|
||||
}catch(...) {
|
||||
|
||||
}
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
HttpStream::~HttpStream() {
|
||||
if (this->length == -1 && this->http1_1 && !done && !this->recv) {
|
||||
try {
|
||||
|
||||
StreamWriter writer(this->strm);
|
||||
writer.newline = "\r\n";
|
||||
writer.WriteLine("0");
|
||||
writer.WriteLine();
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace Tesses::Framework::Http
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,62 +1,72 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/Http/MountableServer.hpp"
|
||||
|
||||
namespace Tesses::Framework::Http {
|
||||
std::string MountableServer::Subpath(Filesystem::VFSPath fullPath, Filesystem::VFSPath offsetPath)
|
||||
{
|
||||
if(fullPath.path.size() < offsetPath.path.size()) return {}; //this shouldn't happen but here just in case
|
||||
std::string MountableServer::Subpath(Filesystem::VFSPath fullPath,
|
||||
Filesystem::VFSPath offsetPath) {
|
||||
if (fullPath.path.size() < offsetPath.path.size())
|
||||
return {}; // this shouldn't happen but here just in case
|
||||
Filesystem::VFSPath p;
|
||||
p.relative=false;
|
||||
|
||||
for(size_t i = offsetPath.path.size(); i < fullPath.path.size(); i++)
|
||||
{
|
||||
p.relative = false;
|
||||
|
||||
for (size_t i = offsetPath.path.size(); i < fullPath.path.size(); i++) {
|
||||
p.path.push_back(fullPath.path[i]);
|
||||
}
|
||||
return p.ToString();
|
||||
}
|
||||
bool MountableServer::StartsWith(Filesystem::VFSPath fullPath, Filesystem::VFSPath offsetPath)
|
||||
{
|
||||
if(fullPath.path.size() < offsetPath.path.size()) return false;
|
||||
for(size_t i = 0; i < offsetPath.path.size(); i++)
|
||||
{
|
||||
if(fullPath.path[i] != offsetPath.path[i]) return false;
|
||||
bool MountableServer::StartsWith(Filesystem::VFSPath fullPath,
|
||||
Filesystem::VFSPath offsetPath) {
|
||||
if (fullPath.path.size() < offsetPath.path.size())
|
||||
return false;
|
||||
for (size_t i = 0; i < offsetPath.path.size(); i++) {
|
||||
if (fullPath.path[i] != offsetPath.path[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
MountableServer::MountableServer() : MountableServer(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
MountableServer::MountableServer(std::shared_ptr<IHttpServer> root)
|
||||
{
|
||||
MountableServer::MountableServer() : MountableServer(nullptr) {}
|
||||
MountableServer::MountableServer(std::shared_ptr<IHttpServer> root) {
|
||||
this->root = root;
|
||||
}
|
||||
|
||||
void MountableServer::Mount(std::string path, std::shared_ptr<IHttpServer> server)
|
||||
{
|
||||
this->servers.insert(this->servers.begin(), std::pair<std::string,std::shared_ptr<IHttpServer>>(path, server));
|
||||
void MountableServer::Mount(std::string path,
|
||||
std::shared_ptr<IHttpServer> server) {
|
||||
this->servers.insert(
|
||||
this->servers.begin(),
|
||||
std::pair<std::string, std::shared_ptr<IHttpServer>>(path, server));
|
||||
}
|
||||
void MountableServer::Unmount(std::string path)
|
||||
{
|
||||
for(auto i = this->servers.begin(); i != this->servers.end(); i++)
|
||||
{
|
||||
auto& item = *i;
|
||||
if(item.first == path)
|
||||
{
|
||||
void MountableServer::Unmount(std::string path) {
|
||||
for (auto i = this->servers.begin(); i != this->servers.end(); i++) {
|
||||
auto &item = *i;
|
||||
if (item.first == path) {
|
||||
this->servers.erase(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
bool MountableServer::Handle(ServerContext& ctx)
|
||||
{
|
||||
bool MountableServer::Handle(ServerContext &ctx) {
|
||||
std::string oldPath = ctx.path;
|
||||
for(auto item : this->servers)
|
||||
{
|
||||
if(StartsWith(oldPath, item.first))
|
||||
{
|
||||
for (auto item : this->servers) {
|
||||
if (StartsWith(oldPath, item.first)) {
|
||||
ctx.path = Subpath(oldPath, item.first);
|
||||
if(item.second->Handle(ctx))
|
||||
{
|
||||
if (item.second->Handle(ctx)) {
|
||||
ctx.path = oldPath;
|
||||
return true;
|
||||
}
|
||||
@@ -64,11 +74,10 @@ bool MountableServer::Handle(ServerContext& ctx)
|
||||
break;
|
||||
}
|
||||
}
|
||||
ctx.path=oldPath;
|
||||
if(this->root && this->root->Handle(ctx)) return true;
|
||||
ctx.path = oldPath;
|
||||
if (this->root && this->root->Handle(ctx))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
MountableServer::~MountableServer()
|
||||
{
|
||||
}
|
||||
}
|
||||
MountableServer::~MountableServer() {}
|
||||
} // namespace Tesses::Framework::Http
|
||||
|
||||
@@ -1,97 +1,104 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/Http/RouteServer.hpp"
|
||||
|
||||
namespace Tesses::Framework::Http
|
||||
{
|
||||
|
||||
RouteServer::RouteServerRoute::RouteServerRoute(std::string route, std::string method, ServerRequestHandler handler) : method(method), handler(handler)
|
||||
{
|
||||
auto path = Tesses::Framework::Filesystem::VFSPath::ParseUriPath(route);
|
||||
for(auto item : path.path)
|
||||
{
|
||||
if(item.size() > 2 && item[0] == '{' && item[item.size()-1] == '}')
|
||||
{
|
||||
this->parts.emplace_back( item.substr(1,item.size()-2),true);
|
||||
}
|
||||
else {
|
||||
this->parts.emplace_back(item,false);
|
||||
}
|
||||
namespace Tesses::Framework::Http {
|
||||
|
||||
RouteServer::RouteServerRoute::RouteServerRoute(std::string route,
|
||||
std::string method,
|
||||
ServerRequestHandler handler)
|
||||
: method(method), handler(handler) {
|
||||
auto path = Tesses::Framework::Filesystem::VFSPath::ParseUriPath(route);
|
||||
for (auto item : path.path) {
|
||||
if (item.size() > 2 && item[0] == '{' && item[item.size() - 1] == '}') {
|
||||
this->parts.emplace_back(item.substr(1, item.size() - 2), true);
|
||||
} else {
|
||||
this->parts.emplace_back(item, false);
|
||||
}
|
||||
}
|
||||
bool RouteServer::RouteServerRoute::Equals(Tesses::Framework::Filesystem::VFSPath& path, HttpDictionary& args)
|
||||
{
|
||||
if(path.path.size() != this->parts.size()) return false;
|
||||
|
||||
|
||||
for(size_t i = 0; i < this->parts.size(); i++)
|
||||
{
|
||||
auto& part = this->parts[i];
|
||||
if(part.second)
|
||||
args.SetValue(part.first, Tesses::Framework::Http::HttpUtils::UrlPathDecode(path.path[i]));
|
||||
else if(part.first != path.path[i]) return false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
RouteServer::RouteServer(std::shared_ptr<IHttpServer> root) : root(root)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void RouteServer::Add(std::string method, std::string pattern, ServerRequestHandler handler)
|
||||
{
|
||||
this->routes.emplace_back(pattern,method,handler);
|
||||
}
|
||||
|
||||
bool RouteServer::Handle(ServerContext& ctx)
|
||||
{
|
||||
auto pathArgs = ctx.pathArguments;
|
||||
auto path = Tesses::Framework::Filesystem::VFSPath::ParseUriPath(ctx.path);
|
||||
for(auto& svr : this->routes)
|
||||
{
|
||||
if(svr.method != ctx.method && !((svr.method == "GET" && ctx.method == "HEAD") || (svr.method == "HEAD" && ctx.method == "GET"))) continue;
|
||||
ctx.pathArguments = pathArgs;
|
||||
if(svr.Equals(path, ctx.pathArguments) && svr.handler && svr.handler(ctx))
|
||||
return true;
|
||||
|
||||
}
|
||||
ctx.pathArguments = pathArgs;
|
||||
|
||||
if(this->root)
|
||||
return this->root->Handle(ctx);
|
||||
}
|
||||
bool RouteServer::RouteServerRoute::Equals(
|
||||
Tesses::Framework::Filesystem::VFSPath &path, HttpDictionary &args) {
|
||||
if (path.path.size() != this->parts.size())
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < this->parts.size(); i++) {
|
||||
auto &part = this->parts[i];
|
||||
if (part.second)
|
||||
args.SetValue(part.first,
|
||||
Tesses::Framework::Http::HttpUtils::UrlPathDecode(
|
||||
path.path[i]));
|
||||
else if (part.first != path.path[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
void RouteServer::Get(std::string pattern, ServerRequestHandler handler)
|
||||
{
|
||||
Add("GET",pattern,handler);
|
||||
}
|
||||
void RouteServer::Post(std::string pattern, ServerRequestHandler handler)
|
||||
{
|
||||
Add("POST",pattern,handler);
|
||||
}
|
||||
void RouteServer::Put(std::string pattern, ServerRequestHandler handler)
|
||||
{
|
||||
Add("PUT",pattern,handler);
|
||||
}
|
||||
void RouteServer::Patch(std::string pattern, ServerRequestHandler handler)
|
||||
{
|
||||
Add("PATCH",pattern,handler);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void RouteServer::Delete(std::string pattern, ServerRequestHandler handler)
|
||||
{
|
||||
Add("DELETE",pattern,handler);
|
||||
}
|
||||
RouteServer::RouteServer(std::shared_ptr<IHttpServer> root) : root(root) {}
|
||||
|
||||
void RouteServer::Trace(std::string pattern, ServerRequestHandler handler)
|
||||
{
|
||||
Add("TRACE",pattern,handler);
|
||||
}
|
||||
void RouteServer::Options(std::string pattern, ServerRequestHandler handler)
|
||||
{
|
||||
Add("OPTIONS",pattern,handler);
|
||||
}
|
||||
}
|
||||
void RouteServer::Add(std::string method, std::string pattern,
|
||||
ServerRequestHandler handler) {
|
||||
this->routes.emplace_back(pattern, method, handler);
|
||||
}
|
||||
|
||||
bool RouteServer::Handle(ServerContext &ctx) {
|
||||
auto pathArgs = ctx.pathArguments;
|
||||
auto path = Tesses::Framework::Filesystem::VFSPath::ParseUriPath(ctx.path);
|
||||
for (auto &svr : this->routes) {
|
||||
if (svr.method != ctx.method &&
|
||||
!((svr.method == "GET" && ctx.method == "HEAD") ||
|
||||
(svr.method == "HEAD" && ctx.method == "GET")))
|
||||
continue;
|
||||
ctx.pathArguments = pathArgs;
|
||||
if (svr.Equals(path, ctx.pathArguments) && svr.handler &&
|
||||
svr.handler(ctx))
|
||||
return true;
|
||||
}
|
||||
ctx.pathArguments = pathArgs;
|
||||
|
||||
if (this->root)
|
||||
return this->root->Handle(ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
void RouteServer::Get(std::string pattern, ServerRequestHandler handler) {
|
||||
Add("GET", pattern, handler);
|
||||
}
|
||||
void RouteServer::Post(std::string pattern, ServerRequestHandler handler) {
|
||||
Add("POST", pattern, handler);
|
||||
}
|
||||
void RouteServer::Put(std::string pattern, ServerRequestHandler handler) {
|
||||
Add("PUT", pattern, handler);
|
||||
}
|
||||
void RouteServer::Patch(std::string pattern, ServerRequestHandler handler) {
|
||||
Add("PATCH", pattern, handler);
|
||||
}
|
||||
|
||||
void RouteServer::Delete(std::string pattern, ServerRequestHandler handler) {
|
||||
Add("DELETE", pattern, handler);
|
||||
}
|
||||
|
||||
void RouteServer::Trace(std::string pattern, ServerRequestHandler handler) {
|
||||
Add("TRACE", pattern, handler);
|
||||
}
|
||||
void RouteServer::Options(std::string pattern, ServerRequestHandler handler) {
|
||||
Add("OPTIONS", pattern, handler);
|
||||
}
|
||||
} // namespace Tesses::Framework::Http
|
||||
@@ -1,67 +1,79 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/Http/WebSocket.hpp"
|
||||
namespace Tesses::Framework::Http
|
||||
{
|
||||
namespace Tesses::Framework::Http {
|
||||
|
||||
CallbackWebSocketConnection::CallbackWebSocketConnection()
|
||||
{
|
||||
CallbackWebSocketConnection::CallbackWebSocketConnection() {}
|
||||
CallbackWebSocketConnection::CallbackWebSocketConnection(
|
||||
std::function<void(std::function<void(WebSocketMessage &)>,
|
||||
std::function<void()>, std::function<void()>)>
|
||||
onOpen,
|
||||
std::function<void(WebSocketMessage &)> onReceive,
|
||||
std::function<void(bool)> onClose) {
|
||||
this->onOpen = onOpen;
|
||||
this->onReceive = onReceive;
|
||||
this->onClose = onClose;
|
||||
}
|
||||
|
||||
}
|
||||
CallbackWebSocketConnection::CallbackWebSocketConnection(std::function<void(std::function<void(WebSocketMessage&)>,std::function<void()>,std::function<void()>)> onOpen, std::function<void(WebSocketMessage&)> onReceive, std::function<void(bool)> onClose)
|
||||
{
|
||||
this->onOpen = onOpen;
|
||||
this->onReceive = onReceive;
|
||||
this->onClose = onClose;
|
||||
}
|
||||
void CallbackWebSocketConnection::OnOpen(
|
||||
std::function<void(WebSocketMessage &)> sendMessage,
|
||||
std::function<void()> ping, std::function<void()> closeConnection) {
|
||||
if (this->onOpen)
|
||||
this->onOpen(sendMessage, ping, closeConnection);
|
||||
}
|
||||
void CallbackWebSocketConnection::OnReceive(WebSocketMessage &message) {
|
||||
if (this->onReceive)
|
||||
this->onReceive(message);
|
||||
}
|
||||
void CallbackWebSocketConnection::OnClose(bool clean) {
|
||||
if (this->onClose)
|
||||
this->onClose(clean);
|
||||
}
|
||||
|
||||
void CallbackWebSocketConnection::OnOpen(std::function<void(WebSocketMessage&)> sendMessage, std::function<void()> ping, std::function<void()> closeConnection)
|
||||
{
|
||||
if(this->onOpen)
|
||||
this->onOpen(sendMessage,ping,closeConnection);
|
||||
}
|
||||
void CallbackWebSocketConnection::OnReceive(WebSocketMessage& message)
|
||||
{
|
||||
if(this->onReceive)
|
||||
this->onReceive(message);
|
||||
}
|
||||
void CallbackWebSocketConnection::OnClose(bool clean)
|
||||
{
|
||||
if(this->onClose)
|
||||
this->onClose(clean);
|
||||
}
|
||||
WebSocketMessage::WebSocketMessage() {
|
||||
this->isBinary = false;
|
||||
this->data = {};
|
||||
}
|
||||
WebSocketMessage::WebSocketMessage(std::vector<uint8_t> data) {
|
||||
this->isBinary = true;
|
||||
this->data = data;
|
||||
}
|
||||
WebSocketMessage::WebSocketMessage(const void *data, size_t len) {
|
||||
this->isBinary = true;
|
||||
this->data = {};
|
||||
this->data.insert(this->data.end(), (uint8_t *)data,
|
||||
((uint8_t *)data) + len);
|
||||
}
|
||||
WebSocketMessage::WebSocketMessage(std::string message) {
|
||||
this->isBinary = false;
|
||||
this->data = {};
|
||||
this->data.insert(this->data.end(), message.begin(), message.end());
|
||||
}
|
||||
std::string WebSocketMessage::ToString() {
|
||||
std::string str = {};
|
||||
str.insert(str.end(), this->data.begin(), this->data.end());
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
WebSocketMessage::WebSocketMessage()
|
||||
{
|
||||
this->isBinary=false;
|
||||
this->data={};
|
||||
}
|
||||
WebSocketMessage::WebSocketMessage(std::vector<uint8_t> data)
|
||||
{
|
||||
this->isBinary = true;
|
||||
this->data = data;
|
||||
}
|
||||
WebSocketMessage::WebSocketMessage(const void* data, size_t len)
|
||||
{
|
||||
this->isBinary=true;
|
||||
this->data={};
|
||||
this->data.insert(this->data.end(),(uint8_t*)data,((uint8_t*)data)+len);
|
||||
}
|
||||
WebSocketMessage::WebSocketMessage(std::string message)
|
||||
{
|
||||
this->isBinary=false;
|
||||
this->data={};
|
||||
this->data.insert(this->data.end(),message.begin(), message.end());
|
||||
}
|
||||
std::string WebSocketMessage::ToString()
|
||||
{
|
||||
std::string str = {};
|
||||
str.insert(str.end(),this->data.begin(),this->data.end());
|
||||
return str;
|
||||
}
|
||||
|
||||
void SendWebSocketMessage(std::function<void(WebSocketMessage&)> cb, std::string text)
|
||||
{
|
||||
WebSocketMessage message(text);
|
||||
cb(message);
|
||||
}
|
||||
}
|
||||
void SendWebSocketMessage(std::function<void(WebSocketMessage &)> cb,
|
||||
std::string text) {
|
||||
WebSocketMessage message(text);
|
||||
cb(message);
|
||||
}
|
||||
} // namespace Tesses::Framework::Http
|
||||
@@ -1,149 +1,159 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/Mail/Smtp.hpp"
|
||||
#include "TessesFramework/Crypto/Crypto.hpp"
|
||||
#include "TessesFramework/Http/HttpUtils.hpp"
|
||||
#include "TessesFramework/Streams/MemoryStream.hpp"
|
||||
#include "TessesFramework/TextStreams/StreamWriter.hpp"
|
||||
#include "TessesFramework/Http/HttpUtils.hpp"
|
||||
namespace Tesses::Framework::Mail
|
||||
{
|
||||
static void SMTP_ATTACHMENT_WRITE(std::string& myStr, std::shared_ptr<Tesses::Framework::Streams::MemoryStream> strm)
|
||||
{
|
||||
std::string txt = Tesses::Framework::Crypto::Base64_Encode(strm->GetBuffer());
|
||||
bool first=true;
|
||||
size_t read;
|
||||
size_t offset = 0;
|
||||
do {
|
||||
if(!first) myStr.append("\r\n");
|
||||
|
||||
read = std::min<size_t>(72, txt.size()-offset);
|
||||
|
||||
if(read > 0)
|
||||
myStr.insert(myStr.end(),txt.begin()+offset,txt.begin()+offset+read);
|
||||
|
||||
offset+=read;
|
||||
first=false;
|
||||
} while(read > 0);
|
||||
namespace Tesses::Framework::Mail {
|
||||
static void SMTP_ATTACHMENT_WRITE(
|
||||
std::string &myStr,
|
||||
std::shared_ptr<Tesses::Framework::Streams::MemoryStream> strm) {
|
||||
std::string txt =
|
||||
Tesses::Framework::Crypto::Base64_Encode(strm->GetBuffer());
|
||||
bool first = true;
|
||||
size_t read;
|
||||
size_t offset = 0;
|
||||
do {
|
||||
if (!first)
|
||||
myStr.append("\r\n");
|
||||
|
||||
}
|
||||
read = std::min<size_t>(72, txt.size() - offset);
|
||||
|
||||
SMTPBody::~SMTPBody()
|
||||
{
|
||||
if (read > 0)
|
||||
myStr.insert(myStr.end(), txt.begin() + offset,
|
||||
txt.begin() + offset + read);
|
||||
|
||||
}
|
||||
offset += read;
|
||||
first = false;
|
||||
} while (read > 0);
|
||||
}
|
||||
|
||||
SMTPStringBody::SMTPStringBody()
|
||||
{
|
||||
|
||||
}
|
||||
SMTPStringBody::SMTPStringBody(std::string text, std::string mimeType)
|
||||
{
|
||||
this->text = text;
|
||||
this->mimeType=mimeType;
|
||||
}
|
||||
void SMTPStringBody::Write(std::shared_ptr<Tesses::Framework::Streams::Stream> strm)
|
||||
{
|
||||
strm->WriteBlock((const uint8_t*)this->text.c_str(),this->text.size());
|
||||
}
|
||||
SMTPStreamBody::SMTPStreamBody(std::string mimeType,std::shared_ptr<Tesses::Framework::Streams::Stream> strm)
|
||||
{
|
||||
this->mimeType = mimeType;
|
||||
this->stream = strm;
|
||||
}
|
||||
|
||||
void SMTPStreamBody::Write(std::shared_ptr<Tesses::Framework::Streams::Stream> strm)
|
||||
{
|
||||
this->stream->Seek(0L,Tesses::Framework::Streams::SeekOrigin::Begin);
|
||||
this->stream->CopyTo(strm);
|
||||
}
|
||||
SMTPStreamBody::~SMTPStreamBody()
|
||||
{
|
||||
}
|
||||
SMTPClient::SMTPClient(std::shared_ptr<Tesses::Framework::Streams::Stream> stream)
|
||||
{
|
||||
this->strm = stream;
|
||||
this->body = nullptr;
|
||||
}
|
||||
|
||||
void SMTPClient::Send()
|
||||
{
|
||||
std::string emailHeaders = "EHLO ";
|
||||
emailHeaders.append(this->domain);
|
||||
emailHeaders.append("\r\nAUTH LOGIN\r\n");
|
||||
std::vector<uint8_t> data;
|
||||
data.insert(data.begin(), this->username.begin(),this->username.end());
|
||||
SMTPBody::~SMTPBody() {}
|
||||
|
||||
emailHeaders.append(Tesses::Framework::Crypto::Base64_Encode(data));
|
||||
emailHeaders.append("\r\n");
|
||||
data.clear();
|
||||
data.insert(data.begin(),this->password.begin(),this->password.end());
|
||||
emailHeaders.append(Tesses::Framework::Crypto::Base64_Encode(data));
|
||||
emailHeaders.append("\r\n");
|
||||
emailHeaders.append("MAIL FROM:<");
|
||||
|
||||
emailHeaders.append(this->from);
|
||||
emailHeaders.append(">\r\n");
|
||||
|
||||
emailHeaders.append("RCPT TO:<");
|
||||
emailHeaders.append(to);
|
||||
emailHeaders.append(">\r\n");
|
||||
|
||||
emailHeaders.append("DATA\r\n");
|
||||
std::string boundary = "joel&<<94292025209248";
|
||||
emailHeaders.append("From: ");
|
||||
emailHeaders.append(this->from_name);
|
||||
emailHeaders.append(" <");
|
||||
emailHeaders.append(this->from);
|
||||
emailHeaders.append(">\r\nSubject: ");
|
||||
emailHeaders.append(this->subject);
|
||||
emailHeaders.append("\r\n");
|
||||
emailHeaders.append("Content-Type: multipart/mixed; boundary=");
|
||||
SMTPStringBody::SMTPStringBody() {}
|
||||
SMTPStringBody::SMTPStringBody(std::string text, std::string mimeType) {
|
||||
this->text = text;
|
||||
this->mimeType = mimeType;
|
||||
}
|
||||
void SMTPStringBody::Write(
|
||||
std::shared_ptr<Tesses::Framework::Streams::Stream> strm) {
|
||||
strm->WriteBlock((const uint8_t *)this->text.c_str(), this->text.size());
|
||||
}
|
||||
SMTPStreamBody::SMTPStreamBody(
|
||||
std::string mimeType,
|
||||
std::shared_ptr<Tesses::Framework::Streams::Stream> strm) {
|
||||
this->mimeType = mimeType;
|
||||
this->stream = strm;
|
||||
}
|
||||
|
||||
void SMTPStreamBody::Write(
|
||||
std::shared_ptr<Tesses::Framework::Streams::Stream> strm) {
|
||||
this->stream->Seek(0L, Tesses::Framework::Streams::SeekOrigin::Begin);
|
||||
this->stream->CopyTo(strm);
|
||||
}
|
||||
SMTPStreamBody::~SMTPStreamBody() {}
|
||||
SMTPClient::SMTPClient(
|
||||
std::shared_ptr<Tesses::Framework::Streams::Stream> stream) {
|
||||
this->strm = stream;
|
||||
this->body = nullptr;
|
||||
}
|
||||
|
||||
void SMTPClient::Send() {
|
||||
std::string emailHeaders = "EHLO ";
|
||||
emailHeaders.append(this->domain);
|
||||
emailHeaders.append("\r\nAUTH LOGIN\r\n");
|
||||
std::vector<uint8_t> data;
|
||||
data.insert(data.begin(), this->username.begin(), this->username.end());
|
||||
|
||||
emailHeaders.append(Tesses::Framework::Crypto::Base64_Encode(data));
|
||||
emailHeaders.append("\r\n");
|
||||
data.clear();
|
||||
data.insert(data.begin(), this->password.begin(), this->password.end());
|
||||
emailHeaders.append(Tesses::Framework::Crypto::Base64_Encode(data));
|
||||
emailHeaders.append("\r\n");
|
||||
emailHeaders.append("MAIL FROM:<");
|
||||
|
||||
emailHeaders.append(this->from);
|
||||
emailHeaders.append(">\r\n");
|
||||
|
||||
emailHeaders.append("RCPT TO:<");
|
||||
emailHeaders.append(to);
|
||||
emailHeaders.append(">\r\n");
|
||||
|
||||
emailHeaders.append("DATA\r\n");
|
||||
std::string boundary = "joel&<<94292025209248";
|
||||
emailHeaders.append("From: ");
|
||||
emailHeaders.append(this->from_name);
|
||||
emailHeaders.append(" <");
|
||||
emailHeaders.append(this->from);
|
||||
emailHeaders.append(">\r\nSubject: ");
|
||||
emailHeaders.append(this->subject);
|
||||
emailHeaders.append("\r\n");
|
||||
emailHeaders.append("Content-Type: multipart/mixed; boundary=");
|
||||
emailHeaders.append(boundary);
|
||||
emailHeaders.append("\r\n\r\n--");
|
||||
emailHeaders.append(boundary);
|
||||
emailHeaders.append("\r\nContent-Type: ");
|
||||
emailHeaders.append(this->body->mimeType);
|
||||
emailHeaders.append("; charset=utf-8\r\n\r\n");
|
||||
Tesses::Framework::TextStreams::StreamWriter writer(this->strm);
|
||||
writer.Write(emailHeaders);
|
||||
this->body->Write(this->strm);
|
||||
|
||||
if (this->attachments.empty()) {
|
||||
emailHeaders = "\r\n--";
|
||||
emailHeaders.append(boundary);
|
||||
emailHeaders.append("\r\n\r\n--");
|
||||
emailHeaders.append(boundary);
|
||||
emailHeaders.append("\r\nContent-Type: ");
|
||||
emailHeaders.append(this->body->mimeType);
|
||||
emailHeaders.append("; charset=utf-8\r\n\r\n");
|
||||
Tesses::Framework::TextStreams::StreamWriter writer(this->strm);
|
||||
emailHeaders.append("--\r\n.\r\n");
|
||||
writer.Write(emailHeaders);
|
||||
this->body->Write(this->strm);
|
||||
|
||||
if(this->attachments.empty())
|
||||
{
|
||||
emailHeaders="\r\n--";
|
||||
} else {
|
||||
for (auto item : this->attachments) {
|
||||
emailHeaders = "\r\n--";
|
||||
emailHeaders.append(boundary);
|
||||
emailHeaders.append("--\r\n.\r\n");
|
||||
emailHeaders.append("\r\n");
|
||||
emailHeaders.append("Content-Type: ");
|
||||
emailHeaders.append(item.second->mimeType);
|
||||
emailHeaders.append("; name=\"");
|
||||
std::string name =
|
||||
Tesses::Framework::Http::HttpUtils::UrlPathEncode(item.first,
|
||||
true);
|
||||
emailHeaders.append(name);
|
||||
emailHeaders.append(
|
||||
"\"\r\nContent-Transfer-Encoding: "
|
||||
"base64\r\nContent-Disposition: attachment; filename=\"");
|
||||
emailHeaders.append(name);
|
||||
emailHeaders.append("\"\r\n\r\n");
|
||||
std::shared_ptr<Tesses::Framework::Streams::MemoryStream> ms =
|
||||
std::make_shared<Tesses::Framework::Streams::MemoryStream>(
|
||||
true);
|
||||
item.second->Write(ms);
|
||||
SMTP_ATTACHMENT_WRITE(emailHeaders, ms);
|
||||
// emailHeaders.append(Tesses::Framework::Crypto::Base64_Encode(ms.GetBuffer()));
|
||||
writer.Write(emailHeaders);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(auto item : this->attachments)
|
||||
{
|
||||
emailHeaders="\r\n--";
|
||||
emailHeaders.append(boundary);
|
||||
emailHeaders.append("\r\n");
|
||||
emailHeaders.append("Content-Type: ");
|
||||
emailHeaders.append(item.second->mimeType);
|
||||
emailHeaders.append("; name=\"");
|
||||
std::string name = Tesses::Framework::Http::HttpUtils::UrlPathEncode(item.first,true);
|
||||
emailHeaders.append(name);
|
||||
emailHeaders.append("\"\r\nContent-Transfer-Encoding: base64\r\nContent-Disposition: attachment; filename=\"");
|
||||
emailHeaders.append(name);
|
||||
emailHeaders.append("\"\r\n\r\n");
|
||||
std::shared_ptr<Tesses::Framework::Streams::MemoryStream> ms = std::make_shared<Tesses::Framework::Streams::MemoryStream>(true);
|
||||
item.second->Write(ms);
|
||||
SMTP_ATTACHMENT_WRITE(emailHeaders,ms);
|
||||
//emailHeaders.append(Tesses::Framework::Crypto::Base64_Encode(ms.GetBuffer()));
|
||||
writer.Write(emailHeaders);
|
||||
}
|
||||
|
||||
emailHeaders="\r\n--";
|
||||
emailHeaders.append(boundary);
|
||||
emailHeaders.append("--\r\n.\r\n");
|
||||
writer.Write(emailHeaders);
|
||||
}
|
||||
emailHeaders = "\r\n--";
|
||||
emailHeaders.append(boundary);
|
||||
emailHeaders.append("--\r\n.\r\n");
|
||||
writer.Write(emailHeaders);
|
||||
}
|
||||
}
|
||||
|
||||
SMTPClient::~SMTPClient()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
SMTPClient::~SMTPClient() {}
|
||||
} // namespace Tesses::Framework::Mail
|
||||
@@ -1,338 +1,338 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/Platform/Environment.hpp"
|
||||
#include "TessesFramework/Http/HttpUtils.hpp"
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS)
|
||||
#include "sago/platform_folders.h"
|
||||
#endif
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#include "TessesFramework/Filesystem/VFSFix.hpp"
|
||||
#include "TessesFramework/Text/StringConverter.hpp"
|
||||
#include <windows.h>
|
||||
|
||||
using namespace Tesses::Framework::Text::StringConverter;
|
||||
#endif
|
||||
#if !defined(_WIN32)
|
||||
extern char** environ;
|
||||
#endif
|
||||
#if !defined(_WIN32)
|
||||
extern char **environ;
|
||||
#endif
|
||||
|
||||
using namespace Tesses::Framework::Filesystem;
|
||||
namespace Tesses::Framework::Platform::Environment
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
const char EnvPathSeperator=';';
|
||||
#else
|
||||
const char EnvPathSeperator=':';
|
||||
#endif
|
||||
PortableAppConfig portable_config;
|
||||
namespace Tesses::Framework::Platform::Environment {
|
||||
#if defined(_WIN32)
|
||||
const char EnvPathSeperator = ';';
|
||||
#else
|
||||
const char EnvPathSeperator = ':';
|
||||
#endif
|
||||
PortableAppConfig portable_config;
|
||||
|
||||
namespace SpecialFolders
|
||||
{
|
||||
VFSPath GetTemp()
|
||||
{
|
||||
namespace SpecialFolders {
|
||||
VFSPath GetTemp() {
|
||||
|
||||
if(portable_config.temp)
|
||||
return *portable_config.temp;
|
||||
return std::filesystem::temp_directory_path().string();
|
||||
}
|
||||
VFSPath GetHomeFolder()
|
||||
{
|
||||
if (portable_config.temp)
|
||||
return *portable_config.temp;
|
||||
return std::filesystem::temp_directory_path().string();
|
||||
}
|
||||
VFSPath GetHomeFolder() {
|
||||
|
||||
if(portable_config.user)
|
||||
return *portable_config.user;
|
||||
if (portable_config.user)
|
||||
return *portable_config.user;
|
||||
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE)
|
||||
return sago::getHomeDir();
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
return (std::string)"/home/web_user";
|
||||
#elif defined(__ANDROID__)
|
||||
return (std::string)"/sdcard/TF_User";
|
||||
#else
|
||||
return (std::string)"/TF_User";
|
||||
#endif
|
||||
}
|
||||
VFSPath GetDownloads()
|
||||
{
|
||||
if(portable_config.downloads)
|
||||
return *portable_config.downloads;
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE)
|
||||
return sago::getHomeDir();
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
return (std::string) "/home/web_user";
|
||||
#elif defined(__ANDROID__)
|
||||
return (std::string) "/sdcard/TF_User";
|
||||
#else
|
||||
return (std::string) "/TF_User";
|
||||
#endif
|
||||
}
|
||||
VFSPath GetDownloads() {
|
||||
if (portable_config.downloads)
|
||||
return *portable_config.downloads;
|
||||
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE)
|
||||
return sago::getDownloadFolder();
|
||||
#elif defined(__ANDROID__)
|
||||
return (std::string)"/sdcard/Download";
|
||||
#else
|
||||
return GetHomeFolder() / "Downloads";
|
||||
#endif
|
||||
}
|
||||
VFSPath GetMusic()
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE)
|
||||
return sago::getDownloadFolder();
|
||||
#elif defined(__ANDROID__)
|
||||
return (std::string) "/sdcard/Download";
|
||||
#else
|
||||
return GetHomeFolder() / "Downloads";
|
||||
#endif
|
||||
}
|
||||
VFSPath GetMusic() {
|
||||
|
||||
if(portable_config.music)
|
||||
return *portable_config.music;
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE)
|
||||
return sago::getMusicFolder();
|
||||
#elif defined(__ANDROID__)
|
||||
return (std::string)"/sdcard/Music";
|
||||
#else
|
||||
return GetHomeFolder() / "Music";
|
||||
#endif
|
||||
}
|
||||
VFSPath GetPictures()
|
||||
{
|
||||
if (portable_config.music)
|
||||
return *portable_config.music;
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE)
|
||||
return sago::getMusicFolder();
|
||||
#elif defined(__ANDROID__)
|
||||
return (std::string) "/sdcard/Music";
|
||||
#else
|
||||
return GetHomeFolder() / "Music";
|
||||
#endif
|
||||
}
|
||||
VFSPath GetPictures() {
|
||||
|
||||
if(portable_config.pictures)
|
||||
return *portable_config.pictures;
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE)
|
||||
return sago::getPicturesFolder();
|
||||
#elif defined(__ANDROID__)
|
||||
return (std::string)"/sdcard/Pictures";
|
||||
#else
|
||||
return GetHomeFolder() / "Pictures";
|
||||
#endif
|
||||
}
|
||||
VFSPath GetVideos()
|
||||
{
|
||||
if(portable_config.videos)
|
||||
return *portable_config.videos;
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE)
|
||||
return sago::getVideoFolder();
|
||||
#elif defined(__ANDROID__)
|
||||
return (std::string)"/sdcard/Movies";
|
||||
#else
|
||||
return GetHomeFolder() / "Videos";
|
||||
#endif
|
||||
}
|
||||
VFSPath GetDocuments()
|
||||
{
|
||||
if (portable_config.pictures)
|
||||
return *portable_config.pictures;
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE)
|
||||
return sago::getPicturesFolder();
|
||||
#elif defined(__ANDROID__)
|
||||
return (std::string) "/sdcard/Pictures";
|
||||
#else
|
||||
return GetHomeFolder() / "Pictures";
|
||||
#endif
|
||||
}
|
||||
VFSPath GetVideos() {
|
||||
if (portable_config.videos)
|
||||
return *portable_config.videos;
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE)
|
||||
return sago::getVideoFolder();
|
||||
#elif defined(__ANDROID__)
|
||||
return (std::string) "/sdcard/Movies";
|
||||
#else
|
||||
return GetHomeFolder() / "Videos";
|
||||
#endif
|
||||
}
|
||||
VFSPath GetDocuments() {
|
||||
|
||||
if(portable_config.documents)
|
||||
return *portable_config.documents;
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE)
|
||||
return sago::getDocumentsFolder();
|
||||
#elif defined(__ANDROID__)
|
||||
return (std::string)"/sdcard/Documents";
|
||||
#else
|
||||
return GetHomeFolder() / "Documents";
|
||||
#endif
|
||||
}
|
||||
VFSPath GetConfig()
|
||||
{
|
||||
if (portable_config.documents)
|
||||
return *portable_config.documents;
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE)
|
||||
return sago::getDocumentsFolder();
|
||||
#elif defined(__ANDROID__)
|
||||
return (std::string) "/sdcard/Documents";
|
||||
#else
|
||||
return GetHomeFolder() / "Documents";
|
||||
#endif
|
||||
}
|
||||
VFSPath GetConfig() {
|
||||
|
||||
if(portable_config.config)
|
||||
return *portable_config.config;
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE)
|
||||
return sago::getConfigHome();
|
||||
#else
|
||||
return GetHomeFolder() / "Config";
|
||||
#endif
|
||||
}
|
||||
VFSPath GetDesktop()
|
||||
{
|
||||
if(portable_config.desktop)
|
||||
return *portable_config.desktop;
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE)
|
||||
return sago::getDesktopFolder();
|
||||
#else
|
||||
return GetHomeFolder() / "Desktop";
|
||||
#endif
|
||||
}
|
||||
VFSPath GetState()
|
||||
{
|
||||
if (portable_config.config)
|
||||
return *portable_config.config;
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE)
|
||||
return sago::getConfigHome();
|
||||
#else
|
||||
return GetHomeFolder() / "Config";
|
||||
#endif
|
||||
}
|
||||
VFSPath GetDesktop() {
|
||||
if (portable_config.desktop)
|
||||
return *portable_config.desktop;
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE)
|
||||
return sago::getDesktopFolder();
|
||||
#else
|
||||
return GetHomeFolder() / "Desktop";
|
||||
#endif
|
||||
}
|
||||
VFSPath GetState() {
|
||||
|
||||
if(portable_config.state)
|
||||
return *portable_config.state;
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE)
|
||||
return sago::getStateDir();
|
||||
#else
|
||||
return GetHomeFolder() / "State";
|
||||
#endif
|
||||
}
|
||||
VFSPath GetCache()
|
||||
{
|
||||
if (portable_config.state)
|
||||
return *portable_config.state;
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE)
|
||||
return sago::getStateDir();
|
||||
#else
|
||||
return GetHomeFolder() / "State";
|
||||
#endif
|
||||
}
|
||||
VFSPath GetCache() {
|
||||
|
||||
if(portable_config.cache)
|
||||
return *portable_config.cache;
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE)
|
||||
return sago::getCacheDir();
|
||||
#else
|
||||
return GetHomeFolder() / "Cache";
|
||||
#endif
|
||||
}
|
||||
VFSPath GetData()
|
||||
{
|
||||
if (portable_config.cache)
|
||||
return *portable_config.cache;
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE)
|
||||
return sago::getCacheDir();
|
||||
#else
|
||||
return GetHomeFolder() / "Cache";
|
||||
#endif
|
||||
}
|
||||
VFSPath GetData() {
|
||||
|
||||
if(portable_config.data)
|
||||
return *portable_config.data;
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE)
|
||||
return sago::getDataHome();
|
||||
#else
|
||||
return GetHomeFolder() / "Data";
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if (portable_config.data)
|
||||
return *portable_config.data;
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE)
|
||||
return sago::getDataHome();
|
||||
#else
|
||||
return GetHomeFolder() / "Data";
|
||||
#endif
|
||||
}
|
||||
} // namespace SpecialFolders
|
||||
|
||||
VFSPath GetRealExecutablePath(VFSPath realPath)
|
||||
{
|
||||
using namespace Tesses::Framework::Http;
|
||||
VFSPath GetRealExecutablePath(VFSPath realPath) {
|
||||
using namespace Tesses::Framework::Http;
|
||||
|
||||
|
||||
if(!realPath.relative) return realPath;
|
||||
if(LocalFS->FileExists(realPath)) return realPath.MakeAbsolute();
|
||||
const char* path = std::getenv("PATH");
|
||||
#if defined(_WIN32)
|
||||
const char* pathext = std::getenv("PATHEXT");
|
||||
auto pext = HttpUtils::SplitString(pathext,";");
|
||||
pext.push_back({});
|
||||
auto pathParts = HttpUtils::SplitString(path,";");
|
||||
for(auto item : pathParts)
|
||||
{
|
||||
|
||||
auto newPath = LocalFS->SystemToVFSPath(item) / realPath;
|
||||
for(auto item2 : pext)
|
||||
{
|
||||
auto newPathExt = newPath + item2;
|
||||
if(LocalFS->FileExists(newPathExt)) return newPathExt;
|
||||
}
|
||||
if(LocalFS->FileExists(newPath)) return newPath;
|
||||
}
|
||||
if (!realPath.relative)
|
||||
return realPath;
|
||||
#else
|
||||
|
||||
auto pathParts = HttpUtils::SplitString(path,":");
|
||||
for(auto item : pathParts)
|
||||
{
|
||||
auto newPath = LocalFS->SystemToVFSPath(item) / realPath;
|
||||
if(LocalFS->FileExists(newPath)) return newPath;
|
||||
}
|
||||
if (LocalFS->FileExists(realPath))
|
||||
return realPath.MakeAbsolute();
|
||||
#endif
|
||||
}
|
||||
const char *path = std::getenv("PATH");
|
||||
#if defined(_WIN32)
|
||||
const char *pathext = std::getenv("PATHEXT");
|
||||
auto pext = HttpUtils::SplitString(pathext, ";");
|
||||
pext.push_back({});
|
||||
auto pathParts = HttpUtils::SplitString(path, ";");
|
||||
for (auto item : pathParts) {
|
||||
|
||||
std::optional<std::string> GetVariable(std::string name)
|
||||
{
|
||||
auto res = std::getenv(name.c_str());
|
||||
if(res == nullptr) return std::nullopt;
|
||||
std::string value = res;
|
||||
return value;
|
||||
}
|
||||
void SetVariable(std::string name, std::optional<std::string> var)
|
||||
{
|
||||
if (var)
|
||||
#if defined(_WIN32)
|
||||
{
|
||||
std::u16string nameu16 = {};
|
||||
|
||||
std::u16string varu16 = {};
|
||||
|
||||
UTF16::FromUTF8(nameu16, name);
|
||||
UTF16::FromUTF8(varu16, var.value());
|
||||
SetEnvironmentVariableW((LPCWSTR)nameu16.c_str(),(LPCWSTR)varu16.c_str());
|
||||
auto newPath = LocalFS->SystemToVFSPath(item) / realPath;
|
||||
for (auto item2 : pext) {
|
||||
auto newPathExt = newPath + item2;
|
||||
if (LocalFS->FileExists(newPathExt))
|
||||
return newPathExt;
|
||||
}
|
||||
#else
|
||||
setenv(name.c_str(), var->c_str(),1);
|
||||
#endif
|
||||
else
|
||||
#if defined(_WIN32)
|
||||
{
|
||||
std::u16string nameu16 = {};
|
||||
if (LocalFS->FileExists(newPath))
|
||||
return newPath;
|
||||
}
|
||||
return realPath;
|
||||
#else
|
||||
|
||||
UTF16::FromUTF8(nameu16, name);
|
||||
auto pathParts = HttpUtils::SplitString(path, ":");
|
||||
for (auto item : pathParts) {
|
||||
auto newPath = LocalFS->SystemToVFSPath(item) / realPath;
|
||||
if (LocalFS->FileExists(newPath))
|
||||
return newPath;
|
||||
}
|
||||
return realPath.MakeAbsolute();
|
||||
#endif
|
||||
}
|
||||
|
||||
SetEnvironmentVariableW((LPCWSTR)nameu16.c_str(),NULL);
|
||||
}
|
||||
#else
|
||||
std::optional<std::string> GetVariable(std::string name) {
|
||||
auto res = std::getenv(name.c_str());
|
||||
if (res == nullptr)
|
||||
return std::nullopt;
|
||||
std::string value = res;
|
||||
return value;
|
||||
}
|
||||
void SetVariable(std::string name, std::optional<std::string> var) {
|
||||
if (var)
|
||||
#if defined(_WIN32)
|
||||
{
|
||||
std::u16string nameu16 = {};
|
||||
|
||||
std::u16string varu16 = {};
|
||||
|
||||
UTF16::FromUTF8(nameu16, name);
|
||||
UTF16::FromUTF8(varu16, var.value());
|
||||
SetEnvironmentVariableW((LPCWSTR)nameu16.c_str(),
|
||||
(LPCWSTR)varu16.c_str());
|
||||
}
|
||||
#else
|
||||
setenv(name.c_str(), var->c_str(), 1);
|
||||
#endif
|
||||
else
|
||||
#if defined(_WIN32)
|
||||
{
|
||||
std::u16string nameu16 = {};
|
||||
|
||||
UTF16::FromUTF8(nameu16, name);
|
||||
|
||||
SetEnvironmentVariableW((LPCWSTR)nameu16.c_str(), NULL);
|
||||
}
|
||||
#else
|
||||
unsetenv(name.c_str());
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void GetEnvironmentVariables(
|
||||
std::vector<std::pair<std::string, std::string>> &env) {
|
||||
#if defined(_WIN32)
|
||||
auto environ0 = GetEnvironmentStringsW();
|
||||
auto envthing = environ0;
|
||||
while (*envthing) {
|
||||
std::u16string str = (const char16_t *)envthing;
|
||||
std::string stru8;
|
||||
UTF8::FromUTF16(stru8, str);
|
||||
auto items = Http::HttpUtils::SplitString(stru8, "=", 2);
|
||||
if (items.size() == 2) {
|
||||
|
||||
env.push_back(
|
||||
std::pair<std::string, std::string>(items[0], items[1]));
|
||||
} else if (items.size() == 1) {
|
||||
env.push_back(std::pair<std::string, std::string>(items[0], ""));
|
||||
}
|
||||
envthing += str.size() + 1;
|
||||
}
|
||||
FreeEnvironmentStringsW(environ0);
|
||||
#else
|
||||
for (char **envthing = environ; *envthing != NULL; envthing++) {
|
||||
// if(*envthing == NULL) break;
|
||||
auto items = Http::HttpUtils::SplitString(*envthing, "=", 2);
|
||||
if (items.size() == 2) {
|
||||
|
||||
|
||||
void GetEnvironmentVariables(std::vector<std::pair<std::string,std::string>>& env)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
auto environ0 = GetEnvironmentStringsW();
|
||||
auto envthing = environ0;
|
||||
while(*envthing)
|
||||
{
|
||||
std::u16string str = (const char16_t*)envthing;
|
||||
std::string stru8;
|
||||
UTF8::FromUTF16(stru8, str);
|
||||
auto items = Http::HttpUtils::SplitString(stru8, "=", 2);
|
||||
if(items.size() == 2)
|
||||
{
|
||||
|
||||
env.push_back(std::pair<std::string,std::string>(items[0],items[1]));
|
||||
}
|
||||
else if(items.size() == 1)
|
||||
{
|
||||
env.push_back(std::pair<std::string,std::string>(items[0],""));
|
||||
}
|
||||
envthing += str.size() + 1;
|
||||
}
|
||||
FreeEnvironmentStringsW(environ0);
|
||||
#else
|
||||
for(char** envthing = environ; *envthing != NULL; envthing++)
|
||||
{
|
||||
//if(*envthing == NULL) break;
|
||||
auto items = Http::HttpUtils::SplitString(*envthing,"=",2);
|
||||
if(items.size() == 2)
|
||||
{
|
||||
|
||||
env.push_back(std::pair<std::string,std::string>(items[0],items[1]));
|
||||
}
|
||||
else if(items.size() == 1)
|
||||
{
|
||||
env.push_back(std::pair<std::string,std::string>(items[0],""));
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
env.push_back(
|
||||
std::pair<std::string, std::string>(items[0], items[1]));
|
||||
} else if (items.size() == 1) {
|
||||
env.push_back(std::pair<std::string, std::string>(items[0], ""));
|
||||
}
|
||||
}
|
||||
std::string GetPlatform()
|
||||
{
|
||||
#if defined(__ANDROID__)
|
||||
return "Android";
|
||||
#endif
|
||||
#if defined(__SWITCH__)
|
||||
return "Nintendo Switch";
|
||||
#endif
|
||||
#if defined(__PS2__)
|
||||
return "PlayStation 2";
|
||||
#endif
|
||||
#if defined(GEKKO)
|
||||
#if defined(HW_RVL)
|
||||
return "Nintendo Wii";
|
||||
#endif
|
||||
return "Nintendo Gamecube";
|
||||
#endif
|
||||
#if defined(WIN32) || defined(_WIN32)
|
||||
return "Windows";
|
||||
#endif
|
||||
#if defined(linux)
|
||||
return "Linux";
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
std::string GetPlatform() {
|
||||
#if defined(__ANDROID__)
|
||||
return "Android";
|
||||
#endif
|
||||
#if defined(__SWITCH__)
|
||||
return "Nintendo Switch";
|
||||
#endif
|
||||
#if defined(__PS2__)
|
||||
return "PlayStation 2";
|
||||
#endif
|
||||
#if defined(GEKKO)
|
||||
#if defined(HW_RVL)
|
||||
return "Nintendo Wii";
|
||||
#endif
|
||||
return "Nintendo Gamecube";
|
||||
#endif
|
||||
#if defined(WIN32) || defined(_WIN32)
|
||||
return "Windows";
|
||||
#endif
|
||||
#if defined(__FreeBSD__)
|
||||
return "FreeBSD";
|
||||
#endif
|
||||
#if defined(__NetBSD__)
|
||||
return "NetBSD";
|
||||
#endif
|
||||
#if defined(__linux__)
|
||||
return "Linux";
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "TargetConditionals.h"
|
||||
#if TARGET_OS_MAC
|
||||
return "MacOS";
|
||||
#endif
|
||||
#if TARGET_OS_IOS
|
||||
return "iOS";
|
||||
#endif
|
||||
#if TARGET_OS_TV
|
||||
return "Apple TV";
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
#include "TargetConditionals.h"
|
||||
#if TARGET_OS_MAC
|
||||
return "MacOS";
|
||||
#endif
|
||||
#if TARGET_OS_IOS
|
||||
return "iOS";
|
||||
#endif
|
||||
#if TARGET_OS_TV
|
||||
return "Apple TV";
|
||||
#endif
|
||||
|
||||
#if TARGET_OS_WATCH
|
||||
return "Apple Watch";
|
||||
#endif
|
||||
#if TARGET_OS_WATCH
|
||||
return "Apple Watch";
|
||||
#endif
|
||||
|
||||
#if __EMSCRIPTEN__
|
||||
return "WebAssembly";
|
||||
#endif
|
||||
#if __EMSCRIPTEN__
|
||||
return "WebAssembly";
|
||||
#endif
|
||||
|
||||
return "Unknown Apple Device";
|
||||
#endif
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
} // namespace Tesses::Framework::Platform::Environment
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,31 +1,34 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/Random.hpp"
|
||||
|
||||
namespace Tesses::Framework {
|
||||
Random::Random() : Random((uint64_t)time(NULL))
|
||||
{
|
||||
Random::Random() : Random((uint64_t)time(NULL)) {}
|
||||
Random::Random(uint64_t seed) : num(seed) {}
|
||||
uint32_t Random::Next(uint32_t max) { return (uint32_t)Next(0, (int32_t)max); }
|
||||
int32_t Random::Next(int32_t min, int32_t max) {
|
||||
uint32_t number = (uint32_t)(Next() >> 31);
|
||||
int32_t range = max - min;
|
||||
|
||||
}
|
||||
Random::Random(uint64_t seed) : num(seed)
|
||||
{
|
||||
|
||||
}
|
||||
uint32_t Random::Next(uint32_t max)
|
||||
{
|
||||
return (uint32_t)Next(0,(int32_t)max);
|
||||
}
|
||||
int32_t Random::Next(int32_t min, int32_t max)
|
||||
{
|
||||
uint32_t number = (uint32_t)(Next() >> 31);
|
||||
int32_t range = max-min;
|
||||
|
||||
return (uint32_t)((((double)number / (double)0xFFFFFFFF) * (double)range)+min);
|
||||
}
|
||||
uint64_t Random::Next()
|
||||
{
|
||||
return num = 6364136223846793005 * num + 1;
|
||||
}
|
||||
uint8_t Random::NextByte()
|
||||
{
|
||||
return (uint8_t)Next(0,256);
|
||||
}
|
||||
}
|
||||
return (uint32_t)((((double)number / (double)0xFFFFFFFF) * (double)range) +
|
||||
min);
|
||||
}
|
||||
uint64_t Random::Next() { return num = 6364136223846793005 * num + 1; }
|
||||
uint8_t Random::NextByte() { return (uint8_t)Next(0, 256); }
|
||||
} // namespace Tesses::Framework
|
||||
@@ -1,253 +1,232 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/Serialization/Bencode.hpp"
|
||||
|
||||
namespace Tesses::Framework::Serialization::Bencode {
|
||||
BeToken BeDictionary::GetValue(BeString key) const
|
||||
{
|
||||
for(auto item : this->tokens)
|
||||
if(item.first == key) return item.second;
|
||||
return BeUndefined();
|
||||
}
|
||||
void BeDictionary::SetValue(BeString key, BeToken value)
|
||||
{
|
||||
if(std::holds_alternative<BeUndefined>(value))
|
||||
{
|
||||
for(auto idx = this->tokens.begin(); idx != this->tokens.end(); idx++)
|
||||
{
|
||||
if(idx->first == key) {
|
||||
this->tokens.erase(idx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(auto& item : this->tokens)
|
||||
{
|
||||
if(item.first == key)
|
||||
{
|
||||
item.second = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
this->tokens.emplace_back(key,value);
|
||||
}
|
||||
}
|
||||
|
||||
BeString::BeString()
|
||||
{
|
||||
|
||||
}
|
||||
BeString::BeString(const std::string& text)
|
||||
{
|
||||
this->data.insert(this->data.end(),text.cbegin(),text.cend());
|
||||
}
|
||||
BeString::BeString(const char* text)
|
||||
{
|
||||
size_t len = strlen(text);
|
||||
this->data.insert(this->data.end(),text,text+len);
|
||||
}
|
||||
BeString::BeString(const std::vector<uint8_t>& data) : data(data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
BeString::operator std::string() const
|
||||
{
|
||||
return std::string(data.cbegin(),data.cend());
|
||||
}
|
||||
|
||||
|
||||
bool operator==(const BeString& lStr, const BeString& rStr)
|
||||
{
|
||||
return lStr.data == rStr.data;
|
||||
}
|
||||
bool operator==(const BeString& lStr, const std::string& rStr)
|
||||
{
|
||||
if(lStr.data.size() != rStr.size()) return false;
|
||||
return std::equal(lStr.data.cbegin(),lStr.data.cend(),rStr.cbegin());
|
||||
}
|
||||
bool operator==(const std::string& lStr, const BeString& rStr)
|
||||
{
|
||||
if(lStr.size() != rStr.data.size()) return false;
|
||||
return std::equal(lStr.cbegin(),lStr.cend(),rStr.data.cbegin());
|
||||
}
|
||||
bool operator==(const BeString& lStr, const char* rStr)
|
||||
{
|
||||
size_t len = strlen(rStr);
|
||||
if(lStr.data.size() != len) return false;
|
||||
return std::equal(lStr.data.cbegin(),lStr.data.cend(),rStr);
|
||||
}
|
||||
bool operator==(const char* lStr, const BeString& rStr)
|
||||
{
|
||||
size_t len = strlen(lStr);
|
||||
if(rStr.data.size() != len) return false;
|
||||
|
||||
return std::equal(lStr,lStr+len,rStr.data.cbegin());
|
||||
}
|
||||
|
||||
bool operator!=(const BeString& lStr, const BeString& rStr)
|
||||
{
|
||||
return !(lStr == rStr);
|
||||
}
|
||||
bool operator!=(const BeString& lStr, const std::string& rStr)
|
||||
{
|
||||
return !(lStr == rStr);
|
||||
}
|
||||
bool operator!=(const std::string& lStr, const BeString& rStr)
|
||||
{
|
||||
return !(lStr == rStr);
|
||||
}
|
||||
bool operator!=(const BeString& lStr, const char* rStr)
|
||||
{
|
||||
return !(lStr == rStr);
|
||||
}
|
||||
bool operator!=(const char* lStr, const BeString& rStr)
|
||||
{
|
||||
return !(lStr == rStr);
|
||||
}
|
||||
|
||||
void Bencode::Save(std::shared_ptr<Tesses::Framework::Streams::Stream> strm,const BeToken& value)
|
||||
{
|
||||
if(std::holds_alternative<BeArray>(value))
|
||||
{
|
||||
auto& array = std::get<BeArray>(value);
|
||||
strm->WriteByte((uint8_t)'l');
|
||||
for(auto& item : array.tokens)
|
||||
{
|
||||
Save(strm,item);
|
||||
}
|
||||
strm->WriteByte((uint8_t)'e');
|
||||
}
|
||||
else if(std::holds_alternative<BeDictionary>(value))
|
||||
{
|
||||
auto& dict = std::get<BeDictionary>(value);
|
||||
strm->WriteByte((uint8_t)'d');
|
||||
for(auto& item : dict.tokens)
|
||||
{
|
||||
Save(strm,item.first);
|
||||
Save(strm,item.second);
|
||||
}
|
||||
strm->WriteByte((uint8_t)'e');
|
||||
}
|
||||
else if(std::holds_alternative<BeString>(value))
|
||||
{
|
||||
auto& str = std::get<BeString>(value);
|
||||
std::string prefix = std::to_string(str.data.size()) + ":";
|
||||
strm->WriteBlock((const uint8_t*)prefix.data(),prefix.size());
|
||||
strm->WriteBlock(str.data.data(),str.data.size());
|
||||
}
|
||||
else if(std::holds_alternative<int64_t>(value))
|
||||
{
|
||||
int64_t val = std::get<int64_t>(value);
|
||||
std::string str = "i" + std::to_string(val) + "e";
|
||||
strm->WriteBlock((const uint8_t*)str.data(),str.size());
|
||||
}
|
||||
}
|
||||
BeToken Bencode::Load(std::shared_ptr<Tesses::Framework::Streams::Stream> strm)
|
||||
{
|
||||
auto chr = strm->ReadByte();
|
||||
switch(chr)
|
||||
{
|
||||
case 'i':
|
||||
{
|
||||
std::string no;
|
||||
while(true) {
|
||||
chr = strm->ReadByte();
|
||||
if(chr == -1) throw std::out_of_range("End of file");
|
||||
if(chr == 'e') break;
|
||||
no.push_back((char)chr);
|
||||
}
|
||||
return std::stoll(no);
|
||||
}
|
||||
BeToken BeDictionary::GetValue(BeString key) const {
|
||||
for (auto item : this->tokens)
|
||||
if (item.first == key)
|
||||
return item.second;
|
||||
return BeUndefined();
|
||||
}
|
||||
void BeDictionary::SetValue(BeString key, BeToken value) {
|
||||
if (std::holds_alternative<BeUndefined>(value)) {
|
||||
for (auto idx = this->tokens.begin(); idx != this->tokens.end();
|
||||
idx++) {
|
||||
if (idx->first == key) {
|
||||
this->tokens.erase(idx);
|
||||
break;
|
||||
case 'd':
|
||||
{
|
||||
BeDictionary dict;
|
||||
while(true)
|
||||
{
|
||||
auto key = Load(strm);
|
||||
if(std::holds_alternative<BeUndefined>(key)) break;
|
||||
if(!std::holds_alternative<BeString>(key)) throw std::runtime_error("Key must be a string");
|
||||
auto value = Load(strm);
|
||||
if(std::holds_alternative<BeUndefined>(key)) throw std::runtime_error("Incomplete dictionary entry");
|
||||
dict.tokens.emplace_back(std::get<BeString>(key),value);
|
||||
}
|
||||
return dict;
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
{
|
||||
BeArray array;
|
||||
while(true)
|
||||
{
|
||||
auto tkn = Load(strm);
|
||||
if(std::holds_alternative<BeUndefined>(tkn)) break;
|
||||
array.tokens.push_back(tkn);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
} else {
|
||||
for (auto &item : this->tokens) {
|
||||
if (item.first == key) {
|
||||
item.second = value;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 'e':
|
||||
return BeUndefined();
|
||||
case -1:
|
||||
}
|
||||
this->tokens.emplace_back(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
BeString::BeString() {}
|
||||
BeString::BeString(const std::string &text) {
|
||||
this->data.insert(this->data.end(), text.cbegin(), text.cend());
|
||||
}
|
||||
BeString::BeString(const char *text) {
|
||||
size_t len = strlen(text);
|
||||
this->data.insert(this->data.end(), text, text + len);
|
||||
}
|
||||
BeString::BeString(const std::vector<uint8_t> &data) : data(data) {}
|
||||
|
||||
BeString::operator std::string() const {
|
||||
return std::string(data.cbegin(), data.cend());
|
||||
}
|
||||
|
||||
bool operator==(const BeString &lStr, const BeString &rStr) {
|
||||
return lStr.data == rStr.data;
|
||||
}
|
||||
bool operator==(const BeString &lStr, const std::string &rStr) {
|
||||
if (lStr.data.size() != rStr.size())
|
||||
return false;
|
||||
return std::equal(lStr.data.cbegin(), lStr.data.cend(), rStr.cbegin());
|
||||
}
|
||||
bool operator==(const std::string &lStr, const BeString &rStr) {
|
||||
if (lStr.size() != rStr.data.size())
|
||||
return false;
|
||||
return std::equal(lStr.cbegin(), lStr.cend(), rStr.data.cbegin());
|
||||
}
|
||||
bool operator==(const BeString &lStr, const char *rStr) {
|
||||
size_t len = strlen(rStr);
|
||||
if (lStr.data.size() != len)
|
||||
return false;
|
||||
return std::equal(lStr.data.cbegin(), lStr.data.cend(), rStr);
|
||||
}
|
||||
bool operator==(const char *lStr, const BeString &rStr) {
|
||||
size_t len = strlen(lStr);
|
||||
if (rStr.data.size() != len)
|
||||
return false;
|
||||
|
||||
return std::equal(lStr, lStr + len, rStr.data.cbegin());
|
||||
}
|
||||
|
||||
bool operator!=(const BeString &lStr, const BeString &rStr) {
|
||||
return !(lStr == rStr);
|
||||
}
|
||||
bool operator!=(const BeString &lStr, const std::string &rStr) {
|
||||
return !(lStr == rStr);
|
||||
}
|
||||
bool operator!=(const std::string &lStr, const BeString &rStr) {
|
||||
return !(lStr == rStr);
|
||||
}
|
||||
bool operator!=(const BeString &lStr, const char *rStr) {
|
||||
return !(lStr == rStr);
|
||||
}
|
||||
bool operator!=(const char *lStr, const BeString &rStr) {
|
||||
return !(lStr == rStr);
|
||||
}
|
||||
|
||||
void Bencode::Save(std::shared_ptr<Tesses::Framework::Streams::Stream> strm,
|
||||
const BeToken &value) {
|
||||
if (std::holds_alternative<BeArray>(value)) {
|
||||
auto &array = std::get<BeArray>(value);
|
||||
strm->WriteByte((uint8_t)'l');
|
||||
for (auto &item : array.tokens) {
|
||||
Save(strm, item);
|
||||
}
|
||||
strm->WriteByte((uint8_t)'e');
|
||||
} else if (std::holds_alternative<BeDictionary>(value)) {
|
||||
auto &dict = std::get<BeDictionary>(value);
|
||||
strm->WriteByte((uint8_t)'d');
|
||||
for (auto &item : dict.tokens) {
|
||||
Save(strm, item.first);
|
||||
Save(strm, item.second);
|
||||
}
|
||||
strm->WriteByte((uint8_t)'e');
|
||||
} else if (std::holds_alternative<BeString>(value)) {
|
||||
auto &str = std::get<BeString>(value);
|
||||
std::string prefix = std::to_string(str.data.size()) + ":";
|
||||
strm->WriteBlock((const uint8_t *)prefix.data(), prefix.size());
|
||||
strm->WriteBlock(str.data.data(), str.data.size());
|
||||
} else if (std::holds_alternative<int64_t>(value)) {
|
||||
int64_t val = std::get<int64_t>(value);
|
||||
std::string str = "i" + std::to_string(val) + "e";
|
||||
strm->WriteBlock((const uint8_t *)str.data(), str.size());
|
||||
}
|
||||
}
|
||||
BeToken
|
||||
Bencode::Load(std::shared_ptr<Tesses::Framework::Streams::Stream> strm) {
|
||||
auto chr = strm->ReadByte();
|
||||
switch (chr) {
|
||||
case 'i': {
|
||||
std::string no;
|
||||
while (true) {
|
||||
chr = strm->ReadByte();
|
||||
if (chr == -1)
|
||||
throw std::out_of_range("End of file");
|
||||
default:
|
||||
{
|
||||
std::string no({(char)chr});
|
||||
while(true) {
|
||||
chr = strm->ReadByte();
|
||||
if(chr == -1) throw std::out_of_range("End of file");
|
||||
if(chr == ':') break;
|
||||
no.push_back((char)chr);
|
||||
}
|
||||
auto len = std::stoll(no);
|
||||
if(len < 0) throw std::out_of_range("Less than zero byte string");
|
||||
BeString str;
|
||||
str.data.resize((size_t)len);
|
||||
|
||||
size_t result = strm->ReadBlock(str.data.data(),str.data.size());
|
||||
if(result != str.data.size() || result != (size_t)len) throw std::out_of_range("Didn't read entire string");
|
||||
return str;
|
||||
|
||||
|
||||
}
|
||||
if (chr == 'e')
|
||||
break;
|
||||
no.push_back((char)chr);
|
||||
}
|
||||
return std::stoll(no);
|
||||
} break;
|
||||
case 'd': {
|
||||
BeDictionary dict;
|
||||
while (true) {
|
||||
auto key = Load(strm);
|
||||
if (std::holds_alternative<BeUndefined>(key))
|
||||
break;
|
||||
if (!std::holds_alternative<BeString>(key))
|
||||
throw std::runtime_error("Key must be a string");
|
||||
auto value = Load(strm);
|
||||
if (std::holds_alternative<BeUndefined>(key))
|
||||
throw std::runtime_error("Incomplete dictionary entry");
|
||||
dict.tokens.emplace_back(std::get<BeString>(key), value);
|
||||
}
|
||||
return dict;
|
||||
} break;
|
||||
case 'l': {
|
||||
BeArray array;
|
||||
while (true) {
|
||||
auto tkn = Load(strm);
|
||||
if (std::holds_alternative<BeUndefined>(tkn))
|
||||
break;
|
||||
array.tokens.push_back(tkn);
|
||||
}
|
||||
return array;
|
||||
} break;
|
||||
case 'e':
|
||||
return BeUndefined();
|
||||
case -1:
|
||||
throw std::out_of_range("End of file");
|
||||
default: {
|
||||
std::string no({(char)chr});
|
||||
while (true) {
|
||||
chr = strm->ReadByte();
|
||||
if (chr == -1)
|
||||
throw std::out_of_range("End of file");
|
||||
if (chr == ':')
|
||||
break;
|
||||
no.push_back((char)chr);
|
||||
}
|
||||
auto len = std::stoll(no);
|
||||
if (len < 0)
|
||||
throw std::out_of_range("Less than zero byte string");
|
||||
BeString str;
|
||||
str.data.resize((size_t)len);
|
||||
|
||||
size_t result = strm->ReadBlock(str.data.data(), str.data.size());
|
||||
if (result != str.data.size() || result != (size_t)len)
|
||||
throw std::out_of_range("Didn't read entire string");
|
||||
return str;
|
||||
|
||||
} break;
|
||||
}
|
||||
Json::JToken Bencode::ToJson(const BeToken& tkn)
|
||||
{
|
||||
if(std::holds_alternative<BeDictionary>(tkn))
|
||||
{
|
||||
auto& dict = std::get<BeDictionary>(tkn);
|
||||
Json::JObject o;
|
||||
for(auto& itm : dict.tokens)
|
||||
{
|
||||
o.SetValue(itm.first,ToJson(itm.second));
|
||||
}
|
||||
return o;
|
||||
}
|
||||
Json::JToken Bencode::ToJson(const BeToken &tkn) {
|
||||
if (std::holds_alternative<BeDictionary>(tkn)) {
|
||||
auto &dict = std::get<BeDictionary>(tkn);
|
||||
Json::JObject o;
|
||||
for (auto &itm : dict.tokens) {
|
||||
o.SetValue(itm.first, ToJson(itm.second));
|
||||
}
|
||||
if(std::holds_alternative<BeArray>(tkn))
|
||||
{
|
||||
auto& array = std::get<BeArray>(tkn);
|
||||
Json::JArray a;
|
||||
for(auto& itm : array.tokens)
|
||||
{
|
||||
a.Add(ToJson(itm));
|
||||
}
|
||||
return a;
|
||||
}
|
||||
if(std::holds_alternative<BeString>(tkn))
|
||||
{
|
||||
return (std::string)std::get<BeString>(tkn);
|
||||
}
|
||||
if(std::holds_alternative<int64_t>(tkn))
|
||||
{
|
||||
return std::get<int64_t>(tkn);
|
||||
}
|
||||
return Json::JUndefined();
|
||||
return o;
|
||||
}
|
||||
void Bencode::Print(std::shared_ptr<Tesses::Framework::TextStreams::TextWriter> writer, BeToken tkn)
|
||||
{
|
||||
writer->WriteLine(Json::Json::Encode(ToJson(tkn),true));
|
||||
if (std::holds_alternative<BeArray>(tkn)) {
|
||||
auto &array = std::get<BeArray>(tkn);
|
||||
Json::JArray a;
|
||||
for (auto &itm : array.tokens) {
|
||||
a.Add(ToJson(itm));
|
||||
}
|
||||
return a;
|
||||
}
|
||||
}
|
||||
if (std::holds_alternative<BeString>(tkn)) {
|
||||
return (std::string)std::get<BeString>(tkn);
|
||||
}
|
||||
if (std::holds_alternative<int64_t>(tkn)) {
|
||||
return std::get<int64_t>(tkn);
|
||||
}
|
||||
return Json::JUndefined();
|
||||
}
|
||||
void Bencode::Print(
|
||||
std::shared_ptr<Tesses::Framework::TextStreams::TextWriter> writer,
|
||||
BeToken tkn) {
|
||||
writer->WriteLine(Json::Json::Encode(ToJson(tkn), true));
|
||||
}
|
||||
} // namespace Tesses::Framework::Serialization::Bencode
|
||||
@@ -1,319 +1,284 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/Serialization/BitConverter.hpp"
|
||||
|
||||
namespace Tesses::Framework::Serialization
|
||||
{
|
||||
double BitConverter::ToDoubleBits(uint64_t v)
|
||||
{
|
||||
static_assert(sizeof(double) == sizeof(uint64_t), "double is not the same size as uint64_t");
|
||||
double dest=0;
|
||||
memcpy(&dest,&v, sizeof(uint64_t));
|
||||
return dest;
|
||||
}
|
||||
uint64_t BitConverter::ToUintBits(double v)
|
||||
{
|
||||
//as static_assert is compile time we don't need it here
|
||||
uint64_t dest = 0;
|
||||
memcpy(&dest,&v, sizeof(uint64_t));
|
||||
return dest;
|
||||
}
|
||||
float BitConverter::ToFloatBits(uint32_t v)
|
||||
{
|
||||
static_assert(sizeof(float) == sizeof(uint32_t), "float is not the same size as uint32_t");
|
||||
float dest=0;
|
||||
memcpy(&dest,&v, sizeof(uint32_t));
|
||||
return dest;
|
||||
}
|
||||
uint32_t BitConverter::ToUint32Bits(float v)
|
||||
{
|
||||
//as static_assert is compile time we don't need it here
|
||||
uint32_t dest = 0;
|
||||
memcpy(&dest,&v, sizeof(uint32_t));
|
||||
return dest;
|
||||
}
|
||||
double BitConverter::ToDoubleBE(uint8_t& b)
|
||||
{
|
||||
return ToDoubleBits(ToUint64BE(b));
|
||||
}
|
||||
float BitConverter::ToFloatBE(uint8_t& b)
|
||||
{
|
||||
return ToFloatBits(ToUint32BE(b));
|
||||
}
|
||||
uint64_t BitConverter::ToUint64BE(uint8_t& b)
|
||||
{
|
||||
uint8_t* b2 = &b;
|
||||
uint64_t v = 0;
|
||||
v |= ((uint64_t)b2[0] << 56);
|
||||
v |= ((uint64_t)b2[1] << 48);
|
||||
v |= ((uint64_t)b2[2] << 40);
|
||||
v |= ((uint64_t)b2[3] << 32);
|
||||
v |= ((uint64_t)b2[4] << 24);
|
||||
v |= ((uint64_t)b2[5] << 16);
|
||||
v |= ((uint64_t)b2[6] << 8);
|
||||
v |= (uint64_t)b2[7];
|
||||
return v;
|
||||
}
|
||||
uint32_t BitConverter::ToUint32BE(uint8_t& b)
|
||||
{
|
||||
uint8_t* b2 = &b;
|
||||
uint32_t v = 0;
|
||||
|
||||
v |= ((uint32_t)b2[0] << 24);
|
||||
v |= ((uint32_t)b2[1] << 16);
|
||||
v |= ((uint32_t)b2[2] << 8);
|
||||
v |= (uint32_t)b2[3];
|
||||
return v;
|
||||
}
|
||||
uint16_t BitConverter::ToUint16BE(uint8_t& b)
|
||||
{
|
||||
uint8_t* b2 = &b;
|
||||
uint16_t v = 0;
|
||||
|
||||
|
||||
v |= ((uint16_t)b2[0] << 8);
|
||||
v |= (uint16_t)b2[1];
|
||||
return v;
|
||||
}
|
||||
double BitConverter::ToDoubleLE(uint8_t& b)
|
||||
{
|
||||
return ToDoubleBits(ToUint64LE(b));
|
||||
}
|
||||
float BitConverter::ToFloatLE(uint8_t& b)
|
||||
{
|
||||
return ToFloatBits(ToUint32LE(b));
|
||||
}
|
||||
uint64_t BitConverter::ToUint64LE(uint8_t& b)
|
||||
{
|
||||
uint8_t* b2 = &b;
|
||||
uint64_t v = 0;
|
||||
v |= (uint64_t)b2[0];
|
||||
v |= ((uint64_t)b2[1] << 8);
|
||||
v |= ((uint64_t)b2[2] << 16);
|
||||
v |= ((uint64_t)b2[3] << 24);
|
||||
v |= ((uint64_t)b2[4] << 32);
|
||||
v |= ((uint64_t)b2[5] << 40);
|
||||
v |= ((uint64_t)b2[6] << 48);
|
||||
v |= ((uint64_t)b2[7] << 56);
|
||||
|
||||
return v;
|
||||
}
|
||||
uint32_t BitConverter::ToUint32LE(uint8_t& b)
|
||||
{
|
||||
uint8_t* b2 = &b;
|
||||
uint32_t v = 0;
|
||||
v |= (uint32_t)b2[0];
|
||||
v |= ((uint32_t)b2[1] << 8);
|
||||
v |= ((uint32_t)b2[2] << 16);
|
||||
v |= ((uint32_t)b2[3] << 24);
|
||||
|
||||
return v;
|
||||
}
|
||||
uint16_t BitConverter::ToUint16LE(uint8_t& b)
|
||||
{
|
||||
uint8_t* b2 = &b;
|
||||
uint16_t v = 0;
|
||||
|
||||
v |= (uint16_t)b2[0];
|
||||
v |= ((uint16_t)b2[1] << 8);
|
||||
|
||||
return v;
|
||||
}
|
||||
void BitConverter::FromDoubleBE(uint8_t& b, double v)
|
||||
{
|
||||
FromUint64BE(b,ToUintBits(v));
|
||||
}
|
||||
void BitConverter::FromUint64BE(uint8_t& b, uint64_t v)
|
||||
{
|
||||
uint8_t* b2 = &b;
|
||||
b2[0] = (uint8_t)(v >> 56);
|
||||
b2[1] = (uint8_t)(v >> 48);
|
||||
b2[2] = (uint8_t)(v >> 40);
|
||||
b2[3] = (uint8_t)(v >> 32);
|
||||
b2[4] = (uint8_t)(v >> 24);
|
||||
b2[5] = (uint8_t)(v >> 16);
|
||||
b2[6] = (uint8_t)(v >> 8);
|
||||
b2[7] = (uint8_t)v;
|
||||
}
|
||||
void BitConverter::FromUint32BE(uint8_t& b, uint32_t v)
|
||||
{
|
||||
uint8_t* b2 = &b;
|
||||
|
||||
b2[0] = (uint8_t)(v >> 24);
|
||||
b2[1] = (uint8_t)(v >> 16);
|
||||
b2[2] = (uint8_t)(v >> 8);
|
||||
b2[3] = (uint8_t)v;
|
||||
}
|
||||
void BitConverter::FromUint16BE(uint8_t& b, uint16_t v)
|
||||
{
|
||||
uint8_t* b2 = &b;
|
||||
|
||||
b2[0] = (uint8_t)(v >> 8);
|
||||
b2[1] = (uint8_t)v;
|
||||
}
|
||||
namespace Tesses::Framework::Serialization {
|
||||
double BitConverter::ToDoubleBits(uint64_t v) {
|
||||
static_assert(sizeof(double) == sizeof(uint64_t),
|
||||
"double is not the same size as uint64_t");
|
||||
double dest = 0;
|
||||
memcpy(&dest, &v, sizeof(uint64_t));
|
||||
return dest;
|
||||
}
|
||||
uint64_t BitConverter::ToUintBits(double v) {
|
||||
// as static_assert is compile time we don't need it here
|
||||
uint64_t dest = 0;
|
||||
memcpy(&dest, &v, sizeof(uint64_t));
|
||||
return dest;
|
||||
}
|
||||
float BitConverter::ToFloatBits(uint32_t v) {
|
||||
static_assert(sizeof(float) == sizeof(uint32_t),
|
||||
"float is not the same size as uint32_t");
|
||||
float dest = 0;
|
||||
memcpy(&dest, &v, sizeof(uint32_t));
|
||||
return dest;
|
||||
}
|
||||
uint32_t BitConverter::ToUint32Bits(float v) {
|
||||
// as static_assert is compile time we don't need it here
|
||||
uint32_t dest = 0;
|
||||
memcpy(&dest, &v, sizeof(uint32_t));
|
||||
return dest;
|
||||
}
|
||||
double BitConverter::ToDoubleBE(uint8_t &b) {
|
||||
return ToDoubleBits(ToUint64BE(b));
|
||||
}
|
||||
float BitConverter::ToFloatBE(uint8_t &b) { return ToFloatBits(ToUint32BE(b)); }
|
||||
uint64_t BitConverter::ToUint64BE(uint8_t &b) {
|
||||
uint8_t *b2 = &b;
|
||||
uint64_t v = 0;
|
||||
v |= ((uint64_t)b2[0] << 56);
|
||||
v |= ((uint64_t)b2[1] << 48);
|
||||
v |= ((uint64_t)b2[2] << 40);
|
||||
v |= ((uint64_t)b2[3] << 32);
|
||||
v |= ((uint64_t)b2[4] << 24);
|
||||
v |= ((uint64_t)b2[5] << 16);
|
||||
v |= ((uint64_t)b2[6] << 8);
|
||||
v |= (uint64_t)b2[7];
|
||||
return v;
|
||||
}
|
||||
uint32_t BitConverter::ToUint32BE(uint8_t &b) {
|
||||
uint8_t *b2 = &b;
|
||||
uint32_t v = 0;
|
||||
|
||||
void BitConverter::FromDoubleLE(uint8_t& b, double v)
|
||||
{
|
||||
FromUint64BE(b,ToUintBits(v));
|
||||
}
|
||||
void BitConverter::FromFloatLE(uint8_t& b, float v)
|
||||
{
|
||||
FromUint32LE(b,ToUint32Bits(v));
|
||||
}
|
||||
void BitConverter::FromFloatBE(uint8_t& b, float v)
|
||||
{
|
||||
FromUint32BE(b,ToUint32Bits(v));
|
||||
}
|
||||
void BitConverter::FromUint64LE(uint8_t& b, uint64_t v)
|
||||
{
|
||||
uint8_t* b2 = &b;
|
||||
b2[0] = (uint8_t)v;
|
||||
b2[1] = (uint8_t)(v >> 8);
|
||||
b2[2] = (uint8_t)(v >> 16);
|
||||
b2[3] = (uint8_t)(v >> 24);
|
||||
b2[4] = (uint8_t)(v >> 32);
|
||||
b2[5] = (uint8_t)(v >> 40);
|
||||
b2[6] = (uint8_t)(v >> 48);
|
||||
b2[7] = (uint8_t)(v >> 56);
|
||||
|
||||
}
|
||||
void BitConverter::FromUint32LE(uint8_t& b, uint32_t v)
|
||||
{
|
||||
uint8_t* b2 = &b;
|
||||
|
||||
b2[0] = (uint8_t)v;
|
||||
b2[1] = (uint8_t)(v >> 8);
|
||||
b2[2] = (uint8_t)(v >> 16);
|
||||
b2[3] = (uint8_t)(v >> 24);
|
||||
|
||||
}
|
||||
void BitConverter::FromUint16LE(uint8_t& b, uint16_t v)
|
||||
{
|
||||
uint8_t* b2 = &b;
|
||||
|
||||
v |= ((uint32_t)b2[0] << 24);
|
||||
v |= ((uint32_t)b2[1] << 16);
|
||||
v |= ((uint32_t)b2[2] << 8);
|
||||
v |= (uint32_t)b2[3];
|
||||
return v;
|
||||
}
|
||||
uint16_t BitConverter::ToUint16BE(uint8_t &b) {
|
||||
uint8_t *b2 = &b;
|
||||
uint16_t v = 0;
|
||||
|
||||
b2[0] = (uint8_t)v;
|
||||
b2[1] = (uint8_t)(v >> 8);
|
||||
}
|
||||
void BitConverter::FromUuid(uint8_t& b, const Uuid& uuid)
|
||||
{
|
||||
uint8_t* b2 = &b;
|
||||
FromUint32BE(b2[0], uuid.time_low);
|
||||
FromUint16BE(b2[4], uuid.time_mid);
|
||||
FromUint16BE(b2[6], uuid.time_hi_and_version);
|
||||
b2[8] = uuid.clock_seq_hi_and_reserved;
|
||||
b2[9] = uuid.clock_seq_low;
|
||||
for(size_t i = 0; i < 6; i++)
|
||||
b2[i+10] = uuid.node[i];
|
||||
|
||||
}
|
||||
|
||||
v |= ((uint16_t)b2[0] << 8);
|
||||
v |= (uint16_t)b2[1];
|
||||
return v;
|
||||
}
|
||||
double BitConverter::ToDoubleLE(uint8_t &b) {
|
||||
return ToDoubleBits(ToUint64LE(b));
|
||||
}
|
||||
float BitConverter::ToFloatLE(uint8_t &b) { return ToFloatBits(ToUint32LE(b)); }
|
||||
uint64_t BitConverter::ToUint64LE(uint8_t &b) {
|
||||
uint8_t *b2 = &b;
|
||||
uint64_t v = 0;
|
||||
v |= (uint64_t)b2[0];
|
||||
v |= ((uint64_t)b2[1] << 8);
|
||||
v |= ((uint64_t)b2[2] << 16);
|
||||
v |= ((uint64_t)b2[3] << 24);
|
||||
v |= ((uint64_t)b2[4] << 32);
|
||||
v |= ((uint64_t)b2[5] << 40);
|
||||
v |= ((uint64_t)b2[6] << 48);
|
||||
v |= ((uint64_t)b2[7] << 56);
|
||||
|
||||
Uuid BitConverter::ToUuid(uint8_t& b)
|
||||
{
|
||||
Uuid uuid;
|
||||
BitConverter::ToUuid(b,uuid);
|
||||
return uuid;
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
uint32_t BitConverter::ToUint32LE(uint8_t &b) {
|
||||
uint8_t *b2 = &b;
|
||||
uint32_t v = 0;
|
||||
v |= (uint32_t)b2[0];
|
||||
v |= ((uint32_t)b2[1] << 8);
|
||||
v |= ((uint32_t)b2[2] << 16);
|
||||
v |= ((uint32_t)b2[3] << 24);
|
||||
|
||||
void BitConverter::ToUuid(uint8_t& b, Uuid& uuid)
|
||||
{
|
||||
uint8_t* b2 = &b;
|
||||
uuid.time_low = ToUint32BE(b2[0]);
|
||||
return v;
|
||||
}
|
||||
uint16_t BitConverter::ToUint16LE(uint8_t &b) {
|
||||
uint8_t *b2 = &b;
|
||||
uint16_t v = 0;
|
||||
|
||||
uuid.time_mid = ToUint16BE(b2[4]);
|
||||
v |= (uint16_t)b2[0];
|
||||
v |= ((uint16_t)b2[1] << 8);
|
||||
|
||||
uuid.time_hi_and_version = ToUint16BE(b2[6]);
|
||||
|
||||
uuid.clock_seq_hi_and_reserved = b2[8];
|
||||
uuid.clock_seq_low = b2[9];
|
||||
for(size_t i = 0; i < 6; i++)
|
||||
uuid.node[i]= b2[i+10];
|
||||
return v;
|
||||
}
|
||||
void BitConverter::FromDoubleBE(uint8_t &b, double v) {
|
||||
FromUint64BE(b, ToUintBits(v));
|
||||
}
|
||||
void BitConverter::FromUint64BE(uint8_t &b, uint64_t v) {
|
||||
uint8_t *b2 = &b;
|
||||
b2[0] = (uint8_t)(v >> 56);
|
||||
b2[1] = (uint8_t)(v >> 48);
|
||||
b2[2] = (uint8_t)(v >> 40);
|
||||
b2[3] = (uint8_t)(v >> 32);
|
||||
b2[4] = (uint8_t)(v >> 24);
|
||||
b2[5] = (uint8_t)(v >> 16);
|
||||
b2[6] = (uint8_t)(v >> 8);
|
||||
b2[7] = (uint8_t)v;
|
||||
}
|
||||
void BitConverter::FromUint32BE(uint8_t &b, uint32_t v) {
|
||||
uint8_t *b2 = &b;
|
||||
|
||||
}
|
||||
b2[0] = (uint8_t)(v >> 24);
|
||||
b2[1] = (uint8_t)(v >> 16);
|
||||
b2[2] = (uint8_t)(v >> 8);
|
||||
b2[3] = (uint8_t)v;
|
||||
}
|
||||
void BitConverter::FromUint16BE(uint8_t &b, uint16_t v) {
|
||||
uint8_t *b2 = &b;
|
||||
|
||||
int64_t BitConverter::ToSint64BE(uint8_t& b)
|
||||
{
|
||||
uint64_t src = ToUint64BE(b);
|
||||
int64_t dest = 0;
|
||||
memcpy(&dest,&src,sizeof(uint64_t));
|
||||
return dest;
|
||||
}
|
||||
int64_t BitConverter::ToSint64LE(uint8_t& b)
|
||||
{
|
||||
uint64_t src = ToUint64LE(b);
|
||||
int64_t dest = 0;
|
||||
memcpy(&dest,&src,sizeof(uint64_t));
|
||||
return dest;
|
||||
}
|
||||
b2[0] = (uint8_t)(v >> 8);
|
||||
b2[1] = (uint8_t)v;
|
||||
}
|
||||
|
||||
void BitConverter::FromDoubleLE(uint8_t &b, double v) {
|
||||
FromUint64BE(b, ToUintBits(v));
|
||||
}
|
||||
void BitConverter::FromFloatLE(uint8_t &b, float v) {
|
||||
FromUint32LE(b, ToUint32Bits(v));
|
||||
}
|
||||
void BitConverter::FromFloatBE(uint8_t &b, float v) {
|
||||
FromUint32BE(b, ToUint32Bits(v));
|
||||
}
|
||||
void BitConverter::FromUint64LE(uint8_t &b, uint64_t v) {
|
||||
uint8_t *b2 = &b;
|
||||
b2[0] = (uint8_t)v;
|
||||
b2[1] = (uint8_t)(v >> 8);
|
||||
b2[2] = (uint8_t)(v >> 16);
|
||||
b2[3] = (uint8_t)(v >> 24);
|
||||
b2[4] = (uint8_t)(v >> 32);
|
||||
b2[5] = (uint8_t)(v >> 40);
|
||||
b2[6] = (uint8_t)(v >> 48);
|
||||
b2[7] = (uint8_t)(v >> 56);
|
||||
}
|
||||
void BitConverter::FromUint32LE(uint8_t &b, uint32_t v) {
|
||||
uint8_t *b2 = &b;
|
||||
|
||||
int32_t BitConverter::ToSint32BE(uint8_t& b)
|
||||
{
|
||||
uint32_t src = ToUint32BE(b);
|
||||
int32_t dest = 0;
|
||||
memcpy(&dest,&src,sizeof(uint32_t));
|
||||
return dest;
|
||||
}
|
||||
int32_t BitConverter::ToSint32LE(uint8_t& b)
|
||||
{
|
||||
uint32_t src = ToUint32LE(b);
|
||||
int32_t dest = 0;
|
||||
memcpy(&dest,&src,sizeof(uint32_t));
|
||||
return dest;
|
||||
}
|
||||
b2[0] = (uint8_t)v;
|
||||
b2[1] = (uint8_t)(v >> 8);
|
||||
b2[2] = (uint8_t)(v >> 16);
|
||||
b2[3] = (uint8_t)(v >> 24);
|
||||
}
|
||||
void BitConverter::FromUint16LE(uint8_t &b, uint16_t v) {
|
||||
uint8_t *b2 = &b;
|
||||
|
||||
b2[0] = (uint8_t)v;
|
||||
b2[1] = (uint8_t)(v >> 8);
|
||||
}
|
||||
void BitConverter::FromUuid(uint8_t &b, const Uuid &uuid) {
|
||||
uint8_t *b2 = &b;
|
||||
FromUint32BE(b2[0], uuid.time_low);
|
||||
FromUint16BE(b2[4], uuid.time_mid);
|
||||
FromUint16BE(b2[6], uuid.time_hi_and_version);
|
||||
b2[8] = uuid.clock_seq_hi_and_reserved;
|
||||
b2[9] = uuid.clock_seq_low;
|
||||
for (size_t i = 0; i < 6; i++)
|
||||
b2[i + 10] = uuid.node[i];
|
||||
}
|
||||
|
||||
int16_t BitConverter::ToSint16BE(uint8_t& b)
|
||||
{
|
||||
uint16_t src = ToUint16BE(b);
|
||||
int16_t dest = 0;
|
||||
memcpy(&dest,&src,sizeof(uint16_t));
|
||||
return dest;
|
||||
}
|
||||
int16_t BitConverter::ToSint16LE(uint8_t& b)
|
||||
{
|
||||
uint16_t src = ToUint16LE(b);
|
||||
int16_t dest = 0;
|
||||
memcpy(&dest,&src,sizeof(uint16_t));
|
||||
return dest;
|
||||
}
|
||||
|
||||
Uuid BitConverter::ToUuid(uint8_t &b) {
|
||||
Uuid uuid;
|
||||
BitConverter::ToUuid(b, uuid);
|
||||
return uuid;
|
||||
}
|
||||
|
||||
void BitConverter::FromSint64BE(uint8_t& b, int64_t v)
|
||||
{
|
||||
uint64_t dest = 0;
|
||||
memcpy(&dest,&v,sizeof(uint64_t));
|
||||
FromUint64BE(b, dest);
|
||||
}
|
||||
void BitConverter::FromSint32BE(uint8_t& b, int32_t v)
|
||||
{
|
||||
uint32_t dest = 0;
|
||||
memcpy(&dest,&v,sizeof(uint32_t));
|
||||
FromUint32BE(b, dest);
|
||||
}
|
||||
void BitConverter::FromSint16BE(uint8_t& b, int16_t v)
|
||||
{
|
||||
uint16_t dest = 0;
|
||||
memcpy(&dest,&v,sizeof(uint16_t));
|
||||
FromUint16BE(b, dest);
|
||||
}
|
||||
|
||||
void BitConverter::FromSint64LE(uint8_t& b, int64_t v)
|
||||
{
|
||||
uint64_t dest = 0;
|
||||
memcpy(&dest,&v,sizeof(uint64_t));
|
||||
FromUint64LE(b, dest);
|
||||
}
|
||||
void BitConverter::FromSint32LE(uint8_t& b, int32_t v)
|
||||
{
|
||||
uint32_t dest = 0;
|
||||
memcpy(&dest,&v,sizeof(uint32_t));
|
||||
FromUint32LE(b, dest);
|
||||
}
|
||||
void BitConverter::FromSint16LE(uint8_t& b, int16_t v)
|
||||
{
|
||||
uint16_t dest = 0;
|
||||
memcpy(&dest,&v,sizeof(uint16_t));
|
||||
FromUint16LE(b, dest);
|
||||
}
|
||||
}
|
||||
void BitConverter::ToUuid(uint8_t &b, Uuid &uuid) {
|
||||
uint8_t *b2 = &b;
|
||||
uuid.time_low = ToUint32BE(b2[0]);
|
||||
|
||||
uuid.time_mid = ToUint16BE(b2[4]);
|
||||
|
||||
uuid.time_hi_and_version = ToUint16BE(b2[6]);
|
||||
|
||||
uuid.clock_seq_hi_and_reserved = b2[8];
|
||||
uuid.clock_seq_low = b2[9];
|
||||
for (size_t i = 0; i < 6; i++)
|
||||
uuid.node[i] = b2[i + 10];
|
||||
}
|
||||
|
||||
int64_t BitConverter::ToSint64BE(uint8_t &b) {
|
||||
uint64_t src = ToUint64BE(b);
|
||||
int64_t dest = 0;
|
||||
memcpy(&dest, &src, sizeof(uint64_t));
|
||||
return dest;
|
||||
}
|
||||
int64_t BitConverter::ToSint64LE(uint8_t &b) {
|
||||
uint64_t src = ToUint64LE(b);
|
||||
int64_t dest = 0;
|
||||
memcpy(&dest, &src, sizeof(uint64_t));
|
||||
return dest;
|
||||
}
|
||||
|
||||
int32_t BitConverter::ToSint32BE(uint8_t &b) {
|
||||
uint32_t src = ToUint32BE(b);
|
||||
int32_t dest = 0;
|
||||
memcpy(&dest, &src, sizeof(uint32_t));
|
||||
return dest;
|
||||
}
|
||||
int32_t BitConverter::ToSint32LE(uint8_t &b) {
|
||||
uint32_t src = ToUint32LE(b);
|
||||
int32_t dest = 0;
|
||||
memcpy(&dest, &src, sizeof(uint32_t));
|
||||
return dest;
|
||||
}
|
||||
|
||||
int16_t BitConverter::ToSint16BE(uint8_t &b) {
|
||||
uint16_t src = ToUint16BE(b);
|
||||
int16_t dest = 0;
|
||||
memcpy(&dest, &src, sizeof(uint16_t));
|
||||
return dest;
|
||||
}
|
||||
int16_t BitConverter::ToSint16LE(uint8_t &b) {
|
||||
uint16_t src = ToUint16LE(b);
|
||||
int16_t dest = 0;
|
||||
memcpy(&dest, &src, sizeof(uint16_t));
|
||||
return dest;
|
||||
}
|
||||
|
||||
void BitConverter::FromSint64BE(uint8_t &b, int64_t v) {
|
||||
uint64_t dest = 0;
|
||||
memcpy(&dest, &v, sizeof(uint64_t));
|
||||
FromUint64BE(b, dest);
|
||||
}
|
||||
void BitConverter::FromSint32BE(uint8_t &b, int32_t v) {
|
||||
uint32_t dest = 0;
|
||||
memcpy(&dest, &v, sizeof(uint32_t));
|
||||
FromUint32BE(b, dest);
|
||||
}
|
||||
void BitConverter::FromSint16BE(uint8_t &b, int16_t v) {
|
||||
uint16_t dest = 0;
|
||||
memcpy(&dest, &v, sizeof(uint16_t));
|
||||
FromUint16BE(b, dest);
|
||||
}
|
||||
|
||||
void BitConverter::FromSint64LE(uint8_t &b, int64_t v) {
|
||||
uint64_t dest = 0;
|
||||
memcpy(&dest, &v, sizeof(uint64_t));
|
||||
FromUint64LE(b, dest);
|
||||
}
|
||||
void BitConverter::FromSint32LE(uint8_t &b, int32_t v) {
|
||||
uint32_t dest = 0;
|
||||
memcpy(&dest, &v, sizeof(uint32_t));
|
||||
FromUint32LE(b, dest);
|
||||
}
|
||||
void BitConverter::FromSint16LE(uint8_t &b, int16_t v) {
|
||||
uint16_t dest = 0;
|
||||
memcpy(&dest, &v, sizeof(uint16_t));
|
||||
FromUint16LE(b, dest);
|
||||
}
|
||||
} // namespace Tesses::Framework::Serialization
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,82 +1,102 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/Serialization/SQLite.hpp"
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_SQLITE)
|
||||
#include "sqlite/sqlite3-mod.h"
|
||||
#endif
|
||||
namespace Tesses::Framework::Serialization {
|
||||
int SQLiteDatabase::collector(void* user, int count,char** vals, char** keys)
|
||||
{
|
||||
auto list = static_cast<std::vector<std::vector<std::pair<std::string,std::optional<std::string>>>>*>(user);
|
||||
std::vector<std::pair<std::string,std::optional<std::string>>> d;
|
||||
for(int i = 0; i < count; i++)
|
||||
{
|
||||
std::string key = keys[i] == nullptr ? "" : keys[i];
|
||||
std::optional<std::string> value = vals[i] == nullptr ? std::nullopt : (std::optional<std::string>)vals[i];
|
||||
|
||||
d.push_back(std::pair<std::string,std::optional<std::string>>(key,value));
|
||||
}
|
||||
list->push_back(d);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SQLiteDatabase::SQLiteDatabase(Tesses::Framework::Filesystem::VFSPath path)
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_SQLITE)
|
||||
auto name = path.ToString();
|
||||
sqlite3* sqlite;
|
||||
int rc =sqlite3_open(name.c_str(),&sqlite);
|
||||
if(rc)
|
||||
{
|
||||
std::string error = sqlite3_errmsg(sqlite);
|
||||
throw std::runtime_error(error);
|
||||
}
|
||||
this->data = static_cast<void*>(sqlite);
|
||||
#endif
|
||||
}
|
||||
std::string SQLiteDatabase::Escape(std::string text)
|
||||
{
|
||||
std::string myStr = "\'";
|
||||
for(auto c : text)
|
||||
{
|
||||
if(c == '\'') myStr += "\'\'";
|
||||
else
|
||||
myStr += c;
|
||||
}
|
||||
myStr += '\'';
|
||||
return myStr;
|
||||
}
|
||||
bool SQLiteDatabase::IsEnabled()
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_SQLITE)
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
std::vector<std::vector<std::pair<std::string,std::optional<std::string>>>> SQLiteDatabase::Exec(std::string statement)
|
||||
{
|
||||
std::vector<std::vector<std::pair<std::string,std::optional<std::string>>>> items;
|
||||
|
||||
Exec(statement,items);
|
||||
|
||||
return items;
|
||||
}
|
||||
void SQLiteDatabase::Exec(std::string statement,std::vector<std::vector<std::pair<std::string,std::optional<std::string>>>>& items)
|
||||
{
|
||||
int SQLiteDatabase::collector(void *user, int count, char **vals, char **keys) {
|
||||
auto list = static_cast<std::vector<
|
||||
std::vector<std::pair<std::string, std::optional<std::string>>>> *>(
|
||||
user);
|
||||
std::vector<std::pair<std::string, std::optional<std::string>>> d;
|
||||
for (int i = 0; i < count; i++) {
|
||||
std::string key = keys[i] == nullptr ? "" : keys[i];
|
||||
std::optional<std::string> value =
|
||||
vals[i] == nullptr ? std::nullopt
|
||||
: (std::optional<std::string>)vals[i];
|
||||
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_SQLITE)
|
||||
char* err;
|
||||
int res = sqlite3_exec(static_cast<sqlite3*>(this->data),statement.c_str(),SQLiteDatabase::collector,&items,&err);
|
||||
if(res != SQLITE_OK)
|
||||
{
|
||||
std::string errstr = err == nullptr ? "" : err;
|
||||
sqlite3_free(err);
|
||||
throw std::runtime_error(errstr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
SQLiteDatabase::~SQLiteDatabase()
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_SQLITE)
|
||||
sqlite3_close(static_cast<sqlite3*>(this->data));
|
||||
#endif
|
||||
d.push_back(
|
||||
std::pair<std::string, std::optional<std::string>>(key, value));
|
||||
}
|
||||
list->push_back(d);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SQLiteDatabase::SQLiteDatabase(Tesses::Framework::Filesystem::VFSPath path) {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_SQLITE)
|
||||
auto name = path.ToString();
|
||||
sqlite3 *sqlite;
|
||||
int rc = sqlite3_open(name.c_str(), &sqlite);
|
||||
if (rc) {
|
||||
std::string error = sqlite3_errmsg(sqlite);
|
||||
throw std::runtime_error(error);
|
||||
}
|
||||
this->data = static_cast<void *>(sqlite);
|
||||
#endif
|
||||
}
|
||||
std::string SQLiteDatabase::Escape(std::string text) {
|
||||
std::string myStr = "\'";
|
||||
for (auto c : text) {
|
||||
if (c == '\'')
|
||||
myStr += "\'\'";
|
||||
else
|
||||
myStr += c;
|
||||
}
|
||||
myStr += '\'';
|
||||
return myStr;
|
||||
}
|
||||
bool SQLiteDatabase::IsEnabled() {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_SQLITE)
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
std::vector<std::vector<std::pair<std::string, std::optional<std::string>>>>
|
||||
SQLiteDatabase::Exec(std::string statement) {
|
||||
std::vector<std::vector<std::pair<std::string, std::optional<std::string>>>>
|
||||
items;
|
||||
|
||||
Exec(statement, items);
|
||||
|
||||
return items;
|
||||
}
|
||||
void SQLiteDatabase::Exec(
|
||||
std::string statement,
|
||||
std::vector<std::vector<std::pair<std::string, std::optional<std::string>>>>
|
||||
&items) {
|
||||
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_SQLITE)
|
||||
char *err;
|
||||
int res =
|
||||
sqlite3_exec(static_cast<sqlite3 *>(this->data), statement.c_str(),
|
||||
SQLiteDatabase::collector, &items, &err);
|
||||
if (res != SQLITE_OK) {
|
||||
std::string errstr = err == nullptr ? "" : err;
|
||||
sqlite3_free(err);
|
||||
throw std::runtime_error(errstr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
SQLiteDatabase::~SQLiteDatabase() {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_SQLITE)
|
||||
sqlite3_close(static_cast<sqlite3 *>(this->data));
|
||||
#endif
|
||||
}
|
||||
} // namespace Tesses::Framework::Serialization
|
||||
|
||||
@@ -1,66 +1,69 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/Streams/BufferedStream.hpp"
|
||||
namespace Tesses::Framework::Streams {
|
||||
BufferedStream::BufferedStream(std::shared_ptr<Stream> strm, size_t bufferSize)
|
||||
{
|
||||
this->strm = strm;
|
||||
this->bufferSize = bufferSize;
|
||||
this->buffer = new uint8_t[bufferSize];
|
||||
this->read = 0;
|
||||
BufferedStream::BufferedStream(std::shared_ptr<Stream> strm,
|
||||
size_t bufferSize) {
|
||||
this->strm = strm;
|
||||
this->bufferSize = bufferSize;
|
||||
this->buffer = new uint8_t[bufferSize];
|
||||
this->read = 0;
|
||||
this->offset = 0;
|
||||
}
|
||||
|
||||
bool BufferedStream::EndOfStream() {
|
||||
if (this->offset < this->read)
|
||||
return false;
|
||||
return this->strm->EndOfStream();
|
||||
}
|
||||
bool BufferedStream::CanRead() {
|
||||
if (this->offset < this->read)
|
||||
return true;
|
||||
return this->strm->CanRead();
|
||||
}
|
||||
bool BufferedStream::CanWrite() { return this->strm->CanWrite(); }
|
||||
size_t BufferedStream::Read(uint8_t *buff, size_t sz) {
|
||||
if (this->offset < this->read) {
|
||||
sz = std::min(sz, this->read - this->offset);
|
||||
|
||||
memcpy(buff, this->buffer + this->offset, sz);
|
||||
this->offset += sz;
|
||||
return sz;
|
||||
}
|
||||
if (sz < this->bufferSize) {
|
||||
this->read = this->strm->Read(this->buffer, this->bufferSize);
|
||||
this->offset = 0;
|
||||
}
|
||||
|
||||
bool BufferedStream::EndOfStream()
|
||||
{
|
||||
if(this->offset < this->read) return false;
|
||||
return this->strm->EndOfStream();
|
||||
}
|
||||
bool BufferedStream::CanRead()
|
||||
{
|
||||
if(this->offset < this->read) return true;
|
||||
return this->strm->CanRead();
|
||||
}
|
||||
bool BufferedStream::CanWrite()
|
||||
{
|
||||
return this->strm->CanWrite();
|
||||
}
|
||||
size_t BufferedStream::Read(uint8_t* buff, size_t sz)
|
||||
{
|
||||
if(this->offset < this->read)
|
||||
{
|
||||
sz = std::min(sz,this->read-this->offset);
|
||||
|
||||
memcpy(buff, this->buffer+this->offset, sz);
|
||||
this->offset+=sz;
|
||||
return sz;
|
||||
}
|
||||
if(sz < this->bufferSize)
|
||||
{
|
||||
this->read = this->strm->Read(this->buffer, this->bufferSize);
|
||||
this->offset=0;
|
||||
|
||||
sz = std::min(sz,this->read-this->offset);
|
||||
|
||||
memcpy(buff, this->buffer+this->offset, sz);
|
||||
this->offset+=sz;
|
||||
return sz;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this->strm->Read(buff, sz);
|
||||
}
|
||||
}
|
||||
size_t BufferedStream::Write(const uint8_t* buff, size_t sz)
|
||||
{
|
||||
return this->strm->Write(buff,sz);
|
||||
}
|
||||
|
||||
BufferedStream::~BufferedStream()
|
||||
{
|
||||
delete buffer;
|
||||
}
|
||||
sz = std::min(sz, this->read - this->offset);
|
||||
|
||||
void BufferedStream::Close()
|
||||
{
|
||||
this->strm->Close();
|
||||
memcpy(buff, this->buffer + this->offset, sz);
|
||||
this->offset += sz;
|
||||
return sz;
|
||||
} else {
|
||||
return this->strm->Read(buff, sz);
|
||||
}
|
||||
}
|
||||
}
|
||||
size_t BufferedStream::Write(const uint8_t *buff, size_t sz) {
|
||||
return this->strm->Write(buff, sz);
|
||||
}
|
||||
|
||||
BufferedStream::~BufferedStream() { delete buffer; }
|
||||
|
||||
void BufferedStream::Close() { this->strm->Close(); }
|
||||
} // namespace Tesses::Framework::Streams
|
||||
@@ -1,169 +1,165 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/Streams/ByteReader.hpp"
|
||||
#include "TessesFramework/Serialization/BitConverter.hpp"
|
||||
namespace Tesses::Framework::Streams
|
||||
{
|
||||
std::shared_ptr<Stream> ByteReader::GetStream()
|
||||
{
|
||||
return this->strm;
|
||||
}
|
||||
ByteReader::ByteReader(std::shared_ptr<Stream> strm)
|
||||
{
|
||||
this->strm = strm;
|
||||
}
|
||||
|
||||
uint8_t ByteReader::ReadU8()
|
||||
{
|
||||
auto r = this->strm->ReadByte();
|
||||
if(r < 0) throw std::runtime_error("End of file");
|
||||
return (uint8_t)r;
|
||||
}
|
||||
uint16_t ByteReader::ReadU16BE()
|
||||
{
|
||||
uint8_t data[2];
|
||||
if(this->strm->ReadBlock(data,2) != 2) throw std::runtime_error("End of file");
|
||||
uint16_t n = 0;
|
||||
n |= (uint16_t)data[0] << 8;
|
||||
n |= (uint16_t)data[1];
|
||||
namespace Tesses::Framework::Streams {
|
||||
std::shared_ptr<Stream> ByteReader::GetStream() { return this->strm; }
|
||||
ByteReader::ByteReader(std::shared_ptr<Stream> strm) { this->strm = strm; }
|
||||
|
||||
return n;
|
||||
}
|
||||
uint16_t ByteReader::ReadU16LE()
|
||||
{
|
||||
uint8_t data[2];
|
||||
if(this->strm->ReadBlock(data,2) != 2) throw std::runtime_error("End of file");
|
||||
uint16_t n = 0;
|
||||
n |= (uint16_t)data[0];
|
||||
n |= (uint16_t)data[1] << 8;
|
||||
|
||||
uint8_t ByteReader::ReadU8() {
|
||||
auto r = this->strm->ReadByte();
|
||||
if (r < 0)
|
||||
throw std::runtime_error("End of file");
|
||||
return (uint8_t)r;
|
||||
}
|
||||
uint16_t ByteReader::ReadU16BE() {
|
||||
uint8_t data[2];
|
||||
if (this->strm->ReadBlock(data, 2) != 2)
|
||||
throw std::runtime_error("End of file");
|
||||
uint16_t n = 0;
|
||||
n |= (uint16_t)data[0] << 8;
|
||||
n |= (uint16_t)data[1];
|
||||
|
||||
return n;
|
||||
}
|
||||
uint32_t ByteReader::ReadU32BE()
|
||||
{
|
||||
return n;
|
||||
}
|
||||
uint16_t ByteReader::ReadU16LE() {
|
||||
uint8_t data[2];
|
||||
if (this->strm->ReadBlock(data, 2) != 2)
|
||||
throw std::runtime_error("End of file");
|
||||
uint16_t n = 0;
|
||||
n |= (uint16_t)data[0];
|
||||
n |= (uint16_t)data[1] << 8;
|
||||
|
||||
uint8_t data[4];
|
||||
if(this->strm->ReadBlock(data,4) != 4) throw std::runtime_error("End of file");
|
||||
uint32_t n = 0;
|
||||
n |= (uint32_t)data[0] << 24;
|
||||
n |= (uint32_t)data[1] << 16;
|
||||
n |= (uint32_t)data[2] << 8;
|
||||
n |= (uint32_t)data[3];
|
||||
return n;
|
||||
}
|
||||
uint32_t ByteReader::ReadU32BE() {
|
||||
|
||||
return n;
|
||||
}
|
||||
uint32_t ByteReader::ReadU32LE()
|
||||
{
|
||||
uint8_t data[4];
|
||||
if(this->strm->ReadBlock(data,4) != 4) throw std::runtime_error("End of file");
|
||||
uint32_t n = 0;
|
||||
n |= (uint32_t)data[0];
|
||||
n |= (uint32_t)data[1] << 8;
|
||||
n |= (uint32_t)data[2] << 16;
|
||||
n |= (uint32_t)data[3] << 24;
|
||||
return n;
|
||||
}
|
||||
uint64_t ByteReader::ReadU64BE()
|
||||
{
|
||||
uint8_t data[8];
|
||||
if(this->strm->ReadBlock(data,8) != 8) throw std::runtime_error("End of file");
|
||||
uint64_t n = 0;
|
||||
n |= (uint64_t)data[0] << 56;
|
||||
n |= (uint64_t)data[1] << 48;
|
||||
n |= (uint64_t)data[2] << 40;
|
||||
n |= (uint64_t)data[3] << 32;
|
||||
n |= (uint64_t)data[4] << 24;
|
||||
n |= (uint64_t)data[5] << 16;
|
||||
n |= (uint64_t)data[6] << 8;
|
||||
n |= (uint64_t)data[7];
|
||||
uint8_t data[4];
|
||||
if (this->strm->ReadBlock(data, 4) != 4)
|
||||
throw std::runtime_error("End of file");
|
||||
uint32_t n = 0;
|
||||
n |= (uint32_t)data[0] << 24;
|
||||
n |= (uint32_t)data[1] << 16;
|
||||
n |= (uint32_t)data[2] << 8;
|
||||
n |= (uint32_t)data[3];
|
||||
|
||||
return n;
|
||||
}
|
||||
uint64_t ByteReader::ReadU64LE()
|
||||
{
|
||||
uint8_t data[8];
|
||||
if(this->strm->ReadBlock(data,8) != 8) throw std::runtime_error("End of file");
|
||||
uint64_t n = 0;
|
||||
n |= (uint64_t)data[0];
|
||||
n |= (uint64_t)data[1] << 8;
|
||||
n |= (uint64_t)data[2] << 16;
|
||||
n |= (uint64_t)data[3] << 24;
|
||||
n |= (uint64_t)data[4] << 32;
|
||||
n |= (uint64_t)data[5] << 40;
|
||||
n |= (uint64_t)data[6] << 48;
|
||||
n |= (uint64_t)data[7] << 56;
|
||||
|
||||
return n;
|
||||
}
|
||||
uint32_t ByteReader::ReadU32LE() {
|
||||
uint8_t data[4];
|
||||
if (this->strm->ReadBlock(data, 4) != 4)
|
||||
throw std::runtime_error("End of file");
|
||||
uint32_t n = 0;
|
||||
n |= (uint32_t)data[0];
|
||||
n |= (uint32_t)data[1] << 8;
|
||||
n |= (uint32_t)data[2] << 16;
|
||||
n |= (uint32_t)data[3] << 24;
|
||||
return n;
|
||||
}
|
||||
uint64_t ByteReader::ReadU64BE() {
|
||||
uint8_t data[8];
|
||||
if (this->strm->ReadBlock(data, 8) != 8)
|
||||
throw std::runtime_error("End of file");
|
||||
uint64_t n = 0;
|
||||
n |= (uint64_t)data[0] << 56;
|
||||
n |= (uint64_t)data[1] << 48;
|
||||
n |= (uint64_t)data[2] << 40;
|
||||
n |= (uint64_t)data[3] << 32;
|
||||
n |= (uint64_t)data[4] << 24;
|
||||
n |= (uint64_t)data[5] << 16;
|
||||
n |= (uint64_t)data[6] << 8;
|
||||
n |= (uint64_t)data[7];
|
||||
|
||||
return n;
|
||||
}
|
||||
int8_t ByteReader::ReadI8()
|
||||
{
|
||||
auto v=ReadU8();
|
||||
return *(int8_t*)&v;
|
||||
}
|
||||
int16_t ByteReader::ReadI16BE()
|
||||
{
|
||||
auto v=ReadU16BE();
|
||||
return *(int16_t*)&v;
|
||||
}
|
||||
int16_t ByteReader::ReadI16LE()
|
||||
{
|
||||
auto v=ReadU16BE();
|
||||
return *(int16_t*)&v;
|
||||
}
|
||||
int32_t ByteReader::ReadI32BE()
|
||||
{
|
||||
auto v=ReadU32BE();
|
||||
return *(int32_t*)&v;
|
||||
}
|
||||
int32_t ByteReader::ReadI32LE()
|
||||
{
|
||||
auto v=ReadU32LE();
|
||||
return *(int32_t*)&v;
|
||||
}
|
||||
int64_t ByteReader::ReadI64BE()
|
||||
{
|
||||
auto v=ReadU64BE();
|
||||
return *(int64_t*)&v;
|
||||
}
|
||||
int64_t ByteReader::ReadI64LE()
|
||||
{
|
||||
auto v=ReadU64LE();
|
||||
return *(int64_t*)&v;
|
||||
}
|
||||
float ByteReader::ReadF32BE()
|
||||
{
|
||||
auto v=ReadU32BE();
|
||||
return *(float*)&v;
|
||||
}
|
||||
float ByteReader::ReadF32LE()
|
||||
{
|
||||
auto v=ReadU32LE();
|
||||
return *(float*)&v;
|
||||
}
|
||||
double ByteReader::ReadF64BE()
|
||||
{
|
||||
auto v=ReadU64BE();
|
||||
return *(double*)&v;
|
||||
}
|
||||
double ByteReader::ReadF64LE()
|
||||
{
|
||||
auto v=ReadU64LE();
|
||||
return *(double*)&v;
|
||||
}
|
||||
|
||||
Uuid ByteReader::ReadUuid()
|
||||
{
|
||||
uint8_t data[16];
|
||||
if(this->strm->ReadBlock(data, 16) != 16)
|
||||
throw std::runtime_error("End of file");
|
||||
return Serialization::BitConverter::ToUuid(data[0]);
|
||||
}
|
||||
void ByteReader::ReadUuid(Uuid& uuid)
|
||||
{
|
||||
uint8_t data[16];
|
||||
if(this->strm->ReadBlock(data, 16) != 16)
|
||||
throw std::runtime_error("End of file");
|
||||
Serialization::BitConverter::ToUuid(data[0],uuid);
|
||||
}
|
||||
|
||||
}
|
||||
return n;
|
||||
}
|
||||
uint64_t ByteReader::ReadU64LE() {
|
||||
uint8_t data[8];
|
||||
if (this->strm->ReadBlock(data, 8) != 8)
|
||||
throw std::runtime_error("End of file");
|
||||
uint64_t n = 0;
|
||||
n |= (uint64_t)data[0];
|
||||
n |= (uint64_t)data[1] << 8;
|
||||
n |= (uint64_t)data[2] << 16;
|
||||
n |= (uint64_t)data[3] << 24;
|
||||
n |= (uint64_t)data[4] << 32;
|
||||
n |= (uint64_t)data[5] << 40;
|
||||
n |= (uint64_t)data[6] << 48;
|
||||
n |= (uint64_t)data[7] << 56;
|
||||
|
||||
return n;
|
||||
}
|
||||
int8_t ByteReader::ReadI8() {
|
||||
auto v = ReadU8();
|
||||
return *(int8_t *)&v;
|
||||
}
|
||||
int16_t ByteReader::ReadI16BE() {
|
||||
auto v = ReadU16BE();
|
||||
return *(int16_t *)&v;
|
||||
}
|
||||
int16_t ByteReader::ReadI16LE() {
|
||||
auto v = ReadU16BE();
|
||||
return *(int16_t *)&v;
|
||||
}
|
||||
int32_t ByteReader::ReadI32BE() {
|
||||
auto v = ReadU32BE();
|
||||
return *(int32_t *)&v;
|
||||
}
|
||||
int32_t ByteReader::ReadI32LE() {
|
||||
auto v = ReadU32LE();
|
||||
return *(int32_t *)&v;
|
||||
}
|
||||
int64_t ByteReader::ReadI64BE() {
|
||||
auto v = ReadU64BE();
|
||||
return *(int64_t *)&v;
|
||||
}
|
||||
int64_t ByteReader::ReadI64LE() {
|
||||
auto v = ReadU64LE();
|
||||
return *(int64_t *)&v;
|
||||
}
|
||||
float ByteReader::ReadF32BE() {
|
||||
auto v = ReadU32BE();
|
||||
return *(float *)&v;
|
||||
}
|
||||
float ByteReader::ReadF32LE() {
|
||||
auto v = ReadU32LE();
|
||||
return *(float *)&v;
|
||||
}
|
||||
double ByteReader::ReadF64BE() {
|
||||
auto v = ReadU64BE();
|
||||
return *(double *)&v;
|
||||
}
|
||||
double ByteReader::ReadF64LE() {
|
||||
auto v = ReadU64LE();
|
||||
return *(double *)&v;
|
||||
}
|
||||
|
||||
Uuid ByteReader::ReadUuid() {
|
||||
uint8_t data[16];
|
||||
if (this->strm->ReadBlock(data, 16) != 16)
|
||||
throw std::runtime_error("End of file");
|
||||
return Serialization::BitConverter::ToUuid(data[0]);
|
||||
}
|
||||
void ByteReader::ReadUuid(Uuid &uuid) {
|
||||
uint8_t data[16];
|
||||
if (this->strm->ReadBlock(data, 16) != 16)
|
||||
throw std::runtime_error("End of file");
|
||||
Serialization::BitConverter::ToUuid(data[0], uuid);
|
||||
}
|
||||
|
||||
} // namespace Tesses::Framework::Streams
|
||||
@@ -1,140 +1,130 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/Streams/ByteWriter.hpp"
|
||||
#include "TessesFramework/Serialization/BitConverter.hpp"
|
||||
namespace Tesses::Framework::Streams
|
||||
{
|
||||
std::shared_ptr<Stream> ByteWriter::GetStream()
|
||||
{
|
||||
return this->strm;
|
||||
}
|
||||
ByteWriter::ByteWriter(std::shared_ptr<Stream> strm)
|
||||
{
|
||||
this->strm = strm;
|
||||
}
|
||||
|
||||
void ByteWriter::WriteU8(uint8_t v)
|
||||
{
|
||||
strm->WriteByte(v);
|
||||
}
|
||||
void ByteWriter::WriteU16BE(uint16_t v)
|
||||
{
|
||||
uint8_t b[2];
|
||||
b[0] = (uint8_t)(v >> 8);
|
||||
b[1] = (uint8_t)v;
|
||||
strm->WriteBlock(b,2);
|
||||
}
|
||||
void ByteWriter::WriteU16LE(uint16_t v)
|
||||
{
|
||||
uint8_t b[2];
|
||||
b[0] = (uint8_t)v;
|
||||
b[1] = (uint8_t)(v >> 8);
|
||||
strm->WriteBlock(b,2);
|
||||
}
|
||||
void ByteWriter::WriteU32BE(uint32_t v)
|
||||
{
|
||||
uint8_t b[4];
|
||||
b[0] = (uint8_t)(v >> 24);
|
||||
b[1] = (uint8_t)(v >> 16);
|
||||
b[2] = (uint8_t)(v >> 8);
|
||||
b[3] = (uint8_t)v;
|
||||
strm->WriteBlock(b,4);
|
||||
}
|
||||
void ByteWriter::WriteU32LE(uint32_t v)
|
||||
{
|
||||
uint8_t b[4];
|
||||
b[0] = (uint8_t)v;
|
||||
b[1] = (uint8_t)(v >> 8);
|
||||
b[2] = (uint8_t)(v >> 16);
|
||||
b[3] = (uint8_t)(v >> 24);
|
||||
|
||||
strm->WriteBlock(b,4);
|
||||
}
|
||||
void ByteWriter::WriteU64BE(uint64_t v)
|
||||
{
|
||||
uint8_t b[8];
|
||||
b[0] = (uint8_t)(v >> 56);
|
||||
b[1] = (uint8_t)(v >> 48);
|
||||
b[2] = (uint8_t)(v >> 40);
|
||||
b[3] = (uint8_t)(v >> 32);
|
||||
b[4] = (uint8_t)(v >> 24);
|
||||
b[5] = (uint8_t)(v >> 16);
|
||||
b[6] = (uint8_t)(v >> 8);
|
||||
b[7] = (uint8_t)v;
|
||||
strm->WriteBlock(b,8);
|
||||
}
|
||||
void ByteWriter::WriteU64LE(uint64_t v)
|
||||
{
|
||||
uint8_t b[8];
|
||||
|
||||
b[0] = (uint8_t)v;
|
||||
b[1] = (uint8_t)(v >> 8);
|
||||
b[2] = (uint8_t)(v >> 16);
|
||||
b[3] = (uint8_t)(v >> 24);
|
||||
b[4] = (uint8_t)(v >> 32);
|
||||
b[5] = (uint8_t)(v >> 40);
|
||||
b[6] = (uint8_t)(v >> 48);
|
||||
b[7] = (uint8_t)(v >> 56);
|
||||
strm->WriteBlock(b,8);
|
||||
}
|
||||
void ByteWriter::WriteI8(int8_t v)
|
||||
{
|
||||
uint8_t data = *(uint8_t*)&v;
|
||||
WriteU8(data);
|
||||
}
|
||||
void ByteWriter::WriteI16BE(int16_t v)
|
||||
{
|
||||
uint16_t data = *(uint16_t*)&v;
|
||||
WriteU16BE(data);
|
||||
}
|
||||
void ByteWriter::WriteI16LE(int16_t v)
|
||||
{
|
||||
uint16_t data = *(uint16_t*)&v;
|
||||
WriteU16LE(data);
|
||||
}
|
||||
void ByteWriter::WriteI32BE(int32_t v)
|
||||
{
|
||||
uint32_t data = *(uint32_t*)&v;
|
||||
WriteU32BE(data);
|
||||
}
|
||||
void ByteWriter::WriteI32LE(int32_t v)
|
||||
{
|
||||
uint32_t data = *(uint32_t*)&v;
|
||||
WriteU32LE(data);
|
||||
}
|
||||
void ByteWriter::WriteI64BE(int64_t v)
|
||||
{
|
||||
uint64_t data = *(uint64_t*)&v;
|
||||
WriteU64BE(data);
|
||||
}
|
||||
void ByteWriter::WriteI64LE(int64_t v)
|
||||
{
|
||||
uint64_t data = *(uint64_t*)&v;
|
||||
WriteU64LE(data);
|
||||
}
|
||||
void ByteWriter::WriteF32BE(float v)
|
||||
{
|
||||
uint32_t data = *(uint32_t*)&v;
|
||||
WriteU32BE(data);
|
||||
}
|
||||
void ByteWriter::WriteF32LE(float v)
|
||||
{
|
||||
uint32_t data = *(uint32_t*)&v;
|
||||
WriteU32LE(data);
|
||||
}
|
||||
void ByteWriter::WriteF64BE(double v)
|
||||
{
|
||||
uint64_t data = *(uint64_t*)&v;
|
||||
WriteU64BE(data);
|
||||
}
|
||||
void ByteWriter::WriteF64LE(double v)
|
||||
{
|
||||
uint64_t data = *(uint64_t*)&v;
|
||||
WriteU64LE(data);
|
||||
}
|
||||
void ByteWriter::WriteUuid(const Uuid& uuid)
|
||||
{
|
||||
uint8_t data[16];
|
||||
Serialization::BitConverter::FromUuid(data[0], uuid);
|
||||
this->strm->WriteBlock(data, 16);
|
||||
}
|
||||
|
||||
}
|
||||
namespace Tesses::Framework::Streams {
|
||||
std::shared_ptr<Stream> ByteWriter::GetStream() { return this->strm; }
|
||||
ByteWriter::ByteWriter(std::shared_ptr<Stream> strm) { this->strm = strm; }
|
||||
|
||||
void ByteWriter::WriteU8(uint8_t v) { strm->WriteByte(v); }
|
||||
void ByteWriter::WriteU16BE(uint16_t v) {
|
||||
uint8_t b[2];
|
||||
b[0] = (uint8_t)(v >> 8);
|
||||
b[1] = (uint8_t)v;
|
||||
strm->WriteBlock(b, 2);
|
||||
}
|
||||
void ByteWriter::WriteU16LE(uint16_t v) {
|
||||
uint8_t b[2];
|
||||
b[0] = (uint8_t)v;
|
||||
b[1] = (uint8_t)(v >> 8);
|
||||
strm->WriteBlock(b, 2);
|
||||
}
|
||||
void ByteWriter::WriteU32BE(uint32_t v) {
|
||||
uint8_t b[4];
|
||||
b[0] = (uint8_t)(v >> 24);
|
||||
b[1] = (uint8_t)(v >> 16);
|
||||
b[2] = (uint8_t)(v >> 8);
|
||||
b[3] = (uint8_t)v;
|
||||
strm->WriteBlock(b, 4);
|
||||
}
|
||||
void ByteWriter::WriteU32LE(uint32_t v) {
|
||||
uint8_t b[4];
|
||||
b[0] = (uint8_t)v;
|
||||
b[1] = (uint8_t)(v >> 8);
|
||||
b[2] = (uint8_t)(v >> 16);
|
||||
b[3] = (uint8_t)(v >> 24);
|
||||
|
||||
strm->WriteBlock(b, 4);
|
||||
}
|
||||
void ByteWriter::WriteU64BE(uint64_t v) {
|
||||
uint8_t b[8];
|
||||
b[0] = (uint8_t)(v >> 56);
|
||||
b[1] = (uint8_t)(v >> 48);
|
||||
b[2] = (uint8_t)(v >> 40);
|
||||
b[3] = (uint8_t)(v >> 32);
|
||||
b[4] = (uint8_t)(v >> 24);
|
||||
b[5] = (uint8_t)(v >> 16);
|
||||
b[6] = (uint8_t)(v >> 8);
|
||||
b[7] = (uint8_t)v;
|
||||
strm->WriteBlock(b, 8);
|
||||
}
|
||||
void ByteWriter::WriteU64LE(uint64_t v) {
|
||||
uint8_t b[8];
|
||||
|
||||
b[0] = (uint8_t)v;
|
||||
b[1] = (uint8_t)(v >> 8);
|
||||
b[2] = (uint8_t)(v >> 16);
|
||||
b[3] = (uint8_t)(v >> 24);
|
||||
b[4] = (uint8_t)(v >> 32);
|
||||
b[5] = (uint8_t)(v >> 40);
|
||||
b[6] = (uint8_t)(v >> 48);
|
||||
b[7] = (uint8_t)(v >> 56);
|
||||
strm->WriteBlock(b, 8);
|
||||
}
|
||||
void ByteWriter::WriteI8(int8_t v) {
|
||||
uint8_t data = *(uint8_t *)&v;
|
||||
WriteU8(data);
|
||||
}
|
||||
void ByteWriter::WriteI16BE(int16_t v) {
|
||||
uint16_t data = *(uint16_t *)&v;
|
||||
WriteU16BE(data);
|
||||
}
|
||||
void ByteWriter::WriteI16LE(int16_t v) {
|
||||
uint16_t data = *(uint16_t *)&v;
|
||||
WriteU16LE(data);
|
||||
}
|
||||
void ByteWriter::WriteI32BE(int32_t v) {
|
||||
uint32_t data = *(uint32_t *)&v;
|
||||
WriteU32BE(data);
|
||||
}
|
||||
void ByteWriter::WriteI32LE(int32_t v) {
|
||||
uint32_t data = *(uint32_t *)&v;
|
||||
WriteU32LE(data);
|
||||
}
|
||||
void ByteWriter::WriteI64BE(int64_t v) {
|
||||
uint64_t data = *(uint64_t *)&v;
|
||||
WriteU64BE(data);
|
||||
}
|
||||
void ByteWriter::WriteI64LE(int64_t v) {
|
||||
uint64_t data = *(uint64_t *)&v;
|
||||
WriteU64LE(data);
|
||||
}
|
||||
void ByteWriter::WriteF32BE(float v) {
|
||||
uint32_t data = *(uint32_t *)&v;
|
||||
WriteU32BE(data);
|
||||
}
|
||||
void ByteWriter::WriteF32LE(float v) {
|
||||
uint32_t data = *(uint32_t *)&v;
|
||||
WriteU32LE(data);
|
||||
}
|
||||
void ByteWriter::WriteF64BE(double v) {
|
||||
uint64_t data = *(uint64_t *)&v;
|
||||
WriteU64BE(data);
|
||||
}
|
||||
void ByteWriter::WriteF64LE(double v) {
|
||||
uint64_t data = *(uint64_t *)&v;
|
||||
WriteU64LE(data);
|
||||
}
|
||||
void ByteWriter::WriteUuid(const Uuid &uuid) {
|
||||
uint8_t data[16];
|
||||
Serialization::BitConverter::FromUuid(data[0], uuid);
|
||||
this->strm->WriteBlock(data, 16);
|
||||
}
|
||||
|
||||
} // namespace Tesses::Framework::Streams
|
||||
@@ -1,123 +1,126 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/Streams/FileStream.hpp"
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#undef min
|
||||
#endif
|
||||
namespace Tesses::Framework::Streams
|
||||
{
|
||||
void FileStream::SetMode(std::string mode)
|
||||
{
|
||||
this->canRead = false;
|
||||
this->canWrite = false;
|
||||
this->canSeek = true;
|
||||
if(mode.size() >= 1)
|
||||
{
|
||||
if(mode[0] == 'r')
|
||||
{
|
||||
this->canRead = true;
|
||||
}
|
||||
else if(mode[0] == 'w')
|
||||
{
|
||||
this->canWrite = true;
|
||||
}
|
||||
else if(mode[0] == 'a')
|
||||
{
|
||||
this->canSeek = false;
|
||||
this->canWrite = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(((mode.size() >= 2 && mode[1] == '+') || (mode.size() >= 2 && mode[1] == 'b' && mode[2] == '+')))
|
||||
{
|
||||
namespace Tesses::Framework::Streams {
|
||||
void FileStream::SetMode(std::string mode) {
|
||||
this->canRead = false;
|
||||
this->canWrite = false;
|
||||
this->canSeek = true;
|
||||
if (mode.size() >= 1) {
|
||||
if (mode[0] == 'r') {
|
||||
this->canRead = true;
|
||||
} else if (mode[0] == 'w') {
|
||||
this->canWrite = true;
|
||||
} else if (mode[0] == 'a') {
|
||||
this->canSeek = false;
|
||||
this->canWrite = true;
|
||||
}
|
||||
}
|
||||
FileStream::FileStream(std::filesystem::path p, std::string mode)
|
||||
{
|
||||
std::string str = p.string();
|
||||
this->f = fopen(str.c_str(),mode.c_str());
|
||||
this->canSeek = true;
|
||||
this->owns=true;
|
||||
this->SetMode(mode);
|
||||
}
|
||||
FileStream::FileStream(FILE* f, bool owns, std::string mode , bool canSeek)
|
||||
{
|
||||
this->f = f;
|
||||
this->owns = owns;
|
||||
this->SetMode(mode);
|
||||
this->canSeek = canSeek;
|
||||
}
|
||||
size_t FileStream::Read(uint8_t* buff, size_t sz)
|
||||
{
|
||||
if(!CanRead()) throw std::runtime_error("Cannot read from stream");
|
||||
return fread(buff,1, sz, this->f);
|
||||
}
|
||||
size_t FileStream::Write(const uint8_t* buff, size_t sz)
|
||||
{
|
||||
if(!CanWrite()) throw std::runtime_error("Cannot write to stream");
|
||||
return fwrite(buff,1, sz, f);
|
||||
}
|
||||
bool FileStream::CanRead()
|
||||
{
|
||||
return this->canRead && this->f;
|
||||
}
|
||||
bool FileStream::CanWrite()
|
||||
{
|
||||
return this->canWrite && this->f;
|
||||
}
|
||||
bool FileStream::CanSeek()
|
||||
{
|
||||
return this->canSeek && this->f;
|
||||
}
|
||||
bool FileStream::EndOfStream()
|
||||
{
|
||||
if(!f) return true;
|
||||
return feof(this->f);
|
||||
}
|
||||
|
||||
int64_t FileStream::GetPosition()
|
||||
{
|
||||
|
||||
if(!f) return 0;
|
||||
#if defined(_WIN32)
|
||||
return (int64_t)_ftelli64(this->f);
|
||||
#else
|
||||
return (int64_t)ftello(this->f);
|
||||
#endif
|
||||
}
|
||||
void FileStream::Flush()
|
||||
{
|
||||
|
||||
if(!f) return;
|
||||
fflush(this->f);
|
||||
}
|
||||
void FileStream::Seek(int64_t pos, SeekOrigin whence)
|
||||
{
|
||||
|
||||
if(!f) return;
|
||||
#if defined(_WIN32)
|
||||
_fseeki64(this->f,pos,whence == SeekOrigin::Begin ? SEEK_SET : whence == SeekOrigin::Current ? SEEK_CUR : SEEK_END);
|
||||
#else
|
||||
fseeko(this->f,(off_t)pos,whence == SeekOrigin::Begin ? SEEK_SET : whence == SeekOrigin::Current ? SEEK_CUR : SEEK_END);
|
||||
#endif
|
||||
}
|
||||
FileStream::~FileStream()
|
||||
{
|
||||
if(!f) return;
|
||||
if(this->owns)
|
||||
{
|
||||
fclose(this->f);
|
||||
f=NULL;
|
||||
}
|
||||
}
|
||||
void FileStream::Close()
|
||||
{
|
||||
if(!f) return;
|
||||
if(this->owns)
|
||||
{
|
||||
fclose(this->f);
|
||||
f=NULL;
|
||||
}
|
||||
if (((mode.size() >= 2 && mode[1] == '+') ||
|
||||
(mode.size() >= 2 && mode[1] == 'b' && mode[2] == '+'))) {
|
||||
this->canRead = true;
|
||||
this->canWrite = true;
|
||||
}
|
||||
}
|
||||
FileStream::FileStream(std::filesystem::path p, std::string mode) {
|
||||
std::string str = p.string();
|
||||
this->f = fopen(str.c_str(), mode.c_str());
|
||||
this->canSeek = true;
|
||||
this->owns = true;
|
||||
this->SetMode(mode);
|
||||
}
|
||||
FileStream::FileStream(FILE *f, bool owns, std::string mode, bool canSeek) {
|
||||
this->f = f;
|
||||
this->owns = owns;
|
||||
this->SetMode(mode);
|
||||
this->canSeek = canSeek;
|
||||
}
|
||||
size_t FileStream::Read(uint8_t *buff, size_t sz) {
|
||||
if (!CanRead())
|
||||
throw std::runtime_error("Cannot read from stream");
|
||||
return fread(buff, 1, sz, this->f);
|
||||
}
|
||||
size_t FileStream::Write(const uint8_t *buff, size_t sz) {
|
||||
if (!CanWrite())
|
||||
throw std::runtime_error("Cannot write to stream");
|
||||
return fwrite(buff, 1, sz, f);
|
||||
}
|
||||
bool FileStream::CanRead() { return this->canRead && this->f; }
|
||||
bool FileStream::CanWrite() { return this->canWrite && this->f; }
|
||||
bool FileStream::CanSeek() { return this->canSeek && this->f; }
|
||||
bool FileStream::EndOfStream() {
|
||||
if (!f)
|
||||
return true;
|
||||
return feof(this->f);
|
||||
}
|
||||
|
||||
int64_t FileStream::GetPosition() {
|
||||
|
||||
if (!f)
|
||||
return 0;
|
||||
#if defined(_WIN32)
|
||||
return (int64_t)_ftelli64(this->f);
|
||||
#else
|
||||
return (int64_t)ftello(this->f);
|
||||
#endif
|
||||
}
|
||||
void FileStream::Flush() {
|
||||
|
||||
if (!f)
|
||||
return;
|
||||
fflush(this->f);
|
||||
}
|
||||
void FileStream::Seek(int64_t pos, SeekOrigin whence) {
|
||||
|
||||
if (!f)
|
||||
return;
|
||||
#if defined(_WIN32)
|
||||
_fseeki64(this->f, pos,
|
||||
whence == SeekOrigin::Begin ? SEEK_SET
|
||||
: whence == SeekOrigin::Current ? SEEK_CUR
|
||||
: SEEK_END);
|
||||
#else
|
||||
fseeko(this->f, (off_t)pos,
|
||||
whence == SeekOrigin::Begin ? SEEK_SET
|
||||
: whence == SeekOrigin::Current ? SEEK_CUR
|
||||
: SEEK_END);
|
||||
#endif
|
||||
}
|
||||
FileStream::~FileStream() {
|
||||
if (!f)
|
||||
return;
|
||||
if (this->owns) {
|
||||
fclose(this->f);
|
||||
f = NULL;
|
||||
}
|
||||
}
|
||||
void FileStream::Close() {
|
||||
if (!f)
|
||||
return;
|
||||
if (this->owns) {
|
||||
fclose(this->f);
|
||||
f = NULL;
|
||||
}
|
||||
}
|
||||
} // namespace Tesses::Framework::Streams
|
||||
|
||||
@@ -1,74 +1,66 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/Streams/MemoryStream.hpp"
|
||||
|
||||
namespace Tesses::Framework::Streams
|
||||
{
|
||||
MemoryStream::MemoryStream(bool isWritable)
|
||||
{
|
||||
this->offset=0;
|
||||
this->writable = isWritable;
|
||||
namespace Tesses::Framework::Streams {
|
||||
MemoryStream::MemoryStream(bool isWritable) {
|
||||
this->offset = 0;
|
||||
this->writable = isWritable;
|
||||
}
|
||||
std::vector<uint8_t> &MemoryStream::GetBuffer() { return this->buffer; }
|
||||
size_t MemoryStream::Read(uint8_t *buff, size_t sz) {
|
||||
if (this->offset >= this->buffer.size())
|
||||
return 0;
|
||||
|
||||
size_t toRead = std::min(sz, this->buffer.size() - this->offset);
|
||||
|
||||
memcpy(buff, this->buffer.data() + this->offset, toRead);
|
||||
this->offset += toRead;
|
||||
return toRead;
|
||||
}
|
||||
size_t MemoryStream::Write(const uint8_t *buff, size_t sz) {
|
||||
if (!this->writable)
|
||||
return 0;
|
||||
if (this->offset > this->buffer.size()) {
|
||||
this->buffer.resize(this->offset + sz);
|
||||
}
|
||||
std::vector<uint8_t>& MemoryStream::GetBuffer()
|
||||
{
|
||||
return this->buffer;
|
||||
this->buffer.insert(this->buffer.begin() + this->offset, buff, buff + sz);
|
||||
this->offset += sz;
|
||||
return sz;
|
||||
}
|
||||
bool MemoryStream::CanRead() { return true; }
|
||||
bool MemoryStream::CanWrite() { return this->writable; }
|
||||
bool MemoryStream::CanSeek() { return true; }
|
||||
int64_t MemoryStream::GetLength() { return this->buffer.size(); }
|
||||
int64_t MemoryStream::GetPosition() { return (int64_t)this->offset; }
|
||||
void MemoryStream::Seek(int64_t pos, SeekOrigin whence) {
|
||||
switch (whence) {
|
||||
case SeekOrigin::Begin:
|
||||
this->offset = (size_t)pos;
|
||||
break;
|
||||
case SeekOrigin::Current:
|
||||
this->offset += (size_t)pos;
|
||||
break;
|
||||
case SeekOrigin::End:
|
||||
this->offset = (size_t)(this->buffer.size() + pos);
|
||||
break;
|
||||
}
|
||||
size_t MemoryStream::Read(uint8_t* buff, size_t sz)
|
||||
{
|
||||
if(this->offset >= this->buffer.size()) return 0;
|
||||
|
||||
size_t toRead = std::min(sz, this->buffer.size()-this->offset);
|
||||
|
||||
memcpy(buff, this->buffer.data() + this->offset, toRead);
|
||||
this->offset += toRead;
|
||||
return toRead;
|
||||
}
|
||||
size_t MemoryStream::Write(const uint8_t* buff, size_t sz)
|
||||
{
|
||||
if(!this->writable) return 0;
|
||||
if(this->offset > this->buffer.size())
|
||||
{
|
||||
this->buffer.resize(this->offset+sz);
|
||||
}
|
||||
this->buffer.insert(this->buffer.begin()+this->offset, buff, buff+sz);
|
||||
this->offset+=sz;
|
||||
return sz;
|
||||
}
|
||||
bool MemoryStream::CanRead()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool MemoryStream::CanWrite()
|
||||
{
|
||||
return this->writable;
|
||||
}
|
||||
bool MemoryStream::CanSeek()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
int64_t MemoryStream::GetLength()
|
||||
{
|
||||
return this->buffer.size();
|
||||
}
|
||||
int64_t MemoryStream::GetPosition()
|
||||
{
|
||||
return (int64_t)this->offset;
|
||||
}
|
||||
void MemoryStream::Seek(int64_t pos, SeekOrigin whence)
|
||||
{
|
||||
switch(whence)
|
||||
{
|
||||
case SeekOrigin::Begin:
|
||||
this->offset = (size_t)pos;
|
||||
break;
|
||||
case SeekOrigin::Current:
|
||||
this->offset += (size_t)pos;
|
||||
break;
|
||||
case SeekOrigin::End:
|
||||
this->offset = (size_t)(this->buffer.size() + pos);
|
||||
break;
|
||||
}
|
||||
}
|
||||
void MemoryStream::Close()
|
||||
{
|
||||
this->buffer.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
void MemoryStream::Close() { this->buffer.clear(); }
|
||||
} // namespace Tesses::Framework::Streams
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,147 +1,163 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/Streams/PtyStream.hpp"
|
||||
#if !defined(GEKKO) && !defined(__APPLE__) && !defined(__PS2__) && !defined(_WIN32) && !defined(__SWITCH__) && !defined(__FreeBSD__) && defined(TESSESFRAMEWORK_ENABLE_PROCESS)
|
||||
#if defined(__APPLE__)
|
||||
#if !defined(GEKKO) && !defined(__PS2__) && \
|
||||
!defined(_WIN32) && !defined(__SWITCH__) && \
|
||||
defined(TESSESFRAMEWORK_ENABLE_PROCESS)
|
||||
#if __has_include(<util.h>)
|
||||
#include <util.h>
|
||||
#else
|
||||
#elif __has_include(<pty.h>)
|
||||
#include <pty.h>
|
||||
#elif __has_include(<libutil.h>)
|
||||
#include <libutil.h>
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <termios.h>
|
||||
|
||||
#endif
|
||||
namespace Tesses::Framework::Streams {
|
||||
PtyStream::PtyStream(WindowSize windowSize,std::string filename, std::vector<std::string> args, std::vector<std::string> env)
|
||||
{
|
||||
#if !defined(GEKKO) && !defined(__APPLE__) && !defined(__PS2__) && !defined(_WIN32) && !defined(__SWITCH__) && !defined(__FreeBSD__) && defined(TESSESFRAMEWORK_ENABLE_PROCESS)
|
||||
this->wS = windowSize;
|
||||
this->eos=false;
|
||||
winsize sz;
|
||||
sz.ws_col =(unsigned short)windowSize.Columns;
|
||||
sz.ws_row = (unsigned short)windowSize.Rows;
|
||||
sz.ws_xpixel = (unsigned short)windowSize.Width;
|
||||
sz.ws_ypixel = (unsigned short)windowSize.Height;
|
||||
termios ios;
|
||||
cfmakeraw(&ios);
|
||||
PtyStream::PtyStream(WindowSize windowSize, std::string filename,
|
||||
std::vector<std::string> args,
|
||||
std::vector<std::string> env) {
|
||||
#if !defined(GEKKO) && !defined(__PS2__) && \
|
||||
!defined(_WIN32) && !defined(__SWITCH__) && \
|
||||
defined(TESSESFRAMEWORK_ENABLE_PROCESS)
|
||||
this->wS = windowSize;
|
||||
this->eos = false;
|
||||
winsize sz;
|
||||
sz.ws_col = (unsigned short)windowSize.Columns;
|
||||
sz.ws_row = (unsigned short)windowSize.Rows;
|
||||
sz.ws_xpixel = (unsigned short)windowSize.Width;
|
||||
sz.ws_ypixel = (unsigned short)windowSize.Height;
|
||||
termios ios;
|
||||
cfmakeraw(&ios);
|
||||
|
||||
pid= forkpty(&this->socket,NULL,&ios,&sz);
|
||||
if(pid == -1)
|
||||
{
|
||||
this->eos=true;
|
||||
}
|
||||
if(pid == 0)
|
||||
{
|
||||
char** argv = new char*[args.size()+1];
|
||||
argv[args.size()]=NULL;
|
||||
char** envp = new char*[env.size()+1];
|
||||
envp[env.size()]=NULL;
|
||||
|
||||
for(size_t i = 0; i < args.size();i++)
|
||||
{
|
||||
argv[i] = (char*)args[i].c_str();
|
||||
}
|
||||
for(size_t i = 0; i < env.size();i++)
|
||||
{
|
||||
envp[i] = (char*)env[i].c_str();
|
||||
}
|
||||
|
||||
if(execve(filename.c_str(),argv,envp) == -1)
|
||||
{
|
||||
perror("execve returned -1");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
int flags = fcntl(this->socket, F_GETFL, 0);
|
||||
if(flags == -1) {
|
||||
perror("fcntl F_GETFL");
|
||||
this->eos=true;
|
||||
return;
|
||||
}
|
||||
flags |= O_NONBLOCK;
|
||||
|
||||
flags=fcntl(this->socket,F_SETFL,flags);
|
||||
if(flags == -1) {
|
||||
perror("fcntl F_SETFL");
|
||||
|
||||
this->eos=true;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
pid = forkpty(&this->socket, NULL, &ios, &sz);
|
||||
if (pid == -1) {
|
||||
this->eos = true;
|
||||
}
|
||||
bool PtyStream::EndOfStream()
|
||||
{
|
||||
return this->eos;
|
||||
}
|
||||
bool PtyStream::CanRead()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool PtyStream::CanWrite()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
size_t PtyStream::Read(uint8_t* buff, size_t sz)
|
||||
{
|
||||
if(this->eos) return 0;
|
||||
#if !defined(GEKKO) && !defined(__APPLE__) && !defined(__PS2__) && !defined(_WIN32) && !defined(__SWITCH__) && !defined(__FreeBSD__) && defined(TESSESFRAMEWORK_ENABLE_PROCESS)
|
||||
auto res = read(this->socket, buff,sz);
|
||||
|
||||
if(res == -1)
|
||||
{
|
||||
if(errno != EAGAIN && errno != EWOULDBLOCK)
|
||||
this->eos=true;
|
||||
return 0;
|
||||
if (pid == 0) {
|
||||
char **argv = new char *[args.size() + 1];
|
||||
argv[args.size()] = NULL;
|
||||
char **envp = new char *[env.size() + 1];
|
||||
envp[env.size()] = NULL;
|
||||
|
||||
for (size_t i = 0; i < args.size(); i++) {
|
||||
argv[i] = (char *)args[i].c_str();
|
||||
}
|
||||
return (size_t)res;
|
||||
#else
|
||||
for (size_t i = 0; i < env.size(); i++) {
|
||||
envp[i] = (char *)env[i].c_str();
|
||||
}
|
||||
|
||||
if (execve(filename.c_str(), argv, envp) == -1) {
|
||||
perror("execve returned -1");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
int flags = fcntl(this->socket, F_GETFL, 0);
|
||||
if (flags == -1) {
|
||||
perror("fcntl F_GETFL");
|
||||
this->eos = true;
|
||||
return;
|
||||
}
|
||||
flags |= O_NONBLOCK;
|
||||
|
||||
flags = fcntl(this->socket, F_SETFL, flags);
|
||||
if (flags == -1) {
|
||||
perror("fcntl F_SETFL");
|
||||
|
||||
this->eos = true;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
bool PtyStream::EndOfStream() { return this->eos; }
|
||||
bool PtyStream::CanRead() { return true; }
|
||||
bool PtyStream::CanWrite() { return true; }
|
||||
size_t PtyStream::Read(uint8_t *buff, size_t sz) {
|
||||
if (this->eos)
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
size_t PtyStream::Write(const uint8_t* buff, size_t sz)
|
||||
{
|
||||
#if !defined(GEKKO) && !defined(__APPLE__) && !defined(__PS2__) && !defined(_WIN32) && !defined(__SWITCH__) && !defined(__FreeBSD__) && defined(TESSESFRAMEWORK_ENABLE_PROCESS)
|
||||
auto res = write(this->socket, buff,sz);
|
||||
return res;
|
||||
#else
|
||||
#if !defined(GEKKO) && !defined(__PS2__) && \
|
||||
!defined(_WIN32) && !defined(__SWITCH__) && \
|
||||
defined(TESSESFRAMEWORK_ENABLE_PROCESS)
|
||||
auto res = read(this->socket, buff, sz);
|
||||
|
||||
if (res == -1) {
|
||||
if (errno != EAGAIN && errno != EWOULDBLOCK)
|
||||
this->eos = true;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
WindowSize PtyStream::GetWindowSize()
|
||||
{
|
||||
return this->wS;
|
||||
}
|
||||
void PtyStream::Resize(WindowSize windowSize)
|
||||
{
|
||||
#if !defined(GEKKO) && !defined(__APPLE__) && !defined(__PS2__) && !defined(_WIN32) && !defined(__SWITCH__) && !defined(__FreeBSD__) && defined(TESSESFRAMEWORK_ENABLE_PROCESS)
|
||||
this->wS = windowSize;
|
||||
winsize sz;
|
||||
sz.ws_col =(unsigned short)windowSize.Columns;
|
||||
sz.ws_row = (unsigned short)windowSize.Rows;
|
||||
sz.ws_xpixel = (unsigned short)windowSize.Width;
|
||||
sz.ws_ypixel = (unsigned short)windowSize.Height;
|
||||
|
||||
ioctl(this->socket,TIOCSWINSZ,&sz);
|
||||
#endif
|
||||
}
|
||||
PtyStream::~PtyStream()
|
||||
{
|
||||
if(this->eos) return;
|
||||
this->eos=true;
|
||||
#if !defined(GEKKO) && !defined(__APPLE__) && !defined(__PS2__) && !defined(_WIN32) && !defined(__SWITCH__) && !defined(__FreeBSD__) && defined(TESSESFRAMEWORK_ENABLE_PROCESS)
|
||||
close(this->socket);
|
||||
|
||||
kill((pid_t)this->pid,SIGHUP);
|
||||
#endif
|
||||
}
|
||||
void PtyStream::Close()
|
||||
{
|
||||
if(this->eos) return;
|
||||
this->eos=true;
|
||||
#if !defined(GEKKO) && !defined(__APPLE__) && !defined(__PS2__) && !defined(_WIN32) && !defined(__SWITCH__) && !defined(__FreeBSD__) && defined(TESSESFRAMEWORK_ENABLE_PROCESS)
|
||||
close(this->socket);
|
||||
|
||||
kill((pid_t)this->pid,SIGHUP);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
return (size_t)res;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
size_t PtyStream::Write(const uint8_t *buff, size_t sz) {
|
||||
#if !defined(GEKKO) && !defined(__PS2__) && \
|
||||
!defined(_WIN32) && !defined(__SWITCH__) && \
|
||||
defined(TESSESFRAMEWORK_ENABLE_PROCESS)
|
||||
auto res = write(this->socket, buff, sz);
|
||||
return res;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
WindowSize PtyStream::GetWindowSize() { return this->wS; }
|
||||
void PtyStream::Resize(WindowSize windowSize) {
|
||||
#if !defined(GEKKO) && !defined(__PS2__) && \
|
||||
!defined(_WIN32) && !defined(__SWITCH__) && \
|
||||
defined(TESSESFRAMEWORK_ENABLE_PROCESS)
|
||||
this->wS = windowSize;
|
||||
winsize sz;
|
||||
sz.ws_col = (unsigned short)windowSize.Columns;
|
||||
sz.ws_row = (unsigned short)windowSize.Rows;
|
||||
sz.ws_xpixel = (unsigned short)windowSize.Width;
|
||||
sz.ws_ypixel = (unsigned short)windowSize.Height;
|
||||
|
||||
ioctl(this->socket, TIOCSWINSZ, &sz);
|
||||
#endif
|
||||
}
|
||||
PtyStream::~PtyStream() {
|
||||
if (this->eos)
|
||||
return;
|
||||
this->eos = true;
|
||||
#if !defined(GEKKO) && !defined(__PS2__) && \
|
||||
!defined(_WIN32) && !defined(__SWITCH__) && \
|
||||
defined(TESSESFRAMEWORK_ENABLE_PROCESS)
|
||||
close(this->socket);
|
||||
|
||||
kill((pid_t)this->pid, SIGHUP);
|
||||
#endif
|
||||
}
|
||||
void PtyStream::Close() {
|
||||
if (this->eos)
|
||||
return;
|
||||
this->eos = true;
|
||||
#if !defined(GEKKO) && !defined(__PS2__) && \
|
||||
!defined(_WIN32) && !defined(__SWITCH__) && \
|
||||
defined(TESSESFRAMEWORK_ENABLE_PROCESS)
|
||||
close(this->socket);
|
||||
|
||||
kill((pid_t)this->pid, SIGHUP);
|
||||
#endif
|
||||
}
|
||||
} // namespace Tesses::Framework::Streams
|
||||
@@ -1,145 +1,117 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/Streams/Stream.hpp"
|
||||
#include <iostream>
|
||||
|
||||
namespace Tesses::Framework::Streams {
|
||||
int32_t Stream::ReadByte()
|
||||
{
|
||||
uint8_t b;
|
||||
if(Read(&b, 1) == 0) return -1;
|
||||
return b;
|
||||
}
|
||||
void Stream::WriteByte(uint8_t b)
|
||||
{
|
||||
Write(&b, 1);
|
||||
}
|
||||
size_t Stream::Read(uint8_t* buffer, size_t count)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
size_t Stream::Write(const uint8_t* buffer, size_t count)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
size_t Stream::ReadBlock(uint8_t* buffer,size_t len)
|
||||
{
|
||||
size_t read;
|
||||
size_t readTotal = 0;
|
||||
do{
|
||||
read = 1024;
|
||||
if(len < 1024)
|
||||
read = len;
|
||||
if(read > 0)
|
||||
read=this->Read(buffer,read);
|
||||
|
||||
|
||||
|
||||
buffer += read;
|
||||
len -= read;
|
||||
readTotal += read;
|
||||
} while(read > 0);
|
||||
return readTotal;
|
||||
}
|
||||
|
||||
void Stream::WriteBlock(const uint8_t* buffer,size_t len)
|
||||
{
|
||||
size_t read;
|
||||
do{
|
||||
read = 1024;
|
||||
if(len < 1024)
|
||||
read = len;
|
||||
if(read > 0)
|
||||
{
|
||||
size_t r0=read;
|
||||
read=this->Write(buffer,read);
|
||||
|
||||
if(read == 0)
|
||||
{
|
||||
throw std::out_of_range("Failed to write!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
buffer += read;
|
||||
len -= read;
|
||||
} while(read > 0 && !this->EndOfStream());
|
||||
}
|
||||
bool Stream::CanRead()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool Stream::CanWrite()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool Stream::CanSeek()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool Stream::EndOfStream()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int64_t Stream::GetPosition()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int64_t Stream::GetLength()
|
||||
{
|
||||
if(!CanSeek()) return 0;
|
||||
int64_t curPos = GetPosition();
|
||||
Seek(0, SeekOrigin::End);
|
||||
int64_t len = GetPosition();
|
||||
Seek(curPos, SeekOrigin::Begin);
|
||||
return len;
|
||||
}
|
||||
void Stream::Flush()
|
||||
{
|
||||
|
||||
}
|
||||
void Stream::Seek(int64_t pos, SeekOrigin whence)
|
||||
{
|
||||
|
||||
}
|
||||
void Stream::Close()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Stream::CopyToLimit(std::shared_ptr<Stream> strm,uint64_t len, size_t buffSize)
|
||||
{
|
||||
size_t read;
|
||||
std::vector<uint8_t> buffer(buffSize);
|
||||
uint64_t offset = 0;
|
||||
|
||||
do {
|
||||
if(offset >= len) break;
|
||||
read = (size_t)std::min(len-offset,(uint64_t)buffer.size());
|
||||
|
||||
read = this->Read(buffer.data(),read);
|
||||
strm->WriteBlock(buffer.data(), read);
|
||||
|
||||
offset += read;
|
||||
|
||||
} while(read > 0 && !strm->EndOfStream());
|
||||
strm->Flush();
|
||||
|
||||
}
|
||||
|
||||
void Stream::CopyTo(std::shared_ptr<Stream> strm, size_t buffSize)
|
||||
{
|
||||
size_t read;
|
||||
std::vector<uint8_t> buffer(buffSize);
|
||||
do {
|
||||
read = this->Read(buffer.data(),buffer.size());
|
||||
strm->WriteBlock(buffer.data(), read);
|
||||
|
||||
} while(read > 0 && !strm->EndOfStream());
|
||||
strm->Flush();
|
||||
|
||||
|
||||
}
|
||||
Stream::~Stream()
|
||||
{
|
||||
|
||||
}
|
||||
int32_t Stream::ReadByte() {
|
||||
uint8_t b;
|
||||
if (Read(&b, 1) == 0)
|
||||
return -1;
|
||||
return b;
|
||||
}
|
||||
void Stream::WriteByte(uint8_t b) { Write(&b, 1); }
|
||||
size_t Stream::Read(uint8_t *buffer, size_t count) { return 0; }
|
||||
size_t Stream::Write(const uint8_t *buffer, size_t count) { return 0; }
|
||||
size_t Stream::ReadBlock(uint8_t *buffer, size_t len) {
|
||||
size_t read;
|
||||
size_t readTotal = 0;
|
||||
do {
|
||||
read = 1024;
|
||||
if (len < 1024)
|
||||
read = len;
|
||||
if (read > 0)
|
||||
read = this->Read(buffer, read);
|
||||
|
||||
buffer += read;
|
||||
len -= read;
|
||||
readTotal += read;
|
||||
} while (read > 0);
|
||||
return readTotal;
|
||||
}
|
||||
|
||||
void Stream::WriteBlock(const uint8_t *buffer, size_t len) {
|
||||
size_t read;
|
||||
do {
|
||||
read = 1024;
|
||||
if (len < 1024)
|
||||
read = len;
|
||||
if (read > 0) {
|
||||
size_t r0 = read;
|
||||
read = this->Write(buffer, read);
|
||||
|
||||
if (read == 0) {
|
||||
throw std::out_of_range("Failed to write!");
|
||||
}
|
||||
}
|
||||
|
||||
buffer += read;
|
||||
len -= read;
|
||||
} while (read > 0 && !this->EndOfStream());
|
||||
}
|
||||
bool Stream::CanRead() { return false; }
|
||||
bool Stream::CanWrite() { return false; }
|
||||
bool Stream::CanSeek() { return false; }
|
||||
bool Stream::EndOfStream() { return false; }
|
||||
int64_t Stream::GetPosition() { return 0; }
|
||||
int64_t Stream::GetLength() {
|
||||
if (!CanSeek())
|
||||
return 0;
|
||||
int64_t curPos = GetPosition();
|
||||
Seek(0, SeekOrigin::End);
|
||||
int64_t len = GetPosition();
|
||||
Seek(curPos, SeekOrigin::Begin);
|
||||
return len;
|
||||
}
|
||||
void Stream::Flush() {}
|
||||
void Stream::Seek(int64_t pos, SeekOrigin whence) {}
|
||||
void Stream::Close() {}
|
||||
|
||||
void Stream::CopyToLimit(std::shared_ptr<Stream> strm, uint64_t len,
|
||||
size_t buffSize) {
|
||||
size_t read;
|
||||
std::vector<uint8_t> buffer(buffSize);
|
||||
uint64_t offset = 0;
|
||||
|
||||
do {
|
||||
if (offset >= len)
|
||||
break;
|
||||
read = (size_t)std::min(len - offset, (uint64_t)buffer.size());
|
||||
|
||||
read = this->Read(buffer.data(), read);
|
||||
strm->WriteBlock(buffer.data(), read);
|
||||
|
||||
offset += read;
|
||||
|
||||
} while (read > 0 && !strm->EndOfStream());
|
||||
strm->Flush();
|
||||
}
|
||||
|
||||
void Stream::CopyTo(std::shared_ptr<Stream> strm, size_t buffSize) {
|
||||
size_t read;
|
||||
std::vector<uint8_t> buffer(buffSize);
|
||||
do {
|
||||
read = this->Read(buffer.data(), buffer.size());
|
||||
strm->WriteBlock(buffer.data(), read);
|
||||
|
||||
} while (read > 0 && !strm->EndOfStream());
|
||||
strm->Flush();
|
||||
}
|
||||
Stream::~Stream() {}
|
||||
} // namespace Tesses::Framework::Streams
|
||||
|
||||
1825
src/TF_Init.cpp
1825
src/TF_Init.cpp
File diff suppressed because it is too large
Load Diff
@@ -1,68 +1,83 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/Text/HeaderGenerator.hpp"
|
||||
namespace Tesses::Framework::Text {
|
||||
|
||||
void GenerateCHeaderFile(std::shared_ptr<Streams::Stream> strm,std::string name, std::shared_ptr<TextStreams::TextWriter> writer)
|
||||
{
|
||||
const size_t BLK_SZ=1024;
|
||||
writer->WriteLine("#pragma once");
|
||||
writer->WriteLine("#if defined(__cplusplus)");
|
||||
writer->WriteLine("extern \"C\" {");
|
||||
writer->WriteLine("#endif");
|
||||
writer->WriteLine("#include <stdint.h>");
|
||||
writer->WriteLine("#include <stddef.h>");
|
||||
writer->Write("const uint8_t ");
|
||||
writer->Write(name);
|
||||
writer->WriteLine("_data[] = {");
|
||||
uint64_t total = 0;
|
||||
size_t read;
|
||||
|
||||
std::vector<uint8_t> data(BLK_SZ);
|
||||
bool first=true;
|
||||
|
||||
void GenerateCHeaderFile(std::shared_ptr<Streams::Stream> strm,
|
||||
std::string name,
|
||||
std::shared_ptr<TextStreams::TextWriter> writer) {
|
||||
const size_t BLK_SZ = 1024;
|
||||
writer->WriteLine("#pragma once");
|
||||
writer->WriteLine("#if defined(__cplusplus)");
|
||||
writer->WriteLine("extern \"C\" {");
|
||||
writer->WriteLine("#endif");
|
||||
writer->WriteLine("#include <stdint.h>");
|
||||
writer->WriteLine("#include <stddef.h>");
|
||||
writer->Write("const uint8_t ");
|
||||
writer->Write(name);
|
||||
writer->WriteLine("_data[] = {");
|
||||
uint64_t total = 0;
|
||||
size_t read;
|
||||
|
||||
do {
|
||||
read = strm->ReadBlock(data.data(), data.size());
|
||||
std::vector<uint8_t> data(BLK_SZ);
|
||||
bool first = true;
|
||||
|
||||
for(size_t i = 0; i < read; i++)
|
||||
{
|
||||
if(!first) writer->Write(", ");
|
||||
writer->Write((uint64_t)data[i]);
|
||||
first=false;
|
||||
}
|
||||
total += read;
|
||||
} while(read != 0);
|
||||
|
||||
|
||||
do {
|
||||
read = strm->ReadBlock(data.data(), data.size());
|
||||
|
||||
writer->WriteLine("};");
|
||||
writer->Write("const size_t ");
|
||||
writer->Write(name);
|
||||
writer->Write("_length = ");
|
||||
writer->Write(total);
|
||||
writer->WriteLine(";");
|
||||
for (size_t i = 0; i < read; i++) {
|
||||
if (!first)
|
||||
writer->Write(", ");
|
||||
writer->Write((uint64_t)data[i]);
|
||||
first = false;
|
||||
}
|
||||
total += read;
|
||||
} while (read != 0);
|
||||
|
||||
writer->WriteLine("#if defined(__cplusplus)");
|
||||
writer->WriteLine("}");
|
||||
writer->WriteLine("#endif");
|
||||
writer->WriteLine("};");
|
||||
writer->Write("const size_t ");
|
||||
writer->Write(name);
|
||||
writer->Write("_length = ");
|
||||
writer->Write(total);
|
||||
writer->WriteLine(";");
|
||||
|
||||
}
|
||||
|
||||
std::string GenerateCHeaderFile(std::shared_ptr<Streams::Stream> strm,std::string name)
|
||||
{
|
||||
auto writer=std::make_shared<TextStreams::StringWriter>();
|
||||
GenerateCHeaderFile(strm,name,writer);
|
||||
return writer->GetString();
|
||||
}
|
||||
void GenerateCHeaderFile(const std::vector<uint8_t>& data,std::string name, std::shared_ptr<TextStreams::TextWriter> writer)
|
||||
{
|
||||
auto ms = std::make_shared<Tesses::Framework::Streams::MemoryStream>(false);
|
||||
ms->GetBuffer() = data;
|
||||
GenerateCHeaderFile(ms,name,writer);
|
||||
}
|
||||
std::string GenerateCHeaderFile(const std::vector<uint8_t>& data,std::string name)
|
||||
{
|
||||
auto writer = std::make_shared<TextStreams::StringWriter>();
|
||||
GenerateCHeaderFile(data,name,writer);
|
||||
return writer->GetString();
|
||||
}
|
||||
};
|
||||
writer->WriteLine("#if defined(__cplusplus)");
|
||||
writer->WriteLine("}");
|
||||
writer->WriteLine("#endif");
|
||||
}
|
||||
|
||||
std::string GenerateCHeaderFile(std::shared_ptr<Streams::Stream> strm,
|
||||
std::string name) {
|
||||
auto writer = std::make_shared<TextStreams::StringWriter>();
|
||||
GenerateCHeaderFile(strm, name, writer);
|
||||
return writer->GetString();
|
||||
}
|
||||
void GenerateCHeaderFile(const std::vector<uint8_t> &data, std::string name,
|
||||
std::shared_ptr<TextStreams::TextWriter> writer) {
|
||||
auto ms = std::make_shared<Tesses::Framework::Streams::MemoryStream>(false);
|
||||
ms->GetBuffer() = data;
|
||||
GenerateCHeaderFile(ms, name, writer);
|
||||
}
|
||||
std::string GenerateCHeaderFile(const std::vector<uint8_t> &data,
|
||||
std::string name) {
|
||||
auto writer = std::make_shared<TextStreams::StringWriter>();
|
||||
GenerateCHeaderFile(data, name, writer);
|
||||
return writer->GetString();
|
||||
}
|
||||
}; // namespace Tesses::Framework::Text
|
||||
@@ -1,270 +1,244 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/Text/StringConverter.hpp"
|
||||
|
||||
namespace Tesses::Framework::Text::StringConverter {
|
||||
void UTF8::FromUTF16(std::basic_string<char>& utf8, const std::basic_string<char16_t>& utf16)
|
||||
{
|
||||
for (size_t i=0; i < utf16.size();i++)
|
||||
{
|
||||
char32_t c = utf16[i];
|
||||
if ((c & 0xFC00) == 0xD800)
|
||||
{
|
||||
c = (c & 0x03FF) << 10;
|
||||
i++;
|
||||
if (i >= utf16.size()) return;
|
||||
void UTF8::FromUTF16(std::basic_string<char> &utf8,
|
||||
const std::basic_string<char16_t> &utf16) {
|
||||
for (size_t i = 0; i < utf16.size(); i++) {
|
||||
char32_t c = utf16[i];
|
||||
if ((c & 0xFC00) == 0xD800) {
|
||||
c = (c & 0x03FF) << 10;
|
||||
i++;
|
||||
if (i >= utf16.size())
|
||||
return;
|
||||
|
||||
char32_t c2 = utf16[i];
|
||||
if ((c2 & 0xFC00) != 0xDC00)
|
||||
continue;
|
||||
|
||||
char32_t c2 = utf16[i];
|
||||
if ((c2 & 0xFC00) != 0xDC00) continue;
|
||||
c |= (c2 & 0x03FF);
|
||||
|
||||
|
||||
c |= (c2 & 0x03FF);
|
||||
|
||||
c += 0x10000;
|
||||
}
|
||||
|
||||
if (c <= 0x7F)
|
||||
{
|
||||
utf8.push_back((char)c);
|
||||
}
|
||||
else if (c >= 0x80 && c <= 0x7FF)
|
||||
{
|
||||
uint8_t high = 0b11000000 | ((c >> 6) & 0b00011111);
|
||||
uint8_t low = 0b10000000 | (c & 0b00111111);
|
||||
utf8.push_back((char)high);
|
||||
utf8.push_back((char)low);
|
||||
}
|
||||
else if (c >= 0x800 && c <= 0xFFFF)
|
||||
{
|
||||
uint8_t highest = 0b11100000 | ((c >> 12) & 0b00001111);
|
||||
uint8_t high = 0b10000000 | ((c >> 6) & 0b00111111);
|
||||
uint8_t low = 0b10000000 | (c & 0b00111111);
|
||||
utf8.push_back((char)highest);
|
||||
utf8.push_back((char)high);
|
||||
utf8.push_back((char)low);
|
||||
}
|
||||
else if (c >= 0x010000 && c <= 0x10FFFF)
|
||||
{
|
||||
uint8_t highest = 0b11110000 | ((c >> 18) & 0b00000111);
|
||||
uint8_t high = 0b10000000 | ((c >> 12) & 0b00111111);
|
||||
uint8_t low = 0b10000000 | ((c >> 6) & 0b00111111);
|
||||
uint8_t lowest = 0b10000000 | (c & 0b00111111);
|
||||
utf8.push_back((char)highest);
|
||||
utf8.push_back((char)high);
|
||||
utf8.push_back((char)low);
|
||||
utf8.push_back((char)lowest);
|
||||
}
|
||||
|
||||
c += 0x10000;
|
||||
}
|
||||
}
|
||||
void UTF8::FromUTF32(std::basic_string<char>& utf8, const std::basic_string<char32_t>& utf32)
|
||||
{
|
||||
for (auto c : utf32)
|
||||
{
|
||||
if (c <= 0x7F)
|
||||
{
|
||||
utf8.push_back((char)c);
|
||||
}
|
||||
else if (c >= 0x80 && c <= 0x7FF)
|
||||
{
|
||||
uint8_t high = 0b11000000 | ((c >> 6) & 0b00011111);
|
||||
uint8_t low = 0b10000000 | (c & 0b00111111);
|
||||
utf8.push_back((char)high);
|
||||
utf8.push_back((char)low);
|
||||
}
|
||||
else if (c >= 0x800 && c <= 0xFFFF)
|
||||
{
|
||||
uint8_t highest = 0b11100000 | ((c >> 12) & 0b00001111);
|
||||
uint8_t high = 0b10000000 | ((c >> 6) & 0b00111111);
|
||||
uint8_t low = 0b10000000 | (c & 0b00111111);
|
||||
utf8.push_back((char)highest);
|
||||
utf8.push_back((char)high);
|
||||
utf8.push_back((char)low);
|
||||
}
|
||||
else if (c >= 0x010000 && c <= 0x10FFFF)
|
||||
{
|
||||
uint8_t highest = 0b11110000 | ((c >> 18) & 0b00000111);
|
||||
uint8_t high = 0b10000000 | ((c >> 12) & 0b00111111);
|
||||
uint8_t low = 0b10000000 | ((c >> 6) & 0b00111111);
|
||||
uint8_t lowest = 0b10000000 | (c & 0b00111111);
|
||||
utf8.push_back((char)highest);
|
||||
utf8.push_back((char)high);
|
||||
utf8.push_back((char)low);
|
||||
utf8.push_back((char)lowest);
|
||||
}
|
||||
|
||||
if (c <= 0x7F) {
|
||||
utf8.push_back((char)c);
|
||||
} else if (c >= 0x80 && c <= 0x7FF) {
|
||||
uint8_t high = 0b11000000 | ((c >> 6) & 0b00011111);
|
||||
uint8_t low = 0b10000000 | (c & 0b00111111);
|
||||
utf8.push_back((char)high);
|
||||
utf8.push_back((char)low);
|
||||
} else if (c >= 0x800 && c <= 0xFFFF) {
|
||||
uint8_t highest = 0b11100000 | ((c >> 12) & 0b00001111);
|
||||
uint8_t high = 0b10000000 | ((c >> 6) & 0b00111111);
|
||||
uint8_t low = 0b10000000 | (c & 0b00111111);
|
||||
utf8.push_back((char)highest);
|
||||
utf8.push_back((char)high);
|
||||
utf8.push_back((char)low);
|
||||
} else if (c >= 0x010000 && c <= 0x10FFFF) {
|
||||
uint8_t highest = 0b11110000 | ((c >> 18) & 0b00000111);
|
||||
uint8_t high = 0b10000000 | ((c >> 12) & 0b00111111);
|
||||
uint8_t low = 0b10000000 | ((c >> 6) & 0b00111111);
|
||||
uint8_t lowest = 0b10000000 | (c & 0b00111111);
|
||||
utf8.push_back((char)highest);
|
||||
utf8.push_back((char)high);
|
||||
utf8.push_back((char)low);
|
||||
utf8.push_back((char)lowest);
|
||||
}
|
||||
}
|
||||
|
||||
void UTF16::FromUTF8(std::basic_string<char16_t>& utf16, const std::basic_string<char>& utf8)
|
||||
{
|
||||
for (size_t i = 0; i < utf8.size();i++)
|
||||
{
|
||||
uint8_t c = (uint8_t)utf8[i];
|
||||
char32_t cres = 0;
|
||||
if (c <= 127)
|
||||
{
|
||||
cres = (char32_t)c;
|
||||
}
|
||||
else if ((c & 0b11100000) == 0b11000000)
|
||||
{
|
||||
if (i + 1 < utf8.size())
|
||||
{
|
||||
|
||||
uint8_t c2 = (uint8_t)utf8[++i];
|
||||
cres |= c2 & 0b00111111;
|
||||
cres |= (c & 0b00011111) << 6;
|
||||
|
||||
}
|
||||
else {
|
||||
i++;
|
||||
continue;
|
||||
};
|
||||
}
|
||||
else if ((c & 0b11110000) == 0b11100000)
|
||||
{
|
||||
if (i + 2 < utf8.size())
|
||||
{
|
||||
uint8_t c2 = (uint8_t)utf8[++i];
|
||||
uint8_t c3 = (uint8_t)utf8[++i];
|
||||
cres |= c3 & 0b00111111;
|
||||
cres |= (c2 & 0b00111111) << 6;
|
||||
cres |= (c & 0b00001111) << 12;
|
||||
|
||||
}
|
||||
else { i += 2; continue; }
|
||||
}
|
||||
else if ((c & 0b11111000) == 0b11110000)
|
||||
{
|
||||
if (i + 3 < utf8.size())
|
||||
{
|
||||
uint8_t c2 = (uint8_t)utf8[++i];
|
||||
uint8_t c3 = (uint8_t)utf8[++i];
|
||||
uint8_t c4 = (uint8_t)utf8[++i];
|
||||
|
||||
cres |= c4 & 0b00111111;
|
||||
cres |= (c3 & 0b00111111) << 6;
|
||||
cres |= (c2 & 0b00111111) << 12;
|
||||
cres |= (c & 0b00000111) << 18;
|
||||
|
||||
}
|
||||
else { i += 3; continue; }
|
||||
}
|
||||
if (cres >= 0x10000 && cres <= 0x10FFFF)
|
||||
{
|
||||
auto subtracted = cres - 0x10000;
|
||||
|
||||
auto high = (0x3FF & (subtracted >> 10)) | 0xD800;
|
||||
auto low = (0x3FF & subtracted) | 0xDC00;
|
||||
|
||||
utf16.push_back(high);
|
||||
utf16.push_back(low);
|
||||
}
|
||||
else {
|
||||
utf16.push_back((char16_t)cres);
|
||||
}
|
||||
|
||||
}
|
||||
void UTF8::FromUTF32(std::basic_string<char> &utf8,
|
||||
const std::basic_string<char32_t> &utf32) {
|
||||
for (auto c : utf32) {
|
||||
if (c <= 0x7F) {
|
||||
utf8.push_back((char)c);
|
||||
} else if (c >= 0x80 && c <= 0x7FF) {
|
||||
uint8_t high = 0b11000000 | ((c >> 6) & 0b00011111);
|
||||
uint8_t low = 0b10000000 | (c & 0b00111111);
|
||||
utf8.push_back((char)high);
|
||||
utf8.push_back((char)low);
|
||||
} else if (c >= 0x800 && c <= 0xFFFF) {
|
||||
uint8_t highest = 0b11100000 | ((c >> 12) & 0b00001111);
|
||||
uint8_t high = 0b10000000 | ((c >> 6) & 0b00111111);
|
||||
uint8_t low = 0b10000000 | (c & 0b00111111);
|
||||
utf8.push_back((char)highest);
|
||||
utf8.push_back((char)high);
|
||||
utf8.push_back((char)low);
|
||||
} else if (c >= 0x010000 && c <= 0x10FFFF) {
|
||||
uint8_t highest = 0b11110000 | ((c >> 18) & 0b00000111);
|
||||
uint8_t high = 0b10000000 | ((c >> 12) & 0b00111111);
|
||||
uint8_t low = 0b10000000 | ((c >> 6) & 0b00111111);
|
||||
uint8_t lowest = 0b10000000 | (c & 0b00111111);
|
||||
utf8.push_back((char)highest);
|
||||
utf8.push_back((char)high);
|
||||
utf8.push_back((char)low);
|
||||
utf8.push_back((char)lowest);
|
||||
}
|
||||
}
|
||||
void UTF16::FromUTF32(std::basic_string<char16_t>& utf16, const std::basic_string<char32_t>& utf32)
|
||||
{
|
||||
for (auto cres : utf32)
|
||||
{
|
||||
if (cres >= 0x10000 && cres <= 0x10FFFF)
|
||||
{
|
||||
auto subtracted = cres - 0x10000;
|
||||
}
|
||||
|
||||
auto high = (0x3FF & (subtracted >> 10)) | 0xD800;
|
||||
auto low = (0x3FF & subtracted) | 0xDC00;
|
||||
void UTF16::FromUTF8(std::basic_string<char16_t> &utf16,
|
||||
const std::basic_string<char> &utf8) {
|
||||
for (size_t i = 0; i < utf8.size(); i++) {
|
||||
uint8_t c = (uint8_t)utf8[i];
|
||||
char32_t cres = 0;
|
||||
if (c <= 127) {
|
||||
cres = (char32_t)c;
|
||||
} else if ((c & 0b11100000) == 0b11000000) {
|
||||
if (i + 1 < utf8.size()) {
|
||||
|
||||
utf16.push_back(high);
|
||||
utf16.push_back(low);
|
||||
}
|
||||
else {
|
||||
utf16.push_back((char16_t)cres);
|
||||
}
|
||||
}
|
||||
}
|
||||
uint8_t c2 = (uint8_t)utf8[++i];
|
||||
cres |= c2 & 0b00111111;
|
||||
cres |= (c & 0b00011111) << 6;
|
||||
|
||||
void UTF32::FromUTF8(std::basic_string<char32_t>& utf32, const std::basic_string<char>& utf8)
|
||||
{
|
||||
for (size_t i = 0; i < utf8.size();i++)
|
||||
{
|
||||
uint8_t c = (uint8_t)utf8[i];
|
||||
char32_t cres = 0;
|
||||
if (c <= 127)
|
||||
{
|
||||
cres=(char32_t)c;
|
||||
}
|
||||
else if ((c & 0b11100000) == 0b11000000)
|
||||
{
|
||||
if (i + 1 < utf8.size())
|
||||
{
|
||||
|
||||
uint8_t c2 = (uint8_t)utf8[++i];
|
||||
cres |= c2 & 0b00111111;
|
||||
cres |= (c & 0b00011111) << 6;
|
||||
|
||||
}
|
||||
else {
|
||||
i++;
|
||||
continue;
|
||||
};
|
||||
}
|
||||
else if ((c & 0b11110000) == 0b11100000)
|
||||
{
|
||||
if (i + 2 < utf8.size())
|
||||
{
|
||||
uint8_t c2 = (uint8_t)utf8[++i];
|
||||
uint8_t c3 = (uint8_t)utf8[++i];
|
||||
cres |= c3 & 0b00111111;
|
||||
cres |= (c2 & 0b00111111) << 6;
|
||||
cres |= (c & 0b00001111) << 12;
|
||||
|
||||
}
|
||||
else { i += 2; continue; }
|
||||
}
|
||||
else if ((c & 0b11111000) == 0b11110000)
|
||||
{
|
||||
if (i + 3 < utf8.size())
|
||||
{
|
||||
uint8_t c2 = (uint8_t)utf8[++i];
|
||||
uint8_t c3 = (uint8_t)utf8[++i];
|
||||
uint8_t c4 = (uint8_t)utf8[++i];
|
||||
|
||||
cres |= c4 & 0b00111111;
|
||||
cres |= (c3 & 0b00111111) << 6;
|
||||
cres |= (c2 & 0b00111111) << 12;
|
||||
cres |= (c & 0b00000111) << 18;
|
||||
|
||||
}
|
||||
else { i += 3; continue; }
|
||||
}
|
||||
utf32.push_back(cres);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void UTF32::FromUTF16(std::basic_string<char32_t>& utf32, const std::basic_string<char16_t>& utf16)
|
||||
{
|
||||
for (size_t i = 0; i < utf16.size();i++)
|
||||
{
|
||||
char32_t c = utf16[i];
|
||||
if ((c & 0xFC00) == 0xD800)
|
||||
{
|
||||
c = (c & 0x03FF) << 10;
|
||||
} else {
|
||||
i++;
|
||||
if (i >= utf16.size()) return;
|
||||
continue;
|
||||
};
|
||||
} else if ((c & 0b11110000) == 0b11100000) {
|
||||
if (i + 2 < utf8.size()) {
|
||||
uint8_t c2 = (uint8_t)utf8[++i];
|
||||
uint8_t c3 = (uint8_t)utf8[++i];
|
||||
cres |= c3 & 0b00111111;
|
||||
cres |= (c2 & 0b00111111) << 6;
|
||||
cres |= (c & 0b00001111) << 12;
|
||||
|
||||
|
||||
char32_t c2 = utf16[i];
|
||||
if ((c2 & 0xFC00) != 0xDC00) continue;
|
||||
|
||||
|
||||
c |= (c2 & 0x03FF);
|
||||
|
||||
c += 0x10000;
|
||||
} else {
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
utf32.push_back(c);
|
||||
} else if ((c & 0b11111000) == 0b11110000) {
|
||||
if (i + 3 < utf8.size()) {
|
||||
uint8_t c2 = (uint8_t)utf8[++i];
|
||||
uint8_t c3 = (uint8_t)utf8[++i];
|
||||
uint8_t c4 = (uint8_t)utf8[++i];
|
||||
|
||||
cres |= c4 & 0b00111111;
|
||||
cres |= (c3 & 0b00111111) << 6;
|
||||
cres |= (c2 & 0b00111111) << 12;
|
||||
cres |= (c & 0b00000111) << 18;
|
||||
|
||||
} else {
|
||||
i += 3;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (cres >= 0x10000 && cres <= 0x10FFFF) {
|
||||
auto subtracted = cres - 0x10000;
|
||||
|
||||
auto high = (0x3FF & (subtracted >> 10)) | 0xD800;
|
||||
auto low = (0x3FF & subtracted) | 0xDC00;
|
||||
|
||||
utf16.push_back(high);
|
||||
utf16.push_back(low);
|
||||
} else {
|
||||
utf16.push_back((char16_t)cres);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void UTF16::FromUTF32(std::basic_string<char16_t> &utf16,
|
||||
const std::basic_string<char32_t> &utf32) {
|
||||
for (auto cres : utf32) {
|
||||
if (cres >= 0x10000 && cres <= 0x10FFFF) {
|
||||
auto subtracted = cres - 0x10000;
|
||||
|
||||
auto high = (0x3FF & (subtracted >> 10)) | 0xD800;
|
||||
auto low = (0x3FF & subtracted) | 0xDC00;
|
||||
|
||||
utf16.push_back(high);
|
||||
utf16.push_back(low);
|
||||
} else {
|
||||
utf16.push_back((char16_t)cres);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UTF32::FromUTF8(std::basic_string<char32_t> &utf32,
|
||||
const std::basic_string<char> &utf8) {
|
||||
for (size_t i = 0; i < utf8.size(); i++) {
|
||||
uint8_t c = (uint8_t)utf8[i];
|
||||
char32_t cres = 0;
|
||||
if (c <= 127) {
|
||||
cres = (char32_t)c;
|
||||
} else if ((c & 0b11100000) == 0b11000000) {
|
||||
if (i + 1 < utf8.size()) {
|
||||
|
||||
uint8_t c2 = (uint8_t)utf8[++i];
|
||||
cres |= c2 & 0b00111111;
|
||||
cres |= (c & 0b00011111) << 6;
|
||||
|
||||
} else {
|
||||
i++;
|
||||
continue;
|
||||
};
|
||||
} else if ((c & 0b11110000) == 0b11100000) {
|
||||
if (i + 2 < utf8.size()) {
|
||||
uint8_t c2 = (uint8_t)utf8[++i];
|
||||
uint8_t c3 = (uint8_t)utf8[++i];
|
||||
cres |= c3 & 0b00111111;
|
||||
cres |= (c2 & 0b00111111) << 6;
|
||||
cres |= (c & 0b00001111) << 12;
|
||||
|
||||
} else {
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
} else if ((c & 0b11111000) == 0b11110000) {
|
||||
if (i + 3 < utf8.size()) {
|
||||
uint8_t c2 = (uint8_t)utf8[++i];
|
||||
uint8_t c3 = (uint8_t)utf8[++i];
|
||||
uint8_t c4 = (uint8_t)utf8[++i];
|
||||
|
||||
cres |= c4 & 0b00111111;
|
||||
cres |= (c3 & 0b00111111) << 6;
|
||||
cres |= (c2 & 0b00111111) << 12;
|
||||
cres |= (c & 0b00000111) << 18;
|
||||
|
||||
} else {
|
||||
i += 3;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
utf32.push_back(cres);
|
||||
}
|
||||
}
|
||||
|
||||
void UTF32::FromUTF16(std::basic_string<char32_t> &utf32,
|
||||
const std::basic_string<char16_t> &utf16) {
|
||||
for (size_t i = 0; i < utf16.size(); i++) {
|
||||
char32_t c = utf16[i];
|
||||
if ((c & 0xFC00) == 0xD800) {
|
||||
c = (c & 0x03FF) << 10;
|
||||
i++;
|
||||
if (i >= utf16.size())
|
||||
return;
|
||||
|
||||
char32_t c2 = utf16[i];
|
||||
if ((c2 & 0xFC00) != 0xDC00)
|
||||
continue;
|
||||
|
||||
c |= (c2 & 0x03FF);
|
||||
|
||||
c += 0x10000;
|
||||
}
|
||||
utf32.push_back(c);
|
||||
}
|
||||
}
|
||||
} // namespace Tesses::Framework::Text::StringConverter
|
||||
@@ -1,31 +1,43 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "TessesFramework/TextStreams/StdIOReader.hpp"
|
||||
#include "TessesFramework/Console.hpp"
|
||||
|
||||
namespace Tesses::Framework::TextStreams {
|
||||
ConsoleReader::ConsoleReader() {}
|
||||
bool ConsoleReader::ReadBlock(std::string &str, size_t len) {
|
||||
size_t i = 0;
|
||||
|
||||
namespace Tesses::Framework::TextStreams
|
||||
{
|
||||
ConsoleReader::ConsoleReader()
|
||||
{
|
||||
for (; i < len;) {
|
||||
int rd = Console::Read();
|
||||
if (rd == -1)
|
||||
break;
|
||||
|
||||
}
|
||||
bool ConsoleReader::ReadBlock(std::string& str,size_t len)
|
||||
{
|
||||
std::vector<uint8_t> buff(len);
|
||||
|
||||
size_t read=0;
|
||||
size_t readTotal=0;
|
||||
uint8_t* buffOff=buff.data();
|
||||
do {
|
||||
read=fread(buffOff,1,len,stdin);
|
||||
if(read != 0) {readTotal+= read;len-=read; buffOff+=read;}
|
||||
} while(read != 0);
|
||||
if(readTotal == 0) return false;
|
||||
str.append((const char*)buff.data(), readTotal);
|
||||
|
||||
return true;
|
||||
str.push_back((char)rd);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
ConsoleReader StdIn()
|
||||
{
|
||||
return ConsoleReader();
|
||||
}
|
||||
}
|
||||
if (i == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ConsoleReader StdIn() { return ConsoleReader(); }
|
||||
} // namespace Tesses::Framework::TextStreams
|
||||
@@ -1,42 +1,35 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/TextStreams/StdIOWriter.hpp"
|
||||
#if defined(__PS2__)
|
||||
#include <debug.h>
|
||||
#else
|
||||
#include <cstdio>
|
||||
#endif
|
||||
namespace Tesses::Framework::TextStreams
|
||||
{
|
||||
ConsoleWriter::ConsoleWriter(bool isError) : TextWriter()
|
||||
{
|
||||
this->isError=isError;
|
||||
}
|
||||
#include "TessesFramework/Console.hpp"
|
||||
namespace Tesses::Framework::TextStreams {
|
||||
ConsoleWriter::ConsoleWriter(bool isError) : TextWriter() {
|
||||
this->isError = isError;
|
||||
}
|
||||
|
||||
void ConsoleWriter::WriteData(const char* text, size_t len)
|
||||
{
|
||||
#if defined(__PS2__)
|
||||
char lenThing[10];//%.2048s
|
||||
while(len > 0) {
|
||||
int b = std::min((int)2047,(int)len);
|
||||
snprintf(lenThing,18,"%%.%is",b);
|
||||
|
||||
scr_printf(lenThing,text);
|
||||
|
||||
len -= b;
|
||||
text += b;
|
||||
}
|
||||
#else
|
||||
if(isError)
|
||||
fwrite(text,1,len,stderr);
|
||||
else
|
||||
fwrite(text,1,len,stdout);
|
||||
#endif
|
||||
void ConsoleWriter::WriteData(const char *text, size_t len) {
|
||||
if (isError) {
|
||||
Console::ErrorView(std::string_view(text, len));
|
||||
} else {
|
||||
Console::WriteView(std::string_view(text, len));
|
||||
}
|
||||
ConsoleWriter StdOut()
|
||||
{
|
||||
return ConsoleWriter(false);
|
||||
}
|
||||
ConsoleWriter StdErr()
|
||||
{
|
||||
return ConsoleWriter(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
ConsoleWriter StdOut() { return ConsoleWriter(false); }
|
||||
ConsoleWriter StdErr() { return ConsoleWriter(true); }
|
||||
} // namespace Tesses::Framework::TextStreams
|
||||
@@ -1,46 +1,54 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/TextStreams/StreamReader.hpp"
|
||||
#include "TessesFramework/Streams/FileStream.hpp"
|
||||
using Stream = Tesses::Framework::Streams::Stream;
|
||||
using FileStream = Tesses::Framework::Streams::FileStream;
|
||||
|
||||
namespace Tesses::Framework::TextStreams {
|
||||
|
||||
StreamReader::StreamReader(std::filesystem::path path) : StreamReader(std::make_shared<FileStream>(path,"rb"))
|
||||
{
|
||||
|
||||
}
|
||||
bool StreamReader::Rewind()
|
||||
{
|
||||
if(this->strm->CanSeek())
|
||||
{
|
||||
this->strm->Seek((int64_t)0,Tesses::Framework::Streams::SeekOrigin::Begin);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
StreamReader::StreamReader(std::shared_ptr<Stream> strm) : TextReader()
|
||||
{
|
||||
this->strm = strm;
|
||||
}
|
||||
|
||||
std::shared_ptr<Stream> StreamReader::GetStream()
|
||||
{
|
||||
return (this->strm);
|
||||
}
|
||||
|
||||
bool StreamReader::ReadBlock(std::string& str, size_t len)
|
||||
{
|
||||
std::vector<uint8_t> buff(len);
|
||||
|
||||
len = strm->ReadBlock(buff.data(),len);
|
||||
if(len == 0) { return false;}
|
||||
str.append((const char*)buff.data(), len);
|
||||
|
||||
|
||||
StreamReader::StreamReader(std::filesystem::path path)
|
||||
: StreamReader(std::make_shared<FileStream>(path, "rb")) {}
|
||||
bool StreamReader::Rewind() {
|
||||
if (this->strm->CanSeek()) {
|
||||
this->strm->Seek((int64_t)0,
|
||||
Tesses::Framework::Streams::SeekOrigin::Begin);
|
||||
return true;
|
||||
}
|
||||
StreamReader::~StreamReader()
|
||||
{
|
||||
|
||||
return false;
|
||||
}
|
||||
StreamReader::StreamReader(std::shared_ptr<Stream> strm) : TextReader() {
|
||||
this->strm = strm;
|
||||
}
|
||||
|
||||
std::shared_ptr<Stream> StreamReader::GetStream() { return (this->strm); }
|
||||
|
||||
bool StreamReader::ReadBlock(std::string &str, size_t len) {
|
||||
std::vector<uint8_t> buff(len);
|
||||
|
||||
len = strm->ReadBlock(buff.data(), len);
|
||||
if (len == 0) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
str.append((const char *)buff.data(), len);
|
||||
|
||||
return true;
|
||||
}
|
||||
StreamReader::~StreamReader() {}
|
||||
}; // namespace Tesses::Framework::TextStreams
|
||||
@@ -1,28 +1,37 @@
|
||||
#include "TessesFramework/Streams/FileStream.hpp"
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/TextStreams/StreamWriter.hpp"
|
||||
#include "TessesFramework/Streams/FileStream.hpp"
|
||||
using Stream = Tesses::Framework::Streams::Stream;
|
||||
using FileStream = Tesses::Framework::Streams::FileStream;
|
||||
|
||||
namespace Tesses::Framework::TextStreams
|
||||
{
|
||||
std::shared_ptr<Stream> StreamWriter::GetStream()
|
||||
{
|
||||
return this->strm;
|
||||
}
|
||||
StreamWriter::StreamWriter(std::shared_ptr<Stream> strm) : TextWriter()
|
||||
{
|
||||
namespace Tesses::Framework::TextStreams {
|
||||
std::shared_ptr<Stream> StreamWriter::GetStream() { return this->strm; }
|
||||
StreamWriter::StreamWriter(std::shared_ptr<Stream> strm) : TextWriter() {
|
||||
|
||||
this->strm = strm;
|
||||
}
|
||||
StreamWriter::StreamWriter(std::filesystem::path filename, bool append) : StreamWriter(std::make_shared<FileStream>(filename, append ? "ab" : "wb"))
|
||||
{
|
||||
|
||||
}
|
||||
void StreamWriter::WriteData(const char* text, size_t len)
|
||||
{
|
||||
this->strm->WriteBlock((const uint8_t*)text, len);
|
||||
}
|
||||
StreamWriter::~StreamWriter()
|
||||
{
|
||||
}
|
||||
}
|
||||
this->strm = strm;
|
||||
}
|
||||
StreamWriter::StreamWriter(std::filesystem::path filename, bool append)
|
||||
: StreamWriter(
|
||||
std::make_shared<FileStream>(filename, append ? "ab" : "wb")) {}
|
||||
void StreamWriter::WriteData(const char *text, size_t len) {
|
||||
this->strm->WriteBlock((const uint8_t *)text, len);
|
||||
}
|
||||
StreamWriter::~StreamWriter() {}
|
||||
} // namespace Tesses::Framework::TextStreams
|
||||
@@ -1,38 +1,45 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/TextStreams/StringReader.hpp"
|
||||
|
||||
namespace Tesses::Framework::TextStreams {
|
||||
StringReader::StringReader()
|
||||
{
|
||||
this->offset=0;
|
||||
this->str="";
|
||||
}
|
||||
StringReader::StringReader(std::string str)
|
||||
{
|
||||
this->offset=0;
|
||||
this->str=str;
|
||||
}
|
||||
size_t& StringReader::GetOffset()
|
||||
{
|
||||
return this->offset;
|
||||
}
|
||||
std::string& StringReader::GetString()
|
||||
{
|
||||
return this->str;
|
||||
}
|
||||
bool StringReader::Rewind()
|
||||
{
|
||||
this->offset=0;
|
||||
StringReader::StringReader() {
|
||||
this->offset = 0;
|
||||
this->str = "";
|
||||
}
|
||||
StringReader::StringReader(std::string str) {
|
||||
this->offset = 0;
|
||||
this->str = str;
|
||||
}
|
||||
size_t &StringReader::GetOffset() { return this->offset; }
|
||||
std::string &StringReader::GetString() { return this->str; }
|
||||
bool StringReader::Rewind() {
|
||||
this->offset = 0;
|
||||
return true;
|
||||
}
|
||||
bool StringReader::ReadBlock(std::string &str, size_t sz) {
|
||||
if (this->offset < this->str.size()) {
|
||||
size_t len = std::min(sz, this->str.size() - this->offset);
|
||||
str.insert(str.size(), this->str.data() + this->offset, len);
|
||||
offset += len;
|
||||
return true;
|
||||
}
|
||||
bool StringReader::ReadBlock(std::string& str,size_t sz)
|
||||
{
|
||||
if(this->offset < this->str.size())
|
||||
{
|
||||
size_t len = std::min(sz,this->str.size()-this->offset);
|
||||
str.insert(str.size(),this->str.data()+this->offset,len);
|
||||
offset+=len;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} // namespace Tesses::Framework::TextStreams
|
||||
@@ -1,21 +1,28 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/TextStreams/StringWriter.hpp"
|
||||
|
||||
namespace Tesses::Framework::TextStreams
|
||||
{
|
||||
std::string& StringWriter::GetString()
|
||||
{
|
||||
return this->text;
|
||||
}
|
||||
StringWriter::StringWriter() : TextWriter()
|
||||
{
|
||||
|
||||
}
|
||||
StringWriter::StringWriter(std::string str) : TextWriter()
|
||||
{
|
||||
this->text = str;
|
||||
}
|
||||
void StringWriter::WriteData(const char* text, size_t len)
|
||||
{
|
||||
this->text.append(text,len);
|
||||
}
|
||||
}
|
||||
namespace Tesses::Framework::TextStreams {
|
||||
std::string &StringWriter::GetString() { return this->text; }
|
||||
StringWriter::StringWriter() : TextWriter() {}
|
||||
StringWriter::StringWriter(std::string str) : TextWriter() { this->text = str; }
|
||||
void StringWriter::WriteData(const char *text, size_t len) {
|
||||
this->text.append(text, len);
|
||||
}
|
||||
} // namespace Tesses::Framework::TextStreams
|
||||
@@ -1,101 +1,128 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/TextStreams/TextReader.hpp"
|
||||
|
||||
namespace Tesses::Framework::TextStreams
|
||||
{
|
||||
bool TextReader::Rewind()
|
||||
{
|
||||
namespace Tesses::Framework::TextStreams {
|
||||
bool TextReader::Rewind() { return false; }
|
||||
int32_t TextReader::ReadChar() {
|
||||
std::string txt;
|
||||
this->ReadBlock(txt, 1);
|
||||
if (txt.empty()) {
|
||||
eof = true;
|
||||
return -1;
|
||||
}
|
||||
return (uint8_t)txt[0];
|
||||
}
|
||||
std::string TextReader::ReadLine() {
|
||||
std::string str = {};
|
||||
ReadLine(str);
|
||||
return str;
|
||||
}
|
||||
bool TextReader::ReadLineHttp(std::string &str) {
|
||||
if (eof)
|
||||
return false;
|
||||
}
|
||||
int32_t TextReader::ReadChar()
|
||||
{
|
||||
std::string txt;
|
||||
this->ReadBlock(txt,1);
|
||||
if(txt.empty()) { eof=true; return -1;}
|
||||
return (uint8_t)txt[0];
|
||||
}
|
||||
std::string TextReader::ReadLine()
|
||||
{
|
||||
std::string str = {};
|
||||
ReadLine(str);
|
||||
return str;
|
||||
}
|
||||
bool TextReader::ReadLineHttp(std::string& str)
|
||||
{
|
||||
if(eof) return false;
|
||||
bool ret = false;
|
||||
int32_t r = -1;
|
||||
do {
|
||||
r = ReadChar();
|
||||
if(r == -1) {break;}
|
||||
if(r == '\r') continue;
|
||||
if(r == '\n') break;
|
||||
str.push_back((char)(uint8_t)r);
|
||||
ret = true;
|
||||
} while(r != -1);
|
||||
return ret;
|
||||
}
|
||||
bool TextReader::ReadLine(std::string& str)
|
||||
{
|
||||
|
||||
if(eof) return false;
|
||||
bool ret = false;
|
||||
int32_t r = -1;
|
||||
do {
|
||||
r = ReadChar();
|
||||
if(r == -1) break;
|
||||
if(r == '\r') continue;
|
||||
if(r == '\n') return true;
|
||||
str.push_back((char)(uint8_t)r);
|
||||
ret = true;
|
||||
} while(r != -1);
|
||||
return ret;
|
||||
}
|
||||
void TextReader::ReadAllLines(std::vector<std::string>& lines)
|
||||
{
|
||||
if(eof) return;
|
||||
int32_t r = -1;
|
||||
std::string builder;
|
||||
do {
|
||||
r = ReadChar();
|
||||
if(r == -1) break;
|
||||
if(r == '\r') continue;
|
||||
if(r == '\n') {
|
||||
lines.push_back(builder);
|
||||
builder.clear();
|
||||
continue;
|
||||
}
|
||||
builder += (char)r;
|
||||
|
||||
} while(r != -1);
|
||||
}
|
||||
|
||||
std::string TextReader::ReadToEnd()
|
||||
{
|
||||
std::string str = {};
|
||||
ReadToEnd(str);
|
||||
return str;
|
||||
}
|
||||
|
||||
void TextReader::ReadToEnd(std::string& str)
|
||||
{
|
||||
|
||||
if(eof) return;
|
||||
while(ReadBlock(str,1024));
|
||||
}
|
||||
void TextReader::CopyTo(TextWriter& writer, size_t buffSz)
|
||||
{
|
||||
|
||||
if(eof) return;
|
||||
std::string str = {};
|
||||
while(ReadBlock(str,buffSz))
|
||||
{
|
||||
writer.Write(str);
|
||||
str.clear();
|
||||
bool ret = false;
|
||||
int32_t r = -1;
|
||||
do {
|
||||
r = ReadChar();
|
||||
if (r == -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (r == '\r')
|
||||
continue;
|
||||
if (r == '\n')
|
||||
break;
|
||||
str.push_back((char)(uint8_t)r);
|
||||
ret = true;
|
||||
} while (r != -1);
|
||||
return ret;
|
||||
}
|
||||
bool TextReader::ReadLine(std::string &str) {
|
||||
|
||||
TextReader::~TextReader()
|
||||
{
|
||||
if (eof)
|
||||
return false;
|
||||
bool ret = false;
|
||||
int32_t r = -1;
|
||||
do {
|
||||
r = ReadChar();
|
||||
if (r == -1)
|
||||
break;
|
||||
if (r == '\n')
|
||||
{
|
||||
if(!str.empty() && str.back() == '\r') str.resize(str.size()-1);
|
||||
return true;
|
||||
}
|
||||
str.push_back((char)(uint8_t)r);
|
||||
ret = true;
|
||||
} while (r != -1);
|
||||
|
||||
if(!str.empty() && str.back() == '\r') str.resize(str.size()-1);
|
||||
return ret;
|
||||
}
|
||||
void TextReader::ReadAllLines(std::vector<std::string> &lines) {
|
||||
if (eof)
|
||||
return;
|
||||
int32_t r = -1;
|
||||
std::string builder;
|
||||
do {
|
||||
r = ReadChar();
|
||||
if (r == -1)
|
||||
break;
|
||||
|
||||
if (r == '\n') {
|
||||
|
||||
if(!builder.empty() && builder.back() == '\r')
|
||||
builder.resize(builder.size()-1);
|
||||
|
||||
|
||||
lines.push_back(builder);
|
||||
builder.clear();
|
||||
continue;
|
||||
}
|
||||
builder += (char)r;
|
||||
|
||||
} while (r != -1);
|
||||
}
|
||||
|
||||
std::string TextReader::ReadToEnd() {
|
||||
std::string str = {};
|
||||
ReadToEnd(str);
|
||||
return str;
|
||||
}
|
||||
|
||||
void TextReader::ReadToEnd(std::string &str) {
|
||||
|
||||
if (eof)
|
||||
return;
|
||||
while (ReadBlock(str, 1024))
|
||||
;
|
||||
}
|
||||
void TextReader::CopyTo(TextWriter &writer, size_t buffSz) {
|
||||
|
||||
if (eof)
|
||||
return;
|
||||
std::string str = {};
|
||||
while (ReadBlock(str, buffSz)) {
|
||||
writer.Write(str);
|
||||
str.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextReader::~TextReader() {}
|
||||
} // namespace Tesses::Framework::TextStreams
|
||||
@@ -1,115 +1,102 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/TextStreams/TextWriter.hpp"
|
||||
#include "TessesFramework/Http/HttpUtils.hpp"
|
||||
|
||||
namespace Tesses::Framework::TextStreams
|
||||
{
|
||||
void TextWriter::Write(int64_t n)
|
||||
{
|
||||
std::string text = std::to_string(n);
|
||||
WriteData(text.c_str(),text.size());
|
||||
}
|
||||
void TextWriter::Write(uint64_t n)
|
||||
{
|
||||
std::string text = std::to_string(n);
|
||||
WriteData(text.c_str(),text.size());
|
||||
}
|
||||
|
||||
void TextWriter::Write(const void* ptr)
|
||||
{
|
||||
std::string text = "0x";
|
||||
uintptr_t ptr2 = (uintptr_t)ptr;
|
||||
namespace Tesses::Framework::TextStreams {
|
||||
void TextWriter::Write(int64_t n) {
|
||||
std::string text = std::to_string(n);
|
||||
WriteData(text.c_str(), text.size());
|
||||
}
|
||||
void TextWriter::Write(uint64_t n) {
|
||||
std::string text = std::to_string(n);
|
||||
WriteData(text.c_str(), text.size());
|
||||
}
|
||||
|
||||
for(size_t i = 1; i <= sizeof(ptr); i++)
|
||||
{
|
||||
uint8_t v = (uint8_t)(ptr2 >> (int)((sizeof(ptr) - i) * 8));
|
||||
text.push_back(Tesses::Framework::Http::HttpUtils::NibbleToHex(v >> 4));
|
||||
text.push_back(Tesses::Framework::Http::HttpUtils::NibbleToHex(v));
|
||||
}
|
||||
WriteData(text.c_str(),text.size());
|
||||
}
|
||||
void TextWriter::Write(const char* ptr)
|
||||
{
|
||||
WriteData(ptr,strlen(ptr));
|
||||
}
|
||||
void TextWriter::Write(char c)
|
||||
{
|
||||
WriteData(&c,1);
|
||||
}
|
||||
void TextWriter::Write(double d)
|
||||
{
|
||||
std::string text = std::to_string(d);
|
||||
WriteData(text.c_str(),text.size());
|
||||
}
|
||||
|
||||
|
||||
void TextWriter::WriteLine(int64_t n)
|
||||
{
|
||||
std::string text = std::to_string(n);
|
||||
text.append(newline);
|
||||
WriteData(text.c_str(),text.size());
|
||||
}
|
||||
void TextWriter::WriteLine(uint64_t n)
|
||||
{
|
||||
std::string text = std::to_string(n);
|
||||
text.append(newline);
|
||||
WriteData(text.c_str(),text.size());
|
||||
}
|
||||
void TextWriter::WriteLine(const void* ptr)
|
||||
{
|
||||
std::string text = "0x";
|
||||
uintptr_t ptr2 = (uintptr_t)ptr;
|
||||
void TextWriter::Write(const void *ptr) {
|
||||
std::string text = "0x";
|
||||
uintptr_t ptr2 = (uintptr_t)ptr;
|
||||
|
||||
for(size_t i = 1; i <= sizeof(ptr); i++)
|
||||
{
|
||||
uint8_t v = (uint8_t)(ptr2 >> (int)((sizeof(ptr) - i) * 8));
|
||||
text.push_back(Tesses::Framework::Http::HttpUtils::NibbleToHex(v >> 4));
|
||||
text.push_back(Tesses::Framework::Http::HttpUtils::NibbleToHex(v));
|
||||
}
|
||||
text.append(newline);
|
||||
WriteData(text.c_str(),text.size());
|
||||
for (size_t i = 1; i <= sizeof(ptr); i++) {
|
||||
uint8_t v = (uint8_t)(ptr2 >> (int)((sizeof(ptr) - i) * 8));
|
||||
text.push_back(Tesses::Framework::Http::HttpUtils::NibbleToHex(v >> 4));
|
||||
text.push_back(Tesses::Framework::Http::HttpUtils::NibbleToHex(v));
|
||||
}
|
||||
void TextWriter::WriteLine(const char* ptr)
|
||||
{
|
||||
std::string text = ptr;
|
||||
text.append(newline);
|
||||
WriteData(text.c_str(),text.size());
|
||||
}
|
||||
void TextWriter::WriteLine(char c)
|
||||
{
|
||||
std::string text = {c};
|
||||
text.append(newline);
|
||||
WriteData(text.c_str(),text.size());
|
||||
}
|
||||
void TextWriter::WriteLine(double d)
|
||||
{
|
||||
std::string text = std::to_string(d);
|
||||
text.append(newline);
|
||||
WriteData(text.c_str(),text.size());
|
||||
}
|
||||
TextWriter::TextWriter()
|
||||
{
|
||||
#if defined(WIN32) || defined(_WIN32)
|
||||
newline = "\r\n";
|
||||
#else
|
||||
newline = "\n";
|
||||
#endif
|
||||
}
|
||||
void TextWriter::Write(std::string txt)
|
||||
{
|
||||
WriteData(txt.c_str(),txt.size());
|
||||
}
|
||||
void TextWriter::WriteLine(std::string txt)
|
||||
{
|
||||
std::string str = txt;
|
||||
str.append(newline);
|
||||
Write(str);
|
||||
}
|
||||
void TextWriter::WriteLine()
|
||||
{
|
||||
Write(newline);
|
||||
}
|
||||
TextWriter::~TextWriter()
|
||||
{
|
||||
WriteData(text.c_str(), text.size());
|
||||
}
|
||||
void TextWriter::Write(const char *ptr) { WriteData(ptr, strlen(ptr)); }
|
||||
void TextWriter::Write(char c) { WriteData(&c, 1); }
|
||||
void TextWriter::Write(double d) {
|
||||
std::string text = std::to_string(d);
|
||||
WriteData(text.c_str(), text.size());
|
||||
}
|
||||
|
||||
void TextWriter::WriteLine(int64_t n) {
|
||||
std::string text = std::to_string(n);
|
||||
text.append(newline);
|
||||
WriteData(text.c_str(), text.size());
|
||||
}
|
||||
void TextWriter::WriteLine(uint64_t n) {
|
||||
std::string text = std::to_string(n);
|
||||
text.append(newline);
|
||||
WriteData(text.c_str(), text.size());
|
||||
}
|
||||
void TextWriter::WriteLine(const void *ptr) {
|
||||
std::string text = "0x";
|
||||
uintptr_t ptr2 = (uintptr_t)ptr;
|
||||
|
||||
for (size_t i = 1; i <= sizeof(ptr); i++) {
|
||||
uint8_t v = (uint8_t)(ptr2 >> (int)((sizeof(ptr) - i) * 8));
|
||||
text.push_back(Tesses::Framework::Http::HttpUtils::NibbleToHex(v >> 4));
|
||||
text.push_back(Tesses::Framework::Http::HttpUtils::NibbleToHex(v));
|
||||
}
|
||||
}
|
||||
text.append(newline);
|
||||
WriteData(text.c_str(), text.size());
|
||||
}
|
||||
void TextWriter::WriteLine(const char *ptr) {
|
||||
std::string text = ptr;
|
||||
text.append(newline);
|
||||
WriteData(text.c_str(), text.size());
|
||||
}
|
||||
void TextWriter::WriteLine(char c) {
|
||||
std::string text = {c};
|
||||
text.append(newline);
|
||||
WriteData(text.c_str(), text.size());
|
||||
}
|
||||
void TextWriter::WriteLine(double d) {
|
||||
std::string text = std::to_string(d);
|
||||
text.append(newline);
|
||||
WriteData(text.c_str(), text.size());
|
||||
}
|
||||
TextWriter::TextWriter() {
|
||||
#if defined(WIN32) || defined(_WIN32)
|
||||
newline = "\r\n";
|
||||
#else
|
||||
newline = "\n";
|
||||
#endif
|
||||
}
|
||||
void TextWriter::Write(std::string txt) { WriteData(txt.c_str(), txt.size()); }
|
||||
void TextWriter::WriteLine(std::string txt) {
|
||||
std::string str = txt;
|
||||
str.append(newline);
|
||||
Write(str);
|
||||
}
|
||||
void TextWriter::WriteLine() { Write(newline); }
|
||||
TextWriter::~TextWriter() {}
|
||||
} // namespace Tesses::Framework::TextStreams
|
||||
@@ -1,106 +1,99 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/Threading/Mutex.hpp"
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#undef min
|
||||
#elif defined(GEKKO)
|
||||
#include <ogc/mutex.h>
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
namespace Tesses::Framework::Threading
|
||||
{
|
||||
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_THREADING)
|
||||
class MutexHiddenFieldData : public HiddenFieldData
|
||||
{
|
||||
public:
|
||||
#if defined(_WIN32)
|
||||
HANDLE mtx;
|
||||
#elif defined(GEKKO)
|
||||
mutex_t mtx;
|
||||
#else
|
||||
pthread_mutex_t mtx;
|
||||
pthread_mutexattr_t attr;
|
||||
#endif
|
||||
~MutexHiddenFieldData()
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
CloseHandle(mtx);
|
||||
#elif defined(GEKKO)
|
||||
LWP_MutexDestroy(mtx);
|
||||
|
||||
#else
|
||||
pthread_mutex_destroy(&mtx);
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
#endif
|
||||
Mutex::Mutex()
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_THREADING)
|
||||
auto md=this->data.AllocField<MutexHiddenFieldData>();
|
||||
#if defined(_WIN32)
|
||||
md->mtx = CreateMutex(NULL,false,NULL);
|
||||
#elif defined(GEKKO)
|
||||
md->mtx = LWP_MUTEX_NULL;
|
||||
LWP_MutexInit(&md->mtx, true);
|
||||
|
||||
#else
|
||||
pthread_mutexattr_init(&md->attr);
|
||||
pthread_mutexattr_settype(&md->attr,PTHREAD_MUTEX_RECURSIVE);
|
||||
pthread_mutex_init(&md->mtx,&md->attr);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
void Mutex::Lock()
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_THREADING)
|
||||
auto md = this->data.GetField<MutexHiddenFieldData*>();
|
||||
#if defined(_WIN32)
|
||||
WaitForSingleObject(md->mtx, INFINITE);
|
||||
#elif defined(GEKKO)
|
||||
LWP_MutexLock(md->mtx);
|
||||
|
||||
#else
|
||||
pthread_mutex_lock(&md->mtx);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
void Mutex::Unlock()
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_THREADING)
|
||||
auto md = this->data.GetField<MutexHiddenFieldData*>();
|
||||
#if defined(_WIN32)
|
||||
ReleaseMutex(md->mtx);
|
||||
#elif defined(GEKKO)
|
||||
LWP_MutexUnlock(md->mtx);
|
||||
|
||||
#else
|
||||
pthread_mutex_unlock(&md->mtx);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
bool Mutex::TryLock()
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_THREADING)
|
||||
auto md = this->data.GetField<MutexHiddenFieldData*>();
|
||||
#if defined(_WIN32)
|
||||
return WaitForSingleObject(md->mtx, 100) == WAIT_OBJECT_0;
|
||||
#elif defined(GEKKO)
|
||||
return LWP_MutexTryLock(md->mtx) == 0;
|
||||
|
||||
#else
|
||||
return pthread_mutex_trylock(&md->mtx) == 0;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
Mutex::~Mutex()
|
||||
{
|
||||
|
||||
namespace Tesses::Framework::Threading {
|
||||
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_THREADING)
|
||||
class MutexHiddenFieldData : public HiddenFieldData {
|
||||
public:
|
||||
#if defined(_WIN32)
|
||||
HANDLE mtx;
|
||||
#else
|
||||
pthread_mutex_t mtx;
|
||||
pthread_mutexattr_t attr;
|
||||
#endif
|
||||
~MutexHiddenFieldData() {
|
||||
#if defined(_WIN32)
|
||||
CloseHandle(mtx);
|
||||
#else
|
||||
pthread_mutex_destroy(&mtx);
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
#endif
|
||||
Mutex::Mutex() {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_THREADING)
|
||||
auto md = this->data.AllocField<MutexHiddenFieldData>();
|
||||
#if defined(_WIN32)
|
||||
md->mtx = CreateMutex(NULL, false, NULL);
|
||||
#elif defined(GEKKO)
|
||||
md->mtx = LWP_MUTEX_NULL;
|
||||
LWP_MutexInit(&md->mtx, true);
|
||||
|
||||
#else
|
||||
pthread_mutexattr_init(&md->attr);
|
||||
pthread_mutexattr_settype(&md->attr, PTHREAD_MUTEX_RECURSIVE);
|
||||
pthread_mutex_init(&md->mtx, &md->attr);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
void Mutex::Lock() {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_THREADING)
|
||||
auto md = this->data.GetField<MutexHiddenFieldData *>();
|
||||
#if defined(_WIN32)
|
||||
WaitForSingleObject(md->mtx, INFINITE);
|
||||
|
||||
#else
|
||||
pthread_mutex_lock(&md->mtx);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
void Mutex::Unlock() {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_THREADING)
|
||||
auto md = this->data.GetField<MutexHiddenFieldData *>();
|
||||
#if defined(_WIN32)
|
||||
ReleaseMutex(md->mtx);
|
||||
#else
|
||||
pthread_mutex_unlock(&md->mtx);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
bool Mutex::TryLock() {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_THREADING)
|
||||
auto md = this->data.GetField<MutexHiddenFieldData *>();
|
||||
#if defined(_WIN32)
|
||||
return WaitForSingleObject(md->mtx, 100) == WAIT_OBJECT_0;
|
||||
|
||||
#else
|
||||
return pthread_mutex_trylock(&md->mtx) == 0;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
Mutex::~Mutex() {}
|
||||
}; // namespace Tesses::Framework::Threading
|
||||
|
||||
@@ -1,219 +1,214 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/Threading/Thread.hpp"
|
||||
#include "TessesFramework/Common.hpp"
|
||||
#include "TessesFramework/Threading/Mutex.hpp"
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include "TessesFramework/Threading/Mutex.hpp"
|
||||
#include "TessesFramework/Common.hpp"
|
||||
#if defined(__SWITCH__)
|
||||
extern "C" {
|
||||
#include <switch.h>
|
||||
#include <pthread.h>
|
||||
#include <switch.h>
|
||||
}
|
||||
#endif
|
||||
namespace Tesses::Framework::Threading
|
||||
{
|
||||
namespace Tesses::Framework::Threading {
|
||||
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_THREADING)
|
||||
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_THREADING)
|
||||
#if defined(__SWITCH__)
|
||||
Mutex needed_to_be_joined_mtx;
|
||||
class NeedToBeJoinnedThread {
|
||||
|
||||
#if defined(__SWITCH__) || defined(GEKKO)
|
||||
Mutex needed_to_be_joined_mtx;
|
||||
class NeedToBeJoinnedThread {
|
||||
|
||||
static void* cb(void* data)
|
||||
{
|
||||
|
||||
auto ntbjt = static_cast<NeedToBeJoinnedThread*>(data);
|
||||
|
||||
ntbjt->hasInvoked=true;
|
||||
TF_LOG("About to call thread func");
|
||||
if(ntbjt->_cb)
|
||||
ntbjt->_cb();
|
||||
TF_LOG("Finished calling thread func");
|
||||
ntbjt->hasExited=true;
|
||||
static void *cb(void *data) {
|
||||
|
||||
return NULL;
|
||||
}
|
||||
std::function<void()> _cb;
|
||||
std::atomic<bool> hasInvoked=false;
|
||||
#if defined(__SWITCH__)
|
||||
pthread_t thrd;
|
||||
#elif defined(GEKKO)
|
||||
lwp_t thrd;
|
||||
#endif
|
||||
public:
|
||||
NeedToBeJoinnedThread(std::function<void()> cb)
|
||||
{
|
||||
this->_cb = cb;
|
||||
joinned=false;
|
||||
joinning=false;
|
||||
hasExited=false;
|
||||
#if defined(GEKKO)
|
||||
LWP_CreateThread(&thrd, this->cb, static_cast<void*>(this), nullptr,12000, 98);
|
||||
#elif defined(__SWITCH__)
|
||||
pthread_create(&thrd,NULL,this->cb,static_cast<void*>(this));
|
||||
|
||||
#endif
|
||||
}
|
||||
std::atomic<bool> joinned;
|
||||
std::atomic<bool> joinning;
|
||||
std::atomic<bool> hasExited;
|
||||
void Join();
|
||||
void WaitTillInvoked()
|
||||
{
|
||||
while(!hasInvoked);
|
||||
TF_LOG("Invoked!");
|
||||
}
|
||||
};
|
||||
auto ntbjt = static_cast<NeedToBeJoinnedThread *>(data);
|
||||
|
||||
void NeedToBeJoinnedThread::Join()
|
||||
{
|
||||
if(joinning)
|
||||
{
|
||||
return;
|
||||
}
|
||||
joinning=true;
|
||||
#if defined(__SWITCH__)
|
||||
pthread_join(this->thrd,NULL);
|
||||
#elif defined(GEKKO)
|
||||
void* res;
|
||||
LWP_JoinThread(this->thrd,&res);
|
||||
#endif
|
||||
joinned=true;
|
||||
//start the joinning process
|
||||
ntbjt->hasInvoked = true;
|
||||
TF_LOG("About to call thread func");
|
||||
if (ntbjt->_cb)
|
||||
ntbjt->_cb();
|
||||
TF_LOG("Finished calling thread func");
|
||||
ntbjt->hasExited = true;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
std::function<void()> _cb;
|
||||
std::atomic<bool> hasInvoked = false;
|
||||
|
||||
pthread_t thrd;
|
||||
|
||||
public:
|
||||
NeedToBeJoinnedThread(std::function<void()> cb) {
|
||||
this->_cb = cb;
|
||||
joinned = false;
|
||||
joinning = false;
|
||||
hasExited = false;
|
||||
|
||||
pthread_create(&thrd, NULL, this->cb, static_cast<void *>(this));
|
||||
|
||||
std::vector<std::shared_ptr<NeedToBeJoinnedThread>> needToBeJoinnedThread;
|
||||
void JoinAllThreads()
|
||||
{
|
||||
needed_to_be_joined_mtx.Lock();
|
||||
for(auto item : needToBeJoinnedThread)
|
||||
{
|
||||
item->Join();
|
||||
}
|
||||
needToBeJoinnedThread.clear();
|
||||
needed_to_be_joined_mtx.Unlock();
|
||||
|
||||
}
|
||||
void LookForFinishedThreads()
|
||||
{
|
||||
|
||||
needed_to_be_joined_mtx.Lock();
|
||||
for(auto index = needToBeJoinnedThread.begin(); index < needToBeJoinnedThread.end(); index++)
|
||||
{
|
||||
auto& idx = *index;
|
||||
if(idx->hasExited)
|
||||
{
|
||||
if(idx->joinning) while(!idx->joinned);
|
||||
TF_LOG("ABOUT TO JOIN");
|
||||
idx->Join();
|
||||
TF_LOG("JOINNED");
|
||||
needToBeJoinnedThread.erase(index);
|
||||
index--;
|
||||
}
|
||||
}
|
||||
needed_to_be_joined_mtx.Unlock();
|
||||
std::atomic<bool> joinned;
|
||||
std::atomic<bool> joinning;
|
||||
std::atomic<bool> hasExited;
|
||||
void Join();
|
||||
void WaitTillInvoked() {
|
||||
while (!hasInvoked)
|
||||
;
|
||||
TF_LOG("Invoked!");
|
||||
}
|
||||
|
||||
#endif
|
||||
class ThreadHiddenFieldData : public HiddenFieldData {
|
||||
public:
|
||||
#if defined(_WIN32)
|
||||
};
|
||||
|
||||
HANDLE thrd;
|
||||
DWORD thrdId;
|
||||
|
||||
|
||||
|
||||
#elif defined(__SWITCH__) || defined(GEKKO)
|
||||
std::shared_ptr<NeedToBeJoinnedThread> thread;
|
||||
#else
|
||||
pthread_t thrd;
|
||||
#endif
|
||||
|
||||
|
||||
std::function<void()> fn;
|
||||
|
||||
std::atomic<bool> hasInvoked;
|
||||
};
|
||||
|
||||
|
||||
#if defined(_WIN32)
|
||||
static DWORD __stdcall cb(LPVOID data)
|
||||
#elif defined(__SWITCH__)
|
||||
static void cb(void* data)
|
||||
#else
|
||||
static void* cb(void* data)
|
||||
#endif
|
||||
{
|
||||
auto thrd = static_cast<ThreadHiddenFieldData*>(data);
|
||||
|
||||
auto fn = thrd->fn;
|
||||
thrd->hasInvoked=true;
|
||||
fn();
|
||||
#if !defined(_WIN32) && !defined(__SWITCH__)
|
||||
return NULL;
|
||||
#elif(__SWITCH__)
|
||||
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
Thread::Thread(std::function<void()> fn)
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_THREADING)
|
||||
auto data = this->data.AllocField<ThreadHiddenFieldData>();
|
||||
data->hasInvoked=false;
|
||||
data->fn = fn;
|
||||
#if defined(_WIN32)
|
||||
data->thrd = CreateThread(NULL,0,cb,static_cast<LPVOID>(data), 0, &data->thrdId);
|
||||
while(!data->hasInvoked);
|
||||
#elif defined(__SWITCH__) || defined(GEKKO)
|
||||
data->thread = std::make_shared<NeedToBeJoinnedThread>(fn);
|
||||
data->thread->WaitTillInvoked();
|
||||
//threadCreate(,cb,static_cast<void*>(data),NULL,8000000,0x00,-2);
|
||||
#else
|
||||
pthread_create(&data->thrd,NULL,cb,static_cast<void*>(data));
|
||||
while(!data->hasInvoked);
|
||||
//thrd_create(&thrd, cb, static_cast<void*>(this));
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
void Thread::Detach()
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_THREADING)
|
||||
auto data = this->data.GetField<ThreadHiddenFieldData*>();
|
||||
|
||||
#if defined(_WIN32)
|
||||
CloseHandle(data->thrd);
|
||||
#elif defined(__SWITCH__) || defined(GEKKO)
|
||||
TF_LOG("Detaching");
|
||||
needed_to_be_joined_mtx.Lock();
|
||||
needToBeJoinnedThread.push_back(data->thread);
|
||||
needed_to_be_joined_mtx.Unlock();
|
||||
|
||||
TF_LOG("Detached!");
|
||||
#else
|
||||
pthread_detach(data->thrd);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
void NeedToBeJoinnedThread::Join() {
|
||||
if (joinning) {
|
||||
return;
|
||||
}
|
||||
joinning = true;
|
||||
pthread_join(this->thrd, NULL);
|
||||
|
||||
void Thread::Join()
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_THREADING)
|
||||
auto data = this->data.GetField<ThreadHiddenFieldData*>();
|
||||
#if defined(_WIN32)
|
||||
WaitForSingleObject(data->thrd, INFINITE);
|
||||
#elif defined(__SWITCH__) || defined(GEKKO)
|
||||
data->thread->Join();
|
||||
#else
|
||||
pthread_join(data->thrd,NULL);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
joinned = true;
|
||||
// start the joinning process
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<NeedToBeJoinnedThread>> needToBeJoinnedThread;
|
||||
void JoinAllThreads() {
|
||||
needed_to_be_joined_mtx.Lock();
|
||||
for (auto item : needToBeJoinnedThread) {
|
||||
item->Join();
|
||||
}
|
||||
needToBeJoinnedThread.clear();
|
||||
needed_to_be_joined_mtx.Unlock();
|
||||
}
|
||||
void LookForFinishedThreads() {
|
||||
|
||||
needed_to_be_joined_mtx.Lock();
|
||||
for (auto index = needToBeJoinnedThread.begin();
|
||||
index < needToBeJoinnedThread.end(); index++) {
|
||||
auto &idx = *index;
|
||||
if (idx->hasExited) {
|
||||
if (idx->joinning)
|
||||
while (!idx->joinned)
|
||||
;
|
||||
TF_LOG("ABOUT TO JOIN");
|
||||
idx->Join();
|
||||
TF_LOG("JOINNED");
|
||||
needToBeJoinnedThread.erase(index);
|
||||
index--;
|
||||
}
|
||||
}
|
||||
needed_to_be_joined_mtx.Unlock();
|
||||
}
|
||||
|
||||
#endif
|
||||
class ThreadHiddenFieldData : public HiddenFieldData {
|
||||
public:
|
||||
#if defined(_WIN32)
|
||||
|
||||
HANDLE thrd;
|
||||
DWORD thrdId;
|
||||
|
||||
#elif defined(__SWITCH__)
|
||||
std::shared_ptr<NeedToBeJoinnedThread> thread;
|
||||
#else
|
||||
pthread_t thrd;
|
||||
#endif
|
||||
|
||||
std::function<void()> fn;
|
||||
|
||||
std::atomic<bool> hasInvoked;
|
||||
};
|
||||
|
||||
#if defined(_WIN32)
|
||||
static DWORD __stdcall cb(LPVOID data)
|
||||
#elif defined(__SWITCH__)
|
||||
static void cb(void *data)
|
||||
#else
|
||||
static void *cb(void *data)
|
||||
#endif
|
||||
{
|
||||
auto thrd = static_cast<ThreadHiddenFieldData *>(data);
|
||||
|
||||
auto fn = thrd->fn;
|
||||
thrd->hasInvoked = true;
|
||||
fn();
|
||||
#if !defined(_WIN32) && !defined(__SWITCH__)
|
||||
return NULL;
|
||||
#elif (__SWITCH__)
|
||||
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
Thread::Thread(std::function<void()> fn) {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_THREADING)
|
||||
auto data = this->data.AllocField<ThreadHiddenFieldData>();
|
||||
data->hasInvoked = false;
|
||||
data->fn = fn;
|
||||
#if defined(_WIN32)
|
||||
data->thrd =
|
||||
CreateThread(NULL, 0, cb, static_cast<LPVOID>(data), 0, &data->thrdId);
|
||||
while (!data->hasInvoked)
|
||||
;
|
||||
#elif defined(__SWITCH__)
|
||||
data->thread = std::make_shared<NeedToBeJoinnedThread>(fn);
|
||||
data->thread->WaitTillInvoked();
|
||||
// threadCreate(,cb,static_cast<void*>(data),NULL,8000000,0x00,-2);
|
||||
#else
|
||||
pthread_create(&data->thrd, NULL, cb, static_cast<void *>(data));
|
||||
while (!data->hasInvoked)
|
||||
;
|
||||
// thrd_create(&thrd, cb, static_cast<void*>(this));
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
void Thread::Detach() {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_THREADING)
|
||||
auto data = this->data.GetField<ThreadHiddenFieldData *>();
|
||||
|
||||
#if defined(_WIN32)
|
||||
CloseHandle(data->thrd);
|
||||
#elif defined(__SWITCH__)
|
||||
TF_LOG("Detaching");
|
||||
needed_to_be_joined_mtx.Lock();
|
||||
needToBeJoinnedThread.push_back(data->thread);
|
||||
needed_to_be_joined_mtx.Unlock();
|
||||
|
||||
TF_LOG("Detached!");
|
||||
#else
|
||||
pthread_detach(data->thrd);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void Thread::Join() {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_THREADING)
|
||||
auto data = this->data.GetField<ThreadHiddenFieldData *>();
|
||||
#if defined(_WIN32)
|
||||
WaitForSingleObject(data->thrd, INFINITE);
|
||||
#elif defined(__SWITCH__)
|
||||
data->thread->Join();
|
||||
#else
|
||||
pthread_join(data->thrd, NULL);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
} // namespace Tesses::Framework::Threading
|
||||
|
||||
@@ -1,88 +1,93 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/Threading/ThreadPool.hpp"
|
||||
#if !defined(GEKKO)
|
||||
#include <thread>
|
||||
#endif
|
||||
namespace Tesses::Framework::Threading
|
||||
{
|
||||
size_t ThreadPool::GetNumberOfCores()
|
||||
{
|
||||
#if defined(GEKKO)
|
||||
return 1;
|
||||
#elif defined(TESSESFRAMEWORK_ENABLE_THREADING)
|
||||
return (size_t)std::thread::hardware_concurrency();
|
||||
#else
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
size_t ThreadPool::ThreadCount()
|
||||
{
|
||||
return this->threads.size();
|
||||
}
|
||||
bool ThreadPool::Empty()
|
||||
{
|
||||
bool qie;
|
||||
this->mtx.Lock();
|
||||
qie = this->callbacks.empty();
|
||||
this->mtx.Unlock();
|
||||
return qie;
|
||||
}
|
||||
ThreadPool::ThreadPool(size_t threads)
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_THREADING)
|
||||
this->isRunning=true;
|
||||
for(size_t i = 0; i < threads; i++)
|
||||
{
|
||||
this->threads.push_back(new Thread([this,i]()->void{
|
||||
while(true)
|
||||
{
|
||||
this->mtx.Lock();
|
||||
|
||||
if(!this->isRunning)
|
||||
{
|
||||
this->mtx.Unlock();
|
||||
return;
|
||||
}
|
||||
namespace Tesses::Framework::Threading {
|
||||
size_t ThreadPool::GetNumberOfCores() {
|
||||
#if defined(GEKKO)
|
||||
return 1;
|
||||
#elif defined(TESSESFRAMEWORK_ENABLE_THREADING)
|
||||
return (size_t)std::thread::hardware_concurrency();
|
||||
#else
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
size_t ThreadPool::ThreadCount() { return this->threads.size(); }
|
||||
bool ThreadPool::Empty() {
|
||||
bool qie;
|
||||
this->mtx.Lock();
|
||||
qie = this->callbacks.empty();
|
||||
this->mtx.Unlock();
|
||||
return qie;
|
||||
}
|
||||
ThreadPool::ThreadPool(size_t threads) {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_THREADING)
|
||||
this->isRunning = true;
|
||||
for (size_t i = 0; i < threads; i++) {
|
||||
this->threads.push_back(new Thread([this, i]() -> void {
|
||||
while (true) {
|
||||
this->mtx.Lock();
|
||||
|
||||
std::function<void(size_t)> fn=nullptr;
|
||||
|
||||
if(!this->callbacks.empty())
|
||||
{
|
||||
fn=this->callbacks.front();
|
||||
this->callbacks.pop();
|
||||
}
|
||||
if (!this->isRunning) {
|
||||
this->mtx.Unlock();
|
||||
if(fn)
|
||||
fn(i);
|
||||
return;
|
||||
}
|
||||
}));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
void ThreadPool::Schedule(std::function<void(size_t)> cb)
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_THREADING)
|
||||
this->mtx.Lock();
|
||||
this->callbacks.push(cb);
|
||||
this->mtx.Unlock();
|
||||
#endif
|
||||
}
|
||||
ThreadPool::~ThreadPool()
|
||||
{
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_THREADING)
|
||||
while(true)
|
||||
{
|
||||
this->mtx.Lock();
|
||||
auto emp=this->callbacks.empty();
|
||||
if(emp) this->isRunning=false;
|
||||
this->mtx.Unlock();
|
||||
if(emp) break;
|
||||
}
|
||||
|
||||
for(auto item : this->threads)
|
||||
{
|
||||
item->Join();
|
||||
delete item;
|
||||
}
|
||||
#endif
|
||||
std::function<void(size_t)> fn = nullptr;
|
||||
|
||||
if (!this->callbacks.empty()) {
|
||||
fn = this->callbacks.front();
|
||||
this->callbacks.pop();
|
||||
}
|
||||
this->mtx.Unlock();
|
||||
if (fn)
|
||||
fn(i);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
void ThreadPool::Schedule(std::function<void(size_t)> cb) {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_THREADING)
|
||||
this->mtx.Lock();
|
||||
this->callbacks.push(cb);
|
||||
this->mtx.Unlock();
|
||||
#endif
|
||||
}
|
||||
ThreadPool::~ThreadPool() {
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_THREADING)
|
||||
while (true) {
|
||||
this->mtx.Lock();
|
||||
auto emp = this->callbacks.empty();
|
||||
if (emp)
|
||||
this->isRunning = false;
|
||||
this->mtx.Unlock();
|
||||
if (emp)
|
||||
break;
|
||||
}
|
||||
|
||||
for (auto item : this->threads) {
|
||||
item->Join();
|
||||
delete item;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} // namespace Tesses::Framework::Threading
|
||||
397
src/Uuid.cpp
397
src/Uuid.cpp
@@ -1,214 +1,207 @@
|
||||
/*
|
||||
TessesFramework a library to make C++ easier for me, used in CrossLang:
|
||||
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TessesFramework/Uuid.hpp"
|
||||
#include "TessesFramework/Http/HttpUtils.hpp"
|
||||
#include "TessesFramework/Crypto/Crypto.hpp"
|
||||
#include "TessesFramework/Http/HttpUtils.hpp"
|
||||
|
||||
namespace Tesses::Framework {
|
||||
Uuid Uuid::Generate()
|
||||
{
|
||||
//xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
|
||||
Uuid uuid;
|
||||
Uuid::Generate(uuid);
|
||||
return uuid;
|
||||
Uuid Uuid::Generate() {
|
||||
// xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
|
||||
Uuid uuid;
|
||||
Uuid::Generate(uuid);
|
||||
return uuid;
|
||||
}
|
||||
|
||||
void Uuid::Generate(Uuid &uuid) {
|
||||
std::vector<uint8_t> bytes(16);
|
||||
Crypto::RandomBytes(bytes, "TF_UUID");
|
||||
|
||||
uuid.time_low = (uint32_t)bytes[0];
|
||||
uuid.time_low |= (uint32_t)bytes[1] << 8;
|
||||
uuid.time_low |= (uint32_t)bytes[2] << 16;
|
||||
uuid.time_low |= (uint32_t)bytes[3] << 24;
|
||||
uuid.time_mid = (uint16_t)bytes[4];
|
||||
uuid.time_mid |= (uint16_t)bytes[5] << 8;
|
||||
uuid.time_hi_and_version = (uint16_t)bytes[6];
|
||||
uuid.time_hi_and_version |= (uint16_t)bytes[7] << 8;
|
||||
uuid.clock_seq_hi_and_reserved = bytes[8];
|
||||
uuid.clock_seq_low = bytes[9];
|
||||
for (size_t i = 0; i < 6; i++) {
|
||||
uuid.node[i] = bytes[i + 10];
|
||||
}
|
||||
uuid.time_hi_and_version &= ~0x00F0;
|
||||
uuid.time_hi_and_version |= 0x0040;
|
||||
uuid.clock_seq_hi_and_reserved &= ~0b11000000;
|
||||
uuid.clock_seq_hi_and_reserved |= 0b10000000;
|
||||
}
|
||||
|
||||
void Uuid::Generate(Uuid& uuid)
|
||||
{
|
||||
std::vector<uint8_t> bytes(16);
|
||||
Crypto::RandomBytes(bytes, "TF_UUID");
|
||||
|
||||
uuid.time_low = (uint32_t)bytes[0];
|
||||
uuid.time_low |= (uint32_t)bytes[1] << 8;
|
||||
uuid.time_low |= (uint32_t)bytes[2] << 16;
|
||||
uuid.time_low |= (uint32_t)bytes[3] << 24;
|
||||
uuid.time_mid = (uint16_t)bytes[4];
|
||||
uuid.time_mid |= (uint16_t)bytes[5] << 8;
|
||||
uuid.time_hi_and_version = (uint16_t)bytes[6];
|
||||
uuid.time_hi_and_version |= (uint16_t)bytes[7] << 8;
|
||||
uuid.clock_seq_hi_and_reserved = bytes[8];
|
||||
uuid.clock_seq_low = bytes[9];
|
||||
for(size_t i = 0; i < 6; i++)
|
||||
{
|
||||
uuid.node[i] = bytes[i+10];
|
||||
}
|
||||
uuid.time_hi_and_version &= ~0x00F0;
|
||||
uuid.time_hi_and_version |= 0x0040;
|
||||
uuid.clock_seq_hi_and_reserved &= ~0b11000000;
|
||||
uuid.clock_seq_hi_and_reserved |= 0b10000000;
|
||||
|
||||
|
||||
}
|
||||
|
||||
bool Uuid::TryParse(std::string text, Uuid& uuid)
|
||||
{
|
||||
std::array<uint8_t,32> hex_digits;
|
||||
size_t hex_offset = 0;
|
||||
size_t text_offset = 0;
|
||||
for(; text_offset < text.size(); text_offset++)
|
||||
{
|
||||
if(text[text_offset] == '{' && (text_offset != 0 || hex_offset != 0))
|
||||
bool Uuid::TryParse(std::string text, Uuid &uuid) {
|
||||
std::array<uint8_t, 32> hex_digits;
|
||||
size_t hex_offset = 0;
|
||||
size_t text_offset = 0;
|
||||
for (; text_offset < text.size(); text_offset++) {
|
||||
if (text[text_offset] == '{' && (text_offset != 0 || hex_offset != 0))
|
||||
return false;
|
||||
if (text[text_offset] == '}' && hex_offset < 32)
|
||||
return false;
|
||||
if (text[text_offset] == '-' && hex_offset != 8 && hex_offset != 12 &&
|
||||
hex_offset != 16 && hex_offset != 20)
|
||||
return false;
|
||||
if ((text[text_offset] >= 'A' && text[text_offset] <= 'F') ||
|
||||
(text[text_offset] >= 'a' && text[text_offset] <= 'f') ||
|
||||
text[text_offset] >= '0' && text[text_offset] <= '9') {
|
||||
if (hex_offset >= 32)
|
||||
return false;
|
||||
if(text[text_offset] == '}' && hex_offset < 32)
|
||||
return false;
|
||||
if(text[text_offset] == '-' && hex_offset != 8 && hex_offset != 12 && hex_offset != 16 && hex_offset != 20)
|
||||
return false;
|
||||
if((text[text_offset] >= 'A' && text[text_offset] <= 'F') || (text[text_offset] >= 'a' && text[text_offset] <= 'f') || text[text_offset] >= '0' && text[text_offset] <= '9')
|
||||
{
|
||||
if(hex_offset >= 32) return false;
|
||||
hex_digits[hex_offset] = Http::HttpUtils::HexToNibble(text[text_offset]);
|
||||
hex_offset++;
|
||||
}
|
||||
else return false;
|
||||
|
||||
}
|
||||
|
||||
uint8_t b = hex_digits[0] << 4 | hex_digits[1];
|
||||
uuid.time_low = (uint32_t)b;
|
||||
b = hex_digits[2] << 4 | hex_digits[3];
|
||||
uuid.time_low |= (uint32_t)b << 8;
|
||||
b = hex_digits[4] << 4 | hex_digits[5];
|
||||
uuid.time_low |= (uint32_t)b << 16;
|
||||
b = hex_digits[6] << 4 | hex_digits[7];
|
||||
uuid.time_low |= (uint32_t)b << 24;
|
||||
|
||||
b = hex_digits[8] << 4 | hex_digits[9];
|
||||
uuid.time_mid = (uint16_t)b;
|
||||
b = hex_digits[10] << 4 | hex_digits[11];
|
||||
uuid.time_mid |= (uint16_t)b << 8;
|
||||
|
||||
b = hex_digits[12] << 4 | hex_digits[13];
|
||||
uuid.time_hi_and_version = (uint16_t)b;
|
||||
b = hex_digits[14] << 4 | hex_digits[15];
|
||||
uuid.time_hi_and_version |= (uint16_t)b << 8;
|
||||
|
||||
uuid.clock_seq_hi_and_reserved = hex_digits[16] << 4 | hex_digits[17];
|
||||
|
||||
uuid.clock_seq_low = hex_digits[18] << 4 | hex_digits[19];
|
||||
|
||||
for(size_t i = 0; i < 6; i++)
|
||||
{
|
||||
uuid.node[i] = hex_digits[20+(i*2)] << 4 | hex_digits[21+(i*2)];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//9c4994e7-3c82-4c30-a459-8fdcd960b4ac
|
||||
|
||||
std::string Uuid::ToString(UuidStringifyConfig cfg) const
|
||||
{
|
||||
bool hasCurly = ((int)cfg & (int)UuidStringifyConfig::HasCurly) != 0;
|
||||
bool isUppercase = ((int)cfg & (int)UuidStringifyConfig::IsUppercase) != 0;
|
||||
bool hasDash = ((int)cfg & (int)UuidStringifyConfig::HasDashes) != 0;
|
||||
|
||||
std::string uuid_str = "";
|
||||
if(hasCurly)
|
||||
uuid_str += "{";
|
||||
|
||||
uint8_t byte = (uint8_t)(this->time_low & 0xFF);
|
||||
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte>>4,isUppercase);
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte,isUppercase);
|
||||
byte = (uint8_t)((this->time_low >> 8) & 0xFF);
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte>>4,isUppercase);
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte,isUppercase);
|
||||
byte = (uint8_t)((this->time_low >> 16) & 0xFF);
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte>>4,isUppercase);
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte,isUppercase);
|
||||
byte = (uint8_t)((this->time_low >> 24) & 0xFF);
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte>>4,isUppercase);
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte,isUppercase);
|
||||
|
||||
if(hasDash)
|
||||
uuid_str += "-";
|
||||
|
||||
byte = (uint8_t)(this->time_mid & 0xFF);
|
||||
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte>>4,isUppercase);
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte,isUppercase);
|
||||
|
||||
byte = (uint8_t)((this->time_mid >> 8) & 0xFF);
|
||||
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte>>4,isUppercase);
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte,isUppercase);
|
||||
|
||||
if(hasDash)
|
||||
uuid_str += "-";
|
||||
|
||||
byte = (uint8_t)(this->time_hi_and_version & 0xFF);
|
||||
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte>>4,isUppercase);
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte,isUppercase);
|
||||
|
||||
byte = (uint8_t)((this->time_hi_and_version >> 8) & 0xFF);
|
||||
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte>>4,isUppercase);
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte,isUppercase);
|
||||
if(hasDash)
|
||||
uuid_str += "-";
|
||||
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(this->clock_seq_hi_and_reserved>>4,isUppercase);
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(this->clock_seq_hi_and_reserved,isUppercase);
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(this->clock_seq_low>>4,isUppercase);
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(this->clock_seq_low,isUppercase);
|
||||
if(hasDash)
|
||||
uuid_str += "-";
|
||||
|
||||
for(size_t i = 0; i < 6; i++)
|
||||
{
|
||||
byte = this->node[i];
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte>>4,isUppercase);
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte,isUppercase);
|
||||
}
|
||||
|
||||
if(hasCurly)
|
||||
uuid_str += "}";
|
||||
return uuid_str;
|
||||
|
||||
|
||||
|
||||
hex_digits[hex_offset] =
|
||||
Http::HttpUtils::HexToNibble(text[text_offset]);
|
||||
hex_offset++;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Uuid::IsEmpty() const
|
||||
{
|
||||
return this->time_low == 0 &&
|
||||
this->time_mid == 0 &&
|
||||
this->time_hi_and_version == 0 &&
|
||||
this->clock_seq_hi_and_reserved == 0 &&
|
||||
this->clock_seq_low == 0 &&
|
||||
this->node[0] == 0 &&
|
||||
this->node[1] == 0 &&
|
||||
this->node[2] == 0 &&
|
||||
this->node[3] == 0 &&
|
||||
this->node[4] == 0 &&
|
||||
this->node[5] == 0;
|
||||
uint8_t b = hex_digits[0] << 4 | hex_digits[1];
|
||||
uuid.time_low = (uint32_t)b;
|
||||
b = hex_digits[2] << 4 | hex_digits[3];
|
||||
uuid.time_low |= (uint32_t)b << 8;
|
||||
b = hex_digits[4] << 4 | hex_digits[5];
|
||||
uuid.time_low |= (uint32_t)b << 16;
|
||||
b = hex_digits[6] << 4 | hex_digits[7];
|
||||
uuid.time_low |= (uint32_t)b << 24;
|
||||
|
||||
b = hex_digits[8] << 4 | hex_digits[9];
|
||||
uuid.time_mid = (uint16_t)b;
|
||||
b = hex_digits[10] << 4 | hex_digits[11];
|
||||
uuid.time_mid |= (uint16_t)b << 8;
|
||||
|
||||
b = hex_digits[12] << 4 | hex_digits[13];
|
||||
uuid.time_hi_and_version = (uint16_t)b;
|
||||
b = hex_digits[14] << 4 | hex_digits[15];
|
||||
uuid.time_hi_and_version |= (uint16_t)b << 8;
|
||||
|
||||
uuid.clock_seq_hi_and_reserved = hex_digits[16] << 4 | hex_digits[17];
|
||||
|
||||
uuid.clock_seq_low = hex_digits[18] << 4 | hex_digits[19];
|
||||
|
||||
for (size_t i = 0; i < 6; i++) {
|
||||
uuid.node[i] = hex_digits[20 + (i * 2)] << 4 | hex_digits[21 + (i * 2)];
|
||||
}
|
||||
|
||||
bool operator==(const Uuid& left, const Uuid& right)
|
||||
{
|
||||
return left.time_low == right.time_low &&
|
||||
left.time_mid == right.time_mid &&
|
||||
left.time_hi_and_version == right.time_hi_and_version &&
|
||||
left.clock_seq_hi_and_reserved == right.clock_seq_hi_and_reserved &&
|
||||
left.clock_seq_low == right.clock_seq_low &&
|
||||
left.node[0] == right.node[0] &&
|
||||
left.node[1] == right.node[1] &&
|
||||
left.node[2] == right.node[2] &&
|
||||
left.node[3] == right.node[3] &&
|
||||
left.node[4] == right.node[4] &&
|
||||
left.node[5] == right.node[5];
|
||||
|
||||
return true;
|
||||
}
|
||||
// 9c4994e7-3c82-4c30-a459-8fdcd960b4ac
|
||||
|
||||
std::string Uuid::ToString(UuidStringifyConfig cfg) const {
|
||||
bool hasCurly = ((int)cfg & (int)UuidStringifyConfig::HasCurly) != 0;
|
||||
bool isUppercase = ((int)cfg & (int)UuidStringifyConfig::IsUppercase) != 0;
|
||||
bool hasDash = ((int)cfg & (int)UuidStringifyConfig::HasDashes) != 0;
|
||||
|
||||
std::string uuid_str = "";
|
||||
if (hasCurly)
|
||||
uuid_str += "{";
|
||||
|
||||
uint8_t byte = (uint8_t)(this->time_low & 0xFF);
|
||||
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte >> 4, isUppercase);
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte, isUppercase);
|
||||
byte = (uint8_t)((this->time_low >> 8) & 0xFF);
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte >> 4, isUppercase);
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte, isUppercase);
|
||||
byte = (uint8_t)((this->time_low >> 16) & 0xFF);
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte >> 4, isUppercase);
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte, isUppercase);
|
||||
byte = (uint8_t)((this->time_low >> 24) & 0xFF);
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte >> 4, isUppercase);
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte, isUppercase);
|
||||
|
||||
if (hasDash)
|
||||
uuid_str += "-";
|
||||
|
||||
byte = (uint8_t)(this->time_mid & 0xFF);
|
||||
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte >> 4, isUppercase);
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte, isUppercase);
|
||||
|
||||
byte = (uint8_t)((this->time_mid >> 8) & 0xFF);
|
||||
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte >> 4, isUppercase);
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte, isUppercase);
|
||||
|
||||
if (hasDash)
|
||||
uuid_str += "-";
|
||||
|
||||
byte = (uint8_t)(this->time_hi_and_version & 0xFF);
|
||||
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte >> 4, isUppercase);
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte, isUppercase);
|
||||
|
||||
byte = (uint8_t)((this->time_hi_and_version >> 8) & 0xFF);
|
||||
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte >> 4, isUppercase);
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte, isUppercase);
|
||||
if (hasDash)
|
||||
uuid_str += "-";
|
||||
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(
|
||||
this->clock_seq_hi_and_reserved >> 4, isUppercase);
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(this->clock_seq_hi_and_reserved,
|
||||
isUppercase);
|
||||
uuid_str +=
|
||||
Http::HttpUtils::NibbleToHex(this->clock_seq_low >> 4, isUppercase);
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(this->clock_seq_low, isUppercase);
|
||||
if (hasDash)
|
||||
uuid_str += "-";
|
||||
|
||||
for (size_t i = 0; i < 6; i++) {
|
||||
byte = this->node[i];
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte >> 4, isUppercase);
|
||||
uuid_str += Http::HttpUtils::NibbleToHex(byte, isUppercase);
|
||||
}
|
||||
bool operator!=(const Uuid& left, const Uuid& right)
|
||||
{
|
||||
return left.time_low != right.time_low &&
|
||||
left.time_mid != right.time_mid &&
|
||||
left.time_hi_and_version != right.time_hi_and_version &&
|
||||
left.clock_seq_hi_and_reserved != right.clock_seq_hi_and_reserved &&
|
||||
left.clock_seq_low != right.clock_seq_low &&
|
||||
left.node[0] != right.node[0] &&
|
||||
left.node[1] != right.node[1] &&
|
||||
left.node[2] != right.node[2] &&
|
||||
left.node[3] != right.node[3] &&
|
||||
left.node[4] != right.node[4] &&
|
||||
left.node[5] != right.node[5];
|
||||
}
|
||||
}
|
||||
|
||||
if (hasCurly)
|
||||
uuid_str += "}";
|
||||
return uuid_str;
|
||||
}
|
||||
|
||||
bool Uuid::IsEmpty() const {
|
||||
return this->time_low == 0 && this->time_mid == 0 &&
|
||||
this->time_hi_and_version == 0 &&
|
||||
this->clock_seq_hi_and_reserved == 0 && this->clock_seq_low == 0 &&
|
||||
this->node[0] == 0 && this->node[1] == 0 && this->node[2] == 0 &&
|
||||
this->node[3] == 0 && this->node[4] == 0 && this->node[5] == 0;
|
||||
}
|
||||
|
||||
bool operator==(const Uuid &left, const Uuid &right) {
|
||||
return left.time_low == right.time_low && left.time_mid == right.time_mid &&
|
||||
left.time_hi_and_version == right.time_hi_and_version &&
|
||||
left.clock_seq_hi_and_reserved == right.clock_seq_hi_and_reserved &&
|
||||
left.clock_seq_low == right.clock_seq_low &&
|
||||
left.node[0] == right.node[0] && left.node[1] == right.node[1] &&
|
||||
left.node[2] == right.node[2] && left.node[3] == right.node[3] &&
|
||||
left.node[4] == right.node[4] && left.node[5] == right.node[5];
|
||||
}
|
||||
bool operator!=(const Uuid &left, const Uuid &right) {
|
||||
return left.time_low != right.time_low && left.time_mid != right.time_mid &&
|
||||
left.time_hi_and_version != right.time_hi_and_version &&
|
||||
left.clock_seq_hi_and_reserved != right.clock_seq_hi_and_reserved &&
|
||||
left.clock_seq_low != right.clock_seq_low &&
|
||||
left.node[0] != right.node[0] && left.node[1] != right.node[1] &&
|
||||
left.node[2] != right.node[2] && left.node[3] != right.node[3] &&
|
||||
left.node[4] != right.node[4] && left.node[5] != right.node[5];
|
||||
}
|
||||
} // namespace Tesses::Framework
|
||||
Reference in New Issue
Block a user