// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef COMPONENTS_ZUCCHINI_BUFFER_SINK_H_
#define COMPONENTS_ZUCCHINI_BUFFER_SINK_H_

#include <stdint.h>

#include <algorithm>
#include <iterator>

#include "base/logging.h"
#include "components/zucchini/buffer_view.h"

namespace zucchini {

// BufferSink acts like an output stream with convenience methods to serialize
// data into a contiguous sequence of raw data. The underlying MutableBufferView
// emulates a cursor to track current write position, and guards against buffer
// overrun. Where applicable, BufferSink should be passed by pointer to maintain
// cursor progress across writes.
class BufferSink : public MutableBufferView {
 public:
  using iterator = MutableBufferView::iterator;

  using MutableBufferView::MutableBufferView;
  BufferSink() = default;
  explicit BufferSink(MutableBufferView buffer);
  BufferSink(const BufferSink&) = default;
  BufferSink& operator=(BufferSink&&) = default;

  // If sufficient space is available, writes the binary representation of
  // |value| starting at the cursor, while advancing the cursor beyond the
  // written region, and returns true. Otherwise returns false.
  template <class T>
  bool PutValue(const T& value) {
    DCHECK_NE(begin(), nullptr);
    if (Remaining() < sizeof(T))
      return false;
    *reinterpret_cast<T*>(begin()) = value;
    remove_prefix(sizeof(T));
    return true;
  }

  // If sufficient space is available, writes the raw bytes [|first|, |last|)
  // starting at the cursor, while advancing the cursor beyond the written
  // region, and returns true. Otherwise returns false.
  template <class It>
  bool PutRange(It first, It last) {
    static_assert(sizeof(typename std::iterator_traits<It>::value_type) ==
                      sizeof(uint8_t),
                  "value_type should fit in uint8_t");
    DCHECK_NE(begin(), nullptr);
    DCHECK(last >= first);
    if (Remaining() < size_type(last - first))
      return false;
    std::copy(first, last, begin());
    remove_prefix(last - first);
    return true;
  }

  size_type Remaining() const { return size(); }
};

}  // namespace zucchini

#endif  // COMPONENTS_ZUCCHINI_BUFFER_SINK_H_
