strings_unsafe.go (2541B)
1 // Copyright 2018 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 //go:build !purego && !appengine 6 // +build !purego,!appengine 7 8 package strs 9 10 import ( 11 "unsafe" 12 13 "google.golang.org/protobuf/reflect/protoreflect" 14 ) 15 16 type ( 17 stringHeader struct { 18 Data unsafe.Pointer 19 Len int 20 } 21 sliceHeader struct { 22 Data unsafe.Pointer 23 Len int 24 Cap int 25 } 26 ) 27 28 // UnsafeString returns an unsafe string reference of b. 29 // The caller must treat the input slice as immutable. 30 // 31 // WARNING: Use carefully. The returned result must not leak to the end user 32 // unless the input slice is provably immutable. 33 func UnsafeString(b []byte) (s string) { 34 src := (*sliceHeader)(unsafe.Pointer(&b)) 35 dst := (*stringHeader)(unsafe.Pointer(&s)) 36 dst.Data = src.Data 37 dst.Len = src.Len 38 return s 39 } 40 41 // UnsafeBytes returns an unsafe bytes slice reference of s. 42 // The caller must treat returned slice as immutable. 43 // 44 // WARNING: Use carefully. The returned result must not leak to the end user. 45 func UnsafeBytes(s string) (b []byte) { 46 src := (*stringHeader)(unsafe.Pointer(&s)) 47 dst := (*sliceHeader)(unsafe.Pointer(&b)) 48 dst.Data = src.Data 49 dst.Len = src.Len 50 dst.Cap = src.Len 51 return b 52 } 53 54 // Builder builds a set of strings with shared lifetime. 55 // This differs from strings.Builder, which is for building a single string. 56 type Builder struct { 57 buf []byte 58 } 59 60 // AppendFullName is equivalent to protoreflect.FullName.Append, 61 // but optimized for large batches where each name has a shared lifetime. 62 func (sb *Builder) AppendFullName(prefix protoreflect.FullName, name protoreflect.Name) protoreflect.FullName { 63 n := len(prefix) + len(".") + len(name) 64 if len(prefix) == 0 { 65 n -= len(".") 66 } 67 sb.grow(n) 68 sb.buf = append(sb.buf, prefix...) 69 sb.buf = append(sb.buf, '.') 70 sb.buf = append(sb.buf, name...) 71 return protoreflect.FullName(sb.last(n)) 72 } 73 74 // MakeString is equivalent to string(b), but optimized for large batches 75 // with a shared lifetime. 76 func (sb *Builder) MakeString(b []byte) string { 77 sb.grow(len(b)) 78 sb.buf = append(sb.buf, b...) 79 return sb.last(len(b)) 80 } 81 82 func (sb *Builder) grow(n int) { 83 if cap(sb.buf)-len(sb.buf) >= n { 84 return 85 } 86 87 // Unlike strings.Builder, we do not need to copy over the contents 88 // of the old buffer since our builder provides no API for 89 // retrieving previously created strings. 90 sb.buf = make([]byte, 0, 2*(cap(sb.buf)+n)) 91 } 92 93 func (sb *Builder) last(n int) string { 94 return UnsafeString(sb.buf[len(sb.buf)-n:]) 95 }