src/encode_context.cpp (50 lines of code) (raw):
/*
* Copyright (c) 2016-2019 Spotify AB
*
* Licensed 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 <spotify/json/encode_context.hpp>
#include <algorithm>
#include <limits>
#include <spotify/json/detail/cpuid.hpp>
namespace spotify {
namespace json {
encode_context::encode_context(const std::size_t capacity)
: has_sse42(detail::cpuid().has_sse42()),
_buf(static_cast<char *>(capacity ? std::malloc(capacity) : nullptr)),
_ptr(_buf),
_end(_buf + capacity),
_capacity(capacity) {
if (json_unlikely(!_buf && _capacity > 0)) {
throw std::bad_alloc();
}
}
encode_context::~encode_context() {
std::free(_buf);
}
std::unique_ptr<void, decltype(std::free) *> encode_context::steal_data() {
const auto data = _buf;
_buf = nullptr;
_ptr = nullptr;
_end = nullptr;
_capacity = 0;
return std::unique_ptr<void, decltype(std::free) *>(data, &std::free);
}
char *encode_context::grow_buffer(const std::size_t num_bytes) {
const auto old_size = size();
const auto new_size = std::size_t(old_size + num_bytes);
if (json_unlikely(new_size < old_size)) {
// If we overflow the size integer, it means that we need more memory than
// we can possibly provide, so we should throw an allocation exception.
throw std::bad_alloc();
}
auto new_capacity = std::size_t(_capacity * 2);
if (json_unlikely(new_capacity <= _capacity && _capacity)) {
// If we overflow the capacity integer, set the new capacity to the max
// value of the size type, so that we can handle the case of having say
// 3 GB of memory allocated, growing to 4 GB instead of failing to grow.
new_capacity = std::numeric_limits<std::size_t>::max();
}
// Regardless of what capacity we think we want, we need to ensure that it
// is at least as large as the reserved size. We avoid doing any arithmetics
// here to not have to check for overflow yet again.
const auto actual_capacity = std::max(new_size, new_capacity);
auto* new_buf = static_cast<char *>(std::realloc(_buf, actual_capacity));
if (json_unlikely(!new_buf)) {
throw std::bad_alloc();
}
_buf = new_buf;
_ptr = _buf + old_size;
_end = _buf + actual_capacity;
_capacity = actual_capacity;
return _ptr;
}
} // namespace json
} // namespace spotify