//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import SwiftShims

/// Effectively an untyped NSString that doesn't require foundation.
@usableFromInline
internal typealias _CocoaString = AnyObject

#if _runtime(_ObjC)

// Swift's String bridges NSString via this protocol and these
// variables, allowing the core stdlib to remain decoupled from
// Foundation.

@objc private protocol _StringSelectorHolder : _NSCopying {

  @objc var length: Int { get }

  @objc var hash: UInt { get }

  @objc(characterAtIndex:)
  func character(at offset: Int) -> UInt16

  @objc(getCharacters:range:)
  func getCharacters(
   _ buffer: UnsafeMutablePointer<UInt16>, range aRange: _SwiftNSRange
  )

  @objc(_fastCStringContents:)
  func _fastCStringContents(
    _ requiresNulTermination: Int8
  ) -> UnsafePointer<CChar>?

  @objc(_fastCharacterContents)
  func _fastCharacterContents() -> UnsafePointer<UInt16>?

  @objc(getBytes:maxLength:usedLength:encoding:options:range:remainingRange:)
  func getBytes(_ buffer: UnsafeMutableRawPointer?,
   maxLength maxBufferCount: Int,
  usedLength usedBufferCount: UnsafeMutablePointer<Int>?,
    encoding: UInt,
     options: UInt,
       range: _SwiftNSRange,
       remaining leftover: UnsafeMutablePointer<_SwiftNSRange>?) -> Int8

  @objc(compare:options:range:locale:)
  func compare(_ string: _CocoaString,
               options: UInt,
               range: _SwiftNSRange,
               locale: AnyObject?) -> Int

  @objc(newTaggedNSStringWithASCIIBytes_:length_:)
  func createTaggedString(bytes: UnsafePointer<UInt8>,
                          count: Int) -> AnyObject?
  
  @objc(isNSString__)
  func getIsNSString() -> Int8
  
  @objc(_isEqualToBytes:count:encoding:)
  func isEqualToBytes(
    _ bytes: UnsafeRawPointer,
    count: Int,
    encoding: UInt
  ) -> Int8
}

/*
 Passing a _CocoaString through _objc() lets you call ObjC methods that the
 compiler doesn't know about, via the protocol above. In order to get good
 performance, you need a double indirection like this:

  func a -> _objc -> func a'

 because any refcounting @_effects on 'a' will be lost when _objc breaks ARC's
 knowledge that the _CocoaString and _StringSelectorHolder are the same object.
 */
@inline(__always)
private func _objc(_ str: _CocoaString) -> _StringSelectorHolder {
  return unsafe unsafeBitCast(str, to: _StringSelectorHolder.self)
}

@_effects(releasenone)
private func _copyNSString(_ str: _StringSelectorHolder) -> _CocoaString {
  return unsafe str.copy(with: nil)
}

@usableFromInline // @testable
@_effects(releasenone)
internal func _stdlib_binary_CFStringCreateCopy(
  _ source: _CocoaString
) -> _CocoaString {
  return _copyNSString(_objc(source))
}

@_effects(readonly)
private func _NSStringLen(_ str: _StringSelectorHolder) -> Int {
  return str.length
}

@usableFromInline // @testable
@_effects(readonly)
internal func _stdlib_binary_CFStringGetLength(
  _ source: _CocoaString
) -> Int {
  return _NSStringLen(_objc(source))
}

@_effects(readonly) private func isNSStringImpl(
  _ str: _StringSelectorHolder
) -> Bool {
  return str.getIsNSString() != 0
}

@_effects(readonly)
internal func _isNSString(_ str:AnyObject) -> Bool {
  return isNSStringImpl(_objc(str))
}

@_effects(readonly)
private func _NSStringIsEqualToBytesImpl(
  _ str: _CocoaString,
  _ bytes: UnsafeRawPointer,
  _ count: Int,
  _ encoding: UInt
) -> Int8 {
  return unsafe str.isEqualToBytes(bytes, count: count, encoding: encoding)
}

@_effects(readonly)
internal func _NSStringIsEqualToBytes(
  _ str: AnyObject,
  bytes: UnsafeRawPointer,
  count: Int,
  encoding: UInt
) -> Int8 {
  return unsafe _NSStringIsEqualToBytesImpl(
    _objc(str),
    bytes,
    count,
    encoding
  )
}

@_effects(readonly)
private func _NSStringCharactersPtr(_ str: _StringSelectorHolder) -> UnsafeMutablePointer<UTF16.CodeUnit>? {
  return unsafe UnsafeMutablePointer(mutating: str._fastCharacterContents())
}

private func _stdlib_binary_createIndirectTaggedPointerNSString(
  ptr: UnsafePointer<UInt8>,
  count: Int
) -> UnsafeRawPointer? {
  return unsafe _swift_stdlib_CreateIndirectTaggedPointerString(ptr, count);
}

@usableFromInline // @testable
@_effects(readonly)
internal func _stdlib_binary_CFStringGetCharactersPtr(
  _ source: _CocoaString
) -> UnsafeMutablePointer<UTF16.CodeUnit>? {
  return unsafe _NSStringCharactersPtr(_objc(source))
}

@_effects(releasenone)
private func _NSStringGetCharacters(
  from source: _StringSelectorHolder,
  range: Range<Int>,
  into destination: UnsafeMutablePointer<UTF16.CodeUnit>
) {
  unsafe source.getCharacters(destination, range: _SwiftNSRange(
    location: range.startIndex,
    length: range.count)
  )
}

/// Copies a slice of a _CocoaString into contiguous storage of sufficient
/// capacity.
@_effects(releasenone)
internal func _cocoaStringCopyCharacters(
  from source: _CocoaString,
  range: Range<Int>,
  into destination: UnsafeMutablePointer<UTF16.CodeUnit>
) {
  unsafe _NSStringGetCharacters(from: _objc(source), range: range, into: destination)
}

@_effects(readonly)
private func _NSStringGetCharacter(
  _ target: _StringSelectorHolder, _ position: Int
) -> UTF16.CodeUnit {
  return target.character(at: position)
}

@_effects(readonly)
internal func _cocoaStringSubscript(
  _ target: _CocoaString, _ position: Int
) -> UTF16.CodeUnit {
  return _NSStringGetCharacter(_objc(target), position)
}

@_effects(releasenone)
private func _NSStringCopyBytes(
  _ o: _StringSelectorHolder,
  encoding: UInt,
  into bufPtr: UnsafeMutableRawBufferPointer,
  options: UInt,
  UTF16Range: Range<Int>? = nil,
  remainingRange: UnsafeMutablePointer<_SwiftNSRange>? = nil
) -> Int? {
  let ptr = unsafe bufPtr.baseAddress._unsafelyUnwrappedUnchecked
  var tmpRemainingRange = _SwiftNSRange(location: 0, length: 0)
  var usedLen = 0
  let range = if let UTF16Range {
    _SwiftNSRange(location: UTF16Range.lowerBound, length: UTF16Range.count)
  } else {
    _SwiftNSRange(location: 0, length: o.length)
  }
  let success = unsafe 0 != o.getBytes(
    ptr,
    maxLength: bufPtr.count,
    usedLength: &usedLen,
    encoding: encoding,
    options: options,
    range: range,
    remaining: &tmpRemainingRange
  )
  if success {
    if let remainingRange = unsafe remainingRange {
      // Defensive: if getBytes claimed success but made no progress against a
      // non-empty input range, treat as failure so chunked callers can't loop
      // forever on a pathological NSString subclass.
      if usedLen == 0 && tmpRemainingRange.length == range.length && range.length != 0 {
        return nil
      }
      unsafe remainingRange.pointee = tmpRemainingRange
      return usedLen
    } else if tmpRemainingRange.length == 0 {
      return usedLen
    }
  }
  return nil
}

