mirror of
https://github.com/dlang/phobos.git
synced 2025-05-08 20:19:43 +03:00
Merge pull request #2663 from aG0aep6G/lockingtextreader
fix LockingTextReader: issues 13686 and 12320
This commit is contained in:
commit
a0ca85550a
1 changed files with 92 additions and 27 deletions
119
std/stdio.d
119
std/stdio.d
|
@ -2608,15 +2608,15 @@ enum LockType
|
|||
struct LockingTextReader
|
||||
{
|
||||
private File _f;
|
||||
private dchar _crt;
|
||||
private dchar _front;
|
||||
|
||||
this(File f)
|
||||
{
|
||||
import std.exception : enforce;
|
||||
|
||||
enforce(f.isOpen);
|
||||
_f = f;
|
||||
FLOCK(_f._p.handle);
|
||||
readFront();
|
||||
}
|
||||
|
||||
this(this)
|
||||
|
@ -2633,47 +2633,81 @@ struct LockingTextReader
|
|||
void opAssign(LockingTextReader r)
|
||||
{
|
||||
import std.algorithm : swap;
|
||||
|
||||
swap(this, r);
|
||||
}
|
||||
|
||||
@property bool empty()
|
||||
{
|
||||
import std.exception : enforce;
|
||||
|
||||
if (!_f.isOpen || _f.eof) return true;
|
||||
if (_crt == _crt.init)
|
||||
{
|
||||
_crt = FGETC(cast(_iobuf*) _f._p.handle);
|
||||
if (_crt == -1)
|
||||
{
|
||||
.destroy(_f);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
enforce(ungetc(_crt, cast(FILE*) _f._p.handle) == _crt);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return !_f.isOpen || _f.eof;
|
||||
}
|
||||
|
||||
@property dchar front()
|
||||
{
|
||||
version(assert) if (empty) throw new RangeError();
|
||||
return _crt;
|
||||
return _front;
|
||||
}
|
||||
|
||||
/* Read a utf8 sequence from the file, removing the chars from the stream.
|
||||
Returns an empty result when at EOF. */
|
||||
private char[] takeFront(ref char[4] buf)
|
||||
{
|
||||
import std.utf : stride, UTFException;
|
||||
{
|
||||
immutable int c = FGETC(cast(_iobuf*) _f._p.handle);
|
||||
if (c == EOF)
|
||||
return buf[0 .. 0];
|
||||
buf[0] = cast(char) c;
|
||||
}
|
||||
immutable seqLen = stride(buf[]);
|
||||
foreach(ref u; buf[1 .. seqLen])
|
||||
{
|
||||
immutable int c = FGETC(cast(_iobuf*) _f._p.handle);
|
||||
if (c == EOF) // incomplete sequence
|
||||
throw new UTFException("Invalid UTF-8 sequence");
|
||||
u = cast(char) c;
|
||||
}
|
||||
return buf[0 .. seqLen];
|
||||
}
|
||||
|
||||
/* Read a utf8 sequence from the file into _front, putting the chars back so
|
||||
that they can be read again.
|
||||
Destroys/closes the file when at EOF. */
|
||||
private void readFront()
|
||||
{
|
||||
import std.exception : enforce;
|
||||
import std.utf : decodeFront;
|
||||
|
||||
char[4] buf;
|
||||
auto chars = takeFront(buf);
|
||||
|
||||
if (chars.empty)
|
||||
{
|
||||
.destroy(_f);
|
||||
assert(empty);
|
||||
return;
|
||||
}
|
||||
|
||||
auto s = chars;
|
||||
_front = decodeFront(s);
|
||||
|
||||
// Put everything back.
|
||||
foreach(immutable i; 0 .. chars.length)
|
||||
{
|
||||
immutable c = chars[$ - 1 - i];
|
||||
enforce(ungetc(c, cast(FILE*) _f._p.handle) == c);
|
||||
}
|
||||
}
|
||||
|
||||
void popFront()
|
||||
{
|
||||
version(assert) if (empty) throw new RangeError();
|
||||
if (FGETC(cast(_iobuf*) _f._p.handle) == -1)
|
||||
{
|
||||
import std.exception : enforce;
|
||||
|
||||
enforce(_f.eof);
|
||||
}
|
||||
_crt = _crt.init;
|
||||
// Pop the current front.
|
||||
char[4] buf;
|
||||
takeFront(buf);
|
||||
|
||||
// Read the next front, leaving the chars on the stream.
|
||||
readFront();
|
||||
}
|
||||
|
||||
// void unget(dchar c)
|
||||
|
@ -2701,6 +2735,37 @@ unittest
|
|||
//pragma(msg, "--- todo: readf ---");
|
||||
}
|
||||
|
||||
unittest // bugzilla 13686
|
||||
{
|
||||
static import std.file;
|
||||
import std.algorithm : equal;
|
||||
|
||||
auto deleteme = testFilename();
|
||||
std.file.write(deleteme, "Тест");
|
||||
scope(exit) std.file.remove(deleteme);
|
||||
|
||||
string s;
|
||||
File(deleteme).readf("%s", &s);
|
||||
assert(s == "Тест");
|
||||
|
||||
auto ltr = LockingTextReader(File(deleteme));
|
||||
assert(equal(ltr, "Тест"));
|
||||
}
|
||||
|
||||
unittest // bugzilla 12320
|
||||
{
|
||||
static import std.file;
|
||||
auto deleteme = testFilename();
|
||||
std.file.write(deleteme, "ab");
|
||||
scope(exit) std.file.remove(deleteme);
|
||||
auto ltr = LockingTextReader(File(deleteme));
|
||||
assert(ltr.front == 'a');
|
||||
ltr.popFront();
|
||||
assert(ltr.front == 'b');
|
||||
ltr.popFront();
|
||||
assert(ltr.empty);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether $(D T) is a file handle of some kind.
|
||||
*/
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue