Files
swift-mirror/stdlib/public/core/Slice.swift.gyb
Max Moiseev 37bf02f7da [stdlib][swift-3-indexing-model] changes in index(_:stepsFrom:limitedBy:)
- index(_:stepsFrom:limitedBy:) returns Index?
- formIndex(_:stepsFrom:limitedBy) returns Bool
2016-04-14 11:49:27 -07:00

391 lines
14 KiB
Plaintext

//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
%{
from gyb_stdlib_support import (
TRAVERSALS,
collectionForTraversal,
sliceTypeName,
protocolsForCollectionFeatures
)
def get_slice_doc_comment(Self):
return """\
/// A view into a sub-sequence of elements of another collection.
///
/// A `%s` instance stores the base collection, the start and end indices of
/// the view. It does not copy the elements from the collection into separate
/// storage. Thus, creating a slice has `O(1)` complexity.
///
/// A `%s` instance inherits the value or reference semantics of the base
/// collection. That is, if a `%s` instance is wrapped around a mutable
/// collection that has value semantics (for example, `Array`), mutating the
/// original collection would not affect the copy stored inside of the slice.
///
/// An element of a slice is located under the same index in the slice and in
/// the base collection, as long as neither the collection or the slice were
/// mutated. Thus, indices of a slice can be used interchangeably with indices
/// of the base collection.
///
/// - Warning: Long-term storage of `%s` instances is discouraged.
///
/// Because a `%s` presents a *view* onto the storage of some larger
/// collection even after the original collection goes out of scope, storing
/// the slice may prolong the lifetime of elements that are no longer
/// accessible, which can manifest as apparent memory and object leakage. To
/// prevent this effect, use slices only for transient computation.\
""" % (Self, Self, Self, Self, Self)
}%
// FIXME(ABI)(compiler limitation): There should be just one slice type
// that has conditional conformances to `BidirectionalCollection`,
// `RandomAccessCollection`, `RangeReplaceableCollection`, and
// `MutableCollection`.
% for Traversal in TRAVERSALS:
% for Mutable in [ False, True ]:
% for RangeReplaceable in [ False, True ]:
% Self = sliceTypeName(traversal=Traversal, mutable=Mutable, rangeReplaceable=RangeReplaceable)
% BaseRequirements = [collectionForTraversal(Traversal).replace('Collection', 'Indexable')]
% if Mutable:
% BaseRequirements.append('MutableIndexable')
% if RangeReplaceable:
% BaseRequirements.append('RangeReplaceableIndexable')
% BaseRequirements = 'protocol<' + ', '.join(BaseRequirements) + '>'
% SelfProtocols = ', '.join(protocolsForCollectionFeatures(traversal=Traversal, mutable=Mutable, rangeReplaceable=RangeReplaceable))
${get_slice_doc_comment(Self)}
% if Mutable:
///
/// - Warning: `${Self}` requires the setter of `Base.subscript(_: Index)`
/// to not invalidate indices. If you are writing a collection and mutations
/// need to invalidate indices, don't use `${Self}`, use `Slice` or
/// define your own `Base.SubSequence` type that takes that into account.
% end
public struct ${Self}<Base : ${BaseRequirements}>
: ${SelfProtocols} {
public typealias Index = Base.Index
public typealias IndexDistance = Base.IndexDistance
public var _startIndex: Index
public var _endIndex: Index
public var startIndex: Index {
return _startIndex
}
public var endIndex: Index {
return _endIndex
}
public subscript(index: Index) -> Base._Element {
get {
_failEarlyRangeCheck(index, bounds: startIndex..<endIndex)
return _base[index]
}
% if Mutable:
set {
_failEarlyRangeCheck(index, bounds: startIndex..<endIndex)
_base[index] = newValue
// MutableSlice requires that the underlying collection's subscript
// setter does not invalidate indices, so our `startIndex` and `endIndex`
// continue to be valid.
}
% end
}
public typealias SubSequence = ${Self}<Base>
public subscript(bounds: Range<Index>) -> ${Self}<Base> {
get {
_failEarlyRangeCheck(bounds, bounds: startIndex..<endIndex)
return ${Self}(base: _base, bounds: bounds)
}
% if Mutable:
set {
_writeBackMutableSlice(&self, bounds: bounds, slice: newValue)
}
% end
}
// FIXME(ABI)(compiler limitation):
//
// public typealias Indices = Base.Indices
// public var indices: Indices { ... }
//
// We can't do it right now because we don't have enough
// constraints on the Base.Indices type in this context.
@warn_unused_result
public func successor(of i: Index) -> Index {
// FIXME: swift-3-indexing-model: range check.
return _base.successor(of: i)
}
public func formSuccessor(i: inout Index) {
// FIXME: swift-3-indexing-model: range check.
_base.formSuccessor(&i)
}
% if Traversal in ['Bidirectional', 'RandomAccess']:
@warn_unused_result
public func predecessor(of i: Index) -> Index {
// FIXME: swift-3-indexing-model: range check.
return _base.predecessor(of: i)
}
public func formPredecessor(i: inout Index) {
// FIXME: swift-3-indexing-model: range check.
_base.formPredecessor(&i)
}
% end
@warn_unused_result
public func index(n: IndexDistance, stepsFrom i: Index) -> Index {
// FIXME: swift-3-indexing-model: range check.
return _base.index(n, stepsFrom: i)
}
@warn_unused_result
public func index(
n: IndexDistance, stepsFrom i: Index, limitedBy limit: Index
) -> Index? {
// FIXME: swift-3-indexing-model: range check.
return _base.index(n, stepsFrom: i, limitedBy: limit)
}
@warn_unused_result
public func distance(from start: Index, to end: Index) -> IndexDistance {
// FIXME: swift-3-indexing-model: range check.
return _base.distance(from: start, to: end)
}
public func _failEarlyRangeCheck(index: Index, bounds: Range<Index>) {
_base._failEarlyRangeCheck(index, bounds: bounds)
}
public func _failEarlyRangeCheck(range: Range<Index>, bounds: Range<Index>) {
_base._failEarlyRangeCheck(range, bounds: bounds)
}
% if RangeReplaceable:
public init() {
self._base = Base()
self._startIndex = _base.startIndex
self._endIndex = _base.endIndex
}
public init(repeating repeatedValue: Base._Element, count: Int) {
self._base = Base(repeating: repeatedValue, count: count)
self._startIndex = _base.startIndex
self._endIndex = _base.endIndex
}
public init<
S : Sequence where S.Iterator.Element == Base._Element
>(_ elements: S) {
self._base = Base(elements)
self._startIndex = _base.startIndex
self._endIndex = _base.endIndex
}
public mutating func replaceSubrange<
C : Collection where C.Iterator.Element == Base._Element
>(
subRange: Range<Index>, with newElements: C
) {
// FIXME: swift-3-indexing-model: range check.
% if Traversal == 'Forward':
let sliceOffset: IndexDistance =
_base.distance(from: _base.startIndex, to: _startIndex)
let newSliceCount: IndexDistance =
_base.distance(from: _startIndex, to: subRange.lowerBound)
+ _base.distance(from: subRange.upperBound, to: _endIndex)
+ numericCast(newElements.count)
_base.replaceSubrange(subRange, with: newElements)
_startIndex = _base.index(sliceOffset, stepsFrom: _base.startIndex)
_endIndex = _base.index(newSliceCount, stepsFrom: _startIndex)
% else:
if subRange.lowerBound == _base.startIndex {
let newSliceCount: IndexDistance =
_base.distance(from: _startIndex, to: subRange.lowerBound)
+ _base.distance(from: subRange.upperBound, to: _endIndex)
+ numericCast(newElements.count)
_base.replaceSubrange(subRange, with: newElements)
_startIndex = _base.startIndex
_endIndex = _base.index(newSliceCount, stepsFrom: _startIndex)
} else {
let shouldUpdateStartIndex = subRange.lowerBound == _startIndex
let lastValidIndex = _base.predecessor(of: subRange.lowerBound)
let newEndIndexOffset =
_base.distance(from: subRange.upperBound, to: _endIndex)
+ numericCast(newElements.count) + 1
_base.replaceSubrange(subRange, with: newElements)
if shouldUpdateStartIndex {
_startIndex = _base.successor(of: lastValidIndex)
}
_endIndex = _base.index(newEndIndexOffset, stepsFrom: lastValidIndex)
}
% end
}
public mutating func insert(newElement: Base._Element, at i: Index) {
// FIXME: swift-3-indexing-model: range check.
% if Traversal == 'Forward':
let sliceOffset: IndexDistance =
_base.distance(from: _base.startIndex, to: _startIndex)
let newSliceCount: IndexDistance = count + 1
_base.insert(newElement, at: i)
_startIndex = _base.index(sliceOffset, stepsFrom: _base.startIndex)
_endIndex = _base.index(newSliceCount, stepsFrom: _startIndex)
% else:
if i == _base.startIndex {
let newSliceCount: IndexDistance = count + 1
_base.insert(newElement, at: i)
_startIndex = _base.startIndex
_endIndex = _base.index(newSliceCount, stepsFrom: _startIndex)
} else {
let shouldUpdateStartIndex = i == _startIndex
let lastValidIndex = _base.predecessor(of: i)
let newEndIndexOffset = _base.distance(from: i, to: _endIndex) + 2
_base.insert(newElement, at: i)
if shouldUpdateStartIndex {
_startIndex = _base.successor(of: lastValidIndex)
}
_endIndex = _base.index(newEndIndexOffset, stepsFrom: lastValidIndex)
}
% end
}
public mutating func insert<
S : Collection where S.Iterator.Element == Base._Element
>(contentsOf newElements: S, at i: Index) {
// FIXME: swift-3-indexing-model: range check.
% if Traversal == 'Forward':
let sliceOffset: IndexDistance =
_base.distance(from: _base.startIndex, to: _startIndex)
let newSliceCount: IndexDistance =
count + numericCast(newElements.count)
_base.insert(contentsOf: newElements, at: i)
_startIndex = _base.index(sliceOffset, stepsFrom: _base.startIndex)
_endIndex = _base.index(newSliceCount, stepsFrom: _startIndex)
% else:
if i == _base.startIndex {
let newSliceCount: IndexDistance =
count + numericCast(newElements.count)
_base.insert(contentsOf: newElements, at: i)
_startIndex = _base.startIndex
_endIndex = _base.index(newSliceCount, stepsFrom: _startIndex)
} else {
let shouldUpdateStartIndex = i == _startIndex
let lastValidIndex = _base.predecessor(of: i)
let newEndIndexOffset =
_base.distance(from: i, to: _endIndex)
+ numericCast(newElements.count) + 1
_base.insert(contentsOf: newElements, at: i)
if shouldUpdateStartIndex {
_startIndex = _base.successor(of: lastValidIndex)
}
_endIndex = _base.index(newEndIndexOffset, stepsFrom: lastValidIndex)
}
% end
}
public mutating func remove(at i: Index) -> Base._Element {
// FIXME: swift-3-indexing-model: range check.
% if Traversal == 'Forward':
let sliceOffset: IndexDistance =
_base.distance(from: _base.startIndex, to: _startIndex)
let newSliceCount: IndexDistance = count - 1
let result = _base.remove(at: i)
_startIndex = _base.index(sliceOffset, stepsFrom: _base.startIndex)
_endIndex = _base.index(newSliceCount, stepsFrom: _startIndex)
return result
% else:
if i == _base.startIndex {
let newSliceCount: IndexDistance = count - 1
let result = _base.remove(at: i)
_startIndex = _base.startIndex
_endIndex = _base.index(newSliceCount, stepsFrom: _startIndex)
return result
} else {
let shouldUpdateStartIndex = i == _startIndex
let lastValidIndex = _base.predecessor(of: i)
let newEndIndexOffset = _base.distance(from: i, to: _endIndex)
let result = _base.remove(at: i)
if shouldUpdateStartIndex {
_startIndex = _base.successor(of: lastValidIndex)
}
_endIndex = _base.index(newEndIndexOffset, stepsFrom: lastValidIndex)
return result
}
% end
}
public mutating func removeSubrange(bounds: Range<Index>) {
// FIXME: swift-3-indexing-model: range check.
% if Traversal == 'Forward':
let sliceOffset: IndexDistance =
_base.distance(from: _base.startIndex, to: _startIndex)
let newSliceCount: IndexDistance =
count - distance(from: bounds.lowerBound, to: bounds.upperBound)
_base.removeSubrange(bounds)
_startIndex = _base.index(sliceOffset, stepsFrom: _base.startIndex)
_endIndex = _base.index(newSliceCount, stepsFrom: _startIndex)
% else:
if bounds.lowerBound == _base.startIndex {
let newSliceCount: IndexDistance =
count
- _base.distance(from: bounds.lowerBound, to: bounds.upperBound)
_base.removeSubrange(bounds)
_startIndex = _base.startIndex
_endIndex = _base.index(newSliceCount, stepsFrom: _startIndex)
} else {
let shouldUpdateStartIndex = bounds.lowerBound == _startIndex
let lastValidIndex = _base.predecessor(of: bounds.lowerBound)
let newEndIndexOffset =
_base.distance(from: bounds.lowerBound, to: _endIndex)
- _base.distance(from: bounds.lowerBound, to: bounds.upperBound)
+ 1
_base.removeSubrange(bounds)
if shouldUpdateStartIndex {
_startIndex = _base.successor(of: lastValidIndex)
}
_endIndex = _base.index(newEndIndexOffset, stepsFrom: lastValidIndex)
}
% end
}
% end
/// Create a view into collection `base` that allows access within `bounds`.
///
/// - Complexity: O(1).
public init(base: Base, bounds: Range<Index>) {
self._base = base
self._startIndex = bounds.lowerBound
self._endIndex = bounds.upperBound
}
% if Mutable or RangeReplaceable:
internal var _base: Base
% else:
internal let _base: Base
% end
}
% end
% end
% end