Repository: carbon-language/carbon-lang
File: toolchain/lex/string_literal.cpp (C++)

Code before:
      }

      // Handle this escape sequence.
      ExpandAndConsumeEscapeSequence(emitter, contents, buffer_cursor);
      buffer_last_escape = buffer_cursor;
    }
  }
}

auto StringLiteral::ComputeCharValue(Diagnostics::Emitter<const char*>& emitter)
    const -> std::optional<CharLiteralValue> {
  CARBON_DCHECK(kind_ == Kind::Char);
  CARBON_DCHECK(is_terminated_);

  if (hash_level_ != 0) {
    CARBON_DIAGNOSTIC(CharLiteralRaw, Error,
                      "unexpected `#` before character literal");
    emitter.Emit(text_.begin(), CharLiteralRaw);
  }

  // Allocate a buffer sized to the content. Note it's possible this could be
  // more efficient/faster with a `ExpandEscapeSequencesAndRemoveIndent`
  // implementation aware of the buffer size, but this is trying to share logic
  // with string expansion.
  llvm::SmallVector<char, 0> buffer;
  buffer.resize_for_overwrite(content_.size());

  auto result = ExpandEscapeSequencesAndRemoveIndent(
      emitter, content_, 0, /*indent=*/llvm::StringRef(), buffer.data());
  CARBON_CHECK(result.size() <= content_.size(),
               "Content grew from {0} to {1}: `{2}`", content_.size(),
               result.size(), content_);

  llvm::UTF32 target[1];
  const auto* source_cursor =
      reinterpret_cast<const llvm::UTF8*>(result.begin());
  llvm::UTF32* target_cursor = target;
  llvm::ConversionResult conv_result = llvm::ConvertUTF8toUTF32(
      &source_cursor, reinterpret_cast<const llvm::UTF8*>(result.end()),
      &target_cursor, &target[1], llvm::strictConversion);

  switch (conv_result) {
    case llvm::conversionOK: {
      if (target_cursor == target) {
        CARBON_DIAGNOSTIC(CharLiteralEmpty, Error, "empty character literal");
        emitter.Emit(text_.begin(), CharLiteralEmpty);
        return std::nullopt;
      }
      return CharLiteralValue{.value = static_cast<int32_t>(target[0])};
    }
    case llvm::sourceExhausted: {

Review comment (suggestion):
This always performs a heap allocation, which seems unnecessary. All valid cases should fit in a buffer of only a few bytes. Maybe we could use the default `SmallVector` buffer size instead?

(Also would it make sense to use an `llvm::SmallString` given that this is a string buffer?)

Code after:
      }

      // Handle this escape sequence.
      ExpandAndConsumeEscapeSequence(emitter, contents, buffer_cursor);
      buffer_last_escape = buffer_cursor;
    }
  }
}

auto StringLiteral::ComputeCharValue(Diagnostics::Emitter<const char*>& emitter)
    const -> std::optional<CharLiteralValue> {
  CARBON_DCHECK(kind_ == Kind::Char);
  CARBON_DCHECK(is_terminated_);

  if (hash_level_ != 0) {
    CARBON_DIAGNOSTIC(CharLiteralRaw, Error,
                      "unexpected `#` before character literal");
    emitter.Emit(text_.begin(), CharLiteralRaw);
  }

  // Allocate a buffer sized to the content. Note it's possible this could be
  // more efficient/faster with a `ExpandEscapeSequencesAndRemoveIndent`
  // implementation aware of the buffer size, but this is trying to share logic
  // with string expansion.
  llvm::SmallVector<char> buffer;
  buffer.resize_for_overwrite(content_.size());

  auto result = ExpandEscapeSequencesAndRemoveIndent(
      emitter, content_, 0, /*indent=*/llvm::StringRef(), buffer.data());
  CARBON_CHECK(result.size() <= content_.size(),
               "Content grew from {0} to {1}: `{2}`", content_.size(),
               result.size(), content_);

  llvm::UTF32 target[1];
  const auto* source_cursor =
      reinterpret_cast<const llvm::UTF8*>(result.begin());
  llvm::UTF32* target_cursor = target;
  llvm::ConversionResult conv_result = llvm::ConvertUTF8toUTF32(
      &source_cursor, reinterpret_cast<const llvm::UTF8*>(result.end()),
      &target_cursor, std::end(target), llvm::strictConversion);

  switch (conv_result) {
    case llvm::conversionOK: {
      if (target_cursor == target) {
        CARBON_DIAGNOSTIC(CharLiteralEmpty, Error, "empty character literal");
        emitter.Emit(text_.begin(), CharLiteralEmpty);
        return std::nullopt;
      }
      return CharLiteralValue{.value = static_cast<int32_t>(target[0])};
    }
    case llvm::sourceExhausted: {