2023-10-11 21:50:35 +00:00
|
|
|
///
|
|
|
|
/// \file Buffer.cc
|
|
|
|
/// \author kyle
|
|
|
|
/// \created 10/10/23
|
|
|
|
/// \brief Buffer implementation.
|
|
|
|
///
|
|
|
|
/// \section COPYRIGHT
|
|
|
|
/// Copyright 2023 K. Isom <kyle@imap.cc>
|
|
|
|
///
|
|
|
|
/// Permission to use, copy, modify, and/or distribute this software for
|
|
|
|
/// any purpose with or without fee is hereby granted, provided that the
|
|
|
|
/// above copyright notice and this permission notice appear in all copies.
|
|
|
|
///
|
|
|
|
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
|
|
|
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
|
|
|
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
|
|
|
|
/// BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
|
|
|
|
/// OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
|
|
|
/// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
|
|
|
/// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
|
|
|
/// SOFTWARE.
|
|
|
|
///
|
|
|
|
|
|
|
|
|
|
|
|
#include <optional>
|
|
|
|
#include <random>
|
|
|
|
#include <sstream>
|
2023-10-12 06:27:42 +00:00
|
|
|
#include <iterator>
|
2023-10-11 21:50:35 +00:00
|
|
|
|
|
|
|
#include "Buffer.h"
|
|
|
|
|
|
|
|
|
|
|
|
static std::string
|
|
|
|
anonymousName()
|
|
|
|
{
|
|
|
|
std::uniform_int_distribution<> dist(1000, 9999);
|
2023-10-12 06:27:42 +00:00
|
|
|
std::random_device randomDevice;
|
|
|
|
std::mt19937 rng(randomDevice());
|
|
|
|
std::stringstream ss;
|
2023-10-11 21:50:35 +00:00
|
|
|
|
|
|
|
ss << "Buffer<" << dist(rng) << ">";
|
|
|
|
return ss.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Buffer::Buffer()
|
2023-10-12 06:27:42 +00:00
|
|
|
: dirty(false), name(anonymousName())
|
2023-10-11 21:50:35 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Buffer::Buffer(std::string fName)
|
2023-10-12 06:27:42 +00:00
|
|
|
: dirty(false), name(std::move(fName))
|
2023-10-11 21:50:35 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Buffer::Buffer(std::string fName, std::string fPath)
|
2023-10-12 06:27:42 +00:00
|
|
|
: dirty(false), name(std::move(fName)), path(fPath)
|
2023-10-11 21:50:35 +00:00
|
|
|
{
|
|
|
|
if (this->path) {
|
2023-10-12 06:27:42 +00:00
|
|
|
this->file = OptFile (File(fPath));
|
2023-10-11 21:50:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-10-12 06:27:42 +00:00
|
|
|
Buffer::FileStatus
|
|
|
|
Buffer::Flush(OptString altPath)
|
2023-10-11 21:50:35 +00:00
|
|
|
{
|
2023-10-12 06:27:42 +00:00
|
|
|
OptOutFileStream handle = std::nullopt;
|
|
|
|
|
|
|
|
if (altPath) {
|
|
|
|
auto altFile = File(altPath.value());
|
|
|
|
handle = altFile.Flush();
|
|
|
|
} else if (this->file) {
|
|
|
|
handle = this->file.value().Flush();
|
|
|
|
} else {
|
|
|
|
// At this point, we have no alternate path to write out
|
|
|
|
// and this Buffer isn't file-backed. From this point
|
|
|
|
// forward, we can operate under the assumption that
|
|
|
|
// `this->file` is a valid file.
|
|
|
|
return Buffer::FileStatus::FileStatusVirtual;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto realFile = this->file.value();
|
|
|
|
if (!handle) {
|
|
|
|
if (realFile.IsReadOnly()) {
|
|
|
|
return Buffer::FileStatus::FileStatusReadOnly;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!realFile.IsWriteable()) {
|
|
|
|
return Buffer::FileStatus::FileStatusInvalidPermissions;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Buffer::FileStatus::FileStatusIOFailed;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// At this point, we know we're working with a valid file handle.
|
|
|
|
auto realHandle = handle.value();
|
|
|
|
if (!realHandle->good()) {
|
|
|
|
return Buffer::FileStatus::FileStatusIOFailed;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto line : this->contents) {
|
|
|
|
std::copy(line.begin(), line.end(),
|
|
|
|
std::ostream_iterator<uint8_t>(*realHandle, ""));
|
|
|
|
}
|
|
|
|
|
|
|
|
realHandle->flush();
|
|
|
|
realHandle->close();
|
|
|
|
delete realHandle;
|
|
|
|
this->dirty = false;
|
|
|
|
return Buffer::FileStatus::FileStatusOK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Buffer::FileStatus
|
|
|
|
Buffer::Refresh()
|
|
|
|
{
|
|
|
|
if (this->IsVirtual()) {
|
|
|
|
return Buffer::FileStatus::FileStatusVirtual;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto realFile = this->file.value();
|
|
|
|
auto handle = realFile.Refresh();
|
|
|
|
if (!handle) {
|
|
|
|
return FileStatus::FileStatusNonExistent;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto realHandle = handle.value();
|
|
|
|
this->clearContents();
|
|
|
|
this->dirty = false; // clean slate, kiddo
|
|
|
|
|
|
|
|
/// \todo Moral quandary: if the file disappears from disk, we
|
|
|
|
/// probably want to know that, but maybe clearing the
|
|
|
|
/// contents first isn't the right move.
|
|
|
|
if (!realHandle->good()) {
|
|
|
|
delete realHandle;
|
|
|
|
return FileStatus::FileStatusIOFailed;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t currentLine = 0;
|
|
|
|
|
|
|
|
while (realHandle->good()) {
|
|
|
|
}
|
2023-10-11 21:50:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-10-12 06:27:42 +00:00
|
|
|
|
|
|
|
void
|
|
|
|
Buffer::ChangePath(std::string newPath)
|
2023-10-11 21:50:35 +00:00
|
|
|
{
|
|
|
|
this->path = OptString(std::move(newPath));
|
|
|
|
}
|
2023-10-12 06:27:42 +00:00
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
Buffer::clearContents()
|
|
|
|
{
|
|
|
|
for (auto & line : this->contents) {
|
|
|
|
line.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
this->contents.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
Buffer::MarkDirty()
|
|
|
|
{
|
|
|
|
if (this->dirty) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this->dirty = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|