Created
July 16, 2010 22:51
-
-
Save sinfu/479027 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Handle + range I/O | |
* | |
-------------------- | |
% wine dmd -run test | |
Leading char = '/' | |
Read 1024 bytes | |
Read 1024 bytes | |
Read 1024 bytes | |
Read 1024 bytes | |
Read 741 bytes | |
Done! | |
-------------------- | |
*/ | |
import std.stdio; // writeln | |
import std.utf; // toUTF16z | |
version (Windows) | |
{ | |
import std.windows.syserror; | |
import core.sys.windows.windows; | |
enum ERROR_HANDLE_EOF = 38; | |
} | |
void main() | |
{ | |
InputHandle file = new WindowsFileHandle(__FILE__, OpenMode.read); | |
scope(exit) file.close; | |
run(file.byChunk); | |
} | |
void run(InputRange!(ubyte[]) source) | |
{ | |
writeln("Leading char = '", readUnit!char(source), "'"); | |
ubyte[1024] storage = void; | |
ubyte[] inbuf; | |
while ((inbuf = rawRead(source, storage)).length > 0) | |
{ | |
writeln("Read ", inbuf.length, " bytes"); | |
} | |
writeln("Done!"); | |
} | |
/* | |
* Naive implementation | |
*/ | |
enum OpenMode | |
{ | |
read, | |
write, | |
readWrite, | |
} | |
class WindowsFileHandle : InputHandle, OutputHandle | |
{ | |
private HANDLE _handle = INVALID_HANDLE_VALUE; | |
this(string path, OpenMode mode) | |
{ | |
DWORD access; | |
DWORD action; | |
final switch (mode) | |
{ | |
case OpenMode.read: | |
access = GENERIC_READ; | |
action = OPEN_EXISTING; | |
break; | |
case OpenMode.write: | |
access = GENERIC_WRITE; | |
action = CREATE_NEW; | |
break; | |
case OpenMode.readWrite: | |
access = GENERIC_READ | GENERIC_WRITE; | |
action = CREATE_NEW; | |
break; | |
} | |
_handle = CreateFileW(path.toUTF16z(), access, 0, null, action, FILE_ATTRIBUTE_NORMAL, null); | |
if (_handle == INVALID_HANDLE_VALUE) | |
throw new Exception(sysErrorString(GetLastError())); | |
} | |
override void close() | |
{ | |
if (_handle != INVALID_HANDLE_VALUE) | |
{ | |
CloseHandle(_handle); | |
_handle = INVALID_HANDLE_VALUE; | |
} | |
} | |
override @property bool isOpen() | |
{ | |
return _handle != INVALID_HANDLE_VALUE; | |
} | |
/* | |
* byChunk | |
*/ | |
class ByChunk : InputRange!(ubyte[]) | |
{ | |
override ubyte[]* getNext(ref ubyte[] store) | |
{ | |
DWORD bytesRead; | |
BOOL success; | |
success = ReadFile(this.outer._handle, | |
store.ptr, store.length, &bytesRead, null); | |
if (success == FALSE) | |
{ | |
switch (GetLastError()) | |
{ | |
case ERROR_HANDLE_EOF: | |
break; | |
default: | |
throw new Exception(sysErrorString(GetLastError())); | |
} | |
} | |
if (bytesRead == 0) | |
return null; | |
store = store[0 .. bytesRead]; | |
return &store; | |
} | |
} | |
override @property ByChunk byChunk() | |
{ | |
return new ByChunk(); | |
} | |
/* | |
* blockWriter | |
*/ | |
class BlockWriter : OutputRange!(const(ubyte)[]) | |
{ | |
override void put(const(ubyte)[] data) | |
{ | |
while (data.length > 0) | |
{ | |
DWORD bytesWritten; | |
BOOL success; | |
success = WriteFile(this.outer._handle, | |
data.ptr, data.length, &bytesWritten, null); | |
if (success == FALSE) | |
throw new Exception(sysErrorString(GetLastError())); | |
data = data[bytesWritten .. $]; | |
} | |
} | |
} | |
override @property BlockWriter blockWriter() | |
{ | |
return new BlockWriter(); | |
} | |
} | |
//----------------------------------------------------------------------------// | |
// Range I/O utilities | |
//----------------------------------------------------------------------------// | |
/* | |
* | |
*/ | |
ubyte[] rawRead(R)(ref R source, ubyte[] buffer) | |
// if (isInputRange!(R) && is(ElementType!R == ubyte[])) | |
{ | |
if (auto p = source.getNext(buffer)) | |
return *p; | |
else | |
return null; | |
} | |
/* | |
* | |
*/ | |
E readUnit(E, R)(ref R source) | |
// if (isInputRange!(R) && is(ElementType!R == ubyte[])) | |
{ | |
ubyte[E.sizeof] store = void; | |
for (size_t used = 0; used < E.sizeof; ) | |
{ | |
ubyte[] buffer = store[used .. $]; | |
if (auto p = source.getNext(buffer)) | |
used += (*p).length; | |
else | |
throw new Exception("EOF"); | |
} | |
return *cast(E*) store.ptr; | |
} | |
//----------------------------------------------------------------------------// | |
// Handle | |
//----------------------------------------------------------------------------// | |
/* | |
* input handle | |
*/ | |
interface InputHandle | |
{ | |
void close(); | |
@property bool isOpen(); | |
@property InputRange!(ubyte[]) byChunk(); | |
} | |
interface CharacterInputHandle : InputHandle | |
{ | |
@property InputRange!dchar byChar(); | |
@property InputRange!string byLine(); | |
} | |
/* | |
* output handle | |
*/ | |
interface OutputHandle | |
{ | |
void close(); | |
@property bool isOpen(); | |
@property OutputRange!(const(ubyte)[]) blockWriter(); | |
} | |
interface TextOutputHandle : OutputHandle | |
{ | |
@property TextOutputRange textWriter(); | |
} | |
interface TextOutputRange : | |
OutputRange!(const( char)[]), | |
OutputRange!(const(wchar)[]), | |
OutputRange!(const(dchar)[]), | |
OutputRange!dchar | |
{ | |
override void put(const( char)[] str); | |
override void put(const(wchar)[] str); | |
override void put(const(dchar)[] str); | |
override void put(dchar c); | |
} | |
//----------------------------------------------------------------------------// | |
// Range | |
//----------------------------------------------------------------------------// | |
/* | |
* pure input range | |
*/ | |
interface InputRange(E) | |
{ | |
E* getNext(ref E item); | |
} | |
/* | |
* output range | |
*/ | |
interface OutputRange(E) | |
{ | |
void put(E e); | |
} | |
/* | |
* hasLength | |
*/ | |
interface HaveLength(L = size_t) | |
{ | |
@property L length(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment