// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

#ifndef RUNTIME_VM_DATASTREAM_H_
#define RUNTIME_VM_DATASTREAM_H_

#include "platform/assert.h"
#include "platform/utils.h"
#include "vm/allocation.h"
#include "vm/exceptions.h"
#include "vm/globals.h"
#include "vm/os.h"
#include "vm/zone.h"

namespace dart {

static constexpr int8_t kDataBitsPerByte = 7;
static constexpr int8_t kByteMask = (1 << kDataBitsPerByte) - 1;
static constexpr int8_t kMaxUnsignedDataPerByte = kByteMask;
static constexpr int8_t kMinDataPerByte = -(1 << (kDataBitsPerByte - 1));
static constexpr int8_t kMaxDataPerByte =
    (~kMinDataPerByte & kByteMask);  // NOLINT
static constexpr uint8_t kEndByteMarker = (255 - kMaxDataPerByte);
static constexpr uint8_t kEndUnsignedByteMarker =
    (255 - kMaxUnsignedDataPerByte);

struct LEB128Constants : AllStatic {
  // Convenience template for ensuring non-signed types trigger SFINAE.
  template <typename T, typename S>
  using only_if_signed =
      typename std::enable_if<std::is_signed<T>::value, S>::type;

  // Convenience template for ensuring signed types trigger SFINAE.
  template <typename T, typename S>
  using only_if_unsigned =
      typename std::enable_if<std::is_unsigned<T>::value, S>::type;

  // (S)LEB128 encodes 7 bits of data per byte (hence 128).
  static constexpr uint8_t kDataBitsPerByte = 7;
  static constexpr uint8_t kDataByteMask = (1 << kDataBitsPerByte) - 1;
  // If more data follows a given data byte, the high bit is set.
  static constexpr uint8_t kMoreDataMask = (1 << kDataBitsPerByte);
  // For SLEB128, the high bit in the data of the last byte is the sign bit.
  static constexpr uint8_t kSignMask = (1 << (kDataBitsPerByte - 1));
};

class NonStreamingWriteStream;

namespace module_snapshot {
class Deserializer;
}

// Stream for reading various types from a buffer.
class ReadStream : public ValueObject {
 public:
  ReadStream(const uint8_t* buffer, intptr_t size)
      : buffer_(buffer), current_(buffer), end_(buffer + size) {}

  // Creates a ReadStream that starts at a given position in the buffer.
  ReadStream(const uint8_t* buffer, intptr_t size, intptr_t pos)
      : ReadStream(buffer, size) {
    SetPosition(pos);
  }

  template <int N, typename T>
  class Raw {};

  template <typename T>
  class Raw<1, T> {
   public:
    static T Read(ReadStream* st) { return bit_cast<T>(st->ReadByte()); }
  };

  template <typename T>
  class Raw<2, T> {
   public:
    static T Read(ReadStream* st) { return bit_cast<T>(st->Read16()); }
  };

  template <typename T>
  class Raw<4, T> {
   public:
    static T Read(ReadStream* st) { return bit_cast<T>(st->Read32()); }
  };

  template <typename T>
  class Raw<8, T> {
   public:
    static T Read(ReadStream* st) { return bit_cast<T>(st->Read64()); }
  };

  // Reads 'len' bytes from the stream.
  void ReadBytes(void* addr, intptr_t len) {
    ASSERT((end_ - current_) >= len);
    if (len != 0) {
      memmove(addr, current_, len);
    }
    current_ += len;
  }

  template <typename T = intptr_t>
  T ReadUnsigned() {
    return Read<T>(kEndUnsignedByteMarker);
  }

  intptr_t ReadRefId() {
    const int8_t* cursor = reinterpret_cast<const int8_t*>(current_);
    intptr_t result = 0;
    intptr_t byte;
    // clang-format off
#define STAGE                                                                  \
    byte = *cursor++;              /* ldrsb byte, [result], 1  */              \
    result = byte + (result << 7); /* add result, byte, result lsl 7 */        \
    if (byte < 0) goto done;       /* tbnz byte, 63, done */
    STAGE  // 0-7
    STAGE  // 8-14
    STAGE  // 15-21
    STAGE  // 22-28
#undef STAGE
    ASSERT(byte < 0);  // 256MB is enough for anyone...
    // clang-format on
  done:
    current_ = reinterpret_cast<const uint8_t*>(cursor);
    // With big-endian order and the has-more marker being 0, the correction
    // factor to remove the last-byte marker is a constant, which can be folded
    // into subsequent load offsets.
    return result + 128;
  }

