Skip to content

Commit

Permalink
NativeFile: Use the_Foundation's File for I/O
Browse files Browse the repository at this point in the history
Simplify native file access with File from the_Foundation. This will
also use native Win32 APIs on Windows.

Removed the unnecessary File::Truncate mode. One should just clear the
file by calling File::clear() when that is needed.
  • Loading branch information
skyjake committed Feb 9, 2021
1 parent 9987b4b commit cdeccdb
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 138 deletions.
1 change: 0 additions & 1 deletion doomsday/libs/core/include/de/file.h
Expand Up @@ -84,7 +84,6 @@ class DE_PUBLIC File : public filesys::Node, public IIOStream, public IObject
{
ReadOnly = 0,
Write = 0x1,
Truncate = 0x2,
DontPrune = 0x4, ///< File should never be pruned. Used for files we create ourselves.
};

Expand Down
6 changes: 0 additions & 6 deletions doomsday/libs/core/include/de/nativefile.h
Expand Up @@ -83,12 +83,6 @@ class DE_PUBLIC NativeFile : public ByteArrayFile
static NativeFile *newStandalone(const NativePath &nativePath);

protected:
/// Returns the input stream.
std::ifstream &input() const;

/// Returns the output stream.
std::ofstream &output();

/// Close any open streams.
void close();

