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