1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef AAPT_BIG_BUFFER_H
18#define AAPT_BIG_BUFFER_H
19
20#include <cstring>
21#include <memory>
22#include <string>
23#include <type_traits>
24#include <vector>
25
26#include "android-base/logging.h"
27#include "android-base/macros.h"
28
29namespace aapt {
30
31/**
32 * Inspired by protobuf's ZeroCopyOutputStream, offers blocks of memory
33 * in which to write without knowing the full size of the entire payload.
34 * This is essentially a list of memory blocks. As one fills up, another
35 * block is allocated and appended to the end of the list.
36 */
37class BigBuffer {
38 public:
39  /**
40   * A contiguous block of allocated memory.
41   */
42  struct Block {
43    /**
44     * Pointer to the memory.
45     */
46    std::unique_ptr<uint8_t[]> buffer;
47
48    /**
49     * Size of memory that is currently occupied. The actual
50     * allocation may be larger.
51     */
52    size_t size;
53
54   private:
55    friend class BigBuffer;
56
57    /**
58     * The size of the memory block allocation.
59     */
60    size_t block_size_;
61  };
62
63  typedef std::vector<Block>::const_iterator const_iterator;
64
65  /**
66   * Create a BigBuffer with block allocation sizes
67   * of block_size.
68   */
69  explicit BigBuffer(size_t block_size);
70
71  BigBuffer(BigBuffer&& rhs);
72
73  /**
74   * Number of occupied bytes in all the allocated blocks.
75   */
76  size_t size() const;
77
78  /**
79   * Returns a pointer to an array of T, where T is
80   * a POD type. The elements are zero-initialized.
81   */
82  template <typename T>
83  T* NextBlock(size_t count = 1);
84
85  /**
86   * Returns the next block available and puts the size in out_count.
87   * This is useful for grabbing blocks where the size doesn't matter.
88   * Use BackUp() to give back any bytes that were not used.
89   */
90  void* NextBlock(size_t* out_count);
91
92  /**
93   * Backs up count bytes. This must only be called after NextBlock()
94   * and can not be larger than sizeof(T) * count of the last NextBlock()
95   * call.
96   */
97  void BackUp(size_t count);
98
99  /**
100   * Moves the specified BigBuffer into this one. When this method
101   * returns, buffer is empty.
102   */
103  void AppendBuffer(BigBuffer&& buffer);
104
105  /**
106   * Pads the block with 'bytes' bytes of zero values.
107   */
108  void Pad(size_t bytes);
109
110  /**
111   * Pads the block so that it aligns on a 4 byte boundary.
112   */
113  void Align4();
114
115  size_t block_size() const;
116
117  const_iterator begin() const;
118  const_iterator end() const;
119
120  std::string to_string() const;
121
122 private:
123  DISALLOW_COPY_AND_ASSIGN(BigBuffer);
124
125  /**
126   * Returns a pointer to a buffer of the requested size.
127   * The buffer is zero-initialized.
128   */
129  void* NextBlockImpl(size_t size);
130
131  size_t block_size_;
132  size_t size_;
133  std::vector<Block> blocks_;
134};
135
136inline BigBuffer::BigBuffer(size_t block_size)
137    : block_size_(block_size), size_(0) {}
138
139inline BigBuffer::BigBuffer(BigBuffer&& rhs)
140    : block_size_(rhs.block_size_),
141      size_(rhs.size_),
142      blocks_(std::move(rhs.blocks_)) {}
143
144inline size_t BigBuffer::size() const { return size_; }
145
146inline size_t BigBuffer::block_size() const { return block_size_; }
147
148template <typename T>
149inline T* BigBuffer::NextBlock(size_t count) {
150  static_assert(std::is_standard_layout<T>::value,
151                "T must be standard_layout type");
152  CHECK(count != 0);
153  return reinterpret_cast<T*>(NextBlockImpl(sizeof(T) * count));
154}
155
156inline void BigBuffer::BackUp(size_t count) {
157  Block& block = blocks_.back();
158  block.size -= count;
159  size_ -= count;
160}
161
162inline void BigBuffer::AppendBuffer(BigBuffer&& buffer) {
163  std::move(buffer.blocks_.begin(), buffer.blocks_.end(),
164            std::back_inserter(blocks_));
165  size_ += buffer.size_;
166  buffer.blocks_.clear();
167  buffer.size_ = 0;
168}
169
170inline void BigBuffer::Pad(size_t bytes) { NextBlock<char>(bytes); }
171
172inline void BigBuffer::Align4() {
173  const size_t unaligned = size_ % 4;
174  if (unaligned != 0) {
175    Pad(4 - unaligned);
176  }
177}
178
179inline BigBuffer::const_iterator BigBuffer::begin() const {
180  return blocks_.begin();
181}
182
183inline BigBuffer::const_iterator BigBuffer::end() const {
184  return blocks_.end();
185}
186
187}  // namespace aapt
188
189#endif  // AAPT_BIG_BUFFER_H
190