@_effects(releasenone)
internal func _cocoaStringCopyUTF8(
  _ target: _CocoaString,
  into bufPtr: UnsafeMutableRawBufferPointer,
  options: UInt = 0
) -> Int? {
  return unsafe _NSStringCopyBytes(
    _objc(target),
    encoding: _cocoaUTF8Encoding,
    into: bufPtr,
    options: options
  )
}

@_effects(releasenone)
internal func _cocoaStringCopyASCII(
  _ target: _CocoaString,
  into bufPtr: UnsafeMutableRawBufferPointer,
  options: UInt = 0
) -> Int? {
  return unsafe _NSStringCopyBytes(
    _objc(target),
    encoding: _cocoaASCIIEncoding,
    into: bufPtr,
    options: options
  )
}

@_effects(releasenone)
internal func _cocoaStringCopyBytes(
  _ target: _CocoaString,
  encoding: UInt,
  into bufPtr: UnsafeMutableRawBufferPointer,
  options: UInt = 0,
  UTF16Range: Range<Int>,
  remainingRange: inout Range<Int>
) -> Int? {
  var cocoaRemainingRange = _SwiftNSRange(location: 0, length: 0)
  defer {
    remainingRange = cocoaRemainingRange.location ..< cocoaRemainingRange.location + cocoaRemainingRange.length
  }
  return withUnsafeMutablePointer(
    to: &cocoaRemainingRange
  ) { remainingPtr in
    return unsafe _NSStringCopyBytes(
      _objc(target),
      encoding: encoding,
      into: bufPtr,
      options: options,
      UTF16Range: UTF16Range,
      remainingRange: remainingPtr
    )
  }
}

@_effects(readonly)
private func _NSStringUTF8Count(
  _ o: _StringSelectorHolder,
  range: Range<Int>
) -> Int? {
  var remainingRange = _SwiftNSRange(location: 0, length: 0)
  var usedLen = 0
  let success = unsafe 0 != o.getBytes(
    UnsafeMutableRawPointer(Builtin.inttoptr_Word(0._builtinWordValue)),
    maxLength: 0,
    usedLength: &usedLen,
    encoding: _cocoaUTF8Encoding,
    options: 0,
    range: _SwiftNSRange(location: range.startIndex, length: range.count),
    remaining: &remainingRange
  )
  if success && remainingRange.length == 0 {
    return usedLen
  }
  return nil
}

@_effects(readonly)
internal func _cocoaStringUTF8Count(
  _ target: _CocoaString,
  range: Range<Int>
) -> Int? {
  if range.isEmpty { return 0 }
  return _NSStringUTF8Count(_objc(target), range: range)
}

@_effects(readonly)
private func _NSStringCompare(
  _ o: _StringSelectorHolder, _ other: _CocoaString
) -> Int {
  let range = _SwiftNSRange(location: 0, length: o.length)
  let options = UInt(2) /* NSLiteralSearch*/
  return o.compare(other, options: options, range: range, locale: nil)
}

@_effects(readonly)
internal func _cocoaStringCompare(
  _ string: _CocoaString, _ other: _CocoaString
) -> Int {
  return _NSStringCompare(_objc(string), other)
}

@_effects(readonly)
internal func _cocoaHashString(
  _ string: _CocoaString
) -> UInt {
  return _swift_stdlib_CFStringHashNSString(string)
}

@_effects(readonly)
internal func _cocoaHashASCIIBytes(
  _ bytes: UnsafePointer<UInt8>, length: Int
) -> UInt {
  return unsafe _swift_stdlib_CFStringHashCString(bytes, length)
}

// These "trampolines" are effectively objc_msgSend_super.
// They bypass our implementations to use NSString's.

@_effects(readonly)
internal func _cocoaCStringUsingEncodingTrampoline(
  _ string: _CocoaString, _ encoding: UInt
) -> UnsafePointer<UInt8>? {
  return unsafe _swift_stdlib_NSStringCStringUsingEncodingTrampoline(string, encoding)
}

