1 module bc..string; 2 3 public import bc.string.ascii; 4 public import bc.string.conv; 5 public import bc.string.format; 6 public import bc.string.numeric; 7 public import bc.string.string; 8 9 import core.stdc.stdio; 10 11 version (CI_MAIN) 12 { 13 // workaround for dub not supporting unittests with betterC 14 version (D_BetterC) 15 { 16 extern(C) int main() 17 { 18 version (unittest) 19 { 20 import std.meta : AliasSeq; 21 22 alias modules = AliasSeq!(bc..string.ascii, bc..string.conv, bc..string.format, bc..string.numeric, bc..string.string); 23 static foreach (m; modules) 24 { 25 static foreach(u; __traits(getUnitTests, m)) { 26 static if (__traits(getAttributes, u).length) 27 printf("unittest %s:%d | '" ~ __traits(getAttributes, u)[0] ~ "'\n", __traits(getLocation, u)[0].ptr, __traits(getLocation, u)[1]); 28 else 29 printf("unittest %s:%d\n", __traits(getLocation, u)[0].ptr, __traits(getLocation, u)[1]); 30 u(); 31 } 32 } 33 debug printf("All unit tests have been run successfully.\n"); 34 return 0; 35 } 36 else return rtTest(); 37 } 38 39 // workaround for linking error: `undefined reference to `_D4core8internal5array8equality__T8__equalsTaTaZQoFNaNbNiNeMxAaMxQeZb'` 40 bool equals(S1, S2)(S1 a, S2 b) 41 { 42 if (a.length != b.length) return false; 43 import core.stdc.string : strncmp; 44 return strncmp(a.ptr, b.ptr, a.length) == 0; 45 } 46 } 47 else 48 { 49 int main() 50 { 51 version (unittest) return 0; // run automagically 52 else return rtTest(); 53 } 54 55 bool equals(S1, S2)(S1 a, S2 b) { return a == b; } 56 } 57 58 int rtTest()() 59 { 60 // just a compilation test - same as in the README to be sure it actually works.. 61 62 import bc.core.memory; 63 import bc.internal.utf : byCodeUnit; 64 import core.stdc.string : strlen; 65 import std.algorithm : filter; 66 import std.range : chunks; 67 68 assert(nogcFormat!"abcd abcd".equals("abcd abcd")); 69 assert(nogcFormat!"123456789a".equals("123456789a")); 70 version (D_NoBoundsChecks) {} 71 else version (D_BetterC) {} 72 else 73 { 74 () @trusted 75 { 76 import core.exception : RangeError; 77 import std.exception : assertThrown; 78 char[5] buf; 79 version (assert) assertThrown!RangeError(buf.nogcFormatTo!"123412341234"); 80 }(); 81 } 82 83 // literal escape 84 assert(nogcFormat!"123 %%".equals("123 %")); 85 assert(nogcFormat!"%%%%".equals("%%")); 86 87 // %d 88 assert(nogcFormat!"%d"(1234).equals("1234")); 89 assert(nogcFormat!"%4d"(42).equals(" 42")); 90 assert(nogcFormat!"%04d"(42).equals("0042")); 91 assert(nogcFormat!"%04d"(-42).equals("-042")); 92 assert(nogcFormat!"ab%dcd"(1234).equals("ab1234cd")); 93 assert(nogcFormat!"ab%d%d"(1234, 56).equals("ab123456")); 94 95 // %x 96 assert(nogcFormat!"0x%x"(0x1234).equals("0x1234")); 97 98 // %p 99 assert(nogcFormat!("%p")(0x1234).equals("0000000000001234")); 100 101 // %s 102 assert(nogcFormat!"12345%s"("12345").equals("1234512345")); 103 assert(nogcFormat!"12345%s"(12345).equals("1234512345")); 104 enum Floop {XXX, YYY, ZZZ} 105 assert(nogcFormat!"12345%s"(Floop.YYY).equals("12345YYY")); 106 char[4] str = "foo\0"; 107 assert(nogcFormat!"%s"(&str[0]).equals("foo")); 108 109 version (D_BetterC) {} // can't import std.uuid in betterC 110 else 111 { 112 import std.uuid; 113 assert(nogcFormat!"%s"( 114 parseUUID("22390768-cced-325f-8f0f-cfeaa19d0ccd")) 115 .equals("22390768-cced-325f-8f0f-cfeaa19d0ccd")); 116 } 117 118 // array format 119 version (D_BetterC) 120 { 121 int[] arr = (cast(int*)enforceMalloc(int.sizeof*10))[0..10]; 122 foreach (i; 0..10) arr[i] = i; 123 scope (exit) pureFree(arr.ptr); 124 } 125 else auto arr = [0,1,2,3,4,5,6,7,8,9]; 126 127 assert(nogcFormat!"foo %(%d %)"(arr[1..4]).equals("foo 1 2 3")); 128 assert(nogcFormat!"foo %-(%d %)"(arr[1..4]).equals("foo 1 2 3")); 129 assert(nogcFormat!"foo %(-%d-%|, %)"(arr[1..4]).equals("foo -1-, -2-, -3-")); 130 assert(nogcFormat!"%(0x%02x %)"(arr[1..4]).equals("0x01 0x02 0x03")); 131 assert(nogcFormat!"%(%(%d %)\n%)"(arr[1..$].chunks(3)).equals("1 2 3\n4 5 6\n7 8 9")); 132 133 // range format 134 auto r = arr.filter!(a => a < 5); 135 assert(nogcFormat!"%s"(r).equals("[0, 1, 2, 3, 4]")); 136 137 // Arg num 138 assert(!__traits(compiles, nogcFormat!"abc"(5))); 139 assert(!__traits(compiles, nogcFormat!"%d"())); 140 assert(!__traits(compiles, nogcFormat!"%d a %d"(5))); 141 142 // Format error 143 assert(!__traits(compiles, nogcFormat!"%"())); 144 assert(!__traits(compiles, nogcFormat!"abcd%d %"(15))); 145 assert(!__traits(compiles, nogcFormat!"%$"(1))); 146 assert(!__traits(compiles, nogcFormat!"%d"("hello"))); 147 assert(!__traits(compiles, nogcFormat!"%x"("hello"))); 148 149 assert(nogcFormat!"Hello %s"(5).equals("Hello 5")); 150 151 struct Foo { int x, y; } 152 assert(nogcFormat!("Hello %s")(Foo(1, 2)).equals("Hello Foo(x=1, y=2)")); 153 154 version (D_BetterC) 155 { 156 struct Nullable(T) // can't be instanciated in betterC - fake just for the UT 157 { 158 T get() { return T.init; } 159 bool isNull() { return true; } 160 void nullify() {} 161 } 162 } 163 else import std.typecons : Nullable; 164 struct Msg { Nullable!string foo; } 165 assert(nogcFormat!"%s"(Msg.init).equals("Msg(foo=null)")); 166 167 RCString s = "abcd"; 168 assert(nogcFormat!"%s"(s).equals("abcd")); 169 170 version (D_BetterC) {} 171 else version (linux) 172 { 173 import bc.core.system.backtrace : TraceInfo; 174 StringZ buf; 175 buf.nogcFormatTo!"where am i: %s"(TraceInfo.current); 176 printf("%s\n", buf.ptr); 177 } 178 179 assert(numDigits(0) == 1); 180 assert(numDigits(11) == 2); 181 assert(numDigits(-1) == 2); 182 183 assert(numDigits(int.min) == 11); 184 assert(numDigits(int.max) == 10); 185 assert(numDigits(long.min) == 20); 186 assert(numDigits(long.max) == 19); 187 assert(numDigits(ulong.min) == 1); 188 assert(numDigits(ulong.max) == 20); 189 190 string str2 = "abc"; 191 192 // Intended usage 193 assert(strlen(str2.tempCString()) == 3); 194 195 // Correct usage 196 auto tmp = str2.tempCString(); 197 assert(strlen(tmp) == 3); // or `tmp.ptr`, or `tmp.buffPtr` 198 199 // Incorrect usage 200 auto pInvalid1 = str2.tempCString().ptr; 201 const char* pInvalid2 = str2.tempCString(); 202 203 RCString rcs; 204 rcs ~= "fo"; 205 rcs ~= 'o'; 206 rcs ~= "bar"; 207 rcs ~= "baz".byCodeUnit.filter!(a => a == 'z'); 208 assert(rcs.length == "foobarz".length); 209 assert(rcs.data.equals("foobarz")); 210 assert(rcs.equals("foobarz")); 211 assert(rcs.ptr == &rcs.data[0]); 212 assert((rcs.ptr["foobarz".length]) == 0); 213 214 // construction from write like params 215 rcs = RCString.from("foo", 42, "bar"); 216 assert(rcs.equals("foo42bar")); 217 218 auto ss = String("Hello"); 219 assert(ss[].equals("Hello"), s[]); 220 ss ~= " String"; 221 assert(ss[].equals("Hello String"), ss[]); 222 auto ss2 = ss.clone(); 223 assert(ss[].equals(ss2[])); 224 assert(ss.ptr != ss2.ptr); 225 226 auto ss3 = ss.move(); 227 assert(ss.length == 0); 228 assert(ss3[].equals("Hello String")); 229 230 enum indented = ` 231 SELECT foo, bar, baz 232 FROM foos 233 WHERE id=ANY($1) AND type_id IN ( 234 SELECT id FROM bars WHERE owner=$2 235 )`; 236 237 enum dedented = 238 "SELECT foo, bar, baz\n" ~ 239 " FROM foos\n" ~ 240 " WHERE id=ANY($1) AND type_id IN (\n" ~ 241 " SELECT id FROM bars WHERE owner=$2\n" ~ 242 ")"; 243 244 // assert(dedent(indented) == dedented); 245 246 printf("test run completed\n"); 247 return 0; 248 } 249 }