  intptr_t Position() const { return current_ - buffer_; }
  void SetPosition(intptr_t value) {
    ASSERT((end_ - buffer_) >= value);
    current_ = buffer_ + value;
  }

  void Align(intptr_t alignment, intptr_t offset = 0) {
    intptr_t position_before = Position();
    intptr_t position_after =
        Utils::RoundUp(position_before, alignment, offset);
    Advance(position_after - position_before);
  }

  const uint8_t* AddressOfCurrentPosition() const { return current_; }

  void Advance(intptr_t value) {
    ASSERT((end_ - current_) >= value);
    current_ = current_ + value;
  }

  intptr_t PendingBytes() const {
    ASSERT(end_ >= current_);
    return (end_ - current_);
  }

  template <typename T>
  T Read() {
    return Read<T>(kEndByteMarker);
  }

  uword ReadWordWith32BitReads() {
    constexpr intptr_t kNumRead32PerWord = kBitsPerWord / kBitsPerInt32;

    uword value = 0;
    for (intptr_t j = 0; j < kNumRead32PerWord; j++) {
      const auto partial_value = Raw<kInt32Size, uint32_t>::Read(this);
      value |= (static_cast<uword>(partial_value) << (j * kBitsPerInt32));
    }
    return value;
  }

 private:
  using C = LEB128Constants;

 public:
  template <typename T = uintptr_t>
  C::only_if_unsigned<T, T> ReadLEB128() {
    constexpr intptr_t kBitsPerT = kBitsPerByte * sizeof(T);
    T r = 0;
    uint8_t s = 0;
    uint8_t b;
    do {
      ASSERT(s < kBitsPerT);
      b = ReadByte();
      r |= static_cast<T>(b & C::kDataByteMask) << s;
      s += C::kDataBitsPerByte;
    } while ((b & C::kMoreDataMask) != 0);
    ASSERT(s < C::kDataBitsPerByte + kBitsPerT);
    return r;
  }

  template <typename T>
  C::only_if_signed<T, T> ReadLEB128() {
    return bit_cast<T>(ReadLEB128<typename std::make_unsigned<T>::type>());
  }

  template <typename T>
  C::only_if_unsigned<T, T> ReadSLEB128() {
    constexpr intptr_t kBitsPerT = kBitsPerByte * sizeof(T);
    T r = 0;
    uint8_t s = 0;
    uint8_t b;
    do {
      ASSERT(s < kBitsPerT);
      b = ReadByte();
      r |= static_cast<T>(b & C::kDataByteMask) << s;
      s += C::kDataBitsPerByte;
    } while ((b & C::kMoreDataMask) != 0);
    ASSERT(s < C::kDataBitsPerByte + kBitsPerT);
    // At this point, [s] contains how many data bits have made it into the
    // value. If the value is negative and the count of data bits is less than
    // the size of the value, then we need to extend the sign by setting the
    // remaining (unset) most significant bits (MSBs).
    T sign_bits = 0;
    if ((b & C::kSignMask) != 0 && s < kBitsPerT) {
      // Create a bitmask for the current data bits and invert it.
      sign_bits = ~((static_cast<T>(1) << s) - 1);
    }
    return r | sign_bits;
  }

  template <typename T = intptr_t>
  C::only_if_signed<T, T> ReadSLEB128() {
    return bit_cast<T>(ReadSLEB128<typename std::make_unsigned<T>::type>());
  }

 private:
  uint16_t Read16() { return Read16(kEndByteMarker); }

  uint32_t Read32() { return Read32(kEndByteMarker); }

  uint64_t Read64() { return Read64(kEndByteMarker); }

