Files
tesses-framework/src/Filesystem/MemoryFilesystem.cpp
2025-05-10 19:52:23 -05:00

669 lines
20 KiB
C++

#include "TessesFramework/Filesystem/MemoryFilesystem.hpp"
#include <iostream>
namespace Tesses::Framework::Filesystem
{
MemoryFilesystemStream::MemoryFilesystemStream(std::shared_ptr<Tesses::Framework::Threading::Mutex> mtx, std::shared_ptr<MemoryFileData> data,bool canRead, bool canWrite, bool canSeek)
{
this->mtx = mtx;
this->data = data;
this->canRead = canRead;
this->canWrite = canWrite;
this->canSeek = canSeek;
this->pos=0;
}
size_t MemoryFilesystemStream::Read(uint8_t* buff, size_t sz)
{
if(!this->canRead) return 0;
if(this->pos >= this->data->file.size()) return 0;
size_t toRead = std::min(sz, (size_t)(this->data->file.size()-this->pos));
memcpy(buff, this->data->file.data() + this->pos, toRead);
this->pos += toRead;
return toRead;
}
size_t MemoryFilesystemStream::Write(const uint8_t* buff, size_t sz)
{
if(!this->canWrite) return 0;
if(this->canSeek)
{
if(this->pos > this->data->file.size())
{
this->data->file.resize(this->pos+sz);
}
this->data->file.insert(this->data->file.begin()+this->pos, buff, buff+sz);
this->pos+=sz;
}
else
{
this->data->file.insert(this->data->file.end(), buff, buff+sz);
}
return sz;
}
bool MemoryFilesystemStream::CanRead()
{
return canRead;
}
bool MemoryFilesystemStream::CanWrite()
{
return canWrite;
}
bool MemoryFilesystemStream::CanSeek()
{
return canSeek;
}
int64_t MemoryFilesystemStream::GetPosition()
{
if(!this->canSeek) return (int64_t)this->data->file.size();
return pos;
}
void MemoryFilesystemStream::Flush()
{
//its already flushed
}
void MemoryFilesystemStream::Seek(int64_t pos, Streams::SeekOrigin whence)
{
if(canSeek) return;
switch(whence)
{
case Streams::SeekOrigin::Begin:
this->pos = (size_t)pos;
break;
case Streams::SeekOrigin::Current:
this->pos += (size_t)pos;
break;
case Streams::SeekOrigin::End:
this->pos = (size_t)(this->data->file.size() + pos);
break;
}
}
MemoryFilesystemStream::~MemoryFilesystemStream()
{
mtx->Lock();
if(this->canWrite) this->data->canAccess=true;
this->data->readers--;
mtx->Unlock();
}
MemoryEntry* MemoryFilesystem::GetEntry(VFSPath path,bool followSymlink)
{
if(path.relative) return nullptr;
if(path.path.empty()) return &this->root;
auto entry = GetEntry(path.GetParent(),true);
if(entry == nullptr) return nullptr;
auto dir = dynamic_cast<MemoryDirectory*>(entry);
if(dir != nullptr)
{
for(auto item : dir->entries)
{
if(item->name == path.GetFileName())
{
auto link = dynamic_cast<MemorySymlink*>(item);
if(followSymlink && link != nullptr)
{
item = GetEntry(link->linkedTo,true);
}
return item;
}
}
}
return nullptr;
}
Streams::Stream* MemoryFilesystem::OpenFile(VFSPath path, std::string mode)
{
bool canRead=false;
bool canWrite=false;
bool canSeek=true;
bool mustExist=false;
bool truncate=false;
if(mode.size() >= 1)
{
if(mode[0] == 'r')
{
canRead = true;
mustExist=true;
}
else if(mode[0] == 'w')
{
canWrite = true;
truncate=true;
}
else if(mode[0] == 'a')
{
canSeek = false;
canWrite = true;
}
}
if(((mode.size() >= 2 && mode[1] == '+') || (mode.size() >= 2 && mode[1] == 'b' && mode[2] == '+')))
{
canRead = true;
canWrite = true;
}
mtx->Lock();
if(mustExist)
{
auto file = GetEntry(path,true);
if(file == nullptr)
{
mtx->Unlock();
return nullptr;
}
auto f = dynamic_cast<MemoryFile*>(file);
if(f == nullptr)
{
mtx->Unlock();
return nullptr;
}
if(!f->data->canAccess)
{
mtx->Unlock();
return nullptr;
}
if(canWrite && f->data->readers > 0)
{
mtx->Unlock();
return nullptr;
}
f->data->readers++;
if(canWrite) f->data->canAccess=false;
mtx->Unlock();
return new MemoryFilesystemStream(mtx,f->data,canRead,canWrite,canSeek);
}
else
{
auto file = dynamic_cast<MemoryFile*>(GetEntry(path,true));
if(file != nullptr)
{
if(!file->data->canAccess)
{
mtx->Unlock();
return nullptr;
}
if(file->data->readers > 0)
{
mtx->Unlock();
return nullptr;
}
file->data->canAccess=false;
file->data->readers++;
if(truncate) {file->data->file.clear(); file->data->lastWrite=Date::DateTime::NowUTC();}
mtx->Unlock();
return new MemoryFilesystemStream(mtx,file->data,canRead,canWrite,canSeek);
}
auto dir = GetEntry(path.GetParent(),true);
if(dir == nullptr)
{
mtx->Unlock();
return nullptr;
}
auto myDir = dynamic_cast<MemoryDirectory*>(dir);
if(myDir == nullptr)
{
mtx->Unlock();
return nullptr;
}
MemoryFile* thefile=nullptr;
for(auto f : myDir->entries)
{
if(f->name == path.GetFileName())
{
auto symlink = dynamic_cast<MemorySymlink*>(f);
while(symlink != nullptr)
{
auto ent = GetEntry(symlink->name,false);
auto sym = dynamic_cast<MemorySymlink*>(f);
if(sym != nullptr)
symlink = sym;
else
{
auto myDir0 = dynamic_cast<MemoryDirectory*>(GetEntry(symlink->linkedTo.GetParent(),true));
if(myDir0 != nullptr)
{
for(auto f2 : myDir0->entries)
{
if(f2->name == symlink->linkedTo.GetFileName())
{
auto myFile = dynamic_cast<MemoryFile*>(f2);
if(myFile != nullptr)
{
thefile = myFile;
break;
}
else
{
mtx->Unlock();
return nullptr;
}
}
}
myDir = myDir0;
}
else
{
mtx->Unlock();
return nullptr;
}
break;
}
}
break;
}
}
if(thefile == nullptr)
{
MemoryFile* f = new MemoryFile();
f->name = path.GetFileName();
f->data = std::make_shared<MemoryFileData>();
f->data->canAccess=false;
f->data->readers++;
myDir->entries.push_back(f);
mtx->Unlock();
return new MemoryFilesystemStream(mtx,f->data,canRead,canWrite,canSeek);
}
if(thefile != nullptr)
{
if(!thefile->data->canAccess)
{
mtx->Unlock();
return nullptr;
}
if(thefile->data->readers > 0)
{
mtx->Unlock();
return nullptr;
}
thefile->data->canAccess=false;
thefile->data->readers++;
if(truncate) {thefile->data->file.clear(); thefile->data->lastWrite=Date::DateTime::NowUTC();}
mtx->Unlock();
return new MemoryFilesystemStream(mtx,thefile->data,canRead,canWrite,canSeek);
}
}
mtx->Unlock();
return nullptr;
}
void MemoryFilesystem::CreateDirectory(VFSPath path)
{
if(path.relative) return;
if(path.path.empty()) return;
mtx->Lock();
MemoryDirectory* dir=&root;
for(auto part : path.path)
{
bool have=false;
for(auto dirent : dir->entries)
{
if(dirent->name == part)
{
auto symlink = dynamic_cast<MemorySymlink*>(dirent);
if(symlink != nullptr)
{
dirent = GetEntry(symlink->linkedTo,true);
}
auto dirdirent = dynamic_cast<MemoryDirectory*>(dirent);
if(dirdirent != nullptr)
{
dir = dirdirent;
have=true;
break;
}
else
{
mtx->Unlock();
return;
}
}
}
if(!have)
{
MemoryDirectory* dir2 = new MemoryDirectory();
dir2->name = part;
dir2->lastWrite=Date::DateTime::NowUTC();
dir->entries.push_back(dir2);
dir->lastWrite=Date::DateTime::NowUTC();
dir=dir2;
}
}
mtx->Unlock();
}
void MemoryFilesystem::DeleteFile(VFSPath path)
{
if(path.relative || path.path.empty()) return;
mtx->Lock();
MemoryDirectory* dir=&root;
if(path.path.size() > 1)
{
dir = dynamic_cast<MemoryDirectory*>(GetEntry(path.GetParent(),true));
}
if(dir == nullptr)
{
mtx->Unlock();
return;
}
std::string fname = path.GetFileName();
for(auto index = dir->entries.begin(); index < dir->entries.end(); index++)
{
auto item = *index;
if(item->name == fname)
{
auto p = dynamic_cast<MemoryDirectory*>(item);
if(p == nullptr)
{
delete item;
dir->entries.erase(index);
dir->lastWrite=Date::DateTime::NowUTC();
}
break;
}
}
mtx->Unlock();
}
bool MemoryFilesystem::RegularFileExists(VFSPath path)
{
if(path.relative) return false;
if(path.path.empty()) return false;
mtx->Lock();
auto f = GetEntry(path,false);
mtx->Unlock();
return dynamic_cast<MemoryFile*>(f) != nullptr;
}
bool MemoryFilesystem::SymlinkExists(VFSPath path)
{
if(path.relative) return false;
if(path.path.empty()) return false;
mtx->Lock();
auto f = GetEntry(path,false);
mtx->Unlock();
return dynamic_cast<MemorySymlink*>(f) != nullptr;
}
bool MemoryFilesystem::DirectoryExists(VFSPath path)
{
if(path.relative) return false;
if(path.path.empty()) return true;
mtx->Lock();
auto f = GetEntry(path,false);
mtx->Unlock();
return dynamic_cast<MemoryDirectory*>(f) != nullptr;
}
void MemoryFilesystem::DeleteDirectory(VFSPath path)
{
if(path.relative || path.path.empty()) return;
mtx->Lock();
MemoryDirectory* dir=&root;
if(path.path.size() > 1)
{
dir = dynamic_cast<MemoryDirectory*>(GetEntry(path.GetParent(),true));
}
if(dir == nullptr)
{
mtx->Unlock();
return;
}
std::string fname = path.GetFileName();
for(auto index = dir->entries.begin(); index < dir->entries.end(); index++)
{
auto item = *index;
if(item->name == fname)
{
auto p = dynamic_cast<MemoryDirectory*>(item);
if(p != nullptr)
{
delete item;
dir->entries.erase(index);
dir->lastWrite=Date::DateTime::NowUTC();
}
break;
}
}
mtx->Unlock();
}
void MemoryFilesystem::CreateSymlink(VFSPath existingFile, VFSPath symlinkFile)
{
if(existingFile.relative || symlinkFile.relative || symlinkFile.path.empty()) return;
mtx->Lock();
MemoryDirectory* dir=&root;
if(symlinkFile.path.size() > 1)
{
dir = dynamic_cast<MemoryDirectory*>(GetEntry(symlinkFile.GetParent(),true));
}
if(dir == nullptr)
{
mtx->Unlock();
return;
}
std::string fname = symlinkFile.GetFileName();
for(auto index = dir->entries.begin(); index < dir->entries.end(); index++)
{
auto item = *index;
if(item->name == fname)
{
auto p = dynamic_cast<MemorySymlink*>(item);
if(p != nullptr)
{
p->linkedTo = existingFile;
p->lastWrite = Date::DateTime::NowUTC();
}
mtx->Unlock();
return;
}
}
MemorySymlink* symlink = new MemorySymlink();
symlink->name = fname;
symlink->linkedTo = existingFile;
symlink->lastWrite = Date::DateTime::NowUTC();
dir->entries.push_back(symlink);
dir->lastWrite = Date::DateTime::NowUTC();
mtx->Unlock();
}
VFSPathEnumerator MemoryFilesystem::EnumeratePaths(VFSPath path)
{
std::pair<size_t,std::vector<std::string>>* paths=new std::pair<size_t,std::vector<std::string>>();
paths->first=0;
mtx->Lock();
auto dir = dynamic_cast<MemoryDirectory*>(GetEntry(path,true));
if(dir != nullptr)
{
for(auto item : dir->entries) paths->second.push_back(item->name);
}
mtx->Unlock();
return VFSPathEnumerator([paths,path](VFSPath& _path)->bool{
if(paths->first < paths->second.size())
{
_path = path / paths->second[paths->first++];
return true;
}
return false;
},[paths]()->void{
delete paths;
});
}
void MemoryFilesystem::CreateHardlink(VFSPath existingFile, VFSPath newName)
{
mtx->Lock();
auto existing = dynamic_cast<MemoryFile*>(GetEntry(existingFile,true));
if(existing == nullptr)
{
mtx->Unlock();
return;
}
MemoryDirectory* dir=&root;
if(newName.path.size() > 1)
{
dir = dynamic_cast<MemoryDirectory*>(GetEntry(newName.GetParent(),true));
}
if(dir == nullptr)
{
mtx->Unlock();
return;
}
std::string fname = newName.GetFileName();
for(auto index = dir->entries.begin(); index < dir->entries.end(); index++)
{
auto item = *index;
if(item->name == fname)
{
mtx->Unlock();
return;
}
}
MemoryFile* memFile = new MemoryFile();
memFile->name = fname;
memFile->data = existing->data;
dir->entries.push_back(memFile);
dir->lastWrite=Date::DateTime::NowUTC();
mtx->Unlock();
}
void MemoryFilesystem::MoveFile(VFSPath src, VFSPath dest)
{
DeleteFile(dest);
CreateHardlink(src,dest);
DeleteFile(src);
}
void MemoryFilesystem::MoveDirectory(VFSPath src, VFSPath dest)
{
CreateDirectory(dest);
for(auto ent : EnumeratePaths(src))
{
VFSPath destPath = dest / ent.GetFileName();
if(FileExists(ent)) MoveFile(ent,destPath);
if(DirectoryExists(ent)) MoveDirectory(ent,destPath);
}
DeleteDirectory(src);
}
VFSPath MemoryFilesystem::ReadLink(VFSPath p)
{
mtx->Lock();
VFSPath p2;
auto symlink = dynamic_cast<MemorySymlink*>(GetEntry(p,false));
if(symlink != nullptr)
{
p2 = symlink->linkedTo;
}
mtx->Unlock();
return p2;
}
std::string MemoryFilesystem::VFSPathToSystem(VFSPath path)
{
return path.ToString();
}
VFSPath MemoryFilesystem::SystemToVFSPath(std::string path)
{
return path;
}
void MemoryFilesystem::GetDate(VFSPath path, Date::DateTime& lastWrite, Date::DateTime& lastAccess)
{
mtx->Lock();
auto node = GetEntry(path,false);
auto dir = dynamic_cast<MemoryDirectory*>(node);
auto file = dynamic_cast<MemoryFile*>(node);
auto sym = dynamic_cast<MemorySymlink*>(node);
if(dir != nullptr) lastWrite = dir->lastWrite;
if(file != nullptr) lastWrite = file->data->lastWrite;
if(sym != nullptr) lastWrite = sym->lastWrite;
mtx->Unlock();
lastAccess = lastWrite;
}
void MemoryFilesystem::SetDate(VFSPath path, Date::DateTime lastWrite, Date::DateTime lastAccess)
{
mtx->Lock();
auto node = GetEntry(path,false);
auto dir = dynamic_cast<MemoryDirectory*>(node);
auto file = dynamic_cast<MemoryFile*>(node);
auto sym = dynamic_cast<MemorySymlink*>(node);
if(dir != nullptr) dir->lastWrite = lastWrite;
if(file != nullptr) file->data->lastWrite = lastWrite;
if(sym != nullptr) sym->lastWrite = lastWrite;
mtx->Unlock();
}
MemoryFilesystem::~MemoryFilesystem()
{
}
MemoryFilesystem::MemoryFilesystem()
{
mtx = std::make_shared<Threading::Mutex>();
}
MemoryEntry::~MemoryEntry()
{
}
MemoryFile::~MemoryFile()
{
}
MemoryDirectory::MemoryDirectory()
{
this->lastWrite = Date::DateTime::NowUTC();
}
MemoryDirectory::~MemoryDirectory()
{
for(auto item : this->entries) delete item;
}
MemoryFileData::MemoryFileData()
{
this->lastWrite = Date::DateTime::NowUTC();
this->canAccess=true;
this->readers=0;
}
}