mirror of https://go.googlesource.com/go
90 lines
2.6 KiB
Go
90 lines
2.6 KiB
Go
// Copyright 2024 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 unique
|
|
|
|
import (
|
|
"internal/abi"
|
|
"internal/stringslite"
|
|
"unsafe"
|
|
)
|
|
|
|
// clone makes a copy of value, and may update string values found in value
|
|
// with a cloned version of those strings. The purpose of explicitly cloning
|
|
// strings is to avoid accidentally giving a large string a long lifetime.
|
|
//
|
|
// Note that this will clone strings in structs and arrays found in value,
|
|
// and will clone value if it itself is a string. It will not, however, clone
|
|
// strings if value is of interface or slice type (that is, found via an
|
|
// indirection).
|
|
func clone[T comparable](value T, seq *cloneSeq) T {
|
|
for _, offset := range seq.stringOffsets {
|
|
ps := (*string)(unsafe.Pointer(uintptr(unsafe.Pointer(&value)) + offset))
|
|
*ps = stringslite.Clone(*ps)
|
|
}
|
|
return value
|
|
}
|
|
|
|
// singleStringClone describes how to clone a single string.
|
|
var singleStringClone = cloneSeq{stringOffsets: []uintptr{0}}
|
|
|
|
// cloneSeq describes how to clone a value of a particular type.
|
|
type cloneSeq struct {
|
|
stringOffsets []uintptr
|
|
}
|
|
|
|
// makeCloneSeq creates a cloneSeq for a type.
|
|
func makeCloneSeq(typ *abi.Type) cloneSeq {
|
|
if typ == nil {
|
|
return cloneSeq{}
|
|
}
|
|
if typ.Kind() == abi.String {
|
|
return singleStringClone
|
|
}
|
|
var seq cloneSeq
|
|
switch typ.Kind() {
|
|
case abi.Struct:
|
|
buildStructCloneSeq(typ, &seq, 0)
|
|
case abi.Array:
|
|
buildArrayCloneSeq(typ, &seq, 0)
|
|
}
|
|
return seq
|
|
}
|
|
|
|
// buildStructCloneSeq populates a cloneSeq for an abi.Type that has Kind abi.Struct.
|
|
func buildStructCloneSeq(typ *abi.Type, seq *cloneSeq, baseOffset uintptr) {
|
|
styp := typ.StructType()
|
|
for i := range styp.Fields {
|
|
f := &styp.Fields[i]
|
|
switch f.Typ.Kind() {
|
|
case abi.String:
|
|
seq.stringOffsets = append(seq.stringOffsets, baseOffset+f.Offset)
|
|
case abi.Struct:
|
|
buildStructCloneSeq(f.Typ, seq, baseOffset+f.Offset)
|
|
case abi.Array:
|
|
buildArrayCloneSeq(f.Typ, seq, baseOffset+f.Offset)
|
|
}
|
|
}
|
|
}
|
|
|
|
// buildArrayCloneSeq populates a cloneSeq for an abi.Type that has Kind abi.Array.
|
|
func buildArrayCloneSeq(typ *abi.Type, seq *cloneSeq, baseOffset uintptr) {
|
|
atyp := typ.ArrayType()
|
|
etyp := atyp.Elem
|
|
offset := baseOffset
|
|
for range atyp.Len {
|
|
switch etyp.Kind() {
|
|
case abi.String:
|
|
seq.stringOffsets = append(seq.stringOffsets, offset)
|
|
case abi.Struct:
|
|
buildStructCloneSeq(etyp, seq, offset)
|
|
case abi.Array:
|
|
buildArrayCloneSeq(etyp, seq, offset)
|
|
}
|
|
offset += etyp.Size()
|
|
align := uintptr(etyp.FieldAlign())
|
|
offset = (offset + align - 1) &^ (align - 1)
|
|
}
|
|
}
|