diff --git a/.codecov.yml b/.codecov.yml
index d084b0217db..569f0257521 100644
--- a/.codecov.yml
+++ b/.codecov.yml
@@ -1,6 +1,10 @@
# Documentation: https://github.com/codecov/support/wiki/codecov.yml
codecov:
+ ci:
+ - "circleci.com"
+ notify:
+ after_n_builds: 1 # send notifications after the first upload
bot: dlang-bot
coverage:
diff --git a/.dscanner.ini b/.dscanner.ini
index a2b0999c75a..a5610f7d100 100644
--- a/.dscanner.ini
+++ b/.dscanner.ini
@@ -2,20 +2,20 @@
[analysis.config.StaticAnalysisConfig]
; Check variable, class, struct, interface, union, and function names against
; the Phobos style guide
-style_check="disabled"
+style_check="enabled"
; Check for array literals that cause unnecessary allocation
enum_array_literal_check="enabled"
; Check for poor exception handling practices
-exception_check="disabled" ; FIXME
+exception_check="enabled"
; Check for use of the deprecated 'delete' keyword
delete_check="enabled"
; Check for use of the deprecated floating point operators
float_operator_check="enabled"
; Check number literals for readability
-number_style_check="skip-unittest";
+number_style_check="enabled"
; Checks that opEquals, opCmp, toHash, and toString are either const, immutable
; , or inout.
-object_const_check="disabled"
+object_const_check="enabled"
; Checks for .. expressions where the left side is larger than the right.
backwards_range_check="enabled"
; Checks for if statements whose 'then' block is the same as the 'else' block
@@ -23,43 +23,44 @@ if_else_same_check="enabled"
; Checks for some problems with constructors
constructor_check="enabled"
; Checks for unused variables and function parameters
-unused_variable_check="disabled"
+unused_variable_check="enabled"
; Checks for unused labels
-unused_label_check="disabled" ; FIXME
+unused_label_check="enabled"
; Checks for duplicate attributes
duplicate_attribute="enabled"
; Checks that opEquals and toHash are both defined or neither are defined
-opequals_tohash_check="disabled"
+opequals_tohash_check="enabled"
; Checks for subtraction from .length properties
-length_subtraction_check="disabled"
-; Checks for methods or properties whose names conflict with built-in properties
-builtin_property_names_check="enabled"; FIXME
+length_subtraction_check="enabled"
+; Checks for methods or properties whose names conflict with built-in propertie
+; s
+builtin_property_names_check="enabled"
; Checks for confusing code in inline asm statements
-asm_style_check="disabled"; FIXME
+asm_style_check="enabled"
; Checks for confusing logical operator precedence
-logical_precedence_check="disabled"
+logical_precedence_check="enabled"
; Checks for undocumented public declarations
-undocumented_declaration_check="disabled"; FIXME
+undocumented_declaration_check="enabled"
; Checks for poor placement of function attributes
-function_attribute_check="disabled"
+function_attribute_check="enabled"
; Checks for use of the comma operator
comma_expression_check="enabled"
; Checks for local imports that are too broad
local_import_check="skip-unittest"
; Checks for variables that could be declared immutable
-could_be_immutable_check="disabled"
+could_be_immutable_check="enabled"
; Checks for redundant expressions in if statements
redundant_if_check="enabled"
; Checks for redundant parenthesis
redundant_parens_check="enabled"
; Checks for mismatched argument and parameter names
-mismatched_args_check="disabled"
+mismatched_args_check="enabled"
; Checks for labels with the same name as variables
-label_var_same_name_check="disabled"
+label_var_same_name_check="enabled"
; Checks for lines longer than 120 characters
long_line_check="enabled"
; Checks for assignment to auto-ref function parameters
-auto_ref_assignment_check="disabled" ; FIXME
+auto_ref_assignment_check="enabled"
; Checks for incorrect infinite range definitions
incorrect_infinite_range_check="enabled"
; Checks for asserts that are always true
@@ -71,10 +72,94 @@ static_if_else_check="enabled"
; Check for unclear lambda syntax
lambda_return_check="enabled"
; Check for auto function without return statement
-auto_function_check = "enabled"
-; Check for explicitly annotated unittests
-explicitly_annotated_unittests = "enabled"
+auto_function_check="enabled"
; Check for sortedness of imports
-imports_sortedness = "disabled"
+imports_sortedness="enabled"
+; Check for explicitly annotated unittests
+explicitly_annotated_unittests="enabled"
+; Check for properly documented public functions (Returns, Params)
+properly_documented_public_functions="enabled"
; Check for useless usage of the final attribute
-final_attribute_check = "enabled"
+final_attribute_check="enabled"
+; Check for virtual calls in the class constructors
+vcall_in_ctor="enabled"
+; Check for useless user defined initializers
+useless_initializer="enabled"
+; Check allman brace style
+allman_braces_check="enabled"
+; Check for redundant attributes
+redundant_attributes_check="enabled"
+; Check for public declarations without a documented unittest
+has_public_example="enabled"
+
+; Configure which modules are checked with a specific checker
+; Please help to extend these checks onto more Phobos modules
+; Process:
+; - Pick your favorite check
+; - Remove a module from the blacklist
+; - Run DScanner
+; - Fix the warnings
+; - Submit a PR to Phobos
+; - GOTO: Remove a module
+;
+; Some checks are currently disabled.
+; For more details, please see https://github.com/dlang/phobos/pull/5501
+[analysis.config.ModuleFilters]
+; Check for uses of the old-style alias syntax
+alias_syntax_check="-std.traits,-std.typecons"
+; Check allman brace style
+allman_braces_check="+disabled"
+; Checks for confusing code in inline asm statements
+asm_style_check="-std.math"
+; Checks for assignment to auto-ref function parameters
+auto_ref_assignment_check="-std.algorithm.mutation,-std.format,-std.typecons"
+; Checks for variables that could be declared immutable
+could_be_immutable_check="-std.algorithm.comparison,-std.algorithm.iteration,-std.algorithm.mutation,-std.algorithm.searching,-std.algorithm.setops,-std.algorithm.sorting,-std.array,-std.base64,-std.bigint,-std.bitmanip,-std.complex,-std.concurrency,-std.container,-std.container.array,-std.container.binaryheap,-std.container.dlist,-std.container.rbtree,-std.container.slist,-std.container.util,-std.conv,-std.csv,-std.datetime,-std.datetime.date,-std.datetime.interval,-std.datetime.stopwatch,-std.datetime.systime,-std.datetime.timezone,-std.digest.crc,-std.digest,-std.digest.hmac,-std.digest.md,-std.digest.murmurhash,-std.digest.ripemd,-std.digest.sha,-std.encoding,-std.exception,-std.experimental.allocator,-std.experimental.allocator.building_blocks.affix_allocator,-std.experimental.allocator.building_blocks.allocator_list,-std.experimental.allocator.building_blocks.bitmapped_block,-std.experimental.allocator.building_blocks.bucketizer,-std.experimental.allocator.building_blocks.fallback_allocator,-std.experimental.allocator.building_blocks.free_list,-std.experimental.allocator.building_blocks.free_tree,-std.experimental.allocator.building_blocks.kernighan_ritchie,-std.experimental.allocator.building_blocks.region,-std.experimental.allocator.building_blocks.stats_collector,-std.experimental.allocator.gc_allocator,-std.experimental.allocator.mallocator,-std.experimental.allocator.typed,-std.experimental.checkedint,-std.experimental.logger.core,-std.experimental.logger.multilogger,-std.experimental.typecons,-std.file,-std.format,-std.functional,-std.getopt,-std.internal.cstring,-std.internal.digest.sha_SSSE3,-std.internal.math.biguintcore,-std.internal.math.biguintnoasm,-std.internal.math.biguintx86,-std.internal.math.errorfunction,-std.internal.math.gammafunction,-std.internal.scopebuffer,-std.internal.test.dummyrange,-std.json,-std.math,-std.mathspecial,-std.meta,-std.mmfile,-std.net.curl,-std.net.isemail,-std.numeric,-std.outbuffer,-std.parallelism,-std.path,-std.process,-std.random,-std.range,-std.range.interfaces,-std.range.primitives,-std.regex,-std.regex.internal.backtracking,-std.regex.internal.generator,-std.regex.internal.ir,-std.regex.internal.kickstart,-std.regex.internal.parser,-std.regex.internal.tests,-std.regex.internal.thompson,-std.signals,-std.socket,-std.stdio,-std.string,-std.traits,-std.typecons,-std.uni,-std.uri,-std.utf,-std.uuid,-std.variant,-std.windows.registry,-std.xml,-std.zip,-std.zlib"
+; Check for poor exception handling practices
+exception_check="-std.concurrency,-std.net.curl,-std.parallelism,-std.range,-std.socket,-std.typecons"
+; Checks for poor placement of function attributes
+function_attribute_check="-std.algorithm.iteration,-std.concurrency,-std.conv,-std.datetime.interval,-std.exception,-std.functional,-std.net.curl,-std.numeric,-std.parallelism,-std.random,-std.range,-std.range.primitives,-std.socket,-std.traits,-std.typecons,-std.uni"
+; Check for public declarations without a documented unittest
+has_public_example="-etc.c.curl,-etc.c.sqlite3,-etc.c.zlib,-std.bitmanip,-std.complex,-std.concurrency,-std.container.array,-std.container.dlist,-std.container.rbtree,-std.container.slist,-std.conv,-std.csv,-std.datetime,-std.datetime.date,-std.datetime.interval,-std.datetime.stopwatch,-std.datetime.systime,-std.datetime.timezone,-std.demangle,-std.digest,-std.digest.hmac,-std.digest.murmurhash,-std.digest.sha,-std.encoding,-std.exception,-std.experimental.allocator,-std.experimental.allocator.building_blocks.allocator_list,-std.experimental.allocator.building_blocks.bitmapped_block,-std.experimental.allocator.building_blocks.fallback_allocator,-std.experimental.allocator.building_blocks.free_list,-std.experimental.allocator.building_blocks.free_tree,-std.experimental.allocator.building_blocks.null_allocator,-std.experimental.allocator.building_blocks.stats_collector,-std.experimental.allocator.common,-std.experimental.allocator.mmap_allocator,-std.experimental.allocator.typed,-std.experimental.checkedint,-std.experimental.logger.core,-std.experimental.logger.filelogger,-std.experimental.logger.multilogger,-std.experimental.typecons,-std.file,-std.format,-std.getopt,-std.internal.math.biguintcore,-std.internal.math.biguintnoasm,-std.internal.math.errorfunction,-std.internal.math.gammafunction,-std.internal.scopebuffer,-std.internal.test.dummyrange,-std.json,-std.math,-std.mathspecial,-std.mmfile,-std.net.curl,-std.net.isemail,-std.numeric,-std.outbuffer,-std.parallelism,-std.path,-std.process,-std.random,-std.range,-std.range.interfaces,-std.range.primitives,-std.regex,-std.regex.internal.ir,-std.socket,-std.stdio,-std.string,-std.traits,-std.typecons,-std.uni,-std.uri,-std.utf,-std.uuid,-std.variant,-std.xml,-std.zip,-std.zlib"
+; Check for sortedness of imports
+imports_sortedness="+disabled"
+;imports_sortedness="-etc.c.curl,-std.algorithm.comparison,-std.algorithm.internal,-std.algorithm.iteration,-std.algorithm.mutation,-std.algorithm.searching,-std.algorithm.setops,-std.algorithm.sorting,-std.array,-std.bigint,-std.bitmanip,-std.c.freebsd.socket,-std.c.linux.pthread,-std.c.process,-std.complex,-std.concurrency,-std.container.array,-std.container.binaryheap,-std.container.dlist,-std.container.rbtree,-std.container.slist,-std.container.util,-std.conv,-std.datetime,-std.datetime.date,-std.datetime.interval,-std.datetime.systime,-std.datetime.timezone,-std.digest,-std.digest.hmac,-std.exception,-std.experimental.allocator,-std.experimental.allocator.building_blocks,-std.experimental.allocator.building_blocks.affix_allocator,-std.experimental.allocator.building_blocks.allocator_list,-std.experimental.allocator.building_blocks.free_list,-std.experimental.allocator.building_blocks.free_tree,-std.experimental.allocator.building_blocks.kernighan_ritchie,-std.experimental.allocator.building_blocks.region,-std.experimental.allocator.common,-std.experimental.allocator.mallocator,-std.experimental.allocator.mmap_allocator,-std.experimental.allocator.showcase,-std.experimental.allocator.typed,-std.experimental.checkedint,-std.experimental.logger.core,-std.experimental.typecons,-std.file,-std.format,-std.functional,-std.getopt,-std.internal.math.biguintcore,-std.internal.test.dummyrange,-std.json,-std.math,-std.meta,-std.mmfile,-std.net.curl,-std.net.isemail,-std.numeric,-std.outbuffer,-std.parallelism,-std.path,-std.process,-std.random,-std.range,-std.range.primitives,-std.regex,-std.regex.internal.backtracking,-std.regex.internal.generator,-std.regex.internal.kickstart,-std.regex.internal.parser,-std.regex.internal.tests,-std.signals,-std.socket,-std.stdio,-std.string,-std.uni,-std.utf,-std.uuid,-std.variant,-std.windows.charset,-std.windows.registry,-std.windows.syserror,-std.zip"
+; Checks for labels with the same name as variables
+label_var_same_name_check="-std.algorithm.iteration,-std.algorithm.sorting,-std.array,-std.bigint,-std.bitmanip,-std.conv,-std.encoding,-std.experimental.allocator.building_blocks.segregator,-std.experimental.typecons,-std.format,-std.internal.digest.sha_SSSE3,-std.parallelism,-std.process,-std.typecons,-std.utf"
+; Checks for subtraction from .length properties
+length_subtraction_check="+disabled"
+;length_subtraction_check="-std.algorithm.internal,-std.algorithm.iteration,-std.algorithm.mutation,-std.algorithm.searching,-std.algorithm.sorting,-std.array,-std.concurrency,-std.container.array,-std.container.binaryheap,-std.conv,-std.datetime.timezone,-std.experimental.allocator.building_blocks.segregator,-std.experimental.logger.core,-std.file,-std.format,-std.getopt,-std.internal.math.biguintcore,-std.internal.math.biguintnoasm,-std.internal.math.biguintx86,-std.internal.scopebuffer,-std.math,-std.net.curl,-std.net.isemail,-std.numeric,-std.parallelism,-std.path,-std.process,-std.range,-std.regex,-std.regex.internal.parser,-std.regex.internal.tests,-std.string,-std.uni,-std.windows.charset,-std.windows.registry,-std.zip"
+; Checks for confusing logical operator precedence
+logical_precedence_check="+disabled"
+;logical_precedence_check="-std.algorithm.mutation,-std.algorithm.searching,-std.algorithm.setops,-std.algorithm.sorting,-std.array,-std.container.array,-std.conv,-std.experimental.checkedint,-std.file,-std.format,-std.getopt,-std.math,-std.net.isemail,-std.path,-std.range,-std.range.primitives,-std.stdio,-std.string"
+; Checks for lines with more than 120 visual characters - see https://github.com/dlang/phobos/pull/5500 for details
+long_line_check="-std.datetime.timezone"
+; Checks for mismatched argument and parameter names
+mismatched_args_check="-std.container.dlist,-std.encoding,-std.internal.math.biguintcore,-std.math,-std.net.curl,-std.numeric,-std.range.primitives,-std.uni"
+; Check number literals for readability
+number_style_check="+disabled"
+;number_style_check="-std.algorithm.iteration,-std.algorithm.sorting,-std.array,-std.bigint,-std.bitmanip,-std.container.array,-std.conv,-std.datetime.date,-std.datetime.systime,-std.datetime.timezone,-std.digest.crc,-std.digest,-std.digest.md,-std.digest.ripemd,-std.digest.sha,-std.experimental.allocator.building_blocks.free_tree,-std.experimental.allocator.building_blocks.kernighan_ritchie,-std.experimental.checkedint,-std.file,-std.format,-std.functional,-std.internal.math.biguintcore,-std.internal.math.gammafunction,-std.json,-std.math,-std.outbuffer,-std.parallelism,-std.random,-std.range,-std.regex.internal.generator,-std.utf,-std.zip,-std.zlib"
+; Checks that opEquals, opCmp, toHash, and toString are either const, immutable
+; , or inout.
+object_const_check="-std.algorithm.searching,-std.array,-std.bitmanip,-std.concurrency,-std.container.rbtree,-std.conv,-std.datetime.interval,-std.encoding,-std.exception,-std.experimental.checkedint,-std.experimental.logger.core,-std.experimental.typecons,-std.format,-std.functional,-std.meta,-std.numeric,-std.range,-std.regex,-std.stdio,-std.typecons,-std.variant,-std.xml"
+; Checks that opEquals and toHash are both defined or neither are defined
+opequals_tohash_check="-std.algorithm.searching,-std.array,-std.complex,-std.container.array,-std.container.dlist,-std.container.rbtree,-std.container.slist,-std.datetime,-std.datetime.date,-std.experimental.checkedint,-std.functional,-std.internal.test.dummyrange,-std.json,-std.numeric,-std.random,-std.range,-std.socket,-std.traits,-std.typecons,-std.uni"
+; Check for properly documented public functions (Returns, Params)
+properly_documented_public_functions="-etc.c.odbc.sql,-etc.c.odbc.sqlext,-etc.c.zlib,-std.algorithm.comparison,-std.algorithm.iteration,-std.algorithm.mutation,-std.algorithm.searching,-std.algorithm.setops,-std.algorithm.sorting,-std.array,-std.ascii,-std.base64,-std.bigint,-std.bitmanip,-std.complex,-std.container,-std.container.array,-std.container.binaryheap,-std.container.dlist,-std.container.rbtree,-std.container.slist,-std.container.util,-std.csv,-std.datetime,-std.datetime.date,-std.datetime.interval,-std.datetime.stopwatch,-std.datetime.systime,-std.datetime.timezone,-std.demangle,-std.digest.crc,-std.digest,-std.digest.hmac,-std.digest.md,-std.digest.murmurhash,-std.digest.ripemd,-std.digest.sha,-std.encoding,-std.exception,-std.experimental.allocator,-std.experimental.allocator.building_blocks.affix_allocator,-std.experimental.allocator.building_blocks.allocator_list,-std.experimental.allocator.building_blocks.bitmapped_block,-std.experimental.allocator.building_blocks.bucketizer,-std.experimental.allocator.building_blocks.fallback_allocator,-std.experimental.allocator.building_blocks.free_list,-std.experimental.allocator.building_blocks.free_tree,-std.experimental.allocator.building_blocks.kernighan_ritchie,-std.experimental.allocator.building_blocks.null_allocator,-std.experimental.allocator.building_blocks.quantizer,-std.experimental.allocator.building_blocks.region,-std.experimental.allocator.building_blocks.scoped_allocator,-std.experimental.allocator.building_blocks.segregator,-std.experimental.allocator.building_blocks.stats_collector,-std.experimental.allocator.common,-std.experimental.allocator.gc_allocator,-std.experimental.allocator.mallocator,-std.experimental.allocator.showcase,-std.experimental.allocator.typed,-std.experimental.checkedint,-std.experimental.logger.core,-std.experimental.logger.filelogger,-std.file,-std.format,-std.functional,-std.getopt,-std.internal.cstring,-std.internal.math.biguintcore,-std.internal.math.biguintnoasm,-std.internal.scopebuffer,-std.internal.test.dummyrange,-std.internal.unicode_tables,-std.json,-std.math,-std.mathspecial,-std.meta,-std.mmfile,-std.net.curl,-std.net.isemail,-std.numeric,-std.outbuffer,-std.parallelism,-std.path,-std.random,-std.range,-std.range.interfaces,-std.range.primitives,-std.regex,-std.regex.internal.backtracking,-std.regex.internal.ir,-std.regex.internal.kickstart,-std.regex.internal.parser,-std.regex.internal.thompson,-std.signals,-std.socket,-std.stdio,-std.string,-std.traits,-std.typecons,-std.uni,-std.uri,-std.utf,-std.uuid,-std.variant,-std.xml,-std.zip,-std.zlib"
+; Check for redundant attributes
+redundant_attributes_check="-std.concurrency,-std.digest.md,-std.digest.ripemd,-std.digest.sha,-std.internal.math.biguintcore,-std.math,-std.meta,-std.range,-std.regex.internal.ir,-std.uni,-std.windows.registry"
+; Check variable, class, struct, interface, union, and function names against
+; the Phobos style guide
+style_check="+disabled"
+;style_check="-etc.c.curl,-etc.c.odbc.sqlext,-etc.c.odbc.sqltypes,-etc.c.odbc.sqlucode,-etc.c.sqlite3,-etc.c.zlib,-std.algorithm.comparison,-std.algorithm.internal,-std.algorithm.iteration,-std.algorithm.mutation,-std.algorithm.sorting,-std.array,-std.base64,-std.bitmanip,-std.c.linux.linux,-std.compiler,-std.container.array,-std.conv,-std.datetime.date,-std.datetime.interval,-std.datetime.systime,-std.digest,-std.digest.murmurhash,-std.digest.sha,-std.encoding,-std.exception,-std.experimental.allocator,-std.experimental.allocator.building_blocks.affix_allocator,-std.experimental.allocator.building_blocks.allocator_list,-std.experimental.allocator.building_blocks.bucketizer,-std.experimental.allocator.building_blocks.fallback_allocator,-std.experimental.allocator.building_blocks.free_list,-std.experimental.allocator.building_blocks.free_tree,-std.experimental.allocator.building_blocks.null_allocator,-std.experimental.allocator.building_blocks.region,-std.experimental.allocator.building_blocks.segregator,-std.experimental.allocator.common,-std.experimental.allocator.gc_allocator,-std.experimental.allocator.mallocator,-std.experimental.allocator.mmap_allocator,-std.experimental.checkedint,-std.experimental.typecons,-std.format,-std.functional,-std.getopt,-std.internal.digest.sha_SSSE3,-std.internal.math.errorfunction,-std.internal.math.gammafunction,-std.internal.test.dummyrange,-std.internal.unicode_tables,-std.json,-std.math,-std.meta,-std.numeric,-std.parallelism,-std.path,-std.process,-std.random,-std.range,-std.range.primitives,-std.regex,-std.regex.internal.ir,-std.regex.internal.kickstart,-std.signals,-std.socket,-std.stdio,-std.string,-std.uni,-std.uri,-std.utf,-std.uuid,-std.variant,-std.zlib"
+; Checks for undocumented public declarations
+undocumented_declaration_check="-etc.c.curl,-etc.c.odbc.sql,-etc.c.odbc.sqlext,-etc.c.odbc.sqltypes,-etc.c.odbc.sqlucode,-etc.c.sqlite3,-etc.c.zlib,-std.algorithm.sorting,-std.array,-std.ascii,-std.base64,-std.bitmanip,-std.c.linux.linux,-std.c.linux.socket,-std.c.osx.socket,-std.c.process,-std.compiler,-std.complex,-std.concurrency,-std.container,-std.container.array,-std.container.binaryheap,-std.container.dlist,-std.container.rbtree,-std.container.slist,-std.conv,-std.csv,-std.datetime.date,-std.digest,-std.digest.hmac,-std.digest.md,-std.digest.murmurhash,-std.digest.ripemd,-std.digest.sha,-std.encoding,-std.exception,-std.experimental.allocator,-std.experimental.allocator.building_blocks.affix_allocator,-std.experimental.allocator.building_blocks.allocator_list,-std.experimental.allocator.building_blocks.bitmapped_block,-std.experimental.allocator.building_blocks.fallback_allocator,-std.experimental.allocator.building_blocks.free_list,-std.experimental.allocator.building_blocks.free_tree,-std.experimental.allocator.building_blocks.kernighan_ritchie,-std.experimental.allocator.building_blocks.quantizer,-std.experimental.allocator.building_blocks.region,-std.experimental.allocator.building_blocks.segregator,-std.experimental.allocator.building_blocks.stats_collector,-std.experimental.allocator.gc_allocator,-std.experimental.allocator.mallocator,-std.experimental.checkedint,-std.experimental.logger.core,-std.experimental.typecons,-std.file,-std.format,-std.functional,-std.internal.digest.sha_SSSE3,-std.internal.math.biguintcore,-std.internal.math.biguintnoasm,-std.internal.math.biguintx86,-std.internal.math.errorfunction,-std.internal.math.gammafunction,-std.internal.test.dummyrange,-std.internal.test.uda,-std.internal.windows.advapi32,-std.json,-std.math,-std.mmfile,-std.numeric,-std.outbuffer,-std.parallelism,-std.path,-std.process,-std.regex,-std.regex.internal.parser,-std.signals,-std.socket,-std.stdio,-std.string,-std.system,-std.traits,-std.uni,-std.utf,-std.variant,-std.windows.charset,-std.windows.registry,-std.windows.syserror,-std.xml,-std.zip,-std.zlib"
+; Checks for unused labels
+unused_label_check="-std.conv,-std.format,-std.internal.math.biguintx86,-std.regex.internal.thompson,-std.signals,-std.uni"
+; Checks for unused variables and function parameters
+unused_variable_check="-std.algorithm.comparison,-std.algorithm.iteration,-std.algorithm.mutation,-std.algorithm.searching,-std.algorithm.setops,-std.algorithm.sorting,-std.array,-std.bigint,-std.bitmanip,-std.complex,-std.concurrency,-std.container,-std.container.array,-std.container.dlist,-std.container.rbtree,-std.container.slist,-std.conv,-std.csv,-std.datetime,-std.datetime.date,-std.datetime.interval,-std.datetime.stopwatch,-std.datetime.systime,-std.datetime.timezone,-std.digest.crc,-std.digest,-std.digest.md,-std.digest.murmurhash,-std.digest.ripemd,-std.digest.sha,-std.encoding,-std.exception,-std.experimental.allocator,-std.experimental.allocator.building_blocks.affix_allocator,-std.experimental.allocator.building_blocks.bitmapped_block,-std.experimental.allocator.building_blocks.bucketizer,-std.experimental.allocator.building_blocks.kernighan_ritchie,-std.experimental.allocator.building_blocks.stats_collector,-std.experimental.allocator.common,-std.experimental.allocator.gc_allocator,-std.experimental.allocator.mallocator,-std.experimental.allocator.typed,-std.experimental.checkedint,-std.experimental.logger.core,-std.experimental.typecons,-std.file,-std.format,-std.functional,-std.getopt,-std.internal.cstring,-std.internal.digest.sha_SSSE3,-std.internal.math.biguintcore,-std.internal.math.biguintx86,-std.internal.math.errorfunction,-std.internal.scopebuffer,-std.internal.test.dummyrange,-std.json,-std.math,-std.meta,-std.mmfile,-std.net.curl,-std.numeric,-std.parallelism,-std.process,-std.random,-std.range,-std.range.interfaces,-std.range.primitives,-std.regex.internal.backtracking,-std.regex.internal.tests,-std.regex.internal.thompson,-std.signals,-std.socket,-std.stdio,-std.string,-std.traits,-std.typecons,-std.uni,-std.uri,-std.utf,-std.uuid,-std.variant,-std.windows.registry,-std.xml,-std.zip,-std.zlib"
+; Check for useless user defined initializers
+useless_initializer="+disabled"
+;useless_initializer="-etc.c.odbc.sqlext,-etc.c.zlib,-std.algorithm.iteration,-std.algorithm.mutation,-std.algorithm.searching,-std.algorithm.setops,-std.algorithm.sorting,-std.array,-std.bigint,-std.bitmanip,-std.compiler,-std.container.array,-std.container.dlist,-std.container.rbtree,-std.conv,-std.csv,-std.datetime.systime,-std.digest.md,-std.digest.murmurhash,-std.digest.ripemd,-std.encoding,-std.exception,-std.experimental.allocator,-std.experimental.allocator.building_blocks.bitmapped_block,-std.experimental.allocator.building_blocks.free_tree,-std.experimental.allocator.building_blocks.kernighan_ritchie,-std.experimental.allocator.common,-std.experimental.allocator.mallocator,-std.experimental.logger.core,-std.experimental.logger.multilogger,-std.file,-std.format,-std.functional,-std.getopt,-std.internal.cstring,-std.internal.digest.sha_SSSE3,-std.internal.math.biguintcore,-std.internal.math.biguintnoasm,-std.internal.math.biguintx86,-std.internal.math.gammafunction,-std.internal.test.dummyrange,-std.json,-std.math,-std.net.curl,-std.numeric,-std.parallelism,-std.path,-std.process,-std.random,-std.range,-std.range.interfaces,-std.range.primitives,-std.regex,-std.regex.internal.backtracking,-std.regex.internal.generator,-std.regex.internal.ir,-std.regex.internal.kickstart,-std.regex.internal.parser,-std.regex.internal.tests,-std.regex.internal.thompson,-std.signals,-std.socket,-std.stdio,-std.string,-std.uni,-std.uri,-std.utf,-std.uuid,-std.variant,-std.windows.registry,-std.windows.syserror,-std.xml,-std.zip,-std.zlib"
+; Check for virtual calls in the class constructors
+vcall_in_ctor="-std.socket,-std.xml"
diff --git a/changelog/std-base64-base64urlnopadding.dd b/changelog/std-base64-base64urlnopadding.dd
new file mode 100644
index 00000000000..2d4d64e38ac
--- /dev/null
+++ b/changelog/std-base64-base64urlnopadding.dd
@@ -0,0 +1,11 @@
+`Base64URLNoPadding` (URL-safe Base64 without padding) was added
+
+$(REF Base64URLNoPadding, std, base64) allows encoding/decoding without padding:
+
+---
+import std.base64 : Base64URLNoPadding;
+
+ubyte[] data = [0x83, 0xd7, 0x30, 0x7b, 0xef];
+assert(Base64URLNoPadding.encode(data) == "g9cwe-8");
+assert(Base64URLNoPadding.decode("g9cwe-8") == data);
+---
diff --git a/changelog/std-digest-package.dd b/changelog/std-digest-package.dd
new file mode 100644
index 00000000000..e94f7d82113
--- /dev/null
+++ b/changelog/std-digest-package.dd
@@ -0,0 +1,8 @@
+`std.digest.digest` was renamed to `std.digest`.
+
+$(B Motivation):
+
+The fully qualified name of the digest function template was `std.digest.digest.digest`.
+This is because `std.digest` is a package, with a module named `digest` in it, and the function `digest` inside that.
+
+$(MREF std, digest) contains the former `std.digest.digest` package.
diff --git a/changelog/std-meta-stride.dd b/changelog/std-meta-stride.dd
new file mode 100644
index 00000000000..a01da903d9e
--- /dev/null
+++ b/changelog/std-meta-stride.dd
@@ -0,0 +1,9 @@
+`std.meta.Stride` was added
+
+$(REF Stride, std,meta) allows selecting a subset of template by a step size and offset:
+
+---
+alias attribs = AliasSeq!(short, int, long, ushort, uint, ulong);
+static assert(is(Stride!(3, attribs) == AliasSeq!(short, ushort)));
+static assert(is(Stride!(3, attribs[1 .. $]) == AliasSeq!(int, uint)));
+---
diff --git a/changelog/std-socket-abstract.dd b/changelog/std-socket-abstract.dd
new file mode 100644
index 00000000000..1ccc8bf145b
--- /dev/null
+++ b/changelog/std-socket-abstract.dd
@@ -0,0 +1,10 @@
+`std.socket.UnixAddress` now supports abstract addresses.
+
+UNIX domain sockets are usually identified by pathnames. Linux offers a
+non-portable extension to this scheme, known as abstract socket addresses,
+which are independent of the filesystem. An abstract socket address starts with
+a null byte (`'\0'`), e.g.:
+
+---
+auto addr = new UnixAddress("\0/tmp/dbus-OtHLWmCLPR");
+---
diff --git a/circle.yml b/circle.yml
index 72801310c99..aedcc77f9fb 100644
--- a/circle.yml
+++ b/circle.yml
@@ -8,7 +8,6 @@ test:
override:
- ./circleci.sh setup-repos
- ./circleci.sh style_lint
- - ./circleci.sh has_public_example
- ./circleci.sh publictests
- ./circleci.sh coverage:
parallel: true
diff --git a/circleci.sh b/circleci.sh
index 80e528b5ac4..d2882804924 100755
--- a/circleci.sh
+++ b/circleci.sh
@@ -100,11 +100,6 @@ style_lint()
# dscanner needs a more up-to-date DMD version
source "$(CURL_USER_AGENT=\"$CURL_USER_AGENT\" bash ~/dlang/install.sh dmd-$DSCANNER_DMD_VER --activate)"
- # some style tools are at the tools repo
- clone https://github.com/dlang/tools.git ../tools master
- # fix to a specific version of https://github.com/dlang/tools/tree/master/styles
- git -C ../tools checkout 60583c8363ff25d00017dffdb18c7ee7e7d9a343
-
make -f posix.mak style_lint DUB=$DUB
}
@@ -115,12 +110,13 @@ coverage()
# remove all existing coverage files (just in case)
rm -rf $(find -name '*.lst')
- # currently using the test_runner yields wrong code coverage results
- # see https://github.com/dlang/phobos/pull/4719 for details
- ENABLE_COVERAGE="1" make -f posix.mak MODEL=$MODEL unittest-debug
+ # Coverage information of the test runner can be missing for some template instatiations.
+ # https://issues.dlang.org/show_bug.cgi?id=16397
+ # ENABLE_COVERAGE="1" make -j$N -f posix.mak MODEL=$MODEL unittest-debug
- # instead we run all tests individually
- make -f posix.mak $(find std etc -name "*.d" | sed "s/[.]d$/.test")
+ # So instead we run all tests individually (hoping that that doesn't break any tests).
+ # -cov is enabled by the %.test target itself
+ make -j$N -f posix.mak $(find std etc -name "*.d" | sed "s/[.]d$/.test/")
# Remove coverage information from lines with non-deterministic coverage.
# These lines are annotated with a comment containing "nocoverage".
@@ -130,13 +126,13 @@ coverage()
# extract publictests and run them independently
publictests()
{
- make -f posix.mak -j$N publictests DUB=$DUB
-}
+ # checkout a specific version of https://github.com/dlang/tools
+ if [ ! -d ../tools ] ; then
+ clone https://github.com/dlang/tools.git ../tools master
+ fi
+ git -C ../tools checkout df3dfa3061d25996ac98158d3bdb3525c8d89445
-# check modules for public unittests
-has_public_example()
-{
- make -f posix.mak -j$N has_public_example DUB=$DUB
+ make -f posix.mak -j$N publictests DUB=$DUB
}
case $1 in
@@ -144,7 +140,8 @@ case $1 in
setup-repos) setup_repos ;;
coverage) coverage ;;
publictests) publictests ;;
- has_public_example) has_public_example;;
style_lint) style_lint ;;
+ # has_public_example has been removed and is kept for compatibility with older PRs
+ has_public_example) echo "OK" ;;
*) echo "Unknown command"; exit 1;;
esac
diff --git a/etc/c/curl.d b/etc/c/curl.d
index f5c709f0c07..65c56c49e5b 100644
--- a/etc/c/curl.d
+++ b/etc/c/curl.d
@@ -117,7 +117,7 @@ alias curl_socket_t = socket_t;
/// jdrewsen - Would like to get socket error constant from std.socket by it is private atm.
version(Windows)
{
- private import core.sys.windows.windows, core.sys.windows.winsock2;
+ import core.sys.windows.windows, core.sys.windows.winsock2;
enum CURL_SOCKET_BAD = SOCKET_ERROR;
}
version(Posix) enum CURL_SOCKET_BAD = -1;
diff --git a/etc/c/odbc/sqlext.d b/etc/c/odbc/sqlext.d
index c54ce29de1a..323ce568242 100644
--- a/etc/c/odbc/sqlext.d
+++ b/etc/c/odbc/sqlext.d
@@ -14,8 +14,8 @@ See_Also: $(LINK2 https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/odb
module etc.c.odbc.sqlext;
-private import etc.c.odbc.sql;
-private import etc.c.odbc.sqltypes;
+import etc.c.odbc.sql;
+import etc.c.odbc.sqltypes;
extern (Windows):
diff --git a/index.d b/index.d
index 52fed13c351..2792b7b0326 100644
--- a/index.d
+++ b/index.d
@@ -21,12 +21,12 @@ $(BOOKTABLE ,
$(LEADINGROW Algorithms & ranges)
$(TR
$(TDNW
- $(LINK2 std_algorithm.html, std.algorithm)$(BR)
- $(LINK2 std_range.html, std.range)$(BR)
- $(LINK2 std_range_primitives.html, std.range.primitives)$(BR)
- $(LINK2 std_range_interfaces.html, std.range.interfaces)$(BR)
+ $(MREF std,algorithm)$(BR)
+ $(MREF std,range)$(BR)
+ $(MREF std,range,primitives)$(BR)
+ $(MREF std,range,interfaces)$(BR)
)
- $(TD Generic algorithms that work with $(LINK2 std_range.html, ranges)
+ $(TD Generic algorithms that work with $(MREF_ALTTEXT ranges, std,range)
of any type, including strings, arrays, and other kinds of
sequentially-accessed data. Algorithms include searching,
comparison, iteration, sorting, set operations, and mutation.
@@ -35,8 +35,8 @@ $(BOOKTABLE ,
$(LEADINGROW Array manipulation)
$(TR
$(TDNW
- $(LINK2 std_array.html, std.array)$(BR)
- $(LINK2 std_algorithm.html, std.algorithm)$(BR)
+ $(MREF std,array)$(BR)
+ $(MREF std,algorithm)$(BR)
)
$(TD Convenient operations commonly used with built-in arrays.
Note that many common array operations are subsets of more generic
@@ -47,155 +47,155 @@ $(BOOKTABLE ,
$(LEADINGROW Containers)
$(TR
$(TDNW
- $(LINK2 std_container_array.html, std.container.array)$(BR)
- $(LINK2 std_container_binaryheap.html, std.container.binaryheap)$(BR)
- $(LINK2 std_container_dlist.html, std.container.dlist)$(BR)
- $(LINK2 std_container_rbtree.html, std.container.rbtree)$(BR)
- $(LINK2 std_container_slist.html, std.container.slist)$(BR)
+ $(MREF std,container,array)$(BR)
+ $(MREF std,container,binaryheap)$(BR)
+ $(MREF std,container,dlist)$(BR)
+ $(MREF std,container,rbtree)$(BR)
+ $(MREF std,container,slist)$(BR)
)
- $(TD See $(LINK2 std_container.html, std.container.*) for an
+ $(TD See $(MREF_ALTTEXT std.container.*, std,container) for an
overview.
)
)
$(LEADINGROW Data formats)
$(TR
- $(TDNW $(LINK2 std_base64.html, std.base64))
+ $(TDNW $(MREF std,base64))
$(TD Encoding / decoding Base64 format.)
)
$(TR
- $(TDNW $(LINK2 std_csv.html, std.csv))
+ $(TDNW $(MREF std,csv))
$(TD Read Comma Separated Values and its variants from an input range of $(CODE dchar).)
)
$(TR
- $(TDNW $(LINK2 std_json.html, std.json))
+ $(TDNW $(MREF std,json))
$(TD Read/write data in JSON format.)
)
$(TR
- $(TDNW $(LINK2 std_xml.html, std.xml))
+ $(TDNW $(MREF std,xml))
$(TD Read/write data in XML format.)
)
$(TR
- $(TDNW $(LINK2 std_zip.html, std.zip))
+ $(TDNW $(MREF std,zip))
$(TD Read/write data in the ZIP archive format.)
)
$(TR
- $(TDNW $(LINK2 std_zlib.html, std.zlib))
+ $(TDNW $(MREF std,zlib))
$(TD Compress/decompress data using the zlib library.)
)
$(LEADINGROW Data integrity)
$(TR
- $(TDNW $(LINK2 std_experimental_checkedint.html, std.experimental.checkedint))
+ $(TDNW $(MREF std,experimental,checkedint))
$(TD Checked integral types.)
)
$(TR
- $(TDNW $(LINK2 std_digest_crc.html, std.digest.crc))
+ $(TDNW $(MREF std,digest,crc))
$(TD Cyclic Redundancy Check (32-bit) implementation.)
)
$(TR
- $(TDNW $(LINK2 std_digest_digest.html, std.digest.digest))
+ $(TDNW $(MREF std,digest,digest))
$(TD Compute digests such as md5, sha1 and crc32.)
)
$(TR
- $(TDNW $(LINK2 std_digest_hmac.html, std.digest.hmac))
+ $(TDNW $(MREF std,digest,hmac))
$(TD Compute HMAC digests of arbitrary data.)
)
$(TR
- $(TDNW $(LINK2 std_digest_md.html, std.digest.md))
+ $(TDNW $(MREF std,digest,md))
$(TD Compute MD5 hash of arbitrary data.)
)
$(TR
- $(TDNW $(LINK2 std_digest_murmurhash.html, std.digest.murmurhash))
+ $(TDNW $(MREF std,digest,murmurhash))
$(TD Compute MurmurHash of arbitrary data.)
)
$(TR
- $(TDNW $(LINK2 std_digest_ripemd.html, std.digest.ripemd))
+ $(TDNW $(MREF std,digest,ripemd))
$(TD Compute RIPEMD-160 hash of arbitrary data.)
)
$(TR
- $(TDNW $(LINK2 std_digest_sha.html, std.digest.sha))
+ $(TDNW $(MREF std,digest,sha))
$(TD Compute SHA1 and SHA2 hashes of arbitrary data.)
)
$(LEADINGROW Date & time)
$(TR
- $(TDNW $(LINK2 std_datetime.html, std.datetime))
+ $(TDNW $(MREF std,datetime))
$(TD Provides convenient access to date and time representations.)
)
$(TR
- $(TDNW $(LINK2 core_time.html, core.time))
+ $(TDNW $(MREF core,time))
$(TD Implements low-level time primitives.)
)
$(LEADINGROW Exception handling)
$(TR
- $(TDNW $(LINK2 std_exception.html, std.exception))
+ $(TDNW $(MREF std,exception))
$(TD Implements routines related to exceptions.)
)
$(TR
- $(TDNW $(LINK2 core_exception.html, core.exception))
+ $(TDNW $(MREF core,exception))
$(TD Defines built-in exception types and low-level
language hooks required by the compiler.)
)
$(LEADINGROW External library bindings)
$(TR
- $(TDNW $(LINK2 etc_c_curl.html, etc.c.curl))
+ $(TDNW $(MREF etc,c,curl))
$(TD Interface to libcurl C library.)
)
$(TR
- $(TDNW $(LINK2 etc_c_odbc_sql.html, etc.c.odbc.sql))
+ $(TDNW $(MREF etc,c,odbc,sql))
$(TD Interface to ODBC C library.)
)
$(TR
- $(TDNW $(LINK2 etc_c_odbc_sqlext.html, etc.c.odbc.sqlext))
+ $(TDNW $(MREF etc,c,odbc,sqlext))
)
$(TR
- $(TDNW $(LINK2 etc_c_odbc_sqltypes.html, etc.c.odbc.sqltypes))
+ $(TDNW $(MREF etc,c,odbc,sqltypes))
)
$(TR
- $(TDNW $(LINK2 etc_c_odbc_sqlucode.html, etc.c.odbc.sqlucode))
+ $(TDNW $(MREF etc,c,odbc,sqlucode))
)
$(TR
- $(TDNW $(LINK2 etc_c_sqlite3.html, etc.c.sqlite3))
+ $(TDNW $(MREF etc,c,sqlite3))
$(TD Interface to SQLite C library.)
)
$(TR
- $(TDNW $(LINK2 etc_c_zlib.html, etc.c.zlib))
+ $(TDNW $(MREF etc,c,zlib))
$(TD Interface to zlib C library.)
)
$(LEADINGROW I/O & File system)
$(TR
- $(TDNW $(LINK2 std_file.html, std.file))
+ $(TDNW $(MREF std,file))
$(TD Manipulate files and directories.)
)
$(TR
- $(TDNW $(LINK2 std_path.html, std.path))
+ $(TDNW $(MREF std,path))
$(TD Manipulate strings that represent filesystem paths.)
)
$(TR
- $(TDNW $(LINK2 std_stdio.html, std.stdio))
+ $(TDNW $(MREF std,stdio))
$(TD Perform buffered I/O.)
)
$(LEADINGROW Interoperability)
$(TR
$(TDNW
- $(LINK2 core_stdc_complex.html, core.stdc.complex)$(BR)
- $(LINK2 core_stdc_ctype.html, core.stdc.ctype)$(BR)
- $(LINK2 core_stdc_errno.html, core.stdc.errno)$(BR)
- $(LINK2 core_stdc_fenv.html, core.stdc.fenv)$(BR)
- $(LINK2 core_stdc_float_.html, core.stdc.float_)$(BR)
- $(LINK2 core_stdc_inttypes.html, core.stdc.inttypes)$(BR)
- $(LINK2 core_stdc_limits.html, core.stdc.limits)$(BR)
- $(LINK2 core_stdc_locale.html, core.stdc.locale)$(BR)
- $(LINK2 core_stdc_math.html, core.stdc.math)$(BR)
- $(LINK2 core_stdc_signal.html, core.stdc.signal)$(BR)
- $(LINK2 core_stdc_stdarg.html, core.stdc.stdarg)$(BR)
- $(LINK2 core_stdc_stddef.html, core.stdc.stddef)$(BR)
- $(LINK2 core_stdc_stdint.html, core.stdc.stdint)$(BR)
- $(LINK2 core_stdc_stdio.html, core.stdc.stdio)$(BR)
- $(LINK2 core_stdc_stdlib.html, core.stdc.stdlib)$(BR)
- $(LINK2 core_stdc_string.html, core.stdc.string)$(BR)
- $(LINK2 core_stdc_tgmath.html, core.stdc.tgmath)$(BR)
- $(LINK2 core_stdc_time.html, core.stdc.time)$(BR)
- $(LINK2 core_stdc_wchar_.html, core.stdc.wchar_)$(BR)
- $(LINK2 core_stdc_wctype.html, core.stdc.wctype)$(BR)
+ $(MREF core,stdc,complex)$(BR)
+ $(MREF core,stdc,ctype)$(BR)
+ $(MREF core,stdc,errno)$(BR)
+ $(MREF core,stdc,fenv)$(BR)
+ $(MREF core,stdc,float_)$(BR)
+ $(MREF core,stdc,inttypes)$(BR)
+ $(MREF core,stdc,limits)$(BR)
+ $(MREF core,stdc,locale)$(BR)
+ $(MREF core,stdc,math)$(BR)
+ $(MREF core,stdc,signal)$(BR)
+ $(MREF core,stdc,stdarg)$(BR)
+ $(MREF core,stdc,stddef)$(BR)
+ $(MREF core,stdc,stdint)$(BR)
+ $(MREF core,stdc,stdio)$(BR)
+ $(MREF core,stdc,stdlib)$(BR)
+ $(MREF core,stdc,string)$(BR)
+ $(MREF core,stdc,tgmath)$(BR)
+ $(MREF core,stdc,time)$(BR)
+ $(MREF core,stdc,wchar_)$(BR)
+ $(MREF core,stdc,wctype)$(BR)
)
$(TD
D bindings for standard C headers.$(BR)$(BR)
@@ -206,253 +206,253 @@ $(BOOKTABLE ,
)
$(LEADINGROW Memory management)
$(TR
- $(TDNW $(LINK2 core_memory.html, core.memory))
+ $(TDNW $(MREF core,memory))
$(TD Control the built-in garbage collector.)
)
$(TR
- $(TDNW $(LINK2 std_typecons.html, std.typecons))
+ $(TDNW $(MREF std,typecons))
$(TD Build scoped variables and reference-counted types.)
)
$(LEADINGROW Metaprogramming)
$(TR
- $(TDNW $(LINK2 core_attribute.html, core.attribute))
+ $(TDNW $(MREF core,attribute))
$(TD Definitions of special attributes recognized by the compiler.)
)
$(TR
- $(TDNW $(LINK2 core_demangle.html, core.demangle))
+ $(TDNW $(MREF core,demangle))
$(TD Convert $(I mangled) D symbol identifiers to source representation.)
)
$(TR
- $(TDNW $(LINK2 std_demangle.html, std.demangle))
+ $(TDNW $(MREF std,demangle))
$(TD A simple wrapper around core.demangle.)
)
$(TR
- $(TDNW $(LINK2 std_meta.html, std.meta))
+ $(TDNW $(MREF std,meta))
$(TD Construct and manipulate template argument lists (aka type lists).)
)
$(TR
- $(TDNW $(LINK2 std_traits.html, std.traits))
+ $(TDNW $(MREF std,traits))
$(TD Extract information about types and symbols at compile time.)
)
$(TR
- $(TDNW $(LINK2 std_typecons.html, std.typecons))
+ $(TDNW $(MREF std,typecons))
$(TD Construct new, useful general purpose types.)
)
$(LEADINGROW Multitasking)
$(TR
- $(TDNW $(LINK2 std_concurrency.html, std.concurrency))
+ $(TDNW $(MREF std,concurrency))
$(TD Low level messaging API for threads.)
)
$(TR
- $(TDNW $(LINK2 std_parallelism.html, std.parallelism))
+ $(TDNW $(MREF std,parallelism))
$(TD High level primitives for SMP parallelism.)
)
$(TR
- $(TDNW $(LINK2 std_process.html, std.process))
+ $(TDNW $(MREF std,process))
$(TD Starting and manipulating processes.)
)
$(TR
- $(TDNW $(LINK2 core_atomic.html, core.atomic))
+ $(TDNW $(MREF core,atomic))
$(TD Basic support for lock-free concurrent programming.)
)
$(TR
- $(TDNW $(LINK2 core_sync_barrier.html, core.sync.barrier))
+ $(TDNW $(MREF core,sync,barrier))
$(TD Synchronize the progress of a group of threads.)
)
$(TR
- $(TDNW $(LINK2 core_sync_condition.html, core.sync.condition))
+ $(TDNW $(MREF core,sync,condition))
$(TD Synchronized condition checking.)
)
$(TR
- $(TDNW $(LINK2 core_sync_exception.html, core.sync.exception))
+ $(TDNW $(MREF core,sync,exception))
$(TD Base class for synchronization exceptions.)
)
$(TR
- $(TDNW $(LINK2 core_sync_mutex.html, core.sync.mutex))
+ $(TDNW $(MREF core,sync,mutex))
$(TD Mutex for mutually exclusive access.)
)
$(TR
- $(TDNW $(LINK2 core_sync_rwmutex.html, core.sync.rwmutex))
+ $(TDNW $(MREF core,sync,rwmutex))
$(TD Shared read access and mutually exclusive write access.)
)
$(TR
- $(TDNW $(LINK2 core_sync_semaphore.html, core.sync.semaphore))
+ $(TDNW $(MREF core,sync,semaphore))
$(TD General use synchronization semaphore.)
)
$(TR
- $(TDNW $(LINK2 core_thread.html, core.thread))
+ $(TDNW $(MREF core,thread))
$(TD Thread creation and management.)
)
$(LEADINGROW Networking)
$(TR
- $(TDNW $(LINK2 std_socket.html, std.socket))
+ $(TDNW $(MREF std,socket))
$(TD Socket primitives.)
)
$(TR
- $(TDNW $(LINK2 std_net_curl.html, std.net.curl))
+ $(TDNW $(MREF std,net,curl))
$(TD Networking client functionality as provided by libcurl.)
)
$(TR
- $(TDNW $(LINK2 std_net_isemail.html, std.net.isemail))
+ $(TDNW $(MREF std,net,isemail))
$(TD Validates an email address according to RFCs 5321, 5322 and others.)
)
$(TR
- $(TDNW $(LINK2 std_uri.html, std.uri))
+ $(TDNW $(MREF std,uri))
$(TD Encode and decode Uniform Resource Identifiers (URIs).)
)
$(TR
- $(TDNW $(LINK2 std_uuid.html, std.uuid))
+ $(TDNW $(MREF std,uuid))
$(TD Universally-unique identifiers for resources in distributed
systems.)
)
$(LEADINGROW Numeric)
$(TR
- $(TDNW $(LINK2 std_bigint.html, std.bigint))
+ $(TDNW $(MREF std,bigint))
$(TD An arbitrary-precision integer type.)
)
$(TR
- $(TDNW $(LINK2 std_complex.html, std.complex))
+ $(TDNW $(MREF std,complex))
$(TD A complex number type.)
)
$(TR
- $(TDNW $(LINK2 std_math.html, std.math))
+ $(TDNW $(MREF std,math))
$(TD Elementary mathematical functions (powers, roots, trigonometry).)
)
$(TR
- $(TDNW $(LINK2 std_mathspecial.html, std.mathspecial))
+ $(TDNW $(MREF std,mathspecial))
$(TD Families of transcendental functions.)
)
$(TR
- $(TDNW $(LINK2 std_numeric.html, std.numeric))
+ $(TDNW $(MREF std,numeric))
$(TD Floating point numerics functions.)
)
$(TR
- $(TDNW $(LINK2 std_random.html, std.random))
+ $(TDNW $(MREF std,random))
$(TD Pseudo-random number generators.)
)
$(TR
- $(TDNW $(LINK2 core_checkedint.html, core.checkedint))
+ $(TDNW $(MREF core,checkedint))
$(TD Range-checking integral arithmetic primitives.)
)
$(TR
- $(TDNW $(LINK2 core_math.html, core.math))
+ $(TDNW $(MREF core,math))
$(TD Built-in mathematical intrinsics.)
)
$(LEADINGROW Paradigms)
$(TR
- $(TDNW $(LINK2 std_functional.html, std.functional))
+ $(TDNW $(MREF std,functional))
$(TD Functions that manipulate other functions.)
)
$(TR
- $(TDNW $(LINK2 std_algorithm.html, std.algorithm))
+ $(TDNW $(MREF std,algorithm))
$(TD Generic algorithms for processing sequences.)
)
$(TR
- $(TDNW $(LINK2 std_signals.html, std.signals))
+ $(TDNW $(MREF std,signals))
$(TD Signal-and-slots framework for event-driven programming.)
)
$(LEADINGROW Runtime utilities)
$(TR
- $(TDNW $(LINK2 object.html, object))
+ $(TDNW $(MREF1 object))
$(TD Core language definitions. Automatically imported.)
)
$(TR
- $(TDNW $(LINK2 std_getopt.html, std.getopt))
+ $(TDNW $(MREF std,getopt))
$(TD Parsing of command-line arguments.)
)
$(TR
- $(TDNW $(LINK2 std_compiler.html, std.compiler))
+ $(TDNW $(MREF std,compiler))
$(TD Host compiler vendor string and language version.)
)
$(TR
- $(TDNW $(LINK2 std_system.html, std.system))
+ $(TDNW $(MREF std,system))
$(TD Runtime environment, such as OS type and endianness.)
)
$(TR
- $(TDNW $(LINK2 core_cpuid.html, core.cpuid))
+ $(TDNW $(MREF core,cpuid))
$(TD Capabilities of the CPU the program is running on.)
)
$(TR
- $(TDNW $(LINK2 core_memory.html, core.memory))
+ $(TDNW $(MREF core,memory))
$(TD Control the built-in garbage collector.)
)
$(TR
- $(TDNW $(LINK2 core_runtime.html, core.runtime))
+ $(TDNW $(MREF core,runtime))
$(TD Control and configure the D runtime.)
)
$(LEADINGROW String manipulation)
$(TR
- $(TDNW $(LINK2 std_string.html, std.string))
+ $(TDNW $(MREF std,string))
$(TD Algorithms that work specifically with strings.)
)
$(TR
- $(TDNW $(LINK2 std_array.html, std.array))
+ $(TDNW $(MREF std,array))
$(TD Manipulate builtin arrays.)
)
$(TR
- $(TDNW $(LINK2 std_algorithm.html, std.algorithm))
+ $(TDNW $(MREF std,algorithm))
$(TD Generic algorithms for processing sequences.)
)
$(TR
- $(TDNW $(LINK2 std_uni.html, std.uni))
+ $(TDNW $(MREF std,uni))
$(TD Fundamental Unicode algorithms and data structures.)
)
$(TR
- $(TDNW $(LINK2 std_utf.html, std.utf))
+ $(TDNW $(MREF std,utf))
$(TD Encode and decode UTF-8, UTF-16 and UTF-32 strings.)
)
$(TR
- $(TDNW $(LINK2 std_format.html, std.format))
+ $(TDNW $(MREF std,format))
$(TD Format data into strings.)
)
$(TR
- $(TDNW $(LINK2 std_path.html, std.path))
+ $(TDNW $(MREF std,path))
$(TD Manipulate strings that represent filesystem paths.)
)
$(TR
- $(TDNW $(LINK2 std_regex.html, std.regex))
+ $(TDNW $(MREF std,regex))
$(TD Regular expressions.)
)
$(TR
- $(TDNW $(LINK2 std_ascii.html, std.ascii))
+ $(TDNW $(MREF std,ascii))
$(TD Routines specific to the ASCII subset of Unicode.)
)
$(TR
- $(TDNW $(LINK2 std_encoding.html, std.encoding))
+ $(TDNW $(MREF std,encoding))
$(TD Handle and transcode between various text encodings.)
)
$(TR
- $(TDNW $(LINK2 std_windows_charset.html, std.windows.charset))
+ $(TDNW $(MREF std,windows,charset))
$(TD Windows specific character set support.)
)
$(TR
- $(TDNW $(LINK2 std_outbuffer.html, std.outbuffer))
+ $(TDNW $(MREF std,outbuffer))
$(TD Serialize data to $(CODE ubyte) arrays.)
)
$(LEADINGROW Type manipulations)
$(TR
- $(TDNW $(LINK2 std_conv.html, std.conv))
+ $(TDNW $(MREF std,conv))
$(TD Convert types from one type to another.)
)
$(TR
- $(TDNW $(LINK2 std_typecons.html, std.typecons))
+ $(TDNW $(MREF std,typecons))
$(TD Type constructors for scoped variables, ref counted types, etc.)
)
$(TR
- $(TDNW $(LINK2 std_bitmanip.html, std.bitmanip))
+ $(TDNW $(MREF std,bitmanip))
$(TD High level bit level manipulation, bit arrays, bit fields.)
)
$(TR
- $(TDNW $(LINK2 std_variant.html, std.variant))
+ $(TDNW $(MREF std,variant))
$(TD Discriminated unions and algebraic types.)
)
$(TR
- $(TDNW $(LINK2 core_bitop.html, core.bitop))
+ $(TDNW $(MREF core,bitop))
$(TD Low level bit manipulation.)
)
$(LEADINGROW Vector programming)
$(TR
- $(TDNW $(LINK2 core_simd.html, core.simd))
+ $(TDNW $(MREF core,simd))
$(TD SIMD intrinsics)
)
@@ -460,15 +460,15 @@ $(COMMENT
$(LEADINGROW Undocumented modules (intentionally omitted).)
$(TR
$(TDNW
- $(LINK2 core_sync_config.html, core.sync.config)$(BR)
- $(LINK2 std_container_util.html, std.container.util)$(BR)
- $(LINK2 std_regex_internal_backtracking.html, std.regex.internal.backtracking)$(BR)
- $(LINK2 std_regex_internal_generator.html, std.regex.internal.generator)$(BR)
- $(LINK2 std_regex_internal_ir.html, std.regex.internal.ir)$(BR)
- $(LINK2 std_regex_internal_kickstart.html, std.regex.internal.kickstart)$(BR)
- $(LINK2 std_regex_internal_parser.html, std.regex.internal.parser)$(BR)
- $(LINK2 std_regex_internal_tests.html, std.regex.internal.tests)$(BR)
- $(LINK2 std_regex_internal_thompson.html, std.regex.internal.thompson)$(BR)
+ $(MREF core,sync,config)$(BR)
+ $(MREF std,container,util)$(BR)
+ $(MREF std,regex,internal,backtracking)$(BR)
+ $(MREF std,regex,internal,generator)$(BR)
+ $(MREF std,regex,internal,ir)$(BR)
+ $(MREF std,regex,internal,kickstart)$(BR)
+ $(MREF std,regex,internal,parser)$(BR)
+ $(MREF std,regex,internal,tests)$(BR)
+ $(MREF std,regex,internal,thompson)$(BR)
)
$(TD
Internal modules.
@@ -476,21 +476,21 @@ $(COMMENT
)
$(TR
$(TDNW
- $(LINK2 core_vararg.html, core.vararg)$(BR)
- $(LINK2 std_c_fenv.html, std.c.fenv)$(BR)
- $(LINK2 std_c_linux_linux.html, std.c.linux_linux)$(BR)
- $(LINK2 std_c_linux_socket.html, std.c.linux_socket)$(BR)
- $(LINK2 std_c_locale.html, std.c.locale)$(BR)
- $(LINK2 std_c_math.html, std.c.math)$(BR)
- $(LINK2 std_c_process.html, std.c.process)$(BR)
- $(LINK2 std_c_stdarg.html, std.c.stdarg)$(BR)
- $(LINK2 std_c_stddef.html, std.c.stddef)$(BR)
- $(LINK2 std_c_stdio.html, std.c.stdio)$(BR)
- $(LINK2 std_c_stdlib.html, std.c.stdlib)$(BR)
- $(LINK2 std_c_string.html, std.c.string)$(BR)
- $(LINK2 std_c_time.html, std.c.time)$(BR)
- $(LINK2 std_c_wcharh.html, std.c.wcharh)$(BR)
- $(LINK2 std_stdint.html, std.stdint)$(BR)
+ $(MREF core,vararg)$(BR)
+ $(MREF std,c,fenv)$(BR)
+ $(MREF std,c,linux,linux)$(BR)
+ $(MREF std,c,linux,socket)$(BR)
+ $(MREF std,c,locale)$(BR)
+ $(MREF std,c,math)$(BR)
+ $(MREF std,c,process)$(BR)
+ $(MREF std,c,stdarg)$(BR)
+ $(MREF std,c,stddef)$(BR)
+ $(MREF std,c,stdio)$(BR)
+ $(MREF std,c,stdlib)$(BR)
+ $(MREF std,c,string)$(BR)
+ $(MREF std,c,time)$(BR)
+ $(MREF std,c,wcharh)$(BR)
+ $(MREF std,stdint)$(BR)
)
$(TDN
Redirect modules.
@@ -498,8 +498,8 @@ $(COMMENT
)
$(TR
$(TDNW
- $(LINK2 std_mmfile.html, std.mmfile)$(BR)
- $(LINK2 std_typetuple.html, std.typetuple)$(BR)
+ $(MREF std,mmfile)$(BR)
+ $(MREF std,typetuple)$(BR)
)
$(TD
Deprecated modules.
@@ -507,11 +507,11 @@ $(COMMENT
)
$(TR
$(TDNW
- $(LINK2 std_experimental_logger.html, std.experimental.logger)$(BR)
- $(LINK2 std_experimental_logger_core.html, std.experimental.logger.core)$(BR)
- $(LINK2 std_experimental_logger_filelogger.html, std.experimental.logger.filelogger)$(BR)
- $(LINK2 std_experimental_logger_multilogger.html, std.experimental.logger.multilogger)$(BR)
- $(LINK2 std_experimental_logger_nulllogger.html, std.experimental.logger.nulllogger)$(BR)
+ $(MREF std,experimental,logger)$(BR)
+ $(MREF std,experimental,logger,core)$(BR)
+ $(MREF std,experimental,logger,filelogger)$(BR)
+ $(MREF std,experimental,logger,multilogger)$(BR)
+ $(MREF std,experimental,logger,nulllogger)$(BR)
)
$(TD
Experimental modules.
diff --git a/osmodel.mak b/osmodel.mak
deleted file mode 100644
index d0f160ae374..00000000000
--- a/osmodel.mak
+++ /dev/null
@@ -1,55 +0,0 @@
-# This Makefile snippet detects the OS and the architecture MODEL
-# Keep this file in sync between druntime, phobos, and dmd repositories!
-
-ifeq (,$(OS))
- uname_S:=$(shell uname -s)
- ifeq (Darwin,$(uname_S))
- OS:=osx
- endif
- ifeq (Linux,$(uname_S))
- OS:=linux
- endif
- ifeq (FreeBSD,$(uname_S))
- OS:=freebsd
- endif
- ifeq (NetBSD,$(uname_S))
- OS:=netbsd
- endif
- ifeq (OpenBSD,$(uname_S))
- OS:=openbsd
- endif
- ifeq (Solaris,$(uname_S))
- OS:=solaris
- endif
- ifeq (SunOS,$(uname_S))
- OS:=solaris
- endif
- ifeq (,$(OS))
- $(error Unrecognized or unsupported OS for uname: $(uname_S))
- endif
-endif
-
-# When running make from XCode it may set environment var OS=MACOS.
-# Adjust it here:
-ifeq (MACOS,$(OS))
- OS:=osx
-endif
-
-ifeq (,$(MODEL))
- ifeq ($(OS), solaris)
- uname_M:=$(shell isainfo -n)
- else
- uname_M:=$(shell uname -m)
- endif
- ifneq (,$(findstring $(uname_M),x86_64 amd64))
- MODEL:=64
- endif
- ifneq (,$(findstring $(uname_M),i386 i586 i686))
- MODEL:=32
- endif
- ifeq (,$(MODEL))
- $(error Cannot figure 32/64 model from uname -m: $(uname_M))
- endif
-endif
-
-MODEL_FLAG:=-m$(MODEL)
diff --git a/posix.mak b/posix.mak
index 653bb8e870e..321bdb1066b 100644
--- a/posix.mak
+++ b/posix.mak
@@ -31,8 +31,10 @@
QUIET:=
DEBUGGER=gdb
+GIT_HOME=https://github.com/dlang
+DMD_DIR=../dmd
-include osmodel.mak
+include $(DMD_DIR)/src/osmodel.mak
ifeq (osx,$(OS))
export MACOSX_DEPLOYMENT_TARGET=10.7
@@ -61,8 +63,9 @@ ZIPFILE = phobos.zip
ROOT_OF_THEM_ALL = generated
ROOT = $(ROOT_OF_THEM_ALL)/$(OS)/$(BUILD)/$(MODEL)
DUB=dub
-GIT_HOME=https://github.com/dlang
TOOLS_DIR=../tools
+DSCANNER_HASH=071cd08a6de9bbe1720c763b0aff4d19864b27f1
+DSCANNER_DIR=../dscanner-$(DSCANNER_HASH)
# Documentation-related stuff
DOCSRC = ../dlang.org
@@ -94,7 +97,7 @@ ifeq ($(OS),win32wine)
DMD = wine dmd.exe
RUN = wine
else
- DMD = ../dmd/generated/$(OS)/release/$(MODEL)/dmd
+ DMD = $(DMD_DIR)/generated/$(OS)/release/$(MODEL)/dmd
ifeq ($(OS),win32)
CC = dmc
else
@@ -112,7 +115,7 @@ else
endif
# Set DFLAGS
-DFLAGS=-conf= -I$(DRUNTIME_PATH)/import $(DMDEXTRAFLAGS) -w -dip25 $(MODEL_FLAG) $(PIC)
+DFLAGS=-conf= -I$(DRUNTIME_PATH)/import $(DMDEXTRAFLAGS) -w -de -dip25 $(MODEL_FLAG) $(PIC)
ifeq ($(BUILD),debug)
DFLAGS += -g -debug
else
@@ -140,7 +143,7 @@ LINKDL:=$(if $(findstring $(OS),linux),-L-ldl,)
TIMELIMIT:=$(if $(shell which timelimit 2>/dev/null || true),timelimit -t 90 ,)
# Set VERSION, where the file is that contains the version string
-VERSION=../dmd/VERSION
+VERSION=$(DMD_DIR)/VERSION
# Set LIB, the ultimate target
ifeq (,$(findstring win,$(OS)))
@@ -179,14 +182,14 @@ PACKAGE_std = array ascii base64 bigint bitmanip compiler complex concurrency \
conv csv demangle encoding exception file format \
functional getopt json math mathspecial meta mmfile numeric \
outbuffer parallelism path process random signals socket stdint \
- stdio string system traits typecons typetuple uni \
+ stdio string system traits typecons uni \
uri utf uuid variant xml zip zlib
PACKAGE_std_experimental = checkedint typecons
PACKAGE_std_algorithm = comparison iteration mutation package searching setops \
sorting
PACKAGE_std_container = array binaryheap dlist package rbtree slist util
PACKAGE_std_datetime = date interval package stopwatch systime timezone
-PACKAGE_std_digest = crc digest hmac md murmurhash ripemd sha
+PACKAGE_std_digest = crc digest hmac md murmurhash package ripemd sha
PACKAGE_std_experimental_logger = core filelogger \
nulllogger multilogger package
PACKAGE_std_experimental_allocator = \
@@ -227,6 +230,7 @@ EXTRA_MODULES_INTERNAL := $(addprefix std/, \
scopebuffer test/dummyrange \
$(addprefix unicode_, comp decomp grapheme norm tables) \
) \
+ typetuple \
)
EXTRA_MODULES += $(EXTRA_DOCUMENTABLES) $(EXTRA_MODULES_INTERNAL)
@@ -259,12 +263,12 @@ SHARED=$(if $(findstring $(OS),linux freebsd),1,)
# A blacklist of ignored module is provided as not all public unittest in
# Phobos are independently runnable yet
IGNORED_PUBLICTESTS= $(addprefix std/, \
- base64 $(addprefix experimental/allocator/, \
+ $(addprefix experimental/allocator/, \
building_blocks/free_list building_blocks/quantizer \
- ) digest/hmac \
- file math stdio traits typecons uuid)
+ ) \
+ math stdio traits)
PUBLICTESTS= $(addsuffix .publictests,$(filter-out $(IGNORED_PUBLICTESTS), $(D_MODULES)))
-TEST_EXTRACTOR=$(TOOLS_DIR)/styles/test_extractor
+TESTS_EXTRACTOR=$(ROOT)/tests_extractor
PUBLICTESTS_DIR=$(ROOT)/publictests
################################################################################
@@ -504,12 +508,12 @@ changelog.html: changelog.dd
${TOOLS_DIR}:
git clone --depth=1 ${GIT_HOME}/$(@F) $@
+
$(TOOLS_DIR)/checkwhitespace.d: | $(TOOLS_DIR)
-$(TOOLS_DIR)/styles/tests_extractor.d: | $(TOOLS_DIR)
-$(TOOLS_DIR)/styles/has_public_example.d: | $(TOOLS_DIR)
+$(TOOLS_DIR)/tests_extractor.d: | $(TOOLS_DIR)
#################### test for undesired white spaces ##########################
-CWS_TOCHECK = posix.mak win32.mak win64.mak osmodel.mak
+CWS_TOCHECK = posix.mak win32.mak win64.mak
CWS_TOCHECK += $(ALL_D_FILES) index.d
checkwhitespace: $(LIB) $(TOOLS_DIR)/checkwhitespace.d
@@ -521,20 +525,25 @@ checkwhitespace: $(LIB) $(TOOLS_DIR)/checkwhitespace.d
# See also: http://dlang.org/dstyle.html
#############################
-../dscanner:
- git clone https://github.com/Hackerpilot/Dscanner ../dscanner
- git -C ../dscanner checkout tags/v0.4.0
- git -C ../dscanner submodule update --init --recursive
+$(DSCANNER_DIR):
+ git clone https://github.com/dlang-community/Dscanner $@
+ git -C $@ checkout $(DSCANNER_HASH)
+ git -C $@ submodule update --init --recursive
-../dscanner/dsc: ../dscanner
+$(DSCANNER_DIR)/dsc: | $(DSCANNER_DIR) $(DMD) $(LIB)
# debug build is faster, but disable 'missing import' messages (missing core from druntime)
- sed 's/dparse_verbose/StdLoggerDisableWarning/' ../dscanner/makefile > dscanner_makefile_tmp
- mv dscanner_makefile_tmp ../dscanner/makefile
- make -C ../dscanner githash debug
+ sed 's/dparse_verbose/StdLoggerDisableWarning/' $(DSCANNER_DIR)/makefile > $(DSCANNER_DIR)/dscanner_makefile_tmp
+ mv $(DSCANNER_DIR)/dscanner_makefile_tmp $(DSCANNER_DIR)/makefile
+ DC=$(DMD) DFLAGS="$(DFLAGS) -defaultlib=$(LIB)" make -C $(DSCANNER_DIR) githash debug
+
+style: publictests style_lint
-style: has_public_example publictests style_lint
+# runs static code analysis with Dscanner
+dscanner: | $(DSCANNER_DIR)/dsc
+ @echo "Running DScanner"
+ $(DSCANNER_DIR)/dsc --config .dscanner.ini --styleCheck etc std -I.
-style_lint: ../dscanner/dsc $(LIB)
+style_lint: dscanner $(LIB)
@echo "Check for trailing whitespace"
grep -nr '[[:blank:]]$$' etc std ; test $$? -eq 1
@@ -578,32 +587,24 @@ style_lint: ../dscanner/dsc $(LIB)
@echo "Check that Ddoc runs without errors"
$(DMD) $(DFLAGS) -defaultlib= -debuglib= $(LIB) -w -D -Df/dev/null -main -c -o- $$(find etc std -type f -name '*.d') 2>&1 | grep -v "Deprecation:"; test $$? -eq 1
- # at the moment libdparse has problems to parse some modules (->excludes)
- @echo "Running DScanner"
- ../dscanner/dsc --config .dscanner.ini --styleCheck $$(find etc std -type f -name '*.d' | grep -vE 'std/traits.d|std/typecons.d') -I.
-
################################################################################
# Check for missing imports in public unittest examples.
################################################################################
publictests: $(PUBLICTESTS)
-$(TEST_EXTRACTOR): $(TOOLS_DIR)/styles/tests_extractor.d $(LIB)
- DFLAGS="$(DFLAGS) $(LIB) -defaultlib= -debuglib= $(LINKDL)" $(DUB) build --force --compiler=$${PWD}/$(DMD) --root=$(TOOLS_DIR)/styles -c tests_extractor
+$(TESTS_EXTRACTOR): $(TOOLS_DIR)/tests_extractor.d | $(LIB)
+ DFLAGS="$(DFLAGS) $(LIB) -defaultlib= -debuglib= $(LINKDL)" $(DUB) build --force --compiler=$${PWD}/$(DMD) --single $<
+ mv $(TOOLS_DIR)/tests_extractor $@
################################################################################
# Extract public tests of a module and test them in an separate file (i.e. without its module)
# This is done to check for potentially missing imports in the examples, e.g.
# make -f posix.mak std/format.publictests
################################################################################
-%.publictests: %.d $(LIB) $(TEST_EXTRACTOR) | $(PUBLICTESTS_DIR)/.directory
- @$(TEST_EXTRACTOR) --inputdir $< --outputdir $(PUBLICTESTS_DIR)
+%.publictests: %.d $(LIB) $(TESTS_EXTRACTOR) | $(PUBLICTESTS_DIR)/.directory
+ @$(TESTS_EXTRACTOR) --inputdir $< --outputdir $(PUBLICTESTS_DIR)
@$(DMD) $(DFLAGS) -defaultlib= -debuglib= $(LIB) -main -unittest -run $(PUBLICTESTS_DIR)/$(subst /,_,$<)
-has_public_example: $(LIB)
- # checks whether public function have public examples (for now some modules are excluded)
- rm -rf ./out
- DFLAGS="$(DFLAGS) $(LIB) $(LINKDL)" $(DUB) -v --compiler=$${PWD}/$(DMD) --root=../tools/styles -c has_public_example -- --inputdir std --ignore "array.d,allocator,base64.d,bitmanip.d,concurrency.d,conv.d,csv.d,datetime/date.d,datetime/interval.d,datetime/package.d,datetime/stopwatch.d,datetime/systime.d,datetime/timezone.d,demangle.d,digest/hmac.d,digest/sha.d,encoding.d,exception.d,file.d,format.d,getopt.d,index.d,internal,isemail.d,json.d,logger/core.d,logger/nulllogger.d,math.d,mathspecial.d,net/curl.d,numeric.d,parallelism.d,path.d,process.d,random.d,range,regex/package.d,socket.d,stdio.d,string.d,traits.d,typecons.d,uni.d,unittest.d,uri.d,utf.d,uuid.d,xml.d,zlib.d"
-
.PHONY : auto-tester-build
auto-tester-build: all checkwhitespace
diff --git a/std/algorithm/comparison.d b/std/algorithm/comparison.d
index 566b686b16a..faa4d444fbd 100644
--- a/std/algorithm/comparison.d
+++ b/std/algorithm/comparison.d
@@ -1007,6 +1007,16 @@ enum EditOp : char
remove = 'r'
}
+///
+@safe unittest
+{
+ with(EditOp)
+ {
+ assert(levenshteinDistanceAndPath("foo", "foobar")[1] == [none, none, none, insert, insert, insert]);
+ assert(levenshteinDistanceAndPath("banana", "fazan")[1] == [substitute, none, substitute, none, none, remove]);
+ }
+}
+
private struct Levenshtein(Range, alias equals, CostType = size_t)
{
EditOp[] path()
diff --git a/std/algorithm/iteration.d b/std/algorithm/iteration.d
index 74eba6c3447..1ed78fb210e 100644
--- a/std/algorithm/iteration.d
+++ b/std/algorithm/iteration.d
@@ -5098,30 +5098,6 @@ if (isRandomAccessRange!Range && hasLength!Range)
private Range _r;
private bool _empty;
- // Explicitly undocumented. It will be removed in June 2017. @@@DEPRECATED_2017-06@@@
- deprecated("Private variable. Use front()")
- @property size_t[] indices() pure nothrow @nogc @safe { return _indices; }
-
- // Explicitly undocumented. It will be removed in June 2017. @@@DEPRECATED_2017-06@@@
- deprecated("Private variable. Don't set it manually")
- @property void indices(size_t[] indices) pure nothrow @nogc @safe { _indices = indices; }
-
- // Explicitly undocumented. It will be removed in June 2017. @@@DEPRECATED_2017-06@@@
- deprecated("Private variable. Use front()")
- @property size_t[] state() pure nothrow @nogc @safe { return _state; }
-
- // Explicitly undocumented. It will be removed in June 2017. @@@DEPRECATED_2017-06@@@
- deprecated("Private variable. Don't set it manually")
- @property void state(size_t[] state) pure nothrow @nogc @safe { state = state; }
-
- // Explicitly undocumented. It will be removed in June 2017. @@@DEPRECATED_2017-06@@@
- deprecated("Private variable. Access will be forbidden.")
- @property Range r() pure nothrow @nogc @safe { return _r; }
-
- // Explicitly undocumented. It will be removed in June 2017. @@@DEPRECATED_2017-06@@@
- deprecated("Private variable. Don't set it manually")
- @property void r(Range r) pure nothrow @nogc @safe { _r = r; }
-
///
this(Range r)
{
diff --git a/std/algorithm/mutation.d b/std/algorithm/mutation.d
index 49f3c850c7f..e4149e35149 100644
--- a/std/algorithm/mutation.d
+++ b/std/algorithm/mutation.d
@@ -1659,9 +1659,52 @@ enum SwapStrategy
stable,
}
+///
+@safe unittest
+{
+ import std.stdio;
+ import std.algorithm.sorting : partition;
+ int[] a = [0, 1, 2, 3];
+ assert(remove!(SwapStrategy.stable)(a, 1) == [0, 2, 3]);
+ a = [0, 1, 2, 3];
+ assert(remove!(SwapStrategy.unstable)(a, 1) == [0, 3, 2]);
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.sorting : partition;
+
+ // Put stuff greater than 3 on the left
+ auto arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+ assert(partition!(a => a > 3, SwapStrategy.stable)(arr) == [1, 2, 3]);
+ assert(arr == [4, 5, 6, 7, 8, 9, 10, 1, 2, 3]);
+
+ arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+ assert(partition!(a => a > 3, SwapStrategy.semistable)(arr) == [2, 3, 1]);
+ assert(arr == [4, 5, 6, 7, 8, 9, 10, 2, 3, 1]);
+
+ arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+ assert(partition!(a => a > 3, SwapStrategy.unstable)(arr) == [3, 2, 1]);
+ assert(arr == [10, 9, 8, 4, 5, 6, 7, 3, 2, 1]);
+}
+
/**
-Eliminates elements at given offsets from $(D range) and returns the
-shortened range. In the simplest call, one element is removed.
+Eliminates elements at given offsets from `range` and returns the shortened
+range.
+
+For example, here is how to _remove a single element from an array:
+
+----
+string[] a = [ "a", "b", "c", "d" ];
+a = a.remove(1); // remove element at offset 1
+assert(a == [ "a", "c", "d"]);
+----
+
+Note that `remove` does not change the length of the original _range directly;
+instead, it returns the shortened _range. If its return value is not assigned to
+the original _range, the original _range will retain its original length, though
+its contents will have changed:
----
int[] a = [ 3, 5, 7, 8 ];
@@ -1669,15 +1712,13 @@ assert(remove(a, 1) == [ 3, 7, 8 ]);
assert(a == [ 3, 7, 8, 8 ]);
----
-In the case above the element at offset $(D 1) is removed and $(D
-remove) returns the range smaller by one element. The original array
-has remained of the same length because all functions in $(D
-std.algorithm) only change $(I content), not $(I topology). The value
-$(D 8) is repeated because $(LREF move) was invoked to
-move elements around and on integers $(D move) simply copies the source to
-the destination. To replace $(D a) with the effect of the removal,
-simply assign $(D a = remove(a, 1)). The slice will be rebound to the
-shorter array and the operation completes with maximal efficiency.
+The element at _offset `1` has been removed and the rest of the elements have
+shifted up to fill its place, however, the original array remains of the same
+length. This is because all functions in `std.algorithm` only change $(I
+content), not $(I topology). The value `8` is repeated because $(LREF move) was
+invoked to rearrange elements, and on integers `move` simply copies the source
+to the destination. To replace `a` with the effect of the removal, simply
+assign the slice returned by `remove` to it, as shown in the first example.
Multiple indices can be passed into $(D remove). In that case,
elements at the respective indices are all removed. The indices must
@@ -1689,7 +1730,7 @@ assert(remove(a, 1, 3, 5) ==
[ 0, 2, 4, 6, 7, 8, 9, 10 ]);
----
-(Note how all indices refer to slots in the $(I original) array, not
+(Note that all indices refer to slots in the $(I original) array, not
in the array as it is being progressively shortened.) Finally, any
combination of integral offsets and tuples composed of two integral
offsets can be passed in.
@@ -1735,7 +1776,7 @@ cases.))
Params:
s = a SwapStrategy to determine if the original order needs to be preserved
- range = a $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives)
+ range = a $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,_range,primitives)
with a length member
offset = which element(s) to remove
@@ -1970,7 +2011,7 @@ if (s == SwapStrategy.stable
/**
Reduces the length of the
-$(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives) $(D range) by removing
+$(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,_range,primitives) $(D range) by removing
elements that satisfy $(D pred). If $(D s = SwapStrategy.unstable),
elements are moved from the right end of the range over the elements
to eliminate. If $(D s = SwapStrategy.stable) (the default),
diff --git a/std/algorithm/package.d b/std/algorithm/package.d
index 656dbcfd85e..4c9a72f71c9 100644
--- a/std/algorithm/package.d
+++ b/std/algorithm/package.d
@@ -116,7 +116,8 @@ $(TR
$(SUBREF setops, cartesianProduct)
$(SUBREF setops, largestPartialIntersection)
$(SUBREF setops, largestPartialIntersectionWeighted)
- $(SUBREF setops, nWayUnion)
+ $(SUBREF setops, multiwayMerge)
+ $(SUBREF setops, multiwayUnion)
$(SUBREF setops, setDifference)
$(SUBREF setops, setIntersection)
$(SUBREF setops, setSymmetricDifference)
diff --git a/std/algorithm/searching.d b/std/algorithm/searching.d
index 72a6f341d39..5ea5f801c4d 100644
--- a/std/algorithm/searching.d
+++ b/std/algorithm/searching.d
@@ -604,6 +604,11 @@ $(D 2).
The third version counts the elements for which $(D pred(x)) is $(D
true). Performs $(BIGOH haystack.length) evaluations of $(D pred).
+The fourth version counts the number of elements in a range. It is
+an optimization for the third version: if the given range has the
+`length` property the count is returned right away, otherwise
+performs $(BIGOH haystack.length) to walk the range.
+
Note: Regardless of the overload, $(D count) will not accept
infinite ranges for $(D haystack).
@@ -630,6 +635,7 @@ if (isInputRange!Range && !isInfinite!Range &&
// count elements in range
int[] a = [ 1, 2, 4, 3, 2, 5, 3, 2, 4 ];
+ assert(count(a) == 9);
assert(count(a, 2) == 3);
assert(count!("a > b")(a, 2) == 5);
// count range in range
@@ -637,7 +643,7 @@ if (isInputRange!Range && !isInfinite!Range &&
assert(count("ababab", "abab") == 1);
assert(count("ababab", "abx") == 0);
// fuzzy count range in range
- assert(count!((a, b) => std.uni.toLower(a) == std.uni.toLower(b))("AbcAdFaBf", "ab") == 2);
+ assert(count!((a, b) => toLower(a) == toLower(b))("AbcAdFaBf", "ab") == 2);
// count predicate in range
assert(count!("a > 1")(a) == 8);
}
@@ -692,7 +698,7 @@ if (isForwardRange!R1 && !isInfinite!R1 &&
}
/// Ditto
-size_t count(alias pred = "true", R)(R haystack)
+size_t count(alias pred, R)(R haystack)
if (isInputRange!R && !isInfinite!R &&
is(typeof(unaryFun!pred(haystack.front)) : bool))
{
@@ -703,6 +709,13 @@ if (isInputRange!R && !isInfinite!R &&
return result;
}
+/// Ditto
+size_t count(R)(R haystack)
+if (isInputRange!R && !isInfinite!R)
+{
+ return walkLength(haystack);
+}
+
@safe unittest
{
int[] a = [ 1, 2, 4, 3, 2, 5, 3, 2, 4 ];
@@ -1875,25 +1888,202 @@ such position exists, returns $(D haystack) advanced to termination).
*/
R1 find(alias pred = "a == b", R1, R2)(R1 haystack, scope R2 needle)
if (isForwardRange!R1 && isForwardRange!R2
- && is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool)
- && !isRandomAccessRange!R1)
+ && is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool))
{
- static if (is(typeof(pred == "a == b")) && pred == "a == b" && isSomeString!R1 && isSomeString!R2
- && haystack[0].sizeof == needle[0].sizeof)
+ static if (!isRandomAccessRange!R1)
{
- // return cast(R1) find(representation(haystack), representation(needle));
- // Specialization for simple string search
- alias Representation =
- Select!(haystack[0].sizeof == 1, ubyte[],
- Select!(haystack[0].sizeof == 2, ushort[], uint[]));
- // Will use the array specialization
- static TO force(TO, T)(T r) @trusted { return cast(TO) r; }
- return force!R1(.find!(pred, Representation, Representation)
- (force!Representation(haystack), force!Representation(needle)));
+ static if (is(typeof(pred == "a == b")) && pred == "a == b" && isSomeString!R1 && isSomeString!R2
+ && haystack[0].sizeof == needle[0].sizeof)
+ {
+ // return cast(R1) find(representation(haystack), representation(needle));
+ // Specialization for simple string search
+ alias Representation =
+ Select!(haystack[0].sizeof == 1, ubyte[],
+ Select!(haystack[0].sizeof == 2, ushort[], uint[]));
+ // Will use the array specialization
+ static TO force(TO, T)(T r) @trusted { return cast(TO) r; }
+ return force!R1(.find!(pred, Representation, Representation)
+ (force!Representation(haystack), force!Representation(needle)));
+ }
+ else
+ {
+ return simpleMindedFind!pred(haystack, needle);
+ }
}
- else
+ else static if (!isBidirectionalRange!R2 || !hasSlicing!R1)
{
- return simpleMindedFind!pred(haystack, needle);
+ static if (!is(ElementType!R1 == ElementType!R2))
+ {
+ return simpleMindedFind!pred(haystack, needle);
+ }
+ else
+ {
+ // Prepare the search with needle's first element
+ if (needle.empty)
+ return haystack;
+
+ haystack = .find!pred(haystack, needle.front);
+
+ static if (hasLength!R1 && hasLength!R2 && is(typeof(takeNone(haystack)) == R1))
+ {
+ if (needle.length > haystack.length)
+ return takeNone(haystack);
+ }
+ else
+ {
+ if (haystack.empty)
+ return haystack;
+ }
+
+ needle.popFront();
+ size_t matchLen = 1;
+
+ // Loop invariant: haystack[0 .. matchLen] matches everything in
+ // the initial needle that was popped out of needle.
+ for (;;)
+ {
+ // Extend matchLength as much as possible
+ for (;;)
+ {
+ import std.range : takeNone;
+
+ if (needle.empty || haystack.empty)
+ return haystack;
+
+ static if (hasLength!R1 && is(typeof(takeNone(haystack)) == R1))
+ {
+ if (matchLen == haystack.length)
+ return takeNone(haystack);
+ }
+
+ if (!binaryFun!pred(haystack[matchLen], needle.front))
+ break;
+
+ ++matchLen;
+ needle.popFront();
+ }
+
+ auto bestMatch = haystack[0 .. matchLen];
+ haystack.popFront();
+ haystack = .find!pred(haystack, bestMatch);
+ }
+ }
+ }
+ else // static if (hasSlicing!R1 && isBidirectionalRange!R2)
+ {
+ if (needle.empty) return haystack;
+
+ static if (hasLength!R2)
+ {
+ immutable needleLength = needle.length;
+ }
+ else
+ {
+ immutable needleLength = walkLength(needle.save);
+ }
+ if (needleLength > haystack.length)
+ {
+ return haystack[haystack.length .. haystack.length];
+ }
+ // Optimization in case the ranges are both SortedRanges.
+ // Binary search can be used to find the first occurence
+ // of the first element of the needle in haystack.
+ // When it is found O(walklength(needle)) steps are performed.
+ // 8829 enhancement
+ import std.algorithm.comparison : mismatch;
+ import std.range : SortedRange;
+ static if (is(R1 == R2)
+ && is(R1 : SortedRange!TT, TT)
+ && pred == "a == b")
+ {
+ auto needleFirstElem = needle[0];
+ auto partitions = haystack.trisect(needleFirstElem);
+ auto firstElemLen = partitions[1].length;
+ size_t count = 0;
+
+ if (firstElemLen == 0)
+ return haystack[$ .. $];
+
+ while (needle.front() == needleFirstElem)
+ {
+ needle.popFront();
+ ++count;
+
+ if (count > firstElemLen)
+ return haystack[$ .. $];
+ }
+
+ auto m = mismatch(partitions[2], needle);
+
+ if (m[1].empty)
+ return haystack[partitions[0].length + partitions[1].length - count .. $];
+ }
+ else static if (isRandomAccessRange!R2)
+ {
+ immutable lastIndex = needleLength - 1;
+ auto last = needle[lastIndex];
+ size_t j = lastIndex, skip = 0;
+ for (; j < haystack.length;)
+ {
+ if (!binaryFun!pred(haystack[j], last))
+ {
+ ++j;
+ continue;
+ }
+ immutable k = j - lastIndex;
+ // last elements match
+ for (size_t i = 0;; ++i)
+ {
+ if (i == lastIndex)
+ return haystack[k .. haystack.length];
+ if (!binaryFun!pred(haystack[k + i], needle[i]))
+ break;
+ }
+ if (skip == 0)
+ {
+ skip = 1;
+ while (skip < needleLength && needle[needleLength - 1 - skip] != needle[needleLength - 1])
+ {
+ ++skip;
+ }
+ }
+ j += skip;
+ }
+ }
+ else
+ {
+ // @@@BUG@@@
+ // auto needleBack = moveBack(needle);
+ // Stage 1: find the step
+ size_t step = 1;
+ auto needleBack = needle.back;
+ needle.popBack();
+ for (auto i = needle.save; !i.empty && i.back != needleBack;
+ i.popBack(), ++step)
+ {
+ }
+ // Stage 2: linear find
+ size_t scout = needleLength - 1;
+ for (;;)
+ {
+ if (scout >= haystack.length)
+ break;
+ if (!binaryFun!pred(haystack[scout], needleBack))
+ {
+ ++scout;
+ continue;
+ }
+ // Found a match with the last element in the needle
+ auto cand = haystack[scout + 1 - needleLength .. haystack.length];
+ if (startsWith!pred(cand, needle))
+ {
+ // found
+ return cand;
+ }
+ scout += step;
+ }
+ }
+ return haystack[haystack.length .. haystack.length];
}
}
@@ -1932,126 +2122,6 @@ if (isForwardRange!R1 && isForwardRange!R2
assert(equal(r, SList!int(2, 5, 7, 3)[]));
}
-/// ditto
-R1 find(alias pred = "a == b", R1, R2)(R1 haystack, scope R2 needle)
-if (isRandomAccessRange!R1 && hasLength!R1 && hasSlicing!R1 && isBidirectionalRange!R2
- && is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool))
-{
- if (needle.empty) return haystack;
-
- static if (hasLength!R2)
- {
- immutable needleLength = needle.length;
- }
- else
- {
- immutable needleLength = walkLength(needle.save);
- }
- if (needleLength > haystack.length)
- {
- return haystack[haystack.length .. haystack.length];
- }
- // Optimization in case the ranges are both SortedRanges.
- // Binary search can be used to find the first occurence
- // of the first element of the needle in haystack.
- // When it is found O(walklength(needle)) steps are performed.
- // 8829 enhancement
- import std.algorithm.comparison : mismatch;
- import std.range : SortedRange;
- static if (is(R1 == R2)
- && is(R1 : SortedRange!TT, TT)
- && pred == "a == b")
- {
- auto needleFirstElem = needle[0];
- auto partitions = haystack.trisect(needleFirstElem);
- auto firstElemLen = partitions[1].length;
- size_t count = 0;
-
- if (firstElemLen == 0)
- return haystack[$ .. $];
-
- while (needle.front() == needleFirstElem)
- {
- needle.popFront();
- ++count;
-
- if (count > firstElemLen)
- return haystack[$ .. $];
- }
-
- auto m = mismatch(partitions[2], needle);
-
- if (m[1].empty)
- return haystack[partitions[0].length + partitions[1].length - count .. $];
- }
- else static if (isRandomAccessRange!R2)
- {
- immutable lastIndex = needleLength - 1;
- auto last = needle[lastIndex];
- size_t j = lastIndex, skip = 0;
- for (; j < haystack.length;)
- {
- if (!binaryFun!pred(haystack[j], last))
- {
- ++j;
- continue;
- }
- immutable k = j - lastIndex;
- // last elements match
- for (size_t i = 0;; ++i)
- {
- if (i == lastIndex)
- return haystack[k .. haystack.length];
- if (!binaryFun!pred(haystack[k + i], needle[i]))
- break;
- }
- if (skip == 0)
- {
- skip = 1;
- while (skip < needleLength && needle[needleLength - 1 - skip] != needle[needleLength - 1])
- {
- ++skip;
- }
- }
- j += skip;
- }
- }
- else
- {
- // @@@BUG@@@
- // auto needleBack = moveBack(needle);
- // Stage 1: find the step
- size_t step = 1;
- auto needleBack = needle.back;
- needle.popBack();
- for (auto i = needle.save; !i.empty && i.back != needleBack;
- i.popBack(), ++step)
- {
- }
- // Stage 2: linear find
- size_t scout = needleLength - 1;
- for (;;)
- {
- if (scout >= haystack.length)
- break;
- if (!binaryFun!pred(haystack[scout], needleBack))
- {
- ++scout;
- continue;
- }
- // Found a match with the last element in the needle
- auto cand = haystack[scout + 1 - needleLength .. haystack.length];
- if (startsWith!pred(cand, needle))
- {
- // found
- return cand;
- }
- scout += step;
- }
- }
- return haystack[haystack.length .. haystack.length];
-}
-
@safe unittest
{
import std.range;
@@ -2089,69 +2159,6 @@ if (isRandomAccessRange!R1 && hasLength!R1 && hasSlicing!R1 && isBidirectionalRa
auto r = BiRange([1, 2, 3, 10, 11, 4]);
}
-/// ditto
-R1 find(alias pred = "a == b", R1, R2)(R1 haystack, scope R2 needle)
-if (isRandomAccessRange!R1 && isForwardRange!R2 && !isBidirectionalRange!R2 &&
- is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool))
-{
- static if (!is(ElementType!R1 == ElementType!R2))
- {
- return simpleMindedFind!pred(haystack, needle);
- }
- else
- {
- // Prepare the search with needle's first element
- if (needle.empty)
- return haystack;
-
- haystack = .find!pred(haystack, needle.front);
-
- static if (hasLength!R1 && hasLength!R2 && is(typeof(takeNone(haystack)) == R1))
- {
- if (needle.length > haystack.length)
- return takeNone(haystack);
- }
- else
- {
- if (haystack.empty)
- return haystack;
- }
-
- needle.popFront();
- size_t matchLen = 1;
-
- // Loop invariant: haystack[0 .. matchLen] matches everything in
- // the initial needle that was popped out of needle.
- for (;;)
- {
- // Extend matchLength as much as possible
- for (;;)
- {
- import std.range : takeNone;
-
- if (needle.empty || haystack.empty)
- return haystack;
-
- static if (hasLength!R1 && is(typeof(takeNone(haystack)) == R1))
- {
- if (matchLen == haystack.length)
- return takeNone(haystack);
- }
-
- if (!binaryFun!pred(haystack[matchLen], needle.front))
- break;
-
- ++matchLen;
- needle.popFront();
- }
-
- auto bestMatch = haystack[0 .. matchLen];
- haystack.popFront();
- haystack = .find!pred(haystack, bestMatch);
- }
- }
-}
-
@safe unittest
{
import std.container : SList;
@@ -3894,7 +3901,8 @@ if (isInputRange!Range && !isInfinite!Range &&
/**
Skip over the initial portion of the first given range that matches the second
-range, or do nothing if there is no match.
+range, or if no second range is given skip over the elements that fullfil pred.
+Do nothing if there is no match.
Params:
pred = The predicate that determines whether elements from each respective
@@ -3905,9 +3913,9 @@ Params:
representing the initial segment of $(D r1) to skip over.
Returns:
-true if the initial segment of $(D r1) matches $(D r2), and $(D r1) has been
-advanced to the point past this segment; otherwise false, and $(D r1) is left
-in its original position.
+true if the initial segment of $(D r1) matches $(D r2) or $(D pred) evaluates to true,
+and $(D r1) has been advanced to the point past this segment; otherwise false, and
+$(D r1) is left in its original position.
*/
bool skipOver(R1, R2)(ref R1 r1, R2 r2)
if (isForwardRange!R1 && isInputRange!R2
@@ -3953,6 +3961,20 @@ if (is(typeof(binaryFun!pred(r1.front, r2.front))) &&
return r2.empty;
}
+/// Ditto
+bool skipOver(alias pred, R)(ref R r1)
+if (isForwardRange!R &&
+ ifTestable!(typeof(r1.front), unaryFun!pred))
+{
+ if (r1.empty || !unaryFun!pred(r1.front))
+ return false;
+
+ do
+ r1.popFront();
+ while (!r1.empty && unaryFun!pred(r1.front));
+ return true;
+}
+
///
@safe unittest
{
@@ -3969,6 +3991,16 @@ if (is(typeof(binaryFun!pred(r1.front, r2.front))) &&
assert(r1 == ["abc", "def", "hij"]);
assert(skipOver!((a, b) => a.equal(b))(r1, r2));
assert(r1 == ["def", "hij"]);
+
+ import std.ascii : isWhite;
+ import std.range.primitives : empty;
+
+ auto s2 = "\t\tvalue";
+ auto s3 = "";
+ auto s4 = "\t\t\t";
+ assert(s2.skipOver!isWhite && s2 == "value");
+ assert(!s3.skipOver!isWhite);
+ assert(s4.skipOver!isWhite && s3.empty);
}
/**
diff --git a/std/algorithm/setops.d b/std/algorithm/setops.d
index f948cb0635c..f7a0080af2c 100644
--- a/std/algorithm/setops.d
+++ b/std/algorithm/setops.d
@@ -3,6 +3,14 @@
This is a submodule of $(MREF std, algorithm).
It contains generic algorithms that implement set operations.
+The functions $(LREF multiwayMerge), $(LREF multiwayUnion), $(LREF setDifference),
+$(LREF setIntersection), $(LREF setSymmetricDifference) expect a range of sorted
+ranges as input.
+
+All algorithms are generalized to accept as input not only sets but also
+$(HTTP https://en.wikipedia.org/wiki/Multiset, multisets). Each algorithm
+documents behaviour in the presence of duplicated inputs.
+
$(SCRIPT inhibitQuickIndex = 1;)
$(BOOKTABLE Cheat Sheet,
$(TR $(TH Function Name) $(TH Description))
@@ -13,9 +21,10 @@ $(T2 largestPartialIntersection,
$(T2 largestPartialIntersectionWeighted,
Copies out the values that occur most frequently (multiplied by
per-value weights) in a range of ranges.)
-$(T2 nWayUnion,
- Computes the union of a set of sets implemented as a range of sorted
- ranges.)
+$(T2 multiwayMerge,
+ Merges a range of sorted ranges.)
+$(T2 multiwayUnion,
+ Computes the union of a range of sorted ranges.)
$(T2 setDifference,
Lazily computes the set difference of two or more sorted ranges.)
$(T2 setIntersection,
@@ -568,6 +577,11 @@ array of the occurrences and then selecting its top items, and also
requires less memory ($(D largestPartialIntersection) builds its
result directly in $(D tgt) and requires no extra memory).
+If at least one of the ranges is a multiset, then all occurences
+of a duplicate element are taken into account. The result is
+equivalent to merging all ranges and picking the most frequent
+$(D tgt.length) elements.
+
Warning: Because $(D largestPartialIntersection) does not allocate
extra memory, it will leave $(D ror) modified. Namely, $(D
largestPartialIntersection) assumes ownership of $(D ror) and
@@ -616,6 +630,22 @@ void largestPartialIntersection
largestPartialIntersection(a, c);
assert(c[0] == tuple(1.0, 3u));
// 1.0 occurs in 3 inputs
+
+ // multiset
+ double[][] x =
+ [
+ [1, 1, 1, 1, 4, 7, 8],
+ [1, 7],
+ [1, 7, 8],
+ [4, 7],
+ [7]
+ ];
+ auto y = new Tuple!(double, uint)[2];
+ largestPartialIntersection(x.dup, y);
+ // 7.0 occurs 5 times
+ assert(y[0] == tuple(7.0, 5u));
+ // 1.0 occurs 6 times
+ assert(y[1] == tuple(1.0, 6u));
}
import std.algorithm.sorting : SortOutput; // FIXME
@@ -625,6 +655,11 @@ import std.algorithm.sorting : SortOutput; // FIXME
Similar to $(D largestPartialIntersection), but associates a weight
with each distinct element in the intersection.
+If at least one of the ranges is a multiset, then all occurences
+of a duplicate element are taken into account. The result
+is equivalent to merging all input ranges and picking the highest
+$(D tgt.length), weight-based ranking elements.
+
Params:
less = The predicate the ranges are sorted by.
ror = A range of $(REF_ALTTEXT forward ranges, isForwardRange, std,range,primitives)
@@ -647,7 +682,7 @@ void largestPartialIntersectionWeighted
{
return weights[a[0]] * a[1] > weights[b[0]] * b[1];
}
- topNCopy!heapComp(group(nWayUnion!less(ror)), tgt, sorted);
+ topNCopy!heapComp(group(multiwayMerge!less(ror)), tgt, sorted);
}
///
@@ -672,6 +707,20 @@ void largestPartialIntersectionWeighted
assert(b[0] == tuple(4.0, 2u));
// 4.0 occurs 2 times -> 4.6 (2 * 2.3)
// 7.0 occurs 3 times -> 4.4 (3 * 1.1)
+
+ // multiset
+ double[][] x =
+ [
+ [ 1, 1, 1, 4, 7, 8 ],
+ [ 1, 7 ],
+ [ 1, 7, 8],
+ [ 4 ],
+ [ 7 ],
+ ];
+ auto y = new Tuple!(double, uint)[1];
+ largestPartialIntersectionWeighted(x, y, weights);
+ assert(y[0] == tuple(1.0, 5u));
+ // 1.0 occurs 5 times -> 1.2 * 5 = 6
}
@system unittest
@@ -744,21 +793,30 @@ void largestPartialIntersectionWeighted
assert(arrayOne == arrayTwo);
}
-// NWayUnion
+// MultiwayMerge
/**
-Computes the union of multiple sets. The input sets are passed as a
+Merges multiple sets. The input sets are passed as a
range of ranges and each is assumed to be sorted by $(D
less). Computation is done lazily, one union element at a time. The
complexity of one $(D popFront) operation is $(BIGOH
log(ror.length)). However, the length of $(D ror) decreases as ranges
in it are exhausted, so the complexity of a full pass through $(D
-NWayUnion) is dependent on the distribution of the lengths of ranges
+MultiwayMerge) is dependent on the distribution of the lengths of ranges
contained within $(D ror). If all ranges have the same length $(D n)
(worst case scenario), the complexity of a full pass through $(D
-NWayUnion) is $(BIGOH n * ror.length * log(ror.length)), i.e., $(D
+MultiwayMerge) is $(BIGOH n * ror.length * log(ror.length)), i.e., $(D
log(ror.length)) times worse than just spanning all ranges in
turn. The output comes sorted (unstably) by $(D less).
+The length of the resulting range is the sum of all lengths of
+the ranges passed as input. This means that all elements (duplicates
+included) are transferred to the resulting range.
+
+For backward compatibility, `multiwayMerge` is available under
+the name `nWayUnion` and `MultiwayMerge` under the name of `NWayUnion` .
+Future code should use `multiwayMerge` and `MultiwayMerge` as `nWayUnion`
+and `NWayUnion` will be deprecated.
+
Params:
less = Predicate the given ranges are sorted by.
ror = A range of ranges sorted by `less` to compute the union for.
@@ -766,14 +824,14 @@ Params:
Returns:
A range of the union of the ranges in `ror`.
-Warning: Because $(D NWayUnion) does not allocate extra memory, it
-will leave $(D ror) modified. Namely, $(D NWayUnion) assumes ownership
+Warning: Because $(D MultiwayMerge) does not allocate extra memory, it
+will leave $(D ror) modified. Namely, $(D MultiwayMerge) assumes ownership
of $(D ror) and discretionarily swaps and advances elements of it. If
you want $(D ror) to preserve its contents after the call, you may
-want to pass a duplicate to $(D NWayUnion) (and perhaps cache the
+want to pass a duplicate to $(D MultiwayMerge) (and perhaps cache the
duplicate in between calls).
*/
-struct NWayUnion(alias less, RangeOfRanges)
+struct MultiwayMerge(alias less, RangeOfRanges)
{
import std.container : BinaryHeap;
@@ -830,7 +888,7 @@ struct NWayUnion(alias less, RangeOfRanges)
}
/// Ditto
-NWayUnion!(less, RangeOfRanges) nWayUnion
+MultiwayMerge!(less, RangeOfRanges) multiwayMerge
(alias less = "a < b", RangeOfRanges)
(RangeOfRanges ror)
{
@@ -853,7 +911,75 @@ NWayUnion!(less, RangeOfRanges) nWayUnion
auto witness = [
1, 1, 1, 4, 4, 7, 7, 7, 7, 8, 8
];
- assert(equal(nWayUnion(a), witness));
+ assert(equal(multiwayMerge(a), witness));
+
+ double[][] b =
+ [
+ // range with duplicates
+ [ 1, 1, 4, 7, 8 ],
+ [ 7 ],
+ [ 1, 7, 8],
+ [ 4 ],
+ [ 7 ],
+ ];
+ // duplicates are propagated to the resulting range
+ assert(equal(multiwayMerge(b), witness));
+}
+
+alias nWayUnion = multiwayMerge;
+alias NWayUnion = MultiwayMerge;
+
+/**
+Computes the union of multiple ranges. The input ranges are passed
+as a range of ranges and each is assumed to be sorted by $(D
+less). Computation is done lazily, one union element at a time.
+`multiwayUnion(ror)` is functionally equivalent to `multiwayMerge(ror).uniq`.
+
+"The output of multiwayUnion has no duplicates even when its inputs contain duplicates."
+
+Params:
+ less = Predicate the given ranges are sorted by.
+ ror = A range of ranges sorted by `less` to compute the intersection for.
+
+Returns:
+ A range of the union of the ranges in `ror`.
+
+See also: $(LREF multiwayMerge)
+ */
+auto multiwayUnion(alias less = "a < b", RangeOfRanges)(RangeOfRanges ror)
+{
+ import std.algorithm.iteration : uniq;
+ return ror.multiwayMerge.uniq;
+}
+
+///
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+
+ // sets
+ double[][] a =
+ [
+ [ 1, 4, 7, 8 ],
+ [ 1, 7 ],
+ [ 1, 7, 8],
+ [ 4 ],
+ [ 7 ],
+ ];
+
+ auto witness = [1, 4, 7, 8];
+ assert(equal(multiwayUnion(a), witness));
+
+ // multisets
+ double[][] b =
+ [
+ [ 1, 1, 1, 4, 7, 8 ],
+ [ 1, 7 ],
+ [ 1, 7, 7, 8],
+ [ 4 ],
+ [ 7 ],
+ ];
+ assert(equal(multiwayUnion(b), witness));
}
/**
@@ -861,6 +987,11 @@ Lazily computes the difference of $(D r1) and $(D r2). The two ranges
are assumed to be sorted by $(D less). The element types of the two
ranges must have a common type.
+
+In the case of multisets, considering that element `a` appears `x`
+times in $(D r1) and `y` times and $(D r2), the number of occurences
+of `a` in the resulting range is going to be `x-y` if x > y or 0 othwerise.
+
Params:
less = Predicate the given ranges are sorted by.
r1 = The first range.
@@ -950,10 +1081,18 @@ SetDifference!(less, R1, R2) setDifference(alias less = "a < b", R1, R2)
import std.algorithm.comparison : equal;
import std.range.primitives : isForwardRange;
+ //sets
int[] a = [ 1, 2, 4, 5, 7, 9 ];
int[] b = [ 0, 1, 2, 4, 7, 8 ];
- assert(equal(setDifference(a, b), [5, 9][]));
+ assert(equal(setDifference(a, b), [5, 9]));
static assert(isForwardRange!(typeof(setDifference(a, b))));
+
+ // multisets
+ int[] x = [1, 1, 1, 2, 3];
+ int[] y = [1, 1, 2, 4, 5];
+ auto r = setDifference(x, y);
+ assert(equal(r, [1, 3]));
+ assert(setDifference(r, x).empty);
}
@safe unittest // Issue 10460
@@ -972,6 +1111,10 @@ Lazily computes the intersection of two or more input ranges $(D
ranges). The ranges are assumed to be sorted by $(D less). The element
types of the ranges must have a common type.
+In the case of multisets, the range with the minimum number of
+occurences of a given element, propagates the number of
+occurences of this element to the resulting range.
+
Params:
less = Predicate the given ranges are sorted by.
ranges = The ranges to compute the intersection for.
@@ -1085,12 +1228,19 @@ if (Rs.length >= 2 && allSatisfy!(isInputRange, Rs) &&
{
import std.algorithm.comparison : equal;
+ // sets
int[] a = [ 1, 2, 4, 5, 7, 9 ];
int[] b = [ 0, 1, 2, 4, 7, 8 ];
int[] c = [ 0, 1, 4, 5, 7, 8 ];
assert(equal(setIntersection(a, a), a));
assert(equal(setIntersection(a, b), [1, 2, 4, 7]));
assert(equal(setIntersection(a, b, c), [1, 4, 7]));
+
+ // multisets
+ int[] d = [ 1, 1, 2, 2, 7, 7 ];
+ int[] e = [ 1, 1, 1, 7];
+ assert(equal(setIntersection(a, d), [1, 2, 7]));
+ assert(equal(setIntersection(d, e), [1, 1, 7]));
}
@safe unittest
@@ -1130,6 +1280,12 @@ r2). The two ranges are assumed to be sorted by $(D less), and the
output is also sorted by $(D less). The element types of the two
ranges must have a common type.
+If both ranges are sets (without duplicated elements), the resulting
+range is going to be a set. If at least one of the ranges is a multiset,
+the number of occurences of an element `x` in the resulting range is `abs(a-b)`
+where `a` is the number of occurences of `x` in $(D r1), `b` is the number of
+occurences of `x` in $(D r2), and `abs` is the absolute value.
+
If both arguments are ranges of L-values of the same type then
$(D SetSymmetricDifference) will also be a range of L-values of
that type.
@@ -1241,10 +1397,17 @@ setSymmetricDifference(alias less = "a < b", R1, R2)
import std.algorithm.comparison : equal;
import std.range.primitives : isForwardRange;
+ // sets
int[] a = [ 1, 2, 4, 5, 7, 9 ];
int[] b = [ 0, 1, 2, 4, 7, 8 ];
assert(equal(setSymmetricDifference(a, b), [0, 5, 8, 9][]));
static assert(isForwardRange!(typeof(setSymmetricDifference(a, b))));
+
+ //mutisets
+ int[] c = [1, 1, 1, 1, 2, 2, 2, 4, 5, 6];
+ int[] d = [1, 1, 2, 2, 2, 2, 4, 7, 9];
+ assert(equal(setSymmetricDifference(c, d), setSymmetricDifference(d, c)));
+ assert(equal(setSymmetricDifference(c, d), [1, 1, 2, 5, 6, 7, 9]));
}
@safe unittest // Issue 10460
diff --git a/std/algorithm/sorting.d b/std/algorithm/sorting.d
index 99ac551649e..7e338d907d3 100644
--- a/std/algorithm/sorting.d
+++ b/std/algorithm/sorting.d
@@ -895,7 +895,7 @@ if (ss == SwapStrategy.unstable && isRandomAccessRange!Range
auto a = new int[](uniform(0, 100, r));
foreach (ref e; a)
{
- e = uniform(0, 50);
+ e = uniform(0, 50, r);
}
auto pieces = partition3(a, 25);
assert(pieces[0].length + pieces[1].length + pieces[2].length == a.length);
@@ -994,15 +994,15 @@ if (isRandomAccessRange!Range && !isInfinite!Range &&
"r and index must be same length for makeIndex.");
static if (IndexType.sizeof < size_t.sizeof)
{
- enforce(r.length <= IndexType.max, "Cannot create an index with " ~
+ enforce(r.length <= size_t(1) + IndexType.max, "Cannot create an index with " ~
"element type " ~ IndexType.stringof ~ " with length " ~
to!string(r.length) ~ ".");
}
- for (IndexType i = 0; i < r.length; ++i)
- {
- index[cast(size_t) i] = i;
- }
+ // Use size_t as loop index to avoid overflow on ++i,
+ // e.g. when squeezing 256 elements into a ubyte index.
+ foreach (size_t i; 0 .. r.length)
+ index[i] = cast(IndexType) i;
// sort the index
sort!((a, b) => binaryFun!less(r[cast(size_t) a], r[cast(size_t) b]), ss)
@@ -1056,6 +1056,22 @@ if (isRandomAccessRange!Range && !isInfinite!Range &&
(index3));
}
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ ubyte[256] index = void;
+ iota(256).makeIndex(index[]);
+ assert(index[].equal(iota(256)));
+ byte[128] sindex = void;
+ iota(128).makeIndex(sindex[]);
+ assert(sindex[].equal(iota(128)));
+
+ auto index2 = new uint[10];
+ 10.iota.makeIndex(index2);
+ assert(index2.equal(10.iota));
+}
+
struct Merge(alias less = "a < b", Rs...)
if (Rs.length >= 2 &&
allSatisfy!(isInputRange, Rs) &&
@@ -3402,7 +3418,7 @@ body
r.swapAt(rite, pivot);
}
// Second loop: make left and pivot meet
- outer: for (; rite > pivot; --rite)
+ for (; rite > pivot; --rite)
{
if (!lp(r[rite], r[oldPivot])) continue;
while (rite > pivot)
@@ -3748,58 +3764,46 @@ void topNIndex(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable,
(Range r, RangeIndex index, SortOutput sorted = No.sortOutput)
if (isRandomAccessRange!Range &&
isRandomAccessRange!RangeIndex &&
- hasAssignableElements!RangeIndex &&
- isIntegral!(ElementType!(RangeIndex)))
+ hasAssignableElements!RangeIndex)
{
static assert(ss == SwapStrategy.unstable,
"Stable swap strategy not implemented yet.");
- import std.container : BinaryHeap;
- import std.exception : enforce;
-
+ import std.container.binaryheap : BinaryHeap;
if (index.empty) return;
- enforce(ElementType!(RangeIndex).max >= index.length,
- "Index type too small");
- bool indirectLess(ElementType!(RangeIndex) a, ElementType!(RangeIndex) b)
- {
- return binaryFun!(less)(r[a], r[b]);
- }
- auto heap = BinaryHeap!(RangeIndex, indirectLess)(index, 0);
- foreach (i; 0 .. r.length)
- {
- heap.conditionalInsert(cast(ElementType!RangeIndex) i);
- }
- if (sorted == Yes.sortOutput)
- {
- while (!heap.empty) heap.removeFront();
- }
-}
-/// ditto
-void topNIndex(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable,
- Range, RangeIndex)
- (Range r, RangeIndex index, SortOutput sorted = No.sortOutput)
-if (isRandomAccessRange!Range &&
- isRandomAccessRange!RangeIndex &&
- hasAssignableElements!RangeIndex &&
- is(ElementType!(RangeIndex) == ElementType!(Range)*))
-{
- static assert(ss == SwapStrategy.unstable,
- "Stable swap strategy not implemented yet.");
+ static if (isIntegral!(ElementType!(RangeIndex)))
+ {
+ import std.exception : enforce;
- import std.container : BinaryHeap;
+ enforce(ElementType!(RangeIndex).max >= index.length,
+ "Index type too small");
+ bool indirectLess(ElementType!(RangeIndex) a, ElementType!(RangeIndex) b)
+ {
+ return binaryFun!(less)(r[a], r[b]);
+ }
+ auto heap = BinaryHeap!(RangeIndex, indirectLess)(index, 0);
+ foreach (i; 0 .. r.length)
+ {
+ heap.conditionalInsert(cast(ElementType!RangeIndex) i);
+ }
- if (index.empty) return;
- static bool indirectLess(const ElementType!(RangeIndex) a,
- const ElementType!(RangeIndex) b)
- {
- return binaryFun!less(*a, *b);
}
- auto heap = BinaryHeap!(RangeIndex, indirectLess)(index, 0);
- foreach (i; 0 .. r.length)
+ else static if (is(ElementType!(RangeIndex) == ElementType!(Range)*))
{
- heap.conditionalInsert(&r[i]);
+ static bool indirectLess(const ElementType!(RangeIndex) a,
+ const ElementType!(RangeIndex) b)
+ {
+ return binaryFun!less(*a, *b);
+ }
+ auto heap = BinaryHeap!(RangeIndex, indirectLess)(index, 0);
+ foreach (i; 0 .. r.length)
+ {
+ heap.conditionalInsert(&r[i]);
+ }
}
+ else static assert(0, "Invalid ElementType");
+
if (sorted == Yes.sortOutput)
{
while (!heap.empty) heap.removeFront();
diff --git a/std/array.d b/std/array.d
index e3d94ad17ae..8fc605e0109 100644
--- a/std/array.d
+++ b/std/array.d
@@ -212,6 +212,18 @@ if (isNarrowString!String)
return cast(typeof(return)) str.toUTF32;
}
+///
+@safe unittest
+{
+ import std.range.primitives : isRandomAccessRange;
+
+ assert("Hello D".array == "Hello D"d);
+ static assert(isRandomAccessRange!string == false);
+
+ assert("Hello D"w.array == "Hello D"d);
+ static assert(isRandomAccessRange!dstring == true);
+}
+
@system unittest
{
// @system due to array!string
@@ -534,7 +546,7 @@ if (isDynamicArray!T && allSatisfy!(isIntegral, I) && hasIndirections!(ElementEn
return arrayAllocImpl!(false, T, ST)(sizes);
}
-///
+/// ditto
auto uninitializedArray(T, I...)(I sizes) nothrow @trusted
if (isDynamicArray!T && allSatisfy!(isIntegral, I) && !hasIndirections!(ElementEncodingType!T))
{
@@ -588,6 +600,18 @@ if (isDynamicArray!T && allSatisfy!(isIntegral, I))
return arrayAllocImpl!(true, T, ST)(sizes);
}
+///
+@safe pure nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : repeat;
+
+ auto arr = minimallyInitializedArray!(int[])(42);
+ assert(arr.length == 42);
+ // Elements aren't necessarily initialized to 0
+ assert(!arr.equal(0.repeat(42)));
+}
+
@safe pure nothrow unittest
{
cast(void) minimallyInitializedArray!(int[][][][][])();
@@ -1337,8 +1361,6 @@ if (isInputRange!S && !isDynamicArray!S)
{
import std.conv : to;
- debug(std_array) printf("array.replicate.unittest\n");
-
foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[]))
{
S s;
@@ -1363,6 +1385,9 @@ $(D @safe), $(D pure) and $(D CTFE)-able.
Params:
s = the string to split
+Returns:
+ An array of each word in `s`
+
See_Also:
$(REF splitter, std,algorithm,iteration) for a version that splits using any
separator.
@@ -1402,6 +1427,29 @@ if (isSomeString!S)
return result;
}
+///
+@safe unittest
+{
+ string str = "Hello World!";
+ assert(str.split == ["Hello", "World!"]);
+
+ string str2 = "Hello\t\tWorld\t!";
+ assert(str2.split == ["Hello", "World", "!"]);
+}
+
+/**
+ * `split` allocates memory, so the same effect can be achieved lazily
+ * using $(REF splitter, std,algorithm,iteration).
+ */
+@safe unittest
+{
+ import std.ascii : isWhite;
+ import std.algorithm.comparison : equal;
+
+ string str = "Hello World!";
+ assert(str.splitter!(isWhite).equal(["Hello", "World!"]));
+}
+
@safe unittest
{
import std.conv : to;
@@ -1509,12 +1557,20 @@ if (isForwardRange!Range && is(typeof(unaryFun!isTerminator(range.front))))
return range.splitter!isTerminator.array;
}
+///
+@safe unittest
+{
+ import std.uni : isWhite;
+ assert("Learning,D,is,fun".split(",") == ["Learning", "D", "is", "fun"]);
+ assert("Learning D is fun".split!isWhite == ["Learning", "D", "is", "fun"]);
+ assert("Learning D is fun".split(" D ") == ["Learning", "is fun"]);
+}
+
@safe unittest
{
import std.algorithm.comparison : cmp;
import std.conv;
- debug(std_array) printf("array.split\n");
foreach (S; AliasSeq!(string, wstring, dstring,
immutable(string), immutable(wstring), immutable(dstring),
char[], wchar[], dchar[],
@@ -1845,8 +1901,6 @@ if (isInputRange!RoR &&
import std.conv : to;
import std.range;
- debug(std_array) printf("array.join.unittest\n");
-
foreach (R; AliasSeq!(string, wstring, dstring))
{
R word1 = "日本語";
@@ -2068,8 +2122,6 @@ if (isOutputRange!(Sink, E) && isDynamicArray!(E[])
import std.algorithm.comparison : cmp;
import std.conv : to;
- debug(std_array) printf("array.replace.unittest\n");
-
foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[]))
{
foreach (T; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[]))
@@ -2512,8 +2564,6 @@ if (isDynamicArray!(E[]) &&
import std.algorithm.comparison : cmp;
import std.conv : to;
- debug(std_array) printf("array.replaceFirst.unittest\n");
-
foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[],
const(char[]), immutable(char[])))
{
@@ -2627,8 +2677,6 @@ if (isDynamicArray!(E[]) &&
import std.algorithm.comparison : cmp;
import std.conv : to;
- debug(std_array) printf("array.replaceLast.unittest\n");
-
foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[],
const(char[]), immutable(char[])))
{
@@ -2704,7 +2752,6 @@ body
@system unittest
{
import std.algorithm.comparison : cmp;
- debug(std_array) printf("array.replaceSlice.unittest\n");
string s = "hello";
string slice = s[2 .. 4];
@@ -3186,6 +3233,23 @@ if (isDynamicArray!A)
}
}
+///
+@system pure nothrow
+unittest
+{
+ int[] a = [1, 2];
+ auto app2 = appender(&a);
+ assert(app2.data == [1, 2]);
+ assert(a == [1, 2]);
+ app2 ~= 3;
+ app2 ~= [4, 5, 6];
+ assert(app2.data == [1, 2, 3, 4, 5, 6]);
+ assert(a == [1, 2, 3, 4, 5, 6]);
+
+ app2.reserve(5);
+ assert(app2.capacity >= 5);
+}
+
/++
Convenience function that returns an $(LREF Appender) instance,
optionally initialized with $(D array).
@@ -3308,6 +3372,25 @@ Appender!(E[]) appender(A : E[], E)(auto ref A array)
});
}
+///
+@safe pure nothrow
+unittest
+{
+ auto w = appender!string;
+ // pre-allocate space for at least 10 elements (this avoids costly reallocations)
+ w.reserve(10);
+ assert(w.capacity >= 10);
+
+ w.put('a'); // single elements
+ w.put("bc"); // multiple elements
+
+ // use the append syntax
+ w ~= 'd';
+ w ~= "ef";
+
+ assert(w.data == "abcdef");
+}
+
@safe pure nothrow unittest
{
{
@@ -3571,6 +3654,23 @@ RefAppender!(E[]) appender(P : E[]*, E)(P arrayPtr)
return RefAppender!(E[])(arrayPtr);
}
+///
+@system pure nothrow
+unittest
+{
+ int[] a = [1, 2];
+ auto app2 = appender(&a);
+ assert(app2.data == [1, 2]);
+ assert(a == [1, 2]);
+ app2 ~= 3;
+ app2 ~= [4, 5, 6];
+ assert(app2.data == [1, 2, 3, 4, 5, 6]);
+ assert(a == [1, 2, 3, 4, 5, 6]);
+
+ app2.reserve(5);
+ assert(app2.capacity >= 5);
+}
+
@system unittest
{
import std.exception;
diff --git a/std/ascii.d b/std/ascii.d
index e5fa3e33d1a..3867c563305 100644
--- a/std/ascii.d
+++ b/std/ascii.d
@@ -87,6 +87,29 @@ enum LetterCase : bool
lower /// Lower case letters
}
+///
+@safe unittest
+{
+ import std.conv : to;
+
+ assert(42.to!string(16, LetterCase.upper) == "2A");
+ assert(42.to!string(16, LetterCase.lower) == "2a");
+}
+
+///
+@system unittest
+{
+ import std.digest.hmac : hmac;
+ import std.digest.digest : toHexString;
+ import std.digest.sha : SHA1;
+ import std.string : representation;
+
+ const sha1HMAC = "A very long phrase".representation
+ .hmac!SHA1("secret".representation)
+ .toHexString!(LetterCase.lower);
+ assert(sha1HMAC == "49f2073c7bf58577e8c9ae59fe8cfd37c9ab94e5");
+}
+
/// Newline sequence for this system.
version(Windows)
immutable newline = "\r\n";
diff --git a/std/base64.d b/std/base64.d
index 59c941e27b4..20acea251e9 100644
--- a/std/base64.d
+++ b/std/base64.d
@@ -104,14 +104,30 @@ alias Base64URL = Base64Impl!('-', '_');
assert(Base64URL.decode("g9cwegE_") == data);
}
+/**
+ * Unpadded variation of Base64 encoding that is safe for use in URLs and
+ * filenames, as used in RFCs 4648 and 7515 (JWS/JWT/JWE).
+ *
+ * See $(LREF Base64Impl) for a description of available methods.
+ */
+alias Base64URLNoPadding = Base64Impl!('-', '_', Base64.NoPadding);
+
+///
+@safe unittest
+{
+ ubyte[] data = [0x83, 0xd7, 0x30, 0x7b, 0xef];
+ assert(Base64URLNoPadding.encode(data) == "g9cwe-8");
+ assert(Base64URLNoPadding.decode("g9cwe-8") == data);
+}
/**
* Template for implementing Base64 encoding and decoding.
*
* For most purposes, direct usage of this template is not necessary; instead,
- * this module provides two default implementations: $(LREF Base64) and
- * $(LREF Base64URL), that implement basic Base64 encoding and a variant
- * intended for use in URLs and filenames, respectively.
+ * this module provides default implementations: $(LREF Base64), implementing
+ * basic Base64 encoding, and $(LREF Base64URL) and $(LREF Base64URLNoPadding),
+ * that implement the Base64 variant for use in URLs and filenames, with
+ * and without padding, respectively.
*
* Customized Base64 encoding schemes can be implemented by instantiating this
* template with the appropriate arguments. For example:
@@ -178,7 +194,7 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
ubyte[] data = [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e];
// Allocate a buffer large enough to hold the encoded string.
- auto buf = new char[encodeLength(data.length)];
+ auto buf = new char[Base64.encodeLength(data.length)];
Base64.encode(data, buf);
assert(buf == "Gis8TV1u");
@@ -1721,6 +1737,21 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
}
}
+///
+@safe unittest
+{
+ import std.string : representation;
+
+ // pre-defined: alias Base64 = Base64Impl!('+', '/');
+ ubyte[] emptyArr;
+ assert(Base64.encode(emptyArr) == "");
+ assert(Base64.encode("f".representation) == "Zg==");
+ assert(Base64.encode("foo".representation) == "Zm9v");
+
+ alias Base64Re = Base64Impl!('!', '=', Base64.NoPadding);
+ assert(Base64Re.encode("f".representation) == "Zg");
+ assert(Base64Re.encode("foo".representation) == "Zm9v");
+}
/**
* Exception thrown upon encountering Base64 encoding or decoding errors.
@@ -1734,6 +1765,13 @@ class Base64Exception : Exception
}
}
+///
+@system unittest
+{
+ import std.exception : assertThrown;
+ assertThrown!Base64Exception(Base64.decode("ab|c"));
+}
+
@system unittest
{
diff --git a/std/bigint.d b/std/bigint.d
index 861df64b1ce..a5e96b17e47 100644
--- a/std/bigint.d
+++ b/std/bigint.d
@@ -27,10 +27,10 @@ module std.bigint;
import std.conv : ConvException;
-private import std.format : FormatSpec, FormatException;
-private import std.internal.math.biguintcore;
-private import std.range.primitives;
-private import std.traits;
+import std.format : FormatSpec, FormatException;
+import std.internal.math.biguintcore;
+import std.range.primitives;
+import std.traits;
/** A struct representing an arbitrary precision integer.
*
diff --git a/std/c/linux/pthread.d b/std/c/linux/pthread.d
index 041bc570c26..bc8a1f3ae5b 100644
--- a/std/c/linux/pthread.d
+++ b/std/c/linux/pthread.d
@@ -14,6 +14,4 @@ deprecated("Import core.sys.posix.pthread or the appropriate core.sys.posix.* mo
module std.c.linux.pthread;
version (linux):
-import std.c.linux.linux;
-
public import core.sys.posix.pthread;
diff --git a/std/c/linux/socket.d b/std/c/linux/socket.d
index fcc577d1cd2..2e677c8b06a 100644
--- a/std/c/linux/socket.d
+++ b/std/c/linux/socket.d
@@ -13,7 +13,7 @@ deprecated("Import the appropriate core.sys.posix.* modules instead")
module std.c.linux.socket;
version (linux):
-private import core.stdc.stdint;
+import core.stdc.stdint;
public import core.sys.posix.arpa.inet;
public import core.sys.posix.netdb;
public import core.sys.posix.netinet.in_;
diff --git a/std/c/osx/socket.d b/std/c/osx/socket.d
index 167af36a88e..4921e07a473 100644
--- a/std/c/osx/socket.d
+++ b/std/c/osx/socket.d
@@ -13,7 +13,7 @@ deprecated("Import the appropriate core.sys.posix.* instead")
module std.c.osx.socket;
version (OSX):
-private import core.stdc.stdint;
+import core.stdc.stdint;
public import core.sys.posix.arpa.inet;
public import core.sys.posix.netdb;
public import core.sys.posix.netinet.in_;
diff --git a/std/c/process.d b/std/c/process.d
index 14fc729c6c7..bafe962ebbe 100644
--- a/std/c/process.d
+++ b/std/c/process.d
@@ -12,7 +12,7 @@
deprecated("Import core.stdc.stdlib or the appropriate core.sys.posix.* modules instead")
module std.c.process;
-private import core.stdc.stddef;
+import core.stdc.stddef;
public import core.stdc.stdlib : exit, abort, system;
extern (C):
diff --git a/std/concurrency.d b/std/concurrency.d
index 397c9d76c82..acac8e102b7 100644
--- a/std/concurrency.d
+++ b/std/concurrency.d
@@ -19,38 +19,6 @@
* schedulers are available that multiplex fibers across the main thread or
* use some combination of the two approaches.
*
- * Synposis:
- * ---
- * import std.stdio;
- * import std.concurrency;
- *
- * void spawnedFunc(Tid ownerTid)
- * {
- * // Receive a message from the owner thread.
- * receive(
- * (int i) { writeln("Received the number ", i);}
- * );
- *
- * // Send a message back to the owner thread
- * // indicating success.
- * send(ownerTid, true);
- * }
- *
- * void main()
- * {
- * // Start spawnedFunc in a new thread.
- * auto childTid = spawn(&spawnedFunc, thisTid);
- *
- * // Send the number 42 to this new thread.
- * send(childTid, 42);
- *
- * // Receive the result code.
- * auto wasSuccessful = receiveOnly!(bool);
- * assert(wasSuccessful);
- * writeln("Successfully printed number.");
- * }
- * ---
- *
* Copyright: Copyright Sean Kelly 2009 - 2014.
* License: Boost License 1.0.
* Authors: Sean Kelly, Alex Rønne Petersen, Martin Nowak
@@ -70,8 +38,38 @@ import core.sync.condition;
import core.sync.mutex;
import core.thread;
import std.range.primitives;
+import std.range.interfaces : InputRange;
import std.traits;
+///
+@system unittest
+{
+ __gshared string received;
+ static void spawnedFunc(Tid ownerTid)
+ {
+ import std.conv : text;
+ // Receive a message from the owner thread.
+ receive((int i){
+ received = text("Received the number ", i);
+
+ // Send a message back to the owner thread
+ // indicating success.
+ send(ownerTid, true);
+ });
+ }
+
+ // Start spawnedFunc in a new thread.
+ auto childTid = spawn(&spawnedFunc, thisTid);
+
+ // Send the number 42 to this new thread.
+ send(childTid, 42);
+
+ // Receive the result code.
+ auto wasSuccessful = receiveOnly!(bool);
+ assert(wasSuccessful);
+ assert(received == "Received the number 42");
+}
+
private
{
template hasLocalAliasing(T...)
@@ -1496,7 +1494,8 @@ private interface IsGenerator {}
* }
* ---
*/
-class Generator(T) : Fiber, IsGenerator
+class Generator(T) :
+ Fiber, IsGenerator, InputRange!T
{
/**
* Initializes a generator object which is associated with a static
@@ -1585,13 +1584,52 @@ class Generator(T) : Fiber, IsGenerator
}
/**
- * Returns the most recently generated value.
+ * Returns the most recently generated value by shallow copy.
*/
final T front() @property
{
return *m_value;
}
+ /**
+ * Returns the most recently generated value without executing a
+ * copy contructor. Will not compile for element types defining a
+ * postblit, because Generator does not return by reference.
+ */
+ final T moveFront()
+ {
+ static if (!hasElaborateCopyConstructor!T)
+ {
+ return front;
+ }
+ else
+ {
+ static assert(0,
+ "Fiber front is always rvalue and thus cannot be moved since it defines a postblit.");
+ }
+ }
+
+ final int opApply(scope int delegate(T) loopBody)
+ {
+ int broken;
+ for (; !empty; popFront())
+ {
+ broken = loopBody(front);
+ if (broken) break;
+ }
+ return broken;
+ }
+
+ final int opApply(scope int delegate(size_t, T) loopBody)
+ {
+ int broken;
+ for (size_t i; !empty; ++i, popFront())
+ {
+ broken = loopBody(i, front);
+ if (broken) break;
+ }
+ return broken;
+ }
private:
T* m_value;
}
@@ -1668,6 +1706,39 @@ void yield(T)(T value)
testScheduler(new ThreadScheduler);
testScheduler(new FiberScheduler);
}
+///
+@system unittest
+{
+ import std.range;
+
+ InputRange!int myIota = iota(10).inputRangeObject;
+
+ myIota.popFront();
+ myIota.popFront();
+ assert(myIota.moveFront == 2);
+ assert(myIota.front == 2);
+ myIota.popFront();
+ assert(myIota.front == 3);
+
+ //can be assigned to std.range.interfaces.InputRange directly
+ myIota = new Generator!int(
+ {
+ foreach (i; 0 .. 10) yield(i);
+ });
+
+ myIota.popFront();
+ myIota.popFront();
+ assert(myIota.moveFront == 2);
+ assert(myIota.front == 2);
+ myIota.popFront();
+ assert(myIota.front == 3);
+
+ size_t[2] counter = [0, 0];
+ foreach (i, unused; myIota) counter[] += [1, i];
+
+ assert(myIota.empty);
+ assert(counter == [7, 21]);
+}
private
{
@@ -2164,7 +2235,7 @@ private
if (m_last is m_first)
m_last = null;
else if (m_last is n.next)
- m_last = n;
+ m_last = n; // nocoverage
Node* to_free = n.next;
n.next = n.next.next;
freeNode(to_free);
diff --git a/std/container/array.d b/std/container/array.d
index af01deff11e..eee890182ff 100644
--- a/std/container/array.d
+++ b/std/container/array.d
@@ -727,10 +727,10 @@ if (!is(Unqual!T == bool))
/**
* Forwards to `insertBack`.
*/
- void opOpAssign(string op, Stuff)(Stuff stuff)
+ void opOpAssign(string op, Stuff)(auto ref Stuff stuff)
if (op == "~")
{
- static if (is(typeof(stuff[])))
+ static if (is(typeof(stuff[])) && isImplicitlyConvertible!(typeof(stuff[0]), T))
{
insertBack(stuff[]);
}
@@ -2400,3 +2400,20 @@ if (is(Unqual!T == bool))
assert(a.length == 11, to!string(a.length));
assert(a[5]);
}
+@system unittest
+{
+ alias V3 = int[3];
+ V3 v = [1, 2, 3];
+ Array!V3 arr;
+ arr ~= v;
+ assert(arr[0] == [1, 2, 3]);
+}
+@system unittest
+{
+ alias V3 = int[3];
+ V3[2] v = [[1, 2, 3], [4, 5, 6]];
+ Array!V3 arr;
+ arr ~= v;
+ assert(arr[0] == [1, 2, 3]);
+ assert(arr[1] == [4, 5, 6]);
+}
diff --git a/std/container/rbtree.d b/std/container/rbtree.d
index 7db3ca61408..089dde36f84 100644
--- a/std/container/rbtree.d
+++ b/std/container/rbtree.d
@@ -688,7 +688,7 @@ private struct RBRange(N)
/**
* pop the front element from the range
*
- * complexity: amortized $(BIGOH 1)
+ * Complexity: amortized $(BIGOH 1)
*/
void popFront()
{
@@ -698,7 +698,7 @@ private struct RBRange(N)
/**
* pop the back element from the range
*
- * complexity: amortized $(BIGOH 1)
+ * Complexity: amortized $(BIGOH 1)
*/
void popBack()
{
diff --git a/std/conv.d b/std/conv.d
index 85a62cfdc08..042409a1e98 100644
--- a/std/conv.d
+++ b/std/conv.d
@@ -3991,7 +3991,7 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) &&
string text(T...)(T args)
if (T.length > 0) { return textImpl!string(args); }
-// @@@DEPRECATED_2017-06@@@
+// @@@DEPRECATED_2018-06@@@
deprecated("Calling `text` with 0 arguments is deprecated")
string text(T...)(T args)
if (T.length == 0) { return textImpl!string(args); }
@@ -4000,7 +4000,7 @@ if (T.length == 0) { return textImpl!string(args); }
wstring wtext(T...)(T args)
if (T.length > 0) { return textImpl!wstring(args); }
-// @@@DEPRECATED_2017-06@@@
+// @@@DEPRECATED_2018-06@@@
deprecated("Calling `wtext` with 0 arguments is deprecated")
wstring wtext(T...)(T args)
if (T.length == 0) { return textImpl!wstring(args); }
@@ -4017,7 +4017,7 @@ if (T.length > 0) { return textImpl!dstring(args); }
assert(dtext(42, ' ', 1.5, ": xyz") == "42 1.5: xyz"d);
}
-// @@@DEPRECATED_2017-06@@@
+// @@@DEPRECATED_2018-06@@@
deprecated("Calling `dtext` with 0 arguments is deprecated")
dstring dtext(T...)(T args)
if (T.length == 0) { return textImpl!dstring(args); }
diff --git a/std/csv.d b/std/csv.d
index a896159b5b0..b10f1e65b60 100644
--- a/std/csv.d
+++ b/std/csv.d
@@ -1099,7 +1099,6 @@ public:
}
}
-///
@safe pure unittest
{
import std.algorithm.comparison : equal;
@@ -1145,7 +1144,7 @@ private:
size_t[] _popCount;
public:
/*
- * params:
+ * Params:
* input = Pointer to a character input range
* delimiter = Separator for each column
* quote = Character used for quotation
@@ -1349,7 +1348,7 @@ public:
* start with either a delimiter or record break (\n, \r\n, \r) which
* must be removed for subsequent calls.
*
- * params:
+ * Params:
* input = Any CSV input
* ans = The first field in the input
* sep = The character to represent a comma in the specification
diff --git a/std/datetime/date.d b/std/datetime/date.d
index 453ebc3327f..ca1b0b644fa 100644
--- a/std/datetime/date.d
+++ b/std/datetime/date.d
@@ -115,7 +115,7 @@ public:
date = The date portion of $(LREF DateTime).
tod = The time portion of $(LREF DateTime).
+/
- this(in Date date, in TimeOfDay tod = TimeOfDay.init) @safe pure nothrow
+ this(in Date date, in TimeOfDay tod = TimeOfDay.init) @safe pure nothrow @nogc
{
_date = date;
_tod = tod;
@@ -146,7 +146,7 @@ public:
/++
Params:
year = The year portion of the date.
- month = The month portion of the date.
+ month = The month portion of the date (January is 1).
day = The day portion of the date.
hour = The hour portion of the time;
minute = The minute portion of the time;
@@ -184,7 +184,7 @@ public:
$(TR $(TD this > rhs) $(TD > 0))
)
+/
- int opCmp(in DateTime rhs) @safe const pure nothrow
+ int opCmp(in DateTime rhs) const @safe pure nothrow @nogc
{
immutable dateResult = _date.opCmp(rhs._date);
@@ -394,7 +394,7 @@ public:
/++
The date portion of $(LREF DateTime).
+/
- @property Date date() @safe const pure nothrow
+ @property Date date() const @safe pure nothrow @nogc
{
return _date;
}
@@ -424,7 +424,7 @@ public:
Params:
date = The Date to set this $(LREF DateTime)'s date portion to.
+/
- @property void date(in Date date) @safe pure nothrow
+ @property void date(in Date date) @safe pure nothrow @nogc
{
_date = date;
}
@@ -446,7 +446,7 @@ public:
/++
The time portion of $(LREF DateTime).
+/
- @property TimeOfDay timeOfDay() @safe const pure nothrow
+ @property TimeOfDay timeOfDay() const @safe pure nothrow @nogc
{
return _tod;
}
@@ -477,7 +477,7 @@ public:
tod = The $(REF TimeOfDay,std,datetime,date) to set this
$(LREF DateTime)'s time portion to.
+/
- @property void timeOfDay(in TimeOfDay tod) @safe pure nothrow
+ @property void timeOfDay(in TimeOfDay tod) @safe pure nothrow @nogc
{
_tod = tod;
}
@@ -500,7 +500,7 @@ public:
Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive
are B.C.
+/
- @property short year() @safe const pure nothrow
+ @property short year() const @safe pure nothrow @nogc
{
return _date.year;
}
@@ -573,7 +573,7 @@ public:
Throws:
$(REF DateTimeException,std,datetime,date) if $(D isAD) is true.
+/
- @property short yearBC() @safe const pure
+ @property short yearBC() const @safe pure
{
return _date.yearBC;
}
@@ -643,7 +643,7 @@ public:
/++
Month of a Gregorian Year.
+/
- @property Month month() @safe const pure nothrow
+ @property Month month() const @safe pure nothrow @nogc
{
return _date.month;
}
@@ -713,7 +713,7 @@ public:
/++
Day of a Gregorian Month.
+/
- @property ubyte day() @safe const pure nothrow
+ @property ubyte day() const @safe pure nothrow @nogc
{
return _date.day;
}
@@ -856,7 +856,7 @@ public:
/++
Hours past midnight.
+/
- @property ubyte hour() @safe const pure nothrow
+ @property ubyte hour() const @safe pure nothrow @nogc
{
return _tod.hour;
}
@@ -906,7 +906,7 @@ public:
/++
Minutes past the hour.
+/
- @property ubyte minute() @safe const pure nothrow
+ @property ubyte minute() const @safe pure nothrow @nogc
{
return _tod.minute;
}
@@ -956,7 +956,7 @@ public:
/++
Seconds past the minute.
+/
- @property ubyte second() @safe const pure nothrow
+ @property ubyte second() const @safe pure nothrow @nogc
{
return _tod.second;
}
@@ -1023,7 +1023,7 @@ public:
causing the month to increment.
+/
ref DateTime add(string units)
- (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow
+ (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow @nogc
if (units == "years" || units == "months")
{
_date.add!units(value, allowOverflow);
@@ -1085,7 +1085,7 @@ public:
causing the month to increment.
+/
ref DateTime roll(string units)
- (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow
+ (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow @nogc
if (units == "years" || units == "months")
{
_date.roll!units(value, allowOverflow);
@@ -1151,7 +1151,7 @@ public:
value = The number of $(D_PARAM units) to add to this
$(LREF DateTime).
+/
- ref DateTime roll(string units)(long value) @safe pure nothrow
+ ref DateTime roll(string units)(long value) @safe pure nothrow @nogc
if (units == "days")
{
_date.roll!"days"(value);
@@ -1192,7 +1192,7 @@ public:
// Shares documentation with "days" version.
- ref DateTime roll(string units)(long value) @safe pure nothrow
+ ref DateTime roll(string units)(long value) @safe pure nothrow @nogc
if (units == "hours" ||
units == "minutes" ||
units == "seconds")
@@ -2079,7 +2079,7 @@ public:
duration = The $(REF Duration, core,time) to add to or subtract from
this $(LREF DateTime).
+/
- DateTime opBinary(string op)(Duration duration) @safe const pure nothrow
+ DateTime opBinary(string op)(Duration duration) const @safe pure nothrow @nogc
if (op == "+" || op == "-")
{
DateTime retval = this;
@@ -2156,7 +2156,7 @@ public:
// Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@
deprecated("Use Duration instead of TickDuration.")
- DateTime opBinary(string op)(in TickDuration td) @safe const pure nothrow
+ DateTime opBinary(string op)(in TickDuration td) const @safe pure nothrow @nogc
if (op == "+" || op == "-")
{
DateTime retval = this;
@@ -2198,7 +2198,7 @@ public:
duration = The duration to add to or subtract from this
$(LREF DateTime).
+/
- ref DateTime opOpAssign(string op, D)(in D duration) @safe pure nothrow
+ ref DateTime opOpAssign(string op, D)(in D duration) @safe pure nothrow @nogc
if ((op == "+" || op == "-") &&
(is(Unqual!D == Duration) ||
is(Unqual!D == TickDuration)))
@@ -2300,7 +2300,7 @@ public:
// Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@
deprecated("Use Duration instead of TickDuration.")
- ref DateTime opOpAssign(string op)(TickDuration td) @safe pure nothrow
+ ref DateTime opOpAssign(string op)(TickDuration td) @safe pure nothrow @nogc
if (op == "+" || op == "-")
{
DateTime retval = this;
@@ -2350,7 +2350,7 @@ public:
$(TR $(TD DateTime) $(TD -) $(TD DateTime) $(TD -->) $(TD duration))
)
+/
- Duration opBinary(string op)(in DateTime rhs) @safe const pure nothrow
+ Duration opBinary(string op)(in DateTime rhs) const @safe pure nothrow @nogc
if (op == "-")
{
immutable dateResult = _date - rhs.date;
@@ -2435,7 +2435,7 @@ public:
Params:
rhs = The $(LREF DateTime) to subtract from this one.
+/
- int diffMonths(in DateTime rhs) @safe const pure nothrow
+ int diffMonths(in DateTime rhs) const @safe pure nothrow @nogc
{
return _date.diffMonths(rhs._date);
}
@@ -2478,7 +2478,7 @@ public:
/++
Whether this $(LREF DateTime) is in a leap year.
+/
- @property bool isLeapYear() @safe const pure nothrow
+ @property bool isLeapYear() const @safe pure nothrow @nogc
{
return _date.isLeapYear;
}
@@ -2497,7 +2497,7 @@ public:
/++
Day of the week this $(LREF DateTime) is on.
+/
- @property DayOfWeek dayOfWeek() @safe const pure nothrow
+ @property DayOfWeek dayOfWeek() const @safe pure nothrow @nogc
{
return _date.dayOfWeek;
}
@@ -2516,7 +2516,7 @@ public:
/++
Day of the year this $(LREF DateTime) is on.
+/
- @property ushort dayOfYear() @safe const pure nothrow
+ @property ushort dayOfYear() const @safe pure nothrow @nogc
{
return _date.dayOfYear;
}
@@ -2567,7 +2567,7 @@ public:
/++
The Xth day of the Gregorian Calendar that this $(LREF DateTime) is on.
+/
- @property int dayOfGregorianCal() @safe const pure nothrow
+ @property int dayOfGregorianCal() const @safe pure nothrow @nogc
{
return _date.dayOfGregorianCal;
}
@@ -2605,7 +2605,7 @@ public:
days = The day of the Gregorian Calendar to set this $(LREF DateTime)
to.
+/
- @property void dayOfGregorianCal(int days) @safe pure nothrow
+ @property void dayOfGregorianCal(int days) @safe pure nothrow @nogc
{
_date.dayOfGregorianCal = days;
}
@@ -2654,7 +2654,7 @@ public:
See_Also:
$(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
+/
- @property ubyte isoWeek() @safe const pure nothrow
+ @property ubyte isoWeek() const @safe pure nothrow
{
return _date.isoWeek;
}
@@ -2675,7 +2675,7 @@ public:
$(LREF DateTime) is in. The time portion of endOfMonth is always
23:59:59.
+/
- @property DateTime endOfMonth() @safe const pure nothrow
+ @property DateTime endOfMonth() const @safe pure nothrow
{
try
return DateTime(_date.endOfMonth, TimeOfDay(23, 59, 59));
@@ -2741,7 +2741,7 @@ public:
/++
The last day in the month that this $(LREF DateTime) is in.
+/
- @property ubyte daysInMonth() @safe const pure nothrow
+ @property ubyte daysInMonth() const @safe pure nothrow @nogc
{
return _date.daysInMonth;
}
@@ -2767,7 +2767,7 @@ public:
/++
Whether the current year is a date in A.D.
+/
- @property bool isAD() @safe const pure nothrow
+ @property bool isAD() const @safe pure nothrow @nogc
{
return _date.isAD;
}
@@ -2797,7 +2797,7 @@ public:
returns 2_450_173, while from noon onward, the julian day number would
be 2_450_174, so this function returns 2_450_174.
+/
- @property long julianDay() @safe const pure nothrow
+ @property long julianDay() const @safe pure nothrow @nogc
{
if (_tod._hour < 12)
return _date.julianDay - 1;
@@ -2842,7 +2842,7 @@ public:
The modified $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for any
time on this date (since, the modified Julian day changes at midnight).
+/
- @property long modJulianDay() @safe const pure nothrow
+ @property long modJulianDay() const @safe pure nothrow @nogc
{
return _date.modJulianDay;
}
@@ -2865,13 +2865,22 @@ public:
/++
Converts this $(LREF DateTime) to a string with the format YYYYMMDDTHHMMSS.
+/
- string toISOString() @safe const pure nothrow
+ string toISOString() const @safe pure nothrow
{
import std.format : format;
try
- return format("%sT%s", _date.toISOString(), _tod.toISOString());
+ {
+ return format!("%sT%02d%02d%02d")(
+ _date.toISOString(),
+ _tod._hour,
+ _tod._minute,
+ _tod._second
+ );
+ }
catch (Exception e)
+ {
assert(0, "format() threw.");
+ }
}
///
@@ -2918,13 +2927,22 @@ public:
Converts this $(LREF DateTime) to a string with the format
YYYY-MM-DDTHH:MM:SS.
+/
- string toISOExtString() @safe const pure nothrow
+ string toISOExtString() const @safe pure nothrow
{
import std.format : format;
try
- return format("%sT%s", _date.toISOExtString(), _tod.toISOExtString());
+ {
+ return format!("%sT%02d:%02d:%02d")(
+ _date.toISOExtString(),
+ _tod._hour,
+ _tod._minute,
+ _tod._second
+ );
+ }
catch (Exception e)
+ {
assert(0, "format() threw.");
+ }
}
///
@@ -2970,13 +2988,22 @@ public:
Converts this $(LREF DateTime) to a string with the format
YYYY-Mon-DD HH:MM:SS.
+/
- string toSimpleString() @safe const pure nothrow
+ string toSimpleString() const @safe pure nothrow
{
import std.format : format;
try
- return format("%s %s", _date.toSimpleString(), _tod.toString());
+ {
+ return format!("%s %02d:%02d:%02d")(
+ _date.toSimpleString(),
+ _tod._hour,
+ _tod._minute,
+ _tod._second
+ );
+ }
catch (Exception e)
+ {
assert(0, "format() threw.");
+ }
}
///
@@ -3021,8 +3048,28 @@ public:
/++
Converts this $(LREF DateTime) to a string.
+
+ This function exists to make it easy to convert a $(LREF DateTime) to a
+ string for code that does not care what the exact format is - just that
+ it presents the information in a clear manner. It also makes it easy to
+ simply convert a $(LREF DateTime) to a string when using functions such
+ as `to!string`, `format`, or `writeln` which use toString to convert
+ user-defined types. So, it is unlikely that much code will call
+ toString directly.
+
+ The format of the string is purposefully unspecified, and code that
+ cares about the format of the string should use `toISOString`,
+ `toISOExtString`, `toSimpleString`, or some other custom formatting
+ function that explicitly generates the format that the code needs. The
+ reason is that the code is then clear about what format it's using,
+ making it less error-prone to maintain the code and interact with other
+ software that consumes the generated strings. It's for this same reason
+ that $(LREF DateTime) has no `fromString` function, whereas it does have
+ `fromISOString`, `fromISOExtString`, and `fromSimpleString`.
+
+ The format returned by toString may or may not change in the future.
+/
- string toString() @safe const pure nothrow
+ string toString() const @safe pure nothrow
{
return toSimpleString();
}
@@ -3055,20 +3102,19 @@ public:
if (isSomeString!S)
{
import std.algorithm.searching : countUntil;
- import std.conv : to;
import std.exception : enforce;
import std.format : format;
import std.string : strip;
- immutable dstr = to!dstring(strip(isoString));
+ immutable str = strip(isoString);
- enforce(dstr.length >= 15, new DateTimeException(format("Invalid ISO String: %s", isoString)));
- auto t = dstr.countUntil('T');
+ enforce(str.length >= 15, new DateTimeException(format("Invalid ISO String: %s", isoString)));
+ auto t = str.countUntil('T');
enforce(t != -1, new DateTimeException(format("Invalid ISO String: %s", isoString)));
- immutable date = Date.fromISOString(dstr[0 .. t]);
- immutable tod = TimeOfDay.fromISOString(dstr[t+1 .. $]);
+ immutable date = Date.fromISOString(str[0 .. t]);
+ immutable tod = TimeOfDay.fromISOString(str[t+1 .. $]);
return DateTime(date, tod);
}
@@ -3144,20 +3190,19 @@ public:
if (isSomeString!(S))
{
import std.algorithm.searching : countUntil;
- import std.conv : to;
import std.exception : enforce;
import std.format : format;
import std.string : strip;
- immutable dstr = to!dstring(strip(isoExtString));
+ immutable str = strip(isoExtString);
- enforce(dstr.length >= 15, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
- auto t = dstr.countUntil('T');
+ enforce(str.length >= 15, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
+ auto t = str.countUntil('T');
enforce(t != -1, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
- immutable date = Date.fromISOExtString(dstr[0 .. t]);
- immutable tod = TimeOfDay.fromISOExtString(dstr[t+1 .. $]);
+ immutable date = Date.fromISOExtString(str[0 .. t]);
+ immutable tod = TimeOfDay.fromISOExtString(str[t+1 .. $]);
return DateTime(date, tod);
}
@@ -3232,20 +3277,19 @@ public:
if (isSomeString!(S))
{
import std.algorithm.searching : countUntil;
- import std.conv : to;
import std.exception : enforce;
import std.format : format;
import std.string : strip;
- immutable dstr = to!dstring(strip(simpleString));
+ immutable str = strip(simpleString);
- enforce(dstr.length >= 15, new DateTimeException(format("Invalid string format: %s", simpleString)));
- auto t = dstr.countUntil(' ');
+ enforce(str.length >= 15, new DateTimeException(format("Invalid string format: %s", simpleString)));
+ auto t = str.countUntil(' ');
enforce(t != -1, new DateTimeException(format("Invalid string format: %s", simpleString)));
- immutable date = Date.fromSimpleString(dstr[0 .. t]);
- immutable tod = TimeOfDay.fromISOExtString(dstr[t+1 .. $]);
+ immutable date = Date.fromSimpleString(str[0 .. t]);
+ immutable tod = TimeOfDay.fromISOExtString(str[t+1 .. $]);
return DateTime(date, tod);
}
@@ -3311,7 +3355,7 @@ public:
Returns the $(LREF DateTime) farthest in the past which is representable
by $(LREF DateTime).
+/
- @property static DateTime min() @safe pure nothrow
+ @property static DateTime min() @safe pure nothrow @nogc
out(result)
{
assert(result._date == Date.min);
@@ -3338,7 +3382,7 @@ public:
Returns the $(LREF DateTime) farthest in the future which is
representable by $(LREF DateTime).
+/
- @property static DateTime max() @safe pure nothrow
+ @property static DateTime max() @safe pure nothrow @nogc
out(result)
{
assert(result._date == Date.max);
@@ -3375,7 +3419,7 @@ private:
Params:
seconds = The number of seconds to add to this $(LREF DateTime).
+/
- ref DateTime _addSeconds(long seconds) return @safe pure nothrow
+ ref DateTime _addSeconds(long seconds) return @safe pure nothrow @nogc
{
long hnsecs = convert!("seconds", "hnsecs")(seconds);
hnsecs += convert!("hours", "hnsecs")(_tod._hour);
@@ -3609,7 +3653,7 @@ public:
year = Year of the Gregorian Calendar. Positive values are A.D.
Non-positive values are B.C. with year 0 being the year
prior to 1 A.D.
- month = Month of the year.
+ month = Month of the year (January is 1).
day = Day of the month.
+/
this(int year, int month, int day) @safe pure
@@ -3688,7 +3732,7 @@ public:
day = The Xth day of the Gregorian Calendar that the constructed
$(LREF Date) will be for.
+/
- this(int day) @safe pure nothrow
+ this(int day) @safe pure nothrow @nogc
{
if (day > 0)
{
@@ -3738,20 +3782,14 @@ public:
{
_year = cast(short) years;
- try
- dayOfYear = day;
- catch (Exception e)
- assert(0, "dayOfYear assignment threw.");
+ setDayOfYear(day);
}
}
else if (day <= 0 && -day < daysInLeapYear)
{
_year = 0;
- try
- dayOfYear = (daysInLeapYear + day);
- catch (Exception e)
- assert(0, "dayOfYear assignment threw.");
+ setDayOfYear(daysInLeapYear + day);
}
else
{
@@ -3803,10 +3841,7 @@ public:
_year = cast(short) years;
immutable newDoY = (yearIsLeapYear(_year) ? daysInLeapYear : daysInYear) + day + 1;
- try
- dayOfYear = newDoY;
- catch (Exception e)
- assert(0, "dayOfYear assignment threw.");
+ setDayOfYear(newDoY);
}
}
}
@@ -3831,7 +3866,7 @@ public:
$(TR $(TD this > rhs) $(TD > 0))
)
+/
- int opCmp(in Date rhs) @safe const pure nothrow
+ int opCmp(in Date rhs) const @safe pure nothrow @nogc
{
if (_year < rhs._year)
return -1;
@@ -3939,7 +3974,7 @@ public:
Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive
are B.C.
+/
- @property short year() @safe const pure nothrow
+ @property short year() const @safe pure nothrow @nogc
{
return _year;
}
@@ -4021,7 +4056,7 @@ public:
Throws:
$(REF DateTimeException,std,datetime,date) if $(D isAD) is true.
+/
- @property ushort yearBC() @safe const pure
+ @property ushort yearBC() const @safe pure
{
import std.format : format;
@@ -4096,7 +4131,7 @@ public:
/++
Month of a Gregorian Year.
+/
- @property Month month() @safe const pure nothrow
+ @property Month month() const @safe pure nothrow @nogc
{
return _month;
}
@@ -4166,7 +4201,7 @@ public:
/++
Day of a Gregorian Month.
+/
- @property ubyte day() @safe const pure nothrow
+ @property ubyte day() const @safe pure nothrow @nogc
{
return _day;
}
@@ -4324,7 +4359,8 @@ public:
allowOverflow = Whether the day should be allowed to overflow,
causing the month to increment.
+/
- ref Date add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow
+ @safe pure nothrow @nogc
+ ref Date add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
if (units == "years")
{
_year += value;
@@ -4564,7 +4600,8 @@ public:
// Shares documentation with "years" version.
- ref Date add(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow
+ @safe pure nothrow @nogc
+ ref Date add(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
if (units == "months")
{
auto years = months / 12;
@@ -5104,7 +5141,8 @@ public:
allowOverflow = Whether the day should be allowed to overflow,
causing the month to increment.
+/
- ref Date roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow
+ @safe pure nothrow @nogc
+ ref Date roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
if (units == "years")
{
return add!"years"(value, allowOverflow);
@@ -5148,7 +5186,8 @@ public:
// Shares documentation with "years" version.
- ref Date roll(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow
+ @safe pure nothrow @nogc
+ ref Date roll(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
if (units == "months")
{
months %= 12;
@@ -5742,7 +5781,7 @@ public:
units = The units to add. Must be $(D "days").
days = The number of days to add to this $(LREF Date).
+/
- ref Date roll(string units)(long days) @safe pure nothrow
+ ref Date roll(string units)(long days) @safe pure nothrow @nogc
if (units == "days")
{
immutable limit = maxDay(_year, _month);
@@ -5980,7 +6019,7 @@ public:
duration = The $(REF Duration, core,time) to add to or subtract from
this $(LREF Date).
+/
- Date opBinary(string op)(Duration duration) @safe const pure nothrow
+ Date opBinary(string op)(Duration duration) const @safe pure nothrow @nogc
if (op == "+" || op == "-")
{
Date retval = this;
@@ -6054,7 +6093,7 @@ public:
// Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@
deprecated("Use Duration instead of TickDuration.")
- Date opBinary(string op)(TickDuration td) @safe const pure nothrow
+ Date opBinary(string op)(TickDuration td) const @safe pure nothrow @nogc
if (op == "+" || op == "-")
{
Date retval = this;
@@ -6095,7 +6134,7 @@ public:
duration = The $(REF Duration, core,time) to add to or subtract from
this $(LREF Date).
+/
- ref Date opOpAssign(string op)(Duration duration) @safe pure nothrow
+ ref Date opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc
if (op == "+" || op == "-")
{
immutable days = duration.total!"days";
@@ -6161,7 +6200,7 @@ public:
// Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@
deprecated("Use Duration instead of TickDuration.")
- ref Date opOpAssign(string op)(TickDuration td) @safe pure nothrow
+ ref Date opOpAssign(string op)(TickDuration td) @safe pure nothrow @nogc
if (op == "+" || op == "-")
{
immutable days = convert!("seconds", "days")(td.seconds);
@@ -6210,7 +6249,7 @@ public:
$(TR $(TD Date) $(TD -) $(TD Date) $(TD -->) $(TD duration))
)
+/
- Duration opBinary(string op)(in Date rhs) @safe const pure nothrow
+ Duration opBinary(string op)(in Date rhs) const @safe pure nothrow @nogc
if (op == "-")
{
return dur!"days"(this.dayOfGregorianCal - rhs.dayOfGregorianCal);
@@ -6264,7 +6303,7 @@ public:
Params:
rhs = The $(LREF Date) to subtract from this one.
+/
- int diffMonths(in Date rhs) @safe const pure nothrow
+ int diffMonths(in Date rhs) const @safe pure nothrow @nogc
{
immutable yearDiff = _year - rhs._year;
immutable monthDiff = _month - rhs._month;
@@ -6502,7 +6541,7 @@ public:
/++
Whether this $(LREF Date) is in a leap year.
+/
- @property bool isLeapYear() @safe const pure nothrow
+ @property bool isLeapYear() const @safe pure nothrow @nogc
{
return yearIsLeapYear(_year);
}
@@ -6521,7 +6560,7 @@ public:
/++
Day of the week this $(LREF Date) is on.
+/
- @property DayOfWeek dayOfWeek() @safe const pure nothrow
+ @property DayOfWeek dayOfWeek() const @safe pure nothrow @nogc
{
return getDayOfWeek(dayOfGregorianCal);
}
@@ -6540,7 +6579,7 @@ public:
/++
Day of the year this $(LREF Date) is on.
+/
- @property ushort dayOfYear() @safe const pure nothrow
+ @property ushort dayOfYear() const @safe pure nothrow @nogc
{
if (_month >= Month.jan && _month <= Month.dec)
{
@@ -6595,11 +6634,25 @@ public:
invalid day of the year.
+/
@property void dayOfYear(int day) @safe pure
+ {
+ setDayOfYear!true(day);
+ }
+
+ private void setDayOfYear(bool useExceptions = false)(int day)
{
immutable int[] lastDay = isLeapYear ? lastDayLeap : lastDayNonLeap;
- if (day <= 0 || day > (isLeapYear ? daysInLeapYear : daysInYear))
- throw new DateTimeException("Invalid day of the year.");
+ bool dayOutOfRange = day <= 0 || day > (isLeapYear ? daysInLeapYear : daysInYear);
+ enum errorMsg = "Invalid day of the year.";
+
+ static if (useExceptions)
+ {
+ if (dayOutOfRange) throw new DateTimeException(errorMsg);
+ }
+ else
+ {
+ assert(!dayOutOfRange, errorMsg);
+ }
foreach (i; 1 .. lastDay.length)
{
@@ -6644,7 +6697,7 @@ public:
/++
The Xth day of the Gregorian Calendar that this $(LREF Date) is on.
+/
- @property int dayOfGregorianCal() @safe const pure nothrow
+ @property int dayOfGregorianCal() const @safe pure nothrow @nogc
{
if (isAD)
{
@@ -6733,7 +6786,7 @@ public:
Params:
day = The day of the Gregorian Calendar to set this $(LREF Date) to.
+/
- @property void dayOfGregorianCal(int day) @safe pure nothrow
+ @property void dayOfGregorianCal(int day) @safe pure nothrow @nogc
{
this = Date(day);
}
@@ -6785,7 +6838,7 @@ public:
See_Also:
$(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
+/
- @property ubyte isoWeek() @safe const pure nothrow
+ @property ubyte isoWeek() const @safe pure nothrow
{
immutable weekday = dayOfWeek;
immutable adjustedWeekday = weekday == DayOfWeek.sun ? 7 : weekday;
@@ -6884,7 +6937,7 @@ public:
/++
$(LREF Date) for the last day in the month that this $(LREF Date) is in.
+/
- @property Date endOfMonth() @safe const pure nothrow
+ @property Date endOfMonth() const @safe pure nothrow
{
try
return Date(_year, _month, maxDay(_year, _month));
@@ -6943,7 +6996,7 @@ public:
/++
The last day in the month that this $(LREF Date) is in.
+/
- @property ubyte daysInMonth() @safe const pure nothrow
+ @property ubyte daysInMonth() const @safe pure nothrow @nogc
{
return maxDay(_year, _month);
}
@@ -6999,7 +7052,7 @@ public:
/++
Whether the current year is a date in A.D.
+/
- @property bool isAD() @safe const pure nothrow
+ @property bool isAD() const @safe pure nothrow @nogc
{
return _year > 0;
}
@@ -7032,7 +7085,7 @@ public:
The $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for this
$(LREF Date) at noon (since the Julian day changes at noon).
+/
- @property long julianDay() @safe const pure nothrow
+ @property long julianDay() const @safe pure nothrow @nogc
{
return dayOfGregorianCal + 1_721_425;
}
@@ -7060,7 +7113,7 @@ public:
any time on this date (since, the modified Julian day changes at
midnight).
+/
- @property long modJulianDay() @safe const pure nothrow
+ @property long modJulianDay() const @safe pure nothrow @nogc
{
return julianDay - 2_400_001;
}
@@ -7080,7 +7133,7 @@ public:
/++
Converts this $(LREF Date) to a string with the format YYYYMMDD.
+/
- string toISOString() @safe const pure nothrow
+ string toISOString() const @safe pure nothrow
{
import std.format : format;
try
@@ -7136,7 +7189,7 @@ public:
/++
Converts this $(LREF Date) to a string with the format YYYY-MM-DD.
+/
- string toISOExtString() @safe const pure nothrow
+ string toISOExtString() const @safe pure nothrow
{
import std.format : format;
try
@@ -7192,7 +7245,7 @@ public:
/++
Converts this $(LREF Date) to a string with the format YYYY-Mon-DD.
+/
- string toSimpleString() @safe const pure nothrow
+ string toSimpleString() const @safe pure nothrow
{
import std.format : format;
try
@@ -7248,8 +7301,28 @@ public:
/++
Converts this $(LREF Date) to a string.
+
+ This function exists to make it easy to convert a $(LREF Date) to a
+ string for code that does not care what the exact format is - just that
+ it presents the information in a clear manner. It also makes it easy to
+ simply convert a $(LREF Date) to a string when using functions such as
+ `to!string`, `format`, or `writeln` which use toString to convert
+ user-defined types. So, it is unlikely that much code will call
+ toString directly.
+
+ The format of the string is purposefully unspecified, and code that
+ cares about the format of the string should use `toISOString`,
+ `toISOExtString`, `toSimpleString`, or some other custom formatting
+ function that explicitly generates the format that the code needs. The
+ reason is that the code is then clear about what format it's using,
+ making it less error-prone to maintain the code and interact with other
+ software that consumes the generated strings. It's for this same reason
+ $(LREF Date) has no `fromString` function, whereas it does have
+ `fromISOString`, `fromISOExtString`, and `fromSimpleString`.
+
+ The format returned by toString may or may not change in the future.
+/
- string toString() @safe const pure nothrow
+ string toString() const @safe pure nothrow
{
return toSimpleString();
}
@@ -7654,7 +7727,7 @@ public:
Returns the $(LREF Date) farthest in the past which is representable by
$(LREF Date).
+/
- @property static Date min() @safe pure nothrow
+ @property static Date min() @safe pure nothrow @nogc
{
auto date = Date.init;
date._year = short.min;
@@ -7675,7 +7748,7 @@ public:
Returns the $(LREF Date) farthest in the future which is representable
by $(LREF Date).
+/
- @property static Date max() @safe pure nothrow
+ @property static Date max() @safe pure nothrow @nogc
{
auto date = Date.init;
date._year = short.max;
@@ -7702,7 +7775,7 @@ private:
month = The month of the Gregorian Calendar to test.
day = The day of the month to test.
+/
- static bool _valid(int year, int month, int day) @safe pure nothrow
+ static bool _valid(int year, int month, int day) @safe pure nothrow @nogc
{
if (!valid!"months"(month))
return false;
@@ -7728,7 +7801,7 @@ package:
Params:
days = The number of days to add to this Date.
+/
- ref Date _addDays(long days) return @safe pure nothrow
+ ref Date _addDays(long days) return @safe pure nothrow @nogc
{
dayOfGregorianCal = cast(int)(dayOfGregorianCal + days);
return this;
@@ -7978,7 +8051,7 @@ public:
$(TR $(TD this > rhs) $(TD > 0))
)
+/
- int opCmp(in TimeOfDay rhs) @safe const pure nothrow
+ int opCmp(in TimeOfDay rhs) const @safe pure nothrow @nogc
{
if (_hour < rhs._hour)
return -1;
@@ -8038,7 +8111,7 @@ public:
/++
Hours past midnight.
+/
- @property ubyte hour() @safe const pure nothrow
+ @property ubyte hour() const @safe pure nothrow @nogc
{
return _hour;
}
@@ -8089,7 +8162,7 @@ public:
/++
Minutes past the hour.
+/
- @property ubyte minute() @safe const pure nothrow
+ @property ubyte minute() const @safe pure nothrow @nogc
{
return _minute;
}
@@ -8140,7 +8213,7 @@ public:
/++
Seconds past the minute.
+/
- @property ubyte second() @safe const pure nothrow
+ @property ubyte second() const @safe pure nothrow @nogc
{
return _second;
}
@@ -8204,7 +8277,7 @@ public:
value = The number of $(D_PARAM units) to add to this
$(LREF TimeOfDay).
+/
- ref TimeOfDay roll(string units)(long value) @safe pure nothrow
+ ref TimeOfDay roll(string units)(long value) @safe pure nothrow @nogc
if (units == "hours")
{
return this += dur!"hours"(value);
@@ -8252,7 +8325,7 @@ public:
// Shares documentation with "hours" version.
- ref TimeOfDay roll(string units)(long value) @safe pure nothrow
+ ref TimeOfDay roll(string units)(long value) @safe pure nothrow @nogc
if (units == "minutes" || units == "seconds")
{
import std.format : format;
@@ -8447,7 +8520,7 @@ public:
duration = The $(REF Duration, core,time) to add to or subtract from
this $(LREF TimeOfDay).
+/
- TimeOfDay opBinary(string op)(Duration duration) @safe const pure nothrow
+ TimeOfDay opBinary(string op)(Duration duration) const @safe pure nothrow @nogc
if (op == "+" || op == "-")
{
TimeOfDay retval = this;
@@ -8517,7 +8590,7 @@ public:
// Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@
deprecated("Use Duration instead of TickDuration.")
- TimeOfDay opBinary(string op)(TickDuration td) @safe const pure nothrow
+ TimeOfDay opBinary(string op)(TickDuration td) const @safe pure nothrow @nogc
if (op == "+" || op == "-")
{
TimeOfDay retval = this;
@@ -8559,7 +8632,7 @@ public:
duration = The $(REF Duration, core,time) to add to or subtract from
this $(LREF TimeOfDay).
+/
- ref TimeOfDay opOpAssign(string op)(Duration duration) @safe pure nothrow
+ ref TimeOfDay opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc
if (op == "+" || op == "-")
{
immutable seconds = duration.total!"seconds";
@@ -8612,7 +8685,7 @@ public:
// Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@
deprecated("Use Duration instead of TickDuration.")
- ref TimeOfDay opOpAssign(string op)(TickDuration td) @safe pure nothrow
+ ref TimeOfDay opOpAssign(string op)(TickDuration td) @safe pure nothrow @nogc
if (op == "+" || op == "-")
{
immutable seconds = td.seconds;
@@ -8665,7 +8738,7 @@ public:
Params:
rhs = The $(LREF TimeOfDay) to subtract from this one.
+/
- Duration opBinary(string op)(in TimeOfDay rhs) @safe const pure nothrow
+ Duration opBinary(string op)(in TimeOfDay rhs) const @safe pure nothrow @nogc
if (op == "-")
{
immutable lhsSec = _hour * 3600 + _minute * 60 + _second;
@@ -8706,7 +8779,7 @@ public:
/++
Converts this $(LREF TimeOfDay) to a string with the format HHMMSS.
+/
- string toISOString() @safe const pure nothrow
+ string toISOString() const @safe pure nothrow
{
import std.format : format;
try
@@ -8736,7 +8809,7 @@ public:
/++
Converts this $(LREF TimeOfDay) to a string with the format HH:MM:SS.
+/
- string toISOExtString() @safe const pure nothrow
+ string toISOExtString() const @safe pure nothrow
{
import std.format : format;
try
@@ -8765,8 +8838,28 @@ public:
/++
Converts this TimeOfDay to a string.
+
+ This function exists to make it easy to convert a $(LREF TimeOfDay) to a
+ string for code that does not care what the exact format is - just that
+ it presents the information in a clear manner. It also makes it easy to
+ simply convert a $(LREF TimeOfDay) to a string when using functions such
+ as `to!string`, `format`, or `writeln` which use toString to convert
+ user-defined types. So, it is unlikely that much code will call
+ toString directly.
+
+ The format of the string is purposefully unspecified, and code that
+ cares about the format of the string should use `toISOString`,
+ `toISOExtString`, or some other custom formatting function that
+ explicitly generates the format that the code needs. The reason is that
+ the code is then clear about what format it's using, making it less
+ error-prone to maintain the code and interact with other software that
+ consumes the generated strings. It's for this same reason that
+ $(LREF TimeOfDay) has no `fromString` function, whereas it does have
+ `fromISOString` and `fromISOExtString`.
+
+ The format returned by toString may or may not change in the future.
+/
- string toString() @safe const pure nothrow
+ string toString() const @safe pure nothrow
{
return toISOExtString();
}
@@ -9012,7 +9105,7 @@ public:
/++
Returns midnight.
+/
- @property static TimeOfDay min() @safe pure nothrow
+ @property static TimeOfDay min() @safe pure nothrow @nogc
{
return TimeOfDay.init;
}
@@ -9029,7 +9122,7 @@ public:
/++
Returns one second short of midnight.
+/
- @property static TimeOfDay max() @safe pure nothrow
+ @property static TimeOfDay max() @safe pure nothrow @nogc
{
auto tod = TimeOfDay.init;
tod._hour = maxHour;
@@ -9061,7 +9154,7 @@ private:
Params:
seconds = The number of seconds to add to this TimeOfDay.
+/
- ref TimeOfDay _addSeconds(long seconds) return @safe pure nothrow
+ ref TimeOfDay _addSeconds(long seconds) return @safe pure nothrow @nogc
{
long hnsecs = convert!("seconds", "hnsecs")(seconds);
hnsecs += convert!("hours", "hnsecs")(_hour);
@@ -9163,7 +9256,7 @@ private:
/+
Whether the given values form a valid $(LREF TimeOfDay).
+/
- static bool _valid(int hour, int minute, int second) @safe pure nothrow
+ static bool _valid(int hour, int minute, int second) @safe pure nothrow @nogc
{
return valid!"hours"(hour) && valid!"minutes"(minute) && valid!"seconds"(second);
}
@@ -9199,7 +9292,7 @@ package:
units = The units of time to validate.
value = The number to validate.
+/
-bool valid(string units)(int value) @safe pure nothrow
+bool valid(string units)(int value) @safe pure nothrow @nogc
if (units == "months" ||
units == "hours" ||
units == "minutes" ||
@@ -9230,10 +9323,10 @@ if (units == "months" ||
Params:
units = The units of time to validate.
year = The year of the day to validate.
- month = The month of the day to validate.
+ month = The month of the day to validate (January is 1).
day = The day to validate.
+/
-bool valid(string units)(int year, int month, int day) @safe pure nothrow
+bool valid(string units)(int year, int month, int day) @safe pure nothrow @nogc
if (units == "days")
{
return day > 0 && day <= maxDay(year, month);
@@ -9313,7 +9406,7 @@ if (units == "days")
currDoW = The current day of the week.
dow = The day of the week to get the number of days to.
+/
-int daysToDayOfWeek(DayOfWeek currDoW, DayOfWeek dow) @safe pure nothrow
+int daysToDayOfWeek(DayOfWeek currDoW, DayOfWeek dow) @safe pure nothrow @nogc
{
if (currDoW == dow)
return 0;
@@ -9464,7 +9557,7 @@ int monthsToMonth(int currMonth, int month) @safe pure
Params:
year = The year to to be tested.
+/
-bool yearIsLeapYear(int year) @safe pure nothrow
+bool yearIsLeapYear(int year) @safe pure nothrow @nogc
{
if (year % 400 == 0)
return true;
@@ -9597,7 +9690,7 @@ private:
can handle precision greater than hnsecs, and the few functions in core.time
which deal with "nsecs" deal with it explicitly.
+/
-bool validTimeUnits(string[] units...) @safe pure nothrow
+bool validTimeUnits(string[] units...) @safe pure nothrow @nogc
{
import std.algorithm.searching : canFind;
foreach (str; units)
@@ -9688,7 +9781,7 @@ if (validTimeUnits(lhs, rhs))
// Helper function for CmpTimeUnits.
-private int cmpTimeUnitsCTFE(string lhs, string rhs) @safe pure nothrow
+private int cmpTimeUnitsCTFE(string lhs, string rhs) @safe pure nothrow @nogc
{
import std.algorithm.searching : countUntil;
auto tstrings = timeStrings;
@@ -9754,7 +9847,7 @@ immutable string[12] _monthNames = ["Jan",
year = The year to get the day for.
month = The month of the Gregorian Calendar to get the day for.
+/
-ubyte maxDay(int year, int month) @safe pure nothrow
+ubyte maxDay(int year, int month) @safe pure nothrow @nogc
in
{
assert(valid!"months"(month));
@@ -9844,7 +9937,7 @@ body
Returns:
The number of the given units from converting hnsecs to those units.
+/
-long splitUnitsFromHNSecs(string units)(ref long hnsecs) @safe pure nothrow
+long splitUnitsFromHNSecs(string units)(ref long hnsecs) @safe pure nothrow @nogc
if (validTimeUnits(units) && CmpTimeUnits!(units, "months") < 0)
{
import core.time : convert;
@@ -9873,7 +9966,7 @@ if (validTimeUnits(units) && CmpTimeUnits!(units, "months") < 0)
day = The day of the Gregorian Calendar for which to get the day of
the week.
+/
-DayOfWeek getDayOfWeek(int day) @safe pure nothrow
+DayOfWeek getDayOfWeek(int day) @safe pure nothrow @nogc
{
// January 1st, 1 A.D. was a Monday
if (day >= 0)
diff --git a/std/datetime/interval.d b/std/datetime/interval.d
index 302b4c28e99..936f280bebf 100644
--- a/std/datetime/interval.d
+++ b/std/datetime/interval.d
@@ -7690,7 +7690,8 @@ if (isTimePoint!TP &&
dir = The direction to iterate in. If passing the return value to
$(D fwdRange), use $(D Direction.fwd). If passing it to
$(D bwdRange), use $(D Direction.bwd).
- month = The month that each time point in the range will be in.
+ month = The month that each time point in the range will be in
+ (January is 1).
+/
TP delegate(in TP) everyMonth(TP, Direction dir = Direction.fwd)(int month)
if (isTimePoint!TP &&
diff --git a/std/datetime/systime.d b/std/datetime/systime.d
index 46eee5c8325..a4fc5b3201a 100644
--- a/std/datetime/systime.d
+++ b/std/datetime/systime.d
@@ -7785,11 +7785,12 @@ public:
generated +HH:MM or -HH:MM for the time zone when it was not
$(REF LocalTime,std,datetime,timezone) or
$(REF UTC,std,datetime,timezone), which is not in conformance with
- ISO 9601 for the non-extended string format. This has now been
+ ISO 8601 for the non-extended string format. This has now been
fixed. However, for now, fromISOString will continue to accept the
extended format for the time zone so that any code which has been
writing out the result of toISOString to read in later will continue
- to work.)
+ to work. The current behavior will be kept until July 2019 at which
+ point, fromISOString will be fixed to be standards compliant.)
+/
string toISOString() @safe const nothrow
{
@@ -8180,6 +8181,26 @@ public:
/++
Converts this $(LREF SysTime) to a string.
+
+ This function exists to make it easy to convert a $(LREF SysTime) to a
+ string for code that does not care what the exact format is - just that
+ it presents the information in a clear manner. It also makes it easy to
+ simply convert a $(LREF SysTime) to a string when using functions such
+ as `to!string`, `format`, or `writeln` which use toString to convert
+ user-defined types. So, it is unlikely that much code will call
+ toString directly.
+
+ The format of the string is purposefully unspecified, and code that
+ cares about the format of the string should use `toISOString`,
+ `toISOExtString`, `toSimpleString`, or some other custom formatting
+ function that explicitly generates the format that the code needs. The
+ reason is that the code is then clear about what format it's using,
+ making it less error-prone to maintain the code and interact with other
+ software that consumes the generated strings. It's for this same reason
+ that $(LREF SysTime) has no `fromString` function, whereas it does have
+ `fromISOString`, `fromISOExtString`, and `fromSimpleString`.
+
+ The format returned by toString may or may not change in the future.
+/
string toString() @safe const nothrow
{
@@ -8228,11 +8249,12 @@ public:
$(LREF toISOExtString) and generated +HH:MM or -HH:MM for the time
zone when it was not $(REF LocalTime,std,datetime,timezone) or
$(REF UTC,std,datetime,timezone), which is not in conformance with
- ISO 9601 for the non-extended string format. This has now been
+ ISO 8601 for the non-extended string format. This has now been
fixed. However, for now, fromISOString will continue to accept the
extended format for the time zone so that any code which has been
writing out the result of toISOString to read in later will continue
- to work.)
+ to work. The current behavior will be kept until July 2019 at which
+ point, fromISOString will be fixed to be standards compliant.)
Params:
isoString = A string formatted in the ISO format for dates and times.
@@ -8433,16 +8455,16 @@ public:
test("20101222T172201.0000000+0130", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90));
test("20101222T172201.45+0800", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), east480));
- // @@@DEPRECATED_2017-07@@@
+ // @@@DEPRECATED_2019-07@@@
// This isn't deprecated per se, but that text will make it so that it
- // pops up when deprecations are moved along around July 2017. At that
- // time, the notice on the documentation should be removed, and we may
- // or may not change the behavior of fromISOString to no longer accept
- // ISO extended time zones (the concern being that programs will have
- // written out strings somewhere to read in again that they'll still be
- // reading in for years to come and may not be able to fix, even if the
- // code is fixed). If/when we do change the behavior, these tests will
- // start failing and will need to be updated accordingly.
+ // pops up when deprecations are moved along around July 2019. At that
+ // time, we will update fromISOString so that it is conformant with ISO
+ // 8601, and it will no longer accept ISO extended time zones (it does
+ // currently because of issue #15654 - toISOString used to incorrectly
+ // use the ISO extended time zone format). These tests will then start
+ // failing will need to be updated accordingly. Also, the notes about
+ // this issue in toISOString and fromISOString's documentation will need
+ // to be removed.
test("20101222T172201-01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west60));
test("20101222T172201-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west90));
test("20101222T172201-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west480));
diff --git a/std/datetime/timezone.d b/std/datetime/timezone.d
index 0b3bfa74e2d..4743292c9c5 100644
--- a/std/datetime/timezone.d
+++ b/std/datetime/timezone.d
@@ -142,43 +142,7 @@ public:
return dur!"hnsecs"(utcToTZ(stdTime) - stdTime);
}
- // @@@DEPRECATED_2017-07@@@
- /++
- $(RED Deprecated. Use either PosixTimeZone.getTimeZone or
- WindowsTimeZone.getTimeZone. ($(LREF parseTZConversions) can be
- used to convert time zone names if necessary). Microsoft changes
- their time zones too often for us to compile the conversions into
- Phobos and have them be properly up-to-date. TimeZone.getTimeZone
- will be removed in July 2017.)
-
- Returns a $(LREF TimeZone) with the give name per the TZ Database.
-
- This returns a $(LREF PosixTimeZone) on Posix systems and a
- $(LREF WindowsTimeZone) on Windows systems. For
- $(LREF PosixTimeZone) on Windows, call $(D PosixTimeZone.getTimeZone)
- directly and give it the location of the TZ Database time zone files on
- disk.
-
- On Windows, the given TZ Database name is converted to the corresponding
- time zone name on Windows prior to calling
- $(D WindowsTimeZone.getTimeZone). This function allows for
- the same time zone names on both Windows and Posix systems.
-
- See_Also:
- $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ
- Database)
- $(HTTP en.wikipedia.org/wiki/List_of_tz_database_time_zones, List of
- Time Zones)
- $(HTTP unicode.org/repos/cldr-tmp/trunk/diff/supplemental/zone_tzid.html,
- Windows <-> TZ Database Name Conversion Table)
-
- Params:
- name = The TZ Database name of the desired time zone
-
- Throws:
- $(REF DateTimeException,std,datetime,date) if the given time zone
- could not be found.
- +/
+ // Explicitly undocumented. It will be removed in June 2018. @@@DEPRECATED_2018-07@@@
deprecated("Use PosixTimeZone.getTimeZone or WindowsTimeZone.getTimeZone instead")
static immutable(TimeZone) getTimeZone(string name) @safe
{
@@ -524,35 +488,7 @@ public:
}
- // @@@DEPRECATED_2017-07@@@
- /++
- $(RED Deprecated. Use either PosixTimeZone.getInstalledTZNames or
- WindowsTimeZone.getInstalledTZNames. ($(LREF parseTZConversions)
- can be used to convert time zone names if necessary). Microsoft
- changes their time zones too often for us to compile the
- conversions into Phobos and have them be properly up-to-date.
- TimeZone.getInstalledTZNames will be removed in July 2017.)
-
- Returns a list of the names of the time zones installed on the system.
-
- Providing a sub-name narrows down the list of time zones (which
- can number in the thousands). For example,
- passing in "America" as the sub-name returns only the time zones which
- begin with "America".
-
- On Windows, this function will convert the Windows time zone names to
- the corresponding TZ Database names with
- $(D windowsTZNameToTZDatabaseName). To get the actual Windows time
- zone names, use $(D WindowsTimeZone.getInstalledTZNames) directly.
-
- Params:
- subName = The first part of the time zones desired.
-
- Throws:
- $(D FileException) on Posix systems if it fails to read from disk.
- $(REF DateTimeException,std,datetime,date) on Windows systems if
- it fails to read the registry.
- +/
+ // Explicitly undocumented. It will be removed in June 2018. @@@DEPRECATED_2018-07@@@
deprecated("Use PosixTimeZone.getInstalledTZNames or WindowsTimeZone.getInstalledTZNames instead")
static string[] getInstalledTZNames(string subName = "") @safe
{
@@ -2991,8 +2927,6 @@ else version(Windows)
static immutable(WindowsTimeZone) getTimeZone(string name) @trusted
{
- import std.utf : toUTF16;
-
scope baseKey = Registry.localMachine.getKey(`Software\Microsoft\Windows NT\CurrentVersion\Time Zones`);
foreach (tzKeyName; baseKey.keyNames)
@@ -3015,8 +2949,8 @@ else version(Windows)
TIME_ZONE_INFORMATION tzInfo;
- auto wstdName = toUTF16(stdName);
- auto wdstName = toUTF16(dstName);
+ auto wstdName = stdName.to!wstring;
+ auto wdstName = dstName.to!wstring;
auto wstdNameLen = wstdName.length > 32 ? 32 : wstdName.length;
auto wdstNameLen = wdstName.length > 32 ? 32 : wdstName.length;
@@ -3347,7 +3281,7 @@ else version(Posix)
does.
Params:
- windowsZonesXMLFileText The text from
+ windowsZonesXMLText = The text from
$(HTTP unicode.org/cldr/data/common/supplemental/windowsZones.xml, windowsZones.xml)
Throws:
@@ -3525,34 +3459,7 @@ For terms of use, see http://www.unicode.org/copyright.html
}
-// @@@DEPRECATED_2017-07@@@
-/++
- $(RED Deprecated. Use $(LREF parseTZConversions) instead. Microsoft changes
- their time zones too often for us to compile the conversions into
- Phobos and have them be properly up-to-date.
- tzDatabaseNameToWindowsTZName will be removed in July 2017.)
-
- Converts the given TZ Database name to the corresponding Windows time zone
- name.
-
- Note that in a few cases, a TZ Dabatase name corresponds to two different
- Windows time zone names. So, while in most cases converting from one to the
- other and back again will result in the same time zone name started
- with, in a few case, it'll get a different name.
-
- Also, there are far more TZ Database names than Windows time zones, so some
- of the more exotic TZ Database names don't have corresponding Windows time
- zone names.
-
- Returns null if the given time zone name cannot be converted.
-
- See_Also:
- $(HTTP unicode.org/repos/cldr-tmp/trunk/diff/supplemental/zone_tzid.html,
- Windows <-> TZ Database Name Conversion Table)
-
- Params:
- tzName = The TZ Database name to convert.
- +/
+// Explicitly undocumented. It will be removed in June 2018. @@@DEPRECATED_2018-07@@@
deprecated("Use parseTZConversions instead")
string tzDatabaseNameToWindowsTZName(string tzName) @safe pure nothrow @nogc
{
@@ -4021,25 +3928,7 @@ version(Windows) version(UpdateWindowsTZTranslations) deprecated @system unittes
}
-// @@@DEPRECATED_2017-07@@@
-/++
- $(RED Deprecated. Use $(LREF parseTZConversions) instead. Microsoft changes
- their time zones too often for us to compile the conversions into
- Phobos and have them be properly up-to-date.
- windowsTZNameToTZDatabaseName will be removed in July 2017.)
-
- Converts the given Windows time zone name to a corresponding TZ Database
- name.
-
- Returns null if the given time zone name cannot be converted.
-
- See_Also:
- $(HTTP unicode.org/repos/cldr-tmp/trunk/diff/supplemental/zone_tzid.html,
- Windows <-> TZ Database Name Conversion Table)
-
- Params:
- tzName = The TZ Database name to convert.
- +/
+// Explicitly undocumented. It will be removed in June 2018. @@@DEPRECATED_2018-07@@@
deprecated("Use parseTZConversions instead")
string windowsTZNameToTZDatabaseName(string tzName) @safe pure nothrow @nogc
{
diff --git a/std/digest/crc.d b/std/digest/crc.d
index f210e45e0c7..323c17489c0 100644
--- a/std/digest/crc.d
+++ b/std/digest/crc.d
@@ -18,16 +18,16 @@ $(TR $(TDNW Helpers) $(TD $(MYREF crcHexString) $(MYREF crc32Of) $(MYREF crc64EC
)
*
- * This module conforms to the APIs defined in $(D std.digest.digest). To understand the
- * differences between the template and the OOP API, see $(D std.digest.digest).
+ * This module conforms to the APIs defined in $(D std.digest). To understand the
+ * differences between the template and the OOP API, see $(MREF std, digest).
*
- * This module publicly imports $(D std.digest.digest) and can be used as a stand-alone
+ * This module publicly imports $(MREF std, digest) and can be used as a stand-alone
* module.
*
* Note:
* CRCs are usually printed with the MSB first. When using
- * $(REF toHexString, std,digest,digest) the result will be in an unexpected
- * order. Use $(REF toHexString, std,digest,digest)'s optional order parameter
+ * $(REF toHexString, std,digest) the result will be in an unexpected
+ * order. Use $(REF toHexString, std,digest)'s optional order parameter
* to specify decreasing order for the correct result. The $(LREF crcHexString)
* alias can also be used for this purpose.
*
@@ -58,7 +58,7 @@ $(TR $(TDNW Helpers) $(TD $(MYREF crcHexString) $(MYREF crc32Of) $(MYREF crc64EC
*/
module std.digest.crc;
-public import std.digest.digest;
+public import std.digest;
version(unittest)
import std.exception;
@@ -133,7 +133,7 @@ private T[256][8] genTables(T)(T polynomial)
/**
* Template API CRC32 implementation.
- * See $(D std.digest.digest) for differences between template and OOP API.
+ * See $(D std.digest) for differences between template and OOP API.
*/
alias CRC32 = CRC!(32, 0xEDB88320);
@@ -461,7 +461,7 @@ struct CRC(uint N, ulong P) if (N == 32 || N == 64)
}
/**
- * This is a convenience alias for $(REF digest, std,digest,digest) using the
+ * This is a convenience alias for $(REF digest, std,digest) using the
* CRC32 implementation.
*
* Params:
@@ -496,7 +496,7 @@ ubyte[4] crc32Of(T...)(T data)
}
/**
- * This is a convenience alias for $(REF digest, std,digest,digest) using the
+ * This is a convenience alias for $(REF digest, std,digest) using the
* CRC64-ECMA implementation.
*
* Params:
@@ -569,7 +569,6 @@ ubyte[8] crc64ISOOf(T...)(T data)
}
/**
- * This is a convenience alias for $(REF toHexString, std,digest,digest)
* producing the usual CRC32 string output.
*/
public alias crcHexString = toHexString!(Order.decreasing);
@@ -578,9 +577,9 @@ public alias crcHexString = toHexString!(Order.decreasing, 16);
/**
* OOP API CRC32 implementation.
- * See $(D std.digest.digest) for differences between template and OOP API.
+ * See $(D std.digest) for differences between template and OOP API.
*
- * This is an alias for $(D $(REF WrapperDigest, std,digest,digest)!CRC32), see
+ * This is an alias for $(D $(REF WrapperDigest, std,digest)!CRC32), see
* there for more information.
*/
alias CRC32Digest = WrapperDigest!CRC32;
diff --git a/std/digest/digest.d b/std/digest/digest.d
index 3e120922828..175b9a696bc 100644
--- a/std/digest/digest.d
+++ b/std/digest/digest.d
@@ -1,1171 +1,21 @@
-/**
- * This module describes the _digest APIs used in Phobos. All digests follow
- * these APIs. Additionally, this module contains useful helper methods which
- * can be used with every _digest type.
- *
-$(SCRIPT inhibitQuickIndex = 1;)
-
-$(DIVC quickindex,
-$(BOOKTABLE ,
-$(TR $(TH Category) $(TH Functions)
-)
-$(TR $(TDNW Template API) $(TD $(MYREF isDigest) $(MYREF DigestType) $(MYREF hasPeek)
- $(MYREF hasBlockSize)
- $(MYREF ExampleDigest) $(MYREF _digest) $(MYREF hexDigest) $(MYREF makeDigest)
-)
-)
-$(TR $(TDNW OOP API) $(TD $(MYREF Digest)
-)
-)
-$(TR $(TDNW Helper functions) $(TD $(MYREF toHexString))
-)
-$(TR $(TDNW Implementation helpers) $(TD $(MYREF digestLength) $(MYREF WrapperDigest))
-)
-)
-)
-
- * APIs:
- * There are two APIs for digests: The template API and the OOP API. The template API uses structs
- * and template helpers like $(LREF isDigest). The OOP API implements digests as classes inheriting
- * the $(LREF Digest) interface. All digests are named so that the template API struct is called "$(B x)"
- * and the OOP API class is called "$(B x)Digest". For example we have $(D MD5) <--> $(D MD5Digest),
- * $(D CRC32) <--> $(D CRC32Digest), etc.
- *
- * The template API is slightly more efficient. It does not have to allocate memory dynamically,
- * all memory is allocated on the stack. The OOP API has to allocate in the finish method if no
- * buffer was provided. If you provide a buffer to the OOP APIs finish function, it doesn't allocate,
- * but the $(LREF Digest) classes still have to be created using $(D new) which allocates them using the GC.
- *
- * The OOP API is useful to change the _digest function and/or _digest backend at 'runtime'. The benefit here
- * is that switching e.g. Phobos MD5Digest and an OpenSSLMD5Digest implementation is ABI compatible.
- *
- * If just one specific _digest type and backend is needed, the template API is usually a good fit.
- * In this simplest case, the template API can even be used without templates: Just use the "$(B x)" structs
- * directly.
- *
- * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- * Authors:
- * Johannes Pfau
- *
- * Source: $(PHOBOSSRC std/_digest/_digest.d)
- *
- * CTFE:
- * Digests do not work in CTFE
- *
- * TODO:
- * Digesting single bits (as opposed to bytes) is not implemented. This will be done as another
- * template constraint helper (hasBitDigesting!T) and an additional interface (BitDigest)
- */
-/* Copyright Johannes Pfau 2012.
- * Distributed under the Boost Software License, Version 1.0.
- * (See accompanying file LICENSE_1_0.txt or copy at
- * http://www.boost.org/LICENSE_1_0.txt)
- */
module std.digest.digest;
-public import std.ascii : LetterCase;
-import std.meta : allSatisfy;
-import std.range.primitives;
-import std.traits;
-
-
-///
-@system unittest
-{
- import std.digest.crc;
-
- //Simple example
- char[8] hexHash = hexDigest!CRC32("The quick brown fox jumps over the lazy dog");
- assert(hexHash == "39A34F41");
-
- //Simple example, using the API manually
- CRC32 context = makeDigest!CRC32();
- context.put(cast(ubyte[])"The quick brown fox jumps over the lazy dog");
- ubyte[4] hash = context.finish();
- assert(toHexString(hash) == "39A34F41");
-}
-
-///
-@system unittest
-{
- //Generating the hashes of a file, idiomatic D way
- import std.digest.crc, std.digest.md, std.digest.sha;
- import std.stdio;
-
- // Digests a file and prints the result.
- void digestFile(Hash)(string filename)
- if (isDigest!Hash)
- {
- auto file = File(filename);
- auto result = digest!Hash(file.byChunk(4096 * 1024));
- writefln("%s (%s) = %s", Hash.stringof, filename, toHexString(result));
- }
-
- void main(string[] args)
- {
- foreach (name; args[1 .. $])
- {
- digestFile!MD5(name);
- digestFile!SHA1(name);
- digestFile!CRC32(name);
- }
- }
-}
-///
-@system unittest
-{
- //Generating the hashes of a file using the template API
- import std.digest.crc, std.digest.md, std.digest.sha;
- import std.stdio;
- // Digests a file and prints the result.
- void digestFile(Hash)(ref Hash hash, string filename)
- if (isDigest!Hash)
- {
- File file = File(filename);
-
- //As digests imlement OutputRange, we could use std.algorithm.copy
- //Let's do it manually for now
- foreach (buffer; file.byChunk(4096 * 1024))
- hash.put(buffer);
-
- auto result = hash.finish();
- writefln("%s (%s) = %s", Hash.stringof, filename, toHexString(result));
- }
-
- void uMain(string[] args)
- {
- MD5 md5;
- SHA1 sha1;
- CRC32 crc32;
-
- md5.start();
- sha1.start();
- crc32.start();
-
- foreach (arg; args[1 .. $])
- {
- digestFile(md5, arg);
- digestFile(sha1, arg);
- digestFile(crc32, arg);
- }
- }
-}
-
-///
-@system unittest
-{
- import std.digest.crc, std.digest.md, std.digest.sha;
- import std.stdio;
-
- // Digests a file and prints the result.
- void digestFile(Digest hash, string filename)
- {
- File file = File(filename);
-
- //As digests implement OutputRange, we could use std.algorithm.copy
- //Let's do it manually for now
- foreach (buffer; file.byChunk(4096 * 1024))
- hash.put(buffer);
-
- ubyte[] result = hash.finish();
- writefln("%s (%s) = %s", typeid(hash).toString(), filename, toHexString(result));
- }
-
- void umain(string[] args)
- {
- auto md5 = new MD5Digest();
- auto sha1 = new SHA1Digest();
- auto crc32 = new CRC32Digest();
-
- foreach (arg; args[1 .. $])
- {
- digestFile(md5, arg);
- digestFile(sha1, arg);
- digestFile(crc32, arg);
- }
- }
-}
-
-version(StdDdoc)
- version = ExampleDigest;
-
-version(ExampleDigest)
-{
- /**
- * This documents the general structure of a Digest in the template API.
- * All digest implementations should implement the following members and therefore pass
- * the $(LREF isDigest) test.
- *
- * Note:
- * $(UL
- * $(LI A digest must be a struct (value type) to pass the $(LREF isDigest) test.)
- * $(LI A digest passing the $(LREF isDigest) test is always an $(D OutputRange))
- * )
- */
- struct ExampleDigest
- {
- public:
- /**
- * Use this to feed the digest with data.
- * Also implements the $(REF isOutputRange, std,range,primitives)
- * interface for $(D ubyte) and $(D const(ubyte)[]).
- * The following usages of $(D put) must work for any type which
- * passes $(LREF isDigest):
- * Example:
- * ----
- * ExampleDigest dig;
- * dig.put(cast(ubyte) 0); //single ubyte
- * dig.put(cast(ubyte) 0, cast(ubyte) 0); //variadic
- * ubyte[10] buf;
- * dig.put(buf); //buffer
- * ----
- */
- @trusted void put(scope const(ubyte)[] data...)
- {
-
- }
-
- /**
- * This function is used to (re)initialize the digest.
- * It must be called before using the digest and it also works as a 'reset' function
- * if the digest has already processed data.
- */
- @trusted void start()
- {
-
- }
-
- /**
- * The finish function returns the final hash sum and resets the Digest.
- *
- * Note:
- * The actual type returned by finish depends on the digest implementation.
- * $(D ubyte[16]) is just used as an example. It is guaranteed that the type is a
- * static array of ubytes.
- *
- * $(UL
- * $(LI Use $(LREF DigestType) to obtain the actual return type.)
- * $(LI Use $(LREF digestLength) to obtain the length of the ubyte array.)
- * )
- */
- @trusted ubyte[16] finish()
- {
- return (ubyte[16]).init;
- }
- }
-}
-
-///
-@system unittest
-{
- //Using the OutputRange feature
- import std.algorithm.mutation : copy;
- import std.digest.md;
- import std.range : repeat;
-
- auto oneMillionRange = repeat!ubyte(cast(ubyte)'a', 1000000);
- auto ctx = makeDigest!MD5();
- copy(oneMillionRange, &ctx); //Note: You must pass a pointer to copy!
- assert(ctx.finish().toHexString() == "7707D6AE4E027C70EEA2A935C2296F21");
-}
-
-/**
- * Use this to check if a type is a digest. See $(LREF ExampleDigest) to see what
- * a type must provide to pass this check.
- *
- * Note:
- * This is very useful as a template constraint (see examples)
- *
- * BUGS:
- * $(UL
- * $(LI Does not yet verify that put takes scope parameters.)
- * $(LI Should check that finish() returns a ubyte[num] array)
- * )
- */
-template isDigest(T)
-{
- import std.range : isOutputRange;
- enum bool isDigest = isOutputRange!(T, const(ubyte)[]) && isOutputRange!(T, ubyte) &&
- is(T == struct) &&
- is(typeof(
- {
- T dig = void; //Can define
- dig.put(cast(ubyte) 0, cast(ubyte) 0); //varags
- dig.start(); //has start
- auto value = dig.finish(); //has finish
- }));
-}
-
-///
-@system unittest
-{
- import std.digest.crc;
- static assert(isDigest!CRC32);
-}
-///
-@system unittest
-{
- import std.digest.crc;
- void myFunction(T)()
- if (isDigest!T)
- {
- T dig;
- dig.start();
- auto result = dig.finish();
- }
- myFunction!CRC32();
-}
-
-/**
- * Use this template to get the type which is returned by a digest's $(LREF finish) method.
- */
-template DigestType(T)
-{
- static if (isDigest!T)
- {
- alias DigestType =
- ReturnType!(typeof(
- {
- T dig = void;
- return dig.finish();
- }));
- }
- else
- static assert(false, T.stringof ~ " is not a digest! (fails isDigest!T)");
-}
-
-///
-@system unittest
-{
- import std.digest.crc;
- assert(is(DigestType!(CRC32) == ubyte[4]));
-}
-///
-@system unittest
-{
- import std.digest.crc;
- CRC32 dig;
- dig.start();
- DigestType!CRC32 result = dig.finish();
-}
-
-/**
- * Used to check if a digest supports the $(D peek) method.
- * Peek has exactly the same function signatures as finish, but it doesn't reset
- * the digest's internal state.
- *
- * Note:
- * $(UL
- * $(LI This is very useful as a template constraint (see examples))
- * $(LI This also checks if T passes $(LREF isDigest))
- * )
- */
-template hasPeek(T)
-{
- enum bool hasPeek = isDigest!T &&
- is(typeof(
- {
- T dig = void; //Can define
- DigestType!T val = dig.peek();
- }));
-}
-
-///
-@system unittest
-{
- import std.digest.crc, std.digest.md;
- assert(!hasPeek!(MD5));
- assert(hasPeek!CRC32);
-}
-///
-@system unittest
-{
- import std.digest.crc;
- void myFunction(T)()
- if (hasPeek!T)
- {
- T dig;
- dig.start();
- auto result = dig.peek();
- }
- myFunction!CRC32();
-}
-
-/**
- * Checks whether the digest has a $(D blockSize) member, which contains the
- * digest's internal block size in bits. It is primarily used by $(REF HMAC, std,digest.hmac).
- */
-
-template hasBlockSize(T)
-if (isDigest!T)
-{
- enum bool hasBlockSize = __traits(compiles, { size_t blockSize = T.blockSize; });
-}
-
-///
-@system unittest
-{
- import std.digest.hmac, std.digest.md;
- static assert(hasBlockSize!MD5 && MD5.blockSize == 512);
- static assert(hasBlockSize!(HMAC!MD5) && HMAC!MD5.blockSize == 512);
-}
-
-package template isDigestibleRange(Range)
-{
- import std.digest.md;
- import std.range : isInputRange, ElementType;
- enum bool isDigestibleRange = isInputRange!Range && is(typeof(
- {
- MD5 ha; //Could use any conformant hash
- ElementType!Range val;
- ha.put(val);
- }));
-}
-
-/**
- * This is a convenience function to calculate a hash using the template API.
- * Every digest passing the $(LREF isDigest) test can be used with this function.
- *
- * Params:
- * range= an $(D InputRange) with $(D ElementType) $(D ubyte), $(D ubyte[]) or $(D ubyte[num])
- */
-DigestType!Hash digest(Hash, Range)(auto ref Range range)
-if (!isArray!Range
- && isDigestibleRange!Range)
-{
- import std.algorithm.mutation : copy;
- Hash hash;
- hash.start();
- copy(range, &hash);
- return hash.finish();
-}
-
-///
-@system unittest
-{
- import std.digest.md;
- import std.range : repeat;
- auto testRange = repeat!ubyte(cast(ubyte)'a', 100);
- auto md5 = digest!MD5(testRange);
-}
-
-/**
- * This overload of the digest function handles arrays.
- *
- * Params:
- * data= one or more arrays of any type
- */
-DigestType!Hash digest(Hash, T...)(scope const T data)
-if (allSatisfy!(isArray, typeof(data)))
-{
- Hash hash;
- hash.start();
- foreach (datum; data)
- hash.put(cast(const(ubyte[]))datum);
- return hash.finish();
-}
-
-///
-@system unittest
-{
- import std.digest.crc, std.digest.md, std.digest.sha;
- auto md5 = digest!MD5( "The quick brown fox jumps over the lazy dog");
- auto sha1 = digest!SHA1( "The quick brown fox jumps over the lazy dog");
- auto crc32 = digest!CRC32("The quick brown fox jumps over the lazy dog");
- assert(toHexString(crc32) == "39A34F41");
-}
-
-///
-@system unittest
-{
- import std.digest.crc;
- auto crc32 = digest!CRC32("The quick ", "brown ", "fox jumps over the lazy dog");
- assert(toHexString(crc32) == "39A34F41");
-}
-
-/**
- * This is a convenience function similar to $(LREF digest), but it returns the string
- * representation of the hash. Every digest passing the $(LREF isDigest) test can be used with this
- * function.
- *
- * Params:
- * order= the order in which the bytes are processed (see $(LREF toHexString))
- * range= an $(D InputRange) with $(D ElementType) $(D ubyte), $(D ubyte[]) or $(D ubyte[num])
- */
-char[digestLength!(Hash)*2] hexDigest(Hash, Order order = Order.increasing, Range)(ref Range range)
-if (!isArray!Range && isDigestibleRange!Range)
-{
- return toHexString!order(digest!Hash(range));
-}
-
-///
-@system unittest
-{
- import std.digest.md;
- import std.range : repeat;
- auto testRange = repeat!ubyte(cast(ubyte)'a', 100);
- assert(hexDigest!MD5(testRange) == "36A92CC94A9E0FA21F625F8BFB007ADF");
-}
-
-/**
- * This overload of the hexDigest function handles arrays.
- *
- * Params:
- * order= the order in which the bytes are processed (see $(LREF toHexString))
- * data= one or more arrays of any type
- */
-char[digestLength!(Hash)*2] hexDigest(Hash, Order order = Order.increasing, T...)(scope const T data)
-if (allSatisfy!(isArray, typeof(data)))
-{
- return toHexString!order(digest!Hash(data));
-}
-
-///
-@system unittest
-{
- import std.digest.crc;
- assert(hexDigest!(CRC32, Order.decreasing)("The quick brown fox jumps over the lazy dog") == "414FA339");
-}
-///
-@system unittest
-{
- import std.digest.crc;
- assert(hexDigest!(CRC32, Order.decreasing)("The quick ", "brown ", "fox jumps over the lazy dog") == "414FA339");
-}
-
-/**
- * This is a convenience function which returns an initialized digest, so it's not necessary to call
- * start manually.
- */
-Hash makeDigest(Hash)()
-{
- Hash hash;
- hash.start();
- return hash;
-}
-
-///
-@system unittest
-{
- import std.digest.md;
- auto md5 = makeDigest!MD5();
- md5.put(0);
- assert(toHexString(md5.finish()) == "93B885ADFE0DA089CDF634904FD59F71");
-}
-
-/*+*************************** End of template part, welcome to OOP land **************************/
-
-/**
- * This describes the OOP API. To understand when to use the template API and when to use the OOP API,
- * see the module documentation at the top of this page.
- *
- * The Digest interface is the base interface which is implemented by all digests.
- *
- * Note:
- * A Digest implementation is always an $(D OutputRange)
- */
-interface Digest
-{
- public:
- /**
- * Use this to feed the digest with data.
- * Also implements the $(REF isOutputRange, std,range,primitives)
- * interface for $(D ubyte) and $(D const(ubyte)[]).
- *
- * Example:
- * ----
- * void test(Digest dig)
- * {
- * dig.put(cast(ubyte) 0); //single ubyte
- * dig.put(cast(ubyte) 0, cast(ubyte) 0); //variadic
- * ubyte[10] buf;
- * dig.put(buf); //buffer
- * }
- * ----
- */
- @trusted nothrow void put(scope const(ubyte)[] data...);
-
- /**
- * Resets the internal state of the digest.
- * Note:
- * $(LREF finish) calls this internally, so it's not necessary to call
- * $(D reset) manually after a call to $(LREF finish).
- */
- @trusted nothrow void reset();
-
- /**
- * This is the length in bytes of the hash value which is returned by $(LREF finish).
- * It's also the required size of a buffer passed to $(LREF finish).
- */
- @trusted nothrow @property size_t length() const;
-
- /**
- * The finish function returns the hash value. It takes an optional buffer to copy the data
- * into. If a buffer is passed, it must be at least $(LREF length) bytes big.
- */
- @trusted nothrow ubyte[] finish();
- ///ditto
- nothrow ubyte[] finish(ubyte[] buf);
- //@@@BUG@@@ http://d.puremagic.com/issues/show_bug.cgi?id=6549
- /*in
- {
- assert(buf.length >= this.length);
- }*/
-
- /**
- * This is a convenience function to calculate the hash of a value using the OOP API.
- */
- final @trusted nothrow ubyte[] digest(scope const(void[])[] data...)
- {
- this.reset();
- foreach (datum; data)
- this.put(cast(ubyte[]) datum);
- return this.finish();
- }
-}
-
-///
-@system unittest
-{
- //Using the OutputRange feature
- import std.algorithm.mutation : copy;
- import std.digest.md;
- import std.range : repeat;
-
- auto oneMillionRange = repeat!ubyte(cast(ubyte)'a', 1000000);
- auto ctx = new MD5Digest();
- copy(oneMillionRange, ctx);
- assert(ctx.finish().toHexString() == "7707D6AE4E027C70EEA2A935C2296F21");
-}
-
-///
-@system unittest
-{
- import std.digest.crc, std.digest.md, std.digest.sha;
- ubyte[] md5 = (new MD5Digest()).digest("The quick brown fox jumps over the lazy dog");
- ubyte[] sha1 = (new SHA1Digest()).digest("The quick brown fox jumps over the lazy dog");
- ubyte[] crc32 = (new CRC32Digest()).digest("The quick brown fox jumps over the lazy dog");
- assert(crcHexString(crc32) == "414FA339");
-}
-
-///
-@system unittest
-{
- import std.digest.crc;
- ubyte[] crc32 = (new CRC32Digest()).digest("The quick ", "brown ", "fox jumps over the lazy dog");
- assert(crcHexString(crc32) == "414FA339");
-}
-
-@system unittest
-{
- import std.range : isOutputRange;
- assert(!isDigest!(Digest));
- assert(isOutputRange!(Digest, ubyte));
-}
-
-///
-@system unittest
-{
- void test(Digest dig)
- {
- dig.put(cast(ubyte) 0); //single ubyte
- dig.put(cast(ubyte) 0, cast(ubyte) 0); //variadic
- ubyte[10] buf;
- dig.put(buf); //buffer
- }
-}
-
-/*+*************************** End of OOP part, helper functions follow ***************************/
-
-/**
- * See $(LREF toHexString)
- */
-enum Order : bool
-{
- increasing, ///
- decreasing ///
-}
-
-
-/**
- * Used to convert a hash value (a static or dynamic array of ubytes) to a string.
- * Can be used with the OOP and with the template API.
- *
- * The additional order parameter can be used to specify the order of the input data.
- * By default the data is processed in increasing order, starting at index 0. To process it in the
- * opposite order, pass Order.decreasing as a parameter.
- *
- * The additional letterCase parameter can be used to specify the case of the output data.
- * By default the output is in upper case. To change it to the lower case
- * pass LetterCase.lower as a parameter.
- *
- * Note:
- * The function overloads returning a string allocate their return values
- * using the GC. The versions returning static arrays use pass-by-value for
- * the return value, effectively avoiding dynamic allocation.
- */
-char[num*2] toHexString(Order order = Order.increasing, size_t num, LetterCase letterCase = LetterCase.upper)
-(in ubyte[num] digest)
-{
- static if (letterCase == LetterCase.upper)
- {
- import std.ascii : hexDigits = hexDigits;
- }
- else
- {
- import std.ascii : hexDigits = lowerHexDigits;
- }
-
-
- char[num*2] result;
- size_t i;
-
- static if (order == Order.increasing)
- {
- foreach (u; digest)
- {
- result[i++] = hexDigits[u >> 4];
- result[i++] = hexDigits[u & 15];
- }
- }
- else
- {
- size_t j = num - 1;
- while (i < num*2)
- {
- result[i++] = hexDigits[digest[j] >> 4];
- result[i++] = hexDigits[digest[j] & 15];
- j--;
- }
- }
- return result;
-}
-
-///ditto
-char[num*2] toHexString(LetterCase letterCase, Order order = Order.increasing, size_t num)(in ubyte[num] digest)
-{
- return toHexString!(order, num, letterCase)(digest);
-}
-
-///ditto
-string toHexString(Order order = Order.increasing, LetterCase letterCase = LetterCase.upper)
-(in ubyte[] digest)
-{
- static if (letterCase == LetterCase.upper)
- {
- import std.ascii : hexDigits = hexDigits;
- }
- else
- {
- import std.ascii : hexDigits = lowerHexDigits;
- }
-
- auto result = new char[digest.length*2];
- size_t i;
-
- static if (order == Order.increasing)
- {
- foreach (u; digest)
- {
- result[i++] = hexDigits[u >> 4];
- result[i++] = hexDigits[u & 15];
- }
- }
- else
- {
- import std.range : retro;
- foreach (u; retro(digest))
- {
- result[i++] = hexDigits[u >> 4];
- result[i++] = hexDigits[u & 15];
- }
- }
- import std.exception : assumeUnique;
- // memory was just created, so casting to immutable is safe
- return () @trusted { return assumeUnique(result); }();
-}
-
-///ditto
-string toHexString(LetterCase letterCase, Order order = Order.increasing)(in ubyte[] digest)
-{
- return toHexString!(order, letterCase)(digest);
-}
-
-//For more example unittests, see Digest.digest, digest
-
-///
-@safe unittest
-{
- import std.digest.crc;
- //Test with template API:
- auto crc32 = digest!CRC32("The quick ", "brown ", "fox jumps over the lazy dog");
- //Lower case variant:
- assert(toHexString!(LetterCase.lower)(crc32) == "39a34f41");
- //Usually CRCs are printed in this order, though:
- assert(toHexString!(Order.decreasing)(crc32) == "414FA339");
- assert(toHexString!(LetterCase.lower, Order.decreasing)(crc32) == "414fa339");
-}
-
-///
-@safe unittest
-{
- import std.digest.crc;
- // With OOP API
- auto crc32 = (new CRC32Digest()).digest("The quick ", "brown ", "fox jumps over the lazy dog");
- //Usually CRCs are printed in this order, though:
- assert(toHexString!(Order.decreasing)(crc32) == "414FA339");
-}
-
-@safe unittest
-{
- ubyte[16] data;
- assert(toHexString(data) == "00000000000000000000000000000000");
-
- assert(toHexString(cast(ubyte[4])[42, 43, 44, 45]) == "2A2B2C2D");
- assert(toHexString(cast(ubyte[])[42, 43, 44, 45]) == "2A2B2C2D");
- assert(toHexString!(Order.decreasing)(cast(ubyte[4])[42, 43, 44, 45]) == "2D2C2B2A");
- assert(toHexString!(Order.decreasing, LetterCase.lower)(cast(ubyte[4])[42, 43, 44, 45]) == "2d2c2b2a");
- assert(toHexString!(Order.decreasing)(cast(ubyte[])[42, 43, 44, 45]) == "2D2C2B2A");
-}
-
-/*+*********************** End of public helper part, private helpers follow ***********************/
-
-/*
- * Used to convert from a ubyte[] slice to a ref ubyte[N].
- * This helper is used internally in the WrapperDigest template to wrap the template API's
- * finish function.
- */
-ref T[N] asArray(size_t N, T)(ref T[] source, string errorMsg = "")
-{
- assert(source.length >= N, errorMsg);
- return *cast(T[N]*) source.ptr;
-}
-
-/*
- * Returns the length (in bytes) of the hash value produced by T.
- */
-template digestLength(T)
-if (isDigest!T)
-{
- enum size_t digestLength = (ReturnType!(T.finish)).length;
-}
-
-@safe pure nothrow @nogc
-unittest
-{
- import std.digest.md : MD5;
- import std.digest.sha : SHA1, SHA256, SHA512;
- assert(digestLength!MD5 == 16);
- assert(digestLength!SHA1 == 20);
- assert(digestLength!SHA256 == 32);
- assert(digestLength!SHA512 == 64);
-}
-
-/**
- * Wraps a template API hash struct into a Digest interface.
- * Modules providing digest implementations will usually provide
- * an alias for this template (e.g. MD5Digest, SHA1Digest, ...).
- */
-class WrapperDigest(T)
-if (isDigest!T) : Digest
-{
- protected:
- T _digest;
-
- public final:
- /**
- * Initializes the digest.
- */
- this()
- {
- _digest.start();
- }
-
- /**
- * Use this to feed the digest with data.
- * Also implements the $(REF isOutputRange, std,range,primitives)
- * interface for $(D ubyte) and $(D const(ubyte)[]).
- */
- @trusted nothrow void put(scope const(ubyte)[] data...)
- {
- _digest.put(data);
- }
-
- /**
- * Resets the internal state of the digest.
- * Note:
- * $(LREF finish) calls this internally, so it's not necessary to call
- * $(D reset) manually after a call to $(LREF finish).
- */
- @trusted nothrow void reset()
- {
- _digest.start();
- }
-
- /**
- * This is the length in bytes of the hash value which is returned by $(LREF finish).
- * It's also the required size of a buffer passed to $(LREF finish).
- */
- @trusted nothrow @property size_t length() const pure
- {
- return digestLength!T;
- }
-
- /**
- * The finish function returns the hash value. It takes an optional buffer to copy the data
- * into. If a buffer is passed, it must have a length at least $(LREF length) bytes.
- *
- * Example:
- * --------
- *
- * import std.digest.md;
- * ubyte[16] buf;
- * auto hash = new WrapperDigest!MD5();
- * hash.put(cast(ubyte) 0);
- * auto result = hash.finish(buf[]);
- * //The result is now in result (and in buf). If you pass a buffer which is bigger than
- * //necessary, result will have the correct length, but buf will still have it's original
- * //length
- * --------
- */
- nothrow ubyte[] finish(ubyte[] buf)
- in
- {
- assert(buf.length >= this.length);
- }
- body
- {
- enum string msg = "Buffer needs to be at least " ~ digestLength!(T).stringof ~ " bytes " ~
- "big, check " ~ typeof(this).stringof ~ ".length!";
- asArray!(digestLength!T)(buf, msg) = _digest.finish();
- return buf[0 .. digestLength!T];
- }
-
- ///ditto
- @trusted nothrow ubyte[] finish()
- {
- enum len = digestLength!T;
- auto buf = new ubyte[len];
- asArray!(digestLength!T)(buf) = _digest.finish();
- return buf;
- }
-
- version(StdDdoc)
- {
- /**
- * Works like $(D finish) but does not reset the internal state, so it's possible
- * to continue putting data into this WrapperDigest after a call to peek.
- *
- * These functions are only available if $(D hasPeek!T) is true.
- */
- @trusted ubyte[] peek(ubyte[] buf) const;
- ///ditto
- @trusted ubyte[] peek() const;
- }
- else static if (hasPeek!T)
- {
- @trusted ubyte[] peek(ubyte[] buf) const
- in
- {
- assert(buf.length >= this.length);
- }
- body
- {
- enum string msg = "Buffer needs to be at least " ~ digestLength!(T).stringof ~ " bytes " ~
- "big, check " ~ typeof(this).stringof ~ ".length!";
- asArray!(digestLength!T)(buf, msg) = _digest.peek();
- return buf[0 .. digestLength!T];
- }
-
- @trusted ubyte[] peek() const
- {
- enum len = digestLength!T;
- auto buf = new ubyte[len];
- asArray!(digestLength!T)(buf) = _digest.peek();
- return buf;
- }
- }
-}
-
-///
-@system unittest
-{
- import std.digest.md;
- //Simple example
- auto hash = new WrapperDigest!MD5();
- hash.put(cast(ubyte) 0);
- auto result = hash.finish();
-}
-
-///
-@system unittest
-{
- //using a supplied buffer
- import std.digest.md;
- ubyte[16] buf;
- auto hash = new WrapperDigest!MD5();
- hash.put(cast(ubyte) 0);
- auto result = hash.finish(buf[]);
- //The result is now in result (and in buf). If you pass a buffer which is bigger than
- //necessary, result will have the correct length, but buf will still have it's original
- //length
-}
-
-@safe unittest
-{
- // Test peek & length
- import std.digest.crc;
- auto hash = new WrapperDigest!CRC32();
- assert(hash.length == 4);
- hash.put(cast(const(ubyte[]))"The quick brown fox jumps over the lazy dog");
- assert(hash.peek().toHexString() == "39A34F41");
- ubyte[5] buf;
- assert(hash.peek(buf).toHexString() == "39A34F41");
-}
-
-/**
- * Securely compares two digest representations while protecting against timing
- * attacks. Do not use `==` to compare digest representations.
- *
- * The attack happens as follows:
- *
- * $(OL
- * $(LI An attacker wants to send harmful data to your server, which
- * requires a integrity HMAC SHA1 token signed with a secret.)
- * $(LI The length of the token is known to be 40 characters long due to its format,
- * so the attacker first sends `"0000000000000000000000000000000000000000"`,
- * then `"1000000000000000000000000000000000000000"`, and so on.)
- * $(LI The given HMAC token is compared with the expected token using the
- * `==` string comparison, which returns `false` as soon as the first wrong
- * element is found. If a wrong element is found, then a rejection is sent
- * back to the sender.)
- * $(LI Eventually, the attacker is able to determine the first character in
- * the correct token because the sever takes slightly longer to return a
- * rejection. This is due to the comparison moving on to second item in
- * the two arrays, seeing they are different, and then sending the rejection.)
- * $(LI It may seem like too small of a difference in time for the attacker
- * to notice, but security researchers have shown that differences as
- * small as $(LINK2 http://www.cs.rice.edu/~dwallach/pub/crosby-timing2009.pdf,
- * 20µs can be reliably distinguished) even with network inconsistencies.)
- * $(LI Repeat the process for each character until the attacker has the whole
- * correct token and the server accepts the harmful data. This can be done
- * in a week with the attacker pacing the attack to 10 requests per second
- * with only one client.)
- * )
- *
- * This function defends against this attack by always comparing every single
- * item in the array if the two arrays are the same length. Therefore, this
- * function is always $(BIGOH n) for ranges of the same length.
- *
- * This attack can also be mitigated via rate limiting and banning IPs which have too
- * many rejected requests. However, this does not completely solve the problem,
- * as the attacker could be in control of a bot net. To fully defend against
- * the timing attack, rate limiting, banning IPs, and using this function
- * should be used together.
- *
- * Params:
- * r1 = A digest representation
- * r2 = A digest representation
- * Returns:
- * `true` if both representations are equal, `false` otherwise
- * See_Also:
- * $(LINK2 https://en.wikipedia.org/wiki/Timing_attack, The Wikipedia article
- * on timing attacks).
- */
-bool secureEqual(R1, R2)(R1 r1, R2 r2)
-if (isInputRange!R1 && isInputRange!R2 && !isInfinite!R1 && !isInfinite!R2 &&
- (isIntegral!(ElementEncodingType!R1) || isSomeChar!(ElementEncodingType!R1)) &&
- !is(CommonType!(ElementEncodingType!R1, ElementEncodingType!R2) == void))
-{
- static if (hasLength!R1 && hasLength!R2)
- if (r1.length != r2.length)
- return false;
-
- int result;
-
- static if (isRandomAccessRange!R1 && isRandomAccessRange!R2 &&
- hasLength!R1 && hasLength!R2)
- {
- foreach (i; 0 .. r1.length)
- result |= r1[i] ^ r2[i];
- }
- else static if (hasLength!R1 && hasLength!R2)
- {
- // Lengths are the same so we can squeeze out a bit of performance
- // by not checking if r2 is empty
- for (; !r1.empty; r1.popFront(), r2.popFront())
- {
- result |= r1.front ^ r2.front;
- }
- }
- else
- {
- // Generic case, walk both ranges
- for (; !r1.empty; r1.popFront(), r2.popFront())
- {
- if (r2.empty) return false;
- result |= r1.front ^ r2.front;
- }
- if (!r2.empty) return false;
- }
-
- return result == 0;
-}
-
-///
-@system pure unittest
-{
- import std.digest.hmac : hmac;
- import std.digest.sha : SHA1;
- import std.string : representation;
-
- // a typical HMAC data integrity verification
- auto secret = "A7GZIP6TAQA6OHM7KZ42KB9303CEY0MOV5DD6NTV".representation;
- auto data = "data".representation;
-
- string hex1 = data.hmac!SHA1(secret).toHexString;
- string hex2 = data.hmac!SHA1(secret).toHexString;
- string hex3 = "data1".representation.hmac!SHA1(secret).toHexString;
-
- assert( secureEqual(hex1, hex2));
- assert(!secureEqual(hex1, hex3));
-}
-
-@system pure unittest
-{
- import std.internal.test.dummyrange : ReferenceInputRange;
- import std.range : takeExactly;
- import std.string : representation;
- import std.utf : byWchar, byDchar;
-
- {
- auto hex1 = "02CA3484C375EDD3C0F08D3F50D119E61077".representation;
- auto hex2 = "02CA3484C375EDD3C0F08D3F50D119E610779018".representation;
- assert(!secureEqual(hex1, hex2));
- }
- {
- auto hex1 = "02CA3484C375EDD3C0F08D3F50D119E610779018"w.representation;
- auto hex2 = "02CA3484C375EDD3C0F08D3F50D119E610779018"d.representation;
- assert(secureEqual(hex1, hex2));
- }
- {
- auto hex1 = "02CA3484C375EDD3C0F08D3F50D119E610779018".byWchar;
- auto hex2 = "02CA3484C375EDD3C0F08D3F50D119E610779018".byDchar;
- assert(secureEqual(hex1, hex2));
- }
- {
- auto hex1 = "02CA3484C375EDD3C0F08D3F50D119E61077".byWchar;
- auto hex2 = "02CA3484C375EDD3C0F08D3F50D119E610779018".byDchar;
- assert(!secureEqual(hex1, hex2));
- }
- {
- auto hex1 = new ReferenceInputRange!int([0, 1, 2, 3, 4, 5, 6, 7, 8]).takeExactly(9);
- auto hex2 = new ReferenceInputRange!int([0, 1, 2, 3, 4, 5, 6, 7, 8]).takeExactly(9);
- assert(secureEqual(hex1, hex2));
- }
- {
- auto hex1 = new ReferenceInputRange!int([0, 1, 2, 3, 4, 5, 6, 7, 8]).takeExactly(9);
- auto hex2 = new ReferenceInputRange!int([0, 1, 2, 3, 4, 5, 6, 7, 9]).takeExactly(9);
- assert(!secureEqual(hex1, hex2));
- }
-}
+static import std.digest;
+
+// scheduled for deprecation in 2.077
+// See also: https://github.com/dlang/phobos/pull/5013#issuecomment-313987845
+alias isDigest = std.digest.isDigest;
+alias DigestType = std.digest.DigestType;
+alias hasPeek = std.digest.hasPeek;
+alias hasBlockSize = std.digest.hasBlockSize;
+alias digest = std.digest.digest;
+alias hexDigest = std.digest.hexDigest;
+alias makeDigest = std.digest.makeDigest;
+alias Digest = std.digest.Digest;
+alias Order = std.digest.Order;
+alias toHexString = std.digest.toHexString;
+alias asArray = std.digest.asArray;
+alias digestLength = std.digest.digestLength;
+alias WrapperDigest = std.digest.WrapperDigest;
+alias secureEqual = std.digest.secureEqual;
+alias LetterCase = std.digest.LetterCase;
diff --git a/std/digest/hmac.d b/std/digest/hmac.d
index 20d9821488c..a3ec2c81a8c 100644
--- a/std/digest/hmac.d
+++ b/std/digest/hmac.d
@@ -16,9 +16,11 @@ Source: $(PHOBOSSRC std/digest/_hmac.d)
module std.digest.hmac;
-import std.digest.digest : isDigest, hasBlockSize, isDigestibleRange, DigestType;
+import std.digest : isDigest, hasBlockSize, isDigestibleRange, DigestType;
import std.meta : allSatisfy;
+@safe:
+
/**
* Template API HMAC implementation.
*
@@ -26,21 +28,22 @@ import std.meta : allSatisfy;
* information about the block size, it can be supplied explicitly using
* the second overload.
*
- * This type conforms to $(REF isDigest, std,digest.digest).
+ * This type conforms to $(REF isDigest, std,digest).
*/
-version(StdDdoc)
-/// Computes an HMAC over data read from stdin.
+/// Compute HMAC over an input string
@safe unittest
{
- import std.digest.hmac, std.digest.sha, std.stdio;
+ import std.ascii : LetterCase;
+ import std.digest : toHexString;
+ import std.digest.sha : SHA1;
import std.string : representation;
auto secret = "secret".representation;
- stdin.byChunk(4096)
- .hmac!SHA1(secret)
- .toHexString!(LetterCase.lower)
- .writeln;
+ assert("The quick brown fox jumps over the lazy dog"
+ .representation
+ .hmac!SHA1(secret)
+ .toHexString!(LetterCase.lower) == "198ea1ea04c435c1246b586a06d5cf11c3ffcda6");
}
template HMAC(H)
@@ -258,15 +261,15 @@ if (isDigest!H)
DigestType!H hmac(T...)(scope T data, scope const(ubyte)[] secret)
if (allSatisfy!(isDigestibleRange, typeof(data)))
{
- import std.algorithm.mutation : copy;
+ import std.range.primitives : put;
auto hash = HMAC!(H, blockSize)(secret);
foreach (datum; data)
- copy(datum, &hash);
+ put(hash, datum);
return hash.finish();
}
///
- @system pure nothrow @nogc unittest
+ @safe pure nothrow @nogc unittest
{
import std.algorithm.iteration : map;
import std.digest.hmac, std.digest.sha;
@@ -286,7 +289,7 @@ if (isDigest!H)
version(unittest)
{
- import std.digest.digest : toHexString, LetterCase;
+ import std.digest : toHexString, LetterCase;
alias hex = toHexString!(LetterCase.lower);
}
@@ -300,7 +303,7 @@ unittest
static assert(hasBlockSize!(HMAC!MD5) && HMAC!MD5.blockSize == MD5.blockSize);
}
-@system pure nothrow
+@safe pure nothrow
unittest
{
import std.digest.md : MD5;
diff --git a/std/digest/md.d b/std/digest/md.d
index 8ef7bbcb583..ef7c04d8668 100644
--- a/std/digest/md.d
+++ b/std/digest/md.d
@@ -18,10 +18,10 @@ $(TR $(TDNW Helpers) $(TD $(MYREF md5Of))
)
)
- * This module conforms to the APIs defined in $(D std.digest.digest). To understand the
- * differences between the template and the OOP API, see $(D std.digest.digest).
+ * This module conforms to the APIs defined in $(D std.digest). To understand the
+ * differences between the template and the OOP API, see $(MREF std, digest).
*
- * This module publicly imports $(D std.digest.digest) and can be used as a stand-alone
+ * This module publicly imports $(MREF std, digest) and can be used as a stand-alone
* module.
*
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
@@ -45,7 +45,7 @@ $(TR $(TDNW Helpers) $(TD $(MYREF md5Of))
*/
module std.digest.md;
-public import std.digest.digest;
+public import std.digest;
///
@safe unittest
@@ -91,7 +91,7 @@ private uint rotateLeft(uint x, uint n) @safe pure nothrow @nogc
/**
* Template API MD5 implementation.
- * See $(D std.digest.digest) for differences between template and OOP API.
+ * See $(D std.digest) for differences between template and OOP API.
*/
struct MD5
{
@@ -493,7 +493,7 @@ struct MD5
}
/**
- * This is a convenience alias for $(REF digest, std,digest,digest) using the
+ * This is a convenience alias for $(REF digest, std,digest) using the
* MD5 implementation.
*/
//simple alias doesn't work here, hope this gets inlined...
@@ -511,9 +511,9 @@ auto md5Of(T...)(T data)
/**
* OOP API MD5 implementation.
- * See $(D std.digest.digest) for differences between template and OOP API.
+ * See $(D std.digest) for differences between template and OOP API.
*
- * This is an alias for $(D $(REF WrapperDigest, std,digest,digest)!MD5), see
+ * This is an alias for $(D $(REF WrapperDigest, std,digest)!MD5), see
* there for more information.
*/
alias MD5Digest = WrapperDigest!MD5;
diff --git a/std/digest/murmurhash.d b/std/digest/murmurhash.d
index 89b4b1cd476..74efed56898 100644
--- a/std/digest/murmurhash.d
+++ b/std/digest/murmurhash.d
@@ -22,10 +22,11 @@ $(LI The current implementation is optimized for little endian architectures.
less uniform distribution.)
)
-This module conforms to the APIs defined in $(D std.digest.digest).
+This module conforms to the APIs defined in $(MREF std, digest).
-This module publicly imports $(D std.digest.digest) and can be used as a stand-alone module.
+This module publicly imports $(MREF std, digest) and can be used as a stand-alone module.
+Source: $(PHOBOSSRC std/digest/_murmurhash.d)
License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: Guillaume Chatelet
References: $(LINK2 https://github.com/aappleby/smhasher, Reference implementation)
@@ -41,10 +42,11 @@ module std.digest.murmurhash;
@safe unittest
{
// MurmurHash3!32, MurmurHash3!(128, 32) and MurmurHash3!(128, 64) implement
- // the std.digest.digest Template API.
+ // the std.digest Template API.
static assert(isDigest!(MurmurHash3!32));
// The convenient digest template allows for quick hashing of any data.
ubyte[4] hashed = digest!(MurmurHash3!32)([1, 2, 3, 4]);
+ assert(hashed == [0, 173, 69, 68]);
}
///
@@ -62,6 +64,7 @@ module std.digest.murmurhash;
// - the remaining bits are processed
// - the hash gets finalized
auto hashed = hasher.finish();
+ assert(hashed == [181, 151, 88, 252]);
}
///
@@ -86,9 +89,10 @@ module std.digest.murmurhash;
hasher.finalize();
// Finally get the hashed value.
auto hashed = hasher.getBytes();
+ assert(hashed == [188, 165, 108, 2]);
}
-public import std.digest.digest;
+public import std.digest;
@safe:
diff --git a/std/digest/package.d b/std/digest/package.d
new file mode 100644
index 00000000000..e3b2dfe792c
--- /dev/null
+++ b/std/digest/package.d
@@ -0,0 +1,1171 @@
+/**
+ * This module describes the _digest APIs used in Phobos. All digests follow
+ * these APIs. Additionally, this module contains useful helper methods which
+ * can be used with every _digest type.
+ *
+$(SCRIPT inhibitQuickIndex = 1;)
+
+$(DIVC quickindex,
+$(BOOKTABLE ,
+$(TR $(TH Category) $(TH Functions)
+)
+$(TR $(TDNW Template API) $(TD $(MYREF isDigest) $(MYREF DigestType) $(MYREF hasPeek)
+ $(MYREF hasBlockSize)
+ $(MYREF ExampleDigest) $(MYREF _digest) $(MYREF hexDigest) $(MYREF makeDigest)
+)
+)
+$(TR $(TDNW OOP API) $(TD $(MYREF Digest)
+)
+)
+$(TR $(TDNW Helper functions) $(TD $(MYREF toHexString))
+)
+$(TR $(TDNW Implementation helpers) $(TD $(MYREF digestLength) $(MYREF WrapperDigest))
+)
+)
+)
+
+ * APIs:
+ * There are two APIs for digests: The template API and the OOP API. The template API uses structs
+ * and template helpers like $(LREF isDigest). The OOP API implements digests as classes inheriting
+ * the $(LREF Digest) interface. All digests are named so that the template API struct is called "$(B x)"
+ * and the OOP API class is called "$(B x)Digest". For example we have $(D MD5) <--> $(D MD5Digest),
+ * $(D CRC32) <--> $(D CRC32Digest), etc.
+ *
+ * The template API is slightly more efficient. It does not have to allocate memory dynamically,
+ * all memory is allocated on the stack. The OOP API has to allocate in the finish method if no
+ * buffer was provided. If you provide a buffer to the OOP APIs finish function, it doesn't allocate,
+ * but the $(LREF Digest) classes still have to be created using $(D new) which allocates them using the GC.
+ *
+ * The OOP API is useful to change the _digest function and/or _digest backend at 'runtime'. The benefit here
+ * is that switching e.g. Phobos MD5Digest and an OpenSSLMD5Digest implementation is ABI compatible.
+ *
+ * If just one specific _digest type and backend is needed, the template API is usually a good fit.
+ * In this simplest case, the template API can even be used without templates: Just use the "$(B x)" structs
+ * directly.
+ *
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors:
+ * Johannes Pfau
+ *
+ * Source: $(PHOBOSSRC std/_digest/_package.d)
+ *
+ * CTFE:
+ * Digests do not work in CTFE
+ *
+ * TODO:
+ * Digesting single bits (as opposed to bytes) is not implemented. This will be done as another
+ * template constraint helper (hasBitDigesting!T) and an additional interface (BitDigest)
+ */
+/* Copyright Johannes Pfau 2012.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * http://www.boost.org/LICENSE_1_0.txt)
+ */
+module std.digest;
+
+public import std.ascii : LetterCase;
+import std.meta : allSatisfy;
+import std.range.primitives;
+import std.traits;
+
+
+///
+@system unittest
+{
+ import std.digest.crc;
+
+ //Simple example
+ char[8] hexHash = hexDigest!CRC32("The quick brown fox jumps over the lazy dog");
+ assert(hexHash == "39A34F41");
+
+ //Simple example, using the API manually
+ CRC32 context = makeDigest!CRC32();
+ context.put(cast(ubyte[])"The quick brown fox jumps over the lazy dog");
+ ubyte[4] hash = context.finish();
+ assert(toHexString(hash) == "39A34F41");
+}
+
+///
+@system unittest
+{
+ //Generating the hashes of a file, idiomatic D way
+ import std.digest.crc, std.digest.md, std.digest.sha;
+ import std.stdio;
+
+ // Digests a file and prints the result.
+ void digestFile(Hash)(string filename)
+ if (isDigest!Hash)
+ {
+ auto file = File(filename);
+ auto result = digest!Hash(file.byChunk(4096 * 1024));
+ writefln("%s (%s) = %s", Hash.stringof, filename, toHexString(result));
+ }
+
+ void main(string[] args)
+ {
+ foreach (name; args[1 .. $])
+ {
+ digestFile!MD5(name);
+ digestFile!SHA1(name);
+ digestFile!CRC32(name);
+ }
+ }
+}
+///
+@system unittest
+{
+ //Generating the hashes of a file using the template API
+ import std.digest.crc, std.digest.md, std.digest.sha;
+ import std.stdio;
+ // Digests a file and prints the result.
+ void digestFile(Hash)(ref Hash hash, string filename)
+ if (isDigest!Hash)
+ {
+ File file = File(filename);
+
+ //As digests imlement OutputRange, we could use std.algorithm.copy
+ //Let's do it manually for now
+ foreach (buffer; file.byChunk(4096 * 1024))
+ hash.put(buffer);
+
+ auto result = hash.finish();
+ writefln("%s (%s) = %s", Hash.stringof, filename, toHexString(result));
+ }
+
+ void uMain(string[] args)
+ {
+ MD5 md5;
+ SHA1 sha1;
+ CRC32 crc32;
+
+ md5.start();
+ sha1.start();
+ crc32.start();
+
+ foreach (arg; args[1 .. $])
+ {
+ digestFile(md5, arg);
+ digestFile(sha1, arg);
+ digestFile(crc32, arg);
+ }
+ }
+}
+
+///
+@system unittest
+{
+ import std.digest.crc, std.digest.md, std.digest.sha;
+ import std.stdio;
+
+ // Digests a file and prints the result.
+ void digestFile(Digest hash, string filename)
+ {
+ File file = File(filename);
+
+ //As digests implement OutputRange, we could use std.algorithm.copy
+ //Let's do it manually for now
+ foreach (buffer; file.byChunk(4096 * 1024))
+ hash.put(buffer);
+
+ ubyte[] result = hash.finish();
+ writefln("%s (%s) = %s", typeid(hash).toString(), filename, toHexString(result));
+ }
+
+ void umain(string[] args)
+ {
+ auto md5 = new MD5Digest();
+ auto sha1 = new SHA1Digest();
+ auto crc32 = new CRC32Digest();
+
+ foreach (arg; args[1 .. $])
+ {
+ digestFile(md5, arg);
+ digestFile(sha1, arg);
+ digestFile(crc32, arg);
+ }
+ }
+}
+
+version(StdDdoc)
+ version = ExampleDigest;
+
+version(ExampleDigest)
+{
+ /**
+ * This documents the general structure of a Digest in the template API.
+ * All digest implementations should implement the following members and therefore pass
+ * the $(LREF isDigest) test.
+ *
+ * Note:
+ * $(UL
+ * $(LI A digest must be a struct (value type) to pass the $(LREF isDigest) test.)
+ * $(LI A digest passing the $(LREF isDigest) test is always an $(D OutputRange))
+ * )
+ */
+ struct ExampleDigest
+ {
+ public:
+ /**
+ * Use this to feed the digest with data.
+ * Also implements the $(REF isOutputRange, std,range,primitives)
+ * interface for $(D ubyte) and $(D const(ubyte)[]).
+ * The following usages of $(D put) must work for any type which
+ * passes $(LREF isDigest):
+ * Example:
+ * ----
+ * ExampleDigest dig;
+ * dig.put(cast(ubyte) 0); //single ubyte
+ * dig.put(cast(ubyte) 0, cast(ubyte) 0); //variadic
+ * ubyte[10] buf;
+ * dig.put(buf); //buffer
+ * ----
+ */
+ @trusted void put(scope const(ubyte)[] data...)
+ {
+
+ }
+
+ /**
+ * This function is used to (re)initialize the digest.
+ * It must be called before using the digest and it also works as a 'reset' function
+ * if the digest has already processed data.
+ */
+ @trusted void start()
+ {
+
+ }
+
+ /**
+ * The finish function returns the final hash sum and resets the Digest.
+ *
+ * Note:
+ * The actual type returned by finish depends on the digest implementation.
+ * $(D ubyte[16]) is just used as an example. It is guaranteed that the type is a
+ * static array of ubytes.
+ *
+ * $(UL
+ * $(LI Use $(LREF DigestType) to obtain the actual return type.)
+ * $(LI Use $(LREF digestLength) to obtain the length of the ubyte array.)
+ * )
+ */
+ @trusted ubyte[16] finish()
+ {
+ return (ubyte[16]).init;
+ }
+ }
+}
+
+///
+@system unittest
+{
+ //Using the OutputRange feature
+ import std.algorithm.mutation : copy;
+ import std.digest.md;
+ import std.range : repeat;
+
+ auto oneMillionRange = repeat!ubyte(cast(ubyte)'a', 1000000);
+ auto ctx = makeDigest!MD5();
+ copy(oneMillionRange, &ctx); //Note: You must pass a pointer to copy!
+ assert(ctx.finish().toHexString() == "7707D6AE4E027C70EEA2A935C2296F21");
+}
+
+/**
+ * Use this to check if a type is a digest. See $(LREF ExampleDigest) to see what
+ * a type must provide to pass this check.
+ *
+ * Note:
+ * This is very useful as a template constraint (see examples)
+ *
+ * BUGS:
+ * $(UL
+ * $(LI Does not yet verify that put takes scope parameters.)
+ * $(LI Should check that finish() returns a ubyte[num] array)
+ * )
+ */
+template isDigest(T)
+{
+ import std.range : isOutputRange;
+ enum bool isDigest = isOutputRange!(T, const(ubyte)[]) && isOutputRange!(T, ubyte) &&
+ is(T == struct) &&
+ is(typeof(
+ {
+ T dig = void; //Can define
+ dig.put(cast(ubyte) 0, cast(ubyte) 0); //varags
+ dig.start(); //has start
+ auto value = dig.finish(); //has finish
+ }));
+}
+
+///
+@system unittest
+{
+ import std.digest.crc;
+ static assert(isDigest!CRC32);
+}
+///
+@system unittest
+{
+ import std.digest.crc;
+ void myFunction(T)()
+ if (isDigest!T)
+ {
+ T dig;
+ dig.start();
+ auto result = dig.finish();
+ }
+ myFunction!CRC32();
+}
+
+/**
+ * Use this template to get the type which is returned by a digest's $(LREF finish) method.
+ */
+template DigestType(T)
+{
+ static if (isDigest!T)
+ {
+ alias DigestType =
+ ReturnType!(typeof(
+ {
+ T dig = void;
+ return dig.finish();
+ }));
+ }
+ else
+ static assert(false, T.stringof ~ " is not a digest! (fails isDigest!T)");
+}
+
+///
+@system unittest
+{
+ import std.digest.crc;
+ assert(is(DigestType!(CRC32) == ubyte[4]));
+}
+///
+@system unittest
+{
+ import std.digest.crc;
+ CRC32 dig;
+ dig.start();
+ DigestType!CRC32 result = dig.finish();
+}
+
+/**
+ * Used to check if a digest supports the $(D peek) method.
+ * Peek has exactly the same function signatures as finish, but it doesn't reset
+ * the digest's internal state.
+ *
+ * Note:
+ * $(UL
+ * $(LI This is very useful as a template constraint (see examples))
+ * $(LI This also checks if T passes $(LREF isDigest))
+ * )
+ */
+template hasPeek(T)
+{
+ enum bool hasPeek = isDigest!T &&
+ is(typeof(
+ {
+ T dig = void; //Can define
+ DigestType!T val = dig.peek();
+ }));
+}
+
+///
+@system unittest
+{
+ import std.digest.crc, std.digest.md;
+ assert(!hasPeek!(MD5));
+ assert(hasPeek!CRC32);
+}
+///
+@system unittest
+{
+ import std.digest.crc;
+ void myFunction(T)()
+ if (hasPeek!T)
+ {
+ T dig;
+ dig.start();
+ auto result = dig.peek();
+ }
+ myFunction!CRC32();
+}
+
+/**
+ * Checks whether the digest has a $(D blockSize) member, which contains the
+ * digest's internal block size in bits. It is primarily used by $(REF HMAC, std,digest,hmac).
+ */
+
+template hasBlockSize(T)
+if (isDigest!T)
+{
+ enum bool hasBlockSize = __traits(compiles, { size_t blockSize = T.blockSize; });
+}
+
+///
+@system unittest
+{
+ import std.digest.hmac, std.digest.md;
+ static assert(hasBlockSize!MD5 && MD5.blockSize == 512);
+ static assert(hasBlockSize!(HMAC!MD5) && HMAC!MD5.blockSize == 512);
+}
+
+package template isDigestibleRange(Range)
+{
+ import std.digest.md;
+ import std.range : isInputRange, ElementType;
+ enum bool isDigestibleRange = isInputRange!Range && is(typeof(
+ {
+ MD5 ha; //Could use any conformant hash
+ ElementType!Range val;
+ ha.put(val);
+ }));
+}
+
+/**
+ * This is a convenience function to calculate a hash using the template API.
+ * Every digest passing the $(LREF isDigest) test can be used with this function.
+ *
+ * Params:
+ * range= an $(D InputRange) with $(D ElementType) $(D ubyte), $(D ubyte[]) or $(D ubyte[num])
+ */
+DigestType!Hash digest(Hash, Range)(auto ref Range range)
+if (!isArray!Range
+ && isDigestibleRange!Range)
+{
+ import std.algorithm.mutation : copy;
+ Hash hash;
+ hash.start();
+ copy(range, &hash);
+ return hash.finish();
+}
+
+///
+@system unittest
+{
+ import std.digest.md;
+ import std.range : repeat;
+ auto testRange = repeat!ubyte(cast(ubyte)'a', 100);
+ auto md5 = digest!MD5(testRange);
+}
+
+/**
+ * This overload of the digest function handles arrays.
+ *
+ * Params:
+ * data= one or more arrays of any type
+ */
+DigestType!Hash digest(Hash, T...)(scope const T data)
+if (allSatisfy!(isArray, typeof(data)))
+{
+ Hash hash;
+ hash.start();
+ foreach (datum; data)
+ hash.put(cast(const(ubyte[]))datum);
+ return hash.finish();
+}
+
+///
+@system unittest
+{
+ import std.digest.crc, std.digest.md, std.digest.sha;
+ auto md5 = digest!MD5( "The quick brown fox jumps over the lazy dog");
+ auto sha1 = digest!SHA1( "The quick brown fox jumps over the lazy dog");
+ auto crc32 = digest!CRC32("The quick brown fox jumps over the lazy dog");
+ assert(toHexString(crc32) == "39A34F41");
+}
+
+///
+@system unittest
+{
+ import std.digest.crc;
+ auto crc32 = digest!CRC32("The quick ", "brown ", "fox jumps over the lazy dog");
+ assert(toHexString(crc32) == "39A34F41");
+}
+
+/**
+ * This is a convenience function similar to $(LREF digest), but it returns the string
+ * representation of the hash. Every digest passing the $(LREF isDigest) test can be used with this
+ * function.
+ *
+ * Params:
+ * order= the order in which the bytes are processed (see $(LREF toHexString))
+ * range= an $(D InputRange) with $(D ElementType) $(D ubyte), $(D ubyte[]) or $(D ubyte[num])
+ */
+char[digestLength!(Hash)*2] hexDigest(Hash, Order order = Order.increasing, Range)(ref Range range)
+if (!isArray!Range && isDigestibleRange!Range)
+{
+ return toHexString!order(digest!Hash(range));
+}
+
+///
+@system unittest
+{
+ import std.digest.md;
+ import std.range : repeat;
+ auto testRange = repeat!ubyte(cast(ubyte)'a', 100);
+ assert(hexDigest!MD5(testRange) == "36A92CC94A9E0FA21F625F8BFB007ADF");
+}
+
+/**
+ * This overload of the hexDigest function handles arrays.
+ *
+ * Params:
+ * order= the order in which the bytes are processed (see $(LREF toHexString))
+ * data= one or more arrays of any type
+ */
+char[digestLength!(Hash)*2] hexDigest(Hash, Order order = Order.increasing, T...)(scope const T data)
+if (allSatisfy!(isArray, typeof(data)))
+{
+ return toHexString!order(digest!Hash(data));
+}
+
+///
+@system unittest
+{
+ import std.digest.crc;
+ assert(hexDigest!(CRC32, Order.decreasing)("The quick brown fox jumps over the lazy dog") == "414FA339");
+}
+///
+@system unittest
+{
+ import std.digest.crc;
+ assert(hexDigest!(CRC32, Order.decreasing)("The quick ", "brown ", "fox jumps over the lazy dog") == "414FA339");
+}
+
+/**
+ * This is a convenience function which returns an initialized digest, so it's not necessary to call
+ * start manually.
+ */
+Hash makeDigest(Hash)()
+{
+ Hash hash;
+ hash.start();
+ return hash;
+}
+
+///
+@system unittest
+{
+ import std.digest.md;
+ auto md5 = makeDigest!MD5();
+ md5.put(0);
+ assert(toHexString(md5.finish()) == "93B885ADFE0DA089CDF634904FD59F71");
+}
+
+/*+*************************** End of template part, welcome to OOP land **************************/
+
+/**
+ * This describes the OOP API. To understand when to use the template API and when to use the OOP API,
+ * see the module documentation at the top of this page.
+ *
+ * The Digest interface is the base interface which is implemented by all digests.
+ *
+ * Note:
+ * A Digest implementation is always an $(D OutputRange)
+ */
+interface Digest
+{
+ public:
+ /**
+ * Use this to feed the digest with data.
+ * Also implements the $(REF isOutputRange, std,range,primitives)
+ * interface for $(D ubyte) and $(D const(ubyte)[]).
+ *
+ * Example:
+ * ----
+ * void test(Digest dig)
+ * {
+ * dig.put(cast(ubyte) 0); //single ubyte
+ * dig.put(cast(ubyte) 0, cast(ubyte) 0); //variadic
+ * ubyte[10] buf;
+ * dig.put(buf); //buffer
+ * }
+ * ----
+ */
+ @trusted nothrow void put(scope const(ubyte)[] data...);
+
+ /**
+ * Resets the internal state of the digest.
+ * Note:
+ * $(LREF finish) calls this internally, so it's not necessary to call
+ * $(D reset) manually after a call to $(LREF finish).
+ */
+ @trusted nothrow void reset();
+
+ /**
+ * This is the length in bytes of the hash value which is returned by $(LREF finish).
+ * It's also the required size of a buffer passed to $(LREF finish).
+ */
+ @trusted nothrow @property size_t length() const;
+
+ /**
+ * The finish function returns the hash value. It takes an optional buffer to copy the data
+ * into. If a buffer is passed, it must be at least $(LREF length) bytes big.
+ */
+ @trusted nothrow ubyte[] finish();
+ ///ditto
+ nothrow ubyte[] finish(ubyte[] buf);
+ //@@@BUG@@@ http://d.puremagic.com/issues/show_bug.cgi?id=6549
+ /*in
+ {
+ assert(buf.length >= this.length);
+ }*/
+
+ /**
+ * This is a convenience function to calculate the hash of a value using the OOP API.
+ */
+ final @trusted nothrow ubyte[] digest(scope const(void[])[] data...)
+ {
+ this.reset();
+ foreach (datum; data)
+ this.put(cast(ubyte[]) datum);
+ return this.finish();
+ }
+}
+
+///
+@system unittest
+{
+ //Using the OutputRange feature
+ import std.algorithm.mutation : copy;
+ import std.digest.md;
+ import std.range : repeat;
+
+ auto oneMillionRange = repeat!ubyte(cast(ubyte)'a', 1000000);
+ auto ctx = new MD5Digest();
+ copy(oneMillionRange, ctx);
+ assert(ctx.finish().toHexString() == "7707D6AE4E027C70EEA2A935C2296F21");
+}
+
+///
+@system unittest
+{
+ import std.digest.crc, std.digest.md, std.digest.sha;
+ ubyte[] md5 = (new MD5Digest()).digest("The quick brown fox jumps over the lazy dog");
+ ubyte[] sha1 = (new SHA1Digest()).digest("The quick brown fox jumps over the lazy dog");
+ ubyte[] crc32 = (new CRC32Digest()).digest("The quick brown fox jumps over the lazy dog");
+ assert(crcHexString(crc32) == "414FA339");
+}
+
+///
+@system unittest
+{
+ import std.digest.crc;
+ ubyte[] crc32 = (new CRC32Digest()).digest("The quick ", "brown ", "fox jumps over the lazy dog");
+ assert(crcHexString(crc32) == "414FA339");
+}
+
+@system unittest
+{
+ import std.range : isOutputRange;
+ assert(!isDigest!(Digest));
+ assert(isOutputRange!(Digest, ubyte));
+}
+
+///
+@system unittest
+{
+ void test(Digest dig)
+ {
+ dig.put(cast(ubyte) 0); //single ubyte
+ dig.put(cast(ubyte) 0, cast(ubyte) 0); //variadic
+ ubyte[10] buf;
+ dig.put(buf); //buffer
+ }
+}
+
+/*+*************************** End of OOP part, helper functions follow ***************************/
+
+/**
+ * See $(LREF toHexString)
+ */
+enum Order : bool
+{
+ increasing, ///
+ decreasing ///
+}
+
+
+/**
+ * Used to convert a hash value (a static or dynamic array of ubytes) to a string.
+ * Can be used with the OOP and with the template API.
+ *
+ * The additional order parameter can be used to specify the order of the input data.
+ * By default the data is processed in increasing order, starting at index 0. To process it in the
+ * opposite order, pass Order.decreasing as a parameter.
+ *
+ * The additional letterCase parameter can be used to specify the case of the output data.
+ * By default the output is in upper case. To change it to the lower case
+ * pass LetterCase.lower as a parameter.
+ *
+ * Note:
+ * The function overloads returning a string allocate their return values
+ * using the GC. The versions returning static arrays use pass-by-value for
+ * the return value, effectively avoiding dynamic allocation.
+ */
+char[num*2] toHexString(Order order = Order.increasing, size_t num, LetterCase letterCase = LetterCase.upper)
+(in ubyte[num] digest)
+{
+ static if (letterCase == LetterCase.upper)
+ {
+ import std.ascii : hexDigits = hexDigits;
+ }
+ else
+ {
+ import std.ascii : hexDigits = lowerHexDigits;
+ }
+
+
+ char[num*2] result;
+ size_t i;
+
+ static if (order == Order.increasing)
+ {
+ foreach (u; digest)
+ {
+ result[i++] = hexDigits[u >> 4];
+ result[i++] = hexDigits[u & 15];
+ }
+ }
+ else
+ {
+ size_t j = num - 1;
+ while (i < num*2)
+ {
+ result[i++] = hexDigits[digest[j] >> 4];
+ result[i++] = hexDigits[digest[j] & 15];
+ j--;
+ }
+ }
+ return result;
+}
+
+///ditto
+char[num*2] toHexString(LetterCase letterCase, Order order = Order.increasing, size_t num)(in ubyte[num] digest)
+{
+ return toHexString!(order, num, letterCase)(digest);
+}
+
+///ditto
+string toHexString(Order order = Order.increasing, LetterCase letterCase = LetterCase.upper)
+(in ubyte[] digest)
+{
+ static if (letterCase == LetterCase.upper)
+ {
+ import std.ascii : hexDigits = hexDigits;
+ }
+ else
+ {
+ import std.ascii : hexDigits = lowerHexDigits;
+ }
+
+ auto result = new char[digest.length*2];
+ size_t i;
+
+ static if (order == Order.increasing)
+ {
+ foreach (u; digest)
+ {
+ result[i++] = hexDigits[u >> 4];
+ result[i++] = hexDigits[u & 15];
+ }
+ }
+ else
+ {
+ import std.range : retro;
+ foreach (u; retro(digest))
+ {
+ result[i++] = hexDigits[u >> 4];
+ result[i++] = hexDigits[u & 15];
+ }
+ }
+ import std.exception : assumeUnique;
+ // memory was just created, so casting to immutable is safe
+ return () @trusted { return assumeUnique(result); }();
+}
+
+///ditto
+string toHexString(LetterCase letterCase, Order order = Order.increasing)(in ubyte[] digest)
+{
+ return toHexString!(order, letterCase)(digest);
+}
+
+//For more example unittests, see Digest.digest, digest
+
+///
+@safe unittest
+{
+ import std.digest.crc;
+ //Test with template API:
+ auto crc32 = digest!CRC32("The quick ", "brown ", "fox jumps over the lazy dog");
+ //Lower case variant:
+ assert(toHexString!(LetterCase.lower)(crc32) == "39a34f41");
+ //Usually CRCs are printed in this order, though:
+ assert(toHexString!(Order.decreasing)(crc32) == "414FA339");
+ assert(toHexString!(LetterCase.lower, Order.decreasing)(crc32) == "414fa339");
+}
+
+///
+@safe unittest
+{
+ import std.digest.crc;
+ // With OOP API
+ auto crc32 = (new CRC32Digest()).digest("The quick ", "brown ", "fox jumps over the lazy dog");
+ //Usually CRCs are printed in this order, though:
+ assert(toHexString!(Order.decreasing)(crc32) == "414FA339");
+}
+
+@safe unittest
+{
+ ubyte[16] data;
+ assert(toHexString(data) == "00000000000000000000000000000000");
+
+ assert(toHexString(cast(ubyte[4])[42, 43, 44, 45]) == "2A2B2C2D");
+ assert(toHexString(cast(ubyte[])[42, 43, 44, 45]) == "2A2B2C2D");
+ assert(toHexString!(Order.decreasing)(cast(ubyte[4])[42, 43, 44, 45]) == "2D2C2B2A");
+ assert(toHexString!(Order.decreasing, LetterCase.lower)(cast(ubyte[4])[42, 43, 44, 45]) == "2d2c2b2a");
+ assert(toHexString!(Order.decreasing)(cast(ubyte[])[42, 43, 44, 45]) == "2D2C2B2A");
+}
+
+/*+*********************** End of public helper part, private helpers follow ***********************/
+
+/*
+ * Used to convert from a ubyte[] slice to a ref ubyte[N].
+ * This helper is used internally in the WrapperDigest template to wrap the template API's
+ * finish function.
+ */
+ref T[N] asArray(size_t N, T)(ref T[] source, string errorMsg = "")
+{
+ assert(source.length >= N, errorMsg);
+ return *cast(T[N]*) source.ptr;
+}
+
+/*
+ * Returns the length (in bytes) of the hash value produced by T.
+ */
+template digestLength(T)
+if (isDigest!T)
+{
+ enum size_t digestLength = (ReturnType!(T.finish)).length;
+}
+
+@safe pure nothrow @nogc
+unittest
+{
+ import std.digest.md : MD5;
+ import std.digest.sha : SHA1, SHA256, SHA512;
+ assert(digestLength!MD5 == 16);
+ assert(digestLength!SHA1 == 20);
+ assert(digestLength!SHA256 == 32);
+ assert(digestLength!SHA512 == 64);
+}
+
+/**
+ * Wraps a template API hash struct into a Digest interface.
+ * Modules providing digest implementations will usually provide
+ * an alias for this template (e.g. MD5Digest, SHA1Digest, ...).
+ */
+class WrapperDigest(T)
+if (isDigest!T) : Digest
+{
+ protected:
+ T _digest;
+
+ public final:
+ /**
+ * Initializes the digest.
+ */
+ this()
+ {
+ _digest.start();
+ }
+
+ /**
+ * Use this to feed the digest with data.
+ * Also implements the $(REF isOutputRange, std,range,primitives)
+ * interface for $(D ubyte) and $(D const(ubyte)[]).
+ */
+ @trusted nothrow void put(scope const(ubyte)[] data...)
+ {
+ _digest.put(data);
+ }
+
+ /**
+ * Resets the internal state of the digest.
+ * Note:
+ * $(LREF finish) calls this internally, so it's not necessary to call
+ * $(D reset) manually after a call to $(LREF finish).
+ */
+ @trusted nothrow void reset()
+ {
+ _digest.start();
+ }
+
+ /**
+ * This is the length in bytes of the hash value which is returned by $(LREF finish).
+ * It's also the required size of a buffer passed to $(LREF finish).
+ */
+ @trusted nothrow @property size_t length() const pure
+ {
+ return digestLength!T;
+ }
+
+ /**
+ * The finish function returns the hash value. It takes an optional buffer to copy the data
+ * into. If a buffer is passed, it must have a length at least $(LREF length) bytes.
+ *
+ * Example:
+ * --------
+ *
+ * import std.digest.md;
+ * ubyte[16] buf;
+ * auto hash = new WrapperDigest!MD5();
+ * hash.put(cast(ubyte) 0);
+ * auto result = hash.finish(buf[]);
+ * //The result is now in result (and in buf). If you pass a buffer which is bigger than
+ * //necessary, result will have the correct length, but buf will still have it's original
+ * //length
+ * --------
+ */
+ nothrow ubyte[] finish(ubyte[] buf)
+ in
+ {
+ assert(buf.length >= this.length);
+ }
+ body
+ {
+ enum string msg = "Buffer needs to be at least " ~ digestLength!(T).stringof ~ " bytes " ~
+ "big, check " ~ typeof(this).stringof ~ ".length!";
+ asArray!(digestLength!T)(buf, msg) = _digest.finish();
+ return buf[0 .. digestLength!T];
+ }
+
+ ///ditto
+ @trusted nothrow ubyte[] finish()
+ {
+ enum len = digestLength!T;
+ auto buf = new ubyte[len];
+ asArray!(digestLength!T)(buf) = _digest.finish();
+ return buf;
+ }
+
+ version(StdDdoc)
+ {
+ /**
+ * Works like $(D finish) but does not reset the internal state, so it's possible
+ * to continue putting data into this WrapperDigest after a call to peek.
+ *
+ * These functions are only available if $(D hasPeek!T) is true.
+ */
+ @trusted ubyte[] peek(ubyte[] buf) const;
+ ///ditto
+ @trusted ubyte[] peek() const;
+ }
+ else static if (hasPeek!T)
+ {
+ @trusted ubyte[] peek(ubyte[] buf) const
+ in
+ {
+ assert(buf.length >= this.length);
+ }
+ body
+ {
+ enum string msg = "Buffer needs to be at least " ~ digestLength!(T).stringof ~ " bytes " ~
+ "big, check " ~ typeof(this).stringof ~ ".length!";
+ asArray!(digestLength!T)(buf, msg) = _digest.peek();
+ return buf[0 .. digestLength!T];
+ }
+
+ @trusted ubyte[] peek() const
+ {
+ enum len = digestLength!T;
+ auto buf = new ubyte[len];
+ asArray!(digestLength!T)(buf) = _digest.peek();
+ return buf;
+ }
+ }
+}
+
+///
+@system unittest
+{
+ import std.digest.md;
+ //Simple example
+ auto hash = new WrapperDigest!MD5();
+ hash.put(cast(ubyte) 0);
+ auto result = hash.finish();
+}
+
+///
+@system unittest
+{
+ //using a supplied buffer
+ import std.digest.md;
+ ubyte[16] buf;
+ auto hash = new WrapperDigest!MD5();
+ hash.put(cast(ubyte) 0);
+ auto result = hash.finish(buf[]);
+ //The result is now in result (and in buf). If you pass a buffer which is bigger than
+ //necessary, result will have the correct length, but buf will still have it's original
+ //length
+}
+
+@safe unittest
+{
+ // Test peek & length
+ import std.digest.crc;
+ auto hash = new WrapperDigest!CRC32();
+ assert(hash.length == 4);
+ hash.put(cast(const(ubyte[]))"The quick brown fox jumps over the lazy dog");
+ assert(hash.peek().toHexString() == "39A34F41");
+ ubyte[5] buf;
+ assert(hash.peek(buf).toHexString() == "39A34F41");
+}
+
+/**
+ * Securely compares two digest representations while protecting against timing
+ * attacks. Do not use `==` to compare digest representations.
+ *
+ * The attack happens as follows:
+ *
+ * $(OL
+ * $(LI An attacker wants to send harmful data to your server, which
+ * requires a integrity HMAC SHA1 token signed with a secret.)
+ * $(LI The length of the token is known to be 40 characters long due to its format,
+ * so the attacker first sends `"0000000000000000000000000000000000000000"`,
+ * then `"1000000000000000000000000000000000000000"`, and so on.)
+ * $(LI The given HMAC token is compared with the expected token using the
+ * `==` string comparison, which returns `false` as soon as the first wrong
+ * element is found. If a wrong element is found, then a rejection is sent
+ * back to the sender.)
+ * $(LI Eventually, the attacker is able to determine the first character in
+ * the correct token because the sever takes slightly longer to return a
+ * rejection. This is due to the comparison moving on to second item in
+ * the two arrays, seeing they are different, and then sending the rejection.)
+ * $(LI It may seem like too small of a difference in time for the attacker
+ * to notice, but security researchers have shown that differences as
+ * small as $(LINK2 http://www.cs.rice.edu/~dwallach/pub/crosby-timing2009.pdf,
+ * 20µs can be reliably distinguished) even with network inconsistencies.)
+ * $(LI Repeat the process for each character until the attacker has the whole
+ * correct token and the server accepts the harmful data. This can be done
+ * in a week with the attacker pacing the attack to 10 requests per second
+ * with only one client.)
+ * )
+ *
+ * This function defends against this attack by always comparing every single
+ * item in the array if the two arrays are the same length. Therefore, this
+ * function is always $(BIGOH n) for ranges of the same length.
+ *
+ * This attack can also be mitigated via rate limiting and banning IPs which have too
+ * many rejected requests. However, this does not completely solve the problem,
+ * as the attacker could be in control of a bot net. To fully defend against
+ * the timing attack, rate limiting, banning IPs, and using this function
+ * should be used together.
+ *
+ * Params:
+ * r1 = A digest representation
+ * r2 = A digest representation
+ * Returns:
+ * `true` if both representations are equal, `false` otherwise
+ * See_Also:
+ * $(LINK2 https://en.wikipedia.org/wiki/Timing_attack, The Wikipedia article
+ * on timing attacks).
+ */
+bool secureEqual(R1, R2)(R1 r1, R2 r2)
+if (isInputRange!R1 && isInputRange!R2 && !isInfinite!R1 && !isInfinite!R2 &&
+ (isIntegral!(ElementEncodingType!R1) || isSomeChar!(ElementEncodingType!R1)) &&
+ !is(CommonType!(ElementEncodingType!R1, ElementEncodingType!R2) == void))
+{
+ static if (hasLength!R1 && hasLength!R2)
+ if (r1.length != r2.length)
+ return false;
+
+ int result;
+
+ static if (isRandomAccessRange!R1 && isRandomAccessRange!R2 &&
+ hasLength!R1 && hasLength!R2)
+ {
+ foreach (i; 0 .. r1.length)
+ result |= r1[i] ^ r2[i];
+ }
+ else static if (hasLength!R1 && hasLength!R2)
+ {
+ // Lengths are the same so we can squeeze out a bit of performance
+ // by not checking if r2 is empty
+ for (; !r1.empty; r1.popFront(), r2.popFront())
+ {
+ result |= r1.front ^ r2.front;
+ }
+ }
+ else
+ {
+ // Generic case, walk both ranges
+ for (; !r1.empty; r1.popFront(), r2.popFront())
+ {
+ if (r2.empty) return false;
+ result |= r1.front ^ r2.front;
+ }
+ if (!r2.empty) return false;
+ }
+
+ return result == 0;
+}
+
+///
+@system pure unittest
+{
+ import std.digest.hmac : hmac;
+ import std.digest.sha : SHA1;
+ import std.string : representation;
+
+ // a typical HMAC data integrity verification
+ auto secret = "A7GZIP6TAQA6OHM7KZ42KB9303CEY0MOV5DD6NTV".representation;
+ auto data = "data".representation;
+
+ string hex1 = data.hmac!SHA1(secret).toHexString;
+ string hex2 = data.hmac!SHA1(secret).toHexString;
+ string hex3 = "data1".representation.hmac!SHA1(secret).toHexString;
+
+ assert( secureEqual(hex1, hex2));
+ assert(!secureEqual(hex1, hex3));
+}
+
+@system pure unittest
+{
+ import std.internal.test.dummyrange : ReferenceInputRange;
+ import std.range : takeExactly;
+ import std.string : representation;
+ import std.utf : byWchar, byDchar;
+
+ {
+ auto hex1 = "02CA3484C375EDD3C0F08D3F50D119E61077".representation;
+ auto hex2 = "02CA3484C375EDD3C0F08D3F50D119E610779018".representation;
+ assert(!secureEqual(hex1, hex2));
+ }
+ {
+ auto hex1 = "02CA3484C375EDD3C0F08D3F50D119E610779018"w.representation;
+ auto hex2 = "02CA3484C375EDD3C0F08D3F50D119E610779018"d.representation;
+ assert(secureEqual(hex1, hex2));
+ }
+ {
+ auto hex1 = "02CA3484C375EDD3C0F08D3F50D119E610779018".byWchar;
+ auto hex2 = "02CA3484C375EDD3C0F08D3F50D119E610779018".byDchar;
+ assert(secureEqual(hex1, hex2));
+ }
+ {
+ auto hex1 = "02CA3484C375EDD3C0F08D3F50D119E61077".byWchar;
+ auto hex2 = "02CA3484C375EDD3C0F08D3F50D119E610779018".byDchar;
+ assert(!secureEqual(hex1, hex2));
+ }
+ {
+ auto hex1 = new ReferenceInputRange!int([0, 1, 2, 3, 4, 5, 6, 7, 8]).takeExactly(9);
+ auto hex2 = new ReferenceInputRange!int([0, 1, 2, 3, 4, 5, 6, 7, 8]).takeExactly(9);
+ assert(secureEqual(hex1, hex2));
+ }
+ {
+ auto hex1 = new ReferenceInputRange!int([0, 1, 2, 3, 4, 5, 6, 7, 8]).takeExactly(9);
+ auto hex2 = new ReferenceInputRange!int([0, 1, 2, 3, 4, 5, 6, 7, 9]).takeExactly(9);
+ assert(!secureEqual(hex1, hex2));
+ }
+}
diff --git a/std/digest/ripemd.d b/std/digest/ripemd.d
index 0dd472c03e4..5c3153f5e3b 100644
--- a/std/digest/ripemd.d
+++ b/std/digest/ripemd.d
@@ -18,10 +18,10 @@ $(TR $(TDNW Helpers) $(TD $(MYREF ripemd160Of))
)
)
- * This module conforms to the APIs defined in $(D std.digest.digest). To understand the
- * differences between the template and the OOP API, see $(D std.digest.digest).
+ * This module conforms to the APIs defined in $(MREF std, digest). To understand the
+ * differences between the template and the OOP API, see $(MREF std, digest).
*
- * This module publicly imports $(D std.digest.digest) and can be used as a stand-alone
+ * This module publicly imports $(D std.digest) and can be used as a stand-alone
* module.
*
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
@@ -46,7 +46,7 @@ $(TR $(TDNW Helpers) $(TD $(MYREF ripemd160Of))
module std.digest.ripemd;
-public import std.digest.digest;
+public import std.digest;
///
@safe unittest
@@ -95,7 +95,7 @@ private uint rotateLeft(uint x, uint n) @safe pure nothrow @nogc
/**
* Template API RIPEMD160 implementation.
- * See $(D std.digest.digest) for differences between template and OOP API.
+ * See $(D std.digest) for differences between template and OOP API.
*/
struct RIPEMD160
{
@@ -662,7 +662,7 @@ struct RIPEMD160
}
/**
- * This is a convenience alias for $(REF digest, std,digest,digest) using the
+ * This is a convenience alias for $(REF digest, std,digest) using the
* RIPEMD160 implementation.
*/
//simple alias doesn't work here, hope this gets inlined...
@@ -680,9 +680,9 @@ auto ripemd160Of(T...)(T data)
/**
* OOP API RIPEMD160 implementation.
- * See $(D std.digest.digest) for differences between template and OOP API.
+ * See $(D std.digest) for differences between template and OOP API.
*
- * This is an alias for $(D $(REF WrapperDigest, std,digest,digest)!RIPEMD160),
+ * This is an alias for $(D $(REF WrapperDigest, std,digest)!RIPEMD160),
* see there for more information.
*/
alias RIPEMD160Digest = WrapperDigest!RIPEMD160;
diff --git a/std/digest/sha.d b/std/digest/sha.d
index 4f661f7c2b9..3374cbfb634 100644
--- a/std/digest/sha.d
+++ b/std/digest/sha.d
@@ -23,10 +23,10 @@ $(TR $(TDNW Helpers) $(TD $(MYREF sha1Of))
* SHA2 comes in several different versions, all supported by this module:
* SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224 and SHA-512/256.
*
- * This module conforms to the APIs defined in $(D std.digest.digest). To understand the
- * differences between the template and the OOP API, see $(D std.digest.digest).
+ * This module conforms to the APIs defined in $(MREF std, digest). To understand the
+ * differences between the template and the OOP API, see $(MREF std, digest).
*
- * This module publicly imports $(D std.digest.digest) and can be used as a stand-alone
+ * This module publicly imports $(D std.digest) and can be used as a stand-alone
* module.
*
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
@@ -124,7 +124,7 @@ version(unittest)
}
-public import std.digest.digest;
+public import std.digest;
/*
* Helper methods for encoding the buffer.
@@ -193,7 +193,7 @@ private ulong rotateRight(ulong x, uint n) @safe pure nothrow @nogc
* simply use the convenience aliases: SHA1, SHA224, SHA256, SHA384, SHA512,
* SHA512_224 and SHA512_256.
*
- * See $(D std.digest.digest) for differences between template and OOP API.
+ * See $(D std.digest) for differences between template and OOP API.
*/
struct SHA(uint hashBlockSize, uint digestSize)
{
@@ -1122,7 +1122,7 @@ alias SHA512_256 = SHA!(1024, 256); /// SHA alias for SHA-512/256, hash is ubyte
}
/**
- * These are convenience aliases for $(REF digest, std,digest,digest) using the
+ * These are convenience aliases for $(REF digest, std,digest) using the
* SHA implementation.
*/
//simple alias doesn't work here, hope this gets inlined...
@@ -1199,9 +1199,9 @@ auto sha512_256Of(T...)(T data)
/**
* OOP API SHA1 and SHA2 implementations.
- * See $(D std.digest.digest) for differences between template and OOP API.
+ * See $(D std.digest) for differences between template and OOP API.
*
- * This is an alias for $(D $(REF WrapperDigest, std,digest,digest)!SHA1), see
+ * This is an alias for $(D $(REF WrapperDigest, std,digest)!SHA1), see
* there for more information.
*/
alias SHA1Digest = WrapperDigest!SHA1;
diff --git a/std/exception.d b/std/exception.d
index 5767e8710a0..fa00f288b17 100644
--- a/std/exception.d
+++ b/std/exception.d
@@ -1478,6 +1478,34 @@ private bool isUnionAliasedImpl(T)(size_t offset)
static assert( isUnionAliased!(S.A5, 1)); //a5.b1;
}
+package string errnoString(int errno) nothrow @trusted
+{
+ import core.stdc.string : strlen;
+ version (CRuntime_Glibc)
+ {
+ import core.stdc.string : strerror_r;
+ char[1024] buf = void;
+ auto s = strerror_r(errno, buf.ptr, buf.length);
+ }
+ else version (Posix)
+ {
+ // XSI-compliant
+ import core.stdc.string : strerror_r;
+ char[1024] buf = void;
+ const(char)* s;
+ if (strerror_r(errno, buf.ptr, buf.length) == 0)
+ s = buf.ptr;
+ else
+ return "Unknown error";
+ }
+ else
+ {
+ import core.stdc.string : strerror;
+ auto s = strerror(errno);
+ }
+ return s[0 .. s.strlen].idup;
+}
+
/*********************
* Thrown if errors that set $(D errno) occur.
*/
@@ -1485,28 +1513,17 @@ class ErrnoException : Exception
{
final @property uint errno() { return _errno; } /// Operating system error code.
private uint _errno;
+ /// Constructor which takes an error message. The current global $(REF errno, core,stdc,errno) value is used as error code.
this(string msg, string file = null, size_t line = 0) @trusted
{
import core.stdc.errno : errno;
this(msg, errno, file, line);
}
+ /// Constructor which takes an error message and error code.
this(string msg, int errno, string file = null, size_t line = 0) @trusted
{
- import core.stdc.string : strlen;
-
_errno = errno;
- version (CRuntime_Glibc)
- {
- import core.stdc.string : strerror_r;
- char[1024] buf = void;
- auto s = strerror_r(errno, buf.ptr, buf.length);
- }
- else
- {
- import core.stdc.string : strerror;
- auto s = strerror(errno);
- }
- super(msg ~ " (" ~ s[0 .. s.strlen].idup ~ ")", file, line);
+ super(msg ~ " (" ~ errnoString(errno) ~ ")", file, line);
}
@system unittest
@@ -1778,7 +1795,7 @@ Params:
Returns: A wrapper $(D struct) that preserves the range interface of $(D input).
-opSlice:
+Note:
Infinite ranges with slicing support must return an instance of
$(REF Take, std,range) when sliced with a specific lower and upper
bound (see $(REF hasSlicing, std,range,primitives)); $(D handle) deals with
diff --git a/std/experimental/allocator/building_blocks/free_list.d b/std/experimental/allocator/building_blocks/free_list.d
index c43b080a035..25f739c1f6d 100644
--- a/std/experimental/allocator/building_blocks/free_list.d
+++ b/std/experimental/allocator/building_blocks/free_list.d
@@ -817,7 +817,7 @@ struct SharedFreeList(ParentAllocator,
@property size_t max() const shared { return _max; }
@property void max(size_t x) shared
{
- enforce(x >= _min && x >= (void*).sizeof);
+ enforce(x >= min && x >= (void*).sizeof);
enforce(cas(&_max, chooseAtRuntime, x),
"SharedFreeList.max must be initialized exactly once.");
}
@@ -1095,6 +1095,7 @@ struct SharedFreeList(ParentAllocator,
{
import std.experimental.allocator.mallocator : Mallocator;
shared SharedFreeList!(Mallocator, chooseAtRuntime, chooseAtRuntime) a;
+ scope(exit) a.deallocateAll();
auto c = a.allocate(64);
assert(a.reallocate(c, 96));
assert(c.length == 96);
@@ -1105,6 +1106,7 @@ struct SharedFreeList(ParentAllocator,
{
import std.experimental.allocator.mallocator : Mallocator;
shared SharedFreeList!(Mallocator, chooseAtRuntime, chooseAtRuntime, chooseAtRuntime) a;
+ scope(exit) a.deallocateAll;
a.allocate(64);
}
@@ -1112,6 +1114,7 @@ struct SharedFreeList(ParentAllocator,
{
import std.experimental.allocator.mallocator : Mallocator;
shared SharedFreeList!(Mallocator, 30, 40) a;
+ scope(exit) a.deallocateAll;
a.allocate(64);
}
@@ -1119,5 +1122,26 @@ struct SharedFreeList(ParentAllocator,
{
import std.experimental.allocator.mallocator : Mallocator;
shared SharedFreeList!(Mallocator, 30, 40, chooseAtRuntime) a;
+ scope(exit) a.deallocateAll;
+ a.allocate(64);
+}
+
+@system unittest
+{
+ // Pull request #5556
+ import std.experimental.allocator.mallocator : Mallocator;
+ shared SharedFreeList!(Mallocator, 0, chooseAtRuntime) a;
+ scope(exit) a.deallocateAll;
+ a.max = 64;
+ a.allocate(64);
+}
+
+@system unittest
+{
+ // Pull request #5556
+ import std.experimental.allocator.mallocator : Mallocator;
+ shared SharedFreeList!(Mallocator, chooseAtRuntime, 64) a;
+ scope(exit) a.deallocateAll;
+ a.min = 32;
a.allocate(64);
}
diff --git a/std/experimental/allocator/building_blocks/region.d b/std/experimental/allocator/building_blocks/region.d
index 0cc60b9dd58..37dd1b42e34 100644
--- a/std/experimental/allocator/building_blocks/region.d
+++ b/std/experimental/allocator/building_blocks/region.d
@@ -591,7 +591,7 @@ SbrkRegion) adversely.
*/
version(Posix) struct SbrkRegion(uint minAlign = platformAlignment)
{
- private import core.sys.posix.pthread : pthread_mutex_init, pthread_mutex_destroy,
+ import core.sys.posix.pthread : pthread_mutex_init, pthread_mutex_destroy,
pthread_mutex_t, pthread_mutex_lock, pthread_mutex_unlock,
PTHREAD_MUTEX_INITIALIZER;
private static shared pthread_mutex_t sbrkMutex = PTHREAD_MUTEX_INITIALIZER;
diff --git a/std/experimental/allocator/building_blocks/scoped_allocator.d b/std/experimental/allocator/building_blocks/scoped_allocator.d
index 998a05f2894..ff3261f3e05 100644
--- a/std/experimental/allocator/building_blocks/scoped_allocator.d
+++ b/std/experimental/allocator/building_blocks/scoped_allocator.d
@@ -22,9 +22,9 @@ struct ScopedAllocator(ParentAllocator)
testAllocator!(() => ScopedAllocator());
}
- private import std.experimental.allocator.building_blocks.affix_allocator
+ import std.experimental.allocator.building_blocks.affix_allocator
: AffixAllocator;
- private import std.traits : hasMember;
+ import std.traits : hasMember;
import std.typecons : Ternary;
private struct Node
diff --git a/std/experimental/allocator/building_blocks/segregator.d b/std/experimental/allocator/building_blocks/segregator.d
index 83520117935..76ca6f409d0 100644
--- a/std/experimental/allocator/building_blocks/segregator.d
+++ b/std/experimental/allocator/building_blocks/segregator.d
@@ -50,7 +50,7 @@ struct Segregator(size_t threshold, SmallAllocator, LargeAllocator)
/**
This method is defined only if at least one of the allocators defines
it. If $(D SmallAllocator) defines $(D expand) and $(D b.length +
- delta <= threshold), the call is forwarded to $(D SmallAllocator). If $(
+ delta <= threshold), the call is forwarded to $(D SmallAllocator). If $(D
LargeAllocator) defines $(D expand) and $(D b.length > threshold), the
call is forwarded to $(D LargeAllocator). Otherwise, the call returns
$(D false).
diff --git a/std/experimental/allocator/showcase.d b/std/experimental/allocator/showcase.d
index 84bad447671..6985e5dad14 100644
--- a/std/experimental/allocator/showcase.d
+++ b/std/experimental/allocator/showcase.d
@@ -61,10 +61,10 @@ auto mmapRegionList(size_t bytesPerRegion)
static struct Factory
{
size_t bytesPerRegion;
- private import std.algorithm.comparison : max;
- private import std.experimental.allocator.building_blocks.region
+ import std.algorithm.comparison : max;
+ import std.experimental.allocator.building_blocks.region
: Region;
- private import std.experimental.allocator.mmap_allocator
+ import std.experimental.allocator.mmap_allocator
: MmapAllocator;
this(size_t n)
{
diff --git a/std/experimental/checkedint.d b/std/experimental/checkedint.d
index 96dc4f282cf..2fe5301eca5 100644
--- a/std/experimental/checkedint.d
+++ b/std/experimental/checkedint.d
@@ -50,6 +50,9 @@ $(BOOKTABLE ,
second parameter, i.e. `Checked!short` is the same as
$(D Checked!(short, Abort)).
))
+ $(TR $(TD $(LREF Throw)) $(TD
+ fails every incorrect operation by throwing an exception.
+ ))
$(TR $(TD $(LREF Warn)) $(TD
prints incorrect operations to $(REF stderr, std, stdio)
but otherwise preserves the built-in behavior.
diff --git a/std/experimental/logger/core.d b/std/experimental/logger/core.d
index c794d19461a..1cf25568081 100644
--- a/std/experimental/logger/core.d
+++ b/std/experimental/logger/core.d
@@ -2,7 +2,8 @@
module std.experimental.logger.core;
import core.sync.mutex : Mutex;
-import std.datetime;
+import std.datetime.date : DateTime;
+import std.datetime.systime : Clock, SysTime;
import std.range.primitives;
import std.traits;
diff --git a/std/experimental/logger/filelogger.d b/std/experimental/logger/filelogger.d
index 3b6e5ddddd7..8f97b5ba7ea 100644
--- a/std/experimental/logger/filelogger.d
+++ b/std/experimental/logger/filelogger.d
@@ -4,6 +4,12 @@ module std.experimental.logger.filelogger;
import std.experimental.logger.core;
import std.stdio;
+import std.typecons : Flag;
+
+/** An option to create $(LREF FileLogger) directory if it is non-existent.
+*/
+alias CreateFolder = Flag!"CreateFolder";
+
/** This $(D Logger) implementation writes log messages to the associated
file. The name of the file has to be passed on construction time. If the file
is already present new log messages will be append at its end.
@@ -11,7 +17,7 @@ is already present new log messages will be append at its end.
class FileLogger : Logger
{
import std.concurrency : Tid;
- import std.datetime : SysTime;
+ import std.datetime.systime : SysTime;
import std.format : formattedWrite;
/** A constructor for the $(D FileLogger) Logger.
@@ -20,18 +26,57 @@ class FileLogger : Logger
fn = The filename of the output file of the $(D FileLogger). If that
file can not be opened for writting an exception will be thrown.
lv = The $(D LogLevel) for the $(D FileLogger). By default the
- $(D LogLevel) for $(D FileLogger) is $(D LogLevel.all).
Example:
-------------
auto l1 = new FileLogger("logFile");
auto l2 = new FileLogger("logFile", LogLevel.fatal);
+ auto l3 = new FileLogger("logFile", LogLevel.fatal, CreateFolder.yes);
-------------
*/
this(in string fn, const LogLevel lv = LogLevel.all) @safe
{
+ this(fn, lv, CreateFolder.yes);
+ }
+
+ /** A constructor for the $(D FileLogger) Logger that takes a reference to
+ a $(D File).
+
+ The $(D File) passed must be open for all the log call to the
+ $(D FileLogger). If the $(D File) gets closed, using the $(D FileLogger)
+ for logging will result in undefined behaviour.
+
+ Params:
+ fn = The file used for logging.
+ lv = The $(D LogLevel) for the $(D FileLogger). By default the
+ $(D LogLevel) for $(D FileLogger) is $(D LogLevel.all).
+ createFileNameFolder = if yes and fn contains a folder name, this
+ folder will be created.
+
+ Example:
+ -------------
+ auto file = File("logFile.log", "w");
+ auto l1 = new FileLogger(file);
+ auto l2 = new FileLogger(file, LogLevel.fatal);
+ -------------
+ */
+ this(in string fn, const LogLevel lv, CreateFolder createFileNameFolder) @safe
+ {
+ import std.file : exists, mkdirRecurse;
+ import std.path : dirName;
+ import std.conv : text;
+
super(lv);
this.filename = fn;
+
+ if (createFileNameFolder)
+ {
+ auto d = dirName(this.filename);
+ mkdirRecurse(d);
+ assert(exists(d), text("The folder the FileLogger should have",
+ " created in '", d,"' could not be created."));
+ }
+
this.file_.open(this.filename, "a");
}
@@ -158,6 +203,24 @@ class FileLogger : Logger
assert(readLine.indexOf(notWritten) == -1, readLine);
}
+@safe unittest
+{
+ import std.file : rmdirRecurse, exists, deleteme;
+ import std.path : dirName;
+
+ const string tmpFolder = dirName(deleteme);
+ const string filepath = tmpFolder ~ "/bug15771/minas/oops/";
+ const string filename = filepath ~ "output.txt";
+ assert(!exists(filepath));
+
+ auto f = new FileLogger(filename, LogLevel.all, CreateFolder.yes);
+ scope(exit) () @trusted { rmdirRecurse(tmpFolder ~ "/bug15771"); }();
+
+ f.log("Hello World!");
+ assert(exists(filepath));
+ f.file.close();
+}
+
@system unittest
{
import std.array : empty;
diff --git a/std/file.d b/std/file.d
index d39093a37fe..f34e3161a7c 100644
--- a/std/file.d
+++ b/std/file.d
@@ -79,8 +79,10 @@ Source: $(PHOBOSSRC std/_file.d)
module std.file;
import core.stdc.errno, core.stdc.stdlib, core.stdc.string;
+import core.time : abs, dur, hnsecs, seconds;
-import std.datetime;
+import std.datetime.date : DateTime;
+import std.datetime.systime : Clock, SysTime, unixTimeToStdTime;
import std.internal.cstring;
import std.meta;
import std.range.primitives;
@@ -206,8 +208,8 @@ class FileException : Exception
string file = __FILE__,
size_t line = __LINE__) @trusted
{
- auto s = strerror(errno);
- this(name, to!string(s), file, line);
+ import std.exception : errnoString;
+ this(name, errnoString(errno), file, line);
this.errno = errno;
}
}
@@ -970,6 +972,8 @@ if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
{
version(Windows)
{
+ import std.datetime.systime : FILETIMEToSysTime;
+
with (getFileAttributesWin(name))
{
accessTime = FILETIMEToSysTime(&ftLastAccessTime);
@@ -1102,11 +1106,13 @@ else version(Windows)
if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
!isConvertibleToString!R)
{
+ import std.datetime.systime : FILETIMEToSysTime;
+
with (getFileAttributesWin(name))
{
- fileCreationTime = std.datetime.FILETIMEToSysTime(&ftCreationTime);
- fileAccessTime = std.datetime.FILETIMEToSysTime(&ftLastAccessTime);
- fileModificationTime = std.datetime.FILETIMEToSysTime(&ftLastWriteTime);
+ fileCreationTime = FILETIMEToSysTime(&ftCreationTime);
+ fileAccessTime = FILETIMEToSysTime(&ftLastAccessTime);
+ fileModificationTime = FILETIMEToSysTime(&ftLastWriteTime);
}
}
@@ -1210,6 +1216,8 @@ if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
{
version(Windows)
{
+ import std.datetime.systime : SysTimeToFILETIME;
+
auto namez = name.tempCString!FSChar();
static auto trustedCreateFileW(const(FSChar)* namez, DWORD dwDesiredAccess, DWORD dwShareMode,
SECURITY_ATTRIBUTES *lpSecurityAttributes, DWORD dwCreationDisposition,
@@ -2636,7 +2644,6 @@ version(Posix) @system unittest // input range of dchars
version(Windows) string getcwd()
{
import std.conv : to;
- import std.utf : toUTF8;
/* GetCurrentDirectory's return value:
1. function succeeds: the number of characters that are written to
the buffer, not including the terminating null character.
@@ -2650,7 +2657,7 @@ version(Windows) string getcwd()
// we can do it because toUTFX always produces a fresh string
if (n < buffW.length)
{
- return toUTF8(buffW[0 .. n]);
+ return buffW[0 .. n].to!string;
}
else //staticBuff isn't enough
{
@@ -2658,7 +2665,7 @@ version(Windows) string getcwd()
scope(exit) free(ptr);
immutable n2 = GetCurrentDirectoryW(n, ptr);
cenforce(n2 && n2 < n, "getcwd");
- return toUTF8(ptr[0 .. n2]);
+ return ptr[0 .. n2].to!string;
}
}
else version (Solaris) string getcwd()
@@ -2698,7 +2705,7 @@ else version (NetBSD)
* Returns the full path of the current executable.
*
* Throws:
- * $(REF Exception, std,object)
+ * $(REF1 Exception, object)
*/
@trusted string thisExePath ()
{
@@ -2960,12 +2967,13 @@ else version(Windows)
{
struct DirEntry
{
- import std.utf : toUTF8;
public:
alias name this;
this(string path)
{
+ import std.datetime.systime : FILETIMEToSysTime;
+
if (!path.exists())
throw new FileException(path, "File does not exist");
@@ -2974,9 +2982,9 @@ else version(Windows)
with (getFileAttributesWin(path))
{
_size = makeUlong(nFileSizeLow, nFileSizeHigh);
- _timeCreated = std.datetime.FILETIMEToSysTime(&ftCreationTime);
- _timeLastAccessed = std.datetime.FILETIMEToSysTime(&ftLastAccessTime);
- _timeLastModified = std.datetime.FILETIMEToSysTime(&ftLastWriteTime);
+ _timeCreated = FILETIMEToSysTime(&ftCreationTime);
+ _timeLastAccessed = FILETIMEToSysTime(&ftLastAccessTime);
+ _timeLastModified = FILETIMEToSysTime(&ftLastWriteTime);
_attributes = dwFileAttributes;
}
}
@@ -2984,15 +2992,16 @@ else version(Windows)
private this(string path, in WIN32_FIND_DATAW *fd)
{
import core.stdc.wchar_ : wcslen;
+ import std.conv : to;
+ import std.datetime.systime : FILETIMEToSysTime;
import std.path : buildPath;
size_t clength = wcslen(fd.cFileName.ptr);
- _name = toUTF8(fd.cFileName[0 .. clength]);
- _name = buildPath(path, toUTF8(fd.cFileName[0 .. clength]));
+ _name = buildPath(path, fd.cFileName[0 .. clength].to!string);
_size = (cast(ulong) fd.nFileSizeHigh << 32) | fd.nFileSizeLow;
- _timeCreated = std.datetime.FILETIMEToSysTime(&fd.ftCreationTime);
- _timeLastAccessed = std.datetime.FILETIMEToSysTime(&fd.ftLastAccessTime);
- _timeLastModified = std.datetime.FILETIMEToSysTime(&fd.ftLastWriteTime);
+ _timeCreated = FILETIMEToSysTime(&fd.ftCreationTime);
+ _timeLastAccessed = FILETIMEToSysTime(&fd.ftLastAccessTime);
+ _timeLastModified = FILETIMEToSysTime(&fd.ftLastWriteTime);
_attributes = fd.dwFileAttributes;
}
@@ -3420,7 +3429,7 @@ private void copyImpl(const(char)[] f, const(char)[] t, const(FSChar)* fromz, co
{
version(Windows)
{
- assert(preserve == Yes.preserve);
+ assert(preserve == Yes.preserveAttributes);
immutable result = CopyFileW(fromz, toz, false);
if (!result)
{
@@ -3661,12 +3670,23 @@ enum SpanMode
{
/** Only spans one directory. */
shallow,
- /** Spans the directory depth-first, i.e. the content of any
+ /** Spans the directory in
+ $(HTTPS en.wikipedia.org/wiki/Tree_traversal#Post-order,
+ _depth-first $(B post)-order), i.e. the content of any
subdirectory is spanned before that subdirectory itself. Useful
e.g. when recursively deleting files. */
depth,
- /** Spans the directory breadth-first, i.e. the content of any
- subdirectory is spanned right after that subdirectory itself. */
+ /** Spans the directory in
+ $(HTTPS en.wikipedia.org/wiki/Tree_traversal#Pre-order, depth-first
+ $(B pre)-order), i.e. the content of any subdirectory is spanned
+ right after that subdirectory itself.
+
+ Note that $(D SpanMode.breadth) will not result in all directory
+ members occurring before any subdirectory members, i.e. it is not
+ _true
+ $(HTTPS en.wikipedia.org/wiki/Tree_traversal#Breadth-first_search,
+ _breadth-first traversal).
+ */
breadth,
}
@@ -3916,9 +3936,18 @@ public:
Params:
path = The directory to iterate over.
If empty, the current directory will be iterated.
- mode = Whether the directory's sub-directories should be iterated
- over depth-first ($(D_PARAM depth)), breadth-first
- ($(D_PARAM breadth)), or not at all ($(D_PARAM shallow)).
+
+ pattern = Optional string with wildcards, such as $(RED
+ "*.d"). When present, it is used to filter the
+ results by their file name. The supported wildcard
+ strings are described under $(REF globMatch,
+ std,_path).
+
+ mode = Whether the directory's sub-directories should be
+ iterated in depth-first port-order ($(LREF depth)),
+ depth-first pre-order ($(LREF breadth)), or not at all
+ ($(LREF shallow)).
+
followSymlink = Whether symbolic links which point to directories
should be treated as directories and their contents
iterated over.
@@ -3958,6 +3987,12 @@ foreach (d; parallel(dFiles, 1)) //passes by 1 file to each thread
writeln(cmd);
std.process.system(cmd);
}
+
+// Iterate over all D source files in current directory and all its
+// subdirectories
+auto dFiles = dirEntries("","*.{d,di}",SpanMode.depth);
+foreach (d; dFiles)
+ writeln(d.name);
--------------------
+/
auto dirEntries(string path, SpanMode mode, bool followSymlink = true)
@@ -4060,33 +4095,7 @@ auto dirEntries(string path, SpanMode mode, bool followSymlink = true)
dirEntries("", SpanMode.shallow).walkLength();
}
-/++
- Convenience wrapper for filtering file names with a glob pattern.
-
- Params:
- path = The directory to iterate over.
- pattern = String with wildcards, such as $(RED "*.d"). The supported
- wildcard strings are described under
- $(REF globMatch, std,_path).
- mode = Whether the directory's sub-directories should be iterated
- over depth-first ($(D_PARAM depth)), breadth-first
- ($(D_PARAM breadth)), or not at all ($(D_PARAM shallow)).
- followSymlink = Whether symbolic links which point to directories
- should be treated as directories and their contents
- iterated over.
-
- Throws:
- $(D FileException) if the directory does not exist.
-
-Example:
---------------------
-// Iterate over all D source files in current directory and all its
-// subdirectories
-auto dFiles = dirEntries("","*.{d,di}",SpanMode.depth);
-foreach (d; dFiles)
- writeln(d.name);
---------------------
- +/
+/// Ditto
auto dirEntries(string path, string pattern, SpanMode mode,
bool followSymlink = true)
{
@@ -4223,6 +4232,8 @@ slurp(Types...)(string filename, in char[] format)
///
@system unittest
{
+ import std.typecons : tuple;
+
scope(exit)
{
assert(exists(deleteme));
@@ -4276,11 +4287,11 @@ string tempDir() @trusted
{
version(Windows)
{
- import std.utf : toUTF8;
+ import std.conv : to;
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa364992(v=vs.85).aspx
wchar[MAX_PATH + 2] buf;
DWORD len = GetTempPathW(buf.length, buf.ptr);
- if (len) cache = toUTF8(buf[0 .. len]);
+ if (len) cache = buf[0 .. len].to!string;
}
else version(Android)
{
diff --git a/std/format.d b/std/format.d
index e155372fc77..cd6b82ceac8 100644
--- a/std/format.d
+++ b/std/format.d
@@ -438,7 +438,7 @@ My friends are "John", "Nancy".
My friends are John, Nancy.
)
*/
-uint formattedWrite(alias fmt, Writer, A...)(Writer w, A args)
+uint formattedWrite(alias fmt, Writer, A...)(auto ref Writer w, A args)
if (isSomeString!(typeof(fmt)))
{
alias e = checkFormatException!(fmt, A);
@@ -463,35 +463,17 @@ if (isSomeString!(typeof(fmt)))
}
/// ditto
-uint formattedWrite(Writer, Char, A...)(Writer w, in Char[] fmt, A args)
+uint formattedWrite(Writer, Char, A...)(auto ref Writer w, in Char[] fmt, A args)
{
import std.conv : text;
- alias FPfmt = void function(Writer, scope const(void)*, const ref FormatSpec!Char) @safe pure nothrow;
-
auto spec = FormatSpec!Char(fmt);
- FPfmt[A.length] funs;
- const(void)*[A.length] argsAddresses;
- if (!__ctfe)
- {
- foreach (i, Arg; A)
- {
- funs[i] = ()@trusted{ return cast(FPfmt)&formatGeneric!(Writer, Arg, Char); }();
- // We can safely cast away shared because all data is either
- // immutable or completely owned by this function.
- argsAddresses[i] = (ref arg)@trusted{ return cast(const void*) &arg; }(args[i]);
-
- // Reflect formatting @safe/pure ability of each arguments to this function
- if (0) formatValue(w, args[i], spec);
- }
- }
-
// Are we already done with formats? Then just dump each parameter in turn
uint currentArg = 0;
while (spec.writeUpToNextSpec(w))
{
- if (currentArg == funs.length && !spec.indexStart)
+ if (currentArg == A.length && !spec.indexStart)
{
// leftover spec?
enforceFmt(fmt.length == 0,
@@ -568,36 +550,43 @@ uint formattedWrite(Writer, Char, A...)(Writer w, in Char[] fmt, A args)
break;
}
- // Format!
- if (spec.indexStart > 0)
+ // Format an argument
+ // This switch uses a static foreach to generate a jump table.
+ // Currently `spec.indexStart` use the special value '0' to signal
+ // we should use the current argument. An enhancement would be to
+ // always store the index.
+ size_t index = currentArg;
+ if (spec.indexStart != 0)
+ index = spec.indexStart - 1;
+ else
+ ++currentArg;
+ SWITCH: switch (index)
{
- // using positional parameters!
-
- // Make the conditional compilation of this loop explicit, to avoid "statement not reachable" warnings.
- static if (A.length > 0)
+ foreach (i, Tunused; A)
{
- foreach (i; spec.indexStart - 1 .. spec.indexEnd)
+ case i:
+ formatValue(w, args[i], spec);
+ if (currentArg < spec.indexEnd)
+ currentArg = spec.indexEnd;
+ // A little know feature of format is to format a range
+ // of arguments, e.g. `%1:3$` will format the first 3
+ // arguments. Since they have to be consecutive we can
+ // just use explicit fallthrough to cover that case.
+ if (i + 1 < spec.indexEnd)
{
- if (A.length <= i)
- throw new FormatException(
- text("Positional specifier %", i + 1, '$', spec.spec,
- " index exceeds ", A.length));
-
- if (__ctfe)
- formatNth(w, spec, i, args);
+ // You cannot goto case if the next case is the default
+ static if (i + 1 < A.length)
+ goto case;
else
- funs[i](w, argsAddresses[i], spec);
+ goto default;
}
+ else
+ break SWITCH;
}
- if (currentArg < spec.indexEnd) currentArg = spec.indexEnd;
- }
- else
- {
- if (__ctfe)
- formatNth(w, spec, currentArg, args);
- else
- funs[currentArg](w, argsAddresses[currentArg], spec);
- ++currentArg;
+ default:
+ throw new FormatException(
+ text("Positional specifier %", spec.indexStart, '$', spec.spec,
+ " index exceeds ", A.length));
}
}
return currentArg;
@@ -1174,7 +1163,7 @@ if (is(Unqual!Char == Char))
trailing = fmt;
}
- bool writeUpToNextSpec(OutputRange)(OutputRange writer)
+ bool writeUpToNextSpec(OutputRange)(ref OutputRange writer)
{
if (trailing.empty)
return false;
@@ -1735,7 +1724,7 @@ Params:
obj = The value to write.
f = The $(D FormatSpec) defining how to write the value.
*/
-void formatValue(Writer, T, Char)(Writer w, T obj, const ref FormatSpec!Char f)
+void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
if (is(BooleanTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
{
BooleanTypeOf!T val = obj;
@@ -1817,7 +1806,7 @@ Params:
obj = The value to write.
f = The $(D FormatSpec) defining how to write the value.
*/
-void formatValue(Writer, T, Char)(Writer w, T obj, const ref FormatSpec!Char f)
+void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
if (is(Unqual!T == typeof(null)) && !is(T == enum) && !hasToString!(T, Char))
{
enforceFmt(f.spec == 's',
@@ -1855,7 +1844,7 @@ Params:
obj = The value to write.
f = The $(D FormatSpec) defining how to write the value.
*/
-void formatValue(Writer, T, Char)(Writer w, T obj, const ref FormatSpec!Char f)
+void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
if (is(IntegralTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
{
alias U = IntegralTypeOf!T;
@@ -1911,7 +1900,7 @@ if (is(IntegralTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
assert(w.data == "1337");
}
-private void formatIntegral(Writer, T, Char)(Writer w, const(T) val, const ref FormatSpec!Char fs,
+private void formatIntegral(Writer, T, Char)(ref Writer w, const(T) val, const ref FormatSpec!Char fs,
uint base, ulong mask)
{
T arg = val;
@@ -1929,7 +1918,8 @@ private void formatIntegral(Writer, T, Char)(Writer w, const(T) val, const ref F
formatUnsigned(w, (cast(ulong) arg) & mask, fs, base, negative);
}
-private void formatUnsigned(Writer, T, Char)(Writer w, T arg, const ref FormatSpec!Char fs, uint base, bool negative)
+private void formatUnsigned(Writer, T, Char)
+(ref Writer w, T arg, const ref FormatSpec!Char fs, uint base, bool negative)
{
/* Write string:
* leftpad prefix1 prefix2 zerofill digits rightpad
@@ -2131,7 +2121,7 @@ Params:
obj = The value to write.
f = The $(D FormatSpec) defining how to write the value.
*/
-void formatValue(Writer, T, Char)(Writer w, T obj, const ref FormatSpec!Char f)
+void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
if (is(FloatingPointTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
{
import std.algorithm.comparison : min;
@@ -2355,7 +2345,7 @@ Params:
obj = The value to write.
f = The $(D FormatSpec) defining how to write the value.
*/
-void formatValue(Writer, T, Char)(Writer w, T obj, const ref FormatSpec!Char f)
+void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
if (is(Unqual!T : creal) && !is(T == enum) && !hasToString!(T, Char))
{
immutable creal val = obj;
@@ -2411,7 +2401,7 @@ Params:
obj = The value to write.
f = The $(D FormatSpec) defining how to write the value.
*/
-void formatValue(Writer, T, Char)(Writer w, T obj, const ref FormatSpec!Char f)
+void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
if (is(Unqual!T : ireal) && !is(T == enum) && !hasToString!(T, Char))
{
immutable ireal val = obj;
@@ -2458,7 +2448,7 @@ Params:
obj = The value to write.
f = The $(D FormatSpec) defining how to write the value.
*/
-void formatValue(Writer, T, Char)(Writer w, T obj, const ref FormatSpec!Char f)
+void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
if (is(CharTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
{
CharTypeOf!T val = obj;
@@ -2531,7 +2521,7 @@ Params:
obj = The value to write.
f = The $(D FormatSpec) defining how to write the value.
*/
-void formatValue(Writer, T, Char)(Writer w, T obj, const ref FormatSpec!Char f)
+void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
if (is(StringTypeOf!T) && !is(StaticArrayTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
{
Unqual!(StringTypeOf!T) val = obj; // for `alias this`, see bug5371
@@ -2609,7 +2599,7 @@ Params:
obj = The value to write.
f = The $(D FormatSpec) defining how to write the value.
*/
-void formatValue(Writer, T, Char)(Writer w, auto ref T obj, const ref FormatSpec!Char f)
+void formatValue(Writer, T, Char)(auto ref Writer w, auto ref T obj, const ref FormatSpec!Char f)
if (is(StaticArrayTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
{
formatValue(w, obj[], f);
@@ -2652,7 +2642,7 @@ Params:
obj = The value to write.
f = The $(D FormatSpec) defining how to write the value.
*/
-void formatValue(Writer, T, Char)(Writer w, T obj, const ref FormatSpec!Char f)
+void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
if (is(DynamicArrayTypeOf!T) && !is(StringTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
{
static if (is(const(ArrayTypeOf!T) == const(void[])))
@@ -3035,7 +3025,7 @@ if (isInputRange!T)
}
// character formatting with ecaping
-private void formatChar(Writer)(Writer w, in dchar c, in char quote)
+private void formatChar(Writer)(ref Writer w, in dchar c, in char quote)
{
import std.uni : isGraphical;
@@ -3073,7 +3063,7 @@ private void formatChar(Writer)(Writer w, in dchar c, in char quote)
// undocumented because of deprecation
// string elements are formatted like UTF-8 string literals.
-void formatElement(Writer, T, Char)(Writer w, T val, const ref FormatSpec!Char f)
+void formatElement(Writer, T, Char)(auto ref Writer w, T val, const ref FormatSpec!Char f)
if (is(StringTypeOf!T) && !is(T == enum))
{
import std.array : appender;
@@ -3158,7 +3148,7 @@ if (is(StringTypeOf!T) && !is(T == enum))
// undocumented because of deprecation
// Character elements are formatted like UTF-8 character literals.
-void formatElement(Writer, T, Char)(Writer w, T val, const ref FormatSpec!Char f)
+void formatElement(Writer, T, Char)(auto ref Writer w, T val, const ref FormatSpec!Char f)
if (is(CharTypeOf!T) && !is(T == enum))
{
if (f.spec == 's')
@@ -3184,7 +3174,7 @@ if (is(CharTypeOf!T) && !is(T == enum))
// undocumented
// Maybe T is noncopyable struct, so receive it by 'auto ref'.
-void formatElement(Writer, T, Char)(Writer w, auto ref T val, const ref FormatSpec!Char f)
+void formatElement(Writer, T, Char)(auto ref Writer w, auto ref T val, const ref FormatSpec!Char f)
if (!is(StringTypeOf!T) && !is(CharTypeOf!T) || is(T == enum))
{
formatValue(w, val, f);
@@ -3199,7 +3189,7 @@ Params:
obj = The value to write.
f = The $(D FormatSpec) defining how to write the value.
*/
-void formatValue(Writer, T, Char)(Writer w, T obj, const ref FormatSpec!Char f)
+void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
if (is(AssocArrayTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
{
AssocArrayTypeOf!T val = obj;
@@ -3444,7 +3434,7 @@ const string toString();
Otherwise, are formatted just as their type name.
*/
-void formatValue(Writer, T, Char)(Writer w, T val, const ref FormatSpec!Char f)
+void formatValue(Writer, T, Char)(auto ref Writer w, T val, const ref FormatSpec!Char f)
if (is(T == class) && !is(T == enum))
{
enforceValidFormatSpec!(T, Char)(f);
@@ -3593,7 +3583,7 @@ if (is(T == class) && !is(T == enum))
}
/// ditto
-void formatValue(Writer, T, Char)(Writer w, T val, const ref FormatSpec!Char f)
+void formatValue(Writer, T, Char)(auto ref Writer w, T val, const ref FormatSpec!Char f)
if (is(T == interface) && (hasToString!(T, Char) || !is(BuiltinTypeOf!T)) && !is(T == enum))
{
enforceValidFormatSpec!(T, Char)(f);
@@ -3673,7 +3663,7 @@ if (is(T == interface) && (hasToString!(T, Char) || !is(BuiltinTypeOf!T)) && !is
/// ditto
// Maybe T is noncopyable struct, so receive it by 'auto ref'.
-void formatValue(Writer, T, Char)(Writer w, auto ref T val, const ref FormatSpec!Char f)
+void formatValue(Writer, T, Char)(auto ref Writer w, auto ref T val, const ref FormatSpec!Char f)
if ((is(T == struct) || is(T == union)) && (hasToString!(T, Char) || !is(BuiltinTypeOf!T)) && !is(T == enum))
{
enforceValidFormatSpec!(T, Char)(f);
@@ -3808,7 +3798,7 @@ Params:
val = The value to write.
f = The $(D FormatSpec) defining how to write the value.
*/
-void formatValue(Writer, T, Char)(Writer w, T val, const ref FormatSpec!Char f)
+void formatValue(Writer, T, Char)(auto ref Writer w, T val, const ref FormatSpec!Char f)
if (is(T == enum))
{
if (f.spec == 's')
@@ -3877,7 +3867,7 @@ if (is(T == enum))
/**
Pointers are formatted as hex integers.
*/
-void formatValue(Writer, T, Char)(Writer w, T val, const ref FormatSpec!Char f)
+void formatValue(Writer, T, Char)(auto ref Writer w, T val, const ref FormatSpec!Char f)
if (isPointer!T && !is(T == enum) && !hasToString!(T, Char))
{
static if (isInputRange!T)
@@ -3982,7 +3972,7 @@ if (isPointer!T && !is(T == enum) && !hasToString!(T, Char))
/**
Delegates are formatted by 'ReturnType delegate(Parameters) FunctionAttributes'
*/
-void formatValue(Writer, T, Char)(Writer w, scope T, const ref FormatSpec!Char f)
+void formatValue(Writer, T, Char)(auto ref Writer w, scope T, const ref FormatSpec!Char f)
if (isDelegate!T)
{
formatValue(w, T.stringof, f);
@@ -4015,32 +4005,6 @@ if (isDelegate!T)
version (linux) formatTest( &func, "void delegate() @system" );
}
-/*
- Formats an object of type 'D' according to 'f' and writes it to
- 'w'. The pointer 'arg' is assumed to point to an object of type
- 'D'. The untyped signature is for the sake of taking this function's
- address.
- */
-private void formatGeneric(Writer, D, Char)(Writer w, const(void)* arg, const ref FormatSpec!Char f)
-{
- formatValue(w, *cast(D*) arg, f);
-}
-
-private void formatNth(Writer, Char, A...)(Writer w, const ref FormatSpec!Char f, size_t index, A args)
-{
- switch (index)
- {
- foreach (n, _; A)
- {
- case n:
- formatValue(w, args[n], f);
- return;
- }
- default:
- assert(0, "n = "~cast(char)(index + '0'));
- }
-}
-
@safe pure unittest
{
int[] a = [ 1, 3, 2 ];
@@ -6055,3 +6019,10 @@ char[] sformat(Char, Args...)(char[] buf, in Char[] fmt, Args args)
tmp = format("%19,?f", '.', -1234567.891011);
assert(tmp == " -1.234.567.891.011", "'" ~ tmp ~ "'");
}
+
+// Test for multiple indexes
+@safe unittest
+{
+ auto tmp = format("%2:5$s", 1, 2, 3, 4, 5);
+ assert(tmp == "2345", tmp);
+}
diff --git a/std/functional.d b/std/functional.d
index 128c89c5807..2c4104c145f 100644
--- a/std/functional.d
+++ b/std/functional.d
@@ -1169,6 +1169,83 @@ template memoize(alias fun, uint maxSize)
assert(func(int.init) == 1);
}
+// 16079: memoize should work with arrays
+@safe unittest
+{
+ int executed = 0;
+ T median(T)(const T[] nums) {
+ import std.algorithm.sorting : sort;
+ executed++;
+ auto arr = nums.dup;
+ arr.sort();
+ if (arr.length % 2)
+ return arr[$ / 2];
+ else
+ return (arr[$ / 2 - 1]
+ + arr[$ / 2]) / 2;
+ }
+
+ alias fastMedian = memoize!(median!int);
+
+ assert(fastMedian([7, 5, 3]) == 5);
+ assert(fastMedian([7, 5, 3]) == 5);
+
+ assert(executed == 1);
+}
+
+// 16079: memoize should work with structs
+@safe unittest
+{
+ int executed = 0;
+ T pickFirst(T)(T first)
+ {
+ executed++;
+ return first;
+ }
+
+ struct Foo { int k; }
+ Foo A = Foo(3);
+
+ alias first = memoize!(pickFirst!Foo);
+ assert(first(Foo(3)) == A);
+ assert(first(Foo(3)) == A);
+ assert(executed == 1);
+}
+
+// 16079: memoize should work with classes
+@safe unittest
+{
+ int executed = 0;
+ T pickFirst(T)(T first)
+ {
+ executed++;
+ return first;
+ }
+
+ class Bar
+ {
+ size_t k;
+ this(size_t k)
+ {
+ this.k = k;
+ }
+ override size_t toHash()
+ {
+ return k;
+ }
+ override bool opEquals(Object o)
+ {
+ auto b = cast(Bar) o;
+ return b && k == b.k;
+ }
+ }
+
+ alias firstClass = memoize!(pickFirst!Bar);
+ assert(firstClass(new Bar(3)).k == 3);
+ assert(firstClass(new Bar(3)).k == 3);
+ assert(executed == 1);
+}
+
private struct DelegateFaker(F)
{
import std.typecons : FuncInfo, MemberFunctionGenerator;
diff --git a/std/getopt.d b/std/getopt.d
index 0f74064f156..5beddcc23b4 100644
--- a/std/getopt.d
+++ b/std/getopt.d
@@ -947,6 +947,8 @@ private bool handleOption(R)(string option, R receiver, ref string[] args,
static Tuple!(K, V) getter(string input)
{
auto j = indexOf(input, assignChar);
+ enforce!GetOptException(j != -1, "Could not find '"
+ ~ to!string(assignChar) ~ "' in argument '" ~ input ~ "'.");
auto key = input[0 .. j];
auto value = input[j + 1 .. $];
return tuple(to!K(key), to!V(value));
@@ -971,6 +973,26 @@ private bool handleOption(R)(string option, R receiver, ref string[] args,
return ret;
}
+// 17574
+@system unittest
+{
+ import std.algorithm.searching : startsWith;
+
+ try
+ {
+ string[string] mapping;
+ immutable as = arraySep;
+ arraySep = ",";
+ scope (exit)
+ arraySep = as;
+ string[] args = ["testProgram", "-m", "a=b,c=\"d,e,f\""];
+ args.getopt("m", &mapping);
+ assert(false, "Exception not thrown");
+ }
+ catch (GetOptException goe)
+ assert(goe.msg.startsWith("Could not find"));
+}
+
// 5316 - arrays with arraySep
@system unittest
{
diff --git a/std/internal/math/biguintcore.d b/std/internal/math/biguintcore.d
index 6391a70a9f2..9048336e9c2 100644
--- a/std/internal/math/biguintcore.d
+++ b/std/internal/math/biguintcore.d
@@ -46,10 +46,10 @@ alias multibyteAdd = multibyteAddSub!('+');
alias multibyteSub = multibyteAddSub!('-');
-private import core.cpuid;
+import core.cpuid;
public import std.ascii : LetterCase;
-private import std.range.primitives;
-private import std.traits;
+import std.range.primitives;
+import std.traits;
shared static this()
{
@@ -75,8 +75,8 @@ else static if (BigDigit.sizeof == long.sizeof)
}
else static assert(0, "Unsupported BigDigit size");
-private import std.exception : assumeUnique;
-private import std.traits : isIntegral;
+import std.exception : assumeUnique;
+import std.traits : isIntegral;
enum BigDigitBits = BigDigit.sizeof*8;
template maxBigDigits(T)
if (isIntegral!T)
diff --git a/std/internal/scopebuffer.d b/std/internal/scopebuffer.d
index 8d7548a6eb2..70a7c8d1202 100644
--- a/std/internal/scopebuffer.d
+++ b/std/internal/scopebuffer.d
@@ -10,8 +10,8 @@ module std.internal.scopebuffer;
//debug=ScopeBuffer;
-private import core.stdc.stdlib : realloc;
-private import std.traits;
+import core.stdc.stdlib : realloc;
+import std.traits;
/**************************************
* ScopeBuffer encapsulates using a local array as a temporary buffer.
diff --git a/std/internal/windows/advapi32.d b/std/internal/windows/advapi32.d
index d5d04fd1b9b..386c0a9b1d4 100644
--- a/std/internal/windows/advapi32.d
+++ b/std/internal/windows/advapi32.d
@@ -12,7 +12,7 @@ module std.internal.windows.advapi32;
version(Windows):
-private import core.sys.windows.windows;
+import core.sys.windows.windows;
pragma(lib, "advapi32.lib");
diff --git a/std/json.d b/std/json.d
index 0c436de9743..fd6cf41968c 100644
--- a/std/json.d
+++ b/std/json.d
@@ -75,7 +75,8 @@ enum JSONOptions
{
none, /// standard parsing
specialFloatLiterals = 0x1, /// encode NaN and Inf float values as strings
- escapeNonAsciiChars = 0x2 /// encode non ascii characters with an unicode escape sequence
+ escapeNonAsciiChars = 0x2, /// encode non ascii characters with an unicode escape sequence
+ doNotEscapeSlashes = 0x4, /// do not escape slashes ('/')
}
/**
@@ -1153,7 +1154,13 @@ string toJSON(const ref JSONValue root, in bool pretty = false, in JSONOptions o
{
case '"': json.put("\\\""); break;
case '\\': json.put("\\\\"); break;
- case '/': json.put("\\/"); break;
+
+ case '/':
+ if (!(options & JSONOptions.doNotEscapeSlashes))
+ json.put('\\');
+ json.put('/');
+ break;
+
case '\b': json.put("\\b"); break;
case '\f': json.put("\\f"); break;
case '\n': json.put("\\n"); break;
@@ -1842,3 +1849,11 @@ pure nothrow @safe unittest // issue 15884
assert(parseJSON("\"\xFF\"".byChar).str == "\xFF");
assert(parseJSON("\"\U0001D11E\"".byChar).str == "\U0001D11E");
}
+
+@safe unittest // JSONOptions.doNotEscapeSlashes (issue 17587)
+{
+ assert(parseJSON(`"/"`).toString == `"\/"`);
+ assert(parseJSON(`"\/"`).toString == `"\/"`);
+ assert(parseJSON(`"/"`).toString(JSONOptions.doNotEscapeSlashes) == `"/"`);
+ assert(parseJSON(`"\/"`).toString(JSONOptions.doNotEscapeSlashes) == `"/"`);
+}
diff --git a/std/math.d b/std/math.d
index edd5bbe243d..a61cf9e6fba 100644
--- a/std/math.d
+++ b/std/math.d
@@ -787,10 +787,9 @@ real tan(real x) @trusted pure nothrow @nogc
jc trigerr ; // x is NAN, infinity, or empty
// 387's can handle subnormals
SC18: fptan ;
- fstp ST(0) ; // dump X, which is always 1
fstsw AX ;
sahf ;
- jnp Lret ; // C2 = 1 (x is out of range)
+ jnp Clear1 ; // C2 = 1 (x is out of range)
// Do argument reduction to bring x into range
fldpi ;
@@ -808,6 +807,10 @@ trigerr:
}
return real.nan;
+Clear1: asm pure nothrow @nogc{
+ fstp ST(0) ; // dump X, which is always 1
+ }
+
Lret: {}
}
else version(D_InlineAsm_X86_64)
@@ -834,10 +837,9 @@ Lret: {}
jnz trigerr ; // x is NAN, infinity, or empty
// 387's can handle subnormals
SC18: fptan ;
- fstp ST(0) ; // dump X, which is always 1
fstsw AX ;
test AH,4 ;
- jz Lret ; // C2 = 1 (x is out of range)
+ jz Clear1 ; // C2 = 1 (x is out of range)
// Do argument reduction to bring x into range
fldpi ;
@@ -856,6 +858,10 @@ trigerr:
}
return real.nan;
+Clear1: asm pure nothrow @nogc{
+ fstp ST(0) ; // dump X, which is always 1
+ }
+
Lret: {}
}
else
@@ -4914,12 +4920,22 @@ assert(rint(1.1) == 1);
*/
struct FloatingPointControl
{
- alias RoundingMode = uint;
+ alias RoundingMode = uint; ///
- /** IEEE rounding modes.
- * The default mode is roundToNearest.
- */
- version(ARM)
+ version(StdDdoc)
+ {
+ enum : RoundingMode
+ {
+ /** IEEE rounding modes.
+ * The default mode is roundToNearest.
+ */
+ roundToNearest,
+ roundDown, /// ditto
+ roundUp, /// ditto
+ roundToZero /// ditto
+ }
+ }
+ else version(ARM)
{
enum : RoundingMode
{
@@ -4950,10 +4966,40 @@ struct FloatingPointControl
}
}
- /** IEEE hardware exceptions.
- * By default, all exceptions are masked (disabled).
- */
- version(ARM)
+ //// Change the floating-point hardware rounding mode
+ @property void rounding(RoundingMode newMode) @nogc
+ {
+ initialize();
+ setControlState((getControlState() & ~ROUNDING_MASK) | (newMode & ROUNDING_MASK));
+ }
+
+ /// Returns: the currently active rounding mode
+ @property static RoundingMode rounding() @nogc
+ {
+ return cast(RoundingMode)(getControlState() & ROUNDING_MASK);
+ }
+
+ version(StdDdoc)
+ {
+ enum : uint
+ {
+ /** IEEE hardware exceptions.
+ * By default, all exceptions are masked (disabled).
+ *
+ * severeExceptions = The overflow, division by zero, and invalid
+ * exceptions.
+ */
+ subnormalException,
+ inexactException, /// ditto
+ underflowException, /// ditto
+ overflowException, /// ditto
+ divByZeroException, /// ditto
+ invalidException, /// ditto
+ severeExceptions, /// ditto
+ allExceptions, /// ditto
+ }
+ }
+ else version(ARM)
{
enum : uint
{
@@ -4963,7 +5009,6 @@ struct FloatingPointControl
overflowException = 0x0400,
divByZeroException = 0x0200,
invalidException = 0x0100,
- /// Severe = The overflow, division by zero, and invalid exceptions.
severeExceptions = overflowException | divByZeroException
| invalidException,
allExceptions = severeExceptions | underflowException
@@ -4979,7 +5024,6 @@ struct FloatingPointControl
underflowException = 0x0020,
overflowException = 0x0040,
invalidException = 0x0080,
- /// Severe = The overflow, division by zero, and invalid exceptions.
severeExceptions = overflowException | divByZeroException
| invalidException,
allExceptions = severeExceptions | underflowException
@@ -4996,7 +5040,6 @@ struct FloatingPointControl
divByZeroException = 0x04,
subnormalException = 0x02,
invalidException = 0x01,
- /// Severe = The overflow, division by zero, and invalid exceptions.
severeExceptions = overflowException | divByZeroException
| invalidException,
allExceptions = severeExceptions | underflowException
@@ -5072,13 +5115,6 @@ public:
setControlState(getControlState() & ~(exceptions & EXCEPTION_MASK));
}
- //// Change the floating-point hardware rounding mode
- @property void rounding(RoundingMode newMode) @nogc
- {
- initialize();
- setControlState((getControlState() & ~ROUNDING_MASK) | (newMode & ROUNDING_MASK));
- }
-
/// Returns: the exceptions which are currently enabled (unmasked)
@property static uint enabledExceptions() @nogc
{
@@ -5089,12 +5125,6 @@ public:
return (getControlState() & EXCEPTION_MASK);
}
- /// Returns: the currently active rounding mode
- @property static RoundingMode rounding() @nogc
- {
- return cast(RoundingMode)(getControlState() & ROUNDING_MASK);
- }
-
/// Clear all pending exceptions, then restore the original exception state and rounding mode.
~this() @nogc
{
@@ -5286,9 +5316,9 @@ private:
/*********************************
* Determines if $(D_PARAM x) is NaN.
- * params:
+ * Params:
* x = a floating point number.
- * returns:
+ * Returns:
* $(D true) if $(D_PARAM x) is Nan.
*/
bool isNaN(X)(X x) @nogc @trusted pure nothrow
@@ -5374,9 +5404,9 @@ if (isFloatingPoint!(X))
/*********************************
* Determines if $(D_PARAM x) is finite.
- * params:
+ * Params:
* x = a floating point number.
- * returns:
+ * Returns:
* $(D true) if $(D_PARAM x) is finite.
*/
bool isFinite(X)(X x) @trusted pure nothrow @nogc
@@ -5417,9 +5447,9 @@ bool isFinite(X)(X x) @trusted pure nothrow @nogc
*
* A normalized number must not be zero, subnormal, infinite nor $(NAN).
*
- * params:
+ * Params:
* x = a floating point number.
- * returns:
+ * Returns:
* $(D true) if $(D_PARAM x) is normalized.
*/
@@ -5467,9 +5497,9 @@ bool isNormal(X)(X x) @trusted pure nothrow @nogc
* Subnormals (also known as "denormal number"), have a 0 exponent
* and a 0 most significant mantissa bit.
*
- * params:
+ * Params:
* x = a floating point number.
- * returns:
+ * Returns:
* $(D true) if $(D_PARAM x) is a denormal number.
*/
bool isSubnormal(X)(X x) @trusted pure nothrow @nogc
@@ -5529,9 +5559,9 @@ bool isSubnormal(X)(X x) @trusted pure nothrow @nogc
/*********************************
* Determines if $(D_PARAM x) is $(PLUSMN)$(INFIN).
- * params:
+ * Params:
* x = a floating point number.
- * returns:
+ * Returns:
* $(D true) if $(D_PARAM x) is $(PLUSMN)$(INFIN).
*/
bool isInfinity(X)(X x) @nogc @trusted pure nothrow
@@ -5874,7 +5904,7 @@ real NaN(ulong payload) @trusted pure nothrow @nogc
}
}
-@safe pure nothrow @nogc unittest
+@system pure nothrow @nogc unittest // not @safe because taking address of local.
{
static if (floatTraits!(real).realFormat == RealFormat.ieeeDouble)
{
@@ -7482,6 +7512,12 @@ bool approxEqual(T, U)(T lhs, U rhs)
real r = tan(-2.0L);
assert(fabs(r - 2.18504f) < .00001);
+
+ // Verify correct behavior for large inputs
+ assert(!isNaN(tan(0x1p63)));
+ assert(!isNaN(tan(0x1p300L)));
+ assert(!isNaN(tan(-0x1p63)));
+ assert(!isNaN(tan(-0x1p300L)));
}
@safe pure nothrow unittest
diff --git a/std/mathspecial.d b/std/mathspecial.d
index 4b85189e94c..e35c74cc5aa 100644
--- a/std/mathspecial.d
+++ b/std/mathspecial.d
@@ -53,8 +53,8 @@
* Source: $(PHOBOSSRC std/_mathspecial.d)
*/
module std.mathspecial;
-private import std.internal.math.errorfunction;
-private import std.internal.math.gammafunction;
+import std.internal.math.errorfunction;
+import std.internal.math.gammafunction;
public import std.math;
/* ***********************************************
diff --git a/std/meta.d b/std/meta.d
index 77ed211d6e0..308e50f041f 100644
--- a/std/meta.d
+++ b/std/meta.d
@@ -26,6 +26,7 @@
* $(LREF EraseAll)
* $(LREF Filter)
* $(LREF NoDuplicates)
+ * $(LREF Stride)
* ))
* $(TR $(TD Alias sequence type hierarchy) $(TD
* $(LREF DerivedToFront)
@@ -74,6 +75,8 @@ module std.meta;
/**
* Creates a sequence of zero or more aliases. This is most commonly
* used as template parameters or arguments.
+ *
+ * In previous versions of Phobos, this was known as `TypeTuple`.
*/
template AliasSeq(TList...)
{
@@ -342,7 +345,7 @@ if (args.length >= 1)
}
/**
- * Returns a typetuple created from TList with the first occurrence,
+ * Returns an `AliasSeq` created from TList with the first occurrence,
* if any, of T removed.
*/
template Erase(T, TList...)
@@ -400,7 +403,7 @@ if (args.length >= 1)
/**
- * Returns a typetuple created from TList with the all occurrences,
+ * Returns an `AliasSeq` created from TList with the all occurrences,
* if any, of T removed.
*/
template EraseAll(T, TList...)
@@ -463,7 +466,7 @@ if (args.length >= 1)
/**
- * Returns a typetuple created from TList with the all duplicate
+ * Returns an `AliasSeq` created from TList with the all duplicate
* types removed.
*/
template NoDuplicates(TList...)
@@ -522,7 +525,7 @@ template NoDuplicates(TList...)
/**
- * Returns a typetuple created from TList with the first occurrence
+ * Returns an `AliasSeq` created from TList with the first occurrence
* of type T, if found, replaced with type U.
*/
template Replace(T, U, TList...)
@@ -602,7 +605,7 @@ if (args.length >= 2)
}
/**
- * Returns a typetuple created from TList with all occurrences
+ * Returns an `AliasSeq` created from TList with all occurrences
* of type T, if found, replaced with type U.
*/
template ReplaceAll(T, U, TList...)
@@ -682,7 +685,7 @@ if (args.length >= 2)
}
/**
- * Returns a typetuple created from TList with the order reversed.
+ * Returns an `AliasSeq` created from TList with the order reversed.
*/
template Reverse(TList...)
{
@@ -735,7 +738,7 @@ template MostDerived(T, TList...)
}
/**
- * Returns the typetuple TList with the types sorted so that the most
+ * Returns the `AliasSeq` TList with the types sorted so that the most
* derived types come first.
*/
template DerivedToFront(TList...)
@@ -1494,6 +1497,60 @@ template staticIsSorted(alias cmp, Seq...)
static assert(!staticIsSorted!(Comp, uint, short, ubyte, long, ulong));
}
+/**
+Selects a subset of the argument list by stepping with fixed `stepSize` over the list.
+A negative `stepSize` starts iteration with the last list element.
+
+Params:
+ stepSize = Number of elements to increment on each iteration. Can't be `0`.
+ Args = Template arguments
+
+Returns: A template argument list filtered by the selected stride.
+*/
+template Stride(int stepSize, Args...)
+if (stepSize != 0)
+{
+ static if (Args.length == 0)
+ {
+ alias Stride = AliasSeq!();
+ }
+ else static if (stepSize > 0)
+ {
+ static if (stepSize >= Args.length)
+ alias Stride = AliasSeq!(Args[0]);
+ else
+ alias Stride = AliasSeq!(Args[0], Stride!(stepSize, Args[stepSize .. $]));
+ }
+ else
+ {
+ static if (-stepSize >= Args.length)
+ alias Stride = AliasSeq!(Args[$ - 1]);
+ else
+ alias Stride = AliasSeq!(Args[$ - 1], Stride!(stepSize, Args[0 .. $ + stepSize]));
+ }
+}
+
+///
+@safe unittest
+{
+ static assert(is(Stride!(1, short, int, long) == AliasSeq!(short, int, long)));
+ static assert(is(Stride!(2, short, int, long) == AliasSeq!(short, long)));
+ static assert(is(Stride!(-1, short, int, long) == AliasSeq!(long, int, short)));
+ static assert(is(Stride!(-2, short, int, long) == AliasSeq!(long, short)));
+
+ alias attribs = AliasSeq!(short, int, long, ushort, uint, ulong);
+ static assert(is(Stride!(3, attribs) == AliasSeq!(short, ushort)));
+ static assert(is(Stride!(3, attribs[1 .. $]) == AliasSeq!(int, uint)));
+ static assert(is(Stride!(-3, attribs) == AliasSeq!(ulong, long)));
+}
+
+@safe unittest
+{
+ static assert(Pack!(Stride!(5, int)).equals!(int));
+ static assert(Pack!(Stride!(-5, int)).equals!(int));
+ static assert(!__traits(compiles, Stride!(0, int)));
+}
+
// : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : //
private:
diff --git a/std/mmfile.d b/std/mmfile.d
index 627abb37629..a8fdeb39102 100644
--- a/std/mmfile.d
+++ b/std/mmfile.d
@@ -17,13 +17,13 @@
*/
module std.mmfile;
-private import core.stdc.errno;
-private import core.stdc.stdio;
-private import core.stdc.stdlib;
+import core.stdc.errno;
+import core.stdc.stdio;
+import core.stdc.stdlib;
import std.conv, std.exception, std.stdio;
-private import std.file;
-private import std.path;
-private import std.string;
+import std.file;
+import std.path;
+import std.string;
import std.internal.cstring;
@@ -31,16 +31,16 @@ import std.internal.cstring;
version (Windows)
{
- private import core.sys.windows.windows;
- private import std.utf;
- private import std.windows.syserror;
+ import core.sys.windows.windows;
+ import std.utf;
+ import std.windows.syserror;
}
else version (Posix)
{
- private import core.sys.posix.fcntl;
- private import core.sys.posix.sys.mman;
- private import core.sys.posix.sys.stat;
- private import core.sys.posix.unistd;
+ import core.sys.posix.fcntl;
+ import core.sys.posix.sys.mman;
+ import core.sys.posix.sys.stat;
+ import core.sys.posix.unistd;
}
else
{
diff --git a/std/net/curl.d b/std/net/curl.d
index e8212735363..d0d36a44705 100644
--- a/std/net/curl.d
+++ b/std/net/curl.d
@@ -1047,9 +1047,8 @@ private auto _basicHTTP(T)(const(char)[] url, const(void)[] sendData, HTTP clien
};
client.onReceiveStatusLine = (HTTP.StatusLine l) { statusLine = l; };
client.perform();
- enforce!CurlException(statusLine.code / 100 == 2,
- format("HTTP request returned status code %d (%s)",
- statusLine.code, statusLine.reason));
+ enforce(statusLine.code / 100 == 2, new HTTPStatusException(statusLine.code,
+ format("HTTP request returned status code %d (%s)", statusLine.code, statusLine.reason)));
return _decodeContent!T(content.data, client.p.charset);
}
@@ -1063,8 +1062,9 @@ private auto _basicHTTP(T)(const(char)[] url, const(void)[] sendData, HTTP clien
assert(req.hdrs.canFind("GET /path"));
s.send(httpNotFound());
});
- auto e = collectException!CurlException(get(testServer.addr ~ "/path"));
+ auto e = collectException!HTTPStatusException(get(testServer.addr ~ "/path"));
assert(e.msg == "HTTP request returned status code 404 (Not Found)");
+ assert(e.status == 404);
}
// Bugzilla 14760 - content length must be reset after post
@@ -1471,7 +1471,7 @@ private mixin template WorkerThreadProtocol(Unit, alias units)
*/
bool wait(Duration d)
{
- import std.datetime : StopWatch;
+ import std.datetime.stopwatch : StopWatch;
if (state == State.gotUnits)
return true;
@@ -2047,6 +2047,17 @@ private mixin template Protocol()
p.curl.set(CurlOption.localport, cast(long) port);
}
+ /**
+ Set the no proxy flag for the specified host names.
+ Params:
+ test = a list of comma host names that do not require
+ proxy to get reached
+ */
+ void setNoProxy(string hosts)
+ {
+ p.curl.set(CurlOption.noproxy, hosts);
+ }
+
/**
Set the local outgoing port range to use.
This can be used together with the localPort property.
@@ -2122,6 +2133,9 @@ private mixin template Protocol()
http.onReceive = (ubyte[] data) { return data.length; };
http.setAuthentication("user", "pass");
http.perform();
+
+ // Bugzilla 17540
+ http.setNoProxy("www.example.com");
}
/**
@@ -2377,14 +2391,14 @@ private bool decodeLineInto(Terminator, Char = char)(ref const(ubyte)[] basesrc,
* http.perform();
* ---
*
- * See_Also: $(HTTP www.ietf.org/rfc/rfc2616.txt, RFC2616)
+ * See_Also: $(_HTTP www.ietf.org/rfc/rfc2616.txt, RFC2616)
*
*/
struct HTTP
{
mixin Protocol;
- import std.datetime : SysTime;
+ import std.datetime.systime : SysTime;
/// Authentication method equal to $(REF CurlAuth, etc,c,curl)
alias AuthMethod = CurlAuth;
@@ -2933,7 +2947,7 @@ struct HTTP
*
* code = http.getTiming(CurlInfo.namelookup_time, val);
* assert(code == CurlError.ok);
- *---
+ * ---
*/
CurlCode getTiming(CurlInfo timing, ref double val)
{
@@ -3648,7 +3662,7 @@ struct FTP
*
* code = http.getTiming(CurlInfo.namelookup_time, val);
* assert(code == CurlError.ok);
- *---
+ * ---
*/
CurlCode getTiming(CurlInfo timing, ref double val)
{
@@ -4058,6 +4072,33 @@ class CurlTimeoutException : CurlException
}
}
+/++
+ Exception thrown on HTTP request failures, e.g. 404 Not Found.
++/
+class HTTPStatusException : CurlException
+{
+ /++
+ Params:
+ status = The HTTP status code.
+ msg = The message for the exception.
+ file = The file where the exception occurred.
+ line = The line number where the exception occurred.
+ next = The previous exception in the chain of exceptions, if any.
+ +/
+ @safe pure nothrow
+ this(int status,
+ string msg,
+ string file = __FILE__,
+ size_t line = __LINE__,
+ Throwable next = null)
+ {
+ super(msg, file, line, next);
+ this.status = status;
+ }
+
+ immutable int status; /// The HTTP status code
+}
+
/// Equal to $(REF CURLcode, etc,c,curl)
alias CurlCode = CURLcode;
@@ -4158,29 +4199,30 @@ private struct CurlAPI
enforce!CurlException(!_api.global_init(CurlGlobal.all),
"Failed to initialize libcurl");
- return handle;
- }
-
- shared static ~this()
- {
- if (_handle is null) return;
-
- _api.global_cleanup();
- version (Posix)
- {
- import core.sys.posix.dlfcn : dlclose;
- dlclose(_handle);
- }
- else version (Windows)
+ static extern(C) void cleanup()
{
- import core.sys.windows.windows : FreeLibrary;
- FreeLibrary(_handle);
+ if (_handle is null) return;
+ _api.global_cleanup();
+ version (Posix)
+ {
+ import core.sys.posix.dlfcn : dlclose;
+ dlclose(_handle);
+ }
+ else version (Windows)
+ {
+ import core.sys.windows.windows : FreeLibrary;
+ FreeLibrary(_handle);
+ }
+ else
+ static assert(0, "unimplemented");
+ _api = API.init;
+ _handle = null;
}
- else
- static assert(0, "unimplemented");
- _api = API.init;
- _handle = null;
+ import core.stdc.stdlib : atexit;
+ atexit(&cleanup);
+
+ return handle;
}
}
diff --git a/std/net/isemail.d b/std/net/isemail.d
index d37093db375..cd6aa419701 100644
--- a/std/net/isemail.d
+++ b/std/net/isemail.d
@@ -405,7 +405,7 @@ if (isSomeChar!(Char))
index = addressLiteral.lastIndexOf(matchesIp.front);
if (index != 0)
- addressLiteral = addressLiteral.substr(0, index) ~ "0:0";
+ addressLiteral = addressLiteral[0 .. index] ~ "0:0";
}
if (index == 0)
@@ -416,7 +416,7 @@ if (isSomeChar!(Char))
else
{
- auto ipV6 = addressLiteral.substr(5);
+ auto ipV6 = addressLiteral[5 .. $];
matchesIp = ipV6.split(Token.colon);
immutable groupCount = matchesIp.length;
index = ipV6.indexOf(Token.doubleColon);
@@ -445,10 +445,10 @@ if (isSomeChar!(Char))
}
}
- if (ipV6.substr(0, 1) == Token.colon && ipV6.substr(1, 1) != Token.colon)
+ if (ipV6[0 .. 1] == Token.colon && ipV6[1 .. 2] != Token.colon)
returnStatus ~= EmailStatusCode.rfc5322IpV6ColonStart;
- else if (ipV6.substr(-1) == Token.colon && ipV6.substr(-2, -1) != Token.colon)
+ else if (ipV6[$ - 1 .. $] == Token.colon && ipV6[$ - 2 .. $ - 1] != Token.colon)
returnStatus ~= EmailStatusCode.rfc5322IpV6ColonEnd;
else if (!matchesIp
@@ -1768,98 +1768,6 @@ enum AsciiToken
delete_ = 127
}
-/*
- * Returns the portion of string specified by the $(D_PARAM start) and
- * $(D_PARAM length) parameters.
- *
- * Params:
- * str = the input string. Must be one character or longer.
- * start = if $(D_PARAM start) is non-negative, the returned string will start at the
- * $(D_PARAM start)'th position in $(D_PARAM str), counting from zero.
- * For instance, in the string "abcdef", the character at position 0 is 'a',
- * the character at position 2 is 'c', and so forth.
- *
- * If $(D_PARAM start) is negative, the returned string will start at the
- * $(D_PARAM start)'th character from the end of $(D_PARAM str).
- *
- * If $(D_PARAM str) is less than or equal to $(D_PARAM start) characters long,
- * $(D_KEYWORD true) will be returned.
- *
- * length = if $(D_PARAM length) is given and is positive, the string returned will
- * contain at most $(D_PARAM length) characters beginning from $(D_PARAM start)
- * (depending on the length of string).
- *
- * If $(D_PARAM length) is given and is negative, then that many characters
- * will be omitted from the end of string (after the start position has been
- * calculated when a $(D_PARAM start) is negative). If $(D_PARAM start)
- * denotes the position of this truncation or beyond, $(D_KEYWORD false)
- * will be returned.
- *
- * If $(D_PARAM length) is given and is 0, an empty string will be returned.
- *
- * If $(D_PARAM length) is omitted, the substring starting from $(D_PARAM start)
- * until the end of the string will be returned.
- *
- * Returns: the extracted part of string, or an empty string.
- */
-T[] substr (T) (T[] str, ptrdiff_t start = 0, ptrdiff_t length = ptrdiff_t.min)
-{
- ptrdiff_t end = length;
-
- if (start < 0)
- {
- start = str.length + start;
-
- if (end < 0)
- {
- if (end == ptrdiff_t.min)
- end = 0;
-
- end = str.length + end;
- }
-
- else
- end = start + end;
- }
-
- else
- {
- if (end == ptrdiff_t.min)
- end = str.length;
-
- if (end < 0)
- end = str.length + end;
-
- else
- end = start + end;
- }
-
- if (start > end)
- end = start;
-
- if (end > str.length)
- end = str.length;
-
- return str[start .. end];
-}
-
-@safe unittest
-{
- assert("abcdef".substr(-1) == "f");
- assert("abcdef".substr(-2) == "ef");
- assert("abcdef".substr(-3, 1) == "d");
-}
-
-@safe unittest
-{
- assert("abcdef".substr(0, -1) == "abcde");
- assert("abcdef".substr(2, -1) == "cde");
- assert("abcdef".substr(4, -4) == []);
- assert("abcdef".substr(-3, -1) == "de");
- assert("abcdef".substr(1, 1) == "b");
- assert("abcdef".substr(-1, -1) == []);
-}
-
/*
* Compare the two given strings lexicographically. An upper limit of the number of
* characters, that will be used in the comparison, can be specified. Supports both
diff --git a/std/outbuffer.d b/std/outbuffer.d
index 913a29881fd..1d594982cc1 100644
--- a/std/outbuffer.d
+++ b/std/outbuffer.d
@@ -202,6 +202,12 @@ class OutBuffer
fill0(alignsize - nbytes);
}
+ /// Clear the data in the buffer
+ void clear()
+ {
+ offset = 0;
+ }
+
/****************************************
* Optimize common special case alignSize(2)
*/
@@ -368,6 +374,7 @@ class OutBuffer
}
}
+///
@safe unittest
{
import std.string : cmp;
@@ -375,11 +382,16 @@ class OutBuffer
OutBuffer buf = new OutBuffer();
assert(buf.offset == 0);
- buf.write("hello"[]);
+ buf.write("hello");
buf.write(cast(byte) 0x20);
- buf.write("world"[]);
+ buf.write("world");
buf.printf(" %d", 62665);
assert(cmp(buf.toString(), "hello world 62665") == 0);
+
+ buf.clear();
+ assert(cmp(buf.toString(), "") == 0);
+ buf.write("New data");
+ assert(cmp(buf.toString(),"New data") == 0);
}
@safe unittest
diff --git a/std/parallelism.d b/std/parallelism.d
index 51d322172d0..b2775f760e2 100644
--- a/std/parallelism.d
+++ b/std/parallelism.d
@@ -3445,7 +3445,7 @@ private void submitAndExecute(
}
catch (Throwable e)
{
- tasks[0].exception = e;
+ tasks[0].exception = e; // nocoverage
}
tasks[0].taskStatus = TaskStatus.done;
@@ -3500,7 +3500,7 @@ int doSizeZeroCase(R, Delegate)(ref ParallelForeach!R p, Delegate dg)
{
res = dg(elem);
}
- if (res) foreachErr();
+ if (res) break;
index++;
}
}
@@ -3516,10 +3516,11 @@ int doSizeZeroCase(R, Delegate)(ref ParallelForeach!R p, Delegate dg)
{
res = dg(elem);
}
- if (res) foreachErr();
+ if (res) break;
index++;
}
}
+ if (res) foreachErr;
return res;
}
}
@@ -3787,9 +3788,9 @@ private void addToChain(
{
if (firstException)
{
- assert(lastException);
- lastException.next = e;
- lastException = findLastException(e);
+ assert(lastException); // nocoverage
+ lastException.next = e; // nocoverage
+ lastException = findLastException(e); // nocoverage
}
else
{
@@ -4624,3 +4625,12 @@ version(unittest)
arr.parallel.each!"a++";
})));
}
+
+// https://issues.dlang.org/show_bug.cgi?id=17539
+@system unittest
+{
+ import std.random : rndGen;
+ // ensure compilation
+ try foreach (rnd; rndGen.parallel) break;
+ catch (ParallelForeachError e) {}
+}
diff --git a/std/process.d b/std/process.d
index 93627da1e13..74601a02ae3 100644
--- a/std/process.d
+++ b/std/process.d
@@ -2168,7 +2168,6 @@ struct ProcessPipes
/// The $(LREF Pid) of the child process.
@property Pid pid() @safe nothrow
{
- assert(_pid !is null);
return _pid;
}
@@ -2433,19 +2432,8 @@ class ProcessException : Exception
string file = __FILE__,
size_t line = __LINE__)
{
- import std.conv : to;
- version (CRuntime_Glibc)
- {
- import core.stdc.string : strerror_r;
- char[1024] buf;
- auto errnoMsg = to!string(
- core.stdc.string.strerror_r(error, buf.ptr, buf.length));
- }
- else
- {
- import core.stdc.string : strerror;
- auto errnoMsg = to!string(strerror(error));
- }
+ import std.exception : errnoString;
+ auto errnoMsg = errnoString(error);
auto msg = customMsg.empty ? errnoMsg
: customMsg ~ " (" ~ errnoMsg ~ ')';
return new ProcessException(msg, file, line);
diff --git a/std/random.d b/std/random.d
index 7fcda195312..d7f8a563c68 100644
--- a/std/random.d
+++ b/std/random.d
@@ -23,21 +23,6 @@ useful. The standard library provides an alias $(D_PARAM Random) for
whichever generator it considers the most fit for the target
environment.
-Example:
-
-----
-// Generate a uniformly-distributed integer in the range [0, 14]
-auto i = uniform(0, 15);
-
-// Generate a uniformly-distributed real in the range [0, 100)
-// using a specific random generator
-Random gen;
-auto r = uniform(0.0L, 100.0L, gen);
-
-// Generate a 32-bit random number
-auto l = uniform!uint();
-----
-
In addition to random number generators, this module features
distributions, which skew a generator's output statistical
distribution in various ways. So far the uniform distribution for
@@ -70,6 +55,26 @@ module std.random;
import std.range.primitives;
import std.traits;
+///
+@safe unittest
+{
+ // seed a random generator with a constant
+ auto rnd = Random(42);
+
+ // Generate a uniformly-distributed integer in the range [0, 14]
+ // If no random generator is passed, the global `rndGen` would be used
+ auto i = uniform(0, 15, rnd);
+ assert(i == 12);
+
+ // Generate a uniformly-distributed real in the range [0, 100)
+ auto r = uniform(0.0L, 100.0L, rnd);
+ assert(r == 79.65429843861011285);
+
+ // Generate a 32-bit random number
+ auto u = uniform!uint(rnd);
+ assert(u == 4083286876);
+}
+
version(unittest)
{
static import std.meta;
diff --git a/std/range/package.d b/std/range/package.d
index a07b8417e77..4dc7b33bd29 100644
--- a/std/range/package.d
+++ b/std/range/package.d
@@ -397,8 +397,9 @@ pure @safe nothrow unittest
test([ 1, 2, 3, 4, 5 ], [ 5, 4, 3, 2, 1 ]);
test([ 1, 2, 3, 4, 5, 6 ], [ 6, 5, 4, 3, 2, 1 ]);
- immutable foo = [1,2,3].idup;
- auto r = retro(foo);
+ immutable foo = [1,2,3].idup;
+ auto r = retro(foo);
+ assert(equal(r, [3, 2, 1]));
}
pure @safe nothrow unittest
@@ -495,7 +496,7 @@ Params:
Returns:
At minimum, an input range. The resulting range will adopt the
range primitives of the underlying range as long as
- $(REF, hasLength, std,range,primitives) is `true`.
+ $(REF hasLength, std,range,primitives) is `true`.
*/
auto stride(Range)(Range r, size_t n)
if (isInputRange!(Unqual!Range))
@@ -698,9 +699,11 @@ pure @safe nothrow unittest
pure @safe nothrow @nogc unittest
{
+ import std.algorithm.comparison : equal;
+
int[4] testArr = [1,2,3,4];
- //just checking it compiles
- auto s = testArr[].stride(2);
+ static immutable result = [1, 3];
+ assert(equal(testArr[].stride(2), result));
}
debug pure nothrow @system unittest
@@ -1280,7 +1283,7 @@ pure @safe nothrow unittest
// Make sure bug 3311 is fixed. ChainImpl should compile even if not all
// elements are mutable.
- auto c = chain( iota(0, 10), iota(0, 10) );
+ assert(equal(chain(iota(0, 3), iota(0, 3)), [0, 1, 2, 0, 1, 2]));
// Test the case where infinite ranges are present.
auto inf = chain([0,1,2][], cycle([4,5,6][]), [7,8,9][]); // infinite range
@@ -1343,7 +1346,7 @@ pure @safe nothrow @nogc unittest
class Foo{}
immutable(Foo)[] a;
immutable(Foo)[] b;
- auto c = chain(a, b);
+ assert(chain(a, b).empty);
}
/**
@@ -1576,18 +1579,14 @@ Returns:
alias of that range's type.
*/
auto chooseAmong(Ranges...)(size_t index, Ranges rs)
-if (Ranges.length > 2
- && is(typeof(choose(true, rs[0], rs[1])))
- && is(typeof(chooseAmong(0, rs[1 .. $]))))
-{
- return choose(index == 0, rs[0], chooseAmong(index - 1, rs[1 .. $]));
-}
-
-/// ditto
-auto chooseAmong(Ranges...)(size_t index, Ranges rs)
-if (Ranges.length == 2 && is(typeof(choose(true, rs[0], rs[1]))))
+if (Ranges.length >= 2
+ && allSatisfy!(isInputRange, staticMap!(Unqual, Ranges))
+ && !is(CommonType!(staticMap!(ElementType, Ranges)) == void))
{
- return choose(index == 0, rs[0], rs[1]);
+ static if (Ranges.length == 2)
+ return choose(index == 0, rs[0], rs[1]);
+ else
+ return choose(index == 0, rs[0], chooseAmong(index - 1, rs[1 .. $]));
}
///
@@ -1925,11 +1924,23 @@ Returns:
and `length`, `take` offers them as well.
*/
Take!R take(R)(R input, size_t n)
-if (isInputRange!(Unqual!R) && !isInfinite!(Unqual!R) && hasSlicing!(Unqual!R) &&
- !is(R T == Take!T))
+if (isInputRange!(Unqual!R))
{
- import std.algorithm.comparison : min;
- return input[0 .. min(n, input.length)];
+ alias U = Unqual!R;
+ static if (is(R T == Take!T))
+ {
+ import std.algorithm.comparison : min;
+ return R(input.source, min(n, input._maxAvailable));
+ }
+ else static if (!isInfinite!U && hasSlicing!U)
+ {
+ import std.algorithm.comparison : min;
+ return input[0 .. min(n, input.length)];
+ }
+ else
+ {
+ return Take!R(input, n);
+ }
}
/// ditto
@@ -2160,21 +2171,6 @@ pure @safe nothrow unittest
assert(equal(t, [ 1, 2, 3 ]));
}
-/// ditto
-Take!R take(R)(R input, size_t n)
-if (is(R T == Take!T))
-{
- import std.algorithm.comparison : min;
- return R(input.source, min(n, input._maxAvailable));
-}
-
-/// ditto
-Take!(R) take(R)(R input, size_t n)
-if (isInputRange!(Unqual!R) && (isInfinite!(Unqual!R) || !hasSlicing!(Unqual!R) && !is(R T == Take!T)))
-{
- return Take!R(input, n);
-}
-
pure @safe nothrow unittest
{
import std.algorithm.comparison : equal;
@@ -2259,13 +2255,16 @@ pure @safe nothrow @nogc unittest
int[] r1;
Take!(int[]) t1;
t1 = take(r1, 1);
+ assert(t1.empty);
string r2;
Take!string t2;
t2 = take(r2, 1);
+ assert(t2.empty);
Take!(Take!string) t3;
t3 = take(t2, 1);
+ assert(t3.empty);
}
pure @safe nothrow @nogc unittest
@@ -2283,6 +2282,9 @@ pure @safe nothrow @nogc unittest //12731
auto a = repeat(1);
auto s = a[1 .. 5];
s = s[1 .. 3];
+ assert(s.length == 2);
+ assert(s[0] == 1);
+ assert(s[1] == 1);
}
pure @safe nothrow @nogc unittest //13151
@@ -2406,16 +2408,20 @@ pure @safe nothrow unittest
auto a = [ 1, 2, 3, 4, 5 ];
auto b = takeExactly(a, 3);
+ assert(equal(b, [1, 2, 3]));
auto c = takeExactly(b, 2);
+ assert(equal(c, [1, 2]));
+
- auto d = filter!"a > 0"(a);
+
+ auto d = filter!"a > 2"(a);
auto e = takeExactly(d, 3);
- assert(equal(e, [1, 2, 3]));
+ assert(equal(e, [3, 4, 5]));
static assert(is(typeof(e.length) == size_t));
assert(e.length == 3);
- assert(e.front == 1);
+ assert(e.front == 3);
- assert(equal(takeExactly(e, 3), [1, 2, 3]));
+ assert(equal(takeExactly(e, 3), [3, 4, 5]));
}
pure @safe nothrow unittest
@@ -2923,13 +2929,13 @@ pure @safe nothrow @nogc unittest
/++
Convenience function which calls
- `range.$(REF popFrontN, std, range, primitives)(n) and returns `range`.
+ $(REF popFrontN, std, _range, primitives)`(range, n)` and returns `range`.
`drop` makes it easier to pop elements from a range
and then pass it to another function within a single expression,
whereas `popFrontN` would require multiple statements.
`dropBack` provides the same functionality but instead calls
- `range.$(REF popBackN, std, range, primitives)(n)
+ $(REF popBackN, std, _range, primitives)`(range, n)`
Note: `drop` and `dropBack` will only pop $(I up to)
`n` elements but will stop if the range is empty first.
@@ -3212,6 +3218,8 @@ pure @safe nothrow unittest
assert(r[0 .. 4].equal([ 5, 5, 5, 5 ]));
R r2 = r[5 .. $];
+ assert(r2.back == 5);
+ assert(r2.front == 5);
}
/**
@@ -3240,10 +3248,17 @@ pure @safe nothrow unittest //12007
rc = rc.save;
import std.algorithm.setops : cartesianProduct;
+ import std.algorithm.comparison : equal;
+ import std.typecons : tuple;
immutable int[] A = [1,2,3];
immutable int[] B = [4,5,6];
- auto AB = cartesianProduct(A,B);
+ assert(equal(cartesianProduct(A,B),
+ [
+ tuple(1, 4), tuple(1, 5), tuple(1, 6),
+ tuple(2, 4), tuple(2, 5), tuple(2, 6),
+ tuple(3, 4), tuple(3, 5), tuple(3, 6),
+ ]));
}
/**
@@ -3743,7 +3758,6 @@ if (isStaticArray!R)
assert(nums[0] == 2);
immutable int[] immarr = [1, 2, 3];
- auto cycleimm = cycle(immarr);
foreach (DummyType; AllDummyRanges)
{
@@ -3896,7 +3910,7 @@ if (isStaticArray!R)
@safe unittest // 12177
{
- auto a = recurrence!q{a[n - 1] ~ a[n - 2]}("1", "0");
+ static assert(__traits(compiles, recurrence!q{a[n - 1] ~ a[n - 2]}("1", "0")));
}
// Issue 13390
@@ -4529,7 +4543,7 @@ pure @safe unittest
}
R r;
auto z = zip(r, r);
- auto zz = z.save;
+ assert(z.save == z);
}
pure @system unittest
@@ -4568,7 +4582,8 @@ private string lockstepMixin(Ranges...)(bool withIndex, bool reverse)
{
indexDef = q{
size_t index = ranges[0].length-1;
- enforce(_stoppingPolicy == StoppingPolicy.requireSameLength, "lockstep can only be used with foreach_reverse when stoppingPolicy == requireSameLength");
+ enforce(_stoppingPolicy == StoppingPolicy.requireSameLength,
+ "lockstep can only be used with foreach_reverse when stoppingPolicy == requireSameLength");
foreach (range; ranges[1..$])
enforce(range.length == ranges[0].length);
@@ -4840,7 +4855,12 @@ if (allSatisfy!(isInputRange, Ranges))
// Make sure StoppingPolicy.requireSameLength doesn't throw.
auto ls = lockstep(arr1, arr2, StoppingPolicy.requireSameLength);
- foreach (a, b; ls) {}
+ int k = 1;
+ foreach (a, b; ls)
+ {
+ assert(a - b == k);
+ ++k;
+ }
// Make sure StoppingPolicy.requireSameLength throws.
arr2.popBack();
@@ -4854,6 +4874,10 @@ if (allSatisfy!(isInputRange, Ranges))
// Just make sure 1-range case instantiates. This hangs the compiler
// when no explicit stopping policy is specified due to Bug 4652.
auto stuff = lockstep([1,2,3,4,5], StoppingPolicy.shortest);
+ foreach (int i, a; stuff)
+ {
+ assert(stuff[i] == a);
+ }
// Test with indexing.
uint[] res1;
@@ -5541,9 +5565,9 @@ body
import std.math : approxEqual;
auto r = iota(0, 10, 1);
- assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9][]));
+ assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]));
r = iota(0, 11, 3);
- assert(equal(r, [0, 3, 6, 9][]));
+ assert(equal(r, [0, 3, 6, 9]));
assert(r[2] == 6);
auto rf = iota(0.0, 0.5, 0.1);
assert(approxEqual(rf, [0.0, 0.1, 0.2, 0.3, 0.4]));
@@ -5551,10 +5575,7 @@ body
nothrow @nogc @safe unittest
{
- auto t0 = iota(0, 10);
- auto t1 = iota(0, 10, 2);
- auto t2 = iota(1, 1, 0);
- //float overloads use std.conv.to so can't be @nogc or nothrow
+ //float overloads use std.conv.to so can't be @nogc or nothrow
alias ssize_t = Signed!size_t;
assert(iota(ssize_t.max, 0, -1).length == ssize_t.max);
assert(iota(ssize_t.max, ssize_t.min, -1).length == size_t.max);
@@ -5840,8 +5861,8 @@ if (!isIntegral!(CommonType!(B, E)) &&
this(int start) { current = start % wrapAround; }
- bool opEquals(Cyclic c) { return current == c.current; }
- bool opEquals(int i) { return current == i; }
+ bool opEquals(Cyclic c) const { return current == c.current; }
+ bool opEquals(int i) const { return current == i; }
void opUnary(string op)() if (op == "++")
{
current = (current + 1) % wrapAround;
@@ -10717,16 +10738,12 @@ private:
/// ditto
auto refRange(R)(R* range)
-if (isInputRange!R && !is(R == class))
-{
- return RefRange!R(range);
-}
-
-/// ditto
-auto refRange(R)(R* range)
-if (isInputRange!R && is(R == class))
+if (isInputRange!R)
{
- return *range;
+ static if (!is(R == class))
+ return RefRange!R(range);
+ else
+ return *range;
}
/*****************************************************************************/
diff --git a/std/range/primitives.d b/std/range/primitives.d
index 7dc0d018a02..99064ba73ec 100644
--- a/std/range/primitives.d
+++ b/std/range/primitives.d
@@ -161,17 +161,12 @@ Params:
Returns:
true if R is an InputRange, false if not
*/
-template isInputRange(R)
-{
- enum bool isInputRange = is(typeof(
- (inout int = 0)
- {
- R r = R.init; // can define a range object
- if (r.empty) {} // can test for empty
- r.popFront; // can invoke popFront()
- auto h = r.front; // can get the front of the range
- }));
-}
+enum bool isInputRange(R) =
+ is(typeof(R.init) == R)
+ && is(ReturnType!((R r) => r.empty) == bool)
+ && is(typeof((R r) => r.front))
+ && !is(ReturnType!((R r) => r.front) == void)
+ && is(typeof((R r) => r.popFront));
///
@safe unittest
@@ -189,6 +184,40 @@ template isInputRange(R)
static assert( isInputRange!(char[]));
static assert(!isInputRange!(char[4]));
static assert( isInputRange!(inout(int)[]));
+
+ static struct NotDefaultConstructible
+ {
+ @disable this();
+ void popFront();
+ @property bool empty();
+ @property int front();
+ }
+ static assert( isInputRange!NotDefaultConstructible);
+
+ static struct NotDefaultConstructibleOrCopyable
+ {
+ @disable this();
+ @disable this(this);
+ void popFront();
+ @property bool empty();
+ @property int front();
+ }
+ static assert(isInputRange!NotDefaultConstructibleOrCopyable);
+
+ static struct Frontless
+ {
+ void popFront();
+ @property bool empty();
+ }
+ static assert(!isInputRange!Frontless);
+
+ static struct VoidFront
+ {
+ void popFront();
+ @property bool empty();
+ void front();
+ }
+ static assert(!isInputRange!VoidFront);
}
/+
@@ -691,16 +720,8 @@ are:
2: if $(D E) is a non $(empty) $(D InputRange), then placing $(D e) is
guaranteed to not overflow the range.
+/
-package(std) template isNativeOutputRange(R, E)
-{
- enum bool isNativeOutputRange = is(typeof(
- (inout int = 0)
- {
- R r = void;
- E e;
- doPut(r, e);
- }));
-}
+package(std) enum bool isNativeOutputRange(R, E) =
+ is(typeof(doPut(lvalueOf!R, lvalueOf!E)));
@safe unittest
{
@@ -715,21 +736,14 @@ package(std) template isNativeOutputRange(R, E)
if (!r.empty)
put(r, [1, 2]); //May actually error out.
}
+
/++
Returns $(D true) if $(D R) is an output range for elements of type
$(D E). An output range is defined functionally as a range that
supports the operation $(D put(r, e)) as defined above.
+/
-template isOutputRange(R, E)
-{
- enum bool isOutputRange = is(typeof(
- (inout int = 0)
- {
- R r = R.init;
- E e = E.init;
- put(r, e);
- }));
-}
+enum bool isOutputRange(R, E) =
+ is(typeof(put(lvalueOf!R, lvalueOf!E)));
///
@safe unittest
@@ -791,19 +805,8 @@ are the same as for an input range, with the additional requirement
that backtracking must be possible by saving a copy of the range
object with $(D save) and using it later.
*/
-template isForwardRange(R)
-{
- enum bool isForwardRange = isInputRange!R && is(typeof(
- (inout int = 0)
- {
- R r1 = R.init;
- // NOTE: we cannot check typeof(r1.save) directly
- // because typeof may not check the right type there, and
- // because we want to ensure the range can be copied.
- auto s1 = r1.save;
- static assert(is(typeof(s1) == R));
- }));
-}
+enum bool isForwardRange(R) = isInputRange!R
+ && is(ReturnType!((R r) => r.save) == R);
///
@safe unittest
@@ -841,18 +844,9 @@ $(UL $(LI $(D r.back) returns (possibly a reference to) the last
element in the range. Calling $(D r.back) is allowed only if calling
$(D r.empty) has, or would have, returned $(D false).))
*/
-template isBidirectionalRange(R)
-{
- enum bool isBidirectionalRange = isForwardRange!R && is(typeof(
- (inout int = 0)
- {
- R r = R.init;
- r.popBack;
- auto t = r.back;
- auto w = r.front;
- static assert(is(typeof(t) == typeof(w)));
- }));
-}
+enum bool isBidirectionalRange(R) = isForwardRange!R
+ && is(typeof((R r) => r.popBack))
+ && is(ReturnType!((R r) => r.back) == ElementType!R);
///
@safe unittest
@@ -911,29 +905,14 @@ isRandomAccessRange) yields $(D false) for them because they use
variable-length encodings (UTF-8 and UTF-16 respectively). These types
are bidirectional ranges only.
*/
-template isRandomAccessRange(R)
-{
- enum bool isRandomAccessRange = is(typeof(
- (inout int = 0)
- {
- static assert(isBidirectionalRange!R ||
- isForwardRange!R && isInfinite!R);
- R r = R.init;
- auto e = r[1];
- auto f = r.front;
- static assert(is(typeof(e) == typeof(f)));
- static assert(!isNarrowString!R);
- static assert(hasLength!R || isInfinite!R);
-
- static if (is(typeof(r[$])))
- {
- static assert(is(typeof(f) == typeof(r[$])));
-
- static if (!isInfinite!R)
- static assert(is(typeof(f) == typeof(r[$ - 1])));
- }
- }));
-}
+enum bool isRandomAccessRange(R) =
+ is(typeof(lvalueOf!R[1]) == ElementType!R)
+ && !isNarrowString!R
+ && isForwardRange!R
+ && (isBidirectionalRange!R || isInfinite!R)
+ && (hasLength!R || isInfinite!R)
+ && (isInfinite!R || !is(typeof(lvalueOf!R[$ - 1]))
+ || is(typeof(lvalueOf!R[$ - 1]) == ElementType!R));
///
@safe unittest
@@ -1065,20 +1044,13 @@ static if (isRandomAccessRange!R)
static assert(is(typeof(moveAt(r, 0)) == E));
----
*/
-template hasMobileElements(R)
-{
- enum bool hasMobileElements = isInputRange!R && is(typeof(
- (inout int = 0)
- {
- alias E = ElementType!R;
- R r = R.init;
- static assert(is(typeof(moveFront(r)) == E));
- static if (isBidirectionalRange!R)
- static assert(is(typeof(moveBack(r)) == E));
- static if (isRandomAccessRange!R)
- static assert(is(typeof(moveAt(r, 0)) == E));
- }));
-}
+enum bool hasMobileElements(R) =
+ isInputRange!R
+ && is(typeof(moveFront(lvalueOf!R)) == ElementType!R)
+ && (!isBidirectionalRange!R
+ || is(typeof(moveBack(lvalueOf!R)) == ElementType!R))
+ && (!isRandomAccessRange!R
+ || is(typeof(moveAt(lvalueOf!R, 0)) == ElementType!R));
///
@safe unittest
@@ -1271,14 +1243,12 @@ static if (isRandomAccessRange!R) swap(r[0], r.front);
template hasSwappableElements(R)
{
import std.algorithm.mutation : swap;
- enum bool hasSwappableElements = isInputRange!R && is(typeof(
- (inout int = 0)
- {
- R r = R.init;
- swap(r.front, r.front);
- static if (isBidirectionalRange!R) swap(r.back, r.front);
- static if (isRandomAccessRange!R) swap(r[0], r.front);
- }));
+ enum bool hasSwappableElements = isInputRange!R
+ && is(typeof((ref R r) => swap(r.front, r.front)))
+ && (!isBidirectionalRange!R
+ || is(typeof((ref R r) => swap(r.back, r.front))))
+ && (!isRandomAccessRange!R
+ || is(typeof((ref R r) => swap(r[0], r.front))));
}
///
@@ -1308,17 +1278,12 @@ static if (isBidirectionalRange!R) r.back = r.front;
static if (isRandomAccessRange!R) r[0] = r.front;
----
*/
-template hasAssignableElements(R)
-{
- enum bool hasAssignableElements = isInputRange!R && is(typeof(
- (inout int = 0)
- {
- R r = R.init;
- r.front = r.front;
- static if (isBidirectionalRange!R) r.back = r.front;
- static if (isRandomAccessRange!R) r[0] = r.front;
- }));
-}
+enum bool hasAssignableElements(R) = isInputRange!R
+ && is(typeof(lvalueOf!R.front = lvalueOf!R.front))
+ && (!isBidirectionalRange!R
+ || is(typeof(lvalueOf!R.back = lvalueOf!R.back)))
+ && (!isRandomAccessRange!R
+ || is(typeof(lvalueOf!R[0] = lvalueOf!R.front)));
///
@safe unittest
@@ -1347,19 +1312,12 @@ static if (isBidirectionalRange!R) passByRef(r.back);
static if (isRandomAccessRange!R) passByRef(r[0]);
----
*/
-template hasLvalueElements(R)
-{
- enum bool hasLvalueElements = isInputRange!R && is(typeof(
- (inout int = 0)
- {
- void checkRef(ref ElementType!R stuff);
- R r = R.init;
-
- checkRef(r.front);
- static if (isBidirectionalRange!R) checkRef(r.back);
- static if (isRandomAccessRange!R) checkRef(r[0]);
- }));
-}
+enum bool hasLvalueElements(R) = isInputRange!R
+ && is(typeof(((ref x) => x)(lvalueOf!R.front)))
+ && (!isBidirectionalRange!R
+ || is(typeof(((ref x) => x)(lvalueOf!R.back))))
+ && (!isRandomAccessRange!R
+ || is(typeof(((ref x) => x)(lvalueOf!R[0]))));
///
@safe unittest
@@ -1502,76 +1460,33 @@ original range (they both return the same type for infinite ranges). However,
when using $(D opDollar), the result of $(D opSlice) must be that of the
original range type.
-The following code must compile for $(D hasSlicing) to be $(D true):
+The following expression must be true for `hasSlicing` to be `true`:
----
-R r = void;
-
-static if (isInfinite!R)
- typeof(take(r, 1)) s = r[1 .. 2];
-else
-{
- static assert(is(typeof(r[1 .. 2]) == R));
- R s = r[1 .. 2];
-}
-
-s = r[1 .. 2];
-
-static if (is(typeof(r[0 .. $])))
-{
- static assert(is(typeof(r[0 .. $]) == R));
- R t = r[0 .. $];
- t = r[0 .. $];
-
- static if (!isInfinite!R)
+ isForwardRange!R
+ && !isNarrowString!R
+ && is(ReturnType!((R r) => r[1 .. 1].length) == size_t)
+ && (is(typeof(lvalueOf!R[1 .. 1]) == R) || isInfinite!R)
+ && (!is(typeof(lvalueOf!R[0 .. $])) || is(typeof(lvalueOf!R[0 .. $]) == R))
+ && (!is(typeof(lvalueOf!R[0 .. $])) || isInfinite!R
+ || is(typeof(lvalueOf!R[0 .. $ - 1]) == R))
+ && is(typeof((ref R r)
{
- static assert(is(typeof(r[0 .. $ - 1]) == R));
- R u = r[0 .. $ - 1];
- u = r[0 .. $ - 1];
- }
-}
-
-static assert(isForwardRange!(typeof(r[1 .. 2])));
-static assert(hasLength!(typeof(r[1 .. 2])));
+ static assert(isForwardRange!(typeof(r[1 .. 2])));
+ }));
----
*/
-template hasSlicing(R)
-{
- enum bool hasSlicing = isForwardRange!R && !isNarrowString!R && is(typeof(
- (inout int = 0)
+enum bool hasSlicing(R) = isForwardRange!R
+ && !isNarrowString!R
+ && is(ReturnType!((R r) => r[1 .. 1].length) == size_t)
+ && (is(typeof(lvalueOf!R[1 .. 1]) == R) || isInfinite!R)
+ && (!is(typeof(lvalueOf!R[0 .. $])) || is(typeof(lvalueOf!R[0 .. $]) == R))
+ && (!is(typeof(lvalueOf!R[0 .. $])) || isInfinite!R
+ || is(typeof(lvalueOf!R[0 .. $ - 1]) == R))
+ && is(typeof((ref R r)
{
- R r = R.init;
-
- static if (isInfinite!R)
- {
- typeof(r[1 .. 1]) s = r[1 .. 2];
- }
- else
- {
- static assert(is(typeof(r[1 .. 2]) == R));
- R s = r[1 .. 2];
- }
-
- s = r[1 .. 2];
-
- static if (is(typeof(r[0 .. $])))
- {
- static assert(is(typeof(r[0 .. $]) == R));
- R t = r[0 .. $];
- t = r[0 .. $];
-
- static if (!isInfinite!R)
- {
- static assert(is(typeof(r[0 .. $ - 1]) == R));
- R u = r[0 .. $ - 1];
- u = r[0 .. $ - 1];
- }
- }
-
static assert(isForwardRange!(typeof(r[1 .. 2])));
- static assert(hasLength!(typeof(r[1 .. 2])));
}));
-}
///
@safe unittest
diff --git a/std/regex/package.d b/std/regex/package.d
index 3b7c4e7c2a8..f21d9f2e0d6 100644
--- a/std/regex/package.d
+++ b/std/regex/package.d
@@ -79,7 +79,7 @@ $(TR $(TD Objects) $(TD
assert(m.front[1] == "12");
...
- // The result of the $(D matchAll/matchFirst) is directly testable with if/assert/while.
+ // The result of the `matchAll/matchFirst` is directly testable with if/assert/while.
// e.g. test if a string consists of letters:
assert(matchFirst("Letter", `^\p{L}+$`));
---
@@ -350,7 +350,10 @@ public alias StaticRegex(Char) = std.regex.internal.ir.StaticRegex!(Char);
the same character width as $(D pattern).
Params:
- pattern(s) = Regular expression(s) to match
+ pattern = A single regular expression to match.
+ patterns = An array of regular expression strings.
+ The resulting `Regex` object will match any expression;
+ use $(LREF whichPattern) to know which.
flags = The _attributes (g, i, m and x accepted)
Throws: $(D RegexException) if there were any errors during compilation.
diff --git a/std/socket.d b/std/socket.d
index a375e2ee058..e9011685a74 100644
--- a/std/socket.d
+++ b/std/socket.d
@@ -60,7 +60,7 @@ version(Windows)
pragma (lib, "ws2_32.lib");
pragma (lib, "wsock32.lib");
- private import core.sys.windows.windows, std.windows.syserror;
+ import core.sys.windows.windows, std.windows.syserror;
public import core.sys.windows.winsock2;
private alias _ctimeval = core.sys.windows.winsock2.timeval;
private alias _clinger = core.sys.windows.winsock2.linger;
@@ -85,20 +85,20 @@ else version(Posix)
}
}
- private import core.sys.posix.arpa.inet;
- private import core.sys.posix.fcntl;
+ import core.sys.posix.arpa.inet;
+ import core.sys.posix.fcntl;
import core.sys.posix.netdb;
- private import core.sys.posix.netinet.in_;
- private import core.sys.posix.netinet.tcp;
- private import core.sys.posix.sys.select;
- private import core.sys.posix.sys.socket;
- private import core.sys.posix.sys.time;
+ import core.sys.posix.netinet.in_;
+ import core.sys.posix.netinet.tcp;
+ import core.sys.posix.sys.select;
+ import core.sys.posix.sys.socket;
+ import core.sys.posix.sys.time;
import core.sys.posix.sys.un : sockaddr_un;
- private import core.sys.posix.unistd;
+ import core.sys.posix.unistd;
private alias _ctimeval = core.sys.posix.sys.time.timeval;
private alias _clinger = core.sys.posix.sys.socket.linger;
- private import core.stdc.errno;
+ import core.stdc.errno;
enum socket_t : int32_t { init = -1 }
private const int _SOCKET_ERROR = -1;
@@ -125,7 +125,7 @@ version(unittest)
static assert(is(uint32_t == uint));
static assert(is(uint16_t == ushort));
- private import std.stdio : writefln;
+ import std.stdio : writefln;
// Print a message on exception instead of failing the unittest.
private void softUnittest(void delegate() @safe test, int line = __LINE__) @trusted
@@ -1269,6 +1269,20 @@ abstract class Address
/// Returns actual size of underlying $(D sockaddr) structure.
abstract @property socklen_t nameLen() const pure nothrow @nogc;
+ // Socket.remoteAddress, Socket.localAddress, and Socket.receiveFrom
+ // use setNameLen to set the actual size of the address as returned by
+ // getsockname, getpeername, and recvfrom, respectively.
+ // The following implementation is sufficient for fixed-length addresses,
+ // and ensures that the length is not changed.
+ // Must be overridden for variable-length addresses.
+ protected void setNameLen(socklen_t len)
+ {
+ if (len != this.nameLen)
+ throw new AddressException(
+ format("%s expects address of length %d, not %d", typeid(this),
+ this.nameLen, len), 0);
+ }
+
/// Family of this address.
@property AddressFamily addressFamily() const pure nothrow @nogc
{
@@ -1914,7 +1928,22 @@ version(StdDdoc)
/**
* $(D UnixAddress) encapsulates an address for a Unix domain socket
- * ($(D AF_UNIX)). Available only on supported systems.
+ * ($(D AF_UNIX)), i.e. a socket bound to a path name in the file system.
+ * Available only on supported systems.
+ *
+ * Linux also supports an abstract address namespace, in which addresses
+ * are independent of the file system. A socket address is abstract
+ * iff `path` starts with a _null byte (`'\0'`). Null bytes in other
+ * positions of an abstract address are allowed and have no special
+ * meaning.
+ *
+ * Example:
+ * ---
+ * auto addr = new UnixAddress("/var/run/dbus/system_bus_socket");
+ * auto abstractAddr = new UnixAddress("\0/tmp/dbus-OtHLWmCLPR");
+ * ---
+ *
+ * See_Also: $(HTTP http://man7.org/linux/man-pages/man7/unix.7.html, UNIX(7))
*/
class UnixAddress: Address
{
@@ -1947,6 +1976,8 @@ static if (is(sockaddr_un))
class UnixAddress: Address
{
protected:
+ socklen_t _nameLen;
+
struct
{
align (1):
@@ -1958,6 +1989,14 @@ static if (is(sockaddr_un))
{
sun.sun_family = AddressFamily.UNIX;
sun.sun_path = '?';
+ _nameLen = sun.sizeof;
+ }
+
+ override void setNameLen(socklen_t len) @trusted
+ {
+ if (len > sun.sizeof)
+ throw new SocketParameterException("Not enough socket address storage");
+ _nameLen = len;
}
public:
@@ -1973,8 +2012,7 @@ static if (is(sockaddr_un))
override @property socklen_t nameLen() @trusted const
{
- return cast(socklen_t) (sockaddr_un.init.sun_path.offsetof +
- strlen(cast(const(char*)) sun.sun_path.ptr) + 1);
+ return _nameLen;
}
this(in char[] path) @trusted pure
@@ -1982,7 +2020,18 @@ static if (is(sockaddr_un))
enforce(path.length <= sun.sun_path.sizeof, new SocketParameterException("Path too long"));
sun.sun_family = AddressFamily.UNIX;
sun.sun_path.ptr[0 .. path.length] = (cast(byte[]) path)[];
- sun.sun_path.ptr[path.length] = 0;
+ _nameLen = cast(socklen_t)
+ {
+ auto len = sockaddr_un.init.sun_path.offsetof + path.length;
+ // Pathname socket address must be terminated with '\0'
+ // which must be included in the address length.
+ if (sun.sun_path.ptr[0])
+ {
+ sun.sun_path.ptr[path.length] = 0;
+ ++len;
+ }
+ return len;
+ }();
}
this(sockaddr_un addr) pure nothrow @nogc
@@ -1993,7 +2042,11 @@ static if (is(sockaddr_un))
@property string path() @trusted const pure
{
- return to!string(cast(const(char)*)sun.sun_path.ptr);
+ auto len = _nameLen - sockaddr_un.init.sun_path.offsetof;
+ // For pathname socket address we need to strip off the terminating '\0'
+ if (sun.sun_path.ptr[0])
+ --len;
+ return (cast(const(char)*) sun.sun_path.ptr)[0 .. len].idup;
}
override string toString() const pure
@@ -2010,32 +2063,36 @@ static if (is(sockaddr_un))
immutable ubyte[] data = [1, 2, 3, 4];
Socket[2] pair;
- auto name = deleteme ~ "-unix-socket";
- auto address = new UnixAddress(name);
+ auto names = [ deleteme ~ "-unix-socket" ];
+ version (linux)
+ names ~= "\0" ~ deleteme ~ "-abstract\0unix\0socket";
+ foreach (name; names)
+ {
+ auto address = new UnixAddress(name);
- auto listener = new Socket(AddressFamily.UNIX, SocketType.STREAM);
- scope(exit) listener.close();
+ auto listener = new Socket(AddressFamily.UNIX, SocketType.STREAM);
+ scope(exit) listener.close();
+ listener.bind(address);
+ scope(exit) () @trusted { if (name[0]) remove(name.tempCString()); } ();
+ assert(listener.localAddress.toString == name);
- listener.bind(address);
- scope(exit) () @trusted { remove(name.tempCString()); } ();
- assert(listener.localAddress.toString == name);
+ listener.listen(1);
- listener.listen(1);
+ pair[0] = new Socket(AddressFamily.UNIX, SocketType.STREAM);
+ scope(exit) listener.close();
- pair[0] = new Socket(AddressFamily.UNIX, SocketType.STREAM);
- scope(exit) listener.close();
+ pair[0].connect(address);
+ scope(exit) pair[0].close();
- pair[0].connect(address);
- scope(exit) pair[0].close();
+ pair[1] = listener.accept();
+ scope(exit) pair[1].close();
- pair[1] = listener.accept();
- scope(exit) pair[1].close();
+ pair[0].send(data);
- pair[0].send(data);
-
- auto buf = new ubyte[data.length];
- pair[1].receive(buf);
- assert(buf == data);
+ auto buf = new ubyte[data.length];
+ pair[1].receive(buf);
+ assert(buf == data);
+ }
}
}
@@ -2889,8 +2946,7 @@ public:
socklen_t nameLen = addr.nameLen;
if (_SOCKET_ERROR == .getpeername(sock, addr.name, &nameLen))
throw new SocketOSException("Unable to obtain remote socket address");
- if (nameLen > addr.nameLen)
- throw new SocketParameterException("Not enough socket address storage");
+ addr.setNameLen(nameLen);
assert(addr.addressFamily == _family);
return addr;
}
@@ -2902,8 +2958,7 @@ public:
socklen_t nameLen = addr.nameLen;
if (_SOCKET_ERROR == .getsockname(sock, addr.name, &nameLen))
throw new SocketOSException("Unable to obtain local socket address");
- if (nameLen > addr.nameLen)
- throw new SocketParameterException("Not enough socket address storage");
+ addr.setNameLen(nameLen);
assert(addr.addressFamily == _family);
return addr;
}
@@ -3046,6 +3101,7 @@ public:
version(Windows)
{
auto read = .recvfrom(sock, buf.ptr, capToInt(buf.length), cast(int) flags, from.name, &nameLen);
+ from.setNameLen(nameLen);
assert(from.addressFamily == _family);
// if (!read) //connection closed
return read;
@@ -3053,6 +3109,7 @@ public:
else
{
auto read = .recvfrom(sock, buf.ptr, buf.length, cast(int) flags, from.name, &nameLen);
+ from.setNameLen(nameLen);
assert(from.addressFamily == _family);
// if (!read) //connection closed
return read;
diff --git a/std/stdio.d b/std/stdio.d
index 1464445fd7f..ba4bd4592db 100644
--- a/std/stdio.d
+++ b/std/stdio.d
@@ -337,7 +337,7 @@ void main(string[] args)
}
f.writeln("!");
// f exits scope, reference count falls to zero,
- // underlying $(D FILE*) is closed.
+ // underlying `FILE*` is closed.
}
----
$(CONSOLE
@@ -1519,7 +1519,7 @@ Throws:
Example:
---
-// Reads $(D stdin) and writes it to $(D stdout).
+// Reads `stdin` and writes it to `stdout`.
import std.stdio;
void main()
@@ -1603,9 +1603,9 @@ conversion error.
Example:
---
-// Read lines from $(D stdin) into a string
+// Read lines from `stdin` into a string
// Ignore lines starting with '#'
-// Write the string to $(D stdout)
+// Write the string to `stdout`
void main()
{
@@ -1635,7 +1635,7 @@ largest buffer returned by $(D readln):
Example:
---
-// Read lines from $(D stdin) and count words
+// Read lines from `stdin` and count words
void main()
{
@@ -4477,31 +4477,9 @@ Initialize with a message and an error code.
*/
this(string message, uint e = core.stdc.errno.errno) @trusted
{
- import std.conv : to;
-
+ import std.exception : errnoString;
errno = e;
- version (Posix)
- {
- import core.stdc.string : strerror_r;
-
- char[256] buf = void;
- version (CRuntime_Glibc)
- {
- auto s = strerror_r(errno, buf.ptr, buf.length);
- }
- else
- {
- strerror_r(errno, buf.ptr, buf.length);
- auto s = buf.ptr;
- }
- }
- else
- {
- import core.stdc.string : strerror;
-
- auto s = strerror(errno);
- }
- auto sysmsg = to!string(s);
+ auto sysmsg = errnoString(errno);
// If e is 0, we don't use the system error message. (The message
// is "Success", which is rather pointless for an exception.)
super(e == 0 ? message
diff --git a/std/string.d b/std/string.d
index 84a5a34b091..27b40a73d82 100644
--- a/std/string.d
+++ b/std/string.d
@@ -143,15 +143,6 @@ Source: $(PHOBOSSRC std/_string.d)
*/
module std.string;
-//debug=string; // uncomment to turn on debugging trustedPrintf's
-
-debug(string) private
-void trustedPrintf(in char* str) @trusted nothrow @nogc
-{
- import core.stdc.stdio : printf;
- printf("%s", str);
-}
-
version (unittest)
{
private:
@@ -575,7 +566,6 @@ if (isConvertibleToString!Range)
import std.exception : assertCTFEable;
import std.traits : EnumMembers;
import std.utf : byChar, byWchar, byDchar;
- debug(string) trustedPrintf("string.indexOf.unittest\n");
assertCTFEable!(
{
@@ -631,7 +621,6 @@ if (isConvertibleToString!Range)
import std.conv : to;
import std.traits : EnumMembers;
import std.utf : byCodeUnit, byChar, byWchar;
- debug(string) trustedPrintf("string.indexOf(startIdx).unittest\n");
assert("hello".byCodeUnit.indexOf(cast(dchar)'l', 1) == 2);
assert("hello".byWchar.indexOf(cast(dchar)'l', 1) == 2);
@@ -830,7 +819,6 @@ if (!(isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) &&
import std.conv : to;
import std.exception : assertCTFEable;
import std.traits : EnumMembers;
- debug(string) trustedPrintf("string.indexOf.unittest\n");
assertCTFEable!(
{
@@ -901,7 +889,6 @@ unittest
{
import std.conv : to;
import std.traits : EnumMembers;
- debug(string) trustedPrintf("string.indexOf(startIdx).unittest\n");
foreach (S; AliasSeq!(string, wstring, dstring))
{
@@ -1078,7 +1065,6 @@ if (isSomeChar!Char)
import std.conv : to;
import std.exception : assertCTFEable;
import std.traits : EnumMembers;
- debug(string) trustedPrintf("string.lastIndexOf.unittest\n");
assertCTFEable!(
{
@@ -1119,8 +1105,6 @@ if (isSomeChar!Char)
import std.conv : to;
import std.traits : EnumMembers;
- debug(string) trustedPrintf("string.lastIndexOf.unittest\n");
-
foreach (S; AliasSeq!(string, wstring, dstring))
{
assert(lastIndexOf(cast(S) null, 'a') == -1);
@@ -1305,8 +1289,6 @@ if (isSomeChar!Char1 && isSomeChar!Char2)
import std.exception : assertCTFEable;
import std.traits : EnumMembers;
- debug(string) trustedPrintf("string.lastIndexOf.unittest\n");
-
assertCTFEable!(
{
foreach (S; AliasSeq!(string, wstring, dstring))
@@ -1383,8 +1365,6 @@ if (isSomeChar!Char1 && isSomeChar!Char2)
import std.conv : to;
import std.traits : EnumMembers;
- debug(string) trustedPrintf("string.lastIndexOf.unittest\n");
-
foreach (S; AliasSeq!(string, wstring, dstring))
{
foreach (T; AliasSeq!(string, wstring, dstring))
@@ -1631,8 +1611,6 @@ if (isSomeChar!Char && isSomeChar!Char2)
import std.conv : to;
import std.exception : assertCTFEable;
- debug(string) trustedPrintf("string.indexOfAny.unittest\n");
-
assertCTFEable!(
{
foreach (S; AliasSeq!(string, wstring, dstring))
@@ -1672,8 +1650,6 @@ if (isSomeChar!Char && isSomeChar!Char2)
import std.conv : to;
import std.traits : EnumMembers;
- debug(string) trustedPrintf("string.indexOfAny(startIdx).unittest\n");
-
foreach (S; AliasSeq!(string, wstring, dstring))
{
foreach (T; AliasSeq!(string, wstring, dstring))
@@ -1799,8 +1775,6 @@ if (isSomeChar!Char && isSomeChar!Char2)
import std.conv : to;
import std.exception : assertCTFEable;
- debug(string) trustedPrintf("string.lastIndexOfAny.unittest\n");
-
assertCTFEable!(
{
foreach (S; AliasSeq!(string, wstring, dstring))
@@ -1854,8 +1828,6 @@ if (isSomeChar!Char && isSomeChar!Char2)
import std.conv : to;
import std.exception : assertCTFEable;
- debug(string) trustedPrintf("string.lastIndexOfAny(index).unittest\n");
-
assertCTFEable!(
{
foreach (S; AliasSeq!(string, wstring, dstring))
@@ -1981,8 +1953,6 @@ if (isSomeChar!Char && isSomeChar!Char2)
import std.conv : to;
import std.exception : assertCTFEable;
- debug(string) trustedPrintf("string.indexOf.unittest\n");
-
assertCTFEable!(
{
foreach (S; AliasSeq!(string, wstring, dstring))
@@ -2027,8 +1997,6 @@ if (isSomeChar!Char && isSomeChar!Char2)
import std.conv : to;
import std.exception : assertCTFEable;
- debug(string) trustedPrintf("string.indexOfNeither(index).unittest\n");
-
assertCTFEable!(
{
foreach (S; AliasSeq!(string, wstring, dstring))
@@ -2139,8 +2107,6 @@ if (isSomeChar!Char && isSomeChar!Char2)
import std.conv : to;
import std.exception : assertCTFEable;
- debug(string) trustedPrintf("string.lastIndexOfNeither.unittest\n");
-
assertCTFEable!(
{
foreach (S; AliasSeq!(string, wstring, dstring))
@@ -2186,8 +2152,6 @@ if (isSomeChar!Char && isSomeChar!Char2)
import std.conv : to;
import std.exception : assertCTFEable;
- debug(string) trustedPrintf("string.lastIndexOfNeither(index).unittest\n");
-
assertCTFEable!(
{
foreach (S; AliasSeq!(string, wstring, dstring))
@@ -2506,8 +2470,6 @@ if (!isSomeString!S && is(StringTypeOf!S))
import std.conv : to;
import std.exception : assertCTFEable;
- debug(string) trustedPrintf("string.splitLines.unittest\n");
-
assertCTFEable!(
{
foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
@@ -2739,8 +2701,8 @@ if ((hasSlicing!Range && hasLength!Range && isSomeChar!(ElementType!Range) ||
string s = "Hello\nmy\rname\nis";
- /* notice the call to $(D array) to turn the lazy range created by
- lineSplitter comparable to the $(D string[]) created by splitLines.
+ /* notice the call to `array` to turn the lazy range created by
+ lineSplitter comparable to the `string[]` created by splitLines.
*/
assert(lineSplitter(s).array == splitLines(s));
}
@@ -2757,8 +2719,6 @@ if (isConvertibleToString!Range)
import std.conv : to;
import std.exception : assertCTFEable;
- debug(string) trustedPrintf("string.lineSplitter.unittest\n");
-
assertCTFEable!(
{
foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
@@ -3143,8 +3103,6 @@ if (isConvertibleToString!Range)
import std.conv : to;
import std.exception : assertCTFEable;
- debug(string) trustedPrintf("string.strip.unittest\n");
-
assertCTFEable!(
{
foreach (S; AliasSeq!( char[], const char[], string,
@@ -3348,7 +3306,6 @@ if (isConvertibleToString!Range)
import std.conv : to;
import std.exception : assertCTFEable;
- debug(string) trustedPrintf("string.chomp.unittest\n");
string s;
assertCTFEable!(
@@ -3640,8 +3597,6 @@ if (isConvertibleToString!Range)
import std.conv : to;
import std.exception : assertCTFEable;
- debug(string) trustedPrintf("string.chop.unittest\n");
-
assertCTFEable!(
{
foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
@@ -4027,8 +3982,6 @@ unittest
import std.conv : to;
import std.exception : assertCTFEable;
- debug(string) trustedPrintf("string.justify.unittest\n");
-
assertCTFEable!(
{
foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
@@ -4367,8 +4320,6 @@ if (isConvertibleToString!Range)
import std.conv : to;
import std.exception : assertCTFEable;
- debug(string) trustedPrintf("string.detab.unittest\n");
-
assertCTFEable!(
{
foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
@@ -4706,8 +4657,6 @@ unittest
import std.conv : to;
import std.exception : assertCTFEable;
- debug(string) trustedPrintf("string.entab.unittest\n");
-
assertCTFEable!(
{
assert(entab(cast(string) null) is null);
@@ -5283,13 +5232,12 @@ if (isSomeString!S)
}
+deprecated
@safe pure @nogc unittest
{
import std.conv : to;
import std.exception : assertCTFEable;
- debug(string) trustedPrintf("std.string.inPattern.unittest\n");
-
assertCTFEable!(
{
assert(inPattern('x', "x") == 1);
@@ -5358,13 +5306,12 @@ if (isSomeString!S && isSomeString!S1)
return count;
}
+deprecated
@safe pure @nogc unittest
{
import std.conv : to;
import std.exception : assertCTFEable;
- debug(string) trustedPrintf("std.string.count.unittest\n");
-
assertCTFEable!(
{
assert(countchars("abc", "a-c") == 3);
@@ -5412,13 +5359,12 @@ if (isSomeString!S)
return s;
}
+deprecated
@safe pure unittest
{
import std.conv : to;
import std.exception : assertCTFEable;
- debug(string) trustedPrintf("std.string.removechars.unittest\n");
-
assertCTFEable!(
{
assert(removechars("abc", "a-c").length == 0);
@@ -5428,6 +5374,7 @@ if (isSomeString!S)
});
}
+deprecated
@safe pure unittest
{
assert(removechars("abc", "x") == "abc");
@@ -5488,13 +5435,12 @@ S squeeze(S)(S s, in S pattern = null)
return changed ? ((r is null) ? s[0 .. lasti] : cast(S) r) : s;
}
+deprecated
@system pure unittest
{
import std.conv : to;
import std.exception : assertCTFEable;
- debug(string) trustedPrintf("std.string.squeeze.unittest\n");
-
assertCTFEable!(
{
string s;
@@ -5544,6 +5490,7 @@ S1 munch(S1, S2)(ref S1 s, S2 pattern) @safe pure @nogc
}
///
+deprecated
@safe pure @nogc unittest
{
string s = "123abc";
@@ -5553,6 +5500,7 @@ S1 munch(S1, S2)(ref S1 s, S2 pattern) @safe pure @nogc
assert(t == "" && s == "abc");
}
+deprecated
@safe pure @nogc unittest
{
string s = "123€abc";
@@ -5633,8 +5581,6 @@ if (isSomeString!S)
import std.conv : to;
import std.exception : assertCTFEable;
- debug(string) trustedPrintf("std.string.succ.unittest\n");
-
assertCTFEable!(
{
assert(succ(string.init) is null);
@@ -5807,8 +5753,6 @@ C1[] tr(C1, C2, C3, C4 = immutable char)
import std.conv : to;
import std.exception : assertCTFEable;
- debug(string) trustedPrintf("std.string.tr.unittest\n");
-
// Complete list of test types; too slow to test'em all
// alias TestTypes = AliasSeq!(
// char[], const( char)[], immutable( char)[],
@@ -6131,8 +6075,6 @@ if (isSomeString!S ||
import std.conv : to;
import std.exception : assertCTFEable;
- debug(string) trustedPrintf("isNumeric(in string, bool = false).unittest\n");
-
assertCTFEable!(
{
// Test the isNumeric(in string) function
@@ -6446,8 +6388,6 @@ string[string] abbrev(string[] values) @safe pure
import std.conv : to;
import std.exception : assertCTFEable;
- debug(string) trustedPrintf("string.abbrev.unittest\n");
-
assertCTFEable!(
{
string[] values;
@@ -6589,8 +6529,6 @@ if (isConvertibleToString!Range)
import std.conv : to;
import std.exception : assertCTFEable;
- debug(string) trustedPrintf("string.column.unittest\n");
-
assertCTFEable!(
{
assert(column(string.init) == 0);
@@ -6704,8 +6642,6 @@ if (isSomeString!S)
import std.conv : to;
import std.exception : assertCTFEable;
- debug(string) trustedPrintf("string.wrap.unittest\n");
-
assertCTFEable!(
{
assert(wrap(string.init) == "\n");
@@ -6846,8 +6782,6 @@ if (isSomeString!S)
import std.conv : to;
import std.exception : assertCTFEable;
- debug(string) trustedPrintf("string.outdent.unittest\n");
-
template outdent_testStr(S)
{
enum S outdent_testStr =
diff --git a/std/traits.d b/std/traits.d
index e53fc8f7616..ff001f72c32 100644
--- a/std/traits.d
+++ b/std/traits.d
@@ -66,21 +66,19 @@
* $(LREF isImplicitlyConvertible)
* ))
* $(TR $(TD SomethingTypeOf) $(TD
- * $(LREF BooleanTypeOf)
- * $(LREF IntegralTypeOf)
- * $(LREF FloatingPointTypeOf)
- * $(LREF NumericTypeOf)
- * $(LREF UnsignedTypeOf)
- * $(LREF SignedTypeOf)
- * $(LREF CharTypeOf)
- * $(LREF StaticArrayTypeOf)
- * $(LREF DynamicArrayTypeOf)
- * $(LREF ArrayTypeOf)
- * $(LREF StringTypeOf)
- * $(LREF AssocArrayTypeOf)
- * $(LREF BuiltinTypeOf)
+ * $(LREF rvalueOf)
+ * $(LREF lvalueOf)
+ * $(LREF InoutOf)
+ * $(LREF ConstOf)
+ * $(LREF SharedOf)
+ * $(LREF SharedInoutOf)
+ * $(LREF SharedConstOf)
+ * $(LREF ImmutableOf)
+ * $(LREF QualifierOf)
* ))
* $(TR $(TD Categories of types) $(TD
+ * $(LREF allSameType)
+ * $(LREF ifTestable)
* $(LREF isType)
* $(LREF isAggregateType)
* $(LREF isArray)
@@ -164,7 +162,14 @@
*/
module std.traits;
-import std.typetuple; // TypeTuple
+import std.meta : AliasSeq, allSatisfy;
+
+// Legacy inheritance from std.typetuple
+// See also: https://github.com/dlang/phobos/pull/5484#discussion_r122602797
+import std.meta : staticMapMeta = staticMap;
+// TODO: find a way to trigger deprecation warnings
+//deprecated("staticMap is part of std.meta: Please import std.meta")
+alias staticMap = staticMapMeta;
///////////////////////////////////////////////////////////////////////////////
// Functions
@@ -251,44 +256,44 @@ private
static if (is(ucent))
{
- alias CentTypeList = TypeTuple!(cent, ucent);
- alias SignedCentTypeList = TypeTuple!(cent);
- alias UnsignedCentTypeList = TypeTuple!(ucent);
+ alias CentTypeList = AliasSeq!(cent, ucent);
+ alias SignedCentTypeList = AliasSeq!(cent);
+ alias UnsignedCentTypeList = AliasSeq!(ucent);
}
else
{
- alias CentTypeList = TypeTuple!();
- alias SignedCentTypeList = TypeTuple!();
- alias UnsignedCentTypeList = TypeTuple!();
+ alias CentTypeList = AliasSeq!();
+ alias SignedCentTypeList = AliasSeq!();
+ alias UnsignedCentTypeList = AliasSeq!();
}
- alias IntegralTypeList = TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong, CentTypeList);
- alias SignedIntTypeList = TypeTuple!(byte, short, int, long, SignedCentTypeList);
- alias UnsignedIntTypeList = TypeTuple!(ubyte, ushort, uint, ulong, UnsignedCentTypeList);
- alias FloatingPointTypeList = TypeTuple!(float, double, real);
- alias ImaginaryTypeList = TypeTuple!(ifloat, idouble, ireal);
- alias ComplexTypeList = TypeTuple!(cfloat, cdouble, creal);
- alias NumericTypeList = TypeTuple!(IntegralTypeList, FloatingPointTypeList);
- alias CharTypeList = TypeTuple!(char, wchar, dchar);
+ alias IntegralTypeList = AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong, CentTypeList);
+ alias SignedIntTypeList = AliasSeq!(byte, short, int, long, SignedCentTypeList);
+ alias UnsignedIntTypeList = AliasSeq!(ubyte, ushort, uint, ulong, UnsignedCentTypeList);
+ alias FloatingPointTypeList = AliasSeq!(float, double, real);
+ alias ImaginaryTypeList = AliasSeq!(ifloat, idouble, ireal);
+ alias ComplexTypeList = AliasSeq!(cfloat, cdouble, creal);
+ alias NumericTypeList = AliasSeq!(IntegralTypeList, FloatingPointTypeList);
+ alias CharTypeList = AliasSeq!(char, wchar, dchar);
}
package
{
- /// Add specific qualifier to the given type T.
+ // Add the mutable qualifier to the given type T.
template MutableOf(T) { alias MutableOf = T ; }
}
-/// Add specific qualifier to the given type T.
+/// Add the inout qualifier to the given type T.
template InoutOf(T) { alias InoutOf = inout(T) ; }
-/// ditto.
+/// Add the const qualifier to the given type T.
template ConstOf(T) { alias ConstOf = const(T) ; }
-/// ditto.
+/// Add the shared qualifier to the given type T.
template SharedOf(T) { alias SharedOf = shared(T) ; }
-/// ditto.
+/// Add the shared and inout qualifiers to the given type T.
template SharedInoutOf(T) { alias SharedInoutOf = shared(inout(T)); }
-/// ditto.
+/// Add the shared and const qualifiers to the given type T.
template SharedConstOf(T) { alias SharedConstOf = shared(const(T)); }
-/// ditto.
+/// Add the immutable qualifier to the given type T.
template ImmutableOf(T) { alias ImmutableOf = immutable(T) ; }
@safe unittest
@@ -327,7 +332,7 @@ template QualifierOf(T)
version(unittest)
{
- alias TypeQualifierList = TypeTuple!(MutableOf, ConstOf, SharedOf, SharedConstOf, ImmutableOf);
+ alias TypeQualifierList = AliasSeq!(MutableOf, ConstOf, SharedOf, SharedConstOf, ImmutableOf);
struct SubTypeOf(T)
{
@@ -585,7 +590,7 @@ private template fqnSym(alias T)
static assert(fqn!Barrier == "core.sync.barrier.Barrier");
}
-unittest
+@safe unittest
{
struct TemplatedStruct()
{
@@ -608,8 +613,8 @@ private template fqnType(T,
_inout = 3
}
- alias qualifiers = TypeTuple!(is(T == const), is(T == immutable), is(T == shared), is(T == inout));
- alias noQualifiers = TypeTuple!(false, false, false, false);
+ alias qualifiers = AliasSeq!(is(T == const), is(T == immutable), is(T == shared), is(T == inout));
+ alias noQualifiers = AliasSeq!(false, false, false, false);
string storageClassesString(uint psc)() @property
{
@@ -849,7 +854,8 @@ private template fqnType(T,
static assert(fqn!(typeof(data)) == format("shared(const(%s[string])[])", inner_name));
// Function types + function attributes
- static assert(fqn!(typeof(func)) == format("const(%s[string])(ref %s, scope lazy string) ref", inner_name, inner_name));
+ static assert(fqn!(typeof(func)) == format("const(%s[string])(ref %s, scope lazy string) ref",
+ inner_name, inner_name));
static assert(fqn!(typeof(retfunc)) == format("const(%s[string])(return %s) ref", inner_name, inner_name));
static assert(fqn!(typeof(inoutFunc)) == format("inout(%s(inout(%s)))", inner_name, inner_name));
static assert(fqn!(typeof(deleg)) == format("const(%s delegate(double, string) nothrow @safe)", inner_name));
@@ -967,14 +973,14 @@ alias ParameterTypeTuple = Parameters;
@safe unittest
{
int foo(int i, bool b) { return 0; }
- static assert(is(ParameterTypeTuple!foo == TypeTuple!(int, bool)));
- static assert(is(ParameterTypeTuple!(typeof(&foo)) == TypeTuple!(int, bool)));
+ static assert(is(ParameterTypeTuple!foo == AliasSeq!(int, bool)));
+ static assert(is(ParameterTypeTuple!(typeof(&foo)) == AliasSeq!(int, bool)));
struct S { real opCall(real r, int i) { return 0.0; } }
S s;
- static assert(is(ParameterTypeTuple!S == TypeTuple!(real, int)));
- static assert(is(ParameterTypeTuple!(S*) == TypeTuple!(real, int)));
- static assert(is(ParameterTypeTuple!s == TypeTuple!(real, int)));
+ static assert(is(ParameterTypeTuple!S == AliasSeq!(real, int)));
+ static assert(is(ParameterTypeTuple!(S*) == AliasSeq!(real, int)));
+ static assert(is(ParameterTypeTuple!s == AliasSeq!(real, int)));
class Test
{
@@ -1010,8 +1016,11 @@ template arity(alias func)
}
/**
-Returns a tuple consisting of the storage classes of the parameters of a
-function $(D func).
+Get tuple, one per function parameter, of the storage classes of the parameters.
+Params:
+ func = function symbol or type of function, delegate, or pointer to function
+Returns:
+ A tuple of ParameterStorageClass bits
*/
enum ParameterStorageClass : uint
{
@@ -1031,39 +1040,28 @@ enum ParameterStorageClass : uint
template ParameterStorageClassTuple(func...)
if (func.length == 1 && isCallable!func)
{
- alias Func = Unqual!(FunctionTypeOf!func);
+ alias Func = FunctionTypeOf!func;
- /*
- * TypeFuncion:
- * CallConvention FuncAttrs Arguments ArgClose Type
- */
- alias Params = Parameters!Func;
-
- // chop off CallConvention and FuncAttrs
- enum margs = demangleFunctionAttributes(mangledName!Func[1 .. $]).rest;
-
- // demangle Arguments and store parameter storage classes in a tuple
- template demangleNextParameter(string margs, size_t i = 0)
+ static if (is(Func PT == __parameters))
{
- static if (i < Params.length)
- {
- enum demang = demangleParameterStorageClass(margs);
- enum skip = mangledName!(Params[i]).length; // for bypassing Type
- enum rest = demang.rest;
-
- alias demangleNextParameter =
- TypeTuple!(
- demang.value + 0, // workaround: "not evaluatable at ..."
- demangleNextParameter!(rest[skip .. $], i + 1)
- );
- }
- else // went thru all the parameters
+ template StorageClass(size_t i)
{
- alias demangleNextParameter = TypeTuple!();
+ static if (i < PT.length)
+ {
+ alias StorageClass = AliasSeq!(
+ extractParameterStorageClassFlags!(__traits(getParameterStorageClasses, Func, i)),
+ StorageClass!(i + 1));
+ }
+ else
+ alias StorageClass = AliasSeq!();
}
+ alias ParameterStorageClassTuple = StorageClass!0;
+ }
+ else
+ {
+ static assert(0, func[0].stringof ~ " is not a function");
+ alias ParameterStorageClassTuple = AliasSeq!();
}
-
- alias ParameterStorageClassTuple = demangleNextParameter!margs;
}
///
@@ -1081,6 +1079,41 @@ template ParameterStorageClassTuple(func...)
static assert(pstc[2] == STC.none);
}
+/*****************
+ * Convert string tuple Attribs to ParameterStorageClass bits
+ * Params:
+ * Attribs = string tuple
+ * Returns:
+ * ParameterStorageClass bits
+ */
+template extractParameterStorageClassFlags(Attribs...)
+{
+ enum ParameterStorageClass extractParameterStorageClassFlags = ()
+ {
+ auto result = ParameterStorageClass.none;
+ static if (Attribs.length > 0)
+ {
+ foreach (attrib; [Attribs])
+ {
+ final switch (attrib) with (ParameterStorageClass)
+ {
+ case "scope": result |= scope_; break;
+ case "out": result |= out_; break;
+ case "ref": result |= ref_; break;
+ case "lazy": result |= lazy_; break;
+ case "return": result |= return_; break;
+ }
+ }
+ /* Mimic behavor of original version of ParameterStorageClassTuple()
+ * to avoid breaking existing code.
+ */
+ if (result == (ParameterStorageClass.ref_ | ParameterStorageClass.return_))
+ result = ParameterStorageClass.return_;
+ }
+ return result;
+ }();
+}
+
@safe unittest
{
alias STC = ParameterStorageClass;
@@ -1161,15 +1194,15 @@ template ParameterIdentifierTuple(func...)
// Define dummy entities to avoid pointless errors
template Get(size_t i) { enum Get = ""; }
- alias PT = TypeTuple!();
+ alias PT = AliasSeq!();
}
template Impl(size_t i = 0)
{
static if (i == PT.length)
- alias Impl = TypeTuple!();
+ alias Impl = AliasSeq!();
else
- alias Impl = TypeTuple!(Get!i, Impl!(i+1));
+ alias Impl = AliasSeq!(Get!i, Impl!(i+1));
}
alias ParameterIdentifierTuple = Impl!();
@@ -1213,7 +1246,7 @@ template ParameterIdentifierTuple(func...)
static assert([PIT!baw] == ["_param_0", "_param_1", "_param_2"]);
// depends on internal
- void baz(TypeTuple!(int, string, int[]) args){}
+ void baz(AliasSeq!(int, string, int[]) args){}
static assert([PIT!baz] == ["_param_0", "_param_1", "_param_2"]);
+/
}
@@ -1253,15 +1286,15 @@ template ParameterDefaults(func...)
// Define dummy entities to avoid pointless errors
template Get(size_t i) { enum Get = ""; }
- alias PT = TypeTuple!();
+ alias PT = AliasSeq!();
}
template Impl(size_t i = 0)
{
static if (i == PT.length)
- alias Impl = TypeTuple!();
+ alias Impl = AliasSeq!();
else
- alias Impl = TypeTuple!(Get!i, Impl!(i+1));
+ alias Impl = AliasSeq!(Get!i, Impl!(i+1));
}
alias ParameterDefaults = Impl!();
@@ -1290,20 +1323,20 @@ alias ParameterDefaultValueTuple = ParameterDefaults;
static assert(PDVT!bar.length == 2);
static assert(PDVT!bar[0] == 1);
static assert(PDVT!bar[1] == "hello");
- static assert(is(typeof(PDVT!bar) == typeof(TypeTuple!(1, "hello"))));
+ static assert(is(typeof(PDVT!bar) == typeof(AliasSeq!(1, "hello"))));
void baz(int x, int n = 1, string s = "hello"){}
static assert(PDVT!baz.length == 3);
static assert(is(PDVT!baz[0] == void));
static assert( PDVT!baz[1] == 1);
static assert( PDVT!baz[2] == "hello");
- static assert(is(typeof(PDVT!baz) == typeof(TypeTuple!(void, 1, "hello"))));
+ static assert(is(typeof(PDVT!baz) == typeof(AliasSeq!(void, 1, "hello"))));
// bug 10800 - property functions return empty string
@property void foo(int x = 3) { }
static assert(PDVT!foo.length == 1);
static assert(PDVT!foo[0] == 3);
- static assert(is(typeof(PDVT!foo) == typeof(TypeTuple!(3))));
+ static assert(is(typeof(PDVT!foo) == typeof(AliasSeq!(3))));
struct Colour
{
@@ -1544,7 +1577,7 @@ template hasFunctionAttributes(args...)
}
///
-unittest
+@safe unittest
{
real func(real x) pure nothrow @safe;
static assert(hasFunctionAttributes!(func, "@safe", "pure"));
@@ -1559,7 +1592,7 @@ unittest
static assert(!hasFunctionAttributes!(myFunc!bool, "shared"));
}
-unittest
+@system unittest
{
struct S
{
@@ -2052,7 +2085,7 @@ template FunctionTypeOf(func...)
int test(int);
int test() @property;
}
- alias ov = TypeTuple!(__traits(getVirtualFunctions, Overloads, "test"));
+ alias ov = AliasSeq!(__traits(getVirtualFunctions, Overloads, "test"));
alias F_ov0 = FunctionTypeOf!(ov[0]);
alias F_ov1 = FunctionTypeOf!(ov[1]);
alias F_ov2 = FunctionTypeOf!(ov[2]);
@@ -2189,10 +2222,10 @@ version (unittest)
import std.algorithm.iteration : reduce;
alias FA = FunctionAttribute;
- foreach (BaseT; TypeTuple!(typeof(&sc), typeof(&novar), typeof(&cstyle),
+ foreach (BaseT; AliasSeq!(typeof(&sc), typeof(&novar), typeof(&cstyle),
typeof(&dstyle), typeof(&typesafe)))
{
- foreach (T; TypeTuple!(BaseT, FunctionTypeOf!BaseT))
+ foreach (T; AliasSeq!(BaseT, FunctionTypeOf!BaseT))
(){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
enum linkage = functionLinkage!T;
enum attrs = functionAttributes!T;
@@ -2203,7 +2236,7 @@ version (unittest)
// Check that all linkage types work (D-style variadics require D linkage).
static if (variadicFunctionStyle!T != Variadic.d)
{
- foreach (newLinkage; TypeTuple!("D", "C", "Windows", "Pascal", "C++"))
+ foreach (newLinkage; AliasSeq!("D", "C", "Windows", "Pascal", "C++"))
{
alias New = SetFunctionAttributes!(T, newLinkage, attrs);
static assert(functionLinkage!New == newLinkage,
@@ -2317,12 +2350,17 @@ have a context pointer.
*/
template hasNested(T)
{
- import std.meta : anySatisfy;
+ import std.meta : anySatisfy, Filter;
+
static if (isStaticArray!T && T.length)
enum hasNested = hasNested!(typeof(T.init[0]));
else static if (is(T == class) || is(T == struct) || is(T == union))
+ {
+ // prevent infinite recursion for class with member of same type
+ enum notSame(U) = !is(Unqual!T == Unqual!U);
enum hasNested = isNested!T ||
- anySatisfy!(.hasNested, Fields!T);
+ anySatisfy!(.hasNested, Filter!(notSame, Fields!T));
+ }
else
enum hasNested = false;
}
@@ -2388,6 +2426,12 @@ template hasNested(T)
static assert(!__traits(compiles, isNested!(NestedClass[1])));
static assert( hasNested!(NestedClass[1]));
static assert(!hasNested!(NestedClass[0]));
+
+ static class A
+ {
+ A a;
+ }
+ static assert(!hasNested!A);
}
@@ -2406,14 +2450,14 @@ template Fields(T)
else static if (is(T == class))
alias Fields = typeof(T.tupleof);
else
- alias Fields = TypeTuple!T;
+ alias Fields = AliasSeq!T;
}
///
@safe unittest
{
struct S { int x; float y; }
- static assert(is(Fields!S == TypeTuple!(int, float)));
+ static assert(is(Fields!S == AliasSeq!(int, float)));
}
/**
@@ -2423,24 +2467,24 @@ alias FieldTypeTuple = Fields;
@safe unittest
{
- static assert(is(FieldTypeTuple!int == TypeTuple!int));
+ static assert(is(FieldTypeTuple!int == AliasSeq!int));
static struct StaticStruct1 { }
- static assert(is(FieldTypeTuple!StaticStruct1 == TypeTuple!()));
+ static assert(is(FieldTypeTuple!StaticStruct1 == AliasSeq!()));
static struct StaticStruct2 { int a, b; }
- static assert(is(FieldTypeTuple!StaticStruct2 == TypeTuple!(int, int)));
+ static assert(is(FieldTypeTuple!StaticStruct2 == AliasSeq!(int, int)));
int i;
struct NestedStruct1 { void f() { ++i; } }
- static assert(is(FieldTypeTuple!NestedStruct1 == TypeTuple!()));
+ static assert(is(FieldTypeTuple!NestedStruct1 == AliasSeq!()));
struct NestedStruct2 { int a; void f() { ++i; } }
- static assert(is(FieldTypeTuple!NestedStruct2 == TypeTuple!int));
+ static assert(is(FieldTypeTuple!NestedStruct2 == AliasSeq!int));
class NestedClass { int a; void f() { ++i; } }
- static assert(is(FieldTypeTuple!NestedClass == TypeTuple!int));
+ static assert(is(FieldTypeTuple!NestedClass == AliasSeq!int));
}
@@ -2462,37 +2506,37 @@ template FieldNameTuple(T)
else static if (is(T == class))
alias FieldNameTuple = staticMap!(NameOf, T.tupleof);
else
- alias FieldNameTuple = TypeTuple!"";
+ alias FieldNameTuple = AliasSeq!"";
}
///
@safe unittest
{
struct S { int x; float y; }
- static assert(FieldNameTuple!S == TypeTuple!("x", "y"));
- static assert(FieldNameTuple!int == TypeTuple!"");
+ static assert(FieldNameTuple!S == AliasSeq!("x", "y"));
+ static assert(FieldNameTuple!int == AliasSeq!"");
}
@safe unittest
{
- static assert(FieldNameTuple!int == TypeTuple!"");
+ static assert(FieldNameTuple!int == AliasSeq!"");
static struct StaticStruct1 { }
- static assert(is(FieldNameTuple!StaticStruct1 == TypeTuple!()));
+ static assert(is(FieldNameTuple!StaticStruct1 == AliasSeq!()));
static struct StaticStruct2 { int a, b; }
- static assert(FieldNameTuple!StaticStruct2 == TypeTuple!("a", "b"));
+ static assert(FieldNameTuple!StaticStruct2 == AliasSeq!("a", "b"));
int i;
struct NestedStruct1 { void f() { ++i; } }
- static assert(is(FieldNameTuple!NestedStruct1 == TypeTuple!()));
+ static assert(is(FieldNameTuple!NestedStruct1 == AliasSeq!()));
struct NestedStruct2 { int a; void f() { ++i; } }
- static assert(FieldNameTuple!NestedStruct2 == TypeTuple!"a");
+ static assert(FieldNameTuple!NestedStruct2 == AliasSeq!"a");
class NestedClass { int a; void f() { ++i; } }
- static assert(FieldNameTuple!NestedClass == TypeTuple!"a");
+ static assert(FieldNameTuple!NestedClass == AliasSeq!"a");
}
@@ -2506,7 +2550,7 @@ template RepresentationTypeTuple(T)
{
static if (T.length == 0)
{
- alias Impl = TypeTuple!();
+ alias Impl = AliasSeq!();
}
else
{
@@ -2525,7 +2569,7 @@ template RepresentationTypeTuple(T)
}
else
{
- alias Impl = TypeTuple!(T[0], Impl!(T[1 .. $]));
+ alias Impl = AliasSeq!(T[0], Impl!(T[1 .. $]));
}
}
}
@@ -2554,14 +2598,14 @@ template RepresentationTypeTuple(T)
@safe unittest
{
alias S1 = RepresentationTypeTuple!int;
- static assert(is(S1 == TypeTuple!int));
+ static assert(is(S1 == AliasSeq!int));
struct S2 { int a; }
struct S3 { int a; char b; }
struct S4 { S1 a; int b; S3 c; }
- static assert(is(RepresentationTypeTuple!S2 == TypeTuple!int));
- static assert(is(RepresentationTypeTuple!S3 == TypeTuple!(int, char)));
- static assert(is(RepresentationTypeTuple!S4 == TypeTuple!(int, int, int, char)));
+ static assert(is(RepresentationTypeTuple!S2 == AliasSeq!int));
+ static assert(is(RepresentationTypeTuple!S3 == AliasSeq!(int, char)));
+ static assert(is(RepresentationTypeTuple!S4 == AliasSeq!(int, int, int, char)));
struct S11 { int a; float b; }
struct S21 { char[] a; union { S11 b; S11 * c; } }
@@ -3635,7 +3679,7 @@ template hasStaticMember(T, string member)
static void delegate() sd;
void m();
- final void m2() const pure nothrow @nogc @safe;
+ void m2() const pure nothrow @nogc @safe;
inout(int) iom() inout;
static inout(int) iosf(inout int x);
@@ -3916,7 +3960,7 @@ template EnumMembers(E)
//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
/***
- * Get a $(D_PARAM TypeTuple) of the base class and base interfaces of
+ * Get a $(D_PARAM AliasSeq) of the base class and base interfaces of
* this class or interface. $(D_PARAM BaseTypeTuple!Object) returns
* the empty type tuple.
*/
@@ -3934,11 +3978,11 @@ template BaseTypeTuple(A)
interface I1 { }
interface I2 { }
interface I12 : I1, I2 { }
- static assert(is(BaseTypeTuple!I12 == TypeTuple!(I1, I2)));
+ static assert(is(BaseTypeTuple!I12 == AliasSeq!(I1, I2)));
interface I3 : I1 { }
interface I123 : I1, I2, I3 { }
- static assert(is(BaseTypeTuple!I123 == TypeTuple!(I1, I2, I3)));
+ static assert(is(BaseTypeTuple!I123 == AliasSeq!(I1, I2, I3)));
}
@safe unittest
@@ -3958,7 +4002,7 @@ template BaseTypeTuple(A)
}
/**
- * Get a $(D_PARAM TypeTuple) of $(I all) base classes of this class,
+ * Get a $(D_PARAM AliasSeq) of $(I all) base classes of this class,
* in decreasing order. Interfaces are not included. $(D_PARAM
* BaseClassesTuple!Object) yields the empty type tuple.
*/
@@ -3967,16 +4011,16 @@ template BaseClassesTuple(T)
{
static if (is(T == Object))
{
- alias BaseClassesTuple = TypeTuple!();
+ alias BaseClassesTuple = AliasSeq!();
}
else static if (is(BaseTypeTuple!T[0] == Object))
{
- alias BaseClassesTuple = TypeTuple!Object;
+ alias BaseClassesTuple = AliasSeq!Object;
}
else
{
alias BaseClassesTuple =
- TypeTuple!(BaseTypeTuple!T[0],
+ AliasSeq!(BaseTypeTuple!T[0],
BaseClassesTuple!(BaseTypeTuple!T[0]));
}
}
@@ -3988,9 +4032,9 @@ template BaseClassesTuple(T)
class C2 : C1 { }
class C3 : C2 { }
static assert(!BaseClassesTuple!Object.length);
- static assert(is(BaseClassesTuple!C1 == TypeTuple!(Object)));
- static assert(is(BaseClassesTuple!C2 == TypeTuple!(C1, Object)));
- static assert(is(BaseClassesTuple!C3 == TypeTuple!(C2, C1, Object)));
+ static assert(is(BaseClassesTuple!C1 == AliasSeq!(Object)));
+ static assert(is(BaseClassesTuple!C2 == AliasSeq!(C1, Object)));
+ static assert(is(BaseClassesTuple!C3 == AliasSeq!(C2, C1, Object)));
}
@safe unittest
@@ -4001,11 +4045,11 @@ template BaseClassesTuple(T)
static assert(!__traits(compiles, BaseClassesTuple!I));
class C4 : I { }
class C5 : C4, I { }
- static assert(is(BaseClassesTuple!C5 == TypeTuple!(C4, Object)));
+ static assert(is(BaseClassesTuple!C5 == AliasSeq!(C4, Object)));
}
/**
- * Get a $(D_PARAM TypeTuple) of $(I all) interfaces directly or
+ * Get a $(D_PARAM AliasSeq) of $(I all) interfaces directly or
* indirectly inherited by this class or interface. Interfaces do not
* repeat if multiply implemented. $(D_PARAM InterfacesTuple!Object)
* yields the empty type tuple.
@@ -4017,12 +4061,12 @@ template InterfacesTuple(T)
{
static if (T.length)
{
- alias Flatten = TypeTuple!(Flatten!H, Flatten!T);
+ alias Flatten = AliasSeq!(Flatten!H, Flatten!T);
}
else
{
static if (is(H == interface))
- alias Flatten = TypeTuple!(H, InterfacesTuple!H);
+ alias Flatten = AliasSeq!(H, InterfacesTuple!H);
else
alias Flatten = InterfacesTuple!H;
}
@@ -4031,7 +4075,7 @@ template InterfacesTuple(T)
static if (is(T S == super) && S.length)
alias InterfacesTuple = NoDuplicates!(Flatten!S);
else
- alias InterfacesTuple = TypeTuple!();
+ alias InterfacesTuple = AliasSeq!();
}
@safe unittest
@@ -4059,14 +4103,14 @@ template InterfacesTuple(T)
class B2 : J {}
class C2 : B2, Ia, Ib {}
static assert(is(InterfacesTuple!I ==
- TypeTuple!(Ia, Iaa, Iab, Ib, Iba, Ibb)));
+ AliasSeq!(Ia, Iaa, Iab, Ib, Iba, Ibb)));
static assert(is(InterfacesTuple!C2 ==
- TypeTuple!(J, Ia, Iaa, Iab, Ib, Iba, Ibb)));
+ AliasSeq!(J, Ia, Iaa, Iab, Ib, Iba, Ibb)));
}
/**
- * Get a $(D_PARAM TypeTuple) of $(I all) base classes of $(D_PARAM
+ * Get a $(D_PARAM AliasSeq) of $(I all) base classes of $(D_PARAM
* T), in decreasing order, followed by $(D_PARAM T)'s
* interfaces. $(D_PARAM TransitiveBaseTypeTuple!Object) yields the
* empty type tuple.
@@ -4074,10 +4118,10 @@ template InterfacesTuple(T)
template TransitiveBaseTypeTuple(T)
{
static if (is(T == Object))
- alias TransitiveBaseTypeTuple = TypeTuple!();
+ alias TransitiveBaseTypeTuple = AliasSeq!();
else
alias TransitiveBaseTypeTuple =
- TypeTuple!(BaseClassesTuple!T, InterfacesTuple!T);
+ AliasSeq!(BaseClassesTuple!T, InterfacesTuple!T);
}
///
@@ -4118,28 +4162,28 @@ template MemberFunctionsTuple(C, string name)
static if (__traits(hasMember, Node, name) && __traits(compiles, __traits(getMember, Node, name)))
{
// Get all overloads in sight (not hidden).
- alias inSight = TypeTuple!(__traits(getVirtualFunctions, Node, name));
+ alias inSight = AliasSeq!(__traits(getVirtualFunctions, Node, name));
// And collect all overloads in ancestor classes to reveal hidden
// methods. The result may contain duplicates.
template walkThru(Parents...)
{
static if (Parents.length > 0)
- alias walkThru = TypeTuple!(
+ alias walkThru = AliasSeq!(
CollectOverloads!(Parents[0]),
walkThru!(Parents[1 .. $])
);
else
- alias walkThru = TypeTuple!();
+ alias walkThru = AliasSeq!();
}
static if (is(Node Parents == super))
- alias CollectOverloads = TypeTuple!(inSight, walkThru!Parents);
+ alias CollectOverloads = AliasSeq!(inSight, walkThru!Parents);
else
- alias CollectOverloads = TypeTuple!inSight;
+ alias CollectOverloads = AliasSeq!inSight;
}
else
- alias CollectOverloads = TypeTuple!(); // no overloads in this hierarchy
+ alias CollectOverloads = AliasSeq!(); // no overloads in this hierarchy
}
// duplicates in this tuple will be removed by shrink()
@@ -4174,13 +4218,13 @@ template MemberFunctionsTuple(C, string name)
alias shrinkOne = shrinkOne!(rest[0], rest[1 .. $]);
else
// target and rest[0] are distinct.
- alias shrinkOne = TypeTuple!(
+ alias shrinkOne = AliasSeq!(
shrinkOne!(target, rest[1 .. $]),
rest[0] // keep
);
}
else
- alias shrinkOne = TypeTuple!target; // done
+ alias shrinkOne = AliasSeq!target; // done
}
/*
@@ -4191,17 +4235,17 @@ template MemberFunctionsTuple(C, string name)
static if (overloads.length > 0)
{
alias temp = shrinkOne!overloads;
- alias shrink = TypeTuple!(temp[0], shrink!(temp[1 .. $]));
+ alias shrink = AliasSeq!(temp[0], shrink!(temp[1 .. $]));
}
else
- alias shrink = TypeTuple!(); // done
+ alias shrink = AliasSeq!(); // done
}
// done.
alias MemberFunctionsTuple = shrink!overloads;
}
else
- alias MemberFunctionsTuple = TypeTuple!();
+ alias MemberFunctionsTuple = AliasSeq!();
}
///
@@ -4328,7 +4372,7 @@ template TemplateOf(T : Base!Args, alias Base, Args...)
/**
-Returns a $(D TypeTuple) of the template arguments used to instantiate $(D T).
+Returns a $(D AliasSeq) of the template arguments used to instantiate $(D T).
*/
template TemplateArgsOf(alias T : Base!Args, alias Base, Args...)
{
@@ -4345,7 +4389,7 @@ template TemplateArgsOf(T : Base!Args, alias Base, Args...)
@safe unittest
{
struct Foo(T, U) {}
- static assert(is(TemplateArgsOf!(Foo!(int, real)) == TypeTuple!(int, real)));
+ static assert(is(TemplateArgsOf!(Foo!(int, real)) == AliasSeq!(int, real)));
}
@safe unittest
@@ -4362,15 +4406,15 @@ template TemplateArgsOf(T : Base!Args, alias Base, Args...)
enum x = 123;
enum y = "123";
- static assert(is(TemplateArgsOf!(Foo1!(int)) == TypeTuple!(int)));
- static assert(is(TemplateArgsOf!(Foo2!(int, int)) == TypeTuple!(int, int)));
- static assert(__traits(isSame, TemplateArgsOf!(Foo3!(x)), TypeTuple!(x)));
- static assert(TemplateArgsOf!(Foo4!(y)) == TypeTuple!(y));
- static assert(is(TemplateArgsOf!(Foo5!(int)) == TypeTuple!(int)));
- static assert(is(TemplateArgsOf!(Foo6!(int, int)) == TypeTuple!(int, int)));
- static assert(__traits(isSame, TemplateArgsOf!(Foo7!(x)), TypeTuple!(x)));
- static assert(is(TemplateArgsOf!(Foo8!(int).Foo9!(real)) == TypeTuple!(real)));
- static assert(is(TemplateArgsOf!(Foo10!()) == TypeTuple!()));
+ static assert(is(TemplateArgsOf!(Foo1!(int)) == AliasSeq!(int)));
+ static assert(is(TemplateArgsOf!(Foo2!(int, int)) == AliasSeq!(int, int)));
+ static assert(__traits(isSame, TemplateArgsOf!(Foo3!(x)), AliasSeq!(x)));
+ static assert(TemplateArgsOf!(Foo4!(y)) == AliasSeq!(y));
+ static assert(is(TemplateArgsOf!(Foo5!(int)) == AliasSeq!(int)));
+ static assert(is(TemplateArgsOf!(Foo6!(int, int)) == AliasSeq!(int, int)));
+ static assert(__traits(isSame, TemplateArgsOf!(Foo7!(x)), AliasSeq!(x)));
+ static assert(is(TemplateArgsOf!(Foo8!(int).Foo9!(real)) == AliasSeq!(real)));
+ static assert(is(TemplateArgsOf!(Foo10!()) == AliasSeq!()));
}
@@ -4476,62 +4520,62 @@ template ImplicitConversionTargets(T)
{
static if (is(T == bool))
alias ImplicitConversionTargets =
- TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong, CentTypeList,
+ AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong, CentTypeList,
float, double, real, char, wchar, dchar);
else static if (is(T == byte))
alias ImplicitConversionTargets =
- TypeTuple!(short, ushort, int, uint, long, ulong, CentTypeList,
+ AliasSeq!(short, ushort, int, uint, long, ulong, CentTypeList,
float, double, real, char, wchar, dchar);
else static if (is(T == ubyte))
alias ImplicitConversionTargets =
- TypeTuple!(short, ushort, int, uint, long, ulong, CentTypeList,
+ AliasSeq!(short, ushort, int, uint, long, ulong, CentTypeList,
float, double, real, char, wchar, dchar);
else static if (is(T == short))
alias ImplicitConversionTargets =
- TypeTuple!(int, uint, long, ulong, CentTypeList, float, double, real);
+ AliasSeq!(int, uint, long, ulong, CentTypeList, float, double, real);
else static if (is(T == ushort))
alias ImplicitConversionTargets =
- TypeTuple!(int, uint, long, ulong, CentTypeList, float, double, real);
+ AliasSeq!(int, uint, long, ulong, CentTypeList, float, double, real);
else static if (is(T == int))
alias ImplicitConversionTargets =
- TypeTuple!(long, ulong, CentTypeList, float, double, real);
+ AliasSeq!(long, ulong, CentTypeList, float, double, real);
else static if (is(T == uint))
alias ImplicitConversionTargets =
- TypeTuple!(long, ulong, CentTypeList, float, double, real);
+ AliasSeq!(long, ulong, CentTypeList, float, double, real);
else static if (is(T == long))
- alias ImplicitConversionTargets = TypeTuple!(float, double, real);
+ alias ImplicitConversionTargets = AliasSeq!(float, double, real);
else static if (is(T == ulong))
- alias ImplicitConversionTargets = TypeTuple!(float, double, real);
+ alias ImplicitConversionTargets = AliasSeq!(float, double, real);
else static if (is(cent) && is(T == cent))
- alias ImplicitConversionTargets = TypeTuple!(float, double, real);
+ alias ImplicitConversionTargets = AliasSeq!(float, double, real);
else static if (is(ucent) && is(T == ucent))
- alias ImplicitConversionTargets = TypeTuple!(float, double, real);
+ alias ImplicitConversionTargets = AliasSeq!(float, double, real);
else static if (is(T == float))
- alias ImplicitConversionTargets = TypeTuple!(double, real);
+ alias ImplicitConversionTargets = AliasSeq!(double, real);
else static if (is(T == double))
- alias ImplicitConversionTargets = TypeTuple!real;
+ alias ImplicitConversionTargets = AliasSeq!real;
else static if (is(T == char))
alias ImplicitConversionTargets =
- TypeTuple!(wchar, dchar, byte, ubyte, short, ushort,
+ AliasSeq!(wchar, dchar, byte, ubyte, short, ushort,
int, uint, long, ulong, CentTypeList, float, double, real);
else static if (is(T == wchar))
alias ImplicitConversionTargets =
- TypeTuple!(dchar, short, ushort, int, uint, long, ulong, CentTypeList,
+ AliasSeq!(dchar, short, ushort, int, uint, long, ulong, CentTypeList,
float, double, real);
else static if (is(T == dchar))
alias ImplicitConversionTargets =
- TypeTuple!(int, uint, long, ulong, CentTypeList, float, double, real);
+ AliasSeq!(int, uint, long, ulong, CentTypeList, float, double, real);
else static if (is(T : typeof(null)))
- alias ImplicitConversionTargets = TypeTuple!(typeof(null));
+ alias ImplicitConversionTargets = AliasSeq!(typeof(null));
else static if (is(T : Object))
alias ImplicitConversionTargets = TransitiveBaseTypeTuple!(T);
else static if (isDynamicArray!T && !is(typeof(T.init[0]) == const))
alias ImplicitConversionTargets =
- TypeTuple!(const(Unqual!(typeof(T.init[0])))[]);
+ AliasSeq!(const(Unqual!(typeof(T.init[0])))[]);
else static if (is(T : void*))
- alias ImplicitConversionTargets = TypeTuple!(void*);
+ alias ImplicitConversionTargets = AliasSeq!(void*);
else
- alias ImplicitConversionTargets = TypeTuple!();
+ alias ImplicitConversionTargets = AliasSeq!();
}
@safe unittest
@@ -5019,7 +5063,7 @@ int i = rvalueOf!int; // error, no actual value is returned
static struct S { }
int i;
struct Nested { void f() { ++i; } }
- foreach (T; TypeTuple!(int, immutable int, inout int, string, S, Nested, Object))
+ foreach (T; AliasSeq!(int, immutable int, inout int, string, S, Nested, Object))
{
static assert(!__traits(compiles, needLvalue(rvalueOf!T)));
static assert( __traits(compiles, needLvalue(lvalueOf!T)));
@@ -5039,7 +5083,7 @@ int i = rvalueOf!int; // error, no actual value is returned
private template AliasThisTypeOf(T) if (isAggregateType!T)
{
- alias members = TypeTuple!(__traits(getAliasThis, T));
+ alias members = AliasSeq!(__traits(getAliasThis, T));
static if (members.length == 1)
{
@@ -5069,14 +5113,14 @@ template BooleanTypeOf(T)
@safe unittest
{
// unexpected failure, maybe dmd type-merging bug
- foreach (T; TypeTuple!bool)
+ foreach (T; AliasSeq!bool)
foreach (Q; TypeQualifierList)
{
static assert( is(Q!T == BooleanTypeOf!( Q!T )));
static assert( is(Q!T == BooleanTypeOf!( SubTypeOf!(Q!T) )));
}
- foreach (T; TypeTuple!(void, NumericTypeList, ImaginaryTypeList, ComplexTypeList, CharTypeList))
+ foreach (T; AliasSeq!(void, NumericTypeList, ImaginaryTypeList, ComplexTypeList, CharTypeList))
foreach (Q; TypeQualifierList)
{
static assert(!is(BooleanTypeOf!( Q!T )), Q!T.stringof);
@@ -5127,7 +5171,7 @@ template IntegralTypeOf(T)
static assert( is(Q!T == IntegralTypeOf!( SubTypeOf!(Q!T) )));
}
- foreach (T; TypeTuple!(void, bool, FloatingPointTypeList, ImaginaryTypeList, ComplexTypeList, CharTypeList))
+ foreach (T; AliasSeq!(void, bool, FloatingPointTypeList, ImaginaryTypeList, ComplexTypeList, CharTypeList))
foreach (Q; TypeQualifierList)
{
static assert(!is(IntegralTypeOf!( Q!T )));
@@ -5162,7 +5206,7 @@ template FloatingPointTypeOf(T)
static assert( is(Q!T == FloatingPointTypeOf!( SubTypeOf!(Q!T) )));
}
- foreach (T; TypeTuple!(void, bool, IntegralTypeList, ImaginaryTypeList, ComplexTypeList, CharTypeList))
+ foreach (T; AliasSeq!(void, bool, IntegralTypeList, ImaginaryTypeList, ComplexTypeList, CharTypeList))
foreach (Q; TypeQualifierList)
{
static assert(!is(FloatingPointTypeOf!( Q!T )));
@@ -5191,7 +5235,7 @@ template NumericTypeOf(T)
static assert( is(Q!T == NumericTypeOf!( SubTypeOf!(Q!T) )));
}
- foreach (T; TypeTuple!(void, bool, CharTypeList, ImaginaryTypeList, ComplexTypeList))
+ foreach (T; AliasSeq!(void, bool, CharTypeList, ImaginaryTypeList, ComplexTypeList))
foreach (Q; TypeQualifierList)
{
static assert(!is(NumericTypeOf!( Q!T )));
@@ -5252,14 +5296,14 @@ template CharTypeOf(T)
static assert( is(CharTypeOf!( SubTypeOf!(Q!T) )));
}
- foreach (T; TypeTuple!(void, bool, NumericTypeList, ImaginaryTypeList, ComplexTypeList))
+ foreach (T; AliasSeq!(void, bool, NumericTypeList, ImaginaryTypeList, ComplexTypeList))
foreach (Q; TypeQualifierList)
{
static assert(!is(CharTypeOf!( Q!T )));
static assert(!is(CharTypeOf!( SubTypeOf!(Q!T) )));
}
- foreach (T; TypeTuple!(string, wstring, dstring, char[4]))
+ foreach (T; AliasSeq!(string, wstring, dstring, char[4]))
foreach (Q; TypeQualifierList)
{
static assert(!is(CharTypeOf!( Q!T )));
@@ -5284,8 +5328,8 @@ template StaticArrayTypeOf(T)
@safe unittest
{
- foreach (T; TypeTuple!(bool, NumericTypeList, ImaginaryTypeList, ComplexTypeList))
- foreach (Q; TypeTuple!(TypeQualifierList, InoutOf, SharedInoutOf))
+ foreach (T; AliasSeq!(bool, NumericTypeList, ImaginaryTypeList, ComplexTypeList))
+ foreach (Q; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf))
{
static assert(is( Q!( T[1] ) == StaticArrayTypeOf!( Q!( T[1] ) ) ));
@@ -5295,8 +5339,8 @@ template StaticArrayTypeOf(T)
}
}
- foreach (T; TypeTuple!void)
- foreach (Q; TypeTuple!TypeQualifierList)
+ foreach (T; AliasSeq!void)
+ foreach (Q; AliasSeq!TypeQualifierList)
{
static assert(is( StaticArrayTypeOf!( Q!(void[1]) ) == Q!(void[1]) ));
}
@@ -5321,13 +5365,13 @@ template DynamicArrayTypeOf(T)
@safe unittest
{
- foreach (T; TypeTuple!(/*void, */bool, NumericTypeList, ImaginaryTypeList, ComplexTypeList))
- foreach (Q; TypeTuple!(TypeQualifierList, InoutOf, SharedInoutOf))
+ foreach (T; AliasSeq!(/*void, */bool, NumericTypeList, ImaginaryTypeList, ComplexTypeList))
+ foreach (Q; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf))
{
static assert(is( Q!T[] == DynamicArrayTypeOf!( Q!T[] ) ));
static assert(is( Q!(T[]) == DynamicArrayTypeOf!( Q!(T[]) ) ));
- foreach (P; TypeTuple!(MutableOf, ConstOf, ImmutableOf))
+ foreach (P; AliasSeq!(MutableOf, ConstOf, ImmutableOf))
{
static assert(is( Q!(P!T[]) == DynamicArrayTypeOf!( Q!(SubTypeOf!(P!T[])) ) ));
static assert(is( Q!(P!(T[])) == DynamicArrayTypeOf!( Q!(SubTypeOf!(P!(T[]))) ) ));
@@ -5377,7 +5421,7 @@ template StringTypeOf(T)
@safe unittest
{
foreach (T; CharTypeList)
- foreach (Q; TypeTuple!(MutableOf, ConstOf, ImmutableOf, InoutOf))
+ foreach (Q; AliasSeq!(MutableOf, ConstOf, ImmutableOf, InoutOf))
{
static assert(is(Q!T[] == StringTypeOf!( Q!T[] )));
@@ -5392,7 +5436,7 @@ template StringTypeOf(T)
}
foreach (T; CharTypeList)
- foreach (Q; TypeTuple!(SharedOf, SharedConstOf, SharedInoutOf))
+ foreach (Q; AliasSeq!(SharedOf, SharedConstOf, SharedInoutOf))
{
static assert(!is(StringTypeOf!( Q!T[] )));
}
@@ -5422,19 +5466,19 @@ template AssocArrayTypeOf(T)
@safe unittest
{
- foreach (T; TypeTuple!(int/*bool, CharTypeList, NumericTypeList, ImaginaryTypeList, ComplexTypeList*/))
- foreach (P; TypeTuple!(TypeQualifierList, InoutOf, SharedInoutOf))
- foreach (Q; TypeTuple!(TypeQualifierList, InoutOf, SharedInoutOf))
- foreach (R; TypeTuple!(TypeQualifierList, InoutOf, SharedInoutOf))
+ foreach (T; AliasSeq!(int/*bool, CharTypeList, NumericTypeList, ImaginaryTypeList, ComplexTypeList*/))
+ foreach (P; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf))
+ foreach (Q; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf))
+ foreach (R; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf))
{
static assert(is( P!(Q!T[R!T]) == AssocArrayTypeOf!( P!(Q!T[R!T]) ) ));
}
- foreach (T; TypeTuple!(int/*bool, CharTypeList, NumericTypeList, ImaginaryTypeList, ComplexTypeList*/))
- foreach (O; TypeTuple!(TypeQualifierList, InoutOf, SharedInoutOf))
- foreach (P; TypeTuple!TypeQualifierList)
- foreach (Q; TypeTuple!TypeQualifierList)
- foreach (R; TypeTuple!TypeQualifierList)
+ foreach (T; AliasSeq!(int/*bool, CharTypeList, NumericTypeList, ImaginaryTypeList, ComplexTypeList*/))
+ foreach (O; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf))
+ foreach (P; AliasSeq!TypeQualifierList)
+ foreach (Q; AliasSeq!TypeQualifierList)
+ foreach (R; AliasSeq!TypeQualifierList)
{
static assert(is( O!(P!(Q!T[R!T])) == AssocArrayTypeOf!( O!(SubTypeOf!(P!(Q!T[R!T]))) ) ));
}
@@ -5580,7 +5624,7 @@ enum bool isFloatingPoint(T) = __traits(isFloating, T) && !(is(Unqual!T == cfloa
{
enum EF : real { a = 1.414, b = 1.732, c = 2.236 }
- foreach (T; TypeTuple!(FloatingPointTypeList, EF))
+ foreach (T; AliasSeq!(FloatingPointTypeList, EF))
{
foreach (Q; TypeQualifierList)
{
@@ -5653,7 +5697,7 @@ enum bool isNumeric(T) = __traits(isArithmetic, T) && !(is(Unqual!T == bool) ||
@safe unittest
{
- foreach (T; TypeTuple!(NumericTypeList))
+ foreach (T; AliasSeq!(NumericTypeList))
{
foreach (Q; TypeQualifierList)
{
@@ -5754,7 +5798,7 @@ enum bool isUnsigned(T) = __traits(isUnsigned, T) && !(is(Unqual!T == char) ||
@safe unittest
{
- foreach (T; TypeTuple!(UnsignedIntTypeList))
+ foreach (T; AliasSeq!(UnsignedIntTypeList))
{
foreach (Q; TypeQualifierList)
{
@@ -5798,7 +5842,7 @@ enum bool isSigned(T) = __traits(isArithmetic, T) && !__traits(isUnsigned, T);
enum Eubyte : ubyte { e1 = 0 }
static assert(!isSigned!Eubyte);
- foreach (T; TypeTuple!(SignedIntTypeList))
+ foreach (T; AliasSeq!(SignedIntTypeList))
{
foreach (Q; TypeQualifierList)
{
@@ -5854,7 +5898,7 @@ enum bool isSomeChar(T) = is(CharTypeOf!T) && !isAggregateType!T;
{
enum EC : char { a = 'x', b = 'y' }
- foreach (T; TypeTuple!(CharTypeList, EC))
+ foreach (T; AliasSeq!(CharTypeList, EC))
{
foreach (Q; TypeQualifierList)
{
@@ -5906,7 +5950,7 @@ enum bool isSomeString(T) = is(StringTypeOf!T) && !isAggregateType!T && !isStati
@safe unittest
{
- foreach (T; TypeTuple!(char[], dchar[], string, wstring, dstring))
+ foreach (T; AliasSeq!(char[], dchar[], string, wstring, dstring))
{
static assert( isSomeString!( T ));
static assert(!isSomeString!(SubTypeOf!(T)));
@@ -5935,16 +5979,16 @@ enum bool isNarrowString(T) = (is(T : const char[]) || is(T : const wchar[])) &&
@safe unittest
{
- foreach (T; TypeTuple!(char[], string, wstring))
+ foreach (T; AliasSeq!(char[], string, wstring))
{
- foreach (Q; TypeTuple!(MutableOf, ConstOf, ImmutableOf)/*TypeQualifierList*/)
+ foreach (Q; AliasSeq!(MutableOf, ConstOf, ImmutableOf)/*TypeQualifierList*/)
{
static assert( isNarrowString!( Q!T ));
static assert(!isNarrowString!( SubTypeOf!(Q!T) ));
}
}
- foreach (T; TypeTuple!(int, int[], byte[], dchar[], dstring, char[4]))
+ foreach (T; AliasSeq!(int, int[], byte[], dchar[], dstring, char[4]))
{
foreach (Q; TypeQualifierList)
{
@@ -6039,6 +6083,10 @@ enum bool isStaticArray(T) = __traits(isStaticArray, T);
///
@safe unittest
{
+ static assert( isStaticArray!(int[3]));
+ static assert( isStaticArray!(const(int)[5]));
+ static assert( isStaticArray!(const(int)[][5]));
+
static assert(!isStaticArray!(const(int)[]));
static assert(!isStaticArray!(immutable(int)[]));
static assert(!isStaticArray!(const(int)[4][]));
@@ -6051,7 +6099,7 @@ enum bool isStaticArray(T) = __traits(isStaticArray, T);
@safe unittest
{
- foreach (T; TypeTuple!(int[51], int[][2],
+ foreach (T; AliasSeq!(int[51], int[][2],
char[][int][11], immutable char[13u],
const(real)[1], const(real)[1][1], void[0]))
{
@@ -6139,7 +6187,7 @@ enum bool isAssociativeArray(T) = __traits(isAssociativeArray, T);
@property uint[] values() { return null; }
}
- foreach (T; TypeTuple!(int[int], int[string], immutable(char[5])[int]))
+ foreach (T; AliasSeq!(int[int], int[string], immutable(char[5])[int]))
{
foreach (Q; TypeQualifierList)
{
@@ -6205,7 +6253,7 @@ enum bool isPointer(T) = is(T == U*, U) && !isAggregateType!T;
@safe unittest
{
- foreach (T; TypeTuple!(int*, void*, char[]*))
+ foreach (T; AliasSeq!(int*, void*, char[]*))
{
foreach (Q; TypeQualifierList)
{
@@ -7190,10 +7238,10 @@ template mostNegative(T)
///
@safe unittest
{
- foreach (T; TypeTuple!(bool, byte, short, int, long))
+ foreach (T; AliasSeq!(bool, byte, short, int, long))
static assert(mostNegative!T == T.min);
- foreach (T; TypeTuple!(ubyte, ushort, uint, ulong, char, wchar, dchar))
+ foreach (T; AliasSeq!(ubyte, ushort, uint, ulong, char, wchar, dchar))
static assert(mostNegative!T == 0);
}
@@ -7208,7 +7256,7 @@ template Promoted(T)
}
///
-unittest
+@safe unittest
{
ubyte a = 3, b = 5;
static assert(is(typeof(a * b) == Promoted!ubyte));
@@ -7219,17 +7267,17 @@ unittest
static assert(is(Promoted!double == double));
}
-unittest
+@safe unittest
{
// promote to int:
- foreach (T; TypeTuple!(bool, byte, ubyte, short, ushort, char, wchar))
+ foreach (T; AliasSeq!(bool, byte, ubyte, short, ushort, char, wchar))
{
static assert(is(Promoted!T == int));
static assert(is(Promoted!(shared(const T)) == shared(const int)));
}
// already promoted:
- foreach (T; TypeTuple!(int, uint, long, ulong, float, double, real))
+ foreach (T; AliasSeq!(int, uint, long, ulong, float, double, real))
{
static assert(is(Promoted!T == T));
static assert(is(Promoted!(immutable(T)) == immutable(T)));
@@ -7243,92 +7291,34 @@ unittest
/**
Returns the mangled name of symbol or type $(D sth).
-$(D mangledName) is the same as builtin $(D .mangleof) property, except that
-the correct names of property functions are obtained.
---------------------
-module test;
-import std.traits : mangledName;
-
-class C
-{
- int value() @property;
-}
-pragma(msg, C.value.mangleof); // prints "i"
-pragma(msg, mangledName!(C.value)); // prints "_D4test1C5valueMFNdZi"
---------------------
+$(D mangledName) is the same as builtin $(D .mangleof) property, but
+might be more convenient in generic code, e.g. as a template argument
+when invoking staticMap.
*/
template mangledName(sth...)
if (sth.length == 1)
{
- static if (is(typeof(sth[0]) X) && is(X == void))
- {
- // sth[0] is a template symbol
- enum string mangledName = removeDummyEnvelope(Dummy!sth.Hook.mangleof);
- }
- else
- {
- enum string mangledName = sth[0].mangleof;
- }
+ enum string mangledName = sth[0].mangleof;
}
-private template Dummy(T...) { struct Hook {} }
-
-private string removeDummyEnvelope(string s)
+///
+@safe unittest
{
- // remove --> S3std6traits ... Z4Hook
- s = s[12 .. $ - 6];
-
- // remove --> DIGIT+ __T5Dummy
- foreach (i, c; s)
- {
- if (c < '0' || '9' < c)
- {
- s = s[i .. $];
- break;
- }
- }
- s = s[9 .. $]; // __T5Dummy
-
- // remove --> T | V | S
- immutable kind = s[0];
- s = s[1 .. $];
-
- if (kind == 'S') // it's a symbol
- {
- /*
- * The mangled symbol name is packed in LName --> Number Name. Here
- * we are chopping off the useless preceding Number, which is the
- * length of Name in decimal notation.
- *
- * NOTE: n = m + Log(m) + 1; n = LName.length, m = Name.length.
- */
- immutable n = s.length;
- size_t m_upb = 10;
-
- foreach (k; 1 .. 5) // k = Log(m_upb)
- {
- if (n < m_upb + k + 1)
- {
- // Now m_upb/10 <= m < m_upb; hence k = Log(m) + 1.
- s = s[k .. $];
- break;
- }
- m_upb *= 10;
- }
- }
-
- return s;
+ alias TL = staticMap!(mangledName, int, const int, immutable int);
+ static assert(TL == AliasSeq!("i", "xi", "yi"));
}
+version(unittest) void freeFunc(string);
+
@safe unittest
{
class C { int value() @property { return 0; } }
static assert(mangledName!int == int.mangleof);
static assert(mangledName!C == C.mangleof);
+ static assert(mangledName!(C.value) == C.value.mangleof);
static assert(mangledName!(C.value)[$ - 12 .. $] == "5valueMFNdZi");
static assert(mangledName!mangledName == "3std6traits11mangledName");
- static assert(mangledName!removeDummyEnvelope ==
- "_D3std6traits19removeDummyEnvelopeFAyaZAya");
+ static assert(mangledName!freeFunc == "_D3std6traits8freeFuncFAyaZv");
int x;
static if (is(typeof({ return x; }) : int delegate() pure)) // issue 9148
static assert(mangledName!((int a) { return a+x; }) == "DFNaNbNiNfiZi"); // pure nothrow @safe @nogc
@@ -7783,7 +7773,7 @@ template getSymbolsByUDA(alias symbol, alias attribute)
}
// #16387: getSymbolsByUDA works with structs but fails with classes
-unittest
+@safe unittest
{
enum Attr;
class A
@@ -7984,4 +7974,5 @@ enum isCopyable(S) = is(typeof(
static assert(isCopyable!C1);
static assert(isCopyable!int);
+ static assert(isCopyable!(int[]));
}
diff --git a/std/typecons.d b/std/typecons.d
index 44b986ff759..ff79209ecb5 100644
--- a/std/typecons.d
+++ b/std/typecons.d
@@ -4,31 +4,58 @@
This module implements a variety of type constructors, i.e., templates
that allow construction of new, useful general-purpose types.
-Source: $(PHOBOSSRC std/_typecons.d)
-
-Synopsis:
-
-----
-// value tuples
-alias Coord = Tuple!(float, "x", float, "y", float, "z");
-Coord c;
-c[1] = 1; // access by index
-c.z = 1; // access by given name
-alias DicEntry = Tuple!(string, string); // names can be omitted
-
-// Rebindable references to const and immutable objects
-void bar()
-{
- const w1 = new Widget, w2 = new Widget;
- w1.foo();
- // w1 = w2 would not work; can't rebind const object
- auto r = Rebindable!(const Widget)(w1);
- // invoke method as if r were a Widget object
- r.foo();
- // rebind r to refer to another object
- r = w2;
-}
-----
+$(SCRIPT inhibitQuickIndex = 1;)
+$(BOOKTABLE,
+$(TR $(TH Category) $(TH Functions))
+$(TR $(TD Tuple) $(TD
+ $(LREF isTuple)
+ $(LREF Tuple)
+ $(LREF tuple)
+ $(LREF reverse)
+))
+$(TR $(TD Flags) $(TD
+ $(LREF BitFlags)
+ $(LREF isBitFlagEnum)
+ $(LREF Flag)
+ $(LREF No)
+ $(LREF Yes)
+))
+$(TR $(TD Memory allocation) $(TD
+ $(LREF RefCounted)
+ $(LREF refCounted)
+ $(LREF RefCountedAutoInitialize)
+ $(LREF scoped)
+ $(LREF Unique)
+))
+$(TR $(TD Code generation) $(TD
+ $(LREF AutoImplement)
+ $(LREF BlackHole)
+ $(LREF generateAssertTrap)
+ $(LREF generateEmptyFunction)
+ $(LREF WhiteHole)
+))
+$(TR $(TD Nullable) $(TD
+ $(LREF Nullable)
+ $(LREF nullable)
+ $(LREF NullableRef)
+ $(LREF nullableRef)
+))
+$(TR $(TD Proxies) $(TD
+ $(LREF Proxy)
+ $(LREF rebindable)
+ $(LREF Rebindable)
+ $(LREF ReplaceType)
+ $(LREF unwrap)
+ $(LREF wrap)
+))
+$(TR $(TD Types) $(TD
+ $(LREF alignForSize)
+ $(LREF Ternary)
+ $(LREF Typedef)
+ $(LREF TypedefType)
+ $(LREF UnqualRef)
+))
+)
Copyright: Copyright the respective authors, 2008-
License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
@@ -44,6 +71,39 @@ import core.stdc.stdint : uintptr_t;
import std.meta; // : AliasSeq, allSatisfy;
import std.traits;
+///
+@safe unittest
+{
+ // value tuples
+ alias Coord = Tuple!(int, "x", int, "y", int, "z");
+ Coord c;
+ c[1] = 1; // access by index
+ c.z = 1; // access by given name
+ assert(c == Coord(0, 1, 1));
+
+ // names can be omitted
+ alias DicEntry = Tuple!(string, string);
+
+ // tuples can also be constructed on instantiation
+ assert(tuple(2, 3, 4)[1] == 3);
+ // construction on instantiation works with names too
+ assert(tuple!("x", "y", "z")(2, 3, 4).y == 3);
+
+ // Rebindable references to const and immutable objects
+ {
+ class Widget { void foo() const @safe {} }
+ const w1 = new Widget, w2 = new Widget;
+ w1.foo();
+ // w1 = w2 would not work; can't rebind const object
+ auto r = Rebindable!(const Widget)(w1);
+ // invoke method as if r were a Widget object
+ r.foo();
+ // rebind r to refer to another object
+ r = w2;
+ }
+}
+
+Source: $(PHOBOSSRC std/_typecons.d)
debug(Unique) import std.stdio;
/**
@@ -61,8 +121,9 @@ $(D T) to deallocate or clean up any non-GC resources.
If it is desirable to persist a $(D Unique!T) outside of its original
scope, then it can be transferred. The transfer can be explicit, by
calling $(D release), or implicit, when returning Unique from a
-function. The resource $(D T) can be a polymorphic class object, in
-which case Unique behaves polymorphically too.
+function. The resource $(D T) can be a polymorphic class object or
+instance of an interface, in which case Unique behaves polymorphically
+too.
If $(D T) is a value type, then $(D Unique!T) will be implemented
as a reference to a $(D T).
@@ -70,7 +131,7 @@ as a reference to a $(D T).
struct Unique(T)
{
/** Represents a reference to $(D T). Resolves to $(D T*) if $(D T) is a value type. */
-static if (is(T:Object))
+static if (is(T == class) || is(T == interface))
alias RefT = T;
else
alias RefT = T*;
@@ -280,6 +341,51 @@ private:
assert(!ub2.isEmpty);
}
+@system unittest
+{
+ debug(Unique) writeln("Unique interface");
+ interface Bar
+ {
+ int val() const;
+ }
+ class BarImpl : Bar
+ {
+ static int count;
+ this()
+ {
+ count++;
+ }
+ ~this()
+ {
+ count--;
+ }
+ int val() const { return 4; }
+ }
+ alias UBar = Unique!Bar;
+ UBar g(UBar u)
+ {
+ debug(Unique) writeln("inside g");
+ return u.release;
+ }
+ void consume(UBar u)
+ {
+ assert(u.val() == 4);
+ // Resource automatically deleted here
+ }
+ auto ub = UBar(new BarImpl);
+ assert(BarImpl.count == 1);
+ assert(!ub.isEmpty);
+ assert(ub.val == 4);
+ static assert(!__traits(compiles, {auto ub3 = g(ub);}));
+ debug(Unique) writeln("Calling g");
+ auto ub2 = g(ub.release);
+ debug(Unique) writeln("Returned from g");
+ assert(ub.isEmpty);
+ assert(!ub2.isEmpty);
+ consume(ub2.release);
+ assert(BarImpl.count == 0);
+}
+
@system unittest
{
debug(Unique) writeln("Unique struct");
@@ -1255,7 +1361,7 @@ private template ReverseTupleSpecs(T...)
}
// ensure that internal Tuple unittests are compiled
-unittest
+@safe unittest
{
Tuple!() t;
}
@@ -2859,7 +2965,7 @@ Returns:
// https://issues.dlang.org/show_bug.cgi?id=11135
// disable test until https://issues.dlang.org/show_bug.cgi?id=15316 gets fixed
-version (none) unittest
+version (none) @system unittest
{
foreach (T; AliasSeq!(float, double, real))
{
@@ -4717,6 +4823,7 @@ if (!isMutable!Target)
///
@system unittest
{
+ import std.traits : FunctionAttribute, functionAttributes;
interface A { int run(); }
interface B { int stop(); @property int status(); }
class X
@@ -5152,7 +5259,7 @@ scope, they will automatically increment or decrement the reference
count. When the reference count goes down to zero, $(D RefCounted)
will call $(D destroy) against the payload and call $(D free) to
deallocate the store. If the $(D T) payload contains any references
-to GC-allocated memory, then $(RefCounted) will add it to the GC memory
+to GC-allocated memory, then `RefCounted` will add it to the GC memory
that is scanned for pointers, and remove it from GC scanning before
$(D free) is called on the store.
@@ -5425,7 +5532,7 @@ assert(refCountedStore.isInitialized)).
///
pure @system nothrow @nogc unittest
{
- // A pair of an $(D int) and a $(D size_t) - the latter being the
+ // A pair of an `int` and a `size_t` - the latter being the
// reference count - will be dynamically allocated
auto rc1 = RefCounted!int(5);
assert(rc1 == 5);
@@ -5720,7 +5827,7 @@ mixin template Proxy(alias a)
static if (!is(typeof(this) == class))
{
- private import std.traits;
+ import std.traits;
static if (isAssignable!ValueType)
{
auto ref opAssign(this X)(auto ref typeof(this) v)
@@ -5740,10 +5847,22 @@ mixin template Proxy(alias a)
auto ref opSliceAssign(this X, V )(auto ref V v) { return a[] = v; }
auto ref opSliceAssign(this X, V, B, E)(auto ref V v, auto ref B b, auto ref E e) { return a[b .. e] = v; }
- auto ref opOpAssign (string op, this X, V )(auto ref V v) { return mixin("a " ~op~"= v"); }
- auto ref opIndexOpAssign(string op, this X, V, D...)(auto ref V v, auto ref D i) { return mixin("a[i] " ~op~"= v"); }
- auto ref opSliceOpAssign(string op, this X, V )(auto ref V v) { return mixin("a[] " ~op~"= v"); }
- auto ref opSliceOpAssign(string op, this X, V, B, E)(auto ref V v, auto ref B b, auto ref E e) { return mixin("a[b .. e] "~op~"= v"); }
+ auto ref opOpAssign (string op, this X, V )(auto ref V v)
+ {
+ return mixin("a " ~op~"= v");
+ }
+ auto ref opIndexOpAssign(string op, this X, V, D...)(auto ref V v, auto ref D i)
+ {
+ return mixin("a[i] " ~op~"= v");
+ }
+ auto ref opSliceOpAssign(string op, this X, V )(auto ref V v)
+ {
+ return mixin("a[] " ~op~"= v");
+ }
+ auto ref opSliceOpAssign(string op, this X, V, B, E)(auto ref V v, auto ref B b, auto ref E e)
+ {
+ return mixin("a[b .. e] "~op~"= v");
+ }
template opDispatch(string name)
{
@@ -6630,7 +6749,7 @@ template scoped(T)
size_t* currD = cast(size_t*) &Scoped_store[$ - size_t.sizeof];
if (d != *currD)
{
- import core.stdc.string;
+ import core.stdc.string : memmove;
memmove(alignedStore, Scoped_store.ptr + *currD, __traits(classInstanceSize, T));
*currD = d;
}
@@ -7328,6 +7447,8 @@ public:
/// BitFlags can be manipulated with the usual operators
@safe @nogc pure nothrow unittest
{
+ import std.traits : EnumMembers;
+
// You can use such an enum with BitFlags straight away
enum Enum
{
@@ -7339,7 +7460,7 @@ public:
BitFlags!Enum flags1;
assert(!(flags1 & (Enum.A | Enum.B | Enum.C)));
- // You need to specify the $(D unsafe) parameter for enum with custom values
+ // You need to specify the `unsafe` parameter for enum with custom values
enum UnsafeEnum
{
A,
diff --git a/std/uni.d b/std/uni.d
index d71e482063b..88307e6e00a 100644
--- a/std/uni.d
+++ b/std/uni.d
@@ -2069,8 +2069,6 @@ pure:
{
import std.range : assumeSorted;
-public:
-
/**
Construct from another code point set of any type.
*/
@@ -2436,7 +2434,7 @@ public:
---
*/
- private import std.format : FormatException, FormatSpec;
+ private import std.format : FormatSpec;
/***************************************
* Obtain a textual representation of this InversionList
@@ -2495,7 +2493,7 @@ public:
@safe unittest
{
import std.exception : assertThrown;
- import std.format : format;
+ import std.format : format, FormatException;
assertThrown!FormatException(format("%a", unicode.ASCII));
}
@@ -2546,7 +2544,6 @@ private:
return this;
}
- ///
@safe unittest
{
assert(unicode.Cyrillic.intersect('-').byInterval.empty);
@@ -4340,7 +4337,6 @@ if (sumOfIntegerTuple!sizes == 21)
}
}
-///
@system pure unittest
{
import std.algorithm.comparison : max;
@@ -4644,7 +4640,6 @@ public struct MatcherConcept
return this;
}
- ///
@safe unittest
{
auto m = utfMatcher!char(unicode.Number);
@@ -4762,9 +4757,9 @@ template Utf8Matcher()
static auto encode(size_t sz)(dchar ch)
if (sz > 1)
{
- import std.utf : encode;
+ import std.utf : encodeUTF = encode;
char[4] buf;
- std.utf.encode(buf, ch);
+ encodeUTF(buf, ch);
char[sz] ret;
buf[0] &= leadMask!sz;
foreach (n; 1 .. sz)
@@ -6288,7 +6283,8 @@ enum EMPTY_CASE_TRIE = ushort.max;// from what gen_uni uses internally
// control - '\r'
enum controlSwitch = `
- case '\u0000':..case '\u0008':case '\u000E':..case '\u001F':case '\u007F':..case '\u0084':case '\u0086':..case '\u009F': case '\u0009':..case '\u000C': case '\u0085':
+ case '\u0000':..case '\u0008':case '\u000E':..case '\u001F':case '\u007F':..
+ case '\u0084':case '\u0086':..case '\u009F': case '\u0009':..case '\u000C': case '\u0085':
`;
// TODO: redo the most of hangul stuff algorithmically in case of Graphemes too
// kill unrolled switches
@@ -7379,7 +7375,7 @@ package auto simpleCaseFoldings(dchar ch) @safe
return len == 0;
}
- @property uint length() const
+ @property size_t length() const
{
if (isSmall)
{
diff --git a/std/uri.d b/std/uri.d
index 75208e1982c..0852955a9b8 100644
--- a/std/uri.d
+++ b/std/uri.d
@@ -24,8 +24,8 @@
module std.uri;
//debug=uri; // uncomment to turn on debugging writefln's
-debug(uri) private import std.stdio;
-private import std.traits : isSomeChar;
+debug(uri) import std.stdio;
+import std.traits : isSomeChar;
/** This Exception is thrown if something goes wrong when encoding or
decoding a URI.
diff --git a/std/utf.d b/std/utf.d
index 339243cfcef..4f4344ba295 100644
--- a/std/utf.d
+++ b/std/utf.d
@@ -65,10 +65,6 @@ import std.range.primitives;
import std.traits; // isSomeChar, isSomeString
import std.typecons; // Flag, Yes, No
-//debug=utf; // uncomment to turn on debugging printf's
-
-debug (utf) import core.stdc.stdio : printf;
-
/++
Exception thrown on errors in std.utf functions.
@@ -259,7 +255,6 @@ bool isValidDchar(dchar c) pure nothrow @safe @nogc
pure nothrow @safe @nogc unittest
{
import std.exception;
- debug(utf) printf("utf.isValidDchar.unittest\n");
assertCTFEable!(
{
@@ -1895,7 +1890,6 @@ version(unittest) private void testBadDecodeBack(R)(R range, size_t line = __LIN
{
import std.conv : to;
import std.exception;
- debug(utf) printf("utf.decode.unittest\n");
assertCTFEable!(
{
@@ -2371,7 +2365,6 @@ void encode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar)(
@safe unittest
{
import std.exception;
- debug(utf) printf("utf.encode.unittest\n");
assertCTFEable!(
{
@@ -3118,7 +3111,6 @@ if (isSomeChar!C)
@safe pure unittest
{
import std.exception;
- debug(utf) printf("utf.toUTF.unittest\n");
assertCTFEable!(
{
diff --git a/std/uuid.d b/std/uuid.d
index a2db742c4bb..c804e8eb7e8 100644
--- a/std/uuid.d
+++ b/std/uuid.d
@@ -1710,6 +1710,18 @@ public class UUIDParsingException : Exception
}
///
+@safe unittest
+{
+ import std.exception : collectException;
+
+ const inputUUID = "this-is-an-invalid-uuid";
+ auto ex = collectException!UUIDParsingException(UUID(inputUUID));
+ assert(ex !is null); // check that exception was thrown
+ assert(ex.input == inputUUID);
+ assert(ex.position == 0);
+ assert(ex.reason == UUIDParsingException.Reason.tooLittle);
+}
+
@safe unittest
{
auto ex = new UUIDParsingException("foo", 10, UUIDParsingException.Reason.tooMuch);
diff --git a/std/variant.d b/std/variant.d
index 8ca53732568..574e2c5a375 100644
--- a/std/variant.d
+++ b/std/variant.d
@@ -10,38 +10,6 @@ Such types are useful
for type-uniform binary interfaces, interfacing with scripting
languages, and comfortable exploratory programming.
-Synopsis:
-----
-Variant a; // Must assign before use, otherwise exception ensues
-// Initialize with an integer; make the type int
-Variant b = 42;
-assert(b.type == typeid(int));
-// Peek at the value
-assert(b.peek!(int) !is null && *b.peek!(int) == 42);
-// Automatically convert per language rules
-auto x = b.get!(real);
-// Assign any other type, including other variants
-a = b;
-a = 3.14;
-assert(a.type == typeid(double));
-// Implicit conversions work just as with built-in types
-assert(a < b);
-// Check for convertibility
-assert(!a.convertsTo!(int)); // double not convertible to int
-// Strings and all other arrays are supported
-a = "now I'm a string";
-assert(a == "now I'm a string");
-a = new int[42]; // can also assign arrays
-assert(a.length == 42);
-a[5] = 7;
-assert(a[5] == 7);
-// Can also assign class values
-class Foo {}
-auto foo = new Foo;
-a = foo;
-assert(*a.peek!(Foo) == foo); // and full type information is preserved
-----
-
A $(LREF Variant) object can hold a value of any type, with very few
restrictions (such as `shared` types and noncopyable types). Setting the value
is as immediate as assigning to the `Variant` object. To read back the value of
@@ -67,6 +35,43 @@ module std.variant;
import std.meta, std.traits, std.typecons;
+///
+@system unittest
+{
+ Variant a; // Must assign before use, otherwise exception ensues
+ // Initialize with an integer; make the type int
+ Variant b = 42;
+ assert(b.type == typeid(int));
+ // Peek at the value
+ assert(b.peek!(int) !is null && *b.peek!(int) == 42);
+ // Automatically convert per language rules
+ auto x = b.get!(real);
+
+ // Assign any other type, including other variants
+ a = b;
+ a = 3.14;
+ assert(a.type == typeid(double));
+ // Implicit conversions work just as with built-in types
+ assert(a < b);
+ // Check for convertibility
+ assert(!a.convertsTo!(int)); // double not convertible to int
+ // Strings and all other arrays are supported
+ a = "now I'm a string";
+ assert(a == "now I'm a string");
+
+ // can also assign arrays
+ a = new int[42];
+ assert(a.length == 42);
+ a[5] = 7;
+ assert(a[5] == 7);
+
+ // Can also assign class values
+ class Foo {}
+ auto foo = new Foo;
+ a = foo;
+ assert(*a.peek!(Foo) == foo); // and full type information is preserved
+}
+
/++
Gives the $(D sizeof) the largest type given.
+/
diff --git a/std/windows/charset.d b/std/windows/charset.d
index c6abe7a381a..ee7211d446b 100644
--- a/std/windows/charset.d
+++ b/std/windows/charset.d
@@ -50,11 +50,10 @@ else:
version (Windows):
-private import core.sys.windows.windows;
-private import std.conv;
-private import std.string;
-private import std.utf;
-private import std.windows.syserror;
+import core.sys.windows.windows;
+import std.conv;
+import std.string;
+import std.windows.syserror;
import std.internal.cstring;
@@ -114,7 +113,7 @@ string fromMBSz(immutable(char)* s, int codePage = 0)
sysErrorString(GetLastError()));
}
- return std.utf.toUTF8(result[0 .. result.length-1]); // omit trailing null
+ return result[0 .. result.length-1].to!string; // omit trailing null
}
}
return s[0 .. c-s]; // string is ASCII, no conversion necessary
diff --git a/std/windows/registry.d b/std/windows/registry.d
index 0b5e3d2d1c3..073e568df65 100644
--- a/std/windows/registry.d
+++ b/std/windows/registry.d
@@ -43,9 +43,8 @@ import std.array;
import std.conv;
import std.exception;
import std.internal.cstring;
-private import std.internal.windows.advapi32;
+import std.internal.windows.advapi32;
import std.system : Endian, endian;
-import std.utf : toUTF8, toUTF16;
import std.windows.syserror;
//debug = winreg;
@@ -224,7 +223,7 @@ enum REG_VALUE_TYPE : DWORD
/* ************* private *************** */
-private import core.sys.windows.winnt :
+import core.sys.windows.winnt :
DELETE ,
READ_CONTROL ,
WRITE_DAC ,
@@ -241,7 +240,7 @@ private import core.sys.windows.winnt :
SPECIFIC_RIGHTS_ALL ;
-private import core.sys.windows.winreg :
+import core.sys.windows.winreg :
REG_CREATED_NEW_KEY ,
REG_OPENED_EXISTING_KEY ;
@@ -557,7 +556,7 @@ body
if (wstr.length && wstr[$-1] == '\0')
wstr.length = wstr.length - 1;
assert(wstr.length == 0 || wstr[$-1] != '\0');
- value = toUTF8(wstr);
+ value = wstr.to!string;
break;
case REG_VALUE_TYPE.REG_DWORD_LITTLE_ENDIAN:
@@ -623,7 +622,7 @@ body
value.length = list.length;
foreach (i, ref v; value)
{
- v = toUTF8(list[i]);
+ v = list[i].to!string;
}
}
@@ -752,7 +751,7 @@ private void regProcessNthKey(HKEY hkey, scope void delegate(scope LONG delegate
immutable res = regEnumKeyName(hkey, index, sName, cchName);
if (res == ERROR_SUCCESS)
{
- name = toUTF8(sName[0 .. cchName]);
+ name = sName[0 .. cchName].to!string;
}
return res;
});
@@ -774,7 +773,7 @@ private void regProcessNthValue(HKEY hkey, scope void delegate(scope LONG delega
immutable res = regEnumValueName(hkey, index, sName, cchName);
if (res == ERROR_SUCCESS)
{
- name = toUTF8(sName[0 .. cchName]);
+ name = sName[0 .. cchName].to!string;
}
return res;
});
@@ -1098,7 +1097,7 @@ public:
wstring[] data = new wstring[value.length+1];
foreach (i, ref s; data[0..$-1])
{
- s = toUTF16(value[i]);
+ s = value[i].to!wstring;
}
data[$-1] = "\0";
auto ws = std.array.join(data, "\0"w);
@@ -1235,7 +1234,7 @@ public:
ExpandEnvironmentStringsW(srcTmp, newValue.ptr, to!DWORD(newValue.length)),
"Failed to expand environment variables");
- return toUTF8(newValue[0 .. count-1]); // remove trailing 0
+ return newValue[0 .. count-1].to!string; // remove trailing 0
}
/**
diff --git a/std/zip.d b/std/zip.d
index 47a3aa27a78..f27bbe0d6b1 100644
--- a/std/zip.d
+++ b/std/zip.d
@@ -98,7 +98,7 @@ enum CompressionMethod : ushort
final class ArchiveMember
{
import std.conv : to, octal;
- import std.datetime : DosFileTime, SysTime, SysTimeToDosFileTime;
+ import std.datetime.systime : DosFileTime, SysTime, SysTimeToDosFileTime;
/**
* Read/Write: Usually the file name of the archive member; it is used to
@@ -289,7 +289,7 @@ final class ZipArchive
import std.algorithm.comparison : max;
import std.bitmanip : littleEndianToNative, nativeToLittleEndian;
import std.conv : to;
- import std.datetime : DosFileTime;
+ import std.datetime.systime : DosFileTime;
string comment; /// Read/Write: the archive comment. Must be less than 65536 bytes in length.
diff --git a/std/zlib.d b/std/zlib.d
index 7965f3055b0..e6cce240fd5 100644
--- a/std/zlib.d
+++ b/std/zlib.d
@@ -674,8 +674,8 @@ class UnCompress
/* ========================== unittest ========================= */
-private import std.random;
-private import std.stdio;
+import std.random;
+import std.stdio;
@system unittest // by Dave
{
diff --git a/win32.mak b/win32.mak
index 22a6de97fa8..4c6821c42c2 100644
--- a/win32.mak
+++ b/win32.mak
@@ -43,7 +43,7 @@ DRUNTIMELIB=$(DRUNTIME)\lib\druntime.lib
## Flags for dmd D compiler
-DFLAGS=-conf= -O -release -w -dip25 -I$(DRUNTIME)\import
+DFLAGS=-conf= -O -release -w -de -dip25 -I$(DRUNTIME)\import
#DFLAGS=-unittest -g
#DFLAGS=-unittest -cov -g
@@ -208,7 +208,8 @@ SRC_STD_DIGEST= \
std\digest\ripemd.d \
std\digest\digest.d \
std\digest\hmac.d \
- std\digest\murmurhash.d
+ std\digest\murmurhash.d \
+ std\digest\package.d
SRC_STD_NET= \
std\net\isemail.d \
diff --git a/win64.mak b/win64.mak
index b0c49217a7a..6a7c552b4e0 100644
--- a/win64.mak
+++ b/win64.mak
@@ -46,7 +46,7 @@ DRUNTIMELIB=$(DRUNTIME)\lib\druntime$(MODEL).lib
## Flags for dmd D compiler
-DFLAGS=-conf= -m$(MODEL) -O -release -w -dip25 -I$(DRUNTIME)\import
+DFLAGS=-conf= -m$(MODEL) -O -release -w -de -dip25 -I$(DRUNTIME)\import
#DFLAGS=-m$(MODEL) -unittest -g
#DFLAGS=-m$(MODEL) -unittest -cov -g
@@ -233,7 +233,8 @@ SRC_STD_DIGEST= \
std\digest\ripemd.d \
std\digest\digest.d \
std\digest\hmac.d \
- std\digest\murmurhash.d
+ std\digest\murmurhash.d \
+ std\digest\package.d
SRC_STD_NET= \
std\net\isemail.d \