Source file src/unique/clone.go

     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  
     5  package unique
     6  
     7  import (
     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).
    21  func 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.
    30  var singleStringClone = cloneSeq{stringOffsets: []uintptr{0}}
    31  
    32  // cloneSeq describes how to clone a value of a particular type.
    33  type cloneSeq struct {
    34  	stringOffsets []uintptr
    35  }
    36  
    37  // makeCloneSeq creates a cloneSeq for a type.
    38  func 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.
    56  func 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.
    72  func 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  

View as plain text