1// Copyright 2024 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package unique 6 7import ( 8 "internal/abi" 9 "internal/stringslite" 10 "unsafe" 11) 12 13// clone makes a copy of value, and may update string values found in value 14// with a cloned version of those strings. The purpose of explicitly cloning 15// strings is to avoid accidentally giving a large string a long lifetime. 16// 17// Note that this will clone strings in structs and arrays found in value, 18// and will clone value if it itself is a string. It will not, however, clone 19// strings if value is of interface or slice type (that is, found via an 20// indirection). 21func clone[T comparable](value T, seq *cloneSeq) T { 22 for _, offset := range seq.stringOffsets { 23 ps := (*string)(unsafe.Pointer(uintptr(unsafe.Pointer(&value)) + offset)) 24 *ps = stringslite.Clone(*ps) 25 } 26 return value 27} 28 29// singleStringClone describes how to clone a single string. 30var singleStringClone = cloneSeq{stringOffsets: []uintptr{0}} 31 32// cloneSeq describes how to clone a value of a particular type. 33type cloneSeq struct { 34 stringOffsets []uintptr 35} 36 37// makeCloneSeq creates a cloneSeq for a type. 38func makeCloneSeq(typ *abi.Type) cloneSeq { 39 if typ == nil { 40 return cloneSeq{} 41 } 42 if typ.Kind() == abi.String { 43 return singleStringClone 44 } 45 var seq cloneSeq 46 switch typ.Kind() { 47 case abi.Struct: 48 buildStructCloneSeq(typ, &seq, 0) 49 case abi.Array: 50 buildArrayCloneSeq(typ, &seq, 0) 51 } 52 return seq 53} 54 55// buildStructCloneSeq populates a cloneSeq for an abi.Type that has Kind abi.Struct. 56func buildStructCloneSeq(typ *abi.Type, seq *cloneSeq, baseOffset uintptr) { 57 styp := typ.StructType() 58 for i := range styp.Fields { 59 f := &styp.Fields[i] 60 switch f.Typ.Kind() { 61 case abi.String: 62 seq.stringOffsets = append(seq.stringOffsets, baseOffset+f.Offset) 63 case abi.Struct: 64 buildStructCloneSeq(f.Typ, seq, baseOffset+f.Offset) 65 case abi.Array: 66 buildArrayCloneSeq(f.Typ, seq, baseOffset+f.Offset) 67 } 68 } 69} 70 71// buildArrayCloneSeq populates a cloneSeq for an abi.Type that has Kind abi.Array. 72func buildArrayCloneSeq(typ *abi.Type, seq *cloneSeq, baseOffset uintptr) { 73 atyp := typ.ArrayType() 74 etyp := atyp.Elem 75 offset := baseOffset 76 for range atyp.Len { 77 switch etyp.Kind() { 78 case abi.String: 79 seq.stringOffsets = append(seq.stringOffsets, offset) 80 case abi.Struct: 81 buildStructCloneSeq(etyp, seq, offset) 82 case abi.Array: 83 buildArrayCloneSeq(etyp, seq, offset) 84 } 85 offset += etyp.Size() 86 align := uintptr(etyp.FieldAlign()) 87 offset = (offset + align - 1) &^ (align - 1) 88 } 89} 90