Validate indexes and ranges passed into Data so that bounding conditions are respected (#1061)

diff --git a/Foundation/Data.swift b/Foundation/Data.swift
index b4d22c1..fd5b4a4 100644
--- a/Foundation/Data.swift
+++ b/Foundation/Data.swift
@@ -319,6 +319,7 @@
     
     @inline(__always)
     public func append(_ bytes: UnsafeRawPointer, length: Int) {
+        precondition(length >= 0, "Length of appending bytes must be positive")
         switch _backing {
         case .swift:
             let origLength = _length
@@ -1087,6 +1088,18 @@
         _sliceRange = range
     }
     
+    @_versioned
+    internal func _validateIndex(_ index: Int, message: String? = nil) {
+        precondition(_sliceRange.contains(index), message ?? "Index \(index) is out of bounds of range \(_sliceRange)")
+    }
+    
+    @_versioned
+    internal func _validateRange<R: RangeExpression>(_ range: R) where R.Bound == Int {
+        let r = range.relative(to: 0..<R.Bound.max)
+        precondition(r.lowerBound >= _sliceRange.lowerBound && r.lowerBound <= _sliceRange.upperBound, "Range \(r) is out of bounds of range \(_sliceRange)")
+        precondition(r.upperBound >= _sliceRange.lowerBound && r.upperBound <= _sliceRange.upperBound, "Range \(r) is out of bounds of range \(_sliceRange)")
+    }
+    
     // -----------------------------------
     // MARK: - Properties and Functions
     
@@ -1099,6 +1112,7 @@
         }
         @inline(__always)
         set {
+            precondition(count >= 0, "count must be positive")
             if !isKnownUniquelyReferenced(&_backing) {
                 _backing = _backing.mutableCopy(_sliceRange)
             }
@@ -1142,14 +1156,15 @@
     /// - warning: This method does not verify that the contents at pointer have enough space to hold `count` bytes.
     @inline(__always)
     public func copyBytes(to pointer: UnsafeMutablePointer<UInt8>, count: Int) {
+        precondition(count >= 0, "count of bytes to copy must be positive")
         if count == 0 { return }
-        memcpy(UnsafeMutableRawPointer(pointer), _backing.bytes!.advanced(by: _sliceRange.lowerBound), count)
+        memcpy(UnsafeMutableRawPointer(pointer), _backing.bytes!.advanced(by: _sliceRange.lowerBound), Swift.min(count, _sliceRange.count))
     }
     
     @inline(__always)
     private func _copyBytesHelper(to pointer: UnsafeMutableRawPointer, from range: NSRange) {
         if range.length == 0 { return }
-        memcpy(UnsafeMutableRawPointer(pointer), _backing.bytes!.advanced(by: range.location), range.length)
+        memcpy(UnsafeMutableRawPointer(pointer), _backing.bytes!.advanced(by: range.location), Swift.min(range.length, _sliceRange.count))
     }
     
     /// Copy a subset of the contents of the data to a pointer.
@@ -1175,16 +1190,11 @@
         let copyRange : Range<Index>
         if let r = range {
             guard !r.isEmpty else { return 0 }
-            precondition(r.lowerBound >= 0)
-            precondition(r.lowerBound < cnt, "The range is outside the bounds of the data")
-            
-            precondition(r.upperBound >= 0)
-            precondition(r.upperBound <= cnt, "The range is outside the bounds of the data")
-            
             copyRange = r.lowerBound..<(r.lowerBound + Swift.min(buffer.count * MemoryLayout<DestinationType>.stride, r.count))
         } else {
             copyRange = 0..<Swift.min(buffer.count * MemoryLayout<DestinationType>.stride, cnt)
         }
+        _validateRange(copyRange)
         
         guard !copyRange.isEmpty else { return 0 }
         
@@ -1243,6 +1253,7 @@
     public func range(of dataToFind: Data, options: Data.SearchOptions = [], in range: Range<Index>? = nil) -> Range<Index>? {
         let nsRange : NSRange
         if let r = range {
+            _validateRange(r)
             nsRange = NSMakeRange(r.lowerBound, r.upperBound - r.lowerBound)
         } else {
             nsRange = NSMakeRange(0, _backing.length)
@@ -1266,6 +1277,7 @@
     
     @inline(__always)
     public mutating func append(_ bytes: UnsafePointer<UInt8>, count: Int) {
+        precondition(count >= 0, "count must be positive")
         if count == 0 { return }
         if !isKnownUniquelyReferenced(&_backing) {
             _backing = _backing.mutableCopy(_sliceRange)
@@ -1326,6 +1338,9 @@
     /// - parameter range: The range in the data to set to `0`.
     @inline(__always)
     public mutating func resetBytes(in range: Range<Index>) {
+        // it is worth noting that the range here may be out of bounds of the Data itself (which triggers a growth)
+        precondition(range.lowerBound >= 0, "Ranges must be positive bounds")
+        precondition(range.upperBound >= 0, "Ranges must be positive bounds")
         let range = NSMakeRange(range.lowerBound, range.upperBound - range.lowerBound)
         if !isKnownUniquelyReferenced(&_backing) {
             _backing = _backing.mutableCopy(_sliceRange)
@@ -1346,6 +1361,7 @@
     /// - parameter data: The replacement data.
     @inline(__always)
     public mutating func replaceSubrange(_ subrange: Range<Index>, with data: Data) {
+        _validateRange(subrange)
         let nsRange = NSMakeRange(subrange.lowerBound, subrange.upperBound - subrange.lowerBound)
         let cnt = data.count
         if !isKnownUniquelyReferenced(&_backing) {
@@ -1361,6 +1377,7 @@
     
     @inline(__always)
     public mutating func replaceSubrange(_ subrange: CountableRange<Index>, with data: Data) {
+        _validateRange(subrange)
         let nsRange = NSMakeRange(subrange.lowerBound, subrange.upperBound - subrange.lowerBound)
         let cnt = data.count
         if !isKnownUniquelyReferenced(&_backing) {
@@ -1383,6 +1400,7 @@
     /// - parameter buffer: The replacement bytes.
     @inline(__always)
     public mutating func replaceSubrange<SourceType>(_ subrange: Range<Index>, with buffer: UnsafeBufferPointer<SourceType>) {
+        _validateRange(subrange)
         let nsRange = NSMakeRange(subrange.lowerBound, subrange.upperBound - subrange.lowerBound)
         let bufferCount = buffer.count * MemoryLayout<SourceType>.stride
         
@@ -1405,19 +1423,13 @@
     @inline(__always)
     public mutating func replaceSubrange<ByteCollection : Collection>(_ subrange: Range<Index>, with newElements: ByteCollection)
         where ByteCollection.Iterator.Element == Data.Iterator.Element {
-            
+            _validateRange(subrange)
             // Calculate this once, it may not be O(1)
             let replacementCount: Int = numericCast(newElements.count)
             let currentCount = self.count
             let subrangeCount = subrange.count
             
-            if currentCount < subrange.lowerBound + subrangeCount {
-                if subrangeCount == 0 {
-                    preconditionFailure("location \(subrange.lowerBound) exceeds data count \(currentCount)")
-                } else {
-                    preconditionFailure("range \(subrange) exceeds data count \(currentCount)")
-                }
-            }
+            _validateRange(subrange)
             
             let resultCount = currentCount - subrangeCount + replacementCount
             if resultCount != currentCount {
@@ -1446,6 +1458,7 @@
     
     @inline(__always)
     public mutating func replaceSubrange(_ subrange: Range<Index>, with bytes: UnsafeRawPointer, count cnt: Int) {
+        _validateRange(subrange)
         let nsRange = NSMakeRange(subrange.lowerBound, subrange.upperBound - subrange.lowerBound)
         if !isKnownUniquelyReferenced(&_backing) {
             _backing = _backing.mutableCopy(_sliceRange)
@@ -1461,11 +1474,11 @@
     /// - parameter range: The range to copy.
     @inline(__always)
     public func subdata(in range: Range<Index>) -> Data {
+        _validateRange(range)
         let length = count
         if count == 0 {
             return Data()
         }
-        precondition(length >= range.upperBound)
         return _backing.subdata(in: range)
     }
     
@@ -1502,6 +1515,7 @@
     
     @inline(__always)
     public func advanced(by amount: Int) -> Data {
+        _validateIndex(startIndex + amount)
         let length = count - amount
         precondition(length > 0)
         return withUnsafeBytes { (ptr: UnsafePointer<UInt8>) -> Data in
@@ -1518,10 +1532,12 @@
     public subscript(index: Index) -> UInt8 {
         @inline(__always)
         get {
+            _validateIndex(index)
             return _backing.bytes!.advanced(by: index).assumingMemoryBound(to: UInt8.self).pointee
         }
         @inline(__always)
         set {
+            _validateIndex(index)
             if !isKnownUniquelyReferenced(&_backing) {
                 _backing = _backing.mutableCopy(_sliceRange)
             }
@@ -1532,6 +1548,7 @@
     public subscript(bounds: Range<Index>) -> Data {
         @inline(__always)
         get {
+            _validateRange(bounds)
             return Data(backing: _backing, range: bounds)
         }
         @inline(__always)
@@ -1547,14 +1564,18 @@
             let range = rangeExpression.relative(to: 0..<R.Bound.max)
             let start: Int = numericCast(range.lowerBound)
             let end: Int = numericCast(range.upperBound)
-            return Data(backing: _backing, range: start..<end)
+            let r: Range<Int> = start..<end
+            _validateRange(r)
+            return Data(backing: _backing, range: r)
         }
         @inline(__always)
         set {
             let range = rangeExpression.relative(to: 0..<R.Bound.max)
             let start: Int = numericCast(range.lowerBound)
             let end: Int = numericCast(range.upperBound)
-            replaceSubrange(start..<end, with: newValue)
+            let r: Range<Int> = start..<end
+            _validateRange(r)
+            replaceSubrange(r, with: newValue)
         }
         
     }
diff --git a/TestFoundation/TestNSData.swift b/TestFoundation/TestNSData.swift
index 7df6584..020b2c1 100644
--- a/TestFoundation/TestNSData.swift
+++ b/TestFoundation/TestNSData.swift
@@ -48,7 +48,7 @@
             ("testCopyBytes_oversized", testCopyBytes_oversized),
             ("testCopyBytes_ranges", testCopyBytes_ranges),
             ("testCopyBytes_undersized", testCopyBytes_undersized),
-            // ("testCopyBytes", testCopyBytes),  Disabled to due failure, we want this passing - but API matching is more important right now
+             ("testCopyBytes", testCopyBytes),
             ("testCustomDeallocator", testCustomDeallocator),
             ("testDataInSet", testDataInSet),
             ("testEquality", testEquality),