1 /// \file Text.d
2 
3 module tagion.betterC.utils.Text;
4 
5 @nogc:
6 
7 import std.traits : Unqual, isIntegral, isSigned;
8 import tagion.betterC.utils.Memory;
9 import tagion.betterC.utils.platform : calloc;
10 
11 //import core.stdc.stdio;
12 
13 struct Text {
14 @nogc:
15     protected {
16         char[] str;
17         size_t index;
18     }
19 
20     this(const size_t size) {
21         if (size > 0) {
22             str.create(size);
23         }
24     }
25 
26     this(const(char[]) _str) {
27         this(_str.length + 1);
28         str[0 .. _str.length] = _str[0 .. $];
29         index = _str.length;
30         str[$ - 1] = '\0';
31     }
32     /**
33        This takes over the overship of the data
34      */
35     this(ref Text _surrender) {
36         this.str = _surrender.str;
37         this.index = _surrender.index;
38         _surrender.str = null;
39         _surrender.index = 0;
40     }
41 
42     char[] expropriate() {
43         scope (exit) {
44             str = null;
45             index = 0;
46         }
47         return str[0 .. index];
48     }
49 
50     @property size_t length() const pure {
51         return index;
52     }
53 
54     char opIndex(const size_t i) pure const {
55         if (i < index) {
56             return str[i];
57         }
58         return '\0';
59     }
60 
61     string opSlice(const size_t from, const size_t to) const
62     in {
63         assert(from <= to);
64         assert(to <= index);
65     }
66     do {
67         return cast(string)(str[from .. to]);
68     }
69 
70     string opSlice() const pure {
71         return cast(immutable) str[0 .. index];
72     }
73 
74     alias serialize = opSlice;
75     void opOpAssign(string op)(const(char[]) cat) if (op == "~") {
76         const new_index = index + cat.length;
77         scope (exit) {
78             index = new_index;
79         }
80         if (index + cat.length + 1 > str.length) {
81             resize(str, index + cat.length + 1);
82         }
83         str[index .. new_index] = cat;
84         str[new_index] = '\0';
85     }
86 
87     ref Text opCall(const(char[]) cat) return {
88         opOpAssign!"~"(cat);
89         return this;
90     }
91 
92     ref Text opCall(T)(T num, const size_t base = 10) if (isIntegral!T) {
93         //const negative=(num < 0);
94         enum numbers = "0123456789abcdef";
95         static if (isSigned!T) {
96             enum max_size = T.min.stringof.length + 1;
97         }
98         else {
99             enum max_size = T.max.stringof.length + 1;
100         }
101 
102         if (index + max_size > str.length) {
103             resize(str, index + max_size);
104         }
105         static if (isSigned!T) {
106             if (num < 0) {
107                 str[index] = '-';
108                 num = -num;
109                 index++;
110             }
111         }
112         const(char[]) fill_numbers(T num, char[] s) {
113             alias Mutable = Unqual!T;
114             Mutable n = num;
115             uint i;
116             do {
117                 const n_index = cast(uint)(n % cast(T) base);
118                 s[i++] = numbers[n_index];
119                 n /= base;
120             }
121             while (n > 0);
122             return s[0 .. i];
123         }
124 
125         char[max_size] buf;
126         const reverse_numbers = fill_numbers(num, buf);
127         foreach_reverse (i, c; reverse_numbers) {
128             str[index] = c;
129             index++;
130         }
131         str[index] = '\0';
132         return this;
133     }
134 
135     void dispose() {
136         str.dispose;
137         index = 0;
138     }
139 
140     ~this() {
141         str.dispose;
142     }
143 }
144 
145 unittest {
146     //    import core.stdc.stdio;
147     Text text;
148     immutable(char[12]) check = "Some text 42";
149     size_t size = 4;
150     text ~= check[0 .. size];
151     assert(text.serialize == check[0 .. size]);
152     text ~= check[size .. size + 6];
153     size += 6;
154     assert(text.serialize == check[0 .. size]);
155     text(42);
156     assert(text.serialize == check);
157 }