// Copyright 2025 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.package runtimeimport ()// hexdumpWords prints a word-oriented hex dump of [p, p+len).//// If mark != nil, it will be passed to hexdumper.mark.func hexdumpWords(, uintptr, func(uintptr, hexdumpMarker)) {printlock()// Provide a default annotation := func( uintptr, hexdumpMarker) {if != nil { (, ) }// Can we symbolize this value? := *(*uintptr)(unsafe.Pointer()) := findfunc()if .valid() { .start()print("<", funcname(), "+", hex(-.entry()), ">\n") } } := hexdumper{addr: , mark: } .write(unsafe.Slice((*byte)(unsafe.Pointer()), )) .close()printunlock()}// hexdumper is a Swiss-army knife hex dumper.//// To use, optionally set addr and wordBytes, then call write repeatedly,// followed by close.type hexdumper struct {// addr is the address to print for the first byte of data. addr uintptr// addrBytes is the number of bytes of addr to print. If this is 0, it // defaults to goarch.PtrSize. addrBytes uint8// wordBytes is the number of bytes in a word. If wordBytes is 1, this // prints a byte-oriented dump. If it's > 1, this interprets the data as a // sequence of words of the given size. If it's 0, it's treated as // goarch.PtrSize. wordBytes uint8// mark is an optional function that can annotate values in the hex dump. // // If non-nil, it is called with the address of every complete, aligned word // in the hex dump. // // If it decides to print an annotation, it must first call m.start(), then // print the annotation, followed by a new line. mark func(addr uintptr, m hexdumpMarker)// Below here is state ready int8// 0=need to init state; 1=need to print header; 2=ready// dataBuf accumulates a line at a time of data, in case it's split across // buffers. dataBuf [16]byte dataPos uint8 dataSkip uint8// Skip first n bytes of buf on first line// toPos maps from byte offset in data to a visual offset in the printed line. toPos [16]byte}type hexdumpMarker struct { chars int}func ( *hexdumper) ( []byte) {if .ready == 0 { .init() }// Handle leading dataif .dataPos > 0 { := copy(.dataBuf[.dataPos:], ) .dataPos += uint8() = [:]if .dataPos < uint8(len(.dataBuf)) {return } .flushLine(.dataBuf[:]) .dataPos = 0 }// Handle full lines in dataforlen() >= len(.dataBuf) { .flushLine([:len(.dataBuf)]) = [len(.dataBuf):] }// Handle trailing data .dataPos = uint8(copy(.dataBuf[:], ))}func ( *hexdumper) () {if .dataPos > 0 { .flushLine(.dataBuf[:.dataPos]) }}func ( *hexdumper) () {const = len(.dataBuf)if .addrBytes == 0 { .addrBytes = goarch.PtrSize } elseif .addrBytes < 0 || .addrBytes > goarch.PtrSize {throw("invalid addrBytes") }if .wordBytes == 0 { .wordBytes = goarch.PtrSize } := int(.wordBytes)if < 0 || >= || &(-1) != 0 {throw("invalid wordBytes") }// Construct position mapping.for := range .toPos {// First, calculate the "field" within the line, applying byte swizzling. := 0ifgoarch.BigEndian { = } else { = ^ int(-1) }// Translate this field into a visual offset. // "00112233 44556677 8899AABB CCDDEEFF" .toPos[] = byte(*2 + /4 + /8) }// The first line may need to skip some fields to get to alignment. // Round down the starting address. := .addr &^ uintptr(-1)// Skip bytes to get to alignment. .dataPos = uint8(.addr - ) .dataSkip = uint8(.addr - ) .addr = // We're ready to print the header. .ready = 1}func ( *hexdumper) ( []byte) {const = len(.dataBuf)const = 2 * goarch.PtrSizeconst = ": " := int(2*.addrBytes) + len()// dataChars uses the same formula to toPos above. We calculate it with the // "last field", then add the size of the last field.const = (-1)*2 + (-1)/4 + (-1)/8 + 2const = " " := + + len()const = := + var [ + len() + + len() + + 1]byte := func() {for := range { [] = ' ' } } ()if .ready == 1 {// Print column offsets header.for , := range .toPos { .fmtHex([+int(+1):][:1], uint64()) }// Print ASCII offsets.for := range { .fmtHex([+:][:1], uint64()) } [] = '\n'gwrite([:+1]) () .ready = 2 }// Format address. .fmtHex([:2*.addrBytes], uint64(.addr))copy([2*.addrBytes:], )// Format data in hex and ASCII.for , := range {if < int(.dataSkip) {continue } := .toPos[] .fmtHex([+int():][:2], uint64())copy([+:], ) := uint8('.')if >= ' ' && <= '~' { = } [+] = }// Trim buffer. := + len() [] = '\n' := [:+1]// Print.gwrite()// Print marks.if .mark != nil { ()for := 0; +int(.wordBytes) <= len(); += int(.wordBytes) {if < int(.dataSkip) {continue } := .addr + uintptr()// Find the position of the left edge of this word := + int(min(.toPos[], .toPos[+int(.wordBytes)-1])) .mark(, hexdumpMarker{}) } } .addr += uintptr() .dataPos = 0 .dataSkip = 0}// fmtHex formats v in base 16 into buf. It fills all of buf. If buf is too// small to represent v, it the output will start with '*'.func ( *hexdumper) ( []byte, uint64) {const = "0123456789abcdef" := len() - 1for ; >= 0; -- { [] = [%16] /= 16 }if != 0 {// Indicate that we couldn't fit the whole number. [0] = '*' }}func ( hexdumpMarker) () {var [64]bytefor := range { [] = ' ' }for .chars > len() {gwrite([:]) .chars -= len() }gwrite([:.chars])print("^ ")}
The pages are generated with Goldsv0.8.3-preview. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.