910e62b5创建于 1月15日历史提交
// Copyright 2022 The Chromium Authors
// 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 <string_view>

#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.
  std::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 != std::string_view::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 == std::string_view::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_value()) {
      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 = std::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;

std::optional<std::string_view> VariableDictionary::Find(
    types::VariableName name) const& {
  auto iter = entries_.find(name.GetName());
  if (iter == entries_.end()) {
    return std::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(),
        ResolvedSourceString::SubstitutionState::kNoSubstitutions);
  }

  // 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,
        ResolvedSourceString::SubstitutionState::kContainsSubstitutions);
  }

  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());
    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);

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

  return ResolvedSourceString::Create(
      {}, input.Line(), input.Column(), string_buf,
      ResolvedSourceString::SubstitutionState::kContainsSubstitutions);
}

}  // namespace media::hls