blob: 0e7dc1e2caebb375be6a42d6e3df2ff70ddf0bbd [file] [log] [blame]
// Copyright 2019 Google LLC
//
// 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
//
// https://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.
// -----------------------------------------------------------------------------
//
// DataSource is an abstraction of a source of data. Its actual implementation
// may be a static array, a stream etc.
//
// Author: Yannis Guyon (yguyon@google.com)
#ifndef WP2_UTILS_DATA_SOURCE_H_
#define WP2_UTILS_DATA_SOURCE_H_
#include <cassert>
#include <cstddef>
#include <cstdint>
namespace WP2 {
//------------------------------------------------------------------------------
// Inheritable class giving access to available data through TryGetNext().
// Fetch() is called when more data than GetNumNextBytes() is needed.
//
// Algorithms can access data with TryGetNext(), tell what they used with
// MarkNumBytesAsRead(), and higher program logic can decide whether to keep
// data (UnmarkAllReadBytes()) or discard it (Discard(GetNumReadBytes())).
class DataSource {
protected:
DataSource() = default;
DataSource(const uint8_t* data, size_t data_size)
: available_bytes_(data),
num_available_bytes_(data_size),
num_read_bytes_(0) {}
public:
DataSource(const DataSource&) = delete; // Makes no sense to duplicate it.
DataSource(DataSource&&) = default;
virtual ~DataSource() = default;
const DataSource& operator=(const DataSource&) = delete;
DataSource& operator=(DataSource&&) = default;
// Gets next available 'num_bytes' (or more) into '*data_pointer'; it might
// Fetch() them. Returns false if it can not be fulfilled. Data pointer is
// valid only until the next TryGetNext() call (internal buffer may be moved).
bool TryGetNext(size_t num_bytes, const uint8_t** data_pointer);
// Returns the maximum number of bytes that could have been successfully
// returned by last TryGetNext() call (excluding read or discarded bytes).
size_t GetNumNextBytes() const;
// Returns how many bytes have been read so far (excluding discarded bytes).
size_t GetNumReadBytes() const;
// Returns how many bytes have been discarded so far.
size_t GetNumDiscardedBytes() const;
// Marks 'num_bytes' as read; following TryGetNext() results will be shifted
// by 'num_bytes'.
void MarkNumBytesAsRead(size_t num_bytes);
// Resets 'num_read_bytes_' to 0.
void UnmarkAllReadBytes();
// Data can not be accessed again after it's been discarded.
void Discard(size_t num_bytes);
// Equivalent to TryGetNext() and, if successful, MarkNumBytesAsRead().
// Optimized to do as few instructions as possible if there is enough data.
inline bool TryReadNext(size_t num_bytes, const uint8_t** data_pointer) {
assert(data_pointer != nullptr);
*data_pointer = available_bytes_ + num_read_bytes_;
num_read_bytes_ += num_bytes;
if (num_read_bytes_ > num_available_bytes_) {
num_read_bytes_ -= num_bytes; // Fetch() might use GetNumNextBytes().
if (!Fetch(num_bytes)) {
*data_pointer = nullptr;
return false;
}
// available_bytes_ might change during Fetch().
*data_pointer = available_bytes_ + num_read_bytes_;
num_read_bytes_ += num_bytes;
// Fetch must return false if there isn't enough data.
assert(num_read_bytes_ <= num_available_bytes_);
}
return true;
}
// Data pointer that stays valid even if the internal buffer gets reallocated.
class DataHandle {
public:
DataHandle() = default;
DataHandle(const DataSource* const data_source, size_t offset, size_t size)
: data_source_(data_source), offset_(offset), size_(size) {}
const uint8_t* GetBytes() const;
size_t GetSize() const { return size_; }
private:
const DataSource* data_source_ = nullptr;
size_t offset_ = 0;
size_t size_ = 0;
};
// Returns a 'data_handle' that is valid until its bytes are discarded or
// until this DataSource instance is destroyed.
bool TryGetNext(size_t num_bytes, DataHandle* data_handle);
bool TryReadNext(size_t num_bytes, DataHandle* data_handle);
// Sets all members to default values.
virtual void Reset();
private:
// Override and set 'available_bytes_', 'num_available_bytes_' with at least
// 'num_read_bytes_ + num_requested_bytes' or return false.
virtual bool Fetch(size_t num_requested_bytes) = 0;
// Called after Discard() to signal that 'num_bytes' will not be used anymore
// and can be freed. If there are more discarded bytes than available, the
// difference must be discarded when there are new bytes. Remember to update
// 'available_bytes_' and 'num_available_bytes_' if the buffer changed.
virtual void OnDiscard(size_t num_bytes) = 0;
protected:
// Data is not owned, this is just a pointer to a part of an existing buffer.
const uint8_t* available_bytes_ = nullptr;
size_t num_available_bytes_ = 0;
size_t num_read_bytes_ = 0; // Number of available_bytes_ marked as read.
size_t num_discarded_bytes_ = 0;
};
//------------------------------------------------------------------------------
// Use an existing buffer as a data source.
// ExternalDataSource makes it possible to Update() the buffer pointed by
// available_bytes_ and num_available_bytes_, when it gets reallocated or when
// more data is available.
class ExternalDataSource : public DataSource {
public:
ExternalDataSource() noexcept : DataSource() {}
ExternalDataSource(const uint8_t* data, size_t data_size)
: DataSource(data, data_size) {}
void Update(const uint8_t* data, size_t data_size);
private:
bool Fetch(size_t num_requested_bytes) override;
void OnDiscard(size_t num_bytes) override;
};
//------------------------------------------------------------------------------
} // namespace WP2
#endif /* WP2_UTILS_DATA_SOURCE_H_ */