forked from swiftlang/swift
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathStringBuffer.swift
238 lines (208 loc) · 7.8 KB
/
StringBuffer.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 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
//
//===----------------------------------------------------------------------===//
struct _StringBufferIVars {
init(_ elementWidth: Int) {
_sanityCheck(elementWidth == 1 || elementWidth == 2)
usedEnd = nil
capacityAndElementShift = elementWidth - 1
}
init(
usedEnd: UnsafeMutablePointer<RawByte>,
byteCapacity: Int,
elementWidth: Int
) {
_sanityCheck(elementWidth == 1 || elementWidth == 2)
_sanityCheck((byteCapacity & 0x1) == 0)
self.usedEnd = usedEnd
self.capacityAndElementShift = byteCapacity + (elementWidth - 1)
}
// This stored property should be stored at offset zero. We perform atomic
// operations on it using _HeapBuffer's pointer.
var usedEnd: UnsafeMutablePointer<RawByte>
var capacityAndElementShift: Int
var byteCapacity: Int {
return capacityAndElementShift & ~0x1
}
var elementShift: Int {
return capacityAndElementShift & 0x1
}
}
// FIXME: Wanted this to be a subclass of
// _HeapBuffer<_StringBufferIVars,UTF16.CodeUnit>, but
// <rdar://problem/15520519> (Can't call static method of derived
// class of generic class with dependent argument type) prevents it.
public struct _StringBuffer {
// Make this a buffer of UTF-16 code units so that it's properly
// aligned for them if that's what we store.
typealias _Storage = _HeapBuffer<_StringBufferIVars, UTF16.CodeUnit>
typealias HeapBufferStorage
= _HeapBufferStorage<_StringBufferIVars, UTF16.CodeUnit>
init(_ storage: _Storage) {
_storage = storage
}
public init(capacity: Int, initialSize: Int, elementWidth: Int) {
_sanityCheck(elementWidth == 1 || elementWidth == 2)
_sanityCheck(initialSize <= capacity)
// We don't check for elementWidth overflow and underflow because
// elementWidth is known to be 1 or 2.
let elementShift = elementWidth &- 1
// We need at least 1 extra byte if we're storing 8-bit elements,
// because indexing will always grab 2 consecutive bytes at a
// time.
let capacityBump = 1 &- elementShift
// Used to round capacity up to nearest multiple of 16 bits, the
// element size of our storage.
let divRound = 1 &- elementShift
_storage = _Storage(
HeapBufferStorage.self,
_StringBufferIVars(elementWidth),
(capacity + capacityBump + divRound) >> divRound
)
self.usedEnd = start + (initialSize << elementShift)
_storage.value.capacityAndElementShift
= ((_storage._capacity() - capacityBump) << 1) + elementShift
}
@warn_unused_result
static func fromCodeUnits<
Encoding : UnicodeCodecType, Input : CollectionType // SequenceType?
where Input.Generator.Element == Encoding.CodeUnit
>(
encoding: Encoding.Type, input: Input, repairIllFormedSequences: Bool,
minimumCapacity: Int = 0
) -> (_StringBuffer?, hadError: Bool) {
// Determine how many UTF-16 code units we'll need
let inputStream = input.generate()
guard let (utf16Count, isAscii) = UTF16.measure(encoding, input: inputStream,
repairIllFormedSequences: repairIllFormedSequences) else {
return (.None, true)
}
// Allocate storage
let result = _StringBuffer(
capacity: max(utf16Count, minimumCapacity),
initialSize: utf16Count,
elementWidth: isAscii ? 1 : 2)
if isAscii {
var p = UnsafeMutablePointer<UTF8.CodeUnit>(result.start)
let sink: (UTF32.CodeUnit) -> Void = {
(p++).memory = UTF8.CodeUnit($0)
}
let hadError = transcode(
encoding, UTF32.self, input.generate(), sink,
stopOnError: true)
_sanityCheck(!hadError, "string can not be ASCII if there were decoding errors")
return (result, hadError)
}
else {
var p = result._storage.baseAddress
let sink: (UTF16.CodeUnit) -> Void = {
(p++).memory = $0
}
let hadError = transcode(
encoding, UTF16.self, input.generate(), sink,
stopOnError: !repairIllFormedSequences)
return (result, hadError)
}
}
/// A pointer to the start of this buffer's data area.
public var start: UnsafeMutablePointer<RawByte> {
return UnsafeMutablePointer(_storage.baseAddress)
}
/// A past-the-end pointer for this buffer's stored data.
var usedEnd: UnsafeMutablePointer<RawByte> {
get {
return _storage.value.usedEnd
}
set(newValue) {
_storage.value.usedEnd = newValue
}
}
var usedCount: Int {
return (usedEnd - start) >> elementShift
}
/// A past-the-end pointer for this buffer's available storage.
var capacityEnd: UnsafeMutablePointer<RawByte> {
return start + _storage.value.byteCapacity
}
/// The number of elements that can be stored in this buffer.
public var capacity: Int {
return _storage.value.byteCapacity >> elementShift
}
/// 1 if the buffer stores UTF-16; 0 otherwise.
var elementShift: Int {
return _storage.value.elementShift
}
/// The number of bytes per element.
var elementWidth: Int {
return elementShift + 1
}
// Return true iff we have the given capacity for the indicated
// substring. This is what we need to do so that users can call
// reserveCapacity on String and subsequently use that capacity, in
// two separate phases. Operations with one-phase growth should use
// "grow()," below.
@warn_unused_result
func hasCapacity(
cap: Int, forSubRange r: Range<UnsafePointer<RawByte>>
) -> Bool {
// The substring to be grown could be pointing in the middle of this
// _StringBuffer.
let offset = (r.startIndex - UnsafePointer(start)) >> elementShift
return cap + offset <= capacity
}
/// Attempt to claim unused capacity in the buffer.
///
/// Operation succeeds if there is sufficient capacity, and either:
/// - the buffer is uniquely-refereced, or
/// - `oldUsedEnd` points to the end of the currently used capacity.
///
/// - parameter subRange: Range of the substring that the caller tries
/// to extend.
/// - parameter newUsedCount: The desired size of the substring.
mutating func grow(
subRange: Range<UnsafePointer<RawByte>>, newUsedCount: Int
) -> Bool {
var newUsedCount = newUsedCount
// The substring to be grown could be pointing in the middle of this
// _StringBuffer. Adjust the size so that it covers the imaginary
// substring from the start of the buffer to `oldUsedEnd`.
newUsedCount += (subRange.startIndex - UnsafePointer(start)) >> elementShift
if _slowPath(newUsedCount > capacity) {
return false
}
let newUsedEnd = start + (newUsedCount << elementShift)
if _fastPath(self._storage.isUniquelyReferenced()) {
usedEnd = newUsedEnd
return true
}
// Optimization: even if the buffer is shared, but the substring we are
// trying to grow is located at the end of the buffer, it can be grown in
// place. The operation should be implemented in a thread-safe way,
// though.
//
// if usedEnd == subRange.endIndex {
// usedEnd = newUsedEnd
// return true
// }
let usedEndPhysicalPtr =
UnsafeMutablePointer<UnsafeMutablePointer<RawByte>>(_storage._value)
var expected = UnsafeMutablePointer<RawByte>(subRange.endIndex)
if _stdlib_atomicCompareExchangeStrongPtr(
object: usedEndPhysicalPtr, expected: &expected, desired: newUsedEnd) {
return true
}
return false
}
var _anyObject: AnyObject? {
return _storage.storage != nil ? .Some(_storage.storage!) : .None
}
var _storage: _Storage
}