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

Code before:
    ///
    /// - parameters:
    ///     - format: ``HexDumpFormat`` to use for the dump.
    public func hexDump(format: HexDumpFormat) -> String {
        switch format.value {
        case .plain(let maxBytes):
            if let maxBytes = maxBytes {
                return self.hexDumpPlain(maxBytes: maxBytes)
            } else {
                return self.hexDumpPlain()
            }

        case .detailed(let maxBytes):
            if let maxBytes = maxBytes {
                return self.hexDumpDetailed(maxBytes: maxBytes)
            } else {
                return self.hexdumpDetailed()
            }
        }
    }
}

extension String {
    /// Plain decode a string representing a hexadecimal sequence them into an UInt8 sequence
    /// - Complexity: O(n)
    public var hexPlainDecodedBytes: [UInt8] {
        let stringWithoutWhiteSpaces = self.filter { !$0.isWhitespace }
        return sequence(
            state: stringWithoutWhiteSpaces,
            next: { remainder in
                guard remainder.count >= 2 else {
                    precondition(remainder.count == 0, "Uneven number of hex encoded bytes within string")
                    return nil
                }
                let nextTwo = remainder.prefix(2)
                remainder.removeFirst(2)
                return UInt8(nextTwo, radix: 16)
            }
        ).map { $0 }
    }
}


Review comment (suggestion):
This should not be `public`.

We also don't want to round-trip it through an Array: we should be able to convert directly into `ByteBuffer`.

Code after:
        let separator = "........  .. .. .. .. .. .. .. ..  .. .. .. .. .. .. .. ..  ..................\n"

        // reserve capacity for the maxBytes dumped, plus the separator line, and buffer length line.
        var result = ""
        result.reserveCapacity(maxBytes / 16 * 79 + 79 + 8)

        var buffer = self

        // Dump the front part of the buffer first, up to maxBytes/2 bytes.
        // Safe to force-unwrap because we know the buffer has more readable bytes than maxBytes.
        var front = buffer.readSlice(length: maxBytes / 2)!
        var bufferOffset = 0
        while front.readableBytes > 0 {
            // Safe to force-unwrap because buffer is guaranteed to have at least one byte in it.
            let slice = front.readSlice(length: min(16, front.readableBytes))!

            // This will only be non-zero on the last line of this loop
            let paddingAfter = 16 - slice.readableBytes
            result += slice._hexDumpLine(lineOffset: bufferOffset, paddingAfter: paddingAfter)
            bufferOffset += slice.readableBytes
        }

        result += separator

        // Dump the back maxBytes/2 bytes.
        bufferOffset = buffer.writerIndex - maxBytes / 2
        buffer.moveReaderIndex(to: bufferOffset)
        var back = buffer.readSlice(length: buffer.readableBytes)!

        // On the first line of the back part, we might want less than 16 bytes, with padding on the left.
        // But if this is also the last line, than take whatever is left.
        let lineLength = min(16 - bufferOffset % 16, back.readableBytes)

        // Line offset is the offset of the first byte of this line in a full buffer hex dump.
        // It may not match `bufferOffset` in the first line of the `back` part.
        let lineOffset = bufferOffset - bufferOffset % 16

        // Safe to force-unwrap because `back` is guaranteed to have at least one byte.
        let slice = back.readSlice(length: lineLength)!

        // paddingBefore is going to be applied both in the center column and the right column of the line.
        result += slice._hexDumpLine(lineOffset: lineOffset, paddingBefore: 16 - lineLength)
        bufferOffset += lineLength

        // Now dump the rest of the back part of the buffer.
        while back.readableBytes > 0 {
            let slice = back.readSlice(length: min(16, back.readableBytes))!
            result += slice._hexDumpLine(lineOffset: bufferOffset)
            bufferOffset += slice.readableBytes
        }