summaryrefslogtreecommitdiff
path: root/chromium/media/formats/hls/variable_dictionary.cc
blob: a24000de886ffb24130addb732aac6da670372e6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// Copyright 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/formats/hls/variable_dictionary.h"

#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "media/formats/hls/parse_status.h"
#include "media/formats/hls/source_string.h"
#include "media/formats/hls/types.h"

namespace media::hls {

namespace {

struct GetNextVariableResult {
  // The portion of the string prior to the variable
  SourceString head;

  // The variable name and the portion of the string following it, if one was
  // found.
  absl::optional<std::pair<types::VariableName, SourceString>> tail;
};

GetNextVariableResult GetNextVariable(const SourceString input) {
  // Iterate through occurrences of "{$" in the string.
  for (size_t ref_start = input.Str().find("{$");
       ref_start != base::StringPiece::npos;
       ref_start = input.Str().find("{$", ref_start + 2)) {
    auto remaining_input = input;

    // Extract the substring prior to the variable reference
    const auto head = remaining_input.Consume(ref_start);
    remaining_input.Consume(2);

    // Find the end of the variable reference sequence. If this fails there will
    // be no more valid variable references.
    const auto ref_end = remaining_input.Str().find_first_of('}');
    if (ref_end == base::StringPiece::npos) {
      break;
    }

    // Validate the variable name. If this fails, ignore this sequence and keep
    // searching.
    auto var_name_result =
        types::VariableName::Parse(remaining_input.Consume(ref_end));
    remaining_input.Consume(1);
    if (var_name_result.has_error()) {
      continue;
    }
    auto var_name = std::move(var_name_result).value();

    return GetNextVariableResult{
        /*.head =*/ head, /*.tail =*/ std::make_pair(var_name, remaining_input)};
  }

  return GetNextVariableResult{/*.head =*/ input, /*.tail =*/ absl::nullopt};
}

}  // namespace

VariableDictionary::SubstitutionBuffer::SubstitutionBuffer() = default;

VariableDictionary::SubstitutionBuffer::~SubstitutionBuffer() = default;

VariableDictionary::VariableDictionary() = default;

VariableDictionary::~VariableDictionary() = default;

VariableDictionary::VariableDictionary(VariableDictionary&&) = default;

VariableDictionary& VariableDictionary::operator=(VariableDictionary&&) =
    default;

absl::optional<base::StringPiece> VariableDictionary::Find(
    types::VariableName name) const& {
  auto iter = entries_.find(name.GetName());
  if (iter == entries_.end()) {
    return absl::nullopt;
  }

  return *iter->second;
}

bool VariableDictionary::Insert(types::VariableName name, std::string value) {
  return entries_
      .try_emplace(std::move(name).GetName(),
                   std::make_unique<std::string>(std::move(value)))
      .second;
}

ParseStatus::Or<ResolvedSourceString> VariableDictionary::Resolve(
    SourceString input,
    SubstitutionBuffer& buffer) const {
  // Get the first variable reference. If this fails, there were no references
  // and we don't need to allocate anything.
  auto next_var = GetNextVariable(input);
  if (!next_var.tail) {
    return ResolvedSourceString::Create(
        {}, input.Line(), input.Column(), input.Str(),
        ResolvedSourceStringState{.contains_substitutions = false});
  }

  // If there was a variable reference, but it consisted of the entire input
  // string, then simply return a reference to the substitution string.
  if (next_var.head.Empty() && next_var.tail->second.Empty()) {
    auto value = Find(next_var.tail->first);
    if (!value) {
      return ParseStatus(ParseStatusCode::kVariableUndefined)
          .WithData("key", next_var.tail->first.GetName());
    }

    return ResolvedSourceString::Create(
        {}, input.Line(), input.Column(), *value,
        ResolvedSourceStringState{.contains_substitutions = true});
  }

  auto& string_buf = buffer.strings_.emplace_back();

  while (true) {
    // Append the substring leading to the variable, and abort if there was no
    // variable reference
    string_buf.append(next_var.head.Str().data(), next_var.head.Str().size());
    if (!next_var.tail) {
      break;
    }

    // Look up the variable value
    auto value = Find(next_var.tail->first);
    if (!value) {
      return ParseStatus(ParseStatusCode::kVariableUndefined)
          .WithData("key", next_var.tail->first.GetName());
    }
    string_buf.append(value->data(), value->size());

    next_var = GetNextVariable(next_var.tail->second);
  }

  return ResolvedSourceString::Create(
      {}, input.Line(), input.Column(), string_buf,
      ResolvedSourceStringState{.contains_substitutions = true});
}

}  // namespace media::hls