├── .circleci └── config.yml ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── README.md ├── dub.sdl ├── index.d ├── meson.build ├── proposed-mir-architecture.gv ├── source └── mir │ ├── algebraic.d │ ├── bitmanip.d │ ├── bitop.d │ ├── checkedint.d │ ├── complex │ ├── math.d │ └── package.d │ ├── conv.d │ ├── enums.d │ ├── exception.d │ ├── functional.d │ ├── internal │ ├── memory.d │ ├── meta.d │ └── utility.d │ ├── math │ ├── common.d │ ├── constant.d │ ├── ieee.d │ └── package.d │ ├── primitives.d │ ├── qualifier.d │ ├── reflection.d │ ├── string_table.d │ └── utility.d └── test_examples.sh /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | orbs: 4 | mirci: libmir/upload_docs@0.3.0 5 | 6 | workflows: 7 | version: 2 8 | build-deploy: 9 | jobs: 10 | - mirci/test_and_build_docs: 11 | filters: 12 | tags: 13 | only: /^v(\d)+(\.(\d)+)+$/ 14 | - mirci/upload_docs: 15 | to: mir-core.libmir.org 16 | requires: 17 | - mirci/test_and_build_docs 18 | filters: 19 | branches: 20 | ignore: /.*/ 21 | tags: 22 | only: /^v(\d)+(\.(\d)+)+$/ 23 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | workflow_dispatch: 11 | # allow this workflow to be triggered manually 12 | 13 | jobs: 14 | builder: 15 | name: 'Build and test on ${{ matrix.arch }}-${{ matrix.os }}/${{ matrix.dc }}' 16 | runs-on: ${{ matrix.os }} 17 | continue-on-error: ${{ contains(matrix.dc, 'beta') }} 18 | env: 19 | ARCH: ${{ matrix.arch }} 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | dc: [ldc-latest, ldc-beta, dmd-latest, dmd-beta] 24 | os: [ubuntu-latest, windows-latest] 25 | arch: [x86, x86_64] 26 | include: 27 | - dc: ldc-latest 28 | os: macos-latest 29 | arch: x86_64 30 | - dc: dmd-latest 31 | os: macos-latest 32 | arch: x86_64 33 | steps: 34 | - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 35 | - uses: dlang-community/setup-dlang@763d869b4d67e50c3ccd142108c8bca2da9df166 36 | with: 37 | compiler: ${{ matrix.dc }} 38 | - name: Install multi-lib for 32-bit systems 39 | if: matrix.arch == 'x86' && matrix.os == 'ubuntu-latest' 40 | run: sudo apt-get install gcc-multilib 41 | - id: build 42 | name: Test building 43 | run: | 44 | dub test --arch=$ARCH --build=unittest-cov 45 | shell: bash 46 | - id: coverage 47 | uses: codecov/codecov-action@f32b3a3741e1053eb607407145bc9619351dc93b 48 | 49 | 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.a 2 | *.log 3 | *.lst 4 | *.o 5 | *.pdf 6 | *.s 7 | *.exe 8 | *.sublime-project 9 | *.sublime-workspace 10 | .dub 11 | .generated 12 | .vscode 13 | __* 14 | dub.selections.json 15 | web 16 | .DS_* 17 | docs 18 | *.html 19 | docs.json 20 | out/ 21 | build/ 22 | files 23 | *.lib 24 | mir-core-test-library 25 | bench_ldexp_frexp 26 | builddir 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/libmir/mir-core.svg?branch=master)](https://travis-ci.org/libmir/mir-core) 2 | 3 | Mir Core 4 | ============== 5 | 6 | Base software building blocks: Algebraic types (aka sumtype/tagged union/variant), universal reflection API, basic math, and more. 7 | 8 | #### Code Constraints 9 | 10 | 1. generic code only 11 | 2. no runtime dependency : betterC compatible when compiled with LDC in release mode. Exceptions: `@nogc` `mir.exception`. 12 | 3. no complex algorithms 13 | -------------------------------------------------------------------------------- /dub.sdl: -------------------------------------------------------------------------------- 1 | name "mir-core" 2 | description "Base software building blocks: Algebraic types (aka sumtype/tagged union/variant), universal reflection API, basic math, and more" 3 | 4 | authors "Ilia Ki" "Phobos authors (see information per file)" 5 | copyright "Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments" 6 | license "Apache-2.0" 7 | 8 | buildType "unittest" { 9 | buildOptions "unittests" "debugMode" "debugInfo" 10 | versions "mir_core_test" 11 | } 12 | 13 | buildType "unittest-verbose" { 14 | buildOptions "unittests" "debugMode" "debugInfo" 15 | versions "mir_core_test" 16 | dflags "-checkaction=context" "-allinst" 17 | } 18 | 19 | buildType "unittest-dip1008" { 20 | buildOptions "unittests" "debugMode" "debugInfo" 21 | versions "mir_core_test" 22 | dflags "-lowmem" "-preview=dip1008" 23 | } 24 | 25 | buildType "unittest-dip1000" { 26 | buildOptions "unittests" "debugMode" "debugInfo" 27 | versions "mir_core_test" 28 | dflags "-lowmem" "-preview=dip1000" 29 | } 30 | -------------------------------------------------------------------------------- /index.d: -------------------------------------------------------------------------------- 1 | Ddoc 2 | 3 | $(P JSON Parsing and Serialization library.) 4 | 5 | $(P The following table is a quick reference guide for which Mir Core modules to 6 | use for a given category of functionality.) 7 | 8 | $(BOOKTABLE , 9 | $(TR 10 | $(TH Modules) 11 | $(TH Description) 12 | ) 13 | $(TR $(TDNW $(MREF mir,algebraic)) $(TD Generic variant and nullable types )) 14 | $(TR $(TDNW $(MREF mir,exception)) $(TD @nogc MirException with formatting)) 15 | $(TR $(TDNW $(MREF mir,reflection)) $(TD Compile time reflection utilities )) 16 | $(TR 17 | $(TDNW $(MREF mir,bitmanip)) 18 | $(TD Bit-level manipulation facilities) 19 | ) 20 | $(TR 21 | $(TDNW $(MREF mir,conv)) 22 | $(TD Conversion utilities) 23 | ) 24 | $(TR 25 | $(TDNW $(MREF mir,functional)) 26 | $(TD Functions that manipulate other functions) 27 | ) 28 | $(TR 29 | $(TDNW $(MREF mir,primitives)) 30 | $(TD Templates used to check primitives and 31 | range primitives for arrays with multi-dimensional like API support) 32 | ) 33 | $(TR 34 | $(TDNW $(MREF mir,qualifier)) 35 | $(TD Const and Immutable qualifiers helpers for Mir Type System.) 36 | ) 37 | $(TR 38 | $(TDNW $(MREF mir,utility)) 39 | $(TD Utilities) 40 | ) 41 | $(TR 42 | $(TDNW $(MREF mir,enums)) 43 | $(TD Utilities to work with enums) 44 | ) 45 | $(TR 46 | $(TDNW $(MREF mir,string_table)) 47 | $(TD Mir String Table designed for fast deserialization routines) 48 | ) 49 | $(LEADINGROW Integer Routines) 50 | $(TR 51 | $(TDNW $(MREF mir,bitop)) 52 | $(TD A collection of bit-level operations) 53 | ) 54 | $(TR 55 | $(TDNW $(MREF mir,checkedint)) 56 | $(TD Integral arithmetic primitives that check for out-of-range results) 57 | ) 58 | $(LEADINGROW Basic Math) 59 | $(TR 60 | $(TDNW $(MREF mir,math)) 61 | $(TD Publicly imports 62 | $(MREF mir,math,common), 63 | $(MREF mir,math,constant), 64 | $(MREF mir,math,ieee). 65 | ) 66 | ) 67 | $(TR 68 | $(TDNW $(MREF mir,math,common)) 69 | $(TD Common floating point math functions) 70 | ) 71 | $(TR 72 | $(TDNW $(MREF mir,complex)) 73 | $(TD Generic complex type) 74 | ) 75 | $(TR 76 | $(TDNW $(MREF mir,complex,math)) 77 | $(TD Basic complex math) 78 | ) 79 | $(TR 80 | $(TDNW $(MREF mir,math,constant)) 81 | $(TD Math constants) 82 | ) 83 | $(TR 84 | $(TDNW $(MREF mir,math,ieee)) 85 | $(TD Base floating point routines) 86 | ) 87 | ) 88 | 89 | Copyright: Copyright © 2020-, Ilia Ki. 90 | 91 | Macros: 92 | TITLE=Mir Core 93 | WIKI=Mir Core 94 | DDOC_BLANKLINE= 95 | _= 96 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('mir-core', 'd', version : '1.1.1', license: 'Apache-2.0') 2 | 3 | description = 'Mir Core - Base software building blocks and conventions' 4 | 5 | subprojects = [] 6 | 7 | has_cpp_headers = false 8 | 9 | dc = meson.get_compiler('d') 10 | 11 | sources_list = [ 12 | 'mir/algebraic', 13 | 'mir/bitmanip', 14 | 'mir/bitop', 15 | 'mir/checkedint', 16 | 'mir/complex/math', 17 | 'mir/complex/package', 18 | 'mir/conv', 19 | 'mir/enums', 20 | 'mir/exception', 21 | 'mir/functional', 22 | 'mir/internal/memory', 23 | 'mir/internal/meta', 24 | 'mir/internal/utility', 25 | 'mir/math/common', 26 | 'mir/math/constant', 27 | 'mir/math/ieee', 28 | 'mir/math/package', 29 | 'mir/primitives', 30 | 'mir/qualifier', 31 | 'mir/reflection', 32 | 'mir/string_table', 33 | 'mir/utility', 34 | ] 35 | 36 | sources = [] 37 | foreach s : sources_list 38 | sources += 'source/' + s + '.d' 39 | endforeach 40 | 41 | if dc.get_id() == 'gcc' 42 | add_project_arguments([ 43 | '-fpreview=dip1008', 44 | ], language: 'd') 45 | else 46 | add_project_arguments([ 47 | '-preview=dip1008', 48 | '-lowmem', 49 | ], language: 'd') 50 | endif 51 | 52 | required_deps = [] 53 | 54 | foreach p : subprojects 55 | required_deps += dependency(p, fallback : [p, p.underscorify() + '_dep']) 56 | endforeach 57 | 58 | directories = ['source'] 59 | 60 | if has_cpp_headers 61 | directories += 'include' 62 | endif 63 | 64 | directories = include_directories(directories) 65 | 66 | this_lib = library(meson.project_name(), 67 | sources, 68 | include_directories: directories, 69 | install: true, 70 | version: meson.project_version(), 71 | dependencies: required_deps, 72 | ) 73 | 74 | this_dep = declare_dependency( 75 | link_with: [this_lib], 76 | include_directories: directories, 77 | dependencies: required_deps, 78 | ) 79 | 80 | test_versions = ['mir_core_test'] 81 | 82 | if has_cpp_headers 83 | install_subdir('include/', 84 | strip_directory :true, 85 | install_dir: 'include/', 86 | ) 87 | endif 88 | 89 | install_subdir('source/', 90 | strip_directory : true, 91 | install_dir: 'include/d/' + meson.project_name(), 92 | ) 93 | 94 | import('pkgconfig').generate(this_lib, 95 | description: description, 96 | subdirs: 'd/' + meson.project_name(), 97 | ) 98 | 99 | mir_core_dep = this_dep 100 | mir_core_lib = this_lib 101 | 102 | test_subdirs = [] 103 | -------------------------------------------------------------------------------- /proposed-mir-architecture.gv: -------------------------------------------------------------------------------- 1 | digraph G { 2 | rankdir=BT; 3 | node [style=filled]; 4 | "mir-core" [color=lightblue] 5 | "mir" [color=lightblue] 6 | "mir-algorithm" [color=lightblue] 7 | "mir-image" [color=lightblue] 8 | "mir-color" [color=lightblue] 9 | "mir-core" [color=lightblue] 10 | "mir-core" -> "mir-color" 11 | "mir-core" -> "mir" 12 | "mir-image" -> "mir-cv" 13 | "mir-cpuid" -> "mir-cv" 14 | "mir-compute" -> "mir-cv" 15 | "mir" -> "mir-algorithm" 16 | "mir-algorithm" -> "mir-image" 17 | "mir-algorithm" -> "mir-model" 18 | "mir-core" -> "mir-algorithm" 19 | "mir-core" -> "mir-optim" 20 | "mir-core" -> "mir-cpuid" 21 | "mir-core" -> "mir-random" 22 | "cblas" -> "mir-blas" 23 | "lapack" -> "mir-lapack" 24 | "mir" -> "mir-blas" 25 | "mir-blas" -> "mir-lapack" 26 | "dcompute" -> "mir-compute" 27 | "mir-color" -> "mir-image" 28 | "mir" -> "mir-compute" 29 | "mir-lapack" -> "mir-optim" 30 | "mir-algorithm" -> "mir-json" 31 | "mir-optim" -> "mir-model" 32 | "mir" -> "mir-sparse" 33 | "mir-algorithm" -> "mir-sparse" 34 | "mir-sparse" -> "mir-model" 35 | } 36 | -------------------------------------------------------------------------------- /source/mir/bitmanip.d: -------------------------------------------------------------------------------- 1 | /++ 2 | Bit-level manipulation facilities. 3 | 4 | $(SCRIPT inhibitQuickIndex = 1;) 5 | $(BOOKTABLE, 6 | $(TR $(TH Category) $(TH Functions)) 7 | $(TR $(TD Bit constructs) $(TD 8 | $(LREF bitfields) 9 | )) 10 | $(TR $(TD Tagging) $(TD 11 | $(LREF taggedClassRef) 12 | $(LREF taggedPointer) 13 | )) 14 | ) 15 | 16 | License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). 17 | Authors: $(HTTP digitalmars.com, Walter Bright), 18 | $(HTTP erdani.org, Andrei Alexandrescu), 19 | Amaury SECHET 20 | +/ 21 | module mir.bitmanip; 22 | 23 | import std.traits; 24 | 25 | private string normString()(string str) 26 | { 27 | // if (str.length && (str[$-1] == 'U' || str[$-1] == 'u')) str = str[0 .. $-1]; 28 | // if (str.length && (str[$-1] == 'L' || str[$-1] == 'l')) str = str[0 .. $-1]; 29 | return str; 30 | } 31 | 32 | private template createAccessors( 33 | string store, T, string name, size_t len, size_t offset) 34 | { 35 | static if (!name.length) 36 | { 37 | // No need to create any accessor 38 | enum result = ""; 39 | } 40 | else static if (len == 0) 41 | { 42 | // Fields of length 0 are always zero 43 | enum result = "enum "~T.stringof~" "~name~" = 0;\n"; 44 | } 45 | else 46 | { 47 | enum ulong 48 | maskAllElse = ((~0uL) >> (64 - len)) << offset, 49 | signBitCheck = 1uL << (len - 1); 50 | 51 | static if (T.min < 0) 52 | { 53 | enum long minVal = -(1uL << (len - 1)); 54 | enum long maxVal = (1uL << (len - 1)) - 1; 55 | alias UT = Unsigned!(T); 56 | enum UT extendSign = cast(UT)~((~0uL) >> (64 - len)); 57 | } 58 | else 59 | { 60 | enum ulong minVal = 0; 61 | enum ulong maxVal = (~0uL) >> (64 - len); 62 | enum extendSign = 0; 63 | } 64 | 65 | static if (is(T == bool)) 66 | { 67 | static assert(len == 1); 68 | enum result = 69 | // getter 70 | "@property bool " ~ name ~ "()() @safe pure nothrow @nogc const { return " 71 | ~"("~store~" & "~ maskAllElse.stringof ~") != 0;}\n" 72 | // setter 73 | ~"@property void " ~ name ~ "()(bool v) @safe pure nothrow @nogc { " 74 | ~"if (v) "~store~" |= "~ maskAllElse.stringof ~";" 75 | ~"else "~store~" &= cast(typeof("~store~"))(-1-cast(typeof("~store~"))"~ maskAllElse.stringof ~");}\n"; 76 | } 77 | else 78 | { 79 | // getter 80 | enum result = "@property "~T.stringof~" "~name~"()() @safe pure nothrow @nogc const { ulong result = " 81 | ~"(ulong("~store~") & " 82 | ~ maskAllElse.stringof ~ ") >>" 83 | ~ offset.stringof ~ ";" 84 | ~ (T.min < 0 85 | ? "if (result >= " ~ signBitCheck.stringof 86 | ~ ") result |= " ~ extendSign.stringof ~ ";" 87 | : "") 88 | ~ " return cast("~T.stringof~") result;}\n" 89 | // setter 90 | ~"@property void "~name~"()("~T.stringof~" v) @safe pure nothrow @nogc { " 91 | ~"assert(v >= "~name~`_min, "Value is smaller than the minimum value of bitfield '`~name~`'"); ` 92 | ~"assert(v <= "~name~`_max, "Value is greater than the maximum value of bitfield '`~name~`'"); ` 93 | ~store~" = cast(typeof("~store~"))" 94 | ~" (("~store~" & (-1-cast(typeof("~store~"))"~ maskAllElse.stringof ~"))" 95 | ~" | ((cast(typeof("~store~")) v << "~ offset.stringof ~")" 96 | ~" & "~ maskAllElse.stringof ~"));}\n" 97 | // constants 98 | ~"enum "~T.stringof~" "~name~"_min = cast("~T.stringof~")" 99 | ~ (minVal == minVal.min && minVal.min < 0 ? "long.min" : minVal.stringof) ~"; " 100 | ~" enum "~T.stringof~" "~name~"_max = cast("~T.stringof~")" 101 | ~ maxVal.stringof ~"; "; 102 | } 103 | } 104 | } 105 | 106 | private template createStoreName(Ts...) 107 | { 108 | static if (Ts.length < 2) 109 | enum createStoreName = ""; 110 | else 111 | enum createStoreName = "_" ~ Ts[1] ~ createStoreName!(Ts[3 .. $]); 112 | } 113 | 114 | private template createStorageAndFields(Ts...) 115 | { 116 | enum Name = createStoreName!Ts; 117 | enum Size = sizeOfBitField!Ts; 118 | static if (Size == ubyte.sizeof * 8) 119 | alias StoreType = ubyte; 120 | else static if (Size == ushort.sizeof * 8) 121 | alias StoreType = ushort; 122 | else static if (Size == uint.sizeof * 8) 123 | alias StoreType = uint; 124 | else static if (Size == ulong.sizeof * 8) 125 | alias StoreType = ulong; 126 | else 127 | { 128 | static assert(false, "Field widths must sum to 8, 16, 32, or 64"); 129 | alias StoreType = ulong; // just to avoid another error msg 130 | } 131 | enum result 132 | = "private " ~ StoreType.stringof ~ " " ~ Name ~ ";" 133 | ~ createFields!(Name, 0, Ts).result; 134 | } 135 | 136 | private template createFields(string store, size_t offset, Ts...) 137 | { 138 | static if (Ts.length > 0) 139 | enum result 140 | = createAccessors!(store, Ts[0], Ts[1], Ts[2], offset).result 141 | ~ createFields!(store, offset + Ts[2], Ts[3 .. $]).result; 142 | else 143 | enum result = ""; 144 | } 145 | 146 | private ulong getBitsForAlign()(ulong a) 147 | { 148 | ulong bits = 0; 149 | while ((a & 0x01) == 0) 150 | { 151 | bits++; 152 | a >>= 1; 153 | } 154 | assert(a == 1, "alignment is not a power of 2"); 155 | return bits; 156 | } 157 | 158 | private template createReferenceAccessor(string store, T, ulong bits, string name) 159 | { 160 | import std.traits : CopyTypeQualifiers, PointerTarget; 161 | 162 | static if (is(T == class)) 163 | alias Q = T; 164 | else 165 | alias Q = PointerTarget!T; 166 | 167 | enum storageType = (CopyTypeQualifiers!(Q, void)*).stringof; 168 | enum storage = "private " ~ storageType ~ ' ' ~ store ~ "_ptr;\n"; 169 | enum storage_accessor = "@property ref size_t " ~ store ~ "()() return @trusted pure nothrow @nogc const { " 170 | ~ "return *cast(size_t*) &" ~ store ~ "_ptr;}\n" 171 | ~ "@property void " ~ store ~ "()(size_t v) @trusted pure nothrow @nogc { " 172 | ~ "" ~ store ~ "_ptr = cast(" ~ storageType ~ ") v;}\n"; 173 | 174 | enum mask = (1UL << bits) - 1; 175 | enum maskInv = ~mask; 176 | // getter 177 | enum ref_accessor = "@property "~T.stringof~" "~name~"()() @trusted pure nothrow @nogc const { auto result = " 178 | ~ "("~store~" & "~ maskInv.stringof ~"); " 179 | ~ "return cast("~T.stringof~") cast(" ~ storageType ~ ") result;}\n" 180 | // setter 181 | ~"@property void "~name~"()("~T.stringof~" v) @trusted pure nothrow @nogc { " 182 | ~"assert(((cast(typeof("~store~")) cast(" ~ storageType ~ ") v) & "~ mask.stringof 183 | ~`) == 0, "Value not properly aligned for '`~name~`'"); ` 184 | ~store~" = cast(typeof("~store~"))" 185 | ~" (("~store~" & (cast(typeof("~store~")) "~ mask.stringof ~"))" 186 | ~" | ((cast(typeof("~store~")) cast(" ~ storageType ~ ") v) & (cast(typeof("~store~")) "~ maskInv.stringof ~")));}\n"; 187 | 188 | enum result = storage ~ storage_accessor ~ ref_accessor; 189 | } 190 | 191 | private template sizeOfBitField(T...) 192 | { 193 | static if (T.length < 2) 194 | enum sizeOfBitField = 0; 195 | else 196 | enum sizeOfBitField = T[2] + sizeOfBitField!(T[3 .. $]); 197 | } 198 | 199 | private template createTaggedReference(T, ulong a, string name, Ts...) 200 | { 201 | static assert( 202 | sizeOfBitField!Ts <= getBitsForAlign(a), 203 | "Fields must fit in the bits know to be zero because of alignment." 204 | ); 205 | enum StoreName = createStoreName!(T, name, 0, Ts); 206 | enum result 207 | = createReferenceAccessor!(StoreName, T, sizeOfBitField!Ts, name).result 208 | ~ createFields!(StoreName, 0, Ts, size_t, "", T.sizeof * 8 - sizeOfBitField!Ts).result; 209 | } 210 | 211 | /** 212 | Allows creating bit fields inside $(D_PARAM struct)s and $(D_PARAM 213 | class)es. 214 | 215 | Example: 216 | 217 | ---- 218 | struct A 219 | { 220 | int a; 221 | mixin(bitfields!( 222 | uint, "x", 2, 223 | int, "y", 3, 224 | uint, "z", 2, 225 | bool, "flag", 1)); 226 | } 227 | A obj; 228 | obj.x = 2; 229 | obj.z = obj.x; 230 | ---- 231 | 232 | The example above creates a bitfield pack of eight bits, which fit in 233 | one $(D_PARAM ubyte). The bitfields are allocated starting from the 234 | least significant bit, i.e. x occupies the two least significant bits 235 | of the bitfields storage. 236 | 237 | The sum of all bit lengths in one $(D_PARAM bitfield) instantiation 238 | must be exactly 8, 16, 32, or 64. If padding is needed, just allocate 239 | one bitfield with an empty name. 240 | 241 | Example: 242 | 243 | ---- 244 | struct A 245 | { 246 | mixin(bitfields!( 247 | bool, "flag1", 1, 248 | bool, "flag2", 1, 249 | uint, "", 6)); 250 | } 251 | ---- 252 | 253 | The type of a bit field can be any integral type or enumerated 254 | type. The most efficient type to store in bitfields is $(D_PARAM 255 | bool), followed by unsigned types, followed by signed types. 256 | */ 257 | 258 | template bitfields(T...) 259 | { 260 | enum { bitfields = createStorageAndFields!T.result } 261 | } 262 | 263 | /** 264 | This string mixin generator allows one to create tagged pointers inside $(D_PARAM struct)s and $(D_PARAM class)es. 265 | 266 | A tagged pointer uses the bits known to be zero in a normal pointer or class reference to store extra information. 267 | For example, a pointer to an integer must be 4-byte aligned, so there are 2 bits that are always known to be zero. 268 | One can store a 2-bit integer there. 269 | 270 | The example above creates a tagged pointer in the struct A. The pointer is of type 271 | $(D uint*) as specified by the first argument, and is named x, as specified by the second 272 | argument. 273 | 274 | Following arguments works the same way as $(D bitfield)'s. The bitfield must fit into the 275 | bits known to be zero because of the pointer alignment. 276 | */ 277 | 278 | template taggedPointer(T : T*, string name, Ts...) { 279 | enum taggedPointer = createTaggedReference!(T*, T.alignof, name, Ts).result; 280 | } 281 | 282 | /// 283 | @safe version(mir_core_test) unittest 284 | { 285 | struct A 286 | { 287 | int a; 288 | mixin(taggedPointer!( 289 | uint*, "x", 290 | bool, "b1", 1, 291 | bool, "b2", 1)); 292 | } 293 | A obj; 294 | obj.x = new uint; 295 | obj.b1 = true; 296 | obj.b2 = false; 297 | } 298 | 299 | /** 300 | This string mixin generator allows one to create tagged class reference inside $(D_PARAM struct)s and $(D_PARAM class)es. 301 | 302 | A tagged class reference uses the bits known to be zero in a normal class reference to store extra information. 303 | For example, a pointer to an integer must be 4-byte aligned, so there are 2 bits that are always known to be zero. 304 | One can store a 2-bit integer there. 305 | 306 | The example above creates a tagged reference to an Object in the struct A. This expects the same parameters 307 | as $(D taggedPointer), except the first argument which must be a class type instead of a pointer type. 308 | */ 309 | 310 | template taggedClassRef(T, string name, Ts...) 311 | if (is(T == class)) 312 | { 313 | enum taggedClassRef = createTaggedReference!(T, 8, name, Ts).result; 314 | } 315 | 316 | /// 317 | @safe version(mir_core_test) unittest 318 | { 319 | struct A 320 | { 321 | int a; 322 | mixin(taggedClassRef!( 323 | Object, "o", 324 | uint, "i", 2)); 325 | } 326 | A obj; 327 | obj.o = new Object(); 328 | obj.i = 3; 329 | } 330 | 331 | @safe pure nothrow @nogc 332 | version(mir_core_test) unittest 333 | { 334 | // Degenerate bitfields (#8474 / #11160) tests mixed with range tests 335 | struct Test1 336 | { 337 | mixin(bitfields!(uint, "a", 32, 338 | uint, "b", 4, 339 | uint, "c", 4, 340 | uint, "d", 8, 341 | uint, "e", 16,)); 342 | 343 | static assert(Test1.b_min == 0); 344 | static assert(Test1.b_max == 15); 345 | } 346 | 347 | struct Test2 348 | { 349 | mixin(bitfields!(bool, "a", 0, 350 | ulong, "b", 64)); 351 | 352 | static assert(Test2.b_min == ulong.min); 353 | static assert(Test2.b_max == ulong.max); 354 | } 355 | 356 | struct Test1b 357 | { 358 | mixin(bitfields!(bool, "a", 0, 359 | int, "b", 8)); 360 | } 361 | 362 | struct Test2b 363 | { 364 | mixin(bitfields!(int, "a", 32, 365 | int, "b", 4, 366 | int, "c", 4, 367 | int, "d", 8, 368 | int, "e", 16,)); 369 | 370 | static assert(Test2b.b_min == -8); 371 | static assert(Test2b.b_max == 7); 372 | } 373 | 374 | struct Test3b 375 | { 376 | mixin(bitfields!(bool, "a", 0, 377 | long, "b", 64)); 378 | 379 | static assert(Test3b.b_min == long.min); 380 | static assert(Test3b.b_max == long.max); 381 | } 382 | 383 | struct Test4b 384 | { 385 | mixin(bitfields!(long, "a", 32, 386 | int, "b", 32)); 387 | } 388 | 389 | // Sign extension tests 390 | Test2b t2b; 391 | Test4b t4b; 392 | t2b.b = -5; assert(t2b.b == -5); 393 | t2b.d = -5; assert(t2b.d == -5); 394 | t2b.e = -5; assert(t2b.e == -5); 395 | t4b.a = -5; assert(t4b.a == -5L); 396 | } 397 | 398 | @system version(mir_core_test) unittest 399 | { 400 | struct Test5 401 | { 402 | mixin(taggedPointer!( 403 | int*, "a", 404 | uint, "b", 2)); 405 | } 406 | 407 | Test5 t5; 408 | t5.a = null; 409 | t5.b = 3; 410 | assert(t5.a is null); 411 | assert(t5.b == 3); 412 | 413 | int myint = 42; 414 | t5.a = &myint; 415 | assert(t5.a is &myint); 416 | assert(t5.b == 3); 417 | 418 | struct Test6 419 | { 420 | mixin(taggedClassRef!( 421 | Object, "o", 422 | bool, "b", 1)); 423 | } 424 | 425 | Test6 t6; 426 | t6.o = null; 427 | t6.b = false; 428 | assert(t6.o is null); 429 | assert(t6.b == false); 430 | 431 | auto o = new Object(); 432 | t6.o = o; 433 | t6.b = true; 434 | assert(t6.o is o); 435 | assert(t6.b == true); 436 | } 437 | 438 | @safe version(mir_core_test) unittest 439 | { 440 | static assert(!__traits(compiles, 441 | taggedPointer!( 442 | int*, "a", 443 | uint, "b", 3))); 444 | 445 | static assert(!__traits(compiles, 446 | taggedClassRef!( 447 | Object, "a", 448 | uint, "b", 4))); 449 | 450 | struct S { 451 | mixin(taggedClassRef!( 452 | Object, "a", 453 | bool, "b", 1)); 454 | } 455 | 456 | const S s; 457 | void bar(S s) {} 458 | 459 | static assert(!__traits(compiles, bar(s))); 460 | } 461 | 462 | @safe version(mir_core_test) unittest 463 | { 464 | // Bug #6686 465 | union S { 466 | ulong bits = ulong.max; 467 | mixin (bitfields!( 468 | ulong, "back", 31, 469 | ulong, "front", 33) 470 | ); 471 | } 472 | S num; 473 | 474 | num.bits = ulong.max; 475 | num.back = 1; 476 | assert(num.bits == 0xFFFF_FFFF_8000_0001uL); 477 | } 478 | 479 | @safe version(mir_core_test) unittest 480 | { 481 | // Bug #5942 482 | struct S 483 | { 484 | mixin(bitfields!( 485 | int, "a" , 32, 486 | int, "b" , 32 487 | )); 488 | } 489 | 490 | S data; 491 | data.b = 42; 492 | data.a = 1; 493 | assert(data.b == 42); 494 | } 495 | 496 | @safe version(mir_core_test) unittest 497 | { 498 | struct Test 499 | { 500 | mixin(bitfields!(bool, "a", 1, 501 | uint, "b", 3, 502 | short, "c", 4)); 503 | } 504 | 505 | @safe void test() pure nothrow 506 | { 507 | Test t; 508 | 509 | t.a = true; 510 | t.b = 5; 511 | t.c = 2; 512 | 513 | assert(t.a); 514 | assert(t.b == 5); 515 | assert(t.c == 2); 516 | } 517 | 518 | test(); 519 | } 520 | 521 | @safe version(mir_core_test) unittest 522 | { 523 | { 524 | static struct Integrals { 525 | bool checkExpectations(bool eb, int ei, short es) { return b == eb && i == ei && s == es; } 526 | 527 | mixin(bitfields!( 528 | bool, "b", 1, 529 | uint, "i", 3, 530 | short, "s", 4)); 531 | } 532 | Integrals i; 533 | assert(i.checkExpectations(false, 0, 0)); 534 | i.b = true; 535 | assert(i.checkExpectations(true, 0, 0)); 536 | i.i = 7; 537 | assert(i.checkExpectations(true, 7, 0)); 538 | i.s = -8; 539 | assert(i.checkExpectations(true, 7, -8)); 540 | i.s = 7; 541 | assert(i.checkExpectations(true, 7, 7)); 542 | } 543 | 544 | //Bug# 8876 545 | { 546 | struct MoreIntegrals { 547 | bool checkExpectations(uint eu, ushort es, uint ei) { return u == eu && s == es && i == ei; } 548 | 549 | mixin(bitfields!( 550 | uint, "u", 24, 551 | short, "s", 16, 552 | int, "i", 24)); 553 | } 554 | 555 | MoreIntegrals i; 556 | assert(i.checkExpectations(0, 0, 0)); 557 | i.s = 20; 558 | assert(i.checkExpectations(0, 20, 0)); 559 | i.i = 72; 560 | assert(i.checkExpectations(0, 20, 72)); 561 | i.u = 8; 562 | assert(i.checkExpectations(8, 20, 72)); 563 | i.s = 7; 564 | assert(i.checkExpectations(8, 7, 72)); 565 | } 566 | 567 | enum A { True, False } 568 | enum B { One, Two, Three, Four } 569 | static struct Enums { 570 | bool checkExpectations(A ea, B eb) { return a == ea && b == eb; } 571 | 572 | mixin(bitfields!( 573 | A, "a", 1, 574 | B, "b", 2, 575 | uint, "", 5)); 576 | } 577 | Enums e; 578 | assert(e.checkExpectations(A.True, B.One)); 579 | e.a = A.False; 580 | assert(e.checkExpectations(A.False, B.One)); 581 | e.b = B.Three; 582 | assert(e.checkExpectations(A.False, B.Three)); 583 | 584 | static struct SingleMember { 585 | bool checkExpectations(bool eb) { return b == eb; } 586 | 587 | mixin(bitfields!( 588 | bool, "b", 1, 589 | uint, "", 7)); 590 | } 591 | SingleMember f; 592 | assert(f.checkExpectations(false)); 593 | f.b = true; 594 | assert(f.checkExpectations(true)); 595 | } 596 | 597 | // Issue 12477 598 | @system version(mir_core_test) unittest 599 | { 600 | import std.algorithm.searching : canFind; 601 | import mir.bitmanip : bitfields; 602 | import core.exception : AssertError; 603 | 604 | static struct S 605 | { 606 | mixin(bitfields!( 607 | uint, "a", 6, 608 | int, "b", 2)); 609 | } 610 | 611 | S s; 612 | 613 | try { s.a = uint.max; assert(0); } 614 | catch (AssertError ae) 615 | { assert(ae.msg.canFind("Value is greater than the maximum value of bitfield 'a'"), ae.msg); } 616 | 617 | try { s.b = int.min; assert(0); } 618 | catch (AssertError ae) 619 | { assert(ae.msg.canFind("Value is smaller than the minimum value of bitfield 'b'"), ae.msg); } 620 | } 621 | 622 | @system version(mir_core_test) unittest 623 | { 624 | import core.atomic : atomicStore, atomicLoad, MO = MemoryOrder; 625 | 626 | static struct S 627 | { 628 | mixin(taggedPointer!( 629 | shared(int)*, "si", 630 | bool, "f", 1)); 631 | 632 | this(shared(int)* ptr, bool flag) 633 | { 634 | si = ptr; 635 | f = flag; 636 | } 637 | } 638 | 639 | shared static S s; 640 | shared static int i; 641 | 642 | s.atomicStore!(MO.raw)(S(&i, true)); 643 | assert(s.atomicLoad!(MO.raw) == S(&i, true)); 644 | } 645 | -------------------------------------------------------------------------------- /source/mir/bitop.d: -------------------------------------------------------------------------------- 1 | /++ 2 | This module contains a collection of bit-level operations. 3 | 4 | Authors: Ilia Ki, Phobos & LDC Authors (original Phobos unittests, docs, conventions). 5 | +/ 6 | module mir.bitop; 7 | 8 | version(LDC) 9 | import ldc.intrinsics; 10 | version(GNU) 11 | import gcc.builtins; 12 | 13 | import mir.math.common: fastmath; 14 | 15 | /// Right shift vallue for bit index to get element's index (5 for `uint`). 16 | enum uint bitElemShift(T : ubyte) = 3; 17 | /// ditto 18 | enum uint bitElemShift(T : byte) = 3; 19 | /// ditto 20 | enum uint bitElemShift(T : ushort) = 4; 21 | /// ditto 22 | enum uint bitElemShift(T : short) = 4; 23 | /// ditto 24 | enum uint bitElemShift(T : uint) = 5; 25 | /// ditto 26 | enum uint bitElemShift(T : int) = 5; 27 | /// ditto 28 | enum uint bitElemShift(T : ulong) = 6; 29 | /// ditto 30 | enum uint bitElemShift(T : long) = 6; 31 | static if (is(ucent)) 32 | /// ditto 33 | enum uint bitElemShift(T : ucent) = 7; 34 | /// ditto 35 | static if (is(cent)) 36 | enum uint bitElemShift(T : cent) = 7; 37 | 38 | /// Bit mask for bit index to get element's bit shift (31 for uint). 39 | enum uint bitShiftMask(T : ubyte) = 7; 40 | /// ditto 41 | enum uint bitShiftMask(T : byte) = 7; 42 | /// ditto 43 | enum uint bitShiftMask(T : ushort) = 15; 44 | /// ditto 45 | enum uint bitShiftMask(T : short) = 15; 46 | /// ditto 47 | enum uint bitShiftMask(T : uint) = 31; 48 | /// ditto 49 | enum uint bitShiftMask(T : int) = 31; 50 | /// ditto 51 | enum uint bitShiftMask(T : ulong) = 63; 52 | /// ditto 53 | enum uint bitShiftMask(T : long) = 63; 54 | static if (is(ucent)) 55 | /// ditto 56 | enum uint bitShiftMask(T : ucent) = 127; 57 | static if (is(cent)) 58 | /// ditto 59 | enum uint bitShiftMask(T : cent) = 127; 60 | 61 | // no effect on this function, but better for optimization of other @fastmath code that uses this 62 | @fastmath: 63 | 64 | 65 | /++ 66 | +/ 67 | T nTrailingBitsToCount(T)(in T value, in T popcnt) 68 | if (__traits(isUnsigned, T)) 69 | { 70 | import std.traits; 71 | import mir.internal.utility: Iota; 72 | alias S = Signed!(CommonType!(int, T)); 73 | S mask = S(-1) << T.sizeof * 4; 74 | foreach_reverse (s; Iota!(bitElemShift!T - 1)) 75 | {{ 76 | enum shift = 1 << s; 77 | if (S(popcnt) > S(ctpop(cast(T)(value & ~mask)))) 78 | mask <<= shift; 79 | else 80 | mask >>= shift; 81 | }} 82 | return cttz(cast(T)mask) + (S(popcnt) != ctpop(cast(T)(value & ~mask))); 83 | } 84 | 85 | /// 86 | version(mir_core_test) unittest 87 | { 88 | assert(nTrailingBitsToCount(0xF0u, 3u) == 7); 89 | assert(nTrailingBitsToCount(0xE00u, 3u) == 12); 90 | 91 | foreach(uint i; 1 .. 32) 92 | assert(nTrailingBitsToCount(uint.max, i) == i); 93 | } 94 | 95 | /++ 96 | +/ 97 | T nLeadingBitsToCount(T)(in T value, in T popcnt) 98 | if (__traits(isUnsigned, T)) 99 | { 100 | import std.traits; 101 | import mir.internal.utility: Iota; 102 | alias S = Signed!(CommonType!(int, T)); 103 | S mask = S(-1) << T.sizeof * 4; 104 | foreach_reverse (s; Iota!(bitElemShift!T - 1)) 105 | {{ 106 | enum shift = 1 << s; 107 | if (S(popcnt) > S(ctpop(cast(T)(value & mask)))) 108 | mask >>= shift; 109 | else 110 | mask <<= shift; 111 | }} 112 | return ctlz(cast(T)~mask) + (S(popcnt) != ctpop(cast(T)(value & mask))); 113 | } 114 | 115 | /// 116 | version(mir_core_test) unittest 117 | { 118 | assert(nLeadingBitsToCount(0xF0u, 3u) == 32 - 5); 119 | assert(nLeadingBitsToCount(0x700u, 3u) == 32 - 8); 120 | 121 | foreach(uint i; 1 .. 32) 122 | assert(nLeadingBitsToCount(uint.max, i) == i); 123 | } 124 | 125 | /++ 126 | Tests the bit. 127 | Returns: 128 | A non-zero value if the bit was set, and a zero 129 | if it was clear. 130 | +/ 131 | auto bt(Field, T = typeof(Field.init[size_t.init]))(auto ref Field p, size_t bitnum) 132 | if (__traits(isUnsigned, T)) 133 | { 134 | auto index = bitnum >> bitElemShift!T; 135 | auto mask = T(1) << (bitnum & bitShiftMask!T); 136 | return p[index] & mask; 137 | } 138 | 139 | /// 140 | @system pure version(mir_core_test) unittest 141 | { 142 | size_t[2] array; 143 | 144 | array[0] = 2; 145 | array[1] = 0x100; 146 | 147 | assert(bt(array.ptr, 1)); 148 | assert(array[0] == 2); 149 | assert(array[1] == 0x100); 150 | } 151 | 152 | /++ 153 | Tests and assign the bit. 154 | Returns: 155 | A non-zero value if the bit was set, and a zero if it was clear. 156 | +/ 157 | auto bta(Field, T = typeof(Field.init[size_t.init]))(auto ref Field p, size_t bitnum, bool value) 158 | if (__traits(isUnsigned, T)) 159 | { 160 | auto index = bitnum >> bitElemShift!T; 161 | auto shift = bitnum & bitShiftMask!T; 162 | auto mask = T(1) << shift; 163 | static if (__traits(compiles, &p[size_t.init])) 164 | { 165 | auto qp = &p[index]; 166 | auto q = *qp; 167 | auto ret = q & mask; 168 | *qp = cast(T)((q & ~mask) ^ (T(value) << shift)); 169 | } 170 | else 171 | { 172 | auto q = p[index]; 173 | auto ret = q & mask; 174 | p[index] = cast(T)((q & ~mask) ^ (T(value) << shift)); 175 | } 176 | return ret; 177 | } 178 | 179 | /++ 180 | Tests and complements the bit. 181 | Returns: 182 | A non-zero value if the bit was set, and a zero if it was clear. 183 | +/ 184 | auto btc(Field, T = typeof(Field.init[size_t.init]))(auto ref Field p, size_t bitnum) 185 | if (__traits(isUnsigned, T)) 186 | { 187 | auto index = bitnum >> bitElemShift!T; 188 | auto mask = T(1) << (bitnum & bitShiftMask!T); 189 | static if (__traits(compiles, &p[size_t.init])) 190 | { 191 | auto qp = &p[index]; 192 | auto q = *qp; 193 | auto ret = q & mask; 194 | *qp = cast(T)(q ^ mask); 195 | } 196 | else 197 | { 198 | auto q = p[index]; 199 | auto ret = q & mask; 200 | p[index] = cast(T)(q ^ mask); 201 | } 202 | return ret; 203 | } 204 | 205 | /++ 206 | Tests and resets (sets to 0) the bit. 207 | Returns: 208 | A non-zero value if the bit was set, and a zero if it was clear. 209 | +/ 210 | auto btr(Field, T = typeof(Field.init[size_t.init]))(auto ref Field p, size_t bitnum) 211 | if (__traits(isUnsigned, T)) 212 | { 213 | auto index = bitnum >> bitElemShift!T; 214 | auto mask = T(1) << (bitnum & bitShiftMask!T); 215 | static if (__traits(compiles, &p[size_t.init])) 216 | { 217 | auto qp = &p[index]; 218 | auto q = *qp; 219 | auto ret = q & mask; 220 | *qp = cast(T)(q & ~mask); 221 | } 222 | else 223 | { 224 | auto q = p[index]; 225 | auto ret = q & mask; 226 | p[index] = cast(T)(q & ~mask); 227 | } 228 | return ret; 229 | } 230 | 231 | /++ 232 | Tests and sets the bit. 233 | Params: 234 | p = a non-NULL field / pointer to an array of unsigned integers. 235 | bitnum = a bit number, starting with bit 0 of p[0], 236 | and progressing. It addresses bits like the expression: 237 | --- 238 | p[index / (T.sizeof*8)] & (1 << (index & ((T.sizeof*8) - 1))) 239 | --- 240 | Returns: 241 | A non-zero value if the bit was set, and a zero if it was clear. 242 | +/ 243 | auto bts(Field, T = typeof(Field.init[size_t.init]))(auto ref Field p, size_t bitnum) 244 | if (__traits(isUnsigned, T)) 245 | { 246 | auto index = bitnum >> bitElemShift!T; 247 | auto mask = T(1) << (bitnum & bitShiftMask!T); 248 | static if (__traits(compiles, &p[size_t.init])) 249 | { 250 | auto qp = &p[index]; 251 | auto q = *qp; 252 | auto ret = q & mask; 253 | *qp = cast(T)(q | mask); 254 | } 255 | else 256 | { 257 | auto q = p[index]; 258 | auto ret = q & mask; 259 | p[index] = cast(T)(q | mask); 260 | } 261 | return ret; 262 | } 263 | 264 | /// 265 | @system pure version(mir_core_test) unittest 266 | { 267 | size_t[2] array; 268 | 269 | array[0] = 2; 270 | array[1] = 0x100; 271 | 272 | assert(btc(array.ptr, 35) == 0); 273 | if (size_t.sizeof == 8) 274 | { 275 | assert(array[0] == 0x8_0000_0002); 276 | assert(array[1] == 0x100); 277 | } 278 | else 279 | { 280 | assert(array[0] == 2); 281 | assert(array[1] == 0x108); 282 | } 283 | 284 | assert(btc(array.ptr, 35)); 285 | assert(array[0] == 2); 286 | assert(array[1] == 0x100); 287 | 288 | assert(bts(array.ptr, 35) == 0); 289 | if (size_t.sizeof == 8) 290 | { 291 | assert(array[0] == 0x8_0000_0002); 292 | assert(array[1] == 0x100); 293 | } 294 | else 295 | { 296 | assert(array[0] == 2); 297 | assert(array[1] == 0x108); 298 | } 299 | 300 | assert(btr(array.ptr, 35)); 301 | assert(array[0] == 2); 302 | assert(array[1] == 0x100); 303 | } 304 | 305 | /// The 'ctpop' family of intrinsics counts the number of bits set in a value. 306 | T ctpop(T)(in T src) 307 | if (__traits(isUnsigned, T)) 308 | { 309 | version(LDC) if (!__ctfe) 310 | return llvm_ctpop(src); 311 | version(GNU) if (!__ctfe) 312 | { 313 | static if (T.sizeof < __builtin_clong.sizeof) 314 | return cast(T) __builtin_popcount(src); 315 | else static if (T.sizeof <= __builtin_clong.sizeof) 316 | return cast(T) __builtin_popcountl(src); 317 | else 318 | return cast(T) __builtin_popcountll(src); 319 | } 320 | import core.bitop: popcnt; 321 | return cast(T) popcnt(src); 322 | } 323 | 324 | /++ 325 | The 'ctlz' family of intrinsic functions counts the number of leading zeros in a variable. 326 | Result is undefined if the argument is zero. 327 | +/ 328 | T ctlz(T)(in T src) 329 | if (__traits(isUnsigned, T)) 330 | { 331 | version(LDC) if (!__ctfe) 332 | return llvm_ctlz(src, true); 333 | version(GNU) if (!__ctfe) 334 | { 335 | // Do not zero-extend when counting leading zeroes. 336 | static if (T.sizeof < __builtin_clong.sizeof && T.sizeof >= uint.sizeof) 337 | return cast(T) __builtin_clz(src); 338 | else static if (T.sizeof == __builtin_clong.sizeof) 339 | return cast(T) __builtin_clzl(src); 340 | else static if (T.sizeof > __builtin_clong.sizeof) 341 | return cast(T) __builtin_clzll(src); 342 | } 343 | import core.bitop: bsr; 344 | return cast(T)(T.sizeof * 8 - 1 - bsr(src)); 345 | } 346 | 347 | /// 348 | version (mir_core_test) @nogc nothrow pure @safe version(mir_core_test) unittest 349 | { 350 | assert(ctlz(cast(ubyte) 0b0011_1111) == 2); 351 | assert(ctlz(cast(ushort) 0b0000_0001_1111_1111) == 7); 352 | } 353 | 354 | /++ 355 | The 'ctlzp' family of intrinsic functions counts the number of leading zeros in a variable. 356 | Result is properly defined if the argument is zero. 357 | +/ 358 | T ctlzp(T)(in T src) 359 | if (__traits(isUnsigned, T)) 360 | { 361 | version(LDC) if (!__ctfe) 362 | return llvm_ctlz(src, false); 363 | return src ? ctlz(src) : T.sizeof * 8; 364 | } 365 | 366 | /// 367 | version (mir_core_test) @nogc nothrow pure @safe version(mir_core_test) unittest 368 | { 369 | assert(ctlzp(cast(ubyte) 0b0000_0000) == 8); 370 | assert(ctlzp(cast(ubyte) 0b0011_1111) == 2); 371 | assert(ctlzp(cast(ushort) 0b0000_0001_1111_1111) == 7); 372 | assert(ctlzp(cast(ushort) 0) == 16); 373 | assert(ctlzp(cast(ulong) 0) == 64); 374 | } 375 | 376 | /++ 377 | The 'cttz' family of intrinsic functions counts the number of trailing zeros. 378 | Result is undefined if the argument is zero. 379 | +/ 380 | T cttz(T)(in T src) 381 | if (__traits(isUnsigned, T)) 382 | { 383 | version(LDC) if (!__ctfe) 384 | return llvm_cttz(src, true); 385 | version(GNU) if (!__ctfe) 386 | { 387 | static if (T.sizeof <__builtin_clong.sizeof) 388 | return cast(T) __builtin_ctz(src); 389 | else static if (T.sizeof <=__builtin_clong.sizeof) 390 | return cast(T) __builtin_ctzl(src); 391 | else 392 | return cast(T) __builtin_ctzll(src); 393 | } 394 | import core.bitop: bsf; 395 | return cast(T) bsf(src); 396 | } 397 | 398 | /// 399 | version (mir_core_test) @nogc nothrow pure @safe version(mir_core_test) unittest 400 | { 401 | assert(cttzp(cast(ubyte) 0b11111100) == 2); 402 | assert(cttzp(cast(ushort) 0b1111111110000000) == 7); 403 | } 404 | 405 | /++ 406 | The 'cttz' family of intrinsic functions counts the number of trailing zeros. 407 | Result is properly defined if the argument is zero. 408 | +/ 409 | T cttzp(T)(in T src) 410 | if (__traits(isUnsigned, T)) 411 | { 412 | version(LDC) if (!__ctfe) 413 | return llvm_cttz(src, false); 414 | return src ? cttz(src) : T.sizeof * 8; 415 | } 416 | 417 | /// 418 | version (mir_core_test) @nogc nothrow pure @safe version(mir_core_test) unittest 419 | { 420 | assert(cttzp(cast(ubyte) 0b0000_0000) == 8); 421 | assert(cttzp(cast(ubyte) 0b11111100) == 2); 422 | assert(cttzp(cast(ushort) 0b1111111110000000) == 7); 423 | assert(cttzp(cast(ushort) 0) == 16); 424 | assert(cttzp(cast(ulong) 0) == 64); 425 | } 426 | -------------------------------------------------------------------------------- /source/mir/complex/math.d: -------------------------------------------------------------------------------- 1 | 2 | /++ 3 | Complex math 4 | 5 | Copyright: Ilia Ki; 2010, Lars T. Kyllingstad (original Phobos code) 6 | Authors: Ilia Ki, Lars Tandle Kyllingstad, Don Clugston 7 | +/ 8 | module mir.complex.math; 9 | 10 | public import mir.complex; 11 | 12 | /++ 13 | Params: z = A complex number. 14 | Returns: The square root of `z`. 15 | +/ 16 | Complex!T sqrt(T)(Complex!T z) @safe pure nothrow @nogc 17 | { 18 | import mir.math.common: fabs, fmin, fmax, sqrt; 19 | 20 | if (z == 0) 21 | return typeof(return)(0, 0); 22 | auto x = fabs(z.re); 23 | auto y = fabs(z.im); 24 | auto n = fmin(x, y); 25 | auto m = fmax(x, y); 26 | auto r = n / m; 27 | auto w = sqrt(m) * sqrt(0.5f * ((x >= y ? 1 : r) + sqrt(1 + r * r))); 28 | auto s = typeof(return)(w, z.im / (w + w)); 29 | if (z.re < 0) 30 | { 31 | s = typeof(return)(s.im, s.re); 32 | if (z.im < 0) 33 | s = -s; 34 | } 35 | return s; 36 | } 37 | 38 | /// 39 | @safe pure nothrow unittest 40 | { 41 | assert(sqrt(complex(0.0)) == 0.0); 42 | assert(sqrt(complex(1.0, 0)) == 1.0); 43 | assert(sqrt(complex(-1.0, 0)) == complex(0, 1.0)); 44 | assert(sqrt(complex(-8.0, -6.0)) == complex(1.0, -3.0)); 45 | } 46 | 47 | @safe pure nothrow unittest 48 | { 49 | assert(complex(1.0, 1.0).sqrt.approxEqual(complex(1.098684113467809966, 0.455089860562227341))); 50 | assert(complex(0.5, 2.0).sqrt.approxEqual(complex(1.131713924277869410, 0.883615530875513265))); 51 | } 52 | 53 | /** 54 | * Calculate the natural logarithm of x. 55 | * The branch cut is along the negative axis. 56 | * Params: 57 | * x = A complex number 58 | * Returns: 59 | * The complex natural logarithm of `x` 60 | * 61 | * $(TABLE_SV 62 | * $(TR $(TH x) $(TH log(x))) 63 | * $(TR $(TD (-0, +0)) $(TD (-$(INFIN), $(PI)))) 64 | * $(TR $(TD (+0, +0)) $(TD (-$(INFIN), +0))) 65 | * $(TR $(TD (any, +$(INFIN))) $(TD (+$(INFIN), $(PI)/2))) 66 | * $(TR $(TD (any, $(NAN))) $(TD ($(NAN), $(NAN)))) 67 | * $(TR $(TD (-$(INFIN), any)) $(TD (+$(INFIN), $(PI)))) 68 | * $(TR $(TD (+$(INFIN), any)) $(TD (+$(INFIN), +0))) 69 | * $(TR $(TD (-$(INFIN), +$(INFIN))) $(TD (+$(INFIN), 3$(PI)/4))) 70 | * $(TR $(TD (+$(INFIN), +$(INFIN))) $(TD (+$(INFIN), $(PI)/4))) 71 | * $(TR $(TD ($(PLUSMN)$(INFIN), $(NAN))) $(TD (+$(INFIN), $(NAN)))) 72 | * $(TR $(TD ($(NAN), any)) $(TD ($(NAN), $(NAN)))) 73 | * $(TR $(TD ($(NAN), +$(INFIN))) $(TD (+$(INFIN), $(NAN)))) 74 | * $(TR $(TD ($(NAN), $(NAN))) $(TD ($(NAN), $(NAN)))) 75 | * ) 76 | */ 77 | Complex!T log(T)(Complex!T x) @safe pure nothrow @nogc 78 | { 79 | import mir.math.constant: PI, PI_4, PI_2; 80 | import mir.math.common: log, fabs, copysign; 81 | alias isNaN = x => x != x; 82 | alias isInfinity = x => x.fabs == T.infinity; 83 | 84 | // Handle special cases explicitly here for better accuracy. 85 | // The order here is important, so that the correct path is chosen. 86 | if (isNaN(x.re)) 87 | { 88 | if (isInfinity(x.im)) 89 | return Complex!T(T.infinity, T.nan); 90 | else 91 | return Complex!T(T.nan, T.nan); 92 | } 93 | if (isInfinity(x.re)) 94 | { 95 | if (isNaN(x.im)) 96 | return Complex!T(T.infinity, T.nan); 97 | else if (isInfinity(x.im)) 98 | { 99 | if (copysign(1, x.re) < 0) 100 | return Complex!T(T.infinity, copysign(3.0 * PI_4, x.im)); 101 | else 102 | return Complex!T(T.infinity, copysign(PI_4, x.im)); 103 | } 104 | else 105 | { 106 | if (copysign(1, x.re) < 0) 107 | return Complex!T(T.infinity, copysign(PI, x.im)); 108 | else 109 | return Complex!T(T.infinity, copysign(0.0, x.im)); 110 | } 111 | } 112 | if (isNaN(x.im)) 113 | return Complex!T(T.nan, T.nan); 114 | if (isInfinity(x.im)) 115 | return Complex!T(T.infinity, copysign(PI_2, x.im)); 116 | if (x.re == 0.0 && x.im == 0.0) 117 | { 118 | if (copysign(1, x.re) < 0) 119 | return Complex!T(-T.infinity, copysign(PI, x.im)); 120 | else 121 | return Complex!T(-T.infinity, copysign(0.0, x.im)); 122 | } 123 | 124 | return Complex!T(log(cabs(x)), arg(x)); 125 | } 126 | 127 | /// 128 | @safe pure nothrow @nogc version(mir_core_test) unittest 129 | { 130 | import mir.math.common: sqrt; 131 | import mir.math.constant: PI; 132 | import mir.math.common: approxEqual; 133 | 134 | auto a = complex(2.0, 1.0); 135 | assert(log(conj(a)) == conj(log(a))); 136 | 137 | assert(log(complex(-1.0L, 0.0L)) == complex(0.0L, PI)); 138 | assert(log(complex(-1.0L, -0.0L)) == complex(0.0L, -PI)); 139 | } 140 | 141 | @safe pure nothrow @nogc version(mir_core_test) unittest 142 | { 143 | import mir.math.common: fabs; 144 | import mir.math.constant: PI, PI_2, PI_4; 145 | alias isNaN = x => x != x; 146 | alias isInfinity = x => x.fabs == x.infinity; 147 | 148 | auto a = log(complex(-0.0L, 0.0L)); 149 | assert(a == complex(-real.infinity, PI)); 150 | auto b = log(complex(0.0L, 0.0L)); 151 | assert(b == complex(-real.infinity, +0.0L)); 152 | auto c = log(complex(1.0L, real.infinity)); 153 | assert(c == complex(real.infinity, PI_2)); 154 | auto d = log(complex(1.0L, real.nan)); 155 | assert(isNaN(d.re) && isNaN(d.im)); 156 | 157 | auto e = log(complex(-real.infinity, 1.0L)); 158 | assert(e == complex(real.infinity, PI)); 159 | auto f = log(complex(real.infinity, 1.0L)); 160 | assert(f == complex(real.infinity, 0.0L)); 161 | auto g = log(complex(-real.infinity, real.infinity)); 162 | assert(g == complex(real.infinity, 3.0 * PI_4)); 163 | auto h = log(complex(real.infinity, real.infinity)); 164 | assert(h == complex(real.infinity, PI_4)); 165 | auto i = log(complex(real.infinity, real.nan)); 166 | assert(isInfinity(i.re) && isNaN(i.im)); 167 | 168 | auto j = log(complex(real.nan, 1.0L)); 169 | assert(isNaN(j.re) && isNaN(j.im)); 170 | auto k = log(complex(real.nan, real.infinity)); 171 | assert(isInfinity(k.re) && isNaN(k.im)); 172 | auto l = log(complex(real.nan, real.nan)); 173 | assert(isNaN(l.re) && isNaN(l.im)); 174 | } 175 | 176 | @safe pure nothrow @nogc version(mir_core_test) unittest 177 | { 178 | import mir.math.constant: PI; 179 | 180 | auto a = log(fromPolar(1.0, PI / 6.0)); 181 | assert(approxEqual(a, complex(0.0L, 0.523598775598298873077L), 0.0, 1e-15)); 182 | 183 | auto b = log(fromPolar(1.0, PI / 3.0)); 184 | assert(approxEqual(b, complex(0.0L, 1.04719755119659774615L), 0.0, 1e-15)); 185 | 186 | auto c = log(fromPolar(1.0, PI / 2.0)); 187 | assert(approxEqual(c, complex(0.0L, 1.57079632679489661923L), 0.0, 1e-15)); 188 | 189 | auto d = log(fromPolar(1.0, 2.0 * PI / 3.0)); 190 | assert(approxEqual(d, complex(0.0L, 2.09439510239319549230L), 0.0, 1e-15)); 191 | 192 | auto e = log(fromPolar(1.0, 5.0 * PI / 6.0)); 193 | assert(approxEqual(e, complex(0.0L, 2.61799387799149436538L), 0.0, 1e-15)); 194 | 195 | auto f = log(complex(-1.0L, 0.0L)); 196 | assert(approxEqual(f, complex(0.0L, PI), 0.0, 1e-15)); 197 | } 198 | 199 | /++ 200 | Calculates e$(SUPERSCRIPT x). 201 | Params: 202 | x = A complex number 203 | Returns: 204 | The complex base e exponential of `x` 205 | $(TABLE_SV 206 | $(TR $(TH x) $(TH exp(x))) 207 | $(TR $(TD ($(PLUSMN)0, +0)) $(TD (1, +0))) 208 | $(TR $(TD (any, +$(INFIN))) $(TD ($(NAN), $(NAN)))) 209 | $(TR $(TD (any, $(NAN)) $(TD ($(NAN), $(NAN))))) 210 | $(TR $(TD (+$(INFIN), +0)) $(TD (+$(INFIN), +0))) 211 | $(TR $(TD (-$(INFIN), any)) $(TD ($(PLUSMN)0, cis(x.im)))) 212 | $(TR $(TD (+$(INFIN), any)) $(TD ($(PLUSMN)$(INFIN), cis(x.im)))) 213 | $(TR $(TD (-$(INFIN), +$(INFIN))) $(TD ($(PLUSMN)0, $(PLUSMN)0))) 214 | $(TR $(TD (+$(INFIN), +$(INFIN))) $(TD ($(PLUSMN)$(INFIN), $(NAN)))) 215 | $(TR $(TD (-$(INFIN), $(NAN))) $(TD ($(PLUSMN)0, $(PLUSMN)0))) 216 | $(TR $(TD (+$(INFIN), $(NAN))) $(TD ($(PLUSMN)$(INFIN), $(NAN)))) 217 | $(TR $(TD ($(NAN), +0)) $(TD ($(NAN), +0))) 218 | $(TR $(TD ($(NAN), any)) $(TD ($(NAN), $(NAN)))) 219 | $(TR $(TD ($(NAN), $(NAN))) $(TD ($(NAN), $(NAN)))) 220 | ) 221 | +/ 222 | Complex!T exp(T)(Complex!T x) @trusted pure nothrow @nogc // TODO: @safe 223 | { 224 | import mir.math.common: exp, fabs, copysign; 225 | alias isNaN = x => x != x; 226 | alias isInfinity = x => x.fabs == T.infinity; 227 | 228 | // Handle special cases explicitly here, as fromPolar will otherwise 229 | // cause them to return Complex!T(NaN, NaN), or with the wrong sign. 230 | if (isInfinity(x.re)) 231 | { 232 | if (isNaN(x.im)) 233 | { 234 | if (copysign(1, x.re) < 0) 235 | return Complex!T(0, copysign(0, x.im)); 236 | else 237 | return x; 238 | } 239 | if (isInfinity(x.im)) 240 | { 241 | if (copysign(1, x.re) < 0) 242 | return Complex!T(0, copysign(0, x.im)); 243 | else 244 | return Complex!T(T.infinity, -T.nan); 245 | } 246 | if (x.im == 0) 247 | { 248 | if (copysign(1, x.re) < 0) 249 | return Complex!T(0); 250 | else 251 | return Complex!T(T.infinity); 252 | } 253 | } 254 | if (isNaN(x.re)) 255 | { 256 | if (isNaN(x.im) || isInfinity(x.im)) 257 | return Complex!T(T.nan, T.nan); 258 | if (x.im == 0) 259 | return x; 260 | } 261 | if (x.re == 0) 262 | { 263 | if (isNaN(x.im) || isInfinity(x.im)) 264 | return Complex!T(T.nan, T.nan); 265 | if (x.im == 0) 266 | return Complex!T(1, 0); 267 | } 268 | 269 | return fromPolar!T(exp(x.re), x.im); 270 | } 271 | 272 | /// 273 | @safe pure nothrow @nogc version(mir_core_test) unittest 274 | { 275 | import mir.math.constant: PI; 276 | 277 | assert(exp(complex(0.0, 0.0)) == complex(1.0, 0.0)); 278 | 279 | auto a = complex(2.0, 1.0); 280 | assert(exp(conj(a)) == conj(exp(a))); 281 | 282 | auto b = exp(complex(0.0, 1.0) * double(PI)); 283 | assert(approxEqual(b, complex(-1.0), 0.0, 1e-15)); 284 | } 285 | 286 | @safe pure nothrow @nogc version(mir_core_test) unittest 287 | { 288 | import mir.math.common: fabs; 289 | 290 | alias isNaN = x => x != x; 291 | alias isInfinity = x => x.fabs == x.infinity; 292 | 293 | auto a = exp(complex(0.0, double.infinity)); 294 | assert(isNaN(a.re) && isNaN(a.im)); 295 | auto b = exp(complex(0.0, double.infinity)); 296 | assert(isNaN(b.re) && isNaN(b.im)); 297 | auto c = exp(complex(0.0, double.nan)); 298 | assert(isNaN(c.re) && isNaN(c.im)); 299 | 300 | auto d = exp(complex(+double.infinity, 0.0)); 301 | assert(d == complex(double.infinity, 0.0)); 302 | auto e = exp(complex(-double.infinity, 0.0)); 303 | assert(e == complex(0.0)); 304 | auto f = exp(complex(-double.infinity, 1.0)); 305 | assert(f == complex(0.0)); 306 | auto g = exp(complex(+double.infinity, 1.0)); 307 | assert(g == complex(double.infinity, double.infinity)); 308 | auto h = exp(complex(-double.infinity, +double.infinity)); 309 | assert(h == complex(0.0)); 310 | auto i = exp(complex(+double.infinity, +double.infinity)); 311 | assert(isInfinity(i.re) && isNaN(i.im)); 312 | auto j = exp(complex(-double.infinity, double.nan)); 313 | assert(j == complex(0.0)); 314 | auto k = exp(complex(+double.infinity, double.nan)); 315 | assert(isInfinity(k.re) && isNaN(k.im)); 316 | 317 | auto l = exp(complex(double.nan, 0)); 318 | assert(isNaN(l.re) && l.im == 0.0); 319 | auto m = exp(complex(double.nan, 1)); 320 | assert(isNaN(m.re) && isNaN(m.im)); 321 | auto n = exp(complex(double.nan, double.nan)); 322 | assert(isNaN(n.re) && isNaN(n.im)); 323 | } 324 | 325 | @safe pure nothrow @nogc version(mir_core_test) unittest 326 | { 327 | import mir.math.constant : PI; 328 | 329 | auto a = exp(complex(0.0, -PI)); 330 | assert(approxEqual(a, complex(-1.0L), 0.0, 1e-15)); 331 | 332 | auto b = exp(complex(0.0, -2.0 * PI / 3.0)); 333 | assert(approxEqual(b, complex(-0.5L, -0.866025403784438646763L))); 334 | 335 | auto c = exp(complex(0.0, PI / 3.0)); 336 | assert(approxEqual(c, complex(0.5L, 0.866025403784438646763L))); 337 | 338 | auto d = exp(complex(0.0, 2.0 * PI / 3.0)); 339 | assert(approxEqual(d, complex(-0.5L, 0.866025403784438646763L))); 340 | 341 | auto e = exp(complex(0.0, PI)); 342 | assert(approxEqual(e, complex(-1.0L), 0.0, 1e-15)); 343 | } 344 | 345 | /++ 346 | Computes whether two values are approximately equal, admitting a maximum 347 | relative difference, and a maximum absolute difference. 348 | Params: 349 | lhs = First item to compare. 350 | rhs = Second item to compare. 351 | maxRelDiff = Maximum allowable difference relative to `rhs`. Defaults to `0.5 ^^ 20`. 352 | maxAbsDiff = Maximum absolute difference. Defaults to `0.5 ^^ 20`. 353 | 354 | Returns: 355 | `true` if the two items are equal or approximately equal under either criterium. 356 | +/ 357 | bool approxEqual(T)(Complex!T lhs, Complex!T rhs, const T maxRelDiff = 0x1p-20f, const T maxAbsDiff = 0x1p-20f) 358 | { 359 | import mir.math.common: approxEqual; 360 | return approxEqual(lhs.re, rhs.re, maxRelDiff, maxAbsDiff) 361 | && approxEqual(lhs.im, rhs.im, maxRelDiff, maxAbsDiff); 362 | } 363 | 364 | /// Complex types works as `approxEqual(l.re, r.re) && approxEqual(l.im, r.im)` 365 | @safe pure nothrow @nogc version(mir_core_test) unittest 366 | { 367 | assert(approxEqual(complex(1.0, 1), complex(1.0000001, 1), 1.0000001)); 368 | assert(!approxEqual(complex(100000.0, 0), complex(100001.0, 0))); 369 | } 370 | -------------------------------------------------------------------------------- /source/mir/complex/package.d: -------------------------------------------------------------------------------- 1 | /++ 2 | Complex numbers 3 | 4 | Copyright: Ilia Ki; 2010, Lars T. Kyllingstad (original Phobos code) 5 | Authors: Ilia Ki, Lars Tandle Kyllingstad, Don Clugston 6 | +/ 7 | module mir.complex; 8 | 9 | import mir.math.common: optmath; 10 | 11 | private alias CommonType(A, B) = typeof(A.init + B.init); 12 | 13 | @optmath: 14 | 15 | /++ 16 | Generic complex number type 17 | +/ 18 | struct Complex(T) 19 | if (is(T == float) || is(T == double) || is(T == real)) 20 | { 21 | import mir.internal.utility: isComplex; 22 | import std.traits: isNumeric; 23 | 24 | @optmath: 25 | 26 | /++ 27 | Real part. Default value is zero. 28 | +/ 29 | T re = 0; 30 | /++ 31 | Imaginary part. Default value is zero. 32 | +/ 33 | T im = 0; 34 | 35 | /// 36 | ref Complex opAssign(R)(Complex!R rhs) 37 | if (!is(R == T)) 38 | { 39 | this.re = rhs.re; 40 | this.im = rhs.im; 41 | return this; 42 | } 43 | 44 | /// 45 | ref Complex opAssign(F)(const F rhs) 46 | if (isNumeric!F) 47 | { 48 | this.re = rhs; 49 | this.im = 0; 50 | return this; 51 | } 52 | 53 | /// 54 | ref Complex opOpAssign(string op : "+", R)(Complex!R rhs) return 55 | { 56 | re += rhs.re; 57 | im += rhs.im; 58 | return this; 59 | } 60 | 61 | /// 62 | ref Complex opOpAssign(string op : "-", R)(Complex!R rhs) return 63 | { 64 | re -= rhs.re; 65 | im -= rhs.im; 66 | return this; 67 | } 68 | 69 | /// 70 | ref Complex opOpAssign(string op, R)(Complex!R rhs) return 71 | if (op == "*" || op == "/") 72 | { 73 | return this = this.opBinary!op(rhs); 74 | } 75 | 76 | /// 77 | ref Complex opOpAssign(string op : "+", R)(const R rhs) return 78 | if (isNumeric!R) 79 | { 80 | re += rhs; 81 | return this; 82 | } 83 | 84 | /// 85 | ref Complex opOpAssign(string op : "-", R)(const R rhs) return 86 | if (isNumeric!R) 87 | { 88 | re -= rhs; 89 | return this; 90 | } 91 | 92 | /// 93 | ref Complex opOpAssign(string op : "*", R)(const R rhs) return 94 | if (isNumeric!R) 95 | { 96 | re *= rhs; 97 | return this; 98 | } 99 | 100 | /// 101 | ref Complex opOpAssign(string op : "/", R)(const R rhs) return 102 | if (isNumeric!R) 103 | { 104 | re /= rhs; 105 | return this; 106 | } 107 | 108 | scope const: 109 | 110 | /// 111 | bool opEquals(const Complex rhs) 112 | { 113 | return re == rhs.re && im == rhs.im; 114 | } 115 | 116 | /// 117 | size_t toHash() 118 | { 119 | T[2] val = [re, im]; 120 | return hashOf(val) ; 121 | } 122 | 123 | /// 124 | bool opEquals(R)(Complex!R rhs) 125 | if (!is(R == T)) 126 | { 127 | return re == rhs.re && im == rhs.im; 128 | } 129 | 130 | /// 131 | bool opEquals(F)(const F rhs) 132 | if (isNumeric!F) 133 | { 134 | return re == rhs && im == 0; 135 | } 136 | 137 | /// 138 | Complex opUnary(string op : "+")() 139 | { 140 | return this; 141 | } 142 | 143 | /// 144 | Complex opUnary(string op : "-")() 145 | { 146 | return typeof(return)(-re, -im); 147 | } 148 | 149 | /// 150 | Complex!(CommonType!(T, R)) opBinary(string op : "+", R)(Complex!R rhs) 151 | { 152 | return typeof(return)(re + rhs.re, im + rhs.im); 153 | } 154 | 155 | /// 156 | Complex!(CommonType!(T, R)) opBinary(string op : "-", R)(Complex!R rhs) 157 | { 158 | return typeof(return)(re - rhs.re, im - rhs.im); 159 | } 160 | 161 | /// 162 | Complex!(CommonType!(T, R)) opBinary(string op : "*", R)(Complex!R rhs) 163 | { 164 | return typeof(return)(re * rhs.re - im * rhs.im, re * rhs.im + im * rhs.re); 165 | } 166 | 167 | /// 168 | Complex!(CommonType!(T, R)) opBinary(string op : "/", R)(Complex!R rhs) 169 | { 170 | // TODO: use more precise algorithm 171 | auto norm = rhs.re * rhs.re + rhs.im * rhs.im; 172 | return typeof(return)( 173 | (re * rhs.re + im * rhs.im) / norm, 174 | (im * rhs.re - re * rhs.im) / norm, 175 | ); 176 | } 177 | 178 | /// 179 | Complex!(CommonType!(T, R)) opBinary(string op : "+", R)(const R rhs) 180 | if (isNumeric!R) 181 | { 182 | return typeof(return)(re + rhs, im); 183 | } 184 | 185 | /// 186 | Complex!(CommonType!(T, R)) opBinary(string op : "-", R)(const R rhs) 187 | if (isNumeric!R) 188 | { 189 | return typeof(return)(re - rhs, im); 190 | } 191 | 192 | /// 193 | Complex!(CommonType!(T, R)) opBinary(string op : "*", R)(const R rhs) 194 | if (isNumeric!R) 195 | { 196 | return typeof(return)(re * rhs, im * rhs); 197 | } 198 | 199 | /// 200 | Complex!(CommonType!(T, R)) opBinary(string op : "/", R)(const R rhs) 201 | if (isNumeric!R) 202 | { 203 | return typeof(return)(re / rhs, im / rhs); 204 | } 205 | 206 | 207 | /// 208 | Complex!(CommonType!(T, R)) opBinaryRight(string op : "+", R)(const R rhs) 209 | if (isNumeric!R) 210 | { 211 | return typeof(return)(rhs + re, im); 212 | } 213 | 214 | /// 215 | Complex!(CommonType!(T, R)) opBinaryRight(string op : "-", R)(const R rhs) 216 | if (isNumeric!R) 217 | { 218 | return typeof(return)(rhs - re, -im); 219 | } 220 | 221 | /// 222 | Complex!(CommonType!(T, R)) opBinaryRight(string op : "*", R)(const R rhs) 223 | if (isNumeric!R) 224 | { 225 | return typeof(return)(rhs * re, rhs * im); 226 | } 227 | 228 | /// 229 | Complex!(CommonType!(T, R)) opBinaryRight(string op : "/", R)(const R rhs) 230 | if (isNumeric!R) 231 | { 232 | // TODO: use more precise algorithm 233 | auto norm = this.re * this.re + this.im * this.im; 234 | return typeof(return)( 235 | rhs * (this.re / norm), 236 | -rhs * (this.im / norm), 237 | ); 238 | } 239 | 240 | /// 241 | R opCast(R)() 242 | if (isNumeric!R || isComplex!R) 243 | { 244 | static if (isNumeric!R) 245 | return cast(R) re; 246 | else 247 | return R(re, im); 248 | } 249 | } 250 | 251 | /// ditto 252 | Complex!T complex(T)(const T re, const T im = 0) 253 | if (is(T == float) || is(T == double) || is(T == real)) 254 | { 255 | return typeof(return)(re, im); 256 | } 257 | 258 | private alias _cdouble_ = Complex!double; 259 | private alias _cfloat_ = Complex!float; 260 | private alias _creal_ = Complex!real; 261 | 262 | /// 263 | unittest 264 | { 265 | auto a = complex(1.0, 3); 266 | auto b = a; 267 | b.re += 3; 268 | a = b; 269 | assert(a == b); 270 | 271 | a = Complex!float(5, 6); 272 | assert(a == Complex!real(5, 6)); 273 | 274 | a += b; 275 | a -= b; 276 | a *= b; 277 | a /= b; 278 | 279 | a = a + b; 280 | a = a - b; 281 | a = a * b; 282 | a = a / b; 283 | 284 | a += 2; 285 | a -= 2; 286 | a *= 2; 287 | a /= 2; 288 | 289 | a = a + 2; 290 | a = a - 2; 291 | a = a * 2; 292 | a = a / 2; 293 | 294 | a = 2 + a; 295 | a = 2 - a; 296 | a = 2 * a; 297 | a = 2 / a; 298 | 299 | a = -a; 300 | a = +a; 301 | 302 | assert(a != 4.0); 303 | a = 4; 304 | assert(a == 4); 305 | assert(cast(int)a == 4); 306 | assert(cast(Complex!float)a == 4); 307 | 308 | import std.complex : StdComplex = Complex; 309 | assert(cast(StdComplex!double)a == StdComplex!double(4, 0)); 310 | } 311 | 312 | /** 313 | Constructs a complex number given its absolute value and argument. 314 | Params: 315 | modulus = The modulus 316 | argument = The argument 317 | Returns: The complex number with the given modulus and argument. 318 | */ 319 | Complex!T fromPolar(T)(const T modulus, const T argument) 320 | @safe pure nothrow @nogc 321 | if (__traits(isFloating, T)) 322 | { 323 | import mir.math.common: sin, cos; 324 | return typeof(return)(modulus * cos(argument), modulus * sin(argument)); 325 | } 326 | 327 | /// 328 | @safe pure nothrow version(mir_core_test) unittest 329 | { 330 | import mir.math : approxEqual, PI, sqrt; 331 | auto z = fromPolar(sqrt(2.0), double(PI / 4)); 332 | assert(approxEqual(z.re, 1.0)); 333 | assert(approxEqual(z.im, 1.0)); 334 | } 335 | 336 | /++ 337 | Params: z = A complex number. 338 | Returns: The complex conjugate of `z`. 339 | +/ 340 | Complex!T conj(T)(Complex!T z) @safe pure nothrow @nogc 341 | { 342 | return Complex!T(z.re, -z.im); 343 | } 344 | 345 | /// 346 | @safe pure nothrow version(mir_core_test) unittest 347 | { 348 | assert(conj(complex(1.0)) == complex(1.0)); 349 | assert(conj(complex(1.0, 2.0)) == complex(1.0, -2.0)); 350 | } 351 | 352 | /++ 353 | Params: z = A complex number. 354 | Returns: The argument (or phase) of `z`. 355 | +/ 356 | T arg(T)(Complex!T z) @safe pure nothrow @nogc 357 | { 358 | import std.math.trigonometry : atan2; 359 | return atan2(z.im, z.re); 360 | } 361 | 362 | /// 363 | @safe pure nothrow version(mir_core_test) unittest 364 | { 365 | import mir.math.constant: PI_2, PI_4; 366 | assert(arg(complex(1.0)) == 0.0); 367 | assert(arg(complex(0.0L, 1.0L)) == PI_2); 368 | assert(arg(complex(1.0L, 1.0L)) == PI_4); 369 | } 370 | 371 | 372 | /** 373 | Params: z = A complex number. 374 | Returns: The absolute value (or modulus) of `z`. 375 | */ 376 | T cabs(T)(Complex!T z) @safe pure nothrow @nogc 377 | { 378 | import std.math.algebraic : hypot; 379 | return hypot(z.re, z.im); 380 | } 381 | 382 | /// 383 | @safe pure nothrow version(mir_core_test) unittest 384 | { 385 | import mir.math.common: sqrt; 386 | assert(cabs(complex(1.0)) == 1.0); 387 | assert(cabs(complex(0.0, 1.0)) == 1.0); 388 | assert(cabs(complex(1.0L, -2.0L)) == sqrt(5.0L)); 389 | } 390 | 391 | @safe pure nothrow @nogc version(mir_core_test) unittest 392 | { 393 | import mir.math.common: sqrt; 394 | assert(cabs(complex(0.0L, -3.2L)) == 3.2L); 395 | assert(cabs(complex(0.0L, 71.6L)) == 71.6L); 396 | assert(cabs(complex(-1.0L, 1.0L)) == sqrt(2.0L)); 397 | } 398 | -------------------------------------------------------------------------------- /source/mir/conv.d: -------------------------------------------------------------------------------- 1 | /++ 2 | Conversion utilities. 3 | 4 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 | Authors: Ilia Ki 6 | +/ 7 | module mir.conv; 8 | 9 | version (D_Exceptions) import mir.exception: toMutable; 10 | public import core.lifetime: emplace; 11 | 12 | import std.traits; 13 | 14 | private template isMirString(T) 15 | { 16 | static if (isSomeString!T) 17 | { 18 | enum isMirString = true; 19 | } 20 | else 21 | { 22 | static if (__traits(compiles, {import mir.small_string;})) 23 | { 24 | import mir.small_string; 25 | enum isMirString = is(T : SmallString!size, size_t size); 26 | } 27 | else 28 | { 29 | enum isMirString = false; 30 | } 31 | } 32 | } 33 | 34 | /++ 35 | The `to` template converts a value from one type _to another. 36 | The source type is deduced and the target type must be specified, for example the 37 | expression `to!int(42.0)` converts the number 42 from 38 | `double` _to `int`. The conversion is "unsafe", i.e., 39 | it does not check for overflow. 40 | +/ 41 | template to(T) 42 | { 43 | /// 44 | auto ref T to(A...)(auto ref A args) 45 | if (A.length > 0) 46 | { 47 | import mir.utility; 48 | import mir.functional: forward; 49 | static if (A.length == 1 && isImplicitlyConvertible!(A[0], T)) 50 | return args[0]; 51 | else 52 | static if (is(T == class) && is(typeof(new T(forward!args)))) 53 | return new T(forward!args); 54 | else 55 | static if (is(typeof(T(args)))) 56 | return T(forward!args); 57 | else 58 | static if (A.length == 1) 59 | { 60 | alias I = A[0]; 61 | alias arg = args[0]; 62 | static if (is(typeof(cast(T) arg)) && !(isDynamicArray!T && isDynamicArray!I) && !isSomeString!T) 63 | return cast(T) forward!arg; 64 | else 65 | static if (isSomeString!I && is(T == enum)) 66 | { 67 | import mir.enums; 68 | uint index = void; 69 | if (getEnumIndexFromKey!T(arg, index)._expect(true)) 70 | return index.unsafeEnumFromIndex!T; 71 | static immutable msg = "Can not convert string to the enum " ~ T.stringof; 72 | version (D_Exceptions) 73 | { 74 | static immutable Exception exc = new Exception(msg); 75 | throw exc.toMutable; 76 | } 77 | else 78 | { 79 | assert(0, msg); 80 | } 81 | } 82 | else 83 | static if (is(I == enum) && isSomeString!T) 84 | { 85 | import mir.enums; 86 | uint id = void; 87 | if (getEnumIndex(arg, id)._expect(true)) 88 | return enumStrings!I[id]; 89 | assert(0); 90 | } 91 | else 92 | static if (isMirString!I && !isSomeString!T) 93 | { 94 | static assert (__traits(compiles, { import mir.parse: fromString; })); 95 | import mir.parse: fromString; 96 | return fromString!(Unqual!T)(arg[]); 97 | } 98 | else 99 | static if (!isSomeString!I && isMirString!T) 100 | { 101 | // static if (is(Unqual!I == typeof(null))) 102 | // { 103 | // enum immutable(T) ret = "null"; 104 | // static if (isImplicitlyConvertible!(immutable T, T)) 105 | // return ret; 106 | // else 107 | // return .to!T(ret[]); 108 | // } 109 | // else 110 | static if (is(Unqual!I == bool)) 111 | { 112 | enum immutable(T) t = "true"; 113 | enum immutable(T) f = "false"; 114 | auto ret = arg ? t : f; 115 | static if (isImplicitlyConvertible!(immutable T, T)) 116 | return ret; 117 | else 118 | return .to!T(ret[]); 119 | } 120 | else 121 | { 122 | static if (isImplicitlyConvertible!(T, string) && __traits(compiles, () {const(char)[] s = arg.toString; return s;})) 123 | { 124 | auto ret = arg.toString; 125 | static if (is(typeof(ret) == string)) 126 | return ret; 127 | else 128 | return ret.idup; 129 | } 130 | else 131 | { 132 | static assert (__traits(compiles, { import mir.format: print; })); 133 | import mir.format: print; 134 | static if (isSomeString!T) 135 | { 136 | static if (isNumeric!I) 137 | { 138 | import mir.appender: UnsafeArrayBuffer; 139 | alias C = Unqual!(ForeachType!T); 140 | C[64] array = void; 141 | auto buffer = UnsafeArrayBuffer!C(array); 142 | } 143 | else 144 | { 145 | import mir.appender: scopedBuffer; 146 | auto buffer = scopedBuffer!(Unqual!(ForeachType!T)); 147 | } 148 | buffer.print(arg); 149 | static if (isMutable!(ForeachType!(T))) 150 | return buffer.data.dup; 151 | else 152 | return buffer.data.idup; 153 | } 154 | else 155 | { 156 | Unqual!T buffer; 157 | buffer.print(arg); 158 | return buffer; 159 | } 160 | } 161 | } 162 | } 163 | else 164 | static if (is(I : const(C)[], C) && is(T : immutable(C)[])) 165 | { 166 | static if (is(I : immutable(C)[])) 167 | return arg; 168 | else 169 | return idup(arg); 170 | } 171 | else 172 | static if (is(I : const(D)[], D) && is(T : D[])) 173 | { 174 | static if (is(I : D[])) 175 | return arg; 176 | else 177 | return dup(arg); 178 | } 179 | else 180 | static assert(0, T.stringof); 181 | } 182 | else 183 | static assert(0, T.stringof); 184 | } 185 | } 186 | 187 | /// 188 | @safe pure @nogc 189 | version(mir_core_test) unittest 190 | { 191 | enum E 192 | { 193 | A, 194 | B, 195 | C, 196 | } 197 | 198 | assert(to!E("B") == E.B); 199 | assert(to!string(E.B) == "B"); 200 | assert(to!string(null) is null); 201 | assert(to!string(true) == "true"); 202 | assert(to!string(false) == "false"); 203 | 204 | enum S : wstring 205 | { 206 | a = "A", 207 | b = "B", 208 | } 209 | 210 | assert(to!wstring(S.b) == "B"w); 211 | assert(to!S("B"w) == S.b); 212 | } 213 | 214 | /++ 215 | Emplace helper function. 216 | +/ 217 | void emplaceInitializer(T)(scope ref T chunk) @trusted pure nothrow 218 | 219 | { 220 | // Emplace T.init. 221 | // Previously, an immutable static and memcpy were used to hold an initializer. 222 | // With improved unions, this is no longer needed. 223 | union UntypedInit 224 | { 225 | T dummy; 226 | } 227 | static struct UntypedStorage 228 | { 229 | align(T.alignof) void[T.sizeof] dummy; 230 | } 231 | 232 | () @trusted { 233 | *cast(UntypedStorage*) &chunk = cast(UntypedStorage) UntypedInit.init; 234 | } (); 235 | } 236 | 237 | /++ 238 | +/ 239 | T[] uninitializedFillDefault(T)(return scope T[] array) nothrow @nogc 240 | { 241 | static if (__VERSION__ < 2083) 242 | { 243 | static if (__traits(isIntegral, T) && 0 == cast(T) (T.init + 1)) 244 | { 245 | import core.stdc.string : memset; 246 | memset(array.ptr, 0xff, T.sizeof * array.length); 247 | return array; 248 | } 249 | else 250 | { 251 | pragma(inline, false); 252 | foreach(ref e; array) 253 | emplaceInitializer(e); 254 | return array; 255 | } 256 | } 257 | else 258 | { 259 | static if (__traits(isZeroInit, T)) 260 | { 261 | import core.stdc.string : memset; 262 | memset(array.ptr, 0, T.sizeof * array.length); 263 | return array; 264 | } 265 | else static if (__traits(isIntegral, T) && 0 == cast(T) (T.init + 1)) 266 | { 267 | import core.stdc.string : memset; 268 | memset(array.ptr, 0xff, T.sizeof * array.length); 269 | return array; 270 | } 271 | else 272 | { 273 | pragma(inline, false); 274 | foreach(ref e; array) 275 | emplaceInitializer(e); 276 | return array; 277 | } 278 | } 279 | } 280 | 281 | /// 282 | pure nothrow @nogc @system 283 | version(mir_core_test) unittest 284 | { 285 | static struct S { int x = 42; @disable this(this); } 286 | 287 | int[5] expected = [42, 42, 42, 42, 42]; 288 | S[5] arr = void; 289 | uninitializedFillDefault(arr); 290 | assert((cast(int*) arr.ptr)[0 .. arr.length] == expected); 291 | } 292 | 293 | /// 294 | @system 295 | version(mir_core_test) unittest 296 | { 297 | int[] a = [1, 2, 4]; 298 | uninitializedFillDefault(a); 299 | assert(a == [0, 0, 0]); 300 | } 301 | 302 | /++ 303 | Destroy structs and unions usnig `__xdtor` member if any. 304 | Do nothing for other types. 305 | +/ 306 | void xdestroy(T)(scope T[] ar) 307 | { 308 | static if ((is(T == struct) || is(T == union)) && __traits(hasMember, T, "__xdtor")) 309 | { 310 | static if (__traits(isSame, T, __traits(parent, ar[0].__xdtor))) 311 | { 312 | pragma(inline, false); 313 | foreach_reverse (ref e; ar) 314 | e.__xdtor(); 315 | } 316 | } 317 | } 318 | 319 | /// 320 | nothrow @nogc version(mir_core_test) unittest 321 | { 322 | __gshared int d; 323 | __gshared int c; 324 | struct D { ~this() nothrow @nogc {d++;} } 325 | extern(C++) 326 | struct C { ~this() nothrow @nogc {c++;} } 327 | C[2] carray; 328 | D[2] darray; 329 | carray.xdestroy; 330 | darray.xdestroy; 331 | assert(c == 2); 332 | assert(d == 2); 333 | c = 0; 334 | d = 0; 335 | } 336 | 337 | 338 | template emplaceRef(T) 339 | { 340 | void emplaceRef(UT, Args...)(ref UT chunk, auto ref Args args) 341 | { 342 | import core.lifetime: forward; 343 | import core.internal.lifetime: emplaceRef; 344 | return emplaceRef!T(chunk, forward!args); 345 | } 346 | } 347 | 348 | void emplaceRef(UT, Args...)(ref UT chunk, auto ref Args args) 349 | if (is(UT == Unqual!UT)) 350 | { 351 | import core.lifetime: forward; 352 | emplaceRef!UT(chunk, forward!args); 353 | } 354 | -------------------------------------------------------------------------------- /source/mir/enums.d: -------------------------------------------------------------------------------- 1 | /++ 2 | Enum utilities. 3 | 4 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 | Authors: Ilia Ki 6 | Macros: 7 | +/ 8 | module mir.enums; 9 | 10 | private bool hasSeqGrow(T)(T[] elems) 11 | if (__traits(isIntegral, T)) 12 | { 13 | assert(elems.length); 14 | auto min = elems[0]; 15 | foreach (i, e; elems) 16 | if (i != e - min) 17 | return false; 18 | return true; 19 | } 20 | 21 | /++ 22 | Enum index that corresponds of the list returned by `std.traits.EnumMembers`. 23 | Returns: 24 | enum member position index in the enum definition that corresponds the `value`. 25 | +/ 26 | bool getEnumIndex(T)(const T value, ref uint index) 27 | @safe pure nothrow @nogc 28 | if (is(T == enum)) 29 | { 30 | import std.traits: EnumMembers, isSomeString; 31 | import mir.utility: _expect; 32 | 33 | static if (__traits(isFloating, T)) 34 | { 35 | // TODO: index based binary searach 36 | foreach (i, member; enumMembers!T) 37 | { 38 | if (value == member) 39 | { 40 | index = cast(uint) i; 41 | return true; 42 | } 43 | } 44 | return false; 45 | } 46 | else 47 | static if (!__traits(isIntegral, T)) //strings 48 | { 49 | enum string[1] stringEnumValue(alias symbol) = [symbol]; 50 | return getEnumIndexFromKey!(T, false, stringEnumValue)(value, index); 51 | } 52 | else 53 | static if (hasSeqGrow(enumMembers!T)) 54 | { 55 | import std.traits: Unsigned; 56 | const shifted = cast(Unsigned!(typeof(value - T.min)))(value - T.min); 57 | if (_expect(shifted < enumMembers!T.length, true)) 58 | { 59 | index = cast(uint) shifted; 60 | return true; 61 | } 62 | return false; 63 | } 64 | else 65 | static if (is(T : bool)) 66 | { 67 | index = !value; 68 | return true; 69 | } 70 | else 71 | { 72 | import std.traits: Unsigned; 73 | alias U = Unsigned!(typeof(T.max - T.min + 1)); 74 | 75 | enum length = cast(size_t)cast(U)(T.max - T.min + 1); 76 | 77 | const shifted = cast(size_t)cast(U)(value - T.min); 78 | 79 | static if (length <= 255) 80 | { 81 | static immutable ubyte[length] table = (){ 82 | ubyte[length] ret; 83 | foreach (i, member; enumMembers!T) 84 | { 85 | ret[member - T.min] = cast(ubyte)(i + 1); 86 | } 87 | return ret; 88 | }(); 89 | 90 | if (_expect(shifted < length, true)) 91 | { 92 | int id = table[shifted] - 1; 93 | if (_expect(id >= 0, true)) 94 | { 95 | index = id; 96 | return true; 97 | } 98 | } 99 | return false; 100 | } 101 | else 102 | { 103 | switch (value) 104 | { 105 | foreach (i, member; EnumMembers!T) 106 | { 107 | case member: 108 | index = i; 109 | return true; 110 | } 111 | default: return false; 112 | } 113 | } 114 | } 115 | } 116 | 117 | /// 118 | @safe pure nothrow @nogc 119 | version(mir_core_test) unittest 120 | { 121 | import std.meta: AliasSeq; 122 | 123 | enum Common { a, b, c } 124 | enum Reversed { a = 1, b = 0, c = -1 } 125 | enum Shifted { a = -4, b, c } 126 | enum Small { a = -4, b, c = 10 } 127 | enum Big { a = -4, b, c = 1000 } 128 | enum InverseBool { True = true, False = false } 129 | enum FP : float { a = -4, b, c } 130 | enum S : string { a = "а", b = "б", c = "ц" } 131 | 132 | uint index = -1; 133 | foreach (E; AliasSeq!(Common, Reversed, Shifted, Small, Big, FP, S)) 134 | { 135 | assert(getEnumIndex(E.a, index) && index == 0); 136 | assert(getEnumIndex(E.b, index) && index == 1); 137 | assert(getEnumIndex(E.c, index) && index == 2); 138 | } 139 | 140 | assert(getEnumIndex(InverseBool.True, index) && index == 0); 141 | assert(getEnumIndex(InverseBool.False, index) && index == 1); 142 | } 143 | 144 | /++ 145 | Static immutable instance of `[std.traits.EnumMembers!T]`. 146 | +/ 147 | template enumMembers(T) 148 | if (is(T == enum)) 149 | { 150 | import std.traits: EnumMembers; 151 | /// 152 | static immutable T[EnumMembers!T.length] enumMembers = [EnumMembers!T]; 153 | } 154 | 155 | /// 156 | version(mir_core_test) unittest 157 | { 158 | enum E {a = 1, b = -1, c} 159 | static assert(enumMembers!E == [E.a, E.b, E.c]); 160 | } 161 | 162 | /++ 163 | Static immutable instance of Enum Identifiers. 164 | +/ 165 | template enumIdentifiers(T) 166 | if (is(T == enum)) 167 | { 168 | import std.traits: EnumMembers; 169 | static immutable string[EnumMembers!T.length] enumIdentifiers = () { 170 | string[EnumMembers!T.length] identifiers; 171 | static foreach(i, member; EnumMembers!T) 172 | identifiers[i] = __traits(identifier, EnumMembers!T[i]); 173 | return identifiers; 174 | } (); 175 | } 176 | 177 | /// 178 | version(mir_core_test) unittest 179 | { 180 | enum E {z = 1, b = -1, c} 181 | static assert(enumIdentifiers!E == ["z", "b", "c"]); 182 | } 183 | 184 | /++ 185 | Aliases itself to $(LREF enumMembers) for string enums and 186 | $(LREF enumIdentifiers) for integral and floating point enums. 187 | +/ 188 | template enumStrings(T) 189 | if (is(T == enum)) 190 | { 191 | static if (is(T : C[], C)) 192 | alias enumStrings = enumMembers!T; 193 | else 194 | alias enumStrings = enumIdentifiers!T; 195 | } 196 | 197 | /// 198 | version(mir_core_test) unittest 199 | { 200 | enum E {z = 1, b = -1, c} 201 | static assert(enumStrings!E == ["z", "b", "c"]); 202 | 203 | enum S {a = "A", b = "B", c = ""} 204 | static assert(enumStrings!S == [S.a, S.b, S.c]); 205 | } 206 | 207 | /++ 208 | Params: 209 | index = enum index `std.traits.EnumMembers!T` 210 | Returns: 211 | A enum value that corresponds to the index. 212 | Note: 213 | The function doesn't check that index is less then `EnumMembers!T.length`. 214 | +/ 215 | T unsafeEnumFromIndex(T)(size_t index) 216 | @trusted pure nothrow @nogc 217 | if (is(T == enum)) 218 | { 219 | static if (__traits(isIntegral, T)) 220 | enum bool fastConv = hasSeqGrow(enumMembers!T); 221 | else 222 | enum bool fastConv = false; 223 | 224 | assert(index < enumMembers!T.length); 225 | 226 | static if (fastConv) 227 | { 228 | return cast(T) (index + enumMembers!T[0]); 229 | } 230 | else 231 | { 232 | return enumMembers!T[index]; 233 | } 234 | } 235 | 236 | /// 237 | version(mir_core_test) 238 | unittest 239 | { 240 | enum Linear 241 | { 242 | one = 1, 243 | two = 2 244 | } 245 | 246 | static assert(is(typeof(unsafeEnumFromIndex!Linear(0)) == Linear)); 247 | assert(unsafeEnumFromIndex!Linear(0) == Linear.one); 248 | assert(unsafeEnumFromIndex!Linear(1) == Linear.two); 249 | 250 | enum Mixed 251 | { 252 | one = 1, 253 | oneAgain = 1, 254 | two = 2 255 | } 256 | 257 | assert(unsafeEnumFromIndex!Mixed(0) == Mixed.one); 258 | assert(unsafeEnumFromIndex!Mixed(1) == Mixed.one); 259 | assert(unsafeEnumFromIndex!Mixed(2) == Mixed.two); 260 | } 261 | 262 | /++ 263 | Params: 264 | T = enum type to introspect 265 | key = some string that corresponds to some key name of the given enum 266 | index = resulting enum index if this method returns true. 267 | Returns: 268 | boolean whether the key was found in the enum keys and if so, index is set. 269 | +/ 270 | template getEnumIndexFromKey(T, bool caseInsensitive = true, getKeysTemplate...) 271 | if (is(T == enum) && getKeysTemplate.length <= 1) 272 | { 273 | /// 274 | bool getEnumIndexFromKey(C)(scope const(C)[] key, ref uint index) 275 | @safe pure nothrow @nogc 276 | if (is(C == char) || is(C == wchar) || is(C == dchar)) 277 | { 278 | import mir.string_table; 279 | import mir.utility: simpleSort, _expect; 280 | import std.traits: EnumMembers; 281 | import std.meta: staticIndexOf; 282 | 283 | alias String = immutable(C)[]; 284 | 285 | static if (getKeysTemplate.length) 286 | { 287 | alias keysOfImpl = getKeysTemplate[0]; 288 | enum String[] keysOf(alias symbol) = keysOfImpl!symbol; 289 | } 290 | else 291 | static if (is(T : W[], W)) 292 | enum String[1] keysOf(alias symbol) = [cast(String)symbol]; 293 | else 294 | enum String[1] keysOf(alias symbol) = [__traits(identifier, symbol)]; 295 | 296 | enum keys = () { 297 | String[] keys; 298 | foreach(i, member; EnumMembers!T) 299 | keys ~= keysOf!(EnumMembers!T[i]); 300 | return keys; 301 | } (); 302 | 303 | static if (keys.length == 0) 304 | { 305 | return false; 306 | } 307 | else 308 | { 309 | enum indexLength = keys.length + 1; 310 | alias ct = createTable!C; 311 | static immutable table = ct!(keys, caseInsensitive); 312 | static immutable indices = () 313 | { 314 | minimalSignedIndexType!indexLength[indexLength] indices; 315 | 316 | foreach (i, member; EnumMembers!T) 317 | foreach (key; keysOf!(EnumMembers!T[i])) 318 | { 319 | static if (caseInsensitive) 320 | { 321 | key = key.dup.fastToUpperInPlace; 322 | } 323 | indices[table[key]] = i; 324 | } 325 | 326 | return indices; 327 | } (); 328 | 329 | uint stringId = void; 330 | if (_expect(table.get(key, stringId), true)) 331 | { 332 | index = indices[stringId]; 333 | return true; 334 | } 335 | return false; 336 | } 337 | } 338 | } 339 | 340 | /// 341 | unittest 342 | { 343 | enum Short 344 | { 345 | hello, 346 | world 347 | } 348 | 349 | enum Long 350 | { 351 | This, 352 | Is, 353 | An, 354 | Enum, 355 | With, 356 | Lots, 357 | Of, 358 | Very, 359 | Long, 360 | EntriesThatArePartiallyAlsoVeryLongInStringLengthAsWeNeedToTestALotOfDifferentCasesThatCouldHappenInRealWorldCode_tm 361 | } 362 | 363 | uint i; 364 | assert(getEnumIndexFromKey!Short("hello", i)); 365 | assert(i == 0); 366 | assert(getEnumIndexFromKey!Short("world", i)); 367 | assert(i == 1); 368 | assert(!getEnumIndexFromKey!Short("foo", i)); 369 | 370 | assert(getEnumIndexFromKey!Short("HeLlO", i)); 371 | assert(i == 0); 372 | assert(getEnumIndexFromKey!Short("WoRLd", i)); 373 | assert(i == 1); 374 | 375 | assert(!getEnumIndexFromKey!(Short, false)("HeLlO", i)); 376 | assert(!getEnumIndexFromKey!(Short, false)("WoRLd", i)); 377 | 378 | assert(getEnumIndexFromKey!Long("Is", i)); 379 | assert(i == 1); 380 | assert(getEnumIndexFromKey!Long("Long", i)); 381 | assert(i == 8); 382 | assert(getEnumIndexFromKey!Long("EntriesThatArePartiallyAlsoVeryLongInStringLengthAsWeNeedToTestALotOfDifferentCasesThatCouldHappenInRealWorldCode_tm", i)); 383 | assert(i == 9); 384 | assert(!getEnumIndexFromKey!Long("EntriesThatArePartiallyAlsoVeryLongInStringLengthAsWeNeedToTestALotOfDifferentCasesThatCouldHappenInRealWorldCodeatm", i)); 385 | 386 | assert(!getEnumIndexFromKey!(Long, false)("EntriesThatArePartiallyAlsoVeryLongInStringLengthAsWeNeedToTestALotOfDifferentCasesThatCouldHappenInRealWorldCode_tM", i)); 387 | assert(!getEnumIndexFromKey!(Long, false)("entriesThatArePartiallyAlsoVeryLongInStringLengthAsWeNeedToTestALotOfDifferentCasesThatCouldHappenInRealWorldCode_tm", i)); 388 | } 389 | -------------------------------------------------------------------------------- /source/mir/exception.d: -------------------------------------------------------------------------------- 1 | /++ 2 | `@nogc` exceptions and errors definitions. 3 | 4 | Most of the API Requires DIP1008. 5 | +/ 6 | module mir.exception; 7 | 8 | version(D_Exceptions): 9 | 10 | version(D_Ddoc) 11 | private enum _version_D_Ddoc = true; 12 | else 13 | private enum _version_D_Ddoc = false; 14 | 15 | private enum NOGCEXP = __traits(compiles, (()@nogc {throw new Exception("");})()); 16 | private enum HASFORMAT = __traits(compiles, (()@nogc {import mir.format;})()); 17 | 18 | package template staticException(string fmt, string file, int line) 19 | { 20 | static immutable staticException = new Exception(fmt, file, line); 21 | } 22 | 23 | @trusted pure nothrow @nogc 24 | Exception toMutable()(immutable Exception e) 25 | { 26 | return cast() e; 27 | } 28 | 29 | @trusted pure nothrow @nogc 30 | Error toMutable()(immutable Error e) 31 | { 32 | return cast() e; 33 | } 34 | 35 | @trusted pure nothrow @nogc 36 | Exception toMutable()(const Exception e) 37 | { 38 | return cast() e; 39 | } 40 | 41 | @trusted pure nothrow @nogc 42 | Error toMutable()(const Error e) 43 | { 44 | return cast() e; 45 | } 46 | 47 | /// 48 | auto ref enforce(string fmt, string file = __FILE__, int line = __LINE__, Expr)(scope auto return ref Expr arg) @trusted 49 | { 50 | version(LDC) pragma(inline, true); 51 | import core.lifetime: forward; 52 | import mir.utility: _expect; 53 | static if (__traits(compiles, arg !is null)) 54 | { 55 | if (_expect(arg !is null, true)) 56 | return forward!arg; 57 | } 58 | else 59 | { 60 | if (_expect(cast(bool)arg, true)) 61 | return forward!arg; 62 | } 63 | throw staticException!(fmt, file, line).toMutable; 64 | } 65 | 66 | /// 67 | @safe pure nothrow @nogc 68 | version (mir_core_test) unittest 69 | { 70 | import mir.exception; 71 | try enforce!"Msg"(false); 72 | catch(Exception e) assert(e.msg == "Msg"); 73 | } 74 | 75 | /++ 76 | +/ 77 | class MirException : Exception 78 | { 79 | /// 80 | mixin MirThrowableImpl; 81 | } 82 | 83 | /// Generic style 84 | version (mir_test) static if (NOGCEXP && HASFORMAT) 85 | @safe pure nothrow @nogc 86 | unittest 87 | { 88 | static if (__traits(compiles, (()@nogc {import mir.format;})())) 89 | { 90 | import mir.exception; 91 | try throw new MirException("Hi D", 2, "!"); 92 | catch(MirException e) assert(e.scopeMessage == "Hi D2!"); 93 | } 94 | } 95 | 96 | /// Generic style, GC allocated MSG 97 | version (mir_test) static if (NOGCEXP && HASFORMAT) 98 | @safe pure nothrow @nogc 99 | unittest 100 | { 101 | static if (__traits(compiles, (()@nogc {import mir.format;})())) 102 | { 103 | import mir.exception; 104 | try throw new MirException("Hi D", 2, "!"); 105 | catch(Exception e) assert(e.message == "Hi D2!"); 106 | } 107 | } 108 | 109 | /// C++ style 110 | version (mir_test) static if (NOGCEXP && HASFORMAT) 111 | @safe pure nothrow @nogc 112 | unittest 113 | { 114 | static if (__traits(compiles, (()@nogc {import mir.format;})())) 115 | { 116 | import mir.exception; 117 | import mir.format; 118 | try throw new MirException(stringBuf() << "Hi D" << 2 << "!" << getData); 119 | catch(Exception e) assert(e.scopeMessage == "Hi D2!"); 120 | } 121 | } 122 | 123 | /// Low-level style 124 | version (mir_test) static if (NOGCEXP && HASFORMAT) 125 | @safe pure nothrow @nogc 126 | unittest 127 | { 128 | static if (__traits(compiles, (()@nogc {import mir.format;})())) 129 | { 130 | import mir.exception; 131 | import mir.format; 132 | auto buffer = stringBuf(); 133 | try throw new MirException(buf.print( "Hi D", 2, "!").data); 134 | catch(Exception e) assert(e.msg == "Hi D2!"); 135 | } 136 | } 137 | 138 | /// 139 | version (mir_core_test) static if (NOGCEXP) 140 | @safe pure nothrow @nogc 141 | unittest 142 | { 143 | @safe pure nothrow @nogc 144 | bool func(scope const(char)[] msg) 145 | { 146 | /// scope messages are copied 147 | try throw new MirException(msg); 148 | catch(Exception e) assert(e.msg == msg); 149 | 150 | /// immutable strings are not copied 151 | static immutable char[] gmsg = "global msg"; 152 | try throw new MirException(gmsg); 153 | catch(Exception e) assert(e.msg is gmsg); 154 | 155 | return __ctfe; 156 | } 157 | 158 | assert(func("runtime-time check") == 0); 159 | 160 | static assert(func("compile-time check") == 1); 161 | } 162 | 163 | // /// 164 | // auto ref enforce(T, Args...)(scope auto return ref T arg, lazy @nogc Args args, string file = __FILE__, int line = __LINE__) @nogc 165 | // if (Args.length) 166 | // { 167 | // import mir.utility: _expect; 168 | // static if (__traits(compiles, arg !is null)) 169 | // { 170 | // if (_expect(arg !is null, true)) 171 | // return arg; 172 | // } 173 | // else 174 | // { 175 | // if (_expect(cast(bool)arg, true)) 176 | // return arg; 177 | // } 178 | // import mir.format; 179 | // stringBuf buf; 180 | // throw new MirException(buf.print(args).data, file, line); 181 | // } 182 | 183 | // /// 184 | // @safe pure nothrow @nogc 185 | // version (mir_core_test) unittest static if (NOGCEXP && HASFORMAT) 186 | // { 187 | // import mir.exception; 188 | // try enforce(false, "Hi D", 2, "!"); 189 | // catch(Exception e) assert(e.msg == "Hi D2!"); 190 | // } 191 | 192 | // /// 193 | // auto ref enforce(T)(scope auto return ref T arg, lazy scope const(char)[] msg, string file = __FILE__, int line = __LINE__) @nogc 194 | // { 195 | // import core.lifetime: forward; 196 | // import mir.utility: _expect; 197 | // static if (__traits(compiles, arg !is null)) 198 | // { 199 | // if (_expect(arg !is null, true)) 200 | // return forward!arg[0]; 201 | // } 202 | // else 203 | // { 204 | // if (_expect(cast(bool)arg, true)) 205 | // return forward!arg[0]; 206 | // } 207 | // throw new MirException(msg, file, line); 208 | // } 209 | 210 | // /// 211 | // @safe pure nothrow @nogc 212 | // version (mir_core_test) unittest static if (NOGCEXP && HASFORMAT) 213 | // { 214 | // import mir.exception; 215 | // try enforce(false, "Msg"); 216 | // catch(Exception e) assert(e.msg == "Msg"); 217 | // } 218 | 219 | 220 | /++ 221 | +/ 222 | class MirError : Error 223 | { 224 | /// 225 | mixin MirThrowableImpl; 226 | } 227 | 228 | /// 229 | @system pure nothrow @nogc 230 | version (mir_test) static if (NOGCEXP) 231 | unittest 232 | { 233 | @system pure nothrow @nogc 234 | bool func(scope const(char)[] msg) 235 | { 236 | /// scope messages are copied 237 | try throw new MirException(msg); 238 | catch(Exception e) assert(e.msg == msg); 239 | 240 | /// immutable strings are not copied 241 | static immutable char[] gmsg = "global msg"; 242 | try throw new MirError(gmsg); 243 | catch(Error e) assert(e.msg is gmsg); 244 | 245 | return __ctfe; 246 | } 247 | 248 | assert(func("runtime-time check") == 0); 249 | 250 | static assert(func("compile-time check") == 1); 251 | } 252 | 253 | /++ 254 | +/ 255 | mixin template MirThrowableImpl() 256 | { 257 | private bool _global; 258 | private char[maxMirExceptionMsgLen] _payload = void; 259 | import mir.exception: maxMirExceptionMsgLen, mirExceptionInitilizePayloadImpl; 260 | 261 | const(char)[] _msg; 262 | 263 | override string message() const @safe pure nothrow 264 | { 265 | return _msg ? _msg.idup : msg; 266 | } 267 | 268 | const(char)[] scopeMessage() scope const @safe pure nothrow @nogc 269 | { 270 | return _msg ? _msg : msg; 271 | } 272 | 273 | /++ 274 | Params: 275 | msg = message. No-scope `msg` is assumed to have the same lifetime as the throwable. scope strings are copied to internal buffer. 276 | file = file name, zero terminated global string 277 | line = line number 278 | nextInChain = next exception in the chain (optional) 279 | +/ 280 | @nogc @trusted pure nothrow this(scope const(char)[] msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null) 281 | { 282 | this._msg = mirExceptionInitilizePayloadImpl(_payload, msg); 283 | super(cast(immutable)this._msg, file, line, nextInChain); 284 | } 285 | 286 | /// ditto 287 | @nogc @trusted pure nothrow this(scope const(char)[] msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__) 288 | { 289 | this._msg = mirExceptionInitilizePayloadImpl(_payload, msg); 290 | super(cast(immutable)this._msg, file, line, nextInChain); 291 | } 292 | 293 | /// ditto 294 | @nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null) 295 | { 296 | this._global = true; 297 | super(msg, file, line, nextInChain); 298 | } 299 | 300 | /// ditto 301 | @nogc @safe pure nothrow this(string msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__) 302 | { 303 | this._global = true; 304 | super(msg, file, line, nextInChain); 305 | } 306 | 307 | /// 308 | ~this() @trusted 309 | { 310 | import mir.internal.memory: free; 311 | if (!_global && msg.ptr != _payload.ptr) 312 | free(cast(void*)msg.ptr); 313 | } 314 | 315 | /++ 316 | Generic multiargument overload. 317 | Constructs a string using the `print` function. 318 | +/ 319 | this(Args...)(auto ref scope const Args args, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null) pure 320 | if (Args.length > 1 && !is(Args[$ - 1] == Throwable)) 321 | { 322 | static assert (__traits(compiles, {import mir.format;}), "MirThrowableImpl needs mir-algorithm for mir.format and exception formatting."); 323 | import mir.format; 324 | auto buf = stringBuf(); 325 | foreach(ref arg; args) 326 | buf.print(arg); 327 | this(buf.data, file, line, nextInChain); 328 | } 329 | 330 | this(Args...)(auto ref scope const Args args) pure @trusted 331 | if (Args.length > 4 && is(Args[$ - 1] == Throwable)) 332 | { 333 | static assert (__traits(compiles, {import mir.format;}), "MirThrowableImpl needs mir-algorithm for mir.format and exception formatting."); 334 | import mir.format; 335 | auto buf = stringBuf(); 336 | foreach(ref arg; args[0 .. $ - 3]) 337 | buf.print(arg); 338 | this(buf.data, args[$ - 3 .. $ - 1], cast() args[$ - 1]); 339 | } 340 | } 341 | 342 | /// 343 | enum maxMirExceptionMsgLen = 447; 344 | 345 | pragma(inline, false) 346 | pure nothrow @nogc @safe 347 | const(char)[] mirExceptionInitilizePayloadImpl(ref return char[maxMirExceptionMsgLen] payload, scope const(char)[] msg) 348 | { 349 | import mir.internal.memory: malloc; 350 | import core.stdc.string: memcpy; 351 | if (msg.length > payload.length) 352 | { 353 | if (auto ret = (() @trusted 354 | { 355 | if (__ctfe) 356 | return null; 357 | if (auto ptr = malloc(msg.length)) 358 | { 359 | memcpy(ptr, msg.ptr, msg.length); 360 | return cast(const(char)[]) ptr[0 .. msg.length]; 361 | } 362 | return null; 363 | })()) 364 | return ret; 365 | msg = msg[0 .. payload.length]; 366 | // remove tail UTF-8 symbol chunk if any 367 | uint c = msg[$-1]; 368 | if (c > 0b_0111_1111) 369 | { 370 | do { 371 | c = msg[$-1]; 372 | msg = msg[0 .. $ - 1]; 373 | } 374 | while (msg.length && c < 0b_1100_0000); 375 | } 376 | } 377 | if (__ctfe) 378 | payload[][0 .. msg.length] = msg; 379 | else 380 | (() @trusted => memcpy(payload.ptr, msg.ptr, msg.length))(); 381 | return payload[0 .. msg.length]; 382 | } 383 | -------------------------------------------------------------------------------- /source/mir/internal/memory.d: -------------------------------------------------------------------------------- 1 | /++ 2 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 3 | Authors: $(HTTP erdani.com, Andrei Alexandrescu), Ilia Ki (Mir rework of original Phobos code) 4 | +/ 5 | module mir.internal.memory; 6 | 7 | pure nothrow @nogc extern(C) 8 | { 9 | /// 10 | void* malloc(size_t size); 11 | /// 12 | void* calloc(size_t nmemb, size_t size); 13 | /// 14 | void* realloc(void* ptr, size_t size); 15 | /// 16 | void free(void* ptr); 17 | } 18 | 19 | pure: 20 | 21 | enum uint platformAlignment = double.alignof > real.alignof ? double.alignof : real.alignof; 22 | 23 | @nogc nothrow: 24 | 25 | @safe pure 26 | package bool isGoodDynamicAlignment()(uint x) 27 | { 28 | return (x & -x) > (x - 1) && x >= (void*).sizeof; 29 | } 30 | 31 | version (Posix) 32 | private extern(C) int posix_memalign(void**, size_t, size_t); 33 | 34 | version (Windows) 35 | { 36 | // DMD Win 32 bit, DigitalMars C standard library misses the _aligned_xxx 37 | // functions family (snn.lib) 38 | version(CRuntime_DigitalMars) 39 | { 40 | // Helper to cast the infos written before the aligned pointer 41 | // this header keeps track of the size (required to realloc) and of 42 | // the base ptr (required to free). 43 | private struct AlignInfo() 44 | { 45 | void* basePtr; 46 | size_t size; 47 | 48 | @nogc nothrow pragma(inline, true) 49 | static AlignInfo* opCall()(void* ptr) 50 | { 51 | return cast(AlignInfo*) (ptr - AlignInfo.sizeof); 52 | } 53 | } 54 | 55 | 56 | private void* _aligned_malloc()(size_t size, size_t alignment) 57 | { 58 | size_t offset = alignment + size_t.sizeof * 2 - 1; 59 | 60 | // unaligned chunk 61 | void* basePtr = malloc(size + offset); 62 | if (!basePtr) return null; 63 | 64 | // get aligned location within the chunk 65 | void* alignedPtr = cast(void**)((cast(size_t)(basePtr) + offset) 66 | & ~(alignment - 1)); 67 | 68 | // write the header before the aligned pointer 69 | AlignInfo!()* head = AlignInfo!()(alignedPtr); 70 | head.basePtr = basePtr; 71 | head.size = size; 72 | 73 | return alignedPtr; 74 | } 75 | 76 | 77 | private void* _aligned_realloc()(void* ptr, size_t size, size_t alignment) 78 | { 79 | import core.stdc.string : memcpy; 80 | 81 | if (!ptr) return _aligned_malloc(size, alignment); 82 | 83 | // gets the header from the existing pointer 84 | AlignInfo!()* head = AlignInfo!()(ptr); 85 | 86 | // gets a new aligned pointer 87 | void* alignedPtr = _aligned_malloc(size, alignment); 88 | if (!alignedPtr) 89 | { 90 | //to https://msdn.microsoft.com/en-us/library/ms235462.aspx 91 | //see Return value: in this case the original block is unchanged 92 | return null; 93 | } 94 | 95 | // copy existing data 96 | memcpy(alignedPtr, ptr, head.size); 97 | free(head.basePtr); 98 | 99 | return alignedPtr; 100 | } 101 | 102 | 103 | private void _aligned_free()(void *ptr) 104 | { 105 | if (!ptr) return; 106 | AlignInfo!()* head = AlignInfo!()(ptr); 107 | free(head.basePtr); 108 | } 109 | 110 | } 111 | // DMD Win 64 bit, uses microsoft standard C library which implements them 112 | else 113 | { 114 | private extern(C) void _aligned_free(void *); 115 | private extern(C) void* _aligned_malloc(size_t, size_t); 116 | private extern(C) void* _aligned_realloc(void *, size_t, size_t); 117 | } 118 | } 119 | 120 | /** 121 | Uses $(HTTP man7.org/linux/man-pages/man3/posix_memalign.3.html, 122 | $(D posix_memalign)) on Posix and 123 | $(HTTP msdn.microsoft.com/en-us/library/8z34s9c6(v=vs.80).aspx, 124 | $(D __aligned_malloc)) on Windows. 125 | */ 126 | version(Posix) 127 | @trusted 128 | void* alignedAllocate()(size_t bytes, uint a) 129 | { 130 | import core.stdc.errno : ENOMEM, EINVAL; 131 | assert(a.isGoodDynamicAlignment); 132 | void* result; 133 | auto code = posix_memalign(&result, a, bytes); 134 | if (code == ENOMEM) 135 | return null; 136 | 137 | else if (code == EINVAL) 138 | { 139 | assert(0, "AlignedMallocator.alignment is not a power of two " 140 | ~"multiple of (void*).sizeof, according to posix_memalign!"); 141 | } 142 | else if (code != 0) 143 | assert (0, "posix_memalign returned an unknown code!"); 144 | 145 | else 146 | return result; 147 | } 148 | else version(Windows) 149 | @trusted 150 | void* alignedAllocate()(size_t bytes, uint a) 151 | { 152 | return _aligned_malloc(bytes, a); 153 | } 154 | else static assert(0); 155 | 156 | /** 157 | Calls $(D free(b.ptr)) on Posix and 158 | $(HTTP msdn.microsoft.com/en-US/library/17b5h8td(v=vs.80).aspx, 159 | $(D __aligned_free(b.ptr))) on Windows. 160 | */ 161 | version (Posix) 162 | { 163 | alias alignedFree = free; 164 | } 165 | else 166 | version (Windows) 167 | @system 168 | void alignedFree()(void* b) 169 | { 170 | _aligned_free(b); 171 | } 172 | else static assert(0); 173 | 174 | /** 175 | On Posix, uses $(D alignedAllocate) and copies data around because there is 176 | no realloc for aligned memory. On Windows, calls 177 | $(HTTP msdn.microsoft.com/en-US/library/y69db7sx(v=vs.80).aspx, 178 | $(D __aligned_realloc(b.ptr, newSize, a))). 179 | */ 180 | version (Windows) 181 | @system 182 | void alignedReallocate()(ref void* b, size_t s, uint a) 183 | { 184 | if (!s) 185 | { 186 | alignedFree(b); 187 | b = null; 188 | return; 189 | } 190 | auto p = cast(ubyte*) _aligned_realloc(b, s, a); 191 | if (!p) return; 192 | b = p; 193 | } 194 | 195 | /// 196 | version(mir_core_test) unittest 197 | { 198 | auto buffer = alignedAllocate(1024 * 1024 * 4, 128); 199 | alignedFree(buffer); 200 | //... 201 | } 202 | 203 | version (CRuntime_DigitalMars) version(unittest) 204 | private size_t addr(ref void* ptr) { return cast(size_t) ptr; } 205 | 206 | version(CRuntime_DigitalMars) 207 | version(mir_core_test) unittest 208 | { 209 | void* m; 210 | 211 | m = _aligned_malloc(16, 0x10); 212 | if (m) 213 | { 214 | assert((m.addr & 0xF) == 0); 215 | _aligned_free(m); 216 | } 217 | 218 | m = _aligned_malloc(16, 0x100); 219 | if (m) 220 | { 221 | assert((m.addr & 0xFF) == 0); 222 | _aligned_free(m); 223 | } 224 | 225 | m = _aligned_malloc(16, 0x1000); 226 | if (m) 227 | { 228 | assert((m.addr & 0xFFF) == 0); 229 | _aligned_free(m); 230 | } 231 | 232 | m = _aligned_malloc(16, 0x10); 233 | if (m) 234 | { 235 | assert((cast(size_t)m & 0xF) == 0); 236 | m = _aligned_realloc(m, 32, 0x10000); 237 | if (m) assert((m.addr & 0xFFFF) == 0); 238 | _aligned_free(m); 239 | } 240 | 241 | m = _aligned_malloc(8, 0x10); 242 | if (m) 243 | { 244 | *cast(ulong*) m = 0X01234567_89ABCDEF; 245 | m = _aligned_realloc(m, 0x800, 0x1000); 246 | if (m) assert(*cast(ulong*) m == 0X01234567_89ABCDEF); 247 | _aligned_free(m); 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /source/mir/internal/utility.d: -------------------------------------------------------------------------------- 1 | /// 2 | module mir.internal.utility; 3 | 4 | private alias AliasSeq(T...) = T; 5 | 6 | /// 7 | alias Iota(size_t j) = Iota!(0, j); 8 | 9 | /// 10 | template Iota(size_t i, size_t j) 11 | { 12 | static assert(i <= j, "Iota: i should be less than or equal to j"); 13 | static if (i == j) 14 | alias Iota = AliasSeq!(); 15 | else 16 | alias Iota = AliasSeq!(i, Iota!(i + 1, j)); 17 | } 18 | 19 | /// 20 | template realType(C) 21 | if (__traits(isFloating, C) || isComplex!C) 22 | { 23 | import std.traits: Unqual; 24 | static if (isComplex!C) 25 | alias realType = typeof(Unqual!C.init.re); 26 | else 27 | alias realType = Unqual!C; 28 | } 29 | 30 | /// 31 | template isComplex(C) 32 | { 33 | static if (is(C == struct) || is(C == enum)) 34 | { 35 | static if (hasField!(C, "re") && hasField!(C, "im") && C.init.tupleof.length == 2) 36 | enum isComplex = isFloatingPoint!(typeof(C.init.tupleof[0])); 37 | else 38 | static if (__traits(getAliasThis, C).length == 1) 39 | enum isComplex = .isComplex!(typeof(__traits(getMember, C, __traits(getAliasThis, C)[0]))); 40 | else 41 | enum isComplex = false; 42 | } 43 | else 44 | { 45 | // for backward compatability with cfloat, cdouble and creal 46 | enum isComplex = __traits(isFloating, C) && !isFloatingPoint!C && !is(C : __vector(F[N]), F, size_t N); 47 | } 48 | } 49 | 50 | /// 51 | version(mir_core_test) 52 | unittest 53 | { 54 | static assert(!isComplex!double); 55 | import mir.complex: Complex; 56 | static assert(isComplex!(Complex!double)); 57 | import std.complex: PhobosComplex = Complex; 58 | static assert(isComplex!(PhobosComplex!double)); 59 | 60 | static struct C 61 | { 62 | Complex!double value; 63 | alias value this; 64 | } 65 | 66 | static assert(isComplex!C); 67 | } 68 | 69 | /// 70 | version(LDC) 71 | version(mir_core_test) 72 | unittest 73 | { 74 | static assert(!isComplex!(__vector(double[2]))); 75 | } 76 | 77 | /// 78 | template isComplexOf(C, F) 79 | if (isFloatingPoint!F) 80 | { 81 | static if (isComplex!C) 82 | enum isComplexOf = is(typeof(C.init.re) == F); 83 | else 84 | enum isComplexOf = false; 85 | } 86 | 87 | /// 88 | template isFloatingPoint(C) 89 | { 90 | import std.traits: Unqual; 91 | alias U = Unqual!C; 92 | enum isFloatingPoint = is(U == double) || is(U == float) || is(U == real); 93 | } 94 | 95 | // copy to reduce imports 96 | enum bool hasField(T, string member) = __traits(compiles, (ref T aggregate) { return __traits(getMember, aggregate, member).offsetof; }); 97 | -------------------------------------------------------------------------------- /source/mir/math/common.d: -------------------------------------------------------------------------------- 1 | /++ 2 | Common floating point math functions. 3 | 4 | This module has generic LLVM-oriented API compatible with all D compilers. 5 | 6 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 7 | Authors: Ilia Ki, Phobos Team 8 | +/ 9 | module mir.math.common; 10 | 11 | import mir.internal.utility: isComplex, isComplexOf, isFloatingPoint; 12 | 13 | version(LDC) 14 | { 15 | static import ldc.attributes; 16 | 17 | private alias AliasSeq(T...) = T; 18 | 19 | /++ 20 | Functions attribute, an alias for `AliasSeq!(llvmFastMathFlag("contract"));`. 21 | 22 | $(UL 23 | $(LI 1. Allow floating-point contraction (e.g. fusing a multiply followed by an addition into a fused multiply-and-add). ) 24 | ) 25 | 26 | Note: Can be used with all compilers. 27 | +/ 28 | alias fmamath = AliasSeq!(ldc.attributes.llvmFastMathFlag("contract")); 29 | 30 | /++ 31 | Functions attribute, an alias for `AliasSeq!(llvmFastMathFlag("fast"))`. 32 | 33 | It is similar to $(LREF fastmath), but does not allow unsafe-fp-math. 34 | This flag does NOT force LDC to use the reciprocal of an argument rather than perform division. 35 | 36 | This flag is default for string lambdas. 37 | 38 | Note: Can be used with all compilers. 39 | +/ 40 | alias optmath = AliasSeq!(ldc.attributes.llvmFastMathFlag("fast")); 41 | 42 | /++ 43 | Functions attribute, an alias for `ldc.attributes.fastmath` . 44 | 45 | $(UL 46 | 47 | $(LI 1. Enable optimizations that make unsafe assumptions about IEEE math (e.g. that addition is associative) or may not work for all input ranges. 48 | These optimizations allow the code generator to make use of some instructions which would otherwise not be usable (such as fsin on X86). ) 49 | 50 | $(LI 2. Allow optimizations to assume the arguments and result are not NaN. 51 | Such optimizations are required to retain defined behavior over NaNs, 52 | but the value of the result is undefined. ) 53 | 54 | $(LI 3. Allow optimizations to assume the arguments and result are not +$(BACKTICK)-inf. 55 | Such optimizations are required to retain defined behavior over +$(BACKTICK)-Inf, 56 | but the value of the result is undefined. ) 57 | 58 | $(LI 4. Allow optimizations to treat the sign of a zero argument or result as insignificant. ) 59 | 60 | $(LI 5. Allow optimizations to use the reciprocal of an argument rather than perform division. ) 61 | 62 | $(LI 6. Allow floating-point contraction (e.g. fusing a multiply followed by an addition into a fused multiply-and-add). ) 63 | 64 | $(LI 7. Allow algebraically equivalent transformations that may dramatically change results in floating point (e.g. reassociate). ) 65 | ) 66 | 67 | Note: Can be used with all compilers. 68 | +/ 69 | alias fastmath = ldc.attributes.fastmath; 70 | } 71 | else 72 | enum 73 | { 74 | /++ 75 | Functions attribute, an alias for `AliasSeq!(llvmFastMathFlag("contract"));`. 76 | 77 | $(UL 78 | $(LI Allow floating-point contraction (e.g. fusing a multiply followed by an addition into a fused multiply-and-add). ) 79 | ) 80 | 81 | Note: Can be used with all compilers. 82 | +/ 83 | fmamath, 84 | 85 | /++ 86 | Functions attribute, an alias for `AliasSeq!(llvmAttr("unsafe-fp-math", "false"), llvmFastMathFlag("fast"))`. 87 | 88 | It is similar to $(LREF fastmath), but does not allow unsafe-fp-math. 89 | This flag does NOT force LDC to use the reciprocal of an argument rather than perform division. 90 | 91 | This flag is default for string lambdas. 92 | 93 | Note: Can be used with all compilers. 94 | +/ 95 | optmath, 96 | 97 | /++ 98 | Functions attribute, an alias for `ldc.attributes.fastmath = AliasSeq!(llvmAttr("unsafe-fp-math", "true"), llvmFastMathFlag("fast"))` . 99 | 100 | $(UL 101 | 102 | $(LI Enable optimizations that make unsafe assumptions about IEEE math (e.g. that addition is associative) or may not work for all input ranges. 103 | These optimizations allow the code generator to make use of some instructions which would otherwise not be usable (such as fsin on X86). ) 104 | 105 | $(LI Allow optimizations to assume the arguments and result are not NaN. 106 | Such optimizations are required to retain defined behavior over NaNs, 107 | but the value of the result is undefined. ) 108 | 109 | $(LI Allow optimizations to assume the arguments and result are not +$(BACKTICK)-inf. 110 | Such optimizations are required to retain defined behavior over +$(BACKTICK)-Inf, 111 | but the value of the result is undefined. ) 112 | 113 | $(LI Allow optimizations to treat the sign of a zero argument or result as insignificant. ) 114 | 115 | $(LI Allow optimizations to use the reciprocal of an argument rather than perform division. ) 116 | 117 | $(LI Allow floating-point contraction (e.g. fusing a multiply followed by an addition into a fused multiply-and-add). ) 118 | 119 | $(LI Allow algebraically equivalent transformations that may dramatically change results in floating point (e.g. reassociate). ) 120 | ) 121 | 122 | Note: Can be used with all compilers. 123 | +/ 124 | fastmath 125 | } 126 | 127 | version(LDC) 128 | { 129 | import ldc.intrinsics: LLVM_version; 130 | nothrow @nogc pure @safe: 131 | 132 | pragma(LDC_intrinsic, "llvm.sqrt.f#") 133 | /// 134 | T sqrt(T)(in T val) if (isFloatingPoint!T); 135 | 136 | pragma(LDC_intrinsic, "llvm.sin.f#") 137 | /// 138 | T sin(T)(in T val) if (isFloatingPoint!T); 139 | 140 | pragma(LDC_intrinsic, "llvm.cos.f#") 141 | /// 142 | T cos(T)(in T val) if (isFloatingPoint!T); 143 | 144 | static if (LLVM_version >= 1300) 145 | pragma(LDC_intrinsic, "llvm.powi.f#.i32") 146 | /// 147 | T powi(T)(in T val, int power) if (isFloatingPoint!T); 148 | else 149 | pragma(LDC_intrinsic, "llvm.powi.f#") 150 | /// 151 | T powi(T)(in T val, int power) if (isFloatingPoint!T); 152 | 153 | version(mir_core_test) 154 | unittest 155 | { 156 | assert(powi(3.0, int(2)) == 9); 157 | float f = 3; 158 | assert(powi(f, int(2)) == 9); 159 | } 160 | 161 | pragma(LDC_intrinsic, "llvm.pow.f#") 162 | /// 163 | T pow(T)(in T val, in T power) if (isFloatingPoint!T); 164 | 165 | pragma(LDC_intrinsic, "llvm.exp.f#") 166 | /// 167 | T exp(T)(in T val) if (isFloatingPoint!T); 168 | 169 | pragma(LDC_intrinsic, "llvm.log.f#") 170 | /// 171 | T log(T)(in T val) if (isFloatingPoint!T); 172 | 173 | pragma(LDC_intrinsic, "llvm.fma.f#") 174 | /// 175 | T fma(T)(T vala, T valb, T valc) if (isFloatingPoint!T); 176 | 177 | pragma(LDC_intrinsic, "llvm.fabs.f#") 178 | /// 179 | T fabs(T)(in T val) if (isFloatingPoint!T); 180 | 181 | pragma(LDC_intrinsic, "llvm.floor.f#") 182 | /// 183 | T floor(T)(in T val) if (isFloatingPoint!T); 184 | 185 | pragma(LDC_intrinsic, "llvm.exp2.f#") 186 | /// 187 | T exp2(T)(in T val) if (isFloatingPoint!T); 188 | 189 | pragma(LDC_intrinsic, "llvm.log10.f#") 190 | /// 191 | T log10(T)(in T val) if (isFloatingPoint!T); 192 | 193 | pragma(LDC_intrinsic, "llvm.log2.f#") 194 | /// 195 | T log2(T)(in T val) if (isFloatingPoint!T); 196 | 197 | pragma(LDC_intrinsic, "llvm.ceil.f#") 198 | /// 199 | T ceil(T)(in T val) if (isFloatingPoint!T); 200 | 201 | pragma(LDC_intrinsic, "llvm.trunc.f#") 202 | /// 203 | T trunc(T)(in T val) if (isFloatingPoint!T); 204 | 205 | pragma(LDC_intrinsic, "llvm.rint.f#") 206 | /// 207 | T rint(T)(in T val) if (isFloatingPoint!T); 208 | 209 | pragma(LDC_intrinsic, "llvm.nearbyint.f#") 210 | /// 211 | T nearbyint(T)(in T val) if (isFloatingPoint!T); 212 | 213 | pragma(LDC_intrinsic, "llvm.copysign.f#") 214 | /// 215 | T copysign(T)(in T mag, in T sgn) if (isFloatingPoint!T); 216 | 217 | pragma(LDC_intrinsic, "llvm.round.f#") 218 | /// 219 | T round(T)(in T val) if (isFloatingPoint!T); 220 | 221 | pragma(LDC_intrinsic, "llvm.fmuladd.f#") 222 | /// 223 | T fmuladd(T)(in T vala, in T valb, in T valc) if (isFloatingPoint!T); 224 | 225 | pragma(LDC_intrinsic, "llvm.minnum.f#") 226 | /// 227 | T fmin(T)(in T vala, in T valb) if (isFloatingPoint!T); 228 | 229 | pragma(LDC_intrinsic, "llvm.maxnum.f#") 230 | /// 231 | T fmax(T)(in T vala, in T valb) if (isFloatingPoint!T); 232 | } 233 | else version(GNU) 234 | { 235 | static import gcc.builtins; 236 | 237 | // Calls GCC builtin for either float (suffix "f"), double (no suffix), or real (suffix "l"). 238 | private enum mixinGCCBuiltin(string fun) = 239 | `static if (T.mant_dig == float.mant_dig) return gcc.builtins.__builtin_`~fun~`f(x);`~ 240 | ` else static if (T.mant_dig == double.mant_dig) return gcc.builtins.__builtin_`~fun~`(x);`~ 241 | ` else static if (T.mant_dig == real.mant_dig) return gcc.builtins.__builtin_`~fun~`l(x);`~ 242 | ` else static assert(0);`; 243 | 244 | // As above but for two-argument function. 245 | private enum mixinGCCBuiltin2(string fun) = 246 | `static if (T.mant_dig == float.mant_dig) return gcc.builtins.__builtin_`~fun~`f(x, y);`~ 247 | ` else static if (T.mant_dig == double.mant_dig) return gcc.builtins.__builtin_`~fun~`(x, y);`~ 248 | ` else static if (T.mant_dig == real.mant_dig) return gcc.builtins.__builtin_`~fun~`l(x, y);`~ 249 | ` else static assert(0);`; 250 | 251 | /// 252 | T sqrt(T)(in T x) if (isFloatingPoint!T) { mixin(mixinGCCBuiltin!`sqrt`); } 253 | /// 254 | T sin(T)(in T x) if (isFloatingPoint!T) { mixin(mixinGCCBuiltin!`sin`); } 255 | /// 256 | T cos(T)(in T x) if (isFloatingPoint!T) { mixin(mixinGCCBuiltin!`cos`); } 257 | /// 258 | T pow(T)(in T x, in T power) if (isFloatingPoint!T) { alias y = power; mixin(mixinGCCBuiltin2!`pow`); } 259 | /// 260 | T powi(T)(in T x, int power) if (isFloatingPoint!T) { alias y = power; mixin(mixinGCCBuiltin2!`powi`); } 261 | /// 262 | T exp(T)(in T x) if (isFloatingPoint!T) { mixin(mixinGCCBuiltin!`exp`); } 263 | /// 264 | T log(T)(in T x) if (isFloatingPoint!T) { mixin(mixinGCCBuiltin!`log`); } 265 | /// 266 | T fabs(T)(in T x) if (isFloatingPoint!T) { mixin(mixinGCCBuiltin!`fabs`); } 267 | /// 268 | T floor(T)(in T x) if (isFloatingPoint!T) { mixin(mixinGCCBuiltin!`floor`); } 269 | /// 270 | T exp2(T)(in T x) if (isFloatingPoint!T) { mixin(mixinGCCBuiltin!`exp2`); } 271 | /// 272 | T log10(T)(in T x) if (isFloatingPoint!T) { mixin(mixinGCCBuiltin!`log10`); } 273 | /// 274 | T log2(T)(in T x) if (isFloatingPoint!T) { mixin(mixinGCCBuiltin!`log2`); } 275 | /// 276 | T ceil(T)(in T x) if (isFloatingPoint!T) { mixin(mixinGCCBuiltin!`ceil`); } 277 | /// 278 | T trunc(T)(in T x) if (isFloatingPoint!T) { mixin(mixinGCCBuiltin!`trunc`); } 279 | /// 280 | T rint(T)(in T x) if (isFloatingPoint!T) { mixin(mixinGCCBuiltin!`rint`); } 281 | /// 282 | T nearbyint(T)(in T x) if (isFloatingPoint!T) { mixin(mixinGCCBuiltin!`nearbyint`); } 283 | /// 284 | T copysign(T)(in T x, in T sgn) if (isFloatingPoint!T) { alias y = sgn; mixin(mixinGCCBuiltin2!`copysign`); } 285 | /// 286 | T round(T)(in T x) if (isFloatingPoint!T) { mixin(mixinGCCBuiltin!`round`); } 287 | /// 288 | T fmuladd(T)(in T a, in T b, in T c) if (isFloatingPoint!T) 289 | { 290 | static if (T.mant_dig == float.mant_dig) 291 | return gcc.builtins.__builtin_fmaf(a, b, c); 292 | else static if (T.mant_dig == double.mant_dig) 293 | return gcc.builtins.__builtin_fma(a, b, c); 294 | else static if (T.mant_dig == real.mant_dig) 295 | return gcc.builtins.__builtin_fmal(a, b, c); 296 | else 297 | static assert(0); 298 | } 299 | version(mir_core_test) 300 | unittest { assert(fmuladd!double(2, 3, 4) == 2 * 3 + 4); } 301 | /// 302 | T fmin(T)(in T x, in T y) if (isFloatingPoint!T) { mixin(mixinGCCBuiltin2!`fmin`); } 303 | /// 304 | T fmax(T)(in T x, in T y) if (isFloatingPoint!T) { mixin(mixinGCCBuiltin2!`fmax`); } 305 | } 306 | else static if (__VERSION__ >= 2082) // DMD 2.082 onward. 307 | { 308 | static import std.math; 309 | static import core.stdc.math; 310 | 311 | // Calls either std.math or cmath function for either float (suffix "f") 312 | // or double (no suffix). std.math will always be used during CTFE or for 313 | // arguments with greater than double precision or if the cmath function 314 | // is impure. 315 | private enum mixinCMath(string fun) = 316 | `pragma(inline, true); 317 | static if (!is(typeof(std.math.`~fun~`(0.5f)) == float) 318 | && is(typeof(() pure => core.stdc.math.`~fun~`f(0.5f)))) 319 | if (!__ctfe) 320 | { 321 | static if (T.mant_dig == float.mant_dig) return core.stdc.math.`~fun~`f(x); 322 | else static if (T.mant_dig == double.mant_dig) return core.stdc.math.`~fun~`(x); 323 | } 324 | return std.math.`~fun~`(x);`; 325 | 326 | // As above but for two-argument function (both arguments must be floating point). 327 | private enum mixinCMath2(string fun) = 328 | `pragma(inline, true); 329 | static if (!is(typeof(std.math.`~fun~`(0.5f, 0.5f)) == float) 330 | && is(typeof(() pure => core.stdc.math.`~fun~`f(0.5f, 0.5f)))) 331 | if (!__ctfe) 332 | { 333 | static if (T.mant_dig == float.mant_dig) return core.stdc.math.`~fun~`f(x, y); 334 | else static if (T.mant_dig == double.mant_dig) return core.stdc.math.`~fun~`(x, y); 335 | } 336 | return std.math.`~fun~`(x, y);`; 337 | 338 | // Some std.math functions have appropriate return types (float, 339 | // double, real) without need for a wrapper. We can alias them 340 | // directly but we leave the templates afterwards for documentation 341 | // purposes and so explicit template instantiation still works. 342 | // The aliases will always match before the templates. 343 | // Note that you cannot put any "static if" around the aliases or 344 | // compilation will fail due to conflict with the templates! 345 | alias sqrt = std.math.sqrt; 346 | alias sin = std.math.sin; 347 | alias cos = std.math.cos; 348 | alias exp = std.math.exp; 349 | //alias fabs = std.math.fabs; 350 | alias floor = std.math.floor; 351 | alias exp2 = std.math.exp2; 352 | alias ceil = std.math.ceil; 353 | alias rint = std.math.rint; 354 | 355 | /// 356 | T sqrt(T)(in T x) if (isFloatingPoint!T) { return std.math.sqrt(x); } 357 | /// 358 | T sin(T)(in T x) if (isFloatingPoint!T) { return std.math.sin(x); } 359 | /// 360 | T cos(T)(in T x) if (isFloatingPoint!T) { return std.math.cos(x); } 361 | /// 362 | T pow(T)(in T x, in T power) if (isFloatingPoint!T) { alias y = power; mixin(mixinCMath2!`pow`); } 363 | /// 364 | T powi(T)(in T x, int power) if (isFloatingPoint!T) { alias y = power; mixin(mixinCMath2!`pow`); } 365 | /// 366 | T exp(T)(in T x) if (isFloatingPoint!T) { return std.math.exp(x); } 367 | /// 368 | T log(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`log`); } 369 | /// 370 | T fabs(T)(in T x) if (isFloatingPoint!T) { return std.math.fabs(x); } 371 | /// 372 | T floor(T)(in T x) if (isFloatingPoint!T) { return std.math.floor(x); } 373 | /// 374 | T exp2(T)(in T x) if (isFloatingPoint!T) { return std.math.exp2(x); } 375 | /// 376 | T log10(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`log10`); } 377 | /// 378 | T log2(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`log2`); } 379 | /// 380 | T ceil(T)(in T x) if (isFloatingPoint!T) { return std.math.ceil(x); } 381 | /// 382 | T trunc(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`trunc`); } 383 | /// 384 | T rint(T)(in T x) if (isFloatingPoint!T) { return std.math.rint(x); } 385 | /// 386 | T nearbyint(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`nearbyint`); } 387 | /// 388 | T copysign(T)(in T mag, in T sgn) if (isFloatingPoint!T) 389 | { 390 | alias x = mag; 391 | alias y = sgn; 392 | mixin(mixinCMath2!`copysign`); 393 | } 394 | /// 395 | T round(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`round`); } 396 | /// 397 | T fmuladd(T)(in T a, in T b, in T c) if (isFloatingPoint!T) { return a * b + c; } 398 | version(mir_core_test) 399 | unittest { assert(fmuladd!double(2, 3, 4) == 2 * 3 + 4); } 400 | /// 401 | T fmin(T)(in T x, in T y) if (isFloatingPoint!T) 402 | { 403 | version (Windows) // https://issues.dlang.org/show_bug.cgi?id=19798 404 | { 405 | version (CRuntime_Microsoft) 406 | mixin(mixinCMath2!`fmin`); 407 | else 408 | return std.math.fmin(x, y); 409 | } 410 | else 411 | mixin(mixinCMath2!`fmin`); 412 | } 413 | /// 414 | T fmax(T)(in T x, in T y) if (isFloatingPoint!T) 415 | { 416 | version (Windows) // https://issues.dlang.org/show_bug.cgi?id=19798 417 | { 418 | version (CRuntime_Microsoft) 419 | mixin(mixinCMath2!`fmax`); 420 | else 421 | return std.math.fmax(x, y); 422 | } 423 | else 424 | mixin(mixinCMath2!`fmax`); 425 | } 426 | 427 | version (mir_core_test) @nogc nothrow pure @safe unittest 428 | { 429 | // Check the aliases are correct. 430 | static assert(is(typeof(sqrt(1.0f)) == float)); 431 | static assert(is(typeof(sin(1.0f)) == float)); 432 | static assert(is(typeof(cos(1.0f)) == float)); 433 | static assert(is(typeof(exp(1.0f)) == float)); 434 | static assert(is(typeof(fabs(1.0f)) == float)); 435 | static assert(is(typeof(floor(1.0f)) == float)); 436 | static assert(is(typeof(exp2(1.0f)) == float)); 437 | static assert(is(typeof(ceil(1.0f)) == float)); 438 | static assert(is(typeof(rint(1.0f)) == float)); 439 | 440 | auto x = sqrt!float(2.0f); // Explicit template instantiation still works. 441 | auto fp = &sqrt!float; // Can still take function address. 442 | 443 | // Test for DMD linker problem with fmin on Windows. 444 | static assert(is(typeof(fmin!float(1.0f, 1.0f)))); 445 | static assert(is(typeof(fmax!float(1.0f, 1.0f)))); 446 | } 447 | } 448 | else // DMD version prior to 2.082 449 | { 450 | static import std.math; 451 | static import core.stdc.math; 452 | 453 | // Calls either std.math or cmath function for either float (suffix "f") 454 | // or double (no suffix). std.math will always be used during CTFE or for 455 | // arguments with greater than double precision or if the cmath function 456 | // is impure. 457 | private enum mixinCMath(string fun) = 458 | `pragma(inline, true); 459 | static if (!is(typeof(std.math.`~fun~`(0.5f)) == float) 460 | && is(typeof(() pure => core.stdc.math.`~fun~`f(0.5f)))) 461 | if (!__ctfe) 462 | { 463 | static if (T.mant_dig == float.mant_dig) return core.stdc.math.`~fun~`f(x); 464 | else static if (T.mant_dig == double.mant_dig) return core.stdc.math.`~fun~`(x); 465 | } 466 | return std.math.`~fun~`(x);`; 467 | 468 | // As above but for two-argument function (both arguments must be floating point). 469 | private enum mixinCMath2(string fun) = 470 | `pragma(inline, true); 471 | static if (!is(typeof(std.math.`~fun~`(0.5f, 0.5f)) == float) 472 | && is(typeof(() pure => core.stdc.math.`~fun~`f(0.5f, 0.5f)))) 473 | if (!__ctfe) 474 | { 475 | static if (T.mant_dig == float.mant_dig) return core.stdc.math.`~fun~`f(x, y); 476 | else static if (T.mant_dig == double.mant_dig) return core.stdc.math.`~fun~`(x, y); 477 | } 478 | return std.math.`~fun~`(x, y);`; 479 | 480 | // Some std.math functions have appropriate return types (float, 481 | // double, real) without need for a wrapper. 482 | alias sqrt = std.math.sqrt; 483 | 484 | /// 485 | T sqrt(T)(in T x) if (isFloatingPoint!T) { return std.math.sqrt(x); } 486 | /// 487 | T sin(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`sin`); } 488 | /// 489 | T cos(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`cos`); } 490 | /// 491 | T pow(T)(in T x, in T power) if (isFloatingPoint!T) { alias y = power; mixin(mixinCMath2!`pow`); } 492 | /// 493 | T powi(T)(in T x, int power) if (isFloatingPoint!T) { alias y = power; mixin(mixinCMath2!`pow`); } 494 | /// 495 | T exp(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`exp`); } 496 | /// 497 | T log(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`log`); } 498 | /// 499 | T fabs(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`fabs`); } 500 | /// 501 | T floor(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`floor`); } 502 | /// 503 | T exp2(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`exp2`); } 504 | /// 505 | T log10(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`log10`); } 506 | /// 507 | T log2(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`log2`); } 508 | /// 509 | T ceil(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`ceil`); } 510 | /// 511 | T trunc(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`trunc`); } 512 | /// 513 | T rint(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`rint`); } 514 | /// 515 | T nearbyint(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`nearbyint`); } 516 | /// 517 | T copysign(T)(in T mag, in T sgn) if (isFloatingPoint!T) 518 | { 519 | alias x = mag; 520 | alias y = sgn; 521 | mixin(mixinCMath2!`copysign`); 522 | } 523 | /// 524 | T round(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`round`); } 525 | /// 526 | T fmuladd(T)(in T a, in T b, in T c) if (isFloatingPoint!T) { return a * b + c; } 527 | version(mir_core_test) 528 | unittest { assert(fmuladd!double(2, 3, 4) == 2 * 3 + 4); } 529 | /// 530 | T fmin(T)(in T x, in T y) if (isFloatingPoint!T) 531 | { 532 | version (Windows) // https://issues.dlang.org/show_bug.cgi?id=19798 533 | { 534 | version (CRuntime_Microsoft) 535 | mixin(mixinCMath2!`fmin`); 536 | else 537 | return std.math.fmin(x, y); 538 | } 539 | else 540 | mixin(mixinCMath2!`fmin`); 541 | } 542 | /// 543 | T fmax(T)(in T x, in T y) if (isFloatingPoint!T) 544 | { 545 | version (Windows) // https://issues.dlang.org/show_bug.cgi?id=19798 546 | { 547 | version (CRuntime_Microsoft) 548 | mixin(mixinCMath2!`fmax`); 549 | else 550 | return std.math.fmax(x, y); 551 | } 552 | else 553 | mixin(mixinCMath2!`fmax`); 554 | } 555 | 556 | version (mir_core_test) @nogc nothrow pure @safe unittest 557 | { 558 | // Check the aliases are correct. 559 | static assert(is(typeof(sqrt(1.0f)) == float)); 560 | auto x = sqrt!float(2.0f); // Explicit template instantiation still works. 561 | auto fp = &sqrt!float; // Can still take function address. 562 | 563 | // Test for DMD linker problem with fmin on Windows. 564 | static assert(is(typeof(fmin!float(1.0f, 1.0f)))); 565 | static assert(is(typeof(fmax!float(1.0f, 1.0f)))); 566 | } 567 | } 568 | 569 | version (mir_core_test) 570 | @nogc nothrow pure @safe unittest 571 | { 572 | import mir.math: PI, feqrel; 573 | assert(feqrel(pow(2.0L, -0.5L), cos(PI / 4)) >= real.mant_dig - 1); 574 | } 575 | 576 | /// Overload for cdouble, cfloat and creal 577 | @optmath auto fabs(T)(in T x) 578 | if (isComplex!T) 579 | { 580 | return x.re * x.re + x.im * x.im; 581 | } 582 | 583 | /// 584 | version(mir_core_test) unittest 585 | { 586 | import mir.complex; 587 | assert(fabs(Complex!double(3, 4)) == 25); 588 | } 589 | 590 | /++ 591 | Computes whether two values are approximately equal, admitting a maximum 592 | relative difference, and a maximum absolute difference. 593 | Params: 594 | lhs = First item to compare. 595 | rhs = Second item to compare. 596 | maxRelDiff = Maximum allowable difference relative to `rhs`. Defaults to `0.5 ^^ 20`. 597 | maxAbsDiff = Maximum absolute difference. Defaults to `0.5 ^^ 20`. 598 | 599 | Returns: 600 | `true` if the two items are equal or approximately equal under either criterium. 601 | +/ 602 | bool approxEqual(T)(const T lhs, const T rhs, const T maxRelDiff = T(0x1p-20f), const T maxAbsDiff = T(0x1p-20f)) 603 | if (isFloatingPoint!T) 604 | { 605 | if (rhs == lhs) // infs 606 | return true; 607 | auto diff = fabs(lhs - rhs); 608 | if (diff <= maxAbsDiff) 609 | return true; 610 | diff /= fabs(rhs); 611 | return diff <= maxRelDiff; 612 | } 613 | 614 | /// 615 | @safe pure nothrow @nogc version(mir_core_test) unittest 616 | { 617 | assert(approxEqual(1.0, 1.0000001)); 618 | assert(approxEqual(1.0f, 1.0000001f)); 619 | assert(approxEqual(1.0L, 1.0000001L)); 620 | 621 | assert(approxEqual(10000000.0, 10000001)); 622 | assert(approxEqual(10000000f, 10000001f)); 623 | assert(!approxEqual(100000.0L, 100001L)); 624 | } 625 | 626 | /// ditto 627 | bool approxEqual(T)(const T lhs, const T rhs, float maxRelDiff = 0x1p-20f, float maxAbsDiff = 0x1p-20f) 628 | if (isComplexOf!(T, float)) 629 | { 630 | return approxEqual(lhs.re, rhs.re, maxRelDiff, maxAbsDiff) 631 | && approxEqual(lhs.im, rhs.im, maxRelDiff, maxAbsDiff); 632 | } 633 | 634 | deprecated("Use mir.complex.approxEqual instead"): 635 | 636 | /// ditto 637 | bool approxEqual(T)(const T lhs, const T rhs, double maxRelDiff = 0x1p-20f, double maxAbsDiff = 0x1p-20f) 638 | if (isComplexOf!(T, double)) 639 | { 640 | return approxEqual(lhs.re, rhs.re, maxRelDiff, maxAbsDiff) 641 | && approxEqual(lhs.im, rhs.im, maxRelDiff, maxAbsDiff); 642 | } 643 | 644 | /// ditto 645 | bool approxEqual(T)(const T lhs, const T rhs, real maxRelDiff = 0x1p-20f, real maxAbsDiff = 0x1p-20f) 646 | if (isComplexOf!(T, real)) 647 | { 648 | return approxEqual(lhs.re, rhs.re, maxRelDiff, maxAbsDiff) 649 | && approxEqual(lhs.im, rhs.im, maxRelDiff, maxAbsDiff); 650 | } 651 | 652 | /// Complex types works as `approxEqual(l.re, r.re) && approxEqual(l.im, r.im)` 653 | @safe pure nothrow @nogc version(mir_core_test) unittest 654 | { 655 | import mir.internal.utility: isComplexOf; 656 | static struct UserComplex(T) { T re, im; } 657 | alias _cdouble = UserComplex!double; 658 | 659 | static assert(isComplexOf!(_cdouble, double)); 660 | 661 | assert(approxEqual(_cdouble(1.0, 1), _cdouble(1.0000001, 1), 1.0000001)); 662 | assert(!approxEqual(_cdouble(100000.0L, 0), _cdouble(100001L, 0))); 663 | } 664 | -------------------------------------------------------------------------------- /source/mir/math/constant.d: -------------------------------------------------------------------------------- 1 | /// Math constants 2 | /// Authors: Phobos Team, Ilia Ki 3 | module mir.math.constant; 4 | 5 | enum real E = 0x1.5bf0a8b1457695355fb8ac404e7a8p+1L; /++ e = 2.718281... +/ 6 | enum real LOG2T = 0x1.a934f0979a3715fc9257edfe9b5fbp+1L; /++ $(SUB log, 2)10 = 3.321928... +/ 7 | enum real LOG2E = 0x1.71547652b82fe1777d0ffda0d23a8p+0L; /++ $(SUB log, 2)e = 1.442695... +/ 8 | enum real LOG2 = 0x1.34413509f79fef311f12b35816f92p-2L; /++ $(SUB log, 10)2 = 0.301029... +/ 9 | enum real LOG10E = 0x1.bcb7b1526e50e32a6ab7555f5a67cp-2L; /++ $(SUB log, 10)e = 0.434294... +/ 10 | enum real LN2 = 0x1.62e42fefa39ef35793c7673007e5fp-1L; /++ ln 2 = 0.693147... +/ 11 | enum real LN10 = 0x1.26bb1bbb5551582dd4adac5705a61p+1L; /++ ln 10 = 2.302585... +/ 12 | enum real PI = 0x1.921fb54442d18469898cc51701b84p+1L; /++ π = 3.141592... +/ 13 | enum real PI_2 = PI/2; /++ $(PI) / 2 = 1.570796... +/ 14 | enum real PI_4 = PI/4; /++ $(PI) / 4 = 0.785398... +/ 15 | enum real M_1_PI = 0x1.45f306dc9c882a53f84eafa3ea69cp-2L; /++ 1 / $(PI) = 0.318309... +/ 16 | enum real M_2_PI = 2*M_1_PI; /++ 2 / $(PI) = 0.636619... +/ 17 | enum real M_2_SQRTPI = 0x1.20dd750429b6d11ae3a914fed7fd8p+0L; /++ 2 / $(SQRT)$(PI) = 1.128379... +/ 18 | enum real SQRT2 = 0x1.6a09e667f3bcc908b2fb1366ea958p+0L; /++ $(SQRT)2 = 1.414213... +/ 19 | enum real SQRT1_2 = SQRT2 / 2; /++ $(SQRT)$(HALF) = 0.707106... +/ 20 | 21 | /++ 22 | Exponent of minus Euler–Mascheroni constant. 23 | +/ 24 | enum real ExpMEuler = 0x0.8fbbcf07f2e5f2c56894d7014c3086p0; 25 | enum real GoldenRatio = 0x1.9e3779b97f4a7c15f39cc0605cedc8341082276bf3a27251f86c6a11d0c18p+0L; /++ Golden Ratio = (1 + sqrt(5)) / 2 = 1.6180339... ++/ 26 | enum real GoldenRatioInverseSquared = 0x0.61c8864680b583ea0c633f9fa31237cbef7dd8940c5d8dae079395ee2f3e71p+0L; /++ Golden Ratio ^ -2 = 0.381966... ++/ 27 | -------------------------------------------------------------------------------- /source/mir/math/package.d: -------------------------------------------------------------------------------- 1 | /++ 2 | $(H1 Math Functionality) 3 | $(BOOKTABLE $(H2 Math modules), 4 | $(TR $(TH Module) $(TH Math kind)) 5 | $(T2M common, Common math functions) 6 | $(T2M constant, Constants) 7 | $(T2M ieee, Basic IEEE-754 numerical functions) 8 | ) 9 | Macros: 10 | SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, math, $1)$(NBSP) 11 | T2M=$(TR $(TDNW $(MREF mir,math,$1)) $(TD $+)) 12 | T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) 13 | +/ 14 | module mir.math; 15 | 16 | public import mir.math.common; 17 | public import mir.math.constant; 18 | public import mir.math.ieee; 19 | -------------------------------------------------------------------------------- /source/mir/primitives.d: -------------------------------------------------------------------------------- 1 | /++ 2 | Templates used to check primitives and 3 | range primitives for arrays with multi-dimensional like API support. 4 | 5 | Note: 6 | UTF strings behaves like common arrays in Mir. 7 | `std.uni.byCodePoint` can be used to create a range of characters. 8 | 9 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 10 | Authors: Ilia Ki, $(HTTP erdani.com, Andrei Alexandrescu), David Simcha, and 11 | $(HTTP jmdavisprog.com, Jonathan M Davis). Credit for some of the ideas 12 | in building this module goes to 13 | $(HTTP fantascienza.net/leonardo/so/, Leonardo Maffi) 14 | +/ 15 | module mir.primitives; 16 | 17 | import mir.internal.utility; 18 | import mir.math.common: optmath; 19 | import std.traits; 20 | 21 | @optmath: 22 | 23 | /++ 24 | Returns: `true` if `R` has a `length` member that returns an 25 | integral type implicitly convertible to `size_t`. 26 | 27 | `R` does not have to be a range. 28 | +/ 29 | enum bool hasLength(R) = is(typeof( 30 | (const R r, inout int = 0) 31 | { 32 | size_t l = r.length; 33 | })); 34 | 35 | /// 36 | @safe version(mir_core_test) unittest 37 | { 38 | static assert(hasLength!(char[])); 39 | static assert(hasLength!(int[])); 40 | static assert(hasLength!(inout(int)[])); 41 | 42 | struct B { size_t length() const { return 0; } } 43 | struct C { @property size_t length() const { return 0; } } 44 | static assert(hasLength!(B)); 45 | static assert(hasLength!(C)); 46 | } 47 | 48 | /++ 49 | Returns: `true` if `R` has a `shape` member that returns an static array type of size_t[N]. 50 | +/ 51 | enum bool hasShape(R) = is(typeof( 52 | (const R r, inout int = 0) 53 | { 54 | auto l = r.shape; 55 | alias F = typeof(l); 56 | import std.traits; 57 | static assert(isStaticArray!F); 58 | static assert(is(ForeachType!F == size_t)); 59 | })); 60 | 61 | /// 62 | @safe version(mir_core_test) unittest 63 | { 64 | static assert(hasShape!(char[])); 65 | static assert(hasShape!(int[])); 66 | static assert(hasShape!(inout(int)[])); 67 | 68 | struct B { size_t length() const { return 0; } } 69 | struct C { @property size_t length() const { return 0; } } 70 | static assert(hasShape!(B)); 71 | static assert(hasShape!(C)); 72 | } 73 | 74 | /// 75 | auto shape(Range)(scope const auto ref Range range) @property 76 | if (hasLength!Range || hasShape!Range) 77 | { 78 | static if (__traits(hasMember, Range, "shape")) 79 | { 80 | return range.shape; 81 | } 82 | else 83 | { 84 | size_t[1] ret; 85 | ret[0] = range.length; 86 | return ret; 87 | } 88 | } 89 | 90 | /// 91 | version(mir_core_test) unittest 92 | { 93 | static assert([2, 2, 2].shape == [3]); 94 | } 95 | 96 | /// 97 | template DimensionCount(T) 98 | { 99 | import mir.ndslice.slice: Slice, SliceKind; 100 | /// Extracts dimension count from a $(LREF Slice). Alias for $(LREF isSlice). 101 | static if(is(T : Slice!(Iterator, N, kind), Iterator, size_t N, SliceKind kind)) 102 | enum size_t DimensionCount = N; 103 | else 104 | static if (hasShape!T) 105 | enum size_t DimensionCount = typeof(T.init.shape).length; 106 | else 107 | enum size_t DimensionCount = 1; 108 | } 109 | 110 | package(mir) bool anyEmptyShape(size_t N)(scope const auto ref size_t[N] shape) @property 111 | { 112 | foreach (i; Iota!N) 113 | if (shape[i] == 0) 114 | return true; 115 | return false; 116 | } 117 | 118 | /// 119 | bool anyEmpty(Range)(scope auto ref Range range) @property 120 | if (hasShape!Range || __traits(hasMember, Range, "anyEmpty") || is(ReturnType!((Range r) => r.empty) == bool)) 121 | { 122 | static if (__traits(hasMember, Range, "anyEmpty")) 123 | { 124 | return range.anyEmpty; 125 | } 126 | else 127 | static if (__traits(hasMember, Range, "shape")) 128 | { 129 | return anyEmptyShape(range.shape); 130 | } 131 | else 132 | { 133 | return range.empty; 134 | } 135 | } 136 | 137 | /// 138 | size_t elementCount(Range)(scope const auto ref Range range) @property 139 | if (hasShape!Range || __traits(hasMember, Range, "elementCount")) 140 | { 141 | static if (__traits(hasMember, Range, "elementCount")) 142 | { 143 | return range.elementCount; 144 | } 145 | else 146 | { 147 | auto sh = range.shape; 148 | size_t ret = sh[0]; 149 | foreach(i; Iota!(1, sh.length)) 150 | { 151 | ret *= sh[i]; 152 | } 153 | return ret; 154 | } 155 | } 156 | 157 | deprecated("use elementCount instead") 158 | alias elementsCount = elementCount; 159 | 160 | 161 | /++ 162 | Returns the element type of a struct with `.DeepElement` inner alias or a type of common array. 163 | Returns `ForeachType` if struct does not have `.DeepElement` member. 164 | +/ 165 | template DeepElementType(S) 166 | if (is(S == struct) || is(S == class) || is(S == interface)) 167 | { 168 | static if (__traits(hasMember, S, "DeepElement")) 169 | alias DeepElementType = S.DeepElement; 170 | else 171 | alias DeepElementType = ForeachType!S; 172 | } 173 | 174 | /// ditto 175 | alias DeepElementType(S : T[], T) = T; 176 | 177 | /+ ARRAY PRIMITIVES +/ 178 | pragma(inline, true): 179 | 180 | /// 181 | bool empty(size_t dim = 0, T)(scope const T[] ar) 182 | if (!dim) 183 | { 184 | return !ar.length; 185 | } 186 | 187 | /// 188 | version(mir_core_test) unittest 189 | { 190 | assert((int[]).init.empty); 191 | assert(![1].empty!0); // Slice-like API 192 | } 193 | 194 | /// 195 | ref inout(T) front(size_t dim = 0, T)(scope return inout(T)[] ar) 196 | if (!dim && !is(Unqual!T[] == void[])) 197 | { 198 | assert(ar.length, "Accessing front of an empty array."); 199 | return ar[0]; 200 | } 201 | 202 | /// 203 | version(mir_core_test) unittest 204 | { 205 | assert(*&[3, 4].front == 3); // access be ref 206 | assert([3, 4].front!0 == 3); // Slice-like API 207 | } 208 | 209 | 210 | /// 211 | ref inout(T) back(size_t dim = 0, T)(scope return inout(T)[] ar) 212 | if (!dim && !is(Unqual!T[] == void[])) 213 | { 214 | assert(ar.length, "Accessing back of an empty array."); 215 | return ar[$ - 1]; 216 | } 217 | 218 | /// 219 | version(mir_core_test) unittest 220 | { 221 | assert(*&[3, 4].back == 4); // access be ref 222 | assert([3, 4].back!0 == 4); // Slice-like API 223 | } 224 | 225 | /// 226 | void popFront(size_t dim = 0, T)(scope ref inout(T)[] ar) 227 | if (!dim && !is(Unqual!T[] == void[])) 228 | { 229 | assert(ar.length, "Evaluating popFront() on an empty array."); 230 | ar = ar[1 .. $]; 231 | } 232 | 233 | /// 234 | version(mir_core_test) unittest 235 | { 236 | auto ar = [3, 4]; 237 | ar.popFront; 238 | assert(ar == [4]); 239 | ar.popFront!0; // Slice-like API 240 | assert(ar == []); 241 | } 242 | 243 | /// 244 | void popBack(size_t dim = 0, T)(scope ref inout(T)[] ar) 245 | if (!dim && !is(Unqual!T[] == void[])) 246 | { 247 | assert(ar.length, "Evaluating popBack() on an empty array."); 248 | ar = ar[0 .. $ - 1]; 249 | } 250 | 251 | /// 252 | version(mir_core_test) unittest 253 | { 254 | auto ar = [3, 4]; 255 | ar.popBack; 256 | assert(ar == [3]); 257 | ar.popBack!0; // Slice-like API 258 | assert(ar == []); 259 | } 260 | 261 | /// 262 | size_t popFrontN(size_t dim = 0, T)(scope ref inout(T)[] ar, size_t n) 263 | if (!dim && !is(Unqual!T[] == void[])) 264 | { 265 | n = ar.length < n ? ar.length : n; 266 | ar = ar[n .. $]; 267 | return n; 268 | } 269 | 270 | /// 271 | version(mir_core_test) unittest 272 | { 273 | auto ar = [3, 4]; 274 | ar.popFrontN(1); 275 | assert(ar == [4]); 276 | ar.popFrontN!0(10); // Slice-like API 277 | assert(ar == []); 278 | } 279 | 280 | /// 281 | size_t popBackN(size_t dim = 0, T)(scope ref inout(T)[] ar, size_t n) 282 | if (!dim && !is(Unqual!T[] == void[])) 283 | { 284 | n = ar.length < n ? ar.length : n; 285 | ar = ar[0 .. $ - n]; 286 | return n; 287 | } 288 | 289 | /// 290 | version(mir_core_test) unittest 291 | { 292 | auto ar = [3, 4]; 293 | ar.popBackN(1); 294 | assert(ar == [3]); 295 | ar.popBackN!0(10); // Slice-like API 296 | assert(ar == []); 297 | } 298 | 299 | /// 300 | void popFrontExactly(size_t dim = 0, T)(scope ref inout(T)[] ar, size_t n) 301 | if (!dim && !is(Unqual!T[] == void[])) 302 | { 303 | assert(ar.length >= n, "Evaluating *.popFrontExactly(n) on an array with length less then n."); 304 | ar = ar[n .. $]; 305 | } 306 | 307 | /// 308 | version(mir_core_test) unittest 309 | { 310 | auto ar = [3, 4, 5]; 311 | ar.popFrontExactly(2); 312 | assert(ar == [5]); 313 | ar.popFrontExactly!0(1); // Slice-like API 314 | assert(ar == []); 315 | } 316 | 317 | /// 318 | void popBackExactly(size_t dim = 0, T)(scope ref inout(T)[] ar, size_t n) 319 | if (!dim && !is(Unqual!T[] == void[])) 320 | { 321 | assert(ar.length >= n, "Evaluating *.popBackExactly(n) on an array with length less then n."); 322 | ar = ar[0 .. $ - n]; 323 | } 324 | 325 | /// 326 | version(mir_core_test) unittest 327 | { 328 | auto ar = [3, 4, 5]; 329 | ar.popBackExactly(2); 330 | assert(ar == [3]); 331 | ar.popBackExactly!0(1); // Slice-like API 332 | assert(ar == []); 333 | } 334 | 335 | /// 336 | size_t length(size_t d : 0, T)(in T[] array) 337 | if (d == 0) 338 | { 339 | return array.length; 340 | } 341 | 342 | /// 343 | version(mir_core_test) unittest 344 | { 345 | assert([1, 2].length!0 == 2); 346 | assert([1, 2].elementCount == 2); 347 | } 348 | 349 | /// 350 | inout(T)[] save(T)(scope return inout(T)[] array) 351 | { 352 | return array; 353 | } 354 | 355 | /// 356 | version(mir_core_test) unittest 357 | { 358 | auto a = [1, 2]; 359 | assert(a is a.save); 360 | } 361 | 362 | /** 363 | Returns `true` if `R` is an input range. An input range must 364 | define the primitives `empty`, `popFront`, and `front`. The 365 | following code should compile for any input range. 366 | ---- 367 | R r; // can define a range object 368 | if (r.empty) {} // can test for empty 369 | r.popFront(); // can invoke popFront() 370 | auto h = r.front; // can get the front of the range of non-void type 371 | ---- 372 | The following are rules of input ranges are assumed to hold true in all 373 | Phobos code. These rules are not checkable at compile-time, so not conforming 374 | to these rules when writing ranges or range based code will result in 375 | undefined behavior. 376 | $(UL 377 | $(LI `r.empty` returns `false` if and only if there is more data 378 | available in the range.) 379 | $(LI `r.empty` evaluated multiple times, without calling 380 | `r.popFront`, or otherwise mutating the range object or the 381 | underlying data, yields the same result for every evaluation.) 382 | $(LI `r.front` returns the current element in the range. 383 | It may return by value or by reference.) 384 | $(LI `r.front` can be legally evaluated if and only if evaluating 385 | `r.empty` has, or would have, equaled `false`.) 386 | $(LI `r.front` evaluated multiple times, without calling 387 | `r.popFront`, or otherwise mutating the range object or the 388 | underlying data, yields the same result for every evaluation.) 389 | $(LI `r.popFront` advances to the next element in the range.) 390 | $(LI `r.popFront` can be called if and only if evaluating `r.empty` 391 | has, or would have, equaled `false`.) 392 | ) 393 | Also, note that Phobos code assumes that the primitives `r.front` and 394 | `r.empty` are $(BIGOH 1) time complexity wise or "cheap" in terms of 395 | running time. $(BIGOH) statements in the documentation of range functions 396 | are made with this assumption. 397 | Params: 398 | R = type to be tested 399 | Returns: 400 | `true` if R is an input range, `false` if not 401 | */ 402 | enum bool isInputRange(R) = 403 | is(typeof(R.init) == R) 404 | && is(ReturnType!((R r) => r.empty) == bool) 405 | && is(typeof((return ref R r) => r.front)) 406 | && !is(ReturnType!((R r) => r.front) == void) 407 | && is(typeof((R r) => r.popFront)); 408 | 409 | /** 410 | Returns `true` if `R` is an infinite input range. An 411 | infinite input range is an input range that has a statically-defined 412 | enumerated member called `empty` that is always `false`, 413 | for example: 414 | ---- 415 | struct MyInfiniteRange 416 | { 417 | enum bool empty = false; 418 | ... 419 | } 420 | ---- 421 | */ 422 | 423 | template isInfinite(R) 424 | { 425 | static if (isInputRange!R && __traits(compiles, { enum e = R.empty; })) 426 | enum bool isInfinite = !R.empty; 427 | else 428 | enum bool isInfinite = false; 429 | } 430 | 431 | 432 | /** 433 | The element type of `R`. `R` does not have to be a range. The 434 | element type is determined as the type yielded by `r.front` for an 435 | object `r` of type `R`. For example, `ElementType!(T[])` is 436 | `T` if `T[]` isn't a narrow string; if it is, the element type is 437 | `dchar`. If `R` doesn't have `front`, `ElementType!R` is 438 | `void`. 439 | */ 440 | template ElementType(R) 441 | { 442 | static if (is(typeof(R.init.front.init) T)) 443 | alias ElementType = T; 444 | else 445 | alias ElementType = void; 446 | } 447 | 448 | /++ 449 | This is a best-effort implementation of `length` for any kind of 450 | range. 451 | If `hasLength!Range`, simply returns `range.length` without 452 | checking `upTo` (when specified). 453 | Otherwise, walks the range through its length and returns the number 454 | of elements seen. Performes $(BIGOH n) evaluations of `range.empty` 455 | and `range.popFront()`, where `n` is the effective length of $(D 456 | range). 457 | +/ 458 | auto walkLength(Range)(Range range) 459 | if (isIterable!Range && !isInfinite!Range) 460 | { 461 | static if (hasLength!Range) 462 | return range.length; 463 | else 464 | static if (__traits(hasMember, Range, "walkLength")) 465 | return range.walkLength; 466 | static if (isInputRange!Range) 467 | { 468 | size_t result; 469 | for ( ; !range.empty ; range.popFront() ) 470 | ++result; 471 | return result; 472 | } 473 | else 474 | { 475 | size_t result; 476 | foreach (ref e; range) 477 | ++result; 478 | return result; 479 | } 480 | } 481 | 482 | /++ 483 | Returns `true` if `R` is an output range for elements of type 484 | `E`. An output range is defined functionally as a range that 485 | supports the operation $(D r.put(e)). 486 | +/ 487 | enum bool isOutputRange(R, E) = 488 | is(typeof(R.init.put(E.init))); 489 | -------------------------------------------------------------------------------- /source/mir/qualifier.d: -------------------------------------------------------------------------------- 1 | /++ 2 | Const and Immutable qualifiers helpers for Mir Type System. 3 | 4 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 | Authors: Ilia Ki 6 | 7 | Macros: 8 | NDSLICE = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP) 9 | +/ 10 | module mir.qualifier; 11 | 12 | import std.traits; 13 | 14 | /++ 15 | +/ 16 | template LightScopeOf(T) 17 | { 18 | static if (isPointer!T) 19 | { 20 | alias LightScopeOf = T; 21 | } 22 | else 23 | { 24 | static if (__traits(hasMember, T, "lightScope")) 25 | alias LightScopeOf = typeof(T.init.lightScope()); 26 | else 27 | static if (is(T == immutable)) 28 | alias LightScopeOf = LightImmutableOf!T; 29 | else 30 | static if (is(T == const)) 31 | alias LightScopeOf = LightConstOf!T; 32 | else 33 | alias LightScopeOf = T; 34 | } 35 | } 36 | 37 | /++ 38 | +/ 39 | template LightConstOf(T) 40 | { 41 | static if (isPointer!T) 42 | { 43 | alias LightConstOf = const(PointerTarget!T)*; 44 | } 45 | else 46 | { 47 | alias LightConstOf = typeof(const(T).init.lightConst()); 48 | } 49 | } 50 | 51 | /// ditto 52 | template LightImmutableOf(T) 53 | { 54 | static if (isPointer!T) 55 | { 56 | alias LightImmutableOf = immutable(PointerTarget!T)*; 57 | } 58 | else 59 | { 60 | alias LightImmutableOf = typeof(immutable(T).init.lightImmutable()); 61 | } 62 | } 63 | 64 | @property: 65 | 66 | /++ 67 | Tries to strip a reference counting handles from the value. 68 | This funciton should be used only when the result never skips the current scope. 69 | 70 | This function is used by some algorithms to optimise work with reference counted types. 71 | +/ 72 | auto ref lightScope(T)(auto ref return scope T v) 73 | if (!is(T : P*, P) && __traits(hasMember, T, "lightScope")) 74 | { 75 | return v.lightScope; 76 | } 77 | 78 | /// ditto 79 | auto ref lightScope(T)(auto return ref T v) 80 | if (!is(T : P*, P) && !__traits(hasMember, T, "lightScope")) 81 | { 82 | static if (is(T == immutable)) 83 | return lightImmutable(v); 84 | else 85 | static if (is(T == const)) 86 | return lightConst(v); 87 | else 88 | return v; 89 | } 90 | 91 | /// ditto 92 | auto lightScope(T)(return T v) 93 | if (is(T : P*, P)) 94 | { 95 | return cast(typeof(*v)*) v; 96 | } 97 | 98 | /// 99 | auto lightImmutable(T)(auto ref immutable T v) 100 | if (!is(T : P*, P) && __traits(hasMember, immutable T, "lightImmutable")) 101 | { 102 | return v.lightImmutable; 103 | } 104 | 105 | /// ditto 106 | T lightImmutable(T)(auto ref immutable T e) 107 | if (!isDynamicArray!T && isImplicitlyConvertible!(immutable T, T) && !__traits(hasMember, immutable T, "lightImmutable")) 108 | { 109 | return e; 110 | } 111 | 112 | /// ditto 113 | auto lightImmutable(T)(immutable(T)[] e) 114 | { 115 | return e; 116 | } 117 | 118 | /// ditto 119 | auto lightImmutable(T)(immutable(T)* e) 120 | { 121 | return e; 122 | } 123 | 124 | /// 125 | auto lightConst(T)(auto ref const T v) 126 | if (!is(T : P*, P) && __traits(hasMember, const T, "lightConst")) 127 | { 128 | return v.lightConst; 129 | } 130 | 131 | /// ditto 132 | auto lightConst(T)(auto ref immutable T v) 133 | if (!is(T : P*, P) && __traits(hasMember, immutable T, "lightConst")) 134 | { 135 | return v.lightConst; 136 | } 137 | 138 | /// ditto 139 | T lightConst(T)(auto ref const T e) 140 | if (!isDynamicArray!T && isImplicitlyConvertible!(const T, T) && !__traits(hasMember, const T, "lightConst")) 141 | { 142 | return e; 143 | } 144 | 145 | /// ditto 146 | T lightConst(T)(auto ref immutable T e) 147 | if (!isDynamicArray!T && isImplicitlyConvertible!(immutable T, T) && !__traits(hasMember, immutable T, "lightConst")) 148 | { 149 | return e; 150 | } 151 | 152 | /// ditto 153 | auto lightConst(T)(const(T)[] e) 154 | { 155 | return e; 156 | } 157 | 158 | /// ditto 159 | auto lightConst(T)(immutable(T)[] e) 160 | { 161 | return e; 162 | } 163 | 164 | /// ditto 165 | auto lightConst(T)(const(T)* e) 166 | { 167 | return e; 168 | } 169 | 170 | /// ditto 171 | auto lightConst(T)(immutable(T)* e) 172 | { 173 | return e; 174 | } 175 | 176 | /// 177 | auto trustedImmutable(T)(auto ref const T e) @trusted 178 | { 179 | return lightImmutable(*cast(immutable) &e); 180 | } 181 | -------------------------------------------------------------------------------- /source/mir/reflection.d: -------------------------------------------------------------------------------- 1 | /++ 2 | Base reflection utilities. 3 | 4 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 | Authors: Ilia Ki 6 | Macros: 7 | +/ 8 | module mir.reflection; 9 | 10 | import std.meta; 11 | import std.traits: Parameters, isSomeFunction, FunctionAttribute, functionAttributes, EnumMembers, isAggregateType; 12 | import mir.internal.meta: hasUDA, getUDAs; 13 | import mir.functional: Tuple; 14 | 15 | deprecated 16 | package alias isSomeStruct = isAggregateType; 17 | 18 | /++ 19 | Attribute to force member serialization for static fields, compiletime `enum` members and non-property methods. 20 | +/ 21 | enum reflectSerde; 22 | 23 | /++ 24 | Match types like `std.typeconst: Nullable`. 25 | +/ 26 | template isStdNullable(T) 27 | { 28 | import std.traits : hasMember; 29 | 30 | private __gshared T* aggregate; 31 | 32 | enum bool isStdNullable = 33 | hasMember!(T, "isNull") && 34 | hasMember!(T, "get") && 35 | hasMember!(T, "nullify") && 36 | is(typeof(__traits(getMember, aggregate, "isNull")()) == bool) && 37 | !is(typeof(__traits(getMember, aggregate, "get")()) == void) && 38 | is(typeof(__traits(getMember, aggregate, "nullify")()) == void); 39 | } 40 | 41 | /// 42 | version(mir_core_test) unittest 43 | { 44 | import std.typecons; 45 | static assert(isStdNullable!(Nullable!double)); 46 | } 47 | 48 | /// 49 | version(mir_core_test) unittest 50 | { 51 | import mir.algebraic; 52 | static assert(isStdNullable!(Nullable!double)); 53 | } 54 | 55 | /// Attribute for deprecated API 56 | struct reflectDeprecated(string target) 57 | { 58 | /// 59 | string msg; 60 | 61 | /++ 62 | Number in an issue tracker. Not mandatory. 63 | +/ 64 | uint issueNumber = uint.max; 65 | /++ 66 | Should be kind of version number if one can be given. 67 | Can be something else if that's not possible. Not mandatory. 68 | +/ 69 | string removalTime; 70 | } 71 | 72 | /// Attribute to rename methods, types and functions 73 | template ReflectName(string target) 74 | { 75 | /// 76 | struct ReflectName(Args...) 77 | { 78 | /// 79 | string name; 80 | } 81 | } 82 | 83 | /// ditto 84 | template reflectName(string target = null, Args...) 85 | { 86 | /// 87 | auto reflectName(string name) 88 | { 89 | alias TargetName = ReflectName!target; 90 | return TargetName!Args(name); 91 | } 92 | } 93 | 94 | /// 95 | version(mir_core_test) unittest 96 | { 97 | enum E { A, B, C } 98 | 99 | struct S 100 | { 101 | @reflectName("A") 102 | int a; 103 | 104 | @reflectName!"c++"("B") 105 | int b; 106 | 107 | @reflectName!("C", double)("cd") 108 | @reflectName!("C", float)("cf") 109 | F c(F)() 110 | { 111 | return b; 112 | } 113 | } 114 | 115 | import std.traits: hasUDA; 116 | 117 | alias UniName = ReflectName!null; 118 | alias CppName = ReflectName!"c++"; 119 | alias CName = ReflectName!"C"; 120 | 121 | static assert(hasUDA!(S.a, UniName!()("A"))); 122 | static assert(hasUDA!(S.b, CppName!()("B"))); 123 | 124 | // static assert(hasUDA!(S.c, ReflectName)); // doesn't work for now 125 | static assert(hasUDA!(S.c, CName)); 126 | static assert(hasUDA!(S.c, CName!double)); 127 | static assert(hasUDA!(S.c, CName!float)); 128 | static assert(hasUDA!(S.c, CName!double("cd"))); 129 | static assert(hasUDA!(S.c, CName!float("cf"))); 130 | } 131 | 132 | /// Attribute to rename methods, types and functions 133 | template ReflectMeta(string target, string[] fields) 134 | { 135 | /// 136 | struct ReflectMeta(Args...) 137 | { 138 | /// 139 | Args args; 140 | static foreach(i, field; fields) 141 | mixin(`alias ` ~ field ~` = args[` ~ i.stringof ~`];`); 142 | } 143 | } 144 | 145 | /// ditto 146 | template reflectMeta(string target, string[] fields) 147 | { 148 | /// 149 | auto reflectMeta(Args...)(Args args) 150 | if (args.length <= fields.length) 151 | { 152 | alias TargetMeta = ReflectMeta!(target, fields); 153 | return TargetMeta!Args(args); 154 | } 155 | } 156 | 157 | /// 158 | version(mir_core_test) unittest 159 | { 160 | enum E { A, B, C } 161 | 162 | struct S 163 | { 164 | int a; 165 | @reflectMeta!("c++", ["type"])(E.C) 166 | int b; 167 | } 168 | 169 | import std.traits: hasUDA; 170 | 171 | alias CppMeta = ReflectMeta!("c++", ["type"]); 172 | 173 | static assert(CppMeta!E(E.C).type == E.C); 174 | static assert(!hasUDA!(S.a, CppMeta!E(E.A))); 175 | static assert(hasUDA!(S.b, CppMeta!E(E.C))); 176 | } 177 | 178 | /++ 179 | Attribute to ignore a reflection target 180 | +/ 181 | template reflectIgnore(string target) 182 | { 183 | enum reflectIgnore; 184 | } 185 | 186 | /// 187 | version(mir_core_test) unittest 188 | { 189 | struct S 190 | { 191 | @reflectIgnore!"c++" 192 | int a; 193 | } 194 | 195 | import std.traits: hasUDA; 196 | static assert(hasUDA!(S.a, reflectIgnore!"c++")); 197 | } 198 | 199 | /// Attribute for documentation and unittests 200 | struct ReflectDoc(string target) 201 | { 202 | /// 203 | string text; 204 | /// 205 | reflectUnittest!target test; 206 | 207 | /// 208 | @safe pure nothrow @nogc 209 | this(string text) 210 | { 211 | this.text = text; 212 | } 213 | 214 | /// 215 | @safe pure nothrow @nogc 216 | this(string text, reflectUnittest!target test) 217 | { 218 | this.text = text; 219 | this.test = test; 220 | } 221 | 222 | /// 223 | void toString(W)(scope ref W w) scope const 224 | { 225 | w.put(cast()this.text); 226 | 227 | if (this.test.text.length) 228 | { 229 | w.put("\nExample usage:\n"); 230 | w.put(cast()this.test.text); 231 | } 232 | } 233 | 234 | /// 235 | @safe pure nothrow 236 | string toString()() scope const 237 | { 238 | return this.text ~ "\nExample usage:\n" ~ this.test.text; 239 | } 240 | } 241 | 242 | /++ 243 | Attribute for documentation. 244 | +/ 245 | template reflectDoc(string target = null) 246 | { 247 | /// 248 | ReflectDoc!target reflectDoc(string text) 249 | { 250 | return ReflectDoc!target(text); 251 | } 252 | 253 | /// 254 | ReflectDoc!target reflectDoc(string text, reflectUnittest!target test) 255 | { 256 | return ReflectDoc!target(text, test); 257 | } 258 | } 259 | 260 | /++ 261 | +/ 262 | template reflectGetDocs(string target, alias symbol) 263 | { 264 | static if (hasUDA!(symbol, ReflectDoc!target)) 265 | static immutable(ReflectDoc!target[]) reflectGetDocs = [getUDAs!(symbol, ReflectDoc!target)]; 266 | else 267 | static immutable(ReflectDoc!target[]) reflectGetDocs = null; 268 | } 269 | 270 | /// ditto 271 | template reflectGetDocs(string target) 272 | { 273 | /// 274 | alias reflectGetDocs(alias symbol) = .reflectGetDocs!(target, symbol); 275 | 276 | /// ditto 277 | immutable(ReflectDoc!target)[] reflectGetDocs(T)(T value) 278 | @safe pure nothrow @nogc 279 | if (is(T == enum)) 280 | { 281 | foreach (i, member; EnumMembers!T) 282 | {{ 283 | alias all = __traits(getAttributes, EnumMembers!T[i]); 284 | }} 285 | static immutable ReflectDoc!target[][EnumMembers!T.length] docs = [staticMap!(reflectGetDocs, EnumMembers!T)]; 286 | import mir.enums: getEnumIndex; 287 | uint index = void; 288 | if (getEnumIndex(value, index)) 289 | return docs[index]; 290 | assert(0); 291 | } 292 | } 293 | 294 | /// 295 | version(mir_core_test) unittest 296 | { 297 | enum E 298 | { 299 | @reflectDoc("alpha") 300 | a, 301 | @reflectDoc!"C#"("Beta", reflectUnittest!"C#"("some c# code")) 302 | @reflectDoc("beta") 303 | b, 304 | c, 305 | } 306 | 307 | alias Doc = ReflectDoc!null; 308 | alias CSDoc = ReflectDoc!"C#"; 309 | 310 | static assert(reflectGetDocs!null(E.a) == [Doc("alpha")]); 311 | static assert(reflectGetDocs!"C#"(E.b) == [CSDoc("Beta", reflectUnittest!"C#"("some c# code"))]); 312 | static assert(reflectGetDocs!null(E.b) == [Doc("beta")]); 313 | static assert(reflectGetDocs!null(E.c) is null); 314 | 315 | struct S 316 | { 317 | @reflectDoc("alpha") 318 | @reflectDoc!"C#"("Alpha") 319 | int a; 320 | } 321 | 322 | static assert(reflectGetDocs!(null, S.a) == [Doc("alpha")]); 323 | static assert(reflectGetDocs!("C#", S.a) == [CSDoc("Alpha")]); 324 | 325 | import std.conv: to; 326 | static assert(CSDoc("Beta", reflectUnittest!"C#"("some c# code")).to!string == "Beta\nExample usage:\nsome c# code"); 327 | } 328 | 329 | /++ 330 | Attribute for extern unit-test. 331 | +/ 332 | struct reflectUnittest(string target) 333 | { 334 | /// 335 | string text; 336 | 337 | @safe pure nothrow @nogc: 338 | 339 | this(string text) 340 | { 341 | this.text = text; 342 | } 343 | 344 | this(const typeof(this) other) 345 | { 346 | this.text = other.text; 347 | } 348 | } 349 | 350 | /++ 351 | +/ 352 | template reflectGetUnittest(string target, alias symbol) 353 | { 354 | static if (hasUDA!(symbol, reflectUnittest!target)) 355 | enum string reflectGetUnittest = getUDA!(symbol, reflectUnittest).text; 356 | else 357 | enum string reflectGetUnittest = null; 358 | } 359 | 360 | /// ditto 361 | template reflectGetUnittest(string target) 362 | { 363 | /// 364 | alias reflectGetUnittest(alias symbol) = .reflectGetUnittest!(target, symbol); 365 | 366 | /// 367 | string reflectGetUnittest(T)(T value) 368 | if (is(T == enum)) 369 | { 370 | foreach (i, member; EnumMembers!T) 371 | {{ 372 | alias all = __traits(getAttributes, EnumMembers!T[i]); 373 | }} 374 | static immutable string[EnumMembers!T.length] tests = [staticMap!(reflectGetUnittest, EnumMembers!T)]; 375 | import mir.enums: getEnumIndex; 376 | uint index = void; 377 | if (getEnumIndex(value, index)) 378 | return tests[index]; 379 | assert(0); 380 | } 381 | } 382 | 383 | /// 384 | version(mir_core_test) unittest 385 | { 386 | enum E 387 | { 388 | @reflectUnittest!"c++"("assert(E::a == 0);") 389 | a, 390 | @reflectUnittest!"c++"("assert(E::b == 1);") 391 | b, 392 | c, 393 | } 394 | 395 | static assert(reflectGetUnittest!"c++"(E.a) == "assert(E::a == 0);"); 396 | static assert(reflectGetUnittest!"c++"(E.b) == "assert(E::b == 1);"); 397 | static assert(reflectGetUnittest!"c++"(E.c) is null); 398 | 399 | struct S 400 | { 401 | @reflectUnittest!"c++"("alpha") 402 | int a; 403 | } 404 | 405 | static assert(reflectGetUnittest!("c++", S.a) == "alpha"); 406 | } 407 | 408 | /++ 409 | Returns: single UDA. 410 | +/ 411 | template getUDA(alias symbol, alias attribute) 412 | { 413 | private alias all = getUDAs!(symbol, attribute); 414 | static if (all.length != 1) 415 | static assert(0, "Exactly one " ~ attribute.stringof ~ " attribute is required, " ~ "got " ~ all.length.stringof); 416 | else 417 | { 418 | static if (is(typeof(all[0]))) 419 | enum getUDA = all[0]; 420 | else 421 | alias getUDA = all[0]; 422 | } 423 | } 424 | 425 | /// ditto 426 | template getUDA(T, string member, alias attribute) 427 | { 428 | private alias all = getUDAs!(T, member, attribute); 429 | static if (all.length != 1) 430 | static assert(0, "Exactly one " ~ attribute.stringof ~ " attribute is required, " ~ "got " ~ all.length.stringof); 431 | else 432 | { 433 | static if (is(typeof(all[0]))) 434 | enum getUDA = all[0]; 435 | else 436 | alias getUDA = all[0]; 437 | } 438 | } 439 | 440 | /++ 441 | Checks if T has a field member. 442 | +/ 443 | enum bool isOriginalMember(T, string member) = __traits(identifier, __traits(getMember, T, member)) == member; 444 | 445 | /// 446 | version(mir_core_test) unittest 447 | { 448 | struct D 449 | { 450 | int a; 451 | alias b = a; 452 | } 453 | 454 | static assert(isOriginalMember!(D, "a")); 455 | static assert(!isOriginalMember!(D, "b")); 456 | } 457 | 458 | /++ 459 | Checks if T has a field member. 460 | +/ 461 | enum bool hasField(T, string member) = __traits(compiles, (ref T aggregate) { return __traits(getMember, aggregate, member).offsetof; }); 462 | 463 | deprecated("use 'hasField' instead") alias isField = hasField; 464 | 465 | /// 466 | version(mir_core_test) unittest 467 | { 468 | struct D 469 | { 470 | int gi; 471 | } 472 | 473 | struct I 474 | { 475 | int f; 476 | 477 | D base; 478 | alias base this; 479 | 480 | void gi(double ) @property {} 481 | void gi(uint ) @property {} 482 | } 483 | 484 | struct S 485 | { 486 | int d; 487 | 488 | I i; 489 | alias i this; 490 | 491 | int gm() @property {return 0;} 492 | int gc() const @property {return 0;} 493 | void gs(int) @property {} 494 | } 495 | 496 | static assert(!hasField!(S, "gi")); 497 | static assert(!hasField!(S, "gs")); 498 | static assert(!hasField!(S, "gc")); 499 | static assert(!hasField!(S, "gm")); 500 | static assert(!hasField!(S, "gi")); 501 | static assert(hasField!(S, "d")); 502 | static assert(hasField!(S, "f")); 503 | static assert(hasField!(S, "i")); 504 | } 505 | 506 | /// with classes 507 | version(mir_core_test) unittest 508 | { 509 | class I 510 | { 511 | int f; 512 | 513 | void gi(double ) @property {} 514 | void gi(uint ) @property {} 515 | } 516 | 517 | class S 518 | { 519 | int d; 520 | 521 | I i; 522 | alias i this; 523 | 524 | int gm() @property {return 0;} 525 | int gc() const @property {return 0;} 526 | void gs(int) @property {} 527 | } 528 | 529 | static assert(!hasField!(S, "gi")); 530 | static assert(!hasField!(S, "gs")); 531 | static assert(!hasField!(S, "gc")); 532 | static assert(!hasField!(S, "gm")); 533 | static assert(hasField!(S, "d")); 534 | static assert(hasField!(S, "f")); 535 | static assert(hasField!(S, "i")); 536 | } 537 | 538 | /++ 539 | Checks if member is property. 540 | +/ 541 | template isProperty(T, string member) 542 | { 543 | private __gshared T* aggregate; 544 | 545 | static if (__traits(compiles, isSomeFunction!(__traits(getMember, *aggregate, member)))) 546 | { 547 | static if (isSomeFunction!(__traits(getMember, *aggregate, member))) 548 | { 549 | enum bool isProperty = isPropertyImpl!(__traits(getMember, *aggregate, member)); 550 | } 551 | else 552 | { 553 | enum bool isProperty = false; 554 | } 555 | } 556 | else 557 | enum bool isProperty = false; 558 | } 559 | 560 | /// 561 | version(mir_core_test) unittest 562 | { 563 | struct D 564 | { 565 | int y; 566 | 567 | void gf(double ) @property {} 568 | void gf(uint ) @property {} 569 | } 570 | 571 | struct I 572 | { 573 | int f; 574 | 575 | D base; 576 | alias base this; 577 | 578 | void gi(double ) @property {} 579 | void gi(uint ) @property {} 580 | } 581 | 582 | struct S 583 | { 584 | int d; 585 | 586 | I i; 587 | alias i this; 588 | 589 | int gm() @property {return 0;} 590 | int gc() const @property {return 0;} 591 | void gs(int) @property {} 592 | } 593 | 594 | static assert(isProperty!(S, "gf")); 595 | static assert(isProperty!(S, "gi")); 596 | static assert(isProperty!(S, "gs")); 597 | static assert(isProperty!(S, "gc")); 598 | static assert(isProperty!(S, "gm")); 599 | static assert(!isProperty!(S, "d")); 600 | static assert(!isProperty!(S, "f")); 601 | static assert(!isProperty!(S, "y")); 602 | } 603 | 604 | version(mir_core_test) unittest 605 | { 606 | struct S 607 | { 608 | @reflectSerde enum s = "str"; 609 | enum t = "str"; 610 | } 611 | static assert(hasUDA!(S, "s", reflectSerde)); 612 | static assert(!hasUDA!(S, "t", reflectSerde)); 613 | } 614 | 615 | /++ 616 | Returns: list of the setter properties. 617 | 618 | Note: The implementation ignores templates. 619 | +/ 620 | template getSetters(T, string member) 621 | { 622 | static if (__traits(hasMember, T, member)) 623 | alias getSetters = Filter!(hasSingleArgument, Filter!(isPropertyImpl, __traits(getOverloads, T, member, true))); 624 | else 625 | alias getSetters = AliasSeq!(); 626 | } 627 | 628 | /// 629 | version(mir_core_test) unittest 630 | { 631 | struct I 632 | { 633 | int f; 634 | 635 | void gi(double ) @property {} 636 | void gi(uint ) @property {} 637 | } 638 | 639 | struct S 640 | { 641 | int d; 642 | 643 | I i; 644 | alias i this; 645 | 646 | int gm() @property {return 0;} 647 | int gc() const @property {return 0;} 648 | void gs(int) @property {} 649 | } 650 | 651 | static assert(getSetters!(S, "gi").length == 2); 652 | static assert(getSetters!(S, "gs").length == 1); 653 | static assert(getSetters!(S, "gc").length == 0); 654 | static assert(getSetters!(S, "gm").length == 0); 655 | static assert(getSetters!(S, "d").length == 0); 656 | static assert(getSetters!(S, "f").length == 0); 657 | } 658 | 659 | /++ 660 | Returns: list of the serializable (public getters) members. 661 | +/ 662 | enum string[] SerializableMembers(T) = [Filter!(ApplyLeft!(Serializable, T), SerdeFieldsAndProperties!T)]; 663 | 664 | /// 665 | version(mir_core_test) unittest 666 | { 667 | struct D 668 | { 669 | int y; 670 | 671 | int gf() @property {return 0;} 672 | } 673 | 674 | struct I 675 | { 676 | int f; 677 | 678 | D base; 679 | alias base this; 680 | 681 | int gi() @property {return 0;} 682 | } 683 | 684 | struct S 685 | { 686 | int d; 687 | 688 | package int p; 689 | 690 | enum s = "str"; 691 | @reflectSerde enum t = "str"; 692 | 693 | int gm() @property {return 0;} 694 | 695 | private int q; 696 | 697 | I i; 698 | alias i this; 699 | 700 | int gc() const @property {return 0;} 701 | void gs(int) @property {} 702 | } 703 | 704 | static assert(SerializableMembers!S == ["y", "gf", "f", "gi", "d", "t", "gm", "gc"]); 705 | static assert(SerializableMembers!(const S) == ["y", "f", "d", "t", "gc"]); 706 | } 707 | 708 | /++ 709 | Returns: list of the deserializable (public setters) members. 710 | +/ 711 | enum string[] DeserializableMembers(T) = [Filter!(ApplyLeft!(Deserializable, T), SerdeFieldsAndProperties!T)]; 712 | 713 | /// 714 | version(mir_core_test) unittest 715 | { 716 | struct I 717 | { 718 | int f; 719 | void ga(int) @property {} 720 | } 721 | 722 | struct S 723 | { 724 | int d; 725 | package int p; 726 | 727 | int gm() @property {return 0;} 728 | void gm(int) @property {} 729 | 730 | private int q; 731 | 732 | I i; 733 | alias i this; 734 | 735 | 736 | void gc(int, int) @property {} 737 | void gc(int) @property {} 738 | } 739 | 740 | S s; 741 | // s.gc(0); 742 | 743 | static assert (DeserializableMembers!S == ["f", "ga", "d", "gm", "gc"]); 744 | static assert (DeserializableMembers!(const S) == []); 745 | } 746 | 747 | // This trait defines what members should be serialized - 748 | // public members that are either readable and writable or getter properties 749 | private template Serializable(T, string member) 750 | { 751 | static if (!isPublic!(T, member)) 752 | enum Serializable = false; 753 | else 754 | enum Serializable = isReadable!(T, member); // any readable is good 755 | } 756 | 757 | private enum bool hasSingleArgument(alias fun) = Parameters!fun.length == 1; 758 | private enum bool hasZeroArguments(alias fun) = Parameters!fun.length == 0; 759 | 760 | // This trait defines what members should be serialized - 761 | // public members that are either readable and writable or setter properties 762 | private template Deserializable(T, string member) 763 | { 764 | static if (!isPublic!(T, member)) 765 | enum Deserializable = false; 766 | else 767 | static if (isReadableAndWritable!(T, member)) 768 | enum Deserializable = true; 769 | else 770 | static if (getSetters!(T, member).length == 1) 771 | enum Deserializable = is(typeof((ref T val){ __traits(getMember, val, member) = Parameters!(getSetters!(T, member)[0])[0].init; })); 772 | else 773 | enum Deserializable = false; 774 | } 775 | 776 | private enum SerdeFieldsAndProperties(T) = Reverse!(NoDuplicates!(Reverse!(SerdeFieldsAndPropertiesImpl!T))); 777 | 778 | 779 | private static immutable exlMembers = [ 780 | "opAssign", 781 | "opCast", 782 | "opCmp", 783 | "opEquals", 784 | "opPostMove", 785 | "toHash", 786 | "toString", 787 | "trustedGet", 788 | "deserializeFromAsdf", 789 | "deserializeFromIon", 790 | ]; 791 | 792 | private auto filterMembers(string[] members) 793 | { 794 | string[] ret; 795 | 796 | L: foreach(member; members) 797 | { 798 | if (member.length >= 2 && (member[0 .. 2] == "__" || member[$ - 2 .. $] == "__")) 799 | continue; 800 | foreach(exlMember; exlMembers) 801 | if (exlMember == member) 802 | continue L; 803 | ret ~= member; 804 | } 805 | return ret; 806 | }; 807 | 808 | private template allMembers(T) 809 | { 810 | import std.meta: aliasSeqOf; 811 | static if (isAggregateType!T) 812 | alias allMembers = aliasSeqOf!(filterMembers([__traits(allMembers, T)])); 813 | else 814 | alias allMembers = AliasSeq!(); 815 | } 816 | 817 | private template SerdeFieldsAndPropertiesImpl(T) 818 | { 819 | alias isProperty = ApplyLeft!(.isProperty, T); 820 | alias hasField = ApplyLeft!(.hasField, T); 821 | alias isOriginalMember = ApplyLeft!(.isOriginalMember, T); 822 | private __gshared T* aggregate; 823 | template hasReflectSerde(string member) 824 | { 825 | static if (is(typeof(__traits(getMember, *aggregate, member)))) 826 | enum hasReflectSerde = hasUDA!(T, member, reflectSerde); 827 | else 828 | enum hasReflectSerde = false; 829 | } 830 | alias isMember = templateAnd!(templateOr!(hasField, isProperty, hasReflectSerde), isOriginalMember); 831 | static if (!is(immutable T == Tuple!Types, Types...) && __traits(getAliasThis, T).length) 832 | { 833 | alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T))); 834 | static if (isAggregateType!T) 835 | alias baseMembers = SerdeFieldsAndPropertiesImpl!A; 836 | else 837 | alias baseMembers = AliasSeq!(); 838 | alias members = Erase!(__traits(getAliasThis, T)[0], __traits(allMembers, T)); 839 | alias SerdeFieldsAndPropertiesImpl = AliasSeq!(baseMembers, Filter!(isMember, members)); 840 | } 841 | else 842 | { 843 | import mir.algebraic; 844 | static if (isVariant!T) 845 | alias members = staticMap!(allMembers, T.AllowedTypes); 846 | else 847 | alias members = allMembers!T; 848 | alias SerdeFieldsAndPropertiesImpl = AliasSeq!(Filter!(isMember, members)); 849 | } 850 | } 851 | 852 | // check if the member is readable 853 | private template isReadable(T, string member) 854 | { 855 | private __gshared T* aggregate; 856 | enum bool isReadable = __traits(compiles, { static fun(T)(auto ref T t) {} fun(__traits(getMember, *aggregate, member)); }); 857 | } 858 | 859 | // check if the member is readable/writeble? 860 | private template isReadableAndWritable(T, string member) 861 | { 862 | private __gshared T* aggregate; 863 | enum bool isReadableAndWritable = __traits(compiles, __traits(getMember, *aggregate, member) = __traits(getMember, *aggregate, member)); 864 | } 865 | 866 | package template isPublic(T, string member) 867 | { 868 | private __gshared T* aggregate; 869 | static if (__traits(compiles, { auto _ = __traits(getProtection, __traits(getMember, *aggregate, member)); })) 870 | enum bool isPublic = !__traits(getProtection, __traits(getMember, *aggregate, member)).privateOrPackage; 871 | else 872 | enum bool isPublic = false; 873 | } 874 | 875 | version(unittest) 876 | { 877 | final class ZipArchive 878 | { 879 | public: 880 | static const ushort zip64ExtractVersion = 45; 881 | } 882 | } 883 | 884 | version (mir_core_test) @nogc nothrow pure @safe unittest 885 | { 886 | static assert(!isPublic!(ZipArchive, "zip64ExtractVersion")); 887 | } 888 | 889 | // check if the member is property 890 | private template isSetter(T, string member) 891 | { 892 | private __gshared T* aggregate; 893 | static if (__traits(compiles, isSomeFunction!(__traits(getMember, *aggregate, member)))) 894 | { 895 | static if (isSomeFunction!(__traits(getMember, *aggregate, member))) 896 | { 897 | enum bool isSetter = getSetters!(T, member).length > 0;; 898 | } 899 | else 900 | { 901 | enum bool isSetter = false; 902 | } 903 | } 904 | else 905 | enum bool isSetter = false; 906 | } 907 | 908 | private template isGetter(T, string member) 909 | { 910 | private __gshared T* aggregate; 911 | static if (__traits(compiles, isSomeFunction!(__traits(getMember, *aggregate, member)))) 912 | { 913 | static if (isSomeFunction!(__traits(getMember, *aggregate, member))) 914 | { 915 | enum bool isGetter = Filter!(hasZeroArguments, Filter!(isPropertyImpl, __traits(getOverloads, T, member, true))).length == 1; 916 | } 917 | else 918 | { 919 | enum bool isGetter = false; 920 | } 921 | } 922 | else 923 | enum bool isGetter = false; 924 | } 925 | 926 | private enum bool isPropertyImpl(alias member) = (functionAttributes!member & FunctionAttribute.property) != 0; 927 | 928 | private bool privateOrPackage()(string protection) 929 | { 930 | return protection == "private" || protection == "package"; 931 | } 932 | -------------------------------------------------------------------------------- /source/mir/string_table.d: -------------------------------------------------------------------------------- 1 | /++ 2 | Mir String Table designed for fast deserialization routines. 3 | 4 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 | Authors: Ilia Ki 6 | Macros: 7 | +/ 8 | module mir.string_table; 9 | 10 | /++ 11 | Fast string table used to get key's id. 12 | The keys should be first sorted by length and then lexicographically. 13 | Params: 14 | U = an unsigned type that can hold an index of sorted keys. `U.max` must be less then length of the table. 15 | C = character type 16 | +/ 17 | struct MirStringTable(U, C = char) 18 | if (__traits(isUnsigned, U) && (is(C == char) || is(C == wchar) || is(C == dchar))) 19 | { 20 | /++ 21 | Keys sorted by length and then lexicographically. 22 | +/ 23 | const(immutable(C)[])[] sortedKeys; 24 | 25 | private U[] table; 26 | 27 | /++ 28 | The keys should be first sorted by length and then lexicographically. 29 | 30 | The constructor uses GC. 31 | It can be used in `@nogc` code when if constructed in compile time. 32 | +/ 33 | this()(const(immutable(C)[])[] sortedKeys) 34 | @trusted pure nothrow 35 | { 36 | pragma(inline, false); 37 | this.sortedKeys = sortedKeys; 38 | assert(sortedKeys.length <= U.max); 39 | const largest = sortedKeys.length ? sortedKeys[$ - 1].length : 0; 40 | table = new U[largest + 2]; 41 | size_t ski; 42 | foreach (length; 0 .. largest + 1) 43 | { 44 | while(ski < sortedKeys.length && sortedKeys[ski].length == length) 45 | ski++; 46 | table[length + 1] = cast(U)ski; 47 | } 48 | } 49 | 50 | /++ 51 | Params: 52 | key = string to find index for 53 | index = (ref) index to fill with key's position. 54 | Returns: 55 | true if keys index has been found 56 | +/ 57 | bool get()(scope const C[] key, ref uint index) 58 | const @trusted pure nothrow @nogc 59 | { 60 | import mir.utility: _expect; 61 | if (_expect(key.length + 1 < table.length, true)) 62 | { 63 | 64 | // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 65 | // 0 1 2 3 4 5 6 8 9 10 12 16 66 | 67 | auto low = table[key.length] + 0u; 68 | auto high = table[key.length + 1] + 0u; 69 | auto items = sortedKeys.ptr; 70 | if (low < high) 71 | { 72 | version (none) 73 | { 74 | if (key.length == 0) 75 | { 76 | index = 0; 77 | return true; 78 | } 79 | } 80 | L: do { 81 | auto mid = (low + high) / 2; 82 | 83 | version (all) 84 | { 85 | import core.stdc.string: memcmp; 86 | int r = void; 87 | 88 | if (__ctfe) 89 | r = __cmp(key, items[mid]); 90 | else 91 | version (BigEndian) 92 | r = memcmp(key.ptr, items[mid].ptr, key.length); 93 | else 94 | static if (C.sizeof == 1) 95 | r = memcmp(key.ptr, items[mid].ptr, key.length); 96 | else 97 | r = __cmp(key, items[mid]); 98 | 99 | if (r == 0) 100 | { 101 | index = mid; 102 | return true; 103 | } 104 | if (r > 0) 105 | low = mid + 1; 106 | else 107 | high = mid; 108 | } 109 | else 110 | { 111 | size_t i; 112 | auto value = items[mid]; 113 | do { 114 | if (key[i] < value[i]) 115 | { 116 | high = mid; 117 | continue L; 118 | } 119 | else 120 | if (key[i] > value[i]) 121 | { 122 | low = mid + 1; 123 | continue L; 124 | } 125 | } while (++i < key.length); 126 | index = mid; 127 | return true; 128 | } 129 | } 130 | while(low < high); 131 | } 132 | } 133 | return false; 134 | } 135 | 136 | /// 137 | uint opIndex()(scope const C[] key) 138 | const @trusted pure nothrow @nogc 139 | { 140 | import mir.utility: _expect; 141 | uint ret = 0; 142 | if (get(key, ret)._expect(true)) 143 | return ret; 144 | assert(0); 145 | } 146 | } 147 | 148 | /// ditto 149 | struct MirStringTable(size_t length, size_t maxKeyLength, bool caseInsensetive = false, C = char) 150 | if (is(C == char) || is(C == wchar) || is(C == dchar)) 151 | { 152 | /// 153 | const(immutable(C)[])[length] sortedKeys; 154 | 155 | private alias U = minimalIndexType!length; 156 | private U[maxKeyLength + 2] table; 157 | 158 | /++ 159 | The keys should be first sorted by length and then lexicographically. 160 | 161 | The constructor uses GC. 162 | It can be used in `@nogc` code when if constructed in compile time. 163 | +/ 164 | this(immutable(C)[][length] sortedKeys) 165 | @trusted pure nothrow 166 | { 167 | pragma(inline, false); 168 | this.sortedKeys = sortedKeys; 169 | size_t ski; 170 | foreach (length; 0 .. maxKeyLength + 1) 171 | { 172 | while(ski < sortedKeys.length && sortedKeys[ski].length == length) 173 | ski++; 174 | table[length + 1] = cast(U)ski; 175 | } 176 | } 177 | 178 | /++ 179 | Params: 180 | key = string to find index for 181 | index = (ref) index to fill with key's position. 182 | Returns: 183 | true if keys index has been found 184 | +/ 185 | bool get()(scope const(C)[] key, ref uint index) 186 | const @trusted pure nothrow @nogc 187 | { 188 | import mir.utility: _expect; 189 | if (_expect(key.length <= maxKeyLength, true)) 190 | { 191 | static if (caseInsensetive) 192 | { 193 | C[maxKeyLength] buffer = void; 194 | foreach(i, C c; key) 195 | buffer[i] = c.fastToUpper; 196 | key = buffer[0 .. key.length]; 197 | } 198 | auto low = table[key.length] + 0u; 199 | auto high = table[key.length + 1] + 0u; 200 | auto items = sortedKeys.ptr; 201 | if (low < high) 202 | { 203 | static if (!(maxKeyLength >= 16)) 204 | { 205 | if (key.length == 0) 206 | { 207 | index = 0; 208 | return true; 209 | } 210 | } 211 | L: do { 212 | auto mid = (low + high) / 2; 213 | 214 | static if (maxKeyLength >= 16) 215 | { 216 | import core.stdc.string: memcmp; 217 | int r = void; 218 | 219 | if (__ctfe) 220 | r = __cmp(key, items[mid]); 221 | else 222 | version (BigEndian) 223 | r = memcmp(key.ptr, items[mid].ptr, key.length); 224 | else 225 | static if (C.sizeof == 1) 226 | r = memcmp(key.ptr, items[mid].ptr, key.length); 227 | else 228 | r = __cmp(key, items[mid]); 229 | 230 | if (r == 0) 231 | { 232 | index = mid; 233 | return true; 234 | } 235 | if (r > 0) 236 | low = mid + 1; 237 | else 238 | high = mid; 239 | } 240 | else 241 | { 242 | size_t i; 243 | auto value = items[mid]; 244 | do { 245 | if (key[i] < value[i]) 246 | { 247 | high = mid; 248 | continue L; 249 | } 250 | else 251 | if (key[i] > value[i]) 252 | { 253 | low = mid + 1; 254 | continue L; 255 | } 256 | } while (++i < key.length); 257 | index = mid; 258 | return true; 259 | } 260 | } 261 | while(low < high); 262 | } 263 | } 264 | return false; 265 | } 266 | 267 | /// 268 | uint opIndex()(scope const C[] key) 269 | const @trusted pure nothrow @nogc 270 | { 271 | import mir.utility: _expect; 272 | uint ret = 0; 273 | if (get(key, ret)._expect(true)) 274 | return ret; 275 | assert(0); 276 | } 277 | } 278 | 279 | /// 280 | @safe pure nothrow @nogc 281 | version(mir_core_test) unittest 282 | { 283 | static immutable sortedKeys = ["", "a", "b", "aab", "abb", "aaaaa"]; 284 | static immutable table = MirStringTable!ubyte(sortedKeys); // CTFE 285 | static assert (table[""] == 0); 286 | static assert (table["a"] == 1); 287 | static assert (table["b"] == 2); 288 | static assert (table["abb"] == 4); 289 | assert (table["aaaaa"] == 5); 290 | } 291 | 292 | 293 | /// 294 | @safe pure nothrow 295 | version(mir_core_test) unittest 296 | { 297 | import mir.utility: simpleSort; 298 | auto keys = ["aaaaa", "abb", "", "b", "a", "aab"]; 299 | // sorts keys by length and then lexicographically. 300 | keys.simpleSort!smallerStringFirst; 301 | assert(keys == ["", "a", "b", "aab", "abb", "aaaaa"]); 302 | } 303 | 304 | @safe pure nothrow 305 | version(mir_core_test) unittest 306 | { 307 | import mir.utility: simpleSort; 308 | auto keys = ["aaaaa"w, "abb"w, ""w, "b"w, "a"w, "aab"w]; 309 | // sorts keys by length and then lexicographically. 310 | keys.simpleSort!smallerStringFirst; 311 | assert(keys == [""w, "a"w, "b"w, "aab"w, "abb"w, "aaaaa"w]); 312 | } 313 | 314 | package template minimalIndexType(size_t length) 315 | { 316 | static if (length <= ubyte.max) 317 | alias minimalIndexType = ubyte; 318 | else 319 | static if (length <= ushort.max) 320 | alias minimalIndexType = ushort; 321 | else 322 | static if (length <= uint.max) 323 | alias minimalIndexType = uint; 324 | else 325 | alias minimalIndexType = ulong; 326 | } 327 | 328 | package template minimalSignedIndexType(size_t length) 329 | { 330 | static if (length <= byte.max) 331 | alias minimalSignedIndexType = byte; 332 | else 333 | static if (length <= short.max) 334 | alias minimalSignedIndexType = short; 335 | else 336 | static if (length <= int.max) 337 | alias minimalSignedIndexType = int; 338 | else 339 | alias minimalSignedIndexType = long; 340 | } 341 | 342 | /++ 343 | Compares strings by length and then lexicographically. 344 | +/ 345 | sizediff_t smallerStringFirstCmp(T)(T[] a, T[] b) 346 | { 347 | if (sizediff_t d = a.length - b.length) 348 | { 349 | return d; 350 | } 351 | 352 | import std.traits: Unqual; 353 | static if (is(Unqual!T == ubyte) || is(Unqual!T == char)) 354 | { 355 | import core.stdc.string: memcmp; 356 | if (__ctfe) 357 | return __cmp(a, b); 358 | else 359 | return (() @trusted => memcmp(a.ptr, b.ptr, a.length))(); 360 | } 361 | else 362 | { 363 | return __cmp(a, b); 364 | } 365 | } 366 | 367 | /// 368 | @safe pure nothrow @nogc 369 | version(mir_core_test) unittest 370 | { 371 | assert(smallerStringFirstCmp("aa", "bb") < 0); 372 | assert(smallerStringFirstCmp("aa", "aa") == 0); 373 | assert(smallerStringFirstCmp("aaa", "aa") > 0); 374 | 375 | static assert(smallerStringFirstCmp("aa", "bb") < 0); 376 | static assert(smallerStringFirstCmp("aa", "aa") == 0); 377 | static assert(smallerStringFirstCmp("aaa", "aa") > 0); 378 | } 379 | 380 | /++ 381 | Compares strings by length and then lexicographically. 382 | +/ 383 | template smallerStringFirst(alias direction = "<") 384 | if (direction == "<" || direction == ">") 385 | { 386 | /// 387 | bool smallerStringFirst(T)(T[] a, T[] b) 388 | { 389 | auto r = smallerStringFirstCmp(a, b); 390 | static if (direction == "<") 391 | return r < 0; 392 | else 393 | return r > 0; 394 | } 395 | } 396 | 397 | /// 398 | @safe pure nothrow @nogc 399 | version(mir_core_test) unittest 400 | { 401 | assert(smallerStringFirst("aa", "bb") == true); 402 | assert(smallerStringFirst("aa", "aa") == false); 403 | assert(smallerStringFirst("aaa", "aa") == false); 404 | } 405 | 406 | package auto fastToUpper(C)(const C a) 407 | { // std.ascii may not be inlined 408 | return 'a' <= a && a <= 'z' ? cast(C)(a ^ 0x20) : a; 409 | } 410 | 411 | package @safe pure nothrow @nogc 412 | C[] fastToUpperInPlace(C)(scope return C[] a) 413 | { 414 | foreach(ref C e; a) 415 | e = e.fastToUpper; 416 | return a; 417 | } 418 | 419 | package immutable(C)[][] prepareStringTableKeys(bool caseInsensetive = false, C)(immutable(C)[][] keys) 420 | { 421 | static if (caseInsensetive) 422 | { 423 | foreach (ref key; keys) 424 | { 425 | auto upper = cast(immutable) key.dup.fastToUpperInPlace; 426 | if (upper != key) 427 | key = upper; 428 | } 429 | } 430 | import mir.utility: simpleSort; 431 | return keys.simpleSort!smallerStringFirst; 432 | } 433 | 434 | package template createTable(C) 435 | if (is(C == char) || is(C == wchar) || is(C == dchar)) 436 | { 437 | auto createTable(immutable(C)[][] keys, bool caseInsensetive = false)() 438 | { 439 | static immutable C[][] sortedKeys = prepareStringTableKeys!caseInsensetive(keys); 440 | alias Table = MirStringTable!(sortedKeys.length, sortedKeys.length ? sortedKeys[$ - 1].length : 0, caseInsensetive, C); 441 | static if (sortedKeys.length) 442 | return Table(sortedKeys[0 .. sortedKeys.length]); 443 | else 444 | return Table.init; 445 | } 446 | } 447 | -------------------------------------------------------------------------------- /source/mir/utility.d: -------------------------------------------------------------------------------- 1 | /++ 2 | Generic utilities. 3 | 4 | $(BOOKTABLE Cheat Sheet, 5 | $(TR $(TH Function Name) $(TH Description)) 6 | $(T2 swap, Swaps two values.) 7 | $(T2 extMul, Extended unsigned multiplications.) 8 | $(T2 min, Minimum value.) 9 | $(T2 max, Maximum value.) 10 | ) 11 | 12 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 13 | Authors: Ilia Ki, $(HTTP erdani.com, Andrei Alexandrescu) (original std.* modules), 14 | Macros: 15 | T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) 16 | +/ 17 | module mir.utility; 18 | 19 | import std.traits; 20 | 21 | import mir.math.common: optmath; 22 | 23 | version(LDC) 24 | pragma(LDC_inline_ir) R inlineIR(string s, R, P...)(P) @safe pure nothrow @nogc; 25 | 26 | @optmath: 27 | 28 | version(LDC) 29 | { 30 | /// 31 | public import ldc.intrinsics: _expect = llvm_expect; 32 | } 33 | else version(GNU) 34 | { 35 | import gcc.builtins: __builtin_expect, __builtin_clong; 36 | 37 | /// 38 | T _expect(T)(in T val, in T expected_val) if (__traits(isIntegral, T)) 39 | { 40 | static if (T.sizeof <= __builtin_clong.sizeof) 41 | return cast(T) __builtin_expect(val, expected_val); 42 | else 43 | return val; 44 | } 45 | } 46 | else 47 | { 48 | /// 49 | T _expect(T)(in T val, in T expected_val) if (__traits(isIntegral, T)) 50 | { 51 | return val; 52 | } 53 | } 54 | 55 | public import std.algorithm.mutation: swap; 56 | 57 | void swapStars(I1, I2)(auto ref I1 i1, auto ref I2 i2) 58 | { 59 | static if (__traits(compiles, swap(*i1, *i2))) 60 | { 61 | swap(*i1, *i2); 62 | } 63 | else 64 | { 65 | import mir.functional: unref; 66 | auto e = unref(*i1); 67 | i1[0] = *i2; 68 | i2[0] = e; 69 | } 70 | } 71 | 72 | /++ 73 | Iterates the passed arguments and returns the minimum value. 74 | Params: args = The values to select the minimum from. At least two arguments 75 | must be passed, and they must be comparable with `<`. 76 | Returns: The minimum of the passed-in values. 77 | +/ 78 | auto min(T...)(T args) 79 | if (T.length >= 2) 80 | { 81 | //Get "a" 82 | static if (T.length <= 2) 83 | alias a = args[0]; 84 | else 85 | auto a = min(args[0 .. ($+1)/2]); 86 | alias T0 = typeof(a); 87 | 88 | //Get "b" 89 | static if (T.length <= 3) 90 | alias b = args[$-1]; 91 | else 92 | auto b = min(args[($+1)/2 .. $]); 93 | alias T1 = typeof(b); 94 | 95 | static assert (is(typeof(a < b)), "Invalid arguments: Cannot compare types " ~ T0.stringof ~ " and " ~ T1.stringof ~ "."); 96 | 97 | static if ((isFloatingPoint!T0 && isNumeric!T1) || (isFloatingPoint!T1 && isNumeric!T0)) 98 | { 99 | import mir.math.common: fmin; 100 | return fmin(a, b); 101 | } 102 | else 103 | { 104 | static if (isIntegral!T0 && isIntegral!T1) 105 | static assert(isSigned!T0 == isSigned!T1, 106 | "mir.utility.min is not defined for signed + unsigned pairs because of security reasons." 107 | ~ "Please unify type or use a Phobos analog."); 108 | //Do the "min" proper with a and b 109 | return a < b ? a : b; 110 | } 111 | } 112 | 113 | @safe version(mir_core_test) unittest 114 | { 115 | int a = 5; 116 | short b = 6; 117 | double c = 2; 118 | auto d = min(a, b); 119 | static assert(is(typeof(d) == int)); 120 | assert(d == 5); 121 | auto e = min(a, b, c); 122 | static assert(is(typeof(e) == double)); 123 | assert(e == 2); 124 | } 125 | 126 | /++ 127 | `min` is not defined for arguments of mixed signedness because of security reasons. 128 | Please unify type or use a Phobos analog. 129 | +/ 130 | version(mir_core_test) unittest 131 | { 132 | int a = -10; 133 | uint b = 10; 134 | static assert(!is(typeof(min(a, b)))); 135 | } 136 | 137 | 138 | /++ 139 | Iterates the passed arguments and returns the minimum value. 140 | Params: args = The values to select the minimum from. At least two arguments 141 | must be passed, and they must be comparable with `<`. 142 | Returns: The minimum of the passed-in values. 143 | +/ 144 | auto max(T...)(T args) 145 | if (T.length >= 2) 146 | { 147 | //Get "a" 148 | static if (T.length <= 2) 149 | alias a = args[0]; 150 | else 151 | auto a = max(args[0 .. ($+1)/2]); 152 | alias T0 = typeof(a); 153 | 154 | //Get "b" 155 | static if (T.length <= 3) 156 | alias b = args[$-1]; 157 | else 158 | auto b = max(args[($+1)/2 .. $]); 159 | alias T1 = typeof(b); 160 | 161 | static assert (is(typeof(a < b)), "Invalid arguments: Cannot compare types " ~ T0.stringof ~ " and " ~ T1.stringof ~ "."); 162 | 163 | static if ((isFloatingPoint!T0 && isNumeric!T1) || (isFloatingPoint!T1 && isNumeric!T0)) 164 | { 165 | import mir.math.common: fmax; 166 | return fmax(a, b); 167 | } 168 | else 169 | { 170 | static if (isIntegral!T0 && isIntegral!T1) 171 | static assert(isSigned!T0 == isSigned!T1, 172 | "mir.utility.max is not defined for signed + unsigned pairs because of security reasons." 173 | ~ "Please unify type or use a Phobos analog."); 174 | //Do the "max" proper with a and b 175 | return a > b ? a : b; 176 | } 177 | } 178 | 179 | /// 180 | @safe version(mir_core_test) unittest 181 | { 182 | int a = 5; 183 | short b = 6; 184 | double c = 2; 185 | auto d = max(a, b); 186 | static assert(is(typeof(d) == int)); 187 | assert(d == 6); 188 | auto e = min(a, b, c); 189 | static assert(is(typeof(e) == double)); 190 | assert(e == 2); 191 | } 192 | 193 | /++ 194 | `max` is not defined for arguments of mixed signedness because of security reasons. 195 | Please unify type or use a Phobos analog. 196 | +/ 197 | version(mir_core_test) unittest 198 | { 199 | int a = -10; 200 | uint b = 10; 201 | static assert(!is(typeof(max(a, b)))); 202 | } 203 | 204 | /++ 205 | Return type for $(LREF extMul); 206 | 207 | The payload order of `low` and `high` parts depends on the endianness. 208 | +/ 209 | struct ExtMulResult(I) 210 | if (isUnsigned!I) 211 | { 212 | version (LittleEndian) 213 | { 214 | /// Lower I.sizeof * 8 bits 215 | I low; 216 | /// Higher I.sizeof * 8 bits 217 | I high; 218 | } 219 | else 220 | { 221 | /// Higher I.sizeof * 8 bits 222 | I high; 223 | /// Lower I.sizeof * 8 bits 224 | I low; 225 | } 226 | 227 | T opCast(T : ulong)() 228 | { 229 | static if (is(I == ulong)) 230 | { 231 | return cast(T)low; 232 | } 233 | else 234 | { 235 | return cast(T)(low | (ulong(high) << (I.sizeof * 8))); 236 | } 237 | } 238 | } 239 | 240 | private struct ExtDivResult(I) 241 | if (isUnsigned!I) 242 | { 243 | version (LittleEndian) 244 | { 245 | /// Quotient 246 | I quotient; 247 | /// Remainder 248 | I remainder; 249 | } 250 | else 251 | { 252 | /// Remainder 253 | I remainder; 254 | /// Quotient 255 | I quotient; 256 | } 257 | } 258 | 259 | /++ 260 | Extended unsigned multiplications. 261 | Performs U x U multiplication and returns $(LREF ExtMulResult)!U that contains extended result. 262 | Params: 263 | a = unsigned integer 264 | b = unsigned integer 265 | Returns: 266 | 128bit result if U is ulong or 256bit result if U is ucent. 267 | Optimization: 268 | Algorithm is optimized for LDC (LLVM IR, any target) and for DMD (X86_64). 269 | +/ 270 | ExtMulResult!U extMul(U)(in U a, in U b) @nogc nothrow pure @trusted 271 | if(isUnsigned!U) 272 | { 273 | static if (is(U == ulong)) 274 | alias H = uint; 275 | else // ucent 276 | alias H = ulong; 277 | 278 | enum hbc = H.sizeof * 8; 279 | 280 | static if (U.sizeof < 4) 281 | { 282 | auto ret = uint(a) * b; 283 | version (LittleEndian) 284 | return typeof(return)(cast(U) ret, cast(U)(ret >>> (U.sizeof * 8))); 285 | else 286 | return typeof(return)(cast(U)(ret >>> (U.sizeof * 8)), cast(U) ret); 287 | } 288 | else 289 | static if (is(U == uint)) 290 | { 291 | auto ret = ulong(a) * b; 292 | version (LittleEndian) 293 | return typeof(return)(cast(uint) ret, cast(uint)(ret >>> 32)); 294 | else 295 | return typeof(return)(cast(uint)(ret >>> 32), cast(uint) ret); 296 | } 297 | else 298 | static if (is(U == ulong) && __traits(compiles, ucent.init)) 299 | { 300 | auto ret = ucent(a) * b; 301 | version (LittleEndian) 302 | return typeof(return)(cast(ulong) ret, cast(ulong)(ret >>> 64)); 303 | else 304 | return typeof(return)(cast(ulong)(ret >>> 64), cast(ulong) ret); 305 | } 306 | else 307 | { 308 | if (!__ctfe) 309 | { 310 | static if (size_t.sizeof == 4) 311 | { 312 | // https://github.com/ldc-developers/ldc/issues/2391 313 | } 314 | else 315 | version(LDC) 316 | { 317 | // LLVM IR by n8sh 318 | pragma(inline, true); 319 | static if (is(U == ulong)) 320 | { 321 | auto r = inlineIR!(` 322 | %a = zext i64 %0 to i128 323 | %b = zext i64 %1 to i128 324 | %m = mul i128 %a, %b 325 | %n = lshr i128 %m, 64 326 | %h = trunc i128 %n to i64 327 | %l = trunc i128 %m to i64 328 | %agg1 = insertvalue [2 x i64] undef, i64 %l, 0 329 | %agg2 = insertvalue [2 x i64] %agg1, i64 %h, 1 330 | ret [2 x i64] %agg2`, ulong[2])(a, b); 331 | version (LittleEndian) 332 | return ExtMulResult!U(r[0], r[1]); 333 | else 334 | return ExtMulResult!U(r[1], r[0]); 335 | } 336 | else 337 | static if (false) 338 | { 339 | auto r = inlineIR!(` 340 | %a = zext i128 %0 to i256 341 | %b = zext i128 %1 to i256 342 | %m = mul i256 %a, %b 343 | %n = lshr i256 %m, 128 344 | %h = trunc i256 %n to i128 345 | %l = trunc i256 %m to i128 346 | %agg1 = insertvalue [2 x i128] undef, i128 %l, 0 347 | %agg2 = insertvalue [2 x i128] %agg1, i128 %h, 1 348 | ret [2 x i128] %agg2`, ucent[2])(a, b); 349 | version (LittleEndian) 350 | return ExtMulResult!U(r[0], r[1]); 351 | else 352 | return ExtMulResult!U(r[1], r[0]); 353 | } 354 | } 355 | else 356 | version(D_InlineAsm_X86_64) 357 | { 358 | static if (is(U == ulong)) 359 | { 360 | return extMul_X86_64(a, b); 361 | } 362 | } 363 | } 364 | 365 | U al = cast(H)a; 366 | U ah = a >>> hbc; 367 | U bl = cast(H)b; 368 | U bh = b >>> hbc; 369 | 370 | U p0 = al * bl; 371 | U p1 = al * bh; 372 | U p2 = ah * bl; 373 | U p3 = ah * bh; 374 | 375 | H cy = cast(H)(((p0 >>> hbc) + cast(H)p1 + cast(H)p2) >>> hbc); 376 | U lo = p0 + (p1 << hbc) + (p2 << hbc); 377 | U hi = p3 + (p1 >>> hbc) + (p2 >>> hbc) + cy; 378 | 379 | version(LittleEndian) 380 | return typeof(return)(lo, hi); 381 | else 382 | return typeof(return)(hi, lo); 383 | } 384 | } 385 | 386 | /// 64bit x 64bit -> 128bit 387 | version(mir_core_test) unittest 388 | { 389 | immutable a = 0x93_8d_28_00_0f_50_a5_56; 390 | immutable b = 0x54_c3_2f_e8_cc_a5_97_10; 391 | enum c = extMul(a, b); // Compile time algorithm 392 | assert(extMul(a, b) == c); // Fast runtime algorithm 393 | static assert(c.high == 0x30_da_d1_42_95_4a_50_78); 394 | static assert(c.low == 0x27_9b_4b_b4_9e_fe_0f_60); 395 | } 396 | 397 | /// 32bit x 32bit -> 64bit 398 | version(mir_core_test) unittest 399 | { 400 | immutable a = 0x0f_50_a5_56; 401 | immutable b = 0xcc_a5_97_10; 402 | static assert(cast(ulong)extMul(a, b) == ulong(a) * b); 403 | } 404 | 405 | /// 406 | version(mir_core_test) unittest 407 | { 408 | immutable ushort a = 0xa5_56; 409 | immutable ushort b = 0x97_10; 410 | static assert(cast(uint)extMul(a, b) == a * b); 411 | } 412 | 413 | /// 414 | version(mir_core_test) unittest 415 | { 416 | immutable ubyte a = 0x56; 417 | immutable ubyte b = 0x10; 418 | static assert(cast(ushort)extMul(a, b) == a * b); 419 | } 420 | 421 | version(D_InlineAsm_X86_64) 422 | { 423 | version(Windows) 424 | { 425 | private ulong[2] extMul_X86_64_impl()(ulong a, ulong b) 426 | { 427 | asm @trusted pure nothrow @nogc 428 | { 429 | naked; 430 | mov RAX, RCX; 431 | mul RDX; 432 | ret; 433 | } 434 | } 435 | 436 | private ExtMulResult!ulong extMul_X86_64()(ulong a, ulong b) 437 | { 438 | auto res = extMul_X86_64_impl(a, b); 439 | return ExtMulResult!ulong(res[0], res[1]); 440 | } 441 | } 442 | else 443 | private ExtMulResult!ulong extMul_X86_64()(ulong a, ulong b) 444 | { 445 | asm @trusted pure nothrow @nogc 446 | { 447 | naked; 448 | mov RAX, RDI; 449 | mul RSI; 450 | ret; 451 | } 452 | } 453 | 454 | version(Windows) 455 | { 456 | private ulong[2] extDiv_X86_64_impl()(ulong high, ulong low, ulong d) 457 | { 458 | asm @trusted pure nothrow @nogc 459 | { 460 | naked; 461 | mov RAX, RCX; 462 | div RDX; 463 | ret; 464 | } 465 | } 466 | 467 | private ExtDivResult!ulong extDiv_X86_64()(ExtMulResult!ulong pair, ulong d) 468 | { 469 | auto res = extDiv_X86_64_impl(pair.high, pair.low); 470 | return ExtDivResult!ulong(res[0], res[1]); 471 | } 472 | } 473 | else 474 | private ExtDivResult!ulong extDiv_X86_64()(ExtMulResult!ulong pair, ulong d) 475 | { 476 | asm @trusted pure nothrow @nogc 477 | { 478 | naked; 479 | mov RAX, RDI; 480 | div RSI; 481 | ret; 482 | } 483 | } 484 | } 485 | 486 | version(LDC) {} else version(D_InlineAsm_X86_64) 487 | @nogc nothrow pure @safe version(mir_core_test) unittest 488 | { 489 | immutable a = 0x93_8d_28_00_0f_50_a5_56; 490 | immutable b = 0x54_c3_2f_e8_cc_a5_97_10; 491 | 492 | immutable ExtMulResult!ulong c = extMul_X86_64(a, b); 493 | 494 | assert(c.high == 0x30_da_d1_42_95_4a_50_78); 495 | assert(c.low == 0x27_9b_4b_b4_9e_fe_0f_60); 496 | } 497 | 498 | // draft 499 | // https://www.codeproject.com/Tips/785014/UInt-Division-Modulus 500 | private ulong divmod128by64(const ulong u1, const ulong u0, ulong v, out ulong r) 501 | { 502 | const ulong b = 1L << 32; 503 | ulong un1, un0, vn1, vn0, q1, q0, un32, un21, un10, rhat, left, right; 504 | 505 | import mir.bitop; 506 | 507 | auto s = ctlz(v); 508 | v <<= s; 509 | vn1 = v >> 32; 510 | vn0 = v & 0xffffffff; 511 | 512 | un32 = (u1 << s) | (u0 >> (64 - s)); 513 | un10 = u0 << s; 514 | 515 | un1 = un10 >> 32; 516 | un0 = un10 & 0xffffffff; 517 | 518 | q1 = un32 / vn1; 519 | rhat = un32 % vn1; 520 | 521 | left = q1 * vn0; 522 | right = (rhat << 32) + un1; 523 | 524 | while ((q1 >= b) || (left > right)) 525 | { 526 | --q1; 527 | rhat += vn1; 528 | if (rhat >= b) 529 | break; 530 | left -= vn0; 531 | right = (rhat << 32) | un1; 532 | } 533 | 534 | un21 = (un32 << 32) + (un1 - (q1 * v)); 535 | 536 | q0 = un21 / vn1; 537 | rhat = un21 % vn1; 538 | 539 | left = q0 * vn0; 540 | right = (rhat << 32) | un0; 541 | 542 | while ((q0 >= b) || (left > right)) 543 | { 544 | --q0; 545 | rhat += vn1; 546 | if (rhat >= b) 547 | break; 548 | left -= vn0; 549 | right = (rhat << 32) | un0; 550 | } 551 | 552 | r = ((un21 << 32) + (un0 - (q0 * v))) >> s; 553 | return (q1 << 32) | q0; 554 | } 555 | 556 | /++ 557 | Simple sort algorithm usefull for CTFE code. 558 | +/ 559 | template simpleSort(alias cmp = "a < b") 560 | { 561 | /// 562 | T[] simpleSort(T)(return T[] array) 563 | { 564 | size_t i = 1; 565 | while (i < array.length) 566 | { 567 | size_t j = i; 568 | import mir.functional: naryFun; 569 | while (j > 0 && !naryFun!cmp(array[j - 1], array[j])) 570 | { 571 | swap(array[j - 1], array[j]); 572 | j--; 573 | } 574 | i++; 575 | } 576 | return array; 577 | } 578 | } 579 | -------------------------------------------------------------------------------- /test_examples.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | dub fetch dtools 6 | 7 | # extract examples 8 | dub run dtools:tests_extractor -- -i source -o out 9 | 10 | # compile the examples 11 | for file in $(find out -name "*.d") ; do 12 | echo "Testing: $file" 13 | $DMD -de -unittest -Isource -c -o- "$file" 14 | done 15 | --------------------------------------------------------------------------------