// Copyright 2023 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.

//go:build goexperiment.exectracer2

// Trace string management.

package runtime

// Trace strings.

const maxTraceStringLen = 1024

// traceStringTable is map of string -> unique ID that also manages
// writing strings out into the trace.
type traceStringTable struct {
	// lock protects buf.
	lock mutex
	buf  *traceBuf // string batches to write out to the trace.

	// tab is a mapping of string -> unique ID.
	tab traceMap
}

// put adds a string to the table, emits it, and returns a unique ID for it.
func ( *traceStringTable) ( uintptr,  string) uint64 {
	// Put the string in the table.
	 := stringStructOf(&)
	,  := .tab.put(.str, uintptr(.len))
	if  {
		// Write the string to the buffer.
		systemstack(func() {
			.writeString(, , )
		})
	}
	return 
}

// emit emits a string and creates an ID for it, but doesn't add it to the table. Returns the ID.
func ( *traceStringTable) ( uintptr,  string) uint64 {
	// Grab an ID and write the string to the buffer.
	 := .tab.stealID()
	systemstack(func() {
		.writeString(, , )
	})
	return 
}

// writeString writes the string to t.buf.
//
// Must run on the systemstack because it may flush buffers and thus could acquire trace.lock.
//
//go:systemstack
func ( *traceStringTable) ( uintptr,  uint64,  string) {
	// Truncate the string if necessary.
	if len() > maxTraceStringLen {
		 = [:maxTraceStringLen]
	}

	lock(&.lock)
	 := unsafeTraceWriter(, .buf)

	// Ensure we have a place to write to.
	var  bool
	,  = .ensure(2 + 2*traceBytesPerNumber + len() /* traceEvStrings + traceEvString + ID + len + string data */)
	if  {
		// Annotate the batch as containing strings.
		.byte(byte(traceEvStrings))
	}

	// Write out the string.
	.byte(byte(traceEvString))
	.varint()
	.varint(uint64(len()))
	.stringData()

	// Store back buf if it was updated during ensure.
	.buf = .traceBuf
	unlock(&.lock)
}

// reset clears the string table and flushes any buffers it has.
//
// Must be called only once the caller is certain nothing else will be
// added to this table.
//
// Because it flushes buffers, this may acquire trace.lock and thus
// must run on the systemstack.
//
//go:systemstack
func ( *traceStringTable) ( uintptr) {
	if .buf != nil {
		lock(&trace.lock)
		traceBufFlush(.buf, )
		unlock(&trace.lock)
		.buf = nil
	}

	// Reset the table.
	lock(&.tab.lock)
	.tab.reset()
	unlock(&.tab.lock)
}