1 module fghj.jsonbuffer; 2 3 package struct JsonBuffer(Dg) 4 { 5 Dg sink; 6 // current buffer length 7 size_t length; 8 9 char[4096 * 4] buffer = void; 10 11 /+ 12 Puts char 13 +/ 14 void put(char c) 15 { 16 if(length == buffer.length) 17 { 18 flush; 19 } 20 buffer[length++] = c; 21 } 22 23 /+ 24 Uses compile time loop for values `null`, `true`, `false` 25 +/ 26 void put(string str)() 27 { 28 size_t newLength = length + str.length; 29 if(newLength > buffer.length) 30 { 31 flush; 32 newLength = str.length; 33 } 34 import fghj.utility; 35 // compile time loop 36 foreach(i; Iota!(0, str.length)) 37 buffer[length + i] = str[i]; 38 length = newLength; 39 } 40 41 /+ 42 Puts key/number 43 +/ 44 void putSmallEscaped(in char[] str) 45 { 46 assert(str.length <= ubyte.max); 47 size_t newLength = length + str.length; 48 if(newLength > buffer.length) 49 { 50 flush; 51 newLength = str.length; 52 } 53 buffer[length .. newLength] = str; 54 length = newLength; 55 } 56 57 /++ 58 Decodes byte `b` to the form `u00XX`, where `XX` is 2 hexadecimal characters. 59 +/ 60 private void putUnicode(ubyte b) 61 { 62 buffer[length + 0] = 'u'; 63 buffer[length + 1] = '0'; 64 buffer[length + 2] = '0'; 65 ubyte[2] spl; 66 spl[0] = b >> 4; 67 spl[1] = b & 0xF; 68 buffer[length + 3] = cast(ubyte)(spl[0] < 10 ? spl[0] + '0' : spl[0] - 10 + 'A'); 69 buffer[length + 4] = cast(ubyte)(spl[1] < 10 ? spl[1] + '0' : spl[1] - 10 + 'A'); 70 length += 5; 71 } 72 73 /+ 74 Puts string 75 +/ 76 void put(in char[] str) 77 { 78 import std.range: chunks; 79 import std.string: representation; 80 81 version(SSE42) 82 { 83 import core.simd; 84 import fghj.simd; 85 import ldc.gccbuiltins_x86; 86 87 enum byte16 str2E = [ 88 '\u0001', '\u001F', 89 '\"', '\"', 90 '\\', '\\', 91 '\u007f', '\u007f', 92 '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0']; 93 enum byte16 str3E = ['\"', '\\', '\b', '\f', '\n', '\r', '\t', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0']; 94 byte16 str2 = str2E; 95 byte16 str3 = str3E; 96 97 static immutable emap = ['\"', '\\', 'b', 'f', 'n', 'r', 't']; 98 99 for(auto d = str.representation; d.length;) 100 { 101 if(length + 21 > buffer.length) 102 { 103 flush; 104 } 105 int ecx = void; 106 byte16 str1 = void; 107 if(d.length >= 16) 108 { 109 str1 = loadUnaligned!byte16(cast(ubyte*) d.ptr); 110 storeUnaligned!byte16(str1, cast(ubyte*) buffer.ptr + length); 111 auto cflag = __builtin_ia32_pcmpistric128(str2, str1, 0x04); 112 ecx = __builtin_ia32_pcmpistri128 (str2, str1, 0x04); 113 d = d[ecx .. $]; 114 length += ecx; 115 if(ecx == 16) 116 continue; 117 } 118 else 119 { 120 str1 ^= str1; 121 switch(d.length) 122 { 123 default : goto case; 124 case 0xE+1: str1.array[0xE] = d[0xE]; goto case; 125 case 0xD+1: str1.array[0xD] = d[0xD]; goto case; 126 case 0xC+1: str1.array[0xC] = d[0xC]; goto case; 127 case 0xB+1: str1.array[0xB] = d[0xB]; goto case; 128 case 0xA+1: str1.array[0xA] = d[0xA]; goto case; 129 case 0x9+1: str1.array[0x9] = d[0x9]; goto case; 130 case 0x8+1: str1.array[0x8] = d[0x8]; goto case; 131 case 0x7+1: str1.array[0x7] = d[0x7]; goto case; 132 case 0x6+1: str1.array[0x6] = d[0x6]; goto case; 133 case 0x5+1: str1.array[0x5] = d[0x5]; goto case; 134 case 0x4+1: str1.array[0x4] = d[0x4]; goto case; 135 case 0x3+1: str1.array[0x3] = d[0x3]; goto case; 136 case 0x2+1: str1.array[0x2] = d[0x2]; goto case; 137 case 0x1+1: str1.array[0x1] = d[0x1]; goto case; 138 case 0x0+1: str1.array[0x0] = d[0x0]; goto case; 139 case 0x0 : break; 140 } 141 storeUnaligned!byte16(str1, cast(ubyte*) buffer.ptr + length); 142 auto cflag = __builtin_ia32_pcmpistric128(str2, str1, 0x04); 143 ecx = __builtin_ia32_pcmpistri128 (str2, str1, 0x04); 144 if(!cflag) 145 { 146 length += d.length; 147 break; 148 } 149 d = d[ecx .. $]; 150 length += ecx; 151 } 152 153 int eax = ecx + 1; 154 auto cflag = __builtin_ia32_pcmpestric128(str1, eax, str3, emap.length, 0x00); 155 auto edx = __builtin_ia32_pcmpestri128 (str1, eax, str3, emap.length, 0x00); 156 d = d[1 .. $]; 157 buffer[length + 0] = '\\'; 158 if(cflag) 159 { 160 buffer[length + 1] = emap[edx]; 161 length += 2; 162 continue; 163 } 164 length += 1; 165 putUnicode(str1.array[ecx]); 166 } 167 } 168 else 169 { 170 foreach(chunk; str.representation.chunks(256)) 171 { 172 if(chunk.length * 2 + length + 16 > buffer.length) 173 { 174 flush; 175 } 176 foreach(size_t i, char e; chunk) 177 { 178 switch(e) 179 { 180 case '\b': 181 buffer[length + 0] = '\\'; 182 buffer[length + 1] = 'b'; 183 length += 2; 184 continue; 185 case '\f': 186 buffer[length + 0] = '\\'; 187 buffer[length + 1] = 'f'; 188 length += 2; 189 continue; 190 case '\n': 191 buffer[length + 0] = '\\'; 192 buffer[length + 1] = 'n'; 193 length += 2; 194 continue; 195 case '\r': 196 buffer[length + 0] = '\\'; 197 buffer[length + 1] = 'r'; 198 length += 2; 199 continue; 200 case '\t': 201 buffer[length + 0] = '\\'; 202 buffer[length + 1] = 't'; 203 length += 2; 204 continue; 205 case '\\': 206 buffer[length + 0] = '\\'; 207 buffer[length + 1] = '\\'; 208 length += 2; 209 continue; 210 case '\"': 211 buffer[length + 0] = '\\'; 212 buffer[length + 1] = '\"'; 213 length += 2; 214 continue; 215 case '\0': .. case '\u0007': 216 case '\u000e': .. case '\u001f': 217 case '\u000b': 218 case '\u00ff': 219 buffer[length + 0] = '\\'; 220 length++; 221 putUnicode(e); 222 if(chunk.length * 2 + length + 16 > buffer.length) 223 { 224 flush; 225 } 226 continue; 227 default: 228 buffer[length] = e; 229 length++; 230 } 231 } 232 } 233 } 234 } 235 236 /+ 237 Sends remaining data to `sink`. 238 +/ 239 void flush() 240 { 241 sink(buffer[0 .. length]); 242 length = 0; 243 } 244 }