blob: 84164054c6eb96d4609870385821f225272953bf [file] [log] [blame]
// 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.
#ifndef IMPALA_RUNTIME_STRING_BUFFER_H
#define IMPALA_RUNTIME_STRING_BUFFER_H
#include "common/status.h"
#include "runtime/mem-pool.h"
#include "runtime/mem-tracker.h"
#include "runtime/string-value.h"
#include "util/ubsan.h"
namespace impala {
/// Dynamic-sizable string (similar to std::string) but without as many
/// copies and allocations.
/// StringBuffer is a buffer of char allocated from 'pool'. Current usage and size of the
/// buffer are tracked in 'len_' and 'buffer_size_' respectively. It supports a subset of
/// the std::string functionality but will only allocate bigger string buffers as
/// necessary. std::string tries to be immutable and will reallocate very often.
/// std::string should be avoided in all hot paths.
class StringBuffer {
public:
/// C'tor for StringBuffer. Memory backing the string will be allocated from
/// the pool as necessary. Can optionally be initialized from a StringValue.
StringBuffer(MemPool* pool, StringValue* str = NULL)
: pool_(pool), buffer_(NULL), len_(0), buffer_size_(0) {
DCHECK(pool_ != NULL);
if (str != NULL) {
buffer_ = str->ptr;
len_ = buffer_size_ = str->len;
}
}
/// Append 'str' to the current string, allocating a new buffer as necessary.
/// Return error status if memory limit is exceeded.
Status Append(const char* str, int64_t str_len) WARN_UNUSED_RESULT {
int64_t new_len = len_ + str_len;
if (UNLIKELY(new_len > buffer_size_)) RETURN_IF_ERROR(GrowBuffer(new_len));
Ubsan::MemCpy(buffer_ + len_, str, str_len);
len_ += str_len;
return Status::OK();
}
/// Wrapper around append() for input type 'uint8_t'.
Status Append(const uint8_t* str, int64_t str_len) WARN_UNUSED_RESULT {
return Append(reinterpret_cast<const char*>(str), str_len);
}
/// Clear the underlying StringValue. The allocated buffer can be reused.
void Clear() { len_ = 0; }
/// Reset the usage and size of the buffer. Note that the allocated buffer is
/// retained but cannot be reused.
void Reset() {
len_ = 0;
buffer_size_ = 0;
buffer_ = NULL;
}
/// Returns true if no byte is consumed in the buffer.
bool IsEmpty() const { return len_ == 0; }
/// Grows the buffer to be at least 'new_size', copying over the previous data
/// into the new buffer. The old buffer is not freed. Return an error status if
/// growing the buffer will exceed memory limit.
Status GrowBuffer(int64_t new_size) WARN_UNUSED_RESULT {
if (LIKELY(new_size > buffer_size_)) {
int64_t old_size = buffer_size_;
buffer_size_ = std::max<int64_t>(buffer_size_ * 2, new_size);
char* new_buffer = reinterpret_cast<char*>(pool_->TryAllocate(buffer_size_));
if (UNLIKELY(new_buffer == NULL)) {
string details =
strings::Substitute("StringBuffer failed to grow buffer from $0 to $1 bytes.",
old_size, buffer_size_);
return pool_->mem_tracker()->MemLimitExceeded(NULL, details, buffer_size_);
}
if (LIKELY(len_ > 0)) memcpy(new_buffer, buffer_, len_);
buffer_ = new_buffer;
}
return Status::OK();
}
/// Returns the number of bytes consumed in the buffer.
int64_t len() const { return len_; }
/// Returns the pointer to the buffer. Note that it's the caller's responsibility
/// to not retain the pointer to 'buffer_' across call to Append() as the buffer_
/// may be relocated in Append().
char* buffer() const { return buffer_; }
/// Returns the size of the buffer.
int64_t buffer_size() const { return buffer_size_; }
private:
MemPool* pool_;
char* buffer_;
int64_t len_; // number of bytes consumed in the buffer.
int64_t buffer_size_; // size of the buffer.
};
}
#endif