Skip to content

Commit

Permalink
Merge pull request #2077 from ntrel/byLineCopy
Browse files Browse the repository at this point in the history
Fix Issue 12556 - Add persistent stdio.File.byLine
  • Loading branch information
Михаил Страшун committed Jul 14, 2014
2 parents 45fda72 + 73da5d2 commit 9c41595
Showing 1 changed file with 145 additions and 5 deletions.
150 changes: 145 additions & 5 deletions std/stdio.d
Expand Up @@ -1680,7 +1680,9 @@ may throw $(D StdioException) on I/O error.
Note:
Each $(D front) will not persist after $(D
popFront) is called, so the caller must copy its contents (e.g. by
calling $(D to!string)) if retention is needed.
calling $(D to!string)) when retention is needed. If the caller needs
to retain a copy of every line, use the $(LREF byLineCopy) function
instead.
Params:
Char = Character type for each line, defaulting to $(D char).
Expand Down Expand Up @@ -1727,7 +1729,7 @@ $(D front) after the corresponding $(D popFront) call is made (because
the contents may well have changed).
*/
auto byLine(Terminator = char, Char = char)
(KeepTerminator keepTerminator = KeepTerminator.no,
(KeepTerminator keepTerminator = KeepTerminator.no,
Terminator terminator = '\n')
if (isScalarType!Terminator)
{
Expand All @@ -1736,12 +1738,135 @@ the contents may well have changed).

/// ditto
auto byLine(Terminator, Char = char)
(KeepTerminator keepTerminator, Terminator terminator)
(KeepTerminator keepTerminator, Terminator terminator)
if (is(Unqual!(ElementEncodingType!Terminator) == Char))
{
return ByLine!(Char, Terminator)(this, keepTerminator, terminator);
}

private struct ByLineCopy(Char, Terminator)
{
private:
import std.typecons;

/* Ref-counting stops the source range's ByLineCopyImpl
* from getting out of sync after the range is copied, e.g.
* when accessing range.front, then using std.range.take,
* then accessing range.front again. */
alias Impl = RefCounted!(ByLineCopyImpl!(Char, Terminator),
RefCountedAutoInitialize.no);
Impl impl;

public:
this(File f, KeepTerminator kt, Terminator terminator)
{
impl = Impl(f, kt, terminator);
}

@property bool empty()
{
return impl.refCountedPayload.empty;
}

@property Char[] front()
{
return impl.refCountedPayload.front;
}

void popFront()
{
impl.refCountedPayload.popFront();
}
}

private struct ByLineCopyImpl(Char, Terminator)
{
ByLineImpl!(Unqual!Char, Terminator) impl;
bool gotFront;
Char[] line;

public:
this(File f, KeepTerminator kt, Terminator terminator)
{
impl = ByLineImpl!(Unqual!Char, Terminator)(f, kt, terminator);
}

@property bool empty()
{
return impl.empty;
}

@property front()
{
if (!gotFront)
{
line = impl.front.dup;
gotFront = true;
}
return line;
}

void popFront()
{
impl.popFront();
gotFront = false;
}
}

/**
Returns an input range set up to read from the file handle one line
at a time. Each line will be newly allocated.
The element type for the range will be $(D Char[]). Range
primitives may throw $(D StdioException) on I/O error.
Params:
Char = Character type for each line, defaulting to $(D immutable char).
keepTerminator = Use $(D KeepTerminator.yes) to include the
terminator at the end of each line.
terminator = Line separator ($(D '\n') by default).
Example:
----
import std.algorithm, std.stdio;
// Print sorted lines of a file.
void main()
{
auto sortedLines = File("file.txt") // Open for reading
.byLineCopy() // Read persistent lines
.array() // into an array
.sort(); // then sort them
foreach (line; sortedLines)
writeln(line);
}
----
See_Also:
$(XREF file,readText)
*/
auto byLineCopy(Terminator = char, Char = immutable char)
(KeepTerminator keepTerminator = KeepTerminator.no,
Terminator terminator = '\n')
if (isScalarType!Terminator)
{
return ByLineCopy!(Char, Terminator)(this, keepTerminator, terminator);
}

/// ditto
auto byLineCopy(Terminator, Char = immutable char)
(KeepTerminator keepTerminator, Terminator terminator)
if (is(Unqual!(ElementEncodingType!Terminator) == Unqual!Char))
{
return ByLineCopy!(Char, Terminator)(this, keepTerminator, terminator);
}

unittest
{
static assert(is(typeof(File("").byLine.front) == char[]));
static assert(is(typeof(File("").byLineCopy.front) == string));
static assert(
is(typeof(File("").byLineCopy!(char, char).front) == char[]));
}

unittest
{
static import std.file;
Expand All @@ -1762,7 +1887,7 @@ the contents may well have changed).
f.detach();
assert(!f.isOpen);

void test(Terminator)(string txt, string[] witness,
void test(Terminator)(string txt, in string[] witness,
KeepTerminator kt, Terminator term, bool popFirstLine = false)
{
import std.conv : text;
Expand All @@ -1789,8 +1914,11 @@ the contents may well have changed).
assert(i == witness.length, text(i, " != ", witness.length));

// Issue 11830
auto walkedLength = File(deleteme).byLine.walkLength;
auto walkedLength = File(deleteme).byLine(kt, term).walkLength;
assert(walkedLength == witness.length, text(walkedLength, " != ", witness.length));

// test persistent lines
assert(File(deleteme).byLineCopy(kt, term).array.sort() == witness.dup.sort());
}

KeepTerminator kt = KeepTerminator.no;
Expand Down Expand Up @@ -1855,6 +1983,18 @@ the contents may well have changed).
assert(!file.isOpen);
}

unittest
{
auto deleteme = testFilename();
std.file.write(deleteme, "hi");
scope(success) std.file.remove(deleteme);

auto blc = File(deleteme).byLineCopy;
assert(!blc.empty);
// check front is cached
assert(blc.front is blc.front);
}

template byRecord(Fields...)
{
ByRecord!(Fields) byRecord(string format)
Expand Down

0 comments on commit 9c41595

Please sign in to comment.