195 lines
4.1 KiB
Go
195 lines
4.1 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"unicode"
|
|
|
|
"zombiezen.com/go/capnproto2"
|
|
)
|
|
|
|
type staticData struct {
|
|
name string
|
|
buf []byte
|
|
}
|
|
|
|
func (sd *staticData) init(fileID uint64) {
|
|
sd.name = fmt.Sprintf("x_%x", fileID)
|
|
sd.buf = make([]byte, 0, 4096)
|
|
}
|
|
|
|
func (sd *staticData) copyData(obj capnp.Ptr) (staticDataRef, error) {
|
|
m, _, err := capnp.NewMessage(capnp.SingleSegment(nil))
|
|
if err != nil {
|
|
return staticDataRef{}, err
|
|
}
|
|
err = m.SetRootPtr(obj)
|
|
if err != nil {
|
|
return staticDataRef{}, err
|
|
}
|
|
data, err := m.Marshal()
|
|
if err != nil {
|
|
return staticDataRef{}, err
|
|
}
|
|
ref := staticDataRef{data: sd}
|
|
ref.Start = len(sd.buf)
|
|
sd.buf = append(sd.buf, data...)
|
|
ref.End = len(sd.buf)
|
|
return ref, nil
|
|
}
|
|
|
|
type staticDataRef struct {
|
|
data *staticData
|
|
Start, End int
|
|
}
|
|
|
|
func (ref staticDataRef) IsValid() bool {
|
|
return ref.Start < ref.End
|
|
}
|
|
|
|
func (ref staticDataRef) String() string {
|
|
return fmt.Sprintf("%s[%d:%d]", ref.data.name, ref.Start, ref.End)
|
|
}
|
|
|
|
type imports struct {
|
|
specs []importSpec
|
|
used map[string]bool // keyed on import path
|
|
}
|
|
|
|
var capnpImportSpec = importSpec{path: capnpImport, name: "capnp"}
|
|
|
|
func (i *imports) init() {
|
|
i.specs = nil
|
|
i.used = make(map[string]bool)
|
|
|
|
i.reserve(capnpImportSpec)
|
|
i.reserve(importSpec{path: schemasImport, name: "schemas"})
|
|
i.reserve(importSpec{path: serverImport, name: "server"})
|
|
i.reserve(importSpec{path: textImport, name: "text"})
|
|
i.reserve(importSpec{path: contextImport, name: "context"})
|
|
|
|
i.reserve(importSpec{path: "math", name: "math"})
|
|
i.reserve(importSpec{path: "strconv", name: "strconv"})
|
|
}
|
|
|
|
func (i *imports) Capnp() string {
|
|
return i.add(importSpec{path: capnpImport, name: "capnp"})
|
|
}
|
|
|
|
func (i *imports) Schemas() string {
|
|
return i.add(importSpec{path: schemasImport, name: "schemas"})
|
|
}
|
|
|
|
func (i *imports) Server() string {
|
|
return i.add(importSpec{path: serverImport, name: "server"})
|
|
}
|
|
|
|
func (i *imports) Text() string {
|
|
return i.add(importSpec{path: textImport, name: "text"})
|
|
}
|
|
|
|
func (i *imports) Context() string {
|
|
return i.add(importSpec{path: contextImport, name: "context"})
|
|
}
|
|
|
|
func (i *imports) Math() string {
|
|
return i.add(importSpec{path: "math", name: "math"})
|
|
}
|
|
|
|
func (i *imports) Strconv() string {
|
|
return i.add(importSpec{path: "strconv", name: "strconv"})
|
|
}
|
|
|
|
func (i *imports) usedImports() []importSpec {
|
|
specs := make([]importSpec, 0, len(i.specs))
|
|
for _, s := range i.specs {
|
|
if i.used[s.path] {
|
|
specs = append(specs, s)
|
|
}
|
|
}
|
|
return specs
|
|
}
|
|
|
|
func (i *imports) byPath(path string) (spec importSpec, ok bool) {
|
|
for _, spec = range i.specs {
|
|
if spec.path == path {
|
|
return spec, true
|
|
}
|
|
}
|
|
return importSpec{}, false
|
|
}
|
|
|
|
func (i *imports) byName(name string) (spec importSpec, ok bool) {
|
|
for _, spec = range i.specs {
|
|
if spec.name == name {
|
|
return spec, true
|
|
}
|
|
}
|
|
return importSpec{}, false
|
|
}
|
|
|
|
func (i *imports) add(spec importSpec) (name string) {
|
|
name = i.reserve(spec)
|
|
i.used[spec.path] = true
|
|
return name
|
|
}
|
|
|
|
// reserve adds an import spec without marking it as used.
|
|
func (i *imports) reserve(spec importSpec) (name string) {
|
|
if ispec, ok := i.byPath(spec.path); ok {
|
|
return ispec.name
|
|
}
|
|
if spec.name == "" {
|
|
spec.name = pkgFromImport(spec.path)
|
|
}
|
|
if _, found := i.byName(spec.name); found {
|
|
for base, n := spec.name, uint64(2); ; n++ {
|
|
spec.name = base + strconv.FormatUint(n, 10)
|
|
if _, found = i.byName(spec.name); !found {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
i.specs = append(i.specs, spec)
|
|
return spec.name
|
|
}
|
|
|
|
func pkgFromImport(path string) string {
|
|
if i := strings.LastIndex(path, "/"); i != -1 {
|
|
path = path[i+1:]
|
|
}
|
|
p := []rune(path)
|
|
n := 0
|
|
for _, r := range p {
|
|
if isIdent(r) {
|
|
p[n] = r
|
|
n++
|
|
}
|
|
}
|
|
if n == 0 || !isLower(p[0]) {
|
|
return "pkg" + string(p[:n])
|
|
}
|
|
return string(p[:n])
|
|
}
|
|
|
|
func isLower(r rune) bool {
|
|
return 'a' <= r && r <= 'z' || r == '_'
|
|
}
|
|
|
|
func isIdent(r rune) bool {
|
|
return isLower(r) || 'A' <= r && r <= 'Z' || r >= 0x80 && unicode.IsLetter(r)
|
|
}
|
|
|
|
type importSpec struct {
|
|
path string
|
|
name string
|
|
}
|
|
|
|
func (spec importSpec) String() string {
|
|
if spec.name == "" {
|
|
return strconv.Quote(spec.path)
|
|
}
|
|
return spec.name + " " + strconv.Quote(spec.path)
|
|
}
|