Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 5236 - raw reading for integers and a few refactorings #4912

Merged
merged 8 commits into from
Dec 7, 2016
111 changes: 76 additions & 35 deletions std/format.d
Original file line number Diff line number Diff line change
Expand Up @@ -4300,10 +4300,9 @@ T unformatValue(T, Range, Char)(ref Range input, ref FormatSpec!Char spec)
{
import std.algorithm.searching : find;
import std.conv : parse, text;
if (spec.spec == 's')
{
return parse!T(input);
}

if (spec.spec == 's') return parse!T(input);

enforce(find(acceptedSpecs!long, spec.spec).length,
text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));

Expand Down Expand Up @@ -4362,14 +4361,58 @@ T unformatValue(T, Range, Char)(ref Range input, ref FormatSpec!Char spec)
return parse!T(input);
}

/**
* Function that performs raw reading. Used by unformatValue
* for integral and float types.
*/
private T rawRead(T, Range)(ref Range input)
if (is(Unqual!(ElementEncodingType!Range) == char)
|| is(Unqual!(ElementEncodingType!Range) == byte)
|| is(Unqual!(ElementEncodingType!Range) == ubyte))
{
union X
{
ubyte[T.sizeof] raw;
T typed;
}
X x;
foreach (i; 0 .. T.sizeof)
{
static if (isSomeString!Range)
{
x.raw[i] = input[0];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will not compile if Range is a wide string: you'd attempt to assign a wide char (2 or 4 bytes) over a single byte. I suggest you change the constraint for rawRead to just ElementType!(Range).sizeof == 1. There is a crazier possibility by which you do accept wide strings and you do implement raw reads, but only if T.sizeof is a multiple of the character size. Probably we don't need this; most people who'd entertain the idea of a raw read will do so from a stream of bytes or a narrow string.

input = input[1 .. $];
}
else
{
// TODO: recheck this
x.raw[i] = input.front;
input.popFront();
}
}
return x.typed;
}

/**
Reads an integral value and returns it.
*/
T unformatValue(T, Range, Char)(ref Range input, ref FormatSpec!Char spec)
if (isInputRange!Range && isIntegral!T && !is(T == enum))
if (isInputRange!Range && isIntegral!T && !is(T == enum) && isSomeChar!(ElementType!Range))
{

import std.algorithm.searching : find;
import std.conv : parse, text;

if (spec.spec == 'r')
{
static if (is(Unqual!(ElementEncodingType!Range) == char)
|| is(Unqual!(ElementEncodingType!Range) == byte)
|| is(Unqual!(ElementEncodingType!Range) == ubyte))
return rawRead!T(input);
else
throw new Exception("The raw read specifier %r may only be used with narrow strings and ranges of bytes.");
}

enforce(find(acceptedSpecs!T, spec.spec).length,
text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));

Expand All @@ -4381,54 +4424,52 @@ T unformatValue(T, Range, Char)(ref Range input, ref FormatSpec!Char spec)
spec.spec == 'b' ? 2 :
spec.spec == 's' || spec.spec == 'd' || spec.spec == 'u' ? 10 : 0;
assert(base != 0);

return parse!T(input, base);

}

unittest
{
union B
{
char[int.sizeof] untyped;
int typed;
}
B b;
b.typed = 5;
char[] input = b.untyped[];
int witness;
formattedRead(input, "%r", &witness);
assert(witness == b.typed);
}

/**
Reads a floating-point value and returns it.
*/
T unformatValue(T, Range, Char)(ref Range input, ref FormatSpec!Char spec)
if (isFloatingPoint!T && !is(T == enum))
if (isFloatingPoint!T && !is(T == enum) && isInputRange!Range && isSomeChar!(ElementType!Range) && !is(Range == enum))
{
import std.algorithm.searching : find;
import std.conv : parse, text;

if (spec.spec == 'r')
{
// raw read
//enforce(input.length >= T.sizeof);
enforce(
isSomeString!Range || ElementType!(Range).sizeof == 1,
"Cannot parse input of type %s".format(Range.stringof)
);
union X
{
ubyte[T.sizeof] raw;
T typed;
}
X x;
foreach (i; 0 .. T.sizeof)
{
static if (isSomeString!Range)
{
x.raw[i] = input[0];
input = input[1 .. $];
}
else
{
// TODO: recheck this
x.raw[i] = cast(ubyte) input.front;
input.popFront();
}
}
return x.typed;
static if (is(Unqual!(ElementEncodingType!Range) == char)
|| is(Unqual!(ElementEncodingType!Range) == byte)
|| is(Unqual!(ElementEncodingType!Range) == ubyte))
return rawRead!T(input);
else
throw new Exception("The raw read specifier %r may only be used with narrow strings and ranges of bytes.");
}

enforce(find(acceptedSpecs!T, spec.spec).length,
text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));

return parse!T(input);
}

version(none)unittest
unittest
{
union A
{
Expand Down Expand Up @@ -4470,7 +4511,7 @@ version(none)unittest
* Reads one character and returns it.
*/
T unformatValue(T, Range, Char)(ref Range input, ref FormatSpec!Char spec)
if (isInputRange!Range && isSomeChar!T && !is(T == enum))
if (isInputRange!Range && isSomeChar!T && !is(T == enum) && isSomeChar!(ElementType!Range))
{
import std.algorithm.searching : find;
import std.conv : to, text;
Expand Down