| /* |
| * Copyright (C) 2012 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include <wtf/StringPrintStream.h> |
| |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <wtf/MallocSpan.h> |
| #include <wtf/StdLibExtras.h> |
| |
| namespace WTF { |
| |
| StringPrintStream::StringPrintStream() |
| { |
| m_buffer = std::span { m_inlineBuffer }; |
| m_buffer[0] = 0; // Make sure that we always have a null terminator. |
| } |
| |
| StringPrintStream::~StringPrintStream() |
| { |
| if (m_buffer.data() != m_inlineBuffer.data()) |
| fastFree(m_buffer.data()); |
| } |
| |
| WTF_ALLOW_UNSAFE_BUFFER_USAGE_BEGIN |
| |
| void StringPrintStream::vprintf(const char* format, va_list passedArgList) |
| { |
| ASSERT_WITH_SECURITY_IMPLICATION(m_length < m_buffer.size()); |
| ASSERT(!m_buffer[m_length]); |
| |
| while (true) { |
| va_list argList; |
| va_copy(argList, passedArgList); |
| |
| auto remaining = m_buffer.subspan(m_length); |
| int numberOfBytesNotIncludingTerminatorThatWouldHaveBeenWritten = vsnprintf(remaining.data(), remaining.size(), format, argList); |
| |
| va_end(argList); |
| |
| RELEASE_ASSERT(numberOfBytesNotIncludingTerminatorThatWouldHaveBeenWritten >= 0); |
| |
| size_t numberOfBytesThatWouldHaveBeenWritten = static_cast<size_t>(numberOfBytesNotIncludingTerminatorThatWouldHaveBeenWritten) + 1; |
| |
| if (m_length + numberOfBytesThatWouldHaveBeenWritten <= m_buffer.size()) { |
| m_length += static_cast<size_t>(numberOfBytesNotIncludingTerminatorThatWouldHaveBeenWritten); |
| ASSERT(!m_buffer[m_length]); |
| return; // This means that vsnprintf() succeeded. |
| } |
| |
| increaseSize(m_length + numberOfBytesThatWouldHaveBeenWritten); |
| } |
| } |
| |
| CString StringPrintStream::toCString() const |
| { |
| ASSERT(m_length == strlenSpan(m_buffer)); |
| return CString(m_buffer.first(m_length)); |
| } |
| |
| void StringPrintStream::reset() |
| { |
| m_length = 0; |
| m_buffer[0] = 0; |
| } |
| |
| Expected<String, UTF8ConversionError> StringPrintStream::tryToString() const |
| { |
| ASSERT(m_length == strlenSpan(m_buffer)); |
| if (m_length > String::MaxLength) |
| return makeUnexpected(UTF8ConversionError::OutOfMemory); |
| return String::fromUTF8(m_buffer.first(m_length)); |
| } |
| |
| String StringPrintStream::toString() const |
| { |
| ASSERT(m_length == strlenSpan(m_buffer)); |
| return String::fromUTF8(m_buffer.first(m_length)); |
| } |
| |
| String StringPrintStream::toStringWithLatin1Fallback() const |
| { |
| ASSERT(m_length == strlenSpan(m_buffer)); |
| return String::fromUTF8WithLatin1Fallback(m_buffer.first(m_length)); |
| } |
| |
| WTF_ALLOW_UNSAFE_BUFFER_USAGE_END |
| |
| void StringPrintStream::increaseSize(size_t newSize) |
| { |
| ASSERT_WITH_SECURITY_IMPLICATION(newSize > m_buffer.size()); |
| ASSERT(newSize > m_inlineBuffer.size()); |
| |
| // Use exponential resizing to reduce thrashing. |
| newSize = newSize << 1; |
| |
| // Use fastMalloc instead of fastRealloc because we know that for the sizes we're using, |
| // fastRealloc will just do malloc+free anyway. Also, this simplifies the code since |
| // we can't realloc the inline buffer. |
| auto newBuffer = MallocSpan<char>::malloc(newSize); |
| memcpySpan(newBuffer.mutableSpan(), m_buffer.first(m_length + 1)); |
| if (m_buffer.data() != m_inlineBuffer.data()) |
| fastFree(m_buffer.data()); |
| m_buffer = newBuffer.leakSpan(); |
| } |
| |
| } // namespace WTF |