language/internal: include body of old compose

This is like the old code but different. Prevents
some internals from having to be exposed.

Change-Id: Ibfd0c98e7ed276faab20789fd753f8ef5cf6bda5
Reviewed-on: https://go-review.googlesource.com/95820
Run-TryBot: Marcel van Lohuizen <mpvl@golang.org>
Reviewed-by: Nigel Tao <nigeltao@golang.org>
diff --git a/language/internal/compose.go b/language/internal/compose.go
new file mode 100644
index 0000000..772c3d4
--- /dev/null
+++ b/language/internal/compose.go
@@ -0,0 +1,101 @@
+// 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.
+
+package language
+
+import (
+	"sort"
+	"strings"
+)
+
+type Builder struct {
+	Tag Tag
+
+	Private string // the x extension
+	Ext     []string
+	Variant []string
+
+	Err error
+}
+
+func (b *Builder) Make() Tag {
+	t := b.Tag
+
+	if len(b.Ext) > 0 || len(b.Variant) > 0 {
+		sort.Sort(sortVariants(b.Variant))
+		sort.Strings(b.Ext)
+		if b.Private != "" {
+			b.Ext = append(b.Ext, b.Private)
+		}
+		n := maxCoreSize + tokenLen(b.Variant...) + tokenLen(b.Ext...)
+		buf := make([]byte, n)
+		p := t.genCoreBytes(buf)
+		t.pVariant = byte(p)
+		p += appendTokens(buf[p:], b.Variant...)
+		t.pExt = uint16(p)
+		p += appendTokens(buf[p:], b.Ext...)
+		t.str = string(buf[:p])
+	} else if b.Private != "" {
+		t.str = b.Private
+		t.RemakeString()
+	}
+	return t
+}
+
+func (b *Builder) SetTag(t Tag) {
+	b.Tag.LangID = t.LangID
+	b.Tag.RegionID = t.RegionID
+	b.Tag.ScriptID = t.ScriptID
+	// TODO: optimize
+	b.Variant = b.Variant[:0]
+	if variants := t.Variants(); variants != "" {
+		for _, vr := range strings.Split(variants[1:], "-") {
+			b.Variant = append(b.Variant, vr)
+		}
+	}
+	b.Ext, b.Private = b.Ext[:0], ""
+	for _, e := range t.Extensions() {
+		b.AddExt(e)
+	}
+}
+
+func (b *Builder) AddExt(e string) {
+	if e == "" {
+	} else if e[0] == 'x' {
+		b.Private = e
+	} else {
+		b.Ext = append(b.Ext, e)
+	}
+}
+
+func tokenLen(token ...string) (n int) {
+	for _, t := range token {
+		n += len(t) + 1
+	}
+	return
+}
+
+func appendTokens(b []byte, token ...string) int {
+	p := 0
+	for _, t := range token {
+		b[p] = '-'
+		copy(b[p+1:], t)
+		p += 1 + len(t)
+	}
+	return p
+}
+
+type sortVariants []string
+
+func (s sortVariants) Len() int {
+	return len(s)
+}
+
+func (s sortVariants) Swap(i, j int) {
+	s[j], s[i] = s[i], s[j]
+}
+
+func (s sortVariants) Less(i, j int) bool {
+	return variantIndex[s[i]] < variantIndex[s[j]]
+}