// 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 runtime

import (
	
	
)

// 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 data
	if .dataPos > 0 {
		 := copy(.dataBuf[.dataPos:], )
		.dataPos += uint8()
		 = [:]
		if .dataPos < uint8(len(.dataBuf)) {
			return
		}
		.flushLine(.dataBuf[:])
		.dataPos = 0
	}

	// Handle full lines in data
	for len() >= 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
	} else if .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.
		 := 0
		if goarch.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.PtrSize
	const  = ": "
	 := 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 + 2
	const  = "  "
	 :=  +  + 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() - 1
	for ;  >= 0; -- {
		[] = [%16]
		 /= 16
	}
	if  != 0 {
		// Indicate that we couldn't fit the whole number.
		[0] = '*'
	}
}

func ( hexdumpMarker) () {
	var  [64]byte
	for  := range  {
		[] = ' '
	}
	for .chars > len() {
		gwrite([:])
		.chars -= len()
	}
	gwrite([:.chars])
	print("^ ")
}