Home | History | Annotate | Download | only in protozero
      1 /*
      2  * Copyright (C) 2017 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 INCLUDE_PERFETTO_PROTOZERO_SCATTERED_STREAM_WRITER_H_
     18 #define INCLUDE_PERFETTO_PROTOZERO_SCATTERED_STREAM_WRITER_H_
     19 
     20 #include <assert.h>
     21 #include <stddef.h>
     22 #include <stdint.h>
     23 #include <string.h>
     24 
     25 #include "perfetto/base/export.h"
     26 #include "perfetto/base/utils.h"
     27 #include "perfetto/protozero/contiguous_memory_range.h"
     28 
     29 namespace protozero {
     30 
     31 // This class deals with the following problem: append-only proto messages want
     32 // to write a stream of bytes, without caring about the implementation of the
     33 // underlying buffer (which concretely will be either the trace ring buffer
     34 // or a heap-allocated buffer). The main deal is: proto messages don't know in
     35 // advance what their size will be.
     36 // Due to the tracing buffer being split into fixed-size chunks, on some
     37 // occasions, these writes need to be spread over two (or more) non-contiguous
     38 // chunks of memory. Similarly, when the buffer is backed by the heap, we want
     39 // to avoid realloc() calls, as they might cause a full copy of the contents
     40 // of the buffer.
     41 // The purpose of this class is to abstract away the non-contiguous write logic.
     42 // This class knows how to deal with writes as long as they fall in the same
     43 // ContiguousMemoryRange and defers the chunk-chaining logic to the Delegate.
     44 class PERFETTO_EXPORT ScatteredStreamWriter {
     45  public:
     46   class Delegate {
     47    public:
     48     virtual ~Delegate();
     49     virtual ContiguousMemoryRange GetNewBuffer() = 0;
     50   };
     51 
     52   explicit ScatteredStreamWriter(Delegate* delegate);
     53   ~ScatteredStreamWriter();
     54 
     55   inline void WriteByte(uint8_t value) {
     56     if (write_ptr_ >= cur_range_.end)
     57       Extend();
     58     *write_ptr_++ = value;
     59   }
     60 
     61   // Assumes that the caller checked that there is enough headroom.
     62   // TODO(primiano): perf optimization, this is a tracing hot path. The
     63   // compiler can make strong optimization on memcpy if the size arg is a
     64   // constexpr. Make a templated variant of this for fixed-size writes.
     65   // TODO(primiano): restrict / noalias might also help.
     66   inline void WriteBytesUnsafe(const uint8_t* src, size_t size) {
     67     uint8_t* const end = write_ptr_ + size;
     68     assert(end <= cur_range_.end);
     69     memcpy(write_ptr_, src, size);
     70     write_ptr_ = end;
     71   }
     72 
     73   inline void WriteBytes(const uint8_t* src, size_t size) {
     74     uint8_t* const end = write_ptr_ + size;
     75     if (PERFETTO_LIKELY(end <= cur_range_.end))
     76       return WriteBytesUnsafe(src, size);
     77     WriteBytesSlowPath(src, size);
     78   }
     79 
     80   void WriteBytesSlowPath(const uint8_t* src, size_t size);
     81 
     82   // Reserves a fixed amount of bytes to be backfilled later. The reserved range
     83   // is guaranteed to be contiguous and not span across chunks. |size| has to be
     84   // <= than the size of a new buffer returned by the Delegate::GetNewBuffer().
     85   uint8_t* ReserveBytes(size_t size);
     86 
     87   // Fast (but unsafe) version of the above. The caller must have previously
     88   // checked that there are at least |size| contiguous bytes available.
     89   // Returns only the start pointer of the reservation.
     90   uint8_t* ReserveBytesUnsafe(size_t size) {
     91     uint8_t* begin = write_ptr_;
     92     write_ptr_ += size;
     93     assert(write_ptr_ <= cur_range_.end);
     94     return begin;
     95   }
     96 
     97   // Resets the buffer boundaries and the write pointer to the given |range|.
     98   // Subsequent WriteByte(s) will write into |range|.
     99   void Reset(ContiguousMemoryRange range);
    100 
    101   // Number of contiguous free bytes in |cur_range_| that can be written without
    102   // requesting a new buffer.
    103   size_t bytes_available() const {
    104     return static_cast<size_t>(cur_range_.end - write_ptr_);
    105   }
    106 
    107   uint8_t* write_ptr() const { return write_ptr_; }
    108 
    109  private:
    110   ScatteredStreamWriter(const ScatteredStreamWriter&) = delete;
    111   ScatteredStreamWriter& operator=(const ScatteredStreamWriter&) = delete;
    112 
    113   void Extend();
    114 
    115   Delegate* const delegate_;
    116   ContiguousMemoryRange cur_range_;
    117   uint8_t* write_ptr_;
    118 };
    119 
    120 }  // namespace protozero
    121 
    122 #endif  // INCLUDE_PERFETTO_PROTOZERO_SCATTERED_STREAM_WRITER_H_
    123