Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions stdlib/public/Darwin/Foundation/Data.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2084,10 +2084,16 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
if buffer.count < buffer.capacity {
buffer.append(byte: element)
} else {
buffer.withUnsafeBytes {_representation.append(contentsOf: $0)}
buffer.withUnsafeBytes { _representation.append(contentsOf: $0) }
buffer.count = 0
}
Comment thread
palimondo marked this conversation as resolved.
}

// If we've still got bytes left in the buffer (i.e. the loop ended before we filled up the buffer and cleared it out), append them.
if buffer.count > 0 {
buffer.withUnsafeBytes { _representation.append(contentsOf: $0) }
buffer.count = 0
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll be throwing away (releasing) the buffer as you exit this block, so resetting the count is unnecessary.

}
}
}
}
Expand Down Expand Up @@ -2372,10 +2378,16 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
if buffer.count < buffer.capacity {
buffer.append(byte: element)
} else {
buffer.withUnsafeBytes {_representation.append(contentsOf: $0)}
buffer.withUnsafeBytes { _representation.append(contentsOf: $0) }
buffer.count = 0
}
}
Comment thread
palimondo marked this conversation as resolved.

// If we've still got bytes left in the buffer (i.e. the loop ended before we filled up the buffer and cleared it out), append them.
if buffer.count > 0 {
buffer.withUnsafeBytes { _representation.append(contentsOf: $0) }
buffer.count = 0
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't need to reset this on exit.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I realize — I was hoping to avoid future bugs in case someone tries to use buffer after this last write. I'll leave a comment instead.

}
}
}

Expand Down
53 changes: 53 additions & 0 deletions test/stdlib/TestData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1183,6 +1183,57 @@ class TestData : TestDataSuper {
expectEqual(Data(bytes: [1]), slice)
}

// This test uses `repeatElement` to produce a sequence -- the produced sequence reports its actual count as its `.underestimatedCount`.
Comment thread
palimondo marked this conversation as resolved.
func test_appendingNonContiguousSequence_exactCount() {
var d = Data()

// d should go from .empty representation to .inline.
// Appending a small enough sequence to fit in linline should actually be copied.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: linline

d.append(contentsOf: repeatElement(UInt8(0x01), count: 2))
Comment thread
palimondo marked this conversation as resolved.
Outdated
expectEqual(Data([0x01, 0x01]), d)

// Appending another small sequence should similarly still work.
d.append(contentsOf: repeatElement(UInt8(0x02), count: 1))
expectEqual(Data([0x01, 0x01, 0x02]), d)

// If we append a sequence of elements larger than a single InlineData, the internal append here should buffer.
// We want to make sure that buffering in this way does not accidentally drop trailing elements on the floor.
d.append(contentsOf: repeatElement(UInt8(0x03), count: 24))
Comment thread
palimondo marked this conversation as resolved.
Outdated
expectEqual(Data([0x01, 0x01, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03]), d)
}

// This test is like test_appendingNonContiguousSequence_exactCount but uses a sequence which reports 0 for its `.underestimatedCount`.
// This attempts to hit the worst-case scenario of `Data.append<S>(_:)` -- a discontiguous sequence of unknown length.
func test_appendingNonContiguousSequence_underestimatedCount() {
// underestimatedRepeatedElement does the same thing as repeatElement, but reports an `.underestimatedCount` of 0.
let underestimatedRepeatedElement = { (element: UInt8, count: Int) in
return sequence(state: count) { (count: inout Int) -> UInt8? in
defer { count -= 1 }
return count > 0 ? element : nil
}
}

var d = Data()

// d should go from .empty representation to .inline.
// Appending a small enough sequence to fit in linline should actually be copied.
d.append(contentsOf: underestimatedRepeatedElement(UInt8(0x01), 2))
expectEqual(Data([0x01, 0x01]), d)

// Appending another small sequence should similarly still work.
d.append(contentsOf: underestimatedRepeatedElement(UInt8(0x02), 1))
expectEqual(Data([0x01, 0x01, 0x02]), d)

// If we append a sequence of elements larger than a single InlineData, the internal append here should buffer.
// We want to make sure that buffering in this way does not accidentally drop trailing elements on the floor.
d.append(contentsOf: underestimatedRepeatedElement(UInt8(0x03), 24))
expectEqual(Data([0x01, 0x01, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03]), d)
}

func test_sequenceInitializers() {
let seq = repeatElement(UInt8(0x02), count: 3) // ensure we fall into the sequence case

Expand Down Expand Up @@ -3753,6 +3804,8 @@ DataTests.test("test_copyBytes1") { TestData().test_copyBytes1() }
DataTests.test("test_copyBytes2") { TestData().test_copyBytes2() }
DataTests.test("test_sliceOfSliceViaRangeExpression") { TestData().test_sliceOfSliceViaRangeExpression() }
DataTests.test("test_appendingSlices") { TestData().test_appendingSlices() }
DataTests.test("test_appendingNonContiguousSequence_exactCount") { TestData().test_appendingNonContiguousSequence_exactCount() }
DataTests.test("test_appendingNonContiguousSequence_underestimatedCount") { TestData().test_appendingNonContiguousSequence_underestimatedCount() }
DataTests.test("test_sequenceInitializers") { TestData().test_sequenceInitializers() }
DataTests.test("test_reversedDataInit") { TestData().test_reversedDataInit() }
DataTests.test("test_replaceSubrangeReferencingMutable") { TestData().test_replaceSubrangeReferencingMutable() }
Expand Down