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#include "src/tracing/core/sliced_protobuf_input_stream.h"
18
19#include <algorithm>
20
21#include "perfetto/base/logging.h"
22
23namespace perfetto {
24
25SlicedProtobufInputStream::SlicedProtobufInputStream(const Slices* slices)
26    : slices_(slices), cur_slice_(slices_->begin()) {}
27
28SlicedProtobufInputStream::~SlicedProtobufInputStream() = default;
29
30bool SlicedProtobufInputStream::Next(const void** data, int* size) {
31  if (cur_slice_ == slices_->end())
32    return false;
33
34  PERFETTO_DCHECK(Validate());
35  *data = reinterpret_cast<const void*>(
36      reinterpret_cast<uintptr_t>(cur_slice_->start) + pos_in_cur_slice_);
37  *size = static_cast<int>(cur_slice_->size - pos_in_cur_slice_);
38  cur_slice_++;
39  pos_in_cur_slice_ = 0;
40  PERFETTO_DCHECK(Validate());
41
42  return true;
43}
44
45void SlicedProtobufInputStream::BackUp(int count) {
46  size_t n = static_cast<size_t>(count);
47  PERFETTO_DCHECK(Validate());
48  while (n) {
49    if (cur_slice_ == slices_->end() || pos_in_cur_slice_ == 0) {
50      if (cur_slice_ == slices_->begin()) {
51        // The protobuf library is violating its contract and backing up more
52        // bytes than available.
53        PERFETTO_DCHECK(false);
54        return;
55      }
56      cur_slice_--;
57      pos_in_cur_slice_ = cur_slice_->size;
58      continue;
59    }
60
61    const size_t decrement = std::min(n, pos_in_cur_slice_);
62    pos_in_cur_slice_ -= decrement;
63    n -= decrement;
64  }
65  PERFETTO_DCHECK(Validate());
66}
67
68bool SlicedProtobufInputStream::Skip(int count) {
69  PERFETTO_DCHECK(Validate());
70  size_t n = static_cast<size_t>(count);
71  while (n) {
72    PERFETTO_DCHECK(Validate());
73    if (cur_slice_ == slices_->end())
74      return false;
75
76    const size_t increment = std::min(n, cur_slice_->size - pos_in_cur_slice_);
77    pos_in_cur_slice_ += increment;
78    n -= increment;
79
80    if (pos_in_cur_slice_ >= cur_slice_->size) {
81      cur_slice_++;
82      pos_in_cur_slice_ = 0;
83    }
84  }
85  PERFETTO_DCHECK(Validate());
86  return true;
87}
88
89google::protobuf::int64 SlicedProtobufInputStream::ByteCount() const {
90  PERFETTO_DCHECK(Validate());
91  google::protobuf::int64 count = 0;
92  for (auto it = slices_->begin(); it != slices_->end(); it++) {
93    if (it == cur_slice_) {
94      count += static_cast<google::protobuf::int64>(pos_in_cur_slice_);
95      break;
96    }
97    count += static_cast<google::protobuf::int64>(it->size);
98  }
99  return count;
100}
101
102bool SlicedProtobufInputStream::Validate() const {
103  return ((cur_slice_ == slices_->end() && pos_in_cur_slice_ == 0) ||
104          pos_in_cur_slice_ < cur_slice_->size ||
105          (pos_in_cur_slice_ == 0 && cur_slice_->size == 0));
106}
107
108}  // namespace perfetto
109