Repository: apple/swift-nio
File: Sources/NIOCore/Codec.swift (Swift)

Code before:
public final class MessageToByteHandler<Encoder: MessageToByteEncoder>: ChannelOutboundHandler {
    public typealias OutboundOut = ByteBuffer
    public typealias OutboundIn = Encoder.OutboundIn

    private enum State {
        case notInChannelYet
        case operational
        case error(Error)
        case done

        var readyToBeAddedToChannel: Bool {
            switch self {
            case .notInChannelYet:
                return true
            case .operational, .error, .done:
                return false
            }
        }
    }

    private var state: State = .notInChannelYet
    private let encoder: Encoder
    private var buffer: ByteBuffer? = nil
    private let maxBufferCapacity: Int?

    public init(_ encoder: Encoder, maxBufferCapacity: Int) {
        self.encoder = encoder
        self.maxBufferCapacity = maxBufferCapacity.nextPowerOf2()
    }
    
    public init(_ encoder: Encoder) {
        self.encoder = encoder
        self.maxBufferCapacity = nil
    }
}

@available(*, unavailable)
extension MessageToByteHandler: Sendable {}

extension MessageToByteHandler {
    public func handlerAdded(context: ChannelHandlerContext) {
        precondition(self.state.readyToBeAddedToChannel,
                     "illegal state when adding to Channel: \(self.state)")
        self.state = .operational
        self.buffer = context.channel.allocator.buffer(capacity: 256)
    }

    public func handlerRemoved(context: ChannelHandlerContext) {
        self.state = .done
        self.buffer = nil
    }

Review comment (suggestion):
This is no longer a `maxBufferCapacity` but rather the desired capacity to keep the buffer at.
```suggestion
    public init(_ encoder: Encoder, desiredBufferCapacity: Int) {
```

Code after:
extension ByteToMessageHandler: RemovableChannelHandler {
    public func removeHandler(context: ChannelHandlerContext, removalToken: ChannelHandlerContext.RemovalToken) {
        precondition(self.removalState == .notBeingRemoved)
        self.removalState = .removalStarted
        context.eventLoop.assumeIsolated().execute {
            self.processLeftovers(context: context)
            assert(!self.state.isLeftoversNeedProcessing, "illegal state: \(self.state)")
            switch self.removalState {
            case .removalStarted:
                self.removalState = .removalCompleted
            case .handlerRemovedCalled:
                // if we're here, then the channel has also been torn down between the start and the completion of
                // the user-triggered removal. That's okay.
                ()
            default:
                assertionFailure("illegal removal state: \(self.removalState)")
            }
            // this is necessary as it'll complete the promise.
            context.leavePipeline(removalToken: removalToken)
        }
    }
}

/// A handler which turns a given `MessageToByteEncoder` into a `ChannelOutboundHandler` that can then be added to a
/// `ChannelPipeline`.
public final class MessageToByteHandler<Encoder: MessageToByteEncoder>: ChannelOutboundHandler {
    public typealias OutboundOut = ByteBuffer
    public typealias OutboundIn = Encoder.OutboundIn

    private enum State {
        case notInChannelYet
        case operational
        case error(Error)
        case done

        var readyToBeAddedToChannel: Bool {
            switch self {
            case .notInChannelYet:
                return true
            case .operational, .error, .done:
                return false
            }
        }
    }

    private var state: State = .notInChannelYet
    private let encoder: Encoder
    private var buffer: ByteBuffer? = nil
    private let desiredBufferCapacity: Int?

    public init(_ encoder: Encoder, desiredBufferCapacity: Int) {