1 module reversineer.strings; 2 3 private ubyte[] writeTableString(bool expand, Table)(ubyte[] data, const Table table, string str, out size_t index) @safe pure if (is(Table : const(string)[ubyte]) || is(Table : const(string)[char])) { 4 import std.algorithm.searching : startsWith; 5 import std.exception : enforce; 6 while (str.length > 0) { 7 bool found; 8 foreach (k, v; table) { 9 if (str.startsWith(v)) { 10 found = true; 11 str = str[v.length .. $]; 12 if (index >= data.length) { 13 enforce(expand, "String too large"); 14 data.length = index + 1; 15 } 16 data[index++] = k; 17 break; 18 } 19 } 20 assert(found, "Table does not contain anything matching '"~str~"'"); 21 } 22 return data; 23 } 24 25 align(1) struct SimpleChar(alias table) { 26 align(1): 27 ubyte val; 28 string toChar() const @safe { 29 import std.format : format; 30 if (val in table) { 31 return table[val]; 32 } else { 33 return format!"[%02X]"(val); 34 } 35 } 36 } 37 38 @safe pure unittest { 39 SimpleChar!(['a': "bb"]) c; 40 assert(c.toChar == "[00]"); 41 } 42 43 align(1) struct SimpleString(alias table, ubyte terminator, size_t Length) { 44 align(1): 45 ubyte[Length] str; 46 size_t length() const @safe { 47 return Length; 48 } 49 string toString() const @safe { 50 string result; 51 foreach (chr; str) { 52 if (chr == terminator) { 53 break; 54 } 55 result ~= SimpleChar!table(chr).toChar(); 56 } 57 return result; 58 } 59 void opAssign(const string input) @safe { 60 str[] = terminator; 61 foreach (i, inChar; input) { 62 bool found; 63 foreach (k, v; table) { 64 if (v == [inChar]) { 65 found = true; 66 str[i] = k; 67 break; 68 } 69 } 70 assert(found, "String does not support character '"~inChar~"'"); 71 } 72 } 73 string toSiryulType()() @safe { 74 return toString(); 75 } 76 static SimpleString fromSiryulType()(string val) @safe { 77 SimpleString str; 78 str = val; 79 return str; 80 } 81 } 82 83 struct SimpleStringDynamic(alias table, ubyte terminator) { 84 import std.algorithm.searching : countUntil; 85 ubyte[] str; 86 size_t length() const @safe { 87 return str.countUntil!(x => x == terminator); 88 } 89 string toString() const @safe { 90 string result; 91 foreach (chr; str) { 92 if (chr == terminator) { 93 break; 94 } 95 result ~= SimpleChar!table(chr).toChar(); 96 } 97 return result; 98 } 99 void opAssign(string input) @safe { 100 size_t index; 101 str = writeTableString!true(str[], table, input, index); 102 str.length = index; 103 } 104 string toSiryulType()() @safe { 105 return toString(); 106 } 107 static SimpleStringDynamic fromSiryulType()(string val) @safe { 108 SimpleStringDynamic str; 109 str = val; 110 return str; 111 } 112 } 113 114 @safe pure unittest { 115 const str = SimpleStringDynamic!(['a': "bb"], 0)(['a', 'a', 'a', 0]); 116 assert(str.toString == "bbbbbb"); 117 SimpleStringDynamic!(['a': "bb"], 0) tmp; 118 tmp = "bbbbbbbb"; 119 assert(tmp.str == ['a', 'a', 'a', 'a']); 120 tmp = "bb"; 121 assert(tmp.str == ['a']); 122 immutable string[ubyte] testTable = [0x01: "abc"]; 123 const str2 = SimpleStringDynamic!(testTable, 0)([1, 1, 1, 0]); 124 assert(str2.toString == "abcabcabc"); 125 } 126 127 align(1) struct SimpleStrings(alias table, ubyte terminator, size_t Length) { 128 align(1): 129 ubyte[Length] str; 130 size_t length() const @safe { 131 return Length; 132 } 133 string[] toString() const @safe { 134 import std.algorithm.iteration : joiner, map, splitter; 135 string[] result; 136 string buf; 137 foreach (chr; str) { 138 if (chr == terminator) { 139 result ~= buf; 140 buf = ""; 141 } else { 142 buf ~= SimpleChar!table(chr).toChar(); 143 } 144 } 145 if (buf.length > 0) { 146 result ~= buf; 147 } 148 return result; 149 } 150 void opAssign(const string input) @safe { 151 str[] = terminator; 152 size_t _; 153 writeTableString!false(str[0 .. $], table, input, _); 154 } 155 void opAssign(const string[] input) @safe { 156 str[] = terminator; 157 size_t offset; 158 foreach (inputStr; input) { 159 writeTableString!false(str[offset .. $], table, inputStr, offset); 160 offset++; 161 } 162 } 163 auto opSlice() { 164 import std.algorithm.iteration : map, splitter; 165 return str[].splitter(terminator).map!(x => SimpleStringDynamic!(table, terminator)(x)); 166 } 167 string[] toSiryulType()() @safe { 168 return toString(); 169 } 170 static SimpleStrings fromSiryulType()(string[] val) @safe { 171 SimpleStrings str; 172 str = val; 173 return str; 174 } 175 } 176 177 @safe pure unittest { 178 import std.conv : text; 179 const strings = SimpleStrings!(['a': "bb"], 0, 3)(['a', 'a', 'a']); 180 assert(strings.toString == ["bbbbbb"]); 181 SimpleStrings!(['a': "bb"], 0, 10) test; 182 test = ["bb", "bb"]; 183 assert(test.toString == ["bb", "bb", "", "", "", "", "", ""]); 184 }