/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "src/trace_processor/util/streaming_line_reader.h"

#include "perfetto/base/logging.h"
#include "perfetto/ext/base/utils.h"

namespace perfetto {
namespace trace_processor {
namespace util {

StreamingLineReader::StreamingLineReader(LinesCallback cb)
    : lines_callback_(std::move(cb)) {}
StreamingLineReader::~StreamingLineReader() = default;

char* StreamingLineReader::BeginWrite(size_t write_buf_size) {
  PERFETTO_DCHECK(size_before_write_ == 0);
  size_before_write_ = buf_.size();
  buf_.resize(size_before_write_ + write_buf_size);
  return &buf_[size_before_write_];
}

void StreamingLineReader::EndWrite(size_t size_written) {
  PERFETTO_DCHECK(size_before_write_ + size_written <= buf_.size());
  buf_.resize(size_before_write_ + size_written);
  size_before_write_ = 0;

  size_t consumed = Tokenize(base::StringView(buf_.data(), buf_.size()));
  PERFETTO_DCHECK(consumed <= buf_.size());

  // Unless we got very lucky, the last line in the chunk just written will be
  // incomplete. Move it to the beginning of the buffer so it gets glued
  // together on the next {Begin,End}Write() call.
  buf_.erase(buf_.begin(), buf_.begin() + static_cast<ssize_t>(consumed));
}

size_t StreamingLineReader::Tokenize(base::StringView input) {
  size_t chars_consumed = 0;
  const char* line_start = input.data();
  std::vector<base::StringView> lines;
  lines.reserve(1000);  // An educated guess to avoid silly expansions.
  for (const char* c = input.data(); c < input.end(); ++c) {
    if (*c != '\n')
      continue;
    lines.emplace_back(line_start, static_cast<size_t>(c - line_start));
    line_start = c + 1;
    chars_consumed = static_cast<size_t>(c + 1 - input.data());
  }  // for(c : input)

  PERFETTO_DCHECK(lines.empty() ^ (chars_consumed != 0));
  if (!lines.empty())
    lines_callback_(lines);
  return chars_consumed;
}

}  // namespace util
}  // namespace trace_processor
}  // namespace perfetto
