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