├── .editorconfig ├── .gitignore ├── LICENSE ├── README.md ├── dscanner.ini ├── dub.json ├── meta ├── dub.json └── tanya │ └── meta │ ├── metafunction.d │ ├── package.d │ ├── trait.d │ └── transform.d ├── middle ├── dub.json ├── middle-test-library └── tanya │ └── memory │ ├── allocator.d │ ├── lifetime.d │ ├── mallocator.d │ ├── mmappool.d │ ├── op.d │ ├── package.d │ └── smartref.d ├── os ├── dub.json └── tanya │ └── os │ ├── error.d │ └── package.d ├── source └── tanya │ ├── algorithm │ ├── iteration.d │ ├── mutation.d │ └── package.d │ ├── container │ ├── array.d │ ├── buffer.d │ ├── entry.d │ ├── hashtable.d │ ├── list.d │ ├── package.d │ ├── set.d │ └── string.d │ ├── conv.d │ ├── format.d │ ├── hash │ ├── lookup.d │ └── package.d │ ├── math │ ├── package.d │ └── random.d │ ├── net │ ├── iface.d │ ├── inet.d │ ├── ip.d │ ├── package.d │ └── uri.d │ └── range │ ├── adapter.d │ ├── array.d │ ├── package.d │ └── primitive.d ├── test ├── dub.json └── tanya │ └── test │ ├── assertion.d │ ├── package.d │ └── stub.d └── tests └── tanya ├── algorithm └── tests │ ├── iteration.d │ └── mutation.d ├── container └── tests │ ├── array.d │ ├── buffer.d │ ├── entry.d │ ├── hashtable.d │ ├── list.d │ ├── set.d │ └── string.d ├── hash └── tests │ └── lookup.d ├── math └── tests │ ├── package.d │ └── random.d ├── memory └── tests │ ├── lifetime.d │ ├── mallocator.d │ ├── mmappool.d │ ├── op.d │ └── smartref.d ├── meta └── tests │ ├── metafunction.d │ └── trait.d ├── net └── tests │ ├── iface.d │ ├── inet.d │ ├── ip.d │ └── uri.d ├── os └── tests │ └── error.d ├── range └── tests │ ├── adapter.d │ └── primitive.d └── tests ├── conv.d └── format.d /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 4 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binary 2 | *.[oa] 3 | *.exe 4 | *.lib 5 | 6 | # D 7 | .dub 8 | dub.selections.json 9 | 10 | __test__*__ 11 | __test__*__.core 12 | tanya-*test-* 13 | /dub_platform_probe[_-]* 14 | 15 | /docs/ 16 | /docs.json 17 | 18 | /*.lst 19 | 20 | # Ninja build 21 | .ninja_* 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tanya 2 | 3 | [![Dub version](https://img.shields.io/dub/v/tanya.svg)](https://code.dlang.org/packages/tanya) 4 | [![Dub downloads](https://img.shields.io/dub/dt/tanya.svg)](https://code.dlang.org/packages/tanya) 5 | [![License: MPL 2.0](https://img.shields.io/badge/license-MPL_2.0-blue.svg)](https://opensource.org/licenses/MPL-2.0) 6 | 7 | Tanya is a general purpose library for D programming language. 8 | 9 | Its aim is to simplify the manual memory management in D and to provide a 10 | guarantee with @nogc attribute that there are no hidden allocations on the 11 | Garbage Collector heap. Everything in the library is usable in @nogc code. 12 | Tanya provides data structures and utilities to facilitate painless systems 13 | programming in D. 14 | 15 | - [API Documentation](https://docs.caraus.tech/tanya) 16 | 17 | ## Overview 18 | 19 | Tanya consists of the following packages and (top-level) modules: 20 | 21 | * `algorithm`: Collection of generic algorithms. 22 | * `bitmanip`: Bit manipulation. 23 | * `container`: Queue, Array, Singly and doubly linked lists, Buffers, UTF-8 24 | string, Set, Hash table. 25 | * `conv`: This module provides functions for converting between different 26 | types. 27 | * `format`: Formatting and conversion functions. 28 | * `hash`: Hash algorithms. 29 | * `math`: Arbitrary precision integer and a set of functions. 30 | * `memory`: Tools for manual memory management (allocators, smart pointers). 31 | * `meta`: Template metaprogramming. This package contains utilities to acquire 32 | type information at compile-time, to transform from one type to another. It has 33 | also different algorithms for iterating, searching and modifying template 34 | arguments. 35 | * `net`: URL-Parsing, network programming. 36 | * `os`: Platform-independent interfaces to operating system functionality. 37 | * `range`: Generic functions and templates for D ranges. 38 | * `test`: Test suite for unittest-blocks. 39 | * `typecons`: Templates that allow to build new types based on the available 40 | ones. 41 | 42 | 43 | ## NogcD 44 | 45 | To achieve programming without the Garbage Collection tanya uses a subset of D: 46 | NogcD. 47 | 48 | ### Allocators 49 | 50 | Memory management is done with allocators. Instead of using `new` to create an 51 | instance of a class, an allocator is used: 52 | 53 | ```d 54 | import tanya.memory; 55 | 56 | class A 57 | { 58 | this(int arg) 59 | { 60 | } 61 | } 62 | 63 | A a = defaultAllocator.make!A(5); 64 | 65 | defaultAllocator.dispose(a); 66 | ``` 67 | 68 | As you can see, the user is responsible for deallocation, therefore `dispose` 69 | is called at the end. 70 | 71 | If you want to change the `defaultAllocator` to something different, you 72 | probably want to do it at the program's beginning. Or you can invoke another 73 | allocator directly. It is important to ensure that the object is destroyed 74 | using the same allocator that was used to allocate it. 75 | 76 | What if I get an allocated object from some function? The generic rule is: If 77 | you haven't requested the memory yourself (with `make`), you don't need to free 78 | it. 79 | 80 | `tanya.memory.smartref` contains smart pointers, helpers that can take care of 81 | a proper deallocation in some cases for you. 82 | 83 | ### Exceptions 84 | 85 | Since exceptions are normal classes in D, they are allocated and dellocated the 86 | same as described above, but: 87 | 88 | 1. The caller is **always** responsible for destroying a caught exception. 89 | 2. Exceptions are **always** allocated and should be always allocated with the 90 | `defaultAllocator`. 91 | 92 | ```d 93 | import tanya.memory; 94 | 95 | void functionThatThrows() 96 | { 97 | throw defaultAlocator.make!Exception("An error occurred"); 98 | } 99 | 100 | try 101 | { 102 | functionThatThrows() 103 | } 104 | catch (Exception e) 105 | { 106 | defaultAllocator.dispose(e); 107 | } 108 | ``` 109 | 110 | ### Built-in array operations and containers 111 | 112 | Arrays are commonly used in programming. D's built-in arrays often rely on the 113 | GC. It is inconvenient to change their size, reserve memory for future use and 114 | so on. Containers can help here. The following example demonstrates how 115 | `tanya.container.array.Array` can be used instead of `int[]`. 116 | 117 | ```d 118 | import tanya.container.array; 119 | 120 | Array!int arr; 121 | 122 | // Reserve memory if I know that my container will contain approximately 15 123 | // elements. 124 | arr.reserve(15); 125 | 126 | arr.insertBack(5); // Add one element. 127 | arr.length = 10; // New elements are initialized to 0. 128 | 129 | // Iterate over the first five elements. 130 | foreach (el; arr[0 .. 5]) 131 | { 132 | } 133 | 134 | int i = arr[7]; // Access 8th element. 135 | ``` 136 | 137 | There are more containers in the `tanya.container` package. 138 | 139 | 140 | ### Immutability 141 | 142 | Immutability doesn't play nice with manual memory management since the 143 | allocated storage should be initialized (mutated) and then released (mutated). 144 | `immutable` is used only for non-local immutable declarations (that are 145 | evaluated at compile time), static immutable data, strings (`immutable(char)[]`, 146 | `immutable(wchar)[]` and `immutable(dchar)[]`). 147 | 148 | 149 | ### Unsupported features 150 | 151 | The following features depend on GC and aren't supported: 152 | 153 | - `lazy` parameters (allocate a closure which is evaluated when then the 154 | parameter is used) 155 | 156 | - `synchronized` blocks 157 | 158 | 159 | ## Development 160 | 161 | ### Supported compilers 162 | 163 | | DMD | GCC | 164 | |:-------:|:---------:| 165 | | 2.100.0 | 12.1 | 166 | 167 | ## Further characteristics 168 | 169 | - Tanya is a native D library 170 | 171 | - Tanya is cross-platform. The development happens on a 64-bit Linux, but it 172 | is being tested on Windows and FreeBSD as well 173 | 174 | - Tanya favours generic algorithms therefore there is no auto-decoding. Char 175 | arrays are handled as any other array type 176 | 177 | - The library isn't thread-safe yet 178 | 179 | - Complex numbers (`cfloat`, `cdouble`, `creal`, `ifloat`, `idouble`, `ireal`) 180 | aren't supported 181 | 182 | 183 | ## Feedback 184 | 185 | Any feedback about your experience with tanya would be greatly appreciated. Feel free to 186 | [contact me](mailto:belka@caraus.de). 187 | -------------------------------------------------------------------------------- /dscanner.ini: -------------------------------------------------------------------------------- 1 | ; Configure which static analysis checks are skip-unittest 2 | [analysis.config.StaticAnalysisConfig] 3 | ; Check variable, class, struct, interface, union, and function names against t 4 | ; he Phobos style guide 5 | style_check="disabled" 6 | ; Check for array literals that cause unnecessary allocation 7 | enum_array_literal_check="skip-unittest" 8 | ; Check for poor exception handling practices 9 | exception_check="skip-unittest" 10 | ; Check for use of the deprecated 'delete' keyword 11 | delete_check="skip-unittest" 12 | ; Check for use of the deprecated floating point operators 13 | float_operator_check="skip-unittest" 14 | ; Check number literals for readability 15 | number_style_check="disabled" 16 | ; Checks that opEquals, opCmp, toHash, and toString are either const, immutable 17 | ; , or inout. 18 | object_const_check="disabled" 19 | ; Checks for .. expressions where the left side is larger than the right. 20 | backwards_range_check="skip-unittest" 21 | ; Checks for if statements whose 'then' block is the same as the 'else' block 22 | if_else_same_check="skip-unittest" 23 | ; Checks for some problems with constructors 24 | constructor_check="skip-unittest" 25 | ; Checks for unused variables and function parameters 26 | unused_variable_check="skip-unittest" 27 | ; Checks for unused labels 28 | unused_label_check="skip-unittest" 29 | ; Checks for duplicate attributes 30 | duplicate_attribute="skip-unittest" 31 | ; Checks that opEquals and toHash are both defined or neither are defined 32 | opequals_tohash_check="disabled" 33 | ; Checks for subtraction from .length properties 34 | length_subtraction_check="disabled" 35 | ; Checks for methods or properties whose names conflict with built-in propertie 36 | ; s 37 | builtin_property_names_check="skip-unittest" 38 | ; Checks for confusing code in inline asm statements 39 | asm_style_check="skip-unittest" 40 | ; Checks for confusing logical operator precedence 41 | logical_precedence_check="skip-unittest" 42 | ; Checks for undocumented public declarations 43 | undocumented_declaration_check="disabled" 44 | ; Checks for poor placement of function attributes 45 | function_attribute_check="skip-unittest" 46 | ; Checks for use of the comma operator 47 | comma_expression_check="skip-unittest" 48 | ; Checks for local imports that are too broad 49 | local_import_check="disabled" 50 | ; Checks for variables that could be declared immutable 51 | could_be_immutable_check="disabled" 52 | ; Checks for redundant expressions in if statements 53 | redundant_if_check="skip-unittest" 54 | ; Checks for redundant parenthesis 55 | redundant_parens_check="skip-unittest" 56 | ; Checks for mismatched argument and parameter names 57 | mismatched_args_check="skip-unittest" 58 | ; Checks for labels with the same name as variables 59 | label_var_same_name_check="disabled" 60 | ; Checks for lines longer than 120 characters 61 | long_line_check="skip-unittest" 62 | ; Checks for assignment to auto-ref function parameters 63 | auto_ref_assignment_check="disabled" 64 | ; Checks for incorrect infinite range definitions 65 | incorrect_infinite_range_check="skip-unittest" 66 | ; Checks for asserts that are always true 67 | useless_assert_check="skip-unittest" 68 | ; Check for uses of the old-style alias syntax 69 | alias_syntax_check="disabled" 70 | ; Checks for else if that should be else static if 71 | static_if_else_check="skip-unittest" 72 | ; Check for unclear lambda syntax 73 | lambda_return_check="skip-unittest" 74 | ; Check for auto function without return statement 75 | auto_function_check="skip-unittest" 76 | ; Check for sortedness of imports 77 | imports_sortedness="skip-unittest" 78 | ; Check for explicitly annotated unittests 79 | explicitly_annotated_unittests="disabled" 80 | ; Check for useless usage of the final attribute 81 | final_attribute_check="skip-unittest" 82 | -------------------------------------------------------------------------------- /dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tanya", 3 | "description": "@nogc library. Containers, networking, metaprogramming, memory management, utilities", 4 | "license": "MPL-2.0", 5 | "copyright": "© Eugene Wissner ", 6 | "authors": [ 7 | "Eugene Wissner" 8 | ], 9 | 10 | "targetType": "library", 11 | 12 | "dependencies": { 13 | "tanya:meta": "*", 14 | "tanya:os": "*", 15 | "tanya:middle": "*", 16 | "tanya:test": "*", 17 | "mir-linux-kernel": "~>1.0.0" 18 | }, 19 | 20 | "subPackages": [ 21 | "./meta", 22 | "./os", 23 | "./middle", 24 | "./test" 25 | ], 26 | 27 | "configurations": [ 28 | { 29 | "name": "library", 30 | "targetType": "staticLibrary", 31 | "versions": ["TanyaPhobos"] 32 | }, 33 | { 34 | "name": "dynamic", 35 | "targetType": "dynamicLibrary", 36 | "versions": ["TanyaPhobos"] 37 | }, 38 | { 39 | "name": "native", 40 | "targetType": "library", 41 | "platforms": ["linux-x86_64"], 42 | "versions": ["TanyaNative"] 43 | }, 44 | { 45 | "name": "unittest", 46 | "versions": ["TanyaPhobos"], 47 | "importPaths": [ 48 | "./source", 49 | "./tests" 50 | ], 51 | "sourcePaths": [ 52 | "./source", 53 | "./tests" 54 | ] 55 | }, 56 | { 57 | "name": "unittest-native", 58 | "platforms": ["linux-x86_64"], 59 | "versions": ["TanyaNative"], 60 | "importPaths": [ 61 | "./source", 62 | "./tests" 63 | ], 64 | "sourcePaths": [ 65 | "./source", 66 | "./tests" 67 | ] 68 | } 69 | ], 70 | 71 | "dflags-dmd": ["-dip1000"], 72 | 73 | "libs-windows": ["advapi32"], 74 | "libs-windows-x86_mscoff": ["iphlpapi"], 75 | "libs-windows-x86_64": ["iphlpapi"] 76 | } 77 | -------------------------------------------------------------------------------- /meta/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "meta", 3 | "description": "Template metaprogramming", 4 | "targetType": "library", 5 | 6 | "sourcePaths": [ 7 | "." 8 | ], 9 | "importPaths": [ 10 | "." 11 | ], 12 | "dflags-dmd": ["-dip1000"] 13 | } 14 | -------------------------------------------------------------------------------- /meta/tanya/meta/package.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | /** 6 | * Template metaprogramming. 7 | * 8 | * This package contains utilities to acquire type information at compile-time, 9 | * to transform from one type to another. It has also different algorithms for 10 | * iterating, searching and modifying template arguments. 11 | * 12 | * Copyright: Eugene Wissner 2017-2020. 13 | * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, 14 | * Mozilla Public License, v. 2.0). 15 | * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) 16 | * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/meta/tanya/meta/package.d, 17 | * tanya/meta/package.d) 18 | */ 19 | module tanya.meta; 20 | 21 | public import tanya.meta.metafunction; 22 | public import tanya.meta.trait; 23 | public import tanya.meta.transform; 24 | -------------------------------------------------------------------------------- /middle/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "middle", 3 | "description": "Runtime, middle-level utilities", 4 | "targetType": "library", 5 | 6 | "dependencies": { 7 | "tanya:meta": "*", 8 | "tanya:os": "*" 9 | }, 10 | 11 | "dependencies-linux": { 12 | "mir-linux-kernel": "~>1.0.0" 13 | }, 14 | 15 | "sourcePaths": [ 16 | "." 17 | ], 18 | "importPaths": [ 19 | "." 20 | ], 21 | "dflags-dmd": ["-dip1000"] 22 | } 23 | -------------------------------------------------------------------------------- /middle/middle-test-library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/belka-ew/tanya/4acf163b428c9d61054ebe917c4b4c89324a20d4/middle/middle-test-library -------------------------------------------------------------------------------- /middle/tanya/memory/mallocator.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | /** 6 | * Allocator based on $(D_PSYMBOL malloc), $(D_PSYMBOL realloc) and 7 | * $(D_PSYMBOL free). 8 | * 9 | * Copyright: Eugene Wissner 2017-2020. 10 | * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, 11 | * Mozilla Public License, v. 2.0). 12 | * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) 13 | * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/middle/tanya/memory/mallocator.d, 14 | * tanya/memory/mallocator.d) 15 | */ 16 | module tanya.memory.mallocator; 17 | 18 | import core.stdc.stdlib; 19 | import tanya.memory.allocator; 20 | 21 | /** 22 | * Wrapper for $(D_PSYMBOL malloc)/$(D_PSYMBOL realloc)/$(D_PSYMBOL free) from 23 | * the C standard library. 24 | */ 25 | final class Mallocator : Allocator 26 | { 27 | private alias MallocType = extern (C) void* function(size_t) 28 | @nogc nothrow pure @system; 29 | private alias FreeType = extern (C) void function(void*) 30 | @nogc nothrow pure @system; 31 | private alias ReallocType = extern (C) void* function(void*, size_t) 32 | @nogc nothrow pure @system; 33 | 34 | /** 35 | * Allocates $(D_PARAM size) bytes of memory. 36 | * 37 | * Params: 38 | * size = Amount of memory to allocate. 39 | * 40 | * Returns: The pointer to the new allocated memory. 41 | */ 42 | void[] allocate(size_t size) @nogc nothrow pure shared @system 43 | { 44 | if (size == 0) 45 | { 46 | return null; 47 | } 48 | auto p = (cast(MallocType) &malloc)(size + psize); 49 | 50 | return p is null ? null : p[psize .. psize + size]; 51 | } 52 | 53 | /// 54 | @nogc nothrow pure @system unittest 55 | { 56 | auto p = Mallocator.instance.allocate(20); 57 | assert(p.length == 20); 58 | Mallocator.instance.deallocate(p); 59 | 60 | p = Mallocator.instance.allocate(0); 61 | assert(p.length == 0); 62 | } 63 | 64 | /** 65 | * Deallocates a memory block. 66 | * 67 | * Params: 68 | * p = A pointer to the memory block to be freed. 69 | * 70 | * Returns: Whether the deallocation was successful. 71 | */ 72 | bool deallocate(void[] p) @nogc nothrow pure shared @system 73 | { 74 | if (p !is null) 75 | { 76 | (cast(FreeType) &free)(p.ptr - psize); 77 | } 78 | return true; 79 | } 80 | 81 | /// 82 | @nogc nothrow pure @system unittest 83 | { 84 | void[] p; 85 | assert(Mallocator.instance.deallocate(p)); 86 | 87 | p = Mallocator.instance.allocate(10); 88 | assert(Mallocator.instance.deallocate(p)); 89 | } 90 | 91 | /** 92 | * Reallocating in place isn't supported. 93 | * 94 | * Params: 95 | * p = A pointer to the memory block. 96 | * size = Size of the reallocated block. 97 | * 98 | * Returns: $(D_KEYWORD false). 99 | */ 100 | bool reallocateInPlace(ref void[] p, size_t size) 101 | @nogc nothrow pure shared @system 102 | { 103 | cast(void) size; 104 | return false; 105 | } 106 | 107 | /// 108 | @nogc nothrow pure @system unittest 109 | { 110 | void[] p; 111 | assert(!Mallocator.instance.reallocateInPlace(p, 8)); 112 | } 113 | 114 | /** 115 | * Increases or decreases the size of a memory block. 116 | * 117 | * Params: 118 | * p = A pointer to the memory block. 119 | * size = Size of the reallocated block. 120 | * 121 | * Returns: Whether the reallocation was successful. 122 | */ 123 | bool reallocate(ref void[] p, size_t size) 124 | @nogc nothrow pure shared @system 125 | { 126 | if (size == 0) 127 | { 128 | if (deallocate(p)) 129 | { 130 | p = null; 131 | return true; 132 | } 133 | } 134 | else if (p is null) 135 | { 136 | p = allocate(size); 137 | return p is null ? false : true; 138 | } 139 | else 140 | { 141 | auto r = (cast(ReallocType) &realloc)(p.ptr - psize, size + psize); 142 | 143 | if (r !is null) 144 | { 145 | p = r[psize .. psize + size]; 146 | return true; 147 | } 148 | } 149 | return false; 150 | } 151 | 152 | /// 153 | @nogc nothrow pure @system unittest 154 | { 155 | void[] p; 156 | 157 | assert(Mallocator.instance.reallocate(p, 20)); 158 | assert(p.length == 20); 159 | 160 | assert(Mallocator.instance.reallocate(p, 30)); 161 | assert(p.length == 30); 162 | 163 | assert(Mallocator.instance.reallocate(p, 10)); 164 | assert(p.length == 10); 165 | 166 | assert(Mallocator.instance.reallocate(p, 0)); 167 | assert(p is null); 168 | } 169 | 170 | /** 171 | * Returns: The alignment offered. 172 | */ 173 | @property uint alignment() const @nogc nothrow pure @safe shared 174 | { 175 | return (void*).alignof; 176 | } 177 | 178 | static private shared(Mallocator) instantiate() @nogc nothrow @system 179 | { 180 | if (instance_ is null) 181 | { 182 | const size = __traits(classInstanceSize, Mallocator) + psize; 183 | void* p = malloc(size); 184 | 185 | if (p !is null) 186 | { 187 | p[psize .. size] = typeid(Mallocator).initializer[]; 188 | instance_ = cast(shared Mallocator) p[psize .. size].ptr; 189 | } 190 | } 191 | return instance_; 192 | } 193 | 194 | /** 195 | * Static allocator instance and initializer. 196 | * 197 | * Returns: The global $(D_PSYMBOL Allocator) instance. 198 | */ 199 | static @property shared(Mallocator) instance() @nogc nothrow pure @system 200 | { 201 | return (cast(GetPureInstance!Mallocator) &instantiate)(); 202 | } 203 | 204 | /// 205 | @nogc nothrow pure @system unittest 206 | { 207 | assert(instance is instance); 208 | } 209 | 210 | private enum ushort psize = 8; 211 | 212 | private shared static Mallocator instance_; 213 | } 214 | -------------------------------------------------------------------------------- /middle/tanya/memory/op.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | /** 6 | * Set of operations on memory blocks. 7 | * 8 | * Copyright: Eugene Wissner 2017-2020. 9 | * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, 10 | * Mozilla Public License, v. 2.0). 11 | * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) 12 | * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/middle/tanya/memory/op.d, 13 | * tanya/memory/op.d) 14 | */ 15 | module tanya.memory.op; 16 | 17 | import core.stdc.string; 18 | 19 | private enum alignMask = size_t.sizeof - 1; 20 | 21 | /** 22 | * Copies $(D_PARAM source) into $(D_PARAM target). 23 | * 24 | * $(D_PARAM source) and $(D_PARAM target) shall not overlap so that 25 | * $(D_PARAM source) points ahead of $(D_PARAM target). 26 | * 27 | * $(D_PARAM target) shall have enough space for $(D_INLINECODE source.length) 28 | * elements. 29 | * 30 | * Params: 31 | * source = Memory to copy from. 32 | * target = Destination memory. 33 | * 34 | * See_Also: $(D_PSYMBOL copyBackward). 35 | * 36 | * Precondition: $(D_INLINECODE source.length <= target.length). 37 | */ 38 | void copy(const void[] source, void[] target) @nogc nothrow pure @trusted 39 | in 40 | { 41 | assert(source.length <= target.length); 42 | assert(source.length == 0 || source.ptr !is null); 43 | assert(target.length == 0 || target.ptr !is null); 44 | } 45 | do 46 | { 47 | memcpy(target.ptr, source.ptr, source.length); 48 | } 49 | 50 | /// 51 | @nogc nothrow pure @safe unittest 52 | { 53 | ubyte[9] source = [1, 2, 3, 4, 5, 6, 7, 8, 9]; 54 | ubyte[9] target; 55 | source.copy(target); 56 | assert(equal(source, target)); 57 | } 58 | 59 | /* 60 | * size_t value each of which bytes is set to `Byte`. 61 | */ 62 | private template filledBytes(ubyte Byte, ubyte I = 0) 63 | { 64 | static if (I == size_t.sizeof) 65 | { 66 | enum size_t filledBytes = Byte; 67 | } 68 | else 69 | { 70 | enum size_t filledBytes = (filledBytes!(Byte, I + 1) << 8) | Byte; 71 | } 72 | } 73 | 74 | /** 75 | * Fills $(D_PARAM memory) with the single byte $(D_PARAM c). 76 | * 77 | * Param: 78 | * c = The value to fill $(D_PARAM memory) with. 79 | * memory = Memory block. 80 | */ 81 | void fill(ubyte c = 0)(void[] memory) @trusted 82 | in 83 | { 84 | assert(memory.length == 0 || memory.ptr !is null); 85 | } 86 | do 87 | { 88 | memset(memory.ptr, c, memory.length); 89 | } 90 | 91 | /// 92 | @nogc nothrow pure @safe unittest 93 | { 94 | ubyte[9] memory = [1, 2, 3, 4, 5, 6, 7, 8, 9]; 95 | memory.fill!0(); 96 | foreach (ubyte v; memory) 97 | { 98 | assert(v == 0); 99 | } 100 | } 101 | 102 | /** 103 | * Copies starting from the end of $(D_PARAM source) into the end of 104 | * $(D_PARAM target). 105 | * 106 | * $(D_PSYMBOL copyBackward) copies the elements in reverse order, but the 107 | * order of elements in the $(D_PARAM target) is exactly the same as in the 108 | * $(D_PARAM source). 109 | * 110 | * $(D_PARAM source) and $(D_PARAM target) shall not overlap so that 111 | * $(D_PARAM target) points ahead of $(D_PARAM source). 112 | * 113 | * $(D_PARAM target) shall have enough space for $(D_INLINECODE source.length) 114 | * elements. 115 | * 116 | * Params: 117 | * source = Memory to copy from. 118 | * target = Destination memory. 119 | * 120 | * See_Also: $(D_PSYMBOL copy). 121 | * 122 | * Precondition: $(D_INLINECODE source.length <= target.length). 123 | */ 124 | void copyBackward(const void[] source, void[] target) @nogc nothrow pure @trusted 125 | in 126 | { 127 | assert(source.length <= target.length); 128 | assert(source.length == 0 || source.ptr !is null); 129 | assert(target.length == 0 || target.ptr !is null); 130 | } 131 | do 132 | { 133 | memmove(target.ptr, source.ptr, source.length); 134 | } 135 | 136 | /// 137 | @nogc nothrow pure @safe unittest 138 | { 139 | ubyte[6] mem = [ 'a', 'a', 'b', 'b', 'c', 'c' ]; 140 | ubyte[6] expected = [ 'a', 'a', 'a', 'a', 'b', 'b' ]; 141 | 142 | copyBackward(mem[0 .. 4], mem[2 .. $]); 143 | assert(equal(expected, mem)); 144 | } 145 | 146 | /** 147 | * Finds the first occurrence of $(D_PARAM needle) in $(D_PARAM haystack) if 148 | * any. 149 | * 150 | * Params: 151 | * haystack = Memory block. 152 | * needle = A byte. 153 | * 154 | * Returns: The subrange of $(D_PARAM haystack) whose first element is the 155 | * first occurrence of $(D_PARAM needle). If $(D_PARAM needle) 156 | * couldn't be found, an empty `inout void[]` is returned. 157 | */ 158 | inout(void[]) find(return inout void[] haystack, ubyte needle) 159 | @nogc nothrow pure @trusted 160 | in 161 | { 162 | assert(haystack.length == 0 || haystack.ptr !is null); 163 | } 164 | do 165 | { 166 | auto length = haystack.length; 167 | const size_t needleWord = size_t.max * needle; 168 | enum size_t highBits = filledBytes!(0x01, 0); 169 | enum size_t mask = filledBytes!(0x80, 0); 170 | 171 | // Align 172 | auto bytes = cast(inout(ubyte)*) haystack; 173 | while (length > 0 && ((cast(size_t) bytes) & 3) != 0) 174 | { 175 | if (*bytes == needle) 176 | { 177 | return bytes[0 .. length]; 178 | } 179 | ++bytes; 180 | --length; 181 | } 182 | 183 | // Check if some of the words has the needle 184 | auto words = cast(inout(size_t)*) bytes; 185 | while (length >= size_t.sizeof) 186 | { 187 | if ((((*words ^ needleWord) - highBits) & (~*words) & mask) != 0) 188 | { 189 | break; 190 | } 191 | ++words; 192 | length -= size_t.sizeof; 193 | } 194 | 195 | // Find the exact needle position in the word 196 | bytes = cast(inout(ubyte)*) words; 197 | while (length > 0) 198 | { 199 | if (*bytes == needle) 200 | { 201 | return bytes[0 .. length]; 202 | } 203 | ++bytes; 204 | --length; 205 | } 206 | 207 | return haystack[$ .. $]; 208 | } 209 | 210 | /// 211 | @nogc nothrow pure @safe unittest 212 | { 213 | const ubyte[9] haystack = ['a', 'b', 'c', 'd', 'e', 'f', 'b', 'g', 'h']; 214 | 215 | assert(equal(find(haystack, 'a'), haystack[])); 216 | assert(equal(find(haystack, 'b'), haystack[1 .. $])); 217 | assert(equal(find(haystack, 'c'), haystack[2 .. $])); 218 | assert(equal(find(haystack, 'd'), haystack[3 .. $])); 219 | assert(equal(find(haystack, 'e'), haystack[4 .. $])); 220 | assert(equal(find(haystack, 'f'), haystack[5 .. $])); 221 | assert(equal(find(haystack, 'h'), haystack[8 .. $])); 222 | assert(find(haystack, 'i').length == 0); 223 | 224 | assert(find(null, 'a').length == 0); 225 | } 226 | 227 | /** 228 | * Looks for `\0` in the $(D_PARAM haystack) and returns the part of the 229 | * $(D_PARAM haystack) ahead of it. 230 | * 231 | * Returns $(D_KEYWORD null) if $(D_PARAM haystack) doesn't contain a null 232 | * character. 233 | * 234 | * Params: 235 | * haystack = Memory block. 236 | * 237 | * Returns: The subrange that spans all bytes before the null character or 238 | * $(D_KEYWORD null) if the $(D_PARAM haystack) doesn't contain any. 239 | */ 240 | inout(char[]) findNullTerminated(return inout char[] haystack) 241 | @nogc nothrow pure @trusted 242 | in 243 | { 244 | assert(haystack.length == 0 || haystack.ptr !is null); 245 | } 246 | do 247 | { 248 | auto length = haystack.length; 249 | enum size_t highBits = filledBytes!(0x01, 0); 250 | enum size_t mask = filledBytes!(0x80, 0); 251 | 252 | // Align 253 | auto bytes = cast(inout(ubyte)*) haystack; 254 | while (length > 0 && ((cast(size_t) bytes) & 3) != 0) 255 | { 256 | if (*bytes == '\0') 257 | { 258 | return haystack[0 .. haystack.length - length]; 259 | } 260 | ++bytes; 261 | --length; 262 | } 263 | 264 | // Check if some of the words contains 0 265 | auto words = cast(inout(size_t)*) bytes; 266 | while (length >= size_t.sizeof) 267 | { 268 | if (((*words - highBits) & (~*words) & mask) != 0) 269 | { 270 | break; 271 | } 272 | ++words; 273 | length -= size_t.sizeof; 274 | } 275 | 276 | // Find the exact 0 position in the word 277 | bytes = cast(inout(ubyte)*) words; 278 | while (length > 0) 279 | { 280 | if (*bytes == '\0') 281 | { 282 | return haystack[0 .. haystack.length - length]; 283 | } 284 | ++bytes; 285 | --length; 286 | } 287 | 288 | return null; 289 | } 290 | 291 | /// 292 | @nogc nothrow pure @safe unittest 293 | { 294 | assert(equal(findNullTerminated("abcdef\0gh"), "abcdef")); 295 | assert(equal(findNullTerminated("\0garbage"), "")); 296 | assert(equal(findNullTerminated("\0"), "")); 297 | assert(equal(findNullTerminated("cstring\0"), "cstring")); 298 | assert(findNullTerminated(null) is null); 299 | assert(findNullTerminated("abcdef") is null); 300 | } 301 | 302 | /** 303 | * Compares two memory areas $(D_PARAM r1) and $(D_PARAM r2) for equality. 304 | * 305 | * Params: 306 | * r1 = First memory block. 307 | * r2 = Second memory block. 308 | * 309 | * Returns: $(D_KEYWORD true) if $(D_PARAM r1) and $(D_PARAM r2) are equal, 310 | * $(D_KEYWORD false) otherwise. 311 | */ 312 | bool equal(const void[] r1, const void[] r2) @nogc nothrow pure @trusted 313 | in 314 | { 315 | assert(r1.length == 0 || r1.ptr !is null); 316 | assert(r2.length == 0 || r2.ptr !is null); 317 | } 318 | do 319 | { 320 | return r1.length == r2.length && memcmp(r1.ptr, r2.ptr, r1.length) == 0; 321 | } 322 | 323 | /// 324 | @nogc nothrow pure @safe unittest 325 | { 326 | assert(equal("asdf", "asdf")); 327 | assert(!equal("asd", "asdf")); 328 | assert(!equal("asdf", "asd")); 329 | assert(!equal("asdf", "qwer")); 330 | } 331 | -------------------------------------------------------------------------------- /middle/tanya/memory/package.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | /** 6 | * Dynamic memory management. 7 | * 8 | * Copyright: Eugene Wissner 2016-2020. 9 | * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, 10 | * Mozilla Public License, v. 2.0). 11 | * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) 12 | * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/middle/tanya/memory/package.d, 13 | * tanya/memory/package.d) 14 | */ 15 | module tanya.memory; 16 | 17 | public import tanya.memory.allocator; 18 | public import tanya.memory.lifetime; 19 | -------------------------------------------------------------------------------- /os/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "os", 3 | "description": "Platform-independent interfaces to operating system functionality", 4 | "targetType": "library", 5 | 6 | "dependencies": { 7 | "tanya:meta": "*" 8 | }, 9 | 10 | "sourcePaths": [ 11 | "." 12 | ], 13 | "importPaths": [ 14 | "." 15 | ], 16 | "dflags-dmd": ["-dip1000"] 17 | } 18 | -------------------------------------------------------------------------------- /os/tanya/os/package.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | /** 6 | * This package provides platform-independent interfaces to operating system 7 | * functionality. 8 | * 9 | * Copyright: Eugene Wissner 2017-2020. 10 | * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, 11 | * Mozilla Public License, v. 2.0). 12 | * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) 13 | * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/os/tanya/os/package.d, 14 | * tanya/os/package.d) 15 | */ 16 | module tanya.os; 17 | 18 | public import tanya.os.error; 19 | -------------------------------------------------------------------------------- /source/tanya/algorithm/iteration.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | /** 6 | * Iteration algorithms. 7 | * 8 | * These algorithms wrap other ranges and modify the way, how the original 9 | * range is iterated, or the order in which its elements are accessed. 10 | * 11 | * All algorithms in this module are lazy, they request the next element of the 12 | * original range on demand. 13 | * 14 | * Copyright: Eugene Wissner 2018-2021. 15 | * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, 16 | * Mozilla Public License, v. 2.0). 17 | * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) 18 | * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/algorithm/iteration.d, 19 | * tanya/algorithm/iteration.d) 20 | */ 21 | module tanya.algorithm.iteration; 22 | 23 | import std.typecons; 24 | import tanya.memory.lifetime; 25 | import tanya.meta.trait; 26 | import tanya.meta.transform; 27 | import tanya.range; 28 | 29 | private struct SingletonByValue(E) 30 | { 31 | private Nullable!E element; 32 | 33 | @disable this(); 34 | 35 | private this(U)(ref U element) 36 | if (is(U == E)) 37 | { 38 | this.element = move(element); 39 | } 40 | 41 | private this(U)(ref U element) 42 | if (is(Unqual!U == Nullable!(Unqual!E)) || is(Unqual!U == Nullable!(const E))) 43 | { 44 | if (!element.isNull) 45 | { 46 | this.element = element.get; 47 | } 48 | } 49 | 50 | @property ref inout(E) front() inout 51 | in 52 | { 53 | assert(!empty); 54 | } 55 | do 56 | { 57 | return this.element.get; 58 | } 59 | 60 | alias back = front; 61 | 62 | void popFront() 63 | in 64 | { 65 | assert(!empty); 66 | } 67 | do 68 | { 69 | this.element.nullify(); 70 | } 71 | 72 | alias popBack = popFront; 73 | 74 | @property bool empty() const 75 | { 76 | return this.element.isNull; 77 | } 78 | 79 | @property size_t length() const 80 | { 81 | return !this.element.isNull; 82 | } 83 | 84 | auto save() 85 | { 86 | return SingletonByValue!E(this.element); 87 | } 88 | 89 | ref inout(E) opIndex(size_t i) inout 90 | in 91 | { 92 | assert(!empty); 93 | assert(i == 0); 94 | } 95 | do 96 | { 97 | return this.element.get; 98 | } 99 | } 100 | 101 | private struct SingletonByRef(E) 102 | { 103 | private E* element; 104 | 105 | @disable this(); 106 | 107 | private this(return ref E element) @trusted 108 | { 109 | this.element = &element; 110 | } 111 | 112 | @property ref inout(E) front() inout return 113 | in 114 | { 115 | assert(!empty); 116 | } 117 | do 118 | { 119 | return *this.element; 120 | } 121 | 122 | alias back = front; 123 | 124 | void popFront() 125 | in 126 | { 127 | assert(!empty); 128 | } 129 | do 130 | { 131 | this.element = null; 132 | } 133 | 134 | alias popBack = popFront; 135 | 136 | @property bool empty() const 137 | { 138 | return this.element is null; 139 | } 140 | 141 | @property size_t length() const 142 | { 143 | return this.element !is null; 144 | } 145 | 146 | auto save() return 147 | { 148 | return typeof(this)(*this.element); 149 | } 150 | 151 | ref inout(E) opIndex(size_t i) inout return 152 | in 153 | { 154 | assert(!empty); 155 | assert(i == 0); 156 | } 157 | do 158 | { 159 | return *this.element; 160 | } 161 | } 162 | 163 | /** 164 | * Creates a bidirectional and random-access range with the single element 165 | * $(D_PARAM element). 166 | * 167 | * If $(D_PARAM element) is passed by value the resulting range stores it 168 | * internally. If $(D_PARAM element) is passed by reference, the resulting 169 | * range keeps only a pointer to the element. 170 | * 171 | * Params: 172 | * E = Element type. 173 | * element = Element. 174 | * 175 | * Returns: A range with one element. 176 | */ 177 | auto singleton(E)(return E element) 178 | if (isMutable!E) 179 | { 180 | return SingletonByValue!E(element); 181 | } 182 | 183 | /// ditto 184 | auto singleton(E)(return ref E element) 185 | { 186 | return SingletonByRef!E(element); 187 | } 188 | 189 | /// 190 | @nogc nothrow pure @safe unittest 191 | { 192 | auto singleChar = singleton('a'); 193 | 194 | assert(singleChar.length == 1); 195 | assert(singleChar.front == 'a'); 196 | 197 | singleChar.popFront(); 198 | assert(singleChar.empty); 199 | } 200 | 201 | /** 202 | * Accumulates all elements of a range using a function. 203 | * 204 | * $(D_PSYMBOL foldr) takes a function, a bidirectional range and the initial 205 | * value. The function takes this initial value and the first element of the 206 | * range (in this order), puts them together and returns the result. The return 207 | * type of the function should be the same as the type of the initial value. 208 | * This is than repeated for all the remaining elements of the range, whereby 209 | * the value returned by the passed function is used at the place of the 210 | * initial value. 211 | * 212 | * $(D_PSYMBOL foldr) accumulates from right to left. 213 | * 214 | * Params: 215 | * F = Callable accepting the accumulator and a range element. 216 | */ 217 | template foldr(F...) 218 | if (F.length == 1) 219 | { 220 | /** 221 | * Params: 222 | * R = Bidirectional range type. 223 | * T = Type of the accumulated value. 224 | * range = Bidirectional range. 225 | * init = Initial value. 226 | * 227 | * Returns: Accumulated value. 228 | */ 229 | auto foldr(R, T)(scope R range, auto ref T init) 230 | if (isBidirectionalRange!R) 231 | { 232 | if (range.empty) 233 | { 234 | return init; 235 | } 236 | else 237 | { 238 | auto acc = F[0](init, getAndPopBack(range)); 239 | return foldr(range, acc); 240 | } 241 | } 242 | } 243 | 244 | /// 245 | @nogc nothrow pure @safe unittest 246 | { 247 | int[3] range = [1, 2, 3]; 248 | int[3] output; 249 | const int[3] expected = [3, 2, 1]; 250 | 251 | alias f = (acc, x) { 252 | acc.front = x; 253 | acc.popFront; 254 | return acc; 255 | }; 256 | const actual = foldr!f(range[], output[]); 257 | 258 | assert(output[] == expected[]); 259 | } 260 | -------------------------------------------------------------------------------- /source/tanya/algorithm/mutation.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | /** 6 | * Algorithms that modify its arguments. 7 | * 8 | * Copyright: Eugene Wissner 2017-2020. 9 | * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, 10 | * Mozilla Public License, v. 2.0). 11 | * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) 12 | * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/algorithm/mutation.d, 13 | * tanya/algorithm/mutation.d) 14 | */ 15 | module tanya.algorithm.mutation; 16 | 17 | static import tanya.memory.lifetime; 18 | static import tanya.memory.op; 19 | import tanya.meta.trait; 20 | import tanya.meta.transform; 21 | import tanya.range; 22 | 23 | /** 24 | * Copies the $(D_PARAM source) range into the $(D_PARAM target) range. 25 | * 26 | * Params: 27 | * Source = Input range type. 28 | * Target = Output range type. 29 | * source = Source input range. 30 | * target = Target output range. 31 | * 32 | * Returns: $(D_PARAM target) range, whose front element is the one past the 33 | * last element copied. 34 | * 35 | * Precondition: $(D_PARAM target) should be large enough to accept all 36 | * $(D_PARAM source) elements. 37 | */ 38 | Target copy(Source, Target)(Source source, Target target) 39 | if (isInputRange!Source && isOutputRange!(Target, ElementType!Source)) 40 | in 41 | { 42 | static if (hasLength!Source && hasLength!Target) 43 | { 44 | assert(target.length >= source.length); 45 | } 46 | } 47 | do 48 | { 49 | alias E = ElementType!Source; 50 | static if (isDynamicArray!Source 51 | && is(Unqual!E == ElementType!Target) 52 | && !hasElaborateCopyConstructor!E 53 | && !hasElaborateAssign!E 54 | && !hasElaborateDestructor!E) 55 | { 56 | if (source.ptr < target.ptr 57 | && (() @trusted => (target.ptr - source.ptr) < source.length)()) 58 | { 59 | tanya.memory.op.copyBackward(source, target); 60 | } 61 | else if (source.ptr !is target.ptr) 62 | { 63 | tanya.memory.op.copy(source, target); 64 | } 65 | return target[source.length .. $]; 66 | } 67 | else 68 | { 69 | for (; !source.empty; source.popFront()) 70 | { 71 | put(target, source.front); 72 | } 73 | return target; 74 | } 75 | } 76 | 77 | /// 78 | @nogc nothrow pure @safe unittest 79 | { 80 | import std.algorithm.comparison : equal; 81 | 82 | const int[2] source = [1, 2]; 83 | int[2] target = [3, 4]; 84 | 85 | copy(source[], target[]); 86 | assert(equal(source[], target[])); 87 | } 88 | 89 | /** 90 | * Fills $(D_PARAM range) with $(D_PARAM value). 91 | * 92 | * Params: 93 | * Range = Input range type. 94 | * Value = Filler type. 95 | * range = Input range. 96 | * value = Filler. 97 | */ 98 | void fill(Range, Value)(Range range, auto ref Value value) 99 | if (isInputRange!Range && isAssignable!(ElementType!Range, Value)) 100 | { 101 | static if (!isDynamicArray!Range && is(typeof(range[] = value))) 102 | { 103 | range[] = value; 104 | } 105 | else 106 | { 107 | for (; !range.empty; range.popFront()) 108 | { 109 | range.front = value; 110 | } 111 | } 112 | } 113 | 114 | /// 115 | @nogc nothrow pure @safe unittest 116 | { 117 | import std.algorithm.comparison : equal; 118 | 119 | int[6] actual; 120 | const int[6] expected = [1, 1, 1, 1, 1, 1]; 121 | 122 | fill(actual[], 1); 123 | assert(equal(actual[], expected[])); 124 | } 125 | 126 | /** 127 | * Fills $(D_PARAM range) with $(D_PARAM value) assuming the elements of the 128 | * $(D_PARAM range) aren't initialized. 129 | * 130 | * Params: 131 | * Range = Input range type. 132 | * Value = Initializer type. 133 | * range = Input range. 134 | * value = Initializer. 135 | */ 136 | void uninitializedFill(Range, Value)(Range range, auto ref Value value) 137 | if (isInputRange!Range && hasLvalueElements!Range 138 | && isAssignable!(ElementType!Range, Value)) 139 | { 140 | static if (hasElaborateDestructor!(ElementType!Range)) 141 | { 142 | for (; !range.empty; range.popFront()) 143 | { 144 | ElementType!Range* p = &range.front; 145 | tanya.memory.lifetime.emplace!(ElementType!Range)(cast(void[]) (p[0 .. 1]), value); 146 | } 147 | } 148 | else 149 | { 150 | fill(range, value); 151 | } 152 | } 153 | 154 | /// 155 | @nogc nothrow pure @safe unittest 156 | { 157 | import std.algorithm.comparison : equal; 158 | 159 | int[6] actual = void; 160 | const int[6] expected = [1, 1, 1, 1, 1, 1]; 161 | 162 | uninitializedFill(actual[], 1); 163 | assert(equal(actual[], expected[])); 164 | } 165 | 166 | /** 167 | * Initializes all elements of the $(D_PARAM range) assuming that they are 168 | * uninitialized. 169 | * 170 | * Params: 171 | * Range = Input range type 172 | * range = Input range. 173 | */ 174 | void initializeAll(Range)(Range range) @trusted 175 | if (isInputRange!Range && hasLvalueElements!Range) 176 | { 177 | import tanya.memory.op : copy, fill; 178 | alias T = ElementType!Range; 179 | 180 | static if (isDynamicArray!Range && __traits(isZeroInit, T)) 181 | { 182 | fill!0(range); 183 | } 184 | else 185 | { 186 | static immutable init = T.init; 187 | for (; !range.empty; range.popFront()) 188 | { 189 | copy((&init)[0 .. 1], (&range.front)[0 .. 1]); 190 | } 191 | } 192 | } 193 | 194 | /// 195 | @nogc nothrow pure @safe unittest 196 | { 197 | import std.algorithm.comparison : equal; 198 | 199 | int[2] actual = void; 200 | const int[2] expected = [0, 0]; 201 | 202 | initializeAll(actual[]); 203 | assert(equal(actual[], expected[])); 204 | } 205 | 206 | /** 207 | * Destroys all elements in the $(D_PARAM range). 208 | * 209 | * This function has effect only if the element type of $(D_PARAM Range) has 210 | * an elaborate destructor, i.e. it is a $(D_PSYMBOL struct) with an explicit 211 | * or generated by the compiler destructor. 212 | * 213 | * Params: 214 | * Range = Input range type. 215 | * range = Input range. 216 | */ 217 | void destroyAll(Range)(Range range) 218 | if (isInputRange!Range && hasLvalueElements!Range) 219 | { 220 | tanya.memory.lifetime.destroyAllImpl!(Range, ElementType!Range)(range); 221 | } 222 | 223 | /// 224 | @nogc nothrow pure @trusted unittest 225 | { 226 | static struct WithDtor 227 | { 228 | private size_t* counter; 229 | ~this() @nogc nothrow pure 230 | { 231 | if (this.counter !is null) 232 | { 233 | ++(*this.counter); 234 | } 235 | } 236 | } 237 | 238 | size_t counter; 239 | WithDtor[2] withDtor = [WithDtor(&counter), WithDtor(&counter)]; 240 | 241 | destroyAll(withDtor[]); 242 | 243 | assert(counter == 2); 244 | } 245 | -------------------------------------------------------------------------------- /source/tanya/algorithm/package.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | /** 6 | * Collection of generic algorithms. 7 | * 8 | * Copyright: Eugene Wissner 2017-2021. 9 | * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, 10 | * Mozilla Public License, v. 2.0). 11 | * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) 12 | * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/algorithm/package.d, 13 | * tanya/algorithm/package.d) 14 | */ 15 | module tanya.algorithm; 16 | 17 | public import tanya.algorithm.iteration; 18 | public import tanya.algorithm.mutation; 19 | -------------------------------------------------------------------------------- /source/tanya/container/entry.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | /* 6 | * Internal package used by containers that rely on entries/nodes. 7 | * 8 | * Copyright: Eugene Wissner 2016-2022. 9 | * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, 10 | * Mozilla Public License, v. 2.0). 11 | * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) 12 | * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/container/entry.d, 13 | * tanya/container/entry.d) 14 | */ 15 | module tanya.container.entry; 16 | 17 | import tanya.container.array; 18 | import tanya.memory.allocator; 19 | import tanya.memory.lifetime; 20 | import tanya.meta.trait; 21 | import tanya.meta.transform; 22 | 23 | package struct SEntry(T) 24 | { 25 | // Item content. 26 | T content; 27 | 28 | // Next item. 29 | SEntry* next; 30 | } 31 | 32 | package struct DEntry(T) 33 | { 34 | // Item content. 35 | T content; 36 | 37 | // Previous and next item. 38 | DEntry* next, prev; 39 | } 40 | 41 | package enum BucketStatus : byte 42 | { 43 | deleted = -1, 44 | empty = 0, 45 | used = 1, 46 | } 47 | 48 | package struct Bucket(K, V = void) 49 | { 50 | static if (is(V == void)) 51 | { 52 | K key_; 53 | } 54 | else 55 | { 56 | package struct KV 57 | { 58 | package K key; 59 | package V value; 60 | } 61 | KV kv; 62 | } 63 | BucketStatus status = BucketStatus.empty; 64 | 65 | this()(ref K key) 66 | { 67 | this.key = key; 68 | } 69 | 70 | @property void key()(ref K key) 71 | { 72 | this.key() = key; 73 | this.status = BucketStatus.used; 74 | } 75 | 76 | @property ref inout(K) key() inout 77 | { 78 | static if (is(V == void)) 79 | { 80 | return this.key_; 81 | } 82 | else 83 | { 84 | return this.kv.key; 85 | } 86 | } 87 | 88 | void moveKey(ref K key) 89 | { 90 | move(key, this.key()); 91 | this.status = BucketStatus.used; 92 | } 93 | 94 | bool opEquals(T)(ref const T key) const 95 | { 96 | return this.status == BucketStatus.used && this.key == key; 97 | } 98 | 99 | bool opEquals(ref const(typeof(this)) that) const 100 | { 101 | return key == that.key && this.status == that.status; 102 | } 103 | 104 | void remove() 105 | { 106 | static if (hasElaborateDestructor!K) 107 | { 108 | destroy(key); 109 | } 110 | this.status = BucketStatus.deleted; 111 | } 112 | } 113 | 114 | // Possible sizes for the hash-based containers. 115 | package static immutable size_t[33] primes = [ 116 | 0, 3, 7, 13, 23, 37, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 117 | 24593, 49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469, 118 | 12582917, 25165843, 50331653, 100663319, 201326611, 402653189, 119 | 805306457, 1610612741, 3221225473 120 | ]; 121 | 122 | package(tanya.container) struct HashArray(alias hasher, K, V = void) 123 | { 124 | alias Key = K; 125 | alias Value = V; 126 | alias Bucket = .Bucket!(Key, Value); 127 | alias Buckets = Array!Bucket; 128 | 129 | Buckets array; 130 | size_t lengthIndex; 131 | size_t length; 132 | 133 | this(shared Allocator allocator) 134 | in 135 | { 136 | assert(allocator !is null); 137 | } 138 | do 139 | { 140 | this.array = Buckets(allocator); 141 | } 142 | 143 | this(T)(ref T data, shared Allocator allocator) 144 | if (is(Unqual!T == HashArray)) 145 | in 146 | { 147 | assert(allocator !is null); 148 | } 149 | do 150 | { 151 | this.array = Buckets(data.array, allocator); 152 | this.lengthIndex = data.lengthIndex; 153 | this.length = data.length; 154 | } 155 | 156 | // Move constructor 157 | void move(ref HashArray data, shared Allocator allocator) 158 | in 159 | { 160 | assert(allocator !is null); 161 | } 162 | do 163 | { 164 | this.array = Buckets(.move(data.array), allocator); 165 | this.lengthIndex = data.lengthIndex; 166 | this.length = data.length; 167 | } 168 | 169 | void swap(ref HashArray data) 170 | { 171 | .swap(this.array, data.array); 172 | .swap(this.lengthIndex, data.lengthIndex); 173 | .swap(this.length, data.length); 174 | } 175 | 176 | void opAssign()(ref typeof(this) that) 177 | { 178 | this.array = that.array; 179 | this.lengthIndex = that.lengthIndex; 180 | this.length = that.length; 181 | } 182 | 183 | @property size_t bucketCount() const 184 | { 185 | return primes[this.lengthIndex]; 186 | } 187 | 188 | /* 189 | * Returns bucket position for `hash`. `0` may mean the 0th position or an 190 | * empty `buckets` array. 191 | */ 192 | size_t locateBucket(T)(ref const T key) const 193 | { 194 | return this.array.length == 0 ? 0 : hasher(key) % bucketCount; 195 | } 196 | 197 | /* 198 | * If the key doesn't already exists, returns an empty bucket the key can 199 | * be inserted in and adjusts the element count. Otherwise returns the 200 | * bucket containing the key. 201 | */ 202 | ref Bucket insert(ref Key key) 203 | { 204 | const newLengthIndex = this.lengthIndex + 1; 205 | if (newLengthIndex != primes.length) 206 | { 207 | foreach (ref e; this.array[locateBucket(key) .. $]) 208 | { 209 | if (e == key) 210 | { 211 | return e; 212 | } 213 | else if (e.status != BucketStatus.used) 214 | { 215 | ++this.length; 216 | return e; 217 | } 218 | } 219 | 220 | this.rehashToSize(newLengthIndex); 221 | } 222 | 223 | foreach (ref e; this.array[locateBucket(key) .. $]) 224 | { 225 | if (e == key) 226 | { 227 | return e; 228 | } 229 | else if (e.status != BucketStatus.used) 230 | { 231 | ++this.length; 232 | return e; 233 | } 234 | } 235 | 236 | this.array.length = this.array.length + 1; 237 | ++this.length; 238 | return this.array[$ - 1]; 239 | } 240 | 241 | // Takes an index in the primes array. 242 | void rehashToSize(const size_t n) 243 | in 244 | { 245 | assert(n < primes.length); 246 | } 247 | do 248 | { 249 | auto storage = typeof(this.array)(primes[n], this.array.allocator); 250 | DataLoop: foreach (ref e1; this.array[]) 251 | { 252 | if (e1.status == BucketStatus.used) 253 | { 254 | auto bucketPosition = hasher(e1.key) % primes[n]; 255 | 256 | foreach (ref e2; storage[bucketPosition .. $]) 257 | { 258 | if (e2.status != BucketStatus.used) // Insert the key 259 | { 260 | .move(e1, e2); 261 | continue DataLoop; 262 | } 263 | } 264 | storage.insertBack(.move(e1)); 265 | } 266 | } 267 | .move(storage, this.array); 268 | this.lengthIndex = n; 269 | } 270 | 271 | void rehash(const size_t n) 272 | { 273 | size_t lengthIndex; 274 | for (; lengthIndex < primes.length; ++lengthIndex) 275 | { 276 | if (primes[lengthIndex] >= n) 277 | { 278 | break; 279 | } 280 | } 281 | if (lengthIndex > this.lengthIndex) 282 | { 283 | this.rehashToSize(lengthIndex); 284 | } 285 | } 286 | 287 | @property size_t capacity() const 288 | { 289 | return this.array.length; 290 | } 291 | 292 | void clear() 293 | { 294 | this.array.clear(); 295 | this.length = 0; 296 | } 297 | 298 | size_t remove(ref Key key) 299 | { 300 | foreach (ref e; this.array[locateBucket(key) .. $]) 301 | { 302 | if (e == key) // Found. 303 | { 304 | e.remove(); 305 | --this.length; 306 | return 1; 307 | } 308 | else if (e.status == BucketStatus.empty) 309 | { 310 | break; 311 | } 312 | } 313 | return 0; 314 | } 315 | 316 | bool opBinaryRight(string op : "in", T)(ref const T key) const 317 | { 318 | foreach (ref e; this.array[locateBucket(key) .. $]) 319 | { 320 | if (e == key) // Found. 321 | { 322 | return true; 323 | } 324 | else if (e.status == BucketStatus.empty) 325 | { 326 | break; 327 | } 328 | } 329 | return false; 330 | } 331 | } 332 | -------------------------------------------------------------------------------- /source/tanya/container/package.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | /** 6 | * Abstract data types whose instances are collections of other objects. 7 | * 8 | * Copyright: Eugene Wissner 2016-2020. 9 | * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, 10 | * Mozilla Public License, v. 2.0). 11 | * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) 12 | * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/container/package.d, 13 | * tanya/container/package.d) 14 | */ 15 | module tanya.container; 16 | 17 | public import tanya.container.array; 18 | public import tanya.container.buffer; 19 | public import tanya.container.hashtable; 20 | public import tanya.container.list; 21 | public import tanya.container.set; 22 | public import tanya.container.string; 23 | -------------------------------------------------------------------------------- /source/tanya/hash/lookup.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | /** 6 | * Non-cryptographic, lookup hash functions. 7 | * 8 | * Copyright: Eugene Wissner 2018-2020. 9 | * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, 10 | * Mozilla Public License, v. 2.0). 11 | * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) 12 | * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/hash/lookup.d, 13 | * tanya/hash/lookup.d) 14 | */ 15 | module tanya.hash.lookup; 16 | 17 | import std.traits : isScalarType; 18 | import tanya.meta.trait; 19 | import tanya.range.primitive; 20 | 21 | private struct Hasher 22 | { 23 | static if (size_t.sizeof == 4) 24 | { 25 | enum uint offsetBasis = 2166136261; 26 | enum uint prime = 16777619; 27 | } 28 | else static if (size_t.sizeof == 8) 29 | { 30 | enum ulong offsetBasis = 14695981039346656037UL; 31 | enum ulong prime = 1099511628211UL; 32 | } 33 | else static if (size_t.sizeof == 16) 34 | { 35 | enum size_t offsetBasis = (size_t(0x6c62272e07bb0142UL) << 64) + 0x62b821756295c58dUL; 36 | enum size_t prime = (size_t(1) << 88) + (1 << 8) + 0x3b; 37 | } 38 | else 39 | { 40 | static assert(false, "FNV requires at least 32-bit hash length"); 41 | } 42 | 43 | size_t hash = offsetBasis; 44 | 45 | void opCall(T)(auto ref T key) 46 | { 47 | static if (is(typeof(key.toHash()) == size_t)) 48 | { 49 | opCall(key.toHash()); // Combine user-defined hashes 50 | } 51 | else static if (isScalarType!T || isPointer!T) 52 | { 53 | // Treat as an array of words 54 | static if (T.sizeof % size_t.sizeof == 0 55 | && T.alignof >= size_t.alignof) 56 | alias CastT = size_t; 57 | // (64-bit or 128-bit) Treat as an array of ints 58 | else static if (T.sizeof % uint.sizeof == 0 59 | && T.alignof >= uint.alignof) 60 | alias CastT = uint; 61 | // Treat as an array of bytes 62 | else 63 | alias CastT = ubyte; 64 | add((() @trusted => (cast(const CastT*) &key)[0 .. T.sizeof / CastT.sizeof])()); 65 | } 66 | else static if (isArray!T && isScalarType!(ElementType!T)) 67 | { 68 | // Treat as an array of words 69 | static if (ElementType!T.sizeof % size_t.sizeof == 0 70 | && ElementType!T.alignof >= size_t.alignof) 71 | alias CastT = size_t; 72 | // (64-bit or 128-bit) Treat as an array of ints 73 | else static if (ElementType!T.sizeof % uint.sizeof == 0 74 | && ElementType!T.alignof >= uint.alignof) 75 | alias CastT = uint; 76 | // Treat as an array of bytes 77 | else 78 | alias CastT = ubyte; 79 | add(cast(const CastT[]) key); 80 | } 81 | else static if (is(T == typeof(null))) 82 | { 83 | add(key); 84 | } 85 | else static if (isInputRange!T && !isInfinite!T) 86 | { 87 | foreach (e; key) 88 | { 89 | opCall(e); 90 | } 91 | } 92 | else 93 | { 94 | static assert(false, "Hash function is not available"); 95 | } 96 | } 97 | 98 | void add(scope const ubyte[] key) @nogc nothrow pure @safe 99 | { 100 | // FNV-1a 101 | foreach (c; key) 102 | { 103 | this.hash = (this.hash ^ c) * prime; 104 | } 105 | } 106 | 107 | void add(scope const size_t[] key) @nogc nothrow pure @safe 108 | { 109 | static if (size_t.sizeof == 4) 110 | { 111 | // Partial MurmurHash3_x86_32 (no finalization) 112 | enum uint c1 = 0xcc9e2d51; 113 | enum uint c2 = 0x1b873593; 114 | alias h1 = hash; 115 | foreach (x; key) 116 | { 117 | auto k1 = x * c1; 118 | k1 = (k1 << 15) | (k1 >> (32 - 15)); 119 | k1 *= c2; 120 | 121 | h1 ^= k1; 122 | h1 = (h1 << 13) | (h1 >> (32 - 13)); 123 | h1 = h1 * 5 + 0xe6546b64; 124 | } 125 | } 126 | else static if (size_t.sizeof == 8) 127 | { 128 | // Partial 64-bit MurmurHash64A (no finalization) 129 | alias h = hash; 130 | enum ulong m = 0xc6a4a7935bd1e995UL; 131 | foreach (x; key) 132 | { 133 | auto k = x * m; 134 | k ^= k >>> 47; 135 | k *= m; 136 | 137 | h ^= k; 138 | h *= m; 139 | } 140 | } 141 | else static if (size_t.sizeof == 16) 142 | { 143 | // Partial MurmurHash3_x64_128 (no finalization) 144 | // treating each size_t as a pair of ulong. 145 | ulong h1 = cast(ulong) hash; 146 | ulong h2 = cast(ulong) (hash >> 64); 147 | 148 | enum ulong c1 = 0x87c37b91114253d5UL; 149 | enum ulong c2 = 0x4cf5ad432745937fUL; 150 | 151 | foreach (x; key) 152 | { 153 | auto k1 = cast(ulong) x; 154 | auto k2 = cast(ulong) (x >> 64); 155 | 156 | k1 *= c1; k1 = (k1 << 32) | (k1 >> (64 - 31)); k1 *= c2; h1 ^= k1; 157 | h1 = (h1 << 27) | (h1 >> (64 - 27)); h1 += h2; h1 = h1*5+0x52dce729; 158 | k2 *= c2; k2 = (k2 << 33) | (k2 >> (64 - 33)); k2 *= c1; h2 ^= k2; 159 | h2 = (h2 << 31) | (h2 >> (64 - 31)); h2 += h1; h2 = h2*5+0x38495ab5; 160 | } 161 | 162 | hash = cast(size_t) h1 + ((cast(size_t) h2) << 64); 163 | } 164 | else 165 | { 166 | static assert(0, "Hash length must be either 32, 64, or 128 bits."); 167 | } 168 | } 169 | 170 | static if (size_t.sizeof != uint.sizeof) 171 | void add(scope const uint[] key) @nogc nothrow pure @trusted 172 | { 173 | static if (size_t.sizeof == 8) 174 | { 175 | // Partial 32-bit MurmurHash64B (no finalization) 176 | enum uint m = 0x5bd1e995; 177 | enum r = 24; 178 | 179 | uint h1 = cast(uint) hash; 180 | uint h2 = cast(uint) (hash >> 32); 181 | const(uint)* data = key.ptr; 182 | auto len = key.length; 183 | 184 | for (; len >= 2; data += 2, len -= 2) 185 | { 186 | uint k1 = data[0]; 187 | k1 *= m; k1 ^= k1 >> r; k1 *= m; 188 | h1 *= m; h1 ^= k1; 189 | 190 | uint k2 = data[1]; 191 | k2 *= m; k2 ^= k2 >> r; k2 *= m; 192 | h2 *= m; h2 ^= k2; 193 | } 194 | if (len) 195 | { 196 | uint k1 = data[0]; 197 | k1 *= m; k1 ^= k1 >> r; k1 *= m; 198 | h1 *= m; h1 ^= k1; 199 | } 200 | hash = cast(ulong) h1 + ((cast(ulong) h2) << 32); 201 | } 202 | else static if (size_t.sizeof == 16) 203 | { 204 | // Partial MurmurHash3_x86_128 (no finalization) 205 | enum uint c1 = 0x239b961b; 206 | enum uint c2 = 0xab0e9789; 207 | enum uint c3 = 0x38b34ae5; 208 | enum uint c4 = 0xa1e38b93; 209 | 210 | uint h1 = cast(uint) hash; 211 | uint h2 = cast(uint) (hash >> 32); 212 | uint h3 = cast(uint) (hash >> 64); 213 | uint h4 = cast(uint) (hash >> 96); 214 | const(uint)* data = key.ptr; 215 | auto len = key.length; 216 | 217 | for (; len >= 4; data += 4, len -= 4) 218 | { 219 | uint k1 = data[0]; 220 | uint k2 = data[1]; 221 | uint k3 = data[2]; 222 | uint k4 = data[3]; 223 | 224 | h1 = (h1 << 19) | (h1 >> (32 - 19)); h1 += h2; h1 = h1*5+0x561ccd1b; 225 | k2 *= c2; k2 = (k2 << 16) | (k2 >> (32 - 16)); k2 *= c3; h2 ^= k2; 226 | h2 = (h2 << 17) | (h2 >> (32 - 17)); h2 += h3; h2 = h2*5+0x0bcaa747; 227 | k3 *= c3; k3 = (k3 << 17) | (k3 >> (32 - 17)); k3 *= c4; h3 ^= k3; 228 | h3 = (h3 << 15) | (h3 >> (32 - 15)); h3 += h4; h3 = h3*5+0x96cd1c35; 229 | k4 *= c4; k4 = (k4 << 18) | (k4 >> (32 - 18)); k4 *= c1; h4 ^= k4; 230 | h4 = (h4 << 13) | (h4 >> (32 - 13)); h4 += h1; h4 = h4*5+0x32ac3b17; 231 | } 232 | uint k1, k2, k3; 233 | switch (len) // 0, 1, 2, 3 234 | { 235 | case 3: 236 | k3 = data[2]; 237 | k3 *= c3; k3 = (k3 << 17) | (k3 >> (32 - 17)); k3 *= c4; h3 ^= k3; 238 | goto case; 239 | case 2: 240 | k2 = data[1]; 241 | k2 *= c2; k2 = (k2 << 16) | (k2 >> (32 - 16)); k2 *= c3; h2 ^= k2; 242 | goto case; 243 | case 1: 244 | k1 = data[0]; 245 | k1 *= c1; k1 = (k1 << 15) | (k1 >> (32 - 15)); k1 *= c2; h1 ^= k1; 246 | break; 247 | } 248 | hash = cast(size_t) h1 + 249 | ((cast(size_t) h2) << 32) + 250 | ((cast(size_t) h3) << 64) + 251 | ((cast(size_t) h4) << 96); 252 | } 253 | else 254 | { 255 | static assert(0, "Hash length must be either 32, 64, or 128 bits."); 256 | } 257 | } 258 | } 259 | 260 | /** 261 | * Takes an argument of an arbitrary type $(D_PARAM T) and calculates the hash 262 | * value. 263 | * 264 | * Hash calculation is supported for all scalar types. Aggregate types, like 265 | * $(D_KEYWORD struct)s, should implement `toHash`-function: 266 | * --- 267 | * size_t toHash() const 268 | * { 269 | * return hash; 270 | * } 271 | * --- 272 | * 273 | * For pointers and for scalar types implicitly convertible to `size_t` this 274 | * is an identity operation (i.e. the value is cast to `size_t` and returned 275 | * unaltered). Integer types wider than `size_t` are XOR folded down to 276 | * `size_t`. Other scalar types use an architecture-dependent hash function 277 | * based on their width and alignment. 278 | * If the type provides a `toHash`-function, only `toHash()` is called and its 279 | * result is returned. 280 | * 281 | * This function also accepts input ranges that contain hashable elements. 282 | * Individual values are combined then and the resulting hash is returned. 283 | * 284 | * Params: 285 | * T = Hashable type. 286 | * key = Hashable value. 287 | * 288 | * Returns: Calculated hash value. 289 | * 290 | * See_Also: $(LINK http://www.isthe.com/chongo/tech/comp/fnv/). 291 | */ 292 | size_t hash(T)(auto ref T key) 293 | { 294 | static if (is(typeof(key.toHash()) == size_t)) 295 | { 296 | return key.toHash(); 297 | } 298 | else static if ((isIntegral!T || isSomeChar!T || isBoolean!T) 299 | && T.sizeof <= size_t.sizeof) 300 | { 301 | return cast(size_t) key; 302 | } 303 | else static if (isIntegral!T && T.sizeof > size_t.sizeof) 304 | { 305 | return cast(size_t) (key ^ (key >>> (size_t.sizeof * 8))); 306 | } 307 | else static if (isPointer!T || is(T : typeof(null))) 308 | { 309 | return (() @trusted => cast(size_t) key)(); 310 | } 311 | else 312 | { 313 | Hasher hasher; 314 | hasher(key); 315 | return hasher.hash; 316 | } 317 | } 318 | 319 | /** 320 | * Determines whether $(D_PARAM hasher) is hash function for $(D_PARAM T), i.e. 321 | * it is callable with a value of type $(D_PARAM T) and returns a 322 | * $(D_PSYMBOL size_t) value. 323 | * 324 | * Params: 325 | * hasher = Hash function candidate. 326 | * T = Type to test the hash function with. 327 | * 328 | * Returns: $(D_KEYWORD true) if $(D_PARAM hasher) is a hash function for 329 | * $(D_PARAM T), $(D_KEYWORD false) otherwise. 330 | */ 331 | template isHashFunction(alias hasher, T) 332 | { 333 | private alias wrapper = (T x) => hasher(x); 334 | enum bool isHashFunction = is(typeof(wrapper(T.init)) == size_t); 335 | } 336 | 337 | /// 338 | @nogc nothrow pure @safe unittest 339 | { 340 | static assert(isHashFunction!(hash, int)); 341 | } 342 | -------------------------------------------------------------------------------- /source/tanya/hash/package.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | /** 6 | * Copyright: Eugene Wissner 2018-2020. 7 | * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, 8 | * Mozilla Public License, v. 2.0). 9 | * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) 10 | * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/hash/package.d, 11 | * tanya/hash/package.d) 12 | */ 13 | module tanya.hash; 14 | 15 | public import tanya.hash.lookup; 16 | -------------------------------------------------------------------------------- /source/tanya/math/random.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | /** 6 | * Random number generator. 7 | * 8 | * Copyright: Eugene Wissner 2016-2022. 9 | * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, 10 | * Mozilla Public License, v. 2.0). 11 | * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) 12 | * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/math/random.d, 13 | * tanya/math/random.d) 14 | */ 15 | module tanya.math.random; 16 | 17 | import std.typecons; 18 | import tanya.memory.allocator; 19 | 20 | /// Maximum amount gathered from the entropy sources. 21 | enum maxGather = 128; 22 | 23 | /** 24 | * Exception thrown if random number generating fails. 25 | */ 26 | class EntropyException : Exception 27 | { 28 | /** 29 | * Params: 30 | * msg = Message to output. 31 | * file = The file where the exception occurred. 32 | * line = The line number where the exception occurred. 33 | * next = The previous exception in the chain of exceptions, if any. 34 | */ 35 | this(string msg, 36 | string file = __FILE__, 37 | size_t line = __LINE__, 38 | Throwable next = null) const @nogc nothrow pure @safe 39 | { 40 | super(msg, file, line, next); 41 | } 42 | } 43 | 44 | /** 45 | * Interface for implementing entropy sources. 46 | */ 47 | abstract class EntropySource 48 | { 49 | /// Amount of already generated entropy. 50 | protected ushort size_; 51 | 52 | /** 53 | * Returns: Minimum bytes required from the entropy source. 54 | */ 55 | @property ubyte threshold() const @nogc nothrow pure @safe; 56 | 57 | /** 58 | * Returns: Whether this entropy source is strong. 59 | */ 60 | @property bool strong() const @nogc nothrow pure @safe; 61 | 62 | /** 63 | * Returns: Amount of already generated entropy. 64 | */ 65 | @property ushort size() const @nogc nothrow pure @safe 66 | { 67 | return size_; 68 | } 69 | 70 | /** 71 | * Params: 72 | * size = Amount of already generated entropy. Cannot be smaller than the 73 | * already set value. 74 | */ 75 | @property void size(ushort size) @nogc nothrow pure @safe 76 | { 77 | size_ = size; 78 | } 79 | 80 | /** 81 | * Poll the entropy source. 82 | * 83 | * Params: 84 | * output = Buffer to save the generate random sequence (the method will 85 | * to fill the buffer). 86 | * 87 | * Returns: Number of bytes that were copied to the $(D_PARAM output) 88 | * or nothing on error. 89 | * 90 | * Postcondition: Returned length is less than or equal to 91 | * $(D_PARAM output) length. 92 | */ 93 | Nullable!ubyte poll(out ubyte[maxGather] output) @nogc; 94 | } 95 | 96 | version (CRuntime_Bionic) 97 | { 98 | version = SecureARC4Random; 99 | } 100 | else version (OSX) 101 | { 102 | version = SecureARC4Random; 103 | } 104 | else version (OpenBSD) 105 | { 106 | version = SecureARC4Random; 107 | } 108 | else version (NetBSD) 109 | { 110 | version = SecureARC4Random; 111 | } 112 | else version (Solaris) 113 | { 114 | version = SecureARC4Random; 115 | } 116 | 117 | version (linux) 118 | { 119 | import core.stdc.config : c_long; 120 | private extern(C) c_long syscall(c_long number, ...) @nogc nothrow @system; 121 | 122 | /** 123 | * Uses getrandom system call. 124 | */ 125 | class PlatformEntropySource : EntropySource 126 | { 127 | /** 128 | * Returns: Minimum bytes required from the entropy source. 129 | */ 130 | override @property ubyte threshold() const @nogc nothrow pure @safe 131 | { 132 | return 32; 133 | } 134 | 135 | /** 136 | * Returns: Whether this entropy source is strong. 137 | */ 138 | override @property bool strong() const @nogc nothrow pure @safe 139 | { 140 | return true; 141 | } 142 | 143 | /** 144 | * Poll the entropy source. 145 | * 146 | * Params: 147 | * output = Buffer to save the generate random sequence (the method will 148 | * to fill the buffer). 149 | * 150 | * Returns: Number of bytes that were copied to the $(D_PARAM output) 151 | * or nothing on error. 152 | */ 153 | override Nullable!ubyte poll(out ubyte[maxGather] output) @nogc nothrow 154 | out (length) 155 | { 156 | assert(length.isNull || length.get <= maxGather); 157 | } 158 | do 159 | { 160 | // int getrandom(void *buf, size_t buflen, unsigned int flags); 161 | import mir.linux._asm.unistd : NR_getrandom; 162 | auto length = syscall(NR_getrandom, output.ptr, output.length, 0); 163 | Nullable!ubyte ret; 164 | 165 | if (length >= 0) 166 | { 167 | ret = cast(ubyte) length; 168 | } 169 | return ret; 170 | } 171 | } 172 | } 173 | else version (SecureARC4Random) 174 | { 175 | private extern(C) void arc4random_buf(scope void* buf, size_t nbytes) 176 | @nogc nothrow @system; 177 | 178 | /** 179 | * Uses arc4random_buf. 180 | */ 181 | class PlatformEntropySource : EntropySource 182 | { 183 | /** 184 | * Returns: Minimum bytes required from the entropy source. 185 | */ 186 | override @property ubyte threshold() const @nogc nothrow pure @safe 187 | { 188 | return 32; 189 | } 190 | 191 | /** 192 | * Returns: Whether this entropy source is strong. 193 | */ 194 | override @property bool strong() const @nogc nothrow pure @safe 195 | { 196 | return true; 197 | } 198 | 199 | /** 200 | * Poll the entropy source. 201 | * 202 | * Params: 203 | * output = Buffer to save the generate random sequence (the method will 204 | * to fill the buffer). 205 | * 206 | * Returns: Number of bytes that were copied to the $(D_PARAM output) 207 | * or nothing on error. 208 | */ 209 | override Nullable!ubyte poll(out ubyte[maxGather] output) 210 | @nogc nothrow @safe 211 | out (length) 212 | { 213 | assert(length.isNull || length.get <= maxGather); 214 | } 215 | do 216 | { 217 | (() @trusted => arc4random_buf(output.ptr, output.length))(); 218 | return Nullable!ubyte(cast(ubyte) (output.length)); 219 | } 220 | } 221 | } 222 | else version (Windows) 223 | { 224 | import core.sys.windows.basetsd : ULONG_PTR; 225 | import core.sys.windows.winbase : GetLastError; 226 | import core.sys.windows.wincrypt; 227 | import core.sys.windows.windef : BOOL, DWORD, PBYTE; 228 | import core.sys.windows.winerror : NTE_BAD_KEYSET; 229 | import core.sys.windows.winnt : LPCSTR, LPCWSTR; 230 | 231 | private extern(Windows) @nogc nothrow 232 | { 233 | BOOL CryptGenRandom(HCRYPTPROV, DWORD, PBYTE); 234 | BOOL CryptAcquireContextA(HCRYPTPROV*, LPCSTR, LPCSTR, DWORD, DWORD); 235 | BOOL CryptAcquireContextW(HCRYPTPROV*, LPCWSTR, LPCWSTR, DWORD, DWORD); 236 | BOOL CryptReleaseContext(HCRYPTPROV, ULONG_PTR); 237 | } 238 | 239 | private bool initCryptGenRandom(scope ref HCRYPTPROV hProvider) 240 | @nogc nothrow @trusted 241 | { 242 | // https://msdn.microsoft.com/en-us/library/windows/desktop/aa379886(v=vs.85).aspx 243 | // For performance reasons, we recommend that you set the pszContainer 244 | // parameter to NULL and the dwFlags parameter to CRYPT_VERIFYCONTEXT 245 | // in all situations where you do not require a persisted key. 246 | // CRYPT_SILENT is intended for use with applications for which the UI 247 | // cannot be displayed by the CSP. 248 | if (!CryptAcquireContextW(&hProvider, 249 | null, 250 | null, 251 | PROV_RSA_FULL, 252 | CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) 253 | { 254 | if (GetLastError() != NTE_BAD_KEYSET) 255 | { 256 | return false; 257 | } 258 | // Attempt to create default container 259 | if (!CryptAcquireContextA(&hProvider, 260 | null, 261 | null, 262 | PROV_RSA_FULL, 263 | CRYPT_NEWKEYSET | CRYPT_SILENT)) 264 | { 265 | return false; 266 | } 267 | } 268 | 269 | return true; 270 | } 271 | 272 | class PlatformEntropySource : EntropySource 273 | { 274 | private HCRYPTPROV hProvider; 275 | 276 | /** 277 | * Uses CryptGenRandom. 278 | */ 279 | this() @nogc 280 | { 281 | if (!initCryptGenRandom(hProvider)) 282 | { 283 | throw defaultAllocator.make!EntropyException("CryptAcquireContextW failed."); 284 | } 285 | assert(hProvider > 0, "hProvider not properly initialized."); 286 | } 287 | 288 | ~this() @nogc nothrow @safe 289 | { 290 | if (hProvider > 0) 291 | { 292 | (() @trusted => CryptReleaseContext(hProvider, 0))(); 293 | } 294 | } 295 | 296 | /** 297 | * Returns: Minimum bytes required from the entropy source. 298 | */ 299 | override @property ubyte threshold() const @nogc nothrow pure @safe 300 | { 301 | return 32; 302 | } 303 | 304 | /** 305 | * Returns: Whether this entropy source is strong. 306 | */ 307 | override @property bool strong() const @nogc nothrow pure @safe 308 | { 309 | return true; 310 | } 311 | 312 | /** 313 | * Poll the entropy source. 314 | * 315 | * Params: 316 | * output = Buffer to save the generate random sequence (the method will 317 | * to fill the buffer). 318 | * 319 | * Returns: Number of bytes that were copied to the $(D_PARAM output) 320 | * or nothing on error. 321 | */ 322 | override Nullable!ubyte poll(out ubyte[maxGather] output) 323 | @nogc nothrow @safe 324 | out (length) 325 | { 326 | assert(length.isNull || length.get <= maxGather); 327 | } 328 | do 329 | { 330 | Nullable!ubyte ret; 331 | 332 | assert(hProvider > 0, "hProvider not properly initialized"); 333 | if ((() @trusted => CryptGenRandom(hProvider, output.length, cast(PBYTE) output.ptr))()) 334 | { 335 | ret = cast(ubyte) (output.length); 336 | } 337 | return ret; 338 | } 339 | } 340 | } 341 | -------------------------------------------------------------------------------- /source/tanya/net/iface.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | /** 6 | * Network interfaces. 7 | * 8 | * Copyright: Eugene Wissner 2018-2020. 9 | * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, 10 | * Mozilla Public License, v. 2.0). 11 | * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) 12 | * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/net/iface.d, 13 | * tanya/net/iface.d) 14 | */ 15 | module tanya.net.iface; 16 | 17 | import tanya.algorithm.mutation; 18 | import tanya.container.string; 19 | import tanya.meta.trait; 20 | import tanya.meta.transform; 21 | import tanya.range; 22 | 23 | version (Windows) 24 | { 25 | private union NET_LUID_LH { ulong Value, Info; } 26 | private alias NET_LUID = NET_LUID_LH; 27 | private alias NET_IFINDEX = uint; 28 | private enum IF_MAX_STRING_SIZE = 256; 29 | extern(Windows) @nogc nothrow private @system 30 | { 31 | uint ConvertInterfaceNameToLuidA(const(char)* InterfaceName, 32 | NET_LUID* InterfaceLuid); 33 | uint ConvertInterfaceLuidToIndex(const(NET_LUID)* InterfaceLuid, 34 | NET_IFINDEX* InterfaceIndex); 35 | uint ConvertInterfaceIndexToLuid(NET_IFINDEX InterfaceIndex, 36 | NET_LUID* InterfaceLuid); 37 | uint ConvertInterfaceLuidToNameA(const(NET_LUID)* InterfaceLuid, 38 | char* InterfaceName, 39 | size_t Length); 40 | } 41 | } 42 | else version (Posix) 43 | { 44 | import core.sys.posix.net.if_; 45 | } 46 | 47 | /** 48 | * Converts the name of a network interface to its index. 49 | * 50 | * If an interface with the name $(D_PARAM name) cannot be found or another 51 | * error occurres, returns 0. 52 | * 53 | * Params: 54 | * name = Interface name. 55 | * 56 | * Returns: Returns interface index or 0. 57 | */ 58 | uint nameToIndex(R)(R name) @trusted 59 | if (isInputRange!R && is(Unqual!(ElementType!R) == char) && hasLength!R) 60 | { 61 | version (Windows) 62 | { 63 | if (name.length > IF_MAX_STRING_SIZE) 64 | { 65 | return 0; 66 | } 67 | char[IF_MAX_STRING_SIZE + 1] buffer; 68 | NET_LUID luid; 69 | 70 | copy(name, buffer[]); 71 | buffer[name.length] = '\0'; 72 | 73 | if (ConvertInterfaceNameToLuidA(buffer.ptr, &luid) != 0) 74 | { 75 | return 0; 76 | } 77 | NET_IFINDEX index; 78 | if (ConvertInterfaceLuidToIndex(&luid, &index) == 0) 79 | { 80 | return index; 81 | } 82 | return 0; 83 | } 84 | else version (Posix) 85 | { 86 | if (name.length >= IF_NAMESIZE) 87 | { 88 | return 0; 89 | } 90 | char[IF_NAMESIZE] buffer; 91 | 92 | copy(name, buffer[]); 93 | buffer[name.length] = '\0'; 94 | 95 | return if_nametoindex(buffer.ptr); 96 | } 97 | } 98 | 99 | /// 100 | @nogc nothrow @safe unittest 101 | { 102 | version (linux) 103 | { 104 | assert(nameToIndex("lo") == 1); 105 | } 106 | else version (Windows) 107 | { 108 | assert(nameToIndex("loopback_0") == 1); 109 | } 110 | else 111 | { 112 | assert(nameToIndex("lo0") == 1); 113 | } 114 | assert(nameToIndex("ecafretni") == 0); 115 | } 116 | 117 | /** 118 | * Converts the index of a network interface to its name. 119 | * 120 | * If an interface with the $(D_PARAM index) cannot be found or another 121 | * error occurres, returns an empty $(D_PSYMBOL String). 122 | * 123 | * Params: 124 | * index = Interface index. 125 | * 126 | * Returns: Returns interface name or an empty $(D_PSYMBOL String). 127 | */ 128 | String indexToName(uint index) @nogc nothrow @trusted 129 | { 130 | import tanya.memory.op : findNullTerminated; 131 | 132 | version (Windows) 133 | { 134 | NET_LUID luid; 135 | if (ConvertInterfaceIndexToLuid(index, &luid) != 0) 136 | { 137 | return String(); 138 | } 139 | 140 | char[IF_MAX_STRING_SIZE + 1] buffer; 141 | if (ConvertInterfaceLuidToNameA(&luid, 142 | buffer.ptr, 143 | IF_MAX_STRING_SIZE + 1) != 0) 144 | { 145 | return String(); 146 | } 147 | return String(findNullTerminated(buffer)); 148 | } 149 | else version (Posix) 150 | { 151 | char[IF_NAMESIZE] buffer; 152 | if (if_indextoname(index, buffer.ptr) is null) 153 | { 154 | return String(); 155 | } 156 | return String(findNullTerminated(buffer)); 157 | } 158 | } 159 | 160 | /** 161 | * $(D_PSYMBOL AddressFamily) specifies a communication domain; this selects 162 | * the protocol family which will be used for communication. 163 | */ 164 | enum AddressFamily : int 165 | { 166 | unspec = 0, /// Unspecified. 167 | local = 1, /// Local to host (pipes and file-domain). 168 | unix = local, /// POSIX name for PF_LOCAL. 169 | inet = 2, /// IP protocol family. 170 | ax25 = 3, /// Amateur Radio AX.25. 171 | ipx = 4, /// Novell Internet Protocol. 172 | appletalk = 5, /// Appletalk DDP. 173 | netrom = 6, /// Amateur radio NetROM. 174 | bridge = 7, /// Multiprotocol bridge. 175 | atmpvc = 8, /// ATM PVCs. 176 | x25 = 9, /// Reserved for X.25 project. 177 | inet6 = 10, /// IP version 6. 178 | } 179 | -------------------------------------------------------------------------------- /source/tanya/net/inet.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | /** 6 | * Internet utilities. 7 | * 8 | * Copyright: Eugene Wissner 2016-2020. 9 | * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, 10 | * Mozilla Public License, v. 2.0). 11 | * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) 12 | * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/net/inet.d, 13 | * tanya/net/inet.d) 14 | */ 15 | module tanya.net.inet; 16 | 17 | import tanya.meta.trait; 18 | import tanya.meta.transform; 19 | import tanya.range; 20 | 21 | /** 22 | * Represents an unsigned integer as an $(D_KEYWORD ubyte) range. 23 | * 24 | * The range is bidirectional. The byte order is always big-endian. 25 | * 26 | * It can accept any unsigned integral type but the value should fit 27 | * in $(D_PARAM L) bytes. 28 | * 29 | * Params: 30 | * L = Desired range length. 31 | */ 32 | struct NetworkOrder(uint L) 33 | if (L > ubyte.sizeof && L <= ulong.sizeof) 34 | { 35 | static if (L > uint.sizeof) 36 | { 37 | private alias StorageType = ulong; 38 | } 39 | else static if (L > ushort.sizeof) 40 | { 41 | private alias StorageType = uint; 42 | } 43 | else static if (L > ubyte.sizeof) 44 | { 45 | private alias StorageType = ushort; 46 | } 47 | else 48 | { 49 | private alias StorageType = ubyte; 50 | } 51 | 52 | private StorageType value; 53 | private size_t size = L; 54 | 55 | invariant 56 | { 57 | assert(this.size <= L); 58 | } 59 | 60 | /** 61 | * Constructs a new range. 62 | * 63 | * $(D_PARAM T) can be any unsigned type but $(D_PARAM value) cannot be 64 | * larger than the maximum can be stored in $(D_PARAM L) bytes. Otherwise 65 | * an assertion failure will be caused. 66 | * 67 | * Params: 68 | * T = Value type. 69 | * value = The value should be represented by this range. 70 | * 71 | * Precondition: $(D_INLINECODE value <= (2 ^^ (L * 8)) - 1). 72 | */ 73 | this(T)(T value) 74 | if (isUnsigned!T) 75 | in 76 | { 77 | assert(value <= (2 ^^ (L * 8)) - 1); 78 | } 79 | do 80 | { 81 | this.value = value & StorageType.max; 82 | } 83 | 84 | /** 85 | * Returns: LSB. 86 | * 87 | * Precondition: $(D_INLINECODE length > 0). 88 | */ 89 | @property ubyte back() const 90 | in 91 | { 92 | assert(this.length > 0); 93 | } 94 | do 95 | { 96 | return this.value & 0xff; 97 | } 98 | 99 | /** 100 | * Returns: MSB. 101 | * 102 | * Precondition: $(D_INLINECODE length > 0). 103 | */ 104 | @property ubyte front() const 105 | in 106 | { 107 | assert(this.length > 0); 108 | } 109 | do 110 | { 111 | return (this.value >> ((this.length - 1) * 8)) & 0xff; 112 | } 113 | 114 | /** 115 | * Eliminates the LSB. 116 | * 117 | * Precondition: $(D_INLINECODE length > 0). 118 | */ 119 | void popBack() 120 | in 121 | { 122 | assert(this.length > 0); 123 | } 124 | do 125 | { 126 | this.value >>= 8; 127 | --this.size; 128 | } 129 | 130 | /** 131 | * Eliminates the MSB. 132 | * 133 | * Precondition: $(D_INLINECODE length > 0). 134 | */ 135 | void popFront() 136 | in 137 | { 138 | assert(this.length > 0); 139 | } 140 | do 141 | { 142 | this.value &= StorageType.max >> ((StorageType.sizeof - this.length) * 8); 143 | --this.size; 144 | } 145 | 146 | /** 147 | * Returns: Copy of this range. 148 | */ 149 | typeof(this) save() const 150 | { 151 | return this; 152 | } 153 | 154 | /** 155 | * Returns: Whether the range is empty. 156 | */ 157 | @property bool empty() const 158 | { 159 | return this.length == 0; 160 | } 161 | 162 | /** 163 | * Returns: Byte length. 164 | */ 165 | @property size_t length() const 166 | { 167 | return this.size; 168 | } 169 | } 170 | 171 | /// 172 | @nogc nothrow pure @safe unittest 173 | { 174 | auto networkOrder = NetworkOrder!3(0xae34e2u); 175 | assert(!networkOrder.empty); 176 | assert(networkOrder.front == 0xae); 177 | 178 | networkOrder.popFront(); 179 | assert(networkOrder.length == 2); 180 | assert(networkOrder.front == 0x34); 181 | assert(networkOrder.back == 0xe2); 182 | 183 | networkOrder.popBack(); 184 | assert(networkOrder.length == 1); 185 | assert(networkOrder.front == 0x34); 186 | assert(networkOrder.front == 0x34); 187 | 188 | networkOrder.popFront(); 189 | assert(networkOrder.empty); 190 | } 191 | 192 | /** 193 | * Converts the $(D_KEYWORD ubyte) input range $(D_PARAM range) to 194 | * $(D_PARAM T). 195 | * 196 | * The byte order of $(D_PARAM r) is assumed to be big-endian. The length 197 | * cannot be larger than $(D_INLINECODE T.sizeof). Otherwise an assertion 198 | * failure will be caused. 199 | * 200 | * Params: 201 | * T = Desired return type. 202 | * R = Range type. 203 | * range = Input range. 204 | * 205 | * Returns: Integral representation of $(D_PARAM range) with the host byte 206 | * order. 207 | */ 208 | T toHostOrder(T = size_t, R)(R range) 209 | if (isInputRange!R 210 | && !isInfinite!R 211 | && is(Unqual!(ElementType!R) == ubyte) 212 | && isUnsigned!T) 213 | { 214 | T ret; 215 | ushort pos = T.sizeof * 8; 216 | 217 | for (; !range.empty && range.front == 0; pos -= 8, range.popFront()) 218 | { 219 | } 220 | for (; !range.empty; range.popFront()) 221 | { 222 | assert(pos != 0); 223 | pos -= 8; 224 | ret |= (cast(T) range.front) << pos; 225 | } 226 | 227 | return ret >> pos; 228 | } 229 | 230 | /// 231 | @nogc nothrow pure @safe unittest 232 | { 233 | const value = 0xae34e2u; 234 | auto networkOrder = NetworkOrder!4(value); 235 | assert(networkOrder.toHostOrder() == value); 236 | } 237 | -------------------------------------------------------------------------------- /source/tanya/net/package.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | /** 6 | * Network programming. 7 | * 8 | * Copyright: Eugene Wissner 2017-2022. 9 | * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, 10 | * Mozilla Public License, v. 2.0). 11 | * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) 12 | * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/net/package.d, 13 | * tanya/net/package.d) 14 | */ 15 | module tanya.net; 16 | 17 | public import tanya.net.iface; 18 | public import tanya.net.inet; 19 | public import tanya.net.ip; 20 | public import tanya.net.uri; 21 | -------------------------------------------------------------------------------- /source/tanya/net/uri.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | /** 6 | * URL parser. 7 | * 8 | * Copyright: Eugene Wissner 2017-2020. 9 | * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, 10 | * Mozilla Public License, v. 2.0). 11 | * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) 12 | * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/net/uri.d, 13 | * tanya/net/uri.d) 14 | */ 15 | module tanya.net.uri; 16 | 17 | import std.ascii; 18 | import tanya.conv; 19 | import tanya.memory.allocator; 20 | 21 | /** 22 | * Thrown if an invalid URI was specified. 23 | */ 24 | final class URIException : Exception 25 | { 26 | /** 27 | * Params: 28 | * msg = The message for the exception. 29 | * file = The file where the exception occurred. 30 | * line = The line number where the exception occurred. 31 | * next = The previous exception in the chain of exceptions, if any. 32 | */ 33 | this(string msg, 34 | string file = __FILE__, 35 | size_t line = __LINE__, 36 | Throwable next = null) @nogc nothrow pure @safe 37 | { 38 | super(msg, file, line, next); 39 | } 40 | } 41 | 42 | /** 43 | * A Unique Resource Locator. 44 | */ 45 | struct URL 46 | { 47 | /// The URL scheme. 48 | const(char)[] scheme; 49 | 50 | /// The username. 51 | const(char)[] user; 52 | 53 | /// The password. 54 | const(char)[] pass; 55 | 56 | /// The hostname. 57 | const(char)[] host; 58 | 59 | /// The port number. 60 | ushort port; 61 | 62 | /// The path. 63 | const(char)[] path; 64 | 65 | /// The query string. 66 | const(char)[] query; 67 | 68 | /// The anchor. 69 | const(char)[] fragment; 70 | 71 | /** 72 | * Attempts to parse an URL from a string. 73 | * Output string data (scheme, user, etc.) are just slices of input string 74 | * (i.e., no memory allocation and copying). 75 | * 76 | * Params: 77 | * source = The string containing the URL. 78 | * 79 | * Throws: $(D_PSYMBOL URIException) if the URL is malformed. 80 | */ 81 | this(const char[] source) @nogc pure 82 | { 83 | ptrdiff_t pos = -1, endPos = source.length, start; 84 | 85 | foreach (i, ref c; source) 86 | { 87 | if (pos == -1 && c == ':') 88 | { 89 | pos = i; 90 | } 91 | if (endPos == source.length && (c == '?' || c == '#')) 92 | { 93 | endPos = i; 94 | } 95 | } 96 | 97 | // Check if the colon is a part of the scheme or the port and parse 98 | // the appropriate part. 99 | if (source.length > 1 && source[0] == '/' && source[1] == '/') 100 | { 101 | // Relative scheme. 102 | start = 2; 103 | } 104 | else if (pos > 0) 105 | { 106 | // Validate scheme: 107 | // [ toLower(alpha) | digit | "+" | "-" | "." ] 108 | foreach (ref c; source[0 .. pos]) 109 | { 110 | if (!c.isAlphaNum && c != '+' && c != '-' && c != '.') 111 | { 112 | goto ParsePath; 113 | } 114 | } 115 | 116 | if (source.length == pos + 1) // only "scheme:" is available. 117 | { 118 | this.scheme = source[0 .. $ - 1]; 119 | return; 120 | } 121 | else if (source.length > pos + 1 && source[pos + 1] == '/') 122 | { 123 | this.scheme = source[0 .. pos]; 124 | 125 | if (source.length > pos + 2 && source[pos + 2] == '/') 126 | { 127 | start = pos + 3; 128 | 129 | if (source.length <= start) 130 | { 131 | // Only "scheme://" is available. 132 | return; 133 | } 134 | if (this.scheme == "file" && source[start] == '/') 135 | { 136 | // Windows drive letters. 137 | if (source.length - start > 2 138 | && source[start + 2] == ':') 139 | { 140 | ++start; 141 | } 142 | goto ParsePath; 143 | } 144 | } 145 | else 146 | { 147 | start = pos + 1; 148 | goto ParsePath; 149 | } 150 | } 151 | else if (!parsePort(source[pos .. $])) 152 | { 153 | // Schemas like mailto: and zlib: may not have any slash after 154 | // them. 155 | this.scheme = source[0 .. pos]; 156 | start = pos + 1; 157 | goto ParsePath; 158 | } 159 | } 160 | else if (pos == 0 && parsePort(source[pos .. $])) 161 | { 162 | // An URL shouldn't begin with a port number. 163 | throw defaultAllocator.make!URIException("URL begins with port"); 164 | } 165 | else 166 | { 167 | goto ParsePath; 168 | } 169 | 170 | // Parse host. 171 | pos = -1; 172 | for (ptrdiff_t i = start; i < source.length; ++i) 173 | { 174 | if (source[i] == '@') 175 | { 176 | pos = i; 177 | } 178 | else if (source[i] == '/') 179 | { 180 | endPos = i; 181 | break; 182 | } 183 | } 184 | 185 | // Check for login and password. 186 | if (pos != -1) 187 | { 188 | // *( unreserved / pct-encoded / sub-delims / ":" ) 189 | foreach (i, c; source[start .. pos]) 190 | { 191 | if (c == ':') 192 | { 193 | if (this.user is null) 194 | { 195 | this.user = source[start .. start + i]; 196 | this.pass = source[start + i + 1 .. pos]; 197 | } 198 | } 199 | else if (!c.isAlpha() && 200 | !c.isDigit() && 201 | c != '!' && 202 | c != ';' && 203 | c != '=' && 204 | c != '_' && 205 | c != '~' && 206 | !(c >= '$' && c <= '.')) 207 | { 208 | this.scheme = this.user = this.pass = null; 209 | throw make!URIException(defaultAllocator, 210 | "Restricted characters in user information"); 211 | } 212 | } 213 | if (this.user is null) 214 | { 215 | this.user = source[start .. pos]; 216 | } 217 | 218 | start = ++pos; 219 | } 220 | 221 | pos = endPos; 222 | if (endPos <= 1 || source[start] != '[' || source[endPos - 1] != ']') 223 | { 224 | // Short circuit portscan. 225 | // IPv6 embedded address. 226 | for (ptrdiff_t i = endPos - 1; i >= start; --i) 227 | { 228 | if (source[i] == ':') 229 | { 230 | pos = i; 231 | if (this.port == 0 && !parsePort(source[i .. endPos])) 232 | { 233 | this.scheme = this.user = this.pass = null; 234 | throw defaultAllocator.make!URIException("Invalid port"); 235 | } 236 | break; 237 | } 238 | } 239 | } 240 | 241 | // Check if we have a valid host, if we don't reject the string as URL. 242 | if (pos <= start) 243 | { 244 | this.scheme = this.user = this.pass = null; 245 | throw defaultAllocator.make!URIException("Invalid host"); 246 | } 247 | 248 | this.host = source[start .. pos]; 249 | 250 | if (endPos == source.length) 251 | { 252 | return; 253 | } 254 | 255 | start = endPos; 256 | 257 | ParsePath: 258 | endPos = source.length; 259 | pos = -1; 260 | foreach (i, ref c; source[start .. $]) 261 | { 262 | if (c == '?' && pos == -1) 263 | { 264 | pos = start + i; 265 | } 266 | else if (c == '#') 267 | { 268 | endPos = start + i; 269 | break; 270 | } 271 | } 272 | if (pos == -1) 273 | { 274 | pos = endPos; 275 | } 276 | 277 | if (pos > start) 278 | { 279 | this.path = source[start .. pos]; 280 | } 281 | if (endPos >= ++pos) 282 | { 283 | this.query = source[pos .. endPos]; 284 | } 285 | if (++endPos <= source.length) 286 | { 287 | this.fragment = source[endPos .. $]; 288 | } 289 | } 290 | 291 | /* 292 | * Attempts to parse and set the port. 293 | * 294 | * Params: 295 | * port = String beginning with a colon followed by the port number and 296 | * an optional path (query string and/or fragment), like: 297 | * `:12345/some_path` or `:12345`. 298 | * 299 | * Returns: Whether the port could be found. 300 | */ 301 | private bool parsePort(const(char)[] port) @nogc nothrow pure @safe 302 | { 303 | auto unparsed = port[1 .. $]; 304 | auto parsed = readIntegral!ushort(unparsed); 305 | if (unparsed.length == 0 || unparsed[0] == '/') 306 | { 307 | this.port = parsed; 308 | return true; 309 | } 310 | return false; 311 | } 312 | } 313 | 314 | /// 315 | @nogc pure @system unittest 316 | { 317 | auto u = URL("example.org"); 318 | assert(u.path == "example.org"); 319 | 320 | u = URL("relative/path"); 321 | assert(u.path == "relative/path"); 322 | 323 | // Host and scheme 324 | u = URL("https://example.org"); 325 | assert(u.scheme == "https"); 326 | assert(u.host == "example.org"); 327 | assert(u.path is null); 328 | assert(u.port == 0); 329 | assert(u.fragment is null); 330 | 331 | // With user and port and path 332 | u = URL("https://hilary:putnam@example.org:443/foo/bar"); 333 | assert(u.scheme == "https"); 334 | assert(u.host == "example.org"); 335 | assert(u.path == "/foo/bar"); 336 | assert(u.port == 443); 337 | assert(u.user == "hilary"); 338 | assert(u.pass == "putnam"); 339 | assert(u.fragment is null); 340 | 341 | // With query string 342 | u = URL("https://example.org/?login=true"); 343 | assert(u.scheme == "https"); 344 | assert(u.host == "example.org"); 345 | assert(u.path == "/"); 346 | assert(u.query == "login=true"); 347 | assert(u.fragment is null); 348 | 349 | // With query string and fragment 350 | u = URL("https://example.org/?login=false#label"); 351 | assert(u.scheme == "https"); 352 | assert(u.host == "example.org"); 353 | assert(u.path == "/"); 354 | assert(u.query == "login=false"); 355 | assert(u.fragment == "label"); 356 | 357 | u = URL("redis://root:password@localhost:2201/path?query=value#fragment"); 358 | assert(u.scheme == "redis"); 359 | assert(u.user == "root"); 360 | assert(u.pass == "password"); 361 | assert(u.host == "localhost"); 362 | assert(u.port == 2201); 363 | assert(u.path == "/path"); 364 | assert(u.query == "query=value"); 365 | assert(u.fragment == "fragment"); 366 | } 367 | 368 | /** 369 | * Attempts to parse an URL from a string and returns the specified component 370 | * of the URL or $(D_PSYMBOL URL) if no component is specified. 371 | * 372 | * Params: 373 | * T = "scheme", "host", "port", "user", "pass", "path", "query", 374 | * "fragment". 375 | * source = The string containing the URL. 376 | * 377 | * Returns: Requested URL component. 378 | */ 379 | auto parseURL(string T)(const char[] source) 380 | if (T == "scheme" 381 | || T == "host" 382 | || T == "user" 383 | || T == "pass" 384 | || T == "path" 385 | || T == "query" 386 | || T == "fragment" 387 | || T == "port") 388 | { 389 | auto ret = URL(source); 390 | return mixin("ret." ~ T); 391 | } 392 | 393 | /// ditto 394 | URL parseURL(const char[] source) @nogc pure 395 | { 396 | return URL(source); 397 | } 398 | 399 | /// 400 | @nogc pure @system unittest 401 | { 402 | auto u = parseURL("http://example.org:5326"); 403 | assert(u.scheme == parseURL!"scheme"("http://example.org:5326")); 404 | assert(u.host == parseURL!"host"("http://example.org:5326")); 405 | assert(u.user == parseURL!"user"("http://example.org:5326")); 406 | assert(u.pass == parseURL!"pass"("http://example.org:5326")); 407 | assert(u.path == parseURL!"path"("http://example.org:5326")); 408 | assert(u.query == parseURL!"query"("http://example.org:5326")); 409 | assert(u.fragment == parseURL!"fragment"("http://example.org:5326")); 410 | assert(u.port == parseURL!"port"("http://example.org:5326")); 411 | } 412 | -------------------------------------------------------------------------------- /source/tanya/range/adapter.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | /** 6 | * Range adapters transform some data structures into ranges. 7 | * 8 | * Copyright: Eugene Wissner 2018-2020. 9 | * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, 10 | * Mozilla Public License, v. 2.0). 11 | * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) 12 | * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/range/adapter.d, 13 | * tanya/range/adapter.d) 14 | */ 15 | module tanya.range.adapter; 16 | 17 | import tanya.algorithm.mutation; 18 | import tanya.memory.lifetime; 19 | import tanya.meta.trait; 20 | import tanya.range; 21 | 22 | private mixin template InserterCtor() 23 | { 24 | private Container* container; 25 | 26 | private this(return scope ref Container container) @trusted 27 | { 28 | this.container = &container; 29 | } 30 | } 31 | 32 | /** 33 | * If $(D_PARAM container) is a container with `insertBack`-support, 34 | * $(D_PSYMBOL backInserter) returns an output range that puts the elements 35 | * into the container with `insertBack`. 36 | * 37 | * The resulting output range supports all types `insertBack` supports. 38 | * 39 | * The range keeps a reference to the container passed to it, it doesn't use 40 | * any other storage. So there is no method to get the written data out of the 41 | * range - the container passed to $(D_PSYMBOL backInserter) contains that data 42 | * and can be used directly after all operations on the output range are 43 | * completed. It also means that the result range is not allowed to outlive its 44 | * container. 45 | * 46 | * Params: 47 | * Container = Container type. 48 | * container = Container used as an output range. 49 | * 50 | * Returns: `insertBack`-based output range. 51 | */ 52 | auto backInserter(Container)(return scope ref Container container) 53 | if (hasMember!(Container, "insertBack")) 54 | { 55 | static struct Inserter 56 | { 57 | void opCall(T)(auto ref T data) 58 | { 59 | this.container.insertBack(forward!data); 60 | } 61 | 62 | mixin InserterCtor; 63 | } 64 | return Inserter(container); 65 | } 66 | 67 | /// 68 | @nogc nothrow pure @safe unittest 69 | { 70 | static struct Container 71 | { 72 | int element; 73 | 74 | void insertBack(int element) 75 | { 76 | this.element = element; 77 | } 78 | } 79 | Container container; 80 | backInserter(container)(5); 81 | 82 | assert(container.element == 5); 83 | } 84 | 85 | /** 86 | * If $(D_PARAM container) is a container with `insertFront`-support, 87 | * $(D_PSYMBOL frontInserter) returns an output range that puts the elements 88 | * into the container with `insertFront`. 89 | * 90 | * The resulting output range supports all types `insertFront` supports. 91 | * 92 | * The range keeps a reference to the container passed to it, it doesn't use 93 | * any other storage. So there is no method to get the written data out of the 94 | * range - the container passed to $(D_PSYMBOL frontInserter) contains that data 95 | * and can be used directly after all operations on the output range are 96 | * completed. It also means that the result range is not allowed to outlive its 97 | * container. 98 | * 99 | * Params: 100 | * Container = Container type. 101 | * container = Container used as an output range. 102 | * 103 | * Returns: `insertFront`-based output range. 104 | */ 105 | auto frontInserter(Container)(return scope ref Container container) 106 | if (hasMember!(Container, "insertFront")) 107 | { 108 | static struct Inserter 109 | { 110 | void opCall(T)(auto ref T data) 111 | { 112 | this.container.insertFront(forward!data); 113 | } 114 | 115 | mixin InserterCtor; 116 | } 117 | return Inserter(container); 118 | } 119 | 120 | /// 121 | @nogc nothrow pure @safe unittest 122 | { 123 | static struct Container 124 | { 125 | int element; 126 | 127 | void insertFront(int element) 128 | { 129 | this.element = element; 130 | } 131 | } 132 | Container container; 133 | frontInserter(container)(5); 134 | 135 | assert(container.element == 5); 136 | } 137 | 138 | /** 139 | * $(D_PSYMBOL arrayInserter) makes an output range out of an array. 140 | * 141 | * The returned output range accepts single values as well as input ranges that 142 | * can be copied into the target array. 143 | * 144 | * Params: 145 | * Array = Array type. 146 | * array = Array. 147 | * 148 | * Returns: An output range writing into $(D_PARAM array). 149 | */ 150 | auto arrayInserter(Array)(return scope ref Array array) 151 | if (isArray!Array) 152 | { 153 | static if (is(Array ArrayT : ArrayT[size], size_t size)) 154 | { 155 | alias E = ArrayT; 156 | } 157 | else 158 | { 159 | alias E = ElementType!Array; 160 | } 161 | 162 | static struct ArrayInserter 163 | { 164 | private E[] data; 165 | 166 | private this(return scope ref Array data) @trusted 167 | { 168 | this.data = data[]; 169 | } 170 | 171 | void opCall(T)(auto ref T data) 172 | if (is(T : E)) 173 | in 174 | { 175 | assert(!this.data.empty); 176 | } 177 | do 178 | { 179 | put(this.data, data); 180 | } 181 | 182 | void opCall(R)(auto ref R data) 183 | if (isInputRange!R && isOutputRange!(E[], ElementType!R)) 184 | { 185 | this.data = copy(data, this.data); 186 | } 187 | } 188 | return ArrayInserter(array); 189 | } 190 | 191 | /// 192 | @nogc nothrow pure @safe unittest 193 | { 194 | int[1] array; 195 | 196 | arrayInserter(array)(5); 197 | assert(array[0] == 5); 198 | } 199 | 200 | /// 201 | @nogc nothrow pure @safe unittest 202 | { 203 | char[1] array; 204 | alias Actual = typeof(arrayInserter(array)); 205 | 206 | static assert(isOutputRange!(Actual, char)); 207 | static assert(isOutputRange!(Actual, char[])); 208 | } 209 | -------------------------------------------------------------------------------- /source/tanya/range/array.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | /** 6 | * $(D_PSYMBOL tanya.range.array) implements range primitives for built-in arrays. 7 | * 8 | * This module is a submodule of 9 | * $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/range/package.d, tanya.range). 10 | * 11 | * After importing of 12 | * $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/range/array.d, tanya/range/array.d) 13 | * built-in arrays can act as bidirectional ranges. For that to work the module 14 | * defines a set of functions that accept a built-in array of any type as their 15 | * first argument, so thanks to UFCS (Uniform Function Call Syntax) they can be 16 | * called as if they were array member functions. For example the arrays the 17 | * `.length`-property, but no `.empty` property. So here can be find the 18 | * $(D_PSYMBOL empty) function. Since $(D_INLINECODE empty(array)) and 19 | * $(D_INLINECODE array.empty) are equal for the arrays, arrays get a faked 20 | * property `empty`. 21 | * 22 | * The functions in this module don't change array elements or its underlying 23 | * storage, but some functions alter the slice. Each array maintains a pointer 24 | * to its data and the length and there can be several pointers which point to 25 | * the same data. Array pointer can be advanced and the length can be reduced 26 | * without changing the underlying storage. So slices offer the possibility to 27 | * have multiple views into the same array, point to different positions inside 28 | * it. 29 | * 30 | * Strings ($(D_INLINECODE char[]), (D_INLINECODE wchar[]) and 31 | * (D_INLINECODE dchar[])) are treated as any other normal array, they aren't 32 | * auto-decoded. 33 | * 34 | * Copyright: Eugene Wissner 2017-2020. 35 | * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, 36 | * Mozilla Public License, v. 2.0). 37 | * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) 38 | * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/range/array.d, 39 | * tanya/range/array.d) 40 | */ 41 | module tanya.range.array; 42 | 43 | /** 44 | * Returns the first element of the $(D_PARAM array). 45 | * 46 | * The element is returned by reference, so $(D_PSYMBOL front) can be also used 47 | * to change the first element of $(D_PARAM array) if it is mutable. 48 | * 49 | * Params: 50 | * T = Element type of $(D_PARAM array). 51 | * array = Built-in array. 52 | * 53 | * Returns: First element. 54 | * 55 | * Precondition: $(D_INLINECODE array.length > 0). 56 | */ 57 | @property ref inout(T) front(T)(return scope inout(T)[] array) 58 | in 59 | { 60 | assert(array.length > 0); 61 | } 62 | do 63 | { 64 | return array[0]; 65 | } 66 | 67 | /// 68 | @nogc nothrow pure @safe unittest 69 | { 70 | string s = "Wenn die Wunde nicht mehr wehtut, schmerzt die Narbe"; 71 | static assert(is(typeof(s.front) == immutable char)); 72 | assert(s.front == 'W'); 73 | 74 | wstring w = "Волны несутся, гремя и сверкая"; 75 | static assert(is(typeof(w.front) == immutable wchar)); 76 | assert(w.front == 'В'); 77 | 78 | dstring d = "Для писателя память - это почти все"; 79 | static assert(is(typeof(d.front) == immutable dchar)); 80 | assert(d.front == 'Д'); 81 | } 82 | 83 | /** 84 | * Returns the last element of the $(D_PARAM array). 85 | * 86 | * The element is returned by reference, so $(D_PSYMBOL back) can be also used 87 | * to change the last element of $(D_PARAM array) if it is mutable. 88 | * 89 | * Params: 90 | * T = Element type of $(D_PARAM array). 91 | * array = Built-in array. 92 | * 93 | * Returns: Last element. 94 | * 95 | * Precondition: $(D_INLINECODE array.length > 0). 96 | */ 97 | @property ref inout(T) back(T)(return scope inout(T)[] array) 98 | in 99 | { 100 | assert(array.length > 0); 101 | } 102 | do 103 | { 104 | return array[$ - 1]; 105 | } 106 | 107 | /// 108 | @nogc nothrow pure @safe unittest 109 | { 110 | string s = "Brecht"; 111 | static assert(is(typeof(s.back) == immutable char)); 112 | assert(s.back == 't'); 113 | 114 | wstring w = "Тютчев"; 115 | static assert(is(typeof(w.back) == immutable wchar)); 116 | assert(w.back == 'в'); 117 | 118 | dstring d = "Паустовский"; 119 | static assert(is(typeof(d.back) == immutable dchar)); 120 | assert(d.back == 'й'); 121 | } 122 | 123 | /** 124 | * $(D_PSYMBOL popFront) and $(D_PSYMBOL popBack) advance the $(D_PARAM array) 125 | * and remove one element from its back, respectively. 126 | * 127 | * $(D_PSYMBOL popFront) and $(D_PSYMBOL popBack) don't alter the array 128 | * storage, they only narrow the view into the array. 129 | * 130 | * Params: 131 | * T = Element type of $(D_PARAM array). 132 | * array = Built-in array. 133 | * 134 | * Precondition: $(D_INLINECODE array.length > 0). 135 | */ 136 | void popFront(T)(ref inout(T)[] array) 137 | in 138 | { 139 | assert(array.length > 0); 140 | } 141 | do 142 | { 143 | array = array[1 .. $]; 144 | } 145 | 146 | /// ditto 147 | void popBack(T)(ref inout(T)[] array) 148 | in 149 | { 150 | assert(array.length > 0); 151 | } 152 | do 153 | { 154 | array = array[0 .. $ - 1]; 155 | } 156 | 157 | /// 158 | @nogc nothrow pure @safe unittest 159 | { 160 | wstring array = "Der finstere Ozean der Metaphysik. Nietzsche"; 161 | 162 | array.popFront(); 163 | assert(array.length == 43); 164 | assert(array.front == 'e'); 165 | 166 | array.popBack(); 167 | assert(array.length == 42); 168 | assert(array.back == 'h'); 169 | } 170 | 171 | /** 172 | * Tests whether $(D_PARAM array) is empty. 173 | * 174 | * Params: 175 | * T = Element type of $(D_PARAM array). 176 | * array = Built-in array. 177 | * 178 | * Returns: $(D_KEYWORD true) if $(D_PARAM array) has no elements, 179 | * $(D_KEYWORD false) otherwise. 180 | */ 181 | @property bool empty(T)(scope const T[] array) 182 | { 183 | return array.length == 0; 184 | } 185 | 186 | /// 187 | @nogc nothrow pure @safe unittest 188 | { 189 | int[1] array; 190 | assert(!array.empty); 191 | assert(array[1 .. 1].empty); 192 | } 193 | 194 | /** 195 | * Returns a copy of the slice $(D_PARAM array). 196 | * 197 | * $(D_PSYMBOL save) doesn't copy the array itself, but only the data pointer 198 | * and the length. 199 | * 200 | * Params: 201 | * T = Element type of $(D_PARAM array). 202 | * array = Built-in array. 203 | * 204 | * Returns: A copy of the slice $(D_PARAM array). 205 | */ 206 | @property inout(T)[] save(T)(return scope inout(T)[] array) 207 | { 208 | return array; 209 | } 210 | 211 | /// 212 | @nogc nothrow pure @safe unittest 213 | { 214 | ubyte[8] array; 215 | auto slice = array.save; 216 | 217 | assert(slice.length == array.length); 218 | slice.popFront(); 219 | assert(slice.length < array.length); 220 | } 221 | -------------------------------------------------------------------------------- /source/tanya/range/package.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | /** 6 | * This package contains generic functions and templates to be used with D 7 | * ranges. 8 | * 9 | * Copyright: Eugene Wissner 2017-2020. 10 | * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, 11 | * Mozilla Public License, v. 2.0). 12 | * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) 13 | * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/range/package.d, 14 | * tanya/range/package.d) 15 | */ 16 | module tanya.range; 17 | 18 | public import tanya.range.adapter; 19 | public import tanya.range.array; 20 | public import tanya.range.primitive; 21 | -------------------------------------------------------------------------------- /test/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test", 3 | "description": "Test suite for unittest-blocks", 4 | "targetType": "library", 5 | 6 | "dependencies": { 7 | "tanya:middle": "*" 8 | }, 9 | 10 | "sourcePaths": [ 11 | "." 12 | ], 13 | "importPaths": [ 14 | "." 15 | ], 16 | "dflags-dmd": ["-dip1000"] 17 | } 18 | -------------------------------------------------------------------------------- /test/tanya/test/assertion.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | /** 6 | * Additional assertions. 7 | * 8 | * This module provides functions that assert whether a given expression 9 | * satisfies some complex condition, that can't be tested with 10 | * $(D_KEYWORD assert) in a single line. Internally all the functions 11 | * just evaluate the expression and call $(D_KEYWORD assert). 12 | * 13 | * The functions can cause segmentation fault if the module is compiled 14 | * in production mode and the condition fails. 15 | * 16 | * Copyright: Eugene Wissner 2017-2020. 17 | * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, 18 | * Mozilla Public License, v. 2.0). 19 | * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) 20 | * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/test/assertion.d, 21 | * tanya/test/assertion.d) 22 | */ 23 | module tanya.test.assertion; 24 | 25 | import tanya.memory.allocator; 26 | import tanya.meta.trait; 27 | 28 | /** 29 | * Asserts whether the function $(D_PARAM expr) throws an exception of type 30 | * $(D_PARAM E). If it does, the exception is catched and properly destroyed. 31 | * If it doesn't, an assertion error is thrown. If the exception doesn't match 32 | * $(D_PARAM E) type, it isn't catched and escapes. 33 | * 34 | * Params: 35 | * E = Expected exception type. 36 | * T = Throwing function type. 37 | * Args = Argument types of the throwing function. 38 | * expr = Throwing function. 39 | * args = Arguments for $(D_PARAM expr). 40 | */ 41 | void assertThrown(E : Exception, T, Args...)(T expr, auto ref Args args) 42 | if (isSomeFunction!T) 43 | { 44 | try 45 | { 46 | cast(void) expr(args); 47 | assert(false, "Expected exception not thrown"); 48 | } 49 | catch (E exception) 50 | { 51 | defaultAllocator.dispose(exception); 52 | } 53 | } 54 | 55 | /// 56 | @nogc nothrow pure @safe unittest 57 | { 58 | // If you want to test that an expression throws, you can wrap it into an 59 | // arrow function. 60 | static struct CtorThrows 61 | { 62 | this(int i) @nogc pure @safe 63 | { 64 | throw defaultAllocator.make!Exception(); 65 | } 66 | } 67 | assertThrown!Exception(() => CtorThrows(8)); 68 | } 69 | 70 | /** 71 | * Asserts that the function $(D_PARAM expr) doesn't throw. 72 | * 73 | * If it does, the thrown exception is catched, properly destroyed and an 74 | * assertion error is thrown instead. 75 | * 76 | * Params: 77 | * T = Tested function type. 78 | * Args = Argument types of $(D_PARAM expr). 79 | * expr = Tested function. 80 | * args = Arguments for $(D_PARAM expr). 81 | */ 82 | void assertNotThrown(T, Args...)(T expr, auto ref Args args) 83 | if (isSomeFunction!T) 84 | { 85 | try 86 | { 87 | cast(void) expr(args); 88 | } 89 | catch (Exception exception) 90 | { 91 | defaultAllocator.dispose(exception); 92 | assert(false, "Unexpected exception thrown"); 93 | } 94 | } 95 | 96 | /// 97 | @nogc nothrow pure @safe unittest 98 | { 99 | // If you want to test that an expression doesn't throw, you can wrap it 100 | // into an arrow function. 101 | static struct S 102 | { 103 | } 104 | assertNotThrown(() => S()); 105 | } 106 | -------------------------------------------------------------------------------- /test/tanya/test/package.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | /** 6 | * Test suite for $(D_KEYWORD unittest)-blocks. 7 | * 8 | * Copyright: Eugene Wissner 2017-2020. 9 | * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, 10 | * Mozilla Public License, v. 2.0). 11 | * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) 12 | * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/test/package.d, 13 | * tanya/test/package.d) 14 | */ 15 | module tanya.test; 16 | 17 | public import tanya.test.assertion; 18 | public import tanya.test.stub; 19 | -------------------------------------------------------------------------------- /test/tanya/test/stub.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | /** 6 | * Range and generic type generators. 7 | * 8 | * Copyright: Eugene Wissner 2018-2020. 9 | * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, 10 | * Mozilla Public License, v. 2.0). 11 | * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) 12 | * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/test/stub.d, 13 | * tanya/test/stub.d) 14 | */ 15 | module tanya.test.stub; 16 | 17 | /** 18 | * Attribute signalizing that the generated range should contain the given 19 | * number of elements. 20 | * 21 | * $(D_PSYMBOL Count) should be always specified with some value and not as a 22 | * type, so $(D_INLINECODE Count(1)) instead just $(D_INLINECODE Count), 23 | * otherwise you can just omit $(D_PSYMBOL Count) and it will default to 0. 24 | * 25 | * $(D_PSYMBOL Count) doesn't generate `.length` property - use 26 | * $(D_PSYMBOL Length) for that. 27 | * 28 | * If neither $(D_PSYMBOL Length) nor $(D_PSYMBOL Infinite) is given, 29 | * $(D_ILNINECODE Count(0)) is assumed. 30 | * 31 | * This attribute conflicts with $(D_PSYMBOL Infinite) and $(D_PSYMBOL Length). 32 | */ 33 | struct Count 34 | { 35 | /// Original range length. 36 | size_t count = 0; 37 | 38 | @disable this(); 39 | 40 | /** 41 | * Constructs the attribute with the given length. 42 | * 43 | * Params: 44 | * count = Original range length. 45 | */ 46 | this(size_t count) @nogc nothrow pure @safe 47 | { 48 | this.count = count; 49 | } 50 | } 51 | 52 | /** 53 | * Attribute signalizing that the generated range should be infinite. 54 | * 55 | * This attribute conflicts with $(D_PSYMBOL Count) and $(D_PSYMBOL Length). 56 | */ 57 | struct Infinite 58 | { 59 | } 60 | 61 | /** 62 | * Generates `.length` property for the range. 63 | * 64 | * The length of the range can be specified as a constructor argument, 65 | * otherwise it is 0. 66 | * 67 | * This attribute conflicts with $(D_PSYMBOL Count) and $(D_PSYMBOL Infinite). 68 | */ 69 | struct Length 70 | { 71 | /// Original range length. 72 | size_t length = 0; 73 | } 74 | 75 | /** 76 | * Attribute signalizing that the generated range should return values by 77 | * reference. 78 | * 79 | * This atribute affects the return values of `.front`, `.back` and `[]`. 80 | */ 81 | struct WithLvalueElements 82 | { 83 | } 84 | 85 | /** 86 | * Generates an input range. 87 | * 88 | * Params: 89 | * E = Element type. 90 | */ 91 | mixin template InputRangeStub(E = int) 92 | { 93 | import tanya.meta.metafunction : Alias; 94 | import tanya.meta.trait : evalUDA, getUDAs, hasUDA; 95 | 96 | /* 97 | * Aliases for the attribute lookups to access them faster 98 | */ 99 | private enum bool infinite = hasUDA!(typeof(this), Infinite); 100 | private enum bool withLvalueElements = hasUDA!(typeof(this), 101 | WithLvalueElements); 102 | private alias Count = getUDAs!(typeof(this), .Count); 103 | private alias Length = getUDAs!(typeof(this), .Length); 104 | 105 | static if (Count.length != 0) 106 | { 107 | private enum size_t count = Count[0].count; 108 | 109 | static assert (!infinite, 110 | "Range cannot have count and be infinite at the same time"); 111 | static assert (Length.length == 0, 112 | "Range cannot have count and length at the same time"); 113 | } 114 | else static if (Length.length != 0) 115 | { 116 | private enum size_t count = evalUDA!(Length[0]).length; 117 | 118 | static assert (!infinite, 119 | "Range cannot have length and be infinite at the same time"); 120 | } 121 | else static if (!infinite) 122 | { 123 | private enum size_t count = 0; 124 | } 125 | 126 | /* 127 | * Member generation 128 | */ 129 | static if (infinite) 130 | { 131 | enum bool empty = false; 132 | } 133 | else 134 | { 135 | private size_t length_ = count; 136 | 137 | @property bool empty() const @nogc nothrow pure @safe 138 | { 139 | return this.length_ == 0; 140 | } 141 | } 142 | 143 | static if (withLvalueElements) 144 | { 145 | private E* element; // Pointer to enable range copying in save() 146 | } 147 | 148 | void popFront() @nogc nothrow pure @safe 149 | in 150 | { 151 | assert(!empty); 152 | } 153 | do 154 | { 155 | static if (!infinite) 156 | { 157 | --this.length_; 158 | } 159 | } 160 | 161 | static if (withLvalueElements) 162 | { 163 | ref E front() @nogc nothrow pure @safe 164 | in 165 | { 166 | assert(!empty); 167 | } 168 | do 169 | { 170 | return *this.element; 171 | } 172 | } 173 | else 174 | { 175 | E front() @nogc nothrow pure @safe 176 | in 177 | { 178 | assert(!empty); 179 | } 180 | do 181 | { 182 | return E.init; 183 | } 184 | } 185 | 186 | static if (Length.length != 0) 187 | { 188 | size_t length() const @nogc nothrow pure @safe 189 | { 190 | return this.length_; 191 | } 192 | } 193 | } 194 | 195 | /** 196 | * Generates a forward range. 197 | * 198 | * This mixin includes input range primitives as well, but can be combined with 199 | * $(D_PSYMBOL RandomAccessRangeStub). 200 | * 201 | * Params: 202 | * E = Element type. 203 | */ 204 | mixin template ForwardRangeStub(E = int) 205 | { 206 | static if (!is(typeof(this.InputRangeMixin) == void)) 207 | { 208 | mixin InputRangeStub!E InputRangeMixin; 209 | } 210 | 211 | auto save() @nogc nothrow pure @safe 212 | { 213 | return this; 214 | } 215 | } 216 | 217 | /** 218 | * Generates a bidirectional range. 219 | * 220 | * This mixin includes forward range primitives as well, but can be combined with 221 | * $(D_PSYMBOL RandomAccessRangeStub). 222 | * 223 | * Params: 224 | * E = Element type. 225 | */ 226 | mixin template BidirectionalRangeStub(E = int) 227 | { 228 | mixin ForwardRangeStub!E; 229 | 230 | void popBack() @nogc nothrow pure @safe 231 | in 232 | { 233 | assert(!empty); 234 | } 235 | do 236 | { 237 | static if (!infinite) 238 | { 239 | --this.length_; 240 | } 241 | } 242 | 243 | static if (withLvalueElements) 244 | { 245 | ref E back() @nogc nothrow pure @safe 246 | in 247 | { 248 | assert(!empty); 249 | } 250 | do 251 | { 252 | return *this.element; 253 | } 254 | } 255 | else 256 | { 257 | E back() @nogc nothrow pure @safe 258 | in 259 | { 260 | assert(!empty); 261 | } 262 | do 263 | { 264 | return E.init; 265 | } 266 | } 267 | } 268 | 269 | /** 270 | * Generates a random-access range. 271 | * 272 | * This mixin includes input range primitives as well, but can be combined with 273 | * $(D_PSYMBOL ForwardRangeStub) or $(D_PSYMBOL BidirectionalRangeStub). 274 | * 275 | * Note that a random-access range also requires $(D_PSYMBOL Length) or 276 | * $(D_PARAM Infinite) by definition. 277 | * 278 | * Params: 279 | * E = Element type. 280 | */ 281 | mixin template RandomAccessRangeStub(E = int) 282 | { 283 | static if (!is(typeof(this.InputRangeMixin) == void)) 284 | { 285 | mixin InputRangeStub!E InputRangeMixin; 286 | } 287 | 288 | static if (withLvalueElements) 289 | { 290 | ref E opIndex(size_t) @nogc nothrow pure @safe 291 | { 292 | return *this.element; 293 | } 294 | } 295 | else 296 | { 297 | E opIndex(size_t) @nogc nothrow pure @safe 298 | { 299 | return E.init; 300 | } 301 | } 302 | } 303 | 304 | /** 305 | * Struct with a disabled postblit constructor. 306 | * 307 | * $(D_PSYMBOL NonCopyable) can be used as an attribute for 308 | * $(D_PSYMBOL StructStub) or as a standalone type. 309 | */ 310 | struct NonCopyable 311 | { 312 | @disable this(this); 313 | } 314 | 315 | /** 316 | * Struct with an elaborate destructor. 317 | * 318 | * $(D_PSYMBOL WithDtor) can be used as an attribute for 319 | * $(D_PSYMBOL StructStub) or as a standalone type. 320 | * 321 | * When used as a standalone object the constructor of $(D_PSYMBOL WithDtor) 322 | * accepts an additional `counter` argument, which is incremented by the 323 | * destructor. $(D_PSYMBOL WithDtor) stores a pointer to the passed variable, 324 | * so the variable can be investigated after the struct isn't available 325 | * anymore. 326 | */ 327 | struct WithDtor 328 | { 329 | size_t* counter; 330 | 331 | this(ref size_t counter) @nogc nothrow pure @trusted 332 | { 333 | this.counter = &counter; 334 | } 335 | 336 | ~this() @nogc nothrow pure @safe 337 | { 338 | if (this.counter !is null) 339 | { 340 | ++*this.counter; 341 | } 342 | } 343 | } 344 | 345 | /** 346 | * Struct supporting hashing. 347 | * 348 | * $(D_PSYMBOL Hashable) can be used as an attribute for 349 | * $(D_PSYMBOL StructStub) or as a standalone type. 350 | * 351 | * The constructor accepts an additional parameter, which is returned by the 352 | * `toHash()`-function. `0U` is returned if no hash value is given. 353 | */ 354 | struct Hashable 355 | { 356 | size_t hash; 357 | 358 | size_t toHash() const @nogc nothrow pure @safe 359 | { 360 | return this.hash; 361 | } 362 | } 363 | 364 | /** 365 | * Generates a $(D_KEYWORD struct) with common functionality. 366 | * 367 | * To specify the needed functionality use user-defined attributes on the 368 | * $(D_KEYWORD struct) $(D_PSYMBOL StructStub) is mixed in. 369 | * 370 | * Supported attributes are: $(D_PSYMBOL NonCopyable), $(D_PSYMBOL Hashable), 371 | * $(D_PSYMBOL WithDtor). 372 | */ 373 | mixin template StructStub() 374 | { 375 | import tanya.meta.trait : evalUDA, getUDAs, hasUDA; 376 | 377 | static if (hasUDA!(typeof(this), NonCopyable)) 378 | { 379 | @disable this(this); 380 | } 381 | 382 | private alias Hashable = getUDAs!(typeof(this), .Hashable); 383 | static if (Hashable.length > 0) 384 | { 385 | size_t toHash() const @nogc nothrow pure @safe 386 | { 387 | return evalUDA!(Hashable[0]).hash; 388 | } 389 | } 390 | 391 | static if (hasUDA!(typeof(this), WithDtor)) 392 | { 393 | ~this() @nogc nothrow pure @safe 394 | { 395 | } 396 | } 397 | } 398 | -------------------------------------------------------------------------------- /tests/tanya/algorithm/tests/iteration.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | module tanya.algorithm.tests.iteration; 6 | 7 | import tanya.algorithm.iteration; 8 | import tanya.range; 9 | import tanya.test.stub; 10 | 11 | // Singleton range is bidirectional and random-access 12 | @nogc nothrow pure @safe unittest 13 | { 14 | static assert(isBidirectionalRange!(typeof(singleton('a')))); 15 | static assert(isRandomAccessRange!(typeof(singleton('a')))); 16 | 17 | assert({ char a; return isBidirectionalRange!(typeof(singleton(a))); }); 18 | assert({ char a; return isRandomAccessRange!(typeof(singleton(a))); }); 19 | } 20 | 21 | @nogc nothrow pure @safe unittest 22 | { 23 | char a = 'a'; 24 | auto single = singleton(a); 25 | 26 | assert(single.front == 'a'); 27 | assert(single.back == 'a'); 28 | assert(single[0] == 'a'); 29 | assert(single.length == 1); 30 | assert(!single.empty); 31 | } 32 | 33 | // popFront makes SingletonByRef empty 34 | @nogc nothrow pure @safe unittest 35 | { 36 | char a = 'a'; 37 | auto single = singleton(a); 38 | 39 | single.popFront(); 40 | assert(single.empty); 41 | assert(single.length == 0); 42 | assert(single.empty); 43 | } 44 | 45 | // popBack makes SingletonByRef empty 46 | @nogc nothrow pure @safe unittest 47 | { 48 | char a = 'b'; 49 | auto single = singleton(a); 50 | 51 | single.popBack(); 52 | assert(single.empty); 53 | assert(single.length == 0); 54 | assert(single.empty); 55 | } 56 | -------------------------------------------------------------------------------- /tests/tanya/algorithm/tests/mutation.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | module tanya.algorithm.tests.mutation; 5 | 6 | import tanya.algorithm.mutation; 7 | import tanya.range; 8 | import tanya.test.stub; 9 | 10 | // Returns advanced target 11 | @nogc nothrow pure @safe unittest 12 | { 13 | int[5] input = [1, 2, 3, 4, 5]; 14 | assert(copy(input[3 .. 5], input[]).front == 3); 15 | } 16 | 17 | // Copies overlapping arrays 18 | @nogc nothrow pure @safe unittest 19 | { 20 | import std.algorithm.comparison : equal; 21 | 22 | int[6] actual = [1, 2, 3, 4, 5, 6]; 23 | const int[6] expected = [1, 2, 1, 2, 3, 4]; 24 | 25 | copy(actual[0 .. 4], actual[2 .. 6]); 26 | assert(equal(actual[], expected[])); 27 | } 28 | 29 | @nogc nothrow pure @safe unittest 30 | { 31 | static assert(is(typeof(copy((ubyte[]).init, (ushort[]).init)))); 32 | static assert(!is(typeof(copy((ushort[]).init, (ubyte[]).init)))); 33 | } 34 | 35 | @nogc nothrow pure @safe unittest 36 | { 37 | static struct OutPutRange 38 | { 39 | int value; 40 | 41 | void opCall(int value) @nogc nothrow pure @safe 42 | in 43 | { 44 | assert(this.value == 0); 45 | } 46 | do 47 | { 48 | this.value = value; 49 | } 50 | } 51 | int[1] source = [5]; 52 | OutPutRange target; 53 | 54 | assert(copy(source[], target).value == 5); 55 | } 56 | 57 | // [] is called where possible 58 | @nogc nothrow pure @system unittest 59 | { 60 | static struct Slice 61 | { 62 | bool* slicingCalled; 63 | 64 | int front() @nogc nothrow pure @safe 65 | { 66 | return 0; 67 | } 68 | 69 | void front(int) @nogc nothrow pure @safe 70 | { 71 | } 72 | 73 | void popFront() @nogc nothrow pure @safe 74 | { 75 | } 76 | 77 | bool empty() @nogc nothrow pure @safe 78 | { 79 | return true; 80 | } 81 | 82 | void opIndexAssign(int) @nogc nothrow pure @safe 83 | { 84 | *this.slicingCalled = true; 85 | } 86 | } 87 | bool slicingCalled; 88 | auto range = Slice(&slicingCalled); 89 | fill(range, 0); 90 | assert(slicingCalled); 91 | } 92 | 93 | @nogc nothrow pure @safe unittest 94 | { 95 | NonCopyable[] nonCopyable; 96 | initializeAll(nonCopyable); 97 | } 98 | -------------------------------------------------------------------------------- /tests/tanya/container/tests/array.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | module tanya.container.tests.array; 5 | 6 | import std.algorithm.comparison; 7 | import tanya.container.array; 8 | import tanya.memory.allocator; 9 | import tanya.test.stub; 10 | 11 | // const arrays return usable ranges 12 | @nogc nothrow pure @safe unittest 13 | { 14 | auto v = const Array!int([1, 2, 4]); 15 | auto r1 = v[]; 16 | 17 | assert(r1.back == 4); 18 | r1.popBack(); 19 | assert(r1.back == 2); 20 | r1.popBack(); 21 | assert(r1.back == 1); 22 | r1.popBack(); 23 | assert(r1.length == 0); 24 | 25 | static assert(!is(typeof(r1[0] = 5))); 26 | static assert(!is(typeof(v[0] = 5))); 27 | 28 | const r2 = r1[]; 29 | static assert(is(typeof(r2[]))); 30 | } 31 | 32 | @nogc nothrow pure @safe unittest 33 | { 34 | Array!int v1; 35 | const Array!int v2; 36 | 37 | auto r1 = v1[]; 38 | auto r2 = v1[]; 39 | 40 | assert(r1.length == 0); 41 | assert(r2.empty); 42 | assert(r1 == r2); 43 | 44 | v1.insertBack([1, 2, 4]); 45 | assert(v1[] == v1); 46 | assert(v2[] == v2); 47 | assert(v2[] != v1); 48 | assert(v1[] != v2); 49 | assert(v1[].equal(v1[])); 50 | assert(v2[].equal(v2[])); 51 | assert(!v1[].equal(v2[])); 52 | } 53 | 54 | @nogc nothrow pure @safe unittest 55 | { 56 | struct MutableEqualsStruct 57 | { 58 | bool opEquals(typeof(this) that) @nogc nothrow pure @safe 59 | { 60 | return true; 61 | } 62 | } 63 | struct ConstEqualsStruct 64 | { 65 | bool opEquals(const typeof(this) that) const @nogc nothrow pure @safe 66 | { 67 | return true; 68 | } 69 | } 70 | auto v1 = Array!ConstEqualsStruct(); 71 | auto v2 = Array!ConstEqualsStruct(); 72 | assert(v1 == v2); 73 | assert(v1[] == v2); 74 | assert(v1 == v2[]); 75 | assert(v1[].equal(v2[])); 76 | 77 | auto v3 = const Array!ConstEqualsStruct(); 78 | auto v4 = const Array!ConstEqualsStruct(); 79 | assert(v3 == v4); 80 | assert(v3[] == v4); 81 | assert(v3 == v4[]); 82 | assert(v3[].equal(v4[])); 83 | 84 | auto v7 = Array!MutableEqualsStruct(1, MutableEqualsStruct()); 85 | auto v8 = Array!MutableEqualsStruct(1, MutableEqualsStruct()); 86 | assert(v7 == v8); 87 | assert(v7[] == v8); 88 | assert(v7 == v8[]); 89 | assert(v7[].equal(v8[])); 90 | } 91 | 92 | // Destructor can destroy empty arrays 93 | @nogc nothrow pure @safe unittest 94 | { 95 | auto v = Array!WithDtor(); 96 | } 97 | 98 | @nogc nothrow pure @safe unittest 99 | { 100 | class A 101 | { 102 | } 103 | A a1, a2; 104 | auto v1 = Array!A([a1, a2]); 105 | 106 | static assert(is(Array!(A*))); 107 | } 108 | 109 | @nogc nothrow pure @safe unittest 110 | { 111 | auto v = Array!int([5, 15, 8]); 112 | { 113 | size_t i; 114 | 115 | foreach (e; v) 116 | { 117 | assert(i != 0 || e == 5); 118 | assert(i != 1 || e == 15); 119 | assert(i != 2 || e == 8); 120 | ++i; 121 | } 122 | assert(i == 3); 123 | } 124 | { 125 | size_t i = 3; 126 | 127 | foreach_reverse (e; v) 128 | { 129 | --i; 130 | assert(i != 2 || e == 8); 131 | assert(i != 1 || e == 15); 132 | assert(i != 0 || e == 5); 133 | } 134 | assert(i == 0); 135 | } 136 | } 137 | 138 | // const constructor tests 139 | @nogc nothrow pure @system unittest 140 | { 141 | auto v1 = const Array!int([1, 2, 3]); 142 | auto v2 = Array!int(v1); 143 | assert(v1.get !is v2.get); 144 | assert(v1 == v2); 145 | 146 | auto v3 = const Array!int(Array!int([1, 2, 3])); 147 | assert(v1 == v3); 148 | assert(v3.length == 3); 149 | assert(v3.capacity == 3); 150 | } 151 | 152 | @nogc nothrow pure @safe unittest 153 | { 154 | auto v1 = Array!int(defaultAllocator); 155 | } 156 | 157 | @nogc nothrow pure @safe unittest 158 | { 159 | Array!int v; 160 | auto r = v[]; 161 | assert(r.length == 0); 162 | assert(r.empty); 163 | } 164 | 165 | @nogc nothrow pure @safe unittest 166 | { 167 | auto v1 = const Array!int([5, 15, 8]); 168 | Array!int v2; 169 | v2 = v1[0 .. 2]; 170 | assert(equal(v1[0 .. 2], v2[])); 171 | } 172 | 173 | // Move assignment 174 | @nogc nothrow pure @safe unittest 175 | { 176 | Array!int v1; 177 | v1 = Array!int([5, 15, 8]); 178 | } 179 | 180 | // Postblit is safe 181 | @nogc nothrow pure @safe unittest 182 | { 183 | auto array = Array!int(3); 184 | void func(Array!int arg) 185 | { 186 | assert(arg.capacity == 3); 187 | } 188 | func(array); 189 | } 190 | -------------------------------------------------------------------------------- /tests/tanya/container/tests/buffer.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | module tanya.container.tests.buffer; 5 | 6 | import tanya.container.buffer; 7 | 8 | @nogc nothrow pure @safe unittest 9 | { 10 | static assert(is(ReadBuffer!int)); 11 | } 12 | 13 | @nogc nothrow pure @safe unittest 14 | { 15 | static assert(is(typeof(WriteBuffer!int(5)))); 16 | } 17 | 18 | -------------------------------------------------------------------------------- /tests/tanya/container/tests/entry.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | module tanya.container.tests.entry; 5 | 6 | import tanya.container.entry; 7 | import tanya.test.stub; 8 | 9 | // Can be constructed with non-copyable key/values 10 | @nogc nothrow pure @safe unittest 11 | { 12 | static assert(is(Bucket!NonCopyable)); 13 | static assert(is(Bucket!(NonCopyable, NonCopyable))); 14 | 15 | static assert(is(HashArray!((ref NonCopyable) => 0U, NonCopyable))); 16 | static assert(is(HashArray!((ref NonCopyable) => 0U, NonCopyable, NonCopyable))); 17 | } 18 | -------------------------------------------------------------------------------- /tests/tanya/container/tests/hashtable.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | module tanya.container.tests.hashtable; 5 | 6 | import tanya.container.hashtable; 7 | import tanya.test.stub; 8 | 9 | @nogc nothrow pure @safe unittest 10 | { 11 | import tanya.range.primitive : isForwardRange; 12 | static assert(is(HashTable!(string, int) a)); 13 | static assert(is(const HashTable!(string, int))); 14 | static assert(isForwardRange!(HashTable!(string, int).Range)); 15 | 16 | static assert(is(HashTable!(int, int, (ref const int) => size_t.init))); 17 | static assert(is(HashTable!(int, int, (int) => size_t.init))); 18 | } 19 | 20 | // Constructs by reference 21 | @nogc nothrow pure @safe unittest 22 | { 23 | auto hashTable1 = HashTable!(string, int)(7); 24 | auto hashTable2 = HashTable!(string, int)(hashTable1); 25 | assert(hashTable1.length == hashTable2.length); 26 | assert(hashTable1.capacity == hashTable2.capacity); 27 | } 28 | 29 | // Constructs by value 30 | @nogc nothrow pure @safe unittest 31 | { 32 | auto hashTable = HashTable!(string, int)(HashTable!(string, int)(7)); 33 | assert(hashTable.capacity == 7); 34 | } 35 | 36 | // Assigns by reference 37 | @nogc nothrow pure @safe unittest 38 | { 39 | auto hashTable1 = HashTable!(string, int)(7); 40 | HashTable!(string, int) hashTable2; 41 | hashTable1 = hashTable2; 42 | assert(hashTable1.length == hashTable2.length); 43 | assert(hashTable1.capacity == hashTable2.capacity); 44 | } 45 | 46 | // Assigns by value 47 | @nogc nothrow pure @safe unittest 48 | { 49 | HashTable!(string, int) hashTable; 50 | hashTable = HashTable!(string, int)(7); 51 | assert(hashTable.capacity == 7); 52 | } 53 | 54 | // Postblit copies 55 | @nogc nothrow pure @safe unittest 56 | { 57 | auto hashTable = HashTable!(string, int)(7); 58 | void testFunc(HashTable!(string, int) hashTable) 59 | { 60 | assert(hashTable.capacity == 7); 61 | } 62 | testFunc(hashTable); 63 | } 64 | 65 | // Issue 53: https://github.com/caraus-ecms/tanya/issues/53 66 | @nogc nothrow pure @safe unittest 67 | { 68 | { 69 | HashTable!(uint, uint) hashTable; 70 | foreach (uint i; 0 .. 14) 71 | { 72 | hashTable[i + 1] = i; 73 | } 74 | assert(hashTable.length == 14); 75 | } 76 | { 77 | HashTable!(int, int) hashtable; 78 | 79 | hashtable[1194250162] = 3; 80 | hashtable[-1131293824] = 6; 81 | hashtable[838100082] = 9; 82 | 83 | hashtable.rehash(11); 84 | 85 | assert(hashtable[-1131293824] == 6); 86 | } 87 | } 88 | 89 | @nogc nothrow pure @safe unittest 90 | { 91 | static struct String 92 | { 93 | bool opEquals(string) const @nogc nothrow pure @safe 94 | { 95 | return true; 96 | } 97 | 98 | bool opEquals(ref const string) const @nogc nothrow pure @safe 99 | { 100 | return true; 101 | } 102 | 103 | bool opEquals(String) const @nogc nothrow pure @safe 104 | { 105 | return true; 106 | } 107 | 108 | bool opEquals(ref const String) const @nogc nothrow pure @safe 109 | { 110 | return true; 111 | } 112 | 113 | size_t toHash() const @nogc nothrow pure @safe 114 | { 115 | return 0; 116 | } 117 | } 118 | static assert(is(typeof("asdf" in HashTable!(String, int)()))); 119 | static assert(is(typeof(HashTable!(String, int)()["asdf"]))); 120 | } 121 | 122 | // Can have non-copyable keys and elements 123 | @nogc nothrow pure @safe unittest 124 | { 125 | @NonCopyable @Hashable 126 | static struct S 127 | { 128 | mixin StructStub; 129 | } 130 | static assert(is(HashTable!(S, int))); 131 | static assert(is(HashTable!(int, S))); 132 | static assert(is(HashTable!(S, S))); 133 | } 134 | -------------------------------------------------------------------------------- /tests/tanya/container/tests/list.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | module tanya.container.tests.list; 5 | 6 | import tanya.container.list; 7 | import tanya.test.stub; 8 | 9 | @nogc nothrow pure @safe unittest 10 | { 11 | interface Stuff 12 | { 13 | } 14 | static assert(is(SList!Stuff)); 15 | } 16 | 17 | @nogc nothrow pure @safe unittest 18 | { 19 | auto l = SList!int(0, 0); 20 | assert(l.empty); 21 | } 22 | 23 | // foreach called using opIndex(). 24 | @nogc nothrow pure @safe unittest 25 | { 26 | SList!int l; 27 | size_t i; 28 | 29 | l.insertFront(5); 30 | l.insertFront(4); 31 | l.insertFront(9); 32 | foreach (e; l) 33 | { 34 | assert(i != 0 || e == 9); 35 | assert(i != 1 || e == 4); 36 | assert(i != 2 || e == 5); 37 | ++i; 38 | } 39 | } 40 | 41 | @nogc nothrow pure @safe unittest 42 | { 43 | auto l1 = SList!int(); 44 | auto l2 = SList!int([9, 4]); 45 | l1 = l2[]; 46 | assert(l1 == l2); 47 | } 48 | 49 | @nogc nothrow pure @safe unittest 50 | { 51 | class A 52 | { 53 | } 54 | static assert(is(SList!(A*))); 55 | static assert(is(DList!(A*))); 56 | } 57 | 58 | // Removes all elements 59 | @nogc nothrow pure @safe unittest 60 | { 61 | auto l = DList!int([5]); 62 | assert(l.remove(l[]).empty); 63 | } 64 | 65 | @nogc nothrow pure @safe unittest 66 | { 67 | auto l1 = DList!int([5, 234, 30, 1]); 68 | auto l2 = DList!int([5, 1]); 69 | auto r = l1[]; 70 | 71 | r.popFront(); 72 | r.popBack(); 73 | assert(r.front == 234); 74 | assert(r.back == 30); 75 | 76 | assert(!l1.remove(r).empty); 77 | assert(l1 == l2); 78 | } 79 | 80 | @nogc nothrow pure @safe unittest 81 | { 82 | auto l = DList!int(0, 0); 83 | assert(l.empty); 84 | } 85 | 86 | @nogc nothrow pure @safe unittest 87 | { 88 | DList!int l; 89 | l.insertAfter(l[], 234); 90 | assert(l.front == 234); 91 | assert(l.back == 234); 92 | } 93 | 94 | @nogc nothrow pure @safe unittest 95 | { 96 | auto l1 = DList!int(); 97 | auto l2 = DList!int([9, 4]); 98 | l1 = l2[]; 99 | assert(l1 == l2); 100 | } 101 | 102 | // Sets the new head 103 | @nogc nothrow pure @safe unittest 104 | { 105 | auto l1 = DList!int([5, 234, 30, 1]); 106 | auto l2 = DList!int([1]); 107 | auto r = l1[]; 108 | 109 | r.popBack(); 110 | 111 | assert(!l1.remove(r).empty); 112 | assert(l1 == l2); 113 | } 114 | 115 | // Can have non-copyable elements 116 | @nogc nothrow pure @safe unittest 117 | { 118 | static assert(is(SList!NonCopyable)); 119 | static assert(is(DList!NonCopyable)); 120 | } 121 | -------------------------------------------------------------------------------- /tests/tanya/container/tests/set.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | module tanya.container.tests.set; 5 | 6 | import tanya.container.set; 7 | import tanya.memory.allocator; 8 | import tanya.test.stub; 9 | 10 | // Basic insertion logic. 11 | @nogc nothrow pure @safe unittest 12 | { 13 | Set!int set; 14 | 15 | assert(set.insert(5) == 1); 16 | assert(5 in set); 17 | assert(set.capacity == 3); 18 | 19 | assert(set.insert(5) == 0); 20 | assert(5 in set); 21 | assert(set.capacity == 3); 22 | 23 | assert(set.insert(9) == 1); 24 | assert(9 in set); 25 | assert(5 in set); 26 | assert(set.capacity == 3); 27 | 28 | assert(set.insert(7) == 1); 29 | assert(set.insert(8) == 1); 30 | assert(8 in set); 31 | assert(5 in set); 32 | assert(9 in set); 33 | assert(7 in set); 34 | assert(set.capacity == 7); 35 | 36 | assert(set.insert(16) == 1); 37 | assert(16 in set); 38 | assert(set.capacity == 7); 39 | } 40 | 41 | // Static checks. 42 | @nogc nothrow pure @safe unittest 43 | { 44 | import tanya.range.primitive; 45 | 46 | static assert(isBidirectionalRange!(Set!int.ConstRange)); 47 | static assert(isBidirectionalRange!(Set!int.Range)); 48 | 49 | static assert(!isInfinite!(Set!int.Range)); 50 | static assert(!hasLength!(Set!int.Range)); 51 | 52 | static assert(is(Set!uint)); 53 | static assert(is(Set!long)); 54 | static assert(is(Set!ulong)); 55 | static assert(is(Set!short)); 56 | static assert(is(Set!ushort)); 57 | static assert(is(Set!bool)); 58 | } 59 | 60 | @nogc nothrow pure @safe unittest 61 | { 62 | const Set!int set; 63 | assert(set[].empty); 64 | } 65 | 66 | @nogc nothrow pure @safe unittest 67 | { 68 | Set!int set; 69 | set.insert(8); 70 | 71 | auto r1 = set[]; 72 | auto r2 = r1.save(); 73 | 74 | r1.popFront(); 75 | assert(r1.empty); 76 | 77 | r2.popBack(); 78 | assert(r2.empty); 79 | } 80 | 81 | // Initial capacity is 0. 82 | @nogc nothrow pure @safe unittest 83 | { 84 | auto set = Set!int(defaultAllocator); 85 | assert(set.capacity == 0); 86 | } 87 | 88 | // Capacity is set to a prime. 89 | @nogc nothrow pure @safe unittest 90 | { 91 | auto set = Set!int(8); 92 | assert(set.capacity == 13); 93 | } 94 | 95 | // Constructs by reference 96 | @nogc nothrow pure @safe unittest 97 | { 98 | auto set1 = Set!int(7); 99 | auto set2 = Set!int(set1); 100 | assert(set1.length == set2.length); 101 | assert(set1.capacity == set2.capacity); 102 | } 103 | 104 | // Constructs by value 105 | @nogc nothrow pure @safe unittest 106 | { 107 | auto set = Set!int(Set!int(7)); 108 | assert(set.capacity == 7); 109 | } 110 | 111 | // Assigns by reference 112 | @nogc nothrow pure @safe unittest 113 | { 114 | auto set1 = Set!int(7); 115 | Set!int set2; 116 | set1 = set2; 117 | assert(set1.length == set2.length); 118 | assert(set1.capacity == set2.capacity); 119 | } 120 | 121 | // Assigns by value 122 | @nogc nothrow pure @safe unittest 123 | { 124 | Set!int set; 125 | set = Set!int(7); 126 | assert(set.capacity == 7); 127 | } 128 | 129 | // Postblit copies 130 | @nogc nothrow pure @safe unittest 131 | { 132 | auto set = Set!int(7); 133 | void testFunc(Set!int set) 134 | { 135 | assert(set.capacity == 7); 136 | } 137 | testFunc(set); 138 | } 139 | 140 | // Hasher can take argument by ref 141 | @nogc nothrow pure @safe unittest 142 | { 143 | static assert(is(Set!(int, (const ref x) => cast(size_t) x))); 144 | } 145 | 146 | // Can have non-copyable elements 147 | @nogc nothrow pure @safe unittest 148 | { 149 | @NonCopyable @Hashable 150 | static struct S 151 | { 152 | mixin StructStub; 153 | } 154 | static assert(is(Set!S)); 155 | } 156 | -------------------------------------------------------------------------------- /tests/tanya/container/tests/string.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | module tanya.container.tests.string; 5 | 6 | import tanya.container.string; 7 | import tanya.test.assertion; 8 | 9 | @nogc nothrow pure @safe unittest 10 | { 11 | auto s = String(0, 'K'); 12 | assert(s.length == 0); 13 | } 14 | 15 | // Allocates enough space for 3-byte character. 16 | @nogc pure @safe unittest 17 | { 18 | String s; 19 | s.insertBack('\u8100'); 20 | } 21 | 22 | @nogc pure @safe unittest 23 | { 24 | assertThrown!UTFException(() => String(1, cast(dchar) 0xd900)); 25 | assertThrown!UTFException(() => String(1, cast(wchar) 0xd900)); 26 | } 27 | 28 | @nogc nothrow pure @safe unittest 29 | { 30 | auto s1 = String("Buttercup"); 31 | auto s2 = String("Cap"); 32 | s2[] = s1[6 .. $]; 33 | assert(s2 == "cup"); 34 | } 35 | 36 | @nogc nothrow pure @safe unittest 37 | { 38 | auto s1 = String("Wow"); 39 | s1[] = 'a'; 40 | assert(s1 == "aaa"); 41 | } 42 | 43 | @nogc nothrow pure @safe unittest 44 | { 45 | auto s1 = String("ö"); 46 | s1[] = "oe"; 47 | assert(s1 == "oe"); 48 | } 49 | 50 | // Postblit works 51 | @nogc nothrow pure @safe unittest 52 | { 53 | void internFunc(String arg) 54 | { 55 | } 56 | void middleFunc(S...)(S args) 57 | { 58 | foreach (arg; args) 59 | { 60 | internFunc(arg); 61 | } 62 | } 63 | void topFunc(String args) 64 | { 65 | middleFunc(args); 66 | } 67 | topFunc(String("asdf")); 68 | } 69 | 70 | // Const range produces mutable ranges 71 | @nogc pure @safe unittest 72 | { 73 | auto s = const String("И снизу лед, и сверху - маюсь между."); 74 | { 75 | const constRange = s[]; 76 | 77 | auto fromConstRange = constRange[]; 78 | fromConstRange.popFront(); 79 | assert(fromConstRange.front == s[1]); 80 | 81 | fromConstRange = constRange[0 .. $]; 82 | fromConstRange.popFront(); 83 | assert(fromConstRange.front == s[1]); 84 | 85 | assert(constRange.get() is s.get()); 86 | } 87 | { 88 | const constRange = s.byCodePoint(); 89 | 90 | auto fromConstRange = constRange[]; 91 | fromConstRange.popFront(); 92 | assert(fromConstRange.front == ' '); 93 | } 94 | } 95 | 96 | // Can pop multibyte characters 97 | @nogc pure @safe unittest 98 | { 99 | auto s = String("\U00024B62\U00002260"); 100 | auto range = s.byCodePoint(); 101 | 102 | range.popFront(); 103 | assert(!range.empty); 104 | 105 | range.popFront(); 106 | assert(range.empty); 107 | 108 | range = s.byCodePoint(); 109 | range.popFront(); 110 | s[$ - 3] = 0xf0; 111 | assertThrown!UTFException(&(range.popFront)); 112 | } 113 | 114 | // Inserts own char range correctly 115 | @nogc nothrow pure @safe unittest 116 | { 117 | auto s1 = String(`ü`); 118 | String s2; 119 | s2.insertBack(s1[]); 120 | assert(s1 == s2); 121 | } 122 | -------------------------------------------------------------------------------- /tests/tanya/math/tests/package.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | module tanya.math.tests; 5 | 6 | import tanya.math; 7 | 8 | static if (ieeePrecision!float == IEEEPrecision.doubleExtended) 9 | @nogc nothrow pure @safe unittest 10 | { 11 | assert(classify(1.68105e-10) == FloatingPointClass.normal); 12 | assert(classify(1.68105e-4932L) == FloatingPointClass.subnormal); 13 | 14 | // Emulate unnormals, because they aren't generated anymore since i386 15 | FloatBits!real unnormal; 16 | unnormal.exp = 0x123; 17 | unnormal.mantissa = 0x1; 18 | assert(classify(unnormal) == FloatingPointClass.subnormal); 19 | } 20 | -------------------------------------------------------------------------------- /tests/tanya/math/tests/random.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | module tanya.math.tests.random; 5 | 6 | import tanya.math.random; 7 | import tanya.memory.allocator; 8 | 9 | static if (is(PlatformEntropySource)) @nogc @system unittest 10 | { 11 | import tanya.memory.smartref : unique; 12 | 13 | auto source = defaultAllocator.unique!PlatformEntropySource(); 14 | 15 | assert(source.threshold == 32); 16 | assert(source.strong); 17 | } 18 | -------------------------------------------------------------------------------- /tests/tanya/memory/tests/lifetime.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | module tanya.memory.tests.lifetime; 5 | 6 | import tanya.memory.allocator; 7 | import tanya.memory.lifetime; 8 | import tanya.test.stub; 9 | 10 | @nogc nothrow pure @safe unittest 11 | { 12 | int[] p; 13 | 14 | p = defaultAllocator.resize(p, 20); 15 | assert(p.length == 20); 16 | 17 | p = defaultAllocator.resize(p, 30); 18 | assert(p.length == 30); 19 | 20 | p = defaultAllocator.resize(p, 10); 21 | assert(p.length == 10); 22 | 23 | p = defaultAllocator.resize(p, 0); 24 | assert(p is null); 25 | } 26 | 27 | @nogc nothrow pure @system unittest 28 | { 29 | static struct S 30 | { 31 | ~this() @nogc nothrow pure @safe 32 | { 33 | } 34 | } 35 | auto p = cast(S[]) defaultAllocator.allocate(S.sizeof); 36 | 37 | defaultAllocator.dispose(p); 38 | } 39 | 40 | // Works with interfaces. 41 | @nogc nothrow pure @safe unittest 42 | { 43 | interface I 44 | { 45 | } 46 | class C : I 47 | { 48 | } 49 | auto c = defaultAllocator.make!C(); 50 | I i = c; 51 | 52 | defaultAllocator.dispose(i); 53 | defaultAllocator.dispose(i); 54 | } 55 | 56 | // Handles "Cannot access frame pointer" error. 57 | @nogc nothrow pure @safe unittest 58 | { 59 | struct F 60 | { 61 | ~this() @nogc nothrow pure @safe 62 | { 63 | } 64 | } 65 | static assert(is(typeof(emplace!F((void[]).init)))); 66 | } 67 | 68 | // Can emplace structs without a constructor 69 | @nogc nothrow pure @safe unittest 70 | { 71 | static assert(is(typeof(emplace!WithDtor(null, WithDtor())))); 72 | static assert(is(typeof(emplace!WithDtor(null)))); 73 | } 74 | 75 | // Doesn't call a destructor on uninitialized elements 76 | @nogc nothrow pure @system unittest 77 | { 78 | static struct SWithDtor 79 | { 80 | private bool canBeInvoked = false; 81 | ~this() @nogc nothrow pure @safe 82 | { 83 | assert(this.canBeInvoked); 84 | } 85 | } 86 | void[SWithDtor.sizeof] memory = void; 87 | auto actual = emplace!SWithDtor(memory[], SWithDtor(true)); 88 | assert(actual.canBeInvoked); 89 | } 90 | 91 | // Initializes structs if no arguments are given 92 | @nogc nothrow pure @safe unittest 93 | { 94 | static struct SEntry 95 | { 96 | byte content; 97 | } 98 | ubyte[1] mem = [3]; 99 | 100 | assert(emplace!SEntry(cast(void[]) mem[0 .. 1]).content == 0); 101 | } 102 | 103 | // Postblit is called when emplacing a struct 104 | @nogc nothrow pure @system unittest 105 | { 106 | static struct S 107 | { 108 | bool called = false; 109 | this(this) @nogc nothrow pure @safe 110 | { 111 | this.called = true; 112 | } 113 | } 114 | S target; 115 | S* sp = ⌖ 116 | 117 | emplace!S(sp[0 .. 1], S()); 118 | assert(target.called); 119 | } 120 | 121 | // Is pure. 122 | @nogc nothrow pure @system unittest 123 | { 124 | struct S 125 | { 126 | this(this) 127 | { 128 | } 129 | } 130 | S source, target = void; 131 | static assert(is(typeof({ moveEmplace(source, target); }))); 132 | } 133 | 134 | // Moves nested. 135 | @nogc nothrow pure @system unittest 136 | { 137 | struct Nested 138 | { 139 | void method() @nogc nothrow pure @safe 140 | { 141 | } 142 | } 143 | Nested source, target = void; 144 | moveEmplace(source, target); 145 | assert(source == target); 146 | } 147 | 148 | // Emplaces static arrays. 149 | @nogc nothrow pure @system unittest 150 | { 151 | static struct S 152 | { 153 | size_t member; 154 | this(size_t i) @nogc nothrow pure @safe 155 | { 156 | this.member = i; 157 | } 158 | ~this() @nogc nothrow pure @safe 159 | { 160 | } 161 | } 162 | S[2] source = [ S(5), S(5) ], target = void; 163 | moveEmplace(source, target); 164 | assert(source[0].member == 0); 165 | assert(target[0].member == 5); 166 | assert(source[1].member == 0); 167 | assert(target[1].member == 5); 168 | } 169 | 170 | // Moves if source is target. 171 | @nogc nothrow pure @safe unittest 172 | { 173 | int x = 5; 174 | move(x, x); 175 | assert(x == 5); 176 | } 177 | 178 | -------------------------------------------------------------------------------- /tests/tanya/memory/tests/mallocator.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | module tanya.memory.tests.mallocator; 5 | 6 | import tanya.memory.mallocator; 7 | 8 | // Fails with false 9 | @nogc nothrow pure @system unittest 10 | { 11 | void[] p = Mallocator.instance.allocate(20); 12 | void[] oldP = p; 13 | assert(!Mallocator.instance.reallocate(p, size_t.max - 16)); 14 | assert(oldP is p); 15 | Mallocator.instance.deallocate(p); 16 | } 17 | 18 | @nogc nothrow pure unittest 19 | { 20 | assert(Mallocator.instance.alignment == (void*).alignof); 21 | } 22 | -------------------------------------------------------------------------------- /tests/tanya/memory/tests/mmappool.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | module tanya.memory.tests.mmappool; 5 | 6 | import tanya.memory.mmappool; 7 | 8 | @nogc nothrow pure @system unittest 9 | { 10 | auto p = MmapPool.instance.allocate(20); 11 | assert(p); 12 | MmapPool.instance.deallocate(p); 13 | 14 | p = MmapPool.instance.allocate(0); 15 | assert(p.length == 0); 16 | } 17 | 18 | @nogc nothrow pure @system unittest 19 | { 20 | auto p = MmapPool.instance.allocate(20); 21 | 22 | assert(MmapPool.instance.deallocate(p)); 23 | } 24 | 25 | @nogc nothrow pure @system unittest 26 | { 27 | void[] p; 28 | assert(!MmapPool.instance.reallocateInPlace(p, 5)); 29 | assert(p is null); 30 | 31 | p = MmapPool.instance.allocate(1); 32 | auto orig = p.ptr; 33 | 34 | assert(MmapPool.instance.reallocateInPlace(p, 2)); 35 | assert(p.length == 2); 36 | assert(p.ptr == orig); 37 | 38 | assert(MmapPool.instance.reallocateInPlace(p, 4)); 39 | assert(p.length == 4); 40 | assert(p.ptr == orig); 41 | 42 | assert(MmapPool.instance.reallocateInPlace(p, 2)); 43 | assert(p.length == 2); 44 | assert(p.ptr == orig); 45 | 46 | MmapPool.instance.deallocate(p); 47 | } 48 | 49 | @nogc nothrow pure @system unittest 50 | { 51 | void[] p; 52 | MmapPool.instance.reallocate(p, 10 * int.sizeof); 53 | (cast(int[]) p)[7] = 123; 54 | 55 | assert(p.length == 40); 56 | 57 | MmapPool.instance.reallocate(p, 8 * int.sizeof); 58 | 59 | assert(p.length == 32); 60 | assert((cast(int[]) p)[7] == 123); 61 | 62 | MmapPool.instance.reallocate(p, 20 * int.sizeof); 63 | (cast(int[]) p)[15] = 8; 64 | 65 | assert(p.length == 80); 66 | assert((cast(int[]) p)[15] == 8); 67 | assert((cast(int[]) p)[7] == 123); 68 | 69 | MmapPool.instance.reallocate(p, 8 * int.sizeof); 70 | 71 | assert(p.length == 32); 72 | assert((cast(int[]) p)[7] == 123); 73 | 74 | MmapPool.instance.deallocate(p); 75 | } 76 | 77 | // A lot of allocations/deallocations, but it is the minimum caused a 78 | // segmentation fault because MmapPool reallocateInPlace moves a block wrong. 79 | @nogc nothrow pure @system unittest 80 | { 81 | auto a = MmapPool.instance.allocate(16); 82 | auto d = MmapPool.instance.allocate(16); 83 | auto b = MmapPool.instance.allocate(16); 84 | auto e = MmapPool.instance.allocate(16); 85 | auto c = MmapPool.instance.allocate(16); 86 | auto f = MmapPool.instance.allocate(16); 87 | 88 | MmapPool.instance.deallocate(a); 89 | MmapPool.instance.deallocate(b); 90 | MmapPool.instance.deallocate(c); 91 | 92 | a = MmapPool.instance.allocate(50); 93 | MmapPool.instance.reallocateInPlace(a, 64); 94 | MmapPool.instance.deallocate(a); 95 | 96 | a = MmapPool.instance.allocate(1); 97 | auto tmp1 = MmapPool.instance.allocate(1); 98 | auto h1 = MmapPool.instance.allocate(1); 99 | auto tmp2 = cast(ubyte[]) MmapPool.instance.allocate(1); 100 | 101 | auto h2 = MmapPool.instance.allocate(2); 102 | tmp1 = MmapPool.instance.allocate(1); 103 | MmapPool.instance.deallocate(h2); 104 | MmapPool.instance.deallocate(h1); 105 | 106 | h2 = MmapPool.instance.allocate(2); 107 | h1 = MmapPool.instance.allocate(1); 108 | MmapPool.instance.deallocate(h2); 109 | 110 | auto rep = cast(void[]) tmp2; 111 | MmapPool.instance.reallocate(rep, tmp1.length); 112 | tmp2 = cast(ubyte[]) rep; 113 | 114 | MmapPool.instance.reallocate(tmp1, 9); 115 | 116 | rep = cast(void[]) tmp2; 117 | MmapPool.instance.reallocate(rep, tmp1.length); 118 | tmp2 = cast(ubyte[]) rep; 119 | MmapPool.instance.reallocate(tmp1, 17); 120 | 121 | tmp2[$ - 1] = 0; 122 | 123 | MmapPool.instance.deallocate(tmp1); 124 | 125 | b = MmapPool.instance.allocate(16); 126 | 127 | MmapPool.instance.deallocate(h1); 128 | MmapPool.instance.deallocate(a); 129 | MmapPool.instance.deallocate(b); 130 | MmapPool.instance.deallocate(d); 131 | MmapPool.instance.deallocate(e); 132 | MmapPool.instance.deallocate(f); 133 | } 134 | -------------------------------------------------------------------------------- /tests/tanya/memory/tests/op.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | module tanya.memory.tests.op; 5 | 6 | import tanya.memory.op; 7 | 8 | @nogc nothrow pure @system unittest 9 | { 10 | ubyte[2] buffer = 1; 11 | fill!0(buffer[1 .. $]); 12 | assert(buffer[0] == 1 && buffer[1] == 0); 13 | } 14 | 15 | @nogc nothrow pure @safe unittest 16 | { 17 | assert(equal(null, null)); 18 | } 19 | 20 | @nogc nothrow pure @safe unittest 21 | { 22 | ubyte[0] source, target; 23 | source.copy(target); 24 | } 25 | 26 | @nogc nothrow pure @safe unittest 27 | { 28 | ubyte[1] source = [1]; 29 | ubyte[1] target; 30 | source.copy(target); 31 | assert(target[0] == 1); 32 | } 33 | 34 | @nogc nothrow pure @safe unittest 35 | { 36 | ubyte[8] source = [1, 2, 3, 4, 5, 6, 7, 8]; 37 | ubyte[8] target; 38 | source.copy(target); 39 | assert(equal(source, target)); 40 | } 41 | 42 | @nogc nothrow pure @safe unittest 43 | { 44 | ubyte[9] r1 = [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i' ]; 45 | ubyte[9] r2; 46 | 47 | copyBackward(r1, r2); 48 | assert(equal(r1, r2)); 49 | } 50 | 51 | // Compares unanligned memory 52 | @nogc nothrow pure @safe unittest 53 | { 54 | ubyte[16] r1 = [ 55 | 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 56 | 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 57 | ]; 58 | ubyte[16] r2 = [ 59 | 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 60 | 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 61 | ]; 62 | 63 | assert(equal(r1, r2)); 64 | assert(equal(r1[1 .. $], r2[1 .. $])); 65 | assert(equal(r1[0 .. $ - 1], r2[0 .. $ - 1])); 66 | assert(equal(r1[0 .. 8], r2[0 .. 8])); 67 | } 68 | -------------------------------------------------------------------------------- /tests/tanya/memory/tests/smartref.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | module tanya.memory.tests.smartref; 5 | 6 | import tanya.memory.allocator; 7 | import tanya.memory.smartref; 8 | import tanya.meta.trait; 9 | import tanya.test.stub; 10 | 11 | @nogc @system unittest 12 | { 13 | auto rc = defaultAllocator.refCounted!int(5); 14 | rc = defaultAllocator.make!int(7); 15 | assert(*rc == 7); 16 | } 17 | 18 | @nogc @system unittest 19 | { 20 | RefCounted!int rc; 21 | assert(!rc.isInitialized); 22 | rc = null; 23 | assert(!rc.isInitialized); 24 | } 25 | 26 | @nogc @system unittest 27 | { 28 | auto rc = defaultAllocator.refCounted!int(5); 29 | 30 | void func(RefCounted!int param) @nogc 31 | { 32 | assert(param.count == 2); 33 | param = defaultAllocator.make!int(7); 34 | assert(param.count == 1); 35 | assert(*param == 7); 36 | } 37 | func(rc); 38 | assert(rc.count == 1); 39 | assert(*rc == 5); 40 | } 41 | 42 | @nogc @system unittest 43 | { 44 | RefCounted!int rc; 45 | 46 | void func(RefCounted!int param) @nogc 47 | { 48 | assert(param.count == 0); 49 | param = defaultAllocator.make!int(7); 50 | assert(param.count == 1); 51 | assert(*param == 7); 52 | } 53 | func(rc); 54 | assert(rc.count == 0); 55 | } 56 | 57 | @nogc @system unittest 58 | { 59 | RefCounted!int rc1, rc2; 60 | static assert(is(typeof(rc1 = rc2))); 61 | } 62 | 63 | @nogc @system unittest 64 | { 65 | auto rc = RefCounted!int(defaultAllocator); 66 | assert(!rc.isInitialized); 67 | assert(rc.allocator is defaultAllocator); 68 | } 69 | 70 | @nogc @system unittest 71 | { 72 | auto rc = defaultAllocator.refCounted!int(5); 73 | assert(rc.count == 1); 74 | 75 | void func(RefCounted!int rc) @nogc 76 | { 77 | assert(rc.count == 2); 78 | rc = null; 79 | assert(!rc.isInitialized); 80 | assert(rc.count == 0); 81 | } 82 | 83 | assert(rc.count == 1); 84 | func(rc); 85 | assert(rc.count == 1); 86 | 87 | rc = null; 88 | assert(!rc.isInitialized); 89 | assert(rc.count == 0); 90 | } 91 | 92 | @nogc @system unittest 93 | { 94 | auto rc = defaultAllocator.refCounted!int(5); 95 | assert(*rc == 5); 96 | 97 | void func(RefCounted!int rc) @nogc 98 | { 99 | assert(rc.count == 2); 100 | rc = defaultAllocator.refCounted!int(4); 101 | assert(*rc == 4); 102 | assert(rc.count == 1); 103 | } 104 | func(rc); 105 | assert(*rc == 5); 106 | } 107 | 108 | @nogc @system unittest 109 | { 110 | auto rc = defaultAllocator.refCounted!(int[])(5); 111 | assert(rc.length == 5); 112 | } 113 | 114 | @nogc @system unittest 115 | { 116 | auto p1 = defaultAllocator.make!int(5); 117 | auto p2 = p1; 118 | auto rc = RefCounted!int(p1, defaultAllocator); 119 | assert(rc.get() is p2); 120 | } 121 | 122 | @nogc @system unittest 123 | { 124 | size_t destroyed; 125 | { 126 | auto rc = defaultAllocator.refCounted!WithDtor(destroyed); 127 | } 128 | assert(destroyed == 1); 129 | } 130 | 131 | @nogc nothrow pure @system unittest 132 | { 133 | auto s = defaultAllocator.unique!int(5); 134 | assert(*s == 5); 135 | 136 | s = null; 137 | assert(s is null); 138 | } 139 | 140 | @nogc nothrow pure @system unittest 141 | { 142 | auto s = defaultAllocator.unique!int(5); 143 | assert(*s == 5); 144 | 145 | s = defaultAllocator.unique!int(4); 146 | assert(*s == 4); 147 | } 148 | 149 | @nogc nothrow pure @system unittest 150 | { 151 | auto p1 = defaultAllocator.make!int(5); 152 | auto p2 = p1; 153 | 154 | auto rc = Unique!int(p1, defaultAllocator); 155 | assert(rc.get() is p2); 156 | } 157 | 158 | @nogc nothrow pure @system unittest 159 | { 160 | auto rc = Unique!int(defaultAllocator); 161 | assert(rc.allocator is defaultAllocator); 162 | } 163 | 164 | @nogc @system unittest 165 | { 166 | uint destroyed; 167 | auto a = defaultAllocator.make!A(destroyed); 168 | 169 | assert(destroyed == 0); 170 | { 171 | auto rc = RefCounted!A(a, defaultAllocator); 172 | assert(rc.count == 1); 173 | 174 | void func(RefCounted!A rc) @nogc @system 175 | { 176 | assert(rc.count == 2); 177 | } 178 | func(rc); 179 | 180 | assert(rc.count == 1); 181 | } 182 | assert(destroyed == 1); 183 | 184 | RefCounted!int rc; 185 | assert(rc.count == 0); 186 | rc = defaultAllocator.make!int(8); 187 | assert(rc.count == 1); 188 | } 189 | 190 | @nogc nothrow pure @safe unittest 191 | { 192 | static assert(is(ReturnType!(RefCounted!int.get) == inout int*)); 193 | static assert(is(ReturnType!(RefCounted!A.get) == inout A)); 194 | static assert(is(ReturnType!(RefCounted!B.get) == inout B*)); 195 | } 196 | 197 | @nogc nothrow pure @safe unittest 198 | { 199 | static assert(is(RefCounted!B)); 200 | static assert(is(RefCounted!A)); 201 | } 202 | 203 | @nogc @system unittest 204 | { 205 | struct E 206 | { 207 | } 208 | auto b = defaultAllocator.refCounted!B(15); 209 | static assert(is(typeof(b.prop) == int)); 210 | static assert(!is(typeof(defaultAllocator.refCounted!B()))); 211 | 212 | static assert(is(typeof(defaultAllocator.refCounted!E()))); 213 | static assert(!is(typeof(defaultAllocator.refCounted!E(5)))); 214 | { 215 | auto rc = defaultAllocator.refCounted!B(3); 216 | assert(rc.get().prop == 3); 217 | } 218 | { 219 | auto rc = defaultAllocator.refCounted!E(); 220 | assert(rc.count); 221 | } 222 | } 223 | 224 | @nogc nothrow pure @safe unittest 225 | { 226 | static assert(is(typeof(defaultAllocator.unique!B(5)))); 227 | static assert(is(typeof(defaultAllocator.unique!(int[])(5)))); 228 | } 229 | 230 | private class A 231 | { 232 | uint *destroyed; 233 | 234 | this(ref uint destroyed) @nogc 235 | { 236 | this.destroyed = &destroyed; 237 | } 238 | 239 | ~this() @nogc 240 | { 241 | ++(*destroyed); 242 | } 243 | } 244 | 245 | private struct B 246 | { 247 | int prop; 248 | @disable this(); 249 | this(int param1) @nogc 250 | { 251 | prop = param1; 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /tests/tanya/meta/tests/metafunction.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | module tanya.meta.tests.metafunction; 5 | 6 | import tanya.meta.metafunction; 7 | 8 | @nogc nothrow pure @safe unittest 9 | { 10 | enum cmp(int x, int y) = x - y; 11 | static assert(isSorted!(cmp)); 12 | static assert(isSorted!(cmp, 1)); 13 | static assert(isSorted!(cmp, 1, 2, 2)); 14 | static assert(isSorted!(cmp, 1, 2, 2, 4)); 15 | static assert(isSorted!(cmp, 1, 2, 2, 4, 8)); 16 | static assert(!isSorted!(cmp, 32, 2, 2, 4, 8)); 17 | static assert(isSorted!(cmp, 32, 32)); 18 | } 19 | 20 | @nogc nothrow pure @safe unittest 21 | { 22 | enum cmp(int x, int y) = x < y; 23 | static assert(isSorted!(cmp)); 24 | static assert(isSorted!(cmp, 1)); 25 | static assert(isSorted!(cmp, 1, 2, 2)); 26 | static assert(isSorted!(cmp, 1, 2, 2, 4)); 27 | static assert(isSorted!(cmp, 1, 2, 2, 4, 8)); 28 | static assert(!isSorted!(cmp, 32, 2, 2, 4, 8)); 29 | static assert(isSorted!(cmp, 32, 32)); 30 | } 31 | -------------------------------------------------------------------------------- /tests/tanya/meta/tests/trait.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | module tanya.meta.tests.trait; 5 | 6 | import tanya.meta.metafunction; 7 | import tanya.meta.trait; 8 | 9 | // typeof(null) is not a pointer. 10 | @nogc nothrow pure @safe unittest 11 | { 12 | static assert(!isPointer!(typeof(null))); 13 | static assert(!isPointer!(const shared typeof(null))); 14 | 15 | enum typeOfNull : typeof(null) 16 | { 17 | null_ = null, 18 | } 19 | static assert(!isPointer!typeOfNull); 20 | } 21 | 22 | @nogc nothrow pure @safe unittest 23 | { 24 | static struct S 25 | { 26 | @property int opCall() 27 | { 28 | return 0; 29 | } 30 | } 31 | S s; 32 | static assert(isCallable!S); 33 | static assert(isCallable!s); 34 | } 35 | 36 | @nogc nothrow pure @safe unittest 37 | { 38 | static assert(is(FunctionTypeOf!(void delegate()) == function)); 39 | 40 | static void staticFunc() 41 | { 42 | } 43 | auto functionPointer = &staticFunc; 44 | static assert(is(FunctionTypeOf!staticFunc == function)); 45 | static assert(is(FunctionTypeOf!functionPointer == function)); 46 | 47 | void func() 48 | { 49 | } 50 | auto dg = &func; 51 | static assert(is(FunctionTypeOf!func == function)); 52 | static assert(is(FunctionTypeOf!dg == function)); 53 | 54 | interface I 55 | { 56 | @property int prop(); 57 | } 58 | static assert(is(FunctionTypeOf!(I.prop) == function)); 59 | 60 | static struct S 61 | { 62 | void opCall() 63 | { 64 | } 65 | } 66 | class C 67 | { 68 | static void opCall() 69 | { 70 | } 71 | } 72 | S s; 73 | 74 | static assert(is(FunctionTypeOf!s == function)); 75 | static assert(is(FunctionTypeOf!C == function)); 76 | static assert(is(FunctionTypeOf!S == function)); 77 | } 78 | 79 | @nogc nothrow pure @safe unittest 80 | { 81 | static struct S2 82 | { 83 | @property int opCall() 84 | { 85 | return 0; 86 | } 87 | } 88 | S2 s2; 89 | static assert(is(FunctionTypeOf!S2 == function)); 90 | static assert(is(FunctionTypeOf!s2 == function)); 91 | } 92 | 93 | @nogc nothrow pure @safe unittest 94 | { 95 | static assert(!hasElaborateAssign!int); 96 | 97 | static struct S1 98 | { 99 | void opAssign(S1) 100 | { 101 | } 102 | } 103 | static struct S2 104 | { 105 | void opAssign(int) 106 | { 107 | } 108 | } 109 | static struct S3 110 | { 111 | S1 s; 112 | alias s this; 113 | } 114 | static assert(hasElaborateAssign!S1); 115 | static assert(!hasElaborateAssign!(const S1)); 116 | static assert(hasElaborateAssign!(S1[1])); 117 | static assert(!hasElaborateAssign!(S1[0])); 118 | static assert(!hasElaborateAssign!S2); 119 | static assert(hasElaborateAssign!S3); 120 | 121 | static struct S4 122 | { 123 | void opAssign(S4) 124 | { 125 | } 126 | @disable this(this); 127 | } 128 | static assert(hasElaborateAssign!S4); 129 | } 130 | 131 | // Produces a tuple for an enum with only one member 132 | @nogc nothrow pure @safe unittest 133 | { 134 | enum E : int 135 | { 136 | one = 0, 137 | } 138 | static assert(EnumMembers!E == AliasSeq!0); 139 | } 140 | 141 | @nogc nothrow pure @safe unittest 142 | { 143 | class RefCountedStore(T) 144 | { 145 | } 146 | static assert(!isInnerClass!(RefCountedStore!int)); 147 | } 148 | 149 | @nogc nothrow pure @safe unittest 150 | { 151 | static struct DisabledOpEquals 152 | { 153 | @disable bool opEquals(typeof(this)) @nogc nothrow pure @safe; 154 | 155 | int opCmp(typeof(this)) @nogc nothrow pure @safe 156 | { 157 | return 0; 158 | } 159 | } 160 | static assert(!isEqualityComparable!DisabledOpEquals); 161 | static assert(isOrderingComparable!DisabledOpEquals); 162 | 163 | static struct OpEquals 164 | { 165 | bool opEquals(typeof(this)) @nogc nothrow pure @safe 166 | { 167 | return true; 168 | } 169 | } 170 | static assert(isEqualityComparable!OpEquals); 171 | static assert(!isOrderingComparable!OpEquals); 172 | } 173 | -------------------------------------------------------------------------------- /tests/tanya/net/tests/iface.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | module tanya.net.tests.iface; 5 | 6 | import std.algorithm.comparison; 7 | import std.utf; 8 | import tanya.net.iface; 9 | 10 | @nogc nothrow @safe unittest 11 | { 12 | version (linux) 13 | { 14 | assert(equal(indexToName(1)[], "lo".byChar)); 15 | } 16 | else version (Windows) 17 | { 18 | assert(equal(indexToName(1)[], "loopback_0")); 19 | } 20 | else 21 | { 22 | assert(equal(indexToName(1)[], "lo0")); 23 | } 24 | assert(indexToName(uint.max).empty); 25 | } 26 | -------------------------------------------------------------------------------- /tests/tanya/net/tests/inet.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | module tanya.net.tests.inet; 5 | 6 | import tanya.net.inet; 7 | import tanya.range; 8 | 9 | // Static tests 10 | @nogc nothrow pure @safe unittest 11 | { 12 | static assert(isBidirectionalRange!(NetworkOrder!4)); 13 | static assert(isBidirectionalRange!(NetworkOrder!8)); 14 | static assert(!is(NetworkOrder!9)); 15 | static assert(!is(NetworkOrder!1)); 16 | } 17 | -------------------------------------------------------------------------------- /tests/tanya/net/tests/ip.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | module tanya.net.tests.ip; 5 | 6 | import tanya.net.ip; 7 | import tanya.range; 8 | 9 | // Rejects malformed addresses 10 | @nogc nothrow pure @safe unittest 11 | { 12 | assert(address4("256.0.0.1").isNull); 13 | assert(address4(".0.0.1").isNull); 14 | assert(address4("0..0.1").isNull); 15 | assert(address4("0.0.0.").isNull); 16 | assert(address4("0.0.").isNull); 17 | assert(address4("").isNull); 18 | } 19 | 20 | @nogc nothrow pure @safe unittest 21 | { 22 | assert(address4(cast(ubyte[]) []).isNull); 23 | } 24 | 25 | // Assignment and comparison works 26 | @nogc nothrow pure @safe unittest 27 | { 28 | auto address1 = Address4.loopback(); 29 | auto address2 = Address4.any(); 30 | address1 = address2; 31 | assert(address1 == address2); 32 | } 33 | 34 | @nogc nothrow @safe unittest 35 | { 36 | char[18] actual; 37 | 38 | address6("ff00:2:3:4:5:6:7:8").get.toString(arrayInserter(actual)); 39 | assert(actual[] == "ff00:2:3:4:5:6:7:8"); 40 | } 41 | 42 | // Skips zero group in the middle 43 | @nogc nothrow @safe unittest 44 | { 45 | char[12] actual; 46 | 47 | address6("1::4:5:6:7:8").get.toString(arrayInserter(actual)); 48 | assert(actual[] == "1::4:5:6:7:8"); 49 | } 50 | 51 | // Doesn't replace lonely zeroes 52 | @nogc nothrow @safe unittest 53 | { 54 | char[15] actual; 55 | 56 | address6("0:1:0:2:3:0:4:0").get.toString(arrayInserter(actual)); 57 | assert(actual[] == "0:1:0:2:3:0:4:0"); 58 | } 59 | 60 | // Skips zero group at the beginning 61 | @nogc nothrow @safe unittest 62 | { 63 | char[13] actual; 64 | 65 | address6("::3:4:5:6:7:8").get.toString(arrayInserter(actual)); 66 | assert(actual[] == "::3:4:5:6:7:8"); 67 | } 68 | 69 | // Skips zero group at the end 70 | @nogc nothrow @safe unittest 71 | { 72 | char[13] actual; 73 | 74 | address6("1:2:3:4:5:6::").get.toString(arrayInserter(actual)); 75 | assert(actual[] == "1:2:3:4:5:6::"); 76 | } 77 | 78 | @nogc nothrow @safe unittest 79 | { 80 | ubyte[16] expected = [0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8]; 81 | auto actual = address6("1:2:3:4:5:6:7:8"); 82 | assert(actual.get.toBytes() == expected); 83 | } 84 | 85 | @nogc nothrow @safe unittest 86 | { 87 | ubyte[16] expected; 88 | auto actual = address6("::"); 89 | assert(actual.get.toBytes() == expected); 90 | } 91 | 92 | @nogc nothrow @safe unittest 93 | { 94 | ubyte[16] expected = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]; 95 | auto actual = address6("::1"); 96 | assert(actual.get.toBytes() == expected); 97 | } 98 | 99 | @nogc nothrow @safe unittest 100 | { 101 | ubyte[16] expected = [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; 102 | auto actual = address6("1::"); 103 | assert(actual.get.toBytes() == expected); 104 | } 105 | 106 | // Rejects malformed addresses 107 | @nogc nothrow @safe unittest 108 | { 109 | assert(address6("").isNull); 110 | assert(address6(":").isNull); 111 | assert(address6(":a").isNull); 112 | assert(address6("a:").isNull); 113 | assert(address6("1:2:3:4::6:").isNull); 114 | assert(address6("fe80:2:3:4::6:7:8%").isNull); 115 | } 116 | 117 | // Parses embedded IPv4 address 118 | @nogc nothrow @safe unittest 119 | { 120 | ubyte[16] expected = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4]; 121 | auto actual = address6("0:0:0:0:0:0:1.2.3.4"); 122 | assert(actual.get.toBytes() == expected); 123 | } 124 | 125 | @nogc nothrow @safe unittest 126 | { 127 | ubyte[16] expected = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4]; 128 | auto actual = address6("::1.2.3.4"); 129 | assert(actual.get.toBytes() == expected); 130 | } 131 | 132 | @nogc nothrow @safe unittest 133 | { 134 | ubyte[16] expected = [0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 6, 1, 2, 3, 4]; 135 | auto actual = address6("::5:6:1.2.3.4"); 136 | assert(actual.get.toBytes() == expected); 137 | } 138 | 139 | @nogc nothrow @safe unittest 140 | { 141 | assert(address6("0:0:0:0:0:0:1.2.3.").isNull); 142 | assert(address6("0:0:0:0:0:0:1.2:3.4").isNull); 143 | assert(address6("0:0:0:0:0:0:1.2.3.4.").isNull); 144 | assert(address6("fe80:0:0:0:0:0:1.2.3.4%1").get.scopeID == 1); 145 | } 146 | 147 | // Can assign another address 148 | @nogc nothrow pure @safe unittest 149 | { 150 | Address actual = Address4.loopback; 151 | Address expected = Address6.loopback; 152 | actual = expected; 153 | assert(actual == expected); 154 | } 155 | -------------------------------------------------------------------------------- /tests/tanya/net/tests/uri.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | module tanya.net.tests.uri; 5 | 6 | import tanya.net.uri; 7 | import tanya.test.assertion; 8 | 9 | @nogc pure @system unittest 10 | { 11 | const u = URL("127.0.0.1"); 12 | assert(u.path == "127.0.0.1"); 13 | } 14 | 15 | @nogc pure @system unittest 16 | { 17 | const u = URL("http://127.0.0.1"); 18 | assert(u.scheme == "http"); 19 | assert(u.host == "127.0.0.1"); 20 | } 21 | 22 | @nogc pure @system unittest 23 | { 24 | const u = URL("http://127.0.0.1:9000"); 25 | assert(u.scheme == "http"); 26 | assert(u.host == "127.0.0.1"); 27 | assert(u.port == 9000); 28 | } 29 | 30 | @nogc pure @system unittest 31 | { 32 | const u = URL("127.0.0.1:80"); 33 | assert(u.host == "127.0.0.1"); 34 | assert(u.port == 80); 35 | assert(u.path is null); 36 | } 37 | 38 | @nogc pure @system unittest 39 | { 40 | const u = URL("//example.net"); 41 | assert(u.host == "example.net"); 42 | assert(u.scheme is null); 43 | } 44 | 45 | @nogc pure @system unittest 46 | { 47 | const u = URL("//example.net?q=before:after"); 48 | assert(u.host == "example.net"); 49 | assert(u.query == "q=before:after"); 50 | } 51 | 52 | @nogc pure @system unittest 53 | { 54 | const u = URL("localhost:8080"); 55 | assert(u.host == "localhost"); 56 | assert(u.port == 8080); 57 | assert(u.path is null); 58 | } 59 | 60 | @nogc pure @system unittest 61 | { 62 | const u = URL("ftp:"); 63 | assert(u.scheme == "ftp"); 64 | } 65 | 66 | @nogc pure @system unittest 67 | { 68 | const u = URL("file:///C:\\Users"); 69 | assert(u.scheme == "file"); 70 | assert(u.path == "C:\\Users"); 71 | } 72 | 73 | @nogc pure @system unittest 74 | { 75 | const u = URL("localhost:66000"); 76 | assert(u.scheme == "localhost"); 77 | assert(u.path == "66000"); 78 | } 79 | 80 | @nogc pure @system unittest 81 | { 82 | const u = URL("file:///home/"); 83 | assert(u.scheme == "file"); 84 | assert(u.path == "/home/"); 85 | } 86 | 87 | @nogc pure @system unittest 88 | { 89 | const u = URL("file:///home/?q=asdf"); 90 | assert(u.scheme == "file"); 91 | assert(u.path == "/home/"); 92 | assert(u.query == "q=asdf"); 93 | } 94 | 95 | @nogc pure @system unittest 96 | { 97 | const u = URL("http://secret@example.org"); 98 | assert(u.scheme == "http"); 99 | assert(u.host == "example.org"); 100 | assert(u.user == "secret"); 101 | } 102 | 103 | @nogc pure @system unittest 104 | { 105 | const u = URL("h_tp://:80"); 106 | assert(u.path == "h_tp://:80"); 107 | assert(u.port == 0); 108 | } 109 | 110 | @nogc pure @system unittest 111 | { 112 | const u = URL("zlib:/home/user/file.gz"); 113 | assert(u.scheme == "zlib"); 114 | assert(u.path == "/home/user/file.gz"); 115 | } 116 | 117 | @nogc pure @system unittest 118 | { 119 | const u = URL("h_tp:asdf"); 120 | assert(u.path == "h_tp:asdf"); 121 | } 122 | 123 | @nogc pure @system unittest 124 | { 125 | assertThrown!URIException(() => URL("http://:80")); 126 | assertThrown!URIException(() => URL(":80")); 127 | assertThrown!URIException(() => URL("http://u1:p1@u2:p2@example.org")); 128 | assertThrown!URIException(() => URL("http://blah.com:port")); 129 | assertThrown!URIException(() => URL("http://blah.com:66000")); 130 | } 131 | 132 | @nogc pure @system unittest 133 | { 134 | const u = URL("ftp://"); 135 | assert(u.scheme == "ftp"); 136 | } 137 | -------------------------------------------------------------------------------- /tests/tanya/os/tests/error.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | module tanya.os.tests.error; 5 | 6 | import tanya.os.error; 7 | 8 | @nogc nothrow pure @safe unittest 9 | { 10 | ErrorCode ec = cast(ErrorCode.ErrorNo) -1; 11 | assert(ec.toString() is null); 12 | } 13 | -------------------------------------------------------------------------------- /tests/tanya/range/tests/adapter.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | module tanya.range.tests.adapter; 5 | 6 | import tanya.range; 7 | 8 | private struct Container 9 | { 10 | void insertBack(const(char)[]) 11 | { 12 | } 13 | } 14 | 15 | @nogc nothrow pure @safe unittest 16 | { 17 | Container container; 18 | static assert(isOutputRange!(typeof(backInserter(container)), string)); 19 | } 20 | -------------------------------------------------------------------------------- /tests/tanya/range/tests/primitive.d: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | module tanya.range.tests.primitive; 5 | 6 | import tanya.range; 7 | import tanya.test.stub; 8 | 9 | private struct AssertPostblit 10 | { 11 | this(this) @nogc nothrow pure @safe 12 | { 13 | assert(false); 14 | } 15 | } 16 | 17 | @nogc nothrow pure @safe unittest 18 | { 19 | static struct Range1(T) 20 | { 21 | mixin InputRangeStub; 22 | 23 | T empty() const 24 | { 25 | return true; 26 | } 27 | } 28 | static assert(!isInputRange!(Range1!int)); 29 | static assert(!isInputRange!(Range1!(const bool))); 30 | 31 | static struct Range2 32 | { 33 | mixin InputRangeStub; 34 | 35 | int popFront() @nogc nothrow pure @safe 36 | { 37 | return 100; 38 | } 39 | } 40 | static assert(isInputRange!Range2); 41 | 42 | static struct Range3 43 | { 44 | mixin InputRangeStub; 45 | 46 | void front() @nogc nothrow pure @safe 47 | { 48 | } 49 | } 50 | static assert(!isInputRange!Range3); 51 | 52 | static struct Range4 53 | { 54 | mixin InputRangeStub; 55 | 56 | enum bool empty = false; 57 | } 58 | static assert(isInputRange!Range4); 59 | } 60 | 61 | // Ranges with non-copyable elements can be input ranges 62 | @nogc nothrow pure @safe unittest 63 | { 64 | @WithLvalueElements 65 | static struct R 66 | { 67 | mixin InputRangeStub!NonCopyable; 68 | } 69 | static assert(isInputRange!R); 70 | } 71 | 72 | // Ranges with const non-copyable elements can be input ranges 73 | @nogc nothrow pure @safe unittest 74 | { 75 | @WithLvalueElements 76 | static struct R 77 | { 78 | mixin InputRangeStub!(const(NonCopyable)); 79 | } 80 | static assert(isInputRange!R); 81 | } 82 | 83 | @nogc nothrow pure @safe unittest 84 | { 85 | static struct Range1 86 | { 87 | } 88 | static struct Range2 89 | { 90 | mixin InputRangeStub; 91 | 92 | Range1 save() @nogc nothrow pure @safe 93 | { 94 | return Range1(); 95 | } 96 | } 97 | static assert(!isForwardRange!Range2); 98 | 99 | static struct Range3 100 | { 101 | mixin InputRangeStub; 102 | 103 | const(typeof(this)) save() const @nogc nothrow pure @safe 104 | { 105 | return this; 106 | } 107 | } 108 | static assert(!isForwardRange!Range3); 109 | } 110 | 111 | @nogc nothrow pure @safe unittest 112 | { 113 | static struct Range(T, U) 114 | { 115 | mixin BidirectionalRangeStub; 116 | 117 | @property T front() @nogc nothrow pure @safe 118 | { 119 | return T.init; 120 | } 121 | 122 | @property U back() @nogc nothrow pure @safe 123 | { 124 | return U.init; 125 | } 126 | } 127 | static assert(!isBidirectionalRange!(Range!(int, uint))); 128 | static assert(!isBidirectionalRange!(Range!(int, const int))); 129 | } 130 | 131 | // Ranges with non-copyable elements can be bidirectional ranges 132 | @nogc nothrow pure @safe unittest 133 | { 134 | @WithLvalueElements 135 | static struct R 136 | { 137 | mixin BidirectionalRangeStub!NonCopyable; 138 | } 139 | static assert(isBidirectionalRange!R); 140 | } 141 | 142 | @nogc nothrow pure @safe unittest 143 | { 144 | static struct Range1 145 | { 146 | mixin BidirectionalRangeStub; 147 | mixin RandomAccessRangeStub; 148 | } 149 | static assert(!isRandomAccessRange!Range1); 150 | 151 | @Length 152 | static struct Range2(Args...) 153 | { 154 | mixin BidirectionalRangeStub; 155 | 156 | int opIndex(Args) @nogc nothrow pure @safe 157 | { 158 | return 0; 159 | } 160 | } 161 | static assert(isRandomAccessRange!(Range2!size_t)); 162 | static assert(!isRandomAccessRange!(Range2!())); 163 | static assert(!isRandomAccessRange!(Range2!(size_t, size_t))); 164 | 165 | @Length 166 | static struct Range3 167 | { 168 | mixin BidirectionalRangeStub; 169 | 170 | int opIndex(const size_t pos1, const size_t pos2 = 0) 171 | @nogc nothrow pure @safe 172 | { 173 | return 0; 174 | } 175 | } 176 | static assert(isRandomAccessRange!Range3); 177 | 178 | static struct Range4 179 | { 180 | mixin BidirectionalRangeStub; 181 | mixin RandomAccessRangeStub; 182 | 183 | size_t opDollar() const @nogc nothrow pure @safe 184 | { 185 | return 0; 186 | } 187 | } 188 | static assert(!isRandomAccessRange!Range4); 189 | } 190 | 191 | // Ranges with non-copyable elements can be random-access ranges 192 | @nogc nothrow pure @safe unittest 193 | { 194 | @WithLvalueElements @Infinite 195 | static struct R 196 | { 197 | mixin RandomAccessRangeStub!NonCopyable; 198 | } 199 | static assert(isRandomAccessRange!R); 200 | } 201 | 202 | @nogc nothrow pure @safe unittest 203 | { 204 | @Infinite 205 | static struct StaticConstRange 206 | { 207 | mixin InputRangeStub; 208 | 209 | static bool empty = false; 210 | } 211 | static assert(!isInfinite!StaticConstRange); 212 | 213 | @Infinite 214 | static struct TrueRange 215 | { 216 | mixin InputRangeStub; 217 | 218 | static const bool empty = true; 219 | } 220 | static assert(!isInfinite!TrueRange); 221 | } 222 | 223 | @nogc nothrow pure @safe unittest 224 | { 225 | @Infinite 226 | static struct InfiniteRange 227 | { 228 | mixin ForwardRangeStub; 229 | private int i; 230 | 231 | void popFront() @nogc nothrow pure @safe 232 | { 233 | ++this.i; 234 | } 235 | 236 | void popBack() @nogc nothrow pure @safe 237 | { 238 | --this.i; 239 | } 240 | 241 | @property int front() const @nogc nothrow pure @safe 242 | { 243 | return this.i; 244 | } 245 | 246 | @property int back() const @nogc nothrow pure @safe 247 | { 248 | return this.i; 249 | } 250 | } 251 | { 252 | InfiniteRange range; 253 | popFrontExactly(range, 2); 254 | assert(range.front == 2); 255 | popFrontN(range, 2); 256 | assert(range.front == 4); 257 | } 258 | { 259 | InfiniteRange range; 260 | popBackExactly(range, 2); 261 | assert(range.back == -2); 262 | popBackN(range, 2); 263 | assert(range.back == -4); 264 | } 265 | } 266 | 267 | @nogc nothrow pure @safe unittest 268 | { 269 | static struct Range 270 | { 271 | private int[5] a = [1, 2, 3, 4, 5]; 272 | private size_t begin = 0, end = 5; 273 | 274 | Range save() @nogc nothrow pure @safe 275 | { 276 | return this; 277 | } 278 | 279 | void popFront() @nogc nothrow pure @safe 280 | { 281 | ++this.begin; 282 | } 283 | 284 | void popBack() @nogc nothrow pure @safe 285 | { 286 | --this.end; 287 | } 288 | 289 | @property int front() const @nogc nothrow pure @safe 290 | { 291 | return this.a[this.begin]; 292 | } 293 | 294 | @property int back() const @nogc nothrow pure @safe 295 | { 296 | return this.a[this.end - 1]; 297 | } 298 | 299 | @property bool empty() const @nogc nothrow pure @safe 300 | { 301 | return this.begin >= this.end; 302 | } 303 | } 304 | { 305 | Range range; 306 | 307 | popFrontN(range, 3); 308 | assert(range.front == 4); 309 | assert(range.back == 5); 310 | 311 | popFrontN(range, 20); 312 | assert(range.empty); 313 | } 314 | { 315 | Range range; 316 | 317 | popBackN(range, 3); 318 | assert(range.front == 1); 319 | assert(range.back == 2); 320 | 321 | popBackN(range, 20); 322 | assert(range.empty); 323 | } 324 | } 325 | 326 | @nogc nothrow pure @safe unittest 327 | { 328 | // Returns its elements by reference. 329 | @Infinite @WithLvalueElements 330 | static struct R1 331 | { 332 | mixin InputRangeStub!AssertPostblit; 333 | } 334 | static assert(is(typeof(moveFront(R1())))); 335 | 336 | // Returns elements with a postblit constructor by value. moveFront fails. 337 | @Infinite 338 | static struct R2 339 | { 340 | mixin InputRangeStub!AssertPostblit; 341 | } 342 | static assert(!is(typeof(moveFront(R2())))); 343 | } 344 | 345 | @nogc nothrow pure @safe unittest 346 | { 347 | // Returns its elements by reference. 348 | @Infinite @WithLvalueElements 349 | static struct R1 350 | { 351 | mixin BidirectionalRangeStub!AssertPostblit; 352 | } 353 | static assert(is(typeof(moveBack(R1())))); 354 | 355 | // Returns elements with a postblit constructor by value. moveBack fails. 356 | @Infinite 357 | static struct R2 358 | { 359 | mixin BidirectionalRangeStub!AssertPostblit; 360 | } 361 | static assert(!is(typeof(moveBack(R2())))); 362 | } 363 | 364 | @nogc nothrow pure @safe unittest 365 | { 366 | // Returns its elements by reference. 367 | @Infinite @WithLvalueElements 368 | static struct R1 369 | { 370 | mixin RandomAccessRangeStub!AssertPostblit; 371 | } 372 | static assert(is(typeof(moveAt(R1(), 0)))); 373 | 374 | // Returns elements with a postblit constructor by value. moveAt fails. 375 | @Infinite 376 | static struct R2 377 | { 378 | mixin RandomAccessRangeStub!AssertPostblit; 379 | } 380 | static assert(!is(typeof(moveAt(R2(), 0)))); 381 | } 382 | 383 | // Works with non-copyable elements 384 | @nogc nothrow pure @safe unittest 385 | { 386 | static assert(hasLvalueElements!(NonCopyable[])); 387 | } 388 | -------------------------------------------------------------------------------- /tests/tanya/tests/conv.d: -------------------------------------------------------------------------------- 1 | module tanya.tests.conv; 2 | 3 | import tanya.conv; 4 | import tanya.range; 5 | import tanya.test.assertion; 6 | import tanya.test.stub; 7 | 8 | // ':' is not a hex value 9 | @nogc nothrow pure @safe unittest 10 | { 11 | string colon = ":"; 12 | auto actual = readIntegral!ubyte(colon, 16); 13 | assert(actual == 0); 14 | assert(colon.length == 1); 15 | } 16 | 17 | // reads ubyte.max 18 | @nogc nothrow pure @safe unittest 19 | { 20 | string number = "255"; 21 | assert(readIntegral!ubyte(number) == 255); 22 | assert(number.empty); 23 | } 24 | 25 | // detects integer overflow 26 | @nogc nothrow pure @safe unittest 27 | { 28 | string number = "500"; 29 | readIntegral!ubyte(number); 30 | assert(number.front == '0'); 31 | assert(number.length == 1); 32 | } 33 | 34 | // stops on a non-digit 35 | @nogc nothrow pure @safe unittest 36 | { 37 | string number = "10-"; 38 | readIntegral!ubyte(number); 39 | assert(number.front == '-'); 40 | } 41 | 42 | // returns false if the number string is empty 43 | @nogc nothrow pure @safe unittest 44 | { 45 | string number = ""; 46 | readIntegral!ubyte(number); 47 | assert(number.empty); 48 | } 49 | 50 | @nogc nothrow pure @safe unittest 51 | { 52 | string number = "29"; 53 | assert(readIntegral!ubyte(number) == 29); 54 | assert(number.empty); 55 | } 56 | 57 | @nogc nothrow pure @safe unittest 58 | { 59 | string number = "25467"; 60 | readIntegral!ubyte(number); 61 | assert(number.front == '6'); 62 | } 63 | 64 | // Converts lower case hexadecimals 65 | @nogc nothrow pure @safe unittest 66 | { 67 | string number = "a"; 68 | assert(readIntegral!ubyte(number, 16) == 10); 69 | assert(number.empty); 70 | } 71 | 72 | // Converts upper case hexadecimals 73 | @nogc nothrow pure @safe unittest 74 | { 75 | string number = "FF"; 76 | assert(readIntegral!ubyte(number, 16) == 255); 77 | assert(number.empty); 78 | } 79 | 80 | // Handles small overflows 81 | @nogc nothrow pure @safe unittest 82 | { 83 | string number = "256"; 84 | assert(readIntegral!ubyte(number, 10) == 25); 85 | assert(number.front == '6'); 86 | } 87 | 88 | @nogc nothrow pure @safe unittest 89 | { 90 | int val = 5; 91 | assert(val.to!int() == 5); 92 | } 93 | 94 | @nogc nothrow pure @safe unittest 95 | { 96 | // ubyte -> ushort 97 | assert((cast(ubyte) 0).to!ushort == 0); 98 | assert((cast(ubyte) 1).to!ushort == 1); 99 | assert((cast(ubyte) (ubyte.max - 1)).to!ushort == ubyte.max - 1); 100 | assert((cast(ubyte) ubyte.max).to!ushort == ubyte.max); 101 | 102 | // ubyte -> short 103 | assert((cast(ubyte) 0).to!short == 0); 104 | assert((cast(ubyte) 1).to!short == 1); 105 | assert((cast(ubyte) (ubyte.max - 1)).to!short == ubyte.max - 1); 106 | assert((cast(ubyte) ubyte.max).to!short == ubyte.max); 107 | } 108 | 109 | @nogc pure @safe unittest 110 | { 111 | // ubyte <- ushort 112 | assert((cast(ushort) 0).to!ubyte == 0); 113 | assert((cast(ushort) 1).to!ubyte == 1); 114 | assert((cast(ushort) (ubyte.max - 1)).to!ubyte == ubyte.max - 1); 115 | assert((cast(ushort) ubyte.max).to!ubyte == ubyte.max); 116 | 117 | // ubyte <- short 118 | assert((cast(short) 0).to!ubyte == 0); 119 | assert((cast(short) 1).to!ubyte == 1); 120 | assert((cast(short) (ubyte.max - 1)).to!ubyte == ubyte.max - 1); 121 | assert((cast(short) ubyte.max).to!ubyte == ubyte.max); 122 | 123 | // short <-> int 124 | assert(short.min.to!int == short.min); 125 | assert((short.min + 1).to!int == short.min + 1); 126 | assert((cast(short) -1).to!int == -1); 127 | assert((cast(short) 0).to!int == 0); 128 | assert((cast(short) 1).to!int == 1); 129 | assert((short.max - 1).to!int == short.max - 1); 130 | assert(short.max.to!int == short.max); 131 | 132 | assert((cast(int) short.min).to!short == short.min); 133 | assert((cast(int) short.min + 1).to!short == short.min + 1); 134 | assert((cast(int) -1).to!short == -1); 135 | assert((cast(int) 0).to!short == 0); 136 | assert((cast(int) 1).to!short == 1); 137 | assert((cast(int) short.max - 1).to!short == short.max - 1); 138 | assert((cast(int) short.max).to!short == short.max); 139 | 140 | // uint <-> int 141 | assert((cast(uint) 0).to!int == 0); 142 | assert((cast(uint) 1).to!int == 1); 143 | assert((cast(uint) (int.max - 1)).to!int == int.max - 1); 144 | assert((cast(uint) int.max).to!int == int.max); 145 | 146 | assert((cast(int) 0).to!uint == 0); 147 | assert((cast(int) 1).to!uint == 1); 148 | assert((cast(int) (int.max - 1)).to!uint == int.max - 1); 149 | assert((cast(int) int.max).to!uint == int.max); 150 | } 151 | 152 | @nogc pure @safe unittest 153 | { 154 | assertThrown!ConvException(&to!(short, int), int.min); 155 | assertThrown!ConvException(&to!(short, int), int.max); 156 | assertThrown!ConvException(&to!(ushort, uint), uint.max); 157 | assertThrown!ConvException(&to!(uint, int), -1); 158 | } 159 | 160 | @nogc nothrow pure @safe unittest 161 | { 162 | enum Test : int 163 | { 164 | one, 165 | two, 166 | } 167 | assert(Test.one.to!int == 0); 168 | assert(Test.two.to!int == 1); 169 | } 170 | 171 | @nogc pure @safe unittest 172 | { 173 | assertThrown!ConvException(&to!(int, double), 2147483647.5); 174 | assertThrown!ConvException(&to!(int, double), -2147483648.5); 175 | assertThrown!ConvException(&to!(uint, double), -21474.5); 176 | } 177 | 178 | @nogc pure @safe unittest 179 | { 180 | enum Test : uint 181 | { 182 | one, 183 | two, 184 | } 185 | assertThrown!ConvException(&to!(Test, int), 5); 186 | } 187 | 188 | @nogc pure @safe unittest 189 | { 190 | assertThrown!ConvException(&to!(bool, int), -1); 191 | assertThrown!ConvException(&to!(bool, int), 2); 192 | } 193 | 194 | @nogc pure @safe unittest 195 | { 196 | assertThrown!ConvException(() => "1".to!bool); 197 | } 198 | 199 | @nogc pure @safe unittest 200 | { 201 | assertThrown!ConvException(() => "".to!int); 202 | assertThrown!ConvException(() => "-".to!int); 203 | assertThrown!ConvException(() => "-5".to!uint); 204 | assertThrown!ConvException(() => "-129".to!byte); 205 | assertThrown!ConvException(() => "256".to!ubyte); 206 | } 207 | -------------------------------------------------------------------------------- /tests/tanya/tests/format.d: -------------------------------------------------------------------------------- 1 | module tanya.tests.format; 2 | 3 | import tanya.format; 4 | import tanya.range; 5 | 6 | // Converting an integer to string. 7 | @nogc nothrow pure @system unittest 8 | { 9 | char[21] buf; 10 | 11 | assert(integral2String(80, buf) == "80"); 12 | assert(integral2String(-80, buf) == "-80"); 13 | assert(integral2String(0, buf) == "0"); 14 | assert(integral2String(uint.max, buf) == "4294967295"); 15 | assert(integral2String(int.min, buf) == "-2147483648"); 16 | } 17 | 18 | // Doesn't print the first argument repeatedly 19 | @nogc nothrow pure @safe unittest 20 | { 21 | assert(format!"{}{}"(1, 2) == "12"); 22 | } 23 | 24 | @nogc nothrow pure @safe unittest 25 | { 26 | assert(format!"Without arguments"() == "Without arguments"); 27 | assert(format!""().length == 0); 28 | 29 | static assert(!is(typeof(format!"{}"()))); 30 | static assert(!is(typeof(format!"{j}"(5)))); 31 | } 32 | 33 | // Enum 34 | @nogc nothrow pure @safe unittest 35 | { 36 | enum E1 : int 37 | { 38 | one, 39 | two, 40 | } 41 | assert(format!"{}"(E1.one) == "one"); 42 | 43 | const E1 e1; 44 | assert(format!"{}"(e1) == "one"); 45 | } 46 | 47 | // Modifiers 48 | @nogc pure @safe unittest 49 | { 50 | assert(format!"{}"(8.5) == "8.5"); 51 | assert(format!"{}"(8.6) == "8.6"); 52 | assert(format!"{}"(1000) == "1000"); 53 | assert(format!"{}"(1) == "1"); 54 | assert(format!"{}"(10.25) == "10.25"); 55 | assert(format!"{}"(1) == "1"); 56 | assert(format!"{}"(0.01) == "0.01"); 57 | } 58 | 59 | // String printing 60 | @nogc pure @safe unittest 61 | { 62 | assert(format!"{}"("Some weired string") == "Some weired string"); 63 | assert(format!"{}"(cast(string) null) == ""); 64 | assert(format!"{}"('c') == "c"); 65 | } 66 | 67 | // Integer 68 | @nogc pure @safe unittest 69 | { 70 | assert(format!"{}"(8) == "8"); 71 | assert(format!"{}"(8) == "8"); 72 | assert(format!"{}"(-8) == "-8"); 73 | assert(format!"{}"(-8L) == "-8"); 74 | assert(format!"{}"(8) == "8"); 75 | assert(format!"{}"(100000001) == "100000001"); 76 | assert(format!"{}"(99999999L) == "99999999"); 77 | assert(format!"{}"(10) == "10"); 78 | assert(format!"{}"(10L) == "10"); 79 | } 80 | 81 | // Floating point 82 | @nogc pure @safe unittest 83 | { 84 | assert(format!"{}"(0.1234) == "0.1234"); 85 | assert(format!"{}"(0.3) == "0.3"); 86 | assert(format!"{}"(0.333333333333) == "0.333333"); 87 | assert(format!"{}"(38234.1234) == "38234.1"); 88 | assert(format!"{}"(-0.3) == "-0.3"); 89 | assert(format!"{}"(0.000000000000000006) == "6e-18"); 90 | assert(format!"{}"(0.0) == "0"); 91 | assert(format!"{}"(double.init) == "NaN"); 92 | assert(format!"{}"(-double.init) == "-NaN"); 93 | assert(format!"{}"(double.infinity) == "Inf"); 94 | assert(format!"{}"(-double.infinity) == "-Inf"); 95 | assert(format!"{}"(0.000000000000000000000000003) == "3e-27"); 96 | assert(format!"{}"(0.23432e304) == "2.3432e+303"); 97 | assert(format!"{}"(-0.23432e8) == "-2.3432e+07"); 98 | assert(format!"{}"(1e-307) == "1e-307"); 99 | assert(format!"{}"(1e+8) == "1e+08"); 100 | assert(format!"{}"(111234.1) == "111234"); 101 | assert(format!"{}"(0.999) == "0.999"); 102 | assert(format!"{}"(0x1p-16382L) == "0"); 103 | assert(format!"{}"(1e+3) == "1000"); 104 | assert(format!"{}"(38234.1234) == "38234.1"); 105 | assert(format!"{}"(double.max) == "1.79769e+308"); 106 | } 107 | 108 | // typeof(null) 109 | @nogc pure @safe unittest 110 | { 111 | assert(format!"{}"(null) == "null"); 112 | } 113 | 114 | // Boolean 115 | @nogc pure @safe unittest 116 | { 117 | assert(format!"{}"(true) == "true"); 118 | assert(format!"{}"(false) == "false"); 119 | } 120 | 121 | // Unsafe tests with pointers 122 | @nogc pure @system unittest 123 | { 124 | // Pointer convesions 125 | assert(format!"{}"(cast(void*) 1) == "0x1"); 126 | assert(format!"{}"(cast(void*) 20) == "0x14"); 127 | assert(format!"{}"(cast(void*) null) == "0x0"); 128 | } 129 | 130 | // Structs 131 | @nogc pure @safe unittest 132 | { 133 | static struct WithoutStringify1 134 | { 135 | int a; 136 | void func() 137 | { 138 | } 139 | } 140 | assert(format!"{}"(WithoutStringify1(6)) == "WithoutStringify1(6)"); 141 | 142 | static struct WithoutStringify2 143 | { 144 | } 145 | assert(format!"{}"(WithoutStringify2()) == "WithoutStringify2()"); 146 | 147 | static struct WithoutStringify3 148 | { 149 | int a = -2; 150 | int b = 8; 151 | } 152 | assert(format!"{}"(WithoutStringify3()) == "WithoutStringify3(-2, 8)"); 153 | 154 | struct Nested 155 | { 156 | int i; 157 | 158 | void func() 159 | { 160 | } 161 | } 162 | assert(format!"{}"(Nested()) == "Nested(0)"); 163 | 164 | static struct WithToString 165 | { 166 | OR toString(OR)(OR range) const 167 | { 168 | put(range, "toString method"); 169 | return range; 170 | } 171 | } 172 | assert(format!"{}"(WithToString()) == "toString method"); 173 | } 174 | 175 | // Aggregate types 176 | @system unittest // Object.toString has no attributes. 177 | { 178 | import tanya.memory.allocator; 179 | import tanya.memory.smartref; 180 | 181 | interface I 182 | { 183 | } 184 | class A : I 185 | { 186 | } 187 | auto instance = defaultAllocator.unique!A(); 188 | assert(format!"{}"(instance.get()) == instance.get().toString()); 189 | assert(format!"{}"(cast(I) instance.get()) == I.classinfo.name); 190 | assert(format!"{}"(cast(A) null) == "null"); 191 | 192 | class B 193 | { 194 | OR toString(OR)(OR range) const 195 | { 196 | put(range, "Class B"); 197 | return range; 198 | } 199 | } 200 | assert(format!"{}"(cast(B) null) == "null"); 201 | } 202 | 203 | // Unions 204 | unittest 205 | { 206 | union U 207 | { 208 | int i; 209 | char c; 210 | } 211 | assert(format!"{}"(U(2)) == "U"); 212 | } 213 | 214 | // Ranges 215 | @nogc pure @safe unittest 216 | { 217 | static struct Stringish 218 | { 219 | private string content = "Some content"; 220 | 221 | immutable(char) front() const @nogc nothrow pure @safe 222 | { 223 | return this.content[0]; 224 | } 225 | 226 | void popFront() @nogc nothrow pure @safe 227 | { 228 | this.content = this.content[1 .. $]; 229 | } 230 | 231 | bool empty() const @nogc nothrow pure @safe 232 | { 233 | return this.content.length == 0; 234 | } 235 | } 236 | assert(format!"{}"(Stringish()) == "Some content"); 237 | 238 | static struct Intish 239 | { 240 | private int front_ = 3; 241 | 242 | int front() const @nogc nothrow pure @safe 243 | { 244 | return this.front_; 245 | } 246 | 247 | void popFront() @nogc nothrow pure @safe 248 | { 249 | --this.front_; 250 | } 251 | 252 | bool empty() const @nogc nothrow pure @safe 253 | { 254 | return this.front == 0; 255 | } 256 | } 257 | assert(format!"{}"(Intish()) == "[3, 2, 1]"); 258 | } 259 | 260 | // Typeid 261 | nothrow @safe unittest 262 | { 263 | assert(format!"{}"(typeid(int[])) == "int[]"); 264 | 265 | class C 266 | { 267 | } 268 | assert(format!"{}"(typeid(C)) == typeid(C).toString()); 269 | } 270 | --------------------------------------------------------------------------------