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