FileHelpers.swift 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. /*
  2. This source file is part of the Swift System open source project
  3. Copyright (c) 2020 Apple Inc. and the Swift System project authors
  4. Licensed under Apache License v2.0 with Runtime Library Exception
  5. See https://swift.org/LICENSE.txt for license information
  6. */
  7. @available(System 0.0.1, *)
  8. extension FileDescriptor {
  9. /// Runs a closure and then closes the file descriptor, even if an error occurs.
  10. ///
  11. /// - Parameter body: The closure to run.
  12. /// If the closure throws an error,
  13. /// this method closes the file descriptor before it rethrows that error.
  14. ///
  15. /// - Returns: The value returned by the closure.
  16. ///
  17. /// If `body` throws an error
  18. /// or an error occurs while closing the file descriptor,
  19. /// this method rethrows that error.
  20. public func closeAfter<R>(_ body: () throws -> R) throws -> R {
  21. // No underscore helper, since the closure's throw isn't necessarily typed.
  22. let result: R
  23. do {
  24. result = try body()
  25. } catch {
  26. _ = try? self.close() // Squash close error and throw closure's
  27. throw error
  28. }
  29. try self.close()
  30. return result
  31. }
  32. /// Writes a sequence of bytes to the current offset
  33. /// and then updates the offset.
  34. ///
  35. /// - Parameter sequence: The bytes to write.
  36. /// - Returns: The number of bytes written, equal to the number of elements in `sequence`.
  37. ///
  38. /// This method either writes the entire contents of `sequence`,
  39. /// or throws an error if only part of the content was written.
  40. ///
  41. /// Writes to the position associated with this file descriptor, and
  42. /// increments that position by the number of bytes written.
  43. /// See also ``seek(offset:from:)``.
  44. ///
  45. /// If `sequence` doesn't implement
  46. /// the <doc://com.apple.documentation/documentation/swift/sequence/3128824-withcontiguousstorageifavailable> method,
  47. /// temporary space will be allocated as needed.
  48. @_alwaysEmitIntoClient
  49. @discardableResult
  50. public func writeAll<S: Sequence>(
  51. _ sequence: S
  52. ) throws -> Int where S.Element == UInt8 {
  53. return try _writeAll(sequence).get()
  54. }
  55. @usableFromInline
  56. internal func _writeAll<S: Sequence>(
  57. _ sequence: S
  58. ) -> Result<Int, Errno> where S.Element == UInt8 {
  59. sequence._withRawBufferPointer { buffer in
  60. var idx = 0
  61. while idx < buffer.count {
  62. switch _write(
  63. UnsafeRawBufferPointer(rebasing: buffer[idx...]), retryOnInterrupt: true
  64. ) {
  65. case .success(let numBytes): idx += numBytes
  66. case .failure(let err): return .failure(err)
  67. }
  68. }
  69. assert(idx == buffer.count)
  70. return .success(buffer.count)
  71. }
  72. }
  73. /// Writes a sequence of bytes to the given offset.
  74. ///
  75. /// - Parameters:
  76. /// - offset: The file offset where writing begins.
  77. /// - sequence: The bytes to write.
  78. /// - Returns: The number of bytes written, equal to the number of elements in `sequence`.
  79. ///
  80. /// This method either writes the entire contents of `sequence`,
  81. /// or throws an error if only part of the content was written.
  82. /// Unlike ``writeAll(_:)``,
  83. /// this method preserves the file descriptor's existing offset.
  84. ///
  85. /// If `sequence` doesn't implement
  86. /// the <doc://com.apple.documentation/documentation/swift/sequence/3128824-withcontiguousstorageifavailable> method,
  87. /// temporary space will be allocated as needed.
  88. @_alwaysEmitIntoClient
  89. @discardableResult
  90. public func writeAll<S: Sequence>(
  91. toAbsoluteOffset offset: Int64, _ sequence: S
  92. ) throws -> Int where S.Element == UInt8 {
  93. try _writeAll(toAbsoluteOffset: offset, sequence).get()
  94. }
  95. @usableFromInline
  96. internal func _writeAll<S: Sequence>(
  97. toAbsoluteOffset offset: Int64, _ sequence: S
  98. ) -> Result<Int, Errno> where S.Element == UInt8 {
  99. sequence._withRawBufferPointer { buffer in
  100. var idx = 0
  101. while idx < buffer.count {
  102. switch _write(
  103. toAbsoluteOffset: offset + Int64(idx),
  104. UnsafeRawBufferPointer(rebasing: buffer[idx...]),
  105. retryOnInterrupt: true
  106. ) {
  107. case .success(let numBytes): idx += numBytes
  108. case .failure(let err): return .failure(err)
  109. }
  110. }
  111. assert(idx == buffer.count)
  112. return .success(buffer.count)
  113. }
  114. }
  115. }