| // 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 abi.EscapeToResultNonString(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) | |
| } | |
| } |