Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions changelog/std-file-getAvailableDiskSpace.dd
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Added the `std.file.getAvailableDiskSpace` functionality.

$(REF getAvailableDiskSpace, std,file) receives as a parameter the path of a file or
directory in the file system, and returns the available disk space on the mounted filesystem.
If the given path is nonexistent, an exception is thrown.

---
import std.file;
ulong size = getAvailableDiskSpace(".");
assert(size > 0);
---

---
import std.file;
assertThrown(getAvailableDiskSpace("NonExistentFile"));
---

72 changes: 72 additions & 0 deletions std/file.d
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ $(TR $(TD Other) $(TD
$(LREF FileException)
$(LREF PreserveAttributes)
$(LREF SpanMode)
$(LREF getAvailableDiskSpace)
))
)

Expand Down Expand Up @@ -5232,3 +5233,74 @@ string tempDir() @trusted
myFile.write("hello");
assert(myFile.readText == "hello");
}

/**
Returns the available disk space based on a given path.
On Windows, `path` must be a directory; on Posix systems, it can be a file or directory.

Params:
path = on Windows, it must be a directory; on Posix it can be a file or directory
Returns:
Available space in bytes

Throws:
$(LREF FileException) in case of failure
*/
ulong getAvailableDiskSpace(scope const(char)[] path) @safe
{
version (Windows)
{
import core.sys.windows.winbase : GetDiskFreeSpaceExW;
import core.sys.windows.winnt : ULARGE_INTEGER;
import std.internal.cstring : tempCStringW;

ULARGE_INTEGER freeBytesAvailable;
auto err = () @trusted {
return GetDiskFreeSpaceExW(path.tempCStringW(), &freeBytesAvailable, null, null);
} ();
cenforce(err != 0, "Cannot get available disk space");

return freeBytesAvailable.QuadPart;
}
else version (Posix)
{
import std.internal.cstring : tempCString;

version (FreeBSD)
{
import core.sys.freebsd.sys.mount : statfs, statfs_t;

statfs_t stats;
auto err = () @trusted {
return statfs(path.tempCString(), &stats);
} ();
cenforce(err == 0, "Cannot get available disk space");

return stats.f_bavail * stats.f_bsize;
}
else
{
import core.sys.posix.sys.statvfs : statvfs, statvfs_t;

statvfs_t stats;
auto err = () @trusted {
return statvfs(path.tempCString(), &stats);
} ();
cenforce(err == 0, "Cannot get available disk space");

return stats.f_bavail * stats.f_frsize;
}
}
else static assert(0, "Unsupported platform");
}

///
@safe unittest
{
import std.exception : assertThrown;

auto space = getAvailableDiskSpace(".");
assert(space > 0);

assertThrown!FileException(getAvailableDiskSpace("ThisFileDoesNotExist123123"));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can do more tests here. Simple create a directory with deleteme and write files to it and check the resulting file size (if this varies on Windows, version(Posix) it).

Example:

https://dlang.org/phobos-prerelease/std_file.html#mkdir

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That'd be a bit excessive - we'd be testing the OS primitives.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on what you have running on your system, the size could vary a lot.
This may cause such a test to fail at times, despite everything running correctly.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should test that the function works for null and "" (empty strings) input. See e.g. https://issues.dlang.org/show_bug.cgi?id=14980