1 /* 2 * Copyright (C) 2016 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 NVRAM_MESSAGES_IO_H_ 18 #define NVRAM_MESSAGES_IO_H_ 19 20 extern "C" { 21 #include <stddef.h> 22 #include <stdint.h> 23 } 24 25 #include <nvram/messages/blob.h> 26 #include <nvram/messages/compiler.h> 27 28 namespace nvram { 29 30 // Abstraction used by the protobuf decoder to read data. The idea here is that 31 // |InputStreamBuffer| maintains a window of the data to be read. Access to the 32 // contents of the current window is direct, i.e. doesn't need to go through 33 // virtual dispatch to subclasses. Whenever the window is exhausted, the next 34 // window must be set up. This latter operation is left for implementation of 35 // the virtual |Advance()| member function in subclasses, which is entirely free 36 // to pull its data from anywhere. 37 class NVRAM_EXPORT InputStreamBuffer { 38 public: 39 InputStreamBuffer() = default; 40 InputStreamBuffer(const void* data, size_t size); 41 InputStreamBuffer(const void* start, const void* end); 42 virtual ~InputStreamBuffer() = default; 43 44 // Checks whether the stream is exhausted; 45 bool Done(); 46 47 // Consume |size| bytes from the stream and store them in the provided |data| 48 // buffer. Returns false if insufficient bytes are available. 49 bool Read(void* data, size_t size); 50 51 // Consume a single byte and place it in |byte|. Returns true if successful, 52 // i.e. if there was a byte available. 53 bool ReadByte(uint8_t* byte); 54 55 // Discard |size| bytes from the stream. Returns false if there are fewer 56 // bytes available. 57 bool Skip(size_t size); 58 59 protected: 60 // Update the |pos_| and |end_| pointers for the next buffer window. Returns 61 // true if the window was successfully set up, false on I/O errors or stream 62 // exhaustion. The default implementation just returns false to signal 63 // immediate stream exhaustion. Subclasses should override this to pull in 64 // more data from the underlying data source. 65 virtual bool Advance(); 66 67 // Pointers to the buffer to read from. |InputStreamBuffer| only advances 68 // |pos_| until reaching |end_|. At this point, |Advance| is called for the 69 // subclass to initialize the next buffer window and update the pointers. 70 const uint8_t* pos_ = nullptr; 71 const uint8_t* end_ = nullptr; 72 73 // Allow |NestedInputStreamBuffer| to mess with |pos_| and |end_|, also in its 74 // delegate, which isn't necessarily of type |NestedInputStreamBuffer|. 75 friend class NestedInputStreamBuffer; 76 }; 77 78 // An |InputStreamBuffer| implementation that pulls its data from a delegate, 79 // but only up to a predetermined limit of bytes. 80 class NVRAM_EXPORT NestedInputStreamBuffer : public InputStreamBuffer { 81 public: 82 // Initialize a |NestedInputStreamBuffer| to provide at most |size| bytes from 83 // |delegate|. Note that |delegate| must remain valid throughout the life time 84 // of this |NestedInputStreamBuffer|. 85 NestedInputStreamBuffer(InputStreamBuffer* delegate, size_t size); 86 ~NestedInputStreamBuffer() override = default; 87 88 private: 89 // InputStreamBuffer: 90 bool Advance() override; 91 92 // Determine the input window end based on |delegate|'s current window and the 93 // requested |size| to read. If |size| can be satisfied from |delegate|'s 94 // current window, return an end pointer within that window. If size is larger 95 // than the remaining bytes available in |delegate|'s input window, return 96 // |delegate|'s |end_| pointer. 97 static const uint8_t* ClampEnd(InputStreamBuffer* delegate, size_t size); 98 99 InputStreamBuffer* delegate_; 100 size_t remaining_; 101 }; 102 103 // Abstraction used by the protobuf decoder to output data. This class maintains 104 // a current window of memory to write output to. Access to the current window's 105 // bytes is direct and doesn't require virtual dispatch. Once the capacity of 106 // the current window is exhausted, the virtual |Advance()| member function is 107 // invoked to set up a new window. Subclasses are entirely free to implement 108 // this operation as appropriate for their I/O mechanism, for example a 109 // socket-based implementations might flush the buffer to the socket and reset 110 // the window pointers to accept more output. 111 class NVRAM_EXPORT OutputStreamBuffer { 112 public: 113 OutputStreamBuffer() = default; 114 OutputStreamBuffer(void* data, size_t size); 115 OutputStreamBuffer(void* data, void* end); 116 virtual ~OutputStreamBuffer() = default; 117 118 // Checks whether the stream is exhausted. 119 bool Done(); 120 121 // Writes a blob of |size| bytes provided in |data| to the underlying buffer. 122 // Returns false if there is not enough space available. 123 bool Write(const void* data, size_t size); 124 125 // Writes |byte| to the underlying buffer. Returns false if there is not 126 // enough space available. 127 bool WriteByte(uint8_t byte); 128 129 protected: 130 // Set up the next data buffer window in |pos_| and |end_|. Returns true on 131 // success, false on I/O errors or stream exhaustion. The default 132 // implementation unconditionally returns false, i.e. signaling stream 133 // exhaustion after the initial window is filled. Subclasses should override 134 // this to flush buffers to the underlying data sink and set up a fresh buffer 135 // for more data as appropriate. 136 virtual bool Advance(); 137 138 // The |pos_| and |end_| pointers define a window of writable buffer space for 139 // |OutputStreamBuffer| to place data in. |pos_| grows towards |end_| as 140 // writes occur. Once |pos_| hits |end_|, |OutputStreamBuffer| will call 141 // |Advance|, which subclasses can implement to provide a new buffer window in 142 // |pos_| and |end_|. 143 uint8_t* pos_ = nullptr; 144 uint8_t* end_ = nullptr; 145 }; 146 147 // An |OutputStreamBuffer| backed by a single data buffer. 148 class NVRAM_EXPORT ArrayOutputStreamBuffer : public OutputStreamBuffer { 149 public: 150 ArrayOutputStreamBuffer() = default; ArrayOutputStreamBuffer(void * data,size_t size)151 ArrayOutputStreamBuffer(void* data, size_t size) 152 : OutputStreamBuffer(data, size), data_(pos_) {} ArrayOutputStreamBuffer(void * data,void * end)153 ArrayOutputStreamBuffer(void* data, void* end) 154 : OutputStreamBuffer(data, end), data_(pos_) {} 155 ~ArrayOutputStreamBuffer() override = default; 156 157 // Returns the number of bytes already written. bytes_written()158 size_t bytes_written() const { return pos_ - data_; } 159 160 private: 161 uint8_t* data_ = nullptr; 162 }; 163 164 // An |OutputStream| implementation that doesn't write anything, but just counts 165 // the number of bytes written. 166 class NVRAM_EXPORT CountingOutputStreamBuffer : public OutputStreamBuffer { 167 public: 168 CountingOutputStreamBuffer(); 169 ~CountingOutputStreamBuffer() override = default; 170 bytes_written()171 size_t bytes_written() const { 172 return bytes_written_ + (pos_ - scratch_space_); 173 } 174 175 protected: 176 // OutputStreamBuffer: 177 bool Advance() override; 178 179 private: 180 // We share a single scratch buffer that all |CountingOutputStreamBuffer| 181 // instances use as the destination for writes. Its contents are pretty much 182 // unpredictable. 183 // 184 // TODO(mnissler): This adds a static 256 bytes memory allocation to each 185 // process linking to this code. If that becomes a problem, we might want to 186 // be smarter here and dynamically allocate a chunk of memory only when it's 187 // needed, or maybe even map some address space that's not even backed by 188 // actual memory (not sure that's possible). 189 static constexpr size_t kScratchSpaceSize = 256; 190 static uint8_t scratch_space_[kScratchSpaceSize]; 191 192 // Number of bytes that had been written when the last |Advance()| call 193 // occurred. 194 size_t bytes_written_ = 0; 195 }; 196 197 // An |OutputStreamBuffer| implementation that stores all data to a wrapped 198 // |Blob|, growing it as necessary. 199 class NVRAM_EXPORT BlobOutputStreamBuffer : public OutputStreamBuffer { 200 public: 201 // Construct a |BlobOutputStreamBuffer| that stores all written data to 202 // |blob|, which will get resized as necessary. Note that |blob| must remain 203 // valid for the life time of the |BlobOutputStreamBuffer| object. 204 explicit BlobOutputStreamBuffer(Blob* blob); 205 ~BlobOutputStreamBuffer() override = default; 206 207 // Truncate the blob to match the current output size. 208 bool Truncate(); 209 210 protected: 211 // OutputStreamBuffer: 212 bool Advance() override; 213 214 private: 215 Blob* blob_; 216 }; 217 218 // Protobuf wire types. 219 enum class WireType : int8_t { 220 kVarint = 0, 221 kFixed64 = 1, 222 kLengthDelimited = 2, 223 kStartGroup = 3, 224 kEndGroup = 4, 225 kFixed32 = 5, 226 }; 227 228 // A class implementing a parser for the low-level protobuf wire format. It 229 // obtains raw data from a wrapped |InputStream| and offers member functions 230 // that facilitate decoding the data according to the protobuf wire format. 231 class NVRAM_EXPORT ProtoReader { 232 public: 233 // Construct a new |ProtoReader| that consumes data from |stream_buffer|. 234 // |stream_buffer| must remain valid throughout the life time of the 235 // |ProtoReader|. 236 explicit ProtoReader(InputStreamBuffer* stream_buffer); 237 238 // Access to the underlying stream buffer. stream_buffer()239 InputStreamBuffer* stream_buffer() { return stream_buffer_; } 240 241 // Wire type of the current field. wire_type()242 WireType wire_type() const { return static_cast<WireType>(wire_type_); } 243 244 // Field number of the current field. field_number()245 uint64_t field_number() const { return field_number_; } 246 247 // Size of the field data, if known in advance. field_size()248 size_t field_size() const { return field_size_; } 249 250 // Whether all data is consumed. Done()251 bool Done() const { return stream_buffer_->Done(); } 252 253 // Reads the next wire tag from the current position in the underlying 254 // |stream_buffer_| and initializes internal fields. Previous state is 255 // discarded silently. 256 bool ReadWireTag(); 257 258 // Read a varint-encoded field and advances to the next field. Returns true if 259 // successful. 260 bool ReadVarint(uint64_t* value); 261 262 // Read field data. Checks that |size| matches |field_size()| and copies out 263 // the data to the provided |data| buffer. Advances to the next field and 264 // returns true if successful. 265 bool ReadLengthDelimited(void* data, size_t size); 266 267 // Skips over the current field data. 268 bool SkipField(); 269 270 private: 271 static constexpr int8_t kInvalidWireType = -1; 272 273 InputStreamBuffer* stream_buffer_; 274 275 // Information about the current field. |wire_type == kInvalidWireType| 276 // indicates that there is no current field to be consumed. 277 int8_t wire_type_ = kInvalidWireType; 278 uint64_t field_number_ = 0; 279 size_t field_size_ = 0; 280 }; 281 282 // |ProtoWriter| contains logic to write raw data according to the protobuf wire 283 // format to an |OutputStreamBuffer|. 284 class NVRAM_EXPORT ProtoWriter { 285 public: 286 // Construct a |ProtoWriter| which will send its output to |stream_buffer|. 287 // |stream_buffer| must remain valid for the life time of the |ProtoWriter|. 288 explicit ProtoWriter(OutputStreamBuffer* stream_buffer); 289 290 // Access to the underlying stream buffer. stream_buffer()291 OutputStreamBuffer* stream_buffer() { return stream_buffer_; } 292 293 // Sets the field number to use when emitting a tag. set_field_number(uint64_t field_number)294 void set_field_number(uint64_t field_number) { field_number_ = field_number; } 295 296 // Whether the writer has exhausted the underlying |OutputStream|'s capacity. Done()297 bool Done() const { return stream_buffer_->Done(); } 298 299 // Write |value| as a varint-encoded field. Returns true if successful, i.e. 300 // the data was successfully written to |stream_buffer_|. 301 bool WriteVarint(uint64_t value); 302 303 // Write |size| bytes stored at |data| to |stream_buffer_|. Returns true if 304 // successful, i.e. the data was successfully written to |stream_buffer_|. 305 bool WriteLengthDelimited(const void* data, size_t size); 306 307 // Writes a wire tag for a length-delimited field, followed by a length 308 // indication for |size| data bytes. It is up to the caller to emit exactly 309 // |size| bytes to |stream_buffer()|, otherwise the encoded data will be 310 // malformed. 311 bool WriteLengthHeader(size_t size); 312 313 private: 314 // A helper to write a wire tag using the current field number and the 315 // provided wire type. 316 bool WriteWireTag(WireType wire_type); 317 318 OutputStreamBuffer* stream_buffer_; 319 uint64_t field_number_ = 0; 320 }; 321 322 } // namespace nvram 323 324 #endif // NVRAM_MESSAGES_IO_H_ 325