blob: b284a842afa1aa08b7869b5fece38394de887f0e [file] [log] [blame]
// Copyright (c) 2011 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.
// Streams classes.
//
// These memory-resident streams are used for serializing data into a sequential
// region of memory.
// Streams are divided into SourceStreams for reading and SinkStreams for
// writing. Streams are aggregated into Sets which allows several streams to be
// used at once. Example: we can write A1, B1, A2, B2 but achieve the memory
// layout A1 A2 B1 B2 by writing 'A's to one stream and 'B's to another.
#ifndef COURGETTE_STREAMS_H_
#define COURGETTE_STREAMS_H_
#include <stddef.h>
#include <stdint.h>
#include <stdio.h> // for FILE*
#include <string>
#include "courgette/memory_allocator.h"
#include "courgette/region.h"
namespace courgette {
class SourceStream;
class SinkStream;
// Maximum number of streams in a stream set.
static const unsigned int kMaxStreams = 10;
// A simple interface for reading binary data.
class BasicBuffer {
public:
BasicBuffer() {}
virtual ~BasicBuffer() {}
virtual const uint8_t* data() const = 0;
virtual size_t length() const = 0;
};
// A SourceStream allows a region of memory to be scanned by a sequence of Read
// operations. The stream does not own the memory.
class SourceStream {
public:
SourceStream() : start_(nullptr), end_(nullptr), current_(nullptr) {}
SourceStream(const SourceStream&) = delete;
SourceStream& operator=(const SourceStream&) = delete;
// Initializes the SourceStream to yield the bytes at |pointer|. The caller
// still owns the memory at |pointer| and should free the memory only after
// the last use of the stream.
void Init(const void* pointer, size_t length) {
start_ = static_cast<const uint8_t*>(pointer);
end_ = start_ + length;
current_ = start_;
}
// Initializes the SourceStream to yield the bytes in |region|. The caller
// still owns the memory at |region| and should free the memory only after
// the last use of the stream.
void Init(const Region& region) { Init(region.start(), region.length()); }
// Initializes the SourceStream to yield the bytes in |string|. The caller
// still owns the memory at |string| and should free the memory only after
// the last use of the stream.
void Init(const std::string& string) { Init(string.c_str(), string.size()); }
// Initializes the SourceStream to yield the bytes written to |sink|. |sink|
// still owns the memory, so needs to outlive |this|. |sink| should not be
// written to after |this| is initialized.
void Init(const SinkStream& sink);
// Returns number of bytes remaining to be read from stream.
size_t Remaining() const { return end_ - current_; }
// Returns initial length of stream before any data consumed by reading.
size_t OriginalLength() const { return end_ - start_; }
const uint8_t* Buffer() const { return current_; }
bool Empty() const { return current_ == end_; }
// Copies bytes from stream to memory at |destination|. Returns 'false' if
// insufficient data to satisfy request.
bool Read(void* destination, size_t byte_count);
// Reads a varint formatted unsigned integer from stream. Returns 'false' if
// the read failed due to insufficient data or malformed Varint32.
bool ReadVarint32(uint32_t* output_value);
// Reads a varint formatted signed integer from stream. Returns 'false' if
// the read failed due to insufficient data or malformed Varint32.
bool ReadVarint32Signed(int32_t* output_value);
// Initializes |substream| to yield |length| bytes from |this| stream,
// starting at |offset| bytes from the current position. Returns 'false' if
// there are insufficient bytes in |this| stream.
bool ShareSubstream(size_t offset, size_t length, SourceStream* substream);
// Initializes |substream| to yield |length| bytes from |this| stream,
// starting at the current position. Returns 'false' if there are
// insufficient bytes in |this| stream.
bool ShareSubstream(size_t length, SourceStream* substream) {
return ShareSubstream(0, length, substream);
}
// Reads |length| bytes from |this| stream. Initializes |substream| to yield
// the bytes. Returns 'false' if there are insufficient bytes in |this|
// stream.
bool ReadSubstream(size_t length, SourceStream* substream);
// Skips over bytes. Returns 'false' if insufficient data to satisfy request.
bool Skip(size_t byte_count);
private:
const uint8_t* start_; // Points to start of buffer.
const uint8_t* end_; // Points to first location after buffer.
const uint8_t* current_; // Points into buffer at current read location.
};
// A SinkStream accumulates writes into a buffer that it owns. The stream is
// initially in an 'accumulating' state where writes are permitted. Accessing
// the buffer moves the stream into a 'locked' state where no more writes are
// permitted. The stream may also be in a 'retired' state where the buffer
// contents are no longer available.
class SinkStream {
public:
SinkStream() {}
SinkStream(const SinkStream&) = delete;
SinkStream& operator=(const SinkStream&) = delete;
~SinkStream() {}
// Appends |byte_count| bytes from |data| to the stream.
[[nodiscard]] CheckBool Write(const void* data, size_t byte_count);
// Appends the 'varint32' encoding of |value| to the stream.
[[nodiscard]] CheckBool WriteVarint32(uint32_t value);
// Appends the 'varint32' encoding of |value| to the stream.
[[nodiscard]] CheckBool WriteVarint32Signed(int32_t value);
// Appends the 'varint32' encoding of |value| to the stream.
// On platforms where sizeof(size_t) != sizeof(int32_t), do a safety check.
[[nodiscard]] CheckBool WriteSizeVarint32(size_t value);
// Contents of |other| are appended to |this| stream. The |other| stream
// becomes retired.
[[nodiscard]] CheckBool Append(SinkStream* other);
// Returns the number of bytes in this SinkStream
size_t Length() const { return buffer_.size(); }
// Returns a pointer to contiguously allocated Length() bytes in the stream.
// Writing to the stream invalidates the pointer. The SinkStream continues to
// own the memory.
const uint8_t* Buffer() const {
return reinterpret_cast<const uint8_t*>(buffer_.data());
}
// Hints that the stream will grow by an additional |length| bytes.
// Caller must be prepared to handle memory allocation problems.
[[nodiscard]] CheckBool Reserve(size_t length) {
return buffer_.reserve(length + buffer_.size());
}
// Finished with this stream and any storage it has.
void Retire();
private:
NoThrowBuffer<char> buffer_;
};
// A SourceStreamSet is a set of SourceStreams.
class SourceStreamSet {
public:
SourceStreamSet();
SourceStreamSet(const SourceStreamSet&) = delete;
SourceStreamSet& operator=(const SourceStreamSet&) = delete;
~SourceStreamSet();
// Initializes the SourceStreamSet with the stream data in memory at |source|.
// The caller continues to own the memory and should not modify or free the
// memory until the SourceStreamSet destructor has been called.
//
// The layout of the streams are as written by SinkStreamSet::CopyTo.
// Init returns 'false' if the layout is inconsistent with |byte_count|.
bool Init(const void* source, size_t byte_count);
// Initializes |this| from |source|. The caller continues to own the memory
// because it continues to be owned by |source|.
bool Init(SourceStream* source);
// Returns a pointer to one of the sub-streams.
SourceStream* stream(size_t id) {
return id < count_ ? &streams_[id] : nullptr;
}
// Initialize |set| from |this|.
bool ReadSet(SourceStreamSet* set);
// Returns 'true' if all streams are completely consumed.
bool Empty() const;
private:
size_t count_;
SourceStream streams_[kMaxStreams];
};
// A SinkStreamSet is a set of SinkStreams. Data is collected by writing to the
// component streams. When data collection is complete, it is destructively
// transferred, either by flattening into one stream (CopyTo), or transfering
// data pairwise into another SinkStreamSet by calling that SinkStreamSet's
// WriteSet method.
class SinkStreamSet {
public:
SinkStreamSet();
SinkStreamSet(const SinkStreamSet&) = delete;
SinkStreamSet& operator=(const SinkStreamSet&) = delete;
~SinkStreamSet();
// Initializes the SinkStreamSet to have |stream_index_limit| streams. Must
// be <= kMaxStreams. If Init is not called the default is has kMaxStream.
void Init(size_t stream_index_limit);
// Returns a pointer to a substream.
SinkStream* stream(size_t id) {
return id < count_ ? &streams_[id] : nullptr;
}
// CopyTo serializes the streams in this SinkStreamSet into a single target
// stream. The serialized format may be re-read by initializing a
// SourceStreamSet with a buffer containing the data.
[[nodiscard]] CheckBool CopyTo(SinkStream* combined_stream);
// Writes the streams of |set| into the corresponding streams of |this|.
// Stream zero first has some metadata written to it. |set| becomes retired.
// Partner to SourceStreamSet::ReadSet.
[[nodiscard]] CheckBool WriteSet(SinkStreamSet* set);
private:
[[nodiscard]] CheckBool CopyHeaderTo(SinkStream* stream);
size_t count_;
SinkStream streams_[kMaxStreams];
};
} // namespace courgette
#endif // COURGETTE_STREAMS_H_