#ifndef SRC_BYTE_PARSER_H_
#define SRC_BYTE_PARSER_H_
#include <cassert>
#include <cstdint>
#include <string>
#include <utility>
#include <vector>
#include "src/element_parser.h"
#include "webm/callback.h"
#include "webm/element.h"
#include "webm/reader.h"
#include "webm/status.h"
namespace webm {
template <typename T>
class ByteParser : public ElementParser {
public:
static_assert(std::is_same<T, std::vector<std::uint8_t>>::value ||
std::is_same<T, std::string>::value,
"T must be std::vector<std::uint8_t> or std::string");
explicit ByteParser(T default_value = {})
: default_value_(std::move(default_value)) {}
ByteParser(ByteParser&&) = default;
ByteParser& operator=(ByteParser&&) = default;
ByteParser(const ByteParser&) = delete;
ByteParser& operator=(const ByteParser&) = delete;
Status Init(const ElementMetadata& metadata,
std::uint64_t max_size) override {
assert(metadata.size == kUnknownElementSize || metadata.size <= max_size);
if (metadata.size == kUnknownElementSize) {
return Status(Status::kInvalidElementSize);
}
if (metadata.size > std::numeric_limits<std::size_t>::max() ||
metadata.size > value_.max_size()) {
return Status(Status::kNotEnoughMemory);
}
#if WEBM_FUZZER_BYTE_ELEMENT_SIZE_LIMIT
if (metadata.size > WEBM_FUZZER_BYTE_ELEMENT_SIZE_LIMIT) {
return Status(Status::kNotEnoughMemory);
}
#endif
if (metadata.size == 0) {
value_ = default_value_;
total_read_ = default_value_.size();
} else {
value_.resize(static_cast<std::size_t>(metadata.size));
total_read_ = 0;
}
return Status(Status::kOkCompleted);
}
Status Feed(Callback* callback, Reader* reader,
std::uint64_t* num_bytes_read) override {
assert(callback != nullptr);
assert(reader != nullptr);
assert(num_bytes_read != nullptr);
*num_bytes_read = 0;
if (total_read_ == value_.size()) {
return Status(Status::kOkCompleted);
}
Status status;
do {
std::uint64_t local_num_bytes_read = 0;
std::uint8_t* buffer =
reinterpret_cast<std::uint8_t*>(&value_.front()) + total_read_;
std::size_t buffer_size = value_.size() - total_read_;
status = reader->Read(buffer_size, buffer, &local_num_bytes_read);
assert((status.completed_ok() && local_num_bytes_read == buffer_size) ||
(status.ok() && local_num_bytes_read < buffer_size) ||
(!status.ok() && local_num_bytes_read == 0));
*num_bytes_read += local_num_bytes_read;
total_read_ += static_cast<std::size_t>(local_num_bytes_read);
} while (status.code == Status::kOkPartial);
if (std::is_same<T, std::string>::value && status.completed_ok()) {
while (!value_.empty() && value_.back() == '\0') {
value_.pop_back();
}
}
return status;
}
const T& value() const {
assert(total_read_ >= value_.size());
return value_;
}
T* mutable_value() {
assert(total_read_ >= value_.size());
return &value_;
}
private:
T value_;
T default_value_;
std::size_t total_read_;
};
using StringParser = ByteParser<std::string>;
using BinaryParser = ByteParser<std::vector<std::uint8_t>>;
}
#endif