One pass through std.range and friends

* Made emplace faster and replaced calls to it to also make them faster.

* Replaced phobos.d in posix.mak with index.d.

* Added version=StdDdoc to documentation build in posix.mak, and replaced uses of D_Ddoc with it.

* Improved documentation target in posix.mak (target dir automatically created).

* Added nice documentation table and cheat sheet at the top of std.algorithm.

* Replaced a few helper structs in std.range and std.algorithm with local structs, which simplify matters a fair amount.

* Added more constraints to functions in std.algorithm (still work in progress).

* Improved error message in std.algorithm.sort in case of failure to sort.

* std.random.dice(1, 10) now works (no need for array notation std.random.dice([1, 10])).

* Fixed documentation bugs and insufficiencies in std.range (still more to do).

* Improved speed of walkLength.

* Simplified retro.

* Simplified and optimized stride. Also folded stride(stride(r, a), b) into stride(r, a * b).

* Added roundRobin to std.range, which as a perk simplified radial.

* Added takeOne and takeNone to std.range.

* Added unsigned to std.traits.
This commit is contained in:
Andrei Alexandrescu 2011-02-26 15:15:36 -06:00
parent 69d8b2e514
commit 1083bd4e7b
13 changed files with 2861 additions and 2420 deletions

View file

@ -53,9 +53,9 @@ ROOT = $(ROOT_OF_THEM_ALL)/$(OS)/$(BUILD)/$(MODEL)
DOCSRC = ../d-programming-language.org
WEBSITE_DIR = ../web
DOC_OUTPUT_DIR = $(WEBSITE_DIR)/phobos-prerelease
SRC_DOCUMENTABLES = phobos.d $(addsuffix .d,$(STD_MODULES))
SRC_DOCUMENTABLES = index.d $(addsuffix .d,$(STD_MODULES))
STDDOC = $(DOCSRC)/std.ddoc
DDOCFLAGS=-m$(MODEL) -d -c -o- $(STDDOC) -I$(DRUNTIME_PATH)/import $(DMDEXTRAFLAGS)
DDOCFLAGS=-m$(MODEL) -d -c -o- -version=StdDdoc $(STDDOC) -I$(DRUNTIME_PATH)/import $(DMDEXTRAFLAGS)
# Variable defined in an OS-dependent manner (see below)
CC =
@ -288,8 +288,8 @@ $(DRUNTIME) :
###########################################################
# html documentation
$(DOC_OUTPUT_DIR)/%.html : %.d $(STDDOC)
$(DDOC) $(DDOCFLAGS) -Df$@ $<
$(DOC_OUTPUT_DIR)/. :
mkdir -p $@
$(DOC_OUTPUT_DIR)/std_%.html : std/%.d $(STDDOC)
$(DDOC) $(DDOCFLAGS) -Df$@ $<
@ -300,7 +300,10 @@ $(DOC_OUTPUT_DIR)/std_c_%.html : std/c/%.d $(STDDOC)
$(DOC_OUTPUT_DIR)/std_c_linux_%.html : std/c/linux/%.d $(STDDOC)
$(DDOC) $(DDOCFLAGS) -Df$@ $<
html : $(addprefix $(DOC_OUTPUT_DIR)/, $(subst /,_,$(subst .d,.html, \
$(DOC_OUTPUT_DIR)/%.html : %.d $(STDDOC)
$(DDOC) $(DDOCFLAGS) -Df$@ $<
html : $(DOC_OUTPUT_DIR)/. $(addprefix $(DOC_OUTPUT_DIR)/, $(subst /,_,$(subst .d,.html, \
$(SRC_DOCUMENTABLES)))) $(STYLECSS_TGT)
# @$(MAKE) -f $(DOCSRC)/linux.mak -C $(DOCSRC) --no-print-directory

File diff suppressed because it is too large Load diff

View file

@ -54,8 +54,7 @@ if (isIterable!Range && !isNarrowString!Range)
static if (is(typeof(e.opAssign(e))))
{
// this should be in-place construction
auto voidArr = (cast(void*) (result.ptr + i))[0..E.sizeof];
emplace!E(voidArr, e);
emplace!E(result.ptr + i, e);
}
else
{

View file

@ -7,7 +7,6 @@ Source: $(PHOBOSSRC std/_container.d)
Macros:
WIKI = Phobos/StdContainer
TEXTWITHCOMMAS = $0
LEADINGROW = <tr style=leadingrow bgcolor=#E4E9EF><td colspan=3><b><em>$0</em></b></td></tr>
Copyright: Red-black tree code copyright (C) 2008- by Steven Schveighoffer. Other code
copyright 2010- Andrei Alexandrescu. All rights reserved by the respective holders.
@ -1234,7 +1233,7 @@ $(D r) and $(D m) is the length of $(D stuff).
*/
size_t insertAfter(Stuff)(Take!Range r, Stuff stuff)
{
auto orig = r.original;
auto orig = r.source;
if (!orig._head)
{
// Inserting after a null range counts as insertion to the
@ -1287,7 +1286,7 @@ Complexity: $(BIGOH n)
*/
Range linearRemove(Take!Range r)
{
auto orig = r.original;
auto orig = r.source;
// We have something to remove here
if (orig._head == _root)
{
@ -1571,9 +1570,7 @@ struct Array(T) if (!is(T : const(bool)))
reserve(1 + capacity * 3 / 2);
}
assert(capacity > length && _payload.ptr);
emplace!T((cast(void*) (_payload.ptr + _payload.length))
[0 .. T.sizeof],
stuff);
emplace(_payload.ptr + _payload.length, stuff);
_payload = _payload.ptr[0 .. _payload.length + 1];
return 1;
}
@ -1605,17 +1602,17 @@ struct Array(T) if (!is(T : const(bool)))
this(U)(U[] values...) if (isImplicitlyConvertible!(U, T))
{
auto p = malloc(T.sizeof * values.length);
auto p = cast(T*) malloc(T.sizeof * values.length);
if (hasIndirections!T && p)
{
GC.addRange(p, T.sizeof * values.length);
}
foreach (i, e; values)
{
emplace!T(p[i * T.sizeof .. (i + 1) * T.sizeof], e);
assert((cast(T*) p)[i] == e);
emplace(p + i, e);
assert(p[i] == e);
}
_data.RefCounted.initialize((cast(T*) p)[0 .. values.length]);
_data.RefCounted.initialize(p[0 .. values.length]);
}
/**
@ -2089,8 +2086,7 @@ Complexity: $(BIGOH n + m), where $(D m) is the length of $(D stuff)
memmove(_data._payload.ptr + r._a + 1,
_data._payload.ptr + r._a,
T.sizeof * (length - r._a));
emplace!T((cast(void*) (_data._payload.ptr + r._a))[0 .. T.sizeof],
stuff);
emplace(_data._payload.ptr + r._a, stuff);
_data._payload = _data._payload.ptr[0 .. _data._payload.length + 1];
return 1;
}
@ -2114,7 +2110,7 @@ Complexity: $(BIGOH n + m), where $(D m) is the length of $(D stuff)
foreach (p; _data._payload.ptr + r._a ..
_data._payload.ptr + r._a + extra)
{
emplace!T((cast(void*) p)[0 .. T.sizeof], stuff.front);
emplace(p, stuff.front);
stuff.popFront();
}
_data._payload =

View file

@ -3048,7 +3048,6 @@ version (none)
assert(to!string(cr) == to!string(creal.nan));
assert(feq(cr, creal.nan));
}
}
/* **************************************************************
@ -3956,7 +3955,8 @@ template octal(alias s) if (isIntegral!(typeof(s))) {
unittest
{
debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__,
" succeeded.");
// ensure that you get the right types, even with embedded underscores
auto w = octal!"100_000_000_000";
static assert(!is(typeof(w) == int));
@ -4022,114 +4022,86 @@ T toImpl(T, S)(S src) if (is(T == struct) && is(typeof(T(src))))
// Bugzilla 3961
unittest
{
debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__,
" succeeded.");
struct Int { int x; }
Int i = to!Int(1);
}
// emplace
/**
Given a raw memory area $(D chunk), constructs an object of non-$(D
class) type $(D T) at that address. The constructor is passed the
arguments $(D Args). The $(D chunk) must be as least as large as $(D
T) needs and should have an alignment multiple of $(D T)'s alignment.
Given a pointer $(D chunk) to uninitialized memory (but already typed
as $(D T)), constructs an object of non-$(D class) type $(D T) at that
address.
This function can be $(D @trusted) if the corresponding constructor of
$(D T) is $(D @safe).
Returns: A pointer to the newly constructed object.
Returns: A pointer to the newly constructed object (which is the same
as $(D chunk)).
*/
T* emplace(T, Args...)(void[] chunk, Args args) if (!is(T == class))
T* emplace(T)(T* chunk)
if (!is(T == class))
{
enforce(chunk.length >= T.sizeof,
new ConvException("emplace: target size too small"));
auto a = cast(size_t) chunk.ptr;
version (OSX) // for some reason, breaks on other platforms
enforce(a % T.alignof == 0, new ConvException("misalignment"));
auto result = cast(typeof(return)) chunk.ptr;
auto result = cast(typeof(return)) chunk;
static T i;
memcpy(result, &i, T.sizeof);
return result;
}
/**
Given a pointer $(D chunk) to uninitialized memory (but already typed
as a non-class type $(D T)), constructs an object of type $(D T) at
that address from arguments $(D args).
This function can be $(D @trusted) if the corresponding constructor of
$(D T) is $(D @safe).
Returns: A pointer to the newly constructed object (which is the same
as $(D chunk)).
*/
T* emplace(T, Args...)(T* chunk, Args args)
if (!is(T == class) && !is(T == struct) && Args.length == 1)
{
*chunk = args[0];
return chunk;
}
// Specialization for struct
T* emplace(T, Args...)(T* chunk, Args args)
if (is(T == struct))
{
auto result = cast(typeof(return)) chunk;
void initialize()
{
static T i;
memcpy(chunk.ptr, &i, T.sizeof);
memcpy(chunk, &i, T.sizeof);
}
static if (Args.length == 0)
static if (is(typeof(result.__ctor(args))))
{
// Just initialize the thing
// T defines a genuine constructor accepting args
// Go the classic route: write .init first, then call ctor
initialize();
result.__ctor(args);
}
else static if (is(T == struct))
else static if (is(typeof(T(args))))
{
static if (is(typeof(result.__ctor(args))))
{
// T defines a genuine constructor accepting args
// Go the classic route: write .init first, then call ctor
initialize();
result.__ctor(args);
}
else static if (is(typeof(T(args))))
{
// Struct without constructor that has one matching field for
// each argument
initialize();
*result = T(args);
}
else static if (Args.length == 1 && is(Args[0] : T))
{
initialize();
*result = args[0];
}
// Struct without constructor that has one matching field for
// each argument
*result = T(args);
}
else static if (Args.length == 1 && is(Args[0] : T))
else //static if (Args.length == 1 && is(Args[0] : T))
{
// Primitive type. Assignment is fine for initialization.
static assert(Args.length == 1);
//static assert(0, T.stringof ~ " " ~ Args.stringof);
// initialize();
*result = args[0];
}
else
{
static assert(false, "Don't know how to initialize an object of type "
~ T.stringof ~ " with arguments " ~ Args.stringof);
}
return result;
}
unittest
{
debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
int a;
int b = 42;
assert(*emplace!int((cast(void*) &a)[0 .. int.sizeof], b) == 42);
struct S
{
double x = 5, y = 6;
this(int a, int b) { assert(x == 5 && y == 6); x = a; y = b; }
}
auto s1 = new void[S.sizeof];
auto s2 = S(42, 43);
assert(*emplace!S(s1, s2) == s2);
assert(*emplace!S(s1, 44, 45) == S(44, 45));
}
// emplace
/**
Similar to $(D emplace) above, except it receives a pointer to an
uninitialized object of type $(D T). This overload is useful for
e.g. initializing member variables or stack variables defined with $(D
T variable = void).
Returns: A pointer to the newly constructed object.
*/
T* emplace(T, Args...)(T* chunk, Args args) if (!is(T == class))
{
// Since we're treating the memory pointed to by chunk as a raw memory
// block, we need to cast away any qualifiers.
return cast(T*) emplace!(Unqual!T)((cast(void*) chunk)[0 .. T.sizeof], args);
}
// emplace
/**
Given a raw memory area $(D chunk), constructs an object of $(D class)
type $(D T) at that address. The constructor is passed the arguments
@ -4170,6 +4142,60 @@ T emplace(T, Args...)(void[] chunk, Args args) if (is(T == class))
return result;
}
/**
Given a raw memory area $(D chunk), constructs an object of non-$(D
class) type $(D T) at that address. The constructor is passed the
arguments $(D args), if any. The $(D chunk) must be as least as large
as $(D T) needs and should have an alignment multiple of $(D T)'s
alignment.
This function can be $(D @trusted) if the corresponding constructor of
$(D T) is $(D @safe).
Returns: A pointer to the newly constructed object.
*/
T* emplace(T, Args...)(void[] chunk, Args args)
if (!is(T == class))
{
enforce(chunk.length >= T.sizeof,
new ConvException("emplace: chunk size too small"));
auto a = cast(size_t) chunk.ptr;
enforce(a % T.alignof == 0, text(a, " vs. ", T.alignof));
auto result = cast(typeof(return)) chunk.ptr;
return emplace(result, args);
}
unittest
{
struct S { int a, b; }
auto p = new void[S.sizeof];
S s;
s.a = 42;
s.b = 43;
auto s1 = emplace!S(p, s);
assert(s1.a == 42 && s1.b == 43);
}
unittest
{
debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__,
" succeeded.");
int a;
int b = 42;
assert(*emplace!int(&a, b) == 42);
struct S
{
double x = 5, y = 6;
this(int a, int b) { assert(x == 5 && y == 6); x = a; y = b; }
}
auto s1 = new void[S.sizeof];
auto s2 = S(42, 43);
assert(*emplace!S(cast(S*) s1.ptr, s2) == s2);
assert(*emplace!S(cast(S*) s1, 44, 45) == S(44, 45));
}
unittest
{
debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
@ -4200,7 +4226,6 @@ unittest
}
Foo foo;
auto voidArr = (cast(void*) &foo)[0..Foo.sizeof];
emplace!Foo(voidArr, 2U);
emplace!Foo(&foo, 2U);
assert(foo.num == 2);
}

View file

@ -28496,7 +28496,7 @@ public:
}
version(D_Ddoc)
version(StdDdoc)
{
/++
The name of the time zone per the TZ Database. This is the name used to
@ -30587,7 +30587,7 @@ private:
version(D_Ddoc)
version(StdDdoc)
{
/++
@ -31119,7 +31119,7 @@ else version(Windows)
}
version(D_Ddoc)
version(StdDdoc)
{
/++
$(BLUE This function is Posix-Only.)
@ -32189,7 +32189,7 @@ unittest
}
version(D_Ddoc)
version(StdDdoc)
{
version(Windows) {}
else
@ -32982,76 +32982,61 @@ unittest
}
}
/++
Function for starting to a stop watch time when the function is called
and stopping it when its return value goes out of scope and is
destroyed.
version(D_Ddoc)
{
/++
Function for starting to a stop watch time when the function is called
and stopping it when its return value goes out of scope and is destroyed.
When the value that is returned by this function is destroyed, $(D
func) will run. $(D func) is a unary function that takes a $(D
TickDuration).
When the value that is returned by this function is destroyed,
$(D func) will run. $(D func) is a unary function that takes a
$(D TickDuration).
Examples:
Examples:
--------------------
writeln("benchmark start!");
{
auto mt = measureTime!((a){assert(a.seconds);});
doSomething();
auto mt = measureTime!((a){assert(a.seconds);});
doSomething();
}
writeln("benchmark end!");
--------------------
+/
auto measureTime(alias func)();
}
else
+/
@safe auto measureTime(alias func)()
if(isSafe!func)
{
@safe
struct Result
{
auto measureTime(alias func)()
if(isSafe!func)
private StopWatch sw = void;
this(StopWatch.AutoStart as)
{
struct TMP
{
private StopWatch sw = void;
this(StopWatch.AutoStart as)
{
sw = StopWatch(as);
}
~this()
{
unaryFun!(func)(sw.peek());
}
}
return TMP(autoStart);
}
}
@system
{
auto measureTime(alias func)()
if (!isSafe!func)
{
struct TMP
{
private StopWatch sw = void;
this(AutoStart as)
{
sw = StopWatch(as);
}
~this()
{
unaryFun!(func)(sw.peek());
}
}
return TMP(autoStart);
sw = StopWatch(as);
}
~this()
{
unaryFun!(func)(sw.peek());
}
}
return Result(autoStart);
}
@system
unittest
@system auto measureTime(alias func)() if (!isSafe!func)
{
struct TMP
{
private StopWatch sw = void;
this(AutoStart as)
{
sw = StopWatch(as);
}
~this()
{
unaryFun!(func)(sw.peek());
}
}
return TMP(autoStart);
}
@system unittest
{
version(testStdDateTime)
{

View file

@ -601,7 +601,7 @@ unittest
* $(RED Scheduled for deprecation. Please use getTimesWin (for Windows)
* or getTimesPosix (for Posix) instead.)
*/
version(D_Ddoc)
version(StdDdoc)
{
void getTimes(in char[] name,
out d_time ftc,
@ -668,7 +668,7 @@ else
static assert(0, "Unsupported/Unknown OS");
version(D_Ddoc)
version(StdDdoc)
{
/++
$(BLUE This function is Windows-Only.)
@ -877,7 +877,7 @@ else
static assert(0, "Unsupported/Unknown OS");
version(D_Ddoc)
version(StdDdoc)
{
/++
$(RED Scheduled for deprecation. Please use
@ -911,7 +911,7 @@ else
}
version(D_Ddoc)
version(StdDdoc)
{
/++
$(RED Scheduled for deprecation. Please use timeLastModified instead.)
@ -1138,7 +1138,7 @@ uint getAttributes(in char[] name)
}
version(D_Ddoc)
version(StdDdoc)
{
/++
If the given file is a symbolic link, then this returns the attributes of the
@ -1724,7 +1724,7 @@ unittest
}
version(D_Ddoc)
version(StdDdoc)
{
/++
Info on a file, similar to what you'd get from stat on a Posix system.
@ -2497,7 +2497,7 @@ version(Posix) void copy(in char[] from, in char[] to)
cenforce(utime(toz, &utim) != -1, from);
}
version(D_Ddoc)
version(StdDdoc)
{
/++
$(RED Scheduled for deprecation. Please use the version which takes
@ -2553,7 +2553,7 @@ else
}
version(D_Ddoc)
version(StdDdoc)
{
/++
Set access/modified times of file $(D name).

View file

@ -498,7 +498,7 @@ struct FormatSpec(Char)
$(D ubyte.max). ($(D 0) means not used).
*/
ubyte index;
version(D_Ddoc) {
version(StdDdoc) {
/**
The format specifier contained a $(D '-') ($(D printf)
compatibility).

View file

@ -383,8 +383,7 @@ template adjoin(F...) if (F.length)
Tuple!(Head, typeof(.adjoin!(F[1..$])(a)).Types) result = void;
foreach (i, Unused; result.Types)
{
auto store = (cast(void*) &result[i])[0 .. result[i].sizeof];
emplace!(typeof(result[i]))(store, F[i](a));
emplace(&result[i], F[i](a));
}
return result;
}

View file

@ -878,7 +878,7 @@ auto a = uniform(0, 1024, gen);
auto a = uniform(0.0f, 1.0f, gen);
----
*/
version(D_Ddoc)
version(StdDdoc)
CommonType!(T1, T2) uniform(string boundaries = "[$(RPAREN)",
T1, T2, UniformRandomNumberGenerator)
(T1 a, T2 b, ref UniformRandomNumberGenerator urng);
@ -964,7 +964,7 @@ if (is(CommonType!(T1, UniformRandomNumberGenerator) == void) &&
/**
As above, but uses the default generator $(D rndGen).
*/
version(D_Ddoc)
version(StdDdoc)
CommonType!(T1, T2) uniform(string boundaries = "[$(RPAREN)", T1, T2)
(T1 a, T2 b) if (!is(CommonType!(T1, T2) == void));
else
@ -1082,34 +1082,39 @@ auto z = dice(70, 20, 10); // z is 0 70% of the time, 1 30% of the time,
// and 2 10% of the time
----
*/
size_t dice(R, Num)(ref R rnd, Num[] proportions...)
if(isNumeric!Num) {
size_t dice(Rng, Num)(ref Rng rnd, Num[] proportions...)
if (isNumeric!Num && isForwardRange!Rng)
{
return diceImpl(rnd, proportions);
}
/// Ditto
size_t dice(R, Range)(ref R rnd, Range proportions)
if(isForwardRange!Range && isNumeric!(ElementType!Range) && !isArray!Range) {
if (isForwardRange!Range && isNumeric!(ElementType!Range) && !isArray!Range)
{
return diceImpl(rnd, proportions);
}
/// Ditto
size_t dice(Num)(Num[] proportions...)
if(isNumeric!Num) {
size_t dice(Range)(Range proportions)
if (isForwardRange!Range && isNumeric!(ElementType!Range) && !isArray!Range)
{
return diceImpl(rndGen(), proportions);
}
/// Ditto
size_t dice(Range)(Range proportions)
if(isForwardRange!Range && isNumeric!(ElementType!Range) && !isArray!Range) {
size_t dice(Num)(Num[] proportions...)
if (isNumeric!Num)
{
return diceImpl(rndGen(), proportions);
}
private size_t diceImpl(R, Range)(ref R rnd, Range proportions)
if(isForwardRange!Range && isNumeric!(ElementType!Range)) {
immutable sum = reduce!("(assert(b >= 0), a + b)")(0.0, proportions.save);
private size_t diceImpl(Rng, Range)(ref Rng rng, Range proportions)
if (isForwardRange!Range && isNumeric!(ElementType!Range) && isForwardRange!Rng)
{
double sum = reduce!("(assert(b >= 0), a + b)")(0.0, proportions.save);
enforce(sum > 0, "Proportions in a dice cannot sum to zero");
immutable point = uniform(0.0, sum, rnd);
immutable point = uniform(0.0, sum, rng);
assert(point < sum);
auto mass = 0.0;
@ -1123,8 +1128,6 @@ if(isForwardRange!Range && isNumeric!(ElementType!Range)) {
assert(false);
}
unittest {
auto rnd = Random(unpredictableSeed);
auto i = dice(rnd, 0.0, 100.0);
@ -1132,13 +1135,7 @@ unittest {
i = dice(rnd, 100.0, 0.0);
assert(i == 0);
i = dice([100U, 0U]);
assert(i == 0);
i = dice(filter!"a >= 0"([100U, 0U]));
assert(i == 0);
i = dice(rnd, filter!"a >= 0"([100U, 0U]));
i = dice(100U, 0U);
assert(i == 0);
}

File diff suppressed because it is too large Load diff

View file

@ -3376,6 +3376,29 @@ unittest
assert(is(S2 == immutable(int)), S2.stringof);
}
/**
* Returns the corresponding unsigned value for $(D x), e.g. if $(D x)
* has type $(D int), returns $(D cast(uint) x). The advantage
* compared to the cast is that you do not need to rewrite the cast if
* $(D x) later changes type to e.g. $(D long).
*/
auto unsigned(T)(T x) if (isIntegral!T)
{
static if (is(Unqual!T == byte)) return cast(ubyte) x;
else static if (is(Unqual!T == short)) return cast(ushort) x;
else static if (is(Unqual!T == int)) return cast(uint) x;
else static if (is(Unqual!T == long)) return cast(ulong) x;
else
{
static assert(T.min == 0, "Bug in either unsigned or isIntegral");
return x;
}
}
unittest
{
static assert(is(typeof(unsigned(1 + 1)) == uint));
}
/**
Returns the most negative value of the numeric type T.

View file

@ -2286,7 +2286,7 @@ if (!is(T == class))
{
GC.addRange(p.ptr, sz);
}
emplace!T(p[0 .. T.sizeof], args);
emplace(cast(T*) p.ptr, args);
_store = cast(typeof(_store)) p.ptr;
_store._count = 1;
debug(RefCounted) if (debugging) writeln(typeof(this).stringof,