xref: /aosp_15_r20/external/cronet/base/strings/sys_string_conversions_apple.mm (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1// Copyright 2012 The Chromium Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/strings/sys_string_conversions.h"
6
7#import <Foundation/Foundation.h>
8#include <stddef.h>
9
10#include <string_view>
11#include <vector>
12
13#include "base/apple/bridging.h"
14#include "base/apple/foundation_util.h"
15#include "base/apple/scoped_cftyperef.h"
16#include "base/numerics/safe_conversions.h"
17#include "base/strings/string_piece.h"
18
19namespace base {
20
21namespace {
22
23// Converts the supplied CFString into the specified encoding, and returns it as
24// a C++ library string of the template type. Returns an empty string on
25// failure.
26//
27// Do not assert in this function since it is used by the assertion code!
28template <typename StringType>
29StringType CFStringToStringWithEncodingT(CFStringRef cfstring,
30                                         CFStringEncoding encoding) {
31  CFIndex length = CFStringGetLength(cfstring);
32  if (length == 0) {
33    return StringType();
34  }
35
36  CFRange whole_string = CFRangeMake(0, length);
37  CFIndex out_size;
38  CFIndex converted = CFStringGetBytes(cfstring, whole_string, encoding,
39                                       /*lossByte=*/0,
40                                       /*isExternalRepresentation=*/false,
41                                       /*buffer=*/nullptr,
42                                       /*maxBufLen=*/0, &out_size);
43  if (converted == 0 || out_size <= 0) {
44    return StringType();
45  }
46
47  // `out_size` is the number of UInt8-sized units needed in the destination.
48  // A buffer allocated as UInt8 units might not be properly aligned to
49  // contain elements of StringType::value_type.  Use a container for the
50  // proper value_type, and convert `out_size` by figuring the number of
51  // value_type elements per UInt8.  Leave room for a NUL terminator.
52  size_t elements = static_cast<size_t>(out_size) * sizeof(UInt8) /
53                        sizeof(typename StringType::value_type) +
54                    1;
55
56  std::vector<typename StringType::value_type> out_buffer(elements);
57  converted =
58      CFStringGetBytes(cfstring, whole_string, encoding,
59                       /*lossByte=*/0,
60                       /*isExternalRepresentation=*/false,
61                       reinterpret_cast<UInt8*>(&out_buffer[0]), out_size,
62                       /*usedBufLen=*/nullptr);
63  if (converted == 0) {
64    return StringType();
65  }
66
67  out_buffer[elements - 1] = '\0';
68  return StringType(&out_buffer[0], elements - 1);
69}
70
71// Given a C++ library string `in` with an encoding specified by `in_encoding`,
72// converts it to `out_encoding` and returns it as a C++ library string of the
73// `OutStringType` template type. Returns an empty string on failure.
74//
75// Do not assert in this function since it is used by the assertion code!
76template <typename InStringType, typename OutStringType>
77OutStringType StringToStringWithEncodingsT(const InStringType& in,
78                                           CFStringEncoding in_encoding,
79                                           CFStringEncoding out_encoding) {
80  typename InStringType::size_type in_length = in.length();
81  if (in_length == 0) {
82    return OutStringType();
83  }
84
85  apple::ScopedCFTypeRef<CFStringRef> cfstring(CFStringCreateWithBytesNoCopy(
86      kCFAllocatorDefault, reinterpret_cast<const UInt8*>(in.data()),
87      checked_cast<CFIndex>(in_length *
88                            sizeof(typename InStringType::value_type)),
89      in_encoding,
90      /*isExternalRepresentation=*/false, kCFAllocatorNull));
91  if (!cfstring) {
92    return OutStringType();
93  }
94
95  return CFStringToStringWithEncodingT<OutStringType>(cfstring.get(),
96                                                      out_encoding);
97}
98
99// Given a StringPiece `in` with an encoding specified by `in_encoding`, returns
100// it as a CFStringRef. Returns null on failure.
101template <typename CharT>
102apple::ScopedCFTypeRef<CFStringRef> StringPieceToCFStringWithEncodingsT(
103    std::basic_string_view<CharT> in,
104    CFStringEncoding in_encoding) {
105  const auto in_length = in.length();
106  if (in_length == 0) {
107    return apple::ScopedCFTypeRef<CFStringRef>(CFSTR(""),
108                                               base::scoped_policy::RETAIN);
109  }
110
111  return apple::ScopedCFTypeRef<CFStringRef>(CFStringCreateWithBytes(
112      kCFAllocatorDefault, reinterpret_cast<const UInt8*>(in.data()),
113      checked_cast<CFIndex>(in_length * sizeof(CharT)), in_encoding, false));
114}
115
116}  // namespace
117
118// The CFStringEncodings used below specify the byte ordering explicitly,
119// otherwise CFString will be confused when strings don't carry BOMs, as they
120// typically won't.
121
122// Do not assert in this function since it is used by the assertion code!
123std::string SysWideToUTF8(const std::wstring& wide) {
124  return StringToStringWithEncodingsT<std::wstring, std::string>(
125      wide, kCFStringEncodingUTF32LE, kCFStringEncodingUTF8);
126}
127
128// Do not assert in this function since it is used by the assertion code!
129std::wstring SysUTF8ToWide(StringPiece utf8) {
130  return StringToStringWithEncodingsT<StringPiece, std::wstring>(
131      utf8, kCFStringEncodingUTF8, kCFStringEncodingUTF32LE);
132}
133
134std::string SysWideToNativeMB(const std::wstring& wide) {
135  return SysWideToUTF8(wide);
136}
137
138std::wstring SysNativeMBToWide(StringPiece native_mb) {
139  return SysUTF8ToWide(native_mb);
140}
141
142apple::ScopedCFTypeRef<CFStringRef> SysUTF8ToCFStringRef(StringPiece utf8) {
143  return StringPieceToCFStringWithEncodingsT(utf8, kCFStringEncodingUTF8);
144}
145
146apple::ScopedCFTypeRef<CFStringRef> SysUTF16ToCFStringRef(StringPiece16 utf16) {
147  return StringPieceToCFStringWithEncodingsT(utf16, kCFStringEncodingUTF16LE);
148}
149
150NSString* SysUTF8ToNSString(StringPiece utf8) {
151  return base::apple::CFToNSOwnershipCast(SysUTF8ToCFStringRef(utf8).release());
152}
153
154NSString* SysUTF16ToNSString(StringPiece16 utf16) {
155  return base::apple::CFToNSOwnershipCast(
156      SysUTF16ToCFStringRef(utf16).release());
157}
158
159std::string SysCFStringRefToUTF8(CFStringRef ref) {
160  return CFStringToStringWithEncodingT<std::string>(ref, kCFStringEncodingUTF8);
161}
162
163std::u16string SysCFStringRefToUTF16(CFStringRef ref) {
164  return CFStringToStringWithEncodingT<std::u16string>(
165      ref, kCFStringEncodingUTF16LE);
166}
167
168std::string SysNSStringToUTF8(NSString* nsstring) {
169  if (!nsstring) {
170    return std::string();
171  }
172  return SysCFStringRefToUTF8(apple::NSToCFPtrCast(nsstring));
173}
174
175std::u16string SysNSStringToUTF16(NSString* nsstring) {
176  if (!nsstring) {
177    return std::u16string();
178  }
179  return SysCFStringRefToUTF16(apple::NSToCFPtrCast(nsstring));
180}
181
182}  // namespace base
183