Repository: apple/swift-nio
File: Sources/NIOPosix/ControlMessage.swift (Swift)

Code before:
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import NIOCore

#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
import CNIODarwin
#elseif os(Linux) || os(FreeBSD) || os(Android)
import CNIOLinux
#elseif os(Windows)
import CNIOWindows
#endif

/// Memory for use as `cmsghdr` and associated data.
/// Supports multiple messages each with enough storage for multiple `cmsghdr`
struct UnsafeControlMessageStorage: Collection {
    let bytesPerMessage: Int
    let deallocateBuffer: Bool
    var buffer: UnsafeMutableRawBufferPointer

    /// Initialise which includes allocating memory
    /// parameter:
    /// - bytesPerMessage: How many bytes have been allocated for each supported message.
    /// - buffer: The memory allocated to use for control messages.
    init(bytesPerMessage: Int, deallocateBuffer: Bool, buffer: UnsafeMutableRawBufferPointer) {
        self.bytesPerMessage = bytesPerMessage
        self.deallocateBuffer = deallocateBuffer
        self.buffer = buffer
    }

    // Guess that 4 Int32 payload messages is enough for anyone.
    static var bytesPerMessage: Int { NIOBSDSocketControlMessage.space(payloadSize: MemoryLayout<Int32>.stride) * 4 }

    /// Allocate new memory - Caller must call `deallocate` when no longer required.
    /// parameter:
    ///   - msghdrCount: How many `msghdr` structures will be fed from this buffer - we assume 4 Int32 cmsgs for each.
    static func allocate(msghdrCount: Int) -> UnsafeControlMessageStorage {
        let bytesPerMessage = Self.bytesPerMessage
        let buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: bytesPerMessage * msghdrCount,
                                                             alignment: MemoryLayout<cmsghdr>.alignment)
        return UnsafeControlMessageStorage(bytesPerMessage: bytesPerMessage, deallocateBuffer: true, buffer: buffer)
    }

    mutating func deallocate() {
        if self.deallocateBuffer {
            self.buffer.deallocate()
            self.buffer = UnsafeMutableRawBufferPointer(start: UnsafeMutableRawPointer(bitPattern: 0x7eadbeef), count: 0)
        }
    }


Review comment (suggestion):
Can I propose we keep this method `private` and add a new `static func` that makes it clearer that it's borrowing storage? That keeps `deallocateBuffer` out of the interface of this type.

Code after:
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import NIOCore

#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
import CNIODarwin
#elseif os(Linux) || os(FreeBSD) || os(Android)
import CNIOLinux
#elseif os(Windows)
import CNIOWindows
#endif

/// Memory for use as `cmsghdr` and associated data.
/// Supports multiple messages each with enough storage for multiple `cmsghdr`
struct UnsafeControlMessageStorage: Collection {
    let bytesPerMessage: Int
    var buffer: UnsafeMutableRawBufferPointer
    private let deallocateBuffer: Bool

    /// Initialise which includes allocating memory
    /// parameter:
    /// - bytesPerMessage: How many bytes have been allocated for each supported message.
    /// - buffer: The memory allocated to use for control messages.
    /// - deallocateBuffer: buffer owning indicator
    private init(bytesPerMessage: Int, buffer: UnsafeMutableRawBufferPointer, deallocateBuffer: Bool) {
        self.bytesPerMessage = bytesPerMessage
        self.buffer = buffer
        self.deallocateBuffer = deallocateBuffer
    }

    // Guess that 4 Int32 payload messages is enough for anyone.
    static var bytesPerMessage: Int { NIOBSDSocketControlMessage.space(payloadSize: MemoryLayout<Int32>.stride) * 4 }

    /// Allocate new memory - Caller must call `deallocate` when no longer required.
    /// parameter:
    ///   - msghdrCount: How many `msghdr` structures will be fed from this buffer - we assume 4 Int32 cmsgs for each.
    static func allocate(msghdrCount: Int) -> UnsafeControlMessageStorage {
        let bytesPerMessage = Self.bytesPerMessage
        let buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: bytesPerMessage * msghdrCount,
                                                            alignment: MemoryLayout<cmsghdr>.alignment)
        return UnsafeControlMessageStorage(bytesPerMessage: bytesPerMessage, buffer: buffer, deallocateBuffer: true)
    }

    /// Create an instance not owning the buffer
    /// parameter:
    /// - bytesPerMessage: How many bytes have been allocated for each supported message.
    /// - buffer: The memory allocated to use for control messages.
    static func makeNotOwning(bytesPerMessage: Int, buffer: UnsafeMutableRawBufferPointer) -> UnsafeControlMessageStorage {
        precondition(buffer.count >= bytesPerMessage)