@_effects(readonly)
internal func _cocoaLengthOfBytesInEncodingTrampoline(
  _ string: _CocoaString, _ encoding: UInt
) -> UInt {
  return _swift_stdlib_NSStringLengthOfBytesInEncodingTrampoline(string, encoding)
}

@_effects(releasenone)
internal func _cocoaGetCStringTrampoline(
  _ string: _CocoaString,
  _ buffer: UnsafeMutablePointer<UInt8>,
  _ maxLength: Int,
  _ encoding: UInt
) -> Int8 {
  return unsafe Int8(_swift_stdlib_NSStringGetCStringTrampoline(
    string, buffer, maxLength, encoding))
}

//
// Conversion from NSString to Swift's native representation.
//

private var kCFStringEncodingASCII: _swift_shims_CFStringEncoding {
  @inline(__always) get { 0x0600 }
}

private var kCFStringEncodingUTF8: _swift_shims_CFStringEncoding {
  @inline(__always) get { 0x8000100 }
}

private var kCFStringEncodingUTF16: _swift_shims_CFStringEncoding {
  @inline(__always) get { 0x0100 }
}

internal enum _KnownCocoaString {
  case storage
  case shared
  case cocoa
#if _pointerBitWidth(_64)
  case tagged
#endif

  @inline(__always)
  init(_ str: _CocoaString) {

#if _pointerBitWidth(_64)
    if _isObjCTaggedPointer(str) {
      self = .tagged
      return
    }
#endif

    switch unsafe unsafeBitCast(_swift_classOfObjCHeapObject(str), to: UInt.self) {
    case unsafe unsafeBitCast(__StringStorage.self, to: UInt.self):
      self = .storage
    case unsafe unsafeBitCast(__SharedStringStorage.self, to: UInt.self):
      self = .shared
    default:
      self = .cocoa
    }
  }
}

#if _pointerBitWidth(_64)
// Resiliently write a tagged _CocoaString's contents into a buffer.
// The Foundation overlay takes care of bridging tagged pointer strings before
// they reach us, but this may still be called by older code, or by strings
// entering our domain via the arguments to -isEqual:, etc...
@_effects(releasenone) // @opaque
internal func _bridgeTagged(
  _ cocoa: _CocoaString,
  intoUTF8 bufPtr: UnsafeMutableRawBufferPointer
) -> Int? {
  _internalInvariant(_isObjCTaggedPointer(cocoa))
  return unsafe _cocoaStringCopyUTF8(cocoa, into: bufPtr)
}

@_effects(releasenone) // @opaque
internal func _bridgeTaggedASCII(
  _ cocoa: _CocoaString,
  intoUTF8 bufPtr: UnsafeMutableRawBufferPointer
) -> Int? {
  _internalInvariant(_isObjCTaggedPointer(cocoa))
  return unsafe _cocoaStringCopyASCII(cocoa, into: bufPtr)
}
#endif

@_effects(readonly)
private func _NSStringASCIIPointer(_ str: _StringSelectorHolder) -> UnsafePointer<UInt8>? {
  //TODO(String bridging): Unconditionally asking for nul-terminated contents is
  // overly conservative and hurts perf with some NSStrings
  return unsafe str._fastCStringContents(1)?._asUInt8
}

@_effects(readonly)
private func _NSStringUTF8Pointer(_ str: _StringSelectorHolder) -> UnsafePointer<UInt8>? {
  //We don't have a way to ask for UTF8 here currently
  return unsafe _NSStringASCIIPointer(str)
}

@_effects(readonly)
internal func _getNSCFConstantStringContentsPointer(
  _ cocoa: AnyObject
) -> UnsafePointer<UInt8> {
  return unsafe unsafeBitCast(
    cocoa,
    to: UnsafePointer<_swift_shims_builtin_CFString>.self
  ).pointee.str
}

