Commit 421416d1 authored by Filip Roséen's avatar Filip Roséen Committed by Jean-Baptiste Kempf

mkv: Added EbmlTypeDispatcher

The EbmlTypeDispatcher is a type used to "dispatch" an object of type
EbmlElement* to an appropriate callback depending on the dynamic type of
the referred to object.

It can effectivelly replace the massive if-else branches found
throughout the module by not only making the code easier to understand
and maintain, but also by making it _a lot_ faster (benchmarks show a
speed increase between 450 and 700% in terms of lookup).
parent 155fbc6b
......@@ -173,7 +173,9 @@ libmkv_plugin_la_SOURCES = \
demux/mkv/matroska_segment.hpp demux/mkv/matroska_segment.cpp \
demux/mkv/matroska_segment_parse.cpp \
demux/mkv/demux.hpp demux/mkv/demux.cpp \
demux/mkv/dispatcher.hpp \
demux/mkv/Ebml_parser.hpp demux/mkv/Ebml_parser.cpp \
demux/mkv/Ebml_dispatcher.hpp \
demux/mkv/chapters.hpp demux/mkv/chapters.cpp \
demux/mkv/chapter_command.hpp demux/mkv/chapter_command.cpp \
demux/mkv/stream_io_callback.hpp demux/mkv/stream_io_callback.cpp \
......
/*****************************************************************************
* Ebml_dispatcher.hpp : matroska demuxer
*****************************************************************************
* Copyright (C) 2016 VLC authors, VideoLAN, Videolabs SAS
* $Id$
*
* Authors: Filip Roseen <filip@videolabs.io>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef VLC_MKV_EBML_DISPATCHER_HPP_
#define VLC_MKV_EBML_DISPATCHER_HPP_
#include "dispatcher.hpp"
#include "ebml/EbmlElement.h"
#include "ebml/EbmlId.h"
#include <vlc_threads.h>
#include <algorithm>
#include <typeinfo>
#include <vector>
namespace {
namespace detail {
template<class T>
static std::type_info const* typeid_ptr () {
static std::type_info const& ti = typeid (T);
return &ti;
}
}
struct EbmlProcessorEntry {
typedef void (*EbmlProcessor) (EbmlElement*, void*);
EbmlId const* p_ebmlid;
std::type_info const* p_typeid;
EbmlProcessor callback;
EbmlProcessorEntry (EbmlId const& id, std::type_info const* ti, EbmlProcessor cb)
: p_ebmlid (&id), p_typeid (ti), callback (cb)
{ }
};
struct ProcessorEntrySorter {
typedef EbmlProcessorEntry value_type;
bool operator() (value_type const& lhs, value_type const& rhs) const {
EbmlId const& lid = *lhs.p_ebmlid;
EbmlId const& rid = *rhs.p_ebmlid;
return lid.GetLength() < rid.GetLength() || (
!( rid.GetLength() < lid.GetLength() ) && lid.GetValue() < rid.GetValue()
);
}
};
class EbmlTypeDispatcher : public Dispatcher<EbmlTypeDispatcher, EbmlProcessorEntry::EbmlProcessor> {
protected:
typedef std::vector<EbmlProcessorEntry> ProcessorContainer;
public:
void insert (EbmlProcessorEntry const& data) {
_processors.push_back (data);
}
void on_create () {
std::sort (_processors.begin(), _processors.end(), _ebml_sorter);
}
bool send (EbmlElement * const& element, void* payload) const
{
EbmlProcessorEntry eb = EbmlProcessorEntry (
static_cast<EbmlId const&> (*element), NULL, NULL
);
// --------------------------------------------------------------
// Find the appropriate callback for the received EbmlElement
// --------------------------------------------------------------
ProcessorContainer::const_iterator cit_end = _processors.end();
ProcessorContainer::const_iterator cit = std::lower_bound (
_processors.begin(), cit_end, eb, _ebml_sorter
);
if (element && cit != cit_end)
{
// --------------------------------------------------------------
// normally we only need to compare the addresses of the EbmlId
// since libebml returns a reference to a _static_ instance.
// --------------------------------------------------------------
while (cit != cit_end && (cit->p_ebmlid == eb.p_ebmlid || (*cit->p_ebmlid == *eb.p_ebmlid))) {
std::type_info const& ti = typeid (*element);
// --------------------------------------------------------------
// even though the EbmlId are equivalent, we still need to make
// sure that the typeid also matches.
// --------------------------------------------------------------
if (*(cit->p_typeid) == ti) {
cit->callback (element, payload);
return true;
}
++cit;
}
}
if (_default_handler == NULL)
return false;
_default_handler (element, payload);
return true;
}
public:
ProcessorContainer _processors;
static ProcessorEntrySorter _ebml_sorter;
};
} /* end-of-namespace */
#define EBML_ELEMENT_CASE_DEF(EbmlType_, ClassName_, VariableName_, InitializationExpr_) \
MKV_SWITCH_CASE_DEFINITION( ClassName_, EbmlType_, EbmlElement*, VariableName_, vars, \
InitializationExpr_, static_cast<EbmlType_&> (*data) \
)
// -----------------------------------------------------------------------------------
// The use of `detail::typeid_ptr` below is so that we do not have to invoke "typeid"
// every time we are requested to do a lookup. `std::type_info` cannot be copied, so
// we cannot pass it by value.
//
// In C++11 you could use the hash value present inside std::type_info to do lookup,
// but we are stuck in C++03 and have to use the below instead.
// -----------------------------------------------------------------------------------
#define E_CASE(EbmlType_, VariableName_) \
EBML_ELEMENT_CASE_DEF(EbmlType_, EbmlType_, VariableName_, \
(dispatcher.insert( EbmlProcessorEntry( EbmlType_ ::ClassInfos.ClassId(), detail::typeid_ptr<EbmlType_>(), &EbmlType_ ## _callback) ) ) \
)
#define E_CASE_DEFAULT(VariableName_) \
EBML_ELEMENT_CASE_DEF(EbmlElement, ebml_default, VariableName_, \
dispatcher.set_default_handler (&ebml_default_callback) \
)
#endif
/*****************************************************************************
* dispatcher.hpp : matroska demuxer
*****************************************************************************
* Copyright (C) 2016 VLC authors, VideoLAN, Videolabs SAS
* $Id$
*
* Authors: Filip Roseen <filip@videolabs.io>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef VLC_MKV_DISPATCHER_HPP_
#define VLC_MKV_DISPATCHER_HPP_
// ----------------------------------------------------------------------------
// This header contains helpers to simulate lambdas in C++03.
//
// It will be used to create "dispatchers" which can be thought of like a
// `switch` but only more dynamic.
// ----------------------------------------------------------------------------
namespace {
template<class Impl, class Processor>
class Dispatcher {
protected:
Dispatcher() : _default_handler (NULL) { }
public:
template<class It>
void iterate (It beg, It end, void* const& payload) const {
for (; beg != end; ++beg)
static_cast<Impl const*> (this)->Impl::send (*beg, payload);
}
void set_default_handler (Processor const& callback) {
_default_handler = callback;
}
void on_create () {
/* empty default implementation */
}
Processor _default_handler;
};
template<int>
struct DispatcherTag;
template<class T, T*, class DispatcherType>
class DispatchContainer {
public: static DispatcherType dispatcher;
protected: static vlc_mutex_t _dispatcher_lock;
};
template<class T, T* P, class DT>
DT DispatchContainer<T, P, DT>::dispatcher;
template<class T, T* P, class DT>
vlc_mutex_t DispatchContainer<T, P, DT>::_dispatcher_lock = VLC_STATIC_MUTEX;
}
// ----------------------------------------------------------------------------
// * `GroupName_##_tag` is used so that we can refer to a static dispatcher
// of the correct type without instantiating DispatchContainer with a
// locally declared type (since it is illegal in C++03).
//
// We are however allowed to pass the variable of a variable declared
// `extern`, and this will effectively be our handle to the "foreign"
// static dispatcher.
//
// We make the variable have type `DispatcherTag<__LINE__>` so that you
// can use MKV_SWITCH_CREATE with the same name in different parts of the
// translation-unit (without collision).
//
// * `GroupName_ ## _base` is used to declare a bunch of helpers; names that
// must be available in our fake "lambdas" (C++03 is a pain).
// ----------------------------------------------------------------------------
#define MKV_SWITCH_CREATE(DispatchType_, GroupName_, PayloadType_) \
typedef DispatcherTag<__LINE__> GroupName_ ## _tag_t; \
extern GroupName_##_tag_t GroupName_ ## _tag; \
struct GroupName_##_base : DispatchContainer<GroupName_##_tag_t, &GroupName_##_tag, DispatchType_> { \
typedef PayloadType_ payload_t; \
typedef DispatchType_ dispatch_t; \
typedef struct GroupName_ handler_t; \
static void* Payload (payload_t& data) { \
return static_cast<void*> (&data); \
} \
}; \
struct GroupName_ : GroupName_ ## _base
// ----------------------------------------------------------------------------
// * `Dispatcher` is a static function used to access the dispatcher in a
// thread-safe manner. We only want _one_ thread to actually construct
// and initialize it, hence the lock.
// ----------------------------------------------------------------------------
#define MKV_SWITCH_INIT() \
static dispatch_t& Dispatcher () { \
static handler_t * p_handler = NULL; \
vlc_mutex_lock( &_dispatcher_lock ); \
if (unlikely( p_handler == NULL) ) { \
static handler_t handler; \
p_handler = &handler; \
p_handler->dispatcher.on_create (); \
} \
vlc_mutex_unlock( &_dispatcher_lock ); \
return p_handler->dispatcher; \
} struct PleaseAddSemicolon {}
// ----------------------------------------------------------------------------
// * The following is to be used inside `struct GroupName_`, effectivelly
// declaring a local struct and a data-member, `ClassName_ ## _processor`,
// of that type.
//
// When the data-member is constructed it will run `InitializationExpr_`,
// meaning that it can access the static dispatcher and register itself (or
// whatever is desired).
//
// * Since we need to do type-erasure, once again, because of the fact that
// C++03 does not support locally declared types as template-arguments, we
// declare a static function `ClassName_ ## _callback` that will cast our
// payload from `void*` to the appropriate type.
//
// * The body of `ClassName_ ## _handler` will be written by the user of the
// MACRO, and the implementation will effectively be invoked whenever the
// dispatcher decides to (through `ClassName_ ## _callback`).
//
// * Since the type of the `VariableName_` argument to `ClassName_ ## _handler`
// might not necessarily be the same as the type passed from the dispatcher
// (because of the type-erasure), the macro provides a way to change the
// type with a `UnwrapExpr_` (see the function call within `ClassName_ ##
// _callback`).
// ----------------------------------------------------------------------------
#define MKV_SWITCH_CASE_DEFINITION(ClassName_, RealType_, Type_, VariableName_, PayloadName_, InitializationExpr_, UnwrapExpr_) \
struct ClassName_##_processor { \
ClassName_##_processor () { InitializationExpr_; } \
} ClassName_##__wrapper; \
static inline void ClassName_##_callback (Type_ data, void* PayloadName_) { \
ClassName_##_handler (UnwrapExpr_, *static_cast<payload_t*>(PayloadName_)); \
} \
static inline void ClassName_##_handler (RealType_& VariableName_, payload_t& PayloadName_)
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment