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

Code before:
    }

    /// Returns a `String` of hexadecimal digits of bytes in the Buffer,
    /// with formatting compatible with output of `hexdump -C`.
    func hexDumpLong() -> String {
        var result = ""

        var buffer = self
        var lineOffset = 0

        while buffer.readableBytes > 0 {
            let slice = buffer.readSlice(length: min(16, buffer.readableBytes))!
            let lineLength = slice.readableBytes

            result += "\(String(byte: lineOffset, padding: 8))  "

            // Start with a xxd-format hexdump that we already have, then pad it
            // and insert a space in the middle.
            var lineHex = slice.hexDumpShort()
            lineHex.append(String(repeating: " ", count: 49 - lineHex.count))
            lineHex.insert(" ", at: lineHex.index(lineHex.startIndex, offsetBy: 24))
            result += lineHex

            // ASCII column renders the line as ASCII characters, or "." if the character is not printable.
            result += "|" + String(decoding: slice.getBytes(at: 0, length: slice.readableBytes)!, as: Unicode.UTF8.self).map {
                $0 >= " " && $0 < "~" ? $0 : "."
            } + "|\n"

            lineOffset += lineLength
        }

        // Add the last line with the last byte offset
        result += String(byte: lineOffset, padding: 8)
        return result
    }

//    func hexDumpLong(limit: Int) -> String {
//        var result = ""
//
//        // Start with long-format dump of limit/2 bytes
//        // then insert an empty line with "..." in the middle
//        // Add a dump of the last limit/2 bytes
//        // The last line should be the final lineOffset
//
//        
//
//        return result
//    }

    /// Describes a ByteBuffer hexDump format. Can be either xxd output compatible, or hexdump compatible.
    /// You can provide a `maxLength` argument to both formats that will limit the maximum length of the buffer to dump before clipping the dump for readability.

Review comment (suggestion):
Rather than converting the bytes to string space and then mapping them we can do it more cheaply by operating in byte-space. We can also make use of the readable bytes view:

```swift
let printableBytes = slice.readableBytesView.map { 
    let printableRange = UInt8(ascii: " ") ..< UInt8(ascii: "~")
    return printableRange.contains($0) ? $0 : UInt8(ascii: ".")
}

result += "|" 
result += String(decoding: printableBytes, as: UTF8.self)
result += "|\n"
```

Code after:
    /// in a format that's compatible with `xxd -r -p`.
    /// `hexDumpPlain()` always dumps all readable bytes, i.e. from `readerIndex` to `writerIndex`,
    /// so you should set those indices to desired location to get the offset and length that you need to dump.
    private func hexDumpPlain() -> String {
        var hexString = ""
        hexString.reserveCapacity(self.readableBytes * 3)

        for byte in self.readableBytesView {
            hexString += String(byte, radix: 16, padding: 2)
            hexString += " "
        }

        return String(hexString.dropLast())
    }

    /// Return a `String` of space delimited hexadecimal digits of the readable bytes in the buffer,
    /// in a format that's compatible with `xxd -r -p`, but clips the output to the max length of `maxBytes` bytes.
    /// If the dump contains more than the `maxBytes` bytes, this function will return the first `maxBytes/2`
    /// and the last `maxBytes/2` of that, replacing the rest with `...`, i.e. `01 02 03 ... 09 11 12`.
    ///
    /// - parameters:
    ///     - maxBytes: The maximum amount of bytes presented in the dump.
    private func hexDumpPlain(maxBytes: Int) -> String {
        // If the buffer length fits in the max bytes limit in the hex dump, just dump the whole thing.
        if self.readableBytes <= maxBytes {
            return self.hexDump(format: .plain)
        }

        var buffer = self

        // Safe to force-unwrap because we just checked readableBytes is > maxBytes above.
        let front = buffer.readSlice(length: maxBytes / 2)!
        buffer.moveReaderIndex(to: buffer.writerIndex - maxBytes / 2)
        let back = buffer.readSlice(length: buffer.readableBytes)!

        let startHex = front.hexDumpPlain()
        let endHex = back.hexDumpPlain()
        return startHex + " ... " + endHex
    }

    /// Returns a `String` containing a detailed hex dump of this buffer.
    /// Intended to be used internally in ``hexDump(format:)``
    /// - parameters:
    ///     - lineOffset: an offset from the beginning of the outer buffer that is being dumped. It's used to print the line offset in hexdump -C format.
    ///     - paddingBefore: the amount of space to pad before the first byte dumped on this line, used in center and right columns.
    ///     - paddingAfter: the amount of sapce to pad after the last byte on this line, used in center and right columns.
    private func _hexDumpLine(lineOffset: Int, paddingBefore: Int = 0, paddingAfter: Int = 0) -> String {
        // Each line takes 78 visible characters + \n
        var result = ""
        result.reserveCapacity(79)