  template <typename T>
  T Read(uint8_t end_byte_marker) {
    using Unsigned = typename std::make_unsigned<T>::type;
    Unsigned b = ReadByte();
    if (b > kMaxUnsignedDataPerByte) {
      return b - end_byte_marker;
    }
    T r = 0;
    uint8_t s = 0;
    do {
      r |= static_cast<Unsigned>(b) << s;
      s += kDataBitsPerByte;
      b = ReadByte();
    } while (b <= kMaxUnsignedDataPerByte);
    return r | (static_cast<Unsigned>(b - end_byte_marker) << s);
  }

// Setting up needed variables for the unrolled loop sections below.
#define UNROLLED_INIT()                                                        \
  using Unsigned = typename std::make_unsigned<T>::type;                       \
  Unsigned b = ReadByte();                                                     \
  if (b > kMaxUnsignedDataPerByte) {                                           \
    return b - end_byte_marker;                                                \
  }                                                                            \
  T r = b;

// Part of the unrolled loop where the loop may stop, having read the last part,
// or continue reading.
#define UNROLLED_BODY(bit_start)                                               \
  static_assert(bit_start % kDataBitsPerByte == 0,                             \
                "Bit start must be a multiple of the data bits per byte");     \
  static_assert(bit_start >= 0 && bit_start < kBitsPerByte * sizeof(T),        \
                "Starting unrolled body at invalid bit position");             \
  static_assert(bit_start + kDataBitsPerByte < kBitsPerByte * sizeof(T),       \
                "Unrolled body should not contain final bits in value");       \
  b = ReadByte();                                                              \
  if (b > kMaxUnsignedDataPerByte) {                                           \
    return r | (static_cast<T>(b - end_byte_marker) << bit_start);             \
  }                                                                            \
  r |= b << bit_start;

// The end of the unrolled loop.
#define UNROLLED_END(bit_start)                                                \
  static_assert(bit_start % kDataBitsPerByte == 0,                             \
                "Bit start must be a multiple of the data bits per byte");     \
  static_assert(bit_start >= 0 && bit_start < kBitsPerByte * sizeof(T),        \
                "Starting unrolled end at invalid bit position");              \
  static_assert(bit_start + kDataBitsPerByte >= kBitsPerByte * sizeof(T),      \
                "Unrolled end does not contain final bits in value");          \
  b = ReadByte();                                                              \
  ASSERT(b > kMaxUnsignedDataPerByte);                                         \
  return r | (static_cast<T>(b - end_byte_marker) << bit_start);

  uint16_t Read16(uint8_t end_byte_marker) {
    using T = uint16_t;
    UNROLLED_INIT();
    UNROLLED_BODY(7);
    UNROLLED_END(14);
  }

  uint32_t Read32(uint8_t end_byte_marker) {
    using T = uint32_t;
    UNROLLED_INIT();
    UNROLLED_BODY(7);
    UNROLLED_BODY(14);
    UNROLLED_BODY(21);
    UNROLLED_END(28);
  }

  uint64_t Read64(uint8_t end_byte_marker) {
    using T = uint64_t;
    UNROLLED_INIT();
    UNROLLED_BODY(7);
    UNROLLED_BODY(14);
    UNROLLED_BODY(21);
    UNROLLED_BODY(28);
    UNROLLED_BODY(35);
    UNROLLED_BODY(42);
    UNROLLED_BODY(49);
    UNROLLED_BODY(56);
    UNROLLED_END(63);
  }

  DART_FORCE_INLINE uint8_t ReadByte() {
    ASSERT(current_ < end_);
    return *current_++;
  }

 private:
  ReadStream(const uint8_t* buffer, const uint8_t* current, const uint8_t* end)
      : buffer_(buffer), current_(current), end_(end) {}

  const uint8_t* buffer_;
  const uint8_t* current_;
  const uint8_t* end_;

  friend class Deserializer;
  friend class module_snapshot::Deserializer;
  DISALLOW_COPY_AND_ASSIGN(ReadStream);
};

// Base class for streams that write bytes to some backing store. Generally,
// BaseWriteStream is a more appropriate superclass of new WriteStreams since
// it offers more functionality (e.g., Printf).
struct AbstractWriteStream : public ValueObject {
  AbstractWriteStream() {}
  virtual ~AbstractWriteStream() {}

  virtual intptr_t Position() const = 0;
  virtual intptr_t Align(intptr_t alignment, intptr_t offset = 0) = 0;
  virtual void WriteBytes(const void* addr, intptr_t len) = 0;
  virtual void WriteByte(uint8_t value) = 0;

 private:
  using C = LEB128Constants;

