| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 | //  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.//  This source code is licensed under both the GPLv2 (found in the//  COPYING file in the root directory) and Apache 2.0 License//  (found in the LICENSE.Apache file in the root directory).//// Copyright (c) 2011 The LevelDB Authors. All rights reserved.// Use of this source code is governed by a BSD-style license that can be// found in the LICENSE file. See the AUTHORS file for names of contributors.#pragma once#include <algorithm>#include "port/port.h"namespace ROCKSDB_NAMESPACE {// This file contains utilities to handle the alignment of pages and buffers.// Truncate to a multiple of page_size, which is also a page boundary. This// helps to figuring out the right alignment.// Example://   TruncateToPageBoundary(5000, 4096)  => 4096//   TruncateToPageBoundary(10000, 4096) => 8192inline size_t TruncateToPageBoundary(size_t page_size, size_t s) {  s -= (s & (page_size - 1));  assert((s % page_size) == 0);  return s;}// Round up x to a multiple of y.// Example://   Roundup(13, 5)   => 15//   Roundup(201, 16) => 208inline size_t Roundup(size_t x, size_t y) {  return ((x + y - 1) / y) * y;}// Round down x to a multiple of y.// Example://   Rounddown(13, 5)   => 10//   Rounddown(201, 16) => 192inline size_t Rounddown(size_t x, size_t y) { return (x / y) * y; }// AlignedBuffer manages a buffer by taking alignment into consideration, and// aligns the buffer start and end positions. It is mainly used for direct I/O,// though it can be used other purposes as well.// It also supports expanding the managed buffer, and copying whole or part of// the data from old buffer into the new expanded buffer. Such a copy especially// helps in cases avoiding an IO to re-fetch the data from disk.//// Example://   AlignedBuffer buf;//   buf.Alignment(alignment);//   buf.AllocateNewBuffer(user_requested_buf_size);//   ...//   buf.AllocateNewBuffer(2*user_requested_buf_size, /*copy_data*/ true,//                         copy_offset, copy_len);class AlignedBuffer {  size_t alignment_;  std::unique_ptr<char[]> buf_;  size_t capacity_;  size_t cursize_;  char* bufstart_;public:  AlignedBuffer()    : alignment_(),      capacity_(0),      cursize_(0),      bufstart_(nullptr) {  }  AlignedBuffer(AlignedBuffer&& o) ROCKSDB_NOEXCEPT {    *this = std::move(o);  }  AlignedBuffer& operator=(AlignedBuffer&& o) ROCKSDB_NOEXCEPT {    alignment_ = std::move(o.alignment_);    buf_ = std::move(o.buf_);    capacity_ = std::move(o.capacity_);    cursize_ = std::move(o.cursize_);    bufstart_ = std::move(o.bufstart_);    return *this;  }  AlignedBuffer(const AlignedBuffer&) = delete;  AlignedBuffer& operator=(const AlignedBuffer&) = delete;  static bool isAligned(const void* ptr, size_t alignment) {    return reinterpret_cast<uintptr_t>(ptr) % alignment == 0;  }  static bool isAligned(size_t n, size_t alignment) {    return n % alignment == 0;  }  size_t Alignment() const {    return alignment_;  }  size_t Capacity() const {    return capacity_;  }  size_t CurrentSize() const {    return cursize_;  }  const char* BufferStart() const {    return bufstart_;  }  char* BufferStart() { return bufstart_; }  void Clear() {    cursize_ = 0;  }  void Alignment(size_t alignment) {    assert(alignment > 0);    assert((alignment & (alignment - 1)) == 0);    alignment_ = alignment;  }  // Allocates a new buffer and sets the start position to the first aligned  // byte.  //  // requested_capacity: requested new buffer capacity. This capacity will be  //     rounded up based on alignment.  // copy_data: Copy data from old buffer to new buffer. If copy_offset and  //     copy_len are not passed in and the new requested capacity is bigger  //     than the existing buffer's capacity, the data in the exising buffer is  //     fully copied over to the new buffer.  // copy_offset: Copy data from this offset in old buffer.  // copy_len: Number of bytes to copy.  //  // The function does nothing if the new requested_capacity is smaller than  // the current buffer capacity and copy_data is true i.e. the old buffer is  // retained as is.  void AllocateNewBuffer(size_t requested_capacity, bool copy_data = false,                         uint64_t copy_offset = 0, size_t copy_len = 0) {    assert(alignment_ > 0);    assert((alignment_ & (alignment_ - 1)) == 0);    copy_len = copy_len > 0 ? copy_len : cursize_;    if (copy_data && requested_capacity < copy_len) {      // If we are downsizing to a capacity that is smaller than the current      // data in the buffer -- Ignore the request.      return;    }    size_t new_capacity = Roundup(requested_capacity, alignment_);    char* new_buf = new char[new_capacity + alignment_];    char* new_bufstart = reinterpret_cast<char*>(        (reinterpret_cast<uintptr_t>(new_buf) + (alignment_ - 1)) &        ~static_cast<uintptr_t>(alignment_ - 1));    if (copy_data) {      assert(bufstart_ + copy_offset + copy_len <= bufstart_ + cursize_);      memcpy(new_bufstart, bufstart_ + copy_offset, copy_len);      cursize_ = copy_len;    } else {      cursize_ = 0;    }    bufstart_ = new_bufstart;    capacity_ = new_capacity;    buf_.reset(new_buf);  }  // Append to the buffer.  //  // src         : source to copy the data from.  // append_size : number of bytes to copy from src.  // Returns the number of bytes appended.  //  // If append_size is more than the remaining buffer size only the  // remaining-size worth of bytes are copied.  size_t Append(const char* src, size_t append_size) {    size_t buffer_remaining = capacity_ - cursize_;    size_t to_copy = std::min(append_size, buffer_remaining);    if (to_copy > 0) {      memcpy(bufstart_ + cursize_, src, to_copy);      cursize_ += to_copy;    }    return to_copy;  }  // Read from the buffer.  //  // dest      : destination buffer to copy the data to.  // offset    : the buffer offset to start reading from.  // read_size : the number of bytes to copy from the buffer to dest.  // Returns the number of bytes read/copied to dest.  size_t Read(char* dest, size_t offset, size_t read_size) const {    assert(offset < cursize_);    size_t to_read = 0;    if(offset < cursize_) {      to_read = std::min(cursize_ - offset, read_size);    }    if (to_read > 0) {      memcpy(dest, bufstart_ + offset, to_read);    }    return to_read;  }  // Pad to the end of alignment with "padding"  void PadToAlignmentWith(int padding) {    size_t total_size = Roundup(cursize_, alignment_);    size_t pad_size = total_size - cursize_;    if (pad_size > 0) {      assert((pad_size + cursize_) <= capacity_);      memset(bufstart_ + cursize_, padding, pad_size);      cursize_ += pad_size;    }  }  void PadWith(size_t pad_size, int padding) {    assert((pad_size + cursize_) <= capacity_);    memset(bufstart_ + cursize_, padding, pad_size);    cursize_ += pad_size;  }  // After a partial flush move the tail to the beginning of the buffer.  void RefitTail(size_t tail_offset, size_t tail_size) {    if (tail_size > 0) {      memmove(bufstart_, bufstart_ + tail_offset, tail_size);    }    cursize_ = tail_size;  }  // Returns a place to start appending.  // WARNING: Note that it is possible to write past the end of the buffer if  // the buffer is modified without using the write APIs or encapsulation  // offered by AlignedBuffer. It is up to the user to guard against such  // errors.  char* Destination() {    return bufstart_ + cursize_;  }  void Size(size_t cursize) {    cursize_ = cursize;  }};}  // namespace ROCKSDB_NAMESPACE
 |