aboutsummaryrefslogtreecommitdiff
path: root/libphobos/src/std/string.d
diff options
context:
space:
mode:
authorIain Buclaw <ibuclaw@gdcproject.org>2019-06-18 20:42:10 +0200
committerIain Buclaw <ibuclaw@gdcproject.org>2021-11-30 16:53:28 +0100
commit5fee5ec362f7a243f459e6378fd49dfc89dc9fb5 (patch)
tree61d1bdbca854a903c0860406f457f06b2040be7a /libphobos/src/std/string.d
parentb3f60112edcb85b459e60f66c44a55138b1cef49 (diff)
downloadgcc-5fee5ec362f7a243f459e6378fd49dfc89dc9fb5.zip
gcc-5fee5ec362f7a243f459e6378fd49dfc89dc9fb5.tar.gz
gcc-5fee5ec362f7a243f459e6378fd49dfc89dc9fb5.tar.bz2
d: Import dmd b8384668f, druntime e6caaab9, phobos 5ab9ad256 (v2.098.0-beta.1)
The D front-end is now itself written in D, in order to build GDC, you will need a working GDC compiler (GCC version 9.1 or later). GCC changes: - Add support for bootstrapping the D front-end. These add the required components in order to have a D front-end written in D itself. Because the compiler front-end only depends on the core runtime modules, only libdruntime is built for the bootstrap stages. D front-end changes: - Import dmd v2.098.0-beta.1. Druntime changes: - Import druntime v2.098.0-beta.1. Phobos changes: - Import phobos v2.098.0-beta.1. The jump from v2.076.1 to v2.098.0 covers nearly 4 years worth of development on the D programming language and run-time libraries. ChangeLog: * Makefile.def: Add bootstrap to libbacktrace, libphobos, zlib, and libatomic. * Makefile.in: Regenerate. * Makefile.tpl (POSTSTAGE1_HOST_EXPORTS): Fix command for GDC. (STAGE1_CONFIGURE_FLAGS): Add --with-libphobos-druntime-only if target-libphobos-bootstrap. (STAGE2_CONFIGURE_FLAGS): Likewise. * configure: Regenerate. * configure.ac: Add support for bootstrapping D front-end. config/ChangeLog: * acx.m4 (ACX_PROG_GDC): New m4 function. gcc/ChangeLog: * Makefile.in (GDC): New variable. (GDCFLAGS): New variable. * configure: Regenerate. * configure.ac: Add call to ACX_PROG_GDC. Substitute GDCFLAGS. gcc/d/ChangeLog: * dmd/MERGE: Merge upstream dmd b8384668f. * Make-lang.in (d-warn): Use strict warnings. (DMD_WARN_CXXFLAGS): Remove. (DMD_COMPILE): Remove. (CHECKING_DFLAGS): Define. (WARN_DFLAGS): Define. (ALL_DFLAGS): Define. (DCOMPILE.base): Define. (DCOMPILE): Define. (DPOSTCOMPILE): Define. (DLINKER): Define. (DLLINKER): Define. (D_FRONTEND_OBJS): Add new dmd front-end objects. (D_GENERATED_SRCS): Remove. (D_GENERATED_OBJS): Remove. (D_ALL_OBJS): Remove D_GENERATED_OBJS. (d21$(exeext)): Build using DLLINKER and -static-libphobos. (d.tags): Remove dmd/*.c and dmd/root/*.c. (d.mostlyclean): Remove D_GENERATED_SRCS, d/idgen$(build_exeext), d/impcnvgen$(build_exeext). (D_INCLUDES): Include $(srcdir)/d/dmd/res. (CFLAGS-d/id.o): Remove. (CFLAGS-d/impcnvtab.o): Remove. (d/%.o): Build using DCOMPILE and DPOSTCOMPILE. Update dependencies from d/dmd/%.c to d/dmd/%.d. (d/idgen$(build_exeext)): Remove. (d/impcnvgen$(build_exeext)): Remove. (d/id.c): Remove. (d/id.h): Remove. (d/impcnvtab.c): Remove. (d/%.dmdgen.o): Remove. (D_SYSTEM_H): Remove. (d/idgen.dmdgen.o): Remove. (d/impcnvgen.dmdgen.o): Remove. * config-lang.in (boot_language): New variable. * d-attribs.cc: Include dmd/expression.h. * d-builtins.cc: Include d-frontend.h. (build_frontend_type): Update for new front-end interface. (d_eval_constant_expression): Likewise. (d_build_builtins_module): Likewise. (maybe_set_builtin_1): Likewise. (d_build_d_type_nodes): Likewise. * d-codegen.cc (d_decl_context): Likewise. (declaration_reference_p): Likewise. (declaration_type): Likewise. (parameter_reference_p): Likewise. (parameter_type): Likewise. (get_array_length): Likewise. (build_delegate_cst): Likewise. (build_typeof_null_value): Likewise. (identity_compare_p): Likewise. (lower_struct_comparison): Likewise. (build_filename_from_loc): Likewise. (build_assert_call): Remove LIBCALL_SWITCH_ERROR. (build_bounds_index_condition): Call LIBCALL_ARRAYBOUNDS_INDEXP on bounds error. (build_bounds_slice_condition): Call LIBCALL_ARRAYBOUNDS_SLICEP on bounds error. (array_bounds_check): Update for new front-end interface. (checkaction_trap_p): Handle CHECKACTION_context. (get_function_type): Update for new front-end interface. (d_build_call): Likewise. * d-compiler.cc: Remove include of dmd/scope.h. (Compiler::genCmain): Remove. (Compiler::paintAsType): Update for new front-end interface. (Compiler::onParseModule): Likewise. * d-convert.cc (convert_expr): Remove call to LIBCALL_ARRAYCAST. (convert_for_rvalue): Update for new front-end interface. (convert_for_assignment): Likewise. (convert_for_condition): Likewise. (d_array_convert): Likewise. * d-diagnostic.cc (error): Remove. (errorSupplemental): Remove. (warning): Remove. (warningSupplemental): Remove. (deprecation): Remove. (deprecationSupplemental): Remove. (message): Remove. (vtip): New. * d-frontend.cc (global): Remove. (Global::_init): Remove. (Global::startGagging): Remove. (Global::endGagging): Remove. (Global::increaseErrorCount): Remove. (Loc::Loc): Remove. (Loc::toChars): Remove. (Loc::equals): Remove. (isBuiltin): Update for new front-end interface. (eval_builtin): Likewise. (getTypeInfoType): Likewise. (inlineCopy): Remove. * d-incpath.cc: Include d-frontend.h. (add_globalpaths): Call d_gc_malloc to allocate Strings. (add_filepaths): Likewise. * d-lang.cc: Include dmd/id.h, dmd/root/file.h, d-frontend.h. Remove include of dmd/mars.h, id.h. (entrypoint_module): Remove. (entrypoint_root_module): Remove. (deps_write_string): Update for new front-end interface. (deps_write): Likewise. (d_init_options): Call rt_init. Remove setting global params that are default initialized by the front-end. (d_handle_option): Handle OPT_fcheckaction_, OPT_fdump_c___spec_, OPT_fdump_c___spec_verbose, OPT_fextern_std_, OPT_fpreview, OPT_revert, OPT_fsave_mixins_, and OPT_ftransition. (d_post_options): Propagate dip1021 and dip1000 preview flags to dip25, and flag_diagnostics_show_caret to printErrorContext. (d_add_entrypoint_module): Remove. (d_parse_file): Update for new front-end interface. (d_type_promotes_to): Likewise. (d_types_compatible_p): Likewise. * d-longdouble.cc (CTFloat::zero): Remove. (CTFloat::one): Remove. (CTFloat::minusone): Remove. (CTFloat::half): Remove. * d-system.h (POSIX): Remove. (realpath): Remove. (isalpha): Remove. (isalnum): Remove. (isdigit): Remove. (islower): Remove. (isprint): Remove. (isspace): Remove. (isupper): Remove. (isxdigit): Remove. (tolower): Remove. (_mkdir): Remove. (INT32_MAX): Remove. (INT32_MIN): Remove. (INT64_MIN): Remove. (UINT32_MAX): Remove. (UINT64_MAX): Remove. * d-target.cc: Include calls.h. (target): Remove. (define_float_constants): Remove initialization of snan. (Target::_init): Update for new front-end interface. (Target::isVectorTypeSupported): Likewise. (Target::isVectorOpSupported): Remove cases for unordered operators. (TargetCPP::typeMangle): Update for new front-end interface. (TargetCPP::parameterType): Likewise. (Target::systemLinkage): Likewise. (Target::isReturnOnStack): Likewise. (Target::isCalleeDestroyingArgs): Define. (Target::preferPassByRef): Define. * d-tree.h (d_add_entrypoint_module): Remove. * decl.cc (gcc_attribute_p): Update for new front-end interface. (apply_pragma_crt): Define. (DeclVisitor::visit(PragmaDeclaration *)): Handle pragmas crt_constructor and crt_destructor. (DeclVisitor::visit(TemplateDeclaration *)): Update for new front-end interface. (DeclVisitor::visit): Likewise. (DeclVisitor::finish_vtable): Likewise. (get_symbol_decl): Error if template has more than one nesting context. Update for new front-end interface. (make_thunk): Update for new front-end interface. (get_vtable_decl): Likewise. * expr.cc (ExprVisitor::visit): Likewise. (build_return_dtor): Likewise. * imports.cc (ImportVisitor::visit): Likewise. * intrinsics.cc: Include dmd/expression.h. Remove include of dmd/mangle.h. (maybe_set_intrinsic): Update for new front-end interface. * intrinsics.def (INTRINSIC_ROL): Update intrinsic signature. (INTRINSIC_ROR): Likewise. (INTRINSIC_ROR_TIARG): Likewise. (INTRINSIC_TOPREC): Likewise. (INTRINSIC_TOPRECL): Likewise. (INTRINSIC_TAN): Update intrinsic module and signature. (INTRINSIC_ISNAN): Likewise. (INTRINSIC_ISFINITE): Likewise. (INTRINSIC_COPYSIGN): Define intrinsic. (INTRINSIC_COPYSIGNI): Define intrinsic. (INTRINSIC_EXP): Update intrinsic module. (INTRINSIC_EXPM1): Likewise. (INTRINSIC_EXP2): Likewise. (INTRINSIC_LOG): Likewise. (INTRINSIC_LOG2): Likewise. (INTRINSIC_LOG10): Likewise. (INTRINSIC_POW): Likewise. (INTRINSIC_ROUND): Likewise. (INTRINSIC_FLOORF): Likewise. (INTRINSIC_FLOOR): Likewise. (INTRINSIC_FLOORL): Likewise. (INTRINSIC_CEILF): Likewise. (INTRINSIC_CEIL): Likewise. (INTRINSIC_CEILL): Likewise. (INTRINSIC_TRUNC): Likewise. (INTRINSIC_FMIN): Likewise. (INTRINSIC_FMAX): Likewise. (INTRINSIC_FMA): Likewise. (INTRINSIC_VA_ARG): Update intrinsic signature. (INTRINSIC_VASTART): Likewise. * lang.opt (fcheck=): Add alternate aliases for contract switches. (fcheckaction=): New option. (check_action): New Enum and EnumValue entries. (fdump-c++-spec-verbose): New option. (fdump-c++-spec=): New option. (fextern-std=): New option. (extern_stdcpp): New Enum and EnumValue entries (fpreview=): New options. (frevert=): New options. (fsave-mixins): New option. (ftransition=): Update options. * modules.cc (get_internal_fn): Replace Prot with Visibility. (build_internal_fn): Likewise. (build_dso_cdtor_fn): Likewise. (build_module_tree): Remove check for __entrypoint module. * runtime.def (P5): Define. (ARRAYBOUNDS_SLICEP): Define. (ARRAYBOUNDS_INDEXP): Define. (NEWTHROW): Define. (ADCMP2): Remove. (ARRAYCAST): Remove. (SWITCH_STRING): Remove. (SWITCH_USTRING): Remove. (SWITCH_DSTRING): Remove. (SWITCH_ERROR): Remove. * toir.cc (IRVisitor::visit): Update for new front-end interface. (IRVisitor::check_previous_goto): Remove checks for case and default statements. (IRVisitor::visit(SwitchStatement *)): Remove handling of string switch conditions. * typeinfo.cc: Include d-frontend.h. (get_typeinfo_kind): Update for new front-end interface. (make_frontend_typeinfo): Likewise. (TypeInfoVisitor::visit): Likewise. (builtin_typeinfo_p): Likewise. (get_typeinfo_decl): Likewise. (build_typeinfo): Likewise. * types.cc (valist_array_p): Likewise. (make_array_type): Likewise. (merge_aggregate_types): Likewise. (TypeVisitor::visit(TypeBasic *)): Likewise. (TypeVisitor::visit(TypeFunction *)): Likewise. (TypeVisitor::visit(TypeStruct *)): Update comment. * verstr.h: Removed. * d-frontend.h: New file. gcc/po/ChangeLog: * EXCLUDES: Remove d/dmd sources from list. gcc/testsuite/ChangeLog: * gdc.dg/Wcastresult2.d: Update test. * gdc.dg/asm1.d: Likewise. * gdc.dg/asm2.d: Likewise. * gdc.dg/asm3.d: Likewise. * gdc.dg/gdc282.d: Likewise. * gdc.dg/imports/gdc170.d: Likewise. * gdc.dg/intrinsics.d: Likewise. * gdc.dg/pr101672.d: Likewise. * gdc.dg/pr90650a.d: Likewise. * gdc.dg/pr90650b.d: Likewise. * gdc.dg/pr94777a.d: Likewise. * gdc.dg/pr95250.d: Likewise. * gdc.dg/pr96869.d: Likewise. * gdc.dg/pr98277.d: Likewise. * gdc.dg/pr98457.d: Likewise. * gdc.dg/simd1.d: Likewise. * gdc.dg/simd2a.d: Likewise. * gdc.dg/simd2b.d: Likewise. * gdc.dg/simd2c.d: Likewise. * gdc.dg/simd2d.d: Likewise. * gdc.dg/simd2e.d: Likewise. * gdc.dg/simd2f.d: Likewise. * gdc.dg/simd2g.d: Likewise. * gdc.dg/simd2h.d: Likewise. * gdc.dg/simd2i.d: Likewise. * gdc.dg/simd2j.d: Likewise. * gdc.dg/simd7951.d: Likewise. * gdc.dg/torture/gdc309.d: Likewise. * gdc.dg/torture/pr94424.d: Likewise. * gdc.dg/torture/pr94777b.d: Likewise. * lib/gdc-utils.exp (gdc-convert-args): Handle new compiler options. (gdc-convert-test): Handle CXXFLAGS, EXTRA_OBJC_SOURCES, and ARG_SETS test directives. (gdc-do-test): Only import modules in the test run directory. * gdc.dg/pr94777c.d: New test. * gdc.dg/pr96156b.d: New test. * gdc.dg/pr96157c.d: New test. * gdc.dg/simd_ctfe.d: New test. * gdc.dg/torture/simd17344.d: New test. * gdc.dg/torture/simd20052.d: New test. * gdc.dg/torture/simd6.d: New test. * gdc.dg/torture/simd7.d: New test. libphobos/ChangeLog: * libdruntime/MERGE: Merge upstream druntime e6caaab9. * libdruntime/Makefile.am (D_EXTRA_FLAGS): Build libdruntime with -fpreview=dip1000, -fpreview=fieldwise, and -fpreview=dtorfields. (ALL_DRUNTIME_SOURCES): Add DRUNTIME_DSOURCES_STDCXX. (DRUNTIME_DSOURCES): Update list of C binding modules. (DRUNTIME_DSOURCES_STDCXX): Likewise. (DRUNTIME_DSOURCES_LINUX): Likewise. (DRUNTIME_DSOURCES_OPENBSD): Likewise. (DRUNTIME_DISOURCES): Remove __entrypoint.di. * libdruntime/Makefile.in: Regenerated. * libdruntime/__entrypoint.di: Removed. * libdruntime/gcc/deh.d (_d_isbaseof): Update signature. (_d_createTrace): Likewise. (__gdc_begin_catch): Remove reference to the exception. (_d_throw): Increment reference count of thrown object before unwind. (__gdc_personality): Chain exceptions with Throwable.chainTogether. * libdruntime/gcc/emutls.d: Update imports. * libdruntime/gcc/sections/elf.d: Update imports. (DSO.moduleGroup): Update signature. * libdruntime/gcc/sections/macho.d: Update imports. (DSO.moduleGroup): Update signature. * libdruntime/gcc/sections/pecoff.d: Update imports. (DSO.moduleGroup): Update signature. * src/MERGE: Merge upstream phobos 5ab9ad256. * src/Makefile.am (D_EXTRA_DFLAGS): Add -fpreview=dip1000 and -fpreview=dtorfields flags. (PHOBOS_DSOURCES): Update list of std modules. * src/Makefile.in: Regenerate. * testsuite/lib/libphobos.exp (libphobos-dg-test): Handle assembly compile types. (dg-test): Override. (additional_prunes): Define. (libphobos-dg-prune): Filter any additional_prunes set by tests. * testsuite/libphobos.aa/test_aa.d: Update test. * testsuite/libphobos.druntime/druntime.exp (version_flags): Add -fversion=CoreUnittest. * testsuite/libphobos.druntime_shared/druntime_shared.exp (version_flags): Add -fversion=CoreUnittest -fversion=Shared. * testsuite/libphobos.exceptions/unknown_gc.d: Update test. * testsuite/libphobos.hash/test_hash.d: Update test. * testsuite/libphobos.phobos/phobos.exp (version_flags): Add -fversion=StdUnittest * testsuite/libphobos.phobos_shared/phobos_shared.exp (version_flags): Likewise. * testsuite/libphobos.shared/host.c: Update test. * testsuite/libphobos.shared/load.d: Update test. * testsuite/libphobos.shared/load_13414.d: Update test. * testsuite/libphobos.thread/fiber_guard_page.d: Update test. * testsuite/libphobos.thread/tlsgc_sections.d: Update test. * testsuite/testsuite_flags.in: Add -fpreview=dip1000 to --gdcflags. * testsuite/libphobos.shared/link_mod_collision.d: Removed. * testsuite/libphobos.shared/load_mod_collision.d: Removed. * testsuite/libphobos.betterc/betterc.exp: New test. * testsuite/libphobos.config/config.exp: New test. * testsuite/libphobos.gc/gc.exp: New test. * testsuite/libphobos.imports/imports.exp: New test. * testsuite/libphobos.lifetime/lifetime.exp: New test. * testsuite/libphobos.unittest/unittest.exp: New test.
Diffstat (limited to 'libphobos/src/std/string.d')
-rw-r--r--libphobos/src/std/string.d2046
1 files changed, 1354 insertions, 692 deletions
diff --git a/libphobos/src/std/string.d b/libphobos/src/std/string.d
index 1128a090..420b68a 100644
--- a/libphobos/src/std/string.d
+++ b/libphobos/src/std/string.d
@@ -69,9 +69,9 @@ $(TR $(TDNW Miscellaneous)
)
)))
-Objects of types $(D _string), $(D wstring), and $(D dstring) are value types
+Objects of types `string`, `wstring`, and `dstring` are value types
and cannot be mutated element-by-element. For using mutation during building
-strings, use $(D char[]), $(D wchar[]), or $(D dchar[]). The $(D xxxstring)
+strings, use `char[]`, `wchar[]`, or `dchar[]`. The `xxxstring`
types are preferable because they don't exhibit undesired aliasing, thus
making code more robust.
@@ -110,7 +110,7 @@ $(LEADINGROW Publicly imported functions)
))
)
-There is a rich set of functions for _string handling defined in other modules.
+There is a rich set of functions for string handling defined in other modules.
Functions related to Unicode and ASCII are found in $(MREF std, uni)
and $(MREF std, ascii), respectively. Other functions that have a
wider generality than just strings can be found in $(MREF std, algorithm)
@@ -129,26 +129,26 @@ See_Also:
for functions that work with unicode strings
)
-Copyright: Copyright Digital Mars 2007-.
+Copyright: Copyright The D Language Foundation 2007-.
License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: $(HTTP digitalmars.com, Walter Bright),
$(HTTP erdani.org, Andrei Alexandrescu),
- Jonathan M Davis,
- and David L. 'SpottedTiger' Davis
+ $(HTTP jmdavisprog.com, Jonathan M Davis),
+ and David L. 'SpottedTiger' Davis
-Source: $(PHOBOSSRC std/_string.d)
+Source: $(PHOBOSSRC std/string.d)
*/
module std.string;
-version (unittest)
+version (StdUnittest)
{
private:
struct TestAliasedString
{
- string get() @safe @nogc pure nothrow { return _s; }
+ string get() @safe @nogc pure nothrow return scope { return _s; }
alias get this;
@disable this(this);
string _s;
@@ -175,13 +175,13 @@ public import std.format : format, sformat;
import std.typecons : Flag, Yes, No;
public import std.uni : icmp, toLower, toLowerInPlace, toUpper, toUpperInPlace;
-import std.meta; // AliasSeq, staticIndexOf
-import std.range.primitives; // back, ElementEncodingType, ElementType, front,
- // hasLength, hasSlicing, isBidirectionalRange, isForwardRange, isInfinite,
- // isInputRange, isOutputRange, isRandomAccessRange, popBack, popFront, put,
- // save;
-import std.traits; // isConvertibleToString, isNarrowString, isSomeChar,
- // isSomeString, StringTypeOf, Unqual
+import std.meta : AliasSeq, staticIndexOf;
+import std.range.primitives : back, ElementEncodingType, ElementType, front,
+ hasLength, hasSlicing, isBidirectionalRange, isForwardRange, isInfinite,
+ isInputRange, isOutputRange, isRandomAccessRange, popBack, popFront, put,
+ save;
+import std.traits : isConvertibleToString, isNarrowString, isSomeChar,
+ isSomeString, StringTypeOf, Unqual;
//public imports for backward compatibility
public import std.algorithm.comparison : cmp;
@@ -201,46 +201,163 @@ class StringException : Exception
mixin basicExceptionCtors;
}
+///
+@safe pure unittest
+{
+ import std.exception : assertThrown;
+ auto bad = " a\n\tb\n c";
+ assertThrown!StringException(bad.outdent);
+}
/++
Params:
cString = A null-terminated c-style string.
- Returns: A D-style array of $(D char) referencing the same string. The
- returned array will retain the same type qualifiers as the input.
+ Returns: A D-style array of `char`, `wchar` or `dchar` referencing the same
+ string. The returned array will retain the same type qualifiers as the input.
$(RED Important Note:) The returned array is a slice of the original buffer.
The original data is not changed and not copied.
+/
+inout(Char)[] fromStringz(Char)(return scope inout(Char)* cString) @nogc @system pure nothrow
+if (isSomeChar!Char)
+{
+ import core.stdc.stddef : wchar_t;
-inout(char)[] fromStringz(inout(char)* cString) @nogc @system pure nothrow {
- import core.stdc.string : strlen;
- return cString ? cString[0 .. strlen(cString)] : null;
+ static if (is(immutable Char == immutable char))
+ import core.stdc.string : cstrlen = strlen;
+ else static if (is(immutable Char == immutable wchar_t))
+ import core.stdc.wchar_ : cstrlen = wcslen;
+ else
+ static size_t cstrlen(scope const Char* s)
+ {
+ const(Char)* p = s;
+ while (*p)
+ ++p;
+ return p - s;
+ }
+
+ return cString ? cString[0 .. cstrlen(cString)] : null;
+}
+
+/// ditto
+inout(Char)[] fromStringz(Char)(return scope inout(Char)[] cString) @nogc @safe pure nothrow
+if (isSomeChar!Char)
+{
+ foreach (i; 0 .. cString.length)
+ if (cString[i] == '\0')
+ return cString[0 .. i];
+
+ return cString;
}
///
@system pure unittest
{
- assert(fromStringz(null) == null);
- assert(fromStringz("foo") == "foo");
+ assert(fromStringz("foo\0"c.ptr) == "foo"c);
+ assert(fromStringz("foo\0"w.ptr) == "foo"w);
+ assert(fromStringz("foo\0"d.ptr) == "foo"d);
+
+ assert(fromStringz("福\0"c.ptr) == "福"c);
+ assert(fromStringz("福\0"w.ptr) == "福"w);
+ assert(fromStringz("福\0"d.ptr) == "福"d);
+}
+
+///
+@nogc @safe pure nothrow unittest
+{
+ struct C
+ {
+ char[32] name;
+ }
+ assert(C("foo\0"c).name.fromStringz() == "foo"c);
+
+ struct W
+ {
+ wchar[32] name;
+ }
+ assert(W("foo\0"w).name.fromStringz() == "foo"w);
+
+ struct D
+ {
+ dchar[32] name;
+ }
+ assert(D("foo\0"d).name.fromStringz() == "foo"d);
+}
+
+@nogc @safe pure nothrow unittest
+{
+ assert( string.init.fromStringz() == ""c);
+ assert(wstring.init.fromStringz() == ""w);
+ assert(dstring.init.fromStringz() == ""d);
+
+ immutable char[3] a = "foo"c;
+ assert(a.fromStringz() == "foo"c);
+
+ immutable wchar[3] b = "foo"w;
+ assert(b.fromStringz() == "foo"w);
+
+ immutable dchar[3] c = "foo"d;
+ assert(c.fromStringz() == "foo"d);
+}
+
+@system pure unittest
+{
+ char* a = null;
+ assert(fromStringz(a) == null);
+ wchar* b = null;
+ assert(fromStringz(b) == null);
+ dchar* c = null;
+ assert(fromStringz(c) == null);
+
+ const char* d = "foo\0";
+ assert(fromStringz(d) == "foo");
+
+ immutable char* e = "foo\0";
+ assert(fromStringz(e) == "foo");
+
+ const wchar* f = "foo\0";
+ assert(fromStringz(f) == "foo");
+
+ immutable wchar* g = "foo\0";
+ assert(fromStringz(g) == "foo");
+
+ const dchar* h = "foo\0";
+ assert(fromStringz(h) == "foo");
+
+ immutable dchar* i = "foo\0";
+ assert(fromStringz(i) == "foo");
+
+ immutable wchar z = 0x0000;
+ // Test some surrogate pairs
+ // high surrogates are in the range 0xD800 .. 0xDC00
+ // low surrogates are in the range 0xDC00 .. 0xE000
+ // since UTF16 doesn't specify endianness we test both.
+ foreach (wchar[] t; [[0xD800, 0xDC00], [0xD800, 0xE000], [0xDC00, 0xDC00],
+ [0xDC00, 0xE000], [0xDA00, 0xDE00]])
+ {
+ immutable hi = t[0], lo = t[1];
+ assert(fromStringz([hi, lo, z].ptr) == [hi, lo]);
+ assert(fromStringz([lo, hi, z].ptr) == [lo, hi]);
+ }
}
/++
Params:
s = A D-style string.
- Returns: A C-style null-terminated string equivalent to $(D s). $(D s)
- must not contain embedded $(D '\0')'s as any C function will treat the
- first $(D '\0') that it sees as the end of the string. If $(D s.empty) is
- $(D true), then a string containing only $(D '\0') is returned.
+ Returns: A C-style null-terminated string equivalent to `s`. `s`
+ must not contain embedded `'\0'`'s as any C function will treat the
+ first `'\0'` that it sees as the end of the string. If `s.empty` is
+ `true`, then a string containing only `'\0'` is returned.
- $(RED Important Note:) When passing a $(D char*) to a C function, and the C
+ $(RED Important Note:) When passing a `char*` to a C function, and the C
function keeps it around for any reason, make sure that you keep a
reference to it in your D code. Otherwise, it may become invalid during a
garbage collection cycle and cause a nasty bug when the C code tries to use
it.
+/
-immutable(char)* toStringz(const(char)[] s) @trusted pure nothrow
+immutable(char)* toStringz(scope const(char)[] s) @trusted pure nothrow
out (result)
{
import core.stdc.string : strlen, memcmp;
@@ -248,13 +365,18 @@ out (result)
{
auto slen = s.length;
while (slen > 0 && s[slen-1] == 0) --slen;
- assert(strlen(result) == slen);
- assert(result[0 .. slen] == s[0 .. slen]);
+ assert(strlen(result) == slen,
+ "The result c string is shorter than the in input string");
+ assert(result[0 .. slen] == s[0 .. slen],
+ "The input and result string are not equal");
}
}
-body
+do
{
import std.exception : assumeUnique;
+
+ if (s.empty) return "".ptr;
+
/+ Unfortunately, this isn't reliable.
We could make this work if string literals are put
in read-only memory and we test if s[] is pointing into
@@ -278,26 +400,6 @@ body
return &assumeUnique(copy)[0];
}
-/++ Ditto +/
-immutable(char)* toStringz(in string s) @trusted pure nothrow
-{
- if (s.empty) return "".ptr;
- /* Peek past end of s[], if it's 0, no conversion necessary.
- * Note that the compiler will put a 0 past the end of static
- * strings, and the storage allocator will put a 0 past the end
- * of newly allocated char[]'s.
- */
- immutable p = s.ptr + s.length;
- // Is p dereferenceable? A simple test: if the p points to an
- // address multiple of 4, then conservatively assume the pointer
- // might be pointing to a new block of memory, which might be
- // unreadable. Otherwise, it's definitely pointing to valid
- // memory.
- if ((cast(size_t) p & 3) && *p == 0)
- return &s[0];
- return toStringz(cast(const char[]) s);
-}
-
///
pure nothrow @system unittest
{
@@ -325,6 +427,27 @@ pure nothrow @system unittest
const string test2 = "";
p = toStringz(test2);
assert(*p == 0);
+
+ assert(toStringz([]) is toStringz(""));
+}
+
+pure nothrow @system unittest // https://issues.dlang.org/show_bug.cgi?id=15136
+{
+ static struct S
+ {
+ immutable char[5] str;
+ ubyte foo;
+ this(char[5] str) pure nothrow
+ {
+ this.str = str;
+ }
+ }
+ auto s = S("01234");
+ const str = s.str.toStringz;
+ assert(str !is s.str.ptr);
+ assert(*(str + 5) == 0); // Null terminated.
+ s.foo = 42;
+ assert(*(str + 5) == 0); // Still null terminated.
}
@@ -340,27 +463,199 @@ alias CaseSensitive = Flag!"caseSensitive";
s = string or InputRange of characters to search in correct UTF format
c = character to search for
startIdx = starting index to a well-formed code point
- cs = $(D Yes.caseSensitive) or $(D No.caseSensitive)
+ cs = `Yes.caseSensitive` or `No.caseSensitive`
Returns:
- the index of the first occurrence of $(D c) in $(D s) with
- respect to the start index $(D startIdx). If $(D c)
- is not found, then $(D -1) is returned.
- If $(D c) is found the value of the returned index is at least
- $(D startIdx).
+ the index of the first occurrence of `c` in `s` with
+ respect to the start index `startIdx`. If `c`
+ is not found, then `-1` is returned.
+ If `c` is found the value of the returned index is at least
+ `startIdx`.
If the parameters are not valid UTF, the result will still
be in the range [-1 .. s.length], but will not be reliable otherwise.
Throws:
- If the sequence starting at $(D startIdx) does not represent a well
+ If the sequence starting at `startIdx` does not represent a well
formed codepoint, then a $(REF UTFException, std,utf) may be thrown.
See_Also: $(REF countUntil, std,algorithm,searching)
+/
-ptrdiff_t indexOf(Range)(Range s, in dchar c,
- in CaseSensitive cs = Yes.caseSensitive)
-if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) &&
- !isConvertibleToString!Range)
+ptrdiff_t indexOf(Range)(Range s, dchar c, CaseSensitive cs = Yes.caseSensitive)
+if (isInputRange!Range && isSomeChar!(ElementType!Range) && !isSomeString!Range)
+{
+ return _indexOf(s, c, cs);
+}
+
+/// Ditto
+ptrdiff_t indexOf(C)(scope const(C)[] s, dchar c, CaseSensitive cs = Yes.caseSensitive)
+if (isSomeChar!C)
+{
+ return _indexOf(s, c, cs);
+}
+
+/// Ditto
+ptrdiff_t indexOf(Range)(Range s, dchar c, size_t startIdx, CaseSensitive cs = Yes.caseSensitive)
+if (isInputRange!Range && isSomeChar!(ElementType!Range) && !isSomeString!Range)
+{
+ return _indexOf(s, c, startIdx, cs);
+}
+
+/// Ditto
+ptrdiff_t indexOf(C)(scope const(C)[] s, dchar c, size_t startIdx, CaseSensitive cs = Yes.caseSensitive)
+if (isSomeChar!C)
+{
+ return _indexOf(s, c, startIdx, cs);
+}
+
+///
+@safe pure unittest
+{
+ import std.typecons : No;
+
+ string s = "Hello World";
+ assert(indexOf(s, 'W') == 6);
+ assert(indexOf(s, 'Z') == -1);
+ assert(indexOf(s, 'w', No.caseSensitive) == 6);
+}
+
+///
+@safe pure unittest
+{
+ import std.typecons : No;
+
+ string s = "Hello World";
+ assert(indexOf(s, 'W', 4) == 6);
+ assert(indexOf(s, 'Z', 100) == -1);
+ assert(indexOf(s, 'w', 3, No.caseSensitive) == 6);
+}
+
+@safe pure unittest
+{
+ assert(testAliasedString!indexOf("std/string.d", '/'));
+
+ enum S : string { a = "std/string.d" }
+ assert(S.a.indexOf('/') == 3);
+
+ char[S.a.length] sa = S.a[];
+ assert(sa.indexOf('/') == 3);
+}
+
+@safe pure unittest
+{
+ import std.conv : to;
+ import std.exception : assertCTFEable;
+ import std.traits : EnumMembers;
+ import std.utf : byChar, byWchar, byDchar;
+
+ assertCTFEable!(
+ {
+ static foreach (S; AliasSeq!(string, wstring, dstring))
+ {{
+ assert(indexOf(cast(S) null, cast(dchar)'a') == -1);
+ assert(indexOf(to!S("def"), cast(dchar)'a') == -1);
+ assert(indexOf(to!S("abba"), cast(dchar)'a') == 0);
+ assert(indexOf(to!S("def"), cast(dchar)'f') == 2);
+
+ assert(indexOf(to!S("def"), cast(dchar)'a', No.caseSensitive) == -1);
+ assert(indexOf(to!S("def"), cast(dchar)'a', No.caseSensitive) == -1);
+ assert(indexOf(to!S("Abba"), cast(dchar)'a', No.caseSensitive) == 0);
+ assert(indexOf(to!S("def"), cast(dchar)'F', No.caseSensitive) == 2);
+ assert(indexOf(to!S("ödef"), 'ö', No.caseSensitive) == 0);
+
+ S sPlts = "Mars: the fourth Rock (Planet) from the Sun.";
+ assert(indexOf("def", cast(char)'f', No.caseSensitive) == 2);
+ assert(indexOf(sPlts, cast(char)'P', No.caseSensitive) == 23);
+ assert(indexOf(sPlts, cast(char)'R', No.caseSensitive) == 2);
+ }}
+
+ foreach (cs; EnumMembers!CaseSensitive)
+ {
+ assert(indexOf("hello\U00010143\u0100\U00010143", '\u0100', cs) == 9);
+ assert(indexOf("hello\U00010143\u0100\U00010143"w, '\u0100', cs) == 7);
+ assert(indexOf("hello\U00010143\u0100\U00010143"d, '\u0100', cs) == 6);
+
+ assert(indexOf("hello\U00010143\u0100\U00010143".byChar, '\u0100', cs) == 9);
+ assert(indexOf("hello\U00010143\u0100\U00010143".byWchar, '\u0100', cs) == 7);
+ assert(indexOf("hello\U00010143\u0100\U00010143".byDchar, '\u0100', cs) == 6);
+
+ assert(indexOf("hello\U000007FF\u0100\U00010143".byChar, 'l', cs) == 2);
+ assert(indexOf("hello\U000007FF\u0100\U00010143".byChar, '\u0100', cs) == 7);
+ assert(indexOf("hello\U0000EFFF\u0100\U00010143".byChar, '\u0100', cs) == 8);
+
+ assert(indexOf("hello\U00010100".byWchar, '\U00010100', cs) == 5);
+ assert(indexOf("hello\U00010100".byWchar, '\U00010101', cs) == -1);
+ }
+
+ char[10] fixedSizeArray = "0123456789";
+ assert(indexOf(fixedSizeArray, '2') == 2);
+ });
+}
+
+@safe pure unittest
+{
+ assert(testAliasedString!indexOf("std/string.d", '/', 0));
+ assert(testAliasedString!indexOf("std/string.d", '/', 1));
+ assert(testAliasedString!indexOf("std/string.d", '/', 4));
+
+ enum S : string { a = "std/string.d" }
+ assert(S.a.indexOf('/', 0) == 3);
+ assert(S.a.indexOf('/', 1) == 3);
+ assert(S.a.indexOf('/', 4) == -1);
+
+ char[S.a.length] sa = S.a[];
+ assert(sa.indexOf('/', 0) == 3);
+ assert(sa.indexOf('/', 1) == 3);
+ assert(sa.indexOf('/', 4) == -1);
+}
+
+@safe pure unittest
+{
+ import std.conv : to;
+ import std.traits : EnumMembers;
+ import std.utf : byCodeUnit, byChar, byWchar;
+
+ assert("hello".byCodeUnit.indexOf(cast(dchar)'l', 1) == 2);
+ assert("hello".byWchar.indexOf(cast(dchar)'l', 1) == 2);
+ assert("hello".byWchar.indexOf(cast(dchar)'l', 6) == -1);
+
+ static foreach (S; AliasSeq!(string, wstring, dstring))
+ {{
+ assert(indexOf(cast(S) null, cast(dchar)'a', 1) == -1);
+ assert(indexOf(to!S("def"), cast(dchar)'a', 1) == -1);
+ assert(indexOf(to!S("abba"), cast(dchar)'a', 1) == 3);
+ assert(indexOf(to!S("def"), cast(dchar)'f', 1) == 2);
+
+ assert((to!S("def")).indexOf(cast(dchar)'a', 1,
+ No.caseSensitive) == -1);
+ assert(indexOf(to!S("def"), cast(dchar)'a', 1,
+ No.caseSensitive) == -1);
+ assert(indexOf(to!S("def"), cast(dchar)'a', 12,
+ No.caseSensitive) == -1);
+ assert(indexOf(to!S("AbbA"), cast(dchar)'a', 2,
+ No.caseSensitive) == 3);
+ assert(indexOf(to!S("def"), cast(dchar)'F', 2, No.caseSensitive) == 2);
+
+ S sPlts = "Mars: the fourth Rock (Planet) from the Sun.";
+ assert(indexOf("def", cast(char)'f', cast(uint) 2,
+ No.caseSensitive) == 2);
+ assert(indexOf(sPlts, cast(char)'P', 12, No.caseSensitive) == 23);
+ assert(indexOf(sPlts, cast(char)'R', cast(ulong) 1,
+ No.caseSensitive) == 2);
+ }}
+
+ foreach (cs; EnumMembers!CaseSensitive)
+ {
+ assert(indexOf("hello\U00010143\u0100\U00010143", '\u0100', 2, cs)
+ == 9);
+ assert(indexOf("hello\U00010143\u0100\U00010143"w, '\u0100', 3, cs)
+ == 7);
+ assert(indexOf("hello\U00010143\u0100\U00010143"d, '\u0100', 6, cs)
+ == 6);
+ }
+}
+
+private ptrdiff_t _indexOf(Range)(Range s, dchar c, CaseSensitive cs = Yes.caseSensitive)
+if (isInputRange!Range && isSomeChar!(ElementType!Range))
{
static import std.ascii;
static import std.uni;
@@ -484,11 +779,8 @@ if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) &&
return -1;
}
-/// Ditto
-ptrdiff_t indexOf(Range)(Range s, in dchar c, in size_t startIdx,
- in CaseSensitive cs = Yes.caseSensitive)
-if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) &&
- !isConvertibleToString!Range)
+private ptrdiff_t _indexOf(Range)(Range s, dchar c, size_t startIdx, CaseSensitive cs = Yes.caseSensitive)
+if (isInputRange!Range && isSomeChar!(ElementType!Range))
{
static if (isSomeString!(typeof(s)) ||
(hasSlicing!(typeof(s)) && hasLength!(typeof(s))))
@@ -519,263 +811,165 @@ if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) &&
return -1;
}
-///
-@safe pure unittest
+private template _indexOfStr(CaseSensitive cs)
{
- import std.typecons : No;
-
- string s = "Hello World";
- assert(indexOf(s, 'W') == 6);
- assert(indexOf(s, 'Z') == -1);
- assert(indexOf(s, 'w', No.caseSensitive) == 6);
-}
-
-///
-@safe pure unittest
-{
- import std.typecons : No;
-
- string s = "Hello World";
- assert(indexOf(s, 'W', 4) == 6);
- assert(indexOf(s, 'Z', 100) == -1);
- assert(indexOf(s, 'w', 3, No.caseSensitive) == 6);
-}
-
-ptrdiff_t indexOf(Range)(auto ref Range s, in dchar c,
- in CaseSensitive cs = Yes.caseSensitive)
-if (isConvertibleToString!Range)
-{
- return indexOf!(StringTypeOf!Range)(s, c, cs);
-}
-
-ptrdiff_t indexOf(Range)(auto ref Range s, in dchar c, in size_t startIdx,
- in CaseSensitive cs = Yes.caseSensitive)
-if (isConvertibleToString!Range)
-{
- return indexOf!(StringTypeOf!Range)(s, c, startIdx, cs);
-}
-
-@safe pure unittest
-{
- assert(testAliasedString!indexOf("std/string.d", '/'));
-}
-
-@safe pure unittest
-{
- import std.conv : to;
- import std.exception : assertCTFEable;
- import std.traits : EnumMembers;
- import std.utf : byChar, byWchar, byDchar;
-
- assertCTFEable!(
- {
- foreach (S; AliasSeq!(string, wstring, dstring))
- {
- assert(indexOf(cast(S) null, cast(dchar)'a') == -1);
- assert(indexOf(to!S("def"), cast(dchar)'a') == -1);
- assert(indexOf(to!S("abba"), cast(dchar)'a') == 0);
- assert(indexOf(to!S("def"), cast(dchar)'f') == 2);
-
- assert(indexOf(to!S("def"), cast(dchar)'a', No.caseSensitive) == -1);
- assert(indexOf(to!S("def"), cast(dchar)'a', No.caseSensitive) == -1);
- assert(indexOf(to!S("Abba"), cast(dchar)'a', No.caseSensitive) == 0);
- assert(indexOf(to!S("def"), cast(dchar)'F', No.caseSensitive) == 2);
- assert(indexOf(to!S("ödef"), 'ö', No.caseSensitive) == 0);
-
- S sPlts = "Mars: the fourth Rock (Planet) from the Sun.";
- assert(indexOf("def", cast(char)'f', No.caseSensitive) == 2);
- assert(indexOf(sPlts, cast(char)'P', No.caseSensitive) == 23);
- assert(indexOf(sPlts, cast(char)'R', No.caseSensitive) == 2);
- }
-
- foreach (cs; EnumMembers!CaseSensitive)
+ private ptrdiff_t _indexOfStr(Range, Char)(Range s, const(Char)[] sub)
+ if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) &&
+ isSomeChar!Char)
{
- assert(indexOf("hello\U00010143\u0100\U00010143", '\u0100', cs) == 9);
- assert(indexOf("hello\U00010143\u0100\U00010143"w, '\u0100', cs) == 7);
- assert(indexOf("hello\U00010143\u0100\U00010143"d, '\u0100', cs) == 6);
+ alias Char1 = Unqual!(ElementEncodingType!Range);
- assert(indexOf("hello\U00010143\u0100\U00010143".byChar, '\u0100', cs) == 9);
- assert(indexOf("hello\U00010143\u0100\U00010143".byWchar, '\u0100', cs) == 7);
- assert(indexOf("hello\U00010143\u0100\U00010143".byDchar, '\u0100', cs) == 6);
-
- assert(indexOf("hello\U000007FF\u0100\U00010143".byChar, 'l', cs) == 2);
- assert(indexOf("hello\U000007FF\u0100\U00010143".byChar, '\u0100', cs) == 7);
- assert(indexOf("hello\U0000EFFF\u0100\U00010143".byChar, '\u0100', cs) == 8);
-
- assert(indexOf("hello\U00010100".byWchar, '\U00010100', cs) == 5);
- assert(indexOf("hello\U00010100".byWchar, '\U00010101', cs) == -1);
- }
-
- char[10] fixedSizeArray = "0123456789";
- assert(indexOf(fixedSizeArray, '2') == 2);
- });
-}
-
-@safe pure unittest
-{
- assert(testAliasedString!indexOf("std/string.d", '/', 3));
-}
-
-@safe pure unittest
-{
- import std.conv : to;
- import std.traits : EnumMembers;
- import std.utf : byCodeUnit, byChar, byWchar;
+ static if (isSomeString!Range)
+ {
+ static if (is(Char1 == Char) && cs == Yes.caseSensitive)
+ {
+ import std.algorithm.searching : countUntil;
+ return s.representation.countUntil(sub.representation);
+ }
+ else
+ {
+ import std.algorithm.searching : find;
- assert("hello".byCodeUnit.indexOf(cast(dchar)'l', 1) == 2);
- assert("hello".byWchar.indexOf(cast(dchar)'l', 1) == 2);
- assert("hello".byWchar.indexOf(cast(dchar)'l', 6) == -1);
+ const(Char1)[] balance;
+ static if (cs == Yes.caseSensitive)
+ {
+ balance = find(s, sub);
+ }
+ else
+ {
+ balance = find!
+ ((a, b) => toLower(a) == toLower(b))
+ (s, sub);
+ }
+ return () @trusted { return balance.empty ? -1 : balance.ptr - s.ptr; } ();
+ }
+ }
+ else
+ {
+ if (s.empty)
+ return -1;
+ if (sub.empty)
+ return 0; // degenerate case
- foreach (S; AliasSeq!(string, wstring, dstring))
- {
- assert(indexOf(cast(S) null, cast(dchar)'a', 1) == -1);
- assert(indexOf(to!S("def"), cast(dchar)'a', 1) == -1);
- assert(indexOf(to!S("abba"), cast(dchar)'a', 1) == 3);
- assert(indexOf(to!S("def"), cast(dchar)'f', 1) == 2);
+ import std.utf : byDchar, codeLength;
+ auto subr = sub.byDchar; // decode sub[] by dchar's
+ dchar sub0 = subr.front; // cache first character of sub[]
+ subr.popFront();
- assert((to!S("def")).indexOf(cast(dchar)'a', 1,
- No.caseSensitive) == -1);
- assert(indexOf(to!S("def"), cast(dchar)'a', 1,
- No.caseSensitive) == -1);
- assert(indexOf(to!S("def"), cast(dchar)'a', 12,
- No.caseSensitive) == -1);
- assert(indexOf(to!S("AbbA"), cast(dchar)'a', 2,
- No.caseSensitive) == 3);
- assert(indexOf(to!S("def"), cast(dchar)'F', 2, No.caseSensitive) == 2);
+ // Special case for single character search
+ if (subr.empty)
+ return indexOf(s, sub0, cs);
- S sPlts = "Mars: the fourth Rock (Planet) from the Sun.";
- assert(indexOf("def", cast(char)'f', cast(uint) 2,
- No.caseSensitive) == 2);
- assert(indexOf(sPlts, cast(char)'P', 12, No.caseSensitive) == 23);
- assert(indexOf(sPlts, cast(char)'R', cast(ulong) 1,
- No.caseSensitive) == 2);
- }
+ static if (cs == No.caseSensitive)
+ sub0 = toLower(sub0);
- foreach (cs; EnumMembers!CaseSensitive)
- {
- assert(indexOf("hello\U00010143\u0100\U00010143", '\u0100', 2, cs)
- == 9);
- assert(indexOf("hello\U00010143\u0100\U00010143"w, '\u0100', 3, cs)
- == 7);
- assert(indexOf("hello\U00010143\u0100\U00010143"d, '\u0100', 6, cs)
- == 6);
+ /* Classic double nested loop search algorithm
+ */
+ ptrdiff_t index = 0; // count code unit index into s
+ for (auto sbydchar = s.byDchar(); !sbydchar.empty; sbydchar.popFront())
+ {
+ dchar c2 = sbydchar.front;
+ static if (cs == No.caseSensitive)
+ c2 = toLower(c2);
+ if (c2 == sub0)
+ {
+ auto s2 = sbydchar.save; // why s must be a forward range
+ foreach (c; subr.save)
+ {
+ s2.popFront();
+ if (s2.empty)
+ return -1;
+ static if (cs == Yes.caseSensitive)
+ {
+ if (c != s2.front)
+ goto Lnext;
+ }
+ else
+ {
+ if (toLower(c) != toLower(s2.front))
+ goto Lnext;
+ }
+ }
+ return index;
+ }
+ Lnext:
+ index += codeLength!Char1(c2);
+ }
+ return -1;
+ }
}
}
/++
- Searches for substring in $(D s).
+ Searches for substring in `s`.
Params:
s = string or ForwardRange of characters to search in correct UTF format
sub = substring to search for
startIdx = the index into s to start searching from
- cs = $(D Yes.caseSensitive) or $(D No.caseSensitive)
+ cs = `Yes.caseSensitive` (default) or `No.caseSensitive`
Returns:
- the index of the first occurrence of $(D sub) in $(D s) with
- respect to the start index $(D startIdx). If $(D sub) is not found,
- then $(D -1) is returned.
+ the index of the first occurrence of `sub` in `s` with
+ respect to the start index `startIdx`. If `sub` is not found,
+ then `-1` is returned.
If the arguments are not valid UTF, the result will still
be in the range [-1 .. s.length], but will not be reliable otherwise.
- If $(D sub) is found the value of the returned index is at least
- $(D startIdx).
+ If `sub` is found the value of the returned index is at least
+ `startIdx`.
Throws:
- If the sequence starting at $(D startIdx) does not represent a well
+ If the sequence starting at `startIdx` does not represent a well
formed codepoint, then a $(REF UTFException, std,utf) may be thrown.
Bugs:
Does not work with case insensitive strings where the mapping of
tolower and toupper is not 1:1.
+/
-ptrdiff_t indexOf(Range, Char)(Range s, const(Char)[] sub,
- in CaseSensitive cs = Yes.caseSensitive)
+ptrdiff_t indexOf(Range, Char)(Range s, const(Char)[] sub)
if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) &&
isSomeChar!Char)
{
- alias Char1 = Unqual!(ElementEncodingType!Range);
-
- static if (isSomeString!Range)
- {
- import std.algorithm.searching : find;
+ return _indexOfStr!(Yes.caseSensitive)(s, sub);
+}
- const(Char1)[] balance;
- if (cs == Yes.caseSensitive)
- {
- balance = find(s, sub);
- }
- else
- {
- balance = find!
- ((a, b) => toLower(a) == toLower(b))
- (s, sub);
- }
- return () @trusted { return balance.empty ? -1 : balance.ptr - s.ptr; } ();
- }
+/// Ditto
+ptrdiff_t indexOf(Range, Char)(Range s, const(Char)[] sub, in CaseSensitive cs)
+if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) &&
+ isSomeChar!Char)
+{
+ if (cs == Yes.caseSensitive)
+ return indexOf(s, sub);
else
- {
- if (s.empty)
- return -1;
- if (sub.empty)
- return 0; // degenerate case
-
- import std.utf : byDchar, codeLength;
- auto subr = sub.byDchar; // decode sub[] by dchar's
- dchar sub0 = subr.front; // cache first character of sub[]
- subr.popFront();
-
- // Special case for single character search
- if (subr.empty)
- return indexOf(s, sub0, cs);
-
- if (cs == No.caseSensitive)
- sub0 = toLower(sub0);
+ return _indexOfStr!(No.caseSensitive)(s, sub);
+}
- /* Classic double nested loop search algorithm
- */
- ptrdiff_t index = 0; // count code unit index into s
- for (auto sbydchar = s.byDchar(); !sbydchar.empty; sbydchar.popFront())
- {
- dchar c2 = sbydchar.front;
- if (cs == No.caseSensitive)
- c2 = toLower(c2);
- if (c2 == sub0)
- {
- auto s2 = sbydchar.save; // why s must be a forward range
- foreach (c; subr.save)
- {
- s2.popFront();
- if (s2.empty)
- return -1;
- if (cs == Yes.caseSensitive ? c != s2.front
- : toLower(c) != toLower(s2.front)
- )
- goto Lnext;
- }
- return index;
- }
- Lnext:
- index += codeLength!Char1(c2);
- }
+/// Ditto
+ptrdiff_t indexOf(Char1, Char2)(const(Char1)[] s, const(Char2)[] sub,
+ in size_t startIdx)
+@safe
+if (isSomeChar!Char1 && isSomeChar!Char2)
+{
+ if (startIdx >= s.length)
return -1;
- }
+ ptrdiff_t foundIdx = indexOf(s[startIdx .. $], sub);
+ if (foundIdx == -1)
+ return -1;
+ return foundIdx + cast(ptrdiff_t) startIdx;
}
/// Ditto
ptrdiff_t indexOf(Char1, Char2)(const(Char1)[] s, const(Char2)[] sub,
- in size_t startIdx, in CaseSensitive cs = Yes.caseSensitive)
+ in size_t startIdx, in CaseSensitive cs)
@safe
if (isSomeChar!Char1 && isSomeChar!Char2)
{
- if (startIdx < s.length)
- {
- ptrdiff_t foundIdx = indexOf(s[startIdx .. $], sub, cs);
- if (foundIdx != -1)
- {
- return foundIdx + cast(ptrdiff_t) startIdx;
- }
- }
- return -1;
+ if (startIdx >= s.length)
+ return -1;
+ ptrdiff_t foundIdx = indexOf(s[startIdx .. $], sub, cs);
+ if (foundIdx == -1)
+ return -1;
+ return foundIdx + cast(ptrdiff_t) startIdx;
}
///
@@ -800,8 +994,25 @@ if (isSomeChar!Char1 && isSomeChar!Char2)
assert(indexOf(s, "wO", No.caseSensitive) == 6);
}
+@safe pure nothrow @nogc unittest
+{
+ string s = "Hello World";
+ assert(indexOf(s, "Wo", 4) == 6);
+ assert(indexOf(s, "Zo", 100) == -1);
+ assert(indexOf(s, "Wo") == 6);
+ assert(indexOf(s, "Zo") == -1);
+}
+
+ptrdiff_t indexOf(Range, Char)(auto ref Range s, const(Char)[] sub)
+if (!(isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) &&
+ isSomeChar!Char) &&
+ is(StringTypeOf!Range))
+{
+ return indexOf!(StringTypeOf!Range)(s, sub);
+}
+
ptrdiff_t indexOf(Range, Char)(auto ref Range s, const(Char)[] sub,
- in CaseSensitive cs = Yes.caseSensitive)
+ in CaseSensitive cs)
if (!(isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) &&
isSomeChar!Char) &&
is(StringTypeOf!Range))
@@ -809,7 +1020,7 @@ if (!(isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) &&
return indexOf!(StringTypeOf!Range)(s, sub, cs);
}
-@safe pure unittest
+@safe pure nothrow @nogc unittest
{
assert(testAliasedString!indexOf("std/string.d", "string"));
}
@@ -822,10 +1033,10 @@ if (!(isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) &&
assertCTFEable!(
{
- foreach (S; AliasSeq!(string, wstring, dstring))
+ static foreach (S; AliasSeq!(string, wstring, dstring))
{
- foreach (T; AliasSeq!(string, wstring, dstring))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(string, wstring, dstring))
+ {{
assert(indexOf(cast(S) null, to!T("a")) == -1);
assert(indexOf(to!S("def"), to!T("a")) == -1);
assert(indexOf(to!S("abba"), to!T("a")) == 0);
@@ -855,7 +1066,7 @@ if (!(isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) &&
// Thanks to Carlos Santander B. and zwang
assert(indexOf("sus mejores cortesanos. Se embarcaron en el puerto de Dubai y",
to!T("page-break-before"), No.caseSensitive) == -1);
- }();
+ }}
foreach (cs; EnumMembers!CaseSensitive)
{
@@ -890,10 +1101,10 @@ unittest
import std.conv : to;
import std.traits : EnumMembers;
- foreach (S; AliasSeq!(string, wstring, dstring))
+ static foreach (S; AliasSeq!(string, wstring, dstring))
{
- foreach (T; AliasSeq!(string, wstring, dstring))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(string, wstring, dstring))
+ {{
assert(indexOf(cast(S) null, to!T("a"), 1337) == -1);
assert(indexOf(to!S("def"), to!T("a"), 0) == -1);
assert(indexOf(to!S("abba"), to!T("a"), 2) == 3);
@@ -930,7 +1141,7 @@ unittest
// In order for indexOf with and without index to be consistent
assert(indexOf(to!S(""), to!T("")) == indexOf(to!S(""), to!T(""), 0));
- }();
+ }}
foreach (cs; EnumMembers!CaseSensitive)
{
@@ -949,19 +1160,19 @@ unittest
s = string to search
c = character to search for
startIdx = the index into s to start searching from
- cs = $(D Yes.caseSensitive) or $(D No.caseSensitive)
+ cs = `Yes.caseSensitive` or `No.caseSensitive`
Returns:
- The index of the last occurrence of $(D c) in $(D s). If $(D c) is not
- found, then $(D -1) is returned. The $(D startIdx) slices $(D s) in
- the following way $(D s[0 .. startIdx]). $(D startIdx) represents a
- codeunit index in $(D s).
+ The index of the last occurrence of `c` in `s`. If `c` is not
+ found, then `-1` is returned. The `startIdx` slices `s` in
+ the following way $(D s[0 .. startIdx]). `startIdx` represents a
+ codeunit index in `s`.
Throws:
- If the sequence ending at $(D startIdx) does not represent a well
+ If the sequence ending at `startIdx` does not represent a well
formed codepoint, then a $(REF UTFException, std,utf) may be thrown.
- $(D cs) indicates whether the comparisons are case sensitive.
+ `cs` indicates whether the comparisons are case sensitive.
+/
ptrdiff_t lastIndexOf(Char)(const(Char)[] s, in dchar c,
in CaseSensitive cs = Yes.caseSensitive) @safe pure
@@ -1068,8 +1279,8 @@ if (isSomeChar!Char)
assertCTFEable!(
{
- foreach (S; AliasSeq!(string, wstring, dstring))
- {
+ static foreach (S; AliasSeq!(string, wstring, dstring))
+ {{
assert(lastIndexOf(cast(S) null, 'a') == -1);
assert(lastIndexOf(to!S("def"), 'a') == -1);
assert(lastIndexOf(to!S("abba"), 'a') == 3);
@@ -1089,7 +1300,7 @@ if (isSomeChar!Char)
assert(lastIndexOf(to!S("def"), 'f', No.caseSensitive) == 2);
assert(lastIndexOf(sPlts, 'M', No.caseSensitive) == 34);
assert(lastIndexOf(sPlts, 'S', No.caseSensitive) == 40);
- }
+ }}
foreach (cs; EnumMembers!CaseSensitive)
{
@@ -1105,8 +1316,8 @@ if (isSomeChar!Char)
import std.conv : to;
import std.traits : EnumMembers;
- foreach (S; AliasSeq!(string, wstring, dstring))
- {
+ static foreach (S; AliasSeq!(string, wstring, dstring))
+ {{
assert(lastIndexOf(cast(S) null, 'a') == -1);
assert(lastIndexOf(to!S("def"), 'a') == -1);
assert(lastIndexOf(to!S("abba"), 'a', 3) == 0);
@@ -1123,7 +1334,7 @@ if (isSomeChar!Char)
assert(lastIndexOf(to!S("def"), 'f', 4, No.caseSensitive) == -1);
assert(lastIndexOf(sPlts, 'M', sPlts.length -2, No.caseSensitive) == 34);
assert(lastIndexOf(sPlts, 'S', sPlts.length -2, No.caseSensitive) == 40);
- }
+ }}
foreach (cs; EnumMembers!CaseSensitive)
{
@@ -1138,19 +1349,19 @@ if (isSomeChar!Char)
s = string to search
sub = substring to search for
startIdx = the index into s to start searching from
- cs = $(D Yes.caseSensitive) or $(D No.caseSensitive)
+ cs = `Yes.caseSensitive` or `No.caseSensitive`
Returns:
- the index of the last occurrence of $(D sub) in $(D s). If $(D sub) is
- not found, then $(D -1) is returned. The $(D startIdx) slices $(D s)
- in the following way $(D s[0 .. startIdx]). $(D startIdx) represents a
- codeunit index in $(D s).
+ the index of the last occurrence of `sub` in `s`. If `sub` is
+ not found, then `-1` is returned. The `startIdx` slices `s`
+ in the following way $(D s[0 .. startIdx]). `startIdx` represents a
+ codeunit index in `s`.
Throws:
- If the sequence ending at $(D startIdx) does not represent a well
+ If the sequence ending at `startIdx` does not represent a well
formed codepoint, then a $(REF UTFException, std,utf) may be thrown.
- $(D cs) indicates whether the comparisons are case sensitive.
+ `cs` indicates whether the comparisons are case sensitive.
+/
ptrdiff_t lastIndexOf(Char1, Char2)(const(Char1)[] s, const(Char2)[] sub,
in CaseSensitive cs = Yes.caseSensitive) @safe pure
@@ -1169,7 +1380,7 @@ if (isSomeChar!Char1 && isSomeChar!Char2)
if (cs == Yes.caseSensitive)
{
- static if (is(Unqual!Char1 == Unqual!Char2))
+ static if (is(immutable Char1 == immutable Char2))
{
import core.stdc.string : memcmp;
@@ -1181,12 +1392,8 @@ if (isSomeChar!Char1 && isSomeChar!Char2)
{
if (__ctfe)
{
- foreach (j; 1 .. sub.length)
- {
- if (s[i + j] != sub[j])
- continue;
- }
- return i;
+ if (s[i + 1 .. i + sub.length] == sub[1 .. $])
+ return i;
}
else
{
@@ -1270,8 +1477,8 @@ if (isSomeChar!Char1 && isSomeChar!Char2)
{
import std.conv : to;
- foreach (S; AliasSeq!(string, wstring, dstring))
- {
+ static foreach (S; AliasSeq!(string, wstring, dstring))
+ {{
auto r = to!S("").lastIndexOf("hello");
assert(r == -1, to!string(r));
@@ -1280,7 +1487,7 @@ if (isSomeChar!Char1 && isSomeChar!Char2)
r = to!S("").lastIndexOf("");
assert(r == -1, to!string(r));
- }
+ }}
}
@safe pure unittest
@@ -1291,10 +1498,10 @@ if (isSomeChar!Char1 && isSomeChar!Char2)
assertCTFEable!(
{
- foreach (S; AliasSeq!(string, wstring, dstring))
+ static foreach (S; AliasSeq!(string, wstring, dstring))
{
- foreach (T; AliasSeq!(string, wstring, dstring))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(string, wstring, dstring))
+ {{
enum typeStr = S.stringof ~ " " ~ T.stringof;
assert(lastIndexOf(cast(S) null, to!T("a")) == -1, typeStr);
@@ -1329,7 +1536,7 @@ if (isSomeChar!Char1 && isSomeChar!Char2)
assert(lastIndexOf(sPlts, to!T("FOuRTh"), No.caseSensitive) == 10, typeStr);
assert(lastIndexOf(sMars, to!T("whO\'s \'MY"), No.caseSensitive) == 0, typeStr);
assert(lastIndexOf(sMars, to!T(sMars), No.caseSensitive) == 0, typeStr);
- }();
+ }}
foreach (cs; EnumMembers!CaseSensitive)
{
@@ -1343,20 +1550,21 @@ if (isSomeChar!Char1 && isSomeChar!Char2)
});
}
-@safe pure unittest // issue13529
+// https://issues.dlang.org/show_bug.cgi?id=13529
+@safe pure unittest
{
import std.conv : to;
- foreach (S; AliasSeq!(string, wstring, dstring))
+ static foreach (S; AliasSeq!(string, wstring, dstring))
{
- foreach (T; AliasSeq!(string, wstring, dstring))
- {
+ static foreach (T; AliasSeq!(string, wstring, dstring))
+ {{
enum typeStr = S.stringof ~ " " ~ T.stringof;
auto idx = lastIndexOf(to!T("Hällö Wörldö ö"),to!S("ö ö"));
assert(idx != -1, to!string(idx) ~ " " ~ typeStr);
idx = lastIndexOf(to!T("Hällö Wörldö ö"),to!S("ö öd"));
assert(idx == -1, to!string(idx) ~ " " ~ typeStr);
- }
+ }}
}
}
@@ -1365,10 +1573,10 @@ if (isSomeChar!Char1 && isSomeChar!Char2)
import std.conv : to;
import std.traits : EnumMembers;
- foreach (S; AliasSeq!(string, wstring, dstring))
+ static foreach (S; AliasSeq!(string, wstring, dstring))
{
- foreach (T; AliasSeq!(string, wstring, dstring))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(string, wstring, dstring))
+ {{
enum typeStr = S.stringof ~ " " ~ T.stringof;
assert(lastIndexOf(cast(S) null, to!T("a")) == -1, typeStr);
@@ -1396,7 +1604,7 @@ if (isSomeChar!Char1 && isSomeChar!Char2)
assert(lastIndexOf(to!S("abcdefcdef"), to!T("cd"), 4, No.caseSensitive) == 2, typeStr);
assert(lastIndexOf(to!S("abcdefcdef"), to!T("def"), 6, No.caseSensitive) == 3, typeStr);
assert(lastIndexOf(to!S(""), to!T(""), 0) == lastIndexOf(to!S(""), to!T("")), typeStr);
- }();
+ }}
foreach (cs; EnumMembers!CaseSensitive)
{
@@ -1409,6 +1617,19 @@ if (isSomeChar!Char1 && isSomeChar!Char2)
}
}
+// https://issues.dlang.org/show_bug.cgi?id=20783
+@safe pure @nogc unittest
+{
+ enum lastIndex = "aa".lastIndexOf("ab");
+ assert(lastIndex == -1);
+}
+
+@safe pure @nogc unittest
+{
+ enum lastIndex = "hello hello hell h".lastIndexOf("hello");
+ assert(lastIndex == 6);
+}
+
private ptrdiff_t indexOfAnyNeitherImpl(bool forward, bool any, Char, Char2)(
const(Char)[] haystack, const(Char2)[] needles,
in CaseSensitive cs = Yes.caseSensitive) @safe pure
@@ -1527,10 +1748,10 @@ if (isSomeChar!Char && isSomeChar!Char2)
/**
Returns the index of the first occurrence of any of the elements in $(D
- needles) in $(D haystack). If no element of $(D needles) is found,
- then $(D -1) is returned. The $(D startIdx) slices $(D haystack) in the
- following way $(D haystack[startIdx .. $]). $(D startIdx) represents a
- codeunit index in $(D haystack). If the sequence ending at $(D startIdx)
+ needles) in `haystack`. If no element of `needles` is found,
+ then `-1` is returned. The `startIdx` slices `haystack` in the
+ following way $(D haystack[startIdx .. $]). `startIdx` represents a
+ codeunit index in `haystack`. If the sequence ending at `startIdx`
does not represent a well formed codepoint, then a $(REF UTFException, std,utf)
may be thrown.
@@ -1539,7 +1760,7 @@ if (isSomeChar!Char && isSomeChar!Char2)
needles = Strings to search for in haystack.
startIdx = slices haystack like this $(D haystack[startIdx .. $]). If
the startIdx is greater equal the length of haystack the functions
- returns $(D -1).
+ returns `-1`.
cs = Indicates whether the comparisons are case sensitive.
*/
ptrdiff_t indexOfAny(Char,Char2)(const(Char)[] haystack, const(Char2)[] needles,
@@ -1593,8 +1814,8 @@ if (isSomeChar!Char && isSomeChar!Char2)
{
import std.conv : to;
- foreach (S; AliasSeq!(string, wstring, dstring))
- {
+ static foreach (S; AliasSeq!(string, wstring, dstring))
+ {{
auto r = to!S("").indexOfAny("hello");
assert(r == -1, to!string(r));
@@ -1603,7 +1824,7 @@ if (isSomeChar!Char && isSomeChar!Char2)
r = to!S("").indexOfAny("");
assert(r == -1, to!string(r));
- }
+ }}
}
@safe pure unittest
@@ -1613,10 +1834,10 @@ if (isSomeChar!Char && isSomeChar!Char2)
assertCTFEable!(
{
- foreach (S; AliasSeq!(string, wstring, dstring))
+ static foreach (S; AliasSeq!(string, wstring, dstring))
{
- foreach (T; AliasSeq!(string, wstring, dstring))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(string, wstring, dstring))
+ {
assert(indexOfAny(cast(S) null, to!T("a")) == -1);
assert(indexOfAny(to!S("def"), to!T("rsa")) == -1);
assert(indexOfAny(to!S("abba"), to!T("a")) == 0);
@@ -1639,7 +1860,7 @@ if (isSomeChar!Char && isSomeChar!Char2)
No.caseSensitive) == 0);
assert(indexOfAny("\u0100", to!T("\u0100"), No.caseSensitive) == 0);
- }();
+ }
}
}
);
@@ -1650,10 +1871,10 @@ if (isSomeChar!Char && isSomeChar!Char2)
import std.conv : to;
import std.traits : EnumMembers;
- foreach (S; AliasSeq!(string, wstring, dstring))
+ static foreach (S; AliasSeq!(string, wstring, dstring))
{
- foreach (T; AliasSeq!(string, wstring, dstring))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(string, wstring, dstring))
+ {
assert(indexOfAny(cast(S) null, to!T("a"), 1337) == -1);
assert(indexOfAny(to!S("def"), to!T("AaF"), 0) == -1);
assert(indexOfAny(to!S("abba"), to!T("NSa"), 2) == 3);
@@ -1678,7 +1899,7 @@ if (isSomeChar!Char && isSomeChar!Char2)
assert(indexOfAny("\u0100", to!T("\u0100"), 0,
No.caseSensitive) == 0);
- }();
+ }
foreach (cs; EnumMembers!CaseSensitive)
{
@@ -1694,10 +1915,10 @@ if (isSomeChar!Char && isSomeChar!Char2)
/**
Returns the index of the last occurrence of any of the elements in $(D
- needles) in $(D haystack). If no element of $(D needles) is found,
- then $(D -1) is returned. The $(D stopIdx) slices $(D haystack) in the
- following way $(D s[0 .. stopIdx]). $(D stopIdx) represents a codeunit
- index in $(D haystack). If the sequence ending at $(D startIdx) does not
+ needles) in `haystack`. If no element of `needles` is found,
+ then `-1` is returned. The `stopIdx` slices `haystack` in the
+ following way $(D s[0 .. stopIdx]). `stopIdx` represents a codeunit
+ index in `haystack`. If the sequence ending at `startIdx` does not
represent a well formed codepoint, then a $(REF UTFException, std,utf) may be
thrown.
@@ -1706,7 +1927,7 @@ if (isSomeChar!Char && isSomeChar!Char2)
needles = Strings to search for in haystack.
stopIdx = slices haystack like this $(D haystack[0 .. stopIdx]). If
the stopIdx is greater equal the length of haystack the functions
- returns $(D -1).
+ returns `-1`.
cs = Indicates whether the comparisons are case sensitive.
*/
ptrdiff_t lastIndexOfAny(Char,Char2)(const(Char)[] haystack,
@@ -1757,8 +1978,8 @@ if (isSomeChar!Char && isSomeChar!Char2)
{
import std.conv : to;
- foreach (S; AliasSeq!(string, wstring, dstring))
- {
+ static foreach (S; AliasSeq!(string, wstring, dstring))
+ {{
auto r = to!S("").lastIndexOfAny("hello");
assert(r == -1, to!string(r));
@@ -1767,7 +1988,7 @@ if (isSomeChar!Char && isSomeChar!Char2)
r = to!S("").lastIndexOfAny("");
assert(r == -1, to!string(r));
- }
+ }}
}
@safe pure unittest
@@ -1777,10 +1998,10 @@ if (isSomeChar!Char && isSomeChar!Char2)
assertCTFEable!(
{
- foreach (S; AliasSeq!(string, wstring, dstring))
+ static foreach (S; AliasSeq!(string, wstring, dstring))
{
- foreach (T; AliasSeq!(string, wstring, dstring))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(string, wstring, dstring))
+ {{
assert(lastIndexOfAny(cast(S) null, to!T("a")) == -1);
assert(lastIndexOfAny(to!S("def"), to!T("rsa")) == -1);
assert(lastIndexOfAny(to!S("abba"), to!T("a")) == 3);
@@ -1817,7 +2038,7 @@ if (isSomeChar!Char && isSomeChar!Char2)
assert(lastIndexOfAny("\u0100", to!T("\u0100"),
No.caseSensitive) == 0);
- }();
+ }}
}
}
);
@@ -1830,10 +2051,10 @@ if (isSomeChar!Char && isSomeChar!Char2)
assertCTFEable!(
{
- foreach (S; AliasSeq!(string, wstring, dstring))
+ static foreach (S; AliasSeq!(string, wstring, dstring))
{
- foreach (T; AliasSeq!(string, wstring, dstring))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(string, wstring, dstring))
+ {{
enum typeStr = S.stringof ~ " " ~ T.stringof;
assert(lastIndexOfAny(cast(S) null, to!T("a"), 1337) == -1,
@@ -1869,7 +2090,7 @@ if (isSomeChar!Char && isSomeChar!Char2)
No.caseSensitive) == -1, typeStr);
assert(lastIndexOfAny(to!S("ÖABCDEFCDEF"), to!T("ö"), 2,
No.caseSensitive) == 0, typeStr);
- }();
+ }}
}
}
);
@@ -1877,15 +2098,15 @@ if (isSomeChar!Char && isSomeChar!Char2)
/**
Returns the index of the first occurrence of any character not an elements
- in $(D needles) in $(D haystack). If all element of $(D haystack) are
- element of $(D needles) $(D -1) is returned.
+ in `needles` in `haystack`. If all element of `haystack` are
+ element of `needles` `-1` is returned.
Params:
haystack = String to search for needles in.
needles = Strings to search for in haystack.
startIdx = slices haystack like this $(D haystack[startIdx .. $]). If
the startIdx is greater equal the length of haystack the functions
- returns $(D -1).
+ returns `-1`.
cs = Indicates whether the comparisons are case sensitive.
*/
ptrdiff_t indexOfNeither(Char,Char2)(const(Char)[] haystack,
@@ -1935,8 +2156,8 @@ if (isSomeChar!Char && isSomeChar!Char2)
{
import std.conv : to;
- foreach (S; AliasSeq!(string, wstring, dstring))
- {
+ static foreach (S; AliasSeq!(string, wstring, dstring))
+ {{
auto r = to!S("").indexOfNeither("hello");
assert(r == -1, to!string(r));
@@ -1945,7 +2166,7 @@ if (isSomeChar!Char && isSomeChar!Char2)
r = to!S("").indexOfNeither("");
assert(r == -1, to!string(r));
- }
+ }}
}
@safe pure unittest
@@ -1955,10 +2176,10 @@ if (isSomeChar!Char && isSomeChar!Char2)
assertCTFEable!(
{
- foreach (S; AliasSeq!(string, wstring, dstring))
+ static foreach (S; AliasSeq!(string, wstring, dstring))
{
- foreach (T; AliasSeq!(string, wstring, dstring))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(string, wstring, dstring))
+ {
assert(indexOfNeither(cast(S) null, to!T("a")) == -1);
assert(indexOfNeither("abba", "a") == 1);
@@ -1986,7 +2207,7 @@ if (isSomeChar!Char && isSomeChar!Char2)
to!string(indexOfNeither(to!S("äDfEfffg"), to!T("ädFe"),
No.caseSensitive)));
}
- }();
+ }
}
}
);
@@ -1999,10 +2220,10 @@ if (isSomeChar!Char && isSomeChar!Char2)
assertCTFEable!(
{
- foreach (S; AliasSeq!(string, wstring, dstring))
+ static foreach (S; AliasSeq!(string, wstring, dstring))
{
- foreach (T; AliasSeq!(string, wstring, dstring))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(string, wstring, dstring))
+ {
assert(indexOfNeither(cast(S) null, to!T("a"), 1) == -1);
assert(indexOfNeither(to!S("def"), to!T("a"), 1) == 1,
to!string(indexOfNeither(to!S("def"), to!T("a"), 1)));
@@ -2029,7 +2250,7 @@ if (isSomeChar!Char && isSomeChar!Char2)
No.caseSensitive) == 2, to!string(indexOfNeither(
to!S("öDfEfffg"), to!T("äDi"), 2, No.caseSensitive)));
}
- }();
+ }
}
}
);
@@ -2037,15 +2258,15 @@ if (isSomeChar!Char && isSomeChar!Char2)
/**
Returns the last index of the first occurence of any character that is not
- an elements in $(D needles) in $(D haystack). If all element of
- $(D haystack) are element of $(D needles) $(D -1) is returned.
+ an elements in `needles` in `haystack`. If all element of
+ `haystack` are element of `needles` `-1` is returned.
Params:
haystack = String to search for needles in.
needles = Strings to search for in haystack.
stopIdx = slices haystack like this $(D haystack[0 .. stopIdx]) If
the stopIdx is greater equal the length of haystack the functions
- returns $(D -1).
+ returns `-1`.
cs = Indicates whether the comparisons are case sensitive.
*/
ptrdiff_t lastIndexOfNeither(Char,Char2)(const(Char)[] haystack,
@@ -2089,8 +2310,8 @@ if (isSomeChar!Char && isSomeChar!Char2)
{
import std.conv : to;
- foreach (S; AliasSeq!(string, wstring, dstring))
- {
+ static foreach (S; AliasSeq!(string, wstring, dstring))
+ {{
auto r = to!S("").lastIndexOfNeither("hello");
assert(r == -1, to!string(r));
@@ -2099,7 +2320,7 @@ if (isSomeChar!Char && isSomeChar!Char2)
r = to!S("").lastIndexOfNeither("");
assert(r == -1, to!string(r));
- }
+ }}
}
@safe pure unittest
@@ -2109,10 +2330,10 @@ if (isSomeChar!Char && isSomeChar!Char2)
assertCTFEable!(
{
- foreach (S; AliasSeq!(string, wstring, dstring))
+ static foreach (S; AliasSeq!(string, wstring, dstring))
{
- foreach (T; AliasSeq!(string, wstring, dstring))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(string, wstring, dstring))
+ {{
assert(lastIndexOfNeither(cast(S) null, to!T("a")) == -1);
assert(lastIndexOfNeither(to!S("def"), to!T("rsa")) == 2);
assert(lastIndexOfNeither(to!S("dfefffg"), to!T("fgh")) == 2);
@@ -2141,7 +2362,7 @@ if (isSomeChar!Char && isSomeChar!Char2)
assert(lastIndexOfNeither(to!S("dfeffgfffö"), to!T("BNDabCHIJKQEPÖÖSYXÄ??ß"),
No.caseSensitive) == 8, to!string(lastIndexOfNeither(to!S("dfeffgfffö"),
to!T("BNDabCHIJKQEPÖÖSYXÄ??ß"), No.caseSensitive)));
- }();
+ }}
}
}
);
@@ -2154,10 +2375,10 @@ if (isSomeChar!Char && isSomeChar!Char2)
assertCTFEable!(
{
- foreach (S; AliasSeq!(string, wstring, dstring))
+ static foreach (S; AliasSeq!(string, wstring, dstring))
{
- foreach (T; AliasSeq!(string, wstring, dstring))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(string, wstring, dstring))
+ {{
assert(lastIndexOfNeither(cast(S) null, to!T("a"), 1337) == -1);
assert(lastIndexOfNeither(to!S("def"), to!T("f")) == 1);
assert(lastIndexOfNeither(to!S("dfefffg"), to!T("fgh")) == 2);
@@ -2185,7 +2406,7 @@ if (isSomeChar!Char && isSomeChar!Char2)
assert(lastIndexOfNeither(to!S("dfefffg"), to!T("NSA"), 2,
No.caseSensitive) == 1, to!string(lastIndexOfNeither(
to!S("dfefffg"), to!T("NSA"), 2, No.caseSensitive)));
- }();
+ }}
}
}
);
@@ -2193,8 +2414,8 @@ if (isSomeChar!Char && isSomeChar!Char2)
/**
* Returns the _representation of a string, which has the same type
- * as the string except the character type is replaced by $(D ubyte),
- * $(D ushort), or $(D uint) depending on the character width.
+ * as the string except the character type is replaced by `ubyte`,
+ * `ushort`, or `uint` depending on the character width.
*
* Params:
* s = The string to return the _representation of.
@@ -2233,10 +2454,10 @@ if (isSomeChar!Char)
assert(representation(str) is cast(T[]) str);
}
- foreach (Type; AliasSeq!(Tuple!(char , ubyte ),
+ static foreach (Type; AliasSeq!(Tuple!(char , ubyte ),
Tuple!(wchar, ushort),
Tuple!(dchar, uint )))
- {
+ {{
alias Char = Fields!Type[0];
alias Int = Fields!Type[1];
enum immutable(Char)[] hello = "hello";
@@ -2246,13 +2467,13 @@ if (isSomeChar!Char)
test!( Char, Int)(hello.dup);
test!( shared Char, shared Int)(cast(shared) hello.dup);
test!(const shared Char, const shared Int)(hello);
- }
+ }}
});
}
/**
- * Capitalize the first character of $(D s) and convert the rest of $(D s) to
+ * Capitalize the first character of `s` and convert the rest of `s` to
* lowercase.
*
* Params:
@@ -2300,8 +2521,8 @@ if (!isSomeString!S && is(StringTypeOf!S))
assertCTFEable!(
{
- foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[]))
- {
+ static foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[]))
+ {{
S s1 = to!S("FoL");
S s2;
@@ -2325,15 +2546,15 @@ if (!isSomeString!S && is(StringTypeOf!S))
s2 = capitalize(s1);
assert(cmp(s2, "\u0053 \u0069") == 0);
assert(s2 !is s1);
- }
+ }}
});
}
/++
- Split $(D s) into an array of lines according to the unicode standard using
- $(D '\r'), $(D '\n'), $(D "\r\n"), $(REF lineSep, std,uni),
- $(REF paraSep, std,uni), $(D U+0085) (NEL), $(D '\v') and $(D '\f')
- as delimiters. If $(D keepTerm) is set to $(D KeepTerminator.yes), then the
+ Split `s` into an array of lines according to the unicode standard using
+ `'\r'`, `'\n'`, `"\r\n"`, $(REF lineSep, std,uni),
+ $(REF paraSep, std,uni), `U+0085` (NEL), `'\v'` and `'\f'`
+ as delimiters. If `keepTerm` is set to `KeepTerminator.yes`, then the
delimiter is included in the strings returned.
Does not throw on invalid UTF; such is simply passed unchanged
@@ -2345,11 +2566,11 @@ if (!isSomeString!S && is(StringTypeOf!S))
Adheres to $(HTTP www.unicode.org/versions/Unicode7.0.0/ch05.pdf, Unicode 7.0).
Params:
- s = a string of $(D chars), $(D wchars), or $(D dchars), or any custom
- type that casts to a $(D string) type
+ s = a string of `chars`, `wchars`, or `dchars`, or any custom
+ type that casts to a `string` type
keepTerm = whether delimiter is included or not in the results
Returns:
- array of strings, each element is a line that is a slice of $(D s)
+ array of strings, each element is a line that is a slice of `s`
See_Also:
$(LREF lineSplitter)
$(REF splitter, std,algorithm)
@@ -2358,14 +2579,14 @@ if (!isSomeString!S && is(StringTypeOf!S))
alias KeepTerminator = Flag!"keepTerminator";
/// ditto
-S[] splitLines(S)(S s, in KeepTerminator keepTerm = No.keepTerminator) @safe pure
-if (isSomeString!S)
+C[][] splitLines(C)(C[] s, KeepTerminator keepTerm = No.keepTerminator) @safe pure
+if (isSomeChar!C)
{
import std.array : appender;
import std.uni : lineSep, paraSep;
size_t iStart = 0;
- auto retval = appender!(S[])();
+ auto retval = appender!(C[][])();
for (size_t i; i < s.length; ++i)
{
@@ -2454,15 +2675,19 @@ if (isSomeString!S)
assert(splitLines(s) == [s]);
}
-auto splitLines(S)(auto ref S s, in KeepTerminator keepTerm = No.keepTerminator)
-if (!isSomeString!S && is(StringTypeOf!S))
+@safe pure nothrow unittest
{
- return splitLines!(StringTypeOf!S)(s, keepTerm);
+ assert(testAliasedString!splitLines("hello\nworld"));
+
+ enum S : string { a = "hello\nworld" }
+ assert(S.a.splitLines() == ["hello", "world"]);
}
-@safe pure nothrow unittest
+@system pure nothrow unittest
{
- assert(testAliasedString!splitLines("hello\nworld"));
+ // dip1000 cannot express an array of scope arrays, so this is not @safe
+ char[11] sa = "hello\nworld";
+ assert(sa.splitLines() == ["hello", "world"]);
}
@safe pure unittest
@@ -2472,8 +2697,8 @@ if (!isSomeString!S && is(StringTypeOf!S))
assertCTFEable!(
{
- foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
- {
+ static foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
+ {{
auto s = to!S(
"\rpeter\n\rpaul\r\njerry\u2028ice\u2029cream\n\nsunday\n" ~
"mon\u2030day\nschadenfreude\vkindergarten\f\vcookies\u0085"
@@ -2525,7 +2750,7 @@ if (!isSomeString!S && is(StringTypeOf!S))
lines = splitLines(s, Yes.keepTerminator);
assert(lines.length == 14);
assert(lines[13] == "cookies");
- }
+ }}
});
}
@@ -2644,7 +2869,7 @@ public:
{
if (iStart == _unComputed)
{
- assert(!empty);
+ assert(!empty, "Can not popFront an empty range");
front;
}
iStart = _unComputed;
@@ -2663,9 +2888,9 @@ public:
/***********************************
* Split an array or slicable range of characters into a range of lines
- using $(D '\r'), $(D '\n'), $(D '\v'), $(D '\f'), $(D "\r\n"),
- $(REF lineSep, std,uni), $(REF paraSep, std,uni) and $(D '\u0085') (NEL)
- as delimiters. If $(D keepTerm) is set to $(D Yes.keepTerminator), then the
+ using `'\r'`, `'\n'`, `'\v'`, `'\f'`, `"\r\n"`,
+ $(REF lineSep, std,uni), $(REF paraSep, std,uni) and `'\u0085'` (NEL)
+ as delimiters. If `keepTerm` is set to `Yes.keepTerminator`, then the
delimiter is included in the slices returned.
Does not throw on invalid UTF; such is simply passed unchanged
@@ -2676,10 +2901,10 @@ public:
Does not allocate memory.
Params:
- r = array of $(D chars), $(D wchars), or $(D dchars) or a slicable range
+ r = array of `chars`, `wchars`, or `dchars` or a slicable range
keepTerm = whether delimiter is included or not in the results
Returns:
- range of slices of the input range $(D r)
+ range of slices of the input range `r`
See_Also:
$(LREF splitLines)
@@ -2687,13 +2912,18 @@ public:
$(REF splitter, std,regex)
*/
auto lineSplitter(KeepTerminator keepTerm = No.keepTerminator, Range)(Range r)
-if ((hasSlicing!Range && hasLength!Range && isSomeChar!(ElementType!Range) ||
- isSomeString!Range) &&
- !isConvertibleToString!Range)
+if (hasSlicing!Range && hasLength!Range && isSomeChar!(ElementType!Range) && !isSomeString!Range)
{
return LineSplitter!(keepTerm, Range)(r);
}
+/// Ditto
+auto lineSplitter(KeepTerminator keepTerm = No.keepTerminator, C)(C[] r)
+if (isSomeChar!C)
+{
+ return LineSplitter!(keepTerm, C[])(r);
+}
+
///
@safe pure unittest
{
@@ -2707,12 +2937,6 @@ if ((hasSlicing!Range && hasLength!Range && isSomeChar!(ElementType!Range) ||
assert(lineSplitter(s).array == splitLines(s));
}
-auto lineSplitter(KeepTerminator keepTerm = No.keepTerminator, Range)(auto ref Range r)
-if (isConvertibleToString!Range)
-{
- return LineSplitter!(keepTerm, StringTypeOf!Range)(r);
-}
-
@safe pure unittest
{
import std.array : array;
@@ -2721,8 +2945,8 @@ if (isConvertibleToString!Range)
assertCTFEable!(
{
- foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
- {
+ static foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
+ {{
auto s = to!S(
"\rpeter\n\rpaul\r\njerry\u2028ice\u2029cream\n\n" ~
"sunday\nmon\u2030day\nschadenfreude\vkindergarten\f\vcookies\u0085"
@@ -2775,7 +2999,7 @@ if (isConvertibleToString!Range)
lines = lineSplitter!(Yes.keepTerminator)(s).array;
assert(lines.length == 14);
assert(lines[13] == "cookies");
- }
+ }}
});
}
@@ -2796,9 +3020,17 @@ if (isConvertibleToString!Range)
@nogc @safe pure unittest
{
import std.algorithm.comparison : equal;
+ import std.range : only;
+
auto s = "std/string.d";
auto as = TestAliasedString(s);
assert(equal(s.lineSplitter(), as.lineSplitter()));
+
+ enum S : string { a = "hello\nworld" }
+ assert(equal(S.a.lineSplitter(), only("hello", "world")));
+
+ char[S.a.length] sa = S.a[];
+ assert(equal(sa.lineSplitter(), only("hello", "world")));
}
@safe pure unittest
@@ -2813,15 +3045,18 @@ if (isConvertibleToString!Range)
}
/++
- Strips leading whitespace (as defined by $(REF isWhite, std,uni)).
+ Strips leading whitespace (as defined by $(REF isWhite, std,uni)) or
+ as specified in the second argument.
Params:
input = string or $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
of characters
+ chars = string of characters to be stripped
- Returns: $(D input) stripped of leading whitespace.
+ Returns: `input` stripped of leading whitespace or characters
+ specified in the second argument.
- Postconditions: $(D input) and the returned value
+ Postconditions: `input` and the returned value
will share the same tail (see $(REF sameTail, std,array)).
See_Also:
@@ -2831,38 +3066,104 @@ auto stripLeft(Range)(Range input)
if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) &&
!isInfinite!Range && !isConvertibleToString!Range)
{
+ import std.traits : isDynamicArray;
static import std.ascii;
static import std.uni;
- import std.utf : decodeFront;
- while (!input.empty)
+ static if (is(immutable ElementEncodingType!Range == immutable dchar)
+ || is(immutable ElementEncodingType!Range == immutable wchar))
{
- auto c = input.front;
- if (std.ascii.isASCII(c))
+ // Decoding is never needed for dchar. It happens not to be needed
+ // here for wchar because no whitepace is outside the basic
+ // multilingual plane meaning every whitespace character is encoded
+ // with a single wchar and due to the design of UTF-16 those wchars
+ // will not occur as part of the encoding of multi-wchar codepoints.
+ static if (isDynamicArray!Range)
{
- if (!std.ascii.isWhite(c))
- break;
- input.popFront();
+ foreach (i; 0 .. input.length)
+ {
+ if (!std.uni.isWhite(input[i]))
+ return input[i .. $];
+ }
+ return input[$ .. $];
+ }
+ else
+ {
+ while (!input.empty)
+ {
+ if (!std.uni.isWhite(input.front))
+ break;
+ input.popFront();
+ }
+ return input;
+ }
+ }
+ else
+ {
+ static if (isDynamicArray!Range)
+ {
+ // ASCII optimization for dynamic arrays.
+ size_t i = 0;
+ for (const size_t end = input.length; i < end; ++i)
+ {
+ auto c = input[i];
+ if (c >= 0x80) goto NonAsciiPath;
+ if (!std.ascii.isWhite(c)) break;
+ }
+ input = input[i .. $];
+ return input;
+
+ NonAsciiPath:
+ input = input[i .. $];
+ // Fall through to standard case.
+ }
+
+ import std.utf : decode, decodeFront, UseReplacementDchar;
+
+ static if (isNarrowString!Range)
+ {
+ for (size_t index = 0; index < input.length;)
+ {
+ const saveIndex = index;
+ if (!std.uni.isWhite(decode!(UseReplacementDchar.yes)(input, index)))
+ return input[saveIndex .. $];
+ }
+ return input[$ .. $];
}
else
{
- auto save = input.save;
- auto dc = decodeFront(input);
- if (!std.uni.isWhite(dc))
- return save;
+ while (!input.empty)
+ {
+ auto c = input.front;
+ if (std.ascii.isASCII(c))
+ {
+ if (!std.ascii.isWhite(c))
+ break;
+ input.popFront();
+ }
+ else
+ {
+ auto save = input.save;
+ auto dc = decodeFront!(UseReplacementDchar.yes)(input);
+ if (!std.uni.isWhite(dc))
+ return save;
+ }
+ }
+ return input;
}
}
- return input;
}
///
-@safe pure unittest
+nothrow @safe pure unittest
{
import std.uni : lineSep, paraSep;
assert(stripLeft(" hello world ") ==
"hello world ");
assert(stripLeft("\n\t\v\rhello world\n\t\v\r") ==
"hello world\n\t\v\r");
+ assert(stripLeft(" \u2028hello world") ==
+ "hello world");
assert(stripLeft("hello world") ==
"hello world");
assert(stripLeft([lineSep] ~ "hello world" ~ lineSep) ==
@@ -2874,6 +3175,8 @@ if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) &&
import std.utf : byChar;
assert(stripLeft(" hello world "w.byChar).array ==
"hello world ");
+ assert(stripLeft(" \u2022hello world ".byChar).array ==
+ "\u2022hello world ");
}
auto stripLeft(Range)(auto ref Range str)
@@ -2882,19 +3185,77 @@ if (isConvertibleToString!Range)
return stripLeft!(StringTypeOf!Range)(str);
}
-@safe pure unittest
+@nogc nothrow @safe pure unittest
{
assert(testAliasedString!stripLeft(" hello"));
}
+/// Ditto
+auto stripLeft(Range, Char)(Range input, const(Char)[] chars)
+if (((isForwardRange!Range && isSomeChar!(ElementEncodingType!Range)) ||
+ isConvertibleToString!Range) && isSomeChar!Char)
+{
+ static if (isConvertibleToString!Range)
+ return stripLeft!(StringTypeOf!Range)(input, chars);
+ else
+ {
+ for (; !input.empty; input.popFront)
+ {
+ if (chars.indexOf(input.front) == -1)
+ break;
+ }
+ return input;
+ }
+}
+
+///
+@safe pure unittest
+{
+ assert(stripLeft(" hello world ", " ") ==
+ "hello world ");
+ assert(stripLeft("xxxxxhello world ", "x") ==
+ "hello world ");
+ assert(stripLeft("xxxyy hello world ", "xy ") ==
+ "hello world ");
+}
+
+///
+@safe pure unittest
+{
+ import std.array : array;
+ import std.utf : byChar, byWchar, byDchar;
+
+ assert(stripLeft(" xxxyy hello world "w.byChar, "xy ").array ==
+ "hello world ");
+
+ assert(stripLeft("\u2028\u2020hello world\u2028"w.byWchar,
+ "\u2028").array == "\u2020hello world\u2028");
+ assert(stripLeft("\U00010001hello world"w.byWchar, " ").array ==
+ "\U00010001hello world"w);
+ assert(stripLeft("\U00010001 xyhello world"d.byDchar,
+ "\U00010001 xy").array == "hello world"d);
+
+ assert(stripLeft("\u2020hello"w, "\u2020"w) == "hello"w);
+ assert(stripLeft("\U00010001hello"d, "\U00010001"d) == "hello"d);
+ assert(stripLeft(" hello ", "") == " hello ");
+}
+
+@safe pure unittest
+{
+ assert(testAliasedString!stripLeft(" xyz hello", "xyz "));
+}
+
/++
- Strips trailing whitespace (as defined by $(REF isWhite, std,uni)).
+ Strips trailing whitespace (as defined by $(REF isWhite, std,uni)) or
+ as specified in the second argument.
Params:
str = string or random access range of characters
+ chars = string of characters to be stripped
Returns:
- slice of $(D str) stripped of trailing whitespace.
+ slice of `str` stripped of trailing whitespace or characters
+ specified in the second argument.
See_Also:
Generic stripping on ranges: $(REF _stripRight, std, algorithm, mutation)
@@ -2905,63 +3266,60 @@ if (isSomeString!Range ||
!isConvertibleToString!Range &&
isSomeChar!(ElementEncodingType!Range))
{
+ import std.traits : isDynamicArray;
import std.uni : isWhite;
alias C = Unqual!(ElementEncodingType!(typeof(str)));
- static if (isSomeString!(typeof(str)))
+ static if (isSomeString!(typeof(str)) && C.sizeof >= 2)
{
- import std.utf : codeLength;
-
- foreach_reverse (i, dchar c; str)
+ // No whitespace takes multiple wchars to encode and due to
+ // the design of UTF-16 those wchars will not occur as part
+ // of the encoding of multi-wchar codepoints.
+ foreach_reverse (i, C c; str)
{
if (!isWhite(c))
- return str[0 .. i + codeLength!C(c)];
+ return str[0 .. i + 1];
}
-
return str[0 .. 0];
}
else
{
- size_t i = str.length;
- while (i--)
+ // ASCII optimization for dynamic arrays.
+ static if (isDynamicArray!(typeof(str)))
{
- static if (C.sizeof == 4)
+ static import std.ascii;
+ foreach_reverse (i, C c; str)
{
- if (isWhite(str[i]))
- continue;
- break;
- }
- else static if (C.sizeof == 2)
- {
- auto c2 = str[i];
- if (c2 < 0xD800 || c2 >= 0xE000)
+ if (c >= 0x80)
{
- if (isWhite(c2))
- continue;
+ str = str[0 .. i + 1];
+ goto NonAsciiPath;
}
- else if (c2 >= 0xDC00)
+ if (!std.ascii.isWhite(c))
{
- if (i)
- {
- immutable c1 = str[i - 1];
- if (c1 >= 0xD800 && c1 < 0xDC00)
- {
- immutable dchar c = ((c1 - 0xD7C0) << 10) + (c2 - 0xDC00);
- if (isWhite(c))
- {
- --i;
- continue;
- }
- }
- }
+ return str[0 .. i + 1];
}
+ }
+ return str[0 .. 0];
+ }
+
+ NonAsciiPath:
+
+ size_t i = str.length;
+ while (i--)
+ {
+ static if (C.sizeof >= 2)
+ {
+ // No whitespace takes multiple wchars to encode and due to
+ // the design of UTF-16 those wchars will not occur as part
+ // of the encoding of multi-wchar codepoints.
+ if (isWhite(str[i]))
+ continue;
break;
}
else static if (C.sizeof == 1)
{
- import std.utf : byDchar;
-
- char cx = str[i];
+ const cx = str[i];
if (cx <= 0x7F)
{
if (isWhite(cx))
@@ -2970,21 +3328,30 @@ if (isSomeString!Range ||
}
else
{
- size_t stride = 0;
-
- while (1)
+ if (i == 0 || (0b1100_0000 & cx) != 0b1000_0000)
+ break;
+ const uint d = 0b0011_1111 & cx;
+ const c2 = str[i - 1];
+ if ((c2 & 0b1110_0000) == 0b1100_0000) // 2 byte encoding.
{
- ++stride;
- if (!i || (cx & 0xC0) == 0xC0 || stride == 4)
- break;
- cx = str[i - 1];
- if (!(cx & 0x80))
- break;
- --i;
+ if (isWhite(d + (uint(c2 & 0b0001_1111) << 6)))
+ {
+ i--;
+ continue;
+ }
+ break;
}
-
- if (!str[i .. i + stride].byDchar.front.isWhite)
- return str[0 .. i + stride];
+ if (i == 1 || (c2 & 0b1100_0000) != 0b1000_0000)
+ break;
+ const c3 = str[i - 2];
+ // In UTF-8 all whitespace is encoded in 3 bytes or fewer.
+ if ((c3 & 0b1111_0000) == 0b1110_0000 &&
+ isWhite(d + (uint(c2 & 0b0011_1111) << 6) + (uint(c3 & 0b0000_1111) << 12)))
+ {
+ i -= 2;
+ continue;
+ }
+ break;
}
}
else
@@ -2996,7 +3363,7 @@ if (isSomeString!Range ||
}
///
-@safe pure
+nothrow @safe pure
unittest
{
import std.uni : lineSep, paraSep;
@@ -3018,7 +3385,7 @@ if (isConvertibleToString!Range)
return stripRight!(StringTypeOf!Range)(str);
}
-@safe pure unittest
+@nogc nothrow @safe pure unittest
{
assert(testAliasedString!stripRight("hello "));
}
@@ -3034,7 +3401,7 @@ if (isConvertibleToString!Range)
assert(stripRight("\u2028hello world\u2020\u2028".byChar).array == "\u2028hello world\u2020");
assert(stripRight("hello world\U00010001"w.byWchar).array == "hello world\U00010001"w);
- foreach (C; AliasSeq!(char, wchar, dchar))
+ static foreach (C; AliasSeq!(char, wchar, dchar))
{
foreach (s; invalidUTFstrings!C())
{
@@ -3047,16 +3414,73 @@ if (isConvertibleToString!Range)
cast(void) stripRight(ws.byUTF!wchar).array;
}
+/// Ditto
+auto stripRight(Range, Char)(Range str, const(Char)[] chars)
+if (((isBidirectionalRange!Range && isSomeChar!(ElementEncodingType!Range)) ||
+ isConvertibleToString!Range) && isSomeChar!Char)
+{
+ static if (isConvertibleToString!Range)
+ return stripRight!(StringTypeOf!Range)(str, chars);
+ else
+ {
+ for (; !str.empty; str.popBack)
+ {
+ if (chars.indexOf(str.back) == -1)
+ break;
+ }
+ return str;
+ }
+}
+
+///
+@safe pure
+unittest
+{
+ assert(stripRight(" hello world ", "x") ==
+ " hello world ");
+ assert(stripRight(" hello world ", " ") ==
+ " hello world");
+ assert(stripRight(" hello worldxy ", "xy ") ==
+ " hello world");
+}
+
+@safe pure unittest
+{
+ assert(testAliasedString!stripRight("hello xyz ", "xyz "));
+}
+
+@safe pure unittest
+{
+ import std.array : array;
+ import std.utf : byChar, byDchar, byUTF, byWchar;
+
+ assert(stripRight(" hello world xyz ".byChar,
+ "xyz ").array == " hello world");
+ assert(stripRight("\u2028hello world\u2020\u2028"w.byWchar,
+ "\u2028").array == "\u2028hello world\u2020");
+ assert(stripRight("hello world\U00010001"w.byWchar,
+ " ").array == "hello world\U00010001"w);
+ assert(stripRight("hello world\U00010001 xy"d.byDchar,
+ "\U00010001 xy").array == "hello world"d);
+ assert(stripRight("hello\u2020"w, "\u2020"w) == "hello"w);
+ assert(stripRight("hello\U00010001"d, "\U00010001"d) == "hello"d);
+ assert(stripRight(" hello ", "") == " hello ");
+}
+
/++
Strips both leading and trailing whitespace (as defined by
- $(REF isWhite, std,uni)).
+ $(REF isWhite, std,uni)) or as specified in the second argument.
Params:
str = string or random access range of characters
+ chars = string of characters to be stripped
+ leftChars = string of leading characters to be stripped
+ rightChars = string of trailing characters to be stripped
Returns:
- slice of $(D str) stripped of leading and trailing whitespace.
+ slice of `str` stripped of leading and trailing whitespace
+ or characters as specified in the second argument.
See_Also:
Generic stripping on ranges: $(REF _strip, std, algorithm, mutation)
@@ -3105,7 +3529,7 @@ if (isConvertibleToString!Range)
assertCTFEable!(
{
- foreach (S; AliasSeq!( char[], const char[], string,
+ static foreach (S; AliasSeq!( char[], const char[], string,
wchar[], const wchar[], wstring,
dchar[], const dchar[], dstring))
{
@@ -3144,15 +3568,134 @@ if (isConvertibleToString!Range)
});
}
+/// Ditto
+auto strip(Range, Char)(Range str, const(Char)[] chars)
+if (((isBidirectionalRange!Range && isSomeChar!(ElementEncodingType!Range)) ||
+ isConvertibleToString!Range) && isSomeChar!Char)
+{
+ static if (isConvertibleToString!Range)
+ return strip!(StringTypeOf!Range)(str, chars);
+ else
+ return stripRight(stripLeft(str, chars), chars);
+}
+
+///
+@safe pure unittest
+{
+ assert(strip(" hello world ", "x") ==
+ " hello world ");
+ assert(strip(" hello world ", " ") ==
+ "hello world");
+ assert(strip(" xyxyhello worldxyxy ", "xy ") ==
+ "hello world");
+ assert(strip("\u2020hello\u2020"w, "\u2020"w) == "hello"w);
+ assert(strip("\U00010001hello\U00010001"d, "\U00010001"d) == "hello"d);
+ assert(strip(" hello ", "") == " hello ");
+}
+
+@safe pure unittest
+{
+ assert(testAliasedString!strip(" xyz hello world xyz ", "xyz "));
+}
+
+/// Ditto
+auto strip(Range, Char)(Range str, const(Char)[] leftChars, const(Char)[] rightChars)
+if (((isBidirectionalRange!Range && isSomeChar!(ElementEncodingType!Range)) ||
+ isConvertibleToString!Range) && isSomeChar!Char)
+{
+ static if (isConvertibleToString!Range)
+ return strip!(StringTypeOf!Range)(str, leftChars, rightChars);
+ else
+ return stripRight(stripLeft(str, leftChars), rightChars);
+}
+
+///
+@safe pure unittest
+{
+ assert(strip("xxhelloyy", "x", "y") == "hello");
+ assert(strip(" xyxyhello worldxyxyzz ", "xy ", "xyz ") ==
+ "hello world");
+ assert(strip("\u2020hello\u2028"w, "\u2020"w, "\u2028"w) == "hello"w);
+ assert(strip("\U00010001hello\U00010002"d, "\U00010001"d, "\U00010002"d) ==
+ "hello"d);
+ assert(strip(" hello ", "", "") == " hello ");
+}
+
+@safe pure unittest
+{
+ assert(testAliasedString!strip(" xy hello world pq ", "xy ", "pq "));
+}
+
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.conv : to;
+ import std.exception : assertCTFEable;
+
+ assertCTFEable!(
+ {
+ static foreach (S; AliasSeq!( char[], const char[], string,
+ wchar[], const wchar[], wstring,
+ dchar[], const dchar[], dstring))
+ {
+ assert(equal(stripLeft(to!S(" \tfoo\t "), "\t "), "foo\t "));
+ assert(equal(stripLeft(to!S("\u2008 foo\t \u2007"), "\u2008 "),
+ "foo\t \u2007"));
+ assert(equal(stripLeft(to!S("\u0085 μ \u0085 \u00BB \r"), "\u0085 "),
+ "μ \u0085 \u00BB \r"));
+ assert(equal(stripLeft(to!S("1"), " "), "1"));
+ assert(equal(stripLeft(to!S("\U0010FFFE"), " "), "\U0010FFFE"));
+ assert(equal(stripLeft(to!S(""), " "), ""));
+
+ assert(equal(stripRight(to!S(" foo\t "), "\t "), " foo"));
+ assert(equal(stripRight(to!S("\u2008 foo\t \u2007"), "\u2007\t "),
+ "\u2008 foo"));
+ assert(equal(stripRight(to!S("\u0085 μ \u0085 \u00BB \r"), "\r "),
+ "\u0085 μ \u0085 \u00BB"));
+ assert(equal(stripRight(to!S("1"), " "), "1"));
+ assert(equal(stripRight(to!S("\U0010FFFE"), " "), "\U0010FFFE"));
+ assert(equal(stripRight(to!S(""), " "), ""));
+
+ assert(equal(strip(to!S(" foo\t "), "\t "), "foo"));
+ assert(equal(strip(to!S("\u2008 foo\t \u2007"), "\u2008\u2007\t "),
+ "foo"));
+ assert(equal(strip(to!S("\u0085 μ \u0085 \u00BB \r"), "\u0085\r "),
+ "μ \u0085 \u00BB"));
+ assert(equal(strip(to!S("\U0010FFFE"), " "), "\U0010FFFE"));
+ assert(equal(strip(to!S(""), " "), ""));
+
+ assert(equal(strip(to!S(" \nfoo\t "), "\n ", "\t "), "foo"));
+ assert(equal(strip(to!S("\u2008\n foo\t \u2007"),
+ "\u2008\n ", "\u2007\t "), "foo"));
+ assert(equal(strip(to!S("\u0085 μ \u0085 \u00BB μ \u00BB\r"),
+ "\u0085 ", "\u00BB\r "), "μ \u0085 \u00BB μ"));
+ assert(equal(strip(to!S("\U0010FFFE"), " ", " "), "\U0010FFFE"));
+ assert(equal(strip(to!S(""), " ", " "), ""));
+ }
+ });
+}
+
+@safe pure unittest
+{
+ import std.array : sameHead, sameTail;
+ import std.exception : assertCTFEable;
+ assertCTFEable!(
+ {
+ wstring s = " xyz ";
+ assert(s.sameTail(s.stripLeft(" ")));
+ assert(s.sameHead(s.stripRight(" ")));
+ });
+}
+
/++
- If $(D str) ends with $(D delimiter), then $(D str) is returned without
- $(D delimiter) on its end. If it $(D str) does $(I not) end with
- $(D delimiter), then it is returned unchanged.
+ If `str` ends with `delimiter`, then `str` is returned without
+ `delimiter` on its end. If it `str` does $(I not) end with
+ `delimiter`, then it is returned unchanged.
- If no $(D delimiter) is given, then one trailing $(D '\r'), $(D '\n'),
- $(D "\r\n"), $(D '\f'), $(D '\v'), $(REF lineSep, std,uni), $(REF paraSep, std,uni), or $(REF nelSep, std,uni)
- is removed from the end of $(D str). If $(D str) does not end with any of those characters,
+ If no `delimiter` is given, then one trailing `'\r'`, `'\n'`,
+ `"\r\n"`, `'\f'`, `'\v'`, $(REF lineSep, std,uni), $(REF paraSep, std,uni), or $(REF nelSep, std,uni)
+ is removed from the end of `str`. If `str` does not end with any of those characters,
then it is returned unchanged.
Params:
@@ -3229,7 +3772,7 @@ if ((isBidirectionalRange!Range && isSomeChar!(ElementEncodingType!Range) ||
alias C1 = ElementEncodingType!Range;
- static if (is(Unqual!C1 == Unqual!C2) && (isSomeString!Range || (hasSlicing!Range && C2.sizeof == 4)))
+ static if (is(immutable C1 == immutable C2) && (isSomeString!Range || (hasSlicing!Range && C2.sizeof == 4)))
{
import std.algorithm.searching : endsWith;
if (str.endsWith(delimiter))
@@ -3306,11 +3849,9 @@ if (isConvertibleToString!Range)
import std.conv : to;
import std.exception : assertCTFEable;
- string s;
-
assertCTFEable!(
{
- foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
+ static foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
{
// @@@ BUG IN COMPILER, MUST INSERT CAST
assert(chomp(cast(S) null) is null);
@@ -3330,8 +3871,8 @@ if (isConvertibleToString!Range)
assert(chomp(to!S("hello\u2029\u2129")) == "hello\u2029\u2129");
assert(chomp(to!S("hello\u2029\u0185")) == "hello\u2029\u0185");
- foreach (T; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
+ {
// @@@ BUG IN COMPILER, MUST INSERT CAST
assert(chomp(cast(S) null, cast(T) null) is null);
assert(chomp(to!S("hello\n"), cast(T) null) == "hello");
@@ -3342,7 +3883,7 @@ if (isConvertibleToString!Range)
assert(chomp(to!S("hello"), to!T("llo")) == "he");
assert(chomp(to!S("\uFF28ello"), to!T("llo")) == "\uFF28e");
assert(chomp(to!S("\uFF28el\uFF4co"), to!T("l\uFF4co")) == "\uFF28e");
- }();
+ }
}
});
@@ -3361,10 +3902,10 @@ if (isConvertibleToString!Range)
/++
- If $(D str) starts with $(D delimiter), then the part of $(D str) following
- $(D delimiter) is returned. If $(D str) does $(I not) start with
+ If `str` starts with `delimiter`, then the part of `str` following
+ `delimiter` is returned. If `str` does $(I not) start with
- $(D delimiter), then it is returned unchanged.
+ `delimiter`, then it is returned unchanged.
Params:
str = string or $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
@@ -3382,7 +3923,7 @@ if ((isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) ||
{
alias C1 = ElementEncodingType!Range;
- static if (is(Unqual!C1 == Unqual!C2) && (isSomeString!Range || (hasSlicing!Range && C2.sizeof == 4)))
+ static if (is(immutable C1 == immutable C2) && (isSomeString!Range || (hasSlicing!Range && C2.sizeof == 4)))
{
import std.algorithm.searching : startsWith;
if (str.startsWith(delimiter))
@@ -3433,16 +3974,16 @@ unittest
import std.exception : assertCTFEable;
assertCTFEable!(
{
- foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
+ static foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
{
- foreach (T; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
+ {
assert(equal(chompPrefix(to!S("abcdefgh"), to!T("abcde")), "fgh"));
assert(equal(chompPrefix(to!S("abcde"), to!T("abcdefgh")), "abcde"));
assert(equal(chompPrefix(to!S("\uFF28el\uFF4co"), to!T("\uFF28el\uFF4co")), ""));
assert(equal(chompPrefix(to!S("\uFF28el\uFF4co"), to!T("\uFF28el")), "\uFF4co"));
assert(equal(chompPrefix(to!S("\uFF28el"), to!T("\uFF28el\uFF4co")), "\uFF28el"));
- }();
+ }
}
});
@@ -3467,9 +4008,9 @@ unittest
}
/++
- Returns $(D str) without its last character, if there is one. If $(D str)
- ends with $(D "\r\n"), then both are removed. If $(D str) is empty, then
- then it is returned unchanged.
+ Returns `str` without its last character, if there is one. If `str`
+ ends with `"\r\n"`, then both are removed. If `str` is empty, then
+ it is returned unchanged.
Params:
str = string (must be valid UTF)
@@ -3599,7 +4140,7 @@ if (isConvertibleToString!Range)
assertCTFEable!(
{
- foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
+ static foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
{
assert(chop(cast(S) null) is null);
assert(equal(chop(to!S("hello")), "hell"));
@@ -3614,14 +4155,14 @@ if (isConvertibleToString!Range)
/++
- Left justify $(D s) in a field $(D width) characters wide. $(D fillChar)
+ Left justify `s` in a field `width` characters wide. `fillChar`
is the character that will be used to fill up the space in the field that
- $(D s) doesn't fill.
+ `s` doesn't fill.
Params:
s = string
width = minimum field width
- fillChar = used to pad end up to $(D width) characters
+ fillChar = used to pad end up to `width` characters
Returns:
GC allocated string
@@ -3645,14 +4186,14 @@ if (isSomeString!S)
}
/++
- Left justify $(D s) in a field $(D width) characters wide. $(D fillChar)
+ Left justify `s` in a field `width` characters wide. `fillChar`
is the character that will be used to fill up the space in the field that
- $(D s) doesn't fill.
+ `s` doesn't fill.
Params:
r = string or range of characters
width = minimum field width
- fillChar = used to pad end up to $(D width) characters
+ fillChar = used to pad end up to `width` characters
Returns:
a lazy range of the left justified result
@@ -3756,14 +4297,14 @@ if (isConvertibleToString!Range)
}
/++
- Right justify $(D s) in a field $(D width) characters wide. $(D fillChar)
+ Right justify `s` in a field `width` characters wide. `fillChar`
is the character that will be used to fill up the space in the field that
- $(D s) doesn't fill.
+ `s` doesn't fill.
Params:
s = string
width = minimum field width
- fillChar = used to pad end up to $(D width) characters
+ fillChar = used to pad end up to `width` characters
Returns:
GC allocated string
@@ -3787,15 +4328,15 @@ if (isSomeString!S)
}
/++
- Right justify $(D s) in a field $(D width) characters wide. $(D fillChar)
+ Right justify `s` in a field `width` characters wide. `fillChar`
is the character that will be used to fill up the space in the field that
- $(D s) doesn't fill.
+ `s` doesn't fill.
Params:
r = string or $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
of characters
width = minimum field width
- fillChar = used to pad end up to $(D width) characters
+ fillChar = used to pad end up to `width` characters
Returns:
a lazy range of the right justified result
@@ -3836,7 +4377,7 @@ if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) &&
{
// Replace _width with nfill
// (use alias instead of union because CTFE cannot deal with unions)
- assert(_width);
+ assert(_width, "width of 0 not allowed");
static if (hasLength!Range)
{
immutable len = _input.length;
@@ -3900,7 +4441,7 @@ if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) &&
return Result(r, width, fillChar);
}
else
- static assert(0);
+ static assert(0, "Invalid character type of " ~ C.stringof);
}
///
@@ -3947,9 +4488,9 @@ if (isConvertibleToString!Range)
}
/++
- Center $(D s) in a field $(D width) characters wide. $(D fillChar)
+ Center `s` in a field `width` characters wide. `fillChar`
is the character that will be used to fill up the space in the field that
- $(D s) doesn't fill.
+ `s` doesn't fill.
Params:
s = The string to center
@@ -3984,8 +4525,8 @@ unittest
assertCTFEable!(
{
- foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
- {
+ static foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
+ {{
S s = to!S("hello");
assert(leftJustify(s, 2) == "hello");
@@ -4007,20 +4548,20 @@ unittest
assert(leftJustify(s, 8, 'ö') == "helloööö");
assert(rightJustify(s, 8, 'ö') == "öööhello");
assert(center(s, 8, 'ö') == "öhelloöö");
- }
+ }}
});
}
/++
- Center justify $(D r) in a field $(D width) characters wide. $(D fillChar)
+ Center justify `r` in a field `width` characters wide. `fillChar`
is the character that will be used to fill up the space in the field that
- $(D r) doesn't fill.
+ `r` doesn't fill.
Params:
r = string or $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
of characters
width = minimum field width
- fillChar = used to pad end up to $(D width) characters
+ fillChar = used to pad end up to `width` characters
Returns:
a lazy range of the center justified result
@@ -4084,12 +4625,13 @@ if (isConvertibleToString!Range)
assert(testAliasedString!centerJustifier("hello", 8));
}
-@system unittest
+@safe unittest
{
static auto byFwdRange(dstring s)
{
static struct FRange
{
+ @safe:
dstring str;
this(dstring s) { str = s; }
@property bool empty() { return str.length == 0; }
@@ -4124,7 +4666,7 @@ if (isConvertibleToString!Range)
/++
- Replace each tab character in $(D s) with the number of spaces necessary
+ Replace each tab character in `s` with the number of spaces necessary
to align the following character at the next tab stop.
Params:
@@ -4143,7 +4685,7 @@ if ((isForwardRange!Range && isSomeChar!(ElementEncodingType!Range))
}
///
-@system pure unittest
+@safe pure unittest
{
assert(detab(" \n\tx", 9) == " \n x");
}
@@ -4174,7 +4716,7 @@ if ((isForwardRange!Range && isSomeChar!(ElementEncodingType!Range))
}
/++
- Replace each tab character in $(D r) with the number of spaces
+ Replace each tab character in `r` with the number of spaces
necessary to align the following character at the next tab stop.
Params:
@@ -4296,7 +4838,7 @@ if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) &&
}
///
-@system pure unittest
+@safe pure unittest
{
import std.array : array;
@@ -4314,7 +4856,7 @@ if (isConvertibleToString!Range)
assert(testAliasedString!detabber( " ab\t asdf ", 8));
}
-@system pure unittest
+@safe pure unittest
{
import std.algorithm.comparison : cmp;
import std.conv : to;
@@ -4322,8 +4864,8 @@ if (isConvertibleToString!Range)
assertCTFEable!(
{
- foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
- {
+ static foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
+ {{
S s = to!S("This \tis\t a fofof\tof list");
assert(cmp(detab(s), "This is a fofof of list") == 0);
@@ -4340,12 +4882,12 @@ if (isConvertibleToString!Range)
assert(detab("\u0085\t", 9) == "\u0085 ");
assert(detab("\u2028\t", 9) == "\u2028 ");
assert(detab(" \u2029\t", 9) == " \u2029 ");
- }
+ }}
});
}
///
-@system pure unittest
+@safe pure unittest
{
import std.array : array;
import std.utf : byChar, byWchar;
@@ -4361,12 +4903,12 @@ if (isConvertibleToString!Range)
}
/++
- Replaces spaces in $(D s) with the optimal number of tabs.
+ Replaces spaces in `s` with the optimal number of tabs.
All spaces and tabs at the end of a line are removed.
Params:
s = String to convert.
- tabSize = Tab columns are $(D tabSize) spaces apart.
+ tabSize = Tab columns are `tabSize` spaces apart.
Returns:
GC allocated string with spaces replaced with tabs;
@@ -4401,7 +4943,7 @@ if (!(isForwardRange!Range && isSomeChar!(ElementEncodingType!Range)) &&
}
/++
- Replaces spaces in range $(D r) with the optimal number of tabs.
+ Replaces spaces in range `r` with the optimal number of tabs.
All spaces and tabs at the end of a line are removed.
Params:
@@ -4420,7 +4962,7 @@ if (isForwardRange!Range && !isConvertibleToString!Range)
import std.uni : lineSep, paraSep, nelSep;
import std.utf : codeUnitLimit, decodeFront;
- assert(tabSize > 0);
+ assert(tabSize > 0, "tabSize must be greater than 0");
alias C = Unqual!(ElementEncodingType!Range);
static struct Result
@@ -4508,7 +5050,8 @@ if (isForwardRange!Range && !isConvertibleToString!Range)
{
while (1)
{
- assert(_input.length);
+ assert(_input.length, "input did not contain non "
+ ~ "whitespace character");
cx = _input[0];
if (cx == ' ')
++column;
@@ -4523,7 +5066,8 @@ if (isForwardRange!Range && !isConvertibleToString!Range)
{
while (1)
{
- assert(!_input.empty);
+ assert(_input.length, "input did not contain non "
+ ~ "whitespace character");
cx = _input.front;
if (cx == ' ')
++column;
@@ -4716,16 +5260,17 @@ unittest
/++
- Replaces the characters in $(D str) which are keys in $(D transTable) with
- their corresponding values in $(D transTable). $(D transTable) is an AA
- where its keys are $(D dchar) and its values are either $(D dchar) or some
- type of string. Also, if $(D toRemove) is given, the characters in it are
- removed from $(D str) prior to translation. $(D str) itself is unaltered.
+ Replaces the characters in `str` which are keys in `transTable` with
+ their corresponding values in `transTable`. `transTable` is an AA
+ where its keys are `dchar` and its values are either `dchar` or some
+ type of string. Also, if `toRemove` is given, the characters in it are
+ removed from `str` prior to translation. `str` itself is unaltered.
A copy with the changes is returned.
See_Also:
- $(LREF tr)
- $(REF replace, std,array)
+ $(LREF tr),
+ $(REF replace, std,array),
+ $(REF substitute, std,algorithm,iteration)
Params:
str = The original string.
@@ -4756,7 +5301,8 @@ if (isSomeChar!C1 && isSomeChar!C2)
assert(translate("hello world", transTable2) == "h5llorange worangerld");
}
-@safe pure unittest // issue 13018
+// https://issues.dlang.org/show_bug.cgi?id=13018
+@safe pure unittest
{
immutable dchar[dchar] transTable1 = ['e' : '5', 'o' : '7', '5': 'q'];
assert(translate("hello world", transTable1) == "h5ll7 w7rld");
@@ -4774,10 +5320,11 @@ if (isSomeChar!C1 && isSomeChar!C2)
assertCTFEable!(
{
- foreach (S; AliasSeq!( char[], const( char)[], immutable( char)[],
+ static foreach (S; AliasSeq!( char[], const( char)[], immutable( char)[],
wchar[], const(wchar)[], immutable(wchar)[],
dchar[], const(dchar)[], immutable(dchar)[]))
- {
+ {(){ // workaround slow optimizations for large functions
+ // https://issues.dlang.org/show_bug.cgi?id=2396
assert(translate(to!S("hello world"), cast(dchar[dchar])['h' : 'q', 'l' : '5']) ==
to!S("qe55o wor5d"));
assert(translate(to!S("hello world"), cast(dchar[dchar])['o' : 'l', 'l' : '\U00010143']) ==
@@ -4788,13 +5335,14 @@ if (isSomeChar!C1 && isSomeChar!C2)
to!S("hell0 o w0rld"));
assert(translate(to!S("hello world"), cast(dchar[dchar]) null) == to!S("hello world"));
- foreach (T; AliasSeq!( char[], const( char)[], immutable( char)[],
+ static foreach (T; AliasSeq!( char[], const( char)[], immutable( char)[],
wchar[], const(wchar)[], immutable(wchar)[],
dchar[], const(dchar)[], immutable(dchar)[]))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
- foreach (R; AliasSeq!(dchar[dchar], const dchar[dchar],
+ (){ // workaround slow optimizations for large functions
+ // https://issues.dlang.org/show_bug.cgi?id=2396
+ static foreach (R; AliasSeq!(dchar[dchar], const dchar[dchar],
immutable dchar[dchar]))
- {
+ {{
R tt = ['h' : 'q', 'l' : '5'];
assert(translate(to!S("hello world"), tt, to!T("r"))
== to!S("qe55o wo5d"));
@@ -4802,13 +5350,14 @@ if (isSomeChar!C1 && isSomeChar!C2)
== to!S(" wrd"));
assert(translate(to!S("hello world"), tt, to!T("q5"))
== to!S("qe55o wor5d"));
- }
+ }}
}();
auto s = to!S("hello world");
dchar[dchar] transTable = ['h' : 'q', 'l' : '5'];
static assert(is(typeof(s) == typeof(translate(s, transTable))));
- }
+ assert(translate(s, transTable) == "qe55o wor5d");
+ }();}
});
}
@@ -4831,10 +5380,11 @@ if (isSomeChar!C1 && isSomeString!S && isSomeChar!C2)
assertCTFEable!(
{
- foreach (S; AliasSeq!( char[], const( char)[], immutable( char)[],
+ static foreach (S; AliasSeq!( char[], const( char)[], immutable( char)[],
wchar[], const(wchar)[], immutable(wchar)[],
dchar[], const(dchar)[], immutable(dchar)[]))
- {
+ {(){ // workaround slow optimizations for large functions
+ // https://issues.dlang.org/show_bug.cgi?id=2396
assert(translate(to!S("hello world"), ['h' : "yellow", 'l' : "42"]) ==
to!S("yellowe4242o wor42d"));
assert(translate(to!S("hello world"), ['o' : "owl", 'l' : "\U00010143\U00010143"]) ==
@@ -4849,14 +5399,14 @@ if (isSomeChar!C1 && isSomeString!S && isSomeChar!C2)
to!S("hello world"));
assert(translate(to!S("hello world"), cast(string[dchar]) null) == to!S("hello world"));
- foreach (T; AliasSeq!( char[], const( char)[], immutable( char)[],
+ static foreach (T; AliasSeq!( char[], const( char)[], immutable( char)[],
wchar[], const(wchar)[], immutable(wchar)[],
dchar[], const(dchar)[], immutable(dchar)[]))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
-
- foreach (R; AliasSeq!(string[dchar], const string[dchar],
+ (){ // workaround slow optimizations for large functions
+ // https://issues.dlang.org/show_bug.cgi?id=2396
+ static foreach (R; AliasSeq!(string[dchar], const string[dchar],
immutable string[dchar]))
- {
+ {{
R tt = ['h' : "yellow", 'l' : "42"];
assert(translate(to!S("hello world"), tt, to!T("r")) ==
to!S("yellowe4242o wo42d"));
@@ -4868,18 +5418,19 @@ if (isSomeChar!C1 && isSomeString!S && isSomeChar!C2)
to!S(""));
assert(translate(to!S("hello world"), tt, to!T("42")) ==
to!S("yellowe4242o wor42d"));
- }
+ }}
}();
auto s = to!S("hello world");
string[dchar] transTable = ['h' : "silly", 'l' : "putty"];
static assert(is(typeof(s) == typeof(translate(s, transTable))));
- }
+ assert(translate(s, transTable) == "sillyeputtyputtyo worputtyd");
+ }();}
});
}
/++
- This is an overload of $(D translate) which takes an existing buffer to write the contents to.
+ This is an overload of `translate` which takes an existing buffer to write the contents to.
Params:
str = The original string.
@@ -4888,7 +5439,7 @@ if (isSomeChar!C1 && isSomeString!S && isSomeChar!C2)
toRemove = The characters to remove from the string.
buffer = An output range to write the contents to.
+/
-void translate(C1, C2 = immutable char, Buffer)(C1[] str,
+void translate(C1, C2 = immutable char, Buffer)(const(C1)[] str,
in dchar[dchar] transTable,
const(C2)[] toRemove,
Buffer buffer)
@@ -4916,7 +5467,8 @@ if (isSomeChar!C1 && isSomeChar!C2 && isOutputRange!(Buffer, C1))
assert(buffer.data == "h5llorange worangerld");
}
-@safe pure unittest // issue 13018
+// https://issues.dlang.org/show_bug.cgi?id=13018
+@safe pure unittest
{
import std.array : appender;
immutable dchar[dchar] transTable1 = ['e' : '5', 'o' : '7', '5': 'q'];
@@ -4944,8 +5496,8 @@ if (isSomeChar!C1 && isSomeString!S && isSomeChar!C2 && isOutputRange!(Buffer, S
translateImpl(str, transTable, toRemove, buffer);
}
-private void translateImpl(C1, T, C2, Buffer)(C1[] str,
- T transTable,
+private void translateImpl(C1, T, C2, Buffer)(const(C1)[] str,
+ scope T transTable,
const(C2)[] toRemove,
Buffer buffer)
{
@@ -4974,26 +5526,27 @@ private void translateImpl(C1, T, C2, Buffer)(C1[] str,
cases where Unicode processing is not necessary.
Unlike the other overloads of $(LREF _translate), this one does not take
- an AA. Rather, it takes a $(D string) generated by $(LREF makeTransTable).
+ an AA. Rather, it takes a `string` generated by $(LREF makeTransTable).
- The array generated by $(D makeTransTable) is $(D 256) elements long such that
+ The array generated by `makeTransTable` is `256` elements long such that
the index is equal to the ASCII character being replaced and the value is
equal to the character that it's being replaced with. Note that translate
does not decode any of the characters, so you can actually pass it Extended
- ASCII characters if you want to (ASCII only actually uses $(D 128)
+ ASCII characters if you want to (ASCII only actually uses `128`
characters), but be warned that Extended ASCII characters are not valid
- Unicode and therefore will result in a $(D UTFException) being thrown from
+ Unicode and therefore will result in a `UTFException` being thrown from
most other Phobos functions.
Also, because no decoding occurs, it is possible to use this overload to
translate ASCII characters within a proper UTF-8 string without altering the
other, non-ASCII characters. It's replacing any code unit greater than
- $(D 127) with another code unit or replacing any code unit with another code
- unit greater than $(D 127) which will cause UTF validation issues.
+ `127` with another code unit or replacing any code unit with another code
+ unit greater than `127` which will cause UTF validation issues.
See_Also:
- $(LREF tr)
- $(REF replace, std,array)
+ $(LREF tr),
+ $(REF replace, std,array),
+ $(REF substitute, std,algorithm,iteration)
Params:
str = The original string.
@@ -5001,13 +5554,16 @@ private void translateImpl(C1, T, C2, Buffer)(C1[] str,
to replace them with. It is generated by $(LREF makeTransTable).
toRemove = The characters to remove from the string.
+/
-C[] translate(C = immutable char)(in char[] str, in char[] transTable, in char[] toRemove = null) @trusted pure nothrow
-if (is(Unqual!C == char))
+C[] translate(C = immutable char)(scope const(char)[] str, scope const(char)[] transTable,
+ scope const(char)[] toRemove = null) @trusted pure nothrow
+if (is(immutable C == immutable char))
in
{
- assert(transTable.length == 256);
+ import std.conv : to;
+ assert(transTable.length == 256, "transTable had invalid length of " ~
+ to!string(transTable.length));
}
-body
+do
{
bool[256] remTable = false;
@@ -5033,6 +5589,14 @@ body
return cast(C[])(buffer);
}
+///
+@safe pure nothrow unittest
+{
+ auto transTable1 = makeTrans("eo5", "57q");
+ assert(translate("hello world", transTable1) == "h5ll7 w7rld");
+
+ assert(translate("hello world", transTable1, "low") == "h5 rd");
+}
/**
* Do same thing as $(LREF makeTransTable) but allocate the translation table
@@ -5040,7 +5604,7 @@ body
*
* Use $(LREF makeTransTable) instead.
*/
-string makeTrans(in char[] from, in char[] to) @trusted pure nothrow
+string makeTrans(scope const(char)[] from, scope const(char)[] to) @trusted pure nothrow
{
return makeTransTable(from, to)[].idup;
}
@@ -5064,19 +5628,20 @@ string makeTrans(in char[] from, in char[] to) @trusted pure nothrow
* Returns:
* translation array
*/
-
-char[256] makeTransTable(in char[] from, in char[] to) @safe pure nothrow @nogc
+char[256] makeTransTable(scope const(char)[] from, scope const(char)[] to) @safe pure nothrow @nogc
in
{
import std.ascii : isASCII;
- assert(from.length == to.length);
- assert(from.length <= 256);
+ assert(from.length == to.length, "from.length must match to.length");
+ assert(from.length <= 256, "from.length must be <= 256");
foreach (char c; from)
- assert(isASCII(c));
+ assert(isASCII(c),
+ "all characters in from must be valid ascii character");
foreach (char c; to)
- assert(isASCII(c));
+ assert(isASCII(c),
+ "all characters in to must be valid ascii character");
}
-body
+do
{
char[256] result = void;
@@ -5087,6 +5652,13 @@ body
return result;
}
+///
+@safe pure unittest
+{
+ assert(translate("hello world", makeTransTable("hl", "q5")) == "qe55o wor5d");
+ assert(translate("hello world", makeTransTable("12345", "67890")) == "hello world");
+}
+
@safe pure unittest
{
import std.conv : to;
@@ -5094,16 +5666,17 @@ body
assertCTFEable!(
{
- foreach (C; AliasSeq!(char, const char, immutable char))
- {
+ static foreach (C; AliasSeq!(char, const char, immutable char))
+ {{
assert(translate!C("hello world", makeTransTable("hl", "q5")) == to!(C[])("qe55o wor5d"));
auto s = to!(C[])("hello world");
auto transTable = makeTransTable("hl", "q5");
static assert(is(typeof(s) == typeof(translate!C(s, transTable))));
- }
+ assert(translate(s, transTable) == "qe55o wor5d");
+ }}
- foreach (S; AliasSeq!(char[], const(char)[], immutable(char)[]))
+ static foreach (S; AliasSeq!(char[], const(char)[], immutable(char)[]))
{
assert(translate(to!S("hello world"), makeTransTable("hl", "q5")) == to!S("qe55o wor5d"));
assert(translate(to!S("hello \U00010143 world"), makeTransTable("hl", "q5")) ==
@@ -5114,8 +5687,8 @@ body
assert(translate(to!S("hello \U00010143 world"), makeTransTable("12345", "67890")) ==
to!S("hello \U00010143 world"));
- foreach (T; AliasSeq!(char[], const(char)[], immutable(char)[]))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(char[], const(char)[], immutable(char)[]))
+ {
assert(translate(to!S("hello world"), makeTransTable("hl", "q5"), to!T("r")) ==
to!S("qe55o wo5d"));
assert(translate(to!S("hello \U00010143 world"), makeTransTable("hl", "q5"), to!T("r")) ==
@@ -5124,13 +5697,13 @@ body
to!S(" wrd"));
assert(translate(to!S("hello world"), makeTransTable("hl", "q5"), to!T("q5")) ==
to!S("qe55o wor5d"));
- }();
+ }
}
});
}
/++
- This is an $(I $(RED ASCII-only)) overload of $(D translate) which takes an existing buffer to write the contents to.
+ This is an $(I $(RED ASCII-only)) overload of `translate` which takes an existing buffer to write the contents to.
Params:
str = The original string.
@@ -5139,14 +5712,15 @@ body
toRemove = The characters to remove from the string.
buffer = An output range to write the contents to.
+/
-void translate(C = immutable char, Buffer)(in char[] str, in char[] transTable,
- in char[] toRemove, Buffer buffer) @trusted pure
-if (is(Unqual!C == char) && isOutputRange!(Buffer, char))
+void translate(C = immutable char, Buffer)(scope const(char)[] str, scope const(char)[] transTable,
+ scope const(char)[] toRemove, Buffer buffer) @trusted pure
+if (is(immutable C == immutable char) && isOutputRange!(Buffer, char))
in
{
- assert(transTable.length == 256);
+ assert(transTable.length == 256, format!
+ "transTable.length %s must equal 256"(transTable.length));
}
-body
+do
{
bool[256] remTable = false;
@@ -5255,10 +5829,10 @@ if (isSomeString!S)
/++
- Replaces the characters in $(D str) which are in $(D from) with the
- the corresponding characters in $(D to) and returns the resulting string.
+ Replaces the characters in `str` which are in `from` with the
+ the corresponding characters in `to` and returns the resulting string.
- $(D tr) is based on
+ `tr` is based on
$(HTTP pubs.opengroup.org/onlinepubs/9699919799/utilities/_tr.html, Posix's tr),
though it doesn't do everything that the Posix utility does.
@@ -5271,27 +5845,32 @@ if (isSomeString!S)
Modifiers:
$(BOOKTABLE,
$(TR $(TD Modifier) $(TD Description))
- $(TR $(TD $(D 'c')) $(TD Complement the list of characters in $(D from)))
- $(TR $(TD $(D 'd')) $(TD Removes matching characters with no corresponding
- replacement in $(D to)))
- $(TR $(TD $(D 's')) $(TD Removes adjacent duplicates in the replaced
+ $(TR $(TD `'c'`) $(TD Complement the list of characters in `from`))
+ $(TR $(TD `'d'`) $(TD Removes matching characters with no corresponding
+ replacement in `to`))
+ $(TR $(TD `'s'`) $(TD Removes adjacent duplicates in the replaced
characters))
)
- If the modifier $(D 'd') is present, then the number of characters in
- $(D to) may be only $(D 0) or $(D 1).
+ If the modifier `'d'` is present, then the number of characters in
+ `to` may be only `0` or `1`.
- If the modifier $(D 'd') is $(I not) present, and $(D to) is empty, then
- $(D to) is taken to be the same as $(D from).
+ If the modifier `'d'` is $(I not) present, and `to` is empty, then
+ `to` is taken to be the same as `from`.
- If the modifier $(D 'd') is $(I not) present, and $(D to) is shorter than
- $(D from), then $(D to) is extended by replicating the last character in
- $(D to).
+ If the modifier `'d'` is $(I not) present, and `to` is shorter than
+ `from`, then `to` is extended by replicating the last character in
+ `to`.
- Both $(D from) and $(D to) may contain ranges using the $(D '-') character
- (e.g. $(D "a-d") is synonymous with $(D "abcd").) Neither accept a leading
- $(D '^') as meaning the complement of the string (use the $(D 'c') modifier
+ Both `from` and `to` may contain ranges using the `'-'` character
+ (e.g. `"a-d"` is synonymous with `"abcd"`.) Neither accept a leading
+ `'^'` as meaning the complement of the string (use the `'c'` modifier
for that).
+
+ See_Also:
+ $(LREF translate),
+ $(REF replace, std,array),
+ $(REF substitute, std,algorithm,iteration)
+/
C1[] tr(C1, C2, C3, C4 = immutable char)
(C1[] str, const(C2)[] from, const(C3)[] to, const(C4)[] modifiers = null)
@@ -5311,7 +5890,8 @@ C1[] tr(C1, C2, C3, C4 = immutable char)
case 'c': mod_c = 1; break; // complement
case 'd': mod_d = 1; break; // delete unreplaced chars
case 's': mod_s = 1; break; // squeeze duplicated replaced chars
- default: assert(0);
+ default: assert(false, "modifier must be one of ['c', 'd', 's'] not "
+ ~ c);
}
}
@@ -5394,7 +5974,7 @@ C1[] tr(C1, C2, C3, C4 = immutable char)
if (mod_s && modified && newc == lastc)
continue;
result.put(newc);
- assert(newc != dchar.init);
+ assert(newc != dchar.init, "character must not be dchar.init");
modified = true;
lastc = newc;
continue;
@@ -5408,6 +5988,15 @@ C1[] tr(C1, C2, C3, C4 = immutable char)
return result.data;
}
+///
+@safe pure unittest
+{
+ assert(tr("abcdef", "cd", "CD") == "abCDef");
+ assert(tr("1st March, 2018", "March", "MAR", "s") == "1st MAR, 2018");
+ assert(tr("abcdef", "ef", "", "d") == "abcd");
+ assert(tr("14-Jul-87", "a-zA-Z", " ", "cs") == " Jul ");
+}
+
@safe pure unittest
{
import std.algorithm.comparison : equal;
@@ -5447,6 +6036,7 @@ C1[] tr(C1, C2, C3, C4 = immutable char)
auto s = to!S("hello world");
static assert(is(typeof(s) == typeof(tr(s, "he", "if"))));
+ assert(tr(s, "he", "if") == "ifllo world");
}
});
}
@@ -5459,11 +6049,11 @@ C1[] tr(C1, C2, C3, C4 = immutable char)
}
/**
- * Takes a string $(D s) and determines if it represents a number. This function
- * also takes an optional parameter, $(D bAllowSep), which will accept the
- * separator characters $(D ',') and $(D '__') within the string. But these
+ * Takes a string `s` and determines if it represents a number. This function
+ * also takes an optional parameter, `bAllowSep`, which will accept the
+ * separator characters `','` and `'__'` within the string. But these
* characters should be stripped from the string before using any
- * of the conversion functions like $(D to!int()), $(D to!float()), and etc
+ * of the conversion functions like `to!int()`, `to!float()`, and etc
* else an error will occur.
*
* Also please note, that no spaces are allowed within the string
@@ -5476,7 +6066,7 @@ C1[] tr(C1, C2, C3, C4 = immutable char)
* bAllowSep = accept separator characters or not
*
* Returns:
- * $(D bool)
+ * `bool`
*/
bool isNumeric(S)(S s, bool bAllowSep = false)
if (isSomeString!S ||
@@ -5692,7 +6282,7 @@ if (isSomeString!S ||
{
import std.conv : to;
- foreach (T; AliasSeq!(string, char[], wstring, wchar[], dstring, dchar[]))
+ static foreach (T; AliasSeq!(string, char[], wstring, wchar[], dstring, dchar[]))
{
assert("123".to!T.isNumeric());
assert("123UL".to!T.isNumeric());
@@ -5769,7 +6359,6 @@ if (isSomeString!S ||
{
assert(isNumeric(to!string(real.nan)) == true);
assert(isNumeric(to!string(-real.infinity)) == true);
- assert(isNumeric(to!string(123e+2+1234.78Li)) == true);
}
string s = "$250.99-";
@@ -5803,10 +6392,8 @@ if (isSomeString!S ||
* $(LUCKY The Soundex Indexing System)
* $(LREF soundex)
*
- * Bugs:
+ * Note:
* Only works well with English names.
- * There are other arguably better Soundex algorithms,
- * but this one is the standard one.
*/
char[4] soundexer(Range)(Range str)
if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) &&
@@ -5864,12 +6451,25 @@ if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) &&
return result;
}
+/// ditto
char[4] soundexer(Range)(auto ref Range str)
if (isConvertibleToString!Range)
{
return soundexer!(StringTypeOf!Range)(str);
}
+///
+@safe unittest
+{
+ assert(soundexer("Gauss") == "G200");
+ assert(soundexer("Ghosh") == "G200");
+
+ assert(soundexer("Robert") == "R163");
+ assert(soundexer("Rupert") == "R163");
+
+ assert(soundexer("0123^&^^**&^") == ['\0', '\0', '\0', '\0']);
+}
+
/*****************************
* Like $(LREF soundexer), but with different parameters
* and return value.
@@ -5885,7 +6485,7 @@ if (isConvertibleToString!Range)
* See_Also:
* $(LREF soundexer)
*/
-char[] soundex(const(char)[] str, char[] buffer = null)
+char[] soundex(scope const(char)[] str, return scope char[] buffer = null)
@safe pure nothrow
in
{
@@ -5895,13 +6495,15 @@ out (result)
{
if (result !is null)
{
- assert(result.length == 4);
- assert(result[0] >= 'A' && result[0] <= 'Z');
+ assert(result.length == 4, "Result must have length of 4");
+ assert(result[0] >= 'A' && result[0] <= 'Z', "The first character of "
+ ~ " the result must be an upper character not " ~ result);
foreach (char c; result[1 .. 4])
- assert(c >= '0' && c <= '6');
+ assert(c >= '0' && c <= '6', "the last three character of the"
+ ~ " result must be number between 0 and 6 not " ~ result);
}
}
-body
+do
{
char[4] result = soundexer(str);
if (result[0] == 0)
@@ -5912,6 +6514,17 @@ body
return buffer;
}
+///
+@safe unittest
+{
+ assert(soundex("Gauss") == "G200");
+ assert(soundex("Ghosh") == "G200");
+
+ assert(soundex("Robert") == "R163");
+ assert(soundex("Rupert") == "R163");
+
+ assert(soundex("0123^&^^**&^") == null);
+}
@safe pure nothrow unittest
{
@@ -5981,7 +6594,6 @@ body
* auto-complete the string once sufficient characters have been
* entered that uniquely identify it.
*/
-
string[string] abbrev(string[] values) @safe pure
{
import std.algorithm.sorting : sort;
@@ -6095,11 +6707,11 @@ string[string] abbrev(string[] values) @safe pure
*/
size_t column(Range)(Range str, in size_t tabsize = 8)
-if ((isInputRange!Range && isSomeChar!(Unqual!(ElementEncodingType!Range)) ||
+if ((isInputRange!Range && isSomeChar!(ElementEncodingType!Range) ||
isNarrowString!Range) &&
!isConvertibleToString!Range)
{
- static if (is(Unqual!(ElementEncodingType!Range) == char))
+ static if (is(immutable ElementEncodingType!Range == immutable char))
{
// decoding needed for chars
import std.utf : byDchar;
@@ -6378,7 +6990,7 @@ void main() {
* StringException if indentation is done with different sequences
* of whitespace characters.
*/
-S[] outdent(S)(S[] lines) @safe pure
+S[] outdent(S)(return scope S[] lines) @safe pure
if (isSomeString!S)
{
import std.algorithm.searching : startsWith;
@@ -6438,6 +7050,32 @@ if (isSomeString!S)
return lines;
}
+///
+@safe pure unittest
+{
+ auto str1 = [
+ " void main()\n",
+ " {\n",
+ " test();\n",
+ " }\n"
+ ];
+ auto str1Expected = [
+ "void main()\n",
+ "{\n",
+ " test();\n",
+ "}\n"
+ ];
+ assert(str1.outdent == str1Expected);
+
+ auto str2 = [
+ "void main()\n",
+ " {\n",
+ " test();\n",
+ " }\n"
+ ];
+ assert(str2.outdent == str2);
+}
+
@safe pure unittest
{
import std.conv : to;
@@ -6470,8 +7108,8 @@ if (isSomeString!S)
assertCTFEable!(
{
- foreach (S; AliasSeq!(string, wstring, dstring))
- {
+ static foreach (S; AliasSeq!(string, wstring, dstring))
+ {{
enum S blank = "";
assert(blank.outdent() == blank);
static assert(blank.outdent() == blank);
@@ -6527,7 +7165,7 @@ if (isSomeString!S)
enum expected7 = "a \nb ";
assert(testStr7.outdent() == expected7);
static assert(testStr7.outdent() == expected7);
- }
+ }}
});
}
@@ -6538,11 +7176,11 @@ if (isSomeString!S)
assertThrown!StringException(bad.outdent);
}
-/** Assume the given array of integers $(D arr) is a well-formed UTF string and
+/** Assume the given array of integers `arr` is a well-formed UTF string and
return it typed as a UTF string.
-$(D ubyte) becomes $(D char), $(D ushort) becomes $(D wchar) and $(D uint)
-becomes $(D dchar). Type qualifiers are preserved.
+`ubyte` becomes `char`, `ushort` becomes `wchar` and `uint`
+becomes `dchar`. Type qualifiers are preserved.
When compiled with debug mode, this function performs an extra check to make
sure the return value is a valid Unicode string.
@@ -6553,16 +7191,27 @@ Params:
Returns:
arr retyped as an array of chars, wchars, or dchars
+Throws:
+ In debug mode `AssertError`, when the result is not a well-formed UTF string.
+
See_Also: $(LREF representation)
*/
-auto assumeUTF(T)(T[] arr) pure
-if (staticIndexOf!(Unqual!T, ubyte, ushort, uint) != -1)
+auto assumeUTF(T)(T[] arr)
+if (staticIndexOf!(immutable T, immutable ubyte, immutable ushort, immutable uint) != -1)
{
import std.traits : ModifyTypePreservingTQ;
+ import std.exception : collectException;
import std.utf : validate;
+
alias ToUTFType(U) = AliasSeq!(char, wchar, dchar)[U.sizeof / 2];
- auto asUTF = cast(ModifyTypePreservingTQ!(ToUTFType, T)[])arr;
- debug validate(asUTF);
+ auto asUTF = cast(ModifyTypePreservingTQ!(ToUTFType, T)[]) arr;
+
+ debug
+ {
+ scope ex = collectException(validate(asUTF));
+ assert(!ex, ex.msg);
+ }
+
return asUTF;
}
@@ -6573,14 +7222,14 @@ if (staticIndexOf!(Unqual!T, ubyte, ushort, uint) != -1)
immutable(ubyte)[] b = a.representation;
string c = b.assumeUTF;
- assert(a == c);
+ assert(c == "Hölo World");
}
pure @system unittest
{
import std.algorithm.comparison : equal;
- foreach (T; AliasSeq!(char[], wchar[], dchar[]))
- {
+ static foreach (T; AliasSeq!(char[], wchar[], dchar[]))
+ {{
immutable T jti = "Hello World";
T jt = jti.dup;
@@ -6609,5 +7258,18 @@ pure @system unittest
assert(equal(jt, ht));
assert(equal(jt, htc));
assert(equal(jt, hti));
- }
+ }}
+}
+
+pure @system unittest
+{
+ import core.exception : AssertError;
+ import std.exception : assertThrown, assertNotThrown;
+
+ immutable(ubyte)[] a = [ 0xC0 ];
+
+ debug
+ assertThrown!AssertError( () nothrow @nogc @safe {cast(void) a.assumeUTF;} () );
+ else
+ assertNotThrown!AssertError( () nothrow @nogc @safe {cast(void) a.assumeUTF;} () );
}