 public:
  template <typename T>
  C::only_if_unsigned<T, void> WriteLEB128(T value) {
    T remainder = value;
    bool is_last_part;
    do {
      uint8_t part = static_cast<uint8_t>(remainder & C::kDataByteMask);
      remainder >>= C::kDataBitsPerByte;
      // For unsigned types, we're done when the remainder has no bits set.
      is_last_part = remainder == static_cast<T>(0);
      if (!is_last_part) {
        // Mark this part as a non-final part for this value.
        part |= C::kMoreDataMask;
      }
      WriteByte(part);
    } while (!is_last_part);
  }

  template <typename T>
  C::only_if_signed<T, void> WriteLEB128(T value) {
    // If we're trying to LEB128 encode a negative value, chances are we should
    // be using SLEB128 instead.
    ASSERT(value >= 0);
    return WriteLEB128(bit_cast<typename std::make_unsigned<T>::type>(value));
  }

  template <typename T>
  C::only_if_signed<T, void> WriteSLEB128(T value) {
    constexpr intptr_t kBitsPerT = kBitsPerByte * sizeof(T);
    using Unsigned = typename std::make_unsigned<T>::type;
    // Record whether the original value was negative.
    const bool is_negative = value < 0;
    T remainder = value;
    bool is_last_part;
    do {
      uint8_t part = static_cast<uint8_t>(remainder & C::kDataByteMask);
      remainder >>= C::kDataBitsPerByte;
      // For signed types, we're done when either:
      // - the remainder has all bits set and the part's sign bit is set
      //   for negative values, or
      // - the remainder has no bits set and the part's sign bit is unset for
      //   non-negative values.
      // If the remainder matches but the sign bit does not, we need one more
      // part to set the sign bit correctly when decoding.
      if (is_negative) {
        // Right shifts of negative values in C are not guaranteed to be
        // arithmetic. For negative values, set the [kDataBitsPerByte] most
        // significant bits after shifting to ensure the value stays negative.
        constexpr intptr_t preserved_bits = kBitsPerT - C::kDataBitsPerByte;
        // The sign extension mask is the inverse of the preserved bits mask.
        constexpr T sign_extend =
            ~static_cast<T>((static_cast<Unsigned>(1) << preserved_bits) - 1);
        // Sign extend for negative values just in case a non-arithmetic right
        // shift is used by the compiler.
        remainder |= sign_extend;
        ASSERT(remainder < 0);  // Remainder should still be negative.
        is_last_part =
            remainder == ~static_cast<T>(0) && (part & C::kSignMask) != 0;
      } else {
        ASSERT(remainder >= 0);  // Remainder should still be non-negative.
        is_last_part =
            (remainder == static_cast<T>(0) && (part & C::kSignMask) == 0);
      }
      if (!is_last_part) {
        // Mark this part as a non-final part for this value.
        part |= C::kMoreDataMask;
      }
      WriteByte(part);
    } while (!is_last_part);
  }

  template <typename T>
  C::only_if_unsigned<T, void> WriteSLEB128(T value) {
    return WriteSLEB128(bit_cast<typename std::make_signed<T>::type>(value));
  }
};

// Base class for streams that writing various types into a buffer, possibly
// flushing data out periodically to a more permanent store.
class BaseWriteStream : public AbstractWriteStream {
 public:
  explicit BaseWriteStream(intptr_t initial_size)
      : initial_size_(Utils::RoundUpToPowerOfTwo(initial_size)) {}

  DART_FORCE_INLINE intptr_t bytes_written() const { return Position(); }
  virtual intptr_t Position() const { return current_ - buffer_; }
  intptr_t initial_size() const { return initial_size_; }

  virtual intptr_t Align(intptr_t alignment, intptr_t offset = 0) {
    const intptr_t position_before = Position();
    const intptr_t position_after =
        Utils::RoundUp(position_before, alignment, offset);
    const intptr_t length = position_after - position_before;
    if (length != 0) {
      EnsureSpace(length);
      memset(current_, 0, length);
      SetPosition(position_after);
    }
    return length;
  }

  template <int N, typename T>
  class Raw {};

  template <typename T>
  class Raw<1, T> {
   public:
    static void Write(BaseWriteStream* st, T value) {
      st->WriteByte(bit_cast<uint8_t>(value));
    }
  };

  template <typename T>
  class Raw<2, T> {
   public:
    static void Write(BaseWriteStream* st, T value) {
      st->Write<int16_t>(bit_cast<int16_t>(value));
    }
  };

