Home | History | Annotate | Download | only in messages
      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;
    151   ArrayOutputStreamBuffer(void* data, size_t size)
    152       : OutputStreamBuffer(data, size), data_(pos_) {}
    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.
    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 
    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.
    239   InputStreamBuffer* stream_buffer() { return stream_buffer_; }
    240 
    241   // Wire type of the current field.
    242   WireType wire_type() const { return static_cast<WireType>(wire_type_); }
    243 
    244   // Field number of the current field.
    245   uint64_t field_number() const { return field_number_; }
    246 
    247   // Size of the field data, if known in advance.
    248   size_t field_size() const { return field_size_; }
    249 
    250   // Whether all data is consumed.
    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.
    291   OutputStreamBuffer* stream_buffer() { return stream_buffer_; }
    292 
    293   // Sets the field number to use when emitting a tag.
    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.
    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