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 }