diff options
author | Thomas Koenig <tkoenig@gcc.gnu.org> | 2021-09-13 19:49:49 +0200 |
---|---|---|
committer | Thomas Koenig <tkoenig@gcc.gnu.org> | 2021-09-13 19:49:49 +0200 |
commit | b18a97e5dd0935e1c4a626c230f21457d0aad3d5 (patch) | |
tree | c1818f41af6fe780deafb6cd6a183f32085fe654 /libphobos/src | |
parent | e76a53644c9d70e998c0d050e9a456af388c6b61 (diff) | |
download | gcc-b18a97e5dd0935e1c4a626c230f21457d0aad3d5.zip gcc-b18a97e5dd0935e1c4a626c230f21457d0aad3d5.tar.gz gcc-b18a97e5dd0935e1c4a626c230f21457d0aad3d5.tar.bz2 |
Merged current trunk to branch.
Diffstat (limited to 'libphobos/src')
28 files changed, 1544 insertions, 1186 deletions
diff --git a/libphobos/src/MERGE b/libphobos/src/MERGE index cd620c9..01cf594 100644 --- a/libphobos/src/MERGE +++ b/libphobos/src/MERGE @@ -1,4 +1,4 @@ -38873fe6ee70fe8e2b7a41b7c3663e090e27d61b +55bb17543138a87c376a84745f2a30ec00bdecd9 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 3769d8e..9f62510 100644 --- a/libphobos/src/Makefile.am +++ b/libphobos/src/Makefile.am @@ -1,5 +1,5 @@ ## Makefile for the Phobos standard library. -## Copyright (C) 2012-2020 Free Software Foundation, Inc. +## Copyright (C) 2012-2021 Free Software Foundation, Inc. ## ## GCC is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -24,8 +24,8 @@ D_EXTRA_DFLAGS=-nostdinc -I $(srcdir) \ # D flags for compilation AM_DFLAGS= \ - $(phobos_compiler_pic_flag) \ - $(WARN_DFLAGS) $(CHECKING_DFLAGS) $(CET_DFLAGS) + $(phobos_lt_pic_flag) $(phobos_compiler_shared_flag) \ + $(WARN_DFLAGS) $(CHECKING_DFLAGS) $(SECTION_FLAGS) $(CET_DFLAGS) # Flags for other kinds of sources AM_CFLAGS=$(CET_FLAGS) @@ -45,8 +45,12 @@ libgphobos_la_SOURCES = $(ALL_PHOBOS_SOURCES) libgphobos_la_LIBTOOLFLAGS = libgphobos_la_LDFLAGS = -Wc,-nophoboslib,-dstartfiles,-B../libdruntime/gcc \ -version-info $(libtool_VERSION) +if ENABLE_LIBDRUNTIME_ONLY +libgphobos_la_LIBADD = ../libdruntime/libgdruntime_convenience.la +else libgphobos_la_LIBADD = \ ../libdruntime/libgdruntime_convenience.la $(LIBZ) +endif libgphobos_la_DEPENDENCIES = \ ../libdruntime/libgdruntime_convenience.la libgphobos.spec @@ -135,11 +139,11 @@ PHOBOS_DSOURCES = etc/c/curl.d etc/c/sqlite3.d etc/c/zlib.d \ std/regex/internal/backtracking.d std/regex/internal/generator.d \ std/regex/internal/ir.d std/regex/internal/kickstart.d \ std/regex/internal/parser.d std/regex/internal/tests.d \ - std/regex/internal/thompson.d std/regex/package.d std/signals.d \ - std/socket.d std/stdint.d std/stdio.d std/string.d std/system.d \ - std/traits.d std/typecons.d std/typetuple.d std/uni.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/regex/internal/tests2.d std/regex/internal/thompson.d \ + std/regex/package.d std/signals.d std/socket.d std/stdint.d \ + std/stdio.d std/string.d std/system.d std/traits.d std/typecons.d \ + std/typetuple.d std/uni.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 endif diff --git a/libphobos/src/Makefile.in b/libphobos/src/Makefile.in index 2e72178..f8b7648 100644 --- a/libphobos/src/Makefile.in +++ b/libphobos/src/Makefile.in @@ -249,6 +249,7 @@ am__dirstamp = $(am__leading_dot)dirstamp @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/regex/internal/kickstart.lo \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/regex/internal/parser.lo \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/regex/internal/tests.lo \ +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/regex/internal/tests2.lo \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/regex/internal/thompson.lo \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/regex/package.lo \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/signals.lo std/socket.lo \ @@ -387,6 +388,7 @@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ RANLIB = @RANLIB@ +SECTION_FLAGS = @SECTION_FLAGS@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ @@ -414,6 +416,8 @@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ +enable_shared = @enable_shared@ +enable_static = @enable_static@ exec_prefix = @exec_prefix@ gcc_version = @gcc_version@ gdc_include_dir = @gdc_include_dir@ @@ -443,6 +447,7 @@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ phobos_compiler_pic_flag = @phobos_compiler_pic_flag@ phobos_compiler_shared_flag = @phobos_compiler_shared_flag@ +phobos_lt_pic_flag = @phobos_lt_pic_flag@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ @@ -477,8 +482,8 @@ D_EXTRA_DFLAGS = -nostdinc -I $(srcdir) \ # D flags for compilation AM_DFLAGS = \ - $(phobos_compiler_pic_flag) \ - $(WARN_DFLAGS) $(CHECKING_DFLAGS) $(CET_DFLAGS) + $(phobos_lt_pic_flag) $(phobos_compiler_shared_flag) \ + $(WARN_DFLAGS) $(CHECKING_DFLAGS) $(SECTION_FLAGS) $(CET_DFLAGS) # Flags for other kinds of sources @@ -499,9 +504,10 @@ libgphobos_la_LIBTOOLFLAGS = libgphobos_la_LDFLAGS = -Wc,-nophoboslib,-dstartfiles,-B../libdruntime/gcc \ -version-info $(libtool_VERSION) -libgphobos_la_LIBADD = \ - ../libdruntime/libgdruntime_convenience.la $(LIBZ) +@ENABLE_LIBDRUNTIME_ONLY_FALSE@libgphobos_la_LIBADD = \ +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ ../libdruntime/libgdruntime_convenience.la $(LIBZ) +@ENABLE_LIBDRUNTIME_ONLY_TRUE@libgphobos_la_LIBADD = ../libdruntime/libgdruntime_convenience.la libgphobos_la_DEPENDENCIES = \ ../libdruntime/libgdruntime_convenience.la libgphobos.spec @@ -569,12 +575,12 @@ libgphobos_la_LINK = $(LIBTOOL) --tag=D $(libgphobos_la_LIBTOOLFLAGS) \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/regex/internal/backtracking.d std/regex/internal/generator.d \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/regex/internal/ir.d std/regex/internal/kickstart.d \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/regex/internal/parser.d std/regex/internal/tests.d \ -@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/regex/internal/thompson.d std/regex/package.d std/signals.d \ -@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/socket.d std/stdint.d std/stdio.d std/string.d std/system.d \ -@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/traits.d std/typecons.d std/typetuple.d std/uni.d std/uri.d \ -@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/utf.d std/uuid.d std/variant.d std/windows/charset.d \ -@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/windows/registry.d std/windows/syserror.d std/xml.d std/zip.d \ -@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/zlib.d +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/regex/internal/tests2.d std/regex/internal/thompson.d \ +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/regex/package.d std/signals.d std/socket.d std/stdint.d \ +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/stdio.d std/string.d std/system.d std/traits.d std/typecons.d \ +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/typetuple.d std/uni.d std/uri.d std/utf.d std/uuid.d std/variant.d \ +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/windows/charset.d std/windows/registry.d std/windows/syserror.d \ +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/xml.d std/zip.d std/zlib.d # Source file definitions. Boring stuff, auto-generated with @@ -848,6 +854,7 @@ std/regex/internal/ir.lo: std/regex/internal/$(am__dirstamp) std/regex/internal/kickstart.lo: std/regex/internal/$(am__dirstamp) std/regex/internal/parser.lo: std/regex/internal/$(am__dirstamp) std/regex/internal/tests.lo: std/regex/internal/$(am__dirstamp) +std/regex/internal/tests2.lo: std/regex/internal/$(am__dirstamp) std/regex/internal/thompson.lo: std/regex/internal/$(am__dirstamp) std/regex/$(am__dirstamp): @$(MKDIR_P) std/regex diff --git a/libphobos/src/std/algorithm/iteration.d b/libphobos/src/std/algorithm/iteration.d index c77792d..19cfb77 100644 --- a/libphobos/src/std/algorithm/iteration.d +++ b/libphobos/src/std/algorithm/iteration.d @@ -769,9 +769,9 @@ private struct MapResult(alias fun, Range) string s1 = "hello world!"; dstring s2 = "日本語"; dstring s3 = "hello world!"d; - auto ms1 = map!(std.ascii.toUpper)(s1); - auto ms2 = map!(std.ascii.toUpper)(s2); - auto ms3 = map!(std.ascii.toUpper)(s3); + auto ms1 = map!(toUpper)(s1); + auto ms2 = map!(toUpper)(s2); + auto ms3 = map!(toUpper)(s3); static assert(!is(ms1[0])); //narrow strings can't be indexed assert(ms2[0] == '日'); assert(ms3[0] == 'H'); @@ -4450,7 +4450,7 @@ private struct SplitterResult(alias isTerminator, Range) ["là", "dove", "terminava", "quella", "valle"] )); assert(equal( - splitter!(std.uni.isWhite)("là dove terminava quella valle"), + splitter!(isWhite)("là dove terminava quella valle"), ["là", "dove", "terminava", "quella", "valle"] )); assert(equal(splitter!"a=='本'"("日本語"), ["日", "語"])); diff --git a/libphobos/src/std/algorithm/searching.d b/libphobos/src/std/algorithm/searching.d index 6468a87..09073f6 100644 --- a/libphobos/src/std/algorithm/searching.d +++ b/libphobos/src/std/algorithm/searching.d @@ -959,8 +959,8 @@ if (isInputRange!R && import std.ascii : isDigit; import std.uni : isWhite; - assert(countUntil!(std.uni.isWhite)("hello world") == 5); - assert(countUntil!(std.ascii.isDigit)("hello world") == -1); + assert(countUntil!(isWhite)("hello world") == 5); + assert(countUntil!(isDigit)("hello world") == -1); assert(countUntil!"a > 20"([0, 7, 12, 22, 9]) == 3); } diff --git a/libphobos/src/std/container/rbtree.d b/libphobos/src/std/container/rbtree.d index 861da5e..5e31ac2 100644 --- a/libphobos/src/std/container/rbtree.d +++ b/libphobos/src/std/container/rbtree.d @@ -1814,8 +1814,8 @@ assert(equal(rbt[], [5])); test!byte(); } -import std.range.primitives : isInputRange, isSomeString, ElementType; -import std.traits : isArray; +import std.range.primitives : isInputRange, ElementType; +import std.traits : isArray, isSomeString; /++ Convenience function for creating a $(D RedBlackTree!E) from a list of diff --git a/libphobos/src/std/conv.d b/libphobos/src/std/conv.d index 743d203..3560d13 100644 --- a/libphobos/src/std/conv.d +++ b/libphobos/src/std/conv.d @@ -3148,8 +3148,6 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum { version (CRuntime_Microsoft) ld1 = 0x1.FFFFFFFFFFFFFFFEp-16382L; // strtold currently mapped to strtod - else version (CRuntime_Bionic) - ld1 = 0x1.FFFFFFFFFFFFFFFEp-16382L; // strtold currently mapped to strtod else ld1 = strtold(s.ptr, null); } @@ -3969,7 +3967,7 @@ if (isOctalLiteral(num)) /// Ditto template octal(alias decimalInteger) -if (isIntegral!(typeof(decimalInteger))) +if (is(typeof(decimalInteger)) && isIntegral!(typeof(decimalInteger))) { enum octal = octal!(typeof(decimalInteger))(to!string(decimalInteger)); } diff --git a/libphobos/src/std/datetime/systime.d b/libphobos/src/std/datetime/systime.d index 326b544..913d360 100644 --- a/libphobos/src/std/datetime/systime.d +++ b/libphobos/src/std/datetime/systime.d @@ -7,6 +7,15 @@ +/ module std.datetime.systime; +version (OSX) + version = Darwin; +else version (iOS) + version = Darwin; +else version (TVOS) + version = Darwin; +else version (WatchOS) + version = Darwin; + import core.time; import std.datetime.date; import std.datetime.timezone; @@ -39,6 +48,16 @@ version (unittest) initializeTests(); } +version (unittest) private bool clockSupported(ClockType c) +{ + // Skip unsupported clocks on older linux kernels, assume that only + // CLOCK_MONOTONIC and CLOCK_REALTIME exist, as that is the lowest + // common denominator supported by all versions of Linux pre-2.6.12. + version (Linux_Pre_2639) + return c == ClockType.normal || c == ClockType.precise; + else + return true; +} /++ Effectively a namespace to make it clear that the methods it contains are @@ -95,10 +114,13 @@ public: foreach (ct; AliasSeq!(ClockType.coarse, ClockType.precise, ClockType.second)) { scope(failure) writefln("ClockType.%s", ct); - auto value1 = Clock.currTime!ct; - auto value2 = Clock.currTime!ct(UTC()); - assert(value1 <= value2, format("%s %s", value1, value2)); - assert(abs(value1 - value2) <= seconds(2)); + static if (clockSupported(ct)) + { + auto value1 = Clock.currTime!ct; + auto value2 = Clock.currTime!ct(UTC()); + assert(value1 <= value2, format("%s %s (ClockType: %s)", value1, value2, ct)); + assert(abs(value1 - value2) <= seconds(2), format("ClockType.%s", ct)); + } } } @@ -148,18 +170,19 @@ public: static import core.stdc.time; enum hnsecsToUnixEpoch = unixTimeToStdTime(0); - version (OSX) + version (Darwin) { static if (clockType == ClockType.second) return unixTimeToStdTime(core.stdc.time.time(null)); else { import core.sys.posix.sys.time : gettimeofday, timeval; - timeval tv; - if (gettimeofday(&tv, null) != 0) - throw new TimeException("Call to gettimeofday() failed"); + timeval tv = void; + // Posix gettimeofday called with a valid timeval address + // and a null second parameter doesn't fail. + gettimeofday(&tv, null); return convert!("seconds", "hnsecs")(tv.tv_sec) + - convert!("usecs", "hnsecs")(tv.tv_usec) + + tv.tv_usec * 10 + hnsecsToUnixEpoch; } } @@ -175,9 +198,16 @@ public: else static if (clockType == ClockType.normal) alias clockArg = CLOCK_REALTIME; else static if (clockType == ClockType.precise) alias clockArg = CLOCK_REALTIME; else static assert(0, "Previous static if is wrong."); - timespec ts; - if (clock_gettime(clockArg, &ts) != 0) - throw new TimeException("Call to clock_gettime() failed"); + timespec ts = void; + immutable error = clock_gettime(clockArg, &ts); + // Posix clock_gettime called with a valid address and valid clock_id is only + // permitted to fail if the number of seconds does not fit in time_t. If tv_sec + // is long or larger overflow won't happen before 292 billion years A.D. + static if (ts.tv_sec.max < long.max) + { + if (error) + throw new TimeException("Call to clock_gettime() failed"); + } return convert!("seconds", "hnsecs")(ts.tv_sec) + ts.tv_nsec / 100 + hnsecsToUnixEpoch; @@ -192,9 +222,16 @@ public: else static if (clockType == ClockType.precise) alias clockArg = CLOCK_REALTIME_PRECISE; else static if (clockType == ClockType.second) alias clockArg = CLOCK_SECOND; else static assert(0, "Previous static if is wrong."); - timespec ts; - if (clock_gettime(clockArg, &ts) != 0) - throw new TimeException("Call to clock_gettime() failed"); + timespec ts = void; + immutable error = clock_gettime(clockArg, &ts); + // Posix clock_gettime called with a valid address and valid clock_id is only + // permitted to fail if the number of seconds does not fit in time_t. If tv_sec + // is long or larger overflow won't happen before 292 billion years A.D. + static if (ts.tv_sec.max < long.max) + { + if (error) + throw new TimeException("Call to clock_gettime() failed"); + } return convert!("seconds", "hnsecs")(ts.tv_sec) + ts.tv_nsec / 100 + hnsecsToUnixEpoch; @@ -205,12 +242,38 @@ public: return unixTimeToStdTime(core.stdc.time.time(null)); else { - import core.sys.posix.sys.time : gettimeofday, timeval; - timeval tv; - if (gettimeofday(&tv, null) != 0) - throw new TimeException("Call to gettimeofday() failed"); - return convert!("seconds", "hnsecs")(tv.tv_sec) + - convert!("usecs", "hnsecs")(tv.tv_usec) + + import core.sys.netbsd.time : clock_gettime, CLOCK_REALTIME; + timespec ts = void; + immutable error = clock_gettime(CLOCK_REALTIME, &ts); + // Posix clock_gettime called with a valid address and valid clock_id is only + // permitted to fail if the number of seconds does not fit in time_t. If tv_sec + // is long or larger overflow won't happen before 292 billion years A.D. + static if (ts.tv_sec.max < long.max) + { + if (error) + throw new TimeException("Call to clock_gettime() failed"); + } + return convert!("seconds", "hnsecs")(ts.tv_sec) + + ts.tv_nsec / 100 + + hnsecsToUnixEpoch; + } + } + else version (OpenBSD) + { + static if (clockType == ClockType.second) + return unixTimeToStdTime(core.stdc.time.time(null)); + else + { + import core.sys.openbsd.time : clock_gettime, CLOCK_REALTIME; + static if (clockType == ClockType.coarse) alias clockArg = CLOCK_REALTIME; + else static if (clockType == ClockType.normal) alias clockArg = CLOCK_REALTIME; + else static if (clockType == ClockType.precise) alias clockArg = CLOCK_REALTIME; + else static assert(0, "Previous static if is wrong."); + timespec ts; + if (clock_gettime(clockArg, &ts) != 0) + throw new TimeException("Call to clock_gettime() failed"); + return convert!("seconds", "hnsecs")(ts.tv_sec) + + ts.tv_nsec / 100 + hnsecsToUnixEpoch; } } @@ -223,9 +286,16 @@ public: else static if (clockType == ClockType.precise) alias clockArg = CLOCK_REALTIME_PRECISE; else static if (clockType == ClockType.second) alias clockArg = CLOCK_SECOND; else static assert(0, "Previous static if is wrong."); - timespec ts; - if (clock_gettime(clockArg, &ts) != 0) - throw new TimeException("Call to clock_gettime() failed"); + timespec ts = void; + immutable error = clock_gettime(clockArg, &ts); + // Posix clock_gettime called with a valid address and valid clock_id is only + // permitted to fail if the number of seconds does not fit in time_t. If tv_sec + // is long or larger overflow won't happen before 292 billion years A.D. + static if (ts.tv_sec.max < long.max) + { + if (error) + throw new TimeException("Call to clock_gettime() failed"); + } return convert!("seconds", "hnsecs")(ts.tv_sec) + ts.tv_nsec / 100 + hnsecsToUnixEpoch; @@ -241,9 +311,16 @@ public: else static if (clockType == ClockType.normal) alias clockArg = CLOCK_REALTIME; else static if (clockType == ClockType.precise) alias clockArg = CLOCK_REALTIME; else static assert(0, "Previous static if is wrong."); - timespec ts; - if (clock_gettime(clockArg, &ts) != 0) - throw new TimeException("Call to clock_gettime() failed"); + timespec ts = void; + immutable error = clock_gettime(clockArg, &ts); + // Posix clock_gettime called with a valid address and valid clock_id is only + // permitted to fail if the number of seconds does not fit in time_t. If tv_sec + // is long or larger overflow won't happen before 292 billion years A.D. + static if (ts.tv_sec.max < long.max) + { + if (error) + throw new TimeException("Call to clock_gettime() failed"); + } return convert!("seconds", "hnsecs")(ts.tv_sec) + ts.tv_nsec / 100 + hnsecsToUnixEpoch; @@ -270,10 +347,13 @@ public: foreach (ct; AliasSeq!(ClockType.coarse, ClockType.precise, ClockType.second)) { scope(failure) writefln("ClockType.%s", ct); - auto value1 = Clock.currStdTime!ct; - auto value2 = Clock.currStdTime!ct; - assert(value1 <= value2, format("%s %s", value1, value2)); - assert(abs(value1 - value2) <= limit); + static if (clockSupported(ct)) + { + auto value1 = Clock.currStdTime!ct; + auto value2 = Clock.currStdTime!ct; + assert(value1 <= value2, format("%s %s (ClockType: %s)", value1, value2, ct)); + assert(abs(value1 - value2) <= limit); + } } } @@ -9489,7 +9569,7 @@ afterMon: stripAndCheckLen(value[3 .. value.length], "1200:00A".length); } // year - auto found = value[2 .. value.length].find!(not!(std.ascii.isDigit))(); + auto found = value[2 .. value.length].find!(not!(isDigit))(); size_t yearLen = value.length - found.length; if (found.length == 0) throw new DateTimeException("Invalid year"); @@ -9579,7 +9659,7 @@ afterMon: stripAndCheckLen(value[3 .. value.length], "1200:00A".length); case "J": case "j": throw new DateTimeException("Invalid timezone"); default: { - if (all!(std.ascii.isAlpha)(value[0 .. tzLen])) + if (all!(isAlpha)(value[0 .. tzLen])) { tz = new immutable SimpleTimeZone(Duration.zero); break; diff --git a/libphobos/src/std/datetime/timezone.d b/libphobos/src/std/datetime/timezone.d index 7ae1902..9b744ff 100644 --- a/libphobos/src/std/datetime/timezone.d +++ b/libphobos/src/std/datetime/timezone.d @@ -14,6 +14,15 @@ import std.exception : enforce; import std.range.primitives; import std.traits : isIntegral, isSomeString, Unqual; +version (OSX) + version = Darwin; +else version (iOS) + version = Darwin; +else version (TVOS) + version = Darwin; +else version (WatchOS) + version = Darwin; + version (Windows) { import core.stdc.time : time_t; @@ -296,7 +305,7 @@ public: else version (NetBSD) enum utcZone = "UTC"; else version (DragonFlyBSD) enum utcZone = "UTC"; else version (linux) enum utcZone = "UTC"; - else version (OSX) enum utcZone = "UTC"; + else version (Darwin) enum utcZone = "UTC"; else version (Solaris) enum utcZone = "UTC"; else static assert(0, "The location of the UTC timezone file on this Posix platform must be set."); @@ -671,7 +680,11 @@ public: @safe unittest { - assert(LocalTime().dstName !is null); + // tzname, called from dstName, isn't set by default for Musl. + version (CRuntime_Musl) + assert(LocalTime().dstName is null); + else + assert(LocalTime().dstName !is null); version (Posix) { diff --git a/libphobos/src/std/exception.d b/libphobos/src/std/exception.d index 73afadc..56133c9 100644 --- a/libphobos/src/std/exception.d +++ b/libphobos/src/std/exception.d @@ -1478,10 +1478,13 @@ private bool isUnionAliasedImpl(T)(size_t offset) static assert( isUnionAliased!(S.A5, 1)); //a5.b1; } +version (CRuntime_Glibc) version = GNU_STRERROR; +version (CRuntime_UClibc) version = GNU_STRERROR; + package string errnoString(int errno) nothrow @trusted { import core.stdc.string : strlen; - version (CRuntime_Glibc) + version (GNU_STRERROR) { import core.stdc.string : strerror_r; char[1024] buf = void; diff --git a/libphobos/src/std/experimental/allocator/building_blocks/region.d b/libphobos/src/std/experimental/allocator/building_blocks/region.d index 835d093..53f5ef9 100644 --- a/libphobos/src/std/experimental/allocator/building_blocks/region.d +++ b/libphobos/src/std/experimental/allocator/building_blocks/region.d @@ -5,6 +5,15 @@ import std.experimental.allocator.building_blocks.null_allocator; import std.experimental.allocator.common; import std.typecons : Flag, Yes, No; +version (OSX) + version = Darwin; +else version (iOS) + version = Darwin; +else version (TVOS) + version = Darwin; +else version (WatchOS) + version = Darwin; + /** A $(D Region) allocator allocates memory straight from one contiguous chunk. There is no deallocation, and once the region is full, allocation requests @@ -580,14 +589,26 @@ struct InSituRegion(size_t size, size_t minAlign = platformAlignment) assert(a.length == 2001); } -version(CRuntime_Musl) +version (CRuntime_Musl) { // sbrk and brk are disabled in Musl: // https://git.musl-libc.org/cgit/musl/commit/?id=7a995fe706e519a4f55399776ef0df9596101f93 // https://git.musl-libc.org/cgit/musl/commit/?id=863d628d93ea341b6a32661a1654320ce69f6a07 -} else: -private extern(C) void* sbrk(long); -private extern(C) int brk(shared void*); +} +version (DragonFlyBSD) +{ + // sbrk is deprecated in favor of mmap (we could implement a mmap + MAP_NORESERVE + PROT_NONE version) + // brk has been removed + // https://www.dragonflydigest.com/2019/02/22/22586.html + // http://gitweb.dragonflybsd.org/dragonfly.git/commitdiff/dc676eaefa61b0f47bbea1c53eab86fd5ccd78c6 + // http://gitweb.dragonflybsd.org/dragonfly.git/commitdiff/4b5665564ef37dc939a3a9ffbafaab9894c18885 + // http://gitweb.dragonflybsd.org/dragonfly.git/commitdiff/8618d94a0e2ff8303ad93c123a3fa598c26a116e +} +else +{ + private extern(C) void* sbrk(long) nothrow @nogc; + private extern(C) int brk(shared void*) nothrow @nogc; +} /** @@ -599,11 +620,14 @@ that uncontrolled calls to $(D brk) and $(D sbrk) may affect the workings of $(D SbrkRegion) adversely. */ +version (CRuntime_Musl) {} else +version (DragonFlyBSD) {} else version (Posix) struct SbrkRegion(uint minAlign = platformAlignment) { import core.sys.posix.pthread : pthread_mutex_init, pthread_mutex_destroy, pthread_mutex_t, pthread_mutex_lock, pthread_mutex_unlock, - PTHREAD_MUTEX_INITIALIZER; + + PTHREAD_MUTEX_INITIALIZER; private static shared pthread_mutex_t sbrkMutex = PTHREAD_MUTEX_INITIALIZER; import std.typecons : Ternary; @@ -763,7 +787,9 @@ version (Posix) struct SbrkRegion(uint minAlign = platformAlignment) } } -version (Posix) @system unittest +version (CRuntime_Musl) {} else +version (DragonFlyBSD) {} else +version (Posix) @system nothrow @nogc unittest { // Let's test the assumption that sbrk(n) returns the old address const p1 = sbrk(0); @@ -775,7 +801,9 @@ version (Posix) @system unittest sbrk(-4096); } -version (Posix) @system unittest +version (CRuntime_Musl) {} else +version (DragonFlyBSD) {} else +version (Posix) @system nothrow @nogc unittest { import std.typecons : Ternary; alias alloc = SbrkRegion!(8).instance; @@ -786,7 +814,7 @@ version (Posix) @system unittest assert(alloc.owns(a) == Ternary.yes); assert(alloc.owns(b) == Ternary.yes); // reducing the brk does not work on OSX - version (OSX) {} else + version (Darwin) {} else { assert(alloc.deallocate(b)); assert(alloc.deallocateAll); diff --git a/libphobos/src/std/experimental/allocator/mmap_allocator.d b/libphobos/src/std/experimental/allocator/mmap_allocator.d index 945859b..e07d444 100644 --- a/libphobos/src/std/experimental/allocator/mmap_allocator.d +++ b/libphobos/src/std/experimental/allocator/mmap_allocator.d @@ -46,6 +46,21 @@ struct MmapAllocator if (b.ptr) munmap(b.ptr, b.length) == 0 || assert(0); return true; } + + // Anonymous mmap might be zero-filled on all Posix systems but + // not all commit to this in the documentation. + version (linux) + // http://man7.org/linux/man-pages/man2/mmap.2.html + package alias allocateZeroed = allocate; + else version (NetBSD) + // http://netbsd.gw.com/cgi-bin/man-cgi?mmap+2+NetBSD-current + package alias allocateZeroed = allocate; + else version (Solaris) + // https://docs.oracle.com/cd/E88353_01/html/E37841/mmap-2.html + package alias allocateZeroed = allocate; + else version (AIX) + // https://www.ibm.com/support/knowledgecenter/en/ssw_aix_71/com.ibm.aix.basetrf1/mmap.htm + package alias allocateZeroed = allocate; } else version (Windows) { @@ -67,6 +82,8 @@ struct MmapAllocator { return b.ptr is null || VirtualFree(b.ptr, 0, MEM_RELEASE) != 0; } + + package alias allocateZeroed = allocate; } } diff --git a/libphobos/src/std/experimental/logger/nulllogger.d b/libphobos/src/std/experimental/logger/nulllogger.d index fa511be..0c55377 100644 --- a/libphobos/src/std/experimental/logger/nulllogger.d +++ b/libphobos/src/std/experimental/logger/nulllogger.d @@ -31,7 +31,7 @@ class NullLogger : Logger /// @safe unittest { - import std.experimental.logger.nulllogger : LogLevel; + import std.experimental.logger.core : LogLevel; auto nl1 = new NullLogger(LogLevel.all); nl1.info("You will never read this."); diff --git a/libphobos/src/std/experimental/typecons.d b/libphobos/src/std/experimental/typecons.d index 6906f05..07eed8f 100644 --- a/libphobos/src/std/experimental/typecons.d +++ b/libphobos/src/std/experimental/typecons.d @@ -23,8 +23,7 @@ module std.experimental.typecons; import std.meta; // : AliasSeq, allSatisfy; import std.traits; -import std.typecons : Tuple, tuple, Bind, DerivedFunctionType, - isImplicitlyConvertible, mixinAll, staticIota, +import std.typecons : Tuple, tuple, Bind, DerivedFunctionType, mixinAll, staticIota, GetOverloadedMethods; private @@ -113,7 +112,8 @@ if (Targets.length >= 1 && allSatisfy!(isMutable, Targets)) else { enum foundFunc = findCovariantFunction!(TargetMembers[i], Source, SourceMembers); - debug + version (unittest) {} + else debug { static if (foundFunc == -1) pragma(msg, "Could not locate matching function for: ", diff --git a/libphobos/src/std/file.d b/libphobos/src/std/file.d index 9ba9929..99530cb 100644 --- a/libphobos/src/std/file.d +++ b/libphobos/src/std/file.d @@ -164,6 +164,16 @@ class FileException : Exception +/ immutable uint errno; + private this(in char[] name, in char[] msg, string file, size_t line, uint errno) @safe pure + { + if (msg.empty) + super(name.idup, file, line); + else + super(text(name, ": ", msg), file, line); + + this.errno = errno; + } + /++ Constructor which takes an error message. @@ -175,12 +185,7 @@ class FileException : Exception +/ this(in char[] name, in char[] msg, string file = __FILE__, size_t line = __LINE__) @safe pure { - if (msg.empty) - super(name.idup, file, line); - else - super(text(name, ": ", msg), file, line); - - errno = 0; + this(name, msg, file, line, 0); } /++ @@ -200,8 +205,7 @@ class FileException : Exception string file = __FILE__, size_t line = __LINE__) @safe { - this(name, sysErrorString(errno), file, line); - this.errno = errno; + this(name, sysErrorString(errno), file, line, errno); } else version (Posix) this(in char[] name, uint errno = .errno, @@ -209,8 +213,7 @@ class FileException : Exception size_t line = __LINE__) @trusted { import std.exception : errnoString; - this(name, errnoString(errno), file, line); - this.errno = errno; + this(name, errnoString(errno), file, line, errno); } } @@ -1487,6 +1490,15 @@ if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R)) // vfs.timestamp_precision sysctl to a value greater than zero. // - OS X, where the native filesystem (HFS+) stores filesystem // timestamps with 1-second precision. +// +// Note: on linux systems, although in theory a change to a file date +// can be tracked with precision of 4 msecs, this test waits 20 msecs +// to prevent possible problems relative to the CI services the dlang uses, +// as they may have the HZ setting that controls the software clock set to 100 +// (instead of the more common 250). +// see https://man7.org/linux/man-pages/man7/time.7.html +// https://stackoverflow.com/a/14393315, +// https://issues.dlang.org/show_bug.cgi?id=21148 version (FreeBSD) {} else version (DragonFlyBSD) {} else version (OSX) {} else @@ -1505,7 +1517,7 @@ version (OSX) {} else remove(deleteme); assert(time != lastTime); lastTime = time; - Thread.sleep(10.msecs); + Thread.sleep(20.msecs); } } @@ -2289,7 +2301,7 @@ if (isConvertibleToString!R) @safe unittest { - import std.path : mkdir; + import std.file : mkdir; static assert(__traits(compiles, mkdir(TestAliasedString(null)))); } @@ -2754,15 +2766,27 @@ else version (NetBSD) buffer.length *= 2; } } + else version (DragonFlyBSD) + { + import core.sys.dragonflybsd.sys.sysctl : sysctl, CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME; + import std.exception : errnoEnforce, assumeUnique; + + int[4] mib = [CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1]; + size_t len; + + auto result = sysctl(mib.ptr, mib.length, null, &len, null, 0); // get the length of the path + errnoEnforce(result == 0); + + auto buffer = new char[len - 1]; + result = sysctl(mib.ptr, mib.length, buffer.ptr, &len, null, 0); + errnoEnforce(result == 0); + + return buffer.assumeUnique; + } else version (FreeBSD) { + import core.sys.freebsd.sys.sysctl : sysctl, CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME; import std.exception : errnoEnforce, assumeUnique; - enum - { - CTL_KERN = 1, - KERN_PROC = 14, - KERN_PROC_PATHNAME = 12 - } int[4] mib = [CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1]; size_t len; @@ -2778,11 +2802,58 @@ else version (NetBSD) } else version (NetBSD) { - return readLink("/proc/self/exe"); + import core.sys.netbsd.sys.sysctl : sysctl, CTL_KERN, KERN_PROC_ARGS, KERN_PROC_PATHNAME; + import std.exception : errnoEnforce, assumeUnique; + + int[4] mib = [CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME]; + size_t len; + + auto result = sysctl(mib.ptr, mib.length, null, &len, null, 0); // get the length of the path + errnoEnforce(result == 0); + + auto buffer = new char[len - 1]; + result = sysctl(mib.ptr, mib.length, buffer.ptr, &len, null, 0); + errnoEnforce(result == 0); + + return buffer.assumeUnique; } - else version (DragonFlyBSD) + else version (OpenBSD) { - return readLink("/proc/curproc/file"); + import core.sys.openbsd.sys.sysctl : sysctl, CTL_KERN, KERN_PROC_ARGS, KERN_PROC_ARGV; + import core.sys.posix.unistd : getpid; + import std.conv : to; + import std.exception : enforce, errnoEnforce; + import std.process : searchPathFor; + + int[4] mib = [CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV]; + size_t len; + + auto result = sysctl(mib.ptr, mib.length, null, &len, null, 0); + errnoEnforce(result == 0); + + auto argv = new char*[len - 1]; + result = sysctl(mib.ptr, mib.length, argv.ptr, &len, null, 0); + errnoEnforce(result == 0); + + auto argv0 = argv[0]; + if (*argv0 == '/' || *argv0 == '.') + { + import core.sys.posix.stdlib : realpath; + auto absolutePath = realpath(argv0, null); + scope (exit) + { + if (absolutePath) + free(absolutePath); + } + errnoEnforce(absolutePath); + return to!(string)(absolutePath); + } + else + { + auto absolutePath = searchPathFor(to!string(argv0)); + errnoEnforce(absolutePath); + return absolutePath; + } } else version (Solaris) { @@ -4041,7 +4112,8 @@ auto dirEntries(string path, SpanMode mode, bool followSymlink = true) import std.algorithm.searching : startsWith; import std.array : array; import std.conv : to; - import std.path : dirEntries, buildPath, absolutePath; + import std.path : buildPath, absolutePath; + import std.file : dirEntries; import std.process : thisProcessID; import std.range.primitives : walkLength; diff --git a/libphobos/src/std/format.d b/libphobos/src/std/format.d index 64b1bd3..17e5906 100644 --- a/libphobos/src/std/format.d +++ b/libphobos/src/std/format.d @@ -5199,7 +5199,7 @@ body } debug (unformatRange) printf("\t"); debug (unformatRange) if (!input.empty) printf("input.front = %c, ", input.front); - debug (unformatRange) printf("cont = %.*s\n", cont); + debug (unformatRange) printf("cont = %.*s\n", cast(int) cont.length, cont.ptr); bool checkEnd() { @@ -5246,7 +5246,7 @@ body auto sep = spec.sep !is null ? spec.sep : fmt.trailing; debug (unformatRange) { - if (!sep.empty && !input.empty) printf("-> %c, sep = %.*s\n", input.front, sep); + if (!sep.empty && !input.empty) printf("-> %c, sep = %.*s\n", input.front, cast(int) sep.length, sep.ptr); else printf("\n"); } diff --git a/libphobos/src/std/internal/math/biguintcore.d b/libphobos/src/std/internal/math/biguintcore.d index f5cd769..6fc2d16 100644 --- a/libphobos/src/std/internal/math/biguintcore.d +++ b/libphobos/src/std/internal/math/biguintcore.d @@ -2503,13 +2503,13 @@ pure nothrow void printBiguint(const uint [] data) { char [] buff = biguintToHex(new char[data.length*9], data, '_'); - printf("%.*s\n", buff.length, buff.ptr); + printf("%.*s\n", cast(int) buff.length, buff.ptr); } void printDecimalBigUint(BigUint data) { auto str = data.toDecimalString(0); - printf("%.*s\n", str.length, str.ptr); + printf("%.*s\n", cast(int) str.length, str.ptr); } uint [] a, b; diff --git a/libphobos/src/std/math.d b/libphobos/src/std/math.d index 3d18cfa..336c11a 100644 --- a/libphobos/src/std/math.d +++ b/libphobos/src/std/math.d @@ -167,19 +167,14 @@ version (SystemZ) version = IBMZ_Any; version (RISCV32) version = RISCV_Any; version (RISCV64) version = RISCV_Any; -version (D_InlineAsm_X86) -{ - version = InlineAsm_X86_Any; -} -else version (D_InlineAsm_X86_64) -{ - version = InlineAsm_X86_Any; -} +version (D_InlineAsm_X86) version = InlineAsm_X86_Any; +version (D_InlineAsm_X86_64) version = InlineAsm_X86_Any; -version (CRuntime_Microsoft) +version (InlineAsm_X86_Any) version = InlineAsm_X87; +version (InlineAsm_X87) { - version (InlineAsm_X86_Any) - version = MSVC_InlineAsm; + static assert(real.mant_dig == 64); + version (CRuntime_Microsoft) version = InlineAsm_X87_MSVC; } version (X86_64) version = StaticallyHaveSSE; @@ -268,8 +263,8 @@ version (unittest) alias real_t = double; else alias real_t = real; - ix = sprintf(bufx.ptr, "%.*Lg", ndigits, cast(real_t) x); - iy = sprintf(bufy.ptr, "%.*Lg", ndigits, cast(real_t) y); + ix = sprintf(bufx.ptr, is(real_t == real) ? "%.*Lg" : "%.*g", ndigits, cast(real_t) x); + iy = sprintf(bufy.ptr, is(real_t == real) ? "%.*Lg" : "%.*g", ndigits, cast(real_t) y); assert(ix < bufx.length && ix > 0); assert(ix < bufy.length && ix > 0); @@ -3610,7 +3605,7 @@ real log1p(real x) @safe pure nothrow @nogc real log2(real x) @safe pure nothrow @nogc { version (INLINE_YL2X) - return core.math.yl2x(x, 1); + return core.math.yl2x(x, 1.0L); else { // Special cases are the same as for log. @@ -4586,19 +4581,21 @@ real round(real x) @trusted nothrow @nogc * If the fractional part of x is exactly 0.5, the return value is rounded * away from zero. * - * $(BLUE This function is Posix-Only.) + * $(BLUE This function is not implemented for Digital Mars C runtime.) */ long lround(real x) @trusted nothrow @nogc { - version (Posix) - return core.stdc.math.llroundl(x); - else + version (CRuntime_DigitalMars) assert(0, "lround not implemented"); + else + return core.stdc.math.llroundl(x); } -version (Posix) +/// +@safe nothrow @nogc unittest { - @safe nothrow @nogc unittest + version (CRuntime_DigitalMars) {} + else { assert(lround(0.49) == 0); assert(lround(0.5) == 1); diff --git a/libphobos/src/std/parallelism.d b/libphobos/src/std/parallelism.d index 64fa2f9..61d5cea 100644 --- a/libphobos/src/std/parallelism.d +++ b/libphobos/src/std/parallelism.d @@ -40,6 +40,15 @@ License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0) */ module std.parallelism; +version (OSX) + version = Darwin; +else version (iOS) + version = Darwin; +else version (TVOS) + version = Darwin; +else version (WatchOS) + version = Darwin; + /// @system unittest { @@ -86,107 +95,82 @@ import std.meta; import std.range.primitives; import std.traits; -version (OSX) -{ - version = useSysctlbyname; -} -else version (FreeBSD) -{ - version = useSysctlbyname; -} -else version (DragonFlyBSD) -{ - version = useSysctlbyname; -} -else version (NetBSD) -{ - version = useSysctlbyname; -} +/* +(For now public undocumented with reserved name.) +A lazily initialized global constant. The underlying value is a shared global +statically initialized to `outOfBandValue` which must not be a legit value of +the constant. Upon the first call the situation is detected and the global is +initialized by calling `initializer`. The initializer is assumed to be pure +(even if not marked as such), i.e. return the same value upon repeated calls. +For that reason, no special precautions are taken so `initializer` may be called +more than one time leading to benign races on the cached value. -version (Windows) -{ - // BUGS: Only works on Windows 2000 and above. - shared static this() - { - import core.sys.windows.windows : SYSTEM_INFO, GetSystemInfo; - import std.algorithm.comparison : max; +In the quiescent state the cost of the function is an atomic load from a global. - SYSTEM_INFO si; - GetSystemInfo(&si); - totalCPUs = max(1, cast(uint) si.dwNumberOfProcessors); - } +Params: + T = The type of the pseudo-constant (may be qualified) + outOfBandValue = A value that cannot be valid, it is used for initialization + initializer = The function performing initialization; must be `nothrow` -} -else version (linux) -{ - shared static this() - { - import core.sys.posix.unistd : _SC_NPROCESSORS_ONLN, sysconf; - totalCPUs = cast(uint) sysconf(_SC_NPROCESSORS_ONLN); - } -} -else version (Solaris) -{ - shared static this() - { - import core.sys.posix.unistd : _SC_NPROCESSORS_ONLN, sysconf; - totalCPUs = cast(uint) sysconf(_SC_NPROCESSORS_ONLN); - } -} -else version (useSysctlbyname) +Returns: + The lazily initialized value +*/ +@property pure +T __lazilyInitializedConstant(T, alias outOfBandValue, alias initializer)() +if (is(Unqual!T : T) + && is(typeof(initializer()) : T) + && is(typeof(outOfBandValue) : T)) { - extern(C) int sysctlbyname( - const char *, void *, size_t *, void *, size_t - ); - - shared static this() - { - version (OSX) - { - auto nameStr = "machdep.cpu.core_count\0".ptr; - } - else version (FreeBSD) + static T impl() nothrow + { + // Thread-local cache + static Unqual!T tls = outOfBandValue; + auto local = tls; + // Shortest path, no atomic operations + if (local != outOfBandValue) return local; + // Process-level cache + static shared Unqual!T result = outOfBandValue; + // Initialize both process-level cache and tls + local = atomicLoad(result); + if (local == outOfBandValue) { - auto nameStr = "hw.ncpu\0".ptr; + local = initializer(); + atomicStore(result, local); } - else version (DragonFlyBSD) - { - auto nameStr = "hw.ncpu\0".ptr; - } - else version (NetBSD) - { - auto nameStr = "hw.ncpu\0".ptr; - } - - uint ans; - size_t len = uint.sizeof; - sysctlbyname(nameStr, &ans, &len, null, 0); - totalCPUs = ans; + tls = local; + return local; } + import std.traits : SetFunctionAttributes; + alias Fun = SetFunctionAttributes!(typeof(&impl), "D", + functionAttributes!(typeof(&impl)) | FunctionAttribute.pure_); + auto purified = (() @trusted => cast(Fun) &impl)(); + return purified(); } -else -{ - static assert(0, "Don't know how to get N CPUs on this OS."); -} -immutable size_t cacheLineSize; -shared static this() +// Returns the size of a cache line. +alias cacheLineSize = + __lazilyInitializedConstant!(immutable(size_t), size_t.max, cacheLineSizeImpl); + +private size_t cacheLineSizeImpl() @nogc nothrow @trusted { + size_t result = 0; import core.cpuid : datacache; - size_t lineSize = 0; - foreach (cachelevel; datacache) + foreach (ref const cachelevel; datacache) { - if (cachelevel.lineSize > lineSize && cachelevel.lineSize < uint.max) + if (cachelevel.lineSize > result && cachelevel.lineSize < uint.max) { - lineSize = cachelevel.lineSize; + result = cachelevel.lineSize; } } - - cacheLineSize = lineSize; + return result; } +@nogc @safe nothrow unittest +{ + assert(cacheLineSize == cacheLineSizeImpl); +} /* Atomics code. These forward to core.atomic, but are written like this for two reasons: @@ -957,7 +941,81 @@ if (is(typeof(fun(args))) && isSafeTask!F) The total number of CPU cores available on the current machine, as reported by the operating system. */ -immutable uint totalCPUs; +alias totalCPUs = + __lazilyInitializedConstant!(immutable(uint), uint.max, totalCPUsImpl); + +uint totalCPUsImpl() @nogc nothrow @trusted +{ + version (Windows) + { + // BUGS: Only works on Windows 2000 and above. + import core.sys.windows.winbase : SYSTEM_INFO, GetSystemInfo; + import std.algorithm.comparison : max; + SYSTEM_INFO si; + GetSystemInfo(&si); + return max(1, cast(uint) si.dwNumberOfProcessors); + } + else version (linux) + { + import core.sys.linux.sched : CPU_COUNT, cpu_set_t, sched_getaffinity; + import core.sys.posix.unistd : _SC_NPROCESSORS_ONLN, sysconf; + + cpu_set_t set = void; + if (sched_getaffinity(0, cpu_set_t.sizeof, &set) == 0) + { + int count = CPU_COUNT(&set); + if (count > 0) + return cast(uint) count; + } + return cast(uint) sysconf(_SC_NPROCESSORS_ONLN); + } + else version (Darwin) + { + import core.sys.darwin.sys.sysctl : sysctlbyname; + uint result; + size_t len = result.sizeof; + sysctlbyname("hw.physicalcpu", &result, &len, null, 0); + return result; + } + else version (DragonFlyBSD) + { + import core.sys.dragonflybsd.sys.sysctl : sysctlbyname; + uint result; + size_t len = result.sizeof; + sysctlbyname("hw.ncpu", &result, &len, null, 0); + return result; + } + else version (FreeBSD) + { + import core.sys.freebsd.sys.sysctl : sysctlbyname; + uint result; + size_t len = result.sizeof; + sysctlbyname("hw.ncpu", &result, &len, null, 0); + return result; + } + else version (NetBSD) + { + import core.sys.netbsd.sys.sysctl : sysctlbyname; + uint result; + size_t len = result.sizeof; + sysctlbyname("hw.ncpu", &result, &len, null, 0); + return result; + } + else version (Solaris) + { + import core.sys.posix.unistd : _SC_NPROCESSORS_ONLN, sysconf; + return cast(uint) sysconf(_SC_NPROCESSORS_ONLN); + } + else version (OpenBSD) + { + import core.sys.posix.unistd : _SC_NPROCESSORS_ONLN, sysconf; + return cast(uint) sysconf(_SC_NPROCESSORS_ONLN); + } + else + { + static assert(0, "Don't know how to get N CPUs on this OS."); + } +} /* This class serves two purposes: @@ -3302,11 +3360,7 @@ terminating the main thread. }()); } -private shared uint _defaultPoolThreads; -shared static this() -{ - atomicStore(_defaultPoolThreads, totalCPUs - 1); -} +private shared uint _defaultPoolThreads = uint.max; /** These properties get and set the number of worker threads in the $(D TaskPool) @@ -3316,7 +3370,8 @@ number of worker threads in the instance returned by $(D taskPool). */ @property uint defaultPoolThreads() @trusted { - return atomicLoad(_defaultPoolThreads); + const local = atomicLoad(_defaultPoolThreads); + return local < uint.max ? local : totalCPUs - 1; } /// Ditto @@ -3945,7 +4000,7 @@ version (unittest) import std.array : split; import std.conv : text; import std.exception : assertThrown; - import std.math : approxEqual, sqrt, log; + import std.math : approxEqual, sqrt, log, abs; import std.range : indexed, iota, join; import std.typecons : Tuple, tuple; @@ -4274,7 +4329,7 @@ version (unittest) assert(equal(iota(1_000_000), bufTrickTest)); - auto myTask = task!(std.math.abs)(-1); + auto myTask = task!(abs)(-1); taskPool.put(myTask); assert(myTask.spinForce == 1); diff --git a/libphobos/src/std/process.d b/libphobos/src/std/process.d index b0310a8..1e977aa 100644 --- a/libphobos/src/std/process.d +++ b/libphobos/src/std/process.d @@ -887,7 +887,7 @@ version (Windows) @system unittest // Searches the PATH variable for the given executable file, // (checking that it is in fact executable). version (Posix) -private string searchPathFor(in char[] executable) +package(std) string searchPathFor(in char[] executable) @trusted //TODO: @safe nothrow { import std.algorithm.iteration : splitter; @@ -1358,7 +1358,8 @@ version (Windows) /** -Flags that control the behaviour of $(LREF spawnProcess) and +Flags that control the behaviour of process creation functions in this +module. Most flags only apply to $(LREF spawnProcess) and $(LREF spawnShell). Use bitwise OR to combine flags. @@ -1433,6 +1434,21 @@ enum Config Calling $(LREF wait) or $(LREF kill) with the resulting $(D Pid) is invalid. */ detached = 64, + + /** + By default, the $(LREF execute) and $(LREF executeShell) functions + will capture child processes' both stdout and stderr. This can be + undesirable if the standard output is to be processed or otherwise + used by the invoking program, as `execute`'s result would then + contain a mix of output and warning/error messages. + + Specify this flag when calling `execute` or `executeShell` to + cause invoked processes' stderr stream to be sent to $(REF stderr, + std,stdio), and only capture and return standard output. + + This flag has no effect on $(LREF spawnProcess) or $(LREF spawnShell). + */ + stderrPassThrough = 128, } @@ -2487,7 +2503,11 @@ private auto executeImpl(alias pipeFunc, Cmd, ExtraPipeFuncArgs...)( import std.array : appender; import std.typecons : Tuple; - auto p = pipeFunc(commandLine, Redirect.stdout | Redirect.stderrToStdout, + auto redirect = (config & Config.stderrPassThrough) + ? Redirect.stdout + : Redirect.stdout | Redirect.stderrToStdout; + + auto p = pipeFunc(commandLine, redirect, env, config, workDir, extraArgs); auto a = appender!(ubyte[])(); @@ -2551,6 +2571,31 @@ private auto executeImpl(alias pipeFunc, Cmd, ExtraPipeFuncArgs...)( assert(r3.output.empty); } +@system unittest +{ + // Temporarily disable output to stderr so as to not spam the build log. + import std.stdio : stderr; + import std.typecons : Tuple; + import std.file : readText; + import std.traits : ReturnType; + + ReturnType!executeShell r; + auto tmpname = uniqueTempPath; + scope(exit) if (exists(tmpname)) remove(tmpname); + auto t = stderr; + // Open a new scope to minimize code ran with stderr redirected. + { + stderr.open(tmpname, "w"); + scope(exit) stderr = t; + r = executeShell("echo D rox>&2", null, Config.stderrPassThrough); + } + assert(r.status == 0); + assert(r.output.empty); + auto witness = readText(tmpname); + import std.ascii : newline; + assert(witness == "D rox" ~ newline, "'" ~ witness ~ "'"); +} + @safe unittest { import std.typecons : Tuple; @@ -2750,8 +2795,7 @@ private struct TestScript string path; } -version (unittest) -private string uniqueTempPath() @safe +package(std) string uniqueTempPath() @safe { import std.file : tempDir; import std.path : buildPath; diff --git a/libphobos/src/std/range/package.d b/libphobos/src/std/range/package.d index 13601cb..deedb68 100644 --- a/libphobos/src/std/range/package.d +++ b/libphobos/src/std/range/package.d @@ -11379,7 +11379,6 @@ if (isInputRange!R && isIntegral!(ElementType!R)) bw.popFront(); assert(bw[2 * bitsNum - 3] == true); - import core.exception : Error; import std.exception : assertThrown; // Check out of bounds error diff --git a/libphobos/src/std/regex/internal/tests.d b/libphobos/src/std/regex/internal/tests.d index 1c4f295..fe75ce0 100644 --- a/libphobos/src/std/regex/internal/tests.d +++ b/libphobos/src/std/regex/internal/tests.d @@ -8,7 +8,7 @@ package(std.regex): import std.conv, std.exception, std.meta, std.range, std.typecons, std.regex; -import std.regex.internal.parser : Escapables; // characters that need escaping +import std.regex.internal.ir : Escapables; // characters that need escaping alias Sequence(int B, int E) = staticIota!(B, E); @@ -467,654 +467,3 @@ alias Sequence(int B, int E) = staticIota!(B, E); run_tests!match(); //thompson VM } -@safe unittest -{ - auto cr = ctRegex!("abc"); - assert(bmatch("abc",cr).hit == "abc"); - auto cr2 = ctRegex!("ab*c"); - assert(bmatch("abbbbc",cr2).hit == "abbbbc"); -} -@safe unittest -{ - auto cr3 = ctRegex!("^abc$"); - assert(bmatch("abc",cr3).hit == "abc"); - auto cr4 = ctRegex!(`\b(a\B[a-z]b)\b`); - assert(array(match("azb",cr4).captures) == ["azb", "azb"]); -} - -@safe unittest -{ - auto cr5 = ctRegex!("(?:a{2,4}b{1,3}){1,2}"); - assert(bmatch("aaabaaaabbb", cr5).hit == "aaabaaaabbb"); - auto cr6 = ctRegex!("(?:a{2,4}b{1,3}){1,2}?"w); - assert(bmatch("aaabaaaabbb"w, cr6).hit == "aaab"w); -} - -@safe unittest -{ - auto cr7 = ctRegex!(`\r.*?$`,"sm"); - assert(bmatch("abc\r\nxy", cr7).hit == "\r\nxy"); - auto greed = ctRegex!("<packet.*?/packet>"); - assert(bmatch("<packet>text</packet><packet>text</packet>", greed).hit - == "<packet>text</packet>"); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - auto cr8 = ctRegex!("^(a)(b)?(c*)"); - auto m8 = bmatch("abcc",cr8); - assert(m8); - assert(m8.captures[1] == "a"); - assert(m8.captures[2] == "b"); - assert(m8.captures[3] == "cc"); - auto cr9 = ctRegex!("q(a|b)*q"); - auto m9 = match("xxqababqyy",cr9); - assert(m9); - assert(equal(bmatch("xxqababqyy",cr9).captures, ["qababq", "b"])); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - auto rtr = regex("a|b|c"); - enum ctr = regex("a|b|c"); - assert(equal(rtr.ir,ctr.ir)); - //CTFE parser BUG is triggered by group - //in the middle of alternation (at least not first and not last) - enum testCT = regex(`abc|(edf)|xyz`); - auto testRT = regex(`abc|(edf)|xyz`); - assert(equal(testCT.ir,testRT.ir)); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : map; - enum cx = ctRegex!"(A|B|C)"; - auto mx = match("B",cx); - assert(mx); - assert(equal(mx.captures, [ "B", "B"])); - enum cx2 = ctRegex!"(A|B)*"; - assert(match("BAAA",cx2)); - - enum cx3 = ctRegex!("a{3,4}","i"); - auto mx3 = match("AaA",cx3); - assert(mx3); - assert(mx3.captures[0] == "AaA"); - enum cx4 = ctRegex!(`^a{3,4}?[a-zA-Z0-9~]{1,2}`,"i"); - auto mx4 = match("aaaabc", cx4); - assert(mx4); - assert(mx4.captures[0] == "aaaab"); - auto cr8 = ctRegex!("(a)(b)?(c*)"); - auto m8 = bmatch("abcc",cr8); - assert(m8); - assert(m8.captures[1] == "a"); - assert(m8.captures[2] == "b"); - assert(m8.captures[3] == "cc"); - auto cr9 = ctRegex!(".*$", "gm"); - auto m9 = match("First\rSecond", cr9); - assert(m9); - assert(equal(map!"a.hit"(m9), ["First", "", "Second"])); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : map; -//global matching - void test_body(alias matchFn)() - { - string s = "a quick brown fox jumps over a lazy dog"; - auto r1 = regex("\\b[a-z]+\\b","g"); - string[] test; - foreach (m; matchFn(s, r1)) - test ~= m.hit; - assert(equal(test, [ "a", "quick", "brown", "fox", "jumps", "over", "a", "lazy", "dog"])); - auto free_reg = regex(` - - abc - \s+ - " - ( - [^"]+ - | \\ " - )+ - " - z - `, "x"); - auto m = match(`abc "quoted string with \" inside"z`,free_reg); - assert(m); - string mails = " hey@you.com no@spam.net "; - auto rm = regex(`@(?<=\S+@)\S+`,"g"); - assert(equal(map!"a[0]"(matchFn(mails, rm)), ["@you.com", "@spam.net"])); - auto m2 = matchFn("First line\nSecond line",regex(".*$","gm")); - assert(equal(map!"a[0]"(m2), ["First line", "", "Second line"])); - auto m2a = matchFn("First line\nSecond line",regex(".+$","gm")); - assert(equal(map!"a[0]"(m2a), ["First line", "Second line"])); - auto m2b = matchFn("First line\nSecond line",regex(".+?$","gm")); - assert(equal(map!"a[0]"(m2b), ["First line", "Second line"])); - debug(std_regex_test) writeln("!!! FReD FLAGS test done "~matchFn.stringof~" !!!"); - } - test_body!bmatch(); - test_body!match(); -} - -//tests for accumulated std.regex issues and other regressions -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : map; - void test_body(alias matchFn)() - { - //issue 5857 - //matching goes out of control if ... in (...){x} has .*/.+ - auto c = matchFn("axxxzayyyyyzd",regex("(a.*z){2}d")).captures; - assert(c[0] == "axxxzayyyyyzd"); - assert(c[1] == "ayyyyyz"); - auto c2 = matchFn("axxxayyyyyd",regex("(a.*){2}d")).captures; - assert(c2[0] == "axxxayyyyyd"); - assert(c2[1] == "ayyyyy"); - //issue 2108 - //greedy vs non-greedy - auto nogreed = regex("<packet.*?/packet>"); - assert(matchFn("<packet>text</packet><packet>text</packet>", nogreed).hit - == "<packet>text</packet>"); - auto greed = regex("<packet.*/packet>"); - assert(matchFn("<packet>text</packet><packet>text</packet>", greed).hit - == "<packet>text</packet><packet>text</packet>"); - //issue 4574 - //empty successful match still advances the input - string[] pres, posts, hits; - foreach (m; matchFn("abcabc", regex("","g"))) - { - pres ~= m.pre; - posts ~= m.post; - assert(m.hit.empty); - - } - auto heads = [ - "abcabc", - "abcab", - "abca", - "abc", - "ab", - "a", - "" - ]; - auto tails = [ - "abcabc", - "bcabc", - "cabc", - "abc", - "bc", - "c", - "" - ]; - assert(pres == array(retro(heads))); - assert(posts == tails); - //issue 6076 - //regression on .* - auto re = regex("c.*|d"); - auto m = matchFn("mm", re); - assert(!m); - debug(std_regex_test) writeln("!!! FReD REGRESSION test done "~matchFn.stringof~" !!!"); - auto rprealloc = regex(`((.){5}.{1,10}){5}`); - auto arr = array(repeat('0',100)); - auto m2 = matchFn(arr, rprealloc); - assert(m2); - assert(collectException( - regex(r"^(import|file|binary|config)\s+([^\(]+)\(?([^\)]*)\)?\s*$") - ) is null); - foreach (ch; [Escapables]) - { - assert(match(to!string(ch),regex(`[\`~ch~`]`))); - assert(!match(to!string(ch),regex(`[^\`~ch~`]`))); - assert(match(to!string(ch),regex(`[\`~ch~`-\`~ch~`]`))); - } - //bugzilla 7718 - string strcmd = "./myApp.rb -os OSX -path \"/GIT/Ruby Apps/sec\" -conf 'notimer'"; - auto reStrCmd = regex (`(".*")|('.*')`, "g"); - assert(equal(map!"a[0]"(matchFn(strcmd, reStrCmd)), - [`"/GIT/Ruby Apps/sec"`, `'notimer'`])); - } - test_body!bmatch(); - test_body!match(); -} - -// tests for replace -@safe unittest -{ - void test(alias matchFn)() - { - import std.uni : toUpper; - - foreach (i, v; AliasSeq!(string, wstring, dstring)) - { - auto baz(Cap)(Cap m) - if (is(Cap == Captures!(Cap.String))) - { - return toUpper(m.hit); - } - alias String = v; - assert(std.regex.replace!(matchFn)(to!String("ark rapacity"), regex(to!String("r")), to!String("c")) - == to!String("ack rapacity")); - assert(std.regex.replace!(matchFn)(to!String("ark rapacity"), regex(to!String("r"), "g"), to!String("c")) - == to!String("ack capacity")); - assert(std.regex.replace!(matchFn)(to!String("noon"), regex(to!String("^n")), to!String("[$&]")) - == to!String("[n]oon")); - assert(std.regex.replace!(matchFn)( - to!String("test1 test2"), regex(to!String(`\w+`),"g"), to!String("$`:$'") - ) == to!String(": test2 test1 :")); - auto s = std.regex.replace!(baz!(Captures!(String)))(to!String("Strap a rocket engine on a chicken."), - regex(to!String("[ar]"), "g")); - assert(s == "StRAp A Rocket engine on A chicken."); - } - debug(std_regex_test) writeln("!!! Replace test done "~matchFn.stringof~" !!!"); - } - test!(bmatch)(); - test!(match)(); -} - -// tests for splitter -@safe unittest -{ - import std.algorithm.comparison : equal; - auto s1 = ", abc, de, fg, hi, "; - auto sp1 = splitter(s1, regex(", *")); - auto w1 = ["", "abc", "de", "fg", "hi", ""]; - assert(equal(sp1, w1)); - - auto s2 = ", abc, de, fg, hi"; - auto sp2 = splitter(s2, regex(", *")); - auto w2 = ["", "abc", "de", "fg", "hi"]; - - uint cnt; - foreach (e; sp2) - { - assert(w2[cnt++] == e); - } - assert(equal(sp2, w2)); -} - -@safe unittest -{ - char[] s1 = ", abc, de, fg, hi, ".dup; - auto sp2 = splitter(s1, regex(", *")); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - auto s1 = ", abc, de, fg, hi, "; - auto w1 = ["", "abc", "de", "fg", "hi", ""]; - assert(equal(split(s1, regex(", *")), w1[])); -} - -@safe unittest -{ // bugzilla 7141 - string pattern = `[a\--b]`; - assert(match("-", pattern)); - assert(match("b", pattern)); - string pattern2 = `[&-z]`; - assert(match("b", pattern2)); -} -@safe unittest -{//bugzilla 7111 - assert(match("", regex("^"))); -} -@safe unittest -{//bugzilla 7300 - assert(!match("a"d, "aa"d)); -} - -// bugzilla 7551 -@safe unittest -{ - auto r = regex("[]abc]*"); - assert("]ab".matchFirst(r).hit == "]ab"); - assertThrown(regex("[]")); - auto r2 = regex("[]abc--ab]*"); - assert("]ac".matchFirst(r2).hit == "]"); -} - -@safe unittest -{//bugzilla 7674 - assert("1234".replace(regex("^"), "$$") == "$1234"); - assert("hello?".replace(regex(r"\?", "g"), r"\?") == r"hello\?"); - assert("hello?".replace(regex(r"\?", "g"), r"\\?") != r"hello\?"); -} -@safe unittest -{// bugzilla 7679 - import std.algorithm.comparison : equal; - foreach (S; AliasSeq!(string, wstring, dstring)) - (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 - enum re = ctRegex!(to!S(r"\.")); - auto str = to!S("a.b"); - assert(equal(std.regex.splitter(str, re), [to!S("a"), to!S("b")])); - assert(split(str, re) == [to!S("a"), to!S("b")]); - }(); -} -@safe unittest -{//bugzilla 8203 - string data = " - NAME = XPAW01_STA:STATION - NAME = XPAW01_STA - "; - auto uniFileOld = data; - auto r = regex( - r"^NAME = (?P<comp>[a-zA-Z0-9_]+):*(?P<blk>[a-zA-Z0-9_]*)","gm"); - auto uniCapturesNew = match(uniFileOld, r); - for (int i = 0; i < 20; i++) - foreach (matchNew; uniCapturesNew) {} - //a second issue with same symptoms - auto r2 = regex(`([а-яА-Я\-_]+\s*)+(?<=[\s\.,\^])`); - match("аллея Театральная", r2); -} -@safe unittest -{// bugzilla 8637 purity of enforce - auto m = match("hello world", regex("world")); - enforce(m); -} - -// bugzilla 8725 -@safe unittest -{ - static italic = regex( r"\* - (?!\s+) - (.*?) - (?!\s+) - \*", "gx" ); - string input = "this * is* interesting, *very* interesting"; - assert(replace(input, italic, "<i>$1</i>") == - "this * is* interesting, <i>very</i> interesting"); -} - -// bugzilla 8349 -@safe unittest -{ - enum peakRegexStr = r"\>(wgEncode.*Tfbs.*\.(?:narrow)|(?:broad)Peak.gz)</a>"; - enum peakRegex = ctRegex!(peakRegexStr); - //note that the regex pattern itself is probably bogus - assert(match(r"\>wgEncode-blah-Tfbs.narrow</a>", peakRegex)); -} - -// bugzilla 9211 -@safe unittest -{ - import std.algorithm.comparison : equal; - auto rx_1 = regex(r"^(\w)*(\d)"); - auto m = match("1234", rx_1); - assert(equal(m.front, ["1234", "3", "4"])); - auto rx_2 = regex(r"^([0-9])*(\d)"); - auto m2 = match("1234", rx_2); - assert(equal(m2.front, ["1234", "3", "4"])); -} - -// bugzilla 9280 -@safe unittest -{ - string tomatch = "a!b@c"; - static r = regex(r"^(?P<nick>.*?)!(?P<ident>.*?)@(?P<host>.*?)$"); - auto nm = match(tomatch, r); - assert(nm); - auto c = nm.captures; - assert(c[1] == "a"); - assert(c["nick"] == "a"); -} - - -// bugzilla 9579 -@safe unittest -{ - char[] input = ['a', 'b', 'c']; - string format = "($1)"; - // used to give a compile error: - auto re = regex(`(a)`, "g"); - auto r = replace(input, re, format); - assert(r == "(a)bc"); -} - -// bugzilla 9634 -@safe unittest -{ - auto re = ctRegex!"(?:a+)"; - assert(match("aaaa", re).hit == "aaaa"); -} - -//bugzilla 10798 -@safe unittest -{ - auto cr = ctRegex!("[abcd--c]*"); - auto m = "abc".match(cr); - assert(m); - assert(m.hit == "ab"); -} - -// bugzilla 10913 -@system unittest -{ - @system static string foo(const(char)[] s) - { - return s.dup; - } - @safe static string bar(const(char)[] s) - { - return s.dup; - } - () @system { - replace!((a) => foo(a.hit))("blah", regex(`a`)); - }(); - () @safe { - replace!((a) => bar(a.hit))("blah", regex(`a`)); - }(); -} - -// bugzilla 11262 -@safe unittest -{ - enum reg = ctRegex!(r",", "g"); - auto str = "This,List"; - str = str.replace(reg, "-"); - assert(str == "This-List"); -} - -// bugzilla 11775 -@safe unittest -{ - assert(collectException(regex("a{1,0}"))); -} - -// bugzilla 11839 -@safe unittest -{ - import std.algorithm.comparison : equal; - assert(regex(`(?P<var1>\w+)`).namedCaptures.equal(["var1"])); - assert(collectException(regex(`(?P<1>\w+)`))); - assert(regex(`(?P<v1>\w+)`).namedCaptures.equal(["v1"])); - assert(regex(`(?P<__>\w+)`).namedCaptures.equal(["__"])); - assert(regex(`(?P<я>\w+)`).namedCaptures.equal(["я"])); -} - -// bugzilla 12076 -@safe unittest -{ - auto RE = ctRegex!(r"(?<!x[a-z]+)\s([a-z]+)"); - string s = "one two"; - auto m = match(s, RE); -} - -// bugzilla 12105 -@safe unittest -{ - auto r = ctRegex!`.*?(?!a)`; - assert("aaab".matchFirst(r).hit == "aaa"); - auto r2 = ctRegex!`.*(?!a)`; - assert("aaab".matchFirst(r2).hit == "aaab"); -} - -//bugzilla 11784 -@safe unittest -{ - assert("abcdefghijklmnopqrstuvwxyz" - .matchFirst("[a-z&&[^aeiuo]]").hit == "b"); -} - -//bugzilla 12366 -@safe unittest -{ - auto re = ctRegex!(`^((?=(xx+?)\2+$)((?=\2+$)(?=(x+)(\4+$))\5){2})*x?$`); - assert("xxxxxxxx".match(re).empty); - assert(!"xxxx".match(re).empty); -} - -// bugzilla 12582 -@safe unittest -{ - auto r = regex(`(?P<a>abc)`); - assert(collectException("abc".matchFirst(r)["b"])); -} - -// bugzilla 12691 -@safe unittest -{ - assert(bmatch("e@", "^([a-z]|)*$").empty); - assert(bmatch("e@", ctRegex!`^([a-z]|)*$`).empty); -} - -//bugzilla 12713 -@safe unittest -{ - assertThrown(regex("[[a-z]([a-z]|(([[a-z])))")); -} - -//bugzilla 12747 -@safe unittest -{ - assertThrown(regex(`^x(\1)`)); - assertThrown(regex(`^(x(\1))`)); - assertThrown(regex(`^((x)(?=\1))`)); -} - -// bugzilla 14504 -@safe unittest -{ - auto p = ctRegex!("a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?" ~ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); -} - -// bugzilla 14529 -@safe unittest -{ - auto ctPat2 = regex(r"^[CDF]$", "i"); - foreach (v; ["C", "c", "D", "d", "F", "f"]) - assert(matchAll(v, ctPat2).front.hit == v); -} - -// bugzilla 14615 -@safe unittest -{ - import std.array : appender; - import std.regex : replaceFirst, replaceFirstInto, regex; - import std.stdio : writeln; - - auto example = "Hello, world!"; - auto pattern = regex("^Hello, (bug)"); // won't find this one - auto result = replaceFirst(example, pattern, "$1 Sponge Bob"); - assert(result == "Hello, world!"); // Ok. - - auto sink = appender!string; - replaceFirstInto(sink, example, pattern, "$1 Sponge Bob"); - assert(sink.data == "Hello, world!"); - replaceAllInto(sink, example, pattern, "$1 Sponge Bob"); - assert(sink.data == "Hello, world!Hello, world!"); -} - -// bugzilla 15573 -@safe unittest -{ - auto rx = regex("[c d]", "x"); - assert("a b".matchFirst(rx)); -} - -// bugzilla 15864 -@safe unittest -{ - regex(`(<a (?:(?:\w+=\"[^"]*\")?\s*)*href="\.\.?)"`); -} - -@safe unittest -{ - auto r = regex("(?# comment)abc(?# comment2)"); - assert("abc".matchFirst(r)); - assertThrown(regex("(?#...")); -} - -// bugzilla 17075 -@safe unittest -{ - enum titlePattern = `<title>(.+)</title>`; - static titleRegex = ctRegex!titlePattern; - string input = "<title>" ~ "<".repeat(100_000).join; - assert(input.matchFirst(titleRegex).empty); -} - -// bugzilla 17212 -@safe unittest -{ - auto r = regex(" [a] ", "x"); - assert("a".matchFirst(r)); -} - -// bugzilla 17157 -@safe unittest -{ - import std.algorithm.comparison : equal; - auto ctr = ctRegex!"(a)|(b)|(c)|(d)"; - auto r = regex("(a)|(b)|(c)|(d)", "g"); - auto s = "--a--b--c--d--"; - auto outcomes = [ - ["a", "a", "", "", ""], - ["b", "", "b", "", ""], - ["c", "", "", "c", ""], - ["d", "", "", "", "d"] - ]; - assert(equal!equal(s.matchAll(ctr), outcomes)); - assert(equal!equal(s.bmatch(r), outcomes)); -} - -// bugzilla 17667 -@safe unittest -{ - import std.algorithm.searching : canFind; - void willThrow(T, size_t line = __LINE__)(T arg, string msg) - { - auto e = collectException(regex(arg)); - assert(e.msg.canFind(msg), to!string(line) ~ ": " ~ e.msg); - } - willThrow([r".", r"[\(\{[\]\}\)]"], "no matching ']' found while parsing character class"); - willThrow([r"[\", r"123"], "no matching ']' found while parsing character class"); - willThrow([r"[a-", r"123"], "no matching ']' found while parsing character class"); - willThrow([r"[a-\", r"123"], "invalid escape sequence"); - willThrow([r"\", r"123"], "invalid escape sequence"); -} - -// bugzilla 17668 -@safe unittest -{ - import std.algorithm.searching; - auto e = collectException!RegexException(regex(q"<[^]>")); - assert(e.msg.canFind("no operand for '^'")); -} - -// bugzilla 17673 -@safe unittest -{ - string str = `<">`; - string[] regexps = ["abc", "\"|x"]; - auto regexp = regex(regexps); - auto c = matchFirst(str, regexp); - assert(c); - assert(c.whichPattern == 2); -} - diff --git a/libphobos/src/std/regex/internal/tests2.d b/libphobos/src/std/regex/internal/tests2.d new file mode 100644 index 0000000..420f8d3 --- /dev/null +++ b/libphobos/src/std/regex/internal/tests2.d @@ -0,0 +1,662 @@ +// Split-up due to DMD's enormous memory consumption + +module std.regex.internal.tests2; + +package(std.regex): + +import std.conv, std.exception, std.meta, std.range, + std.typecons, std.regex; + +import std.regex.internal.ir : Escapables; // characters that need escaping + +@safe unittest +{ + auto cr = ctRegex!("abc"); + assert(bmatch("abc",cr).hit == "abc"); + auto cr2 = ctRegex!("ab*c"); + assert(bmatch("abbbbc",cr2).hit == "abbbbc"); +} +@safe unittest +{ + auto cr3 = ctRegex!("^abc$"); + assert(bmatch("abc",cr3).hit == "abc"); + auto cr4 = ctRegex!(`\b(a\B[a-z]b)\b`); + assert(array(match("azb",cr4).captures) == ["azb", "azb"]); +} + +@safe unittest +{ + auto cr5 = ctRegex!("(?:a{2,4}b{1,3}){1,2}"); + assert(bmatch("aaabaaaabbb", cr5).hit == "aaabaaaabbb"); + auto cr6 = ctRegex!("(?:a{2,4}b{1,3}){1,2}?"w); + assert(bmatch("aaabaaaabbb"w, cr6).hit == "aaab"w); +} + +@safe unittest +{ + auto cr7 = ctRegex!(`\r.*?$`,"sm"); + assert(bmatch("abc\r\nxy", cr7).hit == "\r\nxy"); + auto greed = ctRegex!("<packet.*?/packet>"); + assert(bmatch("<packet>text</packet><packet>text</packet>", greed).hit + == "<packet>text</packet>"); +} + +@safe unittest +{ + import std.algorithm.comparison : equal; + auto cr8 = ctRegex!("^(a)(b)?(c*)"); + auto m8 = bmatch("abcc",cr8); + assert(m8); + assert(m8.captures[1] == "a"); + assert(m8.captures[2] == "b"); + assert(m8.captures[3] == "cc"); + auto cr9 = ctRegex!("q(a|b)*q"); + auto m9 = match("xxqababqyy",cr9); + assert(m9); + assert(equal(bmatch("xxqababqyy",cr9).captures, ["qababq", "b"])); +} + +@safe unittest +{ + import std.algorithm.comparison : equal; + auto rtr = regex("a|b|c"); + enum ctr = regex("a|b|c"); + assert(equal(rtr.ir,ctr.ir)); + //CTFE parser BUG is triggered by group + //in the middle of alternation (at least not first and not last) + enum testCT = regex(`abc|(edf)|xyz`); + auto testRT = regex(`abc|(edf)|xyz`); + assert(equal(testCT.ir,testRT.ir)); +} + +@safe unittest +{ + import std.algorithm.comparison : equal; + import std.algorithm.iteration : map; + enum cx = ctRegex!"(A|B|C)"; + auto mx = match("B",cx); + assert(mx); + assert(equal(mx.captures, [ "B", "B"])); + enum cx2 = ctRegex!"(A|B)*"; + assert(match("BAAA",cx2)); + + enum cx3 = ctRegex!("a{3,4}","i"); + auto mx3 = match("AaA",cx3); + assert(mx3); + assert(mx3.captures[0] == "AaA"); + enum cx4 = ctRegex!(`^a{3,4}?[a-zA-Z0-9~]{1,2}`,"i"); + auto mx4 = match("aaaabc", cx4); + assert(mx4); + assert(mx4.captures[0] == "aaaab"); + auto cr8 = ctRegex!("(a)(b)?(c*)"); + auto m8 = bmatch("abcc",cr8); + assert(m8); + assert(m8.captures[1] == "a"); + assert(m8.captures[2] == "b"); + assert(m8.captures[3] == "cc"); + auto cr9 = ctRegex!(".*$", "gm"); + auto m9 = match("First\rSecond", cr9); + assert(m9); + assert(equal(map!"a.hit"(m9), ["First", "", "Second"])); +} + +@safe unittest +{ + import std.algorithm.comparison : equal; + import std.algorithm.iteration : map; +//global matching + void test_body(alias matchFn)() + { + string s = "a quick brown fox jumps over a lazy dog"; + auto r1 = regex("\\b[a-z]+\\b","g"); + string[] test; + foreach (m; matchFn(s, r1)) + test ~= m.hit; + assert(equal(test, [ "a", "quick", "brown", "fox", "jumps", "over", "a", "lazy", "dog"])); + auto free_reg = regex(` + + abc + \s+ + " + ( + [^"]+ + | \\ " + )+ + " + z + `, "x"); + auto m = match(`abc "quoted string with \" inside"z`,free_reg); + assert(m); + string mails = " hey@you.com no@spam.net "; + auto rm = regex(`@(?<=\S+@)\S+`,"g"); + assert(equal(map!"a[0]"(matchFn(mails, rm)), ["@you.com", "@spam.net"])); + auto m2 = matchFn("First line\nSecond line",regex(".*$","gm")); + assert(equal(map!"a[0]"(m2), ["First line", "", "Second line"])); + auto m2a = matchFn("First line\nSecond line",regex(".+$","gm")); + assert(equal(map!"a[0]"(m2a), ["First line", "Second line"])); + auto m2b = matchFn("First line\nSecond line",regex(".+?$","gm")); + assert(equal(map!"a[0]"(m2b), ["First line", "Second line"])); + debug(std_regex_test) writeln("!!! FReD FLAGS test done "~matchFn.stringof~" !!!"); + } + test_body!bmatch(); + test_body!match(); +} + +//tests for accumulated std.regex issues and other regressions +@safe unittest +{ + import std.algorithm.comparison : equal; + import std.algorithm.iteration : map; + void test_body(alias matchFn)() + { + //issue 5857 + //matching goes out of control if ... in (...){x} has .*/.+ + auto c = matchFn("axxxzayyyyyzd",regex("(a.*z){2}d")).captures; + assert(c[0] == "axxxzayyyyyzd"); + assert(c[1] == "ayyyyyz"); + auto c2 = matchFn("axxxayyyyyd",regex("(a.*){2}d")).captures; + assert(c2[0] == "axxxayyyyyd"); + assert(c2[1] == "ayyyyy"); + //issue 2108 + //greedy vs non-greedy + auto nogreed = regex("<packet.*?/packet>"); + assert(matchFn("<packet>text</packet><packet>text</packet>", nogreed).hit + == "<packet>text</packet>"); + auto greed = regex("<packet.*/packet>"); + assert(matchFn("<packet>text</packet><packet>text</packet>", greed).hit + == "<packet>text</packet><packet>text</packet>"); + //issue 4574 + //empty successful match still advances the input + string[] pres, posts, hits; + foreach (m; matchFn("abcabc", regex("","g"))) + { + pres ~= m.pre; + posts ~= m.post; + assert(m.hit.empty); + + } + auto heads = [ + "abcabc", + "abcab", + "abca", + "abc", + "ab", + "a", + "" + ]; + auto tails = [ + "abcabc", + "bcabc", + "cabc", + "abc", + "bc", + "c", + "" + ]; + assert(pres == array(retro(heads))); + assert(posts == tails); + //issue 6076 + //regression on .* + auto re = regex("c.*|d"); + auto m = matchFn("mm", re); + assert(!m); + debug(std_regex_test) writeln("!!! FReD REGRESSION test done "~matchFn.stringof~" !!!"); + auto rprealloc = regex(`((.){5}.{1,10}){5}`); + auto arr = array(repeat('0',100)); + auto m2 = matchFn(arr, rprealloc); + assert(m2); + assert(collectException( + regex(r"^(import|file|binary|config)\s+([^\(]+)\(?([^\)]*)\)?\s*$") + ) is null); + foreach (ch; [Escapables]) + { + assert(match(to!string(ch),regex(`[\`~ch~`]`))); + assert(!match(to!string(ch),regex(`[^\`~ch~`]`))); + assert(match(to!string(ch),regex(`[\`~ch~`-\`~ch~`]`))); + } + //bugzilla 7718 + string strcmd = "./myApp.rb -os OSX -path \"/GIT/Ruby Apps/sec\" -conf 'notimer'"; + auto reStrCmd = regex (`(".*")|('.*')`, "g"); + assert(equal(map!"a[0]"(matchFn(strcmd, reStrCmd)), + [`"/GIT/Ruby Apps/sec"`, `'notimer'`])); + } + test_body!bmatch(); + test_body!match(); +} + +// tests for replace +@safe unittest +{ + void test(alias matchFn)() + { + import std.uni : toUpper; + + foreach (i, v; AliasSeq!(string, wstring, dstring)) + { + auto baz(Cap)(Cap m) + if (is(Cap == Captures!(Cap.String))) + { + return toUpper(m.hit); + } + alias String = v; + assert(std.regex.replace!(matchFn)(to!String("ark rapacity"), regex(to!String("r")), to!String("c")) + == to!String("ack rapacity")); + assert(std.regex.replace!(matchFn)(to!String("ark rapacity"), regex(to!String("r"), "g"), to!String("c")) + == to!String("ack capacity")); + assert(std.regex.replace!(matchFn)(to!String("noon"), regex(to!String("^n")), to!String("[$&]")) + == to!String("[n]oon")); + assert(std.regex.replace!(matchFn)( + to!String("test1 test2"), regex(to!String(`\w+`),"g"), to!String("$`:$'") + ) == to!String(": test2 test1 :")); + auto s = std.regex.replace!(baz!(Captures!(String)))(to!String("Strap a rocket engine on a chicken."), + regex(to!String("[ar]"), "g")); + assert(s == "StRAp A Rocket engine on A chicken."); + } + debug(std_regex_test) writeln("!!! Replace test done "~matchFn.stringof~" !!!"); + } + test!(bmatch)(); + test!(match)(); +} + +// tests for splitter +@safe unittest +{ + import std.algorithm.comparison : equal; + auto s1 = ", abc, de, fg, hi, "; + auto sp1 = splitter(s1, regex(", *")); + auto w1 = ["", "abc", "de", "fg", "hi", ""]; + assert(equal(sp1, w1)); + + auto s2 = ", abc, de, fg, hi"; + auto sp2 = splitter(s2, regex(", *")); + auto w2 = ["", "abc", "de", "fg", "hi"]; + + uint cnt; + foreach (e; sp2) + { + assert(w2[cnt++] == e); + } + assert(equal(sp2, w2)); +} + +@safe unittest +{ + char[] s1 = ", abc, de, fg, hi, ".dup; + auto sp2 = splitter(s1, regex(", *")); +} + +@safe unittest +{ + import std.algorithm.comparison : equal; + auto s1 = ", abc, de, fg, hi, "; + auto w1 = ["", "abc", "de", "fg", "hi", ""]; + assert(equal(split(s1, regex(", *")), w1[])); +} + +@safe unittest +{ // bugzilla 7141 + string pattern = `[a\--b]`; + assert(match("-", pattern)); + assert(match("b", pattern)); + string pattern2 = `[&-z]`; + assert(match("b", pattern2)); +} +@safe unittest +{//bugzilla 7111 + assert(match("", regex("^"))); +} +@safe unittest +{//bugzilla 7300 + assert(!match("a"d, "aa"d)); +} + +// bugzilla 7551 +@safe unittest +{ + auto r = regex("[]abc]*"); + assert("]ab".matchFirst(r).hit == "]ab"); + assertThrown(regex("[]")); + auto r2 = regex("[]abc--ab]*"); + assert("]ac".matchFirst(r2).hit == "]"); +} + +@safe unittest +{//bugzilla 7674 + assert("1234".replace(regex("^"), "$$") == "$1234"); + assert("hello?".replace(regex(r"\?", "g"), r"\?") == r"hello\?"); + assert("hello?".replace(regex(r"\?", "g"), r"\\?") != r"hello\?"); +} +@safe unittest +{// bugzilla 7679 + import std.algorithm.comparison : equal; + foreach (S; AliasSeq!(string, wstring, dstring)) + (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 + enum re = ctRegex!(to!S(r"\.")); + auto str = to!S("a.b"); + assert(equal(std.regex.splitter(str, re), [to!S("a"), to!S("b")])); + assert(split(str, re) == [to!S("a"), to!S("b")]); + }(); +} +@safe unittest +{//bugzilla 8203 + string data = " + NAME = XPAW01_STA:STATION + NAME = XPAW01_STA + "; + auto uniFileOld = data; + auto r = regex( + r"^NAME = (?P<comp>[a-zA-Z0-9_]+):*(?P<blk>[a-zA-Z0-9_]*)","gm"); + auto uniCapturesNew = match(uniFileOld, r); + for (int i = 0; i < 20; i++) + foreach (matchNew; uniCapturesNew) {} + //a second issue with same symptoms + auto r2 = regex(`([а-яА-Я\-_]+\s*)+(?<=[\s\.,\^])`); + match("аллея Театральная", r2); +} +@safe unittest +{// bugzilla 8637 purity of enforce + auto m = match("hello world", regex("world")); + enforce(m); +} + +// bugzilla 8725 +@safe unittest +{ + static italic = regex( r"\* + (?!\s+) + (.*?) + (?!\s+) + \*", "gx" ); + string input = "this * is* interesting, *very* interesting"; + assert(replace(input, italic, "<i>$1</i>") == + "this * is* interesting, <i>very</i> interesting"); +} + +// bugzilla 8349 +@safe unittest +{ + enum peakRegexStr = r"\>(wgEncode.*Tfbs.*\.(?:narrow)|(?:broad)Peak.gz)</a>"; + enum peakRegex = ctRegex!(peakRegexStr); + //note that the regex pattern itself is probably bogus + assert(match(r"\>wgEncode-blah-Tfbs.narrow</a>", peakRegex)); +} + +// bugzilla 9211 +@safe unittest +{ + import std.algorithm.comparison : equal; + auto rx_1 = regex(r"^(\w)*(\d)"); + auto m = match("1234", rx_1); + assert(equal(m.front, ["1234", "3", "4"])); + auto rx_2 = regex(r"^([0-9])*(\d)"); + auto m2 = match("1234", rx_2); + assert(equal(m2.front, ["1234", "3", "4"])); +} + +// bugzilla 9280 +@safe unittest +{ + string tomatch = "a!b@c"; + static r = regex(r"^(?P<nick>.*?)!(?P<ident>.*?)@(?P<host>.*?)$"); + auto nm = match(tomatch, r); + assert(nm); + auto c = nm.captures; + assert(c[1] == "a"); + assert(c["nick"] == "a"); +} + + +// bugzilla 9579 +@safe unittest +{ + char[] input = ['a', 'b', 'c']; + string format = "($1)"; + // used to give a compile error: + auto re = regex(`(a)`, "g"); + auto r = replace(input, re, format); + assert(r == "(a)bc"); +} + +// bugzilla 9634 +@safe unittest +{ + auto re = ctRegex!"(?:a+)"; + assert(match("aaaa", re).hit == "aaaa"); +} + +//bugzilla 10798 +@safe unittest +{ + auto cr = ctRegex!("[abcd--c]*"); + auto m = "abc".match(cr); + assert(m); + assert(m.hit == "ab"); +} + +// bugzilla 10913 +@system unittest +{ + @system static string foo(const(char)[] s) + { + return s.dup; + } + @safe static string bar(const(char)[] s) + { + return s.dup; + } + () @system { + replace!((a) => foo(a.hit))("blah", regex(`a`)); + }(); + () @safe { + replace!((a) => bar(a.hit))("blah", regex(`a`)); + }(); +} + +// bugzilla 11262 +@safe unittest +{ + enum reg = ctRegex!(r",", "g"); + auto str = "This,List"; + str = str.replace(reg, "-"); + assert(str == "This-List"); +} + +// bugzilla 11775 +@safe unittest +{ + assert(collectException(regex("a{1,0}"))); +} + +// bugzilla 11839 +@safe unittest +{ + import std.algorithm.comparison : equal; + assert(regex(`(?P<var1>\w+)`).namedCaptures.equal(["var1"])); + assert(collectException(regex(`(?P<1>\w+)`))); + assert(regex(`(?P<v1>\w+)`).namedCaptures.equal(["v1"])); + assert(regex(`(?P<__>\w+)`).namedCaptures.equal(["__"])); + assert(regex(`(?P<я>\w+)`).namedCaptures.equal(["я"])); +} + +// bugzilla 12076 +@safe unittest +{ + auto RE = ctRegex!(r"(?<!x[a-z]+)\s([a-z]+)"); + string s = "one two"; + auto m = match(s, RE); +} + +// bugzilla 12105 +@safe unittest +{ + auto r = ctRegex!`.*?(?!a)`; + assert("aaab".matchFirst(r).hit == "aaa"); + auto r2 = ctRegex!`.*(?!a)`; + assert("aaab".matchFirst(r2).hit == "aaab"); +} + +//bugzilla 11784 +@safe unittest +{ + assert("abcdefghijklmnopqrstuvwxyz" + .matchFirst("[a-z&&[^aeiuo]]").hit == "b"); +} + +//bugzilla 12366 +@safe unittest +{ + auto re = ctRegex!(`^((?=(xx+?)\2+$)((?=\2+$)(?=(x+)(\4+$))\5){2})*x?$`); + assert("xxxxxxxx".match(re).empty); + assert(!"xxxx".match(re).empty); +} + +// bugzilla 12582 +@safe unittest +{ + auto r = regex(`(?P<a>abc)`); + assert(collectException("abc".matchFirst(r)["b"])); +} + +// bugzilla 12691 +@safe unittest +{ + assert(bmatch("e@", "^([a-z]|)*$").empty); + assert(bmatch("e@", ctRegex!`^([a-z]|)*$`).empty); +} + +//bugzilla 12713 +@safe unittest +{ + assertThrown(regex("[[a-z]([a-z]|(([[a-z])))")); +} + +//bugzilla 12747 +@safe unittest +{ + assertThrown(regex(`^x(\1)`)); + assertThrown(regex(`^(x(\1))`)); + assertThrown(regex(`^((x)(?=\1))`)); +} + +// bugzilla 14504 +@safe unittest +{ + auto p = ctRegex!("a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?" ~ + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); +} + +// bugzilla 14529 +@safe unittest +{ + auto ctPat2 = regex(r"^[CDF]$", "i"); + foreach (v; ["C", "c", "D", "d", "F", "f"]) + assert(matchAll(v, ctPat2).front.hit == v); +} + +// bugzilla 14615 +@safe unittest +{ + import std.array : appender; + import std.regex : replaceFirst, replaceFirstInto, regex; + import std.stdio : writeln; + + auto example = "Hello, world!"; + auto pattern = regex("^Hello, (bug)"); // won't find this one + auto result = replaceFirst(example, pattern, "$1 Sponge Bob"); + assert(result == "Hello, world!"); // Ok. + + auto sink = appender!string; + replaceFirstInto(sink, example, pattern, "$1 Sponge Bob"); + assert(sink.data == "Hello, world!"); + replaceAllInto(sink, example, pattern, "$1 Sponge Bob"); + assert(sink.data == "Hello, world!Hello, world!"); +} + +// bugzilla 15573 +@safe unittest +{ + auto rx = regex("[c d]", "x"); + assert("a b".matchFirst(rx)); +} + +// bugzilla 15864 +@safe unittest +{ + regex(`(<a (?:(?:\w+=\"[^"]*\")?\s*)*href="\.\.?)"`); +} + +@safe unittest +{ + auto r = regex("(?# comment)abc(?# comment2)"); + assert("abc".matchFirst(r)); + assertThrown(regex("(?#...")); +} + +// bugzilla 17075 +@safe unittest +{ + enum titlePattern = `<title>(.+)</title>`; + static titleRegex = ctRegex!titlePattern; + string input = "<title>" ~ "<".repeat(100_000).join; + assert(input.matchFirst(titleRegex).empty); +} + +// bugzilla 17212 +@safe unittest +{ + auto r = regex(" [a] ", "x"); + assert("a".matchFirst(r)); +} + +// bugzilla 17157 +@safe unittest +{ + import std.algorithm.comparison : equal; + auto ctr = ctRegex!"(a)|(b)|(c)|(d)"; + auto r = regex("(a)|(b)|(c)|(d)", "g"); + auto s = "--a--b--c--d--"; + auto outcomes = [ + ["a", "a", "", "", ""], + ["b", "", "b", "", ""], + ["c", "", "", "c", ""], + ["d", "", "", "", "d"] + ]; + assert(equal!equal(s.matchAll(ctr), outcomes)); + assert(equal!equal(s.bmatch(r), outcomes)); +} + +// bugzilla 17667 +@safe unittest +{ + import std.algorithm.searching : canFind; + void willThrow(T, size_t line = __LINE__)(T arg, string msg) + { + auto e = collectException(regex(arg)); + assert(e.msg.canFind(msg), to!string(line) ~ ": " ~ e.msg); + } + willThrow([r".", r"[\(\{[\]\}\)]"], "no matching ']' found while parsing character class"); + willThrow([r"[\", r"123"], "no matching ']' found while parsing character class"); + willThrow([r"[a-", r"123"], "no matching ']' found while parsing character class"); + willThrow([r"[a-\", r"123"], "invalid escape sequence"); + willThrow([r"\", r"123"], "invalid escape sequence"); +} + +// bugzilla 17668 +@safe unittest +{ + import std.algorithm.searching; + auto e = collectException!RegexException(regex(q"<[^]>")); + assert(e.msg.canFind("no operand for '^'")); +} + +// bugzilla 17673 +@safe unittest +{ + string str = `<">`; + string[] regexps = ["abc", "\"|x"]; + auto regexp = regex(regexps); + auto c = matchFirst(str, regexp); + assert(c); + assert(c.whichPattern == 2); +} + diff --git a/libphobos/src/std/socket.d b/libphobos/src/std/socket.d index a4ba39c..d7de153 100644 --- a/libphobos/src/std/socket.d +++ b/libphobos/src/std/socket.d @@ -85,10 +85,10 @@ else version (Posix) } } + public import core.sys.posix.netinet.in_; import core.sys.posix.arpa.inet; import core.sys.posix.fcntl; import core.sys.posix.netdb; - import core.sys.posix.netinet.in_; import core.sys.posix.netinet.tcp; import core.sys.posix.sys.select; import core.sys.posix.sys.socket; @@ -146,6 +146,8 @@ class SocketException: Exception mixin basicExceptionCtors; } +version (CRuntime_Glibc) version = GNU_STRERROR; +version (CRuntime_UClibc) version = GNU_STRERROR; /* * Needs to be public so that SocketOSException can be thrown outside of @@ -159,7 +161,7 @@ string formatSocketError(int err) @trusted { char[80] buf; const(char)* cs; - version (CRuntime_Glibc) + version (GNU_STRERROR) { cs = strerror_r(err, buf.ptr, buf.length); } diff --git a/libphobos/src/std/stdio.d b/libphobos/src/std/stdio.d index 4c1ad0b..bbf7857 100644 --- a/libphobos/src/std/stdio.d +++ b/libphobos/src/std/stdio.d @@ -37,48 +37,59 @@ else version (CRuntime_DigitalMars) // Specific to the way Digital Mars C does stdio version = DIGITAL_MARS_STDIO; } - -version (CRuntime_Glibc) +else version (CRuntime_Glibc) { // Specific to the way Gnu C does stdio version = GCC_IO; - version = HAS_GETDELIM; } else version (CRuntime_Bionic) { version = GENERIC_IO; - version = HAS_GETDELIM; } else version (CRuntime_Musl) { version = GENERIC_IO; - version = HAS_GETDELIM; } - -version (OSX) +else version (CRuntime_UClibc) +{ + // uClibc supports GCC IO + version = GCC_IO; +} +else version (OSX) +{ + version = GENERIC_IO; +} +else version (iOS) +{ + version = GENERIC_IO; +} +else version (TVOS) +{ + version = GENERIC_IO; +} +else version (WatchOS) { version = GENERIC_IO; - version = HAS_GETDELIM; } else version (FreeBSD) { version = GENERIC_IO; - version = HAS_GETDELIM; } else version (NetBSD) { version = GENERIC_IO; - version = HAS_GETDELIM; +} +else version (OpenBSD) +{ + version = GENERIC_IO; } else version (DragonFlyBSD) { version = GENERIC_IO; - version = HAS_GETDELIM; } else version (Solaris) { version = GENERIC_IO; - version = NO_GETDELIM; } // Character type used for operating system filesystem APIs @@ -86,12 +97,11 @@ version (Windows) { private alias FSChar = wchar; } -else version (Posix) +else { private alias FSChar = char; } -else - static assert(0); + version (Windows) { @@ -105,6 +115,11 @@ version (Windows) import core.sys.windows.windows : HANDLE; } +version (Posix) +{ + static import core.sys.posix.stdio; // getdelim +} + version (DIGITAL_MARS_STDIO) { extern (C) @@ -244,11 +259,19 @@ else static assert(0, "unsupported C I/O system"); } -version (HAS_GETDELIM) extern(C) nothrow @nogc +static if (__traits(compiles, core.sys.posix.stdio.getdelim)) { - ptrdiff_t getdelim(char**, size_t*, int, FILE*); - // getline() always comes together with getdelim() - ptrdiff_t getline(char**, size_t*, FILE*); + extern(C) nothrow @nogc + { + // @@@DEPRECATED_2.104@@@ + deprecated("To be removed after 2.104. Use core.sys.posix.stdio.getdelim instead.") + ptrdiff_t getdelim(char**, size_t*, int, FILE*); + + // @@@DEPRECATED_2.104@@@ + // getline() always comes together with getdelim() + deprecated("To be removed after 2.104. Use core.sys.posix.stdio.getline instead.") + ptrdiff_t getline(char**, size_t*, FILE*); + } } //------------------------------------------------------------------------------ @@ -4718,59 +4741,142 @@ private struct ReadlnAppender } // Private implementation of readln -version (DIGITAL_MARS_STDIO) -private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orientation /*ignored*/) +private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orientation orientation) { - FLOCK(fps); - scope(exit) FUNLOCK(fps); + version (DIGITAL_MARS_STDIO) + { + FLOCK(fps); + scope(exit) FUNLOCK(fps); - /* Since fps is now locked, we can create an "unshared" version - * of fp. - */ - auto fp = cast(_iobuf*) fps; + /* Since fps is now locked, we can create an "unshared" version + * of fp. + */ + auto fp = cast(_iobuf*) fps; - ReadlnAppender app; - app.initialize(buf); + ReadlnAppender app; + app.initialize(buf); - if (__fhnd_info[fp._file] & FHND_WCHAR) - { /* Stream is in wide characters. - * Read them and convert to chars. - */ - static assert(wchar_t.sizeof == 2); - for (int c = void; (c = FGETWC(fp)) != -1; ) + if (__fhnd_info[fp._file] & FHND_WCHAR) + { /* Stream is in wide characters. + * Read them and convert to chars. + */ + static assert(wchar_t.sizeof == 2); + for (int c = void; (c = FGETWC(fp)) != -1; ) + { + if ((c & ~0x7F) == 0) + { + app.putchar(cast(char) c); + if (c == terminator) + break; + } + else + { + if (c >= 0xD800 && c <= 0xDBFF) + { + int c2 = void; + if ((c2 = FGETWC(fp)) != -1 || + c2 < 0xDC00 && c2 > 0xDFFF) + { + StdioException("unpaired UTF-16 surrogate"); + } + c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00); + } + app.putdchar(cast(dchar) c); + } + } + if (ferror(fps)) + StdioException(); + } + + else if (fp._flag & _IONBF) { - if ((c & ~0x7F) == 0) + /* Use this for unbuffered I/O, when running + * across buffer boundaries, or for any but the common + * cases. + */ + L1: + int c; + while ((c = FGETC(fp)) != -1) { app.putchar(cast(char) c); if (c == terminator) - break; + { + buf = app.data; + return buf.length; + } + } - else - { - if (c >= 0xD800 && c <= 0xDBFF) + + if (ferror(fps)) + StdioException(); + } + else + { + int u = fp._cnt; + char* p = fp._ptr; + int i; + if (fp._flag & _IOTRAN) + { /* Translated mode ignores \r and treats ^Z as end-of-file + */ + char c; + while (1) { - int c2 = void; - if ((c2 = FGETWC(fp)) != -1 || - c2 < 0xDC00 && c2 > 0xDFFF) + if (i == u) // if end of buffer + goto L1; // give up + c = p[i]; + i++; + if (c != '\r') { - StdioException("unpaired UTF-16 surrogate"); + if (c == terminator) + break; + if (c != 0x1A) + continue; + goto L1; + } + else + { if (i != u && p[i] == terminator) + break; + goto L1; } - c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00); } - app.putdchar(cast(dchar) c); + app.putonly(p[0 .. i]); + app.buf[i - 1] = cast(char) terminator; + if (terminator == '\n' && c == '\r') + i++; } + else + { + while (1) + { + if (i == u) // if end of buffer + goto L1; // give up + auto c = p[i]; + i++; + if (c == terminator) + break; + } + app.putonly(p[0 .. i]); + } + fp._cnt -= i; + fp._ptr += i; } - if (ferror(fps)) - StdioException(); - } - else if (fp._flag & _IONBF) + buf = app.data; + return buf.length; + } + else version (MICROSOFT_STDIO) { - /* Use this for unbuffered I/O, when running - * across buffer boundaries, or for any but the common - * cases. + FLOCK(fps); + scope(exit) FUNLOCK(fps); + + /* Since fps is now locked, we can create an "unshared" version + * of fp. */ - L1: + auto fp = cast(_iobuf*) fps; + + ReadlnAppender app; + app.initialize(buf); + int c; while ((c = FGETC(fp)) != -1) { @@ -4785,295 +4891,208 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie if (ferror(fps)) StdioException(); + buf = app.data; + return buf.length; } - else + else static if (__traits(compiles, core.sys.posix.stdio.getdelim)) { - int u = fp._cnt; - char* p = fp._ptr; - int i; - if (fp._flag & _IOTRAN) - { /* Translated mode ignores \r and treats ^Z as end-of-file + import core.stdc.stdlib : free; + import core.stdc.wchar_ : fwide; + + if (orientation == File.Orientation.wide) + { + /* Stream is in wide characters. + * Read them and convert to chars. */ - char c; - while (1) + FLOCK(fps); + scope(exit) FUNLOCK(fps); + auto fp = cast(_iobuf*) fps; + version (Windows) { - if (i == u) // if end of buffer - goto L1; // give up - c = p[i]; - i++; - if (c != '\r') + buf.length = 0; + for (int c = void; (c = FGETWC(fp)) != -1; ) { - if (c == terminator) - break; - if (c != 0x1A) - continue; - goto L1; + if ((c & ~0x7F) == 0) + { buf ~= c; + if (c == terminator) + break; + } + else + { + if (c >= 0xD800 && c <= 0xDBFF) + { + int c2 = void; + if ((c2 = FGETWC(fp)) != -1 || + c2 < 0xDC00 && c2 > 0xDFFF) + { + StdioException("unpaired UTF-16 surrogate"); + } + c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00); + } + import std.utf : encode; + encode(buf, c); + } } - else - { if (i != u && p[i] == terminator) + if (ferror(fp)) + StdioException(); + return buf.length; + } + else version (Posix) + { + buf.length = 0; + for (int c; (c = FGETWC(fp)) != -1; ) + { + import std.utf : encode; + + if ((c & ~0x7F) == 0) + buf ~= cast(char) c; + else + encode(buf, cast(dchar) c); + if (c == terminator) break; - goto L1; } + if (ferror(fps)) + StdioException(); + return buf.length; + } + else + { + static assert(0); } - app.putonly(p[0 .. i]); - app.buf[i - 1] = cast(char) terminator; - if (terminator == '\n' && c == '\r') - i++; } - else + + static char *lineptr = null; + static size_t n = 0; + scope(exit) { - while (1) + if (n > 128 * 1024) { - if (i == u) // if end of buffer - goto L1; // give up - auto c = p[i]; - i++; - if (c == terminator) - break; + // Bound memory used by readln + free(lineptr); + lineptr = null; + n = 0; } - app.putonly(p[0 .. i]); } - fp._cnt -= i; - fp._ptr += i; - } - - buf = app.data; - return buf.length; -} -version (MICROSOFT_STDIO) -private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orientation /*ignored*/) -{ - FLOCK(fps); - scope(exit) FUNLOCK(fps); - - /* Since fps is now locked, we can create an "unshared" version - * of fp. - */ - auto fp = cast(_iobuf*) fps; - - ReadlnAppender app; - app.initialize(buf); - - int c; - while ((c = FGETC(fp)) != -1) - { - app.putchar(cast(char) c); - if (c == terminator) + auto s = core.sys.posix.stdio.getdelim(&lineptr, &n, terminator, fps); + if (s < 0) { - buf = app.data; - return buf.length; + if (ferror(fps)) + StdioException(); + buf.length = 0; // end of file + return 0; } + if (s <= buf.length) + { + buf = buf[0 .. s]; + buf[] = lineptr[0 .. s]; + } + else + { + buf = lineptr[0 .. s].dup; + } + return s; } - - if (ferror(fps)) - StdioException(); - buf = app.data; - return buf.length; -} - -version (HAS_GETDELIM) -private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orientation orientation) -{ - import core.stdc.stdlib : free; - import core.stdc.wchar_ : fwide; - - if (orientation == File.Orientation.wide) + else // version (NO_GETDELIM) { - /* Stream is in wide characters. - * Read them and convert to chars. - */ + import core.stdc.wchar_ : fwide; + FLOCK(fps); scope(exit) FUNLOCK(fps); auto fp = cast(_iobuf*) fps; - version (Windows) + if (orientation == File.Orientation.wide) { - buf.length = 0; - for (int c = void; (c = FGETWC(fp)) != -1; ) + /* Stream is in wide characters. + * Read them and convert to chars. + */ + version (Windows) { - if ((c & ~0x7F) == 0) - { buf ~= c; - if (c == terminator) - break; - } - else + buf.length = 0; + for (int c; (c = FGETWC(fp)) != -1; ) { - if (c >= 0xD800 && c <= 0xDBFF) + if ((c & ~0x7F) == 0) + { buf ~= c; + if (c == terminator) + break; + } + else { - int c2 = void; - if ((c2 = FGETWC(fp)) != -1 || - c2 < 0xDC00 && c2 > 0xDFFF) + if (c >= 0xD800 && c <= 0xDBFF) { - StdioException("unpaired UTF-16 surrogate"); + int c2 = void; + if ((c2 = FGETWC(fp)) != -1 || + c2 < 0xDC00 && c2 > 0xDFFF) + { + StdioException("unpaired UTF-16 surrogate"); + } + c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00); } - c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00); + import std.utf : encode; + encode(buf, c); } - import std.utf : encode; - encode(buf, c); } + if (ferror(fp)) + StdioException(); + return buf.length; } - if (ferror(fp)) - StdioException(); - return buf.length; - } - else version (Posix) - { - buf.length = 0; - for (int c; (c = FGETWC(fp)) != -1; ) + else version (Posix) { import std.utf : encode; - - if ((c & ~0x7F) == 0) - buf ~= cast(char) c; - else - encode(buf, cast(dchar) c); - if (c == terminator) - break; - } - if (ferror(fps)) - StdioException(); - return buf.length; - } - else - { - static assert(0); - } - } - - static char *lineptr = null; - static size_t n = 0; - scope(exit) - { - if (n > 128 * 1024) - { - // Bound memory used by readln - free(lineptr); - lineptr = null; - n = 0; - } - } - - auto s = getdelim(&lineptr, &n, terminator, fps); - if (s < 0) - { - if (ferror(fps)) - StdioException(); - buf.length = 0; // end of file - return 0; - } - - if (s <= buf.length) - { - buf = buf[0 .. s]; - buf[] = lineptr[0 .. s]; - } - else - { - buf = lineptr[0 .. s].dup; - } - return s; -} - -version (NO_GETDELIM) -private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orientation orientation) -{ - import core.stdc.wchar_ : fwide; - - FLOCK(fps); - scope(exit) FUNLOCK(fps); - auto fp = cast(_iobuf*) fps; - if (orientation == File.Orientation.wide) - { - /* Stream is in wide characters. - * Read them and convert to chars. - */ - version (Windows) - { - buf.length = 0; - for (int c; (c = FGETWC(fp)) != -1; ) - { - if ((c & ~0x7F) == 0) - { buf ~= c; + buf.length = 0; + for (int c; (c = FGETWC(fp)) != -1; ) + { + if ((c & ~0x7F) == 0) + buf ~= cast(char) c; + else + encode(buf, cast(dchar) c); if (c == terminator) break; } - else - { - if (c >= 0xD800 && c <= 0xDBFF) - { - int c2 = void; - if ((c2 = FGETWC(fp)) != -1 || - c2 < 0xDC00 && c2 > 0xDFFF) - { - StdioException("unpaired UTF-16 surrogate"); - } - c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00); - } - import std.utf : encode; - encode(buf, c); - } + if (ferror(fps)) + StdioException(); + return buf.length; } - if (ferror(fp)) - StdioException(); - return buf.length; - } - else version (Posix) - { - import std.utf : encode; - buf.length = 0; - for (int c; (c = FGETWC(fp)) != -1; ) + else { - if ((c & ~0x7F) == 0) - buf ~= cast(char) c; - else - encode(buf, cast(dchar) c); - if (c == terminator) - break; + static assert(0); } - if (ferror(fps)) - StdioException(); - return buf.length; - } - else - { - static assert(0); } - } - // Narrow stream - // First, fill the existing buffer - for (size_t bufPos = 0; bufPos < buf.length; ) - { - immutable c = FGETC(fp); - if (c == -1) - { - buf.length = bufPos; - goto endGame; - } - buf[bufPos++] = cast(char) c; - if (c == terminator) + // Narrow stream + // First, fill the existing buffer + for (size_t bufPos = 0; bufPos < buf.length; ) { - // No need to test for errors in file - buf.length = bufPos; - return bufPos; + immutable c = FGETC(fp); + if (c == -1) + { + buf.length = bufPos; + goto endGame; + } + buf[bufPos++] = cast(char) c; + if (c == terminator) + { + // No need to test for errors in file + buf.length = bufPos; + return bufPos; + } } - } - // Then, append to it - for (int c; (c = FGETC(fp)) != -1; ) - { - buf ~= cast(char) c; - if (c == terminator) + // Then, append to it + for (int c; (c = FGETC(fp)) != -1; ) { - // No need to test for errors in file - return buf.length; + buf ~= cast(char) c; + if (c == terminator) + { + // No need to test for errors in file + return buf.length; + } } - } - endGame: - if (ferror(fps)) - StdioException(); - return buf.length; + endGame: + if (ferror(fps)) + StdioException(); + return buf.length; + } } @system unittest diff --git a/libphobos/src/std/system.d b/libphobos/src/std/system.d index e0b3dee..353d692 100644 --- a/libphobos/src/std/system.d +++ b/libphobos/src/std/system.d @@ -30,6 +30,9 @@ immutable win64, /// Microsoft 64 bit Windows systems linux, /// All Linux Systems, except for Android osx, /// Mac OS X + iOS, /// iOS + tvOS, /// tvOS + watchOS, /// watchOS freeBSD, /// FreeBSD netBSD, /// NetBSD dragonFlyBSD, /// DragonFlyBSD @@ -44,6 +47,9 @@ immutable else version (Android) OS os = OS.android; else version (linux) OS os = OS.linux; else version (OSX) OS os = OS.osx; + else version (iOS) OS os = OS.iOS; + else version (tvOS) OS os = OS.tvOS; + else version (watchOS) OS os = OS.watchOS; else version (FreeBSD) OS os = OS.freeBSD; else version (NetBSD) OS os = OS.netBSD; else version (DragonFlyBSD) OS os = OS.dragonFlyBSD; diff --git a/libphobos/src/std/typecons.d b/libphobos/src/std/typecons.d index a63227c..84e876f 100644 --- a/libphobos/src/std/typecons.d +++ b/libphobos/src/std/typecons.d @@ -4204,10 +4204,11 @@ package template OverloadSet(string nam, T...) /* Used by MemberFunctionGenerator. */ -package template FuncInfo(alias func, /+[BUG 4217 ?]+/ T = typeof(&func)) +package template FuncInfo(alias func) +if (is(typeof(&func))) { - alias RT = ReturnType!T; - alias PT = Parameters!T; + alias RT = ReturnType!(typeof(&func)); + alias PT = Parameters!(typeof(&func)); } package template FuncInfo(Func) { @@ -4248,6 +4249,7 @@ private static: // Internal stuffs //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// import std.format; + alias format = std.format.format; enum CONSTRUCTOR_NAME = "__ctor"; @@ -5024,7 +5026,7 @@ package template GetOverloadedMethods(T) enum isMethod = false; } alias follows = AliasSeq!( - std.meta.Filter!(isMethod, __traits(getOverloads, T, name)), + Filter!(isMethod, __traits(getOverloads, T, name)), follows!(i + 1)); } } @@ -5933,13 +5935,7 @@ mixin template Proxy(alias a) // built-in type field, manifest constant, and static non-mutable field enum opDispatch = mixin("a."~name); } - else static if (is(typeof(mixin("a."~name))) || __traits(getOverloads, a, name).length != 0) - { - // field or property function - @property auto ref opDispatch(this X)() { return mixin("a."~name); } - @property auto ref opDispatch(this X, V)(auto ref V v) { return mixin("a."~name~" = v"); } - } - else + else static if (__traits(isTemplate, mixin("a."~name))) { // member template template opDispatch(T...) @@ -5948,6 +5944,13 @@ mixin template Proxy(alias a) auto ref opDispatch(this X, Args...)(auto ref Args args){ return mixin("a."~name~targs~"(args)"); } } } + else + { + // field or property function + @property auto ref opDispatch(this X)() { return mixin("a."~name); } + @property auto ref opDispatch(this X, V)(auto ref V v) { return mixin("a."~name~" = v"); } + } + } import std.traits : isArray; diff --git a/libphobos/src/std/zip.d b/libphobos/src/std/zip.d index 8b130ea..9e55d19 100644 --- a/libphobos/src/std/zip.d +++ b/libphobos/src/std/zip.d @@ -263,8 +263,8 @@ final class ArchiveMember { void print() { - printf("name = '%.*s'\n", name.length, name.ptr); - printf("\tcomment = '%.*s'\n", comment.length, comment.ptr); + printf("name = '%.*s'\n", cast(int) name.length, name.ptr); + printf("\tcomment = '%.*s'\n", cast(int) comment.length, comment.ptr); printf("\tmadeVersion = x%04x\n", _madeVersion); printf("\textractVersion = x%04x\n", extractVersion); printf("\tflags = x%04x\n", flags); @@ -348,7 +348,7 @@ final class ZipArchive printf("\tdiskStartDir = %u\n", diskStartDir); printf("\tnumEntries = %u\n", numEntries); printf("\ttotalEntries = %u\n", totalEntries); - printf("\tcomment = '%.*s'\n", comment.length, comment.ptr); + printf("\tcomment = '%.*s'\n", cast(int) comment.length, comment.ptr); } } |