--- old/src/hotspot/share/utilities/ostream.cpp 2019-03-10 19:12:17.485015968 +0100 +++ new/src/hotspot/share/utilities/ostream.cpp 2019-03-10 19:12:17.269013940 +0100 @@ -938,58 +938,72 @@ } } -bufferedStream::bufferedStream(size_t initial_size, size_t bufmax) : outputStream() { - buffer_length = initial_size; - buffer = NEW_C_HEAP_ARRAY(char, buffer_length, mtInternal); - buffer_pos = 0; - buffer_fixed = false; - buffer_max = bufmax; -} - -bufferedStream::bufferedStream(char* fixed_buffer, size_t fixed_buffer_size, size_t bufmax) : outputStream() { - buffer_length = fixed_buffer_size; - buffer = fixed_buffer; - buffer_pos = 0; - buffer_fixed = true; - buffer_max = bufmax; +bufferedStream::bufferedStream(size_t initial_capacity, size_t max_capacity, size_t flush_threshold) + : outputStream(), + _buffer(NULL), + _pos(0), + _capacity(0), + _owned(true), + _max_capacity(max_capacity), + _flush_threshold(flush_threshold) +{ + assert(initial_capacity > 0 && max_capacity >= initial_capacity, "Sanity"); + _capacity = initial_capacity; + _buffer = NEW_C_HEAP_ARRAY(char, _capacity, mtInternal); +} + +bufferedStream::bufferedStream(char* fixed_buffer, size_t fixed_buffer_size, size_t flush_threshold) +: outputStream(), + _buffer(fixed_buffer), + _pos(0), + _capacity(fixed_buffer_size), + _owned(false), + _max_capacity(fixed_buffer_size), + _flush_threshold(flush_threshold) +{ } void bufferedStream::write(const char* s, size_t len) { - if(buffer_pos + len > buffer_max) { - flush(); + if(_pos + len > _flush_threshold) { + flush(); // Noop for bufferedStream. Flushes and resets for networkStream. } - size_t end = buffer_pos + len; - if (end >= buffer_length) { - if (buffer_fixed) { - // if buffer cannot resize, silently truncate - len = buffer_length - buffer_pos - 1; - } else { - // For small overruns, double the buffer. For larger ones, - // increase to the requested size. - if (end < buffer_length * 2) { - end = buffer_length * 2; - } - buffer = REALLOC_C_HEAP_ARRAY(char, buffer, end, mtInternal); - buffer_length = end; + size_t end = _pos + len; + if (end >= _capacity && _owned && _capacity < _max_capacity) { + // For small overruns, double the buffer. For larger ones, + // increase to the requested size. + if (end < _capacity * 2) { + end = _capacity * 2; + } + // Do not grow beyond max. + if (end > _max_capacity) { + end = _max_capacity; } + _buffer = REALLOC_C_HEAP_ARRAY(char, _buffer, end, mtInternal); + _capacity = end; } - memcpy(buffer + buffer_pos, s, len); - buffer_pos += len; - update_position(s, len); + + const size_t remaining = _capacity - _pos - 1; + len = MIN2(len, remaining); + if (len > 0) { + memcpy(_buffer + _pos, s, len); + _pos += len; + update_position(s, len); + } + } char* bufferedStream::as_string() { - char* copy = NEW_RESOURCE_ARRAY(char, buffer_pos+1); - strncpy(copy, buffer, buffer_pos); - copy[buffer_pos] = 0; // terminating null + char* copy = NEW_RESOURCE_ARRAY(char, _pos+1); + strncpy(copy, _buffer, _pos); + copy[_pos] = 0; // terminating null return copy; } bufferedStream::~bufferedStream() { - if (!buffer_fixed) { - FREE_C_HEAP_ARRAY(char, buffer); + if (_owned) { + FREE_C_HEAP_ARRAY(char, _buffer); } } --- old/src/hotspot/share/utilities/ostream.hpp 2019-03-10 19:12:17.965020476 +0100 +++ new/src/hotspot/share/utilities/ostream.hpp 2019-03-10 19:12:17.753018484 +0100 @@ -252,19 +252,22 @@ // managed in C heap. Not MT-safe. class bufferedStream : public outputStream { protected: - char* buffer; - size_t buffer_pos; - size_t buffer_max; - size_t buffer_length; - bool buffer_fixed; + char* _buffer; + size_t _pos; + size_t _capacity; + const bool _owned; + const size_t _max_capacity; + const size_t _flush_threshold; public: - bufferedStream(size_t initial_bufsize = 256, size_t bufmax = 1024*1024*10); - bufferedStream(char* fixed_buffer, size_t fixed_buffer_size, size_t bufmax = 1024*1024*10); + // Construct a stream which allocates its buffer from C heap up to a maximum of max_bufsize + bufferedStream(size_t initial_capacity = 256, size_t max_capacity = 10 * M, size_t flush_threshold = 10 * M); + // Construct a stream which uses a caller provided buffer + bufferedStream(char* fixed_buffer, size_t max_bufsize, size_t flush_threshold = 10 * M); ~bufferedStream(); virtual void write(const char* c, size_t len); - size_t size() { return buffer_pos; } - const char* base() { return buffer; } - void reset() { buffer_pos = 0; _precount = 0; _position = 0; } + size_t size() { return _pos; } + const char* base() { return _buffer; } + void reset() { _pos = 0; _precount = 0; _position = 0; } char* as_string(); }; --- /dev/null 2019-03-10 14:56:40.176008310 +0100 +++ new/test/hotspot/gtest/utilities/test_ostream.cpp 2019-03-10 19:12:18.225022917 +0100 @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "precompiled.hpp" +#include "unittest.hpp" +#include "utilities/ostream.hpp" + +TEST(BufferedStream, fixed_max_limit) { + // test that bufferedStream honors the max limit in dynamic mode + const int init_sizes[] = { 10, 40, 90, 100, -1 }; + +#define MAXLEN 100 + char tmp[MAXLEN + 1]; + tmp[MAXLEN] = 'X'; + + for (int i = 0; init_sizes[i] != -1; i ++) { + + bufferedStream st(tmp, MAXLEN); + + st.print("012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"); // 120 + ASSERT_LE(st.size(), (size_t)MAXLEN); // should have stopped at 100 + ASSERT_EQ(tmp[MAXLEN], 'X'); + st.reset(); + for (int j = 0; j < MAXLEN * 2; j ++) { + st.print("%c", 41 + (j % 10)); + } + ASSERT_LE(st.size(), (size_t)MAXLEN); // should have stopped at 100 + ASSERT_EQ(tmp[MAXLEN], 'X'); + } + +} + +TEST(BufferedStream, dynamic_max_limit) { + // test that bufferedStream honors the max limit in dynamic mode + const int init_sizes[] = { 10, 40, 90, 100, -1 }; + for (int i = 0; init_sizes[i] != -1; i ++) { + const int max = 100; + bufferedStream st(init_sizes[i], max); + + st.print("012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"); // 120 + ASSERT_LE(st.size(), (size_t)max); // should have stopped at 100 + + st.reset(); + for (int j = 0; j < MAXLEN * 2; j ++) { + st.print("%c", 41 + (j % 10)); + } + ASSERT_LE(st.size(), (size_t)max); // should have stopped at 100 + } +} + + +