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 private { 46 // Reverses closed interval [begin .. end] 47 void strreverse(char* begin, char* end) 48 { 49 char aux; 50 while (end > begin) 51 aux = *end, *end-- = *begin, *begin++ = aux; 52 } 53 54 // Prints $(D value) at the address where $(D str) points to. 55 // Returns number of characters written. 56 size_t itoa(T)(T value, char* str) 57 { 58 char* wstr=str; 59 60 static if (isSigned!T) { 61 ulong uvalue = (value < 0) ? -cast(int)(value) : value; 62 } else { 63 ulong uvalue = value; 64 } 65 66 do { 67 *wstr++ = cast(char)(48 + (uvalue % 10)); 68 } while (uvalue /= 10); 69 70 static if (isSigned!T) { 71 if (value < 0) *wstr++ = '-'; 72 } 73 74 strreverse(str,wstr-1); 75 76 return wstr - str; 77 } 78 } 79 80 /// 81 template isSomeSink(T) { 82 static if (__traits(compiles, T.init("string")))//T == void delegate(const(char)[]))) 83 enum isSomeSink = true; 84 else static if (is(T == char*)) 85 enum isSomeSink = true; 86 else 87 enum isSomeSink = false; 88 } 89 90 private { 91 void writeFloat(T)(ref char* sink, T number) 92 if (isFloatingPoint!T) 93 { 94 char[4] format; 95 format[0] = '%'; 96 format[1] = 'g'; 97 format[2] = '\0'; 98 sink += sprintf(sink, format.ptr, number); 99 } 100 101 void writeFloat(T)(scope void delegate(const(char)[]) sink, T number) 102 if (isFloatingPoint!T) 103 { 104 char[1024] buffer = void; 105 int count; 106 107 auto p = buffer.ptr; 108 auto psize = buffer.length; 109 for (;;) 110 { 111 version(Win32) 112 { 113 count = _snprintf(p,psize,"%g", cast(double)number); 114 if (count != -1) 115 break; 116 psize *= 2; 117 p = cast(char *) alloca(psize); 118 } 119 version(Posix) 120 { 121 count = snprintf(p,psize,"%g", cast(double)number); 122 if (count == -1) 123 psize *= 2; 124 else if (count >= psize) 125 psize = count + 1; 126 else 127 break; 128 p = cast(char *) alloca(psize); 129 } 130 } 131 132 sink(p[0 .. count]); 133 } 134 135 void writeInteger(T)(ref char* sink, T integer) 136 if (isIntegral!T) 137 { 138 sink += itoa(integer, sink); 139 } 140 141 void writeInteger(T)(scope void delegate(const(char)[]) sink, T integer) 142 if (isIntegral!T) 143 { 144 char[32] buf = void; 145 auto len = itoa(integer, buf.ptr); 146 sink(buf[0 .. len]); 147 } 148 149 void writeChar(T)(ref char* sink, T c) 150 if (isSomeChar!T) 151 { 152 *sink++ = c; 153 } 154 155 void writeChar(T)(scope void delegate(const(char)[]) sink, T c) 156 if (isSomeChar!T) 157 { 158 sink((&c)[0 .. 1]); 159 } 160 161 void writeString(T)(ref char* sink, T s) 162 if (isSomeString!T) 163 { 164 auto str = cast(const(char)[])s; 165 core.stdc..string.memcpy(sink, str.ptr, str.length); 166 sink += str.length; 167 } 168 169 void writeString(T)(scope void delegate(const(char)[]) sink, T s) 170 if (isSomeString!T) 171 { 172 sink(cast(const(char)[])s); 173 } 174 175 void writeImpl(Sink, T)(auto ref Sink sink, T value) 176 if (isSomeSink!Sink) 177 { 178 static if (isIntegral!T) 179 writeInteger(sink, value); 180 else static if (isFloatingPoint!T) 181 writeFloat(sink, value); 182 else static if (isSomeChar!T) 183 writeChar(sink, value); 184 else static if (isSomeString!T) 185 writeString(sink, value); 186 else static assert(false, 187 "only integers, floats, chars and strings are supported"); 188 } 189 190 // -------------------- JSON output utils ---------------------------------- 191 192 // JSON doesn't support NaN and +/- infinity. 193 // Therefore the approach taken here is to represent 194 // infinity as 1.0e+1024, and NaN as null. 195 void writeFloatJson(Sink, T)(auto ref Sink sink, T value) 196 if (isFloatingPoint!T) 197 { 198 if (isFinite(value)) { 199 sink.write(value); 200 } else { 201 if (value == float.infinity) { 202 sink.write("1.0e+1024"); 203 } else if (value == -float.infinity) { 204 sink.write("-1.0e+1024"); 205 } else if (isNaN(value)) { 206 sink.write("null"); 207 } else { 208 assert(0); 209 } 210 } 211 } 212 213 immutable char[256] specialCharacterTable = [ 214 /* 0-15 */ 0,0, 0,0,0,0,0,0, 'b','t','n',0, 'f','r',0, 0, 215 /* 16-31 */ 0,0, 0,0,0,0,0,0, 0, 0, 0,0, 0, 0,0, 0, 216 /* 32-47 */ 0,0,'"',0,0,0,0,0, 0, 0, 0,0, 0, 0,0, 0, 217 /* 48-63 */ 0,0, 0,0,0,0,0,0, 0, 0, 0,0, 0, 0,0,'/', 218 /* 64-79 */ 0,0, 0,0,0,0,0,0, 0, 0, 0,0, 0, 0,0, 0, 219 /* 80-95 */ 0,0, 0,0,0,0,0,0, 0, 0, 0,0,'\\', 0,0, 0, 220 /* 96-111 */ 0,0, 0,0,0,0,0,0, 0, 0, 0,0, 0, 0,0, 0, 221 /* 112-127 */ 0,0, 0,0,0,0,0,0, 0, 0, 0,0, 0, 0,0, 0, 222 223 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 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 ]; 232 233 void writeStringJson(Sink, T)(auto ref Sink sink, T s) 234 if (isSomeString!T) 235 { 236 sink.write('"'); 237 foreach (char c; s) { 238 auto sc = specialCharacterTable[cast(ubyte)c]; 239 if (sc == 0) { 240 sink.write(c); 241 } else { 242 sink.write('\\'); 243 sink.write(sc); 244 } 245 } 246 sink.write('"'); 247 } 248 249 void writeCharJson(Sink, T)(auto ref Sink sink, T c) 250 if (isSomeChar!T) 251 { 252 sink.writeStringJson((&c)[0 .. 1]); 253 } 254 255 void writeArrayJson(Sink, T)(auto ref Sink sink, T array) 256 if (isArray!T && __traits(compiles, sink.writeJson(array[0]))) 257 { 258 if (array.length == 0) { 259 sink.write("[]"); 260 return; 261 } 262 263 sink.write('['); 264 foreach (elem; array[0 .. $ - 1]) { 265 sink.writeJson(elem); 266 sink.write(','); 267 } 268 sink.writeJson(array[$ - 1]); 269 sink.write(']'); 270 } 271 272 void writeJsonImpl(Sink, T)(auto ref Sink sink, T value) 273 if (isSomeSink!Sink) 274 { 275 static if (isIntegral!T) 276 writeInteger(sink, value); 277 else static if (isFloatingPoint!T) 278 writeFloatJson(sink, value); 279 else static if (isSomeChar!T) 280 writeCharJson(sink, value); 281 else static if (isSomeString!T) 282 writeStringJson(sink, value); 283 else static if (isArray!T && __traits(compiles, sink.writeJsonImpl(value[0]))) 284 writeArrayJson(sink, value); 285 else static assert(false, 286 "only numbers, chars, strings and arrays are supported"); 287 } 288 } 289 290 /// 291 void write(T)(ref char* sink, T value) { writeImpl(sink, value); } 292 /// 293 void write(T)(scope void delegate(const(char)[]) sink, T value) { writeImpl(sink, value); } 294 295 /// 296 void writeArray(Sink, T, U)(auto ref Sink sink, T array, U delimiter) 297 if (isSomeSink!Sink && isArray!T && (isSomeChar!U || isSomeString!U) && 298 __traits(compiles, sink.write(array[0]))) 299 { 300 if (array.length == 0) 301 return; 302 303 foreach (elem; array[0 .. $ - 1]) { 304 sink.write(elem); 305 sink.write(delimiter); 306 } 307 sink.write(array[$ - 1]); 308 } 309 310 /// Supports numbers, strings, and arrays. No dictionary - because D doesn't have a good one. 311 void writeJson(T)(ref char* sink, T value) { writeJsonImpl(sink, value); } 312 /// ditto 313 void writeJson(T)(scope void delegate(const(char)[]) sink, T value) { writeJsonImpl(sink, value); }