• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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