blob: 999fac1a64a4c17280ded4a94df90fc73346f5a0 [file] [log] [blame]
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 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
//
//===----------------------------------------------------------------------===//
// Declarations to enable ease-of-testing
public // @testable
struct _StringRepresentation {
public var _isASCII: Bool
public var _count: Int
public var _capacity: Int
public enum _Form {
case _small
case _cocoa(object: AnyObject)
case _native(object: AnyObject)
case _immortal(address: UInt)
// TODO: shared native
}
public var _form: _Form
public var _objectIdentifier: ObjectIdentifier? {
switch _form {
case ._cocoa(let object): return ObjectIdentifier(object)
case ._native(let object): return ObjectIdentifier(object)
default: return nil
}
}
}
extension String {
public // @testable
func _classify() -> _StringRepresentation { return _guts._classify() }
@_alwaysEmitIntoClient
public // @testable
func _deconstructUTF8<ToPointer: _Pointer>(
scratch: UnsafeMutableRawBufferPointer?
) -> (
owner: AnyObject?,
ToPointer,
length: Int,
usesScratch: Bool,
allocatedMemory: Bool
) {
_guts._deconstructUTF8(scratch: scratch)
}
}
extension _StringGuts {
internal func _classify() -> _StringRepresentation {
var result = _StringRepresentation(
_isASCII: self.isASCII,
_count: self.count,
_capacity: nativeCapacity ?? 0,
_form: ._small
)
if self.isSmall {
result._capacity = _SmallString.capacity
return result
}
if _object.largeIsCocoa {
result._form = ._cocoa(object: _object.cocoaObject)
return result
}
// TODO: shared native
_internalInvariant(_object.providesFastUTF8)
if _object.isImmortal {
result._form = ._immortal(
address: UInt(bitPattern: _object.nativeUTF8Start))
return result
}
if _object.hasNativeStorage {
_internalInvariant(_object.largeFastIsTailAllocated)
result._form = ._native(object: _object.nativeStorage)
return result
}
fatalError()
}
/*
Deconstruct the string into contiguous UTF-8, allocating memory if necessary
┌────────────────────╥───────────────────────┬─────────────────────┬─────────────┬─────────────────┐
│ Form ║ owner │ pointer+length │ usesScratch │ allocatedMemory │
├────────────────────╫───────────────────────┼─────────────────────┼─────────────┼─────────────────┤
│ small with scratch ║ nil │ `scratch` │ true │ false │
├────────────────────╫───────────────────────┼─────────────────────┼─────────────┼─────────────────┤
│ small w/o scratch ║ extra allocation │ `owner` pointer │ false │ true │
╞════════════════════╬═══════════════════════╪═════════════════════╪═════════════╪═════════════════╡
│ immortal, large ║ nil │ literal pointer │ false │ false │
├────────────────────╫───────────────────────┼─────────────────────┼─────────────┼─────────────────┤
│ native ║ __StringStorage │ tail alloc pointer │ false │ false │
╞════════════════════╬═══════════════════════╪═════════════════════╪═════════════╪═════════════════╡
│ shared ║ __SharedStringStorage │ shared pointer │ false │ false │
├────────────────────╫───────────────────────┼─────────────────────┼─────────────┼─────────────────┤
│ shared, bridged ║ _CocoaString │ cocoa ASCII pointer │ false │ false │
╞════════════════════╬═══════════════════════╪═════════════════════╪═════════════╪═════════════════╡
│ foreign ║ extra allocation │ `owner` pointer │ false │ true │
└────────────────────╨───────────────────────┴─────────────────────┴─────────────┴─────────────────┘
*/
@_alwaysEmitIntoClient
internal // TODO: figure out if this works as a compiler intrinsic
func _deconstructUTF8<ToPointer: _Pointer>(
scratch: UnsafeMutableRawBufferPointer?
) -> (
owner: AnyObject?,
ToPointer,
length: Int,
usesScratch: Bool,
allocatedMemory: Bool
) {
// If we're small, try to copy into the scratch space provided
if self.isSmall {
let smol = self.asSmall
if let scratch = scratch, scratch.count > smol.count {
let scratchStart =
scratch.baseAddress!
smol.withUTF8 { smolUTF8 -> () in
scratchStart.initializeMemory(
as: UInt8.self, from: smolUTF8.baseAddress!, count: smolUTF8.count)
}
scratch[smol.count] = 0
return (
owner: nil,
_convertPointerToPointerArgument(scratchStart),
length: smol.count,
usesScratch: true, allocatedMemory: false)
}
} else if _fastPath(self.isFastUTF8) {
let ptr: ToPointer =
_convertPointerToPointerArgument(self._object.fastUTF8.baseAddress!)
return (
owner: self._object.owner,
ptr,
length: self._object.count,
usesScratch: false, allocatedMemory: false)
}
let (object, ptr, len) = self._allocateForDeconstruct()
return (
owner: object,
_convertPointerToPointerArgument(ptr),
length: len,
usesScratch: false,
allocatedMemory: true)
}
@_alwaysEmitIntoClient
@inline(never) // slow path
internal
func _allocateForDeconstruct() -> (
owner: AnyObject,
UnsafeRawPointer,
length: Int
) {
let utf8 = Array(String(self).utf8) + [0]
let (owner, ptr): (AnyObject?, UnsafeRawPointer) =
_convertConstArrayToPointerArgument(utf8)
// Array's owner cannot be nil, even though it is declared optional...
return (owner: owner!, ptr, length: utf8.count - 1)
}
}