├── .editorconfig ├── .gitignore ├── LICENSE ├── README.md ├── dub.json └── source ├── app.d └── betterc ├── algorithm └── sorting.d ├── dynamicarray.d ├── functional.d ├── map.d ├── nullable.d ├── rbtree.d ├── str.d └── sumtypetest.d /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = tab 6 | indent_size = 4 7 | tab_width = 4 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = tr 12 | max_line_length = 80 13 | 14 | [*.yaml] 15 | indent_style = space 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | docs/ 5 | /libbetterc 6 | libbetterc.so 7 | libbetterc.dylib 8 | libbetterc.dll 9 | libbetterc.a 10 | libbetterc.lib 11 | libbetterc-test-* 12 | *.exe 13 | *.o 14 | *.obj 15 | *.lst 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libbetterc 2 | A tiny library for doing Dlang(betterc) stuff most likely for wasm 3 | -------------------------------------------------------------------------------- /dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "authors": [ 3 | "Robert burner Schadek" 4 | ], 5 | "configurations": [ 6 | { 7 | "name": "default", 8 | "targetType": "library" 9 | }, 10 | { 11 | "name": "unittestBetterc" 12 | , "targetType": "executable" 13 | , "versions": [ "RunTests" ] 14 | , "dflags": 15 | [ "-betterC" 16 | , "-unittest" 17 | , "-cov" 18 | , "-dip1000" 19 | , "-dip25" 20 | , "-preview=dip1021" 21 | ] 22 | }, 23 | { 24 | "name": "wasm" 25 | , "targetType": "library" 26 | , "versions" : ["WASM"] 27 | , "dflags": 28 | [ "-mtriple=wasm32-unknown-unknown-wasm" 29 | , "-betterC" 30 | , "-dip1000" 31 | , "-dip25" 32 | , "-preview=dip1021" 33 | ] 34 | } 35 | ], 36 | "copyright": "CopyRright © 2019, SymmetryInvestments", 37 | "description": "A D library to work with betterC", 38 | "license": "LGPL3", 39 | "name": "libbetterc" 40 | } 41 | -------------------------------------------------------------------------------- /source/app.d: -------------------------------------------------------------------------------- 1 | version(RunTests) { 2 | void main() { 3 | import std.typecons; 4 | import betterc.str; 5 | import betterc.rbtree; 6 | import betterc.map; 7 | import betterc.dynamicarray; 8 | import betterc.sumtype; 9 | import betterc.algorithm.sorting; 10 | import core.stdc.stdio; 11 | printf("libbetterc\n"); 12 | 13 | printf("String\n"); 14 | foreach(u; __traits(getUnitTests, betterc.str)) { 15 | u(); 16 | } 17 | printf("Rbtree\n"); 18 | foreach(u; __traits(getUnitTests, betterc.rbtree)) { 19 | u(); 20 | } 21 | printf("Map\n"); 22 | foreach(u; __traits(getUnitTests, betterc.map)) { 23 | u(); 24 | } 25 | printf("DynamicArray\n"); 26 | foreach(u; __traits(getUnitTests, betterc.dynamicarray)) { 27 | u(); 28 | } 29 | printf("SumType\n"); 30 | foreach(u; __traits(getUnitTests, betterc.sumtype)) { 31 | u(); 32 | } 33 | printf("algorithm.sorting\n"); 34 | foreach(u; __traits(getUnitTests, betterc.algorithm.sorting)) { 35 | u(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /source/betterc/algorithm/sorting.d: -------------------------------------------------------------------------------- 1 | module betterc.algorithm.sorting; 2 | 3 | template hasRandomAccess(T) { 4 | enum hasRandomAccess = __traits(compiles, T[1]); 5 | } 6 | 7 | unittest { 8 | static assert(hasRandomAccess!(int[])); 9 | } 10 | -------------------------------------------------------------------------------- /source/betterc/dynamicarray.d: -------------------------------------------------------------------------------- 1 | module betterc.dynamicarray; 2 | 3 | @nogc nothrow @safe: 4 | 5 | struct DynamicArray(T) { 6 | import core.stdc.stdlib : realloc, free; 7 | 8 | T* ptr; 9 | size_t length; 10 | private size_t _capacity; 11 | 12 | struct Range { 13 | DynamicArray!(T)* ptr; 14 | 15 | size_t low; 16 | size_t high; 17 | 18 | @property bool empty() const { 19 | return this.low == this.high; 20 | } 21 | 22 | @property size_t length() const { 23 | return this.high - this.low; 24 | } 25 | 26 | @property ref T front() { 27 | return (*ptr)[this.low]; 28 | } 29 | 30 | @property ref T back() { 31 | return (*ptr)[this.high - 1]; 32 | } 33 | 34 | ref T opIndex(size_t idx) { 35 | return (*ptr)[this.low + idx]; 36 | } 37 | } 38 | 39 | @disable this(this) { 40 | } 41 | 42 | ~this() { 43 | if(this.ptr) { 44 | () @trusted { free(cast(void*)ptr); }(); 45 | this.length = 0; 46 | this._capacity = 0; 47 | } 48 | } 49 | 50 | @property bool empty() const { 51 | return this.length == 0; 52 | } 53 | 54 | @property size_t opDollar() const { 55 | return this.length; 56 | } 57 | 58 | @property size_t capacity() const { 59 | return this._capacity; 60 | } 61 | 62 | void assureCapacity(size_t cap) { 63 | if(cap > this.length) { 64 | this._capacity = cap; 65 | () @trusted { 66 | this.ptr = cast(T*)realloc(this.ptr, T.sizeof * this._capacity); 67 | }(); 68 | assert(this.ptr); 69 | } 70 | } 71 | 72 | private void assureCapacity() { 73 | if(this.length == this._capacity) { 74 | this._capacity = this._capacity == 0 ? 10 : this._capacity * 2; 75 | this.assureCapacity(this._capacity); 76 | } 77 | } 78 | 79 | void insertBack(T t) { 80 | this.assureCapacity(); 81 | () @trusted { *(this.ptr + this.length) = t; }(); 82 | ++this.length; 83 | } 84 | 85 | void removeBack() { 86 | --this.length; 87 | } 88 | 89 | void insert(const size_t idx, T t) @trusted { 90 | assert(idx < this.length); 91 | this.assureCapacity(); 92 | for(size_t i = this.length; i > idx; --i) { 93 | *(this.ptr + i) = *(this.ptr + i - 1); 94 | } 95 | *(this.ptr + idx) = t; 96 | this.length++; 97 | } 98 | 99 | ref T opIndex(size_t idx) @trusted { 100 | assert(idx < this.length); 101 | return *(this.ptr + idx); 102 | } 103 | 104 | @property ref T front() { 105 | return *(this.ptr); 106 | } 107 | 108 | @property ref T back() @trusted { 109 | return *(this.ptr + (this.length - 1)); 110 | } 111 | 112 | auto opSlice() { 113 | return Range(&this, 0, this.length); 114 | } 115 | 116 | auto opSlice(size_t low, size_t high) { 117 | return Range(&this, low, high); 118 | } 119 | } 120 | 121 | unittest { 122 | DynamicArray!int a; 123 | assert(a.empty); 124 | assert(a.length == 0); 125 | } 126 | 127 | unittest { 128 | DynamicArray!int a; 129 | auto r = a[]; 130 | assert(r.empty); 131 | assert(r.length == 0); 132 | } 133 | 134 | unittest { 135 | DynamicArray!int a; 136 | a.insertBack(1); 137 | assert(a.capacity == 10); 138 | assert(a.length == 1); 139 | assert(a[0] == 1); 140 | assert(a.front == 1); 141 | assert(a.back == 1); 142 | 143 | a.removeBack(); 144 | assert(a.capacity == 10); 145 | assert(a.length == 0); 146 | } 147 | 148 | unittest { 149 | const upTo = 100; 150 | DynamicArray!int a; 151 | foreach(it; 0 .. upTo) { 152 | a.insertBack(it); 153 | assert(a.front == 0); 154 | assert(a.back == it); 155 | foreach(jdx; 0 .. it) { 156 | assert(a[jdx] == jdx); 157 | } 158 | 159 | auto r = a[]; 160 | assert(r.front == 0); 161 | assert(r.back == it); 162 | assert(r.length == it + 1); 163 | 164 | foreach(idx; 0 .. it) { 165 | assert(r[idx] == idx); 166 | } 167 | 168 | auto s = a[0 .. $]; 169 | assert(s.front == 0); 170 | assert(s.back == it); 171 | assert(s.length == it + 1); 172 | 173 | foreach(idx; 0 .. it) { 174 | assert(s[idx] == idx); 175 | } 176 | } 177 | 178 | foreach(it; 0 .. upTo) { 179 | long oldSize = a.length; 180 | a.removeBack(); 181 | long newSize = a.length; 182 | --oldSize; 183 | assert(newSize == oldSize); 184 | } 185 | } 186 | 187 | unittest { 188 | DynamicArray!int a; 189 | foreach(it; [0, 1, 2, 4, 5, 6, 7, 8, 9]) { 190 | a.insertBack(it); 191 | } 192 | a.insert(3, 3); 193 | foreach(it; [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) { 194 | assert(a[it] == it); 195 | } 196 | a.insert(0, -1); 197 | foreach(it; [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) { 198 | assert(a[it] == it - 1); 199 | } 200 | } 201 | 202 | unittest { 203 | DynamicArray!int a; 204 | DynamicArray!int b; 205 | static assert(!__traits(compiles, b = a)); 206 | } 207 | -------------------------------------------------------------------------------- /source/betterc/functional.d: -------------------------------------------------------------------------------- 1 | module betterc.functional; 2 | 3 | bool less(T)(T a, T b) { 4 | return a < b; 5 | } 6 | 7 | bool equal(T)(T a, T b) { 8 | return a == b; 9 | } 10 | 11 | int cmp(T)(T a, T b) { 12 | if(a < b) { 13 | return -1; 14 | } else if(a > b) { 15 | return 1; 16 | } else { 17 | return 0; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /source/betterc/map.d: -------------------------------------------------------------------------------- 1 | module betterc.map; 2 | 3 | import betterc.functional : less, equal; 4 | 5 | @nogc nothrow @safe: 6 | 7 | struct Map(K,V,alias lessThan = less, alias equalTo = equal) { 8 | import betterc.rbtree; 9 | 10 | struct KeyValue(Key, Value) { 11 | Key key; 12 | Value value; 13 | 14 | int opCmp(ref const typeof(this) other) const { 15 | return less(this.key, other.key); 16 | } 17 | 18 | bool opEquals()(auto ref const typeof(this) other) const { 19 | return equalTo(this.key, other.key); 20 | } 21 | } 22 | 23 | bool insert(K key, V value) { 24 | return this.tree.insert(MapNode(key, value)); 25 | } 26 | 27 | Node!(MapNode)* opIndex(this T)(K key) { 28 | MapNode s; 29 | s.key = key; 30 | return this.tree.search(s); 31 | } 32 | 33 | bool remove(K key) { 34 | MapNode s; 35 | s.key = key; 36 | return this.tree.remove(s); 37 | } 38 | 39 | @property size_t length() const pure nothrow { 40 | return this.tree.length; 41 | } 42 | 43 | @property bool empty() const pure nothrow { 44 | return this.tree.length == 0; 45 | } 46 | 47 | alias MapNode = KeyValue!(K,V); 48 | 49 | RBTree!(MapNode) tree; 50 | } 51 | 52 | unittest { 53 | Map!(int,int) map; 54 | assert(map.empty); 55 | assert(map[10] is null); 56 | assert(map.insert(1, 1000)); 57 | assert(!map.empty); 58 | assert(map.length == 1); 59 | assert(map[10] is null); 60 | assert(map[1] !is null); 61 | assert(map[1].value == 1000); 62 | assert(map.remove(1)); 63 | assert(map.empty); 64 | assert(map.length == 0); 65 | } 66 | -------------------------------------------------------------------------------- /source/betterc/nullable.d: -------------------------------------------------------------------------------- 1 | module betterc.nullable; 2 | 3 | @safe pure: 4 | 5 | struct Nullable(T) { 6 | T value; 7 | bool isNull = true; 8 | 9 | this(T nv) { 10 | this.value = nv; 11 | this.isNull = false; 12 | } 13 | 14 | T get(G)(G ifNull) { 15 | return this.isNull 16 | ? ifNull 17 | : this.value; 18 | } 19 | 20 | const(T) get(G)(G ifNull) const { 21 | return this.isNull 22 | ? ifNull 23 | : this.value; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /source/betterc/rbtree.d: -------------------------------------------------------------------------------- 1 | module betterc.rbtree; 2 | 3 | import betterc.functional : less, equal; 4 | version(WASM) { 5 | } else { 6 | import core.stdc.stdio; 7 | } 8 | 9 | @nogc nothrow @safe: 10 | 11 | struct Iterator(T) { 12 | @nogc nothrow @safe: 13 | private Node!(T)* current; 14 | 15 | this(Node!(T)* current) { 16 | this.current = current; 17 | } 18 | 19 | void opUnary(string s)() if(s == "++") { increment(); } 20 | void opUnary(string s)() if(s == "--") { decrement(); } 21 | ref T opUnary(string s)() if(s == "*") { return getData(); } 22 | 23 | void increment() { 24 | Node!(T)* y; 25 | if(null !is (y = this.current.link[true])) { 26 | while(y.link[false] !is null) { 27 | y = y.link[false]; 28 | } 29 | this.current = y; 30 | } else { 31 | y = this.current.parent; 32 | while(y !is null && this.current is y.link[true]) { 33 | this.current = y; 34 | y = y.parent; 35 | } 36 | this.current = y; 37 | } 38 | } 39 | 40 | ref T getData() { 41 | return this.current.getData(); 42 | } 43 | 44 | void decrement() { 45 | Node!(T)* y; 46 | if(null !is (y = this.current.link[false])) { 47 | while(y.link[true] !is null) { 48 | y = y.link[true]; 49 | } 50 | this.current = y; 51 | } else { 52 | y = this.current.parent; 53 | while(y !is null && this.current is y.link[false]) { 54 | this.current = y; 55 | y = y.parent; 56 | } 57 | this.current = y; 58 | } 59 | } 60 | 61 | bool isValid() const { 62 | return this.current !is null; 63 | } 64 | } 65 | 66 | struct Node(T) { 67 | @nogc nothrow @safe: 68 | T data; 69 | bool red; 70 | 71 | Node!(T)*[2] link; 72 | Node!(T)* parent; 73 | 74 | alias getData this; 75 | 76 | ref T getData() { 77 | return this.data; 78 | } 79 | 80 | bool validate(bool root, const Node!(T)* par = null) const { 81 | if(!root) { 82 | if(this.parent is null) { 83 | /*() @trusted { 84 | printf("%s %d %lu\n", __FILE__.ptr,__LINE__, 85 | cast(ulong)": parent is null".ptr); 86 | }(); 87 | */ 88 | return false; 89 | } 90 | if(this.parent !is par) { 91 | /*() @trusted { 92 | printf("%s %d %lu\n", __FILE__.ptr,__LINE__, 93 | cast(ulong)": parent is wrong".ptr); 94 | }(); 95 | */ 96 | return false; 97 | } 98 | } 99 | bool left = true; 100 | bool right = true; 101 | if(this.link[0] !is null) { 102 | assert(this.link[0].parent is &this); 103 | left = this.link[0].validate(false, &this); 104 | } 105 | if(this.link[1] !is null) { 106 | assert(this.link[1].parent is &this); 107 | right = this.link[1].validate(false, &this); 108 | } 109 | return left && right; 110 | } 111 | 112 | void print(int i) { 113 | version(WASM) { 114 | } else { 115 | foreach(it; 0 .. i) { 116 | () @trusted { 117 | printf(" "); 118 | }(); 119 | } 120 | () @trusted { 121 | printf("%lx %d %lx\n", cast(size_t)&this, this.red, 122 | cast(size_t)this.parent); 123 | }(); 124 | if(this.link[0] !is null) { 125 | this.link[0].print(i + 1); 126 | } 127 | if(this.link[1] !is null) { 128 | this.link[1].print(i + 1); 129 | } 130 | } 131 | } 132 | } 133 | 134 | struct RBTree(T, alias ls = less, alias eq = equal) { 135 | @nogc nothrow @safe: 136 | version(WASM) { 137 | void* malloc(size_t); 138 | void free(void*); 139 | } else { 140 | import core.stdc.stdlib : malloc, free; 141 | } 142 | 143 | Node!(T)* newNode(T data) { 144 | Node!(T)* ret = newNode(); 145 | ret.data = data; 146 | return ret; 147 | } 148 | 149 | Node!(T)* newNode() { 150 | Node!T* ret = 151 | () @trusted { return cast(Node!T*)malloc(Node!(T).sizeof); }(); 152 | ret.red = true; 153 | ret.parent = null; 154 | ret.link[0] = null; 155 | ret.link[1] = null; 156 | return ret; 157 | } 158 | 159 | void freeNode(Node!(T)* node) { 160 | if(node !is null) { 161 | () @trusted { free(cast(void*)node); }(); 162 | } 163 | } 164 | 165 | private static bool isRed(const Node!(T)* n) { 166 | return n !is null && n.red; 167 | } 168 | 169 | private static Node!(T)* singleRotate(Node!(T)* node, bool dir) { 170 | Node!(T)* save = node.link[!dir]; 171 | node.link[!dir] = save.link[dir]; 172 | if(node.link[!dir] !is null) { 173 | node.link[!dir].parent = node; 174 | } 175 | save.link[dir] = node; 176 | if(save.link[dir] !is null) { 177 | save.link[dir].parent = save; 178 | } 179 | node.red = true; 180 | save.red = false; 181 | return save; 182 | } 183 | 184 | private static Node!(T)* doubleRotate(Node!(T)* node, bool dir) { 185 | node.link[!dir] = singleRotate(node.link[!dir], !dir); 186 | if(node.link[!dir] !is null) { 187 | node.link[!dir].parent = node; 188 | } 189 | return singleRotate(node, dir); 190 | } 191 | 192 | private static int validate(Node!(T)* node, Node!(T)* parent) { 193 | version(WASM) { 194 | return 0; 195 | } else { 196 | if(node is null) { 197 | return 1; 198 | } else { 199 | if(node.parent !is parent) { 200 | () @trusted { 201 | printf("parent violation %d %d\n", node.parent is null, 202 | parent is null); 203 | }(); 204 | } 205 | if(node.link[0] !is null) { 206 | () @trusted { 207 | if(node.link[0].parent !is node) { 208 | printf("parent violation link wrong\n"); 209 | } 210 | }(); 211 | } 212 | if(node.link[1] !is null) { 213 | () @trusted { 214 | if(node.link[1].parent !is node) { 215 | printf("parent violation link wrong\n"); 216 | } 217 | }(); 218 | } 219 | 220 | Node!(T)* ln = node.link[0]; 221 | Node!(T)* rn = node.link[1]; 222 | 223 | if(isRed(node)) { 224 | if(isRed(ln) || isRed(rn)) { 225 | () @trusted { 226 | printf("Red violation\n"); 227 | }(); 228 | return 0; 229 | } 230 | } 231 | int lh = validate(ln, node); 232 | int rh = validate(rn, node); 233 | 234 | if((ln !is null && ln.data >= node.data) 235 | || (rn !is null && rn.data <= node.data)) 236 | { 237 | () @trusted { 238 | printf("Binary tree violation\n"); 239 | }(); 240 | return 0; 241 | } 242 | 243 | if(lh != 0 && rh != 0 && lh != rh) { 244 | () @trusted { 245 | printf("Black violation %d %d\n", lh, rh); 246 | }(); 247 | return 0; 248 | } 249 | 250 | if(lh != 0 && rh != 0) { 251 | return isRed(node) ? lh : lh +1; 252 | } else { 253 | return 0; 254 | } 255 | } 256 | } 257 | } 258 | 259 | bool validate() { 260 | return validate(this.root, null) != 0 261 | && this.root ? this.root.validate(true) : true; 262 | } 263 | 264 | Node!(T)* search(T data) { 265 | return search(this.root, data); 266 | } 267 | 268 | private Node!(T)* search(Node!(T)* node ,T data) { 269 | if(node is null) { 270 | return null; 271 | } else if(eq(node.data, data)) { 272 | return node; 273 | } else { 274 | bool dir = ls(node.data, data); 275 | return this.search(node.link[dir], data); 276 | } 277 | } 278 | 279 | bool remove(ref Iterator!(T) it, bool dir = true) { 280 | if(it.isValid()) { 281 | T value = *it; 282 | if(dir) 283 | it++; 284 | else 285 | it--; 286 | return this.remove(value); 287 | } else { 288 | return false; 289 | } 290 | } 291 | 292 | bool remove(T data) { 293 | bool done = false; 294 | bool succes = false; 295 | this.root = removeR(this.root, data, done, succes); 296 | if(this.root !is null) { 297 | this.root.red = false; 298 | this.root.parent = null; 299 | } 300 | if(succes) { 301 | this.size--; 302 | } 303 | return succes; 304 | } 305 | 306 | private Node!(T)* removeR(Node!(T)* node, T data, ref bool done, 307 | ref bool succes) { 308 | if(node is null) { 309 | done = true; 310 | } else { 311 | bool dir; 312 | if(eq(node.data, data)) { 313 | succes = true; 314 | if(node.link[0] is null || node.link[1] is null) { 315 | Node!(T)* save = node.link[node.link[0] is null]; 316 | 317 | if(isRed(node)) { 318 | done = true; 319 | } else if(isRed(save)) { 320 | save.red = false; 321 | done = true; 322 | } 323 | freeNode(node); 324 | return save; 325 | } else { 326 | Node!(T)* heir = node.link[0]; 327 | while(heir.link[1] !is null) { 328 | heir = heir.link[1]; 329 | } 330 | 331 | node.data = heir.data; 332 | data = heir.data; 333 | } 334 | } 335 | dir = ls(node.data, data); 336 | node.link[dir] = removeR(node.link[dir], data, done, succes); 337 | if(node.link[dir] !is null) { 338 | node.link[dir].parent = node; 339 | } 340 | 341 | if(!done) { 342 | node = removeBalance(node, dir, done); 343 | } 344 | } 345 | return node; 346 | } 347 | 348 | private Node!(T)* removeBalance(Node!(T)* node, bool dir, ref bool done) { 349 | Node!(T)* p = node; 350 | Node!(T)* s = node.link[!dir]; 351 | if(isRed(s)) { 352 | node = singleRotate(node, dir); 353 | s = p.link[!dir]; 354 | } 355 | 356 | if(s !is null) { 357 | if(!isRed(s.link[0]) && !isRed(s.link[1])) { 358 | if(isRed(p)) { 359 | done = true; 360 | } 361 | p.red = false; 362 | s.red = true; 363 | } else { 364 | bool save = p.red; 365 | bool newRoot = eq(node, p); 366 | 367 | if(isRed(s.link[!dir])) { 368 | p = singleRotate(p, dir); 369 | } else { 370 | p = doubleRotate(p, dir); 371 | } 372 | 373 | p.red = save; 374 | p.link[0].red = false; 375 | p.link[1].red = false; 376 | 377 | if(newRoot) { 378 | node = p; 379 | } else { 380 | node.link[dir] = p; 381 | if(node.link[dir] !is null) { 382 | node.link[dir].parent = node; 383 | } 384 | } 385 | 386 | done = true; 387 | } 388 | } 389 | return node; 390 | } 391 | 392 | bool insert(T data) { 393 | bool success; 394 | this.root = insertImpl(this.root, data, success); 395 | this.root.parent = null; 396 | this.root.red = false; 397 | return success; 398 | } 399 | 400 | private Node!(T)* insertImpl(Node!(T)* root, T data, ref bool success) { 401 | if(root is null) { 402 | root = newNode(data); 403 | this.size++; 404 | success = true; 405 | } else if(data != root.data) { 406 | bool dir = ls(root.data, data); 407 | 408 | root.link[dir] = insertImpl(root.link[dir], data, success); 409 | root.link[dir].parent = root; 410 | 411 | if(isRed(root.link[dir])) { 412 | if(isRed(root.link[!dir])) { 413 | /* Case 1 */ 414 | root.red = true; 415 | root.link[0].red = false; 416 | root.link[1].red = false; 417 | } else { 418 | /* Cases 2 & 3 */ 419 | if(isRed(root.link[dir].link[dir])) { 420 | root = singleRotate( root, !dir ); 421 | } else if(isRed(root.link[dir].link[!dir])) { 422 | root = doubleRotate (root, !dir); 423 | } 424 | } 425 | } 426 | } 427 | 428 | return root; 429 | } 430 | 431 | @property size_t length() const { 432 | return this.size; 433 | } 434 | 435 | Iterator!(T) begin() { 436 | Node!(T)* be = this.root; 437 | if(be is null) 438 | return Iterator!(T)(null); 439 | int count = 0; 440 | while(be.link[0] !is null) { 441 | be = be.link[0]; 442 | count++; 443 | } 444 | auto it = Iterator!(T)(be); 445 | return it; 446 | } 447 | 448 | Iterator!(T) end() { 449 | Node!(T)* end = this.root; 450 | if(end is null) 451 | return Iterator!(T)(null); 452 | while(end.link[1] !is null) 453 | end = end.link[1]; 454 | return Iterator!(T)(end); 455 | } 456 | 457 | Iterator!(T) searchIt(T data) { 458 | return Iterator!(T)(cast(Node!(T)*)search(data)); 459 | } 460 | 461 | bool isEmpty() const { 462 | return this.root is null; 463 | } 464 | 465 | void print() { 466 | if(this.root !is null) { 467 | this.root.print(0); 468 | } 469 | } 470 | 471 | private size_t size; 472 | private Node!(T)* root; 473 | } 474 | 475 | 476 | bool compare(T)(RBTree!(T) t, T[T] s) { 477 | foreach(it; s.values) { 478 | if(t.search(it) is null) { 479 | printf("%d %s\n", __LINE__, " size wrong".ptr); 480 | return false; 481 | } 482 | } 483 | return true; 484 | } 485 | 486 | unittest { 487 | immutable int[41] a = [2811, 1089, 3909, 3593, 1980, 2863, 676, 258, 2499, 3147, 488 | 3321, 3532, 3009, 1526, 2474, 1609, 518, 1451, 796, 2147, 56, 414, 3740, 489 | 2476, 3297, 487, 1397, 973, 2287, 2516, 543, 3784, 916, 2642, 312, 1130, 490 | 756, 210, 170, 3510, 987]; 491 | immutable int[11] b = [0,1,2,3,4,5,6,7,8,9,10]; 492 | immutable int[11] c = [10,9,8,7,6,5,4,3,2,1,0]; 493 | immutable int[12] d = [10,9,8,7,6,5,4,3,2,1,0,11]; 494 | immutable int[12] e = [0,1,2,3,4,5,6,7,8,9,10,-1]; 495 | immutable int[10] f = [11,1,2,3,4,5,6,7,8,0]; 496 | test1(a[]); 497 | test1(b[]); 498 | test1(c[]); 499 | test1(d[]); 500 | test1(e[]); 501 | test1(f[]); 502 | test2(a[]); 503 | } 504 | 505 | private void test1(scope immutable int[] lots) { 506 | RBTree!(int) a; 507 | foreach(idx, it; lots) { 508 | assert(a.insert(it)); 509 | auto iter = a.searchIt(it); 510 | assert(iter.isValid()); 511 | assert(iter.getData() == it); 512 | assert(a.length == idx+1); 513 | foreach(jt; lots[0..idx+1]) { 514 | assert(a.search(jt)); 515 | } 516 | assert(a.validate()); 517 | foreach(jt; lots[0 .. idx]) { 518 | assert(a.search(jt) !is null); 519 | } 520 | 521 | Iterator!(int) ait = a.begin(); 522 | size_t cnt = 0; 523 | while(ait.isValid()) { 524 | assert(a.search(*ait)); 525 | ait++; 526 | cnt++; 527 | } 528 | assert(cnt == a.length); 529 | 530 | ait = a.end(); 531 | cnt = 0; 532 | while(ait.isValid()) { 533 | assert(a.search(*ait)); 534 | ait--; 535 | cnt++; 536 | } 537 | assert(cnt == a.length); 538 | 539 | } 540 | //writeln(__LINE__); 541 | foreach(idx, it; lots) { 542 | assert(a.remove(it)); 543 | assert(a.validate()); 544 | foreach(jt; lots[0..idx+1]) { 545 | assert(!a.search(jt)); 546 | } 547 | foreach(jt; lots[idx+1..$]) { 548 | assert(a.search(jt)); 549 | } 550 | Iterator!(int) ait = a.begin(); 551 | size_t cnt = 0; 552 | while(ait.isValid()) { 553 | assert(a.search(*ait)); 554 | ait++; 555 | cnt++; 556 | } 557 | assert(cnt == a.length); 558 | 559 | ait = a.end(); 560 | cnt = 0; 561 | while(ait.isValid()) { 562 | assert(a.search(*ait)); 563 | ait--; 564 | cnt++; 565 | } 566 | assert(cnt == a.length); 567 | } 568 | assert(a.length == 0); 569 | //writeln(__LINE__); 570 | } 571 | 572 | private void test2(scope immutable int[] lot) { 573 | for(int i = 0; i < lot.length; i++) { 574 | RBTree!(int) itT; 575 | foreach(it; lot) { 576 | itT.insert(it); 577 | } 578 | assert(itT.length == lot.length); 579 | Iterator!(int) be = itT.begin(); 580 | while(be.isValid()) { 581 | assert(itT.remove(be, true)); 582 | } 583 | assert(itT.length == 0); 584 | } 585 | 586 | for(int i = 0; i < lot.length; i++) { 587 | RBTree!(int) itT; 588 | foreach(it; lot) { 589 | itT.insert(it); 590 | } 591 | assert(itT.length == lot.length); 592 | Iterator!(int) be = itT.end(); 593 | while(be.isValid()) { 594 | assert(itT.remove(be, false)); 595 | } 596 | assert(itT.length == 0); 597 | } 598 | } 599 | -------------------------------------------------------------------------------- /source/betterc/str.d: -------------------------------------------------------------------------------- 1 | module betterc.str; 2 | version(WASM) { 3 | void* realloc(void*, size_t) nothrow @safe pure; 4 | void free(void*) nothrow @safe pure; 5 | } else { 6 | import core.stdc.stdlib : realloc, free; 7 | } 8 | 9 | @safe: 10 | struct String { 11 | struct Payload { 12 | char* ptr; 13 | long refCnt; 14 | size_t capacity; 15 | } 16 | 17 | struct StringPayloadHandler { 18 | static Payload* make() @trusted { 19 | Payload* pl; 20 | pl = cast(Payload*)realloc(pl, Payload.sizeof); 21 | pl.ptr = null; 22 | pl.capacity = 0; 23 | pl.refCnt = 1; 24 | 25 | return pl; 26 | } 27 | 28 | static void allocate(Payload* pl, in size_t s) @trusted { 29 | assert(s != 0); 30 | if(s >= pl.capacity) { 31 | pl.ptr = cast(char*)realloc(pl.ptr, s * char.sizeof); 32 | pl.capacity = s; 33 | } 34 | } 35 | 36 | static void deallocate(Payload* pl) @trusted { 37 | realloc(pl.ptr, 0); 38 | pl.capacity = 0; 39 | realloc(pl, 0); 40 | } 41 | 42 | static void incrementRefCnt(Payload* pl) { 43 | if(pl !is null) { 44 | ++(pl.refCnt); 45 | } 46 | } 47 | 48 | static void decrementRefCnt(Payload* pl) { 49 | if(pl !is null) { 50 | --(pl.refCnt); 51 | if(pl.refCnt == 0) { 52 | StringPayloadHandler.deallocate(pl); 53 | } 54 | } 55 | } 56 | } 57 | 58 | this(string s) { 59 | this.assign(s); 60 | } 61 | 62 | this(String n) { 63 | this.assign(n); 64 | } 65 | 66 | this(const String n) @trusted { 67 | this.assign(cast(string)n.storePtr()[0 .. n.length]); 68 | } 69 | 70 | this(this) { 71 | if(this.large !is null) { 72 | StringPayloadHandler.incrementRefCnt(this.large); 73 | } 74 | } 75 | 76 | ~this() @trusted { 77 | if(this.large !is null) { 78 | StringPayloadHandler.decrementRefCnt(cast(Payload*)this.large); 79 | } 80 | } 81 | 82 | 83 | private void assign(string input) @trusted { 84 | if(input.length > SmallSize) { 85 | this.allocate(input.length); 86 | } 87 | 88 | this.storePtr()[0 .. input.length] = input; 89 | this.len = input.length; 90 | } 91 | 92 | private void assign(String n) @trusted { 93 | if(this.large !is null) { 94 | StringPayloadHandler.decrementRefCnt(this.large); 95 | } 96 | 97 | if(n.large !is null) { 98 | this.large = n.large; 99 | StringPayloadHandler.incrementRefCnt(this.large); 100 | } else { 101 | this.small = n.small; 102 | } 103 | 104 | this.offset = n.offset; 105 | this.len = n.length; 106 | } 107 | 108 | private void allocate(const size_t newLen) @trusted { 109 | if(newLen > SmallSize) { 110 | if(this.large is null) { 111 | this.large = StringPayloadHandler.make(); 112 | } 113 | StringPayloadHandler.allocate(this.large, newLen); 114 | } 115 | } 116 | 117 | private bool isSmall() const nothrow { 118 | return this.large is null; 119 | } 120 | 121 | private char* storePtr() scope return @trusted { 122 | if(this.isSmall()) { 123 | return this.small.ptr; 124 | } else { 125 | return this.large.ptr; 126 | } 127 | } 128 | 129 | private const(char)* storePtr() scope return const @trusted { 130 | if(this.isSmall()) { 131 | return this.small.ptr; 132 | } else { 133 | return this.large.ptr; 134 | } 135 | } 136 | 137 | private void moveToFront() { 138 | if(this.offset > 0) { 139 | immutable len = this.length; 140 | if(this.isSmall()) { 141 | for(int i = 0; i < len; ++i) { 142 | this.small[i] = this.small[this.offset + i]; 143 | } 144 | } else { 145 | for(int i = 0; i < len; ++i) { 146 | (() @trusted => 147 | this.large.ptr[i] = this.large.ptr[this.offset + i] 148 | )(); 149 | } 150 | } 151 | this.offset = 0; 152 | this.len = len; 153 | } 154 | } 155 | 156 | private char[] largePtr(in size_t low, in size_t high) @trusted { 157 | return this.large.ptr[low .. high]; 158 | } 159 | 160 | void opAssign(inout(char)[] n) { 161 | if(this.isSmall() && n.length < SmallSize) { 162 | this.small[0 .. n.length] = n; 163 | } else { 164 | if(this.large is null || this.large.refCnt > 1) { 165 | this.large = StringPayloadHandler.make(); 166 | } 167 | 168 | StringPayloadHandler.allocate(this.large, n.length); 169 | this.largePtr(0, n.length)[] = n; 170 | } 171 | 172 | this.len = n.length; 173 | this.offset = 0; 174 | } 175 | 176 | void opAssign(typeof(this) n) { 177 | this.assign(n); 178 | } 179 | 180 | // properties 181 | 182 | @property bool empty() const nothrow { 183 | return this.offset == this.len; 184 | } 185 | 186 | @property size_t length() const nothrow { 187 | return cast(size_t)(this.len - this.offset); 188 | } 189 | 190 | bool opEquals(T)(T other) const 191 | if(is(T == string) || is(T == String) || is(T == const(String))) 192 | { 193 | if(this.length == other.length) { 194 | for(size_t i = 0; i < this.length; ++i) { 195 | if(this[i] != other[i]) { 196 | return false; 197 | } 198 | } 199 | 200 | return true; 201 | } else { 202 | return false; 203 | } 204 | } 205 | 206 | @property char front() const @trusted { 207 | assert(!this.empty); 208 | return this.storePtr()[this.offset .. this.len][0]; 209 | } 210 | 211 | @property char back() const @trusted { 212 | assert(!this.empty); 213 | return this.storePtr()[this.offset .. this.len][$ - 1]; 214 | } 215 | 216 | @property char opIndex(const size_t idx) const @trusted { 217 | assert(!this.empty); 218 | assert(idx < this.len - this.offset); 219 | 220 | return this.storePtr()[this.offset .. this.len][idx]; 221 | } 222 | 223 | typeof(this) opSlice() { 224 | return this; 225 | } 226 | 227 | typeof(this) opSlice(in size_t low, in size_t high) @trusted { 228 | //assert(low <= high); 229 | //assert(high < this.length); 230 | 231 | if(this.isSmall()) { 232 | return String( 233 | cast(immutable(char)[])this.small[ 234 | this.offset + low .. this.offset + high 235 | ] 236 | ); 237 | } else { 238 | auto ret = String(this); 239 | ret.offset += low; 240 | ret.len = this.offset + high; 241 | return ret; 242 | } 243 | } 244 | 245 | void popFront() { 246 | const auto l = stride(this.isSmall() 247 | ? this.small[this.offset .. this.len] 248 | : this.largePtr(this.offset, this.len)); 249 | 250 | assert(!l.isNull); 251 | this.offset += l.get(0U); 252 | } 253 | 254 | void popBack() { 255 | const auto l = strideBack(this.isSmall() 256 | ? this.small[this.offset .. this.len] 257 | : this.largePtr(this.offset, this.len)); 258 | 259 | assert(!l.isNull); 260 | this.len -= l.get(0U); 261 | } 262 | 263 | @property String dup() const { 264 | return String(this); 265 | } 266 | 267 | /// This will malloc the string and leak in betterC 268 | @property string idup() const @trusted { 269 | char* p; 270 | p = cast(char*)realloc(p, (this.length + 1) * char.sizeof); 271 | foreach(idx; 0 .. this.length) { 272 | p[idx] = this[idx]; 273 | } 274 | p[this.length] = '\0'; 275 | return cast(string)p[0 .. this.length]; 276 | } 277 | 278 | int opCmp(ref const(String) other) const { 279 | immutable len = this.length <= other.length 280 | ? this.length 281 | : other.length; 282 | 283 | foreach (const u; 0 .. len) { 284 | if(this[u] != other[u]) { 285 | return this[u] > other[u] ? 1 : -1; 286 | } 287 | } 288 | return this.length < other.length 289 | ? -1 290 | : (this.length > other.length); 291 | } 292 | 293 | enum SmallSize = 16; 294 | ptrdiff_t offset; 295 | ptrdiff_t len; 296 | Payload* large; 297 | char[SmallSize] small; 298 | } 299 | 300 | import betterc.nullable; 301 | 302 | Nullable!uint stride(inout(char)[] str) { 303 | return stride(str, 0); 304 | } 305 | 306 | Nullable!uint stride(inout(char)[] str, size_t index) { 307 | assert(index < str.length, "Past the end of the UTF-8 sequence"); 308 | immutable c = str[index]; 309 | 310 | if (c < 0x80) 311 | return Nullable!uint(1); 312 | else 313 | return strideImpl(c, index); 314 | } 315 | 316 | private Nullable!uint strideImpl(char c, size_t index) @safe pure nothrow 317 | in { assert(c & 0x80); } 318 | do 319 | { 320 | import core.bitop : bsr; 321 | immutable msbs = 7 - bsr((~uint(c)) & 0xFF); 322 | if (c == 0xFF || msbs < 2 || msbs > 4) { 323 | return Nullable!(uint).init; 324 | } 325 | return Nullable!uint(msbs); 326 | } 327 | 328 | Nullable!uint strideBack(inout(char)[] str) { 329 | return strideBack(str, str.length - 1); 330 | } 331 | 332 | Nullable!uint strideBack(inout(char)[] str , size_t index) { 333 | assert(index <= str.length, "Past the end of the UTF-8 sequence"); 334 | assert(index > 0, "Not the end of the UTF-8 sequence"); 335 | 336 | if ((str[index-1] & 0b1100_0000) != 0b1000_0000) 337 | return typeof(return)(1); 338 | 339 | if (index >= 4) { //single verification for most common case 340 | foreach (i; 2 .. 5) { 341 | if((str[index-i] & 0b1100_0000) != 0b1000_0000) { 342 | return typeof(return)(i); 343 | } 344 | } 345 | } else { 346 | foreach(i; 2 .. 4) { 347 | if(index >= i && (str[index-i] & 0b1100_0000) != 0b1000_0000) { 348 | return typeof(return)(i); 349 | } 350 | } 351 | } 352 | return Nullable!(uint).init; 353 | } 354 | 355 | unittest { 356 | string hw = "Hello World"; 357 | auto s = String(hw); 358 | assert(s.length == hw.length); 359 | } 360 | 361 | private @property bool empty(string s) { 362 | return s.length == 0; 363 | } 364 | 365 | private @property char front(string s) { 366 | assert(!s.empty); 367 | return s[0]; 368 | } 369 | 370 | private @property char back(string s) { 371 | assert(!s.empty); 372 | return s[$ - 1]; 373 | } 374 | 375 | private void popFront(ref string s) { 376 | Nullable!uint l = stride(s); 377 | s = s[l.get(0U) .. $]; 378 | } 379 | 380 | private void popBack(ref string s) { 381 | Nullable!uint l = strideBack(s); 382 | s = s[0 .. $ - l.get(0U)]; 383 | } 384 | 385 | unittest { 386 | auto s = String("Hello World"); 387 | } 388 | 389 | unittest { 390 | const a = String("Hello World"); 391 | const b = String("hello World"); 392 | assert(a.opCmp(b) == -1); 393 | assert(b.opCmp(a) == 1); 394 | assert(a.opCmp(a) == 0); 395 | assert(b.opCmp(b) == 0); 396 | } 397 | 398 | unittest { 399 | const a = String("Hello Worl"); 400 | const b = String("hello World"); 401 | assert(a.opCmp(b) == -1); 402 | assert(b.opCmp(a) == 1); 403 | assert(a.opCmp(a) == 0); 404 | assert(b.opCmp(b) == 0); 405 | } 406 | 407 | unittest { 408 | const a = String("Hello Worl"); 409 | const b = String("hello World"); 410 | 411 | assert(a == a); 412 | assert(a != b); 413 | assert(b != a); 414 | } 415 | 416 | unittest { 417 | const a = String("Hello World"); 418 | const b = String("Hello Worlz"); 419 | 420 | assert(a == a); 421 | assert(b == b); 422 | assert(a != b); 423 | assert(b != a); 424 | assert(a.opCmp(b) == -1); 425 | assert(b.opCmp(a) == 1); 426 | assert(a.opCmp(a) == 0); 427 | assert(b.opCmp(b) == 0); 428 | } 429 | 430 | unittest { 431 | import core.stdc.stdio; 432 | enum string[] strs = ["","ABC", "HellWorld", "", "Foobar", 433 | "HellWorldHellWorldHellWorldHellWorldHellWorldHellWorldHellWorldHellWorld", 434 | "ABCD", "Hello", "HellWorldHellWorld", "ölleä", 435 | "hello\U00010143\u0100\U00010143", "£$€¥", "öhelloöö" 436 | ]; 437 | 438 | foreach(idx, strL; strs) { 439 | auto str = strL; 440 | auto s = String(str); 441 | 442 | assert(s.length == str.length); 443 | assert(s.empty == str.empty); 444 | assert(s == str); 445 | 446 | auto istr = s.idup(); 447 | assert(str == istr); 448 | () @trusted { 449 | free(cast(void*)istr.ptr); 450 | }(); 451 | 452 | foreach(it; strs) { 453 | auto cmpS = cast(string)(it); 454 | auto itStr = String(cmpS); 455 | 456 | if(cmpS == str) { 457 | assert(s == cmpS); 458 | assert(s == itStr); 459 | } else { 460 | assert(s != cmpS); 461 | assert(s != itStr); 462 | } 463 | } 464 | 465 | if(s.empty) { // if str is empty we do not need to test access 466 | continue; //methods 467 | } 468 | 469 | assert(s.front == str.front); 470 | assert(s.back == str.back); 471 | assert(s[0] == str[0]); 472 | for(size_t i = 0; i < str.length; ++i) { 473 | assert(str[i] == s[i]); 474 | } 475 | 476 | for(size_t it = 0; it < str.length; ++it) { 477 | for(size_t jt = it; jt < str.length; ++jt) { 478 | auto ss = s[it .. jt]; 479 | auto strc = str[it .. jt]; 480 | 481 | assert(ss.length == strc.length); 482 | assert(ss.empty == strc.empty); 483 | 484 | for(size_t k = 0; k < ss.length; ++k) { 485 | assert(ss[k] == strc[k]); 486 | } 487 | } 488 | } 489 | 490 | String t; 491 | assert(t.empty); 492 | 493 | t = str; 494 | assert(s == t); 495 | assert(!t.empty); 496 | assert(t.front == str.front); 497 | assert(t.back == str.back); 498 | assert(t[0] == str[0]); 499 | assert(t.length == str.length); 500 | 501 | auto tdup = t.dup; 502 | assert(!tdup.empty); 503 | assert(tdup.front == str.front); 504 | assert(tdup.back == str.back); 505 | assert(tdup[0] == str[0]); 506 | assert(tdup.length == str.length); 507 | 508 | istr = t.idup(); 509 | assert(str == istr); 510 | () @trusted { 511 | free(cast(void*)istr.ptr); 512 | }(); 513 | 514 | if(tdup.large !is null) { 515 | assert(tdup.large.refCnt == 1); 516 | } 517 | 518 | s = t; 519 | assert(!s.empty); 520 | assert(s.front == str.front); 521 | assert(s.back == str.back); 522 | assert(s[0] == str[0]); 523 | assert(s.length == str.length); 524 | 525 | auto r = String(s); 526 | assert(!r.empty); 527 | assert(r.front == str.front); 528 | assert(r.back == str.back); 529 | assert(r[0] == str[0]); 530 | assert(r.length == str.length); 531 | 532 | auto g = r[]; 533 | assert(!g.empty); 534 | assert(g.front == str.front); 535 | assert(g.back == str.back); 536 | assert(g[0] == str[0]); 537 | assert(g.length == str.length); 538 | 539 | auto strC = str; 540 | auto strC2 = str; 541 | assert(!strC.empty); 542 | assert(!strC2.empty); 543 | 544 | r.popFront(); 545 | str.popFront(); 546 | assert(str.front == r.front); 547 | assert(s != r); 548 | 549 | r.popBack(); 550 | str.popBack(); 551 | assert(str.back == r.back); 552 | assert(str.front == r.front); 553 | 554 | assert(!strC.empty); 555 | assert(!s.empty); 556 | while(!strC.empty && !s.empty) { 557 | assert(strC.front == s.front); 558 | assert(strC.back == s.back); 559 | assert(strC.length == s.length); 560 | for(size_t i = 0; i < strC.length; ++i) { 561 | assert(strC[i] == s[i]); 562 | } 563 | 564 | strC.popFront(); 565 | s.popFront(); 566 | } 567 | 568 | assert(strC.empty); 569 | assert(s.empty); 570 | 571 | assert(!strC2.empty); 572 | assert(!t.empty); 573 | while(!strC2.empty && !t.empty) { 574 | assert(strC2.front == t.front); 575 | assert(strC2.back == t.back); 576 | assert(strC2.length == t.length); 577 | for(size_t i = 0; i < strC2.length; ++i) { 578 | assert(strC2[i] == t[i]); 579 | } 580 | 581 | strC2.popFront(); 582 | t.popFront(); 583 | string idup2 = t.idup; 584 | assert(t == idup2); 585 | assert(t == strC2, t.idup); 586 | () @trusted { 587 | free(cast(void*)idup2.ptr); 588 | }(); 589 | 590 | t.moveToFront(); 591 | idup2 = t.idup; 592 | assert(t == idup2); 593 | assert(t == strC2, t.idup); 594 | 595 | () @trusted { 596 | free(cast(void*)idup2.ptr); 597 | }(); 598 | } 599 | 600 | assert(strC2.empty); 601 | assert(t.empty); 602 | } 603 | } 604 | -------------------------------------------------------------------------------- /source/betterc/sumtypetest.d: -------------------------------------------------------------------------------- 1 | module betterc.sumtype; 2 | 3 | /* 4 | public import sumtype; 5 | 6 | @nogc @safe nothrow: 7 | 8 | unittest { 9 | double abs(double a) { 10 | return a < 0.0 ? -a : a; 11 | } 12 | 13 | bool approxEqual(double a, double b) { 14 | return a < b ? abs(b - a) < 0.001 : abs(a - b) < 0.001; 15 | } 16 | 17 | struct Fahrenheit { double degrees; } 18 | struct Celsius { double degrees; } 19 | struct Kelvin { double degrees; } 20 | 21 | alias Temperature = SumType!(Fahrenheit, Celsius, Kelvin); 22 | 23 | // Construct from any of the member types. 24 | Temperature t1; 25 | Temperature t2; 26 | Temperature t3; 27 | 28 | () @trusted { 29 | t1 = Fahrenheit(98.6); 30 | t2 = Celsius(100); 31 | t3 = Kelvin(273); 32 | }(); 33 | 34 | // Use pattern matching to access the value. 35 | Fahrenheit toFahrenheit(Temperature t) pure @safe @nogc nothrow 36 | { 37 | return Fahrenheit( 38 | t.match!( 39 | (Fahrenheit f) => f.degrees, 40 | (Celsius c) => c.degrees * 9.0/5 + 32, 41 | (Kelvin k) => k.degrees * 9.0/5 - 459.4 42 | ) 43 | ); 44 | } 45 | 46 | assert(approxEqual(toFahrenheit(t1).degrees, 98.6)); 47 | assert(approxEqual(toFahrenheit(t2).degrees, 212)); 48 | assert(approxEqual(toFahrenheit(t3).degrees, 32)); 49 | } 50 | */ 51 | --------------------------------------------------------------------------------