Overhaul cmake configuration, add console api, fix http code that caused issues with cgi-bin

This commit is contained in:
2026-05-27 03:02:16 -05:00
parent 266ef5f830
commit 8413c67ec6
177 changed files with 20088 additions and 17948 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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
View 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

View File

@@ -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

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 &params) {
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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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