1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
|
module bio.core.utils.stream;
public import contrib.undead.stream;
import core.stdc.stdio;
import core.stdc.errno;
import core.stdc.string;
import core.sys.posix.sys.select;
import std.conv;
import std.string : toStringz;
version(Posix){
private import core.sys.posix.unistd;
}
version(Windows) {
private import std.file;
private import std.utf;
private import core.stdc.windows.windows;
extern (Windows) {
DWORD GetFileType(HANDLE hFile);
}
}
FileMode toFileMode(string mode) {
FileMode result = FileMode.In;
switch (mode) {
case "r", "rb":
result = FileMode.In; // 1000
break;
case "r+", "r+b", "rb+":
result = FileMode.In | FileMode.Out; // 1100
case "w", "wb":
result = FileMode.OutNew; // 0110
break;
case "w+", "w+b", "wb+":
result = FileMode.In | FileMode.OutNew; // 1110
break;
case "a", "ab":
result = FileMode.Append; // 0001
break;
case "a+", "a+b", "ab+":
result = FileMode.In | FileMode.Append; // 1001
break;
default:
break;
}
return result;
}
final class File: contrib.undead.stream.File {
this(string filename, string mode="rb") {
version (Posix) {
// Issue 8528 workaround
auto file = fopen(toStringz(filename), toStringz(mode));
if (file == null) {
throw new OpenException(cast(string) ("Cannot open or create file '"
~ filename ~ "' : ") ~
to!string(strerror(errno)));
}
super(core.stdc.stdio.fileno(file), toFileMode(mode));
}
version (Windows) {
int access, share, createMode;
auto mode_flags = toFileMode(mode);
share |= FILE_SHARE_READ | FILE_SHARE_WRITE;
if (mode_flags & FileMode.In) {
access |= GENERIC_READ;
createMode = OPEN_EXISTING;
}
if (mode_flags & FileMode.Out) {
access |= GENERIC_WRITE;
createMode = OPEN_ALWAYS;
}
if ((mode_flags & FileMode.OutNew) == FileMode.OutNew) {
createMode = CREATE_ALWAYS;
}
auto handle = CreateFileW(std.utf.toUTF16z(filename), access, share,
null, createMode, 0, null);
isopen = handle != INVALID_HANDLE_VALUE;
if (!isopen) {
throw new OpenException(cast(string) ("Cannot open or create file '"
~ filename ~ "'"));
}
super(handle, toFileMode(mode));
}
}
override ulong seek(long offset, SeekPos rel) {
assertSeekable();
auto hFile = handle();
version (Windows) {
int hi = cast(int)(offset>>32);
uint low = SetFilePointer(hFile, cast(int)offset, &hi, rel);
if ((low == INVALID_SET_FILE_POINTER) && (GetLastError() != 0))
throw new SeekException("unable to move file pointer");
ulong result = (cast(ulong)hi << 32) + low;
} else version (Posix) {
// Phobos casts offset to int, leading to throwing an exception
// on large files
auto result = lseek(hFile, cast(off_t)offset, rel);
if (result == cast(typeof(result))-1)
throw new SeekException("unable to move file pointer");
}
readEOF = false;
return cast(ulong)result;
}
override size_t readBlock(void* buffer, size_t size) {
assertReadable();
auto hFile = handle();
version (Windows) {
auto dwSize = to!DWORD(size);
ReadFile(hFile, buffer, dwSize, &dwSize, null);
size = dwSize;
} else version (Posix) {
// http://developerweb.net/viewtopic.php?id=4267
fd_set rset;
timeval timeout;
immutable MAX_IDLE_SECS = 1;
while (true) {
auto ret = core.sys.posix.unistd.read(hFile, buffer, size);
if (ret == -1) {
if (errno == EINTR)
continue;
if (errno == EAGAIN || errno == EWOULDBLOCK) {
FD_ZERO(&rset);
FD_SET(hFile, &rset);
timeout.tv_sec = MAX_IDLE_SECS;
timeout.tv_usec = 0;
ret = select(hFile + 1, &rset, null, null, &timeout);
if (ret <= 0) {
size = 0;
throw new ReadException("read timeout");
}
} else {
throw new ReadException(to!string(strerror(errno)));
}
} else {
size = ret;
break;
}
}
}
readEOF = (size == 0);
return size;
}
override size_t writeBlock(const void* buffer, size_t size) {
assertWriteable();
auto hFile = handle();
version (Windows) {
auto dwSize = to!DWORD(size);
WriteFile(hFile, buffer, dwSize, &dwSize, null);
size = dwSize;
} else version (Posix) {
size = core.sys.posix.unistd.write(hFile, buffer, size);
if (size == -1)
throw new WriteException(to!string(strerror(errno)));
}
return size;
}
}
|