123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802 |
|
/*******************************************************************************
copyright: Copyright (c) 2004 Kris Bell. All rights reserved
license: BSD style: $(LICENSE)
version: Mar 2004: Initial release$(BR)
Dec 2006: Outback release$(BR)
Nov 2008: relocated and simplified
authors: Kris,
John Reimer,
Anders F Bjorklund (Darwin patches),
Chris Sauls (Win95 file support)
*******************************************************************************/
module tango.io.device.File;
private import tango.sys.Common;
private import tango.io.device.Device;
private import stdc = tango.stdc.stringz;
private import tango.core.Octal;
/*******************************************************************************
platform-specific functions
*******************************************************************************/
version (Win32)
private import Utf = tango.text.convert.Utf;
else
private import tango.stdc.posix.unistd;
/*******************************************************************************
Implements a means of reading and writing a generic file. Conduits
are the primary means of accessing external data, and File
extends the basic pattern by providing file-specific methods to
set the file size, seek to a specific file position and so on.
Serial input and output is straightforward. In this example we
copy a file directly to the console:
---
// open a file for reading
auto from = new File ("test.txt");
// stream directly to console
Stdout.copy (from);
---
And here we copy one file to another:
---
// open file for reading
auto from = new File ("test.txt");
// open another for writing
auto to = new File ("copy.txt", File.WriteCreate);
// copy file and close
to.copy.close;
from.close;
---
You can use InputStream.load() to load a file directly into memory:
---
auto file = new File ("test.txt");
auto content = file.load;
file.close;
---
Or use a convenience static function within File:
---
auto content = File.get ("test.txt");
---
A more explicit version with a similar result would be:
---
// open file for reading
auto file = new File ("test.txt");
// create an array to house the entire file
auto content = new char [file.length];
// read the file content. Return value is the number of bytes read
auto bytes = file.read (content);
file.close;
---
Conversely, one may write directly to a File like so:
---
// open file for writing
auto to = new File ("text.txt", File.WriteCreate);
// write an array of content to it
auto bytes = to.write (content);
---
There are equivalent static functions, File.set() and
File.append(), which set or append file content respectively.
File can happily handle random I/O. Here we use seek() to
relocate the file pointer:
---
// open a file for reading and writing
auto file = new File ("random.bin", File.ReadWriteCreate);
// write some data
file.write ("testing");
// rewind to file start
file.seek (0);
// read data back again
char[10] tmp;
auto bytes = file.read (tmp);
file.close;
---
Note that File is unbuffered by default - wrap an instance within
tango.io.stream.Buffered for buffered I/O.
Compile with -version=Win32SansUnicode to enable Win95 & Win32s file
support.
*******************************************************************************/
class File : Device, Device.Seek, Device.Truncate
{
public alias Device.read read;
public alias Device.write write;
/***********************************************************************
Fits into 32 bits ...
***********************************************************************/
align(1) struct Style
{
Access access; /// Access rights.
Open open; /// How to open.
Share share; /// How to share.
Cache cache; /// How to cache.
}
/***********************************************************************
***********************************************************************/
enum Access : ubyte {
Read = 0x01, /// Is readable.
Write = 0x02, /// Is writable.
ReadWrite = 0x03, /// Both.
}
/***********************************************************************
***********************************************************************/
enum Open : ubyte {
Exists=0, /// Must exist.
Create, /// Create or truncate.
Sedate, /// Create if necessary.
Append, /// Create if necessary.
New, /// Can't exist.
};
/***********************************************************************
***********************************************************************/
enum Share : ubyte {
None=0, /// No sharing.
Read, /// Shared reading.
ReadWrite, /// Open for anything.
};
/***********************************************************************
***********************************************************************/
enum Cache : ubyte {
None = 0x00, /// Don't optimize.
Random = 0x01, /// Optimize for random.
Stream = 0x02, /// Optimize for stream.
WriteThru = 0x04, /// Backing-cache flag.
};
/***********************************************************************
Read an existing file.
***********************************************************************/
enum Style ReadExisting = {Access.Read, Open.Exists};
/***********************************************************************
Read an existing file.
***********************************************************************/
enum Style ReadShared = {Access.Read, Open.Exists, Share.Read};
/***********************************************************************
Write on an existing file. Do not create.
***********************************************************************/
enum Style WriteExisting = {Access.Write, Open.Exists};
/***********************************************************************
Write on a clean file. Create if necessary.
***********************************************************************/
enum Style WriteCreate = {Access.Write, Open.Create};
/***********************************************************************
Write at the end of the file.
***********************************************************************/
enum Style WriteAppending = {Access.Write, Open.Append};
/***********************************************************************
Read and write an existing file.
***********************************************************************/
enum Style ReadWriteExisting = {Access.ReadWrite, Open.Exists};
/***********************************************************************
Read & write on a clean file. Create if necessary.
***********************************************************************/
enum Style ReadWriteCreate = {Access.ReadWrite, Open.Create};
/***********************************************************************
Read and Write. Use existing file if present.
***********************************************************************/
enum Style ReadWriteOpen = {Access.ReadWrite, Open.Sedate};
// the file we're working with
private const(char)[] path_;
// the style we're opened with
private Style style_;
/***********************************************************************
Create a File for use with open().
Note that File is unbuffered by default - wrap an instance
within tango.io.stream.Buffered for buffered I/O.
***********************************************************************/
this ()
{
}
/***********************************************************************
Create a File with the provided path and style.
Note that File is unbuffered by default - wrap an instance
within tango.io.stream.Buffered for buffered I/O.
***********************************************************************/
this (const(char[]) path, Style style = ReadExisting)
{
open (path, style);
}
/***********************************************************************
Return the Style used for this file.
***********************************************************************/
@property const Style style ()
{
return style_;
}
/***********************************************************************
Return the path used by this file.
***********************************************************************/
override immutable(char)[] toString ()
{
return path_.idup;
}
/***********************************************************************
Convenience function to return the content of a file.
Returns a slice of the provided output buffer, where
that has sufficient capacity, and allocates from the
heap where the file content is larger.
Content size is determined via the file-system, per
File.length, although that may be misleading for some
*nix systems. An alternative is to use File.load which
loads content until an Eof is encountered.
***********************************************************************/
static void[] get (const(char)[] path, void[] dst = null)
{
scope file = new File (path);
// allocate enough space for the entire file
auto len = cast(size_t) file.length;
if (dst.length < len){
if (dst is null){ // avoid setting the noscan attribute, one should maybe change the return type
dst=new ubyte[](len);
} else {
dst.length = len;
}
}
//read the content
len = file.read (dst);
if (len is file.Eof)
file.error ("File.read :: unexpected eof");
return dst [0 .. len];
}
/***********************************************************************
Convenience function to set file content and length to
reflect the given array.
***********************************************************************/
static void set (const(char)[] path, const(void)[] content)
{
scope file = new File (path, ReadWriteCreate);
file.write (content);
}
/***********************************************************************
Convenience function to append content to a file.
***********************************************************************/
static void append (const(char)[] path, const(void)[] content)
{
scope file = new File (path, WriteAppending);
file.write (content);
}
/***********************************************************************
Windows-specific code
***********************************************************************/
version(Win32)
{
/***************************************************************
Low level open for sub-classes that need to apply specific
attributes.
Return: False in case of failure.
***************************************************************/
protected bool open (const(char)[] path, Style style, DWORD addattr)
{
DWORD attr,
share,
access,
create;
alias DWORD[] Flags;
static const Flags Access =
[
0, // invalid
GENERIC_READ,
GENERIC_WRITE,
GENERIC_READ | GENERIC_WRITE,
];
static const Flags Create =
[
OPEN_EXISTING, // must exist
CREATE_ALWAYS, // truncate always
OPEN_ALWAYS, // create if needed
OPEN_ALWAYS, // (for appending)
CREATE_NEW // can't exist
];
static const Flags Share =
[
0,
FILE_SHARE_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
];
static const Flags Attr =
[
0,
FILE_FLAG_RANDOM_ACCESS,
FILE_FLAG_SEQUENTIAL_SCAN,
0,
FILE_FLAG_WRITE_THROUGH,
];
// remember our settings
assert(path);
path_ = path;
style_ = style;
attr = Attr[style.cache] | addattr;
share = Share[style.share];
create = Create[style.open];
access = Access[style.access];
version(TangoRuntime)
if (scheduler)
attr |= FILE_FLAG_OVERLAPPED;// + FILE_FLAG_NO_BUFFERING;
// zero terminate the path
char[512] zero = void;
auto name = stdc.toStringz (path, zero);
version (Win32SansUnicode)
io.handle = CreateFileA (name, access, share,
null, create,
attr | FILE_ATTRIBUTE_NORMAL,
null);
else
{
// convert to utf16
wchar[512] convert = void;
auto wide = Utf.toString16 (name[0..path.length+1], convert);
// open the file
io.handle = CreateFileW (wide.ptr, access, share,
null, create,
attr | FILE_ATTRIBUTE_NORMAL,
null);
}
if (io.handle is INVALID_HANDLE_VALUE)
return false;
// reset extended error
SetLastError (ERROR_SUCCESS);
// move to end of file?
if (style.open is Open.Append)
*(cast(long*) &io.asynch.Offset) = -1;
else
io.track = true;
// monitor this handle for async I/O?
version(TangoRuntime)
if (scheduler)
scheduler.open (io.handle, toString);
return true;
}
/***************************************************************
Open a file with the provided style.
***************************************************************/
void open (const(char[]) path, Style style = ReadExisting)
{
if (! open (path, style, 0))
error();
}
/***************************************************************
Set the file size to be that of the current seek
position. The file must be writable for this to
succeed.
***************************************************************/
void truncate ()
{
truncate (position);
}
/***************************************************************
Set the file size to be the specified length. The
file must be writable for this to succeed.
***************************************************************/
override void truncate (long size)
{
auto s = seek (size);
assert (s is size);
// must have Generic_Write access
if (! SetEndOfFile (io.handle))
error();
}
/***************************************************************
Set the file seek position to the specified offset
from the given anchor.
***************************************************************/
override long seek (long offset, Anchor anchor = Anchor.Begin)
{
long newOffset;
// hack to ensure overlapped.Offset and file location
// are correctly in synch ...
if (anchor is Anchor.Current)
SetFilePointerEx (io.handle,
*cast(LARGE_INTEGER*) &io.asynch.Offset,
cast(PLARGE_INTEGER) &newOffset, 0);
if (! SetFilePointerEx (io.handle, *cast(LARGE_INTEGER*)
&offset, cast(PLARGE_INTEGER)
&newOffset, anchor))
error();
return (*cast(long*) &io.asynch.Offset) = newOffset;
}
/***************************************************************
Return the current file position.
***************************************************************/
@property long position ()
{
return *cast(long*) &io.asynch.Offset;
}
/***************************************************************
Return the total length of this file.
***************************************************************/
@property long length ()
{
long len;
if (! GetFileSizeEx (io.handle, cast(PLARGE_INTEGER) &len))
error();
return len;
}
/***************************************************************
Instructs the OS to flush it's internal buffers to
the disk device.
NOTE: Due to OS and hardware design, data flushed
cannot be guaranteed to be actually on disk-platters.
Actual durability of data depends on write-caches,
barriers, presence of battery-backup, filesystem and
OS-support.
***************************************************************/
void sync ()
{
if (! FlushFileBuffers (io.handle))
error();
}
}
/***********************************************************************
Unix-specific code. Note that some methods are 32bit only.
***********************************************************************/
version (Posix)
{
/***************************************************************
Low level open for sub-classes that need to apply specific
attributes.
Return:
False in case of failure.
***************************************************************/
protected bool open (const(char[]) path, Style style,
int addflags, int access = octal!(666))
{
alias int[] Flags;
enum O_LARGEFILE = 0x8000;
enum Flags Access =
[
0, // invalid
O_RDONLY,
O_WRONLY,
O_RDWR,
];
enum Flags Create =
[
0, // open existing
O_CREAT | O_TRUNC, // truncate always
O_CREAT, // create if needed
O_APPEND | O_CREAT, // append
O_CREAT | O_EXCL, // can't exist
];
enum short[] Locks =
[
F_WRLCK, // no sharing
F_RDLCK, // shared read
];
// remember our settings
assert(path);
path_ = path;
style_ = style;
// zero terminate and convert to utf16
char[512] zero = void;
auto name = stdc.toStringz (path, zero);
auto mode = Access[style.access] | Create[style.open];
// always open as a large file
handle = posix.open (name, mode | O_LARGEFILE | addflags,
access);
if (handle is -1)
return false;
return true;
}
/***************************************************************
Open a file with the provided style.
Note that files default to no-sharing. That is,
they are locked exclusively to the host process
unless otherwise stipulated. We do this in order
to expose the same default behaviour as Win32.
$(B No file locking for borked POSIX.)
***************************************************************/
void open (const(char[]) path, Style style = ReadExisting)
{
if (! open (path, style, 0))
error();
}
/***************************************************************
Set the file size to be that of the current seek
position. The file must be writable for this to
succeed.
***************************************************************/
void truncate ()
{
truncate (position);
}
/***************************************************************
Set the file size to be the specified length. The
file must be writable for this to succeed.
***************************************************************/
override void truncate (long size)
{
// set filesize to be current seek-position
if (posix.ftruncate (handle, cast(off_t) size) is -1)
error();
}
/***************************************************************
Set the file seek position to the specified offset
from the given anchor.
***************************************************************/
override long seek (long offset, Anchor anchor = Anchor.Begin)
{
long result = posix.lseek (handle, cast(off_t) offset, anchor);
if (result is -1)
error();
return result;
}
/***************************************************************
Return the current file position.
***************************************************************/
@property long position ()
{
return seek (0, Anchor.Current);
}
/***************************************************************
Return the total length of this file.
***************************************************************/
@property long length ()
{
stat_t stats = void;
if (posix.fstat (handle, &stats))
error();
return cast(long) stats.st_size;
}
/***************************************************************
Instructs the OS to flush it's internal buffers to
the disk device.
NOTE: due to OS and hardware design, data flushed
cannot be guaranteed to be actually on disk-platters.
Actual durability of data depends on write-caches,
barriers, presence of battery-backup, filesystem and
OS-support.
***************************************************************/
void sync ()
{
if (fsync (handle))
error();
}
}
}
debug (File)
{
import tango.io.Stdout;
void main()
{
char[10] ff;
auto file = new File("file.d");
auto content = cast(char[]) file.load (file);
assert (content.length is file.length);
assert (file.read(ff) is file.Eof);
assert (file.position is content.length);
file.seek (0);
assert (file.position is 0);
assert (file.read(ff) is 10);
assert (file.position is 10);
assert (file.seek(0, file.Anchor.Current) is 10);
assert (file.seek(0, file.Anchor.Current) is 10);
}
}
|