// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "mojo/public/bindings/lib/buffer.h" #include <assert.h> #include <stdlib.h> #include <string.h> #include <algorithm> #include "mojo/public/bindings/lib/bindings_serialization.h" #include "mojo/public/bindings/lib/bindings_support.h" // Scrub memory in debug builds to help catch use-after-free bugs. #ifdef NDEBUG #define DEBUG_SCRUB(address, size) (void) (address), (void) (size) #else #define DEBUG_SCRUB(address, size) memset(address, 0xCD, size) #endif namespace mojo { //----------------------------------------------------------------------------- Buffer::Buffer() { previous_ = BindingsSupport::Get()->SetCurrentBuffer(this); } Buffer::~Buffer() { Buffer* buf MOJO_ALLOW_UNUSED = BindingsSupport::Get()->SetCurrentBuffer(previous_); assert(buf == this); } Buffer* Buffer::current() { return BindingsSupport::Get()->GetCurrentBuffer(); } //----------------------------------------------------------------------------- namespace internal { ScratchBuffer::ScratchBuffer() : overflow_(NULL) { fixed_.next = NULL; fixed_.cursor = fixed_data_; fixed_.end = fixed_data_ + kMinSegmentSize; } ScratchBuffer::~ScratchBuffer() { // Invoke destructors in reverse order to mirror allocation order. std::deque<PendingDestructor>::reverse_iterator it; for (it = pending_dtors_.rbegin(); it != pending_dtors_.rend(); ++it) it->func(it->address); while (overflow_) { Segment* doomed = overflow_; overflow_ = overflow_->next; DEBUG_SCRUB(doomed, doomed->end - reinterpret_cast<char*>(doomed)); free(doomed); } DEBUG_SCRUB(fixed_data_, sizeof(fixed_data_)); } void* ScratchBuffer::Allocate(size_t delta, Destructor func) { delta = internal::Align(delta); void* result = AllocateInSegment(&fixed_, delta); if (!result) { if (overflow_) result = AllocateInSegment(overflow_, delta); if (!result) { AddOverflowSegment(delta); result = AllocateInSegment(overflow_, delta); } } if (func) { PendingDestructor dtor; dtor.func = func; dtor.address = result; pending_dtors_.push_back(dtor); } return result; } void* ScratchBuffer::AllocateInSegment(Segment* segment, size_t delta) { void* result; if (static_cast<size_t>(segment->end - segment->cursor) >= delta) { result = segment->cursor; memset(result, 0, delta); segment->cursor += delta; } else { result = NULL; } return result; } void ScratchBuffer::AddOverflowSegment(size_t delta) { if (delta < kMinSegmentSize) delta = kMinSegmentSize; // Ensure segment buffer is aligned. size_t segment_size = internal::Align(sizeof(Segment)) + delta; Segment* segment = static_cast<Segment*>(malloc(segment_size)); segment->next = overflow_; segment->cursor = reinterpret_cast<char*>(segment + 1); segment->end = segment->cursor + delta; overflow_ = segment; } //----------------------------------------------------------------------------- FixedBuffer::FixedBuffer(size_t size) : ptr_(NULL), cursor_(0), size_(internal::Align(size)) { ptr_ = static_cast<char*>(calloc(size_, 1)); } FixedBuffer::~FixedBuffer() { free(ptr_); } void* FixedBuffer::Allocate(size_t delta, Destructor dtor) { assert(!dtor); delta = internal::Align(delta); // TODO(darin): Using <assert.h> is probably not going to cut it. assert(delta > 0); assert(cursor_ + delta <= size_); if (cursor_ + delta > size_) return NULL; char* result = ptr_ + cursor_; cursor_ += delta; return result; } void* FixedBuffer::Leak() { char* ptr = ptr_; ptr_ = NULL; cursor_ = 0; size_ = 0; return ptr; } } // namespace internal } // namespace mojo