85bf03e6094c8e7ddde3ce5eb72611f9763eafcb
[vlc.git] / modules / demux / mkv / Ebml_dispatcher.hpp
1 /*****************************************************************************
2  * Ebml_dispatcher.hpp : matroska demuxer
3  *****************************************************************************
4  * Copyright (C) 2016 VLC authors, VideoLAN, Videolabs SAS
5  *
6  * Authors: Filip Roseen <filip@videolabs.io>
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 2.1 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21  *****************************************************************************/
22 #ifndef VLC_MKV_EBML_DISPATCHER_HPP_
23 #define VLC_MKV_EBML_DISPATCHER_HPP_
24
25 #include "dispatcher.hpp"
26
27 #include "ebml/EbmlElement.h"
28 #include "ebml/EbmlId.h"
29
30 #include <vlc_threads.h>
31
32 #include <algorithm>
33 #include <typeinfo>
34 #include <vector>
35
36 namespace {
37   namespace detail {
38     template<class T>
39     static std::type_info const* typeid_ptr () {
40       static std::type_info const& ti = typeid (T);
41       return &ti;
42     }
43   }
44
45   struct EbmlProcessorEntry {
46     typedef void (*EbmlProcessor) (EbmlElement*, void*);
47
48     EbmlId         const* p_ebmlid;
49     std::type_info const* p_typeid;
50     EbmlProcessor         callback;
51
52     EbmlProcessorEntry (EbmlId const& id, std::type_info const* ti, EbmlProcessor cb)
53       : p_ebmlid (&id), p_typeid (ti), callback (cb)
54     { }
55
56   };
57
58   bool operator<( EbmlProcessorEntry const& lhs, EbmlProcessorEntry const& rhs )
59   {
60       EbmlId const& lid = *lhs.p_ebmlid;
61       EbmlId const& rid = *rhs.p_ebmlid;
62
63       return lid.GetLength() < rid.GetLength() || (
64         !( rid.GetLength() < lid.GetLength() ) && lid.GetValue() < rid.GetValue()
65       );
66   }
67
68   class EbmlTypeDispatcher : public Dispatcher<EbmlTypeDispatcher, EbmlProcessorEntry::EbmlProcessor> {
69     protected:
70       typedef std::vector<EbmlProcessorEntry> ProcessorContainer;
71
72     public:
73       void insert (EbmlProcessorEntry const& data) {
74         _processors.push_back (data);
75       }
76
77       void on_create () {
78         std::sort (_processors.begin(), _processors.end());
79       }
80
81       bool send (EbmlElement * const& element, void* payload) const
82       {
83         if ( element == nullptr )
84             return false;
85
86         EbmlProcessorEntry eb = EbmlProcessorEntry (
87           static_cast<EbmlId const&> (*element), NULL, NULL
88         );
89
90         // --------------------------------------------------------------
91         // Find the appropriate callback for the received EbmlElement
92         // --------------------------------------------------------------
93
94         ProcessorContainer::const_iterator cit_end = _processors.end();
95         ProcessorContainer::const_iterator cit     = std::lower_bound (
96             _processors.begin(), cit_end, eb
97         );
98
99         if (cit != cit_end)
100         {
101           // --------------------------------------------------------------
102           // normally we only need to compare the addresses of the EbmlId
103           // since libebml returns a reference to a _static_ instance.
104           // --------------------------------------------------------------
105
106           while (cit != cit_end && (cit->p_ebmlid == eb.p_ebmlid || (*cit->p_ebmlid == *eb.p_ebmlid))) {
107             std::type_info const& ti = typeid (*element);
108
109             // --------------------------------------------------------------
110             // even though the EbmlId are equivalent, we still need to make
111             // sure that the typeid also matches.
112             // --------------------------------------------------------------
113
114             if (*(cit->p_typeid) == ti) {
115               cit->callback (element, payload);
116               return true;
117             }
118
119             ++cit;
120           }
121         }
122
123         if (_default_handler == NULL)
124             return false;
125
126         _default_handler (element, payload);
127         return true;
128       }
129
130     public:
131       ProcessorContainer _processors;
132   };
133
134 } /* end-of-namespace */
135
136 #define EBML_ELEMENT_CASE_DEF(EbmlType_, ClassName_, VariableName_, InitializationExpr_) \
137     MKV_SWITCH_CASE_DEFINITION( ClassName_, EbmlType_, EbmlElement*, VariableName_, vars,          \
138       InitializationExpr_, static_cast<EbmlType_&> (*data)                            \
139     )
140
141 // -----------------------------------------------------------------------------------
142 // The use of `detail::typeid_ptr` below is so that we do not have to invoke "typeid"
143 // every time we are requested to do a lookup. `std::type_info` cannot be copied, so
144 // we cannot pass it by value.
145 //
146 // In C++11 you could use the hash value present inside std::type_info to do lookup,
147 // but we are stuck in C++03 and have to use the below instead.
148 // -----------------------------------------------------------------------------------
149
150 #define E_CASE(EbmlType_, VariableName_)            \
151     EBML_ELEMENT_CASE_DEF(EbmlType_, EbmlType_, VariableName_, \
152       (dispatcher.insert( EbmlProcessorEntry( EbmlType_ ::ClassInfos.ClassId(), detail::typeid_ptr<EbmlType_>(), &EbmlType_ ## _callback) ) ) \
153     )
154
155 #define E_CASE_DEFAULT(VariableName_)                    \
156     EBML_ELEMENT_CASE_DEF(EbmlElement, ebml_default, VariableName_, \
157       dispatcher.set_default_handler (&ebml_default_callback)    \
158     )
159
160 #endif