xref: /trafficserver/src/tscore/TextBuffer.cc (revision 4cfd5a73)
1 /** @file
2 
3   A brief file description
4 
5   @section license License
6 
7   Licensed to the Apache Software Foundation (ASF) under one
8   or more contributor license agreements.  See the NOTICE file
9   distributed with this work for additional information
10   regarding copyright ownership.  The ASF licenses this file
11   to you under the Apache License, Version 2.0 (the
12   "License"); you may not use this file except in compliance
13   with the License.  You may obtain a copy of the License at
14 
15       http://www.apache.org/licenses/LICENSE-2.0
16 
17   Unless required by applicable law or agreed to in writing, software
18   distributed under the License is distributed on an "AS IS" BASIS,
19   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20   See the License for the specific language governing permissions and
21   limitations under the License.
22  */
23 
24 #include <cstdarg>
25 #include <cstdio>
26 #include "tscore/ink_platform.h"
27 #include "tscore/ink_memory.h"
28 #include "tscore/TextBuffer.h"
29 
30 /****************************************************************************
31  *
32  *  TextBuffer.cc - A self-expanding buffer, primarily meant for strings
33  *
34  *
35  *
36  ****************************************************************************/
37 
TextBuffer(int size)38 TextBuffer::TextBuffer(int size)
39 {
40   bufferStart = nullptr;
41   nextAdd     = nullptr;
42   currentSize = spaceLeft = 0;
43   if (size > 0) {
44     // Institute a minimum size
45     if (size < 1024) {
46       size = 1024;
47     }
48 
49     bufferStart = static_cast<char *>(ats_malloc(size));
50     nextAdd     = bufferStart;
51     currentSize = size;
52     spaceLeft   = size - 1; // Leave room for a terminator;
53     nextAdd[0]  = '\0';
54   }
55 }
56 
~TextBuffer()57 TextBuffer::~TextBuffer()
58 {
59   ats_free(bufferStart);
60 }
61 
62 char *
release()63 TextBuffer::release()
64 {
65   char *ret = bufferStart;
66 
67   bufferStart = nextAdd = nullptr;
68   currentSize = spaceLeft = 0;
69 
70   return ret;
71 }
72 
73 // void TextBuffer::reUse()
74 //
75 //   Sets the text buffer for reuse by repositioning the
76 //     ptrs to beginning of buffer.  The buffer space is
77 //     reused
78 void
reUse()79 TextBuffer::reUse()
80 {
81   if (bufferStart != nullptr) {
82     nextAdd    = bufferStart;
83     spaceLeft  = currentSize - 1;
84     nextAdd[0] = '\0';
85   }
86 }
87 
88 // int TextBuffer::copyFrom(void*,int num_bytes)
89 //
90 //
91 //  Copy N bytes (determined by num_bytes) on to the
92 //  end of the buffer.
93 //
94 //  Returns the number of bytes copies or
95 //  -1 if there was insufficient memory
96 int
copyFrom(const void * source,unsigned num_bytes)97 TextBuffer::copyFrom(const void *source, unsigned num_bytes)
98 {
99   // Get more space if necessary
100   if (spaceLeft < num_bytes) {
101     if (enlargeBuffer(num_bytes) == -1) {
102       return -1;
103     }
104   }
105 
106   memcpy(nextAdd, source, num_bytes);
107   spaceLeft -= num_bytes;
108 
109   nextAdd += num_bytes;
110   nextAdd[0] = '\0';
111 
112   return num_bytes;
113 }
114 
115 //  TextBuffer::enlargeBuffer(int n)
116 //
117 //  Enlarge the buffer so at least at N
118 //    bytes are free in the buffer.
119 //
120 //  Always enlarges by a power of two.
121 //
122 //  Returns -1 if insufficient memory,
123 //    zero otherwise
124 int
enlargeBuffer(unsigned N)125 TextBuffer::enlargeBuffer(unsigned N)
126 {
127   unsigned addedSize = 0;
128   unsigned newSize   = (currentSize ? currentSize : 1) * 2;
129   char *newSpace;
130 
131   if (spaceLeft < N) {
132     while ((newSize - currentSize) < N) {
133       newSize *= 2;
134     }
135 
136     addedSize = newSize - currentSize;
137 
138     newSpace = static_cast<char *>(ats_realloc(bufferStart, newSize));
139     if (newSpace != nullptr) {
140       nextAdd     = newSpace + static_cast<unsigned>(nextAdd - bufferStart);
141       bufferStart = newSpace;
142       spaceLeft += addedSize;
143       currentSize = newSize;
144     } else {
145       // Out of Memory, Sigh
146       return -1;
147     }
148   }
149 
150   return 0;
151 }
152 
153 // int TextBuffer::rawReadFromFile
154 //
155 // - Issues a single read command on the file descriptor or handle
156 //   passed in and reads in raw data (not assumed to be text, no
157 //   string terminators added).
158 // - Cannot read from file descriptor on win32 because the win32
159 //   read() function replaces CR-LF with LF if the file is not
160 //   opened in binary mode.
161 int
rawReadFromFile(int fd)162 TextBuffer::rawReadFromFile(int fd)
163 {
164   int readSize;
165 
166   // Check to see if we have got a reasonable amount of space left in our
167   //   buffer, if not try to get some more
168   if (spaceLeft < 4096) {
169     if (enlargeBuffer(4096) == -1) {
170       return -1;
171     }
172   }
173 
174   readSize = read(fd, nextAdd, spaceLeft - 1);
175 
176   if (readSize == 0) { // EOF
177     return 0;
178   } else if (readSize < 0) {
179     // Error on read
180     return readSize;
181   } else {
182     nextAdd = nextAdd + readSize;
183     spaceLeft -= readSize;
184     return readSize;
185   }
186 }
187 
188 // Read the entire contents of the given file descriptor.
189 void
slurp(int fd)190 TextBuffer::slurp(int fd)
191 {
192   int nbytes;
193 
194   do {
195     nbytes = readFromFD(fd);
196   } while (nbytes > 0);
197 }
198 
199 // int TextBuffer::readFromFD(int fd)
200 //
201 // Issues a single read command on the file
202 // descriptor passed in.  Attempts to read a minimum of
203 // 512 bytes from file descriptor passed.
204 int
readFromFD(int fd)205 TextBuffer::readFromFD(int fd)
206 {
207   int readSize;
208 
209   // Check to see if we have got a reasonable amount of space left in our
210   //   buffer, if not try to get some more
211   if (spaceLeft < 512) {
212     if (enlargeBuffer(512) == -1) {
213       return -1;
214     }
215   }
216 
217   readSize = read(fd, nextAdd, spaceLeft - 1);
218 
219   if (readSize == 0) {
220     // Socket is empty so we are done
221     return 0;
222   } else if (readSize < 0) {
223     // Error on read
224     return readSize;
225   } else {
226     nextAdd    = nextAdd + readSize;
227     nextAdd[0] = '\0';
228     spaceLeft -= readSize + 1;
229     return readSize;
230   }
231 }
232 
233 void
vformat(const char * fmt,va_list ap)234 TextBuffer::vformat(const char *fmt, va_list ap)
235 {
236   for (bool done = false; !done;) {
237     int num;
238 
239     // Copy the args in case the buffer isn't big enough and we need to
240     // try again. Vsnprintf modifies the va_list on each pass.
241     va_list args;
242     va_copy(args, ap);
243 
244     num = vsnprintf(this->nextAdd, this->spaceLeft, fmt, args);
245 
246     va_end(args);
247 
248     if (static_cast<unsigned>(num) < this->spaceLeft) {
249       // We had enough space to format including the NUL. Since the returned character
250       // count does not include the NUL, we can just increment and the next format will
251       // overwrite the previous NUL.
252       this->spaceLeft -= num;
253       this->nextAdd += num;
254       done = true;
255     } else {
256       if (enlargeBuffer(num + 1) == -1) {
257         return;
258       }
259     }
260   }
261 }
262 
263 void
format(const char * fmt,...)264 TextBuffer::format(const char *fmt, ...)
265 {
266   va_list ap;
267 
268   va_start(ap, fmt);
269   vformat(fmt, ap);
270   va_end(ap);
271 }
272 
273 void
chomp()274 TextBuffer::chomp()
275 {
276   while ((nextAdd > bufferStart) && (nextAdd[-1] == '\n')) {
277     --nextAdd;
278     ++spaceLeft;
279     *nextAdd = '\0';
280   }
281 }
282