@_effects(readonly) // @opaque
private func _withCocoaASCIIPointer<R>(
  _ str: _CocoaString,
  requireStableAddress: Bool,
  work: (UnsafePointer<UInt8>) -> R?
) -> R? {
  #if _pointerBitWidth(_64)
  if _isObjCTaggedPointer(str) {
    if requireStableAddress {
      return nil // tagged pointer strings don't support _fastCStringContents
    }
    if let smol = _SmallString(taggedASCIICocoa: str) {
      return _StringGuts(smol).withFastUTF8 {
        unsafe work($0.baseAddress._unsafelyUnwrappedUnchecked)
      }
    }
  }
  #endif
  defer { _fixLifetime(str) }
  if let ptr = unsafe _NSStringASCIIPointer(_objc(str)) {
    return unsafe work(ptr)
  }
  return nil
}

// Inline to make sure the optimizer can see through the closure
@_effects(readonly) @inline(__always)
private func _withCocoaUTF8Pointer<R>(
  _ str: _CocoaString,
  requireStableAddress: Bool,
  work: (UnsafePointer<UInt8>) -> R?
) -> R? {
  #if _pointerBitWidth(_64)
  if _isObjCTaggedPointer(str) {
    if requireStableAddress {
      return nil // tagged pointer strings don't support _fastCStringContents
    }
    if let smol = _SmallString(taggedCocoa: str) {
      return _StringGuts(smol).withFastUTF8 {
        unsafe work($0.baseAddress._unsafelyUnwrappedUnchecked)
      }
    }
  }
  #endif
  defer { _fixLifetime(str) }
  if let ptr = unsafe _NSStringUTF8Pointer(_objc(str)) {
    return unsafe work(ptr)
  }
  return nil
}

@_effects(readonly)
internal func withCocoaASCIIPointer<R>(
  _ str: _CocoaString,
  work: (UnsafePointer<UInt8>) -> R?
) -> R? {
  return unsafe _withCocoaASCIIPointer(str, requireStableAddress: false, work: work)
}

@_effects(readonly)
internal func withCocoaUTF8Pointer<R>(
  _ str: _CocoaString,
  work: (UnsafePointer<UInt8>) -> R?
) -> R? {
  return unsafe _withCocoaUTF8Pointer(str, requireStableAddress: false, work: work)
}

@_effects(readonly)
internal func stableCocoaASCIIPointer(_ str: _CocoaString)
  -> UnsafePointer<UInt8>? {
  return unsafe _withCocoaASCIIPointer(str, requireStableAddress: true, work: { unsafe $0 })
}

@_effects(readonly)
internal func stableCocoaUTF8Pointer(_ str: _CocoaString)
  -> UnsafePointer<UInt8>? {
  return unsafe _withCocoaUTF8Pointer(str, requireStableAddress: true, work: { unsafe $0 })
}

@unsafe
private enum CocoaStringPointer {
  case ascii(UnsafePointer<UInt8>)
  case utf8(UnsafePointer<UInt8>)
  case utf16(UnsafePointer<UInt16>)
  case none
}

@_effects(readonly)
private func _getCocoaStringPointer(
  _ cfImmutableValue: _CocoaString
) -> CocoaStringPointer {
  if let ascii = unsafe stableCocoaASCIIPointer(cfImmutableValue) {
    return unsafe .ascii(ascii)
  }
  // We could ask for UTF16 here via _stdlib_binary_CFStringGetCharactersPtr,
  // but we currently have no use for it
  return unsafe .none
}

