天天看点

Swift 里字符串(十)修改字符串

Swift 里字符串(十)修改字符串

append

操作为例

public mutating func append(_ other: String) {
    if self.isEmpty && !_guts.hasNativeStorage {
      self = other
      return
    }
    self._guts.append(other._guts)
  }
           

_StringGuts

做了实际的工作

下面是实际进行

append

的地方

internal mutating func append(_ slicedOther: _StringGutsSlice) {
    defer { self._invariantCheck() }

    if self.isSmall && slicedOther._guts.isSmall {
      // TODO: In-register slicing
      let smolSelf = self.asSmall
      if let smol = slicedOther.withFastUTF8({ otherUTF8 in
        return _SmallString(smolSelf, appending: _SmallString(otherUTF8)!)
      }) {
        self = _StringGuts(smol)
        return
      }
    }
    // 进行 copy-on-write 操作
    prepareForAppendInPlace(otherUTF8Count: slicedOther.utf8Count)

    if slicedOther.isFastUTF8 {
      let otherIsASCII = slicedOther.isASCII
      slicedOther.withFastUTF8 { otherUTF8 in
        self.appendInPlace(otherUTF8, isASCII: otherIsASCII)
      }
      return
    }

    _foreignAppendInPlace(slicedOther)
  }
           

首先判断是否可以安装小字符串处理,然后是重头戏。

我们以实际存储的是

native string

为例分析。

分配内存操作

private mutating func prepareForAppendInPlace(
    otherUTF8Count otherCount: Int
  ) {
    defer {
      _internalInvariant(self.uniqueNativeUnusedCapacity != nil,
        "growth should produce uniqueness")
      _internalInvariant(self.uniqueNativeUnusedCapacity! >= otherCount,
        "growth should produce enough capacity")
    }

    // See if we can accomodate without growing or copying. If we have
    // sufficient capacity, we do not need to grow, and we can skip the copy if
    // unique. Otherwise, growth is required.
    let sufficientCapacity: Bool
    if let unused = self.nativeUnusedCapacity, unused >= otherCount {
      sufficientCapacity = true
    } else {
      sufficientCapacity = false
    }
    //naivestorage的字符串,一定不是UniqueNative的
    if self.isUniqueNative && sufficientCapacity {
      return
    }

    let totalCount = self.utf8Count + otherCount

    // Non-unique storage: just make a copy of the appropriate size, otherwise
    // grow like an array.
    let growthTarget: Int
    if sufficientCapacity {
      growthTarget = totalCount
    } else {
      growthTarget = Swift.max(
        totalCount, _growArrayCapacity(nativeCapacity ?? 0))
    }
    //最后会走到这里来
    self.grow(growthTarget)
  }
           
internal mutating func grow(_ n: Int) {
    defer { self._invariantCheck() }

    _internalInvariant(
      self.uniqueNativeCapacity == nil || self.uniqueNativeCapacity! < n)

    let growthTarget = Swift.max(n, (self.uniqueNativeCapacity ?? 0) * 2)

    if _fastPath(isFastUTF8) {
      let isASCII = self.isASCII
      let storage = self.withFastUTF8 {
      // 分配了内存
        __StringStorage.create(
          initializingFrom: $0, capacity: growthTarget, isASCII: isASCII)
      }

      self = _StringGuts(storage)
      return
    }

    _foreignGrow(growthTarget)
  }
           

在分配好的内存里进行内存copy

internal mutating func appendInPlace(
    _ other: UnsafeBufferPointer<UInt8>, isASCII: Bool
  ) {
    self._object.nativeStorage.appendInPlace(other, isASCII: isASCII)

    // We re-initialize from the modified storage to pick up new count, flags,
    // etc.
    self = _StringGuts(self._object.nativeStorage)
  }
           
@_effects(releasenone)
  internal func appendInPlace(
    _ other: UnsafeBufferPointer<UInt8>, isASCII: Bool
  ) {
    _internalInvariant(self.capacity >= other.count)
    let srcAddr = other.baseAddress._unsafelyUnwrappedUnchecked
    let srcCount = other.count
    self.mutableEnd.initialize(from: srcAddr, count: srcCount)
    _postAppendAdjust(appendedCount: srcCount, appendedIsASCII: isASCII)
  }