blob: 2a02a3f734522cc31d31fba4c4a707f93e1e3993 [file] [log] [blame]
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
#ifndef SRC_MASTER_VALUE_PARSER_H_
#define SRC_MASTER_VALUE_PARSER_H_
#include <cassert>
#include <cstdint>
#include <functional>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include "src/master_parser.h"
#include "src/recursive_parser.h"
#include "src/skip_callback.h"
#include "webm/callback.h"
#include "webm/element.h"
#include "webm/id.h"
#include "webm/reader.h"
#include "webm/status.h"
namespace webm {
// Parses Master elements from an EBML stream, storing child values in a
// structure of type T. This class differs from MasterParser in that
// MasterParser does not collect the parsed data into an object that can then be
// retrieved.
//
// For example, consider the following Foo object, which represents a master
// element that contains two booleans:
//
// struct Foo {
// Element<bool> bar;
// Element<bool> baz;
// };
//
// A FooParser implemented via MasterParser, like below, could be used to parse
// the master element and the boolean children, but the boolean values could not
// be retrieved from the parser.
//
// struct FooParser : public MasterParser {
// FooParser()
// : MasterParser({Id::kBar, new BoolParser}, // std::pair<*> types
// {Id::kBaz, new BoolParser}) {} // omitted for brevity.
// };
//
// However, if FooParser is implemented via MasterValueParser<Foo>, then the
// boolean values will be parsed into a Foo object that can be retrieved from
// FooParser via its value() and mutable_value() methods.
//
// struct FooParser : public MasterValueParser<Foo> {
// FooParser()
// : MasterValueParser(MakeChild<BoolParser>(Id::kBar, &Foo::bar),
// MakeChild<BoolParser>(Id::kBaz, &Foo::baz)) {}
// };
template <typename T>
class MasterValueParser : public ElementParser {
public:
Status Init(const ElementMetadata& metadata,
std::uint64_t max_size) override {
assert(metadata.size == kUnknownElementSize || metadata.size <= max_size);
PreInit();
const Status status = master_parser_.Init(metadata, max_size);
if (!status.completed_ok()) {
return status;
}
return status;
}
void InitAfterSeek(const Ancestory& child_ancestory,
const ElementMetadata& child_metadata) override {
PreInit();
started_done_ = true;
master_parser_.InitAfterSeek(child_ancestory, child_metadata);
}
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 (!parse_complete_) {
// TODO(mjbshaw): just call Reader::Skip if element's size is known and
// action is skip.
SkipCallback skip_callback;
if (action_ == Action::kSkip) {
callback = &skip_callback;
}
Status status = master_parser_.Feed(callback, reader, num_bytes_read);
// Check if we've artificially injected an error code, and if so, switch
// into skipping mode.
if (status.code == Status::kSwitchToSkip) {
assert(started_done_);
assert(action_ == Action::kSkip);
callback = &skip_callback;
std::uint64_t local_num_bytes_read;
status = master_parser_.Feed(callback, reader, &local_num_bytes_read);
*num_bytes_read += local_num_bytes_read;
}
if (!status.completed_ok()) {
return status;
}
parse_complete_ = true;
}
if (!started_done_) {
Status status = OnParseStarted(callback, &action_);
if (!status.completed_ok()) {
return status;
}
started_done_ = true;
}
if (action_ != Action::kSkip) {
return OnParseCompleted(callback);
}
return Status(Status::kOkCompleted);
}
bool GetCachedMetadata(ElementMetadata* metadata) override {
return master_parser_.GetCachedMetadata(metadata);
}
bool WasSkipped() const override { return action_ == Action::kSkip; }
const T& value() const { return value_; }
T* mutable_value() { return &value_; }
protected:
// Users and subclasses are not meant to use the Tag* classes; they're an
// internal implementation detail.
// A tag that will cause the internal ChildParser to use the parsed child as a
// start event.
struct TagUseAsStart {};
// A tag that will cause the internal ChildParser to call OnChildParsed once
// it has been fully parsed.
struct TagNotifyOnParseComplete {};
// A factory that will create a std::pair<Id, std::unique_ptr<ElementParser>>.
// Users and subclasses are not meant to use this class directly, as it is an
// internal implementation detail of this class. Subclasses should use
// MakeChild instead of using this class directly.
template <typename Parser, typename Value, typename... Tags>
class SingleChildFactory {
public:
constexpr SingleChildFactory(Id id, Element<Value> T::*member)
: id_(id), member_(member) {}
// Builds a std::pair<Id, std::unique_ptr<ElementParser>>. The parent
// pointer must be a pointer to the MasterValueParser that is being
// constructed. The given value pointer must be the pointer to the fully
// constructed MasterValueParser::value_ object.
std::pair<Id, std::unique_ptr<ElementParser>> BuildParser(
MasterValueParser* parent, T* value) {
assert(parent != nullptr);
assert(value != nullptr);
Element<Value>* child_member = &(value->*member_);
auto lambda = [child_member](Parser* parser) {
child_member->Set(std::move(*parser->mutable_value()), true);
};
return {id_, MakeChildParser<Parser, Value, Tags...>(
parent, std::move(lambda), child_member)};
}
// If called, OnParseStarted will be called on the parent element when this
// particular element is encountered.
constexpr SingleChildFactory<Parser, Value, TagUseAsStart, Tags...>
UseAsStartEvent() const {
return {id_, member_};
}
// If called, OnChildParsed will be called on the parent element when this
// particular element is fully parsed.
constexpr SingleChildFactory<Parser, Value, TagNotifyOnParseComplete,
Tags...>
NotifyOnParseComplete() const {
return {id_, member_};
}
private:
Id id_;
Element<Value> T::*member_;
};
template <typename Parser, typename Value, typename... Tags>
class RepeatedChildFactory {
public:
constexpr RepeatedChildFactory(Id id,
std::vector<Element<Value>> T::*member)
: id_(id), member_(member) {}
// Builds a std::pair<Id, std::unique_ptr<ElementParser>>. The parent
// pointer must be a pointer to the MasterValueParser that is being
// constructed. The given value pointer must be the pointer to the fully
// constructed MasterValueParser::value_ object.
std::pair<Id, std::unique_ptr<ElementParser>> BuildParser(
MasterValueParser* parent, T* value) {
assert(parent != nullptr);
assert(value != nullptr);
std::vector<Element<Value>>* child_member = &(value->*member_);
auto lambda = [child_member](Parser* parser) {
if (child_member->size() == 1 && !child_member->front().is_present()) {
child_member->clear();
}
child_member->emplace_back(std::move(*parser->mutable_value()), true);
};
return {id_, MakeChildParser<Parser, Value, Tags...>(
parent, std::move(lambda), child_member)};
}
// If called, OnParseStarted will be called on the parent element when this
// particular element is encountered.
constexpr RepeatedChildFactory<Parser, Value, TagUseAsStart, Tags...>
UseAsStartEvent() const {
return {id_, member_};
}
// If called, OnChildParsed will be called on the parent element when this
// particular element is fully parsed.
constexpr RepeatedChildFactory<Parser, Value, TagNotifyOnParseComplete,
Tags...>
NotifyOnParseComplete() const {
return {id_, member_};
}
private:
Id id_;
std::vector<Element<Value>> T::*member_;
};
template <typename Parser, typename... Tags>
class RecursiveChildFactory {
public:
constexpr RecursiveChildFactory(Id id, std::vector<Element<T>> T::*member,
std::size_t max_recursion_depth)
: id_(id), member_(member), max_recursion_depth_(max_recursion_depth) {}
// Builds a std::pair<Id, std::unique_ptr<ElementParser>>. The parent
// pointer must be a pointer to the MasterValueParser that is being
// constructed. The given value pointer must be the pointer to the fully
// constructed MasterValueParser::value_ object.
std::pair<Id, std::unique_ptr<ElementParser>> BuildParser(
MasterValueParser* parent, T* value) {
assert(parent != nullptr);
assert(value != nullptr);
std::vector<Element<T>>* child_member = &(value->*member_);
auto lambda = [child_member](RecursiveParser<Parser>* parser) {
if (child_member->size() == 1 && !child_member->front().is_present()) {
child_member->clear();
}
child_member->emplace_back(std::move(*parser->mutable_value()), true);
};
return {id_, std::unique_ptr<ElementParser>(
new ChildParser<RecursiveParser<Parser>,
decltype(lambda), Tags...>(
parent, std::move(lambda), max_recursion_depth_))};
}
// If called, OnParseStarted will be called on the parent element when this
// particular element is encountered.
constexpr RecursiveChildFactory<Parser, TagUseAsStart, Tags...>
UseAsStartEvent() const {
return {id_, member_, max_recursion_depth_};
}
// If called, OnChildParsed will be called on the parent element when this
// particular element is fully parsed.
constexpr RecursiveChildFactory<Parser, TagNotifyOnParseComplete, Tags...>
NotifyOnParseComplete() const {
return {id_, member_, max_recursion_depth_};
}
private:
Id id_;
std::vector<Element<T>> T::*member_;
std::size_t max_recursion_depth_;
};
// Constructs a new parser. Each argument must be a *ChildFactory, constructed
// from the MakeChild method.
template <typename... Args>
explicit MasterValueParser(Args&&... args)
: master_parser_(args.BuildParser(this, &value_)...) {}
// Returns a factory that will produce a
// std::pair<Id, std::unique_ptr<ElementParser>>. When a child element of the
// given ID is encountered, a parser of type Parser will be used to parse it,
// and store its value in the member pointer when the parse is complete. The
// given default value will be used in the event that the child element has a
// zero size. This method is only meant to be used by subclasses to provide
// the necessary factories to the constructor.
template <typename Parser, typename Value>
static SingleChildFactory<Parser, Value> MakeChild(
Id id, Element<Value> T::*member) {
static_assert(std::is_base_of<ElementParser, Parser>::value,
"Parser must derive from ElementParser");
static_assert(!std::is_base_of<MasterValueParser<T>, Parser>::value,
"Recursive elements should be contained in a std::vector");
return SingleChildFactory<Parser, Value>(id, member);
}
template <typename Parser, typename Value>
static RepeatedChildFactory<Parser, Value> MakeChild(
Id id, std::vector<Element<Value>> T::*member) {
static_assert(std::is_base_of<ElementParser, Parser>::value,
"Parser must derive from ElementParser");
static_assert(!std::is_base_of<MasterValueParser<T>, Parser>::value,
"Recursive elements require a maximum recursion depth");
return RepeatedChildFactory<Parser, Value>(id, member);
}
template <typename Parser>
static RecursiveChildFactory<Parser> MakeChild(
Id id, std::vector<Element<T>> T::*member,
std::size_t max_recursion_depth) {
static_assert(std::is_base_of<MasterValueParser<T>, Parser>::value,
"Child must be recusrive to use maximum recursion depth");
return RecursiveChildFactory<Parser>(id, member, max_recursion_depth);
}
// Gets the metadata for this element, setting the EBML element ID to id. Only
// call after Init() has been called.
ElementMetadata metadata(Id id) const {
return {id, master_parser_.header_size(), master_parser_.size(),
master_parser_.position()};
}
// This method will be called once the element has been fully parsed, or a
// particular child element of interest (see UseAsStartEvent()) is
// encountered. By default it just sets *action to Action::kRead and returns
// Status::kOkCompleted. May be overridden (i.e. in order to call a Callback
// method). Returning anything other than Status::kOkCompleted will stop
// parsing and the status to be returned by Init or Feed (whichever originated
// the call to this method). In this case, resuming parsing will result in
// this method being called again.
virtual Status OnParseStarted(Callback* callback, Action* action) {
assert(callback != nullptr);
assert(action != nullptr);
*action = Action::kRead;
return Status(Status::kOkCompleted);
}
// This method is the companion to OnParseStarted, and will only be called
// after OnParseStarted has already been called and parsing has completed.
// This will not be called if OnParseStarted set the action to Action::kSkip.
// By default it just returns Status::kOkCompleted. Returning anything other
// than Status::kOkCompleted will stop parsing and the status to be returned
// by Init or Feed (whichever originated the call to this method). In this
// case, resuming parsing will result in this method being called again.
virtual Status OnParseCompleted(Callback* callback) {
assert(callback != nullptr);
return Status(Status::kOkCompleted);
}
// Returns true if the OnParseStarted method has already been called and has
// completed.
bool parse_started_event_completed() const { return started_done_; }
// Derived classes may manually call OnParseStarted before calling Feed, in
// which case this method should be called to inform this class that
// OnParseStarted has already been called and it should not be called again.
void set_parse_started_event_completed_with_action(Action action) {
assert(!started_done_);
action_ = action;
started_done_ = true;
}
// This method will be called for each child element that has been fully
// parsed for which NotifyOnParseComplete() was requested. The provided
// metadata is for the child element that has just completed parsing. By
// default this method does nothing.
virtual void OnChildParsed(const ElementMetadata& /* metadata */) {}
private:
T value_;
Action action_ = Action::kRead;
bool parse_complete_;
bool started_done_;
// master_parser_ must be after value_ to ensure correct initialization order.
MasterParser master_parser_;
const ElementMetadata& child_metadata() const {
return master_parser_.child_metadata();
}
// Helper struct that will be std::true_type if Tag is in Tags, or
// std::false_type otherwise.
template <typename Tag, typename... Tags>
struct HasTag;
// Base condition: Tags is empty, so it trivially does not contain Tag.
template <typename Tag>
struct HasTag<Tag> : std::false_type {};
// If the head of the Tags list is a different tag, skip it and check the
// remaining tags.
template <typename Tag, typename DifferentTag, typename... Tags>
struct HasTag<Tag, DifferentTag, Tags...> : HasTag<Tag, Tags...> {};
// If the head of the Tags list is the same as Tag, then we're done.
template <typename Tag, typename... Tags>
struct HasTag<Tag, Tag, Tags...> : std::true_type {};
template <typename Base, typename F, typename... Tags>
class ChildParser : public Base {
public:
using Base::WasSkipped;
template <typename... Args>
explicit ChildParser(MasterValueParser* parent, F consume_element_value,
Args&&... base_args)
: Base(std::forward<Args>(base_args)...),
parent_(parent),
consume_element_value_(std::move(consume_element_value)) {}
ChildParser() = delete;
ChildParser(const ChildParser&) = delete;
ChildParser& operator=(const ChildParser&) = delete;
Status Feed(Callback* callback, Reader* reader,
std::uint64_t* num_bytes_read) override {
*num_bytes_read = 0;
Status status = Prepare(callback);
if (!status.completed_ok()) {
return status;
}
status = Base::Feed(callback, reader, num_bytes_read);
if (status.completed_ok() && parent_->action_ != Action::kSkip &&
!WasSkipped()) {
consume_element_value_(this);
if (has_tag<TagNotifyOnParseComplete>()) {
parent_->OnChildParsed(parent_->child_metadata());
}
}
return status;
}
private:
MasterValueParser* parent_;
F consume_element_value_;
Status Prepare(Callback* callback) {
if (has_tag<TagUseAsStart>() && !parent_->started_done_) {
const Status status =
parent_->OnParseStarted(callback, &parent_->action_);
if (!status.completed_ok()) {
return status;
}
parent_->started_done_ = true;
if (parent_->action_ == Action::kSkip) {
return Status(Status::kSwitchToSkip);
}
}
return Status(Status::kOkCompleted);
}
template <typename Tag>
constexpr static bool has_tag() {
return MasterValueParser::HasTag<Tag, Tags...>::value;
}
};
// Returns a std::unique_ptr<ElementParser> that points to a ChildParser
// when the Parser's constructor does not take a Value parameter.
template <typename Parser, typename Value, typename... Tags, typename F>
static typename std::enable_if<!std::is_constructible<Parser, Value>::value,
std::unique_ptr<ElementParser>>::type
MakeChildParser(MasterValueParser* parent, F consume_element_value, ...) {
return std::unique_ptr<ElementParser>(new ChildParser<Parser, F, Tags...>(
parent, std::move(consume_element_value)));
}
// Returns a std::unique_ptr<ElementParser> that points to a ChildParser
// when the Parser's constructor does take a Value parameter.
template <typename Parser, typename Value, typename... Tags, typename F>
static typename std::enable_if<std::is_constructible<Parser, Value>::value,
std::unique_ptr<ElementParser>>::type
MakeChildParser(MasterValueParser* parent, F consume_element_value,
const Element<Value>* default_value) {
return std::unique_ptr<ElementParser>(new ChildParser<Parser, F, Tags...>(
parent, std::move(consume_element_value), default_value->value()));
}
// Returns a std::unique_ptr<ElementParser> that points to a ChildParser
// when the Parser's constructor does take a Value parameter.
template <typename Parser, typename Value, typename... Tags, typename F>
static typename std::enable_if<std::is_constructible<Parser, Value>::value,
std::unique_ptr<ElementParser>>::type
MakeChildParser(MasterValueParser* parent, F consume_element_value,
const std::vector<Element<Value>>* member) {
Value default_value{};
if (!member->empty()) {
default_value = member->front().value();
}
return std::unique_ptr<ElementParser>(new ChildParser<Parser, F, Tags...>(
parent, std::move(consume_element_value), std::move(default_value)));
}
// Initializes object state. Call immediately before initializing
// master_parser_.
void PreInit() {
value_ = {};
action_ = Action::kRead;
parse_complete_ = false;
started_done_ = false;
}
};
} // namespace webm
#endif // SRC_MASTER_VALUE_PARSER_H_