1 /* 2 This file is part of BioD. 3 Copyright (C) 2012 Artem Tarasov <lomereiter@gmail.com> 4 5 Permission is hereby granted, free of charge, to any person obtaining a 6 copy of this software and associated documentation files (the "Software"), 7 to deal in the Software without restriction, including without limitation 8 the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 and/or sell copies of the Software, and to permit persons to whom the 10 Software is furnished to do so, subject to the following conditions: 11 12 The above copyright notice and this permission notice shall be included in 13 all copies or substantial portions of the Software. 14 15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 DEALINGS IN THE SOFTWARE. 22 23 */ 24 /** 25 $(P This module provides fast formatting functions.) 26 27 $(P Each function has two overloads: 28 $(UL 29 $(LI $(D ref char*) - in this case, function starts 30 writing at the location, and updates the pointer. 31 No checks are done, it's user's responsibility that this is safe.) 32 $(LI $(D scope void delegate(const(char)[])) - formatted data 33 is passed to the delegate for further processing.))) 34 */ 35 module bio.core.utils.format; 36 37 import core.stdc.stdio; 38 import core.stdc.stdlib; 39 static import core.stdc..string; 40 import std..string; 41 import std.traits; 42 import std.array; 43 import std.math; 44 45 /// 46 template isSomeSink(T) { 47 static if (__traits(compiles, T.init("string")))//T == void delegate(const(char)[]))) 48 enum isSomeSink = true; 49 else static if (is(T == char*)) 50 enum isSomeSink = true; 51 else 52 enum isSomeSink = false; 53 } 54 55 private { 56 // Reverses closed interval [begin .. end] 57 void strreverse(char* begin, char* end) 58 { 59 char aux; 60 while (end > begin) 61 aux = *end, *end-- = *begin, *begin++ = aux; 62 } 63 64 // Prints $(D value) at the address where $(D str) points to. 65 // Returns number of characters written. 66 size_t itoa(T)(T value, char* str) 67 { 68 char* wstr=str; 69 70 static if (isSigned!T) { 71 ulong uvalue = (value < 0) ? -cast(int)(value) : value; 72 } else { 73 ulong uvalue = value; 74 } 75 76 do { 77 *wstr++ = cast(char)(48 + (uvalue % 10)); 78 } while (uvalue /= 10); 79 80 static if (isSigned!T) { 81 if (value < 0) *wstr++ = '-'; 82 } 83 84 strreverse(str,wstr-1); 85 86 return wstr - str; 87 } 88 } 89 90 91 private { 92 void writeFloat(T)(ref char* sink, T number) 93 if (isFloatingPoint!T) 94 { 95 char[4] format; 96 format[0] = '%'; 97 format[1] = 'g'; 98 format[2] = '\0'; 99 sink += sprintf(sink, format.ptr, number); 100 } 101 102 void writeFloat(T)(scope void delegate(const(char)[]) sink, T number) 103 if (isFloatingPoint!T) 104 { 105 char[1024] buffer = void; 106 int count; 107 108 auto p = buffer.ptr; 109 auto psize = buffer.length; 110 for (;;) 111 { 112 version(Win32) 113 { 114 count = _snprintf(p,psize,"%g", cast(double)number); 115 if (count != -1) 116 break; 117 psize *= 2; 118 p = cast(char *) alloca(psize); 119 } 120 version(Posix) 121 { 122 count = snprintf(p,psize,"%g", cast(double)number); 123 if (count == -1) 124 psize *= 2; 125 else if (count >= psize) 126 psize = count + 1; 127 else 128 break; 129 p = cast(char *) alloca(psize); 130 } 131 } 132 133 sink(p[0 .. count]); 134 } 135 136 void writeInteger(T)(ref char* sink, T integer) 137 if (isIntegral!T) 138 { 139 sink += itoa(integer, sink); 140 } 141 142 void writeInteger(T)(scope void delegate(const(char)[]) sink, T integer) 143 if (isIntegral!T) 144 { 145 char[32] buf = void; 146 auto len = itoa(integer, buf.ptr); 147 sink(buf[0 .. len]); 148 } 149 150 void writeChar(T)(ref char* sink, T c) 151 if (isSomeChar!T) 152 { 153 *sink++ = c; 154 } 155 156 void writeChar(T)(scope void delegate(const(char)[]) sink, T c) 157 if (isSomeChar!T) 158 { 159 sink((&c)[0 .. 1]); 160 } 161 162 void writeString(T)(ref char* sink, T s) 163 if (isSomeString!T) 164 { 165 auto str = cast(const(char)[])s; 166 core.stdc..string.memcpy(sink, str.ptr, str.length); 167 sink += str.length; 168 } 169 170 void writeString(T)(scope void delegate(const(char)[]) sink, T s) 171 if (isSomeString!T) 172 { 173 sink(cast(const(char)[])s); 174 } 175 176 void writeImpl(Sink, T)(auto ref Sink sink, T value) 177 if (isSomeSink!Sink) 178 { 179 static if (isIntegral!T) 180 writeInteger(sink, value); 181 else static if (isFloatingPoint!T) 182 writeFloat(sink, value); 183 else static if (isSomeChar!T) 184 writeChar(sink, value); 185 else static if (isSomeString!T) 186 writeString(sink, value); 187 else static assert(false, 188 "only integers, floats, chars and strings are supported"); 189 } 190 191 // -------------------- JSON output utils ---------------------------------- 192 193 // JSON doesn't support NaN and +/- infinity. 194 // Therefore the approach taken here is to represent 195 // infinity as 1.0e+1024, and NaN as null. 196 void writeFloatJson(Sink, T)(auto ref Sink sink, T value) 197 if (isFloatingPoint!T) 198 { 199 if (isFinite(value)) { 200 sink.write(value); 201 } else { 202 if (value == float.infinity) { 203 sink.write("1.0e+1024"); 204 } else if (value == -float.infinity) { 205 sink.write("-1.0e+1024"); 206 } else if (isNaN(value)) { 207 sink.write("null"); 208 } else { 209 assert(0); 210 } 211 } 212 } 213 214 immutable char[256] specialCharacterTable = [ 215 /* 0-15 */ 0,0, 0,0,0,0,0,0, 'b','t','n',0, 'f','r',0, 0, 216 /* 16-31 */ 0,0, 0,0,0,0,0,0, 0, 0, 0,0, 0, 0,0, 0, 217 /* 32-47 */ 0,0,'"',0,0,0,0,0, 0, 0, 0,0, 0, 0,0, 0, 218 /* 48-63 */ 0,0, 0,0,0,0,0,0, 0, 0, 0,0, 0, 0,0,'/', 219 /* 64-79 */ 0,0, 0,0,0,0,0,0, 0, 0, 0,0, 0, 0,0, 0, 220 /* 80-95 */ 0,0, 0,0,0,0,0,0, 0, 0, 0,0,'\\', 0,0, 0, 221 /* 96-111 */ 0,0, 0,0,0,0,0,0, 0, 0, 0,0, 0, 0,0, 0, 222 /* 112-127 */ 0,0, 0,0,0,0,0,0, 0, 0, 0,0, 0, 0,0, 0, 223 224 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 225 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 226 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 227 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 228 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 229 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 230 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 231 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0 232 ]; 233 234 void writeStringJson(Sink, T)(auto ref Sink sink, T s) 235 if (isSomeString!T) 236 { 237 sink.write('"'); 238 foreach (char c; s) { 239 auto sc = specialCharacterTable[cast(ubyte)c]; 240 if (sc == 0) { 241 sink.write(c); 242 } else { 243 sink.write('\\'); 244 sink.write(sc); 245 } 246 } 247 sink.write('"'); 248 } 249 250 void writeCharJson(Sink, T)(auto ref Sink sink, T c) 251 if (isSomeChar!T) 252 { 253 sink.writeStringJson((&c)[0 .. 1]); 254 } 255 256 void writeArrayJson(Sink, T)(auto ref Sink sink, T array) 257 if (isArray!T && __traits(compiles, sink.writeJson(array[0]))) 258 { 259 if (array.length == 0) { 260 sink.write("[]"); 261 return; 262 } 263 264 sink.write('['); 265 foreach (elem; array[0 .. $ - 1]) { 266 sink.writeJson(elem); 267 sink.write(','); 268 } 269 sink.writeJson(array[$ - 1]); 270 sink.write(']'); 271 } 272 273 void writeJsonImpl(Sink, T)(auto ref Sink sink, T value) 274 if (isSomeSink!Sink) 275 { 276 static if (isIntegral!T) 277 writeInteger(sink, value); 278 else static if (isFloatingPoint!T) 279 writeFloatJson(sink, value); 280 else static if (isSomeChar!T) 281 writeCharJson(sink, value); 282 else static if (isSomeString!T) 283 writeStringJson(sink, value); 284 else static if (isArray!T && __traits(compiles, sink.writeJsonImpl(value[0]))) 285 writeArrayJson(sink, value); 286 else static assert(false, 287 "only numbers, chars, strings and arrays are supported"); 288 } 289 } 290 291 /// 292 void write(T)(ref char* sink, T value) { writeImpl(sink, value); } 293 /// 294 void write(T)(scope void delegate(const(char)[]) sink, T value) { writeImpl(sink, value); } 295 296 /// 297 void writeArray(Sink, T, U)(auto ref Sink sink, T array, U delimiter) 298 if (isSomeSink!Sink && isArray!T && (isSomeChar!U || isSomeString!U) && 299 __traits(compiles, sink.write(array[0]))) 300 { 301 if (array.length == 0) 302 return; 303 304 foreach (elem; array[0 .. $ - 1]) { 305 sink.write(elem); 306 sink.write(delimiter); 307 } 308 sink.write(array[$ - 1]); 309 } 310 311 /// Supports numbers, strings, and arrays. No dictionary - because D doesn't have a good one. 312 void writeJson(T)(ref char* sink, T value) { writeJsonImpl(sink, value); } 313 /// ditto 314 void writeJson(T)(scope void delegate(const(char)[]) sink, T value) { writeJsonImpl(sink, value); }