Coverage Report

Created: 2025-07-11 06:16

/src/vlc/modules/demux/mkv/string_dispatcher.hpp
Line
Count
Source (jump to first uncovered line)
1
/*****************************************************************************
2
 * string_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_STRING_DISPATCHER_HPP_
23
#define VLC_MKV_STRING_DISPATCHER_HPP_
24
25
#include "dispatcher.hpp"
26
27
#include <map>
28
#include <vector>
29
#include <string>
30
#include <cstring>
31
#include <sstream>
32
33
namespace {
34
  namespace detail {
35
    struct CStringCompare {
36
0
      bool operator () (char const * const& s1, char const * const& s2) const {
37
0
        return std::strcmp (s1, s2) < 0;
38
0
      }
39
    };
40
  }
41
42
  class StringDispatcher : public Dispatcher<StringDispatcher, void(*)(char const*, void*)> {
43
    public:
44
      typedef void(*Processor)(char const*, void*);
45
46
      typedef std::pair<char const *, Processor>                         ProcessorEntry;
47
      typedef std::map <char const *, Processor, detail::CStringCompare> ProcessorContainer;
48
49
      typedef std::vector<std::string>                      GlobParts;
50
      typedef std::vector<std::pair<GlobParts, Processor> > GlobContainer;
51
52
    public:
53
0
      void insert (ProcessorEntry const& data) {
54
0
        _processors.insert (data);
55
0
      }
56
57
0
      void insert_glob (ProcessorEntry const& data) {
58
0
        std::istringstream iss (data.first);
59
0
        std::vector<std::string> parts;
60
61
0
        for (std::string s1; std::getline (iss, s1, '*'); )
62
0
          parts.push_back (s1);
63
64
0
        iss.clear ();
65
0
        iss.unget ();
66
67
0
        if (iss.get () == '*')
68
0
          parts.push_back (std::string ());
69
70
0
        _glob_processors.push_back (std::make_pair (parts, data.second));
71
0
      }
72
73
0
      Processor find_glob_match (char const* const& haystack) const {
74
0
        GlobContainer::const_iterator glob_it     = _glob_processors.begin ();
75
0
        GlobContainer::const_iterator glob_it_end = _glob_processors.end ();
76
77
0
        for ( ; glob_it != glob_it_end; ++glob_it) {
78
0
          Processor const& callback   = glob_it->second;
79
0
          GlobParts const& parts      = glob_it->first;
80
0
          char      const* haystack_p = haystack;
81
82
          /* No parts? match empty haystack only */
83
84
0
          if (parts.size() == 0) {
85
0
            if (*haystack_p) continue;
86
0
            else             return callback;
87
0
          }
88
89
          /* make sure first part is located at the beginning of our haystack */
90
0
          {
91
0
            std::string const& first_part = parts.front ();
92
93
0
            if (strncmp( first_part.c_str(), haystack_p, first_part.size() ))
94
0
              continue;
95
96
0
            haystack_p += first_part.size();
97
0
          }
98
          /* locate every remaining part in the haystack */
99
0
          {
100
0
            GlobParts::const_iterator it  = parts.begin() + 1;
101
0
            GlobParts::const_iterator end = parts.end();
102
103
0
            for ( ; it != end; ++it) {
104
0
              haystack_p = strstr (haystack_p, it->c_str ());
105
106
0
              if (haystack_p == NULL)
107
0
                break;
108
109
0
              haystack_p += it->size ();
110
0
            }
111
112
0
            if ( haystack_p == NULL)             continue; // last search failed
113
0
            if (*haystack_p == '\0')      return callback; // end of parts + end of haystack? success.
114
0
            if (parts.back().size() == 0) return callback; // endof parts + glob at end? success
115
0
          }
116
0
        }
117
118
0
        return NULL;
119
0
      }
120
121
      bool send (char const* const& str, void* const& payload) const
122
0
      {
123
0
        ProcessorContainer::const_iterator cit     = _processors.begin ();
124
0
        ProcessorContainer::const_iterator cit_end = _processors.end ();
125
126
0
        if ((cit = _processors.find (str)) != cit_end) {
127
0
            cit->second (str, payload);
128
0
            return true;
129
0
        }
130
131
0
        if (Processor glob = find_glob_match (str)) {
132
0
          glob (str, payload);
133
0
          return true;
134
0
        }
135
136
0
        if (_default_handler != NULL) {
137
0
            _default_handler (str, payload);
138
0
            return true;
139
0
        }
140
141
0
        return false;
142
0
      }
143
144
    private:
145
      ProcessorContainer _processors;
146
      GlobContainer _glob_processors;
147
  };
148
149
} /* end-of-namespace */
150
151
#define STRD_T0KENPASTE_(a, b) a ## b
152
#define STRD_TOKENPASTE_(a, b) STRD_T0KENPASTE_(a, b)
153
#define STRD_UNIQUE_NAME_      STRD_TOKENPASTE_(StringProcessor_, __LINE__)
154
155
#define STRING_CASE_DEF(ClassName_, VariableName_, InitializationExpr_ )                    \
156
    MKV_SWITCH_CASE_DEFINITION(ClassName_, char const*, char const*, VariableName_, vars, InitializationExpr_, data)
157
158
#define S_CASE(Str_)                                                                         \
159
  STRING_CASE_DEF(STRD_UNIQUE_NAME_, /* ignored */,                                         \
160
    (dispatcher.insert( StringDispatcher::ProcessorEntry(Str_, STRD_TOKENPASTE_(STRD_UNIQUE_NAME_, _callback) ) )) \
161
  )
162
163
#define S_CASE_GLOB(Str_) \
164
  STRING_CASE_DEF(STRD_UNIQUE_NAME_, /* ignored */,                                         \
165
    (dispatcher.insert_glob( StringDispatcher::ProcessorEntry(Str_, STRD_TOKENPASTE_(STRD_UNIQUE_NAME_, _callback) ) )) \
166
  )
167
168
#define S_CASE_DEFAULT(VariableName_)                                                        \
169
  STRING_CASE_DEF(default_handler, VariableName_,                                           \
170
    dispatcher.set_default_handler (&default_handler_callback)                                      \
171
  )
172
173
#endif