  template <typename T>
  class Raw<4, T> {
   public:
    static void Write(BaseWriteStream* st, T value) {
      st->Write<int32_t>(bit_cast<int32_t>(value));
    }
  };

  template <typename T>
  class Raw<8, T> {
   public:
    static void Write(BaseWriteStream* st, T value) {
      st->Write<int64_t>(bit_cast<int64_t>(value));
    }
  };

  void WriteWordWith32BitWrites(uword value) {
    constexpr intptr_t kNumWrite32PerWord = kBitsPerWord / kBitsPerInt32;

    const uint32_t mask = Utils::NBitMask(kBitsPerInt32);
    for (intptr_t j = 0; j < kNumWrite32PerWord; j++) {
      const uint32_t shifted_value = (value >> (j * kBitsPerInt32));
      Raw<kInt32Size, uint32_t>::Write(this, shifted_value & mask);
    }
  }

  template <typename T>
  void WriteUnsigned(T value) {
    ASSERT(value >= 0);
    while (value > kMaxUnsignedDataPerByte) {
      WriteByte(static_cast<uint8_t>(value & kByteMask));
      value = value >> kDataBitsPerByte;
    }
    WriteByte(static_cast<uint8_t>(value + kEndUnsignedByteMarker));
  }

  void WriteRefId(intptr_t value) {
    ASSERT(Utils::IsUint(28, value));  // 256MB is enough for anyone...
    EnsureSpace(4);
    if ((value >> 21) != 0) {
      *current_++ = (value >> 21) & 127;
    }
    if ((value >> 14) != 0) {
      *current_++ = (value >> 14) & 127;
    }
    if ((value >> 7) != 0) {
      *current_++ = (value >> 7) & 127;
    }
    *current_++ = ((value >> 0) & 127) | 128;
  }

  void WriteBytes(const void* addr, intptr_t len) {
    if (len != 0) {
      EnsureSpace(len);
      memmove(current_, addr, len);
      current_ += len;
    }
  }

  void WriteWord(uword value) { WriteFixed(value); }

  void WriteTargetWord(word value);

  void Printf(const char* format, ...) PRINTF_ATTRIBUTE(2, 3) {
    va_list args;
    va_start(args, format);
    VPrintf(format, args);
    va_end(args);
  }

  void VPrintf(const char* format, va_list args) {
    // Measure.
    va_list measure_args;
    va_copy(measure_args, args);
    intptr_t len = Utils::VSNPrint(nullptr, 0, format, measure_args);
    va_end(measure_args);

    // Alloc.
    EnsureSpace(len + 1);

    // Print.
    va_list print_args;
    va_copy(print_args, args);
    Utils::VSNPrint(reinterpret_cast<char*>(current_), len + 1, format,
                    print_args);
    va_end(print_args);
    current_ += len;  // Not len + 1 to swallow the terminating NUL.
  }

  template <typename T>
  void Write(T value) {
    T v = value;
    while (v < kMinDataPerByte || v > kMaxDataPerByte) {
      WriteByte(static_cast<uint8_t>(v & kByteMask));
      v = v >> kDataBitsPerByte;
    }
    WriteByte(static_cast<uint8_t>(v + kEndByteMarker));
  }

  template <typename T>
  void WriteFixed(T value) {
    WriteBytes(&value, sizeof(value));
  }

  DART_FORCE_INLINE virtual void WriteByte(uint8_t value) {
    EnsureSpace(1);
    *current_++ = value;
  }

  void WriteString(const char* cstr) { WriteBytes(cstr, strlen(cstr)); }

 protected:
  void EnsureSpace(intptr_t size_needed) {
    if (Remaining() >= size_needed) return;
    intptr_t increment_size = capacity_;
    if (size_needed > increment_size) {
      increment_size = Utils::RoundUp(size_needed, initial_size_);
    }
    intptr_t new_size = capacity_ + increment_size;
    ASSERT(new_size > capacity_);
    Realloc(new_size);
    if (buffer_ == nullptr) {
      Exceptions::ThrowOOM();
    }
    ASSERT(Remaining() >= size_needed);
  }

  virtual void SetPosition(intptr_t value) {
    EnsureSpace(value - BaseWriteStream::Position());
    current_ = buffer_ + value;
  }

