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
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183

base / strings / sys_string_conversions_apple.mm [blame]

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/strings/sys_string_conversions.h"

#import <Foundation/Foundation.h>
#include <stddef.h>

#include <string_view>
#include <vector>

#include "base/apple/bridging.h"
#include "base/apple/foundation_util.h"
#include "base/apple/scoped_cftyperef.h"
#include "base/numerics/safe_conversions.h"

namespace base {

namespace {

// Converts the supplied CFString into the specified encoding, and returns it as
// a C++ library string of the template type. Returns an empty string on
// failure.
//
// Do not assert in this function since it is used by the assertion code!
template <typename StringType>
StringType CFStringToStringWithEncodingT(CFStringRef cfstring,
                                         CFStringEncoding encoding) {
  CFIndex length = CFStringGetLength(cfstring);
  if (length == 0) {
    return StringType();
  }

  CFRange whole_string = CFRangeMake(0, length);
  CFIndex out_size;
  CFIndex converted = CFStringGetBytes(cfstring, whole_string, encoding,
                                       /*lossByte=*/0,
                                       /*isExternalRepresentation=*/false,
                                       /*buffer=*/nullptr,
                                       /*maxBufLen=*/0, &out_size);
  if (converted == 0 || out_size <= 0) {
    return StringType();
  }

  // `out_size` is the number of UInt8-sized units needed in the destination.
  // A buffer allocated as UInt8 units might not be properly aligned to
  // contain elements of StringType::value_type.  Use a container for the
  // proper value_type, and convert `out_size` by figuring the number of
  // value_type elements per UInt8.  Leave room for a NUL terminator.
  size_t elements = static_cast<size_t>(out_size) * sizeof(UInt8) /
                        sizeof(typename StringType::value_type) +
                    1;

  std::vector<typename StringType::value_type> out_buffer(elements);
  converted =
      CFStringGetBytes(cfstring, whole_string, encoding,
                       /*lossByte=*/0,
                       /*isExternalRepresentation=*/false,
                       reinterpret_cast<UInt8*>(&out_buffer[0]), out_size,
                       /*usedBufLen=*/nullptr);
  if (converted == 0) {
    return StringType();
  }

  out_buffer[elements - 1] = '\0';
  return StringType(&out_buffer[0], elements - 1);
}

// Given a C++ library string `in` with an encoding specified by `in_encoding`,
// converts it to `out_encoding` and returns it as a C++ library string of the
// `OutStringType` template type. Returns an empty string on failure.
//
// Do not assert in this function since it is used by the assertion code!
template <typename InStringType, typename OutStringType>
OutStringType StringToStringWithEncodingsT(const InStringType& in,
                                           CFStringEncoding in_encoding,
                                           CFStringEncoding out_encoding) {
  typename InStringType::size_type in_length = in.length();
  if (in_length == 0) {
    return OutStringType();
  }

  apple::ScopedCFTypeRef<CFStringRef> cfstring(CFStringCreateWithBytesNoCopy(
      kCFAllocatorDefault, reinterpret_cast<const UInt8*>(in.data()),
      checked_cast<CFIndex>(in_length *
                            sizeof(typename InStringType::value_type)),
      in_encoding,
      /*isExternalRepresentation=*/false, kCFAllocatorNull));
  if (!cfstring) {
    return OutStringType();
  }

  return CFStringToStringWithEncodingT<OutStringType>(cfstring.get(),
                                                      out_encoding);
}

// Given a std::string_view `in` with an encoding specified by `in_encoding`,
// returns it as a CFStringRef. Returns null on failure.
template <typename CharT>
apple::ScopedCFTypeRef<CFStringRef> StringPieceToCFStringWithEncodingsT(
    std::basic_string_view<CharT> in,
    CFStringEncoding in_encoding) {
  const auto in_length = in.length();
  if (in_length == 0) {
    return apple::ScopedCFTypeRef<CFStringRef>(CFSTR(""),
                                               base::scoped_policy::RETAIN);
  }

  return apple::ScopedCFTypeRef<CFStringRef>(CFStringCreateWithBytes(
      kCFAllocatorDefault, reinterpret_cast<const UInt8*>(in.data()),
      checked_cast<CFIndex>(in_length * sizeof(CharT)), in_encoding, false));
}

}  // namespace

// The CFStringEncodings used below specify the byte ordering explicitly,
// otherwise CFString will be confused when strings don't carry BOMs, as they
// typically won't.

// Do not assert in this function since it is used by the assertion code!
std::string SysWideToUTF8(const std::wstring& wide) {
  return StringToStringWithEncodingsT<std::wstring, std::string>(
      wide, kCFStringEncodingUTF32LE, kCFStringEncodingUTF8);
}

// Do not assert in this function since it is used by the assertion code!
std::wstring SysUTF8ToWide(std::string_view utf8) {
  return StringToStringWithEncodingsT<std::string_view, std::wstring>(
      utf8, kCFStringEncodingUTF8, kCFStringEncodingUTF32LE);
}

std::string SysWideToNativeMB(const std::wstring& wide) {
  return SysWideToUTF8(wide);
}

std::wstring SysNativeMBToWide(std::string_view native_mb) {
  return SysUTF8ToWide(native_mb);
}

apple::ScopedCFTypeRef<CFStringRef> SysUTF8ToCFStringRef(
    std::string_view utf8) {
  return StringPieceToCFStringWithEncodingsT(utf8, kCFStringEncodingUTF8);
}

apple::ScopedCFTypeRef<CFStringRef> SysUTF16ToCFStringRef(
    std::u16string_view utf16) {
  return StringPieceToCFStringWithEncodingsT(utf16, kCFStringEncodingUTF16LE);
}

NSString* SysUTF8ToNSString(std::string_view utf8) {
  return base::apple::CFToNSOwnershipCast(SysUTF8ToCFStringRef(utf8).release());
}

NSString* SysUTF16ToNSString(std::u16string_view utf16) {
  return base::apple::CFToNSOwnershipCast(
      SysUTF16ToCFStringRef(utf16).release());
}

std::string SysCFStringRefToUTF8(CFStringRef ref) {
  return CFStringToStringWithEncodingT<std::string>(ref, kCFStringEncodingUTF8);
}

std::u16string SysCFStringRefToUTF16(CFStringRef ref) {
  return CFStringToStringWithEncodingT<std::u16string>(
      ref, kCFStringEncodingUTF16LE);
}

std::string SysNSStringToUTF8(NSString* nsstring) {
  if (!nsstring) {
    return std::string();
  }
  return SysCFStringRefToUTF8(apple::NSToCFPtrCast(nsstring));
}

std::u16string SysNSStringToUTF16(NSString* nsstring) {
  if (!nsstring) {
    return std::u16string();
  }
  return SysCFStringRefToUTF16(apple::NSToCFPtrCast(nsstring));
}

}  // namespace base