| /** @file |
| |
| A brief file description |
| |
| @section license License |
| |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| #include <cstdarg> |
| #include <cstdio> |
| #include "tscore/ink_platform.h" |
| #include "tscore/ink_memory.h" |
| #include "tscore/TextBuffer.h" |
| |
| /**************************************************************************** |
| * |
| * TextBuffer.cc - A self-expanding buffer, primarily meant for strings |
| * |
| * |
| * |
| ****************************************************************************/ |
| |
| TextBuffer::TextBuffer(int size) |
| { |
| bufferStart = nullptr; |
| nextAdd = nullptr; |
| currentSize = spaceLeft = 0; |
| if (size > 0) { |
| // Institute a minimum size |
| if (size < 1024) { |
| size = 1024; |
| } |
| |
| bufferStart = static_cast<char *>(ats_malloc(size)); |
| nextAdd = bufferStart; |
| currentSize = size; |
| spaceLeft = size - 1; // Leave room for a terminator; |
| nextAdd[0] = '\0'; |
| } |
| } |
| |
| TextBuffer::~TextBuffer() |
| { |
| ats_free(bufferStart); |
| } |
| |
| char * |
| TextBuffer::release() |
| { |
| char *ret = bufferStart; |
| |
| bufferStart = nextAdd = nullptr; |
| currentSize = spaceLeft = 0; |
| |
| return ret; |
| } |
| |
| // void TextBuffer::reUse() |
| // |
| // Sets the text buffer for reuse by repositioning the |
| // ptrs to beginning of buffer. The buffer space is |
| // reused |
| void |
| TextBuffer::reUse() |
| { |
| if (bufferStart != nullptr) { |
| nextAdd = bufferStart; |
| spaceLeft = currentSize - 1; |
| nextAdd[0] = '\0'; |
| } |
| } |
| |
| // int TextBuffer::copyFrom(void*,int num_bytes) |
| // |
| // |
| // Copy N bytes (determined by num_bytes) on to the |
| // end of the buffer. |
| // |
| // Returns the number of bytes copies or |
| // -1 if there was insufficient memory |
| int |
| TextBuffer::copyFrom(const void *source, unsigned num_bytes) |
| { |
| // Get more space if necessary |
| if (spaceLeft < num_bytes) { |
| if (enlargeBuffer(num_bytes) == -1) { |
| return -1; |
| } |
| } |
| |
| memcpy(nextAdd, source, num_bytes); |
| spaceLeft -= num_bytes; |
| |
| nextAdd += num_bytes; |
| nextAdd[0] = '\0'; |
| |
| return num_bytes; |
| } |
| |
| // TextBuffer::enlargeBuffer(int n) |
| // |
| // Enlarge the buffer so at least at N |
| // bytes are free in the buffer. |
| // |
| // Always enlarges by a power of two. |
| // |
| // Returns -1 if insufficient memory, |
| // zero otherwise |
| int |
| TextBuffer::enlargeBuffer(unsigned N) |
| { |
| unsigned addedSize = 0; |
| unsigned newSize = (currentSize ? currentSize : 1) * 2; |
| char *newSpace; |
| |
| if (spaceLeft < N) { |
| while ((newSize - currentSize) < N) { |
| newSize *= 2; |
| } |
| |
| addedSize = newSize - currentSize; |
| |
| newSpace = static_cast<char *>(ats_realloc(bufferStart, newSize)); |
| if (newSpace != nullptr) { |
| nextAdd = newSpace + static_cast<unsigned>(nextAdd - bufferStart); |
| bufferStart = newSpace; |
| spaceLeft += addedSize; |
| currentSize = newSize; |
| } else { |
| // Out of Memory, Sigh |
| return -1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| // int TextBuffer::rawReadFromFile |
| // |
| // - Issues a single read command on the file descriptor or handle |
| // passed in and reads in raw data (not assumed to be text, no |
| // string terminators added). |
| // - Cannot read from file descriptor on win32 because the win32 |
| // read() function replaces CR-LF with LF if the file is not |
| // opened in binary mode. |
| int |
| TextBuffer::rawReadFromFile(int fd) |
| { |
| int readSize; |
| |
| // Check to see if we have got a reasonable amount of space left in our |
| // buffer, if not try to get some more |
| if (spaceLeft < 4096) { |
| if (enlargeBuffer(4096) == -1) { |
| return -1; |
| } |
| } |
| |
| readSize = read(fd, nextAdd, spaceLeft - 1); |
| |
| if (readSize == 0) { // EOF |
| return 0; |
| } else if (readSize < 0) { |
| // Error on read |
| return readSize; |
| } else { |
| nextAdd = nextAdd + readSize; |
| spaceLeft -= readSize; |
| return readSize; |
| } |
| } |
| |
| // Read the entire contents of the given file descriptor. |
| void |
| TextBuffer::slurp(int fd) |
| { |
| int nbytes; |
| |
| do { |
| nbytes = readFromFD(fd); |
| } while (nbytes > 0); |
| } |
| |
| // int TextBuffer::readFromFD(int fd) |
| // |
| // Issues a single read command on the file |
| // descriptor passed in. Attempts to read a minimum of |
| // 512 bytes from file descriptor passed. |
| int |
| TextBuffer::readFromFD(int fd) |
| { |
| int readSize; |
| |
| // Check to see if we have got a reasonable amount of space left in our |
| // buffer, if not try to get some more |
| if (spaceLeft < 512) { |
| if (enlargeBuffer(512) == -1) { |
| return -1; |
| } |
| } |
| |
| readSize = read(fd, nextAdd, spaceLeft - 1); |
| |
| if (readSize == 0) { |
| // Socket is empty so we are done |
| return 0; |
| } else if (readSize < 0) { |
| // Error on read |
| return readSize; |
| } else { |
| nextAdd = nextAdd + readSize; |
| nextAdd[0] = '\0'; |
| spaceLeft -= readSize + 1; |
| return readSize; |
| } |
| } |
| |
| void |
| TextBuffer::vformat(const char *fmt, va_list ap) |
| { |
| for (bool done = false; !done;) { |
| int num; |
| |
| // Copy the args in case the buffer isn't big enough and we need to |
| // try again. Vsnprintf modifies the va_list on each pass. |
| va_list args; |
| va_copy(args, ap); |
| |
| num = vsnprintf(this->nextAdd, this->spaceLeft, fmt, args); |
| |
| va_end(args); |
| |
| if (static_cast<unsigned>(num) < this->spaceLeft) { |
| // We had enough space to format including the NUL. Since the returned character |
| // count does not include the NUL, we can just increment and the next format will |
| // overwrite the previous NUL. |
| this->spaceLeft -= num; |
| this->nextAdd += num; |
| done = true; |
| } else { |
| if (enlargeBuffer(num + 1) == -1) { |
| return; |
| } |
| } |
| } |
| } |
| |
| void |
| TextBuffer::format(const char *fmt, ...) |
| { |
| va_list ap; |
| |
| va_start(ap, fmt); |
| vformat(fmt, ap); |
| va_end(ap); |
| } |
| |
| void |
| TextBuffer::chomp() |
| { |
| while ((nextAdd > bufferStart) && (nextAdd[-1] == '\n')) { |
| --nextAdd; |
| ++spaceLeft; |
| *nextAdd = '\0'; |
| } |
| } |