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 }