#if !$Embedded
@usableFromInline
@_effects(releasenone) // @opaque
internal func _bridgeCocoaString(_ cocoaString: _CocoaString) -> _StringGuts {
  switch _KnownCocoaString(cocoaString) {
  case .storage:
    return unsafe _unsafeUncheckedDowncast(
      cocoaString, to: __StringStorage.self).asString._guts
  case .shared:
    return unsafe _unsafeUncheckedDowncast(
      cocoaString, to: __SharedStringStorage.self).asString._guts
#if _pointerBitWidth(_64)
  case .tagged:
    // Foundation should be taking care of tagged pointer strings before they
    // reach here, so the only ones reaching this point should be back deployed,
    // which will never have tagged pointer strings that aren't small, hence
    // the force unwrap here.
    return _StringGuts(_SmallString(taggedCocoa: cocoaString)!)
#endif
  case .cocoa:
    // "Copy" it into a value to be sure nobody will modify behind
    // our backs. In practice, when value is already immutable, this
    // just does a retain.
    //
    // TODO: Only in certain circumstances should we emit this call:
    //   1) If it's immutable, just retain it.
    //   2) If it's mutable with no associated information, then a copy must
    //      happen; might as well eagerly bridge it in.
    //   3) If it's mutable with associated information, must make the call
    let immutableCopy
      = _stdlib_binary_CFStringCreateCopy(cocoaString)

#if _pointerBitWidth(_64)
    if _isObjCTaggedPointer(immutableCopy) {
      // Copying a tagged pointer can produce a tagged pointer, but only if it's
      // small enough to definitely fit in a _SmallString
      return unsafe _StringGuts(
        _SmallString(taggedCocoa: immutableCopy).unsafelyUnwrapped
      )
    }
#endif

    let (fastUTF8, isASCII): (Bool, Bool)
    switch unsafe _getCocoaStringPointer(immutableCopy) {
    case .ascii(_): (fastUTF8, isASCII) = (true, true)
    case .utf8(_): (fastUTF8, isASCII) = (true, false)
    default:  (fastUTF8, isASCII) = (false, false)
    }
    let length = _stdlib_binary_CFStringGetLength(immutableCopy)

    return _StringGuts(
      cocoa: immutableCopy,
      providesFastUTF8: fastUTF8,
      isASCII: isASCII,
      length: length)
  }
}

extension String {
  @available(SwiftStdlib 6.1, *)
  @_spi(Foundation)
  public init<Encoding: Unicode.Encoding>(
    _immortalCocoaString: AnyObject,
    count: Int,
    encoding: Encoding.Type
  ) {
    if encoding == Unicode.ASCII.self || encoding == Unicode.UTF8.self {
      self._guts = _StringGuts(
        constantCocoa: _immortalCocoaString,
        providesFastUTF8: true,
        isASCII: encoding == Unicode.ASCII.self,
        length: count)
    } else {
      _precondition(encoding == Unicode.UTF16.self)
      // Only need the very last bit of _bridgeCocoaString here,
      // since we know the fast paths don't apply
      self._guts = _StringGuts(
        cocoa: _immortalCocoaString,
        providesFastUTF8: false,
        isASCII: false,
        length: count)
    }
  }
  
  @_spi(Foundation)
  public init(_cocoaString: AnyObject) {
    self._guts = _bridgeCocoaString(_cocoaString)
  }
}
#endif

@_effects(releasenone)
private func _createNSString(
  _ receiver: _StringSelectorHolder,
  _ ptr: UnsafePointer<UInt8>,
  _ count: Int,
  _ encoding: UInt32
) -> AnyObject? {
  return unsafe receiver.createTaggedString(bytes: ptr, count: count)
}

@_effects(releasenone)
private func _createCFString(
  _ ptr: UnsafePointer<UInt8>,
  _ count: Int,
  _ encoding: UInt32
) -> AnyObject? {
  return unsafe _createNSString(
    unsafeBitCast(__StringStorage.self as AnyClass, to: _StringSelectorHolder.self),
    ptr,
    count,
    encoding
  )
}

