blob: 2baca9bcfcc30602f27516e5e2f916536d4b3288 [file] [log] [blame]
Damien Neil8012b442019-01-18 09:32:24 -08001// 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 fileinit
8
9import (
10 "sync"
11 "unsafe"
12
13 pref "github.com/golang/protobuf/v2/reflect/protoreflect"
14)
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
31// AppendFullName is equivalent to protoreflect.FullName.Append,
32// but optimized for large batches where each name has a shared lifetime.
33func (nb *nameBuilder) AppendFullName(prefix pref.FullName, name []byte) fullName {
34 n := len(prefix) + len(".") + len(name)
35 if len(prefix) == 0 {
36 n -= len(".")
37 }
38 nb.grow(n)
39 nb.sb.WriteString(string(prefix))
40 nb.sb.WriteByte('.')
41 nb.sb.Write(name)
42 return fullName{
43 shortLen: len(name),
44 fullName: pref.FullName(nb.last(n)),
45 }
46}
47
48// MakeString is equivalent to string(b), but optimized for large batches
49// with a shared lifetime.
50func (nb *nameBuilder) MakeString(b []byte) string {
51 nb.grow(len(b))
52 nb.sb.Write(b)
53 return nb.last(len(b))
54}
55
56// MakeJSONName creates a JSON name from the protobuf short name.
57func (nb *nameBuilder) MakeJSONName(s pref.Name) string {
58 nb.grow(len(s))
59 var n int
60 var wasUnderscore bool
61 for i := 0; i < len(s); i++ { // proto identifiers are always ASCII
62 c := s[i]
63 if c != '_' {
64 isLower := 'a' <= c && c <= 'z'
65 if wasUnderscore && isLower {
66 c -= 'a' - 'A'
67 }
68 nb.sb.WriteByte(c)
69 n++
70 }
71 wasUnderscore = c == '_'
72 }
73 return nb.last(n)
74}
75
76func (nb *nameBuilder) last(n int) string {
77 s := nb.sb.String()
78 return s[len(s)-n:]
79}
80
81func (nb *nameBuilder) grow(n int) {
82 const batchSize = 1 << 16
83 if nb.sb.Cap()-nb.sb.Len() < n {
84 nb.sb.Reset()
85 nb.sb.Grow(batchSize)
86 }
87}
88
89// stringsBuilder is a simplified copy of the strings.Builder from Go1.12:
90// * removed the shallow copy check
91// * removed methods that we do not use (e.g. WriteRune)
92//
93// A forked version is used:
94// * to enable Go1.9 support, but strings.Builder was added in Go1.10
95// * for the Cap method, which was missing until Go1.12
96//
97// TODO: Remove this when Go1.12 is the minimally supported toolchain version.
98type stringBuilder struct {
99 buf []byte
100}
101
102func (b *stringBuilder) String() string {
103 return *(*string)(unsafe.Pointer(&b.buf))
104}
105func (b *stringBuilder) Len() int {
106 return len(b.buf)
107}
108func (b *stringBuilder) Cap() int {
109 return cap(b.buf)
110}
111func (b *stringBuilder) Reset() {
112 b.buf = nil
113}
114func (b *stringBuilder) grow(n int) {
115 buf := make([]byte, len(b.buf), 2*cap(b.buf)+n)
116 copy(buf, b.buf)
117 b.buf = buf
118}
119func (b *stringBuilder) Grow(n int) {
120 if n < 0 {
121 panic("stringBuilder.Grow: negative count")
122 }
123 if cap(b.buf)-len(b.buf) < n {
124 b.grow(n)
125 }
126}
127func (b *stringBuilder) Write(p []byte) (int, error) {
128 b.buf = append(b.buf, p...)
129 return len(p), nil
130}
131func (b *stringBuilder) WriteByte(c byte) error {
132 b.buf = append(b.buf, c)
133 return nil
134}
135func (b *stringBuilder) WriteString(s string) (int, error) {
136 b.buf = append(b.buf, s...)
137 return len(s), nil
138}