blob: 317114fe23e5abe1a9b530c9cb18d9cb26099a70 [file] [log] [blame]
Joe Tsaided92f82018-09-21 11:54:57 -07001// 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
Joe Tsaibc534a92018-12-18 16:07:48 -08005// +build !purego,!appengine
Joe Tsaided92f82018-09-21 11:54:57 -07006
Joe Tsaid8881392019-06-06 13:01:53 -07007package filedesc
Joe Tsaided92f82018-09-21 11:54:57 -07008
9import (
Joe Tsaided92f82018-09-21 11:54:57 -070010 "sync"
11 "unsafe"
12
Damien Neile89e6242019-05-13 23:55:40 -070013 pref "google.golang.org/protobuf/reflect/protoreflect"
Joe Tsaided92f82018-09-21 11:54:57 -070014)
15
16var nameBuilderPool = sync.Pool{
17 New: func() interface{} { return new(nameBuilder) },
18}
19
20func getNameBuilder() *nameBuilder {
21 return nameBuilderPool.Get().(*nameBuilder)
22}
23func putNameBuilder(b *nameBuilder) {
24 nameBuilderPool.Put(b)
25}
26
27type nameBuilder struct {
28 sb stringBuilder
29}
30
Joe Tsaid8881392019-06-06 13:01:53 -070031// MakeFullName converts b to a protoreflect.FullName,
32// where b must start with a leading dot.
33func (nb *nameBuilder) MakeFullName(b []byte) pref.FullName {
34 if len(b) == 0 || b[0] != '.' {
35 panic("name reference must be fully qualified")
36 }
37 return pref.FullName(nb.MakeString(b[1:]))
38}
39
40// AppendFullName is equivalent to protoreflect.FullName.Append,
41// but optimized for large batches where each name has a shared lifetime.
42func (nb *nameBuilder) AppendFullName(prefix pref.FullName, name []byte) pref.FullName {
Joe Tsaided92f82018-09-21 11:54:57 -070043 n := len(prefix) + len(".") + len(name)
Joe Tsaid8881392019-06-06 13:01:53 -070044 if len(prefix) == 0 {
45 n -= len(".")
Joe Tsaided92f82018-09-21 11:54:57 -070046 }
Joe Tsaid8881392019-06-06 13:01:53 -070047 nb.grow(n)
48 nb.sb.WriteString(string(prefix))
49 nb.sb.WriteByte('.')
50 nb.sb.Write(name)
51 return pref.FullName(nb.last(n))
52}
53
54// MakeString is equivalent to string(b), but optimized for large batches
55// with a shared lifetime.
56func (nb *nameBuilder) MakeString(b []byte) string {
57 nb.grow(len(b))
58 nb.sb.Write(b)
59 return nb.last(len(b))
60}
61
62func (nb *nameBuilder) last(n int) string {
63 s := nb.sb.String()
64 return s[len(s)-n:]
65}
66
67func (nb *nameBuilder) grow(n int) {
68 const batchSize = 1 << 16
69 if nb.sb.Cap()-nb.sb.Len() < n {
70 nb.sb.Reset()
71 nb.sb.Grow(batchSize)
Joe Tsaided92f82018-09-21 11:54:57 -070072 }
Joe Tsaided92f82018-09-21 11:54:57 -070073}
74
75// stringsBuilder is a simplified copy of the strings.Builder from Go1.12:
76// * removed the shallow copy check
77// * removed methods that we do not use (e.g. WriteRune)
78//
79// A forked version is used:
80// * to enable Go1.9 support, but strings.Builder was added in Go1.10
81// * for the Cap method, which was missing until Go1.12
82//
83// TODO: Remove this when Go1.12 is the minimally supported toolchain version.
84type stringBuilder struct {
85 buf []byte
86}
87
88func (b *stringBuilder) String() string {
89 return *(*string)(unsafe.Pointer(&b.buf))
90}
91func (b *stringBuilder) Len() int {
92 return len(b.buf)
93}
94func (b *stringBuilder) Cap() int {
95 return cap(b.buf)
96}
97func (b *stringBuilder) Reset() {
98 b.buf = nil
99}
100func (b *stringBuilder) grow(n int) {
101 buf := make([]byte, len(b.buf), 2*cap(b.buf)+n)
102 copy(buf, b.buf)
103 b.buf = buf
104}
105func (b *stringBuilder) Grow(n int) {
106 if n < 0 {
107 panic("stringBuilder.Grow: negative count")
108 }
109 if cap(b.buf)-len(b.buf) < n {
110 b.grow(n)
111 }
112}
113func (b *stringBuilder) Write(p []byte) (int, error) {
114 b.buf = append(b.buf, p...)
115 return len(p), nil
116}
117func (b *stringBuilder) WriteByte(c byte) error {
118 b.buf = append(b.buf, c)
119 return nil
120}
121func (b *stringBuilder) WriteString(s string) (int, error) {
122 b.buf = append(b.buf, s...)
123 return len(s), nil
124}