#if !$Embedded
extension String {
  @_effects(releasenone)
  public // SPI(Foundation)
  func _bridgeToObjectiveCImpl() -> AnyObject {

    _connectOrphanedFoundationSubclassesIfNeeded()

    if _guts.isSmall {
      return _guts.asSmall.withUTF8 { bufPtr in
        // Smol ASCII a) may bridge to tagged pointers, b) can't contain a BOM
        if _guts.isSmallASCII,
           bufPtr.count <= 11, //tagged pointers can't fit >11 characters
           let result = unsafe _createCFString(
            bufPtr.baseAddress._unsafelyUnwrappedUnchecked,
            bufPtr.count,
            kCFStringEncodingUTF8
           ) {
          return result
        }
        // We can't form a tagged pointer String, so make a non-small String,
        // and bridge that instead. Also avoids CF deleting any BOM that may be
        // present
        return unsafe __StringStorage.create(
          initializingFrom: bufPtr,
          codeUnitCapacity: bufPtr.count,
          isASCII: _guts.isSmallASCII)
      }
    }
    if _guts._object.isImmortal && !_guts._object.largeFastIsConstantCocoa {
      if _guts.isASCII && _guts._object.isFastZeroTerminated {
        let ptr = unsafe _guts._object.fastUTF8.baseAddress._unsafelyUnwrappedUnchecked
        let count = _guts.count
        if let indirect = unsafe _stdlib_binary_createIndirectTaggedPointerNSString(
          ptr: ptr, count: count
        ) {
          return unsafe unsafeBitCast(indirect, to: AnyObject.self)
        }
      }
      let gutsCountAndFlags = _guts._object._countAndFlags
      return unsafe __SharedStringStorage(
        immortal: _guts._object.fastUTF8.baseAddress!,
        countAndFlags: _StringObject.CountAndFlags(
          sharedCount: _guts.count, isASCII: gutsCountAndFlags.isASCII))
    }

    _internalInvariant(_guts._object.hasObjCBridgeableObject,
      "Unknown non-bridgeable object case")
    return _guts._object.objCBridgeableObject
  }
}

// Note: This function is not intended to be called from Swift.  The
// availability information here is perfunctory; this function isn't considered
// part of the Stdlib's Swift ABI.
@available(SwiftStdlib 6.4, *)
@_cdecl("_SwiftCreateBridgedString")
@usableFromInline
internal func _SwiftCreateBridgedString_DoNotCall(
  bytes: UnsafePointer<UInt8>,
  length: Int,
  encoding: _swift_shims_CFStringEncoding
) -> Unmanaged<AnyObject>? {
  let bufPtr = unsafe UnsafeBufferPointer(start: bytes, count: length)
  let str:String?
  switch encoding {
  case kCFStringEncodingUTF8:
    str = unsafe String(validating: bufPtr, as: Unicode.UTF8.self)
  case kCFStringEncodingASCII:
    str = unsafe String(validating: bufPtr, as: Unicode.ASCII.self)
  case kCFStringEncodingUTF16:
    str = unsafe bufPtr.withMemoryRebound(to: UInt16.self) {
      unsafe String(validating: $0, as: Unicode.UTF16.self)
    }
  default:
    fatalError("Unsupported encoding in shim")
  }
  if let str {
    return unsafe Unmanaged<AnyObject>.passRetained(str._bridgeToObjectiveCImpl())
  }
  return nil
}

@available(SwiftStdlib 6.1, *)
@_spi(Foundation) public func _SwiftCreateImmortalString_ForFoundation(
  buffer: UnsafeBufferPointer<UInt8>,
  isASCII: Bool
) -> String? {
  if buffer.count == 0 {
    return ""
  }
  // Foundation promises to null-terminate all buffers it passes to this SPI
  // So reading one-past-the-end to verify that will only crash on invalid
  let bufferWithTerminator = unsafe UnsafeBufferPointer(
    start: buffer.baseAddress._unsafelyUnwrappedUnchecked,
    count: buffer.count + 1
  )
  unsafe _precondition(bufferWithTerminator[buffer.count] == 0,
    "String buffers must be NUL-terminated")
  if isASCII {
    return unsafe _allASCII(buffer) ?
      String(_StringGuts(buffer, isASCII: true)) :
      nil
  }
  switch unsafe validateUTF8(buffer) {
  case .success(let extraInfo):
    return unsafe String(_StringGuts(buffer, isASCII: extraInfo.isASCII))
  default:
    return nil
  }
}