  DART_FORCE_INLINE intptr_t Remaining() const {
    return capacity_ - BaseWriteStream::Position();
  }

  // Resizes the internal buffer to the requested new capacity. Should set
  // buffer_, capacity_, and current_ appropriately.
  //
  // Instead of templating over an Allocator (which would then cause users
  // of the templated class to need to be templated, etc.), we just add an
  // Realloc method to override appropriately in subclasses. Less flexible,
  // but requires less changes throughout the codebase.
  virtual void Realloc(intptr_t new_capacity) = 0;

  const intptr_t initial_size_;
  uint8_t* buffer_ = nullptr;
  uint8_t* current_ = nullptr;
  intptr_t capacity_ = 0;

  DISALLOW_COPY_AND_ASSIGN(BaseWriteStream);
};

// A base class for non-streaming write streams. Since these streams are
// not flushed periodically, the internal buffer contains all written data
// and can be retrieved via buffer(). NonStreamingWriteStream also provides
// SetPosition as part of its public API for non-sequential writing.
class NonStreamingWriteStream : public BaseWriteStream {
 public:
  explicit NonStreamingWriteStream(intptr_t initial_size)
      : BaseWriteStream(initial_size) {}

 public:
  uint8_t* buffer() const { return buffer_; }

  // Sets the position of the buffer
  DART_FORCE_INLINE void SetPosition(intptr_t value) {
    BaseWriteStream::SetPosition(value);
  }
};

// A non-streaming write stream that uses realloc for reallocation, and frees
// the buffer when destructed unless ownership is transferred using Steal().
class MallocWriteStream : public NonStreamingWriteStream {
 public:
  explicit MallocWriteStream(intptr_t initial_size)
      : NonStreamingWriteStream(initial_size) {}
  ~MallocWriteStream();

  // Resets the stream and returns the original buffer, which is now considered
  // owned by the caller. Sets [*length] to the length of the returned buffer.
  uint8_t* Steal(intptr_t* length) {
    ASSERT(length != nullptr);
    *length = bytes_written();
    uint8_t* const old_buffer = buffer_;
    // We don't immediately reallocate a new space just in case this steal
    // is the last use of this stream.
    current_ = buffer_ = nullptr;
    capacity_ = 0;
    return old_buffer;
  }

 private:
  virtual void Realloc(intptr_t new_size);

  DISALLOW_COPY_AND_ASSIGN(MallocWriteStream);
};

// A non-streaming write stream that uses a zone for reallocation.
class ZoneWriteStream : public NonStreamingWriteStream {
 public:
  ZoneWriteStream(Zone* zone, intptr_t initial_size)
      : NonStreamingWriteStream(initial_size), zone_(zone) {}

 private:
  virtual void Realloc(intptr_t new_size);

  Zone* const zone_;

  DISALLOW_COPY_AND_ASSIGN(ZoneWriteStream);
};

// A streaming write stream that uses the internal buffer only for non-flushed
// data. Like MallocWriteStream, uses realloc for reallocation, and flushes and
// frees the internal buffer when destructed. Since part or all of the written
// data may be flushed and no longer in the internal buffer, it does not provide
// a way to retrieve the written contents.
class StreamingWriteStream : public BaseWriteStream {
 public:
  explicit StreamingWriteStream(intptr_t initial_capacity,
                                Dart_StreamingWriteCallback callback,
                                void* callback_data)
      : BaseWriteStream(initial_capacity),
        callback_(callback),
        callback_data_(callback_data) {}
  ~StreamingWriteStream();

 private:
  // Flushes any unflushed data to callback_data and resets the internal
  // buffer. Changes current_ and flushed_size_ accordingly.
  virtual void Flush();

  virtual void Realloc(intptr_t new_size);

  virtual intptr_t Position() const {
    return flushed_size_ + BaseWriteStream::Position();
  }

  virtual void SetPosition(intptr_t value) {
    // Make sure we're not trying to set the position to already-flushed data.
    ASSERT(value >= flushed_size_);
    BaseWriteStream::SetPosition(value - flushed_size_);
  }

  const Dart_StreamingWriteCallback callback_;
  void* const callback_data_;
  intptr_t flushed_size_ = 0;

  DISALLOW_COPY_AND_ASSIGN(StreamingWriteStream);
};

}  // namespace dart

#endif  // RUNTIME_VM_DATASTREAM_H_
