aboutsummaryrefslogtreecommitdiff
path: root/libphobos
diff options
context:
space:
mode:
authorIain Buclaw <ibuclaw@gdcproject.org>2022-09-27 10:43:32 +0200
committerIain Buclaw <ibuclaw@gdcproject.org>2022-09-27 10:50:18 +0200
commitc8dfa79c9948ce09a7b4071f8059294b1972aef6 (patch)
treecb93655417a5475c6baac88691fc92621b3fa7ce /libphobos
parentbe4a6551ed37c1e7dbdfb9400fc2e2b5d40c5be2 (diff)
downloadgcc-c8dfa79c9948ce09a7b4071f8059294b1972aef6.zip
gcc-c8dfa79c9948ce09a7b4071f8059294b1972aef6.tar.gz
gcc-c8dfa79c9948ce09a7b4071f8059294b1972aef6.tar.bz2
d: Merge upstream dmd d579c467c1, phobos 88aa69b14.
D front-end changes: - Throwing from contracts of `nothrow' functions has been deprecated, as this breaks the guarantees of `nothrow'. - Added language support for initializing the interior pointer of associative arrays using `new' keyword. Phobos changes: - The std.digest.digest module has been removed. - The std.xml module has been removed. gcc/d/ChangeLog: * dmd/MERGE: Merge upstream dmd d579c467c1. * decl.cc (layout_struct_initializer): Update for new front-end interface. * expr.cc (ExprVisitor::visit (AssignExp *)): Remove lowering of array assignments. (ExprVisitor::visit (NewExp *)): Add new lowering of new'ing associative arrays to an _aaNew() library call. * runtime.def (ARRAYSETASSIGN): Remove. (AANEW): Define. libphobos/ChangeLog: * libdruntime/MERGE: Merge upstream druntime d579c467c1. * libdruntime/Makefile.am (DRUNTIME_DSOURCES): Remove rt/arrayassign.d. * libdruntime/Makefile.in: Regenerate. * src/MERGE: Merge upstream phobos 88aa69b14. * src/Makefile.am (PHOBOS_DSOURCES): Remove std/digest/digest.d, std/xml.d. * src/Makefile.in: Regenerate.
Diffstat (limited to 'libphobos')
-rw-r--r--libphobos/libdruntime/MERGE2
-rw-r--r--libphobos/libdruntime/Makefile.am10
-rw-r--r--libphobos/libdruntime/Makefile.in21
-rw-r--r--libphobos/libdruntime/__builtins.di2
-rw-r--r--libphobos/libdruntime/core/demangle.d250
-rw-r--r--libphobos/libdruntime/core/internal/array/arrayassign.d148
-rw-r--r--libphobos/libdruntime/core/internal/dassert.d2
-rw-r--r--libphobos/libdruntime/core/internal/utf.d21
-rw-r--r--libphobos/libdruntime/core/simd.d2
-rw-r--r--libphobos/libdruntime/core/stdc/fenv.d2
-rw-r--r--libphobos/libdruntime/core/stdc/math.d11
-rw-r--r--libphobos/libdruntime/core/sys/darwin/mach/loader.d2
-rw-r--r--libphobos/libdruntime/core/sys/linux/fcntl.d2
-rw-r--r--libphobos/libdruntime/core/sys/posix/dlfcn.d4
-rw-r--r--libphobos/libdruntime/core/sys/posix/mqueue.d4
-rw-r--r--libphobos/libdruntime/core/sys/posix/setjmp.d16
-rw-r--r--libphobos/libdruntime/core/sys/posix/stdlib.d20
-rw-r--r--libphobos/libdruntime/core/sys/posix/sys/types.d12
-rw-r--r--libphobos/libdruntime/core/sys/windows/stacktrace.d3
-rw-r--r--libphobos/libdruntime/core/time.d2
-rw-r--r--libphobos/libdruntime/object.d3
-rw-r--r--libphobos/libdruntime/rt/arrayassign.d60
-rw-r--r--libphobos/libdruntime/rt/lifetime.d60
-rw-r--r--libphobos/src/MERGE2
-rw-r--r--libphobos/src/Makefile.am8
-rw-r--r--libphobos/src/Makefile.in14
-rw-r--r--libphobos/src/index.dd4
-rw-r--r--libphobos/src/std/algorithm/comparison.d20
-rw-r--r--libphobos/src/std/algorithm/iteration.d17
-rw-r--r--libphobos/src/std/algorithm/searching.d76
-rw-r--r--libphobos/src/std/algorithm/sorting.d4
-rw-r--r--libphobos/src/std/compiler.d2
-rw-r--r--libphobos/src/std/concurrency.d7
-rw-r--r--libphobos/src/std/container/array.d31
-rw-r--r--libphobos/src/std/container/dlist.d2
-rw-r--r--libphobos/src/std/conv.d15
-rw-r--r--libphobos/src/std/datetime/stopwatch.d2
-rw-r--r--libphobos/src/std/datetime/timezone.d3
-rw-r--r--libphobos/src/std/digest/crc.d8
-rw-r--r--libphobos/src/std/digest/digest.d3
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/affix_allocator.d4
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/aligned_block_list.d10
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/allocator_list.d20
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/fallback_allocator.d14
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/free_list.d4
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/free_tree.d4
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/quantizer.d8
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/region.d867
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/scoped_allocator.d6
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/segregator.d8
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/stats_collector.d8
-rw-r--r--libphobos/src/std/experimental/allocator/package.d14
-rw-r--r--libphobos/src/std/file.d2
-rw-r--r--libphobos/src/std/format/package.d2
-rw-r--r--libphobos/src/std/internal/math/gammafunction.d2
-rw-r--r--libphobos/src/std/logger/core.d21
-rw-r--r--libphobos/src/std/net/curl.d8
-rw-r--r--libphobos/src/std/numeric.d4
-rw-r--r--libphobos/src/std/path.d29
-rw-r--r--libphobos/src/std/range/primitives.d13
-rw-r--r--libphobos/src/std/socket.d4
-rw-r--r--libphobos/src/std/stdio.d5
-rw-r--r--libphobos/src/std/sumtype.d47
-rw-r--r--libphobos/src/std/typecons.d40
-rw-r--r--libphobos/src/std/uni/package.d2
-rw-r--r--libphobos/src/std/utf.d2
-rw-r--r--libphobos/src/std/xml.d3113
67 files changed, 1331 insertions, 3807 deletions
diff --git a/libphobos/libdruntime/MERGE b/libphobos/libdruntime/MERGE
index 85fc49d..a4c46f3 100644
--- a/libphobos/libdruntime/MERGE
+++ b/libphobos/libdruntime/MERGE
@@ -1,4 +1,4 @@
-817610b16d0f0f469b9fbb28c000956fb910c43f
+4219ba670ce9ff92f3e874f0f048f2c28134c008
The first line of this file holds the git revision number of the last
merge done from the dlang/dmd repository.
diff --git a/libphobos/libdruntime/Makefile.am b/libphobos/libdruntime/Makefile.am
index d828749..45749d7 100644
--- a/libphobos/libdruntime/Makefile.am
+++ b/libphobos/libdruntime/Makefile.am
@@ -211,11 +211,11 @@ DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \
gcc/sections/package.d gcc/sections/pecoff.d gcc/simd.d \
gcc/unwind/arm.d gcc/unwind/arm_common.d gcc/unwind/c6x.d \
gcc/unwind/generic.d gcc/unwind/package.d gcc/unwind/pe.d object.d \
- rt/aApply.d rt/aApplyR.d rt/aaA.d rt/adi.d rt/arrayassign.d \
- rt/arraycat.d rt/cast_.d rt/config.d rt/critical_.d rt/deh.d \
- rt/dmain2.d rt/ehalloc.d rt/invariant.d rt/lifetime.d rt/memory.d \
- rt/minfo.d rt/monitor_.d rt/profilegc.d rt/sections.d rt/tlsgc.d \
- rt/util/typeinfo.d rt/util/utility.d
+ rt/aApply.d rt/aApplyR.d rt/aaA.d rt/adi.d rt/arraycat.d rt/cast_.d \
+ rt/config.d rt/critical_.d rt/deh.d rt/dmain2.d rt/ehalloc.d \
+ rt/invariant.d rt/lifetime.d rt/memory.d rt/minfo.d rt/monitor_.d \
+ rt/profilegc.d rt/sections.d rt/tlsgc.d rt/util/typeinfo.d \
+ rt/util/utility.d
DRUNTIME_DSOURCES_STDCXX = core/stdcpp/allocator.d core/stdcpp/array.d \
core/stdcpp/exception.d core/stdcpp/memory.d core/stdcpp/new_.d \
diff --git a/libphobos/libdruntime/Makefile.in b/libphobos/libdruntime/Makefile.in
index 57660ee..e86721f 100644
--- a/libphobos/libdruntime/Makefile.in
+++ b/libphobos/libdruntime/Makefile.in
@@ -242,11 +242,11 @@ am__objects_1 = core/atomic.lo core/attribute.lo core/bitop.lo \
gcc/unwind/arm_common.lo gcc/unwind/c6x.lo \
gcc/unwind/generic.lo gcc/unwind/package.lo gcc/unwind/pe.lo \
object.lo rt/aApply.lo rt/aApplyR.lo rt/aaA.lo rt/adi.lo \
- rt/arrayassign.lo rt/arraycat.lo rt/cast_.lo rt/config.lo \
- rt/critical_.lo rt/deh.lo rt/dmain2.lo rt/ehalloc.lo \
- rt/invariant.lo rt/lifetime.lo rt/memory.lo rt/minfo.lo \
- rt/monitor_.lo rt/profilegc.lo rt/sections.lo rt/tlsgc.lo \
- rt/util/typeinfo.lo rt/util/utility.lo
+ rt/arraycat.lo rt/cast_.lo rt/config.lo rt/critical_.lo \
+ rt/deh.lo rt/dmain2.lo rt/ehalloc.lo rt/invariant.lo \
+ rt/lifetime.lo rt/memory.lo rt/minfo.lo rt/monitor_.lo \
+ rt/profilegc.lo rt/sections.lo rt/tlsgc.lo rt/util/typeinfo.lo \
+ rt/util/utility.lo
am__objects_2 = core/stdc/libgdruntime_la-errno_.lo
am__objects_3 = core/sys/elf/package.lo
am__objects_4 = core/stdcpp/allocator.lo core/stdcpp/array.lo \
@@ -880,11 +880,11 @@ DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \
gcc/sections/package.d gcc/sections/pecoff.d gcc/simd.d \
gcc/unwind/arm.d gcc/unwind/arm_common.d gcc/unwind/c6x.d \
gcc/unwind/generic.d gcc/unwind/package.d gcc/unwind/pe.d object.d \
- rt/aApply.d rt/aApplyR.d rt/aaA.d rt/adi.d rt/arrayassign.d \
- rt/arraycat.d rt/cast_.d rt/config.d rt/critical_.d rt/deh.d \
- rt/dmain2.d rt/ehalloc.d rt/invariant.d rt/lifetime.d rt/memory.d \
- rt/minfo.d rt/monitor_.d rt/profilegc.d rt/sections.d rt/tlsgc.d \
- rt/util/typeinfo.d rt/util/utility.d
+ rt/aApply.d rt/aApplyR.d rt/aaA.d rt/adi.d rt/arraycat.d rt/cast_.d \
+ rt/config.d rt/critical_.d rt/deh.d rt/dmain2.d rt/ehalloc.d \
+ rt/invariant.d rt/lifetime.d rt/memory.d rt/minfo.d rt/monitor_.d \
+ rt/profilegc.d rt/sections.d rt/tlsgc.d rt/util/typeinfo.d \
+ rt/util/utility.d
DRUNTIME_DSOURCES_STDCXX = core/stdcpp/allocator.d core/stdcpp/array.d \
core/stdcpp/exception.d core/stdcpp/memory.d core/stdcpp/new_.d \
@@ -1364,7 +1364,6 @@ rt/aApply.lo: rt/$(am__dirstamp)
rt/aApplyR.lo: rt/$(am__dirstamp)
rt/aaA.lo: rt/$(am__dirstamp)
rt/adi.lo: rt/$(am__dirstamp)
-rt/arrayassign.lo: rt/$(am__dirstamp)
rt/arraycat.lo: rt/$(am__dirstamp)
rt/cast_.lo: rt/$(am__dirstamp)
rt/config.lo: rt/$(am__dirstamp)
diff --git a/libphobos/libdruntime/__builtins.di b/libphobos/libdruntime/__builtins.di
index cf05333..1c49035 100644
--- a/libphobos/libdruntime/__builtins.di
+++ b/libphobos/libdruntime/__builtins.di
@@ -15,7 +15,7 @@ module __builtins;
/* gcc relies on internal __builtin_xxxx functions and templates to
* accomplish <stdarg.h>. D does the same thing with templates in core.stdc.stdarg.
* Here, we redirect the gcc builtin declarations to the equivalent
- * ones in core.stdc.stdarg, thereby avoiding having to hardware them
+ * ones in core.stdc.stdarg, thereby avoiding having to hardwire them
* into the D compiler.
*/
diff --git a/libphobos/libdruntime/core/demangle.d b/libphobos/libdruntime/core/demangle.d
index ca87f90..c2d4032 100644
--- a/libphobos/libdruntime/core/demangle.d
+++ b/libphobos/libdruntime/core/demangle.d
@@ -82,7 +82,7 @@ pure @safe:
static class ParseException : Exception
{
- @safe pure nothrow this( string msg )
+ this(string msg) @safe pure nothrow
{
super( msg );
}
@@ -91,14 +91,14 @@ pure @safe:
static class OverflowException : Exception
{
- @safe pure nothrow this( string msg )
+ this(string msg) @safe pure nothrow
{
super( msg );
}
}
- static void error( string msg = "Invalid symbol" ) @trusted /* exception only used in module */
+ static noreturn error( string msg = "Invalid symbol" ) @trusted /* exception only used in module */
{
pragma(inline, false); // tame dmd inliner
@@ -110,7 +110,7 @@ pure @safe:
}
- static void overflow( string msg = "Buffer overflow" ) @trusted /* exception only used in module */
+ static noreturn overflow( string msg = "Buffer overflow" ) @trusted /* exception only used in module */
{
pragma(inline, false); // tame dmd inliner
@@ -156,7 +156,6 @@ pure @safe:
if (val >= '0' && val <= '9')
return cast(ubyte)(val - '0');
error();
- return 0;
}
@@ -253,23 +252,22 @@ pure @safe:
put(", ");
}
- char[] put(char c) return scope
+ void put(char c) return scope
{
char[1] val = c;
- return put(val[]);
+ put(val[]);
}
- char[] put( scope const(char)[] val ) return scope
+ void put(scope const(char)[] val) return scope
{
pragma(inline, false); // tame dmd inliner
- if ( val.length )
- {
- if ( !contains( dst[0 .. len], val ) )
- return append( val );
- return shift( val );
- }
- return null;
+ if (!val.length) return;
+
+ if (!contains(dst[0 .. len], val))
+ append(val);
+ else
+ shift(val);
}
@@ -948,18 +946,19 @@ pure @safe:
return dst[beg .. len];
case 'D': // TypeDelegate (D TypeFunction)
popFront();
- auto modbeg = len;
- parseModifier();
- auto modend = len;
+ auto modifiers = parseModifier();
if ( front == 'Q' )
parseBackrefType( () => parseTypeFunction( name, IsDelegate.yes ) );
else
parseTypeFunction( name, IsDelegate.yes );
- if (modend > modbeg)
+ if (modifiers)
{
- // move modifiers behind the function arguments
- shift(dst[modend-1 .. modend]); // trailing space
- shift(dst[modbeg .. modend-1]);
+ // write modifiers behind the function arguments
+ while (auto str = typeCtors.toStringConsume(modifiers))
+ {
+ put(' ');
+ put(str);
+ }
}
return dst[beg .. len];
case 'n': // TypeNone (n)
@@ -1009,7 +1008,6 @@ pure @safe:
}
}
error();
- return null;
}
}
@@ -1110,43 +1108,44 @@ pure @safe:
}
}
- void parseModifier()
+ /// Returns: Flags of `TypeCtor`
+ ushort parseModifier()
{
+ TypeCtor res = TypeCtor.None;
switch ( front )
{
case 'y':
popFront();
- put( "immutable " );
- break;
+ return TypeCtor.Immutable;
case 'O':
popFront();
- put( "shared " );
- if ( front == 'x' )
+ res |= TypeCtor.Shared;
+ if (front == 'x')
goto case 'x';
- if ( front == 'N' )
+ if (front == 'N')
goto case 'N';
- break;
+ return TypeCtor.Shared;
case 'N':
- if ( peek( 1 ) != 'g' )
- break;
+ if (peek( 1 ) != 'g')
+ return res;
popFront();
popFront();
- put( "inout " );
+ res |= TypeCtor.InOut;
if ( front == 'x' )
goto case 'x';
- break;
+ return res;
case 'x':
popFront();
- put( "const " );
- break;
- default: break;
+ res |= TypeCtor.Const;
+ return res;
+ default: return TypeCtor.None;
}
}
- void parseFuncAttr()
+ ushort parseFuncAttr()
{
// FuncAttrs
- breakFuncAttrs:
+ ushort result;
while ('N' == front)
{
popFront();
@@ -1154,27 +1153,27 @@ pure @safe:
{
case 'a': // FuncAttrPure
popFront();
- put( "pure " );
+ result |= FuncAttributes.Pure;
continue;
case 'b': // FuncAttrNoThrow
popFront();
- put( "nothrow " );
+ result |= FuncAttributes.Nothrow;
continue;
case 'c': // FuncAttrRef
popFront();
- put( "ref " );
+ result |= FuncAttributes.Ref;
continue;
case 'd': // FuncAttrProperty
popFront();
- put( "@property " );
+ result |= FuncAttributes.Property;
continue;
case 'e': // FuncAttrTrusted
popFront();
- put( "@trusted " );
+ result |= FuncAttributes.Trusted;
continue;
case 'f': // FuncAttrSafe
popFront();
- put( "@safe " );
+ result |= FuncAttributes.Safe;
continue;
case 'g':
case 'h':
@@ -1188,27 +1187,42 @@ pure @safe:
// if we see these, then we know we're really in
// the parameter list. Rewind and break.
pos--;
- break breakFuncAttrs;
+ return result;
case 'i': // FuncAttrNogc
popFront();
- put( "@nogc " );
+ result |= FuncAttributes.NoGC;
continue;
case 'j': // FuncAttrReturn
popFront();
- put( "return " );
+ if (this.peek(0) == 'N' && this.peek(1) == 'l')
+ {
+ result |= FuncAttributes.ReturnScope;
+ popFront();
+ popFront();
+ } else {
+ result |= FuncAttributes.Return;
+ }
continue;
case 'l': // FuncAttrScope
popFront();
- put( "scope " );
+ if (this.peek(0) == 'N' && this.peek(1) == 'j')
+ {
+ result |= FuncAttributes.ScopeReturn;
+ popFront();
+ popFront();
+ } else {
+ result |= FuncAttributes.Scope;
+ }
continue;
case 'm': // FuncAttrLive
popFront();
- put( "@live " );
+ result |= FuncAttributes.Live;
continue;
default:
error();
}
}
+ return result;
}
void parseFuncArguments() scope
@@ -1346,19 +1360,20 @@ pure @safe:
auto beg = len;
parseCallConvention();
- auto attrbeg = len;
- parseFuncAttr();
+ auto attributes = parseFuncAttr();
auto argbeg = len;
put( '(' );
parseFuncArguments();
put( ')' );
- if (attrbeg < argbeg)
+ if (attributes)
{
- // move function attributes behind arguments
- shift( dst[argbeg - 1 .. argbeg] ); // trailing space
- shift( dst[attrbeg .. argbeg - 1] ); // attributes
- argbeg = attrbeg;
+ // write function attributes behind arguments
+ while (auto str = funcAttrs.toStringConsume(attributes))
+ {
+ put(' ');
+ put(str);
+ }
}
auto retbeg = len;
parseType();
@@ -1900,21 +1915,26 @@ pure @safe:
{
// do not emit "needs this"
popFront();
- parseModifier();
+ auto modifiers = parseModifier();
+ while (auto str = typeCtors.toStringConsume(modifiers))
+ {
+ put(str);
+ put(' ');
+ }
}
if ( isCallConvention( front ) )
{
// we don't want calling convention and attributes in the qualified name
parseCallConvention();
- parseFuncAttr();
- if ( keepAttr )
- {
+ auto attributes = parseFuncAttr();
+ if (keepAttr) {
+ while (auto str = funcAttrs.toStringConsume(attributes))
+ {
+ put(str);
+ put(' ');
+ }
attr = dst[prevlen .. len];
}
- else
- {
- len = prevlen;
- }
put( '(' );
parseFuncArguments();
@@ -2637,6 +2657,12 @@ else
["_D4test4rrs1FNkMJPiZv", "void test.rrs1(return scope out int*)"],
["_D4test4rrs1FNkMKPiZv", "void test.rrs1(return scope ref int*)"],
["_D4test4rrs1FNkMPiZv", "void test.rrs1(return scope int*)"],
+
+ // `scope` and `return` combinations
+ ["_D3foo3Foo3barMNgFNjNlNfZNgPv", "inout return scope @safe inout(void*) foo.Foo.bar()"],
+ ["_D3foo3FooQiMNgFNlNfZv", "inout scope @safe void foo.Foo.foo()"],
+ ["_D3foo3Foo4foorMNgFNjNfZv", "inout return @safe void foo.Foo.foor()"],
+ ["_D3foo3Foo3rabMNgFNlNjNfZv", "inout scope return @safe void foo.Foo.rab()"],
];
@@ -2720,7 +2746,12 @@ unittest
}
/*
+ * Expand an OMF, DMD-generated compressed identifier into its full form
*
+ * This function only has a visible effect for OMF binaries (Win32),
+ * as compression is otherwise not used.
+ *
+ * See_Also: `compiler/src/dmd/backend/compress.d`
*/
string decodeDmdString( const(char)[] ln, ref size_t p ) nothrow pure @safe
{
@@ -2781,3 +2812,92 @@ extern (C) private
errno = err;
}
}
+
+private struct ManglingFlagInfo
+{
+ /// The flag value to use
+ ushort flag;
+
+ /// Human-readable representation
+ string value;
+}
+
+private enum TypeCtor : ushort {
+ None = 0,
+ //// 'x'
+ Const = (1 << 1),
+ /// 'y'
+ Immutable = (1 << 2),
+ /// 'O'
+ Shared = (1 << 3),
+ ///
+ InOut = (1 << 4),
+}
+
+private immutable ManglingFlagInfo[] typeCtors = [
+ ManglingFlagInfo(TypeCtor.Immutable, "immutable"),
+ ManglingFlagInfo(TypeCtor.Shared, "shared"),
+ ManglingFlagInfo(TypeCtor.InOut, "inout"),
+ ManglingFlagInfo(TypeCtor.Const, "const"),
+];
+
+private enum FuncAttributes : ushort {
+ None = 0,
+ //// 'a'
+ Pure = (1 << 1),
+ //// 'b'
+ Nothrow = (1 << 2),
+ //// 'c'
+ Ref = (1 << 3),
+ //// 'd'
+ Property = (1 << 4),
+ //// 'e'
+ Trusted = (1 << 5),
+ //// 'f'
+ Safe = (1 << 6),
+ //// 'i'
+ NoGC = (1 << 7),
+ //// 'j'
+ Return = (1 << 8),
+ //// 'l'
+ Scope = (1 << 9),
+ //// 'm'
+ Live = (1 << 10),
+
+ /// Their order matter
+ ReturnScope = (1 << 11),
+ ScopeReturn = (1 << 12),
+}
+
+// The order in which we process is the same as in compiler/dmd/src/dmangle.d
+private immutable ManglingFlagInfo[] funcAttrs = [
+ ManglingFlagInfo(FuncAttributes.Pure, "pure"),
+ ManglingFlagInfo(FuncAttributes.Nothrow, "nothrow"),
+ ManglingFlagInfo(FuncAttributes.Ref, "ref"),
+ ManglingFlagInfo(FuncAttributes.Property, "@property"),
+ ManglingFlagInfo(FuncAttributes.NoGC, "@nogc"),
+
+ ManglingFlagInfo(FuncAttributes.ReturnScope, "return scope"),
+ ManglingFlagInfo(FuncAttributes.ScopeReturn, "scope return"),
+
+ ManglingFlagInfo(FuncAttributes.Return, "return"),
+ ManglingFlagInfo(FuncAttributes.Scope, "scope"),
+
+ ManglingFlagInfo(FuncAttributes.Live, "@live"),
+ ManglingFlagInfo(FuncAttributes.Trusted, "@trusted"),
+ ManglingFlagInfo(FuncAttributes.Safe, "@safe"),
+];
+
+private string toStringConsume (immutable ManglingFlagInfo[] infos, ref ushort base)
+ @safe pure nothrow @nogc
+{
+ foreach (const ref info; infos)
+ {
+ if ((base & info.flag) == info.flag)
+ {
+ base &= ~info.flag;
+ return info.value;
+ }
+ }
+ return null;
+}
diff --git a/libphobos/libdruntime/core/internal/array/arrayassign.d b/libphobos/libdruntime/core/internal/array/arrayassign.d
index 6132e68..6e3c1fd 100644
--- a/libphobos/libdruntime/core/internal/array/arrayassign.d
+++ b/libphobos/libdruntime/core/internal/array/arrayassign.d
@@ -302,3 +302,151 @@ Tarr _d_arrayassign_r(Tarr : T[], T)(return scope Tarr to, scope Tarr from) @tru
assert(!didThrow);
assert(counter == 0);
}
+
+/**
+ * Sets all elements of an array to a single value. Takes into account postblits,
+ * copy constructors and destructors. For Plain Old Data elements,`rt/memset.d`
+ * is used.
+ *
+ * ---
+ * struct S
+ * {
+ * ~this() {} // destructor, so not Plain Old Data
+ * }
+ *
+ * void main()
+ * {
+ * S[3] arr;
+ * S value;
+ *
+ * arr = value;
+ * // Generates:
+ * // _d_arraysetassign(arr[], value), arr;
+ * }
+ * ---
+ *
+ * Params:
+ * to = destination array
+ * value = the element to set
+ * Returns:
+ * `to`
+ */
+Tarr _d_arraysetassign(Tarr : T[], T)(return scope Tarr to, scope ref T value) @trusted
+{
+ import core.internal.traits : Unqual;
+ import core.lifetime : copyEmplace;
+ import core.stdc.string : memcpy;
+
+ enum elemSize = T.sizeof;
+ void[elemSize] tmp = void;
+
+ foreach (ref dst; to)
+ {
+ memcpy(&tmp, cast(void*) &dst, elemSize);
+ // Use `memcpy` if `T` has a `@disable`d postblit.
+ static if (__traits(isCopyable, T))
+ copyEmplace(value, dst);
+ else
+ memcpy(cast(void*) &value, cast(void*) &dst, elemSize);
+ auto elem = cast(Unqual!T*) &tmp;
+ destroy(*elem);
+ }
+
+ return to;
+}
+
+// postblit and destructor
+@safe unittest
+{
+ string ops;
+ struct S
+ {
+ int val;
+ this(this) { ops ~= "="; }
+ ~this() { ops ~= "~"; }
+ }
+
+ S[4] arr;
+ S s = S(1234);
+ _d_arraysetassign(arr[], s);
+ assert(ops == "=~=~=~=~");
+ assert(arr == [S(1234), S(1234), S(1234), S(1234)]);
+}
+
+// copy constructor
+@safe unittest
+{
+ string ops;
+ struct S
+ {
+ int val;
+ this(const scope ref S rhs)
+ {
+ val = rhs.val;
+ ops ~= "=";
+ }
+ ~this() { ops ~= "~"; }
+ }
+
+ S[4] arr;
+ S s = S(1234);
+ _d_arraysetassign(arr[], s);
+ assert(ops == "=~=~=~=~");
+ assert(arr == [S(1234), S(1234), S(1234), S(1234)]);
+}
+
+// throwing and `nothrow`
+@safe nothrow unittest
+{
+ // Test that throwing works
+ bool didThrow;
+ int counter;
+ struct Throw
+ {
+ int val;
+ this(this)
+ {
+ counter++;
+ if (counter == 2)
+ throw new Exception("Oh no.");
+ }
+ }
+
+ try
+ {
+ Throw[4] a;
+ Throw b = Throw(1);
+ _d_arraysetassign(a[], b);
+ }
+ catch (Exception)
+ {
+ didThrow = true;
+ }
+ assert(didThrow);
+ assert(counter == 2);
+
+ // Test that `nothrow` works
+ didThrow = false;
+ counter = 0;
+ struct NoThrow
+ {
+ int val;
+ this(this) { counter++; }
+ }
+
+ try
+ {
+ NoThrow[4] a;
+ NoThrow b = NoThrow(1);
+ _d_arraysetassign(a[], b);
+ foreach (ref e; a)
+ assert(e == NoThrow(1));
+ }
+ catch (Exception)
+ {
+ didThrow = true;
+ }
+ assert(!didThrow);
+ // The array `a` is destroyed when the `try` block ends.
+ assert(counter == 4);
+}
diff --git a/libphobos/libdruntime/core/internal/dassert.d b/libphobos/libdruntime/core/internal/dassert.d
index 07486c2..dbad0e6 100644
--- a/libphobos/libdruntime/core/internal/dassert.d
+++ b/libphobos/libdruntime/core/internal/dassert.d
@@ -518,7 +518,7 @@ private auto assumeFakeAttributes(T)(T t) @trusted
}
/// Wrapper for `miniFormat` which assumes that the implementation is `@safe`, `@nogc`, ...
-/// s.t. it does not violate the constraints of the the function containing the `assert`.
+/// s.t. it does not violate the constraints of the function containing the `assert`.
private string miniFormatFakeAttributes(T)(const scope ref T t)
{
alias miniT = miniFormat!T;
diff --git a/libphobos/libdruntime/core/internal/utf.d b/libphobos/libdruntime/core/internal/utf.d
index 27bf7f2b..9808b99 100644
--- a/libphobos/libdruntime/core/internal/utf.d
+++ b/libphobos/libdruntime/core/internal/utf.d
@@ -567,17 +567,22 @@ ubyte codeLength(C)(dchar c)
/***********************************
Checks to see if string is well formed or not. $(D S) can be an array
- of $(D char), $(D wchar), or $(D dchar). Throws a $(D UtfException)
- if it is not. Use to check all untrusted input for correctness.
+ of $(D char), $(D wchar), or $(D dchar). Returns $(D false) if it is not.
+ Use to check all untrusted input for correctness.
*/
-@safe pure
-void validate(S)(const scope S s)
+@safe pure nothrow
+bool isValidString(S)(const scope S s)
{
auto len = s.length;
for (size_t i = 0; i < len; )
{
- decode(s, i);
+ try
+ decode(s, i);
+ catch (Exception e)
+ return false;
}
+
+ return true;
}
/* =================== Conversion to UTF8 ======================= */
@@ -626,7 +631,7 @@ char[] toUTF8(return scope char[] buf, dchar c)
string toUTF8(return scope string s)
in
{
- validate(s);
+ assert(isValidString(s));
}
do
{
@@ -787,7 +792,7 @@ wptr toUTF16z(const scope char[] s)
wstring toUTF16(return scope wstring s)
in
{
- validate(s);
+ assert(isValidString(s));
}
do
{
@@ -867,7 +872,7 @@ dstring toUTF32(const scope wchar[] s)
dstring toUTF32(return scope dstring s)
in
{
- validate(s);
+ assert(isValidString(s));
}
do
{
diff --git a/libphobos/libdruntime/core/simd.d b/libphobos/libdruntime/core/simd.d
index f1bf59b..c2b343a 100644
--- a/libphobos/libdruntime/core/simd.d
+++ b/libphobos/libdruntime/core/simd.d
@@ -704,7 +704,7 @@ version (D_SIMD)
// store `v` to location pointed to by `d`
storeUnaligned(cast(T*)d, v);
- // check that the the data was stored correctly
+ // check that the data was stored correctly
foreach (j; 0..T.sizeof)
{
assert(ptrToV[j] == d[j]);
diff --git a/libphobos/libdruntime/core/stdc/fenv.d b/libphobos/libdruntime/core/stdc/fenv.d
index 88123fb..5242ba9 100644
--- a/libphobos/libdruntime/core/stdc/fenv.d
+++ b/libphobos/libdruntime/core/stdc/fenv.d
@@ -483,7 +483,7 @@ else version (CRuntime_UClibc)
alias fexcept_t = ushort;
}
- else version (MIPS32)
+ else version (MIPS_Any)
{
struct fenv_t
{
diff --git a/libphobos/libdruntime/core/stdc/math.d b/libphobos/libdruntime/core/stdc/math.d
index 0393ea5..2666c95 100644
--- a/libphobos/libdruntime/core/stdc/math.d
+++ b/libphobos/libdruntime/core/stdc/math.d
@@ -106,21 +106,14 @@ else version (CRuntime_Bionic)
}
else version (CRuntime_UClibc)
{
- version (X86)
- {
- ///
- enum int FP_ILOGB0 = int.min;
- ///
- enum int FP_ILOGBNAN = int.min;
- }
- else version (X86_64)
+ version (X86_Any)
{
///
enum int FP_ILOGB0 = int.min;
///
enum int FP_ILOGBNAN = int.min;
}
- else version (MIPS32)
+ else version (MIPS_Any)
{
///
enum int FP_ILOGB0 = -int.max;
diff --git a/libphobos/libdruntime/core/sys/darwin/mach/loader.d b/libphobos/libdruntime/core/sys/darwin/mach/loader.d
index f46698c..7713eea 100644
--- a/libphobos/libdruntime/core/sys/darwin/mach/loader.d
+++ b/libphobos/libdruntime/core/sys/darwin/mach/loader.d
@@ -1803,7 +1803,7 @@ version (CoreDdoc)
* image. The isub_image field is an index into the sub-images
* (sub-frameworks and sub-umbrellas list) that made up the two-level image
* that the undefined symbol was found in when it was built by the static
- * link editor. If isub-image is 0 the the symbol is expected to be defined
+ * link editor. If isub-image is 0 the symbol is expected to be defined
* in library and not in the sub-images. If isub-image is non-zero it is an
* index into the array of sub-images for the umbrella with the first index
* in the sub-images being 1. The array of sub-images is the ordered list of
diff --git a/libphobos/libdruntime/core/sys/linux/fcntl.d b/libphobos/libdruntime/core/sys/linux/fcntl.d
index 11c3745..89dc019 100644
--- a/libphobos/libdruntime/core/sys/linux/fcntl.d
+++ b/libphobos/libdruntime/core/sys/linux/fcntl.d
@@ -42,7 +42,7 @@ These cmd values will set locks that conflict with process-associated
record locks, but are "owned" by the open file description, not the
process. This means that they are inherited across fork() like BSD (flock)
locks, and they are only released automatically when the last reference to
-the the open file against which they were acquired is put.
+the open file against which they were acquired is put.
*/
enum
diff --git a/libphobos/libdruntime/core/sys/posix/dlfcn.d b/libphobos/libdruntime/core/sys/posix/dlfcn.d
index a9519ca..04a8e8b 100644
--- a/libphobos/libdruntime/core/sys/posix/dlfcn.d
+++ b/libphobos/libdruntime/core/sys/posix/dlfcn.d
@@ -377,7 +377,7 @@ else version (CRuntime_Musl)
}
else version (CRuntime_UClibc)
{
- version (X86_64)
+ version (X86_Any)
{
enum RTLD_LAZY = 0x0001;
enum RTLD_NOW = 0x0002;
@@ -387,7 +387,7 @@ else version (CRuntime_UClibc)
enum RTLD_LOCAL = 0;
enum RTLD_NODELETE = 0x01000;
}
- else version (MIPS32)
+ else version (MIPS_Any)
{
enum RTLD_LAZY = 0x0001;
enum RTLD_NOW = 0x0002;
diff --git a/libphobos/libdruntime/core/sys/posix/mqueue.d b/libphobos/libdruntime/core/sys/posix/mqueue.d
index 2f1a8c6..7085fc4 100644
--- a/libphobos/libdruntime/core/sys/posix/mqueue.d
+++ b/libphobos/libdruntime/core/sys/posix/mqueue.d
@@ -147,7 +147,7 @@ int mq_notify (mqd_t mqdes, const(sigevent)* notification);
/**
- * Receive the oldest message with the highest priority the the message queue
+ * Receive the oldest message with the highest priority the message queue
*
* Params:
* mqdes = Message queue descriptor.
@@ -164,7 +164,7 @@ ssize_t mq_receive (mqd_t mqdes, char* msg_ptr, size_t msg_len, uint* msg_prio);
/**
- * Receive the oldest message with the highest priority the the message queue,
+ * Receive the oldest message with the highest priority the message queue,
* wait up to a certain timeout.
*
* Params:
diff --git a/libphobos/libdruntime/core/sys/posix/setjmp.d b/libphobos/libdruntime/core/sys/posix/setjmp.d
index 91e3a19..5a15d82 100644
--- a/libphobos/libdruntime/core/sys/posix/setjmp.d
+++ b/libphobos/libdruntime/core/sys/posix/setjmp.d
@@ -370,6 +370,22 @@ else version (CRuntime_UClibc)
double[6] __fpregs;
}
}
+ else version (MIPS64)
+ {
+ struct __jmp_buf
+ {
+ long __pc;
+ long __sp;
+ long[8] __regs;
+ long __fp;
+ long __gp;
+ int __fpc_csr;
+ version (MIPS_N64)
+ double[8] __fpregs;
+ else
+ double[6] __fpregs;
+ }
+ }
else
static assert(0, "unimplemented");
diff --git a/libphobos/libdruntime/core/sys/posix/stdlib.d b/libphobos/libdruntime/core/sys/posix/stdlib.d
index 4c10d4e..df96a3d 100644
--- a/libphobos/libdruntime/core/sys/posix/stdlib.d
+++ b/libphobos/libdruntime/core/sys/posix/stdlib.d
@@ -95,44 +95,44 @@ int posix_memalign(void**, size_t, size_t);
version (CRuntime_Glibc)
{
- int posix_memalign(void**, size_t, size_t);
+ int posix_memalign(scope void**, size_t, size_t) pure;
}
else version (FreeBSD)
{
- int posix_memalign(void**, size_t, size_t);
+ int posix_memalign(scope void**, size_t, size_t) pure;
}
else version (NetBSD)
{
- int posix_memalign(void**, size_t, size_t);
+ int posix_memalign(scope void**, size_t, size_t) pure;
}
else version (OpenBSD)
{
- int posix_memalign(void**, size_t, size_t);
+ int posix_memalign(scope void**, size_t, size_t) pure;
}
else version (DragonFlyBSD)
{
- int posix_memalign(void**, size_t, size_t);
+ int posix_memalign(scope void**, size_t, size_t) pure;
}
else version (Solaris)
{
- int posix_memalign(void**, size_t, size_t);
+ int posix_memalign(scope void**, size_t, size_t) pure;
}
else version (Darwin)
{
- int posix_memalign(void**, size_t, size_t);
+ int posix_memalign(scope void**, size_t, size_t) pure;
}
else version (CRuntime_Bionic)
{
// Added since Lollipop
- int posix_memalign(void**, size_t, size_t);
+ int posix_memalign(scope void**, size_t, size_t) pure;
}
else version (CRuntime_Musl)
{
- int posix_memalign(void**, size_t, size_t);
+ int posix_memalign(scope void**, size_t, size_t) pure;
}
else version (CRuntime_UClibc)
{
- int posix_memalign(void**, size_t, size_t);
+ int posix_memalign(scope void**, size_t, size_t) pure;
}
//
diff --git a/libphobos/libdruntime/core/sys/posix/sys/types.d b/libphobos/libdruntime/core/sys/posix/sys/types.d
index ec229dd..3e515c4 100644
--- a/libphobos/libdruntime/core/sys/posix/sys/types.d
+++ b/libphobos/libdruntime/core/sys/posix/sys/types.d
@@ -1140,6 +1140,18 @@ else version (CRuntime_UClibc)
enum __SIZEOF_PTHREAD_BARRIER_T = 20;
enum __SIZEOF_PTHREAD_BARRIERATTR_T = 4;
}
+ else version (MIPS64)
+ {
+ enum __SIZEOF_PTHREAD_ATTR_T = 56;
+ enum __SIZEOF_PTHREAD_MUTEX_T = 40;
+ enum __SIZEOF_PTHREAD_MUTEXATTR_T = 4;
+ enum __SIZEOF_PTHREAD_COND_T = 48;
+ enum __SIZEOF_PTHREAD_CONDATTR_T = 4;
+ enum __SIZEOF_PTHREAD_RWLOCK_T = 56;
+ enum __SIZEOF_PTHREAD_RWLOCKATTR_T = 8;
+ enum __SIZEOF_PTHREAD_BARRIER_T = 32;
+ enum __SIZEOF_PTHREAD_BARRIERATTR_T = 4;
+ }
else version (ARM)
{
enum __SIZEOF_PTHREAD_ATTR_T = 36;
diff --git a/libphobos/libdruntime/core/sys/windows/stacktrace.d b/libphobos/libdruntime/core/sys/windows/stacktrace.d
index 2922e54..7982085 100644
--- a/libphobos/libdruntime/core/sys/windows/stacktrace.d
+++ b/libphobos/libdruntime/core/sys/windows/stacktrace.d
@@ -288,7 +288,8 @@ private:
auto res = formatStackFrame(pc);
res ~= " in ";
const(char)[] tempSymName = symName[0 .. strlen(symName)];
- //Deal with dmd mangling of long names
+ // Deal with dmd mangling of long names for OMF 32 bits builds
+ // Note that `target.d` only defines `CRuntime_DigitalMars` for OMF builds
version (CRuntime_DigitalMars)
{
size_t decodeIndex = 0;
diff --git a/libphobos/libdruntime/core/time.d b/libphobos/libdruntime/core/time.d
index 9b8391e..ea163a0 100644
--- a/libphobos/libdruntime/core/time.d
+++ b/libphobos/libdruntime/core/time.d
@@ -3362,7 +3362,7 @@ struct TickDuration
$(D gettimeofday) (the decision is made when $(D TickDuration) is
compiled), which unfortunately, is not monotonic, but if
$(D mach_absolute_time) and $(D clock_gettime) aren't available, then
- $(D gettimeofday) is the the best that there is.
+ $(D gettimeofday) is the best that there is.
$(RED Warning):
On some systems, the monotonic clock may stop counting when
diff --git a/libphobos/libdruntime/object.d b/libphobos/libdruntime/object.d
index d842499..83351f2 100644
--- a/libphobos/libdruntime/object.d
+++ b/libphobos/libdruntime/object.d
@@ -2700,7 +2700,7 @@ class Exception : Throwable
* Creates a new instance of Exception. The nextInChain parameter is used
* internally and should always be $(D null) when passed by user code.
* This constructor does not automatically throw the newly-created
- * Exception; the $(D throw) statement should be used for that purpose.
+ * Exception; the $(D throw) expression should be used for that purpose.
*/
@nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null)
{
@@ -4617,6 +4617,7 @@ public import core.internal.array.construction : _d_arrayctor;
public import core.internal.array.construction : _d_arraysetctor;
public import core.internal.array.arrayassign : _d_arrayassign_l;
public import core.internal.array.arrayassign : _d_arrayassign_r;
+public import core.internal.array.arrayassign : _d_arraysetassign;
public import core.internal.array.capacity: _d_arraysetlengthTImpl;
public import core.internal.dassert: _d_assert_fail;
diff --git a/libphobos/libdruntime/rt/arrayassign.d b/libphobos/libdruntime/rt/arrayassign.d
deleted file mode 100644
index c9e2b15..0000000
--- a/libphobos/libdruntime/rt/arrayassign.d
+++ /dev/null
@@ -1,60 +0,0 @@
-/**
- * Implementation of array assignment support routines.
- *
- *
- * Copyright: Copyright Digital Mars 2010 - 2016.
- * License: Distributed under the
- * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
- * Authors: Walter Bright, Kenji Hara
- * Source: $(DRUNTIMESRC rt/_arrayassign.d)
- */
-
-module rt.arrayassign;
-
-private
-{
- import core.internal.util.array;
- import core.stdc.string;
- import core.stdc.stdlib;
- debug(PRINTF) import core.stdc.stdio;
-}
-
-/**
-Set all elements of an array to a single value.
-
----
-p[0 .. count] = value;
----
-
-Takes into account postblits and destructors, for Plain Old Data elements,
-`rt/memset.d` is used.
-
-Params:
- p = pointer to start of array
- value = bytes of the element to set. Size is derived from `ti`.
- count = amount of array elements to set
- ti = type info of the array element type / `value`
-Returns: `p`
-*/
-extern (C) void* _d_arraysetassign(void* p, void* value, int count, TypeInfo ti)
-{
- void* pstart = p;
-
- auto element_size = ti.tsize;
-
- // Need a temporary buffer tmp[] big enough to hold one element
- immutable maxAllocaSize = 512;
- void *ptmp = (element_size > maxAllocaSize) ? malloc(element_size) : alloca(element_size);
-
- foreach (i; 0 .. count)
- {
- memcpy(ptmp, p, element_size);
- memcpy(p, value, element_size);
- ti.postblit(p);
- ti.destroy(ptmp);
- p += element_size;
- }
- if (element_size > maxAllocaSize)
- free(ptmp);
- return pstart;
-}
diff --git a/libphobos/libdruntime/rt/lifetime.d b/libphobos/libdruntime/rt/lifetime.d
index 026001f..f2515c3 100644
--- a/libphobos/libdruntime/rt/lifetime.d
+++ b/libphobos/libdruntime/rt/lifetime.d
@@ -1232,61 +1232,6 @@ debug(PRINTF)
/**
*
*/
-extern (C) void _d_delarray_t(void[]* p, const TypeInfo_Struct ti) @weak
-{
- if (p)
- {
- auto bic = __getBlkInfo(p.ptr);
- auto info = bic ? *bic : GC.query(p.ptr);
-
- if (info.base && (info.attr & BlkAttr.APPENDABLE))
- {
- if (ti) // ti non-null only if ti is a struct with dtor
- {
- void* start = __arrayStart(info);
- size_t length = __arrayAllocLength(info, ti);
- finalize_array(start, length, ti);
- }
-
- // if p is in the cache, clear it there as well
- if (bic)
- bic.base = null;
-
- GC.free(info.base);
- *p = null;
- }
- }
-}
-
-deprecated unittest
-{
- __gshared size_t countDtor = 0;
- struct S
- {
- int x;
- ~this() { countDtor++; }
- }
- // destroy large array with x.ptr not base address of allocation
- auto x = new S[10000];
- void* p = x.ptr;
- assert(GC.addrOf(p) != null);
- _d_delarray_t(cast(void[]*)&x, typeid(typeof(x[0]))); // delete x;
- assert(GC.addrOf(p) == null);
- assert(countDtor == 10000);
-
- // destroy full array even if only slice passed
- auto y = new S[400];
- auto z = y[200 .. 300];
- p = z.ptr;
- assert(GC.addrOf(p) != null);
- _d_delarray_t(cast(void[]*)&z, typeid(typeof(z[0]))); // delete z;
- assert(GC.addrOf(p) == null);
- assert(countDtor == 10000 + 400);
-}
-
-/**
- *
- */
extern (C) void _d_delmemory(void* *p) @weak
{
if (*p)
@@ -2755,11 +2700,6 @@ deprecated unittest
}
dtorCount = 0;
- S1[] arr1 = new S1[7];
- _d_delarray_t(cast(void[]*)&arr1, typeid(typeof(arr1[0]))); // delete arr1;
- assert(dtorCount == 7);
-
- dtorCount = 0;
S1* s2 = new S1;
GC.runFinalizers((cast(char*)(typeid(S1).xdtor))[0..1]);
assert(dtorCount == 1);
diff --git a/libphobos/src/MERGE b/libphobos/src/MERGE
index c8a3771..4d63826 100644
--- a/libphobos/src/MERGE
+++ b/libphobos/src/MERGE
@@ -1,4 +1,4 @@
-b578dfad94770574d7e522557a77276c35943daa
+88aa69b14f8a28255a0ac7626f6509a13cfdb67a
The first line of this file holds the git revision number of the last
merge done from the dlang/phobos repository.
diff --git a/libphobos/src/Makefile.am b/libphobos/src/Makefile.am
index 2413024..655c02e 100644
--- a/libphobos/src/Makefile.am
+++ b/libphobos/src/Makefile.am
@@ -97,9 +97,9 @@ PHOBOS_DSOURCES = etc/c/curl.d etc/c/zlib.d std/algorithm/comparison.d \
std/datetime/interval.d std/datetime/package.d \
std/datetime/stopwatch.d std/datetime/systime.d \
std/datetime/timezone.d std/demangle.d std/digest/crc.d \
- std/digest/digest.d std/digest/hmac.d std/digest/md.d \
- std/digest/murmurhash.d std/digest/package.d std/digest/ripemd.d \
- std/digest/sha.d std/encoding.d std/exception.d \
+ std/digest/hmac.d std/digest/md.d std/digest/murmurhash.d \
+ std/digest/package.d std/digest/ripemd.d std/digest/sha.d \
+ std/encoding.d std/exception.d \
std/experimental/allocator/building_blocks/affix_allocator.d \
std/experimental/allocator/building_blocks/aligned_block_list.d \
std/experimental/allocator/building_blocks/allocator_list.d \
@@ -156,6 +156,6 @@ PHOBOS_DSOURCES = etc/c/curl.d etc/c/zlib.d std/algorithm/comparison.d \
std/stdio.d std/string.d std/sumtype.d std/system.d std/traits.d \
std/typecons.d std/typetuple.d std/uni/package.d std/uri.d std/utf.d \
std/uuid.d std/variant.d std/windows/charset.d std/windows/registry.d \
- std/windows/syserror.d std/xml.d std/zip.d std/zlib.d
+ std/windows/syserror.d std/zip.d std/zlib.d
endif
diff --git a/libphobos/src/Makefile.in b/libphobos/src/Makefile.in
index 562a428..a622958 100644
--- a/libphobos/src/Makefile.in
+++ b/libphobos/src/Makefile.in
@@ -179,7 +179,6 @@ am__dirstamp = $(am__leading_dot)dirstamp
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/datetime/timezone.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/demangle.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/digest/crc.lo \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/digest/digest.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/digest/hmac.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/digest/md.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/digest/murmurhash.lo \
@@ -291,8 +290,7 @@ am__dirstamp = $(am__leading_dot)dirstamp
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/windows/charset.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/windows/registry.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/windows/syserror.lo \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/xml.lo std/zip.lo \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/zlib.lo
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/zip.lo std/zlib.lo
am__objects_2 = $(am__objects_1)
am__objects_3 = $(am__objects_2)
am_libgphobos_la_OBJECTS = $(am__objects_3)
@@ -562,9 +560,9 @@ libgphobos_la_LINK = $(LIBTOOL) --tag=D $(libgphobos_la_LIBTOOLFLAGS) \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/datetime/interval.d std/datetime/package.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/datetime/stopwatch.d std/datetime/systime.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/datetime/timezone.d std/demangle.d std/digest/crc.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/digest/digest.d std/digest/hmac.d std/digest/md.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/digest/murmurhash.d std/digest/package.d std/digest/ripemd.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/digest/sha.d std/encoding.d std/exception.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/digest/hmac.d std/digest/md.d std/digest/murmurhash.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/digest/package.d std/digest/ripemd.d std/digest/sha.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/encoding.d std/exception.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/allocator/building_blocks/affix_allocator.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/allocator/building_blocks/aligned_block_list.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/allocator/building_blocks/allocator_list.d \
@@ -621,7 +619,7 @@ libgphobos_la_LINK = $(LIBTOOL) --tag=D $(libgphobos_la_LIBTOOLFLAGS) \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/stdio.d std/string.d std/sumtype.d std/system.d std/traits.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/typecons.d std/typetuple.d std/uni/package.d std/uri.d std/utf.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/uuid.d std/variant.d std/windows/charset.d std/windows/registry.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/windows/syserror.d std/xml.d std/zip.d std/zlib.d
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/windows/syserror.d std/zip.d std/zlib.d
# Source file definitions. Boring stuff, auto-generated with
@@ -754,7 +752,6 @@ std/digest/$(am__dirstamp):
@$(MKDIR_P) std/digest
@: > std/digest/$(am__dirstamp)
std/digest/crc.lo: std/digest/$(am__dirstamp)
-std/digest/digest.lo: std/digest/$(am__dirstamp)
std/digest/hmac.lo: std/digest/$(am__dirstamp)
std/digest/md.lo: std/digest/$(am__dirstamp)
std/digest/murmurhash.lo: std/digest/$(am__dirstamp)
@@ -964,7 +961,6 @@ std/windows/$(am__dirstamp):
std/windows/charset.lo: std/windows/$(am__dirstamp)
std/windows/registry.lo: std/windows/$(am__dirstamp)
std/windows/syserror.lo: std/windows/$(am__dirstamp)
-std/xml.lo: std/$(am__dirstamp)
std/zip.lo: std/$(am__dirstamp)
std/zlib.lo: std/$(am__dirstamp)
diff --git a/libphobos/src/index.dd b/libphobos/src/index.dd
index 45c248e..4812225 100644
--- a/libphobos/src/index.dd
+++ b/libphobos/src/index.dd
@@ -71,10 +71,6 @@ $(BOOKTABLE ,
$(TD Read/write data in JSON format.)
)
$(TR
- $(TDNW $(MREF std,xml))
- $(TD Read/write data in XML format.)
- )
- $(TR
$(TDNW $(MREF std,zip))
$(TD Read/write data in the ZIP archive format.)
)
diff --git a/libphobos/src/std/algorithm/comparison.d b/libphobos/src/std/algorithm/comparison.d
index b810fbb..5ecb4f6 100644
--- a/libphobos/src/std/algorithm/comparison.d
+++ b/libphobos/src/std/algorithm/comparison.d
@@ -577,14 +577,24 @@ Returns:
and `T3` are different.
*/
T1 clamp(T1, T2, T3)(T1 val, T2 lower, T3 upper)
-if (is(typeof(val.lessThan(lower) ? lower : val.greaterThan(upper) ? upper : val) : T1))
+if (is(typeof(val.lessThan(lower) ? lower : val.greaterThan(upper) ? upper : val))
+ && (is(T2 : T1) && is(T3 : T1)))
+// cannot use :
+// `if (is(typeof(val.lessThan(lower) ? lower : val.greaterThan(upper) ? upper : val) : T1))
+// because of https://issues.dlang.org/show_bug.cgi?id=16235.
+// Once that is fixed, we can simply use the ternary in both the template constraint
+// and the template body
in
{
assert(!lower.greaterThan(upper), "Lower can't be greater than upper.");
}
do
{
- return val.lessThan(lower) ? lower : val.greaterThan(upper) ? upper : val;
+ if (val.lessThan(lower))
+ return lower;
+ else if (val.greaterThan(upper))
+ return upper;
+ return val;
}
///
@@ -637,6 +647,12 @@ do
assert(x.clamp(lo, hi).y == 42);
}
+// https://issues.dlang.org/show_bug.cgi?id=23268
+@safe pure nothrow @nogc unittest
+{
+ static assert(__traits(compiles, clamp(short.init, short.init, cast(const) short.init)));
+}
+
// cmp
/**********************************
Performs a lexicographical comparison on two
diff --git a/libphobos/src/std/algorithm/iteration.d b/libphobos/src/std/algorithm/iteration.d
index 3e828ce..39eff0d 100644
--- a/libphobos/src/std/algorithm/iteration.d
+++ b/libphobos/src/std/algorithm/iteration.d
@@ -771,6 +771,23 @@ private struct MapResult(alias fun, Range)
assert(dd.length == 4);
}
+// Verify fix for: https://issues.dlang.org/show_bug.cgi?id=16034
+@safe unittest
+{
+ struct One
+ {
+ int entry = 1;
+ @disable this(this);
+ }
+
+ One[] ones = [One(), One()];
+
+ import std.algorithm.comparison : equal;
+
+ assert(ones.map!`a.entry + 1`.equal([2, 2]));
+}
+
+
@safe unittest
{
import std.algorithm.comparison : equal;
diff --git a/libphobos/src/std/algorithm/searching.d b/libphobos/src/std/algorithm/searching.d
index 870b1b4..15f7ca9 100644
--- a/libphobos/src/std/algorithm/searching.d
+++ b/libphobos/src/std/algorithm/searching.d
@@ -5002,7 +5002,7 @@ If set to `OpenRight.yes`, then the interval is open to the right
(last element is not included).
Otherwise if set to `OpenRight.no`, then the interval is closed to the right
-(last element included).
+including the entire sentinel.
*/
alias OpenRight = Flag!"openRight";
@@ -5052,6 +5052,7 @@ if (isInputRange!Range)
static if (!is(Sentinel == void))
private Sentinel _sentinel;
private OpenRight _openRight;
+ private bool _matchStarted;
private bool _done;
static if (!is(Sentinel == void))
@@ -5063,7 +5064,19 @@ if (isInputRange!Range)
_input = input;
_sentinel = sentinel;
_openRight = openRight;
- _done = _input.empty || openRight && predSatisfied();
+ static if (isInputRange!Sentinel)
+ {
+ _matchStarted = predSatisfied();
+ _done = _input.empty || _sentinel.empty || openRight && _matchStarted;
+ if (_matchStarted && !_done && !openRight)
+ {
+ _sentinel.popFront;
+ }
+ }
+ else
+ {
+ _done = _input.empty || openRight && predSatisfied();
+ }
}
private this(Range input, Sentinel sentinel, OpenRight openRight,
bool done)
@@ -5118,9 +5131,32 @@ if (isInputRange!Range)
assert(!empty, "Can not popFront of an empty Until");
if (!_openRight)
{
- _done = predSatisfied();
- _input.popFront();
- _done = _done || _input.empty;
+ static if (isInputRange!Sentinel)
+ {
+ _input.popFront();
+ _done = _input.empty || _sentinel.empty;
+ if (!_done)
+ {
+ if (_matchStarted)
+ {
+ _sentinel.popFront;
+ }
+ else
+ {
+ _matchStarted = predSatisfied();
+ if (_matchStarted)
+ {
+ _sentinel.popFront;
+ }
+ }
+ }
+ }
+ else
+ {
+ _done = predSatisfied();
+ _input.popFront();
+ _done = _done || _input.empty;
+ }
}
else
{
@@ -5212,3 +5248,33 @@ pure @safe unittest
assert(equal(r.save, "foo"));
}
}
+// https://issues.dlang.org/show_bug.cgi?id=14543
+pure @safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.uni : toUpper;
+ assert("one two three".until("two").equal("one "));
+ assert("one two three".until("two", OpenRight.no).equal("one two"));
+
+ assert("one two three".until("two", No.openRight).equal("one two"));
+ assert("one two three".until("two", Yes.openRight).equal("one "));
+
+ assert("one two three".until('t', Yes.openRight).equal("one "));
+ assert("one two three".until("", Yes.openRight).equal(""));
+ assert("one two three".until("", No.openRight).equal(""));
+
+ assert("one two three".until("three", No.openRight).equal("one two three"));
+ assert("one two three".until("three", Yes.openRight).equal("one two "));
+
+ assert("one two three".until("one", No.openRight).equal("one"));
+ assert("one two three".until("one", Yes.openRight).equal(""));
+
+ assert("one two three".until("o", No.openRight).equal("o"));
+ assert("one two three".until("o", Yes.openRight).equal(""));
+
+ assert("one two three".until("", No.openRight).equal(""));
+ assert("one two three".until("", Yes.openRight).equal(""));
+
+ assert("one two three".until!((a,b)=>a.toUpper == b)("TWO", No.openRight).equal("one two"));
+}
+
diff --git a/libphobos/src/std/algorithm/sorting.d b/libphobos/src/std/algorithm/sorting.d
index ee68b23..4fc7ee9 100644
--- a/libphobos/src/std/algorithm/sorting.d
+++ b/libphobos/src/std/algorithm/sorting.d
@@ -1642,9 +1642,9 @@ private void multiSortImpl(Range, SwapStrategy ss, funs...)(Range r)
}
// https://issues.dlang.org/show_bug.cgi?id=16413 - @system comparison function
-@safe unittest
+@system unittest
{
- bool lt(int a, int b) { return a < b; } static @system
+ static @system bool lt(int a, int b) { return a < b; }
auto a = [2, 1];
a.multiSort!(lt, lt);
assert(a == [1, 2]);
diff --git a/libphobos/src/std/compiler.d b/libphobos/src/std/compiler.d
index 2f983c5..4ea5bd7 100644
--- a/libphobos/src/std/compiler.d
+++ b/libphobos/src/std/compiler.d
@@ -28,7 +28,7 @@ immutable
gnu = 2, /// GNU D Compiler (GDC)
llvm = 3, /// LLVM D Compiler (LDC)
dotNET = 4, /// D.NET
- sdc = 5, /// Stupid D Compiler (SDC)
+ sdc = 5, /// Snazzy D Compiler (SDC)
}
/// Which vendor produced this compiler.
diff --git a/libphobos/src/std/concurrency.d b/libphobos/src/std/concurrency.d
index fb383ae..bc53236 100644
--- a/libphobos/src/std/concurrency.d
+++ b/libphobos/src/std/concurrency.d
@@ -257,9 +257,12 @@ private
@property ref ThreadInfo thisInfo() nothrow
{
- if (scheduler is null)
+ import core.atomic : atomicLoad;
+
+ auto localScheduler = atomicLoad(scheduler);
+ if (localScheduler is null)
return ThreadInfo.thisInfo;
- return scheduler.thisInfo;
+ return localScheduler.thisInfo;
}
}
diff --git a/libphobos/src/std/container/array.d b/libphobos/src/std/container/array.d
index 08f9ead..ecc4599 100644
--- a/libphobos/src/std/container/array.d
+++ b/libphobos/src/std/container/array.d
@@ -412,9 +412,9 @@ if (!is(immutable T == immutable bool))
.destroy(e);
static if (hasIndirections!T)
- GC.removeRange(_payload.ptr);
+ GC.removeRange(cast(void*) _payload.ptr);
- free(_payload.ptr);
+ free(cast(void*) _payload.ptr);
}
this(this) @disable;
@@ -489,14 +489,14 @@ if (!is(immutable T == immutable bool))
auto newPayload = newPayloadPtr[0 .. oldLength];
// copy old data over to new array
- memcpy(newPayload.ptr, _payload.ptr, T.sizeof * oldLength);
+ memcpy(cast(void*) newPayload.ptr, cast(void*) _payload.ptr, T.sizeof * oldLength);
// Zero out unused capacity to prevent gc from seeing false pointers
- memset(newPayload.ptr + oldLength,
+ memset( cast(void*) (newPayload.ptr + oldLength),
0,
(elements - oldLength) * T.sizeof);
- GC.addRange(newPayload.ptr, sz);
- GC.removeRange(_payload.ptr);
- free(_payload.ptr);
+ GC.addRange(cast(void*) newPayload.ptr, sz);
+ GC.removeRange(cast(void*) _payload.ptr);
+ free(cast(void*) _payload.ptr);
_payload = newPayload;
}
else
@@ -611,12 +611,17 @@ if (!is(immutable T == immutable bool))
return opEquals(rhs);
}
+ // fix https://issues.dlang.org/show_bug.cgi?23140
+ private alias Unshared(T) = T;
+ private alias Unshared(T: shared U, U) = U;
+
/// ditto
bool opEquals(ref const Array rhs) const
{
if (empty) return rhs.empty;
if (rhs.empty) return false;
- return _data._payload == rhs._data._payload;
+
+ return cast(Unshared!(T)[]) _data._payload == cast(Unshared!(T)[]) rhs._data._payload;
}
/**
@@ -1740,6 +1745,16 @@ if (!is(immutable T == immutable bool))
assertThrown!AssertError(array.length = 5);
}
+// https://issues.dlang.org/show_bug.cgi?id=23140
+@system unittest
+{
+ shared class C
+ {
+ }
+
+ Array!C ac;
+ ac = Array!C([new C]);
+}
////////////////////////////////////////////////////////////////////////////////
// Array!bool
////////////////////////////////////////////////////////////////////////////////
diff --git a/libphobos/src/std/container/dlist.d b/libphobos/src/std/container/dlist.d
index 32d56ec..11e3883 100644
--- a/libphobos/src/std/container/dlist.d
+++ b/libphobos/src/std/container/dlist.d
@@ -111,7 +111,7 @@ private struct DRange
static assert(is(ElementType!DRange == BaseNode*));
}
-nothrow @safe pure:
+nothrow @safe @nogc pure:
private BaseNode* _first;
private BaseNode* _last;
diff --git a/libphobos/src/std/conv.d b/libphobos/src/std/conv.d
index 9164e07..0f66065 100644
--- a/libphobos/src/std/conv.d
+++ b/libphobos/src/std/conv.d
@@ -4894,7 +4894,7 @@ if (isOctalLiteral(num))
template octal(alias decimalInteger)
if (is(typeof(decimalInteger)) && isIntegral!(typeof(decimalInteger)))
{
- enum octal = octal!(typeof(decimalInteger))(to!string(decimalInteger));
+ enum octal = convertToOctal(decimalInteger);
}
///
@@ -4910,6 +4910,19 @@ if (is(typeof(decimalInteger)) && isIntegral!(typeof(decimalInteger)))
auto d = octal!"0001_200_000";
}
+/*************************************
+ * Convert a decimal integer to an octal integer with the same digits.
+ * Params:
+ * i = integer to convert
+ * Returns:
+ * octal integer with the same type and same digits
+ */
+private T convertToOctal(T)(T i)
+{
+ assert((i % 10) < 8);
+ return i ? convertToOctal(i / 10) * 8 + i % 10 : 0;
+}
+
/*
Takes a string, num, which is an octal literal, and returns its
value, in the type T specified.
diff --git a/libphobos/src/std/datetime/stopwatch.d b/libphobos/src/std/datetime/stopwatch.d
index d8ecf7d..eedc0ea 100644
--- a/libphobos/src/std/datetime/stopwatch.d
+++ b/libphobos/src/std/datetime/stopwatch.d
@@ -234,7 +234,7 @@ public:
/++
- Peek at the amount of time that the the StopWatch has been running.
+ Peek at the amount of time that the StopWatch has been running.
This does not include any time during which the StopWatch was stopped but
does include $(I all) of the time that it was running and not just the
diff --git a/libphobos/src/std/datetime/timezone.d b/libphobos/src/std/datetime/timezone.d
index 28255f4..b238918 100644
--- a/libphobos/src/std/datetime/timezone.d
+++ b/libphobos/src/std/datetime/timezone.d
@@ -3397,8 +3397,7 @@ struct TZConversions
TZConversions parseTZConversions(string windowsZonesXMLText) @safe pure
{
// This is a bit hacky, since it doesn't properly read XML, but it avoids
- // needing to pull in std.xml (which we're theoretically replacing at some
- // point anyway).
+ // needing to pull in an xml parsing module.
import std.algorithm.iteration : uniq;
import std.algorithm.searching : find;
import std.algorithm.sorting : sort;
diff --git a/libphobos/src/std/digest/crc.d b/libphobos/src/std/digest/crc.d
index 38563b1..b7922bb 100644
--- a/libphobos/src/std/digest/crc.d
+++ b/libphobos/src/std/digest/crc.d
@@ -555,7 +555,7 @@ ubyte[8] crc64ECMAOf(T...)(T data)
}
/**
- * This is a convenience alias for $(REF digest, std,digest,digest) using the
+ * This is a convenience alias for $(REF digest, std,digest) using the
* CRC64-ISO implementation.
*
* Params:
@@ -611,7 +611,7 @@ alias CRC32Digest = WrapperDigest!CRC32;
* OOP API CRC64-ECMA implementation.
* See `std.digest` for differences between template and OOP API.
*
- * This is an alias for $(D $(REF WrapperDigest, std,digest,digest)!CRC64ECMA),
+ * This is an alias for $(D $(REF WrapperDigest, std,digest)!CRC64ECMA),
* see there for more information.
*/
alias CRC64ECMADigest = WrapperDigest!CRC64ECMA;
@@ -620,7 +620,7 @@ alias CRC64ECMADigest = WrapperDigest!CRC64ECMA;
* OOP API CRC64-ISO implementation.
* See `std.digest` for differences between template and OOP API.
*
- * This is an alias for $(D $(REF WrapperDigest, std,digest,digest)!CRC64ISO),
+ * This is an alias for $(D $(REF WrapperDigest, std,digest)!CRC64ISO),
* see there for more information.
*/
alias CRC64ISODigest = WrapperDigest!CRC64ISO;
@@ -628,7 +628,7 @@ alias CRC64ISODigest = WrapperDigest!CRC64ISO;
///
@safe unittest
{
- //Simple example, hashing a string using Digest.digest helper function
+ //Simple example, hashing a string using CRC32Digest.digest helper function
auto crc = new CRC32Digest();
ubyte[] hash = crc.digest("abc");
//Let's get a hash string
diff --git a/libphobos/src/std/digest/digest.d b/libphobos/src/std/digest/digest.d
deleted file mode 100644
index 01fdbd7..0000000
--- a/libphobos/src/std/digest/digest.d
+++ /dev/null
@@ -1,3 +0,0 @@
-// @@@DEPRECATED_2.101@@@
-deprecated("import std.digest instead of std.digest.digest. std.digest.digest will be removed in 2.101")
-module std.digest.digest;
diff --git a/libphobos/src/std/experimental/allocator/building_blocks/affix_allocator.d b/libphobos/src/std/experimental/allocator/building_blocks/affix_allocator.d
index 1e3df91..d0d0b7c 100644
--- a/libphobos/src/std/experimental/allocator/building_blocks/affix_allocator.d
+++ b/libphobos/src/std/experimental/allocator/building_blocks/affix_allocator.d
@@ -521,9 +521,9 @@ version (StdUnittest)
@system unittest
{
- import std.experimental.allocator.building_blocks.region : Region;
+ import std.experimental.allocator.building_blocks.region : BorrowedRegion;
- auto a = AffixAllocator!(Region!(), uint)(Region!()(new ubyte[1024 * 64]));
+ auto a = AffixAllocator!(BorrowedRegion!(), uint)(BorrowedRegion!()(new ubyte[1024 * 64]));
auto b = a.allocate(42);
assert(b.length == 42);
// Test that expand infers from parent
diff --git a/libphobos/src/std/experimental/allocator/building_blocks/aligned_block_list.d b/libphobos/src/std/experimental/allocator/building_blocks/aligned_block_list.d
index 14c6de4..99768bc 100644
--- a/libphobos/src/std/experimental/allocator/building_blocks/aligned_block_list.d
+++ b/libphobos/src/std/experimental/allocator/building_blocks/aligned_block_list.d
@@ -526,7 +526,7 @@ shared struct SharedAlignedBlockList(Allocator, ParentAllocator, ulong theAlignm
///
@system unittest
{
- import std.experimental.allocator.building_blocks.region : SharedRegion;
+ import std.experimental.allocator.building_blocks.region : SharedBorrowedRegion;
import std.experimental.allocator.building_blocks.ascending_page_allocator : SharedAscendingPageAllocator;
import std.experimental.allocator.building_blocks.null_allocator : NullAllocator;
import core.thread : ThreadGroup;
@@ -536,11 +536,11 @@ shared struct SharedAlignedBlockList(Allocator, ParentAllocator, ulong theAlignm
enum maxIter = 10;
/*
- In this example we use 'SharedAlignedBlockList' together with 'SharedRegion',
- in order to create a fast, thread-safe allocator.
+ In this example we use 'SharedAlignedBlockList' together with
+ 'SharedBorrowedRegion', in order to create a fast, thread-safe allocator.
*/
alias SuperAllocator = SharedAlignedBlockList!(
- SharedRegion!(NullAllocator, 1),
+ SharedBorrowedRegion!(1),
SharedAscendingPageAllocator,
4096);
@@ -597,7 +597,7 @@ version (StdUnittest)
SpinLock lock = SpinLock(SpinLock.Contention.brief);
alias SuperAllocator = SharedAlignedBlockList!(
- SharedRegion!(NullAllocator, 1),
+ SharedBorrowedRegion!(1),
SharedAscendingPageAllocator,
1 << 16);
void[][totalAllocs] buf;
diff --git a/libphobos/src/std/experimental/allocator/building_blocks/allocator_list.d b/libphobos/src/std/experimental/allocator/building_blocks/allocator_list.d
index bcab16d..ca83785 100644
--- a/libphobos/src/std/experimental/allocator/building_blocks/allocator_list.d
+++ b/libphobos/src/std/experimental/allocator/building_blocks/allocator_list.d
@@ -679,8 +679,8 @@ version (Posix) @system unittest
{
// Create an allocator based upon 4MB regions, fetched from the GC heap.
import std.algorithm.comparison : max;
- import std.experimental.allocator.building_blocks.region : Region;
- AllocatorList!((n) => Region!()(new ubyte[max(n, 1024 * 4096)])) a;
+ import std.experimental.allocator.building_blocks.region : BorrowedRegion;
+ AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a;
auto b1 = a.alignedAllocate(1024 * 8192, 1024);
assert(b1 !is null); // still works due to overdimensioning
assert(b1.length == 1024 * 8192);
@@ -707,8 +707,8 @@ version (Posix) @system unittest
// Create an allocator based upon 4MB regions, fetched from the GC heap.
import std.algorithm.comparison : max;
- import std.experimental.allocator.building_blocks.region : Region;
- AllocatorList!((n) => Region!()(new ubyte[max(n, 1024 * 4096)])) a;
+ import std.experimental.allocator.building_blocks.region : BorrowedRegion;
+ AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a;
auto b1 = a.alignedAllocate(0, 1);
assert(b1 is null);
@@ -728,8 +728,8 @@ version (Posix) @system unittest
// Create an allocator based upon 4MB regions, fetched from the GC heap.
import std.algorithm.comparison : max;
- import std.experimental.allocator.building_blocks.region : Region;
- AllocatorList!((n) => Region!()(new ubyte[max(n, 1024 * 4096)])) a;
+ import std.experimental.allocator.building_blocks.region : BorrowedRegion;
+ AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a;
auto b0 = a.alignedAllocate(1, 1024);
assert(b0.length == 1);
assert(b0.ptr.alignedAt(1024));
@@ -765,8 +765,8 @@ version (Posix) @system unittest
{
// Create an allocator based upon 4MB regions, fetched from the GC heap.
import std.algorithm.comparison : max;
- import std.experimental.allocator.building_blocks.region : Region;
- AllocatorList!((n) => Region!()(new ubyte[max(n, 1024 * 4096)])) a;
+ import std.experimental.allocator.building_blocks.region : BorrowedRegion;
+ AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a;
auto b1 = a.allocate(1024 * 8192);
assert(b1 !is null); // still works due to overdimensioning
b1 = a.allocate(1024 * 10);
@@ -779,10 +779,10 @@ version (Posix) @system unittest
@system unittest
{
import std.algorithm.comparison : max;
- import std.experimental.allocator.building_blocks.region : Region;
+ import std.experimental.allocator.building_blocks.region : BorrowedRegion;
import std.experimental.allocator.mallocator : Mallocator;
import std.typecons : Ternary;
- AllocatorList!((n) => Region!()(new ubyte[max(n, 1024 * 4096)]), Mallocator) a;
+ AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)]), Mallocator) a;
auto b1 = a.allocate(1024 * 8192);
assert(b1 !is null);
b1 = a.allocate(1024 * 10);
diff --git a/libphobos/src/std/experimental/allocator/building_blocks/fallback_allocator.d b/libphobos/src/std/experimental/allocator/building_blocks/fallback_allocator.d
index b413d73..3990418 100644
--- a/libphobos/src/std/experimental/allocator/building_blocks/fallback_allocator.d
+++ b/libphobos/src/std/experimental/allocator/building_blocks/fallback_allocator.d
@@ -342,12 +342,12 @@ struct FallbackAllocator(Primary, Fallback)
@system unittest
{
- import std.experimental.allocator.building_blocks.region : Region;
+ import std.experimental.allocator.building_blocks.region : BorrowedRegion;
import std.typecons : Ternary;
- auto a = FallbackAllocator!(Region!(), Region!())(
- Region!()(new ubyte[4096 * 1024]),
- Region!()(new ubyte[4096 * 1024]));
+ auto a = FallbackAllocator!(BorrowedRegion!(), BorrowedRegion!())(
+ BorrowedRegion!()(new ubyte[4096 * 1024]),
+ BorrowedRegion!()(new ubyte[4096 * 1024]));
auto b = a.alignedAllocate(42, 8);
assert(b.length == 42);
@@ -506,11 +506,11 @@ version (StdUnittest)
@system unittest
{
- import std.experimental.allocator.building_blocks.region : Region;
+ import std.experimental.allocator.building_blocks.region : BorrowedRegion;
import std.typecons : Ternary;
- alias A = FallbackAllocator!(Region!(), Region!());
- auto a = A(Region!()(new ubyte[16_384]), Region!()(new ubyte[16_384]));
+ alias A = FallbackAllocator!(BorrowedRegion!(), BorrowedRegion!());
+ auto a = A(BorrowedRegion!()(new ubyte[16_384]), BorrowedRegion!()(new ubyte[16_384]));
auto b = a.allocate(42);
assert(b.length == 42);
diff --git a/libphobos/src/std/experimental/allocator/building_blocks/free_list.d b/libphobos/src/std/experimental/allocator/building_blocks/free_list.d
index 7055d66..d2b3209 100644
--- a/libphobos/src/std/experimental/allocator/building_blocks/free_list.d
+++ b/libphobos/src/std/experimental/allocator/building_blocks/free_list.d
@@ -486,9 +486,9 @@ struct FreeList(ParentAllocator,
// Test that deallocateAll infers from parent
@system unittest
{
- import std.experimental.allocator.building_blocks.region : Region;
+ import std.experimental.allocator.building_blocks.region : BorrowedRegion;
- auto fl = FreeList!(Region!(), 0, 16)(Region!()(new ubyte[1024 * 64]));
+ auto fl = FreeList!(BorrowedRegion!(), 0, 16)(BorrowedRegion!()(new ubyte[1024 * 64]));
auto b = fl.allocate(42);
assert(b.length == 42);
assert((() pure nothrow @safe @nogc => fl.expand(b, 48))());
diff --git a/libphobos/src/std/experimental/allocator/building_blocks/free_tree.d b/libphobos/src/std/experimental/allocator/building_blocks/free_tree.d
index bd4bb95..fe59e26 100644
--- a/libphobos/src/std/experimental/allocator/building_blocks/free_tree.d
+++ b/libphobos/src/std/experimental/allocator/building_blocks/free_tree.d
@@ -502,9 +502,9 @@ version (StdUnittest)
@system unittest
{
- import std.experimental.allocator.building_blocks.region : Region;
+ import std.experimental.allocator.building_blocks.region : BorrowedRegion;
- auto a = FreeTree!(Region!())(Region!()(new ubyte[1024 * 64]));
+ auto a = FreeTree!(BorrowedRegion!())(BorrowedRegion!()(new ubyte[1024 * 64]));
auto b = a.allocate(42);
assert(b.length == 42);
assert((() pure nothrow @safe @nogc => a.expand(b, 22))());
diff --git a/libphobos/src/std/experimental/allocator/building_blocks/quantizer.d b/libphobos/src/std/experimental/allocator/building_blocks/quantizer.d
index 762b379..3334a86 100644
--- a/libphobos/src/std/experimental/allocator/building_blocks/quantizer.d
+++ b/libphobos/src/std/experimental/allocator/building_blocks/quantizer.d
@@ -315,14 +315,14 @@ version (StdUnittest)
version (StdUnittest)
@system unittest
{
- import std.experimental.allocator.building_blocks.region : Region;
+ import std.experimental.allocator.building_blocks.region : BorrowedRegion;
import std.typecons : Ternary;
- alias MyAlloc = Quantizer!(Region!(),
+ alias MyAlloc = Quantizer!(BorrowedRegion!(),
(size_t n) => n.roundUpToMultipleOf(64));
- testAllocator!(() => MyAlloc(Region!()(new ubyte[1024 * 64])));
+ testAllocator!(() => MyAlloc(BorrowedRegion!()(new ubyte[1024 * 64])));
- auto a = MyAlloc(Region!()(new ubyte[1024 * 64]));
+ auto a = MyAlloc(BorrowedRegion!()(new ubyte[1024 * 64]));
// Check that empty inherits from parent
assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.yes);
auto b = a.allocate(42);
diff --git a/libphobos/src/std/experimental/allocator/building_blocks/region.d b/libphobos/src/std/experimental/allocator/building_blocks/region.d
index be0d274..8c39784 100644
--- a/libphobos/src/std/experimental/allocator/building_blocks/region.d
+++ b/libphobos/src/std/experimental/allocator/building_blocks/region.d
@@ -29,8 +29,7 @@ the store and the limits. One allocation entails rounding up the allocation
size for alignment purposes, bumping the current pointer, and comparing it
against the limit.
-If `ParentAllocator` is different from $(REF_ALTTEXT `NullAllocator`, NullAllocator, std,experimental,allocator,building_blocks,null_allocator), `Region`
-deallocates the chunk of memory during destruction.
+`Region` deallocates the chunk of memory during destruction.
The `minAlign` parameter establishes alignment. If $(D minAlign > 1), the
sizes of all allocation requests are rounded up to a multiple of `minAlign`.
@@ -38,7 +37,7 @@ Applications aiming at maximum speed may want to choose $(D minAlign = 1) and
control alignment externally.
*/
-struct Region(ParentAllocator = NullAllocator,
+struct Region(ParentAllocator,
uint minAlign = platformAlignment,
Flag!"growDownwards" growDownwards = No.growDownwards)
{
@@ -63,50 +62,42 @@ struct Region(ParentAllocator = NullAllocator,
alias parent = ParentAllocator.instance;
}
- private void* _current, _begin, _end;
+ private BorrowedRegion!(minAlign, growDownwards) _impl;
private void* roundedBegin() const pure nothrow @trusted @nogc
{
- return cast(void*) roundUpToAlignment(cast(size_t) _begin, alignment);
+ return _impl.roundedBegin;
}
private void* roundedEnd() const pure nothrow @trusted @nogc
{
- return cast(void*) roundDownToAlignment(cast(size_t) _end, alignment);
+ return _impl.roundedEnd;
}
/**
Constructs a region backed by a user-provided store.
- Assumes the memory was allocated with `ParentAllocator`
- (if different from $(REF_ALTTEXT `NullAllocator`, NullAllocator, std,experimental,allocator,building_blocks,null_allocator)).
+ Assumes the memory was allocated with `ParentAllocator`.
Params:
- store = User-provided store backing up the region. If $(D
- ParentAllocator) is different from $(REF_ALTTEXT `NullAllocator`, NullAllocator, std,experimental,allocator,building_blocks,null_allocator), memory is assumed to
- have been allocated with `ParentAllocator`.
- n = Bytes to allocate using `ParentAllocator`. This constructor is only
- defined If `ParentAllocator` is different from $(REF_ALTTEXT `NullAllocator`, NullAllocator, std,experimental,allocator,building_blocks,null_allocator). If
- `parent.allocate(n)` returns `null`, the region will be initialized
- as empty (correctly initialized but unable to allocate).
+ store = User-provided store backing up the region. Assumed to have been
+ allocated with `ParentAllocator`.
+ n = Bytes to allocate using `ParentAllocator`. If `parent.allocate(n)`
+ returns `null`, the region will be initialized as empty (correctly
+ initialized but unable to allocate).
*/
this(ubyte[] store) pure nothrow @nogc
{
- _begin = store.ptr;
- _end = store.ptr + store.length;
- static if (growDownwards)
- _current = roundedEnd();
- else
- _current = roundedBegin();
+ _impl = store;
}
/// Ditto
- static if (!is(ParentAllocator == NullAllocator) && !stateSize!ParentAllocator)
+ static if (!stateSize!ParentAllocator)
this(size_t n)
{
this(cast(ubyte[]) (parent.allocate(n.roundUpToAlignment(alignment))));
}
/// Ditto
- static if (!is(ParentAllocator == NullAllocator) && stateSize!ParentAllocator)
+ static if (stateSize!ParentAllocator)
this(ParentAllocator parent, size_t n)
{
this.parent = parent;
@@ -119,18 +110,299 @@ struct Region(ParentAllocator = NullAllocator,
*/
/**
- If `ParentAllocator` is not $(REF_ALTTEXT `NullAllocator`, NullAllocator, std,experimental,allocator,building_blocks,null_allocator) and defines `deallocate`,
- the region defines a destructor that uses `ParentAllocator.deallocate` to free the
- memory chunk.
+ If `ParentAllocator` defines `deallocate`, the region defines a destructor
+ that uses `ParentAllocator.deallocate` to free the memory chunk.
*/
- static if (!is(ParentAllocator == NullAllocator)
- && hasMember!(ParentAllocator, "deallocate"))
+ static if (hasMember!(ParentAllocator, "deallocate"))
~this()
{
- parent.deallocate(_begin[0 .. _end - _begin]);
+ with (_impl) parent.deallocate(_begin[0 .. _end - _begin]);
+ }
+
+ /**
+ Rounds the given size to a multiple of the `alignment`
+ */
+ size_t goodAllocSize(size_t n) const pure nothrow @safe @nogc
+ {
+ return _impl.goodAllocSize(n);
+ }
+
+ /**
+ Alignment offered.
+ */
+ alias alignment = minAlign;
+
+ /**
+ Allocates `n` bytes of memory. The shortest path involves an alignment
+ adjustment (if $(D alignment > 1)), an increment, and a comparison.
+
+ Params:
+ n = number of bytes to allocate
+
+ Returns:
+ A properly-aligned buffer of size `n` or `null` if request could not
+ be satisfied.
+ */
+ void[] allocate(size_t n) pure nothrow @trusted @nogc
+ {
+ return _impl.allocate(n);
+ }
+
+ /**
+ Allocates `n` bytes of memory aligned at alignment `a`.
+
+ Params:
+ n = number of bytes to allocate
+ a = alignment for the allocated block
+
+ Returns:
+ Either a suitable block of `n` bytes aligned at `a`, or `null`.
+ */
+ void[] alignedAllocate(size_t n, uint a) pure nothrow @trusted @nogc
+ {
+ return _impl.alignedAllocate(n, a);
+ }
+
+ /// Allocates and returns all memory available to this region.
+ void[] allocateAll() pure nothrow @trusted @nogc
+ {
+ return _impl.allocateAll;
+ }
+
+ /**
+ Expands an allocated block in place. Expansion will succeed only if the
+ block is the last allocated. Defined only if `growDownwards` is
+ `No.growDownwards`.
+ */
+ static if (growDownwards == No.growDownwards)
+ bool expand(ref void[] b, size_t delta) pure nothrow @safe @nogc
+ {
+ return _impl.expand(b, delta);
+ }
+
+ /**
+ Deallocates `b`. This works only if `b` was obtained as the last call
+ to `allocate`; otherwise (i.e. another allocation has occurred since) it
+ does nothing.
+
+ Params:
+ b = Block previously obtained by a call to `allocate` against this
+ allocator (`null` is allowed).
+ */
+ bool deallocate(void[] b) pure nothrow @nogc
+ {
+ return _impl.deallocate(b);
+ }
+
+ /**
+ Deallocates all memory allocated by this region, which can be subsequently
+ reused for new allocations.
+ */
+ bool deallocateAll() pure nothrow @nogc
+ {
+ return _impl.deallocateAll;
+ }
+
+ /**
+ Queries whether `b` has been allocated with this region.
+
+ Params:
+ b = Arbitrary block of memory (`null` is allowed; `owns(null)` returns
+ `false`).
+
+ Returns:
+ `true` if `b` has been allocated with this region, `false` otherwise.
+ */
+ Ternary owns(const void[] b) const pure nothrow @trusted @nogc
+ {
+ return _impl.owns(b);
+ }
+
+ /**
+ Returns `Ternary.yes` if no memory has been allocated in this region,
+ `Ternary.no` otherwise. (Never returns `Ternary.unknown`.)
+ */
+ Ternary empty() const pure nothrow @safe @nogc
+ {
+ return _impl.empty;
+ }
+
+ /// Nonstandard property that returns bytes available for allocation.
+ size_t available() const @safe pure nothrow @nogc
+ {
+ return _impl.available;
+ }
+}
+
+///
+@system nothrow unittest
+{
+ import std.algorithm.comparison : max;
+ import std.experimental.allocator.building_blocks.allocator_list
+ : AllocatorList;
+ import std.experimental.allocator.mallocator : Mallocator;
+ import std.typecons : Ternary;
+ // Create a scalable list of regions. Each gets at least 1MB at a time by
+ // using malloc.
+ auto batchAllocator = AllocatorList!(
+ (size_t n) => Region!Mallocator(max(n, 1024 * 1024))
+ )();
+ assert(batchAllocator.empty == Ternary.yes);
+ auto b = batchAllocator.allocate(101);
+ assert(b.length == 101);
+ assert(batchAllocator.empty == Ternary.no);
+ // This will cause a second allocation
+ b = batchAllocator.allocate(2 * 1024 * 1024);
+ assert(b.length == 2 * 1024 * 1024);
+ // Destructor will free the memory
+}
+
+@system nothrow @nogc unittest
+{
+ import std.experimental.allocator.mallocator : Mallocator;
+ import std.typecons : Ternary;
+
+ static void testAlloc(Allocator)(ref Allocator a)
+ {
+ assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.yes);
+ const b = a.allocate(101);
+ assert(b.length == 101);
+ assert((() nothrow @safe @nogc => a.owns(b))() == Ternary.yes);
+
+ // Ensure deallocate inherits from parent allocators
+ auto c = a.allocate(42);
+ assert(c.length == 42);
+ assert((() nothrow @nogc => a.deallocate(c))());
+ assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.no);
+ }
+
+ // Create a 64 KB region allocated with malloc
+ auto reg = Region!(Mallocator, Mallocator.alignment,
+ Yes.growDownwards)(1024 * 64);
+ testAlloc(reg);
+
+ // Create a 64 KB shared region allocated with malloc
+ auto sharedReg = SharedRegion!(Mallocator, Mallocator.alignment,
+ Yes.growDownwards)(1024 * 64);
+ testAlloc(sharedReg);
+}
+
+@system nothrow @nogc unittest
+{
+ // test 'this(ubyte[] store)' constructed regions properly clean up
+ // their inner storage after destruction
+ import std.experimental.allocator.mallocator : Mallocator;
+
+ static shared struct LocalAllocator
+ {
+ nothrow @nogc:
+ enum alignment = Mallocator.alignment;
+ void[] buf;
+ bool deallocate(void[] b)
+ {
+ assert(buf.ptr == b.ptr && buf.length == b.length);
+ return true;
+ }
+
+ void[] allocate(size_t n)
+ {
+ return null;
+ }
+
+ }
+
+ enum bufLen = 10 * Mallocator.alignment;
+ void[] tmp = Mallocator.instance.allocate(bufLen);
+
+ LocalAllocator a;
+ a.buf = cast(typeof(a.buf)) tmp[1 .. $];
+
+ auto reg = Region!(LocalAllocator, Mallocator.alignment,
+ Yes.growDownwards)(cast(ubyte[]) a.buf);
+ auto sharedReg = SharedRegion!(LocalAllocator, Mallocator.alignment,
+ Yes.growDownwards)(cast(ubyte[]) a.buf);
+ reg.parent = a;
+ sharedReg.parent = a;
+
+ Mallocator.instance.deallocate(tmp);
+}
+
+version (StdUnittest)
+@system unittest
+{
+ import std.experimental.allocator.mallocator : Mallocator;
+
+ testAllocator!(() => Region!(Mallocator)(1024 * 64));
+ testAllocator!(() => Region!(Mallocator, Mallocator.alignment, Yes.growDownwards)(1024 * 64));
+
+ testAllocator!(() => SharedRegion!(Mallocator)(1024 * 64));
+ testAllocator!(() => SharedRegion!(Mallocator, Mallocator.alignment, Yes.growDownwards)(1024 * 64));
+}
+
+@system nothrow @nogc unittest
+{
+ import std.experimental.allocator.mallocator : Mallocator;
+
+ auto reg = Region!(Mallocator)(1024 * 64);
+ auto b = reg.allocate(101);
+ assert(b.length == 101);
+ assert((() pure nothrow @safe @nogc => reg.expand(b, 20))());
+ assert((() pure nothrow @safe @nogc => reg.expand(b, 73))());
+ assert((() pure nothrow @safe @nogc => !reg.expand(b, 1024 * 64))());
+ assert((() nothrow @nogc => reg.deallocateAll())());
+}
+
+/**
+A `BorrowedRegion` allocates directly from a user-provided block of memory.
+
+Unlike a `Region`, a `BorrowedRegion` does not own the memory it allocates from
+and will not deallocate that memory upon destruction. Instead, it is the user's
+responsibility to ensure that the memory is properly disposed of.
+
+In all other respects, a `BorrowedRegion` behaves exactly like a `Region`.
+*/
+struct BorrowedRegion(uint minAlign = platformAlignment,
+ Flag!"growDownwards" growDownwards = No.growDownwards)
+{
+ static assert(minAlign.isGoodStaticAlignment);
+
+ import std.typecons : Ternary;
+
+ // state
+ private void* _current, _begin, _end;
+
+ private void* roundedBegin() const pure nothrow @trusted @nogc
+ {
+ return cast(void*) roundUpToAlignment(cast(size_t) _begin, alignment);
+ }
+
+ private void* roundedEnd() const pure nothrow @trusted @nogc
+ {
+ return cast(void*) roundDownToAlignment(cast(size_t) _end, alignment);
}
/**
+ Constructs a region backed by a user-provided store.
+
+ Params:
+ store = User-provided store backing up the region.
+ */
+ this(ubyte[] store) pure nothrow @nogc
+ {
+ _begin = store.ptr;
+ _end = store.ptr + store.length;
+ static if (growDownwards)
+ _current = roundedEnd();
+ else
+ _current = roundedBegin();
+ }
+
+ /*
+ TODO: The postblit of `BorrowedRegion` should be disabled because such objects
+ should not be copied around naively.
+ */
+
+ /**
Rounds the given size to a multiple of the `alignment`
*/
size_t goodAllocSize(size_t n) const pure nothrow @safe @nogc
@@ -362,56 +634,32 @@ struct Region(ParentAllocator = NullAllocator,
}
///
-@system nothrow unittest
-{
- import std.algorithm.comparison : max;
- import std.experimental.allocator.building_blocks.allocator_list
- : AllocatorList;
- import std.experimental.allocator.mallocator : Mallocator;
- import std.typecons : Ternary;
- // Create a scalable list of regions. Each gets at least 1MB at a time by
- // using malloc.
- auto batchAllocator = AllocatorList!(
- (size_t n) => Region!Mallocator(max(n, 1024 * 1024))
- )();
- assert(batchAllocator.empty == Ternary.yes);
- auto b = batchAllocator.allocate(101);
- assert(b.length == 101);
- assert(batchAllocator.empty == Ternary.no);
- // This will cause a second allocation
- b = batchAllocator.allocate(2 * 1024 * 1024);
- assert(b.length == 2 * 1024 * 1024);
- // Destructor will free the memory
-}
-
@system nothrow @nogc unittest
{
- import std.experimental.allocator.mallocator : Mallocator;
import std.typecons : Ternary;
- static void testAlloc(Allocator)(ref Allocator a)
- {
- assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.yes);
- const b = a.allocate(101);
- assert(b.length == 101);
- assert((() nothrow @safe @nogc => a.owns(b))() == Ternary.yes);
+ ubyte[1024] store;
+ auto myRegion = BorrowedRegion!(1)(store[]);
- // Ensure deallocate inherits from parent allocators
- auto c = a.allocate(42);
- assert(c.length == 42);
- assert((() nothrow @nogc => a.deallocate(c))());
- assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.no);
- }
+ assert(myRegion.empty == Ternary.yes);
+ assert(myRegion.available == store.length);
- // Create a 64 KB region allocated with malloc
- auto reg = Region!(Mallocator, Mallocator.alignment,
- Yes.growDownwards)(1024 * 64);
- testAlloc(reg);
+ void[] b = myRegion.allocate(101);
- // Create a 64 KB shared region allocated with malloc
- auto sharedReg = SharedRegion!(Mallocator, Mallocator.alignment,
- Yes.growDownwards)(1024 * 64);
- testAlloc(sharedReg);
+ assert(b.length == 101);
+ assert(myRegion.empty == Ternary.no);
+ assert(myRegion.owns(b) == Ternary.yes);
+ assert(myRegion.available == store.length - b.length);
+
+ void[] b2 = myRegion.allocate(256);
+
+ // Can only free the most recent allocation
+ assert(myRegion.deallocate(b) == false);
+ assert(myRegion.deallocate(b2) == true);
+
+ myRegion.deallocateAll();
+
+ assert(myRegion.empty == Ternary.yes);
}
@system nothrow @nogc unittest
@@ -420,76 +668,11 @@ struct Region(ParentAllocator = NullAllocator,
import std.typecons : Ternary;
ubyte[] buf = cast(ubyte[]) AlignedMallocator.instance.alignedAllocate(64, 64);
- auto reg = Region!(NullAllocator, 64, Yes.growDownwards)(buf);
+ auto reg = BorrowedRegion!(64, Yes.growDownwards)(buf);
assert(reg.alignedAllocate(10, 32).length == 10);
assert(!reg.available);
}
-@system nothrow @nogc unittest
-{
- // test 'this(ubyte[] store)' constructed regions properly clean up
- // their inner storage after destruction
- import std.experimental.allocator.mallocator : Mallocator;
-
- static shared struct LocalAllocator
- {
- nothrow @nogc:
- enum alignment = Mallocator.alignment;
- void[] buf;
- bool deallocate(void[] b)
- {
- assert(buf.ptr == b.ptr && buf.length == b.length);
- return true;
- }
-
- void[] allocate(size_t n)
- {
- return null;
- }
-
- }
-
- enum bufLen = 10 * Mallocator.alignment;
- void[] tmp = Mallocator.instance.allocate(bufLen);
-
- LocalAllocator a;
- a.buf = cast(typeof(a.buf)) tmp[1 .. $];
-
- auto reg = Region!(LocalAllocator, Mallocator.alignment,
- Yes.growDownwards)(cast(ubyte[]) a.buf);
- auto sharedReg = SharedRegion!(LocalAllocator, Mallocator.alignment,
- Yes.growDownwards)(cast(ubyte[]) a.buf);
- reg.parent = a;
- sharedReg.parent = a;
-
- Mallocator.instance.deallocate(tmp);
-}
-
-version (StdUnittest)
-@system unittest
-{
- import std.experimental.allocator.mallocator : Mallocator;
-
- testAllocator!(() => Region!(Mallocator)(1024 * 64));
- testAllocator!(() => Region!(Mallocator, Mallocator.alignment, Yes.growDownwards)(1024 * 64));
-
- testAllocator!(() => SharedRegion!(Mallocator)(1024 * 64));
- testAllocator!(() => SharedRegion!(Mallocator, Mallocator.alignment, Yes.growDownwards)(1024 * 64));
-}
-
-@system nothrow @nogc unittest
-{
- import std.experimental.allocator.mallocator : Mallocator;
-
- auto reg = Region!(Mallocator)(1024 * 64);
- auto b = reg.allocate(101);
- assert(b.length == 101);
- assert((() pure nothrow @safe @nogc => reg.expand(b, 20))());
- assert((() pure nothrow @safe @nogc => reg.expand(b, 73))());
- assert((() pure nothrow @safe @nogc => !reg.expand(b, 1024 * 64))());
- assert((() nothrow @nogc => reg.deallocateAll())());
-}
-
/**
`InSituRegion` is a convenient region that carries its storage within itself
@@ -535,7 +718,7 @@ struct InSituRegion(size_t size, size_t minAlign = platformAlignment)
@disable this(this);
// state {
- private Region!(NullAllocator, minAlign, growDownwards) _impl;
+ private BorrowedRegion!(minAlign, growDownwards) _impl;
union
{
private ubyte[size] _store = void;
@@ -992,7 +1175,7 @@ version (Posix) @system nothrow @nogc unittest
The threadsafe version of the `Region` allocator.
Allocations and deallocations are lock-free based using $(REF cas, core,atomic).
*/
-shared struct SharedRegion(ParentAllocator = NullAllocator,
+shared struct SharedRegion(ParentAllocator,
uint minAlign = platformAlignment,
Flag!"growDownwards" growDownwards = No.growDownwards)
{
@@ -1016,45 +1199,36 @@ shared struct SharedRegion(ParentAllocator = NullAllocator,
{
alias parent = ParentAllocator.instance;
}
- private shared void* _current, _begin, _end;
+ private shared SharedBorrowedRegion!(minAlign, growDownwards) _impl;
private void* roundedBegin() const pure nothrow @trusted @nogc
{
- return cast(void*) roundUpToAlignment(cast(size_t) _begin, alignment);
+ return _impl.roundedBegin;
}
private void* roundedEnd() const pure nothrow @trusted @nogc
{
- return cast(void*) roundDownToAlignment(cast(size_t) _end, alignment);
+ return _impl.roundedEnd;
}
/**
Constructs a region backed by a user-provided store.
- Assumes the memory was allocated with `ParentAllocator`
- (if different from $(REF_ALTTEXT `NullAllocator`, NullAllocator, std,experimental,allocator,building_blocks,null_allocator)).
+ Assumes the memory was allocated with `ParentAllocator`.
Params:
- store = User-provided store backing up the region. If `ParentAllocator`
- is different from $(REF_ALTTEXT `NullAllocator`, NullAllocator, std,experimental,allocator,building_blocks,null_allocator), memory is assumed to
- have been allocated with `ParentAllocator`.
- n = Bytes to allocate using `ParentAllocator`. This constructor is only
- defined If `ParentAllocator` is different from $(REF_ALTTEXT `NullAllocator`, NullAllocator, std,experimental,allocator,building_blocks,null_allocator). If
- `parent.allocate(n)` returns `null`, the region will be initialized
- as empty (correctly initialized but unable to allocate).
+ store = User-provided store backing up the region. Assumed to have been
+ allocated with `ParentAllocator`.
+ n = Bytes to allocate using `ParentAllocator`. If `parent.allocate(n)`
+ returns `null`, the region will be initialized as empty (correctly
+ initialized but unable to allocate).
*/
this(ubyte[] store) pure nothrow @nogc
{
- _begin = cast(typeof(_begin)) store.ptr;
- _end = cast(typeof(_end)) (store.ptr + store.length);
- static if (growDownwards)
- _current = cast(typeof(_current)) roundedEnd();
- else
- _current = cast(typeof(_current)) roundedBegin();
+ _impl = store;
}
/// Ditto
- static if (!is(ParentAllocator == NullAllocator))
this(size_t n)
{
this(cast(ubyte[]) (parent.allocate(n.roundUpToAlignment(alignment))));
@@ -1065,7 +1239,7 @@ shared struct SharedRegion(ParentAllocator = NullAllocator,
*/
size_t goodAllocSize(size_t n) const pure nothrow @safe @nogc
{
- return n.roundUpToAlignment(alignment);
+ return _impl.goodAllocSize(n);
}
/**
@@ -1086,38 +1260,7 @@ shared struct SharedRegion(ParentAllocator = NullAllocator,
*/
void[] allocate(size_t n) pure nothrow @trusted @nogc
{
- import core.atomic : cas, atomicLoad;
-
- if (n == 0) return null;
- const rounded = goodAllocSize(n);
-
- shared void* localCurrent, localNewCurrent;
- static if (growDownwards)
- {
- do
- {
- localCurrent = atomicLoad(_current);
- localNewCurrent = localCurrent - rounded;
- if (localNewCurrent > localCurrent || localNewCurrent < _begin)
- return null;
- } while (!cas(&_current, localCurrent, localNewCurrent));
-
- return cast(void[]) localNewCurrent[0 .. n];
- }
- else
- {
- do
- {
- localCurrent = atomicLoad(_current);
- localNewCurrent = localCurrent + rounded;
- if (localNewCurrent < localCurrent || localNewCurrent > _end)
- return null;
- } while (!cas(&_current, localCurrent, localNewCurrent));
-
- return cast(void[]) localCurrent[0 .. n];
- }
-
- assert(0, "Unexpected error in SharedRegion.allocate");
+ return _impl.allocate(n);
}
/**
@@ -1131,27 +1274,7 @@ shared struct SharedRegion(ParentAllocator = NullAllocator,
*/
bool deallocate(void[] b) pure nothrow @nogc
{
- import core.atomic : cas, atomicLoad;
-
- const rounded = goodAllocSize(b.length);
- shared void* localCurrent, localNewCurrent;
-
- // The cas is done only once, because only the last allocation can be reverted
- localCurrent = atomicLoad(_current);
- static if (growDownwards)
- {
- localNewCurrent = localCurrent + rounded;
- if (b.ptr == localCurrent)
- return cas(&_current, localCurrent, localNewCurrent);
- }
- else
- {
- localNewCurrent = localCurrent - rounded;
- if (b.ptr == localNewCurrent)
- return cas(&_current, localCurrent, localNewCurrent);
- }
-
- return false;
+ return _impl.deallocate(b);
}
/**
@@ -1160,16 +1283,7 @@ shared struct SharedRegion(ParentAllocator = NullAllocator,
*/
bool deallocateAll() pure nothrow @nogc
{
- import core.atomic : atomicStore;
- static if (growDownwards)
- {
- atomicStore(_current, cast(shared(void*)) roundedEnd());
- }
- else
- {
- atomicStore(_current, cast(shared(void*)) roundedBegin());
- }
- return true;
+ return _impl.deallocateAll;
}
/**
@@ -1183,45 +1297,7 @@ shared struct SharedRegion(ParentAllocator = NullAllocator,
*/
void[] alignedAllocate(size_t n, uint a) pure nothrow @trusted @nogc
{
- import core.atomic : cas, atomicLoad;
- import std.math.traits : isPowerOf2;
-
- assert(a.isPowerOf2);
- if (n == 0) return null;
-
- const rounded = goodAllocSize(n);
- shared void* localCurrent, localNewCurrent;
-
- static if (growDownwards)
- {
- do
- {
- localCurrent = atomicLoad(_current);
- auto alignedCurrent = cast(void*)(localCurrent - rounded);
- localNewCurrent = cast(shared(void*)) alignedCurrent.alignDownTo(a);
- if (alignedCurrent > localCurrent || localNewCurrent > alignedCurrent ||
- localNewCurrent < _begin)
- return null;
- } while (!cas(&_current, localCurrent, localNewCurrent));
-
- return cast(void[]) localNewCurrent[0 .. n];
- }
- else
- {
- do
- {
- localCurrent = atomicLoad(_current);
- auto alignedCurrent = alignUpTo(cast(void*) localCurrent, a);
- localNewCurrent = cast(shared(void*)) (alignedCurrent + rounded);
- if (alignedCurrent < localCurrent || localNewCurrent < alignedCurrent ||
- localNewCurrent > _end)
- return null;
- } while (!cas(&_current, localCurrent, localNewCurrent));
-
- return cast(void[]) (localNewCurrent - rounded)[0 .. n];
- }
-
- assert(0, "Unexpected error in SharedRegion.alignedAllocate");
+ return _impl.alignedAllocate(n, a);
}
/**
@@ -1236,7 +1312,7 @@ shared struct SharedRegion(ParentAllocator = NullAllocator,
*/
Ternary owns(const void[] b) const pure nothrow @trusted @nogc
{
- return Ternary(b && (&b[0] >= _begin) && (&b[0] + b.length <= _end));
+ return _impl.owns(b);
}
/**
@@ -1245,25 +1321,17 @@ shared struct SharedRegion(ParentAllocator = NullAllocator,
*/
Ternary empty() const pure nothrow @safe @nogc
{
- import core.atomic : atomicLoad;
-
- auto localCurrent = atomicLoad(_current);
- static if (growDownwards)
- return Ternary(localCurrent == roundedEnd());
- else
- return Ternary(localCurrent == roundedBegin());
+ return _impl.empty;
}
/**
- If `ParentAllocator` is not $(REF_ALTTEXT `NullAllocator`, NullAllocator, std,experimental,allocator,building_blocks,null_allocator) and defines `deallocate`,
- the region defines a destructor that uses `ParentAllocator.deallocate` to free the
- memory chunk.
+ If `ParentAllocator` defines `deallocate`, the region defines a destructor
+ that uses `ParentAllocator.deallocate` to free the memory chunk.
*/
- static if (!is(ParentAllocator == NullAllocator)
- && hasMember!(ParentAllocator, "deallocate"))
+ static if (hasMember!(ParentAllocator, "deallocate"))
~this()
{
- parent.deallocate(cast(void[]) _begin[0 .. _end - _begin]);
+ with (_impl) parent.deallocate(cast(void[]) _begin[0 .. _end - _begin]);
}
}
@@ -1397,3 +1465,250 @@ shared struct SharedRegion(ParentAllocator = NullAllocator,
testAlloc(a1, true);
testAlloc(a2, false);
}
+
+/**
+A `SharedBorrowedRegion` allocates directly from a user-provided block of memory.
+
+Unlike a `SharedRegion`, a `SharedBorrowedRegion` does not own the memory it
+allocates from and will not deallocate that memory upon destruction. Instead,
+it is the user's responsibility to ensure that the memory is properly disposed
+of.
+
+In all other respects, a `SharedBorrowedRegion` behaves exactly like a `SharedRegion`.
+*/
+shared struct SharedBorrowedRegion(uint minAlign = platformAlignment,
+ Flag!"growDownwards" growDownwards = No.growDownwards)
+{
+ static assert(minAlign.isGoodStaticAlignment);
+
+ import std.typecons : Ternary;
+
+ // state
+ private void* _current, _begin, _end;
+
+ private void* roundedBegin() shared const pure nothrow @trusted @nogc
+ {
+ return cast(void*) roundUpToAlignment(cast(size_t) _begin, alignment);
+ }
+
+ private void* roundedEnd() shared const pure nothrow @trusted @nogc
+ {
+ return cast(void*) roundDownToAlignment(cast(size_t) _end, alignment);
+ }
+
+ /**
+ Constructs a region backed by a user-provided store.
+
+ Params:
+ store = User-provided store backing up the region. Must not be aliased.
+ */
+ this(ubyte[] store) shared pure nothrow @nogc
+ {
+ _begin = cast(typeof(_begin)) store.ptr;
+ _end = cast(typeof(_end)) (store.ptr + store.length);
+ static if (growDownwards)
+ _current = cast(typeof(_current)) roundedEnd();
+ else
+ _current = cast(typeof(_current)) roundedBegin();
+ }
+
+ /*
+ TODO: The postblit of `SharedBorrowedRegion` should be disabled because
+ such objects should not be copied around naively.
+ */
+
+ /**
+ Rounds the given size to a multiple of the `alignment`
+ */
+ size_t goodAllocSize(size_t n) shared const pure nothrow @safe @nogc
+ {
+ return n.roundUpToAlignment(alignment);
+ }
+
+ /**
+ Alignment offered.
+ */
+ alias alignment = minAlign;
+
+ /**
+ Allocates `n` bytes of memory. The allocation is served by atomically incrementing
+ a pointer which keeps track of the current used space.
+
+ Params:
+ n = number of bytes to allocate
+
+ Returns:
+ A properly-aligned buffer of size `n`, or `null` if request could not
+ be satisfied.
+ */
+ void[] allocate(size_t n) shared pure nothrow @trusted @nogc
+ {
+ import core.atomic : cas, atomicLoad;
+
+ if (n == 0) return null;
+ const rounded = goodAllocSize(n);
+
+ shared void* localCurrent, localNewCurrent;
+ static if (growDownwards)
+ {
+ do
+ {
+ localCurrent = atomicLoad(_current);
+ localNewCurrent = localCurrent - rounded;
+ if (localNewCurrent > localCurrent || localNewCurrent < _begin)
+ return null;
+ } while (!cas(&_current, localCurrent, localNewCurrent));
+
+ return cast(void[]) localNewCurrent[0 .. n];
+ }
+ else
+ {
+ do
+ {
+ localCurrent = atomicLoad(_current);
+ localNewCurrent = localCurrent + rounded;
+ if (localNewCurrent < localCurrent || localNewCurrent > _end)
+ return null;
+ } while (!cas(&_current, localCurrent, localNewCurrent));
+
+ return cast(void[]) localCurrent[0 .. n];
+ }
+
+ assert(0, "Unexpected error in SharedBorrowedRegion.allocate");
+ }
+
+ /**
+ Allocates `n` bytes of memory aligned at alignment `a`.
+
+ Params:
+ n = number of bytes to allocate
+ a = alignment for the allocated block
+
+ Returns:
+ Either a suitable block of `n` bytes aligned at `a`, or `null`.
+ */
+ void[] alignedAllocate(size_t n, uint a) shared pure nothrow @trusted @nogc
+ {
+ import core.atomic : cas, atomicLoad;
+ import std.math.traits : isPowerOf2;
+
+ assert(a.isPowerOf2);
+ if (n == 0) return null;
+
+ const rounded = goodAllocSize(n);
+ shared void* localCurrent, localNewCurrent;
+
+ static if (growDownwards)
+ {
+ do
+ {
+ localCurrent = atomicLoad(_current);
+ auto alignedCurrent = cast(void*)(localCurrent - rounded);
+ localNewCurrent = cast(shared(void*)) alignedCurrent.alignDownTo(a);
+ if (alignedCurrent > localCurrent || localNewCurrent > alignedCurrent ||
+ localNewCurrent < _begin)
+ return null;
+ } while (!cas(&_current, localCurrent, localNewCurrent));
+
+ return cast(void[]) localNewCurrent[0 .. n];
+ }
+ else
+ {
+ do
+ {
+ localCurrent = atomicLoad(_current);
+ auto alignedCurrent = alignUpTo(cast(void*) localCurrent, a);
+ localNewCurrent = cast(shared(void*)) (alignedCurrent + rounded);
+ if (alignedCurrent < localCurrent || localNewCurrent < alignedCurrent ||
+ localNewCurrent > _end)
+ return null;
+ } while (!cas(&_current, localCurrent, localNewCurrent));
+
+ return cast(void[]) (localNewCurrent - rounded)[0 .. n];
+ }
+
+ assert(0, "Unexpected error in SharedBorrowedRegion.alignedAllocate");
+ }
+
+ /**
+ Deallocates `b`. This works only if `b` was obtained as the last call
+ to `allocate`; otherwise (i.e. another allocation has occurred since) it
+ does nothing.
+
+ Params:
+ b = Block previously obtained by a call to `allocate` against this
+ allocator (`null` is allowed).
+ */
+ bool deallocate(void[] b) shared pure nothrow @nogc
+ {
+ import core.atomic : cas, atomicLoad;
+
+ const rounded = goodAllocSize(b.length);
+ shared void* localCurrent, localNewCurrent;
+
+ // The cas is done only once, because only the last allocation can be reverted
+ localCurrent = atomicLoad(_current);
+ static if (growDownwards)
+ {
+ localNewCurrent = localCurrent + rounded;
+ if (b.ptr == localCurrent)
+ return cas(&_current, localCurrent, localNewCurrent);
+ }
+ else
+ {
+ localNewCurrent = localCurrent - rounded;
+ if (b.ptr == localNewCurrent)
+ return cas(&_current, localCurrent, localNewCurrent);
+ }
+
+ return false;
+ }
+
+ /**
+ Deallocates all memory allocated by this region, which can be subsequently
+ reused for new allocations.
+ */
+ bool deallocateAll() shared pure nothrow @nogc
+ {
+ import core.atomic : atomicStore;
+ static if (growDownwards)
+ {
+ atomicStore(_current, cast(shared(void*)) roundedEnd());
+ }
+ else
+ {
+ atomicStore(_current, cast(shared(void*)) roundedBegin());
+ }
+ return true;
+ }
+
+ /**
+ Queries whether `b` has been allocated with this region.
+
+ Params:
+ b = Arbitrary block of memory (`null` is allowed; `owns(null)` returns
+ `false`).
+
+ Returns:
+ `true` if `b` has been allocated with this region, `false` otherwise.
+ */
+ Ternary owns(const void[] b) shared const pure nothrow @trusted @nogc
+ {
+ return Ternary(b && (&b[0] >= _begin) && (&b[0] + b.length <= _end));
+ }
+
+ /**
+ Returns `Ternary.yes` if no memory has been allocated in this region,
+ `Ternary.no` otherwise. (Never returns `Ternary.unknown`.)
+ */
+ Ternary empty() shared const pure nothrow @safe @nogc
+ {
+ import core.atomic : atomicLoad;
+
+ auto localCurrent = atomicLoad(_current);
+ static if (growDownwards)
+ return Ternary(localCurrent == roundedEnd());
+ else
+ return Ternary(localCurrent == roundedBegin());
+ }
+}
diff --git a/libphobos/src/std/experimental/allocator/building_blocks/scoped_allocator.d b/libphobos/src/std/experimental/allocator/building_blocks/scoped_allocator.d
index 8fc7584..96859b0 100644
--- a/libphobos/src/std/experimental/allocator/building_blocks/scoped_allocator.d
+++ b/libphobos/src/std/experimental/allocator/building_blocks/scoped_allocator.d
@@ -259,10 +259,10 @@ version (StdUnittest)
// Test that deallocateAll infers from parent
@system unittest
{
- import std.experimental.allocator.building_blocks.region : Region;
+ import std.experimental.allocator.building_blocks.region : BorrowedRegion;
- ScopedAllocator!(Region!()) a;
- a.parent.parent = Region!()(new ubyte[1024 * 64]);
+ ScopedAllocator!(BorrowedRegion!()) a;
+ a.parent.parent = BorrowedRegion!()(new ubyte[1024 * 64]);
auto b = a.allocate(42);
assert(b.length == 42);
assert((() pure nothrow @safe @nogc => a.expand(b, 22))());
diff --git a/libphobos/src/std/experimental/allocator/building_blocks/segregator.d b/libphobos/src/std/experimental/allocator/building_blocks/segregator.d
index 655db456..ff089bd 100644
--- a/libphobos/src/std/experimental/allocator/building_blocks/segregator.d
+++ b/libphobos/src/std/experimental/allocator/building_blocks/segregator.d
@@ -503,12 +503,12 @@ if (Args.length > 3)
@system unittest
{
- import std.experimental.allocator.building_blocks.region : Region;
+ import std.experimental.allocator.building_blocks.region : BorrowedRegion;
import std.typecons : Ternary;
- auto a = Segregator!(10_240, Region!(), Region!())(
- Region!()(new ubyte[4096 * 1024]),
- Region!()(new ubyte[4096 * 1024]));
+ auto a = Segregator!(10_240, BorrowedRegion!(), BorrowedRegion!())(
+ BorrowedRegion!()(new ubyte[4096 * 1024]),
+ BorrowedRegion!()(new ubyte[4096 * 1024]));
assert((() nothrow @safe @nogc => a.empty)() == Ternary.yes);
auto b = a.alignedAllocate(42, 8);
diff --git a/libphobos/src/std/experimental/allocator/building_blocks/stats_collector.d b/libphobos/src/std/experimental/allocator/building_blocks/stats_collector.d
index d57b3ed..3770af1 100644
--- a/libphobos/src/std/experimental/allocator/building_blocks/stats_collector.d
+++ b/libphobos/src/std/experimental/allocator/building_blocks/stats_collector.d
@@ -845,9 +845,9 @@ public:
@system unittest
{
- import std.experimental.allocator.building_blocks.region : Region;
+ import std.experimental.allocator.building_blocks.region : BorrowedRegion;
- auto a = StatsCollector!(Region!(), Options.all, Options.all)(Region!()(new ubyte[1024 * 64]));
+ auto a = StatsCollector!(BorrowedRegion!(), Options.all, Options.all)(BorrowedRegion!()(new ubyte[1024 * 64]));
auto b = a.allocate(42);
assert(b.length == 42);
// Test that reallocate infers from parent
@@ -859,9 +859,9 @@ public:
@system unittest
{
- import std.experimental.allocator.building_blocks.region : Region;
+ import std.experimental.allocator.building_blocks.region : BorrowedRegion;
- auto a = StatsCollector!(Region!(), Options.all)(Region!()(new ubyte[1024 * 64]));
+ auto a = StatsCollector!(BorrowedRegion!(), Options.all)(BorrowedRegion!()(new ubyte[1024 * 64]));
auto b = a.alignedAllocate(42, 128);
assert(b.length == 42);
assert(b.ptr.alignedAt(128));
diff --git a/libphobos/src/std/experimental/allocator/package.d b/libphobos/src/std/experimental/allocator/package.d
index 62f848f..2177926 100644
--- a/libphobos/src/std/experimental/allocator/package.d
+++ b/libphobos/src/std/experimental/allocator/package.d
@@ -547,24 +547,24 @@ nothrow:
@system unittest
{
- import std.experimental.allocator.building_blocks.region : Region;
+ import std.experimental.allocator.building_blocks.region : BorrowedRegion;
import std.conv : emplace;
- auto reg = Region!()(new ubyte[1024]);
- auto state = reg.allocate(stateSize!(CAllocatorImpl!(Region!(), Yes.indirect)));
- auto regObj = emplace!(CAllocatorImpl!(Region!(), Yes.indirect))(state, &reg);
+ auto reg = BorrowedRegion!()(new ubyte[1024]);
+ auto state = reg.allocate(stateSize!(CAllocatorImpl!(BorrowedRegion!(), Yes.indirect)));
+ auto regObj = emplace!(CAllocatorImpl!(BorrowedRegion!(), Yes.indirect))(state, &reg);
auto rcalloc = RCIAllocator(regObj);
auto b = rcalloc.allocate(10);
assert(b.length == 10);
// The reference counting is zero based
- assert((cast(CAllocatorImpl!(Region!(), Yes.indirect))(rcalloc._alloc)).rc == 1);
+ assert((cast(CAllocatorImpl!(BorrowedRegion!(), Yes.indirect))(rcalloc._alloc)).rc == 1);
{
auto rca2 = rcalloc;
- assert((cast(CAllocatorImpl!(Region!(), Yes.indirect))(rcalloc._alloc)).rc == 2);
+ assert((cast(CAllocatorImpl!(BorrowedRegion!(), Yes.indirect))(rcalloc._alloc)).rc == 2);
}
- assert((cast(CAllocatorImpl!(Region!(), Yes.indirect))(rcalloc._alloc)).rc == 1);
+ assert((cast(CAllocatorImpl!(BorrowedRegion!(), Yes.indirect))(rcalloc._alloc)).rc == 1);
}
@system unittest
diff --git a/libphobos/src/std/file.d b/libphobos/src/std/file.d
index d6cac41..8957089 100644
--- a/libphobos/src/std/file.d
+++ b/libphobos/src/std/file.d
@@ -3714,7 +3714,7 @@ assert(!de2.isFile);
@property bool isSymlink() scope;
/++
- Returns the size of the the file represented by this `DirEntry`
+ Returns the size of the file represented by this `DirEntry`
in bytes.
+/
@property ulong size() scope;
diff --git a/libphobos/src/std/format/package.d b/libphobos/src/std/format/package.d
index 3f6f33a..d83f028 100644
--- a/libphobos/src/std/format/package.d
+++ b/libphobos/src/std/format/package.d
@@ -550,7 +550,7 @@ License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: $(HTTP walterbright.com, Walter Bright), $(HTTP erdani.com,
Andrei Alexandrescu), and Kenji Hara
-Source: $(PHOBOSSRC std/format.d)
+Source: $(PHOBOSSRC std/format/package.d)
*/
module std.format;
diff --git a/libphobos/src/std/internal/math/gammafunction.d b/libphobos/src/std/internal/math/gammafunction.d
index 7f72234..703ecb1 100644
--- a/libphobos/src/std/internal/math/gammafunction.d
+++ b/libphobos/src/std/internal/math/gammafunction.d
@@ -631,7 +631,7 @@ enum real BETA_BIGINV = 1.084202172485504434007e-19L;
* betaIncomplete(a, b, x) = &Gamma;(a+b)/(&Gamma;(a) &Gamma;(b)) *
* $(INTEGRATE 0, x) $(POWER t, a-1)$(POWER (1-t),b-1) dt
*
- * and is the same as the the cumulative distribution function.
+ * and is the same as the cumulative distribution function.
*
* The domain of definition is 0 <= x <= 1. In this
* implementation a and b are restricted to positive values.
diff --git a/libphobos/src/std/logger/core.d b/libphobos/src/std/logger/core.d
index be2bd8d..846f6ee 100644
--- a/libphobos/src/std/logger/core.d
+++ b/libphobos/src/std/logger/core.d
@@ -704,7 +704,7 @@ abstract class Logger
/** This template provides the log functions for the `Logger` `class`
with the `LogLevel` encoded in the function name.
- For further information see the the two functions defined inside of this
+ For further information see the two functions defined inside of this
template.
The aliases following this template create the public names of these log
@@ -1446,7 +1446,7 @@ that the returned reference is only a current snapshot and in the following
code, you must make sure no other thread reassigns to it between reading and
writing `sharedLog`.
-`sharedLog` is only thread-safe if the the used `Logger` is thread-safe.
+`sharedLog` is only thread-safe if the used `Logger` is thread-safe.
The default `Logger` is thread-safe.
-------------
if (sharedLog !is myLogger)
@@ -1559,10 +1559,21 @@ class StdForwardLogger : Logger
}
}
+ auto oldSharedLog = sharedLog;
+
sharedLog = new shared RaceLogger;
- scope(exit) { sharedLog = null; }
- () @trusted { new Thread(() { log("foo"); }).start(); }();
+ scope(exit)
+ {
+ sharedLog = oldSharedLog;
+ }
+ Thread toWaitFor;
+ () @trusted { toWaitFor = new Thread(() { log("foo"); }).start(); }();
log("bar");
+
+ () @trusted
+ {
+ toWaitFor.join();
+ }();
}
/** This `LogLevel` is unqiue to every thread.
@@ -1897,7 +1908,7 @@ version (StdUnittest) private void testFuncNames(Logger logger) @safe
assertThrown!Throwable(logf(LogLevel.fatal, msg, "Yet"));
} ();
lineNumber = __LINE__ - 2;
- assert(l.msg == msg.format("Yet"));
+ assert(l.msg == msg.format("Yet"), l.msg);
assert(l.line == lineNumber);
assert(l.logLevel == LogLevel.all);
diff --git a/libphobos/src/std/net/curl.d b/libphobos/src/std/net/curl.d
index 7ea2ceb..2318279 100644
--- a/libphobos/src/std/net/curl.d
+++ b/libphobos/src/std/net/curl.d
@@ -1973,7 +1973,7 @@ private mixin template Protocol()
}
/**
- * The network interface to use in form of the the IP of the interface.
+ * The network interface to use in form of the IP of the interface.
*
* Example:
* ----
@@ -2706,7 +2706,7 @@ struct HTTP
@property void dnsTimeout(Duration d);
/**
- * The network interface to use in form of the the IP of the interface.
+ * The network interface to use in form of the IP of the interface.
*
* Example:
* ----
@@ -3486,7 +3486,7 @@ struct FTP
@property void dnsTimeout(Duration d);
/**
- * The network interface to use in form of the the IP of the interface.
+ * The network interface to use in form of the IP of the interface.
*
* Example:
* ----
@@ -3912,7 +3912,7 @@ struct SMTP
@property void dnsTimeout(Duration d);
/**
- * The network interface to use in form of the the IP of the interface.
+ * The network interface to use in form of the IP of the interface.
*
* Example:
* ----
diff --git a/libphobos/src/std/numeric.d b/libphobos/src/std/numeric.d
index 96d20c2..df7ac39 100644
--- a/libphobos/src/std/numeric.d
+++ b/libphobos/src/std/numeric.d
@@ -1124,8 +1124,8 @@ public:
*
* References: "On Enclosing Simple Roots of Nonlinear Equations",
* G. Alefeld, F.A. Potra, Yixun Shi, Mathematics of Computation 61,
- * pp733-744 (1993). Fortran code available from $(HTTP
- * www.netlib.org,www.netlib.org) as algorithm TOMS478.
+ * pp733-744 (1993). Fortran code available from
+ * $(HTTP www.netlib.org,www.netlib.org) as algorithm TOMS478.
*
*/
T findRoot(T, DF, DT)(scope DF f, const T a, const T b,
diff --git a/libphobos/src/std/path.d b/libphobos/src/std/path.d
index de180fc..4b5a7ef 100644
--- a/libphobos/src/std/path.d
+++ b/libphobos/src/std/path.d
@@ -3357,8 +3357,10 @@ in
{
// Verify that pattern[] is valid
import std.algorithm.searching : balancedParens;
- assert(balancedParens(pattern, '[', ']', 0));
- assert(balancedParens(pattern, '{', '}', 0));
+ import std.utf : byUTF;
+
+ assert(balancedParens(pattern.byUTF!C, '[', ']', 0));
+ assert(balancedParens(pattern.byUTF!C, '{', '}', 0));
}
do
{
@@ -3959,7 +3961,7 @@ string expandTilde(string inputPath) @safe nothrow
version (Posix)
{
import core.exception : onOutOfMemoryError;
- import core.stdc.errno : errno, ERANGE;
+ import core.stdc.errno : errno, EBADF, ENOENT, EPERM, ERANGE, ESRCH;
import core.stdc.stdlib : malloc, free, realloc;
/* Joins a path from a C string to the remainder of path.
@@ -4065,7 +4067,7 @@ string expandTilde(string inputPath) @safe nothrow
char[] extra_memory;
passwd result;
- while (1)
+ loop: while (1)
{
extra_memory.length += extra_memory_size;
@@ -4088,10 +4090,23 @@ string expandTilde(string inputPath) @safe nothrow
break;
}
- if (errno != ERANGE &&
+ switch (errno)
+ {
+ case ERANGE:
// On BSD and OSX, errno can be left at 0 instead of set to ERANGE
- errno != 0)
- onOutOfMemoryError();
+ case 0:
+ break;
+
+ case ENOENT:
+ case ESRCH:
+ case EBADF:
+ case EPERM:
+ // The given name or uid was not found.
+ break loop;
+
+ default:
+ onOutOfMemoryError();
+ }
// extra_memory isn't large enough
import core.checkedint : mulu;
diff --git a/libphobos/src/std/range/primitives.d b/libphobos/src/std/range/primitives.d
index 31f58fa..cdab401 100644
--- a/libphobos/src/std/range/primitives.d
+++ b/libphobos/src/std/range/primitives.d
@@ -172,7 +172,7 @@ Returns:
enum bool isInputRange(R) =
is(typeof(R.init) == R)
&& is(ReturnType!((R r) => r.empty) == bool)
- && is(typeof((return ref R r) => r.front))
+ && (is(typeof((return ref R r) => r.front)) || is(typeof(ref (return ref R r) => r.front)))
&& !is(ReturnType!((R r) => r.front) == void)
&& is(typeof((R r) => r.popFront));
@@ -227,6 +227,17 @@ enum bool isInputRange(R) =
}
static assert(!isInputRange!VoidFront);
}
+// https://issues.dlang.org/show_bug.cgi?id=16034
+@safe unittest
+{
+ struct One
+ {
+ int entry = 1;
+ @disable this(this);
+ }
+
+ assert(isInputRange!(One[]));
+}
@safe unittest
{
diff --git a/libphobos/src/std/socket.d b/libphobos/src/std/socket.d
index 6ec7985..fb2c2d4 100644
--- a/libphobos/src/std/socket.d
+++ b/libphobos/src/std/socket.d
@@ -2942,7 +2942,7 @@ public:
* Calling `shutdown` before `close` is recommended
* for connection-oriented sockets.
*/
- void close() @trusted nothrow @nogc
+ void close() scope @trusted nothrow @nogc
{
_close(sock);
sock = socket_t.init;
@@ -3641,7 +3641,7 @@ class UdpSocket: Socket
{
checkAttributes!q{nothrow @nogc @trusted};
}
- nothrow @nogc @trusted void close()
+ nothrow @nogc @trusted scope void close()
{
checkAttributes!q{nothrow @nogc @trusted};
}
diff --git a/libphobos/src/std/stdio.d b/libphobos/src/std/stdio.d
index a1fe962..cd1a356 100644
--- a/libphobos/src/std/stdio.d
+++ b/libphobos/src/std/stdio.d
@@ -85,8 +85,7 @@ else version (CRuntime_Musl)
}
else version (CRuntime_UClibc)
{
- // uClibc supports GCC IO
- version = GCC_IO;
+ version = GENERIC_IO;
}
else version (OSX)
{
@@ -589,7 +588,7 @@ Throws: `ErrnoException` if the file could not be opened.
detach();
}
- this(this) @safe nothrow
+ this(this) @safe pure nothrow @nogc
{
if (!_p) return;
assert(atomicLoad(_p.refs));
diff --git a/libphobos/src/std/sumtype.d b/libphobos/src/std/sumtype.d
index 160665c..ee2d73a 100644
--- a/libphobos/src/std/sumtype.d
+++ b/libphobos/src/std/sumtype.d
@@ -753,23 +753,6 @@ public:
}
}
- invariant
- {
- this.match!((ref value) {
- static if (is(typeof(value) == class))
- {
- if (value !is null)
- {
- assert(value);
- }
- }
- else static if (is(typeof(value) == struct))
- {
- assert(&value);
- }
- });
- }
-
// Workaround for https://issues.dlang.org/show_bug.cgi?id=21400
version (StdDdoc)
{
@@ -1330,36 +1313,6 @@ version (D_BetterC) {} else
}));
}
-// Types with invariants
-// Disabled in BetterC due to use of exceptions
-version (D_BetterC) {} else
-version (D_Invariants)
-@system unittest
-{
- import std.exception : assertThrown;
- import core.exception : AssertError;
-
- struct S
- {
- int i;
- invariant { assert(i >= 0); }
- }
-
- class C
- {
- int i;
- invariant { assert(i >= 0); }
- }
-
- SumType!S x;
- x.match!((ref v) { v.i = -1; });
- assertThrown!AssertError(assert(&x));
-
- SumType!C y = new C();
- y.match!((ref v) { v.i = -1; });
- assertThrown!AssertError(assert(&y));
-}
-
// Calls value postblit on self-assignment
@safe unittest
{
diff --git a/libphobos/src/std/typecons.d b/libphobos/src/std/typecons.d
index c7cdc24..094628b 100644
--- a/libphobos/src/std/typecons.d
+++ b/libphobos/src/std/typecons.d
@@ -3793,8 +3793,28 @@ Params:
sink.formatValue(_value, fmt);
}
}
+
+ void toString()(scope void delegate(const(char)[]) sink, scope const ref FormatSpec!char fmt) const
+ {
+ if (isNull)
+ {
+ sink.formatValue("Nullable.null", fmt);
+ }
+ else
+ {
+ sink.formatValue(_value, fmt);
+ }
+ }
}
+@system unittest
+{
+ import std.conv : to;
+
+ const Nullable!(ulong, 0) x = 1;
+ assert(x.to!string == "1");
+}
+
/**
Check if `this` is in the null state.
@@ -4320,8 +4340,28 @@ Params:
sink.formatValue(*_value, fmt);
}
}
+
+ void toString()(scope void delegate(const(char)[]) sink, scope const ref FormatSpec!char fmt) const
+ {
+ if (isNull)
+ {
+ sink.formatValue("Nullable.null", fmt);
+ }
+ else
+ {
+ sink.formatValue(*_value, fmt);
+ }
+ }
}
+@system unittest
+{
+ import std.conv : to;
+
+ const NullableRef!(ulong) x = new ulong(1);
+ assert(x.to!string == "1");
+}
+
/**
Binds the internal state to `value`.
diff --git a/libphobos/src/std/uni/package.d b/libphobos/src/std/uni/package.d
index e12a70c..8a032aa 100644
--- a/libphobos/src/std/uni/package.d
+++ b/libphobos/src/std/uni/package.d
@@ -426,7 +426,7 @@ $(TR $(TD Building blocks) $(TD
$(SECTION Construction of lookup tables)
$(P The Unicode standard describes a set of algorithms that
depend on having the ability to quickly look up various properties
- of a code point. Given the the codespace of about 1 million $(CODEPOINTS),
+ of a code point. Given the codespace of about 1 million $(CODEPOINTS),
it is not a trivial task to provide a space-efficient solution for
the multitude of properties.
)
diff --git a/libphobos/src/std/utf.d b/libphobos/src/std/utf.d
index d22dac8..8d94e12 100644
--- a/libphobos/src/std/utf.d
+++ b/libphobos/src/std/utf.d
@@ -3571,7 +3571,7 @@ enum dchar replacementDchar = '\uFFFD';
* of characters (including strings) or a type that implicitly converts to a string type.
* Returns:
* If `r` is not an auto-decodable string (i.e. a narrow string or a
- * user-defined type that implicits converts to a string type), then `r`
+ * user-defined type that implicitly converts to a string type), then `r`
* is returned.
*
* Otherwise, `r` is converted to its corresponding string type (if it's
diff --git a/libphobos/src/std/xml.d b/libphobos/src/std/xml.d
deleted file mode 100644
index fdfdc3f..0000000
--- a/libphobos/src/std/xml.d
+++ /dev/null
@@ -1,3113 +0,0 @@
-// Written in the D programming language.
-
-/**
-$(RED Warning: This module is considered out-dated and not up to Phobos'
- current standards. It will be removed from Phobos in 2.101.0.
- If you still need it, go to $(LINK https://github.com/DigitalMars/undeaD))
- */
-
-/*
-Classes and functions for creating and parsing XML
-
-The basic architecture of this module is that there are standalone functions,
-classes for constructing an XML document from scratch (Tag, Element and
-Document), and also classes for parsing a pre-existing XML file (ElementParser
-and DocumentParser). The parsing classes <i>may</i> be used to build a
-Document, but that is not their primary purpose. The handling capabilities of
-DocumentParser and ElementParser are sufficiently customizable that you can
-make them do pretty much whatever you want.
-
-Example: This example creates a DOM (Document Object Model) tree
- from an XML file.
-------------------------------------------------------------------------------
-import std.xml;
-import std.stdio;
-import std.string;
-import std.file;
-
-// books.xml is used in various samples throughout the Microsoft XML Core
-// Services (MSXML) SDK.
-//
-// See http://msdn2.microsoft.com/en-us/library/ms762271(VS.85).aspx
-
-void main()
-{
- string s = cast(string) std.file.read("books.xml");
-
- // Check for well-formedness
- check(s);
-
- // Make a DOM tree
- auto doc = new Document(s);
-
- // Plain-print it
- writeln(doc);
-}
-------------------------------------------------------------------------------
-
-Example: This example does much the same thing, except that the file is
- deconstructed and reconstructed by hand. This is more work, but the
- techniques involved offer vastly more power.
-------------------------------------------------------------------------------
-import std.xml;
-import std.stdio;
-import std.string;
-
-struct Book
-{
- string id;
- string author;
- string title;
- string genre;
- string price;
- string pubDate;
- string description;
-}
-
-void main()
-{
- string s = cast(string) std.file.read("books.xml");
-
- // Check for well-formedness
- check(s);
-
- // Take it apart
- Book[] books;
-
- auto xml = new DocumentParser(s);
- xml.onStartTag["book"] = (ElementParser xml)
- {
- Book book;
- book.id = xml.tag.attr["id"];
-
- xml.onEndTag["author"] = (in Element e) { book.author = e.text(); };
- xml.onEndTag["title"] = (in Element e) { book.title = e.text(); };
- xml.onEndTag["genre"] = (in Element e) { book.genre = e.text(); };
- xml.onEndTag["price"] = (in Element e) { book.price = e.text(); };
- xml.onEndTag["publish-date"] = (in Element e) { book.pubDate = e.text(); };
- xml.onEndTag["description"] = (in Element e) { book.description = e.text(); };
-
- xml.parse();
-
- books ~= book;
- };
- xml.parse();
-
- // Put it back together again;
- auto doc = new Document(new Tag("catalog"));
- foreach (book;books)
- {
- auto element = new Element("book");
- element.tag.attr["id"] = book.id;
-
- element ~= new Element("author", book.author);
- element ~= new Element("title", book.title);
- element ~= new Element("genre", book.genre);
- element ~= new Element("price", book.price);
- element ~= new Element("publish-date",book.pubDate);
- element ~= new Element("description", book.description);
-
- doc ~= element;
- }
-
- // Pretty-print it
- writefln(join(doc.pretty(3),"\n"));
-}
--------------------------------------------------------------------------------
-Copyright: Copyright Janice Caron 2008 - 2009.
-License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Authors: Janice Caron
-Source: $(PHOBOSSRC std/xml.d)
-*/
-/*
- Copyright Janice Caron 2008 - 2009.
-Distributed under the Boost Software License, Version 1.0.
- (See accompanying file LICENSE_1_0.txt or copy at
- http://www.boost.org/LICENSE_1_0.txt)
-*/
-deprecated("Will be removed from Phobos in 2.101.0. If you still need it, go to https://github.com/DigitalMars/undeaD")
-module std.xml;
-
-enum cdata = "<![CDATA[";
-
-/*
- * Returns true if the character is a character according to the XML standard
- *
- * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
- *
- * Params:
- * c = the character to be tested
- */
-bool isChar(dchar c) @safe @nogc pure nothrow // rule 2
-{
- if (c <= 0xD7FF)
- {
- if (c >= 0x20)
- return true;
- switch (c)
- {
- case 0xA:
- case 0x9:
- case 0xD:
- return true;
- default:
- return false;
- }
- }
- else if (0xE000 <= c && c <= 0x10FFFF)
- {
- if ((c & 0x1FFFFE) != 0xFFFE) // U+FFFE and U+FFFF
- return true;
- }
- return false;
-}
-
-@safe @nogc nothrow pure unittest
-{
- assert(!isChar(cast(dchar) 0x8));
- assert( isChar(cast(dchar) 0x9));
- assert( isChar(cast(dchar) 0xA));
- assert(!isChar(cast(dchar) 0xB));
- assert(!isChar(cast(dchar) 0xC));
- assert( isChar(cast(dchar) 0xD));
- assert(!isChar(cast(dchar) 0xE));
- assert(!isChar(cast(dchar) 0x1F));
- assert( isChar(cast(dchar) 0x20));
- assert( isChar('J'));
- assert( isChar(cast(dchar) 0xD7FF));
- assert(!isChar(cast(dchar) 0xD800));
- assert(!isChar(cast(dchar) 0xDFFF));
- assert( isChar(cast(dchar) 0xE000));
- assert( isChar(cast(dchar) 0xFFFD));
- assert(!isChar(cast(dchar) 0xFFFE));
- assert(!isChar(cast(dchar) 0xFFFF));
- assert( isChar(cast(dchar) 0x10000));
- assert( isChar(cast(dchar) 0x10FFFF));
- assert(!isChar(cast(dchar) 0x110000));
-
- debug (stdxml_TestHardcodedChecks)
- {
- foreach (c; 0 .. dchar.max + 1)
- assert(isChar(c) == lookup(CharTable, c));
- }
-}
-
-/*
- * Returns true if the character is whitespace according to the XML standard
- *
- * Only the following characters are considered whitespace in XML - space, tab,
- * carriage return and linefeed
- *
- * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
- *
- * Params:
- * c = the character to be tested
- */
-bool isSpace(dchar c) @safe @nogc pure nothrow
-{
- return c == '\u0020' || c == '\u0009' || c == '\u000A' || c == '\u000D';
-}
-
-/*
- * Returns true if the character is a digit according to the XML standard
- *
- * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
- *
- * Params:
- * c = the character to be tested
- */
-bool isDigit(dchar c) @safe @nogc pure nothrow
-{
- if (c <= 0x0039 && c >= 0x0030)
- return true;
- else
- return lookup(DigitTable,c);
-}
-
-@safe @nogc nothrow pure unittest
-{
- debug (stdxml_TestHardcodedChecks)
- {
- foreach (c; 0 .. dchar.max + 1)
- assert(isDigit(c) == lookup(DigitTable, c));
- }
-}
-
-/*
- * Returns true if the character is a letter according to the XML standard
- *
- * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
- *
- * Params:
- * c = the character to be tested
- */
-bool isLetter(dchar c) @safe @nogc nothrow pure // rule 84
-{
- return isIdeographic(c) || isBaseChar(c);
-}
-
-/*
- * Returns true if the character is an ideographic character according to the
- * XML standard
- *
- * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
- *
- * Params:
- * c = the character to be tested
- */
-bool isIdeographic(dchar c) @safe @nogc nothrow pure
-{
- if (c == 0x3007)
- return true;
- if (c <= 0x3029 && c >= 0x3021 )
- return true;
- if (c <= 0x9FA5 && c >= 0x4E00)
- return true;
- return false;
-}
-
-@safe @nogc nothrow pure unittest
-{
- assert(isIdeographic('\u4E00'));
- assert(isIdeographic('\u9FA5'));
- assert(isIdeographic('\u3007'));
- assert(isIdeographic('\u3021'));
- assert(isIdeographic('\u3029'));
-
- debug (stdxml_TestHardcodedChecks)
- {
- foreach (c; 0 .. dchar.max + 1)
- assert(isIdeographic(c) == lookup(IdeographicTable, c));
- }
-}
-
-/*
- * Returns true if the character is a base character according to the XML
- * standard
- *
- * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
- *
- * Params:
- * c = the character to be tested
- */
-bool isBaseChar(dchar c) @safe @nogc nothrow pure
-{
- return lookup(BaseCharTable,c);
-}
-
-/*
- * Returns true if the character is a combining character according to the
- * XML standard
- *
- * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
- *
- * Params:
- * c = the character to be tested
- */
-bool isCombiningChar(dchar c) @safe @nogc nothrow pure
-{
- return lookup(CombiningCharTable,c);
-}
-
-/*
- * Returns true if the character is an extender according to the XML standard
- *
- * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
- *
- * Params:
- * c = the character to be tested
- */
-bool isExtender(dchar c) @safe @nogc nothrow pure
-{
- return lookup(ExtenderTable,c);
-}
-
-/*
- * Encodes a string by replacing all characters which need to be escaped with
- * appropriate predefined XML entities.
- *
- * encode() escapes certain characters (ampersand, quote, apostrophe, less-than
- * and greater-than), and similarly, decode() unescapes them. These functions
- * are provided for convenience only. You do not need to use them when using
- * the std.xml classes, because then all the encoding and decoding will be done
- * for you automatically.
- *
- * If the string is not modified, the original will be returned.
- *
- * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
- *
- * Params:
- * s = The string to be encoded
- *
- * Returns: The encoded string
- *
- * Example:
- * --------------
- * writefln(encode("a > b")); // writes "a &gt; b"
- * --------------
- */
-S encode(S)(S s)
-{
- import std.array : appender;
-
- string r;
- size_t lastI;
- auto result = appender!S();
-
- foreach (i, c; s)
- {
- switch (c)
- {
- case '&': r = "&amp;"; break;
- case '"': r = "&quot;"; break;
- case '\'': r = "&apos;"; break;
- case '<': r = "&lt;"; break;
- case '>': r = "&gt;"; break;
- default: continue;
- }
- // Replace with r
- result.put(s[lastI .. i]);
- result.put(r);
- lastI = i + 1;
- }
-
- if (!result.data.ptr) return s;
- result.put(s[lastI .. $]);
- return result.data;
-}
-
-@safe pure unittest
-{
- auto s = "hello";
- assert(encode(s) is s);
- assert(encode("a > b") == "a &gt; b", encode("a > b"));
- assert(encode("a < b") == "a &lt; b");
- assert(encode("don't") == "don&apos;t");
- assert(encode("\"hi\"") == "&quot;hi&quot;", encode("\"hi\""));
- assert(encode("cat & dog") == "cat &amp; dog");
-}
-
-/*
- * Mode to use for decoding.
- *
- * $(DDOC_ENUM_MEMBERS NONE) Do not decode
- * $(DDOC_ENUM_MEMBERS LOOSE) Decode, but ignore errors
- * $(DDOC_ENUM_MEMBERS STRICT) Decode, and throw exception on error
- */
-enum DecodeMode
-{
- NONE, LOOSE, STRICT
-}
-
-/*
- * Decodes a string by unescaping all predefined XML entities.
- *
- * encode() escapes certain characters (ampersand, quote, apostrophe, less-than
- * and greater-than), and similarly, decode() unescapes them. These functions
- * are provided for convenience only. You do not need to use them when using
- * the std.xml classes, because then all the encoding and decoding will be done
- * for you automatically.
- *
- * This function decodes the entities &amp;amp;, &amp;quot;, &amp;apos;,
- * &amp;lt; and &amp;gt,
- * as well as decimal and hexadecimal entities such as &amp;#x20AC;
- *
- * If the string does not contain an ampersand, the original will be returned.
- *
- * Note that the "mode" parameter can be one of DecodeMode.NONE (do not
- * decode), DecodeMode.LOOSE (decode, but ignore errors), or DecodeMode.STRICT
- * (decode, and throw a DecodeException in the event of an error).
- *
- * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
- *
- * Params:
- * s = The string to be decoded
- * mode = (optional) Mode to use for decoding. (Defaults to LOOSE).
- *
- * Throws: DecodeException if mode == DecodeMode.STRICT and decode fails
- *
- * Returns: The decoded string
- *
- * Example:
- * --------------
- * writefln(decode("a &gt; b")); // writes "a > b"
- * --------------
- */
-string decode(string s, DecodeMode mode=DecodeMode.LOOSE) @safe pure
-{
- import std.algorithm.searching : startsWith;
-
- if (mode == DecodeMode.NONE) return s;
-
- string buffer;
- foreach (ref i; 0 .. s.length)
- {
- char c = s[i];
- if (c != '&')
- {
- if (buffer.length != 0) buffer ~= c;
- }
- else
- {
- if (buffer.length == 0)
- {
- buffer = s[0 .. i].dup;
- }
- if (startsWith(s[i..$],"&#"))
- {
- try
- {
- dchar d;
- string t = s[i..$];
- checkCharRef(t, d);
- char[4] temp;
- import std.utf : encode;
- buffer ~= temp[0 .. encode(temp, d)];
- i = s.length - t.length - 1;
- }
- catch (Err e)
- {
- if (mode == DecodeMode.STRICT)
- throw new DecodeException("Unescaped &");
- buffer ~= '&';
- }
- }
- else if (startsWith(s[i..$],"&amp;" )) { buffer ~= '&'; i += 4; }
- else if (startsWith(s[i..$],"&quot;")) { buffer ~= '"'; i += 5; }
- else if (startsWith(s[i..$],"&apos;")) { buffer ~= '\''; i += 5; }
- else if (startsWith(s[i..$],"&lt;" )) { buffer ~= '<'; i += 3; }
- else if (startsWith(s[i..$],"&gt;" )) { buffer ~= '>'; i += 3; }
- else
- {
- if (mode == DecodeMode.STRICT)
- throw new DecodeException("Unescaped &");
- buffer ~= '&';
- }
- }
- }
- return (buffer.length == 0) ? s : buffer;
-}
-
-@safe pure unittest
-{
- void assertNot(string s) pure
- {
- bool b = false;
- try { decode(s,DecodeMode.STRICT); }
- catch (DecodeException e) { b = true; }
- assert(b,s);
- }
-
- // Assert that things that should work, do
- auto s = "hello";
- assert(decode(s, DecodeMode.STRICT) is s);
- assert(decode("a &gt; b", DecodeMode.STRICT) == "a > b");
- assert(decode("a &lt; b", DecodeMode.STRICT) == "a < b");
- assert(decode("don&apos;t", DecodeMode.STRICT) == "don't");
- assert(decode("&quot;hi&quot;", DecodeMode.STRICT) == "\"hi\"");
- assert(decode("cat &amp; dog", DecodeMode.STRICT) == "cat & dog");
- assert(decode("&#42;", DecodeMode.STRICT) == "*");
- assert(decode("&#x2A;", DecodeMode.STRICT) == "*");
- assert(decode("cat & dog", DecodeMode.LOOSE) == "cat & dog");
- assert(decode("a &gt b", DecodeMode.LOOSE) == "a &gt b");
- assert(decode("&#;", DecodeMode.LOOSE) == "&#;");
- assert(decode("&#x;", DecodeMode.LOOSE) == "&#x;");
- assert(decode("&#2G;", DecodeMode.LOOSE) == "&#2G;");
- assert(decode("&#x2G;", DecodeMode.LOOSE) == "&#x2G;");
-
- // Assert that things that shouldn't work, don't
- assertNot("cat & dog");
- assertNot("a &gt b");
- assertNot("&#;");
- assertNot("&#x;");
- assertNot("&#2G;");
- assertNot("&#x2G;");
-}
-
-/*
- * Class representing an XML document.
- *
- * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
- *
- */
-class Document : Element
-{
- /*
- * Contains all text which occurs before the root element.
- * Defaults to &lt;?xml version="1.0"?&gt;
- */
- string prolog = "<?xml version=\"1.0\"?>";
- /*
- * Contains all text which occurs after the root element.
- * Defaults to the empty string
- */
- string epilog;
-
- /*
- * Constructs a Document by parsing XML text.
- *
- * This function creates a complete DOM (Document Object Model) tree.
- *
- * The input to this function MUST be valid XML.
- * This is enforced by DocumentParser's in contract.
- *
- * Params:
- * s = the complete XML text.
- */
- this(string s)
- in
- {
- assert(s.length != 0);
- }
- do
- {
- auto xml = new DocumentParser(s);
- string tagString = xml.tag.tagString;
-
- this(xml.tag);
- prolog = s[0 .. tagString.ptr - s.ptr];
- parse(xml);
- epilog = *xml.s;
- }
-
- /*
- * Constructs a Document from a Tag.
- *
- * Params:
- * tag = the start tag of the document.
- */
- this(const(Tag) tag)
- {
- super(tag);
- }
-
- const
- {
- /*
- * Compares two Documents for equality
- *
- * Example:
- * --------------
- * Document d1,d2;
- * if (d1 == d2) { }
- * --------------
- */
- override bool opEquals(scope const Object o) const
- {
- const doc = toType!(const Document)(o);
- return prolog == doc.prolog
- && (cast(const) this).Element.opEquals(cast(const) doc)
- && epilog == doc.epilog;
- }
-
- /*
- * Compares two Documents
- *
- * You should rarely need to call this function. It exists so that
- * Documents can be used as associative array keys.
- *
- * Example:
- * --------------
- * Document d1,d2;
- * if (d1 < d2) { }
- * --------------
- */
- override int opCmp(scope const Object o) scope const
- {
- const doc = toType!(const Document)(o);
- if (prolog != doc.prolog)
- return prolog < doc.prolog ? -1 : 1;
- if (int cmp = this.Element.opCmp(doc))
- return cmp;
- if (epilog != doc.epilog)
- return epilog < doc.epilog ? -1 : 1;
- return 0;
- }
-
- /*
- * Returns the hash of a Document
- *
- * You should rarely need to call this function. It exists so that
- * Documents can be used as associative array keys.
- */
- override size_t toHash() scope const @trusted
- {
- return hash(prolog, hash(epilog, (cast() this).Element.toHash()));
- }
-
- /*
- * Returns the string representation of a Document. (That is, the
- * complete XML of a document).
- */
- override string toString() scope const @safe
- {
- return prolog ~ super.toString() ~ epilog;
- }
- }
-}
-
-@system unittest
-{
- // https://issues.dlang.org/show_bug.cgi?id=14966
- auto xml = `<?xml version="1.0" encoding="UTF-8"?><foo></foo>`;
-
- auto a = new Document(xml);
- auto b = new Document(xml);
- assert(a == b);
- assert(!(a < b));
- int[Document] aa;
- aa[a] = 1;
- assert(aa[b] == 1);
-
- b ~= new Element("b");
- assert(a < b);
- assert(b > a);
-}
-
-/*
- * Class representing an XML element.
- *
- * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
- */
-class Element : Item
-{
- Tag tag; // The start tag of the element
- Item[] items; // The element's items
- Text[] texts; // The element's text items
- CData[] cdatas; // The element's CData items
- Comment[] comments; // The element's comments
- ProcessingInstruction[] pis; // The element's processing instructions
- Element[] elements; // The element's child elements
-
- /*
- * Constructs an Element given a name and a string to be used as a Text
- * interior.
- *
- * Params:
- * name = the name of the element.
- * interior = (optional) the string interior.
- *
- * Example:
- * -------------------------------------------------------
- * auto element = new Element("title","Serenity")
- * // constructs the element <title>Serenity</title>
- * -------------------------------------------------------
- */
- this(string name, string interior=null) @safe pure
- {
- this(new Tag(name));
- if (interior.length != 0) opOpAssign!("~")(new Text(interior));
- }
-
- /*
- * Constructs an Element from a Tag.
- *
- * Params:
- * tag_ = the start or empty tag of the element.
- */
- this(const(Tag) tag_) @safe pure
- {
- this.tag = new Tag(tag_.name);
- tag.type = TagType.EMPTY;
- foreach (k,v;tag_.attr) tag.attr[k] = v;
- tag.tagString = tag_.tagString;
- }
-
- /*
- * Append a text item to the interior of this element
- *
- * Params:
- * item = the item you wish to append.
- *
- * Example:
- * --------------
- * Element element;
- * element ~= new Text("hello");
- * --------------
- */
- void opOpAssign(string op)(Text item) @safe pure
- if (op == "~")
- {
- texts ~= item;
- appendItem(item);
- }
-
- /*
- * Append a CData item to the interior of this element
- *
- * Params:
- * item = the item you wish to append.
- *
- * Example:
- * --------------
- * Element element;
- * element ~= new CData("hello");
- * --------------
- */
- void opOpAssign(string op)(CData item) @safe pure
- if (op == "~")
- {
- cdatas ~= item;
- appendItem(item);
- }
-
- /*
- * Append a comment to the interior of this element
- *
- * Params:
- * item = the item you wish to append.
- *
- * Example:
- * --------------
- * Element element;
- * element ~= new Comment("hello");
- * --------------
- */
- void opOpAssign(string op)(Comment item) @safe pure
- if (op == "~")
- {
- comments ~= item;
- appendItem(item);
- }
-
- /*
- * Append a processing instruction to the interior of this element
- *
- * Params:
- * item = the item you wish to append.
- *
- * Example:
- * --------------
- * Element element;
- * element ~= new ProcessingInstruction("hello");
- * --------------
- */
- void opOpAssign(string op)(ProcessingInstruction item) @safe pure
- if (op == "~")
- {
- pis ~= item;
- appendItem(item);
- }
-
- /*
- * Append a complete element to the interior of this element
- *
- * Params:
- * item = the item you wish to append.
- *
- * Example:
- * --------------
- * Element element;
- * Element other = new Element("br");
- * element ~= other;
- * // appends element representing <br />
- * --------------
- */
- void opOpAssign(string op)(Element item) @safe pure
- if (op == "~")
- {
- elements ~= item;
- appendItem(item);
- }
-
- private void appendItem(Item item) @safe pure
- {
- items ~= item;
- if (tag.type == TagType.EMPTY && !item.isEmptyXML)
- tag.type = TagType.START;
- }
-
- private void parse(ElementParser xml)
- {
- xml.onText = (string s) { opOpAssign!("~")(new Text(s)); };
- xml.onCData = (string s) { opOpAssign!("~")(new CData(s)); };
- xml.onComment = (string s) { opOpAssign!("~")(new Comment(s)); };
- xml.onPI = (string s) { opOpAssign!("~")(new ProcessingInstruction(s)); };
-
- xml.onStartTag[null] = (ElementParser xml)
- {
- auto e = new Element(xml.tag);
- e.parse(xml);
- opOpAssign!("~")(e);
- };
-
- xml.parse();
- }
-
- /*
- * Compares two Elements for equality
- *
- * Example:
- * --------------
- * Element e1,e2;
- * if (e1 == e2) { }
- * --------------
- */
- override bool opEquals(scope const Object o) const
- {
- const element = toType!(const Element)(o);
- immutable len = items.length;
- if (len != element.items.length) return false;
- foreach (i; 0 .. len)
- {
- if (!items[i].opEquals(element.items[i])) return false;
- }
- return true;
- }
-
- /*
- * Compares two Elements
- *
- * You should rarely need to call this function. It exists so that Elements
- * can be used as associative array keys.
- *
- * Example:
- * --------------
- * Element e1,e2;
- * if (e1 < e2) { }
- * --------------
- */
- override int opCmp(scope const Object o) @safe const
- {
- const element = toType!(const Element)(o);
- for (uint i=0; ; ++i)
- {
- if (i == items.length && i == element.items.length) return 0;
- if (i == items.length) return -1;
- if (i == element.items.length) return 1;
- if (!items[i].opEquals(element.items[i]))
- return items[i].opCmp(element.items[i]);
- }
- }
-
- /*
- * Returns the hash of an Element
- *
- * You should rarely need to call this function. It exists so that Elements
- * can be used as associative array keys.
- */
- override size_t toHash() scope const @safe
- {
- size_t hash = tag.toHash();
- foreach (item;items) hash += item.toHash();
- return hash;
- }
-
- const
- {
- /*
- * Returns the decoded interior of an element.
- *
- * The element is assumed to contain text <i>only</i>. So, for
- * example, given XML such as "&lt;title&gt;Good &amp;amp;
- * Bad&lt;/title&gt;", will return "Good &amp; Bad".
- *
- * Params:
- * mode = (optional) Mode to use for decoding. (Defaults to LOOSE).
- *
- * Throws: DecodeException if decode fails
- */
- string text(DecodeMode mode=DecodeMode.LOOSE)
- {
- string buffer;
- foreach (item;items)
- {
- Text t = cast(Text) item;
- if (t is null) throw new DecodeException(item.toString());
- buffer ~= decode(t.toString(),mode);
- }
- return buffer;
- }
-
- /*
- * Returns an indented string representation of this item
- *
- * Params:
- * indent = (optional) number of spaces by which to indent this
- * element. Defaults to 2.
- */
- override string[] pretty(uint indent=2) scope
- {
- import std.algorithm.searching : count;
- import std.string : rightJustify;
-
- if (isEmptyXML) return [ tag.toEmptyString() ];
-
- if (items.length == 1)
- {
- auto t = cast(const(Text))(items[0]);
- if (t !is null)
- {
- return [tag.toStartString() ~ t.toString() ~ tag.toEndString()];
- }
- }
-
- string[] a = [ tag.toStartString() ];
- foreach (item;items)
- {
- string[] b = item.pretty(indent);
- foreach (s;b)
- {
- a ~= rightJustify(s,count(s) + indent);
- }
- }
- a ~= tag.toEndString();
- return a;
- }
-
- /*
- * Returns the string representation of an Element
- *
- * Example:
- * --------------
- * auto element = new Element("br");
- * writefln(element.toString()); // writes "<br />"
- * --------------
- */
- override string toString() scope @safe
- {
- if (isEmptyXML) return tag.toEmptyString();
-
- string buffer = tag.toStartString();
- foreach (item;items) { buffer ~= item.toString(); }
- buffer ~= tag.toEndString();
- return buffer;
- }
-
- override @property @safe pure @nogc nothrow bool isEmptyXML() const scope { return items.length == 0; }
- }
-}
-
-/*
- * Tag types.
- *
- * $(DDOC_ENUM_MEMBERS START) Used for start tags
- * $(DDOC_ENUM_MEMBERS END) Used for end tags
- * $(DDOC_ENUM_MEMBERS EMPTY) Used for empty tags
- *
- */
-enum TagType { START, END, EMPTY }
-
-/*
- * Class representing an XML tag.
- *
- * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
- *
- * The class invariant guarantees
- * <ul>
- * <li> that $(B type) is a valid enum TagType value</li>
- * <li> that $(B name) consists of valid characters</li>
- * <li> that each attribute name consists of valid characters</li>
- * </ul>
- */
-class Tag
-{
- TagType type = TagType.START; // Type of tag
- string name; // Tag name
- string[string] attr; // Associative array of attributes
- private string tagString;
-
- invariant()
- {
- string s;
- string t;
-
- assert(type == TagType.START
- || type == TagType.END
- || type == TagType.EMPTY);
-
- s = name;
- try { checkName(s,t); }
- catch (Err e) { assert(false,"Invalid tag name:" ~ e.toString()); }
-
- foreach (k,v;attr)
- {
- s = k;
- try { checkName(s,t); }
- catch (Err e)
- { assert(false,"Invalid attribute name:" ~ e.toString()); }
- }
- }
-
- /*
- * Constructs an instance of Tag with a specified name and type
- *
- * The constructor does not initialize the attributes. To initialize the
- * attributes, you access the $(B attr) member variable.
- *
- * Params:
- * name = the Tag's name
- * type = (optional) the Tag's type. If omitted, defaults to
- * TagType.START.
- *
- * Example:
- * --------------
- * auto tag = new Tag("img",Tag.EMPTY);
- * tag.attr["src"] = "http://example.com/example.jpg";
- * --------------
- */
- this(string name, TagType type=TagType.START) @safe pure
- {
- this.name = name;
- this.type = type;
- }
-
- /* Private constructor (so don't ddoc this!)
- *
- * Constructs a Tag by parsing the string representation, e.g. "<html>".
- *
- * The string is passed by reference, and is advanced over all characters
- * consumed.
- *
- * The second parameter is a dummy parameter only, required solely to
- * distinguish this constructor from the public one.
- */
- private this(ref string s, bool dummy) @safe pure
- {
- import std.algorithm.searching : countUntil;
- import std.ascii : isWhite;
- import std.utf : byCodeUnit;
-
- tagString = s;
- try
- {
- reqc(s,'<');
- if (optc(s,'/')) type = TagType.END;
- ptrdiff_t i = s.byCodeUnit.countUntil(">", "/>", " ", "\t", "\v", "\r", "\n", "\f");
- name = s[0 .. i];
- s = s[i .. $];
-
- i = s.byCodeUnit.countUntil!(a => !isWhite(a));
- s = s[i .. $];
-
- while (s.length > 0 && s[0] != '>' && s[0] != '/')
- {
- i = s.byCodeUnit.countUntil("=", " ", "\t", "\v", "\r", "\n", "\f");
- string key = s[0 .. i];
- s = s[i .. $];
-
- i = s.byCodeUnit.countUntil!(a => !isWhite(a));
- s = s[i .. $];
- reqc(s,'=');
- i = s.byCodeUnit.countUntil!(a => !isWhite(a));
- s = s[i .. $];
-
- immutable char quote = requireOneOf(s,"'\"");
- i = s.byCodeUnit.countUntil(quote);
- string val = decode(s[0 .. i], DecodeMode.LOOSE);
- s = s[i .. $];
- reqc(s,quote);
-
- i = s.byCodeUnit.countUntil!(a => !isWhite(a));
- s = s[i .. $];
- attr[key] = val;
- }
- if (optc(s,'/'))
- {
- if (type == TagType.END) throw new TagException("");
- type = TagType.EMPTY;
- }
- reqc(s,'>');
- tagString.length = tagString.length - s.length;
- }
- catch (XMLException e)
- {
- tagString.length = tagString.length - s.length;
- throw new TagException(tagString);
- }
- }
-
- const
- {
- /*
- * Compares two Tags for equality
- *
- * You should rarely need to call this function. It exists so that Tags
- * can be used as associative array keys.
- *
- * Example:
- * --------------
- * Tag tag1,tag2
- * if (tag1 == tag2) { }
- * --------------
- */
- override bool opEquals(scope Object o)
- {
- const tag = toType!(const Tag)(o);
- return
- (name != tag.name) ? false : (
- (attr != tag.attr) ? false : (
- (type != tag.type) ? false : (
- true )));
- }
-
- /*
- * Compares two Tags
- *
- * Example:
- * --------------
- * Tag tag1,tag2
- * if (tag1 < tag2) { }
- * --------------
- */
- override int opCmp(Object o)
- {
- const tag = toType!(const Tag)(o);
- // Note that attr is an AA, so the comparison is nonsensical (bug 10381)
- return
- ((name != tag.name) ? ( name < tag.name ? -1 : 1 ) :
- ((attr != tag.attr) ? ( cast(void *) attr < cast(void*) tag.attr ? -1 : 1 ) :
- ((type != tag.type) ? ( type < tag.type ? -1 : 1 ) :
- 0 )));
- }
-
- /*
- * Returns the hash of a Tag
- *
- * You should rarely need to call this function. It exists so that Tags
- * can be used as associative array keys.
- */
- override size_t toHash()
- {
- return .hashOf(name);
- }
-
- /*
- * Returns the string representation of a Tag
- *
- * Example:
- * --------------
- * auto tag = new Tag("book",TagType.START);
- * writefln(tag.toString()); // writes "<book>"
- * --------------
- */
- override string toString() @safe
- {
- if (isEmpty) return toEmptyString();
- return (isEnd) ? toEndString() : toStartString();
- }
-
- private
- {
- string toNonEndString() @safe
- {
- import std.format : format;
-
- string s = "<" ~ name;
- foreach (key,val;attr)
- s ~= format(" %s=\"%s\"",key,encode(val));
- return s;
- }
-
- string toStartString() @safe { return toNonEndString() ~ ">"; }
-
- string toEndString() @safe { return "</" ~ name ~ ">"; }
-
- string toEmptyString() @safe { return toNonEndString() ~ " />"; }
- }
-
- /*
- * Returns true if the Tag is a start tag
- *
- * Example:
- * --------------
- * if (tag.isStart) { }
- * --------------
- */
- @property bool isStart() @safe @nogc pure nothrow { return type == TagType.START; }
-
- /*
- * Returns true if the Tag is an end tag
- *
- * Example:
- * --------------
- * if (tag.isEnd) { }
- * --------------
- */
- @property bool isEnd() @safe @nogc pure nothrow { return type == TagType.END; }
-
- /*
- * Returns true if the Tag is an empty tag
- *
- * Example:
- * --------------
- * if (tag.isEmpty) { }
- * --------------
- */
- @property bool isEmpty() @safe @nogc pure nothrow { return type == TagType.EMPTY; }
- }
-}
-
-/*
- * Class representing a comment
- */
-class Comment : Item
-{
- private string content;
-
- /*
- * Construct a comment
- *
- * Params:
- * content = the body of the comment
- *
- * Throws: CommentException if the comment body is illegal (contains "--"
- * or exactly equals "-")
- *
- * Example:
- * --------------
- * auto item = new Comment("This is a comment");
- * // constructs <!--This is a comment-->
- * --------------
- */
- this(string content) @safe pure
- {
- import std.string : indexOf;
-
- if (content == "-" || content.indexOf("--") != -1)
- throw new CommentException(content);
- this.content = content;
- }
-
- /*
- * Compares two comments for equality
- *
- * Example:
- * --------------
- * Comment item1,item2;
- * if (item1 == item2) { }
- * --------------
- */
- override bool opEquals(scope const Object o) const
- {
- const item = toType!(const Item)(o);
- const t = cast(const Comment) item;
- return t !is null && content == t.content;
- }
-
- /*
- * Compares two comments
- *
- * You should rarely need to call this function. It exists so that Comments
- * can be used as associative array keys.
- *
- * Example:
- * --------------
- * Comment item1,item2;
- * if (item1 < item2) { }
- * --------------
- */
- override int opCmp(scope const Object o) scope const
- {
- const item = toType!(const Item)(o);
- const t = cast(const Comment) item;
- return t !is null && (content != t.content
- ? (content < t.content ? -1 : 1 ) : 0 );
- }
-
- /*
- * Returns the hash of a Comment
- *
- * You should rarely need to call this function. It exists so that Comments
- * can be used as associative array keys.
- */
- override size_t toHash() scope const nothrow { return hash(content); }
-
- /*
- * Returns a string representation of this comment
- */
- override string toString() scope const @safe pure nothrow { return "<!--" ~ content ~ "-->"; }
-
- override @property @safe @nogc pure nothrow scope bool isEmptyXML() const { return false; } // Returns false always
-}
-
-// https://issues.dlang.org/show_bug.cgi?id=16241
-@safe unittest
-{
- import std.exception : assertThrown;
- auto c = new Comment("==");
- assert(c.content == "==");
- assertThrown!CommentException(new Comment("--"));
-}
-
-/*
- * Class representing a Character Data section
- */
-class CData : Item
-{
- private string content;
-
- /*
- * Construct a character data section
- *
- * Params:
- * content = the body of the character data segment
- *
- * Throws: CDataException if the segment body is illegal (contains "]]>")
- *
- * Example:
- * --------------
- * auto item = new CData("<b>hello</b>");
- * // constructs <![CDATA[<b>hello</b>]]>
- * --------------
- */
- this(string content) @safe pure
- {
- import std.string : indexOf;
- if (content.indexOf("]]>") != -1) throw new CDataException(content);
- this.content = content;
- }
-
- /*
- * Compares two CDatas for equality
- *
- * Example:
- * --------------
- * CData item1,item2;
- * if (item1 == item2) { }
- * --------------
- */
- override bool opEquals(scope const Object o) const
- {
- const item = toType!(const Item)(o);
- const t = cast(const CData) item;
- return t !is null && content == t.content;
- }
-
- /*
- * Compares two CDatas
- *
- * You should rarely need to call this function. It exists so that CDatas
- * can be used as associative array keys.
- *
- * Example:
- * --------------
- * CData item1,item2;
- * if (item1 < item2) { }
- * --------------
- */
- override int opCmp(scope const Object o) scope const
- {
- const item = toType!(const Item)(o);
- const t = cast(const CData) item;
- return t !is null && (content != t.content
- ? (content < t.content ? -1 : 1 ) : 0 );
- }
-
- /*
- * Returns the hash of a CData
- *
- * You should rarely need to call this function. It exists so that CDatas
- * can be used as associative array keys.
- */
- override size_t toHash() scope const nothrow { return hash(content); }
-
- /*
- * Returns a string representation of this CData section
- */
- override string toString() scope const @safe pure nothrow { return cdata ~ content ~ "]]>"; }
-
- override @property @safe @nogc pure nothrow scope bool isEmptyXML() const { return false; } // Returns false always
-}
-
-/*
- * Class representing a text (aka Parsed Character Data) section
- */
-class Text : Item
-{
- private string content;
-
- /*
- * Construct a text (aka PCData) section
- *
- * Params:
- * content = the text. This function encodes the text before
- * insertion, so it is safe to insert any text
- *
- * Example:
- * --------------
- * auto Text = new CData("a < b");
- * // constructs a &lt; b
- * --------------
- */
- this(string content) @safe pure
- {
- this.content = encode(content);
- }
-
- /*
- * Compares two text sections for equality
- *
- * Example:
- * --------------
- * Text item1,item2;
- * if (item1 == item2) { }
- * --------------
- */
- override bool opEquals(scope const Object o) const
- {
- const item = toType!(const Item)(o);
- const t = cast(const Text) item;
- return t !is null && content == t.content;
- }
-
- /*
- * Compares two text sections
- *
- * You should rarely need to call this function. It exists so that Texts
- * can be used as associative array keys.
- *
- * Example:
- * --------------
- * Text item1,item2;
- * if (item1 < item2) { }
- * --------------
- */
- override int opCmp(scope const Object o) scope const
- {
- const item = toType!(const Item)(o);
- const t = cast(const Text) item;
- return t !is null
- && (content != t.content ? (content < t.content ? -1 : 1 ) : 0 );
- }
-
- /*
- * Returns the hash of a text section
- *
- * You should rarely need to call this function. It exists so that Texts
- * can be used as associative array keys.
- */
- override size_t toHash() scope const nothrow { return hash(content); }
-
- /*
- * Returns a string representation of this Text section
- */
- override string toString() scope const @safe @nogc pure nothrow { return content; }
-
- /*
- * Returns true if the content is the empty string
- */
- override @property @safe @nogc pure nothrow scope bool isEmptyXML() const { return content.length == 0; }
-}
-
-/*
- * Class representing an XML Instruction section
- */
-class XMLInstruction : Item
-{
- private string content;
-
- /*
- * Construct an XML Instruction section
- *
- * Params:
- * content = the body of the instruction segment
- *
- * Throws: XIException if the segment body is illegal (contains ">")
- *
- * Example:
- * --------------
- * auto item = new XMLInstruction("ATTLIST");
- * // constructs <!ATTLIST>
- * --------------
- */
- this(string content) @safe pure
- {
- import std.string : indexOf;
- if (content.indexOf(">") != -1) throw new XIException(content);
- this.content = content;
- }
-
- /*
- * Compares two XML instructions for equality
- *
- * Example:
- * --------------
- * XMLInstruction item1,item2;
- * if (item1 == item2) { }
- * --------------
- */
- override bool opEquals(scope const Object o) const
- {
- const item = toType!(const Item)(o);
- const t = cast(const XMLInstruction) item;
- return t !is null && content == t.content;
- }
-
- /*
- * Compares two XML instructions
- *
- * You should rarely need to call this function. It exists so that
- * XmlInstructions can be used as associative array keys.
- *
- * Example:
- * --------------
- * XMLInstruction item1,item2;
- * if (item1 < item2) { }
- * --------------
- */
- override int opCmp(scope const Object o) scope const
- {
- const item = toType!(const Item)(o);
- const t = cast(const XMLInstruction) item;
- return t !is null
- && (content != t.content ? (content < t.content ? -1 : 1 ) : 0 );
- }
-
- /*
- * Returns the hash of an XMLInstruction
- *
- * You should rarely need to call this function. It exists so that
- * XmlInstructions can be used as associative array keys.
- */
- override size_t toHash() scope const nothrow { return hash(content); }
-
- /*
- * Returns a string representation of this XmlInstruction
- */
- override string toString() scope const @safe pure nothrow { return "<!" ~ content ~ ">"; }
-
- override @property @safe @nogc pure nothrow scope bool isEmptyXML() const { return false; } // Returns false always
-}
-
-/*
- * Class representing a Processing Instruction section
- */
-class ProcessingInstruction : Item
-{
- private string content;
-
- /*
- * Construct a Processing Instruction section
- *
- * Params:
- * content = the body of the instruction segment
- *
- * Throws: PIException if the segment body is illegal (contains "?>")
- *
- * Example:
- * --------------
- * auto item = new ProcessingInstruction("php");
- * // constructs <?php?>
- * --------------
- */
- this(string content) @safe pure
- {
- import std.string : indexOf;
- if (content.indexOf("?>") != -1) throw new PIException(content);
- this.content = content;
- }
-
- /*
- * Compares two processing instructions for equality
- *
- * Example:
- * --------------
- * ProcessingInstruction item1,item2;
- * if (item1 == item2) { }
- * --------------
- */
- override bool opEquals(scope const Object o) const
- {
- const item = toType!(const Item)(o);
- const t = cast(const ProcessingInstruction) item;
- return t !is null && content == t.content;
- }
-
- /*
- * Compares two processing instructions
- *
- * You should rarely need to call this function. It exists so that
- * ProcessingInstructions can be used as associative array keys.
- *
- * Example:
- * --------------
- * ProcessingInstruction item1,item2;
- * if (item1 < item2) { }
- * --------------
- */
- override int opCmp(scope const Object o) scope const
- {
- const item = toType!(const Item)(o);
- const t = cast(const ProcessingInstruction) item;
- return t !is null
- && (content != t.content ? (content < t.content ? -1 : 1 ) : 0 );
- }
-
- /*
- * Returns the hash of a ProcessingInstruction
- *
- * You should rarely need to call this function. It exists so that
- * ProcessingInstructions can be used as associative array keys.
- */
- override size_t toHash() scope const nothrow { return hash(content); }
-
- /*
- * Returns a string representation of this ProcessingInstruction
- */
- override string toString() scope const @safe pure nothrow { return "<?" ~ content ~ "?>"; }
-
- override @property @safe @nogc pure nothrow bool isEmptyXML() scope const { return false; } // Returns false always
-}
-
-/*
- * Abstract base class for XML items
- */
-abstract class Item
-{
- // Compares with another Item of same type for equality
- abstract override bool opEquals(scope const Object o) @safe const;
-
- // Compares with another Item of same type
- abstract override int opCmp(scope const Object o) @safe const;
-
- // Returns the hash of this item
- abstract override size_t toHash() @safe scope const;
-
- // Returns a string representation of this item
- abstract override string toString() @safe scope const;
-
- /*
- * Returns an indented string representation of this item
- *
- * Params:
- * indent = number of spaces by which to indent child elements
- */
- string[] pretty(uint indent) @safe scope const
- {
- import std.string : strip;
- string s = strip(toString());
- return s.length == 0 ? [] : [ s ];
- }
-
- // Returns true if the item represents empty XML text
- abstract @property @safe @nogc pure nothrow bool isEmptyXML() scope const;
-}
-
-/*
- * Class for parsing an XML Document.
- *
- * This is a subclass of ElementParser. Most of the useful functions are
- * documented there.
- *
- * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
- *
- * Bugs:
- * Currently only supports UTF documents.
- *
- * If there is an encoding attribute in the prolog, it is ignored.
- *
- */
-class DocumentParser : ElementParser
-{
- string xmlText;
-
- /*
- * Constructs a DocumentParser.
- *
- * The input to this function MUST be valid XML.
- * This is enforced by the function's in contract.
- *
- * Params:
- * xmlText_ = the entire XML document as text
- *
- */
- this(string xmlText_)
- in
- {
- assert(xmlText_.length != 0);
- try
- {
- // Confirm that the input is valid XML
- check(xmlText_);
- }
- catch (CheckException e)
- {
- // And if it's not, tell the user why not
- assert(false, "\n" ~ e.toString());
- }
- }
- do
- {
- xmlText = xmlText_;
- s = &xmlText;
- super(); // Initialize everything
- parse(); // Parse through the root tag (but not beyond)
- }
-}
-
-@system unittest
-{
- auto doc = new Document("<root><child><grandchild/></child></root>");
- assert(doc.elements.length == 1);
- assert(doc.elements[0].tag.name == "child");
- assert(doc.items == doc.elements);
-}
-
-/*
- * Class for parsing an XML element.
- *
- * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
- *
- * Note that you cannot construct instances of this class directly. You can
- * construct a DocumentParser (which is a subclass of ElementParser), but
- * otherwise, Instances of ElementParser will be created for you by the
- * library, and passed your way via onStartTag handlers.
- *
- */
-class ElementParser
-{
- alias Handler = void delegate(string);
- alias ElementHandler = void delegate(in Element element);
- alias ParserHandler = void delegate(ElementParser parser);
-
- private
- {
- Tag tag_;
- string elementStart;
- string* s;
-
- Handler commentHandler = null;
- Handler cdataHandler = null;
- Handler xiHandler = null;
- Handler piHandler = null;
- Handler rawTextHandler = null;
- Handler textHandler = null;
-
- // Private constructor for start tags
- this(ElementParser parent) @safe @nogc pure nothrow
- {
- s = parent.s;
- this();
- tag_ = parent.tag_;
- }
-
- // Private constructor for empty tags
- this(Tag tag, string* t) @safe @nogc pure nothrow
- {
- s = t;
- this();
- tag_ = tag;
- }
- }
-
- /*
- * The Tag at the start of the element being parsed. You can read this to
- * determine the tag's name and attributes.
- */
- @property @safe @nogc pure nothrow const(Tag) tag() const { return tag_; }
-
- /*
- * Register a handler which will be called whenever a start tag is
- * encountered which matches the specified name. You can also pass null as
- * the name, in which case the handler will be called for any unmatched
- * start tag.
- *
- * Example:
- * --------------
- * // Call this function whenever a <podcast> start tag is encountered
- * onStartTag["podcast"] = (ElementParser xml)
- * {
- * // Your code here
- * //
- * // This is a a closure, so code here may reference
- * // variables which are outside of this scope
- * };
- *
- * // call myEpisodeStartHandler (defined elsewhere) whenever an <episode>
- * // start tag is encountered
- * onStartTag["episode"] = &myEpisodeStartHandler;
- *
- * // call delegate dg for all other start tags
- * onStartTag[null] = dg;
- * --------------
- *
- * This library will supply your function with a new instance of
- * ElementHandler, which may be used to parse inside the element whose
- * start tag was just found, or to identify the tag attributes of the
- * element, etc.
- *
- * Note that your function will be called for both start tags and empty
- * tags. That is, we make no distinction between &lt;br&gt;&lt;/br&gt;
- * and &lt;br/&gt;.
- */
- ParserHandler[string] onStartTag;
-
- /*
- * Register a handler which will be called whenever an end tag is
- * encountered which matches the specified name. You can also pass null as
- * the name, in which case the handler will be called for any unmatched
- * end tag.
- *
- * Example:
- * --------------
- * // Call this function whenever a </podcast> end tag is encountered
- * onEndTag["podcast"] = (in Element e)
- * {
- * // Your code here
- * //
- * // This is a a closure, so code here may reference
- * // variables which are outside of this scope
- * };
- *
- * // call myEpisodeEndHandler (defined elsewhere) whenever an </episode>
- * // end tag is encountered
- * onEndTag["episode"] = &myEpisodeEndHandler;
- *
- * // call delegate dg for all other end tags
- * onEndTag[null] = dg;
- * --------------
- *
- * Note that your function will be called for both start tags and empty
- * tags. That is, we make no distinction between &lt;br&gt;&lt;/br&gt;
- * and &lt;br/&gt;.
- */
- ElementHandler[string] onEndTag;
-
- protected this() @safe @nogc pure nothrow
- {
- elementStart = *s;
- }
-
- /*
- * Register a handler which will be called whenever text is encountered.
- *
- * Example:
- * --------------
- * // Call this function whenever text is encountered
- * onText = (string s)
- * {
- * // Your code here
- *
- * // The passed parameter s will have been decoded by the time you see
- * // it, and so may contain any character.
- * //
- * // This is a a closure, so code here may reference
- * // variables which are outside of this scope
- * };
- * --------------
- */
- @property @safe @nogc pure nothrow void onText(Handler handler) { textHandler = handler; }
-
- /*
- * Register an alternative handler which will be called whenever text
- * is encountered. This differs from onText in that onText will decode
- * the text, whereas onTextRaw will not. This allows you to make design
- * choices, since onText will be more accurate, but slower, while
- * onTextRaw will be faster, but less accurate. Of course, you can
- * still call decode() within your handler, if you want, but you'd
- * probably want to use onTextRaw only in circumstances where you
- * know that decoding is unnecessary.
- *
- * Example:
- * --------------
- * // Call this function whenever text is encountered
- * onText = (string s)
- * {
- * // Your code here
- *
- * // The passed parameter s will NOT have been decoded.
- * //
- * // This is a a closure, so code here may reference
- * // variables which are outside of this scope
- * };
- * --------------
- */
- @safe @nogc pure nothrow void onTextRaw(Handler handler) { rawTextHandler = handler; }
-
- /*
- * Register a handler which will be called whenever a character data
- * segment is encountered.
- *
- * Example:
- * --------------
- * // Call this function whenever a CData section is encountered
- * onCData = (string s)
- * {
- * // Your code here
- *
- * // The passed parameter s does not include the opening <![CDATA[
- * // nor closing ]]>
- * //
- * // This is a a closure, so code here may reference
- * // variables which are outside of this scope
- * };
- * --------------
- */
- @property @safe @nogc pure nothrow void onCData(Handler handler) { cdataHandler = handler; }
-
- /*
- * Register a handler which will be called whenever a comment is
- * encountered.
- *
- * Example:
- * --------------
- * // Call this function whenever a comment is encountered
- * onComment = (string s)
- * {
- * // Your code here
- *
- * // The passed parameter s does not include the opening <!-- nor
- * // closing -->
- * //
- * // This is a a closure, so code here may reference
- * // variables which are outside of this scope
- * };
- * --------------
- */
- @property @safe @nogc pure nothrow void onComment(Handler handler) { commentHandler = handler; }
-
- /*
- * Register a handler which will be called whenever a processing
- * instruction is encountered.
- *
- * Example:
- * --------------
- * // Call this function whenever a processing instruction is encountered
- * onPI = (string s)
- * {
- * // Your code here
- *
- * // The passed parameter s does not include the opening <? nor
- * // closing ?>
- * //
- * // This is a a closure, so code here may reference
- * // variables which are outside of this scope
- * };
- * --------------
- */
- @property @safe @nogc pure nothrow void onPI(Handler handler) { piHandler = handler; }
-
- /*
- * Register a handler which will be called whenever an XML instruction is
- * encountered.
- *
- * Example:
- * --------------
- * // Call this function whenever an XML instruction is encountered
- * // (Note: XML instructions may only occur preceding the root tag of a
- * // document).
- * onPI = (string s)
- * {
- * // Your code here
- *
- * // The passed parameter s does not include the opening <! nor
- * // closing >
- * //
- * // This is a a closure, so code here may reference
- * // variables which are outside of this scope
- * };
- * --------------
- */
- @property @safe @nogc pure nothrow void onXI(Handler handler) { xiHandler = handler; }
-
- /*
- * Parse an XML element.
- *
- * Parsing will continue until the end of the current element. Any items
- * encountered for which a handler has been registered will invoke that
- * handler.
- *
- * Throws: various kinds of XMLException
- */
- void parse()
- {
- import std.algorithm.searching : startsWith;
- import std.string : indexOf;
-
- string t;
- const Tag root = tag_;
- Tag[string] startTags;
- if (tag_ !is null) startTags[tag_.name] = tag_;
-
- while (s.length != 0)
- {
- if (startsWith(*s,"<!--"))
- {
- chop(*s,4);
- t = chop(*s,indexOf(*s,"-->"));
- if (commentHandler.funcptr !is null) commentHandler(t);
- chop(*s,3);
- }
- else if (startsWith(*s,"<![CDATA["))
- {
- chop(*s,9);
- t = chop(*s,indexOf(*s,"]]>"));
- if (cdataHandler.funcptr !is null) cdataHandler(t);
- chop(*s,3);
- }
- else if (startsWith(*s,"<!"))
- {
- chop(*s,2);
- t = chop(*s,indexOf(*s,">"));
- if (xiHandler.funcptr !is null) xiHandler(t);
- chop(*s,1);
- }
- else if (startsWith(*s,"<?"))
- {
- chop(*s,2);
- t = chop(*s,indexOf(*s,"?>"));
- if (piHandler.funcptr !is null) piHandler(t);
- chop(*s,2);
- }
- else if (startsWith(*s,"<"))
- {
- tag_ = new Tag(*s,true);
- if (root is null)
- return; // Return to constructor of derived class
-
- if (tag_.isStart)
- {
- startTags[tag_.name] = tag_;
-
- auto parser = new ElementParser(this);
-
- auto handler = tag_.name in onStartTag;
- if (handler !is null) (*handler)(parser);
- else
- {
- handler = null in onStartTag;
- if (handler !is null) (*handler)(parser);
- }
- }
- else if (tag_.isEnd)
- {
- const startTag = startTags[tag_.name];
- string text;
-
- if (startTag.tagString.length == 0)
- assert(0);
-
- immutable(char)* p = startTag.tagString.ptr
- + startTag.tagString.length;
- immutable(char)* q = &tag_.tagString[0];
- text = decode(p[0..(q-p)], DecodeMode.LOOSE);
-
- auto element = new Element(startTag);
- if (text.length != 0) element ~= new Text(text);
-
- auto handler = tag_.name in onEndTag;
- if (handler !is null) (*handler)(element);
- else
- {
- handler = null in onEndTag;
- if (handler !is null) (*handler)(element);
- }
-
- if (tag_.name == root.name) return;
- }
- else if (tag_.isEmpty)
- {
- Tag startTag = new Tag(tag_.name);
-
- // FIX by hed010gy
- // https://issues.dlang.org/show_bug.cgi?id=2979
- if (tag_.attr.length > 0)
- foreach (tn,tv; tag_.attr) startTag.attr[tn]=tv;
- // END FIX
-
- // Handle the pretend start tag
- string s2;
- auto parser = new ElementParser(startTag,&s2);
- auto handler1 = startTag.name in onStartTag;
- if (handler1 !is null) (*handler1)(parser);
- else
- {
- handler1 = null in onStartTag;
- if (handler1 !is null) (*handler1)(parser);
- }
-
- // Handle the pretend end tag
- auto element = new Element(startTag);
- auto handler2 = tag_.name in onEndTag;
- if (handler2 !is null) (*handler2)(element);
- else
- {
- handler2 = null in onEndTag;
- if (handler2 !is null) (*handler2)(element);
- }
- }
- }
- else
- {
- t = chop(*s,indexOf(*s,"<"));
- if (rawTextHandler.funcptr !is null)
- rawTextHandler(t);
- else if (textHandler.funcptr !is null)
- textHandler(decode(t,DecodeMode.LOOSE));
- }
- }
- }
-
- /*
- * Returns that part of the element which has already been parsed
- */
- override string toString() const @nogc @safe pure nothrow
- {
- assert(elementStart.length >= s.length);
- return elementStart[0 .. elementStart.length - s.length];
- }
-
-}
-
-private
-{
- template Check(string msg)
- {
- string old = s;
-
- void fail() @safe pure
- {
- s = old;
- throw new Err(s,msg);
- }
-
- void fail(Err e) @safe pure
- {
- s = old;
- throw new Err(s,msg,e);
- }
-
- void fail(string msg2) @safe pure
- {
- fail(new Err(s,msg2));
- }
- }
-
- void checkMisc(ref string s) @safe pure // rule 27
- {
- import std.algorithm.searching : startsWith;
-
- mixin Check!("Misc");
-
- try
- {
- if (s.startsWith("<!--")) { checkComment(s); }
- else if (s.startsWith("<?")) { checkPI(s); }
- else { checkSpace(s); }
- }
- catch (Err e) { fail(e); }
- }
-
- void checkDocument(ref string s) @safe pure // rule 1
- {
- mixin Check!("Document");
- try
- {
- checkProlog(s);
- checkElement(s);
- star!(checkMisc)(s);
- }
- catch (Err e) { fail(e); }
- }
-
- void checkChars(ref string s) @safe pure // rule 2
- {
- // TO DO - Fix std.utf stride and decode functions, then use those
- // instead
- import std.format : format;
-
- mixin Check!("Chars");
-
- dchar c;
- ptrdiff_t n = -1;
- // 'i' must not be smaller than size_t because size_t is used internally in
- // aApply.d and it will be cast e.g to (int *) which fails on BigEndian targets.
- foreach (size_t i, dchar d; s)
- {
- if (!isChar(d))
- {
- c = d;
- n = i;
- break;
- }
- }
- if (n != -1)
- {
- s = s[n..$];
- fail(format("invalid character: U+%04X",c));
- }
- }
-
- void checkSpace(ref string s) @safe pure // rule 3
- {
- import std.algorithm.searching : countUntil;
- import std.ascii : isWhite;
- import std.utf : byCodeUnit;
-
- mixin Check!("Whitespace");
- ptrdiff_t i = s.byCodeUnit.countUntil!(a => !isWhite(a));
- if (i == -1 && s.length > 0 && isWhite(s[0]))
- s = s[$ .. $];
- else if (i > -1)
- s = s[i .. $];
- if (s is old) fail();
- }
-
- void checkName(ref string s, out string name) @safe pure // rule 5
- {
- mixin Check!("Name");
-
- if (s.length == 0) fail();
- ptrdiff_t n;
- // 'i' must not be smaller than size_t because size_t is used internally in
- // aApply.d and it will be cast e.g to (int *) which fails on BigEndian targets.
- foreach (size_t i, dchar c; s)
- {
- if (c == '_' || c == ':' || isLetter(c)) continue;
- if (i == 0) fail();
- if (c == '-' || c == '.' || isDigit(c)
- || isCombiningChar(c) || isExtender(c)) continue;
- n = i;
- break;
- }
- name = s[0 .. n];
- s = s[n..$];
- }
-
- void checkAttValue(ref string s) @safe pure // rule 10
- {
- import std.algorithm.searching : countUntil;
- import std.utf : byCodeUnit;
-
- mixin Check!("AttValue");
-
- if (s.length == 0) fail();
- char c = s[0];
- if (c != '\u0022' && c != '\u0027')
- fail("attribute value requires quotes");
- s = s[1..$];
- for (;;)
- {
- s = s[s.byCodeUnit.countUntil(c) .. $];
- if (s.length == 0) fail("unterminated attribute value");
- if (s[0] == '<') fail("< found in attribute value");
- if (s[0] == c) break;
- try { checkReference(s); } catch (Err e) { fail(e); }
- }
- s = s[1..$];
- }
-
- void checkCharData(ref string s) @safe pure // rule 14
- {
- import std.algorithm.searching : startsWith;
-
- mixin Check!("CharData");
-
- while (s.length != 0)
- {
- if (s.startsWith("&")) break;
- if (s.startsWith("<")) break;
- if (s.startsWith("]]>")) fail("]]> found within char data");
- s = s[1..$];
- }
- }
-
- void checkComment(ref string s) @safe pure // rule 15
- {
- import std.string : indexOf;
-
- mixin Check!("Comment");
-
- try { checkLiteral("<!--",s); } catch (Err e) { fail(e); }
- ptrdiff_t n = s.indexOf("--");
- if (n == -1) fail("unterminated comment");
- s = s[n..$];
- try { checkLiteral("-->",s); } catch (Err e) { fail(e); }
- }
-
- void checkPI(ref string s) @safe pure // rule 16
- {
- mixin Check!("PI");
-
- try
- {
- checkLiteral("<?",s);
- checkEnd("?>",s);
- }
- catch (Err e) { fail(e); }
- }
-
- void checkCDSect(ref string s) @safe pure // rule 18
- {
- mixin Check!("CDSect");
-
- try
- {
- checkLiteral(cdata,s);
- checkEnd("]]>",s);
- }
- catch (Err e) { fail(e); }
- }
-
- void checkProlog(ref string s) @safe pure // rule 22
- {
- mixin Check!("Prolog");
-
- try
- {
- /* The XML declaration is optional
- * http://www.w3.org/TR/2008/REC-xml-20081126/#NT-prolog
- */
- opt!(checkXMLDecl)(s);
-
- star!(checkMisc)(s);
- opt!(seq!(checkDocTypeDecl,star!(checkMisc)))(s);
- }
- catch (Err e) { fail(e); }
- }
-
- void checkXMLDecl(ref string s) @safe pure // rule 23
- {
- mixin Check!("XMLDecl");
-
- try
- {
- checkLiteral("<?xml",s);
- checkVersionInfo(s);
- opt!(checkEncodingDecl)(s);
- opt!(checkSDDecl)(s);
- opt!(checkSpace)(s);
- checkLiteral("?>",s);
- }
- catch (Err e) { fail(e); }
- }
-
- void checkVersionInfo(ref string s) @safe pure // rule 24
- {
- mixin Check!("VersionInfo");
-
- try
- {
- checkSpace(s);
- checkLiteral("version",s);
- checkEq(s);
- quoted!(checkVersionNum)(s);
- }
- catch (Err e) { fail(e); }
- }
-
- void checkEq(ref string s) @safe pure // rule 25
- {
- mixin Check!("Eq");
-
- try
- {
- opt!(checkSpace)(s);
- checkLiteral("=",s);
- opt!(checkSpace)(s);
- }
- catch (Err e) { fail(e); }
- }
-
- void checkVersionNum(ref string s) @safe pure // rule 26
- {
- import std.algorithm.searching : countUntil;
- import std.utf : byCodeUnit;
-
- mixin Check!("VersionNum");
-
- s = s[s.byCodeUnit.countUntil('\"') .. $];
- if (s is old) fail();
- }
-
- void checkDocTypeDecl(ref string s) @safe pure // rule 28
- {
- mixin Check!("DocTypeDecl");
-
- try
- {
- checkLiteral("<!DOCTYPE",s);
- //
- // TO DO -- ensure DOCTYPE is well formed
- // (But not yet. That's one of our "future directions")
- //
- checkEnd(">",s);
- }
- catch (Err e) { fail(e); }
- }
-
- void checkSDDecl(ref string s) @safe pure // rule 32
- {
- import std.algorithm.searching : startsWith;
-
- mixin Check!("SDDecl");
-
- try
- {
- checkSpace(s);
- checkLiteral("standalone",s);
- checkEq(s);
- }
- catch (Err e) { fail(e); }
-
- int n = 0;
- if (s.startsWith("'yes'") || s.startsWith("\"yes\"")) n = 5;
- else if (s.startsWith("'no'" ) || s.startsWith("\"no\"" )) n = 4;
- else fail("standalone attribute value must be 'yes', \"yes\","~
- " 'no' or \"no\"");
- s = s[n..$];
- }
-
- void checkElement(ref string s) @safe pure // rule 39
- {
- mixin Check!("Element");
-
- string sname,ename,t;
- try { checkTag(s,t,sname); } catch (Err e) { fail(e); }
-
- if (t == "STag")
- {
- try
- {
- checkContent(s);
- t = s;
- checkETag(s,ename);
- }
- catch (Err e) { fail(e); }
-
- if (sname != ename)
- {
- s = t;
- fail("end tag name \"" ~ ename
- ~ "\" differs from start tag name \""~sname~"\"");
- }
- }
- }
-
- // rules 40 and 44
- void checkTag(ref string s, out string type, out string name) @safe pure
- {
- mixin Check!("Tag");
-
- try
- {
- type = "STag";
- checkLiteral("<",s);
- checkName(s,name);
- star!(seq!(checkSpace,checkAttribute))(s);
- opt!(checkSpace)(s);
- if (s.length != 0 && s[0] == '/')
- {
- s = s[1..$];
- type = "ETag";
- }
- checkLiteral(">",s);
- }
- catch (Err e) { fail(e); }
- }
-
- void checkAttribute(ref string s) @safe pure // rule 41
- {
- mixin Check!("Attribute");
-
- try
- {
- string name;
- checkName(s,name);
- checkEq(s);
- checkAttValue(s);
- }
- catch (Err e) { fail(e); }
- }
-
- void checkETag(ref string s, out string name) @safe pure // rule 42
- {
- mixin Check!("ETag");
-
- try
- {
- checkLiteral("</",s);
- checkName(s,name);
- opt!(checkSpace)(s);
- checkLiteral(">",s);
- }
- catch (Err e) { fail(e); }
- }
-
- void checkContent(ref string s) @safe pure // rule 43
- {
- import std.algorithm.searching : startsWith;
-
- mixin Check!("Content");
-
- try
- {
- while (s.length != 0)
- {
- old = s;
- if (s.startsWith("&")) { checkReference(s); }
- else if (s.startsWith("<!--")) { checkComment(s); }
- else if (s.startsWith("<?")) { checkPI(s); }
- else if (s.startsWith(cdata)) { checkCDSect(s); }
- else if (s.startsWith("</")) { break; }
- else if (s.startsWith("<")) { checkElement(s); }
- else { checkCharData(s); }
- }
- }
- catch (Err e) { fail(e); }
- }
-
- void checkCharRef(ref string s, out dchar c) @safe pure // rule 66
- {
- import std.format : format;
-
- mixin Check!("CharRef");
-
- c = 0;
- try { checkLiteral("&#",s); } catch (Err e) { fail(e); }
- int radix = 10;
- if (s.length != 0 && s[0] == 'x')
- {
- s = s[1..$];
- radix = 16;
- }
- if (s.length == 0) fail("unterminated character reference");
- if (s[0] == ';')
- fail("character reference must have at least one digit");
- while (s.length != 0)
- {
- immutable char d = s[0];
- int n = 0;
- switch (d)
- {
- case 'F','f': ++n; goto case;
- case 'E','e': ++n; goto case;
- case 'D','d': ++n; goto case;
- case 'C','c': ++n; goto case;
- case 'B','b': ++n; goto case;
- case 'A','a': ++n; goto case;
- case '9': ++n; goto case;
- case '8': ++n; goto case;
- case '7': ++n; goto case;
- case '6': ++n; goto case;
- case '5': ++n; goto case;
- case '4': ++n; goto case;
- case '3': ++n; goto case;
- case '2': ++n; goto case;
- case '1': ++n; goto case;
- case '0': break;
- default: n = 100; break;
- }
- if (n >= radix) break;
- c *= radix;
- c += n;
- s = s[1..$];
- }
- if (!isChar(c)) fail(format("U+%04X is not a legal character",c));
- if (s.length == 0 || s[0] != ';') fail("expected ;");
- else s = s[1..$];
- }
-
- void checkReference(ref string s) @safe pure // rule 67
- {
- import std.algorithm.searching : startsWith;
-
- mixin Check!("Reference");
-
- try
- {
- dchar c;
- if (s.startsWith("&#")) checkCharRef(s,c);
- else checkEntityRef(s);
- }
- catch (Err e) { fail(e); }
- }
-
- void checkEntityRef(ref string s) @safe pure // rule 68
- {
- mixin Check!("EntityRef");
-
- try
- {
- string name;
- checkLiteral("&",s);
- checkName(s,name);
- checkLiteral(";",s);
- }
- catch (Err e) { fail(e); }
- }
-
- void checkEncName(ref string s) @safe pure // rule 81
- {
- import std.algorithm.searching : countUntil;
- import std.ascii : isAlpha;
- import std.utf : byCodeUnit;
-
- mixin Check!("EncName");
-
- s = s[s.byCodeUnit.countUntil!(a => !isAlpha(a)) .. $];
- if (s is old) fail();
- s = s[s.byCodeUnit.countUntil('\"', '\'') .. $];
- }
-
- void checkEncodingDecl(ref string s) @safe pure // rule 80
- {
- mixin Check!("EncodingDecl");
-
- try
- {
- checkSpace(s);
- checkLiteral("encoding",s);
- checkEq(s);
- quoted!(checkEncName)(s);
- }
- catch (Err e) { fail(e); }
- }
-
- // Helper functions
-
- void checkLiteral(string literal,ref string s) @safe pure
- {
- import std.string : startsWith;
-
- mixin Check!("Literal");
-
- if (!s.startsWith(literal)) fail("Expected literal \""~literal~"\"");
- s = s[literal.length..$];
- }
-
- void checkEnd(string end,ref string s) @safe pure
- {
- import std.string : indexOf;
- // Deliberately no mixin Check here.
-
- auto n = s.indexOf(end);
- if (n == -1) throw new Err(s,"Unable to find terminating \""~end~"\"");
- s = s[n..$];
- checkLiteral(end,s);
- }
-
- // Metafunctions -- none of these use mixin Check
-
- void opt(alias f)(ref string s)
- {
- try { f(s); } catch (Err e) {}
- }
-
- void plus(alias f)(ref string s)
- {
- f(s);
- star!(f)(s);
- }
-
- void star(alias f)(ref string s)
- {
- while (s.length != 0)
- {
- try { f(s); }
- catch (Err e) { return; }
- }
- }
-
- void quoted(alias f)(ref string s)
- {
- import std.string : startsWith;
-
- if (s.startsWith("'"))
- {
- checkLiteral("'",s);
- f(s);
- checkLiteral("'",s);
- }
- else
- {
- checkLiteral("\"",s);
- f(s);
- checkLiteral("\"",s);
- }
- }
-
- void seq(alias f,alias g)(ref string s)
- {
- f(s);
- g(s);
- }
-}
-
-/*
- * Check an entire XML document for well-formedness
- *
- * Params:
- * s = the document to be checked, passed as a string
- *
- * Throws: CheckException if the document is not well formed
- *
- * CheckException's toString() method will yield the complete hierarchy of
- * parse failure (the XML equivalent of a stack trace), giving the line and
- * column number of every failure at every level.
- */
-void check(string s) @safe pure
-{
- try
- {
- checkChars(s);
- checkDocument(s);
- if (s.length != 0) throw new Err(s,"Junk found after document");
- }
- catch (Err e)
- {
- e.complete(s);
- throw e;
- }
-}
-
-@system pure unittest
-{
- import std.string : indexOf;
-
- try
- {
- check(q"[<?xml version="1.0"?>
- <catalog>
- <book id="bk101">
- <author>Gambardella, Matthew</author>
- <title>XML Developer's Guide</title>
- <genre>Computer</genre>
- <price>44.95</price>
- <publish_date>2000-10-01</publish_date>
- <description>An in-depth look at creating applications
- with XML.</description>
- </book>
- <book id="bk102">
- <author>Ralls, Kim</author>
- <title>Midnight Rain</title>
- <genre>Fantasy</genres>
- <price>5.95</price>
- <publish_date>2000-12-16</publish_date>
- <description>A former architect battles corporate zombies,
- an evil sorceress, and her own childhood to become queen
- of the world.</description>
- </book>
- <book id="bk103">
- <author>Corets, Eva</author>
- <title>Maeve Ascendant</title>
- <genre>Fantasy</genre>
- <price>5.95</price>
- <publish_date>2000-11-17</publish_date>
- <description>After the collapse of a nanotechnology
- society in England, the young survivors lay the
- foundation for a new society.</description>
- </book>
- </catalog>
- ]");
- assert(false);
- }
- catch (CheckException e)
- {
- auto n = e.toString().indexOf("end tag name \"genres\" differs"~
- " from start tag name \"genre\"");
- assert(n != -1);
- }
-}
-
-@system unittest
-{
- string s = q"EOS
-<?xml version="1.0"?>
-<set>
- <one>A</one>
- <!-- comment -->
- <two>B</two>
-</set>
-EOS";
- try
- {
- check(s);
- }
- catch (CheckException e)
- {
- assert(0, e.toString());
- }
-}
-
-@system unittest
-{
- string test_xml = `<?xml version="1.0" encoding='UTF-8'?><r><stream:stream
- xmlns:stream="http://etherx.'jabber'.org/streams"
- xmlns="jabber:'client'" from='jid.pl' id="587a5767"
- xml:lang="en" version="1.0" attr='a"b"c'>
- </stream:stream></r>`;
-
- DocumentParser parser = new DocumentParser(test_xml);
- bool tested = false;
- parser.onStartTag["stream:stream"] = (ElementParser p) {
- assert(p.tag.attr["xmlns"] == "jabber:'client'");
- assert(p.tag.attr["from"] == "jid.pl");
- assert(p.tag.attr["attr"] == "a\"b\"c");
- tested = true;
- };
- parser.parse();
- assert(tested);
-}
-
-@system unittest
-{
- string s = q"EOS
-<?xml version="1.0" encoding="utf-8"?> <Tests>
- <Test thing="What &amp; Up">What &amp; Up Second</Test>
-</Tests>
-EOS";
- auto xml = new DocumentParser(s);
-
- xml.onStartTag["Test"] = (ElementParser xml) {
- assert(xml.tag.attr["thing"] == "What & Up");
- };
-
- xml.onEndTag["Test"] = (in Element e) {
- assert(e.text() == "What & Up Second");
- };
- xml.parse();
-}
-
-@system unittest
-{
- string s = `<tag attr="&quot;value&gt;" />`;
- auto doc = new Document(s);
- assert(doc.toString() == s);
-}
-
-/* The base class for exceptions thrown by this module */
-class XMLException : Exception { this(string msg) @safe pure { super(msg); } }
-
-// Other exceptions
-
-// Thrown during Comment constructor
-class CommentException : XMLException
-{ private this(string msg) @safe pure { super(msg); } }
-
-// Thrown during CData constructor
-class CDataException : XMLException
-{ private this(string msg) @safe pure { super(msg); } }
-
-// Thrown during XMLInstruction constructor
-class XIException : XMLException
-{ private this(string msg) @safe pure { super(msg); } }
-
-// Thrown during ProcessingInstruction constructor
-class PIException : XMLException
-{ private this(string msg) @safe pure { super(msg); } }
-
-// Thrown during Text constructor
-class TextException : XMLException
-{ private this(string msg) @safe pure { super(msg); } }
-
-// Thrown during decode()
-class DecodeException : XMLException
-{ private this(string msg) @safe pure { super(msg); } }
-
-// Thrown if comparing with wrong type
-class InvalidTypeException : XMLException
-{ private this(string msg) @safe pure { super(msg); } }
-
-// Thrown when parsing for Tags
-class TagException : XMLException
-{ private this(string msg) @safe pure { super(msg); } }
-
-/*
- * Thrown during check()
- */
-class CheckException : XMLException
-{
- CheckException err; // Parent in hierarchy
- private string tail;
- /*
- * Name of production rule which failed to parse,
- * or specific error message
- */
- string msg;
- size_t line = 0; // Line number at which parse failure occurred
- size_t column = 0; // Column number at which parse failure occurred
-
- private this(string tail,string msg,Err err=null) @safe pure
- {
- super(null);
- this.tail = tail;
- this.msg = msg;
- this.err = err;
- }
-
- private void complete(string entire) @safe pure
- {
- import std.string : count, lastIndexOf;
- import std.utf : toUTF32;
-
- string head = entire[0..$-tail.length];
- ptrdiff_t n = head.lastIndexOf('\n') + 1;
- line = head.count("\n") + 1;
- dstring t = toUTF32(head[n..$]);
- column = t.length + 1;
- if (err !is null) err.complete(entire);
- }
-
- override string toString() const @safe pure
- {
- import std.format : format;
-
- string s;
- if (line != 0) s = format("Line %d, column %d: ",line,column);
- s ~= msg;
- s ~= '\n';
- if (err !is null) s = err.toString() ~ s;
- return s;
- }
-}
-
-private alias Err = CheckException;
-
-// Private helper functions
-
-private
-{
- inout(T) toType(T)(inout return scope Object o)
- {
- T t = cast(T)(o);
- if (t is null)
- {
- throw new InvalidTypeException("Attempt to compare a "
- ~ T.stringof ~ " with an instance of another type");
- }
- return t;
- }
-
- string chop(ref string s, size_t n) @safe pure nothrow
- {
- if (n == -1) n = s.length;
- string t = s[0 .. n];
- s = s[n..$];
- return t;
- }
-
- bool optc(ref string s, char c) @safe pure nothrow
- {
- immutable bool b = s.length != 0 && s[0] == c;
- if (b) s = s[1..$];
- return b;
- }
-
- void reqc(ref string s, char c) @safe pure
- {
- if (s.length == 0 || s[0] != c) throw new TagException("");
- s = s[1..$];
- }
-
- char requireOneOf(ref string s, string chars) @safe pure
- {
- import std.string : indexOf;
-
- if (s.length == 0 || indexOf(chars,s[0]) == -1)
- throw new TagException("");
- immutable char ch = s[0];
- s = s[1..$];
- return ch;
- }
-
- alias hash = .hashOf;
-
- // Definitions from the XML specification
- immutable CharTable=[0x9,0x9,0xA,0xA,0xD,0xD,0x20,0xD7FF,0xE000,0xFFFD,
- 0x10000,0x10FFFF];
- immutable BaseCharTable=[0x0041,0x005A,0x0061,0x007A,0x00C0,0x00D6,0x00D8,
- 0x00F6,0x00F8,0x00FF,0x0100,0x0131,0x0134,0x013E,0x0141,0x0148,0x014A,
- 0x017E,0x0180,0x01C3,0x01CD,0x01F0,0x01F4,0x01F5,0x01FA,0x0217,0x0250,
- 0x02A8,0x02BB,0x02C1,0x0386,0x0386,0x0388,0x038A,0x038C,0x038C,0x038E,
- 0x03A1,0x03A3,0x03CE,0x03D0,0x03D6,0x03DA,0x03DA,0x03DC,0x03DC,0x03DE,
- 0x03DE,0x03E0,0x03E0,0x03E2,0x03F3,0x0401,0x040C,0x040E,0x044F,0x0451,
- 0x045C,0x045E,0x0481,0x0490,0x04C4,0x04C7,0x04C8,0x04CB,0x04CC,0x04D0,
- 0x04EB,0x04EE,0x04F5,0x04F8,0x04F9,0x0531,0x0556,0x0559,0x0559,0x0561,
- 0x0586,0x05D0,0x05EA,0x05F0,0x05F2,0x0621,0x063A,0x0641,0x064A,0x0671,
- 0x06B7,0x06BA,0x06BE,0x06C0,0x06CE,0x06D0,0x06D3,0x06D5,0x06D5,0x06E5,
- 0x06E6,0x0905,0x0939,0x093D,0x093D,0x0958,0x0961,0x0985,0x098C,0x098F,
- 0x0990,0x0993,0x09A8,0x09AA,0x09B0,0x09B2,0x09B2,0x09B6,0x09B9,0x09DC,
- 0x09DD,0x09DF,0x09E1,0x09F0,0x09F1,0x0A05,0x0A0A,0x0A0F,0x0A10,0x0A13,
- 0x0A28,0x0A2A,0x0A30,0x0A32,0x0A33,0x0A35,0x0A36,0x0A38,0x0A39,0x0A59,
- 0x0A5C,0x0A5E,0x0A5E,0x0A72,0x0A74,0x0A85,0x0A8B,0x0A8D,0x0A8D,0x0A8F,
- 0x0A91,0x0A93,0x0AA8,0x0AAA,0x0AB0,0x0AB2,0x0AB3,0x0AB5,0x0AB9,0x0ABD,
- 0x0ABD,0x0AE0,0x0AE0,0x0B05,0x0B0C,0x0B0F,0x0B10,0x0B13,0x0B28,0x0B2A,
- 0x0B30,0x0B32,0x0B33,0x0B36,0x0B39,0x0B3D,0x0B3D,0x0B5C,0x0B5D,0x0B5F,
- 0x0B61,0x0B85,0x0B8A,0x0B8E,0x0B90,0x0B92,0x0B95,0x0B99,0x0B9A,0x0B9C,
- 0x0B9C,0x0B9E,0x0B9F,0x0BA3,0x0BA4,0x0BA8,0x0BAA,0x0BAE,0x0BB5,0x0BB7,
- 0x0BB9,0x0C05,0x0C0C,0x0C0E,0x0C10,0x0C12,0x0C28,0x0C2A,0x0C33,0x0C35,
- 0x0C39,0x0C60,0x0C61,0x0C85,0x0C8C,0x0C8E,0x0C90,0x0C92,0x0CA8,0x0CAA,
- 0x0CB3,0x0CB5,0x0CB9,0x0CDE,0x0CDE,0x0CE0,0x0CE1,0x0D05,0x0D0C,0x0D0E,
- 0x0D10,0x0D12,0x0D28,0x0D2A,0x0D39,0x0D60,0x0D61,0x0E01,0x0E2E,0x0E30,
- 0x0E30,0x0E32,0x0E33,0x0E40,0x0E45,0x0E81,0x0E82,0x0E84,0x0E84,0x0E87,
- 0x0E88,0x0E8A,0x0E8A,0x0E8D,0x0E8D,0x0E94,0x0E97,0x0E99,0x0E9F,0x0EA1,
- 0x0EA3,0x0EA5,0x0EA5,0x0EA7,0x0EA7,0x0EAA,0x0EAB,0x0EAD,0x0EAE,0x0EB0,
- 0x0EB0,0x0EB2,0x0EB3,0x0EBD,0x0EBD,0x0EC0,0x0EC4,0x0F40,0x0F47,0x0F49,
- 0x0F69,0x10A0,0x10C5,0x10D0,0x10F6,0x1100,0x1100,0x1102,0x1103,0x1105,
- 0x1107,0x1109,0x1109,0x110B,0x110C,0x110E,0x1112,0x113C,0x113C,0x113E,
- 0x113E,0x1140,0x1140,0x114C,0x114C,0x114E,0x114E,0x1150,0x1150,0x1154,
- 0x1155,0x1159,0x1159,0x115F,0x1161,0x1163,0x1163,0x1165,0x1165,0x1167,
- 0x1167,0x1169,0x1169,0x116D,0x116E,0x1172,0x1173,0x1175,0x1175,0x119E,
- 0x119E,0x11A8,0x11A8,0x11AB,0x11AB,0x11AE,0x11AF,0x11B7,0x11B8,0x11BA,
- 0x11BA,0x11BC,0x11C2,0x11EB,0x11EB,0x11F0,0x11F0,0x11F9,0x11F9,0x1E00,
- 0x1E9B,0x1EA0,0x1EF9,0x1F00,0x1F15,0x1F18,0x1F1D,0x1F20,0x1F45,0x1F48,
- 0x1F4D,0x1F50,0x1F57,0x1F59,0x1F59,0x1F5B,0x1F5B,0x1F5D,0x1F5D,0x1F5F,
- 0x1F7D,0x1F80,0x1FB4,0x1FB6,0x1FBC,0x1FBE,0x1FBE,0x1FC2,0x1FC4,0x1FC6,
- 0x1FCC,0x1FD0,0x1FD3,0x1FD6,0x1FDB,0x1FE0,0x1FEC,0x1FF2,0x1FF4,0x1FF6,
- 0x1FFC,0x2126,0x2126,0x212A,0x212B,0x212E,0x212E,0x2180,0x2182,0x3041,
- 0x3094,0x30A1,0x30FA,0x3105,0x312C,0xAC00,0xD7A3];
- immutable IdeographicTable=[0x3007,0x3007,0x3021,0x3029,0x4E00,0x9FA5];
- immutable CombiningCharTable=[0x0300,0x0345,0x0360,0x0361,0x0483,0x0486,
- 0x0591,0x05A1,0x05A3,0x05B9,0x05BB,0x05BD,0x05BF,0x05BF,0x05C1,0x05C2,
- 0x05C4,0x05C4,0x064B,0x0652,0x0670,0x0670,0x06D6,0x06DC,0x06DD,0x06DF,
- 0x06E0,0x06E4,0x06E7,0x06E8,0x06EA,0x06ED,0x0901,0x0903,0x093C,0x093C,
- 0x093E,0x094C,0x094D,0x094D,0x0951,0x0954,0x0962,0x0963,0x0981,0x0983,
- 0x09BC,0x09BC,0x09BE,0x09BE,0x09BF,0x09BF,0x09C0,0x09C4,0x09C7,0x09C8,
- 0x09CB,0x09CD,0x09D7,0x09D7,0x09E2,0x09E3,0x0A02,0x0A02,0x0A3C,0x0A3C,
- 0x0A3E,0x0A3E,0x0A3F,0x0A3F,0x0A40,0x0A42,0x0A47,0x0A48,0x0A4B,0x0A4D,
- 0x0A70,0x0A71,0x0A81,0x0A83,0x0ABC,0x0ABC,0x0ABE,0x0AC5,0x0AC7,0x0AC9,
- 0x0ACB,0x0ACD,0x0B01,0x0B03,0x0B3C,0x0B3C,0x0B3E,0x0B43,0x0B47,0x0B48,
- 0x0B4B,0x0B4D,0x0B56,0x0B57,0x0B82,0x0B83,0x0BBE,0x0BC2,0x0BC6,0x0BC8,
- 0x0BCA,0x0BCD,0x0BD7,0x0BD7,0x0C01,0x0C03,0x0C3E,0x0C44,0x0C46,0x0C48,
- 0x0C4A,0x0C4D,0x0C55,0x0C56,0x0C82,0x0C83,0x0CBE,0x0CC4,0x0CC6,0x0CC8,
- 0x0CCA,0x0CCD,0x0CD5,0x0CD6,0x0D02,0x0D03,0x0D3E,0x0D43,0x0D46,0x0D48,
- 0x0D4A,0x0D4D,0x0D57,0x0D57,0x0E31,0x0E31,0x0E34,0x0E3A,0x0E47,0x0E4E,
- 0x0EB1,0x0EB1,0x0EB4,0x0EB9,0x0EBB,0x0EBC,0x0EC8,0x0ECD,0x0F18,0x0F19,
- 0x0F35,0x0F35,0x0F37,0x0F37,0x0F39,0x0F39,0x0F3E,0x0F3E,0x0F3F,0x0F3F,
- 0x0F71,0x0F84,0x0F86,0x0F8B,0x0F90,0x0F95,0x0F97,0x0F97,0x0F99,0x0FAD,
- 0x0FB1,0x0FB7,0x0FB9,0x0FB9,0x20D0,0x20DC,0x20E1,0x20E1,0x302A,0x302F,
- 0x3099,0x3099,0x309A,0x309A];
- immutable DigitTable=[0x0030,0x0039,0x0660,0x0669,0x06F0,0x06F9,0x0966,
- 0x096F,0x09E6,0x09EF,0x0A66,0x0A6F,0x0AE6,0x0AEF,0x0B66,0x0B6F,0x0BE7,
- 0x0BEF,0x0C66,0x0C6F,0x0CE6,0x0CEF,0x0D66,0x0D6F,0x0E50,0x0E59,0x0ED0,
- 0x0ED9,0x0F20,0x0F29];
- immutable ExtenderTable=[0x00B7,0x00B7,0x02D0,0x02D0,0x02D1,0x02D1,0x0387,
- 0x0387,0x0640,0x0640,0x0E46,0x0E46,0x0EC6,0x0EC6,0x3005,0x3005,0x3031,
- 0x3035,0x309D,0x309E,0x30FC,0x30FE];
-
- bool lookup(const(int)[] table, int c) @safe @nogc nothrow pure
- {
- while (table.length != 0)
- {
- auto m = (table.length >> 1) & ~1;
- if (c < table[m])
- {
- table = table[0 .. m];
- }
- else if (c > table[m+1])
- {
- table = table[m+2..$];
- }
- else return true;
- }
- return false;
- }
-
- string startOf(string s) @safe nothrow pure
- {
- string r;
- foreach (char c;s)
- {
- r ~= (c < 0x20 || c > 0x7F) ? '.' : c;
- if (r.length >= 40) { r ~= "___"; break; }
- }
- return r;
- }
-
- void exit(string s=null)
- {
- throw new XMLException(s);
- }
-}