Expand Down
1 change: 0 additions & 1 deletion doomsday/libs/core/src/filesys/directoryfeed.cpp
Expand Up @@ -258,7 +258,6 @@ void DirectoryFeed::destroyFile(const String &name)
return;
}
if (!path.destroy())
if (remove(path))
{
/// @throw RemoveError The file @a name exists but could not be removed.
throw RemoveError("DirectoryFeed::destroyFile", "Cannot remove \"" + name +
Expand Down
18 changes: 7 additions & 11 deletions doomsday/libs/core/src/filesys/file.cpp
Expand Up @@ -418,17 +418,13 @@ String File::fileListAsText(List<const File *> files)
if (!txt.isEmpty()) txt += "\n";

// Folder / Access flags / source flag / has origin feed.
String flags = Stringf("%c%c%c%c%c",
is<Folder>(f)? 'd' : '-',
f->mode().testFlag(Write)? 'w' : 'r',
f->mode().testFlag(Truncate)? 't' : '-',
f->source() != f? 'i' : '-',
f->originFeed()? 'f' : '-');

txt += flags + Stringf("%9zu %23s %s",
f->size(),
f->status().modifiedAt.asText().c_str(),
f->name().c_str());
txt += Stringf("%c%c%c%c%c",
is<Folder>(f) ? 'd' : '-',
f->mode().testFlag(Write) ? 'w' : 'r',
f->source() != f ? 'i' : '-',
f->originFeed() ? 'f' : '-');
txt += Stringf(
"%9zu %23s %s", f->size(), f->status().modifiedAt.asText().c_str(), f->name().c_str());

// Link target.
if (const LinkFile *link = maybeAs<LinkFile>(f))
Expand Down
183 changes: 65 additions & 118 deletions doomsday/libs/core/src/filesys/nativefile.cpp
@@ -1,7 +1,7 @@
/*
* The Doomsday Engine Project -- libcore
*
* Copyright © 2009-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
* Copyright © 2009-2021 Jaakko Keränen <jaakko.keranen@iki.fi>
*
* @par License
* LGPL: http://www.gnu.org/licenses/lgpl.html
Expand All @@ -22,106 +22,52 @@
#include "de/guard.h"
#include "de/math.h"

#include <the_Foundation/file.h>

namespace de {

DE_PIMPL(NativeFile)
{
/// Path of the native file in the OS file system.
NativePath nativePath;

/// Input stream.
mutable std::ifstream *in;

/// Output stream. Kept open until flush() is called.
/// (Re)opened before changing the contents of the file.
std::ofstream *out;

/// Output file should be truncated before the next write.
bool needTruncation;

NativePath nativePath; // Path of the native file in the OS file system.
iFile * file = nullptr; // NOTE: One NativeFile shouldn't be accessed by multiple
// threads simultaneously (each read/write mutexed).
Impl(Public *i)
: Base(i)
, in(nullptr)
, out(nullptr)
, needTruncation(false)
{}

~Impl()
{
DE_ASSERT(!in);
DE_ASSERT(!out);
}

std::ifstream &getInput()
{
if (!in)
{
// Reading is allowed always.
in = new std::ifstream(nativePath, std::ios::binary);
if (!*in)
{
delete in;
in = nullptr;
/// @throw InputError Opening the input stream failed.
throw InputError("NativeFile::openInput", "Failed to read " + nativePath);
}
}
return *in;
DE_ASSERT(!file);
}

std::ofstream &getOutput()
iFile *getFile()
{
using namespace std;
if (!out)
if (!file)
{
// Are we allowed to output?
self().verifyWriteAccess();

ios::openmode fileMode = ios::binary | ios::out;
if (self().mode() & Truncate)
file = new_File(nativePath.toString());
const bool isWrite = self().mode().testFlag(Write);
// Open with Append mode so that missing files will get created.
// Seek position will be updated anyway when something is written to the file.
if (!open_File(file, isWrite ? append_FileMode | readWrite_FileMode : readOnly_FileMode))
{
if (needTruncation)
iReleasePtr(&file);
if (isWrite)
{
fileMode |= ios::trunc;
needTruncation = false;
throw OutputError("NativeFile::getFile",
Stringf("Failed to write (%s)", strerror(errno)));
}
else
{
throw InputError("NativeFile::getFile", "Failed to read " + nativePath);
}
}
out = new std::ofstream{nativePath, fileMode};
if (!*out)
{
delete out;
out = nullptr;
/// @throw OutputError Opening the output stream failed.
throw OutputError("NativeFile::output",
"Failed to write " + nativePath + " (" + strerror(errno) + ")");
}
if (self().mode() & Truncate)
{
Status st = self().status();
st.size = 0;
st.modifiedAt = Time();
self().setStatus(st);
}
}
return *out;
}

void closeInput()
{
if (in)
{
delete in;
in = 0;
}
return file;
}

void closeOutput()
void closeFile()
{
if (out)
{
delete out;
out = 0;
}
iReleasePtr(&file);
}
};

Expand Down Expand Up @@ -164,17 +110,17 @@ void NativeFile::close()
DE_GUARD(this);

flush();
DE_ASSERT(!d->out);
DE_ASSERT(!d->file);

d->closeInput();
d->closeFile();
}

void NativeFile::flush()
{
DE_GUARD(this);

d->closeOutput();
DE_ASSERT(!d->out);
d->closeFile();
DE_ASSERT(!d->file);
}

const NativePath &NativeFile::nativePath() const
Expand All @@ -188,12 +134,24 @@ void NativeFile::clear()
{
DE_GUARD(this);

File::clear();
File::clear(); // checks for write access

d->closeFile();

Flags oldMode = mode();
setMode(Write | Truncate);
d->getOutput();
File::setMode(oldMode);
if (remove(d->nativePath.toString().c_str()))
{
if (errno != ENOENT)
{
throw OutputError("NativeFile::clear",
"Failed to clear " + d->nativePath +
Stringf(" (%s)", strerror(errno)));
}
}

Status st = status();
st.size = 0;
st.modifiedAt = Time();
setStatus(st);
}

NativeFile::Size NativeFile::size() const
Expand All @@ -209,41 +167,49 @@ void NativeFile::get(Offset at, Byte *values, Size count) const

if (at + count > size())
{
d->closeInput();
d->closeFile();
/// @throw IByteArray::OffsetError The region specified for reading extends
/// beyond the bounds of the file.
throw OffsetError("NativeFile::get", description() + ": cannot read past end of file " +
Stringf("(%zu[+%zu] > %zu)", at, count, size()));
}
auto &in = input();
if (in.tellg() != std::ifstream::pos_type(at)) in.seekg(at);
in.read(reinterpret_cast<char *>(values), count);

d->getFile();
if (pos_File(d->file) != at)
{
seek_File(d->file, at);
}
readData_File(d->file, count, values);

// Close the native input file after the full contents have been read.
if (at + count == size())
{
d->closeInput();
d->closeFile();
}
}

void NativeFile::set(Offset at, const Byte *values, Size count)
{
DE_GUARD(this);

auto &out = output();
if (at > size())
{
/// @throw IByteArray::OffsetError @a at specified a position beyond the
/// end of the file.
throw OffsetError("NativeFile::set", description() + ": cannot write past end of file");
}
out.seekp(at);
out.write(reinterpret_cast<const char *>(values), count);
if (out.bad())

d->getFile();
if (pos_File(d->file) != at)
{
/// @throw OutputError Failure to write to the native file.
throw OutputError("NativeFile::set", description() + ": error writing to file");
seek_File(d->file, at);
}
if (writeData_File(d->file, values, count) != count)
{
throw OutputError("NativeFile::set",
description() + Stringf(": error writing to file (%s)", strerror(errno)));
}

// Update status.
Status st = status();
st.size = max(st.size, at + count);
Expand All @@ -268,25 +234,6 @@ void NativeFile::setMode(const Flags &newMode)

close();
File::setMode(newMode);

if (newMode.testFlag(Truncate))
{
d->needTruncation = true;
}
}

std::ifstream &NativeFile::input() const
{
DE_GUARD(this);

return d->getInput();
}

std::ofstream &NativeFile::output()
{
DE_GUARD(this);

return d->getOutput();
}

} // namespace de
3 changes: 2 additions & 1 deletion doomsday/tests/test_archive/main.cpp
Expand Up @@ -73,7 +73,8 @@ int main(int argc, char **argv)
// FS::refresh() is called. createFile() doesn't interpret anything, just
// makes a plain file.
File &zip2 = app.homeFolder().replaceFile("test2.zip");
zip2.setMode(File::Write | File::Truncate);
zip2.setMode(File::Write);
zip2.clear();
ZipArchive arch;
arch.add(Path("world.txt"), content.toUtf8());
Writer(zip2) << arch;
Expand Down

0 comments on commit cdeccdb

Please sign in to comment.