Initial release
This commit is contained in:
@@ -0,0 +1,533 @@
|
||||
// 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_
|
||||
Reference in New Issue
Block a user