1 // Compiler implementation of the D programming language
2 // Copyright (c) 1999-2015 by Digital Mars
3 // All Rights Reserved
4 // written by Walter Bright
5 // http://www.digitalmars.com
6 // Distributed under the Boost Software License, Version 1.0.
7 // http://www.boost.org/LICENSE_1_0.txt
8 
9 module ddmd.root.outbuffer;
10 
11 import core.stdc.stdarg, core.stdc.stdio, core.stdc..string;
12 import ddmd.root.rmem, ddmd.root.rootobject;
13 
14 struct OutBuffer
15 {
16     ubyte* data;
17     size_t offset;
18     size_t size;
19     int doindent;
20     int level;
21     int notlinehead;
22 
23     extern (C++) ~this()
24     {
25         mem.xfree(data);
26     }
27 
28     extern (C++) char* extractData()
29     {
30         char* p;
31         p = cast(char*)data;
32         data = null;
33         offset = 0;
34         size = 0;
35         return p;
36     }
37 
38     extern (C++) void reserve(size_t nbytes)
39     {
40         //printf("OutBuffer::reserve: size = %d, offset = %d, nbytes = %d\n", size, offset, nbytes);
41         if (size - offset < nbytes)
42         {
43             size = (offset + nbytes) * 2;
44             size = (size + 15) & ~15;
45             data = cast(ubyte*)mem.xrealloc(data, size);
46         }
47     }
48 
49     extern (C++) void setsize(size_t size)
50     {
51         offset = size;
52     }
53 
54     extern (C++) void reset()
55     {
56         offset = 0;
57     }
58 
59     extern (C++) void write(const(void)* data, size_t nbytes)
60     {
61         if (doindent && !notlinehead)
62         {
63             if (level)
64             {
65                 reserve(level);
66                 for (size_t i = 0; i < level; i++)
67                 {
68                     this.data[offset] = '\t';
69                     offset++;
70                 }
71             }
72             notlinehead = 1;
73         }
74         reserve(nbytes);
75         memcpy(this.data + offset, data, nbytes);
76         offset += nbytes;
77     }
78 
79     extern (C++) void writebstring(char* string)
80     {
81         write(string, *string + 1);
82     }
83 
84     extern (C++) void writestring(const(char)* string)
85     {
86         write(string, strlen(string));
87     }
88 
89     extern (C++) void prependstring(const(char)* string)
90     {
91         size_t len = strlen(string);
92         reserve(len);
93         memmove(data + len, data, offset);
94         memcpy(data, string, len);
95         offset += len;
96     }
97 
98     // write newline
99     extern (C++) void writenl()
100     {
101         version (Windows)
102         {
103             writeword(0x0A0D); // newline is CR,LF on Microsoft OS's
104         }
105         else
106         {
107             writeByte('\n');
108         }
109         if (doindent)
110             notlinehead = 0;
111     }
112 
113     extern (C++) void writeByte(uint b)
114     {
115         if (doindent && !notlinehead && b != '\n')
116         {
117             if (level)
118             {
119                 reserve(level);
120                 for (size_t i = 0; i < level; i++)
121                 {
122                     this.data[offset] = '\t';
123                     offset++;
124                 }
125             }
126             notlinehead = 1;
127         }
128         reserve(1);
129         this.data[offset] = cast(ubyte)b;
130         offset++;
131     }
132 
133     extern (C++) void writeUTF8(uint b)
134     {
135         reserve(6);
136         if (b <= 0x7F)
137         {
138             this.data[offset] = cast(ubyte)b;
139             offset++;
140         }
141         else if (b <= 0x7FF)
142         {
143             this.data[offset + 0] = cast(ubyte)((b >> 6) | 0xC0);
144             this.data[offset + 1] = cast(ubyte)((b & 0x3F) | 0x80);
145             offset += 2;
146         }
147         else if (b <= 0xFFFF)
148         {
149             this.data[offset + 0] = cast(ubyte)((b >> 12) | 0xE0);
150             this.data[offset + 1] = cast(ubyte)(((b >> 6) & 0x3F) | 0x80);
151             this.data[offset + 2] = cast(ubyte)((b & 0x3F) | 0x80);
152             offset += 3;
153         }
154         else if (b <= 0x1FFFFF)
155         {
156             this.data[offset + 0] = cast(ubyte)((b >> 18) | 0xF0);
157             this.data[offset + 1] = cast(ubyte)(((b >> 12) & 0x3F) | 0x80);
158             this.data[offset + 2] = cast(ubyte)(((b >> 6) & 0x3F) | 0x80);
159             this.data[offset + 3] = cast(ubyte)((b & 0x3F) | 0x80);
160             offset += 4;
161         }
162         else if (b <= 0x3FFFFFF)
163         {
164             this.data[offset + 0] = cast(ubyte)((b >> 24) | 0xF8);
165             this.data[offset + 1] = cast(ubyte)(((b >> 18) & 0x3F) | 0x80);
166             this.data[offset + 2] = cast(ubyte)(((b >> 12) & 0x3F) | 0x80);
167             this.data[offset + 3] = cast(ubyte)(((b >> 6) & 0x3F) | 0x80);
168             this.data[offset + 4] = cast(ubyte)((b & 0x3F) | 0x80);
169             offset += 5;
170         }
171         else if (b <= 0x7FFFFFFF)
172         {
173             this.data[offset + 0] = cast(ubyte)((b >> 30) | 0xFC);
174             this.data[offset + 1] = cast(ubyte)(((b >> 24) & 0x3F) | 0x80);
175             this.data[offset + 2] = cast(ubyte)(((b >> 18) & 0x3F) | 0x80);
176             this.data[offset + 3] = cast(ubyte)(((b >> 12) & 0x3F) | 0x80);
177             this.data[offset + 4] = cast(ubyte)(((b >> 6) & 0x3F) | 0x80);
178             this.data[offset + 5] = cast(ubyte)((b & 0x3F) | 0x80);
179             offset += 6;
180         }
181         else
182             assert(0);
183     }
184 
185     extern (C++) void prependbyte(uint b)
186     {
187         reserve(1);
188         memmove(data + 1, data, offset);
189         data[0] = cast(ubyte)b;
190         offset++;
191     }
192 
193     extern (C++) void writewchar(uint w)
194     {
195         version (Windows)
196         {
197             writeword(w);
198         }
199         else
200         {
201             write4(w);
202         }
203     }
204 
205     extern (C++) void writeword(uint w)
206     {
207         version (Windows)
208         {
209             uint newline = 0x0A0D;
210         }
211         else
212         {
213             uint newline = '\n';
214         }
215         if (doindent && !notlinehead && w != newline)
216         {
217             if (level)
218             {
219                 reserve(level);
220                 for (size_t i = 0; i < level; i++)
221                 {
222                     this.data[offset] = '\t';
223                     offset++;
224                 }
225             }
226             notlinehead = 1;
227         }
228         reserve(2);
229         *cast(ushort*)(this.data + offset) = cast(ushort)w;
230         offset += 2;
231     }
232 
233     extern (C++) void writeUTF16(uint w)
234     {
235         reserve(4);
236         if (w <= 0xFFFF)
237         {
238             *cast(ushort*)(this.data + offset) = cast(ushort)w;
239             offset += 2;
240         }
241         else if (w <= 0x10FFFF)
242         {
243             *cast(ushort*)(this.data + offset) = cast(ushort)((w >> 10) + 0xD7C0);
244             *cast(ushort*)(this.data + offset + 2) = cast(ushort)((w & 0x3FF) | 0xDC00);
245             offset += 4;
246         }
247         else
248             assert(0);
249     }
250 
251     extern (C++) void write4(uint w)
252     {
253         version (Windows)
254         {
255             bool notnewline = w != 0x000A000D;
256         }
257         else
258         {
259             bool notnewline = true;
260         }
261         if (doindent && !notlinehead && notnewline)
262         {
263             if (level)
264             {
265                 reserve(level);
266                 for (size_t i = 0; i < level; i++)
267                 {
268                     this.data[offset] = '\t';
269                     offset++;
270                 }
271             }
272             notlinehead = 1;
273         }
274         reserve(4);
275         *cast(uint*)(this.data + offset) = w;
276         offset += 4;
277     }
278 
279     extern (C++) void write(const OutBuffer* buf)
280     {
281         if (buf)
282         {
283             reserve(buf.offset);
284             memcpy(data + offset, buf.data, buf.offset);
285             offset += buf.offset;
286         }
287     }
288 
289     extern (C++) void write(RootObject obj)
290     {
291         if (obj)
292         {
293             writestring(obj.toChars());
294         }
295     }
296 
297     extern (C++) void fill0(size_t nbytes)
298     {
299         reserve(nbytes);
300         memset(data + offset, 0, nbytes);
301         offset += nbytes;
302     }
303 
304     extern (C++) void vprintf(const(char)* format, va_list args)
305     {
306         int count;
307         if (doindent)
308             write(null, 0); // perform indent
309         uint psize = 128;
310         for (;;)
311         {
312             reserve(psize);
313             version (Windows)
314             {
315                 count = _vsnprintf(cast(char*)data + offset, psize, format, args);
316                 if (count != -1)
317                     break;
318                 psize *= 2;
319             }
320             else version (Posix)
321             {
322                 va_list va;
323                 va_copy(va, args);
324                 /*
325                  The functions vprintf(), vfprintf(), vsprintf(), vsnprintf()
326                  are equivalent to the functions printf(), fprintf(), sprintf(),
327                  snprintf(), respectively, except that they are called with a
328                  va_list instead of a variable number of arguments. These
329                  functions do not call the va_end macro. Consequently, the value
330                  of ap is undefined after the call. The application should call
331                  va_end(ap) itself afterwards.
332                  */
333                 count = vsnprintf(cast(char*)data + offset, psize, format, va);
334                 va_end(va);
335                 if (count == -1)
336                     psize *= 2;
337                 else if (count >= psize)
338                     psize = count + 1;
339                 else
340                     break;
341             }
342             else
343             {
344                 assert(0);
345             }
346         }
347         offset += count;
348     }
349 
350     extern (C++) void printf(const(char)* format, ...)
351     {
352         va_list ap;
353         va_start(ap, format);
354         vprintf(format, ap);
355         va_end(ap);
356     }
357 
358     extern (C++) void bracket(char left, char right)
359     {
360         reserve(2);
361         memmove(data + 1, data, offset);
362         data[0] = left;
363         data[offset + 1] = right;
364         offset += 2;
365     }
366 
367     /******************
368      * Insert left at i, and right at j.
369      * Return index just past right.
370      */
371     extern (C++) size_t bracket(size_t i, const(char)* left, size_t j, const(char)* right)
372     {
373         size_t leftlen = strlen(left);
374         size_t rightlen = strlen(right);
375         reserve(leftlen + rightlen);
376         insert(i, left, leftlen);
377         insert(j + leftlen, right, rightlen);
378         return j + leftlen + rightlen;
379     }
380 
381     extern (C++) void spread(size_t offset, size_t nbytes)
382     {
383         reserve(nbytes);
384         memmove(data + offset + nbytes, data + offset, this.offset - offset);
385         this.offset += nbytes;
386     }
387 
388     /****************************************
389      * Returns: offset + nbytes
390      */
391     extern (C++) size_t insert(size_t offset, const(void)* p, size_t nbytes)
392     {
393         spread(offset, nbytes);
394         memmove(data + offset, p, nbytes);
395         return offset + nbytes;
396     }
397 
398     extern (C++) void remove(size_t offset, size_t nbytes)
399     {
400         memmove(data + offset, data + offset + nbytes, this.offset - (offset + nbytes));
401         this.offset -= nbytes;
402     }
403 
404     extern (D) const(char)[] peekSlice()
405     {
406         return (cast(const char*)data)[0 .. offset];
407     }
408 
409     // Append terminating null if necessary and get view of internal buffer
410     extern (C++) char* peekString()
411     {
412         if (!offset || data[offset - 1] != '\0')
413         {
414             writeByte(0);
415             offset--; // allow appending more
416         }
417         return cast(char*)data;
418     }
419 
420     // Append terminating null if necessary and take ownership of data
421     extern (C++) char* extractString()
422     {
423         if (!offset || data[offset - 1] != '\0')
424             writeByte(0);
425         return extractData();
426     }
427 }