// At runtime, this class is derived from `__SwiftNativeNSStringBase`,
// which is derived from `NSString`.
//
// The @_swift_native_objc_runtime_base attribute
// This allows us to subclass an Objective-C class and use the fast Swift
// memory allocator.
@objc @_swift_native_objc_runtime_base(__SwiftNativeNSStringBase)
@_spi(Foundation) public class __SwiftNativeNSString {
  @objc internal init() {}
  deinit {}
}

@available(*, unavailable)
extension __SwiftNativeNSString: Sendable {}

// Called by the SwiftObject implementation to get the description of a value
// as an NSString.
@_silgen_name("swift_stdlib_getDescription")
public func _getDescription<T>(_ x: T) -> AnyObject {
  return String(reflecting: x)._bridgeToObjectiveCImpl()
}

@_silgen_name("swift_stdlib_NSStringFromUTF8")
@usableFromInline //this makes the symbol available to the runtime :(
@available(SwiftStdlib 5.2, *)
internal func _NSStringFromUTF8(_ s: UnsafePointer<UInt8>, _ len: Int)
  -> AnyObject {
  return unsafe String(
    decoding: UnsafeBufferPointer(start: s, count: len),
    as: UTF8.self
  )._bridgeToObjectiveCImpl()
}
#endif

#else // !_runtime(_ObjC)

internal class __SwiftNativeNSString {
  internal init() {}
  deinit {}
}

#endif

// Special-case Index <-> Offset converters for bridging and use in accelerating
// the UTF16View in general.
extension StringProtocol {
  @_specialize(where Self == String)
  @_specialize(where Self == Substring)
  public // SPI(Foundation)
  func _toUTF16Offset(_ idx: Index) -> Int {
    return self.utf16.distance(from: self.utf16.startIndex, to: idx)
  }

  @_specialize(where Self == String)
  @_specialize(where Self == Substring)
  public // SPI(Foundation)
  func _toUTF16Index(_ offset: Int) -> Index {
    return self.utf16.index(self.utf16.startIndex, offsetBy: offset)
  }

  @_specialize(where Self == String)
  @_specialize(where Self == Substring)
  public // SPI(Foundation)
  func _toUTF16Offsets(_ indices: Range<Index>) -> Range<Int> {
    if Self.self == String.self {
      let s = unsafe unsafeBitCast(self, to: String.self)
      return s.utf16._offsetRange(for: indices, from: s.startIndex)
    }
    if Self.self == Substring.self {
      let s = unsafe unsafeBitCast(self, to: Substring.self)
      return s._slice._base.utf16._offsetRange(for: indices, from: s.startIndex)
    }
    let startOffset = _toUTF16Offset(indices.lowerBound)
    let endOffset = _toUTF16Offset(indices.upperBound)
    return unsafe Range(uncheckedBounds: (lower: startOffset, upper: endOffset))
  }

  @_specialize(where Self == String)
  @_specialize(where Self == Substring)
  public // SPI(Foundation)
  func _toUTF16Indices(_ range: Range<Int>) -> Range<Index> {
#if hasFeature(Macros)
    if Self.self == String.self {
      let s = unsafe unsafeBitCast(self, to: String.self)
      return s.utf16._indexRange(for: range, from: s.startIndex)
    }
    if Self.self == Substring.self {
      let s = unsafe unsafeBitCast(self, to: Substring.self)
      return s._slice._base.utf16._indexRange(for: range, from: s.startIndex)
    }
#endif
    let lowerbound = _toUTF16Index(range.lowerBound)
    let upperbound = _toUTF16Index(range.upperBound)
    return unsafe Range(uncheckedBounds: (lower: lowerbound, upper: upperbound))
  }
}

extension String {
  public // @testable / @benchmarkable
  func _copyUTF16CodeUnits(
    into buffer: UnsafeMutableBufferPointer<UInt16>,
    range: Range<Int>
  ) {
    _internalInvariant(buffer.count >= range.count)
    let indexRange = self._toUTF16Indices(range)
    unsafe self.utf16._nativeCopy(into: buffer, alignedRange: indexRange)
  }
}
