Skip to content

Commit

Permalink
Merge pull request #2663 from aG0aep6G/lockingtextreader
Browse files Browse the repository at this point in the history
fix LockingTextReader: issues 13686 and 12320
  • Loading branch information
H. S. Teoh committed Nov 13, 2014
2 parents 384d370 + 3d058e2 commit a0ca855
Showing 1 changed file with 91 additions and 26 deletions.
117 changes: 91 additions & 26 deletions std/stdio.d
Expand Up @@ -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)
Expand All @@ -2633,47 +2633,81 @@ struct LockingTextReader
void opAssign(LockingTextReader r)
{
import std.algorithm : swap;

swap(this, r);
}

@property bool empty()
{
import std.exception : enforce;
return !_f.isOpen || _f.eof;
}

@property dchar front()
{
version(assert) if (empty) throw new RangeError();
return _front;
}

if (!_f.isOpen || _f.eof) return true;
if (_crt == _crt.init)
/* 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;
{
_crt = FGETC(cast(_iobuf*) _f._p.handle);
if (_crt == -1)
{
.destroy(_f);
return true;
}
else
{
enforce(ungetc(_crt, cast(FILE*) _f._p.handle) == _crt);
}
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 false;
return buf[0 .. seqLen];
}

@property dchar front()
/* 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()
{
version(assert) if (empty) throw new RangeError();
return _crt;
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)
Expand Down Expand Up @@ -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.
*/
Expand Down

0 comments on commit a0ca855

Please sign in to comment.