diff --git a/.dscanner.ini b/.dscanner.ini index 0404ee5c9..a5610f7d1 100644 --- a/.dscanner.ini +++ b/.dscanner.ini @@ -114,16 +114,16 @@ 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.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" +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.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" +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.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" +;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 @@ -138,26 +138,26 @@ long_line_check="-std.datetime.timezone" 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.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" +;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.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" +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.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" +;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.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" +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.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" +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" diff --git a/changelog/std-digest-package.dd b/changelog/std-digest-package.dd new file mode 100644 index 000000000..e94f7d821 --- /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/posix.mak b/posix.mak index c624f34e2..dbba3ce33 100644 --- a/posix.mak +++ b/posix.mak @@ -189,7 +189,7 @@ 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 = \ diff --git a/std/digest/crc.d b/std/digest/crc.d index 7aaff6551..33f8ca1b1 100644 --- a/std/digest/crc.d +++ b/std/digest/crc.d @@ -18,16 +18,16 @@ $(TR $(TDNW Helpers) $(TD $(MYREF crcHexString) $(MYREF crc32Of)) ) * - * 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 $(D 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. * * 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)) */ 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 1d2eaf541..175b9a696 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 516f6d6c6..a2689d155 100644 --- a/std/digest/hmac.d +++ b/std/digest/hmac.d @@ -16,7 +16,7 @@ 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; /** @@ -26,7 +26,7 @@ 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) @@ -286,7 +286,7 @@ if (isDigest!H) version(unittest) { - import std.digest.digest : toHexString, LetterCase; + import std.digest : toHexString, LetterCase; alias hex = toHexString!(LetterCase.lower); } diff --git a/std/digest/md.d b/std/digest/md.d index 8ef7bbcb5..5f15d958b 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 $(D 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). @@ -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 89b4b1cd4..e8930a7cc 100644 --- a/std/digest/murmurhash.d +++ b/std/digest/murmurhash.d @@ -22,9 +22,9 @@ $(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 $(D std.digest). -This module publicly imports $(D std.digest.digest) and can be used as a stand-alone module. +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). Authors: Guillaume Chatelet @@ -41,7 +41,7 @@ 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]); @@ -88,7 +88,7 @@ module std.digest.murmurhash; auto hashed = hasher.getBytes(); } -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 000000000..e3b2dfe79 --- /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 0dd472c03..60420f4c3 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 $(D std.digest). To understand the + * differences between the template and the OOP API, see $(D 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 4f661f7c2..91ffd5502 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 $(D std.digest). To understand the + * differences between the template and the OOP API, see $(D 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/win32.mak b/win32.mak index e83ef7965..4c6821c42 100644 --- a/win32.mak +++ b/win32.mak @@ -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 c97fd1e4e..6a7c552b4 100644 --- a/win64.mak +++ b/win64.mak @@ -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 \