in daffodil-io/src/main/scala/org/apache/daffodil/io/Dump.scala [243:377]
private[io] def dumpHexAndTextBytes(
startByteAddress0b: Long,
lengthInBytes: Int,
byteBuffer: ByteBuffer,
includeHeadingLine: Boolean,
optEncodingName: Option[String],
indicatorInfoInBytes: Option[(Long, Int)]
): Seq[String] = {
Assert.usage(startByteAddress0b >= 0)
Assert.usage(lengthInBytes >= 0)
val (textDataHeader, textByteWidth, optEncName) = getTextParameters(optEncodingName)
val decoder = getReportingDecoder(optEncName)
val endByteAddress0b = math.max(startByteAddress0b + lengthInBytes - 1, 0)
val addressHeader = """87654321 """
val hexHeader = """0011 2233 4455 6677 8899 aabb ccdd eeff""" // space on the end is needed
val headingHex = addressHeader + hexHeader
val firstGutter = ": "
val offset0b = (startByteAddress0b & 0xf).toInt
val hexRegionInitialWhitespace = {
val offset2 = offset0b / 2
val res = " " * offset2 +
(" " * (offset0b & 0x1)) // blank first half of pair
res
}
val textRegionInitialWhitespace = (" " * textByteWidth) * offset0b
val indicatorLine =
makeHexAndTextIndicatorLine(
indicatorInfoInBytes,
startByteAddress0b,
lengthInBytes,
hexHeader.length,
addressHeader.length,
textByteWidth
)
var isFirstRow = true
var isLastRow = false
val firstLeftAddress = startByteAddress0b & 0x7fffffffffffff0L
val lastLeftAddress =
math.max(0, (startByteAddress0b + lengthInBytes - 1)) & 0x7ffffffffffffff0L
val headingLine = headingHex + " " + textDataHeader
val ab = scala.collection.mutable.ArrayBuffer[String]()
indicatorLine.foreach { line => ab += line }
if (includeHeadingLine) ab += headingLine
val hexsb = new StringBuilder
val txtsb = new StringBuilder
var rowStart0b = offset0b
var limit0b = 15 // except for last row it will be shortened. Inclusive limit.
//
// These vars are used by the txt dump when the multiple bytes of a
// character wrap from one line to the next.
//
paddingFromPriorLine = ""
nPadBytesFromPriorLine = 0
(firstLeftAddress to lastLeftAddress by 16).foreach {
//
// for each line/row, we assemble the address part, the hex part, and the text part
//
addr =>
if (addr == lastLeftAddress) {
isLastRow = true
limit0b = (endByteAddress0b & 0xf).toInt // might be fewer than all 16 for last row
}
val addrString = "%08x".format(addr)
hexsb ++= addrString + firstGutter
if (isFirstRow) {
isFirstRow = false
hexsb ++= hexRegionInitialWhitespace
txtsb ++= textRegionInitialWhitespace
}
//
// Hex dump
//
(rowStart0b to limit0b).foreach { i =>
val bytePos0b = addr + i - startByteAddress0b
val byteValue =
try {
byteBuffer.get(bytePos0b.toInt)
} catch {
case e: IndexOutOfBoundsException => 0.toByte
}
val hex = "%02x".format(byteValue)
val gutter = if ((i & 0x1) == 0) "" else " "
hexsb ++= hex + gutter
}
//
// Text dump
//
textDump(
addr - startByteAddress0b,
rowStart0b,
txtsb,
limit0b,
endByteAddress0b,
byteBuffer,
decoder,
textByteWidth
)
if (isLastRow) {
//
// Trailing spaces on the hex dump
//
((limit0b + 1) to 15).foreach { i =>
val gutter = if ((i & 0x1) == 1) " " else ""
hexsb ++= " " + gutter
}
//
// Trailing spaces on the text dump
//
((limit0b + 1) to 15).foreach { i =>
txtsb ++= (" " * textByteWidth)
}
}
ab += hexsb.mkString + " " + txtsb.mkString
hexsb.clear()
txtsb.clear()
//
// we're done with first row, so subsequent rows will have
// zero as the row start.
//
rowStart0b = 0
}
ab.toSeq
}