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