// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build !purego,!appengine

package filedesc

import (
	"sync"
	"unsafe"

	pref "google.golang.org/protobuf/reflect/protoreflect"
)

var nameBuilderPool = sync.Pool{
	New: func() interface{} { return new(nameBuilder) },
}

func getNameBuilder() *nameBuilder {
	return nameBuilderPool.Get().(*nameBuilder)
}
func putNameBuilder(b *nameBuilder) {
	nameBuilderPool.Put(b)
}

type nameBuilder struct {
	sb stringBuilder
}

// MakeFullName converts b to a protoreflect.FullName,
// where b must start with a leading dot.
func (nb *nameBuilder) MakeFullName(b []byte) pref.FullName {
	if len(b) == 0 || b[0] != '.' {
		panic("name reference must be fully qualified")
	}
	return pref.FullName(nb.MakeString(b[1:]))
}

// AppendFullName is equivalent to protoreflect.FullName.Append,
// but optimized for large batches where each name has a shared lifetime.
func (nb *nameBuilder) AppendFullName(prefix pref.FullName, name []byte) pref.FullName {
	n := len(prefix) + len(".") + len(name)
	if len(prefix) == 0 {
		n -= len(".")
	}
	nb.grow(n)
	nb.sb.WriteString(string(prefix))
	nb.sb.WriteByte('.')
	nb.sb.Write(name)
	return pref.FullName(nb.last(n))
}

// MakeString is equivalent to string(b), but optimized for large batches
// with a shared lifetime.
func (nb *nameBuilder) MakeString(b []byte) string {
	nb.grow(len(b))
	nb.sb.Write(b)
	return nb.last(len(b))
}

func (nb *nameBuilder) last(n int) string {
	s := nb.sb.String()
	return s[len(s)-n:]
}

func (nb *nameBuilder) grow(n int) {
	const batchSize = 1 << 16
	if nb.sb.Cap()-nb.sb.Len() < n {
		nb.sb.Reset()
		nb.sb.Grow(batchSize)
	}
}

// stringsBuilder is a simplified copy of the strings.Builder from Go1.12:
//	* removed the shallow copy check
//	* removed methods that we do not use (e.g. WriteRune)
//
// A forked version is used:
//	* to enable Go1.9 support, but strings.Builder was added in Go1.10
//	* for the Cap method, which was missing until Go1.12
//
// TODO: Remove this when Go1.12 is the minimally supported toolchain version.
type stringBuilder struct {
	buf []byte
}

func (b *stringBuilder) String() string {
	return *(*string)(unsafe.Pointer(&b.buf))
}
func (b *stringBuilder) Len() int {
	return len(b.buf)
}
func (b *stringBuilder) Cap() int {
	return cap(b.buf)
}
func (b *stringBuilder) Reset() {
	b.buf = nil
}
func (b *stringBuilder) grow(n int) {
	buf := make([]byte, len(b.buf), 2*cap(b.buf)+n)
	copy(buf, b.buf)
	b.buf = buf
}
func (b *stringBuilder) Grow(n int) {
	if n < 0 {
		panic("stringBuilder.Grow: negative count")
	}
	if cap(b.buf)-len(b.buf) < n {
		b.grow(n)
	}
}
func (b *stringBuilder) Write(p []byte) (int, error) {
	b.buf = append(b.buf, p...)
	return len(p), nil
}
func (b *stringBuilder) WriteByte(c byte) error {
	b.buf = append(b.buf, c)
	return nil
}
func (b *stringBuilder) WriteString(s string) (int, error) {
	b.buf = append(b.buf, s...)
	return len(s), nil
}
