Merge pull request #673 from mrunalp/runtime_spec_v1
Update to OCI runtime spec v1.0.0
This commit is contained in:
commit
f8f9488e02
83 changed files with 1020 additions and 14970 deletions
|
@ -44,7 +44,7 @@ RUN mkdir -p /usr/src/criu \
|
||||||
&& rm -rf /usr/src/criu
|
&& rm -rf /usr/src/criu
|
||||||
|
|
||||||
# Install runc
|
# Install runc
|
||||||
ENV RUNC_COMMIT 639454475cb9c8b861cc599f8bcd5c8c790ae402
|
ENV RUNC_COMMIT c5ec25487693612aed95673800863e134785f946
|
||||||
RUN set -x \
|
RUN set -x \
|
||||||
&& export GOPATH="$(mktemp -d)" \
|
&& export GOPATH="$(mktemp -d)" \
|
||||||
&& git clone https://github.com/opencontainers/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
|
&& git clone https://github.com/opencontainers/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
|
||||||
|
|
|
@ -450,11 +450,11 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
|
||||||
|
|
||||||
memoryLimit := resources.MemoryLimitInBytes
|
memoryLimit := resources.MemoryLimitInBytes
|
||||||
if memoryLimit != 0 {
|
if memoryLimit != 0 {
|
||||||
specgen.SetLinuxResourcesMemoryLimit(uint64(memoryLimit))
|
specgen.SetLinuxResourcesMemoryLimit(memoryLimit)
|
||||||
}
|
}
|
||||||
|
|
||||||
oomScoreAdj := resources.OomScoreAdj
|
oomScoreAdj := resources.OomScoreAdj
|
||||||
specgen.SetLinuxResourcesOOMScoreAdj(int(oomScoreAdj))
|
specgen.SetProcessOOMScoreAdj(int(oomScoreAdj))
|
||||||
}
|
}
|
||||||
|
|
||||||
if sb.cgroupParent != "" {
|
if sb.cgroupParent != "" {
|
||||||
|
|
|
@ -403,7 +403,7 @@ func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest
|
||||||
|
|
||||||
// Set OOM score adjust of the infra container to be very low
|
// Set OOM score adjust of the infra container to be very low
|
||||||
// so it doesn't get killed.
|
// so it doesn't get killed.
|
||||||
g.SetLinuxResourcesOOMScoreAdj(PodInfraOOMAdj)
|
g.SetProcessOOMScoreAdj(PodInfraOOMAdj)
|
||||||
|
|
||||||
hostNetwork := req.GetConfig().GetLinux().GetSecurityContext().GetNamespaceOptions().HostNetwork
|
hostNetwork := req.GetConfig().GetLinux().GetSecurityContext().GetNamespaceOptions().HostNetwork
|
||||||
|
|
||||||
|
|
|
@ -11,10 +11,10 @@ github.com/containernetworking/cni v0.4.0
|
||||||
google.golang.org/grpc v1.0.1-GA https://github.com/grpc/grpc-go
|
google.golang.org/grpc v1.0.1-GA https://github.com/grpc/grpc-go
|
||||||
github.com/opencontainers/selinux v1.0.0-rc1
|
github.com/opencontainers/selinux v1.0.0-rc1
|
||||||
github.com/opencontainers/go-digest v1.0.0-rc0
|
github.com/opencontainers/go-digest v1.0.0-rc0
|
||||||
github.com/opencontainers/runtime-tools 96cb7c28a32209a530b94bceb491e0137d1f5849
|
github.com/opencontainers/runtime-tools 20db5990713e97e64bc2d340531d61f2edf4cccb
|
||||||
github.com/opencontainers/runc b263a43430ac6996a4302b891688544225197294
|
github.com/opencontainers/runc c5ec25487693612aed95673800863e134785f946
|
||||||
github.com/opencontainers/image-spec v1.0.0-rc6
|
github.com/opencontainers/image-spec v1.0.0-rc6
|
||||||
github.com/opencontainers/runtime-spec v1.0.0-rc5
|
github.com/opencontainers/runtime-spec v1.0.0
|
||||||
github.com/juju/ratelimit acf38b000a03e4ab89e40f20f1e548f4e6ac7f72
|
github.com/juju/ratelimit acf38b000a03e4ab89e40f20f1e548f4e6ac7f72
|
||||||
github.com/tchap/go-patricia v2.2.6
|
github.com/tchap/go-patricia v2.2.6
|
||||||
gopkg.in/cheggaaa/pb.v1 v1.0.7
|
gopkg.in/cheggaaa/pb.v1 v1.0.7
|
||||||
|
|
934
vendor/github.com/Microsoft/hcsshim/mksyscall_windows.go
generated
vendored
934
vendor/github.com/Microsoft/hcsshim/mksyscall_windows.go
generated
vendored
|
@ -1,934 +0,0 @@
|
||||||
// Copyright 2013 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 ignore
|
|
||||||
|
|
||||||
/*
|
|
||||||
mksyscall_windows generates windows system call bodies
|
|
||||||
|
|
||||||
It parses all files specified on command line containing function
|
|
||||||
prototypes (like syscall_windows.go) and prints system call bodies
|
|
||||||
to standard output.
|
|
||||||
|
|
||||||
The prototypes are marked by lines beginning with "//sys" and read
|
|
||||||
like func declarations if //sys is replaced by func, but:
|
|
||||||
|
|
||||||
* The parameter lists must give a name for each argument. This
|
|
||||||
includes return parameters.
|
|
||||||
|
|
||||||
* The parameter lists must give a type for each argument:
|
|
||||||
the (x, y, z int) shorthand is not allowed.
|
|
||||||
|
|
||||||
* If the return parameter is an error number, it must be named err.
|
|
||||||
|
|
||||||
* If go func name needs to be different from it's winapi dll name,
|
|
||||||
the winapi name could be specified at the end, after "=" sign, like
|
|
||||||
//sys LoadLibrary(libname string) (handle uint32, err error) = LoadLibraryA
|
|
||||||
|
|
||||||
* Each function that returns err needs to supply a condition, that
|
|
||||||
return value of winapi will be tested against to detect failure.
|
|
||||||
This would set err to windows "last-error", otherwise it will be nil.
|
|
||||||
The value can be provided at end of //sys declaration, like
|
|
||||||
//sys LoadLibrary(libname string) (handle uint32, err error) [failretval==-1] = LoadLibraryA
|
|
||||||
and is [failretval==0] by default.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
mksyscall_windows [flags] [path ...]
|
|
||||||
|
|
||||||
The flags are:
|
|
||||||
-output
|
|
||||||
Specify output file name (outputs to console if blank).
|
|
||||||
-trace
|
|
||||||
Generate print statement after every syscall.
|
|
||||||
*/
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"go/format"
|
|
||||||
"go/parser"
|
|
||||||
"go/token"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"text/template"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
filename = flag.String("output", "", "output file name (standard output if omitted)")
|
|
||||||
printTraceFlag = flag.Bool("trace", false, "generate print statement after every syscall")
|
|
||||||
systemDLL = flag.Bool("systemdll", true, "whether all DLLs should be loaded from the Windows system directory")
|
|
||||||
)
|
|
||||||
|
|
||||||
func trim(s string) string {
|
|
||||||
return strings.Trim(s, " \t")
|
|
||||||
}
|
|
||||||
|
|
||||||
var packageName string
|
|
||||||
|
|
||||||
func packagename() string {
|
|
||||||
return packageName
|
|
||||||
}
|
|
||||||
|
|
||||||
func syscalldot() string {
|
|
||||||
if packageName == "syscall" {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return "syscall."
|
|
||||||
}
|
|
||||||
|
|
||||||
// Param is function parameter
|
|
||||||
type Param struct {
|
|
||||||
Name string
|
|
||||||
Type string
|
|
||||||
fn *Fn
|
|
||||||
tmpVarIdx int
|
|
||||||
}
|
|
||||||
|
|
||||||
// tmpVar returns temp variable name that will be used to represent p during syscall.
|
|
||||||
func (p *Param) tmpVar() string {
|
|
||||||
if p.tmpVarIdx < 0 {
|
|
||||||
p.tmpVarIdx = p.fn.curTmpVarIdx
|
|
||||||
p.fn.curTmpVarIdx++
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("_p%d", p.tmpVarIdx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BoolTmpVarCode returns source code for bool temp variable.
|
|
||||||
func (p *Param) BoolTmpVarCode() string {
|
|
||||||
const code = `var %s uint32
|
|
||||||
if %s {
|
|
||||||
%s = 1
|
|
||||||
} else {
|
|
||||||
%s = 0
|
|
||||||
}`
|
|
||||||
tmp := p.tmpVar()
|
|
||||||
return fmt.Sprintf(code, tmp, p.Name, tmp, tmp)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SliceTmpVarCode returns source code for slice temp variable.
|
|
||||||
func (p *Param) SliceTmpVarCode() string {
|
|
||||||
const code = `var %s *%s
|
|
||||||
if len(%s) > 0 {
|
|
||||||
%s = &%s[0]
|
|
||||||
}`
|
|
||||||
tmp := p.tmpVar()
|
|
||||||
return fmt.Sprintf(code, tmp, p.Type[2:], p.Name, tmp, p.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringTmpVarCode returns source code for string temp variable.
|
|
||||||
func (p *Param) StringTmpVarCode() string {
|
|
||||||
errvar := p.fn.Rets.ErrorVarName()
|
|
||||||
if errvar == "" {
|
|
||||||
errvar = "_"
|
|
||||||
}
|
|
||||||
tmp := p.tmpVar()
|
|
||||||
const code = `var %s %s
|
|
||||||
%s, %s = %s(%s)`
|
|
||||||
s := fmt.Sprintf(code, tmp, p.fn.StrconvType(), tmp, errvar, p.fn.StrconvFunc(), p.Name)
|
|
||||||
if errvar == "-" {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
const morecode = `
|
|
||||||
if %s != nil {
|
|
||||||
return
|
|
||||||
}`
|
|
||||||
return s + fmt.Sprintf(morecode, errvar)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TmpVarCode returns source code for temp variable.
|
|
||||||
func (p *Param) TmpVarCode() string {
|
|
||||||
switch {
|
|
||||||
case p.Type == "bool":
|
|
||||||
return p.BoolTmpVarCode()
|
|
||||||
case strings.HasPrefix(p.Type, "[]"):
|
|
||||||
return p.SliceTmpVarCode()
|
|
||||||
default:
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TmpVarHelperCode returns source code for helper's temp variable.
|
|
||||||
func (p *Param) TmpVarHelperCode() string {
|
|
||||||
if p.Type != "string" {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return p.StringTmpVarCode()
|
|
||||||
}
|
|
||||||
|
|
||||||
// SyscallArgList returns source code fragments representing p parameter
|
|
||||||
// in syscall. Slices are translated into 2 syscall parameters: pointer to
|
|
||||||
// the first element and length.
|
|
||||||
func (p *Param) SyscallArgList() []string {
|
|
||||||
t := p.HelperType()
|
|
||||||
var s string
|
|
||||||
switch {
|
|
||||||
case t[0] == '*':
|
|
||||||
s = fmt.Sprintf("unsafe.Pointer(%s)", p.Name)
|
|
||||||
case t == "bool":
|
|
||||||
s = p.tmpVar()
|
|
||||||
case strings.HasPrefix(t, "[]"):
|
|
||||||
return []string{
|
|
||||||
fmt.Sprintf("uintptr(unsafe.Pointer(%s))", p.tmpVar()),
|
|
||||||
fmt.Sprintf("uintptr(len(%s))", p.Name),
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
s = p.Name
|
|
||||||
}
|
|
||||||
return []string{fmt.Sprintf("uintptr(%s)", s)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsError determines if p parameter is used to return error.
|
|
||||||
func (p *Param) IsError() bool {
|
|
||||||
return p.Name == "err" && p.Type == "error"
|
|
||||||
}
|
|
||||||
|
|
||||||
// HelperType returns type of parameter p used in helper function.
|
|
||||||
func (p *Param) HelperType() string {
|
|
||||||
if p.Type == "string" {
|
|
||||||
return p.fn.StrconvType()
|
|
||||||
}
|
|
||||||
return p.Type
|
|
||||||
}
|
|
||||||
|
|
||||||
// join concatenates parameters ps into a string with sep separator.
|
|
||||||
// Each parameter is converted into string by applying fn to it
|
|
||||||
// before conversion.
|
|
||||||
func join(ps []*Param, fn func(*Param) string, sep string) string {
|
|
||||||
if len(ps) == 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
a := make([]string, 0)
|
|
||||||
for _, p := range ps {
|
|
||||||
a = append(a, fn(p))
|
|
||||||
}
|
|
||||||
return strings.Join(a, sep)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rets describes function return parameters.
|
|
||||||
type Rets struct {
|
|
||||||
Name string
|
|
||||||
Type string
|
|
||||||
ReturnsError bool
|
|
||||||
FailCond string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrorVarName returns error variable name for r.
|
|
||||||
func (r *Rets) ErrorVarName() string {
|
|
||||||
if r.ReturnsError {
|
|
||||||
return "err"
|
|
||||||
}
|
|
||||||
if r.Type == "error" {
|
|
||||||
return r.Name
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToParams converts r into slice of *Param.
|
|
||||||
func (r *Rets) ToParams() []*Param {
|
|
||||||
ps := make([]*Param, 0)
|
|
||||||
if len(r.Name) > 0 {
|
|
||||||
ps = append(ps, &Param{Name: r.Name, Type: r.Type})
|
|
||||||
}
|
|
||||||
if r.ReturnsError {
|
|
||||||
ps = append(ps, &Param{Name: "err", Type: "error"})
|
|
||||||
}
|
|
||||||
return ps
|
|
||||||
}
|
|
||||||
|
|
||||||
// List returns source code of syscall return parameters.
|
|
||||||
func (r *Rets) List() string {
|
|
||||||
s := join(r.ToParams(), func(p *Param) string { return p.Name + " " + p.Type }, ", ")
|
|
||||||
if len(s) > 0 {
|
|
||||||
s = "(" + s + ")"
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrintList returns source code of trace printing part correspondent
|
|
||||||
// to syscall return values.
|
|
||||||
func (r *Rets) PrintList() string {
|
|
||||||
return join(r.ToParams(), func(p *Param) string { return fmt.Sprintf(`"%s=", %s, `, p.Name, p.Name) }, `", ", `)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetReturnValuesCode returns source code that accepts syscall return values.
|
|
||||||
func (r *Rets) SetReturnValuesCode() string {
|
|
||||||
if r.Name == "" && !r.ReturnsError {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
retvar := "r0"
|
|
||||||
if r.Name == "" {
|
|
||||||
retvar = "r1"
|
|
||||||
}
|
|
||||||
errvar := "_"
|
|
||||||
if r.ReturnsError {
|
|
||||||
errvar = "e1"
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s, _, %s := ", retvar, errvar)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Rets) useLongHandleErrorCode(retvar string) string {
|
|
||||||
const code = `if %s {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = %sEINVAL
|
|
||||||
}
|
|
||||||
}`
|
|
||||||
cond := retvar + " == 0"
|
|
||||||
if r.FailCond != "" {
|
|
||||||
cond = strings.Replace(r.FailCond, "failretval", retvar, 1)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf(code, cond, syscalldot())
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetErrorCode returns source code that sets return parameters.
|
|
||||||
func (r *Rets) SetErrorCode() string {
|
|
||||||
const code = `if r0 != 0 {
|
|
||||||
%s = %sErrno(r0)
|
|
||||||
}`
|
|
||||||
const hrCode = `if int32(r0) < 0 {
|
|
||||||
%s = %sErrno(win32FromHresult(r0))
|
|
||||||
}`
|
|
||||||
if r.Name == "" && !r.ReturnsError {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
if r.Name == "" {
|
|
||||||
return r.useLongHandleErrorCode("r1")
|
|
||||||
}
|
|
||||||
if r.Type == "error" {
|
|
||||||
if r.Name == "hr" {
|
|
||||||
return fmt.Sprintf(hrCode, r.Name, syscalldot())
|
|
||||||
} else {
|
|
||||||
return fmt.Sprintf(code, r.Name, syscalldot())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s := ""
|
|
||||||
switch {
|
|
||||||
case r.Type[0] == '*':
|
|
||||||
s = fmt.Sprintf("%s = (%s)(unsafe.Pointer(r0))", r.Name, r.Type)
|
|
||||||
case r.Type == "bool":
|
|
||||||
s = fmt.Sprintf("%s = r0 != 0", r.Name)
|
|
||||||
default:
|
|
||||||
s = fmt.Sprintf("%s = %s(r0)", r.Name, r.Type)
|
|
||||||
}
|
|
||||||
if !r.ReturnsError {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
return s + "\n\t" + r.useLongHandleErrorCode(r.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fn describes syscall function.
|
|
||||||
type Fn struct {
|
|
||||||
Name string
|
|
||||||
Params []*Param
|
|
||||||
Rets *Rets
|
|
||||||
PrintTrace bool
|
|
||||||
confirmproc bool
|
|
||||||
dllname string
|
|
||||||
dllfuncname string
|
|
||||||
src string
|
|
||||||
// TODO: get rid of this field and just use parameter index instead
|
|
||||||
curTmpVarIdx int // insure tmp variables have uniq names
|
|
||||||
}
|
|
||||||
|
|
||||||
// extractParams parses s to extract function parameters.
|
|
||||||
func extractParams(s string, f *Fn) ([]*Param, error) {
|
|
||||||
s = trim(s)
|
|
||||||
if s == "" {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
a := strings.Split(s, ",")
|
|
||||||
ps := make([]*Param, len(a))
|
|
||||||
for i := range ps {
|
|
||||||
s2 := trim(a[i])
|
|
||||||
b := strings.Split(s2, " ")
|
|
||||||
if len(b) != 2 {
|
|
||||||
b = strings.Split(s2, "\t")
|
|
||||||
if len(b) != 2 {
|
|
||||||
return nil, errors.New("Could not extract function parameter from \"" + s2 + "\"")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ps[i] = &Param{
|
|
||||||
Name: trim(b[0]),
|
|
||||||
Type: trim(b[1]),
|
|
||||||
fn: f,
|
|
||||||
tmpVarIdx: -1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ps, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// extractSection extracts text out of string s starting after start
|
|
||||||
// and ending just before end. found return value will indicate success,
|
|
||||||
// and prefix, body and suffix will contain correspondent parts of string s.
|
|
||||||
func extractSection(s string, start, end rune) (prefix, body, suffix string, found bool) {
|
|
||||||
s = trim(s)
|
|
||||||
if strings.HasPrefix(s, string(start)) {
|
|
||||||
// no prefix
|
|
||||||
body = s[1:]
|
|
||||||
} else {
|
|
||||||
a := strings.SplitN(s, string(start), 2)
|
|
||||||
if len(a) != 2 {
|
|
||||||
return "", "", s, false
|
|
||||||
}
|
|
||||||
prefix = a[0]
|
|
||||||
body = a[1]
|
|
||||||
}
|
|
||||||
a := strings.SplitN(body, string(end), 2)
|
|
||||||
if len(a) != 2 {
|
|
||||||
return "", "", "", false
|
|
||||||
}
|
|
||||||
return prefix, a[0], a[1], true
|
|
||||||
}
|
|
||||||
|
|
||||||
// newFn parses string s and return created function Fn.
|
|
||||||
func newFn(s string) (*Fn, error) {
|
|
||||||
s = trim(s)
|
|
||||||
f := &Fn{
|
|
||||||
Rets: &Rets{},
|
|
||||||
src: s,
|
|
||||||
PrintTrace: *printTraceFlag,
|
|
||||||
}
|
|
||||||
// function name and args
|
|
||||||
prefix, body, s, found := extractSection(s, '(', ')')
|
|
||||||
if !found || prefix == "" {
|
|
||||||
return nil, errors.New("Could not extract function name and parameters from \"" + f.src + "\"")
|
|
||||||
}
|
|
||||||
f.Name = prefix
|
|
||||||
var err error
|
|
||||||
f.Params, err = extractParams(body, f)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// return values
|
|
||||||
_, body, s, found = extractSection(s, '(', ')')
|
|
||||||
if found {
|
|
||||||
r, err := extractParams(body, f)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
switch len(r) {
|
|
||||||
case 0:
|
|
||||||
case 1:
|
|
||||||
if r[0].IsError() {
|
|
||||||
f.Rets.ReturnsError = true
|
|
||||||
} else {
|
|
||||||
f.Rets.Name = r[0].Name
|
|
||||||
f.Rets.Type = r[0].Type
|
|
||||||
}
|
|
||||||
case 2:
|
|
||||||
if !r[1].IsError() {
|
|
||||||
return nil, errors.New("Only last windows error is allowed as second return value in \"" + f.src + "\"")
|
|
||||||
}
|
|
||||||
f.Rets.ReturnsError = true
|
|
||||||
f.Rets.Name = r[0].Name
|
|
||||||
f.Rets.Type = r[0].Type
|
|
||||||
default:
|
|
||||||
return nil, errors.New("Too many return values in \"" + f.src + "\"")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// fail condition
|
|
||||||
_, body, s, found = extractSection(s, '[', ']')
|
|
||||||
if found {
|
|
||||||
f.Rets.FailCond = body
|
|
||||||
}
|
|
||||||
// dll and dll function names
|
|
||||||
s = trim(s)
|
|
||||||
if s == "" {
|
|
||||||
return f, nil
|
|
||||||
}
|
|
||||||
if !strings.HasPrefix(s, "=") {
|
|
||||||
return nil, errors.New("Could not extract dll name from \"" + f.src + "\"")
|
|
||||||
}
|
|
||||||
s = trim(s[1:])
|
|
||||||
a := strings.Split(s, ".")
|
|
||||||
switch len(a) {
|
|
||||||
case 1:
|
|
||||||
f.dllfuncname = a[0]
|
|
||||||
case 2:
|
|
||||||
f.dllname = a[0]
|
|
||||||
f.dllfuncname = a[1]
|
|
||||||
default:
|
|
||||||
return nil, errors.New("Could not extract dll name from \"" + f.src + "\"")
|
|
||||||
}
|
|
||||||
if f.dllfuncname[len(f.dllfuncname)-1] == '?' {
|
|
||||||
f.confirmproc = true
|
|
||||||
f.dllfuncname = f.dllfuncname[0 : len(f.dllfuncname)-1]
|
|
||||||
}
|
|
||||||
return f, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DLLName returns DLL name for function f.
|
|
||||||
func (f *Fn) DLLName() string {
|
|
||||||
if f.dllname == "" {
|
|
||||||
return "kernel32"
|
|
||||||
}
|
|
||||||
return f.dllname
|
|
||||||
}
|
|
||||||
|
|
||||||
// DLLName returns DLL function name for function f.
|
|
||||||
func (f *Fn) DLLFuncName() string {
|
|
||||||
if f.dllfuncname == "" {
|
|
||||||
return f.Name
|
|
||||||
}
|
|
||||||
return f.dllfuncname
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Fn) ConfirmProc() bool {
|
|
||||||
return f.confirmproc
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParamList returns source code for function f parameters.
|
|
||||||
func (f *Fn) ParamList() string {
|
|
||||||
return join(f.Params, func(p *Param) string { return p.Name + " " + p.Type }, ", ")
|
|
||||||
}
|
|
||||||
|
|
||||||
// HelperParamList returns source code for helper function f parameters.
|
|
||||||
func (f *Fn) HelperParamList() string {
|
|
||||||
return join(f.Params, func(p *Param) string { return p.Name + " " + p.HelperType() }, ", ")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParamPrintList returns source code of trace printing part correspondent
|
|
||||||
// to syscall input parameters.
|
|
||||||
func (f *Fn) ParamPrintList() string {
|
|
||||||
return join(f.Params, func(p *Param) string { return fmt.Sprintf(`"%s=", %s, `, p.Name, p.Name) }, `", ", `)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParamCount return number of syscall parameters for function f.
|
|
||||||
func (f *Fn) ParamCount() int {
|
|
||||||
n := 0
|
|
||||||
for _, p := range f.Params {
|
|
||||||
n += len(p.SyscallArgList())
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
// SyscallParamCount determines which version of Syscall/Syscall6/Syscall9/...
|
|
||||||
// to use. It returns parameter count for correspondent SyscallX function.
|
|
||||||
func (f *Fn) SyscallParamCount() int {
|
|
||||||
n := f.ParamCount()
|
|
||||||
switch {
|
|
||||||
case n <= 3:
|
|
||||||
return 3
|
|
||||||
case n <= 6:
|
|
||||||
return 6
|
|
||||||
case n <= 9:
|
|
||||||
return 9
|
|
||||||
case n <= 12:
|
|
||||||
return 12
|
|
||||||
case n <= 15:
|
|
||||||
return 15
|
|
||||||
default:
|
|
||||||
panic("too many arguments to system call")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Syscall determines which SyscallX function to use for function f.
|
|
||||||
func (f *Fn) Syscall() string {
|
|
||||||
c := f.SyscallParamCount()
|
|
||||||
if c == 3 {
|
|
||||||
return syscalldot() + "Syscall"
|
|
||||||
}
|
|
||||||
return syscalldot() + "Syscall" + strconv.Itoa(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SyscallParamList returns source code for SyscallX parameters for function f.
|
|
||||||
func (f *Fn) SyscallParamList() string {
|
|
||||||
a := make([]string, 0)
|
|
||||||
for _, p := range f.Params {
|
|
||||||
a = append(a, p.SyscallArgList()...)
|
|
||||||
}
|
|
||||||
for len(a) < f.SyscallParamCount() {
|
|
||||||
a = append(a, "0")
|
|
||||||
}
|
|
||||||
return strings.Join(a, ", ")
|
|
||||||
}
|
|
||||||
|
|
||||||
// HelperCallParamList returns source code of call into function f helper.
|
|
||||||
func (f *Fn) HelperCallParamList() string {
|
|
||||||
a := make([]string, 0, len(f.Params))
|
|
||||||
for _, p := range f.Params {
|
|
||||||
s := p.Name
|
|
||||||
if p.Type == "string" {
|
|
||||||
s = p.tmpVar()
|
|
||||||
}
|
|
||||||
a = append(a, s)
|
|
||||||
}
|
|
||||||
return strings.Join(a, ", ")
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsUTF16 is true, if f is W (utf16) function. It is false
|
|
||||||
// for all A (ascii) functions.
|
|
||||||
func (_ *Fn) IsUTF16() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// StrconvFunc returns name of Go string to OS string function for f.
|
|
||||||
func (f *Fn) StrconvFunc() string {
|
|
||||||
if f.IsUTF16() {
|
|
||||||
return syscalldot() + "UTF16PtrFromString"
|
|
||||||
}
|
|
||||||
return syscalldot() + "BytePtrFromString"
|
|
||||||
}
|
|
||||||
|
|
||||||
// StrconvType returns Go type name used for OS string for f.
|
|
||||||
func (f *Fn) StrconvType() string {
|
|
||||||
if f.IsUTF16() {
|
|
||||||
return "*uint16"
|
|
||||||
}
|
|
||||||
return "*byte"
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasStringParam is true, if f has at least one string parameter.
|
|
||||||
// Otherwise it is false.
|
|
||||||
func (f *Fn) HasStringParam() bool {
|
|
||||||
for _, p := range f.Params {
|
|
||||||
if p.Type == "string" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
var uniqDllFuncName = make(map[string]bool)
|
|
||||||
|
|
||||||
// IsNotDuplicate is true if f is not a duplicated function
|
|
||||||
func (f *Fn) IsNotDuplicate() bool {
|
|
||||||
funcName := f.DLLFuncName()
|
|
||||||
if uniqDllFuncName[funcName] == false {
|
|
||||||
uniqDllFuncName[funcName] = true
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// HelperName returns name of function f helper.
|
|
||||||
func (f *Fn) HelperName() string {
|
|
||||||
if !f.HasStringParam() {
|
|
||||||
return f.Name
|
|
||||||
}
|
|
||||||
return "_" + f.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Source files and functions.
|
|
||||||
type Source struct {
|
|
||||||
Funcs []*Fn
|
|
||||||
Files []string
|
|
||||||
StdLibImports []string
|
|
||||||
ExternalImports []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (src *Source) Import(pkg string) {
|
|
||||||
src.StdLibImports = append(src.StdLibImports, pkg)
|
|
||||||
sort.Strings(src.StdLibImports)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (src *Source) ExternalImport(pkg string) {
|
|
||||||
src.ExternalImports = append(src.ExternalImports, pkg)
|
|
||||||
sort.Strings(src.ExternalImports)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseFiles parses files listed in fs and extracts all syscall
|
|
||||||
// functions listed in sys comments. It returns source files
|
|
||||||
// and functions collection *Source if successful.
|
|
||||||
func ParseFiles(fs []string) (*Source, error) {
|
|
||||||
src := &Source{
|
|
||||||
Funcs: make([]*Fn, 0),
|
|
||||||
Files: make([]string, 0),
|
|
||||||
StdLibImports: []string{
|
|
||||||
"unsafe",
|
|
||||||
},
|
|
||||||
ExternalImports: make([]string, 0),
|
|
||||||
}
|
|
||||||
for _, file := range fs {
|
|
||||||
if err := src.ParseFile(file); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return src, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DLLs return dll names for a source set src.
|
|
||||||
func (src *Source) DLLs() []string {
|
|
||||||
uniq := make(map[string]bool)
|
|
||||||
r := make([]string, 0)
|
|
||||||
for _, f := range src.Funcs {
|
|
||||||
name := f.DLLName()
|
|
||||||
if _, found := uniq[name]; !found {
|
|
||||||
uniq[name] = true
|
|
||||||
r = append(r, name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseFile adds additional file path to a source set src.
|
|
||||||
func (src *Source) ParseFile(path string) error {
|
|
||||||
file, err := os.Open(path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
s := bufio.NewScanner(file)
|
|
||||||
for s.Scan() {
|
|
||||||
t := trim(s.Text())
|
|
||||||
if len(t) < 7 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !strings.HasPrefix(t, "//sys") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
t = t[5:]
|
|
||||||
if !(t[0] == ' ' || t[0] == '\t') {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
f, err := newFn(t[1:])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
src.Funcs = append(src.Funcs, f)
|
|
||||||
}
|
|
||||||
if err := s.Err(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
src.Files = append(src.Files, path)
|
|
||||||
|
|
||||||
// get package name
|
|
||||||
fset := token.NewFileSet()
|
|
||||||
_, err = file.Seek(0, 0)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
pkg, err := parser.ParseFile(fset, "", file, parser.PackageClauseOnly)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
packageName = pkg.Name.Name
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsStdRepo returns true if src is part of standard library.
|
|
||||||
func (src *Source) IsStdRepo() (bool, error) {
|
|
||||||
if len(src.Files) == 0 {
|
|
||||||
return false, errors.New("no input files provided")
|
|
||||||
}
|
|
||||||
abspath, err := filepath.Abs(src.Files[0])
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
goroot := runtime.GOROOT()
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
abspath = strings.ToLower(abspath)
|
|
||||||
goroot = strings.ToLower(goroot)
|
|
||||||
}
|
|
||||||
sep := string(os.PathSeparator)
|
|
||||||
if !strings.HasSuffix(goroot, sep) {
|
|
||||||
goroot += sep
|
|
||||||
}
|
|
||||||
return strings.HasPrefix(abspath, goroot), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate output source file from a source set src.
|
|
||||||
func (src *Source) Generate(w io.Writer) error {
|
|
||||||
const (
|
|
||||||
pkgStd = iota // any package in std library
|
|
||||||
pkgXSysWindows // x/sys/windows package
|
|
||||||
pkgOther
|
|
||||||
)
|
|
||||||
isStdRepo, err := src.IsStdRepo()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var pkgtype int
|
|
||||||
switch {
|
|
||||||
case isStdRepo:
|
|
||||||
pkgtype = pkgStd
|
|
||||||
case packageName == "windows":
|
|
||||||
// TODO: this needs better logic than just using package name
|
|
||||||
pkgtype = pkgXSysWindows
|
|
||||||
default:
|
|
||||||
pkgtype = pkgOther
|
|
||||||
}
|
|
||||||
if *systemDLL {
|
|
||||||
switch pkgtype {
|
|
||||||
case pkgStd:
|
|
||||||
src.Import("internal/syscall/windows/sysdll")
|
|
||||||
case pkgXSysWindows:
|
|
||||||
default:
|
|
||||||
src.ExternalImport("golang.org/x/sys/windows")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
src.ExternalImport("github.com/Microsoft/go-winio")
|
|
||||||
if packageName != "syscall" {
|
|
||||||
src.Import("syscall")
|
|
||||||
}
|
|
||||||
funcMap := template.FuncMap{
|
|
||||||
"packagename": packagename,
|
|
||||||
"syscalldot": syscalldot,
|
|
||||||
"newlazydll": func(dll string) string {
|
|
||||||
arg := "\"" + dll + ".dll\""
|
|
||||||
if !*systemDLL {
|
|
||||||
return syscalldot() + "NewLazyDLL(" + arg + ")"
|
|
||||||
}
|
|
||||||
switch pkgtype {
|
|
||||||
case pkgStd:
|
|
||||||
return syscalldot() + "NewLazyDLL(sysdll.Add(" + arg + "))"
|
|
||||||
case pkgXSysWindows:
|
|
||||||
return "NewLazySystemDLL(" + arg + ")"
|
|
||||||
default:
|
|
||||||
return "windows.NewLazySystemDLL(" + arg + ")"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
t := template.Must(template.New("main").Funcs(funcMap).Parse(srcTemplate))
|
|
||||||
err = t.Execute(w, src)
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("Failed to execute template: " + err.Error())
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func usage() {
|
|
||||||
fmt.Fprintf(os.Stderr, "usage: mksyscall_windows [flags] [path ...]\n")
|
|
||||||
flag.PrintDefaults()
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
flag.Usage = usage
|
|
||||||
flag.Parse()
|
|
||||||
if len(flag.Args()) <= 0 {
|
|
||||||
fmt.Fprintf(os.Stderr, "no files to parse provided\n")
|
|
||||||
usage()
|
|
||||||
}
|
|
||||||
|
|
||||||
src, err := ParseFiles(flag.Args())
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
|
||||||
if err := src.Generate(&buf); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := format.Source(buf.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
if *filename == "" {
|
|
||||||
_, err = os.Stdout.Write(data)
|
|
||||||
} else {
|
|
||||||
err = ioutil.WriteFile(*filename, data, 0644)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: use println instead to print in the following template
|
|
||||||
const srcTemplate = `
|
|
||||||
|
|
||||||
{{define "main"}}// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT
|
|
||||||
|
|
||||||
package {{packagename}}
|
|
||||||
|
|
||||||
import (
|
|
||||||
{{range .StdLibImports}}"{{.}}"
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{range .ExternalImports}}"{{.}}"
|
|
||||||
{{end}}
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ unsafe.Pointer
|
|
||||||
|
|
||||||
// Do the interface allocations only once for common
|
|
||||||
// Errno values.
|
|
||||||
const (
|
|
||||||
errnoERROR_IO_PENDING = 997
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
errERROR_IO_PENDING error = {{syscalldot}}Errno(errnoERROR_IO_PENDING)
|
|
||||||
)
|
|
||||||
|
|
||||||
// errnoErr returns common boxed Errno values, to prevent
|
|
||||||
// allocations at runtime.
|
|
||||||
func errnoErr(e {{syscalldot}}Errno) error {
|
|
||||||
switch e {
|
|
||||||
case 0:
|
|
||||||
return nil
|
|
||||||
case errnoERROR_IO_PENDING:
|
|
||||||
return errERROR_IO_PENDING
|
|
||||||
}
|
|
||||||
// TODO: add more here, after collecting data on the common
|
|
||||||
// error values see on Windows. (perhaps when running
|
|
||||||
// all.bat?)
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
{{template "dlls" .}}
|
|
||||||
{{template "funcnames" .}})
|
|
||||||
{{range .Funcs}}{{if .HasStringParam}}{{template "helperbody" .}}{{end}}{{template "funcbody" .}}{{end}}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{/* help functions */}}
|
|
||||||
|
|
||||||
{{define "dlls"}}{{range .DLLs}} mod{{.}} = {{newlazydll .}}
|
|
||||||
{{end}}{{end}}
|
|
||||||
|
|
||||||
{{define "funcnames"}}{{range .Funcs}}{{if .IsNotDuplicate}} proc{{.DLLFuncName}} = mod{{.DLLName}}.NewProc("{{.DLLFuncName}}"){{end}}
|
|
||||||
{{end}}{{end}}
|
|
||||||
|
|
||||||
{{define "helperbody"}}
|
|
||||||
func {{.Name}}({{.ParamList}}) {{template "results" .}}{
|
|
||||||
{{template "helpertmpvars" .}} return {{.HelperName}}({{.HelperCallParamList}})
|
|
||||||
}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{define "funcbody"}}
|
|
||||||
func {{.HelperName}}({{.HelperParamList}}) {{template "results" .}}{
|
|
||||||
{{template "tmpvars" .}} {{template "syscallcheck" .}}{{template "syscall" .}}
|
|
||||||
{{template "seterror" .}}{{template "printtrace" .}} return
|
|
||||||
}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{define "helpertmpvars"}}{{range .Params}}{{if .TmpVarHelperCode}} {{.TmpVarHelperCode}}
|
|
||||||
{{end}}{{end}}{{end}}
|
|
||||||
|
|
||||||
{{define "tmpvars"}}{{range .Params}}{{if .TmpVarCode}} {{.TmpVarCode}}
|
|
||||||
{{end}}{{end}}{{end}}
|
|
||||||
|
|
||||||
{{define "results"}}{{if .Rets.List}}{{.Rets.List}} {{end}}{{end}}
|
|
||||||
|
|
||||||
{{define "syscall"}}{{.Rets.SetReturnValuesCode}}{{.Syscall}}(proc{{.DLLFuncName}}.Addr(), {{.ParamCount}}, {{.SyscallParamList}}){{end}}
|
|
||||||
|
|
||||||
{{define "syscallcheck"}}{{if .ConfirmProc}}if {{.Rets.ErrorVarName}} = proc{{.DLLFuncName}}.Find(); {{.Rets.ErrorVarName}} != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
{{end}}{{end}}
|
|
||||||
|
|
||||||
|
|
||||||
{{define "seterror"}}{{if .Rets.SetErrorCode}} {{.Rets.SetErrorCode}}
|
|
||||||
{{end}}{{end}}
|
|
||||||
|
|
||||||
{{define "printtrace"}}{{if .PrintTrace}} print("SYSCALL: {{.Name}}(", {{.ParamPrintList}}") (", {{.Rets.PrintList}}")\n")
|
|
||||||
{{end}}{{end}}
|
|
||||||
|
|
||||||
`
|
|
3
vendor/github.com/containers/image/MAINTAINERS
generated
vendored
3
vendor/github.com/containers/image/MAINTAINERS
generated
vendored
|
@ -1,3 +0,0 @@
|
||||||
Antonio Murdaca <runcom@redhat.com> (@runcom)
|
|
||||||
Brandon Philips <brandon.philips@coreos.com> (@philips)
|
|
||||||
Miloslav Trmac <mitr@redhat.com> (@mtrmac)
|
|
72
vendor/github.com/containers/image/Makefile
generated
vendored
72
vendor/github.com/containers/image/Makefile
generated
vendored
|
@ -1,72 +0,0 @@
|
||||||
.PHONY: all tools test validate lint
|
|
||||||
|
|
||||||
# Which github repostiory and branch to use for testing with skopeo
|
|
||||||
SKOPEO_REPO = projectatomic/skopeo
|
|
||||||
SKOPEO_BRANCH = master
|
|
||||||
# Set SUDO=sudo to run container integration tests using sudo.
|
|
||||||
SUDO =
|
|
||||||
BUILDTAGS = btrfs_noversion libdm_no_deferred_remove
|
|
||||||
BUILDFLAGS := -tags "$(BUILDTAGS)"
|
|
||||||
|
|
||||||
PACKAGES := $(shell go list ./... | grep -v github.com/containers/image/vendor)
|
|
||||||
|
|
||||||
all: tools .gitvalidation test validate
|
|
||||||
|
|
||||||
tools: tools.timestamp
|
|
||||||
|
|
||||||
tools.timestamp: Makefile
|
|
||||||
@go get -u $(BUILDFLAGS) github.com/golang/lint/golint
|
|
||||||
@go get $(BUILDFLAGS) github.com/vbatts/git-validation
|
|
||||||
@go get -u github.com/rancher/trash
|
|
||||||
@touch tools.timestamp
|
|
||||||
|
|
||||||
vendor: tools.timestamp vendor.conf
|
|
||||||
@trash
|
|
||||||
@touch vendor
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -rf vendor tools.timestamp
|
|
||||||
|
|
||||||
test: vendor
|
|
||||||
@go test $(BUILDFLAGS) -cover $(PACKAGES)
|
|
||||||
|
|
||||||
# This is not run as part of (make all), but Travis CI does run this.
|
|
||||||
# Demonstarting a working version of skopeo (possibly with modified SKOPEO_REPO/SKOPEO_BRANCH, e.g.
|
|
||||||
# make test-skopeo SKOPEO_REPO=runcom/skopeo-1 SKOPEO_BRANCH=oci-3 SUDO=sudo
|
|
||||||
# ) is a requirement before merging; note that Travis will only test
|
|
||||||
# the master branch of the upstream repo.
|
|
||||||
test-skopeo:
|
|
||||||
@echo === Testing skopeo build
|
|
||||||
@export GOPATH=$$(mktemp -d) && \
|
|
||||||
skopeo_path=$${GOPATH}/src/github.com/projectatomic/skopeo && \
|
|
||||||
vendor_path=$${skopeo_path}/vendor/github.com/containers/image && \
|
|
||||||
git clone -b $(SKOPEO_BRANCH) https://github.com/$(SKOPEO_REPO) $${skopeo_path} && \
|
|
||||||
rm -rf $${vendor_path} && cp -r . $${vendor_path} && rm -rf $${vendor_path}/vendor && \
|
|
||||||
cd $${skopeo_path} && \
|
|
||||||
make BUILDTAGS="$(BUILDTAGS)" binary-local test-all-local && \
|
|
||||||
$(SUDO) make BUILDTAGS="$(BUILDTAGS)" check && \
|
|
||||||
rm -rf $${skopeo_path}
|
|
||||||
|
|
||||||
validate: lint
|
|
||||||
@go vet $(PACKAGES)
|
|
||||||
@test -z "$$(gofmt -s -l . | grep -ve '^vendor' | tee /dev/stderr)"
|
|
||||||
|
|
||||||
lint:
|
|
||||||
@out="$$(golint $(PACKAGES))"; \
|
|
||||||
if [ -n "$$out" ]; then \
|
|
||||||
echo "$$out"; \
|
|
||||||
exit 1; \
|
|
||||||
fi
|
|
||||||
|
|
||||||
.PHONY: .gitvalidation
|
|
||||||
|
|
||||||
EPOCH_TEST_COMMIT ?= e68e0e1110e64f906f9b482e548f17d73e02e6b1
|
|
||||||
|
|
||||||
# When this is running in travis, it will only check the travis commit range
|
|
||||||
.gitvalidation:
|
|
||||||
@which git-validation > /dev/null 2>/dev/null || (echo "ERROR: git-validation not found. Consider 'make clean && make tools'" && false)
|
|
||||||
ifeq ($(TRAVIS),true)
|
|
||||||
@git-validation -q -run DCO,short-subject,dangling-whitespace
|
|
||||||
else
|
|
||||||
@git-validation -q -run DCO,short-subject,dangling-whitespace -range $(EPOCH_TEST_COMMIT)..HEAD
|
|
||||||
endif
|
|
29
vendor/github.com/containers/image/doc.go
generated
vendored
29
vendor/github.com/containers/image/doc.go
generated
vendored
|
@ -1,29 +0,0 @@
|
||||||
// Package image provides libraries and commands to interact with containers images.
|
|
||||||
//
|
|
||||||
// package main
|
|
||||||
//
|
|
||||||
// import (
|
|
||||||
// "fmt"
|
|
||||||
//
|
|
||||||
// "github.com/containers/image/docker"
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// func main() {
|
|
||||||
// ref, err := docker.ParseReference("//fedora")
|
|
||||||
// if err != nil {
|
|
||||||
// panic(err)
|
|
||||||
// }
|
|
||||||
// img, err := ref.NewImage(nil)
|
|
||||||
// if err != nil {
|
|
||||||
// panic(err)
|
|
||||||
// }
|
|
||||||
// defer img.Close()
|
|
||||||
// b, _, err := img.Manifest()
|
|
||||||
// if err != nil {
|
|
||||||
// panic(err)
|
|
||||||
// }
|
|
||||||
// fmt.Printf("%s", string(b))
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// TODO(runcom)
|
|
||||||
package image
|
|
97
vendor/github.com/containers/storage/pkg/archive/example_changes.go
generated
vendored
97
vendor/github.com/containers/storage/pkg/archive/example_changes.go
generated
vendored
|
@ -1,97 +0,0 @@
|
||||||
// +build ignore
|
|
||||||
|
|
||||||
// Simple tool to create an archive stream from an old and new directory
|
|
||||||
//
|
|
||||||
// By default it will stream the comparison of two temporary directories with junk files
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
"github.com/containers/storage/pkg/archive"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
flDebug = flag.Bool("D", false, "debugging output")
|
|
||||||
flNewDir = flag.String("newdir", "", "")
|
|
||||||
flOldDir = flag.String("olddir", "", "")
|
|
||||||
log = logrus.New()
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
flag.Usage = func() {
|
|
||||||
fmt.Println("Produce a tar from comparing two directory paths. By default a demo tar is created of around 200 files (including hardlinks)")
|
|
||||||
fmt.Printf("%s [OPTIONS]\n", os.Args[0])
|
|
||||||
flag.PrintDefaults()
|
|
||||||
}
|
|
||||||
flag.Parse()
|
|
||||||
log.Out = os.Stderr
|
|
||||||
if (len(os.Getenv("DEBUG")) > 0) || *flDebug {
|
|
||||||
logrus.SetLevel(logrus.DebugLevel)
|
|
||||||
}
|
|
||||||
var newDir, oldDir string
|
|
||||||
|
|
||||||
if len(*flNewDir) == 0 {
|
|
||||||
var err error
|
|
||||||
newDir, err = ioutil.TempDir("", "storage-test-newDir")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(newDir)
|
|
||||||
if _, err := prepareUntarSourceDirectory(100, newDir, true); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
newDir = *flNewDir
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(*flOldDir) == 0 {
|
|
||||||
oldDir, err := ioutil.TempDir("", "storage-test-oldDir")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(oldDir)
|
|
||||||
} else {
|
|
||||||
oldDir = *flOldDir
|
|
||||||
}
|
|
||||||
|
|
||||||
changes, err := archive.ChangesDirs(newDir, oldDir)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
a, err := archive.ExportChanges(newDir, changes)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer a.Close()
|
|
||||||
|
|
||||||
i, err := io.Copy(os.Stdout, a)
|
|
||||||
if err != nil && err != io.EOF {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(os.Stderr, "wrote archive of %d bytes", i)
|
|
||||||
}
|
|
||||||
|
|
||||||
func prepareUntarSourceDirectory(numberOfFiles int, targetPath string, makeLinks bool) (int, error) {
|
|
||||||
fileData := []byte("fooo")
|
|
||||||
for n := 0; n < numberOfFiles; n++ {
|
|
||||||
fileName := fmt.Sprintf("file-%d", n)
|
|
||||||
if err := ioutil.WriteFile(path.Join(targetPath, fileName), fileData, 0700); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
if makeLinks {
|
|
||||||
if err := os.Link(path.Join(targetPath, fileName), path.Join(targetPath, fileName+"-link")); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
totalSize := numberOfFiles * len(fileData)
|
|
||||||
return totalSize, nil
|
|
||||||
}
|
|
10
vendor/github.com/kr/pty/types.go
generated
vendored
10
vendor/github.com/kr/pty/types.go
generated
vendored
|
@ -1,10 +0,0 @@
|
||||||
// +build ignore
|
|
||||||
|
|
||||||
package pty
|
|
||||||
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
type (
|
|
||||||
_C_int C.int
|
|
||||||
_C_uint C.uint
|
|
||||||
)
|
|
17
vendor/github.com/kr/pty/types_dragonfly.go
generated
vendored
17
vendor/github.com/kr/pty/types_dragonfly.go
generated
vendored
|
@ -1,17 +0,0 @@
|
||||||
// +build ignore
|
|
||||||
|
|
||||||
package pty
|
|
||||||
|
|
||||||
/*
|
|
||||||
#define _KERNEL
|
|
||||||
#include <sys/conf.h>
|
|
||||||
#include <sys/param.h>
|
|
||||||
#include <sys/filio.h>
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
const (
|
|
||||||
_C_SPECNAMELEN = C.SPECNAMELEN /* max length of devicename */
|
|
||||||
)
|
|
||||||
|
|
||||||
type fiodgnameArg C.struct_fiodname_args
|
|
15
vendor/github.com/kr/pty/types_freebsd.go
generated
vendored
15
vendor/github.com/kr/pty/types_freebsd.go
generated
vendored
|
@ -1,15 +0,0 @@
|
||||||
// +build ignore
|
|
||||||
|
|
||||||
package pty
|
|
||||||
|
|
||||||
/*
|
|
||||||
#include <sys/param.h>
|
|
||||||
#include <sys/filio.h>
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
const (
|
|
||||||
_C_SPECNAMELEN = C.SPECNAMELEN /* max length of devicename */
|
|
||||||
)
|
|
||||||
|
|
||||||
type fiodgnameArg C.struct_fiodgname_arg
|
|
26
vendor/github.com/opencontainers/runc/README.md
generated
vendored
26
vendor/github.com/opencontainers/runc/README.md
generated
vendored
|
@ -77,6 +77,12 @@ You can run a specific test case by setting the `TESTFLAGS` variable.
|
||||||
# make test TESTFLAGS="-run=SomeTestFunction"
|
# make test TESTFLAGS="-run=SomeTestFunction"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Dependencies Management
|
||||||
|
|
||||||
|
`runc` uses [vndr](https://github.com/LK4D4/vndr) for dependencies management.
|
||||||
|
Please refer to [vndr](https://github.com/LK4D4/vndr) for how to add or update
|
||||||
|
new dependencies.
|
||||||
|
|
||||||
## Using runc
|
## Using runc
|
||||||
|
|
||||||
### Creating an OCI Bundle
|
### Creating an OCI Bundle
|
||||||
|
@ -111,8 +117,8 @@ Assuming you have an OCI bundle from the previous step you can execute the conta
|
||||||
The first way is to use the convenience command `run` that will handle creating, starting, and deleting the container after it exits.
|
The first way is to use the convenience command `run` that will handle creating, starting, and deleting the container after it exits.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# run as root
|
||||||
cd /mycontainer
|
cd /mycontainer
|
||||||
|
|
||||||
runc run mycontainerid
|
runc run mycontainerid
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -159,8 +165,8 @@ Now we can go though the lifecycle operations in your shell.
|
||||||
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# run as root
|
||||||
cd /mycontainer
|
cd /mycontainer
|
||||||
|
|
||||||
runc create mycontainerid
|
runc create mycontainerid
|
||||||
|
|
||||||
# view the container is created and in the "created" state
|
# view the container is created and in the "created" state
|
||||||
|
@ -179,6 +185,22 @@ runc delete mycontainerid
|
||||||
This adds more complexity but allows higher level systems to manage runc and provides points in the containers creation to setup various settings after the container has created and/or before it is deleted.
|
This adds more complexity but allows higher level systems to manage runc and provides points in the containers creation to setup various settings after the container has created and/or before it is deleted.
|
||||||
This is commonly used to setup the container's network stack after `create` but before `start` where the user's defined process will be running.
|
This is commonly used to setup the container's network stack after `create` but before `start` where the user's defined process will be running.
|
||||||
|
|
||||||
|
#### Rootless containers
|
||||||
|
`runc` has the ability to run containers without root privileges. This is called `rootless`. You need to pass some parameters to `runc` in order to run rootless containers. See below and compare with the previous version. Run the following commands as an ordinary user:
|
||||||
|
```bash
|
||||||
|
# Same as the first example
|
||||||
|
mkdir ~/mycontainer
|
||||||
|
cd ~/mycontainer
|
||||||
|
mkdir rootfs
|
||||||
|
docker export $(docker create busybox) | tar -C rootfs -xvf -
|
||||||
|
|
||||||
|
# The --rootless parameter instructs runc spec to generate a configuration for a rootless container, which will allow you to run the container as a non-root user.
|
||||||
|
runc spec --rootless
|
||||||
|
|
||||||
|
# The --root parameter tells runc where to store the container state. It must be writable by the user.
|
||||||
|
runc --root /tmp/runc run mycontainerid
|
||||||
|
```
|
||||||
|
|
||||||
#### Supervisors
|
#### Supervisors
|
||||||
|
|
||||||
`runc` can be used with process supervisors and init systems to ensure that containers are restarted when they exit.
|
`runc` can be used with process supervisors and init systems to ensure that containers are restarted when they exit.
|
||||||
|
|
78
vendor/github.com/opencontainers/runc/libcontainer/README.md
generated
vendored
78
vendor/github.com/opencontainers/runc/libcontainer/README.md
generated
vendored
|
@ -56,10 +56,11 @@ Once you have an instance of the factory created we can create a configuration
|
||||||
struct describing how the container is to be created. A sample would look similar to this:
|
struct describing how the container is to be created. A sample would look similar to this:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
defaultMountFlags := syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV
|
defaultMountFlags := unix.MS_NOEXEC | unix.MS_NOSUID | unix.MS_NODEV
|
||||||
config := &configs.Config{
|
config := &configs.Config{
|
||||||
Rootfs: "/your/path/to/rootfs",
|
Rootfs: "/your/path/to/rootfs",
|
||||||
Capabilities: []string{
|
Capabilities: &configs.Capabilities{
|
||||||
|
Bounding: []string{
|
||||||
"CAP_CHOWN",
|
"CAP_CHOWN",
|
||||||
"CAP_DAC_OVERRIDE",
|
"CAP_DAC_OVERRIDE",
|
||||||
"CAP_FSETID",
|
"CAP_FSETID",
|
||||||
|
@ -75,6 +76,71 @@ config := &configs.Config{
|
||||||
"CAP_KILL",
|
"CAP_KILL",
|
||||||
"CAP_AUDIT_WRITE",
|
"CAP_AUDIT_WRITE",
|
||||||
},
|
},
|
||||||
|
Effective: []string{
|
||||||
|
"CAP_CHOWN",
|
||||||
|
"CAP_DAC_OVERRIDE",
|
||||||
|
"CAP_FSETID",
|
||||||
|
"CAP_FOWNER",
|
||||||
|
"CAP_MKNOD",
|
||||||
|
"CAP_NET_RAW",
|
||||||
|
"CAP_SETGID",
|
||||||
|
"CAP_SETUID",
|
||||||
|
"CAP_SETFCAP",
|
||||||
|
"CAP_SETPCAP",
|
||||||
|
"CAP_NET_BIND_SERVICE",
|
||||||
|
"CAP_SYS_CHROOT",
|
||||||
|
"CAP_KILL",
|
||||||
|
"CAP_AUDIT_WRITE",
|
||||||
|
},
|
||||||
|
Inheritable: []string{
|
||||||
|
"CAP_CHOWN",
|
||||||
|
"CAP_DAC_OVERRIDE",
|
||||||
|
"CAP_FSETID",
|
||||||
|
"CAP_FOWNER",
|
||||||
|
"CAP_MKNOD",
|
||||||
|
"CAP_NET_RAW",
|
||||||
|
"CAP_SETGID",
|
||||||
|
"CAP_SETUID",
|
||||||
|
"CAP_SETFCAP",
|
||||||
|
"CAP_SETPCAP",
|
||||||
|
"CAP_NET_BIND_SERVICE",
|
||||||
|
"CAP_SYS_CHROOT",
|
||||||
|
"CAP_KILL",
|
||||||
|
"CAP_AUDIT_WRITE",
|
||||||
|
},
|
||||||
|
Permitted: []string{
|
||||||
|
"CAP_CHOWN",
|
||||||
|
"CAP_DAC_OVERRIDE",
|
||||||
|
"CAP_FSETID",
|
||||||
|
"CAP_FOWNER",
|
||||||
|
"CAP_MKNOD",
|
||||||
|
"CAP_NET_RAW",
|
||||||
|
"CAP_SETGID",
|
||||||
|
"CAP_SETUID",
|
||||||
|
"CAP_SETFCAP",
|
||||||
|
"CAP_SETPCAP",
|
||||||
|
"CAP_NET_BIND_SERVICE",
|
||||||
|
"CAP_SYS_CHROOT",
|
||||||
|
"CAP_KILL",
|
||||||
|
"CAP_AUDIT_WRITE",
|
||||||
|
},
|
||||||
|
Ambient: []string{
|
||||||
|
"CAP_CHOWN",
|
||||||
|
"CAP_DAC_OVERRIDE",
|
||||||
|
"CAP_FSETID",
|
||||||
|
"CAP_FOWNER",
|
||||||
|
"CAP_MKNOD",
|
||||||
|
"CAP_NET_RAW",
|
||||||
|
"CAP_SETGID",
|
||||||
|
"CAP_SETUID",
|
||||||
|
"CAP_SETFCAP",
|
||||||
|
"CAP_SETPCAP",
|
||||||
|
"CAP_NET_BIND_SERVICE",
|
||||||
|
"CAP_SYS_CHROOT",
|
||||||
|
"CAP_KILL",
|
||||||
|
"CAP_AUDIT_WRITE",
|
||||||
|
},
|
||||||
|
},
|
||||||
Namespaces: configs.Namespaces([]configs.Namespace{
|
Namespaces: configs.Namespaces([]configs.Namespace{
|
||||||
{Type: configs.NEWNS},
|
{Type: configs.NEWNS},
|
||||||
{Type: configs.NEWUTS},
|
{Type: configs.NEWUTS},
|
||||||
|
@ -112,14 +178,14 @@ config := &configs.Config{
|
||||||
Source: "tmpfs",
|
Source: "tmpfs",
|
||||||
Destination: "/dev",
|
Destination: "/dev",
|
||||||
Device: "tmpfs",
|
Device: "tmpfs",
|
||||||
Flags: syscall.MS_NOSUID | syscall.MS_STRICTATIME,
|
Flags: unix.MS_NOSUID | unix.MS_STRICTATIME,
|
||||||
Data: "mode=755",
|
Data: "mode=755",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Source: "devpts",
|
Source: "devpts",
|
||||||
Destination: "/dev/pts",
|
Destination: "/dev/pts",
|
||||||
Device: "devpts",
|
Device: "devpts",
|
||||||
Flags: syscall.MS_NOSUID | syscall.MS_NOEXEC,
|
Flags: unix.MS_NOSUID | unix.MS_NOEXEC,
|
||||||
Data: "newinstance,ptmxmode=0666,mode=0620,gid=5",
|
Data: "newinstance,ptmxmode=0666,mode=0620,gid=5",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -139,7 +205,7 @@ config := &configs.Config{
|
||||||
Source: "sysfs",
|
Source: "sysfs",
|
||||||
Destination: "/sys",
|
Destination: "/sys",
|
||||||
Device: "sysfs",
|
Device: "sysfs",
|
||||||
Flags: defaultMountFlags | syscall.MS_RDONLY,
|
Flags: defaultMountFlags | unix.MS_RDONLY,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
UidMappings: []configs.IDMap{
|
UidMappings: []configs.IDMap{
|
||||||
|
@ -165,7 +231,7 @@ config := &configs.Config{
|
||||||
},
|
},
|
||||||
Rlimits: []configs.Rlimit{
|
Rlimits: []configs.Rlimit{
|
||||||
{
|
{
|
||||||
Type: syscall.RLIMIT_NOFILE,
|
Type: unix.RLIMIT_NOFILE,
|
||||||
Hard: uint64(1025),
|
Hard: uint64(1025),
|
||||||
Soft: uint64(1025),
|
Soft: uint64(1025),
|
||||||
},
|
},
|
||||||
|
|
28
vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/apply_raw.go
generated
vendored
28
vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/apply_raw.go
generated
vendored
|
@ -267,25 +267,8 @@ func getCgroupData(c *configs.Cgroup, pid int) (*cgroupData, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (raw *cgroupData) parentPath(subsystem, mountpoint, root string) (string, error) {
|
|
||||||
// Use GetThisCgroupDir instead of GetInitCgroupDir, because the creating
|
|
||||||
// process could in container and shared pid namespace with host, and
|
|
||||||
// /proc/1/cgroup could point to whole other world of cgroups.
|
|
||||||
initPath, err := cgroups.GetThisCgroupDir(subsystem)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
// This is needed for nested containers, because in /proc/self/cgroup we
|
|
||||||
// see pathes from host, which don't exist in container.
|
|
||||||
relDir, err := filepath.Rel(root, initPath)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return filepath.Join(mountpoint, relDir), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (raw *cgroupData) path(subsystem string) (string, error) {
|
func (raw *cgroupData) path(subsystem string) (string, error) {
|
||||||
mnt, root, err := cgroups.FindCgroupMountpointAndRoot(subsystem)
|
mnt, err := cgroups.FindCgroupMountpoint(subsystem)
|
||||||
// If we didn't mount the subsystem, there is no point we make the path.
|
// If we didn't mount the subsystem, there is no point we make the path.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -297,7 +280,10 @@ func (raw *cgroupData) path(subsystem string) (string, error) {
|
||||||
return filepath.Join(raw.root, filepath.Base(mnt), raw.innerPath), nil
|
return filepath.Join(raw.root, filepath.Base(mnt), raw.innerPath), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
parentPath, err := raw.parentPath(subsystem, mnt, root)
|
// Use GetOwnCgroupPath instead of GetInitCgroupPath, because the creating
|
||||||
|
// process could in container and shared pid namespace with host, and
|
||||||
|
// /proc/1/cgroup could point to whole other world of cgroups.
|
||||||
|
parentPath, err := cgroups.GetOwnCgroupPath(subsystem)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -346,8 +332,8 @@ func removePath(p string, err error) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckCpushares(path string, c int64) error {
|
func CheckCpushares(path string, c uint64) error {
|
||||||
var cpuShares int64
|
var cpuShares uint64
|
||||||
|
|
||||||
if c == 0 {
|
if c == 0 {
|
||||||
return nil
|
return nil
|
||||||
|
|
6
vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpu.go
generated
vendored
6
vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpu.go
generated
vendored
|
@ -55,7 +55,7 @@ func (s *CpuGroup) ApplyDir(path string, cgroup *configs.Cgroup, pid int) error
|
||||||
|
|
||||||
func (s *CpuGroup) SetRtSched(path string, cgroup *configs.Cgroup) error {
|
func (s *CpuGroup) SetRtSched(path string, cgroup *configs.Cgroup) error {
|
||||||
if cgroup.Resources.CpuRtPeriod != 0 {
|
if cgroup.Resources.CpuRtPeriod != 0 {
|
||||||
if err := writeFile(path, "cpu.rt_period_us", strconv.FormatInt(cgroup.Resources.CpuRtPeriod, 10)); err != nil {
|
if err := writeFile(path, "cpu.rt_period_us", strconv.FormatUint(cgroup.Resources.CpuRtPeriod, 10)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,12 +69,12 @@ func (s *CpuGroup) SetRtSched(path string, cgroup *configs.Cgroup) error {
|
||||||
|
|
||||||
func (s *CpuGroup) Set(path string, cgroup *configs.Cgroup) error {
|
func (s *CpuGroup) Set(path string, cgroup *configs.Cgroup) error {
|
||||||
if cgroup.Resources.CpuShares != 0 {
|
if cgroup.Resources.CpuShares != 0 {
|
||||||
if err := writeFile(path, "cpu.shares", strconv.FormatInt(cgroup.Resources.CpuShares, 10)); err != nil {
|
if err := writeFile(path, "cpu.shares", strconv.FormatUint(cgroup.Resources.CpuShares, 10)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if cgroup.Resources.CpuPeriod != 0 {
|
if cgroup.Resources.CpuPeriod != 0 {
|
||||||
if err := writeFile(path, "cpu.cfs_period_us", strconv.FormatInt(cgroup.Resources.CpuPeriod, 10)); err != nil {
|
if err := writeFile(path, "cpu.cfs_period_us", strconv.FormatUint(cgroup.Resources.CpuPeriod, 10)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
3
vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuset.go
generated
vendored
3
vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuset.go
generated
vendored
|
@ -57,10 +57,11 @@ func (s *CpusetGroup) ApplyDir(dir string, cgroup *configs.Cgroup, pid int) erro
|
||||||
if dir == "" {
|
if dir == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
root, err := getCgroupRoot()
|
mountInfo, err := ioutil.ReadFile("/proc/self/mountinfo")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
root := filepath.Dir(cgroups.GetClosestMountpointAncestor(dir, string(mountInfo)))
|
||||||
// 'ensureParent' start with parent because we don't want to
|
// 'ensureParent' start with parent because we don't want to
|
||||||
// explicitly inherit from parent, it could conflict with
|
// explicitly inherit from parent, it could conflict with
|
||||||
// 'cpuset.cpu_exclusive'.
|
// 'cpuset.cpu_exclusive'.
|
||||||
|
|
65
vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/memory.go
generated
vendored
65
vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/memory.go
generated
vendored
|
@ -10,13 +10,19 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall" // only for Errno
|
||||||
|
|
||||||
"github.com/opencontainers/runc/libcontainer/cgroups"
|
"github.com/opencontainers/runc/libcontainer/cgroups"
|
||||||
"github.com/opencontainers/runc/libcontainer/configs"
|
"github.com/opencontainers/runc/libcontainer/configs"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
const cgroupKernelMemoryLimit = "memory.kmem.limit_in_bytes"
|
const (
|
||||||
|
cgroupKernelMemoryLimit = "memory.kmem.limit_in_bytes"
|
||||||
|
cgroupMemorySwapLimit = "memory.memsw.limit_in_bytes"
|
||||||
|
cgroupMemoryLimit = "memory.limit_in_bytes"
|
||||||
|
)
|
||||||
|
|
||||||
type MemoryGroup struct {
|
type MemoryGroup struct {
|
||||||
}
|
}
|
||||||
|
@ -29,14 +35,18 @@ func (s *MemoryGroup) Apply(d *cgroupData) (err error) {
|
||||||
path, err := d.path("memory")
|
path, err := d.path("memory")
|
||||||
if err != nil && !cgroups.IsNotFound(err) {
|
if err != nil && !cgroups.IsNotFound(err) {
|
||||||
return err
|
return err
|
||||||
|
} else if path == "" {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
if memoryAssigned(d.config) {
|
if memoryAssigned(d.config) {
|
||||||
if path != "" {
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||||
if err := os.MkdirAll(path, 0755); err != nil {
|
if err := os.MkdirAll(path, 0755); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
// Only enable kernel memory accouting when this cgroup
|
||||||
if d.config.KernelMemory != 0 {
|
// is created by libcontainer, otherwise we might get
|
||||||
|
// error when people use `cgroupsPath` to join an existed
|
||||||
|
// cgroup whose kernel memory is not initialized.
|
||||||
if err := EnableKernelMemoryAccounting(path); err != nil {
|
if err := EnableKernelMemoryAccounting(path); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -85,7 +95,7 @@ func setKernelMemory(path string, kernelMemoryLimit int64) error {
|
||||||
// once tasks have been attached to the cgroup
|
// once tasks have been attached to the cgroup
|
||||||
if pathErr, ok := err.(*os.PathError); ok {
|
if pathErr, ok := err.(*os.PathError); ok {
|
||||||
if errNo, ok := pathErr.Err.(syscall.Errno); ok {
|
if errNo, ok := pathErr.Err.(syscall.Errno); ok {
|
||||||
if errNo == syscall.EBUSY {
|
if errNo == unix.EBUSY {
|
||||||
return fmt.Errorf("failed to set %s, because either tasks have already joined this cgroup or it has children", cgroupKernelMemoryLimit)
|
return fmt.Errorf("failed to set %s, because either tasks have already joined this cgroup or it has children", cgroupKernelMemoryLimit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,9 +106,18 @@ func setKernelMemory(path string, kernelMemoryLimit int64) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func setMemoryAndSwap(path string, cgroup *configs.Cgroup) error {
|
func setMemoryAndSwap(path string, cgroup *configs.Cgroup) error {
|
||||||
|
// If the memory update is set to -1 we should also
|
||||||
|
// set swap to -1, it means unlimited memory.
|
||||||
|
if cgroup.Resources.Memory == -1 {
|
||||||
|
// Only set swap if it's enabled in kernel
|
||||||
|
if cgroups.PathExists(filepath.Join(path, cgroupMemorySwapLimit)) {
|
||||||
|
cgroup.Resources.MemorySwap = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// When memory and swap memory are both set, we need to handle the cases
|
// When memory and swap memory are both set, we need to handle the cases
|
||||||
// for updating container.
|
// for updating container.
|
||||||
if cgroup.Resources.Memory != 0 && cgroup.Resources.MemorySwap > 0 {
|
if cgroup.Resources.Memory != 0 && cgroup.Resources.MemorySwap != 0 {
|
||||||
memoryUsage, err := getMemoryData(path, "")
|
memoryUsage, err := getMemoryData(path, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -107,29 +126,29 @@ func setMemoryAndSwap(path string, cgroup *configs.Cgroup) error {
|
||||||
// When update memory limit, we should adapt the write sequence
|
// When update memory limit, we should adapt the write sequence
|
||||||
// for memory and swap memory, so it won't fail because the new
|
// for memory and swap memory, so it won't fail because the new
|
||||||
// value and the old value don't fit kernel's validation.
|
// value and the old value don't fit kernel's validation.
|
||||||
if memoryUsage.Limit < uint64(cgroup.Resources.MemorySwap) {
|
if cgroup.Resources.MemorySwap == -1 || memoryUsage.Limit < uint64(cgroup.Resources.MemorySwap) {
|
||||||
if err := writeFile(path, "memory.memsw.limit_in_bytes", strconv.FormatInt(cgroup.Resources.MemorySwap, 10)); err != nil {
|
if err := writeFile(path, cgroupMemorySwapLimit, strconv.FormatInt(cgroup.Resources.MemorySwap, 10)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := writeFile(path, "memory.limit_in_bytes", strconv.FormatInt(cgroup.Resources.Memory, 10)); err != nil {
|
if err := writeFile(path, cgroupMemoryLimit, strconv.FormatInt(cgroup.Resources.Memory, 10)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err := writeFile(path, "memory.limit_in_bytes", strconv.FormatInt(cgroup.Resources.Memory, 10)); err != nil {
|
if err := writeFile(path, cgroupMemoryLimit, strconv.FormatInt(cgroup.Resources.Memory, 10)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := writeFile(path, "memory.memsw.limit_in_bytes", strconv.FormatInt(cgroup.Resources.MemorySwap, 10)); err != nil {
|
if err := writeFile(path, cgroupMemorySwapLimit, strconv.FormatInt(cgroup.Resources.MemorySwap, 10)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if cgroup.Resources.Memory != 0 {
|
if cgroup.Resources.Memory != 0 {
|
||||||
if err := writeFile(path, "memory.limit_in_bytes", strconv.FormatInt(cgroup.Resources.Memory, 10)); err != nil {
|
if err := writeFile(path, cgroupMemoryLimit, strconv.FormatInt(cgroup.Resources.Memory, 10)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if cgroup.Resources.MemorySwap > 0 {
|
if cgroup.Resources.MemorySwap != 0 {
|
||||||
if err := writeFile(path, "memory.memsw.limit_in_bytes", strconv.FormatInt(cgroup.Resources.MemorySwap, 10)); err != nil {
|
if err := writeFile(path, cgroupMemorySwapLimit, strconv.FormatInt(cgroup.Resources.MemorySwap, 10)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -167,12 +186,12 @@ func (s *MemoryGroup) Set(path string, cgroup *configs.Cgroup) error {
|
||||||
}
|
}
|
||||||
if cgroup.Resources.MemorySwappiness == nil || int64(*cgroup.Resources.MemorySwappiness) == -1 {
|
if cgroup.Resources.MemorySwappiness == nil || int64(*cgroup.Resources.MemorySwappiness) == -1 {
|
||||||
return nil
|
return nil
|
||||||
} else if int64(*cgroup.Resources.MemorySwappiness) >= 0 && int64(*cgroup.Resources.MemorySwappiness) <= 100 {
|
} else if *cgroup.Resources.MemorySwappiness <= 100 {
|
||||||
if err := writeFile(path, "memory.swappiness", strconv.FormatInt(*cgroup.Resources.MemorySwappiness, 10)); err != nil {
|
if err := writeFile(path, "memory.swappiness", strconv.FormatUint(*cgroup.Resources.MemorySwappiness, 10)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("invalid value:%d. valid memory swappiness range is 0-100", int64(*cgroup.Resources.MemorySwappiness))
|
return fmt.Errorf("invalid value:%d. valid memory swappiness range is 0-100", *cgroup.Resources.MemorySwappiness)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -224,6 +243,14 @@ func (s *MemoryGroup) GetStats(path string, stats *cgroups.Stats) error {
|
||||||
}
|
}
|
||||||
stats.MemoryStats.KernelTCPUsage = kernelTCPUsage
|
stats.MemoryStats.KernelTCPUsage = kernelTCPUsage
|
||||||
|
|
||||||
|
useHierarchy := strings.Join([]string{"memory", "use_hierarchy"}, ".")
|
||||||
|
value, err := getCgroupParamUint(path, useHierarchy)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if value == 1 {
|
||||||
|
stats.MemoryStats.UseHierarchy = true
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,7 +261,7 @@ func memoryAssigned(cgroup *configs.Cgroup) bool {
|
||||||
cgroup.Resources.KernelMemory > 0 ||
|
cgroup.Resources.KernelMemory > 0 ||
|
||||||
cgroup.Resources.KernelMemoryTCP > 0 ||
|
cgroup.Resources.KernelMemoryTCP > 0 ||
|
||||||
cgroup.Resources.OomKillDisable ||
|
cgroup.Resources.OomKillDisable ||
|
||||||
(cgroup.Resources.MemorySwappiness != nil && *cgroup.Resources.MemorySwappiness != -1)
|
(cgroup.Resources.MemorySwappiness != nil && int64(*cgroup.Resources.MemorySwappiness) != -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMemoryData(path, name string) (cgroups.MemoryData, error) {
|
func getMemoryData(path, name string) (cgroups.MemoryData, error) {
|
||||||
|
|
2
vendor/github.com/opencontainers/runc/libcontainer/cgroups/stats.go
generated
vendored
2
vendor/github.com/opencontainers/runc/libcontainer/cgroups/stats.go
generated
vendored
|
@ -51,6 +51,8 @@ type MemoryStats struct {
|
||||||
KernelUsage MemoryData `json:"kernel_usage,omitempty"`
|
KernelUsage MemoryData `json:"kernel_usage,omitempty"`
|
||||||
// usage of kernel TCP memory
|
// usage of kernel TCP memory
|
||||||
KernelTCPUsage MemoryData `json:"kernel_tcp_usage,omitempty"`
|
KernelTCPUsage MemoryData `json:"kernel_tcp_usage,omitempty"`
|
||||||
|
// if true, memory usage is accounted for throughout a hierarchy of cgroups.
|
||||||
|
UseHierarchy bool `json:"use_hierarchy"`
|
||||||
|
|
||||||
Stats map[string]uint64 `json:"stats,omitempty"`
|
Stats map[string]uint64 `json:"stats,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
23
vendor/github.com/opencontainers/runc/libcontainer/cgroups/systemd/apply_systemd.go
generated
vendored
23
vendor/github.com/opencontainers/runc/libcontainer/cgroups/systemd/apply_systemd.go
generated
vendored
|
@ -5,7 +5,6 @@ package systemd
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -261,12 +260,19 @@ func (m *Manager) Apply(pid int) error {
|
||||||
|
|
||||||
if c.Resources.Memory != 0 {
|
if c.Resources.Memory != 0 {
|
||||||
properties = append(properties,
|
properties = append(properties,
|
||||||
newProp("MemoryLimit", uint64(c.Resources.Memory)))
|
newProp("MemoryLimit", c.Resources.Memory))
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Resources.CpuShares != 0 {
|
if c.Resources.CpuShares != 0 {
|
||||||
properties = append(properties,
|
properties = append(properties,
|
||||||
newProp("CPUShares", uint64(c.Resources.CpuShares)))
|
newProp("CPUShares", c.Resources.CpuShares))
|
||||||
|
}
|
||||||
|
|
||||||
|
// cpu.cfs_quota_us and cpu.cfs_period_us are controlled by systemd.
|
||||||
|
if c.Resources.CpuQuota != 0 && c.Resources.CpuPeriod != 0 {
|
||||||
|
cpuQuotaPerSecUSec := uint64(c.Resources.CpuQuota*1000000) / c.Resources.CpuPeriod
|
||||||
|
properties = append(properties,
|
||||||
|
newProp("CPUQuotaPerSecUSec", cpuQuotaPerSecUSec))
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Resources.BlkioWeight != 0 {
|
if c.Resources.BlkioWeight != 0 {
|
||||||
|
@ -327,15 +333,6 @@ func (m *Manager) GetPaths() map[string]string {
|
||||||
return paths
|
return paths
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeFile(dir, file, data string) error {
|
|
||||||
// Normally dir should not be empty, one case is that cgroup subsystem
|
|
||||||
// is not mounted, we will get empty dir, and we want it fail here.
|
|
||||||
if dir == "" {
|
|
||||||
return fmt.Errorf("no such directory for %s", file)
|
|
||||||
}
|
|
||||||
return ioutil.WriteFile(filepath.Join(dir, file), []byte(data), 0700)
|
|
||||||
}
|
|
||||||
|
|
||||||
func join(c *configs.Cgroup, subsystem string, pid int) (string, error) {
|
func join(c *configs.Cgroup, subsystem string, pid int) (string, error) {
|
||||||
path, err := getSubsystemPath(c, subsystem)
|
path, err := getSubsystemPath(c, subsystem)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -429,7 +426,7 @@ func getSubsystemPath(c *configs.Cgroup, subsystem string) (string, error) {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
initPath, err := cgroups.GetInitCgroupDir(subsystem)
|
initPath, err := cgroups.GetInitCgroup(subsystem)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
56
vendor/github.com/opencontainers/runc/libcontainer/cgroups/utils.go
generated
vendored
56
vendor/github.com/opencontainers/runc/libcontainer/cgroups/utils.go
generated
vendored
|
@ -66,6 +66,21 @@ func isSubsystemAvailable(subsystem string) bool {
|
||||||
return avail
|
return avail
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetClosestMountpointAncestor(dir, mountinfo string) string {
|
||||||
|
deepestMountPoint := ""
|
||||||
|
for _, mountInfoEntry := range strings.Split(mountinfo, "\n") {
|
||||||
|
mountInfoParts := strings.Fields(mountInfoEntry)
|
||||||
|
if len(mountInfoParts) < 5 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
mountPoint := mountInfoParts[4]
|
||||||
|
if strings.HasPrefix(mountPoint, deepestMountPoint) && strings.HasPrefix(dir, mountPoint) {
|
||||||
|
deepestMountPoint = mountPoint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return deepestMountPoint
|
||||||
|
}
|
||||||
|
|
||||||
func FindCgroupMountpointDir() (string, error) {
|
func FindCgroupMountpointDir() (string, error) {
|
||||||
f, err := os.Open("/proc/self/mountinfo")
|
f, err := os.Open("/proc/self/mountinfo")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -109,7 +124,7 @@ type Mount struct {
|
||||||
Subsystems []string
|
Subsystems []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Mount) GetThisCgroupDir(cgroups map[string]string) (string, error) {
|
func (m Mount) GetOwnCgroup(cgroups map[string]string) (string, error) {
|
||||||
if len(m.Subsystems) == 0 {
|
if len(m.Subsystems) == 0 {
|
||||||
return "", fmt.Errorf("no subsystem for mount")
|
return "", fmt.Errorf("no subsystem for mount")
|
||||||
}
|
}
|
||||||
|
@ -203,8 +218,8 @@ func GetAllSubsystems() ([]string, error) {
|
||||||
return subsystems, nil
|
return subsystems, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetThisCgroupDir returns the relative path to the cgroup docker is running in.
|
// GetOwnCgroup returns the relative path to the cgroup docker is running in.
|
||||||
func GetThisCgroupDir(subsystem string) (string, error) {
|
func GetOwnCgroup(subsystem string) (string, error) {
|
||||||
cgroups, err := ParseCgroupFile("/proc/self/cgroup")
|
cgroups, err := ParseCgroupFile("/proc/self/cgroup")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -213,8 +228,16 @@ func GetThisCgroupDir(subsystem string) (string, error) {
|
||||||
return getControllerPath(subsystem, cgroups)
|
return getControllerPath(subsystem, cgroups)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetInitCgroupDir(subsystem string) (string, error) {
|
func GetOwnCgroupPath(subsystem string) (string, error) {
|
||||||
|
cgroup, err := GetOwnCgroup(subsystem)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return getCgroupPathHelper(subsystem, cgroup)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetInitCgroup(subsystem string) (string, error) {
|
||||||
cgroups, err := ParseCgroupFile("/proc/1/cgroup")
|
cgroups, err := ParseCgroupFile("/proc/1/cgroup")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -223,6 +246,31 @@ func GetInitCgroupDir(subsystem string) (string, error) {
|
||||||
return getControllerPath(subsystem, cgroups)
|
return getControllerPath(subsystem, cgroups)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetInitCgroupPath(subsystem string) (string, error) {
|
||||||
|
cgroup, err := GetInitCgroup(subsystem)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return getCgroupPathHelper(subsystem, cgroup)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCgroupPathHelper(subsystem, cgroup string) (string, error) {
|
||||||
|
mnt, root, err := FindCgroupMountpointAndRoot(subsystem)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is needed for nested containers, because in /proc/self/cgroup we
|
||||||
|
// see pathes from host, which don't exist in container.
|
||||||
|
relCgroup, err := filepath.Rel(root, cgroup)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return filepath.Join(mnt, relCgroup), nil
|
||||||
|
}
|
||||||
|
|
||||||
func readProcsFile(dir string) ([]int, error) {
|
func readProcsFile(dir string) ([]int, error) {
|
||||||
f, err := os.Open(filepath.Join(dir, CgroupProcesses))
|
f, err := os.Open(filepath.Join(dir, CgroupProcesses))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
// +build linux freebsd
|
|
||||||
|
|
||||||
package configs
|
package configs
|
||||||
|
|
||||||
type FreezerState string
|
type FreezerState string
|
||||||
|
@ -60,19 +58,19 @@ type Resources struct {
|
||||||
KernelMemoryTCP int64 `json:"kernel_memory_tcp"`
|
KernelMemoryTCP int64 `json:"kernel_memory_tcp"`
|
||||||
|
|
||||||
// CPU shares (relative weight vs. other containers)
|
// CPU shares (relative weight vs. other containers)
|
||||||
CpuShares int64 `json:"cpu_shares"`
|
CpuShares uint64 `json:"cpu_shares"`
|
||||||
|
|
||||||
// CPU hardcap limit (in usecs). Allowed cpu time in a given period.
|
// CPU hardcap limit (in usecs). Allowed cpu time in a given period.
|
||||||
CpuQuota int64 `json:"cpu_quota"`
|
CpuQuota int64 `json:"cpu_quota"`
|
||||||
|
|
||||||
// CPU period to be used for hardcapping (in usecs). 0 to use system default.
|
// CPU period to be used for hardcapping (in usecs). 0 to use system default.
|
||||||
CpuPeriod int64 `json:"cpu_period"`
|
CpuPeriod uint64 `json:"cpu_period"`
|
||||||
|
|
||||||
// How many time CPU will use in realtime scheduling (in usecs).
|
// How many time CPU will use in realtime scheduling (in usecs).
|
||||||
CpuRtRuntime int64 `json:"cpu_rt_quota"`
|
CpuRtRuntime int64 `json:"cpu_rt_quota"`
|
||||||
|
|
||||||
// CPU period to be used for realtime scheduling (in usecs).
|
// CPU period to be used for realtime scheduling (in usecs).
|
||||||
CpuRtPeriod int64 `json:"cpu_rt_period"`
|
CpuRtPeriod uint64 `json:"cpu_rt_period"`
|
||||||
|
|
||||||
// CPU to use
|
// CPU to use
|
||||||
CpusetCpus string `json:"cpuset_cpus"`
|
CpusetCpus string `json:"cpuset_cpus"`
|
||||||
|
@ -114,7 +112,7 @@ type Resources struct {
|
||||||
OomKillDisable bool `json:"oom_kill_disable"`
|
OomKillDisable bool `json:"oom_kill_disable"`
|
||||||
|
|
||||||
// Tuning swappiness behaviour per cgroup
|
// Tuning swappiness behaviour per cgroup
|
||||||
MemorySwappiness *int64 `json:"memory_swappiness"`
|
MemorySwappiness *uint64 `json:"memory_swappiness"`
|
||||||
|
|
||||||
// Set priority of network traffic for container
|
// Set priority of network traffic for container
|
||||||
NetPrioIfpriomap []*IfPrioMap `json:"net_prio_ifpriomap"`
|
NetPrioIfpriomap []*IfPrioMap `json:"net_prio_ifpriomap"`
|
20
vendor/github.com/opencontainers/runc/libcontainer/configs/config.go
generated
vendored
20
vendor/github.com/opencontainers/runc/libcontainer/configs/config.go
generated
vendored
|
@ -113,8 +113,8 @@ type Config struct {
|
||||||
Namespaces Namespaces `json:"namespaces"`
|
Namespaces Namespaces `json:"namespaces"`
|
||||||
|
|
||||||
// Capabilities specify the capabilities to keep when executing the process inside the container
|
// Capabilities specify the capabilities to keep when executing the process inside the container
|
||||||
// All capbilities not specified will be dropped from the processes capability mask
|
// All capabilities not specified will be dropped from the processes capability mask
|
||||||
Capabilities []string `json:"capabilities"`
|
Capabilities *Capabilities `json:"capabilities"`
|
||||||
|
|
||||||
// Networks specifies the container's network setup to be created
|
// Networks specifies the container's network setup to be created
|
||||||
Networks []*Network `json:"networks"`
|
Networks []*Network `json:"networks"`
|
||||||
|
@ -183,6 +183,9 @@ type Config struct {
|
||||||
// NoNewKeyring will not allocated a new session keyring for the container. It will use the
|
// NoNewKeyring will not allocated a new session keyring for the container. It will use the
|
||||||
// callers keyring in this case.
|
// callers keyring in this case.
|
||||||
NoNewKeyring bool `json:"no_new_keyring"`
|
NoNewKeyring bool `json:"no_new_keyring"`
|
||||||
|
|
||||||
|
// Rootless specifies whether the container is a rootless container.
|
||||||
|
Rootless bool `json:"rootless"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Hooks struct {
|
type Hooks struct {
|
||||||
|
@ -197,6 +200,19 @@ type Hooks struct {
|
||||||
Poststop []Hook
|
Poststop []Hook
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Capabilities struct {
|
||||||
|
// Bounding is the set of capabilities checked by the kernel.
|
||||||
|
Bounding []string
|
||||||
|
// Effective is the set of capabilities checked by the kernel.
|
||||||
|
Effective []string
|
||||||
|
// Inheritable is the capabilities preserved across execve.
|
||||||
|
Inheritable []string
|
||||||
|
// Permitted is the limiting superset for effective capabilities.
|
||||||
|
Permitted []string
|
||||||
|
// Ambient is the ambient set of capabilities that are kept.
|
||||||
|
Ambient []string
|
||||||
|
}
|
||||||
|
|
||||||
func (hooks *Hooks) UnmarshalJSON(b []byte) error {
|
func (hooks *Hooks) UnmarshalJSON(b []byte) error {
|
||||||
var state struct {
|
var state struct {
|
||||||
Prestart []CommandHook
|
Prestart []CommandHook
|
||||||
|
|
61
vendor/github.com/opencontainers/runc/libcontainer/configs/config_linux.go
generated
vendored
Normal file
61
vendor/github.com/opencontainers/runc/libcontainer/configs/config_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
package configs
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// HostUID gets the translated uid for the process on host which could be
|
||||||
|
// different when user namespaces are enabled.
|
||||||
|
func (c Config) HostUID(containerId int) (int, error) {
|
||||||
|
if c.Namespaces.Contains(NEWUSER) {
|
||||||
|
if c.UidMappings == nil {
|
||||||
|
return -1, fmt.Errorf("User namespaces enabled, but no uid mappings found.")
|
||||||
|
}
|
||||||
|
id, found := c.hostIDFromMapping(containerId, c.UidMappings)
|
||||||
|
if !found {
|
||||||
|
return -1, fmt.Errorf("User namespaces enabled, but no user mapping found.")
|
||||||
|
}
|
||||||
|
return id, nil
|
||||||
|
}
|
||||||
|
// Return unchanged id.
|
||||||
|
return containerId, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HostRootUID gets the root uid for the process on host which could be non-zero
|
||||||
|
// when user namespaces are enabled.
|
||||||
|
func (c Config) HostRootUID() (int, error) {
|
||||||
|
return c.HostUID(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HostGID gets the translated gid for the process on host which could be
|
||||||
|
// different when user namespaces are enabled.
|
||||||
|
func (c Config) HostGID(containerId int) (int, error) {
|
||||||
|
if c.Namespaces.Contains(NEWUSER) {
|
||||||
|
if c.GidMappings == nil {
|
||||||
|
return -1, fmt.Errorf("User namespaces enabled, but no gid mappings found.")
|
||||||
|
}
|
||||||
|
id, found := c.hostIDFromMapping(containerId, c.GidMappings)
|
||||||
|
if !found {
|
||||||
|
return -1, fmt.Errorf("User namespaces enabled, but no group mapping found.")
|
||||||
|
}
|
||||||
|
return id, nil
|
||||||
|
}
|
||||||
|
// Return unchanged id.
|
||||||
|
return containerId, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HostRootGID gets the root gid for the process on host which could be non-zero
|
||||||
|
// when user namespaces are enabled.
|
||||||
|
func (c Config) HostRootGID() (int, error) {
|
||||||
|
return c.HostGID(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utility function that gets a host ID for a container ID from user namespace map
|
||||||
|
// if that ID is present in the map.
|
||||||
|
func (c Config) hostIDFromMapping(containerID int, uMap []IDMap) (int, bool) {
|
||||||
|
for _, m := range uMap {
|
||||||
|
if (containerID >= m.ContainerID) && (containerID <= (m.ContainerID + m.Size - 1)) {
|
||||||
|
hostID := m.HostID + (containerID - m.ContainerID)
|
||||||
|
return hostID, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1, false
|
||||||
|
}
|
51
vendor/github.com/opencontainers/runc/libcontainer/configs/config_unix.go
generated
vendored
51
vendor/github.com/opencontainers/runc/libcontainer/configs/config_unix.go
generated
vendored
|
@ -1,51 +0,0 @@
|
||||||
// +build freebsd linux
|
|
||||||
|
|
||||||
package configs
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
// HostUID gets the root uid for the process on host which could be non-zero
|
|
||||||
// when user namespaces are enabled.
|
|
||||||
func (c Config) HostUID() (int, error) {
|
|
||||||
if c.Namespaces.Contains(NEWUSER) {
|
|
||||||
if c.UidMappings == nil {
|
|
||||||
return -1, fmt.Errorf("User namespaces enabled, but no user mappings found.")
|
|
||||||
}
|
|
||||||
id, found := c.hostIDFromMapping(0, c.UidMappings)
|
|
||||||
if !found {
|
|
||||||
return -1, fmt.Errorf("User namespaces enabled, but no root user mapping found.")
|
|
||||||
}
|
|
||||||
return id, nil
|
|
||||||
}
|
|
||||||
// Return default root uid 0
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// HostGID gets the root gid for the process on host which could be non-zero
|
|
||||||
// when user namespaces are enabled.
|
|
||||||
func (c Config) HostGID() (int, error) {
|
|
||||||
if c.Namespaces.Contains(NEWUSER) {
|
|
||||||
if c.GidMappings == nil {
|
|
||||||
return -1, fmt.Errorf("User namespaces enabled, but no gid mappings found.")
|
|
||||||
}
|
|
||||||
id, found := c.hostIDFromMapping(0, c.GidMappings)
|
|
||||||
if !found {
|
|
||||||
return -1, fmt.Errorf("User namespaces enabled, but no root group mapping found.")
|
|
||||||
}
|
|
||||||
return id, nil
|
|
||||||
}
|
|
||||||
// Return default root gid 0
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Utility function that gets a host ID for a container ID from user namespace map
|
|
||||||
// if that ID is present in the map.
|
|
||||||
func (c Config) hostIDFromMapping(containerID int, uMap []IDMap) (int, bool) {
|
|
||||||
for _, m := range uMap {
|
|
||||||
if (containerID >= m.ContainerID) && (containerID <= (m.ContainerID + m.Size - 1)) {
|
|
||||||
hostID := m.HostID + (containerID - m.ContainerID)
|
|
||||||
return hostID, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1, false
|
|
||||||
}
|
|
|
@ -1,5 +1,3 @@
|
||||||
// +build linux freebsd
|
|
||||||
|
|
||||||
package configs
|
package configs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -64,12 +62,12 @@ func IsNamespaceSupported(ns NamespaceType) bool {
|
||||||
|
|
||||||
func NamespaceTypes() []NamespaceType {
|
func NamespaceTypes() []NamespaceType {
|
||||||
return []NamespaceType{
|
return []NamespaceType{
|
||||||
|
NEWUSER, // Keep user NS always first, don't move it.
|
||||||
|
NEWIPC,
|
||||||
|
NEWUTS,
|
||||||
NEWNET,
|
NEWNET,
|
||||||
NEWPID,
|
NEWPID,
|
||||||
NEWNS,
|
NEWNS,
|
||||||
NEWUTS,
|
|
||||||
NEWIPC,
|
|
||||||
NEWUSER,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
14
vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_syscall.go
generated
vendored
14
vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_syscall.go
generated
vendored
|
@ -2,19 +2,19 @@
|
||||||
|
|
||||||
package configs
|
package configs
|
||||||
|
|
||||||
import "syscall"
|
import "golang.org/x/sys/unix"
|
||||||
|
|
||||||
func (n *Namespace) Syscall() int {
|
func (n *Namespace) Syscall() int {
|
||||||
return namespaceInfo[n.Type]
|
return namespaceInfo[n.Type]
|
||||||
}
|
}
|
||||||
|
|
||||||
var namespaceInfo = map[NamespaceType]int{
|
var namespaceInfo = map[NamespaceType]int{
|
||||||
NEWNET: syscall.CLONE_NEWNET,
|
NEWNET: unix.CLONE_NEWNET,
|
||||||
NEWNS: syscall.CLONE_NEWNS,
|
NEWNS: unix.CLONE_NEWNS,
|
||||||
NEWUSER: syscall.CLONE_NEWUSER,
|
NEWUSER: unix.CLONE_NEWUSER,
|
||||||
NEWIPC: syscall.CLONE_NEWIPC,
|
NEWIPC: unix.CLONE_NEWIPC,
|
||||||
NEWUTS: syscall.CLONE_NEWUTS,
|
NEWUTS: unix.CLONE_NEWUTS,
|
||||||
NEWPID: syscall.CLONE_NEWPID,
|
NEWPID: unix.CLONE_NEWPID,
|
||||||
}
|
}
|
||||||
|
|
||||||
// CloneFlags parses the container's Namespaces options to set the correct
|
// CloneFlags parses the container's Namespaces options to set the correct
|
||||||
|
|
2
vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_unsupported.go
generated
vendored
2
vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_unsupported.go
generated
vendored
|
@ -1,4 +1,4 @@
|
||||||
// +build !linux,!freebsd
|
// +build !linux
|
||||||
|
|
||||||
package configs
|
package configs
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
// +build linux freebsd
|
|
||||||
|
|
||||||
package devices
|
package devices
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -8,9 +6,11 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"syscall"
|
"syscall" //only for Stat_t
|
||||||
|
|
||||||
"github.com/opencontainers/runc/libcontainer/configs"
|
"github.com/opencontainers/runc/libcontainer/configs"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -38,10 +38,10 @@ func DeviceFromPath(path, permissions string) (*configs.Device, error) {
|
||||||
case mode&os.ModeDevice == 0:
|
case mode&os.ModeDevice == 0:
|
||||||
return nil, ErrNotADevice
|
return nil, ErrNotADevice
|
||||||
case mode&os.ModeCharDevice != 0:
|
case mode&os.ModeCharDevice != 0:
|
||||||
fileModePermissionBits |= syscall.S_IFCHR
|
fileModePermissionBits |= unix.S_IFCHR
|
||||||
devType = 'c'
|
devType = 'c'
|
||||||
default:
|
default:
|
||||||
fileModePermissionBits |= syscall.S_IFBLK
|
fileModePermissionBits |= unix.S_IFBLK
|
||||||
devType = 'b'
|
devType = 'b'
|
||||||
}
|
}
|
||||||
stat_t, ok := fileInfo.Sys().(*syscall.Stat_t)
|
stat_t, ok := fileInfo.Sys().(*syscall.Stat_t)
|
||||||
|
@ -75,7 +75,8 @@ func getDevices(path string) ([]*configs.Device, error) {
|
||||||
switch {
|
switch {
|
||||||
case f.IsDir():
|
case f.IsDir():
|
||||||
switch f.Name() {
|
switch f.Name() {
|
||||||
case "pts", "shm", "fd", "mqueue":
|
// ".lxc" & ".lxd-mounts" added to address https://github.com/lxc/lxd/issues/2825
|
||||||
|
case "pts", "shm", "fd", "mqueue", ".lxc", ".lxd-mounts":
|
||||||
continue
|
continue
|
||||||
default:
|
default:
|
||||||
sub, err := getDevices(filepath.Join(path, f.Name()))
|
sub, err := getDevices(filepath.Join(path, f.Name()))
|
2
vendor/github.com/opencontainers/runc/libcontainer/devices/devices_unsupported.go
generated
vendored
2
vendor/github.com/opencontainers/runc/libcontainer/devices/devices_unsupported.go
generated
vendored
|
@ -1,3 +1,3 @@
|
||||||
// +build windows
|
// +build !linux
|
||||||
|
|
||||||
package devices
|
package devices
|
||||||
|
|
186
vendor/github.com/opencontainers/runc/libcontainer/nsenter/nsexec.c
generated
vendored
186
vendor/github.com/opencontainers/runc/libcontainer/nsenter/nsexec.c
generated
vendored
|
@ -33,7 +33,8 @@ enum sync_t {
|
||||||
SYNC_USERMAP_ACK = 0x41, /* Mapping finished by the parent. */
|
SYNC_USERMAP_ACK = 0x41, /* Mapping finished by the parent. */
|
||||||
SYNC_RECVPID_PLS = 0x42, /* Tell parent we're sending the PID. */
|
SYNC_RECVPID_PLS = 0x42, /* Tell parent we're sending the PID. */
|
||||||
SYNC_RECVPID_ACK = 0x43, /* PID was correctly received by parent. */
|
SYNC_RECVPID_ACK = 0x43, /* PID was correctly received by parent. */
|
||||||
SYNC_CHILD_READY = 0x44, /* The grandchild is ready to return. */
|
SYNC_GRANDCHILD = 0x44, /* The grandchild is ready to run. */
|
||||||
|
SYNC_CHILD_READY = 0x45, /* The child or grandchild is ready to return. */
|
||||||
|
|
||||||
/* XXX: This doesn't help with segfaults and other such issues. */
|
/* XXX: This doesn't help with segfaults and other such issues. */
|
||||||
SYNC_ERR = 0xFF, /* Fatal error, no turning back. The error code follows. */
|
SYNC_ERR = 0xFF, /* Fatal error, no turning back. The error code follows. */
|
||||||
|
@ -71,6 +72,9 @@ struct nlconfig_t {
|
||||||
char *namespaces;
|
char *namespaces;
|
||||||
size_t namespaces_len;
|
size_t namespaces_len;
|
||||||
uint8_t is_setgroup;
|
uint8_t is_setgroup;
|
||||||
|
uint8_t is_rootless;
|
||||||
|
char *oom_score_adj;
|
||||||
|
size_t oom_score_adj_len;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -83,6 +87,8 @@ struct nlconfig_t {
|
||||||
#define UIDMAP_ATTR 27283
|
#define UIDMAP_ATTR 27283
|
||||||
#define GIDMAP_ATTR 27284
|
#define GIDMAP_ATTR 27284
|
||||||
#define SETGROUP_ATTR 27285
|
#define SETGROUP_ATTR 27285
|
||||||
|
#define OOM_SCORE_ADJ_ATTR 27286
|
||||||
|
#define ROOTLESS_ATTR 27287
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Use the raw syscall for versions of glibc which don't include a function for
|
* Use the raw syscall for versions of glibc which don't include a function for
|
||||||
|
@ -171,6 +177,7 @@ static void update_setgroups(int pid, enum policy_t setgroup)
|
||||||
policy = "deny";
|
policy = "deny";
|
||||||
break;
|
break;
|
||||||
case SETGROUPS_DEFAULT:
|
case SETGROUPS_DEFAULT:
|
||||||
|
default:
|
||||||
/* Nothing to do. */
|
/* Nothing to do. */
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -185,7 +192,7 @@ static void update_setgroups(int pid, enum policy_t setgroup)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void update_uidmap(int pid, char *map, int map_len)
|
static void update_uidmap(int pid, char *map, size_t map_len)
|
||||||
{
|
{
|
||||||
if (map == NULL || map_len <= 0)
|
if (map == NULL || map_len <= 0)
|
||||||
return;
|
return;
|
||||||
|
@ -194,7 +201,7 @@ static void update_uidmap(int pid, char *map, int map_len)
|
||||||
bail("failed to update /proc/%d/uid_map", pid);
|
bail("failed to update /proc/%d/uid_map", pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void update_gidmap(int pid, char *map, int map_len)
|
static void update_gidmap(int pid, char *map, size_t map_len)
|
||||||
{
|
{
|
||||||
if (map == NULL || map_len <= 0)
|
if (map == NULL || map_len <= 0)
|
||||||
return;
|
return;
|
||||||
|
@ -203,6 +210,15 @@ static void update_gidmap(int pid, char *map, int map_len)
|
||||||
bail("failed to update /proc/%d/gid_map", pid);
|
bail("failed to update /proc/%d/gid_map", pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void update_oom_score_adj(char *data, size_t len)
|
||||||
|
{
|
||||||
|
if (data == NULL || len <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (write_file(data, len, "/proc/self/oom_score_adj") < 0)
|
||||||
|
bail("failed to update /proc/self/oom_score_adj");
|
||||||
|
}
|
||||||
|
|
||||||
/* A dummy function that just jumps to the given jumpval. */
|
/* A dummy function that just jumps to the given jumpval. */
|
||||||
static int child_func(void *arg) __attribute__ ((noinline));
|
static int child_func(void *arg) __attribute__ ((noinline));
|
||||||
static int child_func(void *arg)
|
static int child_func(void *arg)
|
||||||
|
@ -284,7 +300,7 @@ static void nl_parse(int fd, struct nlconfig_t *config)
|
||||||
/* Retrieve the netlink header. */
|
/* Retrieve the netlink header. */
|
||||||
len = read(fd, &hdr, NLMSG_HDRLEN);
|
len = read(fd, &hdr, NLMSG_HDRLEN);
|
||||||
if (len != NLMSG_HDRLEN)
|
if (len != NLMSG_HDRLEN)
|
||||||
bail("invalid netlink header length %lu", len);
|
bail("invalid netlink header length %zu", len);
|
||||||
|
|
||||||
if (hdr.nlmsg_type == NLMSG_ERROR)
|
if (hdr.nlmsg_type == NLMSG_ERROR)
|
||||||
bail("failed to read netlink message");
|
bail("failed to read netlink message");
|
||||||
|
@ -300,7 +316,7 @@ static void nl_parse(int fd, struct nlconfig_t *config)
|
||||||
|
|
||||||
len = read(fd, data, size);
|
len = read(fd, data, size);
|
||||||
if (len != size)
|
if (len != size)
|
||||||
bail("failed to read netlink payload, %lu != %lu", len, size);
|
bail("failed to read netlink payload, %zu != %zu", len, size);
|
||||||
|
|
||||||
/* Parse the netlink payload. */
|
/* Parse the netlink payload. */
|
||||||
config->data = data;
|
config->data = data;
|
||||||
|
@ -316,6 +332,13 @@ static void nl_parse(int fd, struct nlconfig_t *config)
|
||||||
case CLONE_FLAGS_ATTR:
|
case CLONE_FLAGS_ATTR:
|
||||||
config->cloneflags = readint32(current);
|
config->cloneflags = readint32(current);
|
||||||
break;
|
break;
|
||||||
|
case ROOTLESS_ATTR:
|
||||||
|
config->is_rootless = readint8(current);
|
||||||
|
break;
|
||||||
|
case OOM_SCORE_ADJ_ATTR:
|
||||||
|
config->oom_score_adj = current;
|
||||||
|
config->oom_score_adj_len = payload_len;
|
||||||
|
break;
|
||||||
case NS_PATHS_ATTR:
|
case NS_PATHS_ATTR:
|
||||||
config->namespaces = current;
|
config->namespaces = current;
|
||||||
config->namespaces_len = payload_len;
|
config->namespaces_len = payload_len;
|
||||||
|
@ -413,7 +436,7 @@ void nsexec(void)
|
||||||
{
|
{
|
||||||
int pipenum;
|
int pipenum;
|
||||||
jmp_buf env;
|
jmp_buf env;
|
||||||
int syncpipe[2];
|
int sync_child_pipe[2], sync_grandchild_pipe[2];
|
||||||
struct nlconfig_t config = {0};
|
struct nlconfig_t config = {0};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -424,18 +447,43 @@ void nsexec(void)
|
||||||
if (pipenum == -1)
|
if (pipenum == -1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* make the process non-dumpable */
|
|
||||||
if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) != 0) {
|
|
||||||
bail("failed to set process as non-dumpable");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Parse all of the netlink configuration. */
|
/* Parse all of the netlink configuration. */
|
||||||
nl_parse(pipenum, &config);
|
nl_parse(pipenum, &config);
|
||||||
|
|
||||||
|
/* Set oom_score_adj. This has to be done before !dumpable because
|
||||||
|
* /proc/self/oom_score_adj is not writeable unless you're an privileged
|
||||||
|
* user (if !dumpable is set). All children inherit their parent's
|
||||||
|
* oom_score_adj value on fork(2) so this will always be propagated
|
||||||
|
* properly.
|
||||||
|
*/
|
||||||
|
update_oom_score_adj(config.oom_score_adj, config.oom_score_adj_len);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make the process non-dumpable, to avoid various race conditions that
|
||||||
|
* could cause processes in namespaces we're joining to access host
|
||||||
|
* resources (or potentially execute code).
|
||||||
|
*
|
||||||
|
* However, if the number of namespaces we are joining is 0, we are not
|
||||||
|
* going to be switching to a different security context. Thus setting
|
||||||
|
* ourselves to be non-dumpable only breaks things (like rootless
|
||||||
|
* containers), which is the recommendation from the kernel folks.
|
||||||
|
*/
|
||||||
|
if (config.namespaces) {
|
||||||
|
if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) < 0)
|
||||||
|
bail("failed to set process as non-dumpable");
|
||||||
|
}
|
||||||
|
|
||||||
/* Pipe so we can tell the child when we've finished setting up. */
|
/* Pipe so we can tell the child when we've finished setting up. */
|
||||||
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, syncpipe) < 0)
|
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sync_child_pipe) < 0)
|
||||||
bail("failed to setup sync pipe between parent and child");
|
bail("failed to setup sync pipe between parent and child");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need a new socketpair to sync with grandchild so we don't have
|
||||||
|
* race condition with child.
|
||||||
|
*/
|
||||||
|
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sync_grandchild_pipe) < 0)
|
||||||
|
bail("failed to setup sync pipe between parent and grandchild");
|
||||||
|
|
||||||
/* TODO: Currently we aren't dealing with child deaths properly. */
|
/* TODO: Currently we aren't dealing with child deaths properly. */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -494,9 +542,10 @@ void nsexec(void)
|
||||||
* process.
|
* process.
|
||||||
*/
|
*/
|
||||||
case JUMP_PARENT: {
|
case JUMP_PARENT: {
|
||||||
int len, ready = 0;
|
int len;
|
||||||
pid_t child;
|
pid_t child;
|
||||||
char buf[JSON_MAX];
|
char buf[JSON_MAX];
|
||||||
|
bool ready = false;
|
||||||
|
|
||||||
/* For debugging. */
|
/* For debugging. */
|
||||||
prctl(PR_SET_NAME, (unsigned long) "runc:[0:PARENT]", 0, 0, 0);
|
prctl(PR_SET_NAME, (unsigned long) "runc:[0:PARENT]", 0, 0, 0);
|
||||||
|
@ -513,30 +562,39 @@ void nsexec(void)
|
||||||
* ready, so we can receive all possible error codes
|
* ready, so we can receive all possible error codes
|
||||||
* generated by children.
|
* generated by children.
|
||||||
*/
|
*/
|
||||||
while (ready < 2) {
|
while (!ready) {
|
||||||
enum sync_t s;
|
enum sync_t s;
|
||||||
|
int ret;
|
||||||
|
|
||||||
/* This doesn't need to be global, we're in the parent. */
|
syncfd = sync_child_pipe[1];
|
||||||
int syncfd = syncpipe[1];
|
close(sync_child_pipe[0]);
|
||||||
|
|
||||||
if (read(syncfd, &s, sizeof(s)) != sizeof(s))
|
if (read(syncfd, &s, sizeof(s)) != sizeof(s))
|
||||||
bail("failed to sync with child: next state");
|
bail("failed to sync with child: next state");
|
||||||
|
|
||||||
switch (s) {
|
switch (s) {
|
||||||
case SYNC_ERR: {
|
case SYNC_ERR:
|
||||||
/* We have to mirror the error code of the child. */
|
/* We have to mirror the error code of the child. */
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (read(syncfd, &ret, sizeof(ret)) != sizeof(ret))
|
if (read(syncfd, &ret, sizeof(ret)) != sizeof(ret))
|
||||||
bail("failed to sync with child: read(error code)");
|
bail("failed to sync with child: read(error code)");
|
||||||
|
|
||||||
exit(ret);
|
exit(ret);
|
||||||
}
|
|
||||||
break;
|
|
||||||
case SYNC_USERMAP_PLS:
|
case SYNC_USERMAP_PLS:
|
||||||
/* Enable setgroups(2) if we've been asked to. */
|
/*
|
||||||
|
* Enable setgroups(2) if we've been asked to. But we also
|
||||||
|
* have to explicitly disable setgroups(2) if we're
|
||||||
|
* creating a rootless container (this is required since
|
||||||
|
* Linux 3.19).
|
||||||
|
*/
|
||||||
|
if (config.is_rootless && config.is_setgroup) {
|
||||||
|
kill(child, SIGKILL);
|
||||||
|
bail("cannot allow setgroup in an unprivileged user namespace setup");
|
||||||
|
}
|
||||||
|
|
||||||
if (config.is_setgroup)
|
if (config.is_setgroup)
|
||||||
update_setgroups(child, SETGROUPS_ALLOW);
|
update_setgroups(child, SETGROUPS_ALLOW);
|
||||||
|
if (config.is_rootless)
|
||||||
|
update_setgroups(child, SETGROUPS_DENY);
|
||||||
|
|
||||||
/* Set up mappings. */
|
/* Set up mappings. */
|
||||||
update_uidmap(child, config.uidmap, config.uidmap_len);
|
update_uidmap(child, config.uidmap, config.uidmap_len);
|
||||||
|
@ -548,11 +606,6 @@ void nsexec(void)
|
||||||
bail("failed to sync with child: write(SYNC_USERMAP_ACK)");
|
bail("failed to sync with child: write(SYNC_USERMAP_ACK)");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SYNC_USERMAP_ACK:
|
|
||||||
/* We should _never_ receive acks. */
|
|
||||||
kill(child, SIGKILL);
|
|
||||||
bail("failed to sync with child: unexpected SYNC_USERMAP_ACK");
|
|
||||||
break;
|
|
||||||
case SYNC_RECVPID_PLS: {
|
case SYNC_RECVPID_PLS: {
|
||||||
pid_t old = child;
|
pid_t old = child;
|
||||||
|
|
||||||
|
@ -570,20 +623,46 @@ void nsexec(void)
|
||||||
bail("failed to sync with child: write(SYNC_RECVPID_ACK)");
|
bail("failed to sync with child: write(SYNC_RECVPID_ACK)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ready++;
|
|
||||||
break;
|
|
||||||
case SYNC_RECVPID_ACK:
|
|
||||||
/* We should _never_ receive acks. */
|
|
||||||
kill(child, SIGKILL);
|
|
||||||
bail("failed to sync with child: unexpected SYNC_RECVPID_ACK");
|
|
||||||
break;
|
break;
|
||||||
case SYNC_CHILD_READY:
|
case SYNC_CHILD_READY:
|
||||||
ready++;
|
ready = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
bail("unexpected sync value");
|
bail("unexpected sync value: %u", s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now sync with grandchild. */
|
||||||
|
|
||||||
|
ready = false;
|
||||||
|
while (!ready) {
|
||||||
|
enum sync_t s;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
syncfd = sync_grandchild_pipe[1];
|
||||||
|
close(sync_grandchild_pipe[0]);
|
||||||
|
|
||||||
|
s = SYNC_GRANDCHILD;
|
||||||
|
if (write(syncfd, &s, sizeof(s)) != sizeof(s)) {
|
||||||
|
kill(child, SIGKILL);
|
||||||
|
bail("failed to sync with child: write(SYNC_GRANDCHILD)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read(syncfd, &s, sizeof(s)) != sizeof(s))
|
||||||
|
bail("failed to sync with child: next state");
|
||||||
|
|
||||||
|
switch (s) {
|
||||||
|
case SYNC_ERR:
|
||||||
|
/* We have to mirror the error code of the child. */
|
||||||
|
if (read(syncfd, &ret, sizeof(ret)) != sizeof(ret))
|
||||||
|
bail("failed to sync with child: read(error code)");
|
||||||
|
|
||||||
|
exit(ret);
|
||||||
|
case SYNC_CHILD_READY:
|
||||||
|
ready = true;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
bail("unexpected sync value: %u", s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -615,7 +694,8 @@ void nsexec(void)
|
||||||
enum sync_t s;
|
enum sync_t s;
|
||||||
|
|
||||||
/* We're in a child and thus need to tell the parent if we die. */
|
/* We're in a child and thus need to tell the parent if we die. */
|
||||||
syncfd = syncpipe[0];
|
syncfd = sync_child_pipe[0];
|
||||||
|
close(sync_child_pipe[1]);
|
||||||
|
|
||||||
/* For debugging. */
|
/* For debugging. */
|
||||||
prctl(PR_SET_NAME, (unsigned long) "runc:[1:CHILD]", 0, 0, 0);
|
prctl(PR_SET_NAME, (unsigned long) "runc:[1:CHILD]", 0, 0, 0);
|
||||||
|
@ -653,6 +733,11 @@ void nsexec(void)
|
||||||
* clone_parent rant). So signal our parent to hook us up.
|
* clone_parent rant). So signal our parent to hook us up.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* Switching is only necessary if we joined namespaces. */
|
||||||
|
if (config.namespaces) {
|
||||||
|
if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0)
|
||||||
|
bail("failed to set process as dumpable");
|
||||||
|
}
|
||||||
s = SYNC_USERMAP_PLS;
|
s = SYNC_USERMAP_PLS;
|
||||||
if (write(syncfd, &s, sizeof(s)) != sizeof(s))
|
if (write(syncfd, &s, sizeof(s)) != sizeof(s))
|
||||||
bail("failed to sync with parent: write(SYNC_USERMAP_PLS)");
|
bail("failed to sync with parent: write(SYNC_USERMAP_PLS)");
|
||||||
|
@ -663,6 +748,11 @@ void nsexec(void)
|
||||||
bail("failed to sync with parent: read(SYNC_USERMAP_ACK)");
|
bail("failed to sync with parent: read(SYNC_USERMAP_ACK)");
|
||||||
if (s != SYNC_USERMAP_ACK)
|
if (s != SYNC_USERMAP_ACK)
|
||||||
bail("failed to sync with parent: SYNC_USERMAP_ACK: got %u", s);
|
bail("failed to sync with parent: SYNC_USERMAP_ACK: got %u", s);
|
||||||
|
/* Switching is only necessary if we joined namespaces. */
|
||||||
|
if (config.namespaces) {
|
||||||
|
if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) < 0)
|
||||||
|
bail("failed to set process as dumpable");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -700,6 +790,12 @@ void nsexec(void)
|
||||||
bail("failed to sync with parent: SYNC_RECVPID_ACK: got %u", s);
|
bail("failed to sync with parent: SYNC_RECVPID_ACK: got %u", s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s = SYNC_CHILD_READY;
|
||||||
|
if (write(syncfd, &s, sizeof(s)) != sizeof(s)) {
|
||||||
|
kill(child, SIGKILL);
|
||||||
|
bail("failed to sync with parent: write(SYNC_CHILD_READY)");
|
||||||
|
}
|
||||||
|
|
||||||
/* Our work is done. [Stage 2: JUMP_INIT] is doing the rest of the work. */
|
/* Our work is done. [Stage 2: JUMP_INIT] is doing the rest of the work. */
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
@ -718,11 +814,19 @@ void nsexec(void)
|
||||||
enum sync_t s;
|
enum sync_t s;
|
||||||
|
|
||||||
/* We're in a child and thus need to tell the parent if we die. */
|
/* We're in a child and thus need to tell the parent if we die. */
|
||||||
syncfd = syncpipe[0];
|
syncfd = sync_grandchild_pipe[0];
|
||||||
|
close(sync_grandchild_pipe[1]);
|
||||||
|
close(sync_child_pipe[0]);
|
||||||
|
close(sync_child_pipe[1]);
|
||||||
|
|
||||||
/* For debugging. */
|
/* For debugging. */
|
||||||
prctl(PR_SET_NAME, (unsigned long) "runc:[2:INIT]", 0, 0, 0);
|
prctl(PR_SET_NAME, (unsigned long) "runc:[2:INIT]", 0, 0, 0);
|
||||||
|
|
||||||
|
if (read(syncfd, &s, sizeof(s)) != sizeof(s))
|
||||||
|
bail("failed to sync with parent: read(SYNC_GRANDCHILD)");
|
||||||
|
if (s != SYNC_GRANDCHILD)
|
||||||
|
bail("failed to sync with parent: SYNC_GRANDCHILD: got %u", s);
|
||||||
|
|
||||||
if (setsid() < 0)
|
if (setsid() < 0)
|
||||||
bail("setsid failed");
|
bail("setsid failed");
|
||||||
|
|
||||||
|
@ -732,16 +836,17 @@ void nsexec(void)
|
||||||
if (setgid(0) < 0)
|
if (setgid(0) < 0)
|
||||||
bail("setgid failed");
|
bail("setgid failed");
|
||||||
|
|
||||||
|
if (!config.is_rootless && config.is_setgroup) {
|
||||||
if (setgroups(0, NULL) < 0)
|
if (setgroups(0, NULL) < 0)
|
||||||
bail("setgroups failed");
|
bail("setgroups failed");
|
||||||
|
}
|
||||||
|
|
||||||
s = SYNC_CHILD_READY;
|
s = SYNC_CHILD_READY;
|
||||||
if (write(syncfd, &s, sizeof(s)) != sizeof(s))
|
if (write(syncfd, &s, sizeof(s)) != sizeof(s))
|
||||||
bail("failed to sync with patent: write(SYNC_CHILD_READY)");
|
bail("failed to sync with patent: write(SYNC_CHILD_READY)");
|
||||||
|
|
||||||
/* Close sync pipes. */
|
/* Close sync pipes. */
|
||||||
close(syncpipe[0]);
|
close(sync_grandchild_pipe[0]);
|
||||||
close(syncpipe[1]);
|
|
||||||
|
|
||||||
/* Free netlink data. */
|
/* Free netlink data. */
|
||||||
nl_free(&config);
|
nl_free(&config);
|
||||||
|
@ -751,7 +856,6 @@ void nsexec(void)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
bail("unexpected jump value");
|
bail("unexpected jump value");
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Should never be reached. */
|
/* Should never be reached. */
|
||||||
|
|
29
vendor/github.com/opencontainers/runc/libcontainer/system/linux.go
generated
vendored
29
vendor/github.com/opencontainers/runc/libcontainer/system/linux.go
generated
vendored
|
@ -7,8 +7,10 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"syscall"
|
"syscall" // only for exec
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
// If arg2 is nonzero, set the "child subreaper" attribute of the
|
// If arg2 is nonzero, set the "child subreaper" attribute of the
|
||||||
|
@ -53,8 +55,8 @@ func Execv(cmd string, args []string, env []string) error {
|
||||||
return syscall.Exec(name, args, env)
|
return syscall.Exec(name, args, env)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Prlimit(pid, resource int, limit syscall.Rlimit) error {
|
func Prlimit(pid, resource int, limit unix.Rlimit) error {
|
||||||
_, _, err := syscall.RawSyscall6(syscall.SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(&limit)), uintptr(unsafe.Pointer(&limit)), 0, 0)
|
_, _, err := unix.RawSyscall6(unix.SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(&limit)), uintptr(unsafe.Pointer(&limit)), 0, 0)
|
||||||
if err != 0 {
|
if err != 0 {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -62,7 +64,7 @@ func Prlimit(pid, resource int, limit syscall.Rlimit) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetParentDeathSignal(sig uintptr) error {
|
func SetParentDeathSignal(sig uintptr) error {
|
||||||
if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_PDEATHSIG, sig, 0); err != 0 {
|
if err := unix.Prctl(unix.PR_SET_PDEATHSIG, sig, 0, 0, 0); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -70,15 +72,14 @@ func SetParentDeathSignal(sig uintptr) error {
|
||||||
|
|
||||||
func GetParentDeathSignal() (ParentDeathSignal, error) {
|
func GetParentDeathSignal() (ParentDeathSignal, error) {
|
||||||
var sig int
|
var sig int
|
||||||
_, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_GET_PDEATHSIG, uintptr(unsafe.Pointer(&sig)), 0)
|
if err := unix.Prctl(unix.PR_GET_PDEATHSIG, uintptr(unsafe.Pointer(&sig)), 0, 0, 0); err != nil {
|
||||||
if err != 0 {
|
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
return ParentDeathSignal(sig), nil
|
return ParentDeathSignal(sig), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetKeepCaps() error {
|
func SetKeepCaps() error {
|
||||||
if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_KEEPCAPS, 1, 0); err != 0 {
|
if err := unix.Prctl(unix.PR_SET_KEEPCAPS, 1, 0, 0, 0); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +87,7 @@ func SetKeepCaps() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func ClearKeepCaps() error {
|
func ClearKeepCaps() error {
|
||||||
if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_KEEPCAPS, 0, 0); err != 0 {
|
if err := unix.Prctl(unix.PR_SET_KEEPCAPS, 0, 0, 0, 0); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +95,7 @@ func ClearKeepCaps() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func Setctty() error {
|
func Setctty() error {
|
||||||
if _, _, err := syscall.RawSyscall(syscall.SYS_IOCTL, 0, uintptr(syscall.TIOCSCTTY), 0); err != 0 {
|
if err := unix.IoctlSetInt(0, unix.TIOCSCTTY, 0); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -131,13 +132,5 @@ func RunningInUserNS() bool {
|
||||||
|
|
||||||
// SetSubreaper sets the value i as the subreaper setting for the calling process
|
// SetSubreaper sets the value i as the subreaper setting for the calling process
|
||||||
func SetSubreaper(i int) error {
|
func SetSubreaper(i int) error {
|
||||||
return Prctl(PR_SET_CHILD_SUBREAPER, uintptr(i), 0, 0, 0)
|
return unix.Prctl(PR_SET_CHILD_SUBREAPER, uintptr(i), 0, 0, 0)
|
||||||
}
|
|
||||||
|
|
||||||
func Prctl(option int, arg2, arg3, arg4, arg5 uintptr) (err error) {
|
|
||||||
_, _, e1 := syscall.Syscall6(syscall.SYS_PRCTL, uintptr(option), arg2, arg3, arg4, arg5, 0)
|
|
||||||
if e1 != 0 {
|
|
||||||
err = e1
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
122
vendor/github.com/opencontainers/runc/libcontainer/system/proc.go
generated
vendored
122
vendor/github.com/opencontainers/runc/libcontainer/system/proc.go
generated
vendored
|
@ -1,43 +1,113 @@
|
||||||
package system
|
package system
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// look in /proc to find the process start time so that we can verify
|
// State is the status of a process.
|
||||||
// that this pid has started after ourself
|
type State rune
|
||||||
|
|
||||||
|
const ( // Only values for Linux 3.14 and later are listed here
|
||||||
|
Dead State = 'X'
|
||||||
|
DiskSleep State = 'D'
|
||||||
|
Running State = 'R'
|
||||||
|
Sleeping State = 'S'
|
||||||
|
Stopped State = 'T'
|
||||||
|
TracingStop State = 't'
|
||||||
|
Zombie State = 'Z'
|
||||||
|
)
|
||||||
|
|
||||||
|
// String forms of the state from proc(5)'s documentation for
|
||||||
|
// /proc/[pid]/status' "State" field.
|
||||||
|
func (s State) String() string {
|
||||||
|
switch s {
|
||||||
|
case Dead:
|
||||||
|
return "dead"
|
||||||
|
case DiskSleep:
|
||||||
|
return "disk sleep"
|
||||||
|
case Running:
|
||||||
|
return "running"
|
||||||
|
case Sleeping:
|
||||||
|
return "sleeping"
|
||||||
|
case Stopped:
|
||||||
|
return "stopped"
|
||||||
|
case TracingStop:
|
||||||
|
return "tracing stop"
|
||||||
|
case Zombie:
|
||||||
|
return "zombie"
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("unknown (%c)", s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stat_t represents the information from /proc/[pid]/stat, as
|
||||||
|
// described in proc(5) with names based on the /proc/[pid]/status
|
||||||
|
// fields.
|
||||||
|
type Stat_t struct {
|
||||||
|
// PID is the process ID.
|
||||||
|
PID uint
|
||||||
|
|
||||||
|
// Name is the command run by the process.
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// State is the state of the process.
|
||||||
|
State State
|
||||||
|
|
||||||
|
// StartTime is the number of clock ticks after system boot (since
|
||||||
|
// Linux 2.6).
|
||||||
|
StartTime uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stat returns a Stat_t instance for the specified process.
|
||||||
|
func Stat(pid int) (stat Stat_t, err error) {
|
||||||
|
bytes, err := ioutil.ReadFile(filepath.Join("/proc", strconv.Itoa(pid), "stat"))
|
||||||
|
if err != nil {
|
||||||
|
return stat, err
|
||||||
|
}
|
||||||
|
return parseStat(string(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetProcessStartTime is deprecated. Use Stat(pid) and
|
||||||
|
// Stat_t.StartTime instead.
|
||||||
func GetProcessStartTime(pid int) (string, error) {
|
func GetProcessStartTime(pid int) (string, error) {
|
||||||
data, err := ioutil.ReadFile(filepath.Join("/proc", strconv.Itoa(pid), "stat"))
|
stat, err := Stat(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return parseStartTime(string(data))
|
return fmt.Sprintf("%d", stat.StartTime), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseStartTime(stat string) (string, error) {
|
func parseStat(data string) (stat Stat_t, err error) {
|
||||||
// the starttime is located at pos 22
|
// From proc(5), field 2 could contain space and is inside `(` and `)`.
|
||||||
// from the man page
|
// The following is an example:
|
||||||
//
|
|
||||||
// starttime %llu (was %lu before Linux 2.6)
|
|
||||||
// (22) The time the process started after system boot. In kernels before Linux 2.6, this
|
|
||||||
// value was expressed in jiffies. Since Linux 2.6, the value is expressed in clock ticks
|
|
||||||
// (divide by sysconf(_SC_CLK_TCK)).
|
|
||||||
//
|
|
||||||
// NOTE:
|
|
||||||
// pos 2 could contain space and is inside `(` and `)`:
|
|
||||||
// (2) comm %s
|
|
||||||
// The filename of the executable, in parentheses.
|
|
||||||
// This is visible whether or not the executable is
|
|
||||||
// swapped out.
|
|
||||||
//
|
|
||||||
// the following is an example:
|
|
||||||
// 89653 (gunicorn: maste) S 89630 89653 89653 0 -1 4194560 29689 28896 0 3 146 32 76 19 20 0 1 0 2971844 52965376 3920 18446744073709551615 1 1 0 0 0 0 0 16781312 137447943 0 0 0 17 1 0 0 0 0 0 0 0 0 0 0 0 0 0
|
// 89653 (gunicorn: maste) S 89630 89653 89653 0 -1 4194560 29689 28896 0 3 146 32 76 19 20 0 1 0 2971844 52965376 3920 18446744073709551615 1 1 0 0 0 0 0 16781312 137447943 0 0 0 17 1 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||||
|
i := strings.LastIndex(data, ")")
|
||||||
// get parts after last `)`:
|
if i <= 2 || i >= len(data)-1 {
|
||||||
s := strings.Split(stat, ")")
|
return stat, fmt.Errorf("invalid stat data: %q", data)
|
||||||
parts := strings.Split(strings.TrimSpace(s[len(s)-1]), " ")
|
}
|
||||||
return parts[22-3], nil // starts at 3 (after the filename pos `2`)
|
|
||||||
|
parts := strings.SplitN(data[:i], "(", 2)
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return stat, fmt.Errorf("invalid stat data: %q", data)
|
||||||
|
}
|
||||||
|
|
||||||
|
stat.Name = parts[1]
|
||||||
|
_, err = fmt.Sscanf(parts[0], "%d", &stat.PID)
|
||||||
|
if err != nil {
|
||||||
|
return stat, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// parts indexes should be offset by 3 from the field number given
|
||||||
|
// proc(5), because parts is zero-indexed and we've removed fields
|
||||||
|
// one (PID) and two (Name) in the paren-split.
|
||||||
|
parts = strings.Split(data[i+2:], " ")
|
||||||
|
var state int
|
||||||
|
fmt.Sscanf(parts[3-3], "%c", &state)
|
||||||
|
stat.State = State(state)
|
||||||
|
fmt.Sscanf(parts[22-3], "%d", &stat.StartTime)
|
||||||
|
return stat, nil
|
||||||
}
|
}
|
||||||
|
|
40
vendor/github.com/opencontainers/runc/libcontainer/system/setns_linux.go
generated
vendored
40
vendor/github.com/opencontainers/runc/libcontainer/system/setns_linux.go
generated
vendored
|
@ -1,40 +0,0 @@
|
||||||
package system
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"runtime"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Via http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=7b21fddd087678a70ad64afc0f632e0f1071b092
|
|
||||||
//
|
|
||||||
// We need different setns values for the different platforms and arch
|
|
||||||
// We are declaring the macro here because the SETNS syscall does not exist in th stdlib
|
|
||||||
var setNsMap = map[string]uintptr{
|
|
||||||
"linux/386": 346,
|
|
||||||
"linux/arm64": 268,
|
|
||||||
"linux/amd64": 308,
|
|
||||||
"linux/arm": 375,
|
|
||||||
"linux/ppc": 350,
|
|
||||||
"linux/ppc64": 350,
|
|
||||||
"linux/ppc64le": 350,
|
|
||||||
"linux/s390x": 339,
|
|
||||||
}
|
|
||||||
|
|
||||||
var sysSetns = setNsMap[fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)]
|
|
||||||
|
|
||||||
func SysSetns() uint32 {
|
|
||||||
return uint32(sysSetns)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Setns(fd uintptr, flags uintptr) error {
|
|
||||||
ns, exists := setNsMap[fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)]
|
|
||||||
if !exists {
|
|
||||||
return fmt.Errorf("unsupported platform %s/%s", runtime.GOOS, runtime.GOARCH)
|
|
||||||
}
|
|
||||||
_, _, err := syscall.RawSyscall(ns, fd, flags, 0)
|
|
||||||
if err != 0 {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
6
vendor/github.com/opencontainers/runc/libcontainer/system/syscall_linux_386.go
generated
vendored
6
vendor/github.com/opencontainers/runc/libcontainer/system/syscall_linux_386.go
generated
vendored
|
@ -3,12 +3,12 @@
|
||||||
package system
|
package system
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"syscall"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Setuid sets the uid of the calling thread to the specified uid.
|
// Setuid sets the uid of the calling thread to the specified uid.
|
||||||
func Setuid(uid int) (err error) {
|
func Setuid(uid int) (err error) {
|
||||||
_, _, e1 := syscall.RawSyscall(syscall.SYS_SETUID32, uintptr(uid), 0, 0)
|
_, _, e1 := unix.RawSyscall(unix.SYS_SETUID32, uintptr(uid), 0, 0)
|
||||||
if e1 != 0 {
|
if e1 != 0 {
|
||||||
err = e1
|
err = e1
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ func Setuid(uid int) (err error) {
|
||||||
|
|
||||||
// Setgid sets the gid of the calling thread to the specified gid.
|
// Setgid sets the gid of the calling thread to the specified gid.
|
||||||
func Setgid(gid int) (err error) {
|
func Setgid(gid int) (err error) {
|
||||||
_, _, e1 := syscall.RawSyscall(syscall.SYS_SETGID32, uintptr(gid), 0, 0)
|
_, _, e1 := unix.RawSyscall(unix.SYS_SETGID32, uintptr(gid), 0, 0)
|
||||||
if e1 != 0 {
|
if e1 != 0 {
|
||||||
err = e1
|
err = e1
|
||||||
}
|
}
|
||||||
|
|
6
vendor/github.com/opencontainers/runc/libcontainer/system/syscall_linux_64.go
generated
vendored
6
vendor/github.com/opencontainers/runc/libcontainer/system/syscall_linux_64.go
generated
vendored
|
@ -3,12 +3,12 @@
|
||||||
package system
|
package system
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"syscall"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Setuid sets the uid of the calling thread to the specified uid.
|
// Setuid sets the uid of the calling thread to the specified uid.
|
||||||
func Setuid(uid int) (err error) {
|
func Setuid(uid int) (err error) {
|
||||||
_, _, e1 := syscall.RawSyscall(syscall.SYS_SETUID, uintptr(uid), 0, 0)
|
_, _, e1 := unix.RawSyscall(unix.SYS_SETUID, uintptr(uid), 0, 0)
|
||||||
if e1 != 0 {
|
if e1 != 0 {
|
||||||
err = e1
|
err = e1
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ func Setuid(uid int) (err error) {
|
||||||
|
|
||||||
// Setgid sets the gid of the calling thread to the specified gid.
|
// Setgid sets the gid of the calling thread to the specified gid.
|
||||||
func Setgid(gid int) (err error) {
|
func Setgid(gid int) (err error) {
|
||||||
_, _, e1 := syscall.RawSyscall(syscall.SYS_SETGID, uintptr(gid), 0, 0)
|
_, _, e1 := unix.RawSyscall(unix.SYS_SETGID, uintptr(gid), 0, 0)
|
||||||
if e1 != 0 {
|
if e1 != 0 {
|
||||||
err = e1
|
err = e1
|
||||||
}
|
}
|
||||||
|
|
6
vendor/github.com/opencontainers/runc/libcontainer/system/syscall_linux_arm.go
generated
vendored
6
vendor/github.com/opencontainers/runc/libcontainer/system/syscall_linux_arm.go
generated
vendored
|
@ -3,12 +3,12 @@
|
||||||
package system
|
package system
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"syscall"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Setuid sets the uid of the calling thread to the specified uid.
|
// Setuid sets the uid of the calling thread to the specified uid.
|
||||||
func Setuid(uid int) (err error) {
|
func Setuid(uid int) (err error) {
|
||||||
_, _, e1 := syscall.RawSyscall(syscall.SYS_SETUID32, uintptr(uid), 0, 0)
|
_, _, e1 := unix.RawSyscall(unix.SYS_SETUID32, uintptr(uid), 0, 0)
|
||||||
if e1 != 0 {
|
if e1 != 0 {
|
||||||
err = e1
|
err = e1
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ func Setuid(uid int) (err error) {
|
||||||
|
|
||||||
// Setgid sets the gid of the calling thread to the specified gid.
|
// Setgid sets the gid of the calling thread to the specified gid.
|
||||||
func Setgid(gid int) (err error) {
|
func Setgid(gid int) (err error) {
|
||||||
_, _, e1 := syscall.RawSyscall(syscall.SYS_SETGID32, uintptr(gid), 0, 0)
|
_, _, e1 := unix.RawSyscall(unix.SYS_SETGID32, uintptr(gid), 0, 0)
|
||||||
if e1 != 0 {
|
if e1 != 0 {
|
||||||
err = e1
|
err = e1
|
||||||
}
|
}
|
||||||
|
|
88
vendor/github.com/opencontainers/runc/libcontainer/system/xattrs_linux.go
generated
vendored
88
vendor/github.com/opencontainers/runc/libcontainer/system/xattrs_linux.go
generated
vendored
|
@ -1,99 +1,35 @@
|
||||||
package system
|
package system
|
||||||
|
|
||||||
import (
|
import "golang.org/x/sys/unix"
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _zero uintptr
|
|
||||||
|
|
||||||
// Returns the size of xattrs and nil error
|
|
||||||
// Requires path, takes allocated []byte or nil as last argument
|
|
||||||
func Llistxattr(path string, dest []byte) (size int, err error) {
|
|
||||||
pathBytes, err := syscall.BytePtrFromString(path)
|
|
||||||
if err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
var newpathBytes unsafe.Pointer
|
|
||||||
if len(dest) > 0 {
|
|
||||||
newpathBytes = unsafe.Pointer(&dest[0])
|
|
||||||
} else {
|
|
||||||
newpathBytes = unsafe.Pointer(&_zero)
|
|
||||||
}
|
|
||||||
|
|
||||||
_size, _, errno := syscall.Syscall6(syscall.SYS_LLISTXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(newpathBytes), uintptr(len(dest)), 0, 0, 0)
|
|
||||||
size = int(_size)
|
|
||||||
if errno != 0 {
|
|
||||||
return -1, errno
|
|
||||||
}
|
|
||||||
|
|
||||||
return size, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns a []byte slice if the xattr is set and nil otherwise
|
// Returns a []byte slice if the xattr is set and nil otherwise
|
||||||
// Requires path and its attribute as arguments
|
// Requires path and its attribute as arguments
|
||||||
func Lgetxattr(path string, attr string) ([]byte, error) {
|
func Lgetxattr(path string, attr string) ([]byte, error) {
|
||||||
var sz int
|
var sz int
|
||||||
pathBytes, err := syscall.BytePtrFromString(path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
attrBytes, err := syscall.BytePtrFromString(attr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start with a 128 length byte array
|
// Start with a 128 length byte array
|
||||||
sz = 128
|
dest := make([]byte, 128)
|
||||||
dest := make([]byte, sz)
|
sz, errno := unix.Lgetxattr(path, attr, dest)
|
||||||
destBytes := unsafe.Pointer(&dest[0])
|
|
||||||
_sz, _, errno := syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(destBytes), uintptr(len(dest)), 0, 0)
|
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case errno == syscall.ENODATA:
|
case errno == unix.ENODATA:
|
||||||
return nil, errno
|
return nil, errno
|
||||||
case errno == syscall.ENOTSUP:
|
case errno == unix.ENOTSUP:
|
||||||
return nil, errno
|
return nil, errno
|
||||||
case errno == syscall.ERANGE:
|
case errno == unix.ERANGE:
|
||||||
// 128 byte array might just not be good enough,
|
// 128 byte array might just not be good enough,
|
||||||
// A dummy buffer is used ``uintptr(0)`` to get real size
|
// A dummy buffer is used to get the real size
|
||||||
// of the xattrs on disk
|
// of the xattrs on disk
|
||||||
_sz, _, errno = syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(unsafe.Pointer(nil)), uintptr(0), 0, 0)
|
sz, errno = unix.Lgetxattr(path, attr, []byte{})
|
||||||
sz = int(_sz)
|
if errno != nil {
|
||||||
if sz < 0 {
|
|
||||||
return nil, errno
|
return nil, errno
|
||||||
}
|
}
|
||||||
dest = make([]byte, sz)
|
dest = make([]byte, sz)
|
||||||
destBytes := unsafe.Pointer(&dest[0])
|
sz, errno = unix.Lgetxattr(path, attr, dest)
|
||||||
_sz, _, errno = syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(destBytes), uintptr(len(dest)), 0, 0)
|
if errno != nil {
|
||||||
if errno != 0 {
|
|
||||||
return nil, errno
|
return nil, errno
|
||||||
}
|
}
|
||||||
case errno != 0:
|
case errno != nil:
|
||||||
return nil, errno
|
return nil, errno
|
||||||
}
|
}
|
||||||
sz = int(_sz)
|
|
||||||
return dest[:sz], nil
|
return dest[:sz], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Lsetxattr(path string, attr string, data []byte, flags int) error {
|
|
||||||
pathBytes, err := syscall.BytePtrFromString(path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
attrBytes, err := syscall.BytePtrFromString(attr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var dataBytes unsafe.Pointer
|
|
||||||
if len(data) > 0 {
|
|
||||||
dataBytes = unsafe.Pointer(&data[0])
|
|
||||||
} else {
|
|
||||||
dataBytes = unsafe.Pointer(&_zero)
|
|
||||||
}
|
|
||||||
_, _, errno := syscall.Syscall6(syscall.SYS_LSETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(dataBytes), uintptr(len(data)), uintptr(flags), 0)
|
|
||||||
if errno != 0 {
|
|
||||||
return errno
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
7
vendor/github.com/opencontainers/runc/libcontainer/user/lookup.go
generated
vendored
7
vendor/github.com/opencontainers/runc/libcontainer/user/lookup.go
generated
vendored
|
@ -2,7 +2,8 @@ package user
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"syscall"
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -40,7 +41,7 @@ func lookupUser(filter func(u User) bool) (User, error) {
|
||||||
// user cannot be found (or there is no /etc/passwd file on the filesystem),
|
// user cannot be found (or there is no /etc/passwd file on the filesystem),
|
||||||
// then CurrentUser returns an error.
|
// then CurrentUser returns an error.
|
||||||
func CurrentUser() (User, error) {
|
func CurrentUser() (User, error) {
|
||||||
return LookupUid(syscall.Getuid())
|
return LookupUid(unix.Getuid())
|
||||||
}
|
}
|
||||||
|
|
||||||
// LookupUser looks up a user by their username in /etc/passwd. If the user
|
// LookupUser looks up a user by their username in /etc/passwd. If the user
|
||||||
|
@ -88,7 +89,7 @@ func lookupGroup(filter func(g Group) bool) (Group, error) {
|
||||||
// entry in /etc/passwd. If the group cannot be found (or there is no
|
// entry in /etc/passwd. If the group cannot be found (or there is no
|
||||||
// /etc/group file on the filesystem), then CurrentGroup returns an error.
|
// /etc/group file on the filesystem), then CurrentGroup returns an error.
|
||||||
func CurrentGroup() (Group, error) {
|
func CurrentGroup() (Group, error) {
|
||||||
return LookupGid(syscall.Getgid())
|
return LookupGid(unix.Getgid())
|
||||||
}
|
}
|
||||||
|
|
||||||
// LookupGroup looks up a group by its name in /etc/group. If the group cannot
|
// LookupGroup looks up a group by its name in /etc/group. If the group cannot
|
||||||
|
|
30
vendor/github.com/opencontainers/runc/libcontainer/user/user.go
generated
vendored
30
vendor/github.com/opencontainers/runc/libcontainer/user/user.go
generated
vendored
|
@ -199,18 +199,16 @@ type ExecUser struct {
|
||||||
// files cannot be opened for any reason, the error is ignored and a nil
|
// files cannot be opened for any reason, the error is ignored and a nil
|
||||||
// io.Reader is passed instead.
|
// io.Reader is passed instead.
|
||||||
func GetExecUserPath(userSpec string, defaults *ExecUser, passwdPath, groupPath string) (*ExecUser, error) {
|
func GetExecUserPath(userSpec string, defaults *ExecUser, passwdPath, groupPath string) (*ExecUser, error) {
|
||||||
passwd, err := os.Open(passwdPath)
|
var passwd, group io.Reader
|
||||||
if err != nil {
|
|
||||||
passwd = nil
|
if passwdFile, err := os.Open(passwdPath); err == nil {
|
||||||
} else {
|
passwd = passwdFile
|
||||||
defer passwd.Close()
|
defer passwdFile.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
group, err := os.Open(groupPath)
|
if groupFile, err := os.Open(groupPath); err == nil {
|
||||||
if err != nil {
|
group = groupFile
|
||||||
group = nil
|
defer groupFile.Close()
|
||||||
} else {
|
|
||||||
defer group.Close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetExecUser(userSpec, defaults, passwd, group)
|
return GetExecUser(userSpec, defaults, passwd, group)
|
||||||
|
@ -360,8 +358,8 @@ func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) (
|
||||||
|
|
||||||
// Okay, so it's numeric. We can just roll with this.
|
// Okay, so it's numeric. We can just roll with this.
|
||||||
}
|
}
|
||||||
} else if len(groups) > 0 {
|
} else if len(groups) > 0 && uidErr != nil {
|
||||||
// Supplementary group ids only make sense if in the implicit form.
|
// Supplementary group ids only make sense if in the implicit form for non-numeric users.
|
||||||
user.Sgids = make([]int, len(groups))
|
user.Sgids = make([]int, len(groups))
|
||||||
for i, group := range groups {
|
for i, group := range groups {
|
||||||
user.Sgids[i] = group.Gid
|
user.Sgids[i] = group.Gid
|
||||||
|
@ -433,9 +431,11 @@ func GetAdditionalGroups(additionalGroups []string, group io.Reader) ([]int, err
|
||||||
// that opens the groupPath given and gives it as an argument to
|
// that opens the groupPath given and gives it as an argument to
|
||||||
// GetAdditionalGroups.
|
// GetAdditionalGroups.
|
||||||
func GetAdditionalGroupsPath(additionalGroups []string, groupPath string) ([]int, error) {
|
func GetAdditionalGroupsPath(additionalGroups []string, groupPath string) ([]int, error) {
|
||||||
group, err := os.Open(groupPath)
|
var group io.Reader
|
||||||
if err == nil {
|
|
||||||
defer group.Close()
|
if groupFile, err := os.Open(groupPath); err == nil {
|
||||||
|
group = groupFile
|
||||||
|
defer groupFile.Close()
|
||||||
}
|
}
|
||||||
return GetAdditionalGroups(additionalGroups, group)
|
return GetAdditionalGroups(additionalGroups, group)
|
||||||
}
|
}
|
||||||
|
|
148
vendor/github.com/opencontainers/runc/libcontainer/utils/cmsg.c
generated
vendored
148
vendor/github.com/opencontainers/runc/libcontainer/utils/cmsg.c
generated
vendored
|
@ -1,148 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 SUSE LLC
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "cmsg.h"
|
|
||||||
|
|
||||||
#define error(fmt, ...) \
|
|
||||||
({ \
|
|
||||||
fprintf(stderr, "nsenter: " fmt ": %m\n", ##__VA_ARGS__); \
|
|
||||||
errno = ECOMM; \
|
|
||||||
goto err; /* return value */ \
|
|
||||||
})
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Sends a file descriptor along the sockfd provided. Returns the return
|
|
||||||
* value of sendmsg(2). Any synchronisation and preparation of state
|
|
||||||
* should be done external to this (we expect the other side to be in
|
|
||||||
* recvfd() in the code).
|
|
||||||
*/
|
|
||||||
ssize_t sendfd(int sockfd, struct file_t file)
|
|
||||||
{
|
|
||||||
struct msghdr msg = {0};
|
|
||||||
struct iovec iov[1] = {0};
|
|
||||||
struct cmsghdr *cmsg;
|
|
||||||
int *fdptr;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
union {
|
|
||||||
char buf[CMSG_SPACE(sizeof(file.fd))];
|
|
||||||
struct cmsghdr align;
|
|
||||||
} u;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We need to send some other data along with the ancillary data,
|
|
||||||
* otherwise the other side won't recieve any data. This is very
|
|
||||||
* well-hidden in the documentation (and only applies to
|
|
||||||
* SOCK_STREAM). See the bottom part of unix(7).
|
|
||||||
*/
|
|
||||||
iov[0].iov_base = file.name;
|
|
||||||
iov[0].iov_len = strlen(file.name) + 1;
|
|
||||||
|
|
||||||
msg.msg_name = NULL;
|
|
||||||
msg.msg_namelen = 0;
|
|
||||||
msg.msg_iov = iov;
|
|
||||||
msg.msg_iovlen = 1;
|
|
||||||
msg.msg_control = u.buf;
|
|
||||||
msg.msg_controllen = sizeof(u.buf);
|
|
||||||
|
|
||||||
cmsg = CMSG_FIRSTHDR(&msg);
|
|
||||||
cmsg->cmsg_level = SOL_SOCKET;
|
|
||||||
cmsg->cmsg_type = SCM_RIGHTS;
|
|
||||||
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
|
|
||||||
|
|
||||||
fdptr = (int *) CMSG_DATA(cmsg);
|
|
||||||
memcpy(fdptr, &file.fd, sizeof(int));
|
|
||||||
|
|
||||||
return sendmsg(sockfd, &msg, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Receives a file descriptor from the sockfd provided. Returns the file
|
|
||||||
* descriptor as sent from sendfd(). It will return the file descriptor
|
|
||||||
* or die (literally) trying. Any synchronisation and preparation of
|
|
||||||
* state should be done external to this (we expect the other side to be
|
|
||||||
* in sendfd() in the code).
|
|
||||||
*/
|
|
||||||
struct file_t recvfd(int sockfd)
|
|
||||||
{
|
|
||||||
struct msghdr msg = {0};
|
|
||||||
struct iovec iov[1] = {0};
|
|
||||||
struct cmsghdr *cmsg;
|
|
||||||
struct file_t file = {0};
|
|
||||||
int *fdptr;
|
|
||||||
int olderrno;
|
|
||||||
|
|
||||||
union {
|
|
||||||
char buf[CMSG_SPACE(sizeof(file.fd))];
|
|
||||||
struct cmsghdr align;
|
|
||||||
} u;
|
|
||||||
|
|
||||||
/* Allocate a buffer. */
|
|
||||||
/* TODO: Make this dynamic with MSG_PEEK. */
|
|
||||||
file.name = malloc(TAG_BUFFER);
|
|
||||||
if (!file.name)
|
|
||||||
error("recvfd: failed to allocate file.tag buffer\n");
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We need to "recieve" the non-ancillary data even though we don't
|
|
||||||
* plan to use it at all. Otherwise, things won't work as expected.
|
|
||||||
* See unix(7) and other well-hidden documentation.
|
|
||||||
*/
|
|
||||||
iov[0].iov_base = file.name;
|
|
||||||
iov[0].iov_len = TAG_BUFFER;
|
|
||||||
|
|
||||||
msg.msg_name = NULL;
|
|
||||||
msg.msg_namelen = 0;
|
|
||||||
msg.msg_iov = iov;
|
|
||||||
msg.msg_iovlen = 1;
|
|
||||||
msg.msg_control = u.buf;
|
|
||||||
msg.msg_controllen = sizeof(u.buf);
|
|
||||||
|
|
||||||
ssize_t ret = recvmsg(sockfd, &msg, 0);
|
|
||||||
if (ret < 0)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
cmsg = CMSG_FIRSTHDR(&msg);
|
|
||||||
if (!cmsg)
|
|
||||||
error("recvfd: got NULL from CMSG_FIRSTHDR");
|
|
||||||
if (cmsg->cmsg_level != SOL_SOCKET)
|
|
||||||
error("recvfd: expected SOL_SOCKET in cmsg: %d", cmsg->cmsg_level);
|
|
||||||
if (cmsg->cmsg_type != SCM_RIGHTS)
|
|
||||||
error("recvfd: expected SCM_RIGHTS in cmsg: %d", cmsg->cmsg_type);
|
|
||||||
if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)))
|
|
||||||
error("recvfd: expected correct CMSG_LEN in cmsg: %lu", (unsigned long)cmsg->cmsg_len);
|
|
||||||
|
|
||||||
fdptr = (int *) CMSG_DATA(cmsg);
|
|
||||||
if (!fdptr || *fdptr < 0)
|
|
||||||
error("recvfd: recieved invalid pointer");
|
|
||||||
|
|
||||||
file.fd = *fdptr;
|
|
||||||
return file;
|
|
||||||
|
|
||||||
err:
|
|
||||||
olderrno = errno;
|
|
||||||
free(file.name);
|
|
||||||
errno = olderrno;
|
|
||||||
return (struct file_t){0};
|
|
||||||
}
|
|
76
vendor/github.com/opencontainers/runc/libcontainer/utils/cmsg.go
generated
vendored
76
vendor/github.com/opencontainers/runc/libcontainer/utils/cmsg.go
generated
vendored
|
@ -3,7 +3,7 @@
|
||||||
package utils
|
package utils
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2016 SUSE LLC
|
* Copyright 2016, 2017 SUSE LLC
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -18,28 +18,66 @@ package utils
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include "cmsg.h"
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"unsafe"
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// MaxSendfdLen is the maximum length of the name of a file descriptor being
|
||||||
|
// sent using SendFd. The name of the file handle returned by RecvFd will never
|
||||||
|
// be larger than this value.
|
||||||
|
const MaxNameLen = 4096
|
||||||
|
|
||||||
|
// oobSpace is the size of the oob slice required to store a single FD. Note
|
||||||
|
// that unix.UnixRights appears to make the assumption that fd is always int32,
|
||||||
|
// so sizeof(fd) = 4.
|
||||||
|
var oobSpace = unix.CmsgSpace(4)
|
||||||
|
|
||||||
// RecvFd waits for a file descriptor to be sent over the given AF_UNIX
|
// RecvFd waits for a file descriptor to be sent over the given AF_UNIX
|
||||||
// socket. The file name of the remote file descriptor will be recreated
|
// socket. The file name of the remote file descriptor will be recreated
|
||||||
// locally (it is sent as non-auxiliary data in the same payload).
|
// locally (it is sent as non-auxiliary data in the same payload).
|
||||||
func RecvFd(socket *os.File) (*os.File, error) {
|
func RecvFd(socket *os.File) (*os.File, error) {
|
||||||
file, err := C.recvfd(C.int(socket.Fd()))
|
// For some reason, unix.Recvmsg uses the length rather than the capacity
|
||||||
|
// when passing the msg_controllen and other attributes to recvmsg. So we
|
||||||
|
// have to actually set the length.
|
||||||
|
name := make([]byte, MaxNameLen)
|
||||||
|
oob := make([]byte, oobSpace)
|
||||||
|
|
||||||
|
sockfd := socket.Fd()
|
||||||
|
n, oobn, _, _, err := unix.Recvmsg(int(sockfd), name, oob, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer C.free(unsafe.Pointer(file.name))
|
|
||||||
return os.NewFile(uintptr(file.fd), C.GoString(file.name)), nil
|
if n >= MaxNameLen || oobn != oobSpace {
|
||||||
|
return nil, fmt.Errorf("recvfd: incorrect number of bytes read (n=%d oobn=%d)", n, oobn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Truncate.
|
||||||
|
name = name[:n]
|
||||||
|
oob = oob[:oobn]
|
||||||
|
|
||||||
|
scms, err := unix.ParseSocketControlMessage(oob)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(scms) != 1 {
|
||||||
|
return nil, fmt.Errorf("recvfd: number of SCMs is not 1: %d", len(scms))
|
||||||
|
}
|
||||||
|
scm := scms[0]
|
||||||
|
|
||||||
|
fds, err := unix.ParseUnixRights(&scm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(fds) != 1 {
|
||||||
|
return nil, fmt.Errorf("recvfd: number of fds is not 1: %d", len(fds))
|
||||||
|
}
|
||||||
|
fd := uintptr(fds[0])
|
||||||
|
|
||||||
|
return os.NewFile(fd, string(name)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendFd sends a file descriptor over the given AF_UNIX socket. In
|
// SendFd sends a file descriptor over the given AF_UNIX socket. In
|
||||||
|
@ -47,11 +85,11 @@ func RecvFd(socket *os.File) (*os.File, error) {
|
||||||
// non-auxiliary data in the same payload (allowing to send contextual
|
// non-auxiliary data in the same payload (allowing to send contextual
|
||||||
// information for a file descriptor).
|
// information for a file descriptor).
|
||||||
func SendFd(socket, file *os.File) error {
|
func SendFd(socket, file *os.File) error {
|
||||||
var cfile C.struct_file_t
|
name := []byte(file.Name())
|
||||||
cfile.fd = C.int(file.Fd())
|
if len(name) >= MaxNameLen {
|
||||||
cfile.name = C.CString(file.Name())
|
return fmt.Errorf("sendfd: filename too long: %s", file.Name())
|
||||||
defer C.free(unsafe.Pointer(cfile.name))
|
}
|
||||||
|
oob := unix.UnixRights(int(file.Fd()))
|
||||||
_, err := C.sendfd(C.int(socket.Fd()), cfile)
|
|
||||||
return err
|
return unix.Sendmsg(int(socket.Fd()), name, oob, nil, 0)
|
||||||
}
|
}
|
||||||
|
|
36
vendor/github.com/opencontainers/runc/libcontainer/utils/cmsg.h
generated
vendored
36
vendor/github.com/opencontainers/runc/libcontainer/utils/cmsg.h
generated
vendored
|
@ -1,36 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 SUSE LLC
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#if !defined(CMSG_H)
|
|
||||||
#define CMSG_H
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
/* TODO: Implement this properly with MSG_PEEK. */
|
|
||||||
#define TAG_BUFFER 4096
|
|
||||||
|
|
||||||
/* This mirrors Go's (*os.File). */
|
|
||||||
struct file_t {
|
|
||||||
char *name;
|
|
||||||
int fd;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct file_t recvfd(int sockfd);
|
|
||||||
ssize_t sendfd(int sockfd, struct file_t file);
|
|
||||||
|
|
||||||
#endif /* !defined(CMSG_H) */
|
|
5
vendor/github.com/opencontainers/runc/libcontainer/utils/utils.go
generated
vendored
5
vendor/github.com/opencontainers/runc/libcontainer/utils/utils.go
generated
vendored
|
@ -8,8 +8,9 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -41,7 +42,7 @@ func ResolveRootfs(uncleanRootfs string) (string, error) {
|
||||||
|
|
||||||
// ExitStatus returns the correct exit status for a process based on if it
|
// ExitStatus returns the correct exit status for a process based on if it
|
||||||
// was signaled or exited cleanly
|
// was signaled or exited cleanly
|
||||||
func ExitStatus(status syscall.WaitStatus) int {
|
func ExitStatus(status unix.WaitStatus) int {
|
||||||
if status.Signaled() {
|
if status.Signaled() {
|
||||||
return exitSignalOffset + int(status.Signal())
|
return exitSignalOffset + int(status.Signal())
|
||||||
}
|
}
|
||||||
|
|
17
vendor/github.com/opencontainers/runc/libcontainer/utils/utils_unix.go
generated
vendored
17
vendor/github.com/opencontainers/runc/libcontainer/utils/utils_unix.go
generated
vendored
|
@ -4,8 +4,10 @@ package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"syscall"
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CloseExecFrom(minFd int) error {
|
func CloseExecFrom(minFd int) error {
|
||||||
|
@ -25,9 +27,18 @@ func CloseExecFrom(minFd int) error {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// intentionally ignore errors from syscall.CloseOnExec
|
// intentionally ignore errors from unix.CloseOnExec
|
||||||
syscall.CloseOnExec(fd)
|
unix.CloseOnExec(fd)
|
||||||
// the cases where this might fail are basically file descriptors that have already been closed (including and especially the one that was created when ioutil.ReadDir did the "opendir" syscall)
|
// the cases where this might fail are basically file descriptors that have already been closed (including and especially the one that was created when ioutil.ReadDir did the "opendir" syscall)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewSockPair returns a new unix socket pair
|
||||||
|
func NewSockPair(name string) (parent *os.File, child *os.File, err error) {
|
||||||
|
fds, err := unix.Socketpair(unix.AF_LOCAL, unix.SOCK_STREAM|unix.SOCK_CLOEXEC, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return os.NewFile(uintptr(fds[1]), name+"-p"), os.NewFile(uintptr(fds[0]), name+"-c"), nil
|
||||||
|
}
|
||||||
|
|
21
vendor/github.com/opencontainers/runc/vendor.conf
generated
vendored
Normal file
21
vendor/github.com/opencontainers/runc/vendor.conf
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# OCI runtime-spec. When updating this, make sure you use a version tag rather
|
||||||
|
# than a commit ID so it's much more obvious what version of the spec we are
|
||||||
|
# using.
|
||||||
|
github.com/opencontainers/runtime-spec v1.0.0
|
||||||
|
# Core libcontainer functionality.
|
||||||
|
github.com/mrunalp/fileutils ed869b029674c0e9ce4c0dfa781405c2d9946d08
|
||||||
|
github.com/opencontainers/selinux v1.0.0-rc1
|
||||||
|
github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0
|
||||||
|
github.com/Sirupsen/logrus 26709e2714106fb8ad40b773b711ebce25b78914
|
||||||
|
github.com/syndtr/gocapability db04d3cc01c8b54962a58ec7e491717d06cfcc16
|
||||||
|
github.com/vishvananda/netlink 1e2e08e8a2dcdacaae3f14ac44c5cfa31361f270
|
||||||
|
# systemd integration.
|
||||||
|
github.com/coreos/go-systemd v14
|
||||||
|
github.com/coreos/pkg v3
|
||||||
|
github.com/godbus/dbus v3
|
||||||
|
github.com/golang/protobuf 18c9bb3261723cd5401db4d0c9fbc5c3b6c70fe8
|
||||||
|
# Command-line interface.
|
||||||
|
github.com/docker/docker 0f5c9d301b9b1cca66b3ea0f9dec3b5317d3686d
|
||||||
|
github.com/docker/go-units v0.2.0
|
||||||
|
github.com/urfave/cli d53eb991652b1d438abdd34ce4bfa3ef1539108e
|
||||||
|
golang.org/x/sys 0e0164865330d5cf1c00247be08330bf96e2f87c https://github.com/golang/sys
|
23
vendor/github.com/opencontainers/runtime-spec/README.md
generated
vendored
23
vendor/github.com/opencontainers/runtime-spec/README.md
generated
vendored
|
@ -10,7 +10,6 @@ Additional documentation about how this group operates:
|
||||||
|
|
||||||
- [Code of Conduct][code-of-conduct]
|
- [Code of Conduct][code-of-conduct]
|
||||||
- [Style and Conventions](style.md)
|
- [Style and Conventions](style.md)
|
||||||
- [Roadmap](ROADMAP.md)
|
|
||||||
- [Implementations](implementations.md)
|
- [Implementations](implementations.md)
|
||||||
- [Releases](RELEASES.md)
|
- [Releases](RELEASES.md)
|
||||||
- [project](project.md)
|
- [project](project.md)
|
||||||
|
@ -33,12 +32,7 @@ Example use cases include sophisticated network configuration, volume garbage co
|
||||||
|
|
||||||
### Runtime Developers
|
### Runtime Developers
|
||||||
|
|
||||||
Runtime developers can build runtime implementations that run OCI-compliant bundles and container configuration, containing low-level OS and host specific details, on a particular platform.
|
Runtime developers can build runtime implementations that run OCI-compliant bundles and container configuration, containing low-level OS and host-specific details, on a particular platform.
|
||||||
|
|
||||||
## Releases
|
|
||||||
|
|
||||||
There is a loose [Road Map](./ROADMAP.md).
|
|
||||||
During the `0.x` series of OCI releases we make no backwards compatibility guarantees and intend to break the schema during this series.
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
@ -60,10 +54,16 @@ When in doubt, start on the [mailing-list](#mailing-list).
|
||||||
|
|
||||||
### Weekly Call
|
### Weekly Call
|
||||||
|
|
||||||
The contributors and maintainers of all OCI projects have a weekly meeting Wednesdays at 2:00 PM (USA Pacific).
|
The contributors and maintainers of all OCI projects have a weekly meeting on Wednesdays at:
|
||||||
Everyone is welcome to participate via [UberConference web][uberconference] or audio-only: 415-968-0849 (no PIN needed.)
|
|
||||||
|
* 8:00 AM (USA Pacific), during [odd weeks][iso-week].
|
||||||
|
* 2:00 PM (USA Pacific), during [even weeks][iso-week].
|
||||||
|
|
||||||
|
There is an [iCalendar][rfc5545] format for the meetings [here](meeting.ics).
|
||||||
|
|
||||||
|
Everyone is welcome to participate via [UberConference web][uberconference] or audio-only: +1 415 968 0849 (no PIN needed).
|
||||||
An initial agenda will be posted to the [mailing list](#mailing-list) earlier in the week, and everyone is welcome to propose additional topics or suggest other agenda alterations there.
|
An initial agenda will be posted to the [mailing list](#mailing-list) earlier in the week, and everyone is welcome to propose additional topics or suggest other agenda alterations there.
|
||||||
Minutes are posted to the [mailing list](#mailing-list) and minutes from past calls are archived to the [wiki][runtime-wiki].
|
Minutes are posted to the [mailing list](#mailing-list) and minutes from past calls are archived [here][minutes], with minutes from especially old meetings (September 2015 and earlier) archived [here][runtime-wiki].
|
||||||
|
|
||||||
### Mailing List
|
### Mailing List
|
||||||
|
|
||||||
|
@ -148,7 +148,10 @@ Read more on [How to Write a Git Commit Message][how-to-git-commit] or the Discu
|
||||||
[dev-list]: https://groups.google.com/a/opencontainers.org/forum/#!forum/dev
|
[dev-list]: https://groups.google.com/a/opencontainers.org/forum/#!forum/dev
|
||||||
[how-to-git-commit]: http://chris.beams.io/posts/git-commit
|
[how-to-git-commit]: http://chris.beams.io/posts/git-commit
|
||||||
[irc-logs]: http://ircbot.wl.linuxfoundation.org/eavesdrop/%23opencontainers/
|
[irc-logs]: http://ircbot.wl.linuxfoundation.org/eavesdrop/%23opencontainers/
|
||||||
|
[iso-week]: https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_of_a_given_date
|
||||||
|
[minutes]: http://ircbot.wl.linuxfoundation.org/meetings/opencontainers/
|
||||||
[oci]: https://www.opencontainers.org
|
[oci]: https://www.opencontainers.org
|
||||||
|
[rfc5545]: https://tools.ietf.org/html/rfc5545
|
||||||
[runtime-wiki]: https://github.com/opencontainers/runtime-spec/wiki
|
[runtime-wiki]: https://github.com/opencontainers/runtime-spec/wiki
|
||||||
[uberconference]: https://www.uberconference.com/opencontainers
|
[uberconference]: https://www.uberconference.com/opencontainers
|
||||||
|
|
||||||
|
|
153
vendor/github.com/opencontainers/runtime-spec/specs-go/config.go
generated
vendored
153
vendor/github.com/opencontainers/runtime-spec/specs-go/config.go
generated
vendored
|
@ -6,26 +6,24 @@ import "os"
|
||||||
type Spec struct {
|
type Spec struct {
|
||||||
// Version of the Open Container Runtime Specification with which the bundle complies.
|
// Version of the Open Container Runtime Specification with which the bundle complies.
|
||||||
Version string `json:"ociVersion"`
|
Version string `json:"ociVersion"`
|
||||||
// Platform specifies the configuration's target platform.
|
|
||||||
Platform Platform `json:"platform"`
|
|
||||||
// Process configures the container process.
|
// Process configures the container process.
|
||||||
Process Process `json:"process"`
|
Process *Process `json:"process,omitempty"`
|
||||||
// Root configures the container's root filesystem.
|
// Root configures the container's root filesystem.
|
||||||
Root Root `json:"root"`
|
Root *Root `json:"root,omitempty"`
|
||||||
// Hostname configures the container's hostname.
|
// Hostname configures the container's hostname.
|
||||||
Hostname string `json:"hostname,omitempty"`
|
Hostname string `json:"hostname,omitempty"`
|
||||||
// Mounts configures additional mounts (on top of Root).
|
// Mounts configures additional mounts (on top of Root).
|
||||||
Mounts []Mount `json:"mounts,omitempty"`
|
Mounts []Mount `json:"mounts,omitempty"`
|
||||||
// Hooks configures callbacks for container lifecycle events.
|
// Hooks configures callbacks for container lifecycle events.
|
||||||
Hooks *Hooks `json:"hooks,omitempty"`
|
Hooks *Hooks `json:"hooks,omitempty" platform:"linux,solaris"`
|
||||||
// Annotations contains arbitrary metadata for the container.
|
// Annotations contains arbitrary metadata for the container.
|
||||||
Annotations map[string]string `json:"annotations,omitempty"`
|
Annotations map[string]string `json:"annotations,omitempty"`
|
||||||
|
|
||||||
// Linux is platform specific configuration for Linux based containers.
|
// Linux is platform-specific configuration for Linux based containers.
|
||||||
Linux *Linux `json:"linux,omitempty" platform:"linux"`
|
Linux *Linux `json:"linux,omitempty" platform:"linux"`
|
||||||
// Solaris is platform specific configuration for Solaris containers.
|
// Solaris is platform-specific configuration for Solaris based containers.
|
||||||
Solaris *Solaris `json:"solaris,omitempty" platform:"solaris"`
|
Solaris *Solaris `json:"solaris,omitempty" platform:"solaris"`
|
||||||
// Windows is platform specific configuration for Windows based containers, including Hyper-V containers.
|
// Windows is platform-specific configuration for Windows based containers.
|
||||||
Windows *Windows `json:"windows,omitempty" platform:"windows"`
|
Windows *Windows `json:"windows,omitempty" platform:"windows"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +32,7 @@ type Process struct {
|
||||||
// Terminal creates an interactive terminal for the container.
|
// Terminal creates an interactive terminal for the container.
|
||||||
Terminal bool `json:"terminal,omitempty"`
|
Terminal bool `json:"terminal,omitempty"`
|
||||||
// ConsoleSize specifies the size of the console.
|
// ConsoleSize specifies the size of the console.
|
||||||
ConsoleSize Box `json:"consoleSize,omitempty"`
|
ConsoleSize *Box `json:"consoleSize,omitempty"`
|
||||||
// User specifies user information for the process.
|
// User specifies user information for the process.
|
||||||
User User `json:"user"`
|
User User `json:"user"`
|
||||||
// Args specifies the binary and arguments for the application to execute.
|
// Args specifies the binary and arguments for the application to execute.
|
||||||
|
@ -47,11 +45,13 @@ type Process struct {
|
||||||
// Capabilities are Linux capabilities that are kept for the process.
|
// Capabilities are Linux capabilities that are kept for the process.
|
||||||
Capabilities *LinuxCapabilities `json:"capabilities,omitempty" platform:"linux"`
|
Capabilities *LinuxCapabilities `json:"capabilities,omitempty" platform:"linux"`
|
||||||
// Rlimits specifies rlimit options to apply to the process.
|
// Rlimits specifies rlimit options to apply to the process.
|
||||||
Rlimits []LinuxRlimit `json:"rlimits,omitempty" platform:"linux"`
|
Rlimits []POSIXRlimit `json:"rlimits,omitempty" platform:"linux,solaris"`
|
||||||
// NoNewPrivileges controls whether additional privileges could be gained by processes in the container.
|
// NoNewPrivileges controls whether additional privileges could be gained by processes in the container.
|
||||||
NoNewPrivileges bool `json:"noNewPrivileges,omitempty" platform:"linux"`
|
NoNewPrivileges bool `json:"noNewPrivileges,omitempty" platform:"linux"`
|
||||||
// ApparmorProfile specifies the apparmor profile for the container.
|
// ApparmorProfile specifies the apparmor profile for the container.
|
||||||
ApparmorProfile string `json:"apparmorProfile,omitempty" platform:"linux"`
|
ApparmorProfile string `json:"apparmorProfile,omitempty" platform:"linux"`
|
||||||
|
// Specify an oom_score_adj for the container.
|
||||||
|
OOMScoreAdj *int `json:"oomScoreAdj,omitempty" platform:"linux"`
|
||||||
// SelinuxLabel specifies the selinux context that the container process is run as.
|
// SelinuxLabel specifies the selinux context that the container process is run as.
|
||||||
SelinuxLabel string `json:"selinuxLabel,omitempty" platform:"linux"`
|
SelinuxLabel string `json:"selinuxLabel,omitempty" platform:"linux"`
|
||||||
}
|
}
|
||||||
|
@ -99,23 +99,13 @@ type Root struct {
|
||||||
Readonly bool `json:"readonly,omitempty"`
|
Readonly bool `json:"readonly,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Platform specifies OS and arch information for the host system that the container
|
|
||||||
// is created for.
|
|
||||||
type Platform struct {
|
|
||||||
// OS is the operating system.
|
|
||||||
OS string `json:"os"`
|
|
||||||
// Arch is the architecture
|
|
||||||
Arch string `json:"arch"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mount specifies a mount for a container.
|
// Mount specifies a mount for a container.
|
||||||
type Mount struct {
|
type Mount struct {
|
||||||
// Destination is the path where the mount will be placed relative to the container's root. The path and child directories MUST exist, a runtime MUST NOT create directories automatically to a mount point.
|
// Destination is the absolute path where the mount will be placed in the container.
|
||||||
Destination string `json:"destination"`
|
Destination string `json:"destination"`
|
||||||
// Type specifies the mount kind.
|
// Type specifies the mount kind.
|
||||||
Type string `json:"type,omitempty"`
|
Type string `json:"type,omitempty" platform:"linux,solaris"`
|
||||||
// Source specifies the source path of the mount. In the case of bind mounts on
|
// Source specifies the source path of the mount.
|
||||||
// Linux based systems this would be the file on the host.
|
|
||||||
Source string `json:"source,omitempty"`
|
Source string `json:"source,omitempty"`
|
||||||
// Options are fstab style mount options.
|
// Options are fstab style mount options.
|
||||||
Options []string `json:"options,omitempty"`
|
Options []string `json:"options,omitempty"`
|
||||||
|
@ -132,7 +122,6 @@ type Hook struct {
|
||||||
// Hooks for container setup and teardown
|
// Hooks for container setup and teardown
|
||||||
type Hooks struct {
|
type Hooks struct {
|
||||||
// Prestart is a list of hooks to be run before the container process is executed.
|
// Prestart is a list of hooks to be run before the container process is executed.
|
||||||
// On Linux, they are run after the container namespaces are created.
|
|
||||||
Prestart []Hook `json:"prestart,omitempty"`
|
Prestart []Hook `json:"prestart,omitempty"`
|
||||||
// Poststart is a list of hooks to be run after the container process is started.
|
// Poststart is a list of hooks to be run after the container process is started.
|
||||||
Poststart []Hook `json:"poststart,omitempty"`
|
Poststart []Hook `json:"poststart,omitempty"`
|
||||||
|
@ -140,11 +129,11 @@ type Hooks struct {
|
||||||
Poststop []Hook `json:"poststop,omitempty"`
|
Poststop []Hook `json:"poststop,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Linux contains platform specific configuration for Linux based containers.
|
// Linux contains platform-specific configuration for Linux based containers.
|
||||||
type Linux struct {
|
type Linux struct {
|
||||||
// UIDMapping specifies user mappings for supporting user namespaces on Linux.
|
// UIDMapping specifies user mappings for supporting user namespaces.
|
||||||
UIDMappings []LinuxIDMapping `json:"uidMappings,omitempty"`
|
UIDMappings []LinuxIDMapping `json:"uidMappings,omitempty"`
|
||||||
// GIDMapping specifies group mappings for supporting user namespaces on Linux.
|
// GIDMapping specifies group mappings for supporting user namespaces.
|
||||||
GIDMappings []LinuxIDMapping `json:"gidMappings,omitempty"`
|
GIDMappings []LinuxIDMapping `json:"gidMappings,omitempty"`
|
||||||
// Sysctl are a set of key value pairs that are set for the container on start
|
// Sysctl are a set of key value pairs that are set for the container on start
|
||||||
Sysctl map[string]string `json:"sysctl,omitempty"`
|
Sysctl map[string]string `json:"sysctl,omitempty"`
|
||||||
|
@ -169,11 +158,14 @@ type Linux struct {
|
||||||
ReadonlyPaths []string `json:"readonlyPaths,omitempty"`
|
ReadonlyPaths []string `json:"readonlyPaths,omitempty"`
|
||||||
// MountLabel specifies the selinux context for the mounts in the container.
|
// MountLabel specifies the selinux context for the mounts in the container.
|
||||||
MountLabel string `json:"mountLabel,omitempty"`
|
MountLabel string `json:"mountLabel,omitempty"`
|
||||||
|
// IntelRdt contains Intel Resource Director Technology (RDT) information
|
||||||
|
// for handling resource constraints (e.g., L3 cache) for the container
|
||||||
|
IntelRdt *LinuxIntelRdt `json:"intelRdt,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LinuxNamespace is the configuration for a Linux namespace
|
// LinuxNamespace is the configuration for a Linux namespace
|
||||||
type LinuxNamespace struct {
|
type LinuxNamespace struct {
|
||||||
// Type is the type of Linux namespace
|
// Type is the type of namespace
|
||||||
Type LinuxNamespaceType `json:"type"`
|
Type LinuxNamespaceType `json:"type"`
|
||||||
// Path is a path to an existing namespace persisted on disk that can be joined
|
// Path is a path to an existing namespace persisted on disk that can be joined
|
||||||
// and is of the same type
|
// and is of the same type
|
||||||
|
@ -210,8 +202,8 @@ type LinuxIDMapping struct {
|
||||||
Size uint32 `json:"size"`
|
Size uint32 `json:"size"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LinuxRlimit type and restrictions
|
// POSIXRlimit type and restrictions
|
||||||
type LinuxRlimit struct {
|
type POSIXRlimit struct {
|
||||||
// Type of the rlimit to set
|
// Type of the rlimit to set
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
// Hard is the hard limit for the specified type
|
// Hard is the hard limit for the specified type
|
||||||
|
@ -244,12 +236,12 @@ type linuxBlockIODevice struct {
|
||||||
Minor int64 `json:"minor"`
|
Minor int64 `json:"minor"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LinuxWeightDevice struct holds a `major:minor weight` pair for blkioWeightDevice
|
// LinuxWeightDevice struct holds a `major:minor weight` pair for weightDevice
|
||||||
type LinuxWeightDevice struct {
|
type LinuxWeightDevice struct {
|
||||||
linuxBlockIODevice
|
linuxBlockIODevice
|
||||||
// Weight is the bandwidth rate for the device, range is from 10 to 1000
|
// Weight is the bandwidth rate for the device.
|
||||||
Weight *uint16 `json:"weight,omitempty"`
|
Weight *uint16 `json:"weight,omitempty"`
|
||||||
// LeafWeight is the bandwidth rate for the device while competing with the cgroup's child cgroups, range is from 10 to 1000, CFQ scheduler only
|
// LeafWeight is the bandwidth rate for the device while competing with the cgroup's child cgroups, CFQ scheduler only
|
||||||
LeafWeight *uint16 `json:"leafWeight,omitempty"`
|
LeafWeight *uint16 `json:"leafWeight,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,36 +254,38 @@ type LinuxThrottleDevice struct {
|
||||||
|
|
||||||
// LinuxBlockIO for Linux cgroup 'blkio' resource management
|
// LinuxBlockIO for Linux cgroup 'blkio' resource management
|
||||||
type LinuxBlockIO struct {
|
type LinuxBlockIO struct {
|
||||||
// Specifies per cgroup weight, range is from 10 to 1000
|
// Specifies per cgroup weight
|
||||||
Weight *uint16 `json:"blkioWeight,omitempty"`
|
Weight *uint16 `json:"weight,omitempty"`
|
||||||
// Specifies tasks' weight in the given cgroup while competing with the cgroup's child cgroups, range is from 10 to 1000, CFQ scheduler only
|
// Specifies tasks' weight in the given cgroup while competing with the cgroup's child cgroups, CFQ scheduler only
|
||||||
LeafWeight *uint16 `json:"blkioLeafWeight,omitempty"`
|
LeafWeight *uint16 `json:"leafWeight,omitempty"`
|
||||||
// Weight per cgroup per device, can override BlkioWeight
|
// Weight per cgroup per device, can override BlkioWeight
|
||||||
WeightDevice []LinuxWeightDevice `json:"blkioWeightDevice,omitempty"`
|
WeightDevice []LinuxWeightDevice `json:"weightDevice,omitempty"`
|
||||||
// IO read rate limit per cgroup per device, bytes per second
|
// IO read rate limit per cgroup per device, bytes per second
|
||||||
ThrottleReadBpsDevice []LinuxThrottleDevice `json:"blkioThrottleReadBpsDevice,omitempty"`
|
ThrottleReadBpsDevice []LinuxThrottleDevice `json:"throttleReadBpsDevice,omitempty"`
|
||||||
// IO write rate limit per cgroup per device, bytes per second
|
// IO write rate limit per cgroup per device, bytes per second
|
||||||
ThrottleWriteBpsDevice []LinuxThrottleDevice `json:"blkioThrottleWriteBpsDevice,omitempty"`
|
ThrottleWriteBpsDevice []LinuxThrottleDevice `json:"throttleWriteBpsDevice,omitempty"`
|
||||||
// IO read rate limit per cgroup per device, IO per second
|
// IO read rate limit per cgroup per device, IO per second
|
||||||
ThrottleReadIOPSDevice []LinuxThrottleDevice `json:"blkioThrottleReadIOPSDevice,omitempty"`
|
ThrottleReadIOPSDevice []LinuxThrottleDevice `json:"throttleReadIOPSDevice,omitempty"`
|
||||||
// IO write rate limit per cgroup per device, IO per second
|
// IO write rate limit per cgroup per device, IO per second
|
||||||
ThrottleWriteIOPSDevice []LinuxThrottleDevice `json:"blkioThrottleWriteIOPSDevice,omitempty"`
|
ThrottleWriteIOPSDevice []LinuxThrottleDevice `json:"throttleWriteIOPSDevice,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LinuxMemory for Linux cgroup 'memory' resource management
|
// LinuxMemory for Linux cgroup 'memory' resource management
|
||||||
type LinuxMemory struct {
|
type LinuxMemory struct {
|
||||||
// Memory limit (in bytes).
|
// Memory limit (in bytes).
|
||||||
Limit *uint64 `json:"limit,omitempty"`
|
Limit *int64 `json:"limit,omitempty"`
|
||||||
// Memory reservation or soft_limit (in bytes).
|
// Memory reservation or soft_limit (in bytes).
|
||||||
Reservation *uint64 `json:"reservation,omitempty"`
|
Reservation *int64 `json:"reservation,omitempty"`
|
||||||
// Total memory limit (memory + swap).
|
// Total memory limit (memory + swap).
|
||||||
Swap *uint64 `json:"swap,omitempty"`
|
Swap *int64 `json:"swap,omitempty"`
|
||||||
// Kernel memory limit (in bytes).
|
// Kernel memory limit (in bytes).
|
||||||
Kernel *uint64 `json:"kernel,omitempty"`
|
Kernel *int64 `json:"kernel,omitempty"`
|
||||||
// Kernel memory limit for tcp (in bytes)
|
// Kernel memory limit for tcp (in bytes)
|
||||||
KernelTCP *uint64 `json:"kernelTCP,omitempty"`
|
KernelTCP *int64 `json:"kernelTCP,omitempty"`
|
||||||
// How aggressive the kernel will swap memory pages. Range from 0 to 100.
|
// How aggressive the kernel will swap memory pages.
|
||||||
Swappiness *uint64 `json:"swappiness,omitempty"`
|
Swappiness *uint64 `json:"swappiness,omitempty"`
|
||||||
|
// DisableOOMKiller disables the OOM killer for out of memory conditions
|
||||||
|
DisableOOMKiller *bool `json:"disableOOMKiller,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LinuxCPU for Linux cgroup 'cpu' resource management
|
// LinuxCPU for Linux cgroup 'cpu' resource management
|
||||||
|
@ -330,10 +324,6 @@ type LinuxNetwork struct {
|
||||||
type LinuxResources struct {
|
type LinuxResources struct {
|
||||||
// Devices configures the device whitelist.
|
// Devices configures the device whitelist.
|
||||||
Devices []LinuxDeviceCgroup `json:"devices,omitempty"`
|
Devices []LinuxDeviceCgroup `json:"devices,omitempty"`
|
||||||
// DisableOOMKiller disables the OOM killer for out of memory conditions
|
|
||||||
DisableOOMKiller *bool `json:"disableOOMKiller,omitempty"`
|
|
||||||
// Specify an oom_score_adj for the container.
|
|
||||||
OOMScoreAdj *int `json:"oomScoreAdj,omitempty"`
|
|
||||||
// Memory restriction configuration
|
// Memory restriction configuration
|
||||||
Memory *LinuxMemory `json:"memory,omitempty"`
|
Memory *LinuxMemory `json:"memory,omitempty"`
|
||||||
// CPU resource restriction configuration
|
// CPU resource restriction configuration
|
||||||
|
@ -380,7 +370,7 @@ type LinuxDeviceCgroup struct {
|
||||||
Access string `json:"access,omitempty"`
|
Access string `json:"access,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Solaris contains platform specific configuration for Solaris application containers.
|
// Solaris contains platform-specific configuration for Solaris application containers.
|
||||||
type Solaris struct {
|
type Solaris struct {
|
||||||
// SMF FMRI which should go "online" before we start the container process.
|
// SMF FMRI which should go "online" before we start the container process.
|
||||||
Milestone string `json:"milestone,omitempty"`
|
Milestone string `json:"milestone,omitempty"`
|
||||||
|
@ -427,8 +417,20 @@ type SolarisAnet struct {
|
||||||
|
|
||||||
// Windows defines the runtime configuration for Windows based containers, including Hyper-V containers.
|
// Windows defines the runtime configuration for Windows based containers, including Hyper-V containers.
|
||||||
type Windows struct {
|
type Windows struct {
|
||||||
|
// LayerFolders contains a list of absolute paths to directories containing image layers.
|
||||||
|
LayerFolders []string `json:"layerFolders"`
|
||||||
// Resources contains information for handling resource constraints for the container.
|
// Resources contains information for handling resource constraints for the container.
|
||||||
Resources *WindowsResources `json:"resources,omitempty"`
|
Resources *WindowsResources `json:"resources,omitempty"`
|
||||||
|
// CredentialSpec contains a JSON object describing a group Managed Service Account (gMSA) specification.
|
||||||
|
CredentialSpec interface{} `json:"credentialSpec,omitempty"`
|
||||||
|
// Servicing indicates if the container is being started in a mode to apply a Windows Update servicing operation.
|
||||||
|
Servicing bool `json:"servicing,omitempty"`
|
||||||
|
// IgnoreFlushesDuringBoot indicates if the container is being started in a mode where disk writes are not flushed during its boot process.
|
||||||
|
IgnoreFlushesDuringBoot bool `json:"ignoreFlushesDuringBoot,omitempty"`
|
||||||
|
// HyperV contains information for running a container with Hyper-V isolation.
|
||||||
|
HyperV *WindowsHyperV `json:"hyperv,omitempty"`
|
||||||
|
// Network restriction configuration.
|
||||||
|
Network *WindowsNetwork `json:"network,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// WindowsResources has container runtime resource constraints for containers running on Windows.
|
// WindowsResources has container runtime resource constraints for containers running on Windows.
|
||||||
|
@ -439,26 +441,22 @@ type WindowsResources struct {
|
||||||
CPU *WindowsCPUResources `json:"cpu,omitempty"`
|
CPU *WindowsCPUResources `json:"cpu,omitempty"`
|
||||||
// Storage restriction configuration.
|
// Storage restriction configuration.
|
||||||
Storage *WindowsStorageResources `json:"storage,omitempty"`
|
Storage *WindowsStorageResources `json:"storage,omitempty"`
|
||||||
// Network restriction configuration.
|
|
||||||
Network *WindowsNetworkResources `json:"network,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WindowsMemoryResources contains memory resource management settings.
|
// WindowsMemoryResources contains memory resource management settings.
|
||||||
type WindowsMemoryResources struct {
|
type WindowsMemoryResources struct {
|
||||||
// Memory limit in bytes.
|
// Memory limit in bytes.
|
||||||
Limit *uint64 `json:"limit,omitempty"`
|
Limit *uint64 `json:"limit,omitempty"`
|
||||||
// Memory reservation in bytes.
|
|
||||||
Reservation *uint64 `json:"reservation,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WindowsCPUResources contains CPU resource management settings.
|
// WindowsCPUResources contains CPU resource management settings.
|
||||||
type WindowsCPUResources struct {
|
type WindowsCPUResources struct {
|
||||||
// Number of CPUs available to the container.
|
// Number of CPUs available to the container.
|
||||||
Count *uint64 `json:"count,omitempty"`
|
Count *uint64 `json:"count,omitempty"`
|
||||||
// CPU shares (relative weight to other containers with cpu shares). Range is from 1 to 10000.
|
// CPU shares (relative weight to other containers with cpu shares).
|
||||||
Shares *uint16 `json:"shares,omitempty"`
|
Shares *uint16 `json:"shares,omitempty"`
|
||||||
// Percent of available CPUs usable by the container.
|
// Specifies the portion of processor cycles that this container can use as a percentage times 100.
|
||||||
Percent *uint8 `json:"percent,omitempty"`
|
Maximum *uint16 `json:"maximum,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// WindowsStorageResources contains storage resource management settings.
|
// WindowsStorageResources contains storage resource management settings.
|
||||||
|
@ -471,17 +469,29 @@ type WindowsStorageResources struct {
|
||||||
SandboxSize *uint64 `json:"sandboxSize,omitempty"`
|
SandboxSize *uint64 `json:"sandboxSize,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// WindowsNetworkResources contains network resource management settings.
|
// WindowsNetwork contains network settings for Windows containers.
|
||||||
type WindowsNetworkResources struct {
|
type WindowsNetwork struct {
|
||||||
// EgressBandwidth is the maximum egress bandwidth in bytes per second.
|
// List of HNS endpoints that the container should connect to.
|
||||||
EgressBandwidth *uint64 `json:"egressBandwidth,omitempty"`
|
EndpointList []string `json:"endpointList,omitempty"`
|
||||||
|
// Specifies if unqualified DNS name resolution is allowed.
|
||||||
|
AllowUnqualifiedDNSQuery bool `json:"allowUnqualifiedDNSQuery,omitempty"`
|
||||||
|
// Comma separated list of DNS suffixes to use for name resolution.
|
||||||
|
DNSSearchList []string `json:"DNSSearchList,omitempty"`
|
||||||
|
// Name (ID) of the container that we will share with the network stack.
|
||||||
|
NetworkSharedContainerName string `json:"networkSharedContainerName,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// WindowsHyperV contains information for configuring a container to run with Hyper-V isolation.
|
||||||
|
type WindowsHyperV struct {
|
||||||
|
// UtilityVMPath is an optional path to the image used for the Utility VM.
|
||||||
|
UtilityVMPath string `json:"utilityVMPath,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LinuxSeccomp represents syscall restrictions
|
// LinuxSeccomp represents syscall restrictions
|
||||||
type LinuxSeccomp struct {
|
type LinuxSeccomp struct {
|
||||||
DefaultAction LinuxSeccompAction `json:"defaultAction"`
|
DefaultAction LinuxSeccompAction `json:"defaultAction"`
|
||||||
Architectures []Arch `json:"architectures,omitempty"`
|
Architectures []Arch `json:"architectures,omitempty"`
|
||||||
Syscalls []LinuxSyscall `json:"syscalls"`
|
Syscalls []LinuxSyscall `json:"syscalls,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Arch used for additional architectures
|
// Arch used for additional architectures
|
||||||
|
@ -540,7 +550,7 @@ const (
|
||||||
type LinuxSeccompArg struct {
|
type LinuxSeccompArg struct {
|
||||||
Index uint `json:"index"`
|
Index uint `json:"index"`
|
||||||
Value uint64 `json:"value"`
|
Value uint64 `json:"value"`
|
||||||
ValueTwo uint64 `json:"valueTwo"`
|
ValueTwo uint64 `json:"valueTwo,omitempty"`
|
||||||
Op LinuxSeccompOperator `json:"op"`
|
Op LinuxSeccompOperator `json:"op"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -548,6 +558,13 @@ type LinuxSeccompArg struct {
|
||||||
type LinuxSyscall struct {
|
type LinuxSyscall struct {
|
||||||
Names []string `json:"names"`
|
Names []string `json:"names"`
|
||||||
Action LinuxSeccompAction `json:"action"`
|
Action LinuxSeccompAction `json:"action"`
|
||||||
Args []LinuxSeccompArg `json:"args"`
|
Args []LinuxSeccompArg `json:"args,omitempty"`
|
||||||
Comment string `json:"comment"`
|
}
|
||||||
|
|
||||||
|
// LinuxIntelRdt has container runtime resource constraints
|
||||||
|
// for Intel RDT/CAT which introduced in Linux 4.10 kernel
|
||||||
|
type LinuxIntelRdt struct {
|
||||||
|
// The schema for L3 cache id and capacity bitmask (CBM)
|
||||||
|
// Format: "L3:<cache_id0>=<cbm0>;<cache_id1>=<cbm1>;..."
|
||||||
|
L3CacheSchema string `json:"l3CacheSchema,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
2
vendor/github.com/opencontainers/runtime-spec/specs-go/state.go
generated
vendored
2
vendor/github.com/opencontainers/runtime-spec/specs-go/state.go
generated
vendored
|
@ -9,7 +9,7 @@ type State struct {
|
||||||
// Status is the runtime status of the container.
|
// Status is the runtime status of the container.
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
// Pid is the process ID for the container process.
|
// Pid is the process ID for the container process.
|
||||||
Pid int `json:"pid"`
|
Pid int `json:"pid,omitempty"`
|
||||||
// Bundle is the path to the container's bundle directory.
|
// Bundle is the path to the container's bundle directory.
|
||||||
Bundle string `json:"bundle"`
|
Bundle string `json:"bundle"`
|
||||||
// Annotations are key values associated with the container.
|
// Annotations are key values associated with the container.
|
||||||
|
|
2
vendor/github.com/opencontainers/runtime-spec/specs-go/version.go
generated
vendored
2
vendor/github.com/opencontainers/runtime-spec/specs-go/version.go
generated
vendored
|
@ -11,7 +11,7 @@ const (
|
||||||
VersionPatch = 0
|
VersionPatch = 0
|
||||||
|
|
||||||
// VersionDev indicates development branch. Releases will be empty string.
|
// VersionDev indicates development branch. Releases will be empty string.
|
||||||
VersionDev = "-rc5"
|
VersionDev = ""
|
||||||
)
|
)
|
||||||
|
|
||||||
// Version is the specification version that the package types support.
|
// Version is the specification version that the package types support.
|
||||||
|
|
1
vendor/github.com/opencontainers/runtime-tools/README.md
generated
vendored
1
vendor/github.com/opencontainers/runtime-tools/README.md
generated
vendored
|
@ -1,6 +1,7 @@
|
||||||
# oci-runtime-tool [![Build Status](https://travis-ci.org/opencontainers/runtime-tools.svg?branch=master)](https://travis-ci.org/opencontainers/runtime-tools) [![Go Report Card](https://goreportcard.com/badge/github.com/opencontainers/runtime-tools)](https://goreportcard.com/report/github.com/opencontainers/runtime-tools)
|
# oci-runtime-tool [![Build Status](https://travis-ci.org/opencontainers/runtime-tools.svg?branch=master)](https://travis-ci.org/opencontainers/runtime-tools) [![Go Report Card](https://goreportcard.com/badge/github.com/opencontainers/runtime-tools)](https://goreportcard.com/report/github.com/opencontainers/runtime-tools)
|
||||||
|
|
||||||
oci-runtime-tool is a collection of tools for working with the [OCI runtime specification][runtime-spec].
|
oci-runtime-tool is a collection of tools for working with the [OCI runtime specification][runtime-spec].
|
||||||
|
To build from source code, runtime-tools requires Go 1.7.x or above.
|
||||||
|
|
||||||
## Generating an OCI runtime spec configuration files
|
## Generating an OCI runtime spec configuration files
|
||||||
|
|
||||||
|
|
120
vendor/github.com/opencontainers/runtime-tools/generate/generate.go
generated
vendored
120
vendor/github.com/opencontainers/runtime-tools/generate/generate.go
generated
vendored
|
@ -6,7 +6,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
rspec "github.com/opencontainers/runtime-spec/specs-go"
|
rspec "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
@ -35,15 +34,11 @@ type ExportOptions struct {
|
||||||
func New() Generator {
|
func New() Generator {
|
||||||
spec := rspec.Spec{
|
spec := rspec.Spec{
|
||||||
Version: rspec.Version,
|
Version: rspec.Version,
|
||||||
Platform: rspec.Platform{
|
Root: &rspec.Root{
|
||||||
OS: runtime.GOOS,
|
|
||||||
Arch: runtime.GOARCH,
|
|
||||||
},
|
|
||||||
Root: rspec.Root{
|
|
||||||
Path: "",
|
Path: "",
|
||||||
Readonly: false,
|
Readonly: false,
|
||||||
},
|
},
|
||||||
Process: rspec.Process{
|
Process: &rspec.Process{
|
||||||
Terminal: false,
|
Terminal: false,
|
||||||
User: rspec.User{},
|
User: rspec.User{},
|
||||||
Args: []string{
|
Args: []string{
|
||||||
|
@ -136,7 +131,7 @@ func New() Generator {
|
||||||
"CAP_AUDIT_WRITE",
|
"CAP_AUDIT_WRITE",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Rlimits: []rspec.LinuxRlimit{
|
Rlimits: []rspec.POSIXRlimit{
|
||||||
{
|
{
|
||||||
Type: "RLIMIT_NOFILE",
|
Type: "RLIMIT_NOFILE",
|
||||||
Hard: uint64(1024),
|
Hard: uint64(1024),
|
||||||
|
@ -308,13 +303,13 @@ func (g *Generator) SetVersion(version string) {
|
||||||
|
|
||||||
// SetRootPath sets g.spec.Root.Path.
|
// SetRootPath sets g.spec.Root.Path.
|
||||||
func (g *Generator) SetRootPath(path string) {
|
func (g *Generator) SetRootPath(path string) {
|
||||||
g.initSpec()
|
g.initSpecRoot()
|
||||||
g.spec.Root.Path = path
|
g.spec.Root.Path = path
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetRootReadonly sets g.spec.Root.Readonly.
|
// SetRootReadonly sets g.spec.Root.Readonly.
|
||||||
func (g *Generator) SetRootReadonly(b bool) {
|
func (g *Generator) SetRootReadonly(b bool) {
|
||||||
g.initSpec()
|
g.initSpecRoot()
|
||||||
g.spec.Root.Readonly = b
|
g.spec.Root.Readonly = b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,57 +341,45 @@ func (g *Generator) RemoveAnnotation(key string) {
|
||||||
delete(g.spec.Annotations, key)
|
delete(g.spec.Annotations, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetPlatformOS sets g.spec.Process.OS.
|
|
||||||
func (g *Generator) SetPlatformOS(os string) {
|
|
||||||
g.initSpec()
|
|
||||||
g.spec.Platform.OS = os
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetPlatformArch sets g.spec.Platform.Arch.
|
|
||||||
func (g *Generator) SetPlatformArch(arch string) {
|
|
||||||
g.initSpec()
|
|
||||||
g.spec.Platform.Arch = arch
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetProcessUID sets g.spec.Process.User.UID.
|
// SetProcessUID sets g.spec.Process.User.UID.
|
||||||
func (g *Generator) SetProcessUID(uid uint32) {
|
func (g *Generator) SetProcessUID(uid uint32) {
|
||||||
g.initSpec()
|
g.initSpecProcess()
|
||||||
g.spec.Process.User.UID = uid
|
g.spec.Process.User.UID = uid
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetProcessGID sets g.spec.Process.User.GID.
|
// SetProcessGID sets g.spec.Process.User.GID.
|
||||||
func (g *Generator) SetProcessGID(gid uint32) {
|
func (g *Generator) SetProcessGID(gid uint32) {
|
||||||
g.initSpec()
|
g.initSpecProcess()
|
||||||
g.spec.Process.User.GID = gid
|
g.spec.Process.User.GID = gid
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetProcessCwd sets g.spec.Process.Cwd.
|
// SetProcessCwd sets g.spec.Process.Cwd.
|
||||||
func (g *Generator) SetProcessCwd(cwd string) {
|
func (g *Generator) SetProcessCwd(cwd string) {
|
||||||
g.initSpec()
|
g.initSpecProcess()
|
||||||
g.spec.Process.Cwd = cwd
|
g.spec.Process.Cwd = cwd
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetProcessNoNewPrivileges sets g.spec.Process.NoNewPrivileges.
|
// SetProcessNoNewPrivileges sets g.spec.Process.NoNewPrivileges.
|
||||||
func (g *Generator) SetProcessNoNewPrivileges(b bool) {
|
func (g *Generator) SetProcessNoNewPrivileges(b bool) {
|
||||||
g.initSpec()
|
g.initSpecProcess()
|
||||||
g.spec.Process.NoNewPrivileges = b
|
g.spec.Process.NoNewPrivileges = b
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetProcessTerminal sets g.spec.Process.Terminal.
|
// SetProcessTerminal sets g.spec.Process.Terminal.
|
||||||
func (g *Generator) SetProcessTerminal(b bool) {
|
func (g *Generator) SetProcessTerminal(b bool) {
|
||||||
g.initSpec()
|
g.initSpecProcess()
|
||||||
g.spec.Process.Terminal = b
|
g.spec.Process.Terminal = b
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetProcessApparmorProfile sets g.spec.Process.ApparmorProfile.
|
// SetProcessApparmorProfile sets g.spec.Process.ApparmorProfile.
|
||||||
func (g *Generator) SetProcessApparmorProfile(prof string) {
|
func (g *Generator) SetProcessApparmorProfile(prof string) {
|
||||||
g.initSpec()
|
g.initSpecProcess()
|
||||||
g.spec.Process.ApparmorProfile = prof
|
g.spec.Process.ApparmorProfile = prof
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetProcessArgs sets g.spec.Process.Args.
|
// SetProcessArgs sets g.spec.Process.Args.
|
||||||
func (g *Generator) SetProcessArgs(args []string) {
|
func (g *Generator) SetProcessArgs(args []string) {
|
||||||
g.initSpec()
|
g.initSpecProcess()
|
||||||
g.spec.Process.Args = args
|
g.spec.Process.Args = args
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -411,7 +394,7 @@ func (g *Generator) ClearProcessEnv() {
|
||||||
// AddProcessEnv adds name=value into g.spec.Process.Env, or replaces an
|
// AddProcessEnv adds name=value into g.spec.Process.Env, or replaces an
|
||||||
// existing entry with the given name.
|
// existing entry with the given name.
|
||||||
func (g *Generator) AddProcessEnv(name, value string) {
|
func (g *Generator) AddProcessEnv(name, value string) {
|
||||||
g.initSpec()
|
g.initSpecProcess()
|
||||||
|
|
||||||
env := fmt.Sprintf("%s=%s", name, value)
|
env := fmt.Sprintf("%s=%s", name, value)
|
||||||
for idx := range g.spec.Process.Env {
|
for idx := range g.spec.Process.Env {
|
||||||
|
@ -425,7 +408,7 @@ func (g *Generator) AddProcessEnv(name, value string) {
|
||||||
|
|
||||||
// AddProcessRlimits adds rlimit into g.spec.Process.Rlimits.
|
// AddProcessRlimits adds rlimit into g.spec.Process.Rlimits.
|
||||||
func (g *Generator) AddProcessRlimits(rType string, rHard uint64, rSoft uint64) {
|
func (g *Generator) AddProcessRlimits(rType string, rHard uint64, rSoft uint64) {
|
||||||
g.initSpec()
|
g.initSpecProcess()
|
||||||
for i, rlimit := range g.spec.Process.Rlimits {
|
for i, rlimit := range g.spec.Process.Rlimits {
|
||||||
if rlimit.Type == rType {
|
if rlimit.Type == rType {
|
||||||
g.spec.Process.Rlimits[i].Hard = rHard
|
g.spec.Process.Rlimits[i].Hard = rHard
|
||||||
|
@ -434,7 +417,7 @@ func (g *Generator) AddProcessRlimits(rType string, rHard uint64, rSoft uint64)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
newRlimit := rspec.LinuxRlimit{
|
newRlimit := rspec.POSIXRlimit{
|
||||||
Type: rType,
|
Type: rType,
|
||||||
Hard: rHard,
|
Hard: rHard,
|
||||||
Soft: rSoft,
|
Soft: rSoft,
|
||||||
|
@ -461,7 +444,7 @@ func (g *Generator) ClearProcessRlimits() {
|
||||||
if g.spec == nil {
|
if g.spec == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
g.spec.Process.Rlimits = []rspec.LinuxRlimit{}
|
g.spec.Process.Rlimits = []rspec.POSIXRlimit{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClearProcessAdditionalGids clear g.spec.Process.AdditionalGids.
|
// ClearProcessAdditionalGids clear g.spec.Process.AdditionalGids.
|
||||||
|
@ -474,7 +457,7 @@ func (g *Generator) ClearProcessAdditionalGids() {
|
||||||
|
|
||||||
// AddProcessAdditionalGid adds an additional gid into g.spec.Process.AdditionalGids.
|
// AddProcessAdditionalGid adds an additional gid into g.spec.Process.AdditionalGids.
|
||||||
func (g *Generator) AddProcessAdditionalGid(gid uint32) {
|
func (g *Generator) AddProcessAdditionalGid(gid uint32) {
|
||||||
g.initSpec()
|
g.initSpecProcess()
|
||||||
for _, group := range g.spec.Process.User.AdditionalGids {
|
for _, group := range g.spec.Process.User.AdditionalGids {
|
||||||
if group == gid {
|
if group == gid {
|
||||||
return
|
return
|
||||||
|
@ -485,7 +468,7 @@ func (g *Generator) AddProcessAdditionalGid(gid uint32) {
|
||||||
|
|
||||||
// SetProcessSelinuxLabel sets g.spec.Process.SelinuxLabel.
|
// SetProcessSelinuxLabel sets g.spec.Process.SelinuxLabel.
|
||||||
func (g *Generator) SetProcessSelinuxLabel(label string) {
|
func (g *Generator) SetProcessSelinuxLabel(label string) {
|
||||||
g.initSpec()
|
g.initSpecProcess()
|
||||||
g.spec.Process.SelinuxLabel = label
|
g.spec.Process.SelinuxLabel = label
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -501,16 +484,10 @@ func (g *Generator) SetLinuxMountLabel(label string) {
|
||||||
g.spec.Linux.MountLabel = label
|
g.spec.Linux.MountLabel = label
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetLinuxResourcesDisableOOMKiller sets g.spec.Linux.Resources.DisableOOMKiller.
|
// SetProcessOOMScoreAdj sets g.spec.Process.OOMScoreAdj.
|
||||||
func (g *Generator) SetLinuxResourcesDisableOOMKiller(disable bool) {
|
func (g *Generator) SetProcessOOMScoreAdj(adj int) {
|
||||||
g.initSpecLinuxResources()
|
g.initSpecProcess()
|
||||||
g.spec.Linux.Resources.DisableOOMKiller = &disable
|
g.spec.Process.OOMScoreAdj = &adj
|
||||||
}
|
|
||||||
|
|
||||||
// SetLinuxResourcesOOMScoreAdj sets g.spec.Linux.Resources.OOMScoreAdj.
|
|
||||||
func (g *Generator) SetLinuxResourcesOOMScoreAdj(adj int) {
|
|
||||||
g.initSpecLinuxResources()
|
|
||||||
g.spec.Linux.Resources.OOMScoreAdj = &adj
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetLinuxResourcesCPUShares sets g.spec.Linux.Resources.CPU.Shares.
|
// SetLinuxResourcesCPUShares sets g.spec.Linux.Resources.CPU.Shares.
|
||||||
|
@ -555,32 +532,62 @@ func (g *Generator) SetLinuxResourcesCPUMems(mems string) {
|
||||||
g.spec.Linux.Resources.CPU.Mems = mems
|
g.spec.Linux.Resources.CPU.Mems = mems
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddLinuxResourcesHugepageLimit adds or sets g.spec.Linux.Resources.HugepageLimits.
|
||||||
|
func (g *Generator) AddLinuxResourcesHugepageLimit(pageSize string, limit uint64) {
|
||||||
|
hugepageLimit := rspec.LinuxHugepageLimit{
|
||||||
|
Pagesize: pageSize,
|
||||||
|
Limit: limit,
|
||||||
|
}
|
||||||
|
|
||||||
|
g.initSpecLinuxResources()
|
||||||
|
for i, pageLimit := range g.spec.Linux.Resources.HugepageLimits {
|
||||||
|
if pageLimit.Pagesize == pageSize {
|
||||||
|
g.spec.Linux.Resources.HugepageLimits[i].Limit = limit
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.spec.Linux.Resources.HugepageLimits = append(g.spec.Linux.Resources.HugepageLimits, hugepageLimit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DropLinuxResourcesHugepageLimit drops a hugepage limit from g.spec.Linux.Resources.HugepageLimits.
|
||||||
|
func (g *Generator) DropLinuxResourcesHugepageLimit(pageSize string) error {
|
||||||
|
g.initSpecLinuxResources()
|
||||||
|
for i, pageLimit := range g.spec.Linux.Resources.HugepageLimits {
|
||||||
|
if pageLimit.Pagesize == pageSize {
|
||||||
|
g.spec.Linux.Resources.HugepageLimits = append(g.spec.Linux.Resources.HugepageLimits[:i], g.spec.Linux.Resources.HugepageLimits[i+1:]...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// SetLinuxResourcesMemoryLimit sets g.spec.Linux.Resources.Memory.Limit.
|
// SetLinuxResourcesMemoryLimit sets g.spec.Linux.Resources.Memory.Limit.
|
||||||
func (g *Generator) SetLinuxResourcesMemoryLimit(limit uint64) {
|
func (g *Generator) SetLinuxResourcesMemoryLimit(limit int64) {
|
||||||
g.initSpecLinuxResourcesMemory()
|
g.initSpecLinuxResourcesMemory()
|
||||||
g.spec.Linux.Resources.Memory.Limit = &limit
|
g.spec.Linux.Resources.Memory.Limit = &limit
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetLinuxResourcesMemoryReservation sets g.spec.Linux.Resources.Memory.Reservation.
|
// SetLinuxResourcesMemoryReservation sets g.spec.Linux.Resources.Memory.Reservation.
|
||||||
func (g *Generator) SetLinuxResourcesMemoryReservation(reservation uint64) {
|
func (g *Generator) SetLinuxResourcesMemoryReservation(reservation int64) {
|
||||||
g.initSpecLinuxResourcesMemory()
|
g.initSpecLinuxResourcesMemory()
|
||||||
g.spec.Linux.Resources.Memory.Reservation = &reservation
|
g.spec.Linux.Resources.Memory.Reservation = &reservation
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetLinuxResourcesMemorySwap sets g.spec.Linux.Resources.Memory.Swap.
|
// SetLinuxResourcesMemorySwap sets g.spec.Linux.Resources.Memory.Swap.
|
||||||
func (g *Generator) SetLinuxResourcesMemorySwap(swap uint64) {
|
func (g *Generator) SetLinuxResourcesMemorySwap(swap int64) {
|
||||||
g.initSpecLinuxResourcesMemory()
|
g.initSpecLinuxResourcesMemory()
|
||||||
g.spec.Linux.Resources.Memory.Swap = &swap
|
g.spec.Linux.Resources.Memory.Swap = &swap
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetLinuxResourcesMemoryKernel sets g.spec.Linux.Resources.Memory.Kernel.
|
// SetLinuxResourcesMemoryKernel sets g.spec.Linux.Resources.Memory.Kernel.
|
||||||
func (g *Generator) SetLinuxResourcesMemoryKernel(kernel uint64) {
|
func (g *Generator) SetLinuxResourcesMemoryKernel(kernel int64) {
|
||||||
g.initSpecLinuxResourcesMemory()
|
g.initSpecLinuxResourcesMemory()
|
||||||
g.spec.Linux.Resources.Memory.Kernel = &kernel
|
g.spec.Linux.Resources.Memory.Kernel = &kernel
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetLinuxResourcesMemoryKernelTCP sets g.spec.Linux.Resources.Memory.KernelTCP.
|
// SetLinuxResourcesMemoryKernelTCP sets g.spec.Linux.Resources.Memory.KernelTCP.
|
||||||
func (g *Generator) SetLinuxResourcesMemoryKernelTCP(kernelTCP uint64) {
|
func (g *Generator) SetLinuxResourcesMemoryKernelTCP(kernelTCP int64) {
|
||||||
g.initSpecLinuxResourcesMemory()
|
g.initSpecLinuxResourcesMemory()
|
||||||
g.spec.Linux.Resources.Memory.KernelTCP = &kernelTCP
|
g.spec.Linux.Resources.Memory.KernelTCP = &kernelTCP
|
||||||
}
|
}
|
||||||
|
@ -591,6 +598,12 @@ func (g *Generator) SetLinuxResourcesMemorySwappiness(swappiness uint64) {
|
||||||
g.spec.Linux.Resources.Memory.Swappiness = &swappiness
|
g.spec.Linux.Resources.Memory.Swappiness = &swappiness
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetLinuxResourcesMemoryDisableOOMKiller sets g.spec.Linux.Resources.Memory.DisableOOMKiller.
|
||||||
|
func (g *Generator) SetLinuxResourcesMemoryDisableOOMKiller(disable bool) {
|
||||||
|
g.initSpecLinuxResourcesMemory()
|
||||||
|
g.spec.Linux.Resources.Memory.DisableOOMKiller = &disable
|
||||||
|
}
|
||||||
|
|
||||||
// SetLinuxResourcesNetworkClassID sets g.spec.Linux.Resources.Network.ClassID.
|
// SetLinuxResourcesNetworkClassID sets g.spec.Linux.Resources.Network.ClassID.
|
||||||
func (g *Generator) SetLinuxResourcesNetworkClassID(classid uint32) {
|
func (g *Generator) SetLinuxResourcesNetworkClassID(classid uint32) {
|
||||||
g.initSpecLinuxResourcesNetwork()
|
g.initSpecLinuxResourcesNetwork()
|
||||||
|
@ -839,6 +852,7 @@ func (g *Generator) SetupPrivileged(privileged bool) {
|
||||||
finalCapList = append(finalCapList, fmt.Sprintf("CAP_%s", strings.ToUpper(cap.String())))
|
finalCapList = append(finalCapList, fmt.Sprintf("CAP_%s", strings.ToUpper(cap.String())))
|
||||||
}
|
}
|
||||||
g.initSpecLinux()
|
g.initSpecLinux()
|
||||||
|
g.initSpecProcessCapabilities()
|
||||||
g.spec.Process.Capabilities.Bounding = finalCapList
|
g.spec.Process.Capabilities.Bounding = finalCapList
|
||||||
g.spec.Process.Capabilities.Effective = finalCapList
|
g.spec.Process.Capabilities.Effective = finalCapList
|
||||||
g.spec.Process.Capabilities.Inheritable = finalCapList
|
g.spec.Process.Capabilities.Inheritable = finalCapList
|
||||||
|
@ -869,7 +883,7 @@ func (g *Generator) AddProcessCapability(c string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
g.initSpec()
|
g.initSpecProcessCapabilities()
|
||||||
|
|
||||||
for _, cap := range g.spec.Process.Capabilities.Bounding {
|
for _, cap := range g.spec.Process.Capabilities.Bounding {
|
||||||
if strings.ToUpper(cap) == cp {
|
if strings.ToUpper(cap) == cp {
|
||||||
|
@ -916,7 +930,7 @@ func (g *Generator) DropProcessCapability(c string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
g.initSpec()
|
g.initSpecProcessCapabilities()
|
||||||
|
|
||||||
for i, cap := range g.spec.Process.Capabilities.Bounding {
|
for i, cap := range g.spec.Process.Capabilities.Bounding {
|
||||||
if strings.ToUpper(cap) == cp {
|
if strings.ToUpper(cap) == cp {
|
||||||
|
@ -968,7 +982,7 @@ func mapStrToNamespace(ns string, path string) (rspec.LinuxNamespace, error) {
|
||||||
case "cgroup":
|
case "cgroup":
|
||||||
return rspec.LinuxNamespace{Type: rspec.CgroupNamespace, Path: path}, nil
|
return rspec.LinuxNamespace{Type: rspec.CgroupNamespace, Path: path}, nil
|
||||||
default:
|
default:
|
||||||
return rspec.LinuxNamespace{}, fmt.Errorf("Should not reach here!")
|
return rspec.LinuxNamespace{}, fmt.Errorf("unrecognized namespace %q", ns)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
14
vendor/github.com/opencontainers/runtime-tools/generate/seccomp/parse_action.go
generated
vendored
14
vendor/github.com/opencontainers/runtime-tools/generate/seccomp/parse_action.go
generated
vendored
|
@ -30,8 +30,9 @@ func ParseSyscallFlag(args SyscallOpts, config *rspec.LinuxSeccomp) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
action, _ := parseAction(arguments[0])
|
action, _ := parseAction(arguments[0])
|
||||||
if action == config.DefaultAction {
|
if action == config.DefaultAction && args.argsAreEmpty() {
|
||||||
return fmt.Errorf("default action already set as %s", action)
|
// default already set, no need to make changes
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var newSyscall rspec.LinuxSyscall
|
var newSyscall rspec.LinuxSyscall
|
||||||
|
@ -96,7 +97,7 @@ func ParseDefaultAction(action string, config *rspec.LinuxSeccomp) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
config.DefaultAction = defaultAction
|
config.DefaultAction = defaultAction
|
||||||
err = RemoveAllMatchingRules(config, action)
|
err = RemoveAllMatchingRules(config, defaultAction)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -125,3 +126,10 @@ func newSyscallStruct(name string, action rspec.LinuxSeccompAction, args []rspec
|
||||||
}
|
}
|
||||||
return syscallStruct
|
return syscallStruct
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s SyscallOpts) argsAreEmpty() bool {
|
||||||
|
return (s.Index == "" &&
|
||||||
|
s.Value == "" &&
|
||||||
|
s.ValueTwo == "" &&
|
||||||
|
s.Operator == "")
|
||||||
|
}
|
||||||
|
|
14
vendor/github.com/opencontainers/runtime-tools/generate/seccomp/parse_remove.go
generated
vendored
14
vendor/github.com/opencontainers/runtime-tools/generate/seccomp/parse_remove.go
generated
vendored
|
@ -15,12 +15,7 @@ func RemoveAction(arguments string, config *rspec.LinuxSeccomp) error {
|
||||||
return fmt.Errorf("Cannot remove action from nil Seccomp pointer")
|
return fmt.Errorf("Cannot remove action from nil Seccomp pointer")
|
||||||
}
|
}
|
||||||
|
|
||||||
var syscallsToRemove []string
|
syscallsToRemove := strings.Split(arguments, ",")
|
||||||
if strings.Contains(arguments, ",") {
|
|
||||||
syscallsToRemove = strings.Split(arguments, ",")
|
|
||||||
} else {
|
|
||||||
syscallsToRemove = append(syscallsToRemove, arguments)
|
|
||||||
}
|
|
||||||
|
|
||||||
for counter, syscallStruct := range config.Syscalls {
|
for counter, syscallStruct := range config.Syscalls {
|
||||||
if reflect.DeepEqual(syscallsToRemove, syscallStruct.Names) {
|
if reflect.DeepEqual(syscallsToRemove, syscallStruct.Names) {
|
||||||
|
@ -42,16 +37,11 @@ func RemoveAllSeccompRules(config *rspec.LinuxSeccomp) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveAllMatchingRules will remove any syscall rules that match the specified action
|
// RemoveAllMatchingRules will remove any syscall rules that match the specified action
|
||||||
func RemoveAllMatchingRules(config *rspec.LinuxSeccomp, action string) error {
|
func RemoveAllMatchingRules(config *rspec.LinuxSeccomp, seccompAction rspec.LinuxSeccompAction) error {
|
||||||
if config == nil {
|
if config == nil {
|
||||||
return fmt.Errorf("Cannot remove action from nil Seccomp pointer")
|
return fmt.Errorf("Cannot remove action from nil Seccomp pointer")
|
||||||
}
|
}
|
||||||
|
|
||||||
seccompAction, err := parseAction(action)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, syscall := range config.Syscalls {
|
for _, syscall := range config.Syscalls {
|
||||||
if reflect.DeepEqual(syscall.Action, seccompAction) {
|
if reflect.DeepEqual(syscall.Action, seccompAction) {
|
||||||
RemoveAction(strings.Join(syscall.Names, ","), config)
|
RemoveAction(strings.Join(syscall.Names, ","), config)
|
||||||
|
|
25
vendor/github.com/opencontainers/runtime-tools/generate/seccomp/seccomp_default.go
generated
vendored
25
vendor/github.com/opencontainers/runtime-tools/generate/seccomp/seccomp_default.go
generated
vendored
|
@ -370,26 +370,25 @@ func DefaultProfile(rs *specs.Spec) *rspec.LinuxSeccomp {
|
||||||
var sysCloneFlagsIndex uint
|
var sysCloneFlagsIndex uint
|
||||||
|
|
||||||
capSysAdmin := false
|
capSysAdmin := false
|
||||||
var cap string
|
caps := make(map[string]bool)
|
||||||
var caps []string
|
|
||||||
|
|
||||||
for _, cap = range rs.Process.Capabilities.Bounding {
|
for _, cap := range rs.Process.Capabilities.Bounding {
|
||||||
caps = append(caps, cap)
|
caps[cap] = true
|
||||||
}
|
}
|
||||||
for _, cap = range rs.Process.Capabilities.Effective {
|
for _, cap := range rs.Process.Capabilities.Effective {
|
||||||
caps = append(caps, cap)
|
caps[cap] = true
|
||||||
}
|
}
|
||||||
for _, cap = range rs.Process.Capabilities.Inheritable {
|
for _, cap := range rs.Process.Capabilities.Inheritable {
|
||||||
caps = append(caps, cap)
|
caps[cap] = true
|
||||||
}
|
}
|
||||||
for _, cap = range rs.Process.Capabilities.Permitted {
|
for _, cap := range rs.Process.Capabilities.Permitted {
|
||||||
caps = append(caps, cap)
|
caps[cap] = true
|
||||||
}
|
}
|
||||||
for _, cap = range rs.Process.Capabilities.Ambient {
|
for _, cap := range rs.Process.Capabilities.Ambient {
|
||||||
caps = append(caps, cap)
|
caps[cap] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, cap = range caps {
|
for cap := range caps {
|
||||||
switch cap {
|
switch cap {
|
||||||
case "CAP_DAC_READ_SEARCH":
|
case "CAP_DAC_READ_SEARCH":
|
||||||
syscalls = append(syscalls, []rspec.LinuxSyscall{
|
syscalls = append(syscalls, []rspec.LinuxSyscall{
|
||||||
|
|
21
vendor/github.com/opencontainers/runtime-tools/generate/spec.go
generated
vendored
21
vendor/github.com/opencontainers/runtime-tools/generate/spec.go
generated
vendored
|
@ -10,6 +10,27 @@ func (g *Generator) initSpec() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *Generator) initSpecProcess() {
|
||||||
|
g.initSpec()
|
||||||
|
if g.spec.Process == nil {
|
||||||
|
g.spec.Process = &rspec.Process{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Generator) initSpecProcessCapabilities() {
|
||||||
|
g.initSpecProcess()
|
||||||
|
if g.spec.Process.Capabilities == nil {
|
||||||
|
g.spec.Process.Capabilities = &rspec.LinuxCapabilities{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Generator) initSpecRoot() {
|
||||||
|
g.initSpec()
|
||||||
|
if g.spec.Root == nil {
|
||||||
|
g.spec.Root = &rspec.Root{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (g *Generator) initSpecAnnotations() {
|
func (g *Generator) initSpecAnnotations() {
|
||||||
g.initSpec()
|
g.initSpec()
|
||||||
if g.spec.Annotations == nil {
|
if g.spec.Annotations == nil {
|
||||||
|
|
138
vendor/github.com/opencontainers/runtime-tools/validate/validate.go
generated
vendored
138
vendor/github.com/opencontainers/runtime-tools/validate/validate.go
generated
vendored
|
@ -9,6 +9,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
@ -47,15 +48,27 @@ type Validator struct {
|
||||||
spec *rspec.Spec
|
spec *rspec.Spec
|
||||||
bundlePath string
|
bundlePath string
|
||||||
HostSpecific bool
|
HostSpecific bool
|
||||||
|
platform string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewValidator creates a Validator
|
// NewValidator creates a Validator
|
||||||
func NewValidator(spec *rspec.Spec, bundlePath string, hostSpecific bool) Validator {
|
func NewValidator(spec *rspec.Spec, bundlePath string, hostSpecific bool, platform string) Validator {
|
||||||
return Validator{spec: spec, bundlePath: bundlePath, HostSpecific: hostSpecific}
|
if hostSpecific && platform != runtime.GOOS {
|
||||||
|
platform = runtime.GOOS
|
||||||
|
}
|
||||||
|
return Validator{
|
||||||
|
spec: spec,
|
||||||
|
bundlePath: bundlePath,
|
||||||
|
HostSpecific: hostSpecific,
|
||||||
|
platform: platform,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewValidatorFromPath creates a Validator with specified bundle path
|
// NewValidatorFromPath creates a Validator with specified bundle path
|
||||||
func NewValidatorFromPath(bundlePath string, hostSpecific bool) (Validator, error) {
|
func NewValidatorFromPath(bundlePath string, hostSpecific bool, platform string) (Validator, error) {
|
||||||
|
if hostSpecific && platform != runtime.GOOS {
|
||||||
|
platform = runtime.GOOS
|
||||||
|
}
|
||||||
if bundlePath == "" {
|
if bundlePath == "" {
|
||||||
return Validator{}, fmt.Errorf("Bundle path shouldn't be empty")
|
return Validator{}, fmt.Errorf("Bundle path shouldn't be empty")
|
||||||
}
|
}
|
||||||
|
@ -77,18 +90,17 @@ func NewValidatorFromPath(bundlePath string, hostSpecific bool) (Validator, erro
|
||||||
return Validator{}, err
|
return Validator{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewValidator(&spec, bundlePath, hostSpecific), nil
|
return NewValidator(&spec, bundlePath, hostSpecific, platform), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckAll checks all parts of runtime bundle
|
// CheckAll checks all parts of runtime bundle
|
||||||
func (v *Validator) CheckAll() (msgs []string) {
|
func (v *Validator) CheckAll() (msgs []string) {
|
||||||
|
msgs = append(msgs, v.CheckPlatform()...)
|
||||||
msgs = append(msgs, v.CheckRootfsPath()...)
|
msgs = append(msgs, v.CheckRootfsPath()...)
|
||||||
msgs = append(msgs, v.CheckMandatoryFields()...)
|
msgs = append(msgs, v.CheckMandatoryFields()...)
|
||||||
msgs = append(msgs, v.CheckSemVer()...)
|
msgs = append(msgs, v.CheckSemVer()...)
|
||||||
msgs = append(msgs, v.CheckMounts()...)
|
msgs = append(msgs, v.CheckMounts()...)
|
||||||
msgs = append(msgs, v.CheckPlatform()...)
|
|
||||||
msgs = append(msgs, v.CheckProcess()...)
|
msgs = append(msgs, v.CheckProcess()...)
|
||||||
msgs = append(msgs, v.CheckOS()...)
|
|
||||||
msgs = append(msgs, v.CheckHooks()...)
|
msgs = append(msgs, v.CheckHooks()...)
|
||||||
if v.spec.Linux != nil {
|
if v.spec.Linux != nil {
|
||||||
msgs = append(msgs, v.CheckLinux()...)
|
msgs = append(msgs, v.CheckLinux()...)
|
||||||
|
@ -131,9 +143,9 @@ func (v *Validator) CheckRootfsPath() (msgs []string) {
|
||||||
msgs = append(msgs, fmt.Sprintf("root.path is %q, but it MUST be a child of %q", v.spec.Root.Path, absBundlePath))
|
msgs = append(msgs, fmt.Sprintf("root.path is %q, but it MUST be a child of %q", v.spec.Root.Path, absBundlePath))
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.spec.Platform.OS == "windows" {
|
if v.platform == "windows" {
|
||||||
if v.spec.Root.Readonly {
|
if v.spec.Root.Readonly {
|
||||||
msgs = append(msgs, "root.readonly field MUST be omitted or false when platform.os is windows")
|
msgs = append(msgs, "root.readonly field MUST be omitted or false when target platform is windows")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,37 +168,6 @@ func (v *Validator) CheckSemVer() (msgs []string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckPlatform checks v.spec.Platform
|
|
||||||
func (v *Validator) CheckPlatform() (msgs []string) {
|
|
||||||
logrus.Debugf("check platform")
|
|
||||||
|
|
||||||
validCombins := map[string][]string{
|
|
||||||
"android": {"arm"},
|
|
||||||
"darwin": {"386", "amd64", "arm", "arm64"},
|
|
||||||
"dragonfly": {"amd64"},
|
|
||||||
"freebsd": {"386", "amd64", "arm"},
|
|
||||||
"linux": {"386", "amd64", "arm", "arm64", "mips", "mips64", "mips64le", "mipsle", "ppc64", "ppc64le", "s390x"},
|
|
||||||
"netbsd": {"386", "amd64", "arm"},
|
|
||||||
"openbsd": {"386", "amd64", "arm"},
|
|
||||||
"plan9": {"386", "amd64"},
|
|
||||||
"solaris": {"amd64"},
|
|
||||||
"windows": {"386", "amd64"}}
|
|
||||||
platform := v.spec.Platform
|
|
||||||
for os, archs := range validCombins {
|
|
||||||
if os == platform.OS {
|
|
||||||
for _, arch := range archs {
|
|
||||||
if arch == platform.Arch {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
msgs = append(msgs, fmt.Sprintf("Combination of %q and %q is invalid.", platform.OS, platform.Arch))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
msgs = append(msgs, fmt.Sprintf("Operation system %q of the bundle is not supported yet.", platform.OS))
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// CheckHooks check v.spec.Hooks
|
// CheckHooks check v.spec.Hooks
|
||||||
func (v *Validator) CheckHooks() (msgs []string) {
|
func (v *Validator) CheckHooks() (msgs []string) {
|
||||||
logrus.Debugf("check hooks")
|
logrus.Debugf("check hooks")
|
||||||
|
@ -271,8 +252,7 @@ func (v *Validator) CheckProcess() (msgs []string) {
|
||||||
}
|
}
|
||||||
msgs = append(msgs, v.CheckRlimits()...)
|
msgs = append(msgs, v.CheckRlimits()...)
|
||||||
|
|
||||||
if v.spec.Platform.OS == "linux" {
|
if v.platform == "linux" {
|
||||||
|
|
||||||
if len(process.ApparmorProfile) > 0 {
|
if len(process.ApparmorProfile) > 0 {
|
||||||
profilePath := filepath.Join(v.bundlePath, v.spec.Root.Path, "/etc/apparmor.d", process.ApparmorProfile)
|
profilePath := filepath.Join(v.bundlePath, v.spec.Root.Path, "/etc/apparmor.d", process.ApparmorProfile)
|
||||||
_, err := os.Stat(profilePath)
|
_, err := os.Stat(profilePath)
|
||||||
|
@ -288,32 +268,55 @@ func (v *Validator) CheckProcess() (msgs []string) {
|
||||||
// CheckCapabilities checks v.spec.Process.Capabilities
|
// CheckCapabilities checks v.spec.Process.Capabilities
|
||||||
func (v *Validator) CheckCapabilities() (msgs []string) {
|
func (v *Validator) CheckCapabilities() (msgs []string) {
|
||||||
process := v.spec.Process
|
process := v.spec.Process
|
||||||
if v.spec.Platform.OS == "linux" {
|
if v.platform == "linux" {
|
||||||
var caps []string
|
var effective, permitted, inheritable, ambient bool
|
||||||
|
caps := make(map[string][]string)
|
||||||
|
|
||||||
for _, cap := range process.Capabilities.Bounding {
|
for _, cap := range process.Capabilities.Bounding {
|
||||||
caps = append(caps, cap)
|
caps[cap] = append(caps[cap], "bounding")
|
||||||
}
|
}
|
||||||
for _, cap := range process.Capabilities.Effective {
|
for _, cap := range process.Capabilities.Effective {
|
||||||
caps = append(caps, cap)
|
caps[cap] = append(caps[cap], "effective")
|
||||||
}
|
}
|
||||||
for _, cap := range process.Capabilities.Inheritable {
|
for _, cap := range process.Capabilities.Inheritable {
|
||||||
caps = append(caps, cap)
|
caps[cap] = append(caps[cap], "inheritable")
|
||||||
}
|
}
|
||||||
for _, cap := range process.Capabilities.Permitted {
|
for _, cap := range process.Capabilities.Permitted {
|
||||||
caps = append(caps, cap)
|
caps[cap] = append(caps[cap], "permitted")
|
||||||
}
|
}
|
||||||
for _, cap := range process.Capabilities.Ambient {
|
for _, cap := range process.Capabilities.Ambient {
|
||||||
caps = append(caps, cap)
|
caps[cap] = append(caps[cap], "ambient")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, capability := range caps {
|
for capability, owns := range caps {
|
||||||
if err := CapValid(capability, v.HostSpecific); err != nil {
|
if err := CapValid(capability, v.HostSpecific); err != nil {
|
||||||
msgs = append(msgs, fmt.Sprintf("capability %q is not valid, man capabilities(7)", capability))
|
msgs = append(msgs, fmt.Sprintf("capability %q is not valid, man capabilities(7)", capability))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
effective, permitted, ambient, inheritable = false, false, false, false
|
||||||
|
for _, set := range owns {
|
||||||
|
if set == "effective" {
|
||||||
|
effective = true
|
||||||
|
}
|
||||||
|
if set == "inheritable" {
|
||||||
|
inheritable = true
|
||||||
|
}
|
||||||
|
if set == "permitted" {
|
||||||
|
permitted = true
|
||||||
|
}
|
||||||
|
if set == "ambient" {
|
||||||
|
ambient = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if effective && !permitted {
|
||||||
|
msgs = append(msgs, fmt.Sprintf("effective capability %q is not allowed, as it's not permitted", capability))
|
||||||
|
}
|
||||||
|
if ambient && !(effective && inheritable) {
|
||||||
|
msgs = append(msgs, fmt.Sprintf("ambient capability %q is not allowed, as it's not permitted and inheribate", capability))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logrus.Warnf("process.capabilities validation not yet implemented for OS %q", v.spec.Platform.OS)
|
logrus.Warnf("process.capabilities validation not yet implemented for OS %q", v.platform)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -379,7 +382,7 @@ func supportedMountTypes(OS string, hostSpecific bool) (map[string]bool, error)
|
||||||
func (v *Validator) CheckMounts() (msgs []string) {
|
func (v *Validator) CheckMounts() (msgs []string) {
|
||||||
logrus.Debugf("check mounts")
|
logrus.Debugf("check mounts")
|
||||||
|
|
||||||
supportedTypes, err := supportedMountTypes(v.spec.Platform.OS, v.HostSpecific)
|
supportedTypes, err := supportedMountTypes(v.platform, v.HostSpecific)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msgs = append(msgs, err.Error())
|
msgs = append(msgs, err.Error())
|
||||||
return
|
return
|
||||||
|
@ -400,25 +403,18 @@ func (v *Validator) CheckMounts() (msgs []string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckOS checks v.spec.Platform.OS
|
// CheckPlatform checks v.platform
|
||||||
func (v *Validator) CheckOS() (msgs []string) {
|
func (v *Validator) CheckPlatform() (msgs []string) {
|
||||||
logrus.Debugf("check os")
|
logrus.Debugf("check platform")
|
||||||
|
|
||||||
if v.spec.Platform.OS != "linux" {
|
if v.platform != "linux" && v.platform != "solaris" && v.platform != "windows" {
|
||||||
if v.spec.Linux != nil {
|
msgs = append(msgs, fmt.Sprintf("platform %q is not supported", v.platform))
|
||||||
msgs = append(msgs, fmt.Sprintf("'linux' MUST NOT be set when platform.os is %q", v.spec.Platform.OS))
|
return
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.spec.Platform.OS != "solaris" {
|
if v.platform == "windows" {
|
||||||
if v.spec.Solaris != nil {
|
if v.spec.Windows == nil {
|
||||||
msgs = append(msgs, fmt.Sprintf("'solaris' MUST NOT be set when platform.os is %q", v.spec.Platform.OS))
|
msgs = append(msgs, "'windows' MUST be set when platform is `windows`")
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.spec.Platform.OS != "windows" {
|
|
||||||
if v.spec.Windows != nil {
|
|
||||||
msgs = append(msgs, fmt.Sprintf("'windows' MUST NOT be set when platform.os is %q", v.spec.Platform.OS))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -479,7 +475,7 @@ func (v *Validator) CheckLinux() (msgs []string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.spec.Platform.OS == "linux" && !typeList[rspec.UTSNamespace].newExist && v.spec.Hostname != "" {
|
if v.platform == "linux" && !typeList[rspec.UTSNamespace].newExist && v.spec.Hostname != "" {
|
||||||
msgs = append(msgs, fmt.Sprintf("On Linux, hostname requires a new UTS namespace to be specified as well"))
|
msgs = append(msgs, fmt.Sprintf("On Linux, hostname requires a new UTS namespace to be specified as well"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -656,12 +652,12 @@ func envValid(env string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Validator) rlimitValid(rlimit rspec.LinuxRlimit) (msgs []string) {
|
func (v *Validator) rlimitValid(rlimit rspec.POSIXRlimit) (msgs []string) {
|
||||||
if rlimit.Hard < rlimit.Soft {
|
if rlimit.Hard < rlimit.Soft {
|
||||||
msgs = append(msgs, fmt.Sprintf("hard limit of rlimit %s should not be less than soft limit", rlimit.Type))
|
msgs = append(msgs, fmt.Sprintf("hard limit of rlimit %s should not be less than soft limit", rlimit.Type))
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.spec.Platform.OS == "linux" {
|
if v.platform == "linux" {
|
||||||
for _, val := range defaultRlimits {
|
for _, val := range defaultRlimits {
|
||||||
if val == rlimit.Type {
|
if val == rlimit.Type {
|
||||||
return
|
return
|
||||||
|
@ -669,7 +665,7 @@ func (v *Validator) rlimitValid(rlimit rspec.LinuxRlimit) (msgs []string) {
|
||||||
}
|
}
|
||||||
msgs = append(msgs, fmt.Sprintf("rlimit type %q is invalid", rlimit.Type))
|
msgs = append(msgs, fmt.Sprintf("rlimit type %q is invalid", rlimit.Type))
|
||||||
} else {
|
} else {
|
||||||
logrus.Warnf("process.rlimits validation not yet implemented for OS %q", v.spec.Platform.OS)
|
logrus.Warnf("process.rlimits validation not yet implemented for platform %q", v.platform)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
351
vendor/golang.org/x/text/internal/gen/code.go
generated
vendored
351
vendor/golang.org/x/text/internal/gen/code.go
generated
vendored
|
@ -1,351 +0,0 @@
|
||||||
// Copyright 2015 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 gen
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/gob"
|
|
||||||
"fmt"
|
|
||||||
"hash"
|
|
||||||
"hash/fnv"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
// This file contains utilities for generating code.
|
|
||||||
|
|
||||||
// TODO: other write methods like:
|
|
||||||
// - slices, maps, types, etc.
|
|
||||||
|
|
||||||
// CodeWriter is a utility for writing structured code. It computes the content
|
|
||||||
// hash and size of written content. It ensures there are newlines between
|
|
||||||
// written code blocks.
|
|
||||||
type CodeWriter struct {
|
|
||||||
buf bytes.Buffer
|
|
||||||
Size int
|
|
||||||
Hash hash.Hash32 // content hash
|
|
||||||
gob *gob.Encoder
|
|
||||||
// For comments we skip the usual one-line separator if they are followed by
|
|
||||||
// a code block.
|
|
||||||
skipSep bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *CodeWriter) Write(p []byte) (n int, err error) {
|
|
||||||
return w.buf.Write(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewCodeWriter returns a new CodeWriter.
|
|
||||||
func NewCodeWriter() *CodeWriter {
|
|
||||||
h := fnv.New32()
|
|
||||||
return &CodeWriter{Hash: h, gob: gob.NewEncoder(h)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteGoFile appends the buffer with the total size of all created structures
|
|
||||||
// and writes it as a Go file to the the given file with the given package name.
|
|
||||||
func (w *CodeWriter) WriteGoFile(filename, pkg string) {
|
|
||||||
f, err := os.Create(filename)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Could not create file %s: %v", filename, err)
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
if _, err = w.WriteGo(f, pkg); err != nil {
|
|
||||||
log.Fatalf("Error writing file %s: %v", filename, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteGo appends the buffer with the total size of all created structures and
|
|
||||||
// writes it as a Go file to the the given writer with the given package name.
|
|
||||||
func (w *CodeWriter) WriteGo(out io.Writer, pkg string) (n int, err error) {
|
|
||||||
sz := w.Size
|
|
||||||
w.WriteComment("Total table size %d bytes (%dKiB); checksum: %X\n", sz, sz/1024, w.Hash.Sum32())
|
|
||||||
defer w.buf.Reset()
|
|
||||||
return WriteGo(out, pkg, w.buf.Bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *CodeWriter) printf(f string, x ...interface{}) {
|
|
||||||
fmt.Fprintf(w, f, x...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *CodeWriter) insertSep() {
|
|
||||||
if w.skipSep {
|
|
||||||
w.skipSep = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Use at least two newlines to ensure a blank space between the previous
|
|
||||||
// block. WriteGoFile will remove extraneous newlines.
|
|
||||||
w.printf("\n\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteComment writes a comment block. All line starts are prefixed with "//".
|
|
||||||
// Initial empty lines are gobbled. The indentation for the first line is
|
|
||||||
// stripped from consecutive lines.
|
|
||||||
func (w *CodeWriter) WriteComment(comment string, args ...interface{}) {
|
|
||||||
s := fmt.Sprintf(comment, args...)
|
|
||||||
s = strings.Trim(s, "\n")
|
|
||||||
|
|
||||||
// Use at least two newlines to ensure a blank space between the previous
|
|
||||||
// block. WriteGoFile will remove extraneous newlines.
|
|
||||||
w.printf("\n\n// ")
|
|
||||||
w.skipSep = true
|
|
||||||
|
|
||||||
// strip first indent level.
|
|
||||||
sep := "\n"
|
|
||||||
for ; len(s) > 0 && (s[0] == '\t' || s[0] == ' '); s = s[1:] {
|
|
||||||
sep += s[:1]
|
|
||||||
}
|
|
||||||
|
|
||||||
strings.NewReplacer(sep, "\n// ", "\n", "\n// ").WriteString(w, s)
|
|
||||||
|
|
||||||
w.printf("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *CodeWriter) writeSizeInfo(size int) {
|
|
||||||
w.printf("// Size: %d bytes\n", size)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteConst writes a constant of the given name and value.
|
|
||||||
func (w *CodeWriter) WriteConst(name string, x interface{}) {
|
|
||||||
w.insertSep()
|
|
||||||
v := reflect.ValueOf(x)
|
|
||||||
|
|
||||||
switch v.Type().Kind() {
|
|
||||||
case reflect.String:
|
|
||||||
w.printf("const %s %s = ", name, typeName(x))
|
|
||||||
w.WriteString(v.String())
|
|
||||||
w.printf("\n")
|
|
||||||
default:
|
|
||||||
w.printf("const %s = %#v\n", name, x)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteVar writes a variable of the given name and value.
|
|
||||||
func (w *CodeWriter) WriteVar(name string, x interface{}) {
|
|
||||||
w.insertSep()
|
|
||||||
v := reflect.ValueOf(x)
|
|
||||||
oldSize := w.Size
|
|
||||||
sz := int(v.Type().Size())
|
|
||||||
w.Size += sz
|
|
||||||
|
|
||||||
switch v.Type().Kind() {
|
|
||||||
case reflect.String:
|
|
||||||
w.printf("var %s %s = ", name, typeName(x))
|
|
||||||
w.WriteString(v.String())
|
|
||||||
case reflect.Struct:
|
|
||||||
w.gob.Encode(x)
|
|
||||||
fallthrough
|
|
||||||
case reflect.Slice, reflect.Array:
|
|
||||||
w.printf("var %s = ", name)
|
|
||||||
w.writeValue(v)
|
|
||||||
w.writeSizeInfo(w.Size - oldSize)
|
|
||||||
default:
|
|
||||||
w.printf("var %s %s = ", name, typeName(x))
|
|
||||||
w.gob.Encode(x)
|
|
||||||
w.writeValue(v)
|
|
||||||
w.writeSizeInfo(w.Size - oldSize)
|
|
||||||
}
|
|
||||||
w.printf("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *CodeWriter) writeValue(v reflect.Value) {
|
|
||||||
x := v.Interface()
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.String:
|
|
||||||
w.WriteString(v.String())
|
|
||||||
case reflect.Array:
|
|
||||||
// Don't double count: callers of WriteArray count on the size being
|
|
||||||
// added, so we need to discount it here.
|
|
||||||
w.Size -= int(v.Type().Size())
|
|
||||||
w.writeSlice(x, true)
|
|
||||||
case reflect.Slice:
|
|
||||||
w.writeSlice(x, false)
|
|
||||||
case reflect.Struct:
|
|
||||||
w.printf("%s{\n", typeName(v.Interface()))
|
|
||||||
t := v.Type()
|
|
||||||
for i := 0; i < v.NumField(); i++ {
|
|
||||||
w.printf("%s: ", t.Field(i).Name)
|
|
||||||
w.writeValue(v.Field(i))
|
|
||||||
w.printf(",\n")
|
|
||||||
}
|
|
||||||
w.printf("}")
|
|
||||||
default:
|
|
||||||
w.printf("%#v", x)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteString writes a string literal.
|
|
||||||
func (w *CodeWriter) WriteString(s string) {
|
|
||||||
s = strings.Replace(s, `\`, `\\`, -1)
|
|
||||||
io.WriteString(w.Hash, s) // content hash
|
|
||||||
w.Size += len(s)
|
|
||||||
|
|
||||||
const maxInline = 40
|
|
||||||
if len(s) <= maxInline {
|
|
||||||
w.printf("%q", s)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// We will render the string as a multi-line string.
|
|
||||||
const maxWidth = 80 - 4 - len(`"`) - len(`" +`)
|
|
||||||
|
|
||||||
// When starting on its own line, go fmt indents line 2+ an extra level.
|
|
||||||
n, max := maxWidth, maxWidth-4
|
|
||||||
|
|
||||||
// As per https://golang.org/issue/18078, the compiler has trouble
|
|
||||||
// compiling the concatenation of many strings, s0 + s1 + s2 + ... + sN,
|
|
||||||
// for large N. We insert redundant, explicit parentheses to work around
|
|
||||||
// that, lowering the N at any given step: (s0 + s1 + ... + s63) + (s64 +
|
|
||||||
// ... + s127) + etc + (etc + ... + sN).
|
|
||||||
explicitParens, extraComment := len(s) > 128*1024, ""
|
|
||||||
if explicitParens {
|
|
||||||
w.printf(`(`)
|
|
||||||
extraComment = "; the redundant, explicit parens are for https://golang.org/issue/18078"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print "" +\n, if a string does not start on its own line.
|
|
||||||
b := w.buf.Bytes()
|
|
||||||
if p := len(bytes.TrimRight(b, " \t")); p > 0 && b[p-1] != '\n' {
|
|
||||||
w.printf("\"\" + // Size: %d bytes%s\n", len(s), extraComment)
|
|
||||||
n, max = maxWidth, maxWidth
|
|
||||||
}
|
|
||||||
|
|
||||||
w.printf(`"`)
|
|
||||||
|
|
||||||
for sz, p, nLines := 0, 0, 0; p < len(s); {
|
|
||||||
var r rune
|
|
||||||
r, sz = utf8.DecodeRuneInString(s[p:])
|
|
||||||
out := s[p : p+sz]
|
|
||||||
chars := 1
|
|
||||||
if !unicode.IsPrint(r) || r == utf8.RuneError || r == '"' {
|
|
||||||
switch sz {
|
|
||||||
case 1:
|
|
||||||
out = fmt.Sprintf("\\x%02x", s[p])
|
|
||||||
case 2, 3:
|
|
||||||
out = fmt.Sprintf("\\u%04x", r)
|
|
||||||
case 4:
|
|
||||||
out = fmt.Sprintf("\\U%08x", r)
|
|
||||||
}
|
|
||||||
chars = len(out)
|
|
||||||
}
|
|
||||||
if n -= chars; n < 0 {
|
|
||||||
nLines++
|
|
||||||
if explicitParens && nLines&63 == 63 {
|
|
||||||
w.printf("\") + (\"")
|
|
||||||
}
|
|
||||||
w.printf("\" +\n\"")
|
|
||||||
n = max - len(out)
|
|
||||||
}
|
|
||||||
w.printf("%s", out)
|
|
||||||
p += sz
|
|
||||||
}
|
|
||||||
w.printf(`"`)
|
|
||||||
if explicitParens {
|
|
||||||
w.printf(`)`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteSlice writes a slice value.
|
|
||||||
func (w *CodeWriter) WriteSlice(x interface{}) {
|
|
||||||
w.writeSlice(x, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteArray writes an array value.
|
|
||||||
func (w *CodeWriter) WriteArray(x interface{}) {
|
|
||||||
w.writeSlice(x, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *CodeWriter) writeSlice(x interface{}, isArray bool) {
|
|
||||||
v := reflect.ValueOf(x)
|
|
||||||
w.gob.Encode(v.Len())
|
|
||||||
w.Size += v.Len() * int(v.Type().Elem().Size())
|
|
||||||
name := typeName(x)
|
|
||||||
if isArray {
|
|
||||||
name = fmt.Sprintf("[%d]%s", v.Len(), name[strings.Index(name, "]")+1:])
|
|
||||||
}
|
|
||||||
if isArray {
|
|
||||||
w.printf("%s{\n", name)
|
|
||||||
} else {
|
|
||||||
w.printf("%s{ // %d elements\n", name, v.Len())
|
|
||||||
}
|
|
||||||
|
|
||||||
switch kind := v.Type().Elem().Kind(); kind {
|
|
||||||
case reflect.String:
|
|
||||||
for _, s := range x.([]string) {
|
|
||||||
w.WriteString(s)
|
|
||||||
w.printf(",\n")
|
|
||||||
}
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
|
||||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
||||||
// nLine and nBlock are the number of elements per line and block.
|
|
||||||
nLine, nBlock, format := 8, 64, "%d,"
|
|
||||||
switch kind {
|
|
||||||
case reflect.Uint8:
|
|
||||||
format = "%#02x,"
|
|
||||||
case reflect.Uint16:
|
|
||||||
format = "%#04x,"
|
|
||||||
case reflect.Uint32:
|
|
||||||
nLine, nBlock, format = 4, 32, "%#08x,"
|
|
||||||
case reflect.Uint, reflect.Uint64:
|
|
||||||
nLine, nBlock, format = 4, 32, "%#016x,"
|
|
||||||
case reflect.Int8:
|
|
||||||
nLine = 16
|
|
||||||
}
|
|
||||||
n := nLine
|
|
||||||
for i := 0; i < v.Len(); i++ {
|
|
||||||
if i%nBlock == 0 && v.Len() > nBlock {
|
|
||||||
w.printf("// Entry %X - %X\n", i, i+nBlock-1)
|
|
||||||
}
|
|
||||||
x := v.Index(i).Interface()
|
|
||||||
w.gob.Encode(x)
|
|
||||||
w.printf(format, x)
|
|
||||||
if n--; n == 0 {
|
|
||||||
n = nLine
|
|
||||||
w.printf("\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
w.printf("\n")
|
|
||||||
case reflect.Struct:
|
|
||||||
zero := reflect.Zero(v.Type().Elem()).Interface()
|
|
||||||
for i := 0; i < v.Len(); i++ {
|
|
||||||
x := v.Index(i).Interface()
|
|
||||||
w.gob.EncodeValue(v)
|
|
||||||
if !reflect.DeepEqual(zero, x) {
|
|
||||||
line := fmt.Sprintf("%#v,\n", x)
|
|
||||||
line = line[strings.IndexByte(line, '{'):]
|
|
||||||
w.printf("%d: ", i)
|
|
||||||
w.printf(line)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case reflect.Array:
|
|
||||||
for i := 0; i < v.Len(); i++ {
|
|
||||||
w.printf("%d: %#v,\n", i, v.Index(i).Interface())
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
panic("gen: slice elem type not supported")
|
|
||||||
}
|
|
||||||
w.printf("}")
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteType writes a definition of the type of the given value and returns the
|
|
||||||
// type name.
|
|
||||||
func (w *CodeWriter) WriteType(x interface{}) string {
|
|
||||||
t := reflect.TypeOf(x)
|
|
||||||
w.printf("type %s struct {\n", t.Name())
|
|
||||||
for i := 0; i < t.NumField(); i++ {
|
|
||||||
w.printf("\t%s %s\n", t.Field(i).Name, t.Field(i).Type)
|
|
||||||
}
|
|
||||||
w.printf("}\n")
|
|
||||||
return t.Name()
|
|
||||||
}
|
|
||||||
|
|
||||||
// typeName returns the name of the go type of x.
|
|
||||||
func typeName(x interface{}) string {
|
|
||||||
t := reflect.ValueOf(x).Type()
|
|
||||||
return strings.Replace(fmt.Sprint(t), "main.", "", 1)
|
|
||||||
}
|
|
281
vendor/golang.org/x/text/internal/gen/gen.go
generated
vendored
281
vendor/golang.org/x/text/internal/gen/gen.go
generated
vendored
|
@ -1,281 +0,0 @@
|
||||||
// Copyright 2015 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 gen contains common code for the various code generation tools in the
|
|
||||||
// text repository. Its usage ensures consistency between tools.
|
|
||||||
//
|
|
||||||
// This package defines command line flags that are common to most generation
|
|
||||||
// tools. The flags allow for specifying specific Unicode and CLDR versions
|
|
||||||
// in the public Unicode data repository (http://www.unicode.org/Public).
|
|
||||||
//
|
|
||||||
// A local Unicode data mirror can be set through the flag -local or the
|
|
||||||
// environment variable UNICODE_DIR. The former takes precedence. The local
|
|
||||||
// directory should follow the same structure as the public repository.
|
|
||||||
//
|
|
||||||
// IANA data can also optionally be mirrored by putting it in the iana directory
|
|
||||||
// rooted at the top of the local mirror. Beware, though, that IANA data is not
|
|
||||||
// versioned. So it is up to the developer to use the right version.
|
|
||||||
package gen // import "golang.org/x/text/internal/gen"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"go/build"
|
|
||||||
"go/format"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"sync"
|
|
||||||
"unicode"
|
|
||||||
|
|
||||||
"golang.org/x/text/unicode/cldr"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
url = flag.String("url",
|
|
||||||
"http://www.unicode.org/Public",
|
|
||||||
"URL of Unicode database directory")
|
|
||||||
iana = flag.String("iana",
|
|
||||||
"http://www.iana.org",
|
|
||||||
"URL of the IANA repository")
|
|
||||||
unicodeVersion = flag.String("unicode",
|
|
||||||
getEnv("UNICODE_VERSION", unicode.Version),
|
|
||||||
"unicode version to use")
|
|
||||||
cldrVersion = flag.String("cldr",
|
|
||||||
getEnv("CLDR_VERSION", cldr.Version),
|
|
||||||
"cldr version to use")
|
|
||||||
)
|
|
||||||
|
|
||||||
func getEnv(name, def string) string {
|
|
||||||
if v := os.Getenv(name); v != "" {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
return def
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init performs common initialization for a gen command. It parses the flags
|
|
||||||
// and sets up the standard logging parameters.
|
|
||||||
func Init() {
|
|
||||||
log.SetPrefix("")
|
|
||||||
log.SetFlags(log.Lshortfile)
|
|
||||||
flag.Parse()
|
|
||||||
}
|
|
||||||
|
|
||||||
const header = `// This file was generated by go generate; DO NOT EDIT
|
|
||||||
|
|
||||||
package %s
|
|
||||||
|
|
||||||
`
|
|
||||||
|
|
||||||
// UnicodeVersion reports the requested Unicode version.
|
|
||||||
func UnicodeVersion() string {
|
|
||||||
return *unicodeVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnicodeVersion reports the requested CLDR version.
|
|
||||||
func CLDRVersion() string {
|
|
||||||
return *cldrVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsLocal reports whether data files are available locally.
|
|
||||||
func IsLocal() bool {
|
|
||||||
dir, err := localReadmeFile()
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if _, err = os.Stat(dir); err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// OpenUCDFile opens the requested UCD file. The file is specified relative to
|
|
||||||
// the public Unicode root directory. It will call log.Fatal if there are any
|
|
||||||
// errors.
|
|
||||||
func OpenUCDFile(file string) io.ReadCloser {
|
|
||||||
return openUnicode(path.Join(*unicodeVersion, "ucd", file))
|
|
||||||
}
|
|
||||||
|
|
||||||
// OpenCLDRCoreZip opens the CLDR core zip file. It will call log.Fatal if there
|
|
||||||
// are any errors.
|
|
||||||
func OpenCLDRCoreZip() io.ReadCloser {
|
|
||||||
return OpenUnicodeFile("cldr", *cldrVersion, "core.zip")
|
|
||||||
}
|
|
||||||
|
|
||||||
// OpenUnicodeFile opens the requested file of the requested category from the
|
|
||||||
// root of the Unicode data archive. The file is specified relative to the
|
|
||||||
// public Unicode root directory. If version is "", it will use the default
|
|
||||||
// Unicode version. It will call log.Fatal if there are any errors.
|
|
||||||
func OpenUnicodeFile(category, version, file string) io.ReadCloser {
|
|
||||||
if version == "" {
|
|
||||||
version = UnicodeVersion()
|
|
||||||
}
|
|
||||||
return openUnicode(path.Join(category, version, file))
|
|
||||||
}
|
|
||||||
|
|
||||||
// OpenIANAFile opens the requested IANA file. The file is specified relative
|
|
||||||
// to the IANA root, which is typically either http://www.iana.org or the
|
|
||||||
// iana directory in the local mirror. It will call log.Fatal if there are any
|
|
||||||
// errors.
|
|
||||||
func OpenIANAFile(path string) io.ReadCloser {
|
|
||||||
return Open(*iana, "iana", path)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
dirMutex sync.Mutex
|
|
||||||
localDir string
|
|
||||||
)
|
|
||||||
|
|
||||||
const permissions = 0755
|
|
||||||
|
|
||||||
func localReadmeFile() (string, error) {
|
|
||||||
p, err := build.Import("golang.org/x/text", "", build.FindOnly)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("Could not locate package: %v", err)
|
|
||||||
}
|
|
||||||
return filepath.Join(p.Dir, "DATA", "README"), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getLocalDir() string {
|
|
||||||
dirMutex.Lock()
|
|
||||||
defer dirMutex.Unlock()
|
|
||||||
|
|
||||||
readme, err := localReadmeFile()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
dir := filepath.Dir(readme)
|
|
||||||
if _, err := os.Stat(readme); err != nil {
|
|
||||||
if err := os.MkdirAll(dir, permissions); err != nil {
|
|
||||||
log.Fatalf("Could not create directory: %v", err)
|
|
||||||
}
|
|
||||||
ioutil.WriteFile(readme, []byte(readmeTxt), permissions)
|
|
||||||
}
|
|
||||||
return dir
|
|
||||||
}
|
|
||||||
|
|
||||||
const readmeTxt = `Generated by golang.org/x/text/internal/gen. DO NOT EDIT.
|
|
||||||
|
|
||||||
This directory contains downloaded files used to generate the various tables
|
|
||||||
in the golang.org/x/text subrepo.
|
|
||||||
|
|
||||||
Note that the language subtag repo (iana/assignments/language-subtag-registry)
|
|
||||||
and all other times in the iana subdirectory are not versioned and will need
|
|
||||||
to be periodically manually updated. The easiest way to do this is to remove
|
|
||||||
the entire iana directory. This is mostly of concern when updating the language
|
|
||||||
package.
|
|
||||||
`
|
|
||||||
|
|
||||||
// Open opens subdir/path if a local directory is specified and the file exists,
|
|
||||||
// where subdir is a directory relative to the local root, or fetches it from
|
|
||||||
// urlRoot/path otherwise. It will call log.Fatal if there are any errors.
|
|
||||||
func Open(urlRoot, subdir, path string) io.ReadCloser {
|
|
||||||
file := filepath.Join(getLocalDir(), subdir, filepath.FromSlash(path))
|
|
||||||
return open(file, urlRoot, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func openUnicode(path string) io.ReadCloser {
|
|
||||||
file := filepath.Join(getLocalDir(), filepath.FromSlash(path))
|
|
||||||
return open(file, *url, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: automatically periodically update non-versioned files.
|
|
||||||
|
|
||||||
func open(file, urlRoot, path string) io.ReadCloser {
|
|
||||||
if f, err := os.Open(file); err == nil {
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
r := get(urlRoot, path)
|
|
||||||
defer r.Close()
|
|
||||||
b, err := ioutil.ReadAll(r)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Could not download file: %v", err)
|
|
||||||
}
|
|
||||||
os.MkdirAll(filepath.Dir(file), permissions)
|
|
||||||
if err := ioutil.WriteFile(file, b, permissions); err != nil {
|
|
||||||
log.Fatalf("Could not create file: %v", err)
|
|
||||||
}
|
|
||||||
return ioutil.NopCloser(bytes.NewReader(b))
|
|
||||||
}
|
|
||||||
|
|
||||||
func get(root, path string) io.ReadCloser {
|
|
||||||
url := root + "/" + path
|
|
||||||
fmt.Printf("Fetching %s...", url)
|
|
||||||
defer fmt.Println(" done.")
|
|
||||||
resp, err := http.Get(url)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("HTTP GET: %v", err)
|
|
||||||
}
|
|
||||||
if resp.StatusCode != 200 {
|
|
||||||
log.Fatalf("Bad GET status for %q: %q", url, resp.Status)
|
|
||||||
}
|
|
||||||
return resp.Body
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: use Write*Version in all applicable packages.
|
|
||||||
|
|
||||||
// WriteUnicodeVersion writes a constant for the Unicode version from which the
|
|
||||||
// tables are generated.
|
|
||||||
func WriteUnicodeVersion(w io.Writer) {
|
|
||||||
fmt.Fprintf(w, "// UnicodeVersion is the Unicode version from which the tables in this package are derived.\n")
|
|
||||||
fmt.Fprintf(w, "const UnicodeVersion = %q\n\n", UnicodeVersion())
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteCLDRVersion writes a constant for the CLDR version from which the
|
|
||||||
// tables are generated.
|
|
||||||
func WriteCLDRVersion(w io.Writer) {
|
|
||||||
fmt.Fprintf(w, "// CLDRVersion is the CLDR version from which the tables in this package are derived.\n")
|
|
||||||
fmt.Fprintf(w, "const CLDRVersion = %q\n\n", CLDRVersion())
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteGoFile prepends a standard file comment and package statement to the
|
|
||||||
// given bytes, applies gofmt, and writes them to a file with the given name.
|
|
||||||
// It will call log.Fatal if there are any errors.
|
|
||||||
func WriteGoFile(filename, pkg string, b []byte) {
|
|
||||||
w, err := os.Create(filename)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Could not create file %s: %v", filename, err)
|
|
||||||
}
|
|
||||||
defer w.Close()
|
|
||||||
if _, err = WriteGo(w, pkg, b); err != nil {
|
|
||||||
log.Fatalf("Error writing file %s: %v", filename, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteGo prepends a standard file comment and package statement to the given
|
|
||||||
// bytes, applies gofmt, and writes them to w.
|
|
||||||
func WriteGo(w io.Writer, pkg string, b []byte) (n int, err error) {
|
|
||||||
src := []byte(fmt.Sprintf(header, pkg))
|
|
||||||
src = append(src, b...)
|
|
||||||
formatted, err := format.Source(src)
|
|
||||||
if err != nil {
|
|
||||||
// Print the generated code even in case of an error so that the
|
|
||||||
// returned error can be meaningfully interpreted.
|
|
||||||
n, _ = w.Write(src)
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
return w.Write(formatted)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Repackage rewrites a Go file from belonging to package main to belonging to
|
|
||||||
// the given package.
|
|
||||||
func Repackage(inFile, outFile, pkg string) {
|
|
||||||
src, err := ioutil.ReadFile(inFile)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("reading %s: %v", inFile, err)
|
|
||||||
}
|
|
||||||
const toDelete = "package main\n\n"
|
|
||||||
i := bytes.Index(src, []byte(toDelete))
|
|
||||||
if i < 0 {
|
|
||||||
log.Fatalf("Could not find %q in %s.", toDelete, inFile)
|
|
||||||
}
|
|
||||||
w := &bytes.Buffer{}
|
|
||||||
w.Write(src[i+len(toDelete):])
|
|
||||||
WriteGoFile(outFile, pkg, w.Bytes())
|
|
||||||
}
|
|
58
vendor/golang.org/x/text/internal/triegen/compact.go
generated
vendored
58
vendor/golang.org/x/text/internal/triegen/compact.go
generated
vendored
|
@ -1,58 +0,0 @@
|
||||||
// Copyright 2014 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 triegen
|
|
||||||
|
|
||||||
// This file defines Compacter and its implementations.
|
|
||||||
|
|
||||||
import "io"
|
|
||||||
|
|
||||||
// A Compacter generates an alternative, more space-efficient way to store a
|
|
||||||
// trie value block. A trie value block holds all possible values for the last
|
|
||||||
// byte of a UTF-8 encoded rune. Excluding ASCII characters, a trie value block
|
|
||||||
// always has 64 values, as a UTF-8 encoding ends with a byte in [0x80, 0xC0).
|
|
||||||
type Compacter interface {
|
|
||||||
// Size returns whether the Compacter could encode the given block as well
|
|
||||||
// as its size in case it can. len(v) is always 64.
|
|
||||||
Size(v []uint64) (sz int, ok bool)
|
|
||||||
|
|
||||||
// Store stores the block using the Compacter's compression method.
|
|
||||||
// It returns a handle with which the block can be retrieved.
|
|
||||||
// len(v) is always 64.
|
|
||||||
Store(v []uint64) uint32
|
|
||||||
|
|
||||||
// Print writes the data structures associated to the given store to w.
|
|
||||||
Print(w io.Writer) error
|
|
||||||
|
|
||||||
// Handler returns the name of a function that gets called during trie
|
|
||||||
// lookup for blocks generated by the Compacter. The function should be of
|
|
||||||
// the form func (n uint32, b byte) uint64, where n is the index returned by
|
|
||||||
// the Compacter's Store method and b is the last byte of the UTF-8
|
|
||||||
// encoding, where 0x80 <= b < 0xC0, for which to do the lookup in the
|
|
||||||
// block.
|
|
||||||
Handler() string
|
|
||||||
}
|
|
||||||
|
|
||||||
// simpleCompacter is the default Compacter used by builder. It implements a
|
|
||||||
// normal trie block.
|
|
||||||
type simpleCompacter builder
|
|
||||||
|
|
||||||
func (b *simpleCompacter) Size([]uint64) (sz int, ok bool) {
|
|
||||||
return blockSize * b.ValueSize, true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *simpleCompacter) Store(v []uint64) uint32 {
|
|
||||||
h := uint32(len(b.ValueBlocks) - blockOffset)
|
|
||||||
b.ValueBlocks = append(b.ValueBlocks, v)
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *simpleCompacter) Print(io.Writer) error {
|
|
||||||
// Structures are printed in print.go.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *simpleCompacter) Handler() string {
|
|
||||||
panic("Handler should be special-cased for this Compacter")
|
|
||||||
}
|
|
251
vendor/golang.org/x/text/internal/triegen/print.go
generated
vendored
251
vendor/golang.org/x/text/internal/triegen/print.go
generated
vendored
|
@ -1,251 +0,0 @@
|
||||||
// Copyright 2014 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 triegen
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"strings"
|
|
||||||
"text/template"
|
|
||||||
)
|
|
||||||
|
|
||||||
// print writes all the data structures as well as the code necessary to use the
|
|
||||||
// trie to w.
|
|
||||||
func (b *builder) print(w io.Writer) error {
|
|
||||||
b.Stats.NValueEntries = len(b.ValueBlocks) * blockSize
|
|
||||||
b.Stats.NValueBytes = len(b.ValueBlocks) * blockSize * b.ValueSize
|
|
||||||
b.Stats.NIndexEntries = len(b.IndexBlocks) * blockSize
|
|
||||||
b.Stats.NIndexBytes = len(b.IndexBlocks) * blockSize * b.IndexSize
|
|
||||||
b.Stats.NHandleBytes = len(b.Trie) * 2 * b.IndexSize
|
|
||||||
|
|
||||||
// If we only have one root trie, all starter blocks are at position 0 and
|
|
||||||
// we can access the arrays directly.
|
|
||||||
if len(b.Trie) == 1 {
|
|
||||||
// At this point we cannot refer to the generated tables directly.
|
|
||||||
b.ASCIIBlock = b.Name + "Values"
|
|
||||||
b.StarterBlock = b.Name + "Index"
|
|
||||||
} else {
|
|
||||||
// Otherwise we need to have explicit starter indexes in the trie
|
|
||||||
// structure.
|
|
||||||
b.ASCIIBlock = "t.ascii"
|
|
||||||
b.StarterBlock = "t.utf8Start"
|
|
||||||
}
|
|
||||||
|
|
||||||
b.SourceType = "[]byte"
|
|
||||||
if err := lookupGen.Execute(w, b); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
b.SourceType = "string"
|
|
||||||
if err := lookupGen.Execute(w, b); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := trieGen.Execute(w, b); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range b.Compactions {
|
|
||||||
if err := c.c.Print(w); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func printValues(n int, values []uint64) string {
|
|
||||||
w := &bytes.Buffer{}
|
|
||||||
boff := n * blockSize
|
|
||||||
fmt.Fprintf(w, "\t// Block %#x, offset %#x", n, boff)
|
|
||||||
var newline bool
|
|
||||||
for i, v := range values {
|
|
||||||
if i%6 == 0 {
|
|
||||||
newline = true
|
|
||||||
}
|
|
||||||
if v != 0 {
|
|
||||||
if newline {
|
|
||||||
fmt.Fprintf(w, "\n")
|
|
||||||
newline = false
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, "\t%#02x:%#04x, ", boff+i, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return w.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func printIndex(b *builder, nr int, n *node) string {
|
|
||||||
w := &bytes.Buffer{}
|
|
||||||
boff := nr * blockSize
|
|
||||||
fmt.Fprintf(w, "\t// Block %#x, offset %#x", nr, boff)
|
|
||||||
var newline bool
|
|
||||||
for i, c := range n.children {
|
|
||||||
if i%8 == 0 {
|
|
||||||
newline = true
|
|
||||||
}
|
|
||||||
if c != nil {
|
|
||||||
v := b.Compactions[c.index.compaction].Offset + uint32(c.index.index)
|
|
||||||
if v != 0 {
|
|
||||||
if newline {
|
|
||||||
fmt.Fprintf(w, "\n")
|
|
||||||
newline = false
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, "\t%#02x:%#02x, ", boff+i, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return w.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
trieGen = template.Must(template.New("trie").Funcs(template.FuncMap{
|
|
||||||
"printValues": printValues,
|
|
||||||
"printIndex": printIndex,
|
|
||||||
"title": strings.Title,
|
|
||||||
"dec": func(x int) int { return x - 1 },
|
|
||||||
"psize": func(n int) string {
|
|
||||||
return fmt.Sprintf("%d bytes (%.2f KiB)", n, float64(n)/1024)
|
|
||||||
},
|
|
||||||
}).Parse(trieTemplate))
|
|
||||||
lookupGen = template.Must(template.New("lookup").Parse(lookupTemplate))
|
|
||||||
)
|
|
||||||
|
|
||||||
// TODO: consider the return type of lookup. It could be uint64, even if the
|
|
||||||
// internal value type is smaller. We will have to verify this with the
|
|
||||||
// performance of unicode/norm, which is very sensitive to such changes.
|
|
||||||
const trieTemplate = `{{$b := .}}{{$multi := gt (len .Trie) 1}}
|
|
||||||
// {{.Name}}Trie. Total size: {{psize .Size}}. Checksum: {{printf "%08x" .Checksum}}.
|
|
||||||
type {{.Name}}Trie struct { {{if $multi}}
|
|
||||||
ascii []{{.ValueType}} // index for ASCII bytes
|
|
||||||
utf8Start []{{.IndexType}} // index for UTF-8 bytes >= 0xC0
|
|
||||||
{{end}}}
|
|
||||||
|
|
||||||
func new{{title .Name}}Trie(i int) *{{.Name}}Trie { {{if $multi}}
|
|
||||||
h := {{.Name}}TrieHandles[i]
|
|
||||||
return &{{.Name}}Trie{ {{.Name}}Values[uint32(h.ascii)<<6:], {{.Name}}Index[uint32(h.multi)<<6:] }
|
|
||||||
}
|
|
||||||
|
|
||||||
type {{.Name}}TrieHandle struct {
|
|
||||||
ascii, multi {{.IndexType}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// {{.Name}}TrieHandles: {{len .Trie}} handles, {{.Stats.NHandleBytes}} bytes
|
|
||||||
var {{.Name}}TrieHandles = [{{len .Trie}}]{{.Name}}TrieHandle{
|
|
||||||
{{range .Trie}} { {{.ASCIIIndex}}, {{.StarterIndex}} }, // {{printf "%08x" .Checksum}}: {{.Name}}
|
|
||||||
{{end}}}{{else}}
|
|
||||||
return &{{.Name}}Trie{}
|
|
||||||
}
|
|
||||||
{{end}}
|
|
||||||
// lookupValue determines the type of block n and looks up the value for b.
|
|
||||||
func (t *{{.Name}}Trie) lookupValue(n uint32, b byte) {{.ValueType}}{{$last := dec (len .Compactions)}} {
|
|
||||||
switch { {{range $i, $c := .Compactions}}
|
|
||||||
{{if eq $i $last}}default{{else}}case n < {{$c.Cutoff}}{{end}}:{{if ne $i 0}}
|
|
||||||
n -= {{$c.Offset}}{{end}}
|
|
||||||
return {{print $b.ValueType}}({{$c.Handler}}){{end}}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// {{.Name}}Values: {{len .ValueBlocks}} blocks, {{.Stats.NValueEntries}} entries, {{.Stats.NValueBytes}} bytes
|
|
||||||
// The third block is the zero block.
|
|
||||||
var {{.Name}}Values = [{{.Stats.NValueEntries}}]{{.ValueType}} {
|
|
||||||
{{range $i, $v := .ValueBlocks}}{{printValues $i $v}}
|
|
||||||
{{end}}}
|
|
||||||
|
|
||||||
// {{.Name}}Index: {{len .IndexBlocks}} blocks, {{.Stats.NIndexEntries}} entries, {{.Stats.NIndexBytes}} bytes
|
|
||||||
// Block 0 is the zero block.
|
|
||||||
var {{.Name}}Index = [{{.Stats.NIndexEntries}}]{{.IndexType}} {
|
|
||||||
{{range $i, $v := .IndexBlocks}}{{printIndex $b $i $v}}
|
|
||||||
{{end}}}
|
|
||||||
`
|
|
||||||
|
|
||||||
// TODO: consider allowing zero-length strings after evaluating performance with
|
|
||||||
// unicode/norm.
|
|
||||||
const lookupTemplate = `
|
|
||||||
// lookup{{if eq .SourceType "string"}}String{{end}} returns the trie value for the first UTF-8 encoding in s and
|
|
||||||
// the width in bytes of this encoding. The size will be 0 if s does not
|
|
||||||
// hold enough bytes to complete the encoding. len(s) must be greater than 0.
|
|
||||||
func (t *{{.Name}}Trie) lookup{{if eq .SourceType "string"}}String{{end}}(s {{.SourceType}}) (v {{.ValueType}}, sz int) {
|
|
||||||
c0 := s[0]
|
|
||||||
switch {
|
|
||||||
case c0 < 0x80: // is ASCII
|
|
||||||
return {{.ASCIIBlock}}[c0], 1
|
|
||||||
case c0 < 0xC2:
|
|
||||||
return 0, 1 // Illegal UTF-8: not a starter, not ASCII.
|
|
||||||
case c0 < 0xE0: // 2-byte UTF-8
|
|
||||||
if len(s) < 2 {
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
i := {{.StarterBlock}}[c0]
|
|
||||||
c1 := s[1]
|
|
||||||
if c1 < 0x80 || 0xC0 <= c1 {
|
|
||||||
return 0, 1 // Illegal UTF-8: not a continuation byte.
|
|
||||||
}
|
|
||||||
return t.lookupValue(uint32(i), c1), 2
|
|
||||||
case c0 < 0xF0: // 3-byte UTF-8
|
|
||||||
if len(s) < 3 {
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
i := {{.StarterBlock}}[c0]
|
|
||||||
c1 := s[1]
|
|
||||||
if c1 < 0x80 || 0xC0 <= c1 {
|
|
||||||
return 0, 1 // Illegal UTF-8: not a continuation byte.
|
|
||||||
}
|
|
||||||
o := uint32(i)<<6 + uint32(c1)
|
|
||||||
i = {{.Name}}Index[o]
|
|
||||||
c2 := s[2]
|
|
||||||
if c2 < 0x80 || 0xC0 <= c2 {
|
|
||||||
return 0, 2 // Illegal UTF-8: not a continuation byte.
|
|
||||||
}
|
|
||||||
return t.lookupValue(uint32(i), c2), 3
|
|
||||||
case c0 < 0xF8: // 4-byte UTF-8
|
|
||||||
if len(s) < 4 {
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
i := {{.StarterBlock}}[c0]
|
|
||||||
c1 := s[1]
|
|
||||||
if c1 < 0x80 || 0xC0 <= c1 {
|
|
||||||
return 0, 1 // Illegal UTF-8: not a continuation byte.
|
|
||||||
}
|
|
||||||
o := uint32(i)<<6 + uint32(c1)
|
|
||||||
i = {{.Name}}Index[o]
|
|
||||||
c2 := s[2]
|
|
||||||
if c2 < 0x80 || 0xC0 <= c2 {
|
|
||||||
return 0, 2 // Illegal UTF-8: not a continuation byte.
|
|
||||||
}
|
|
||||||
o = uint32(i)<<6 + uint32(c2)
|
|
||||||
i = {{.Name}}Index[o]
|
|
||||||
c3 := s[3]
|
|
||||||
if c3 < 0x80 || 0xC0 <= c3 {
|
|
||||||
return 0, 3 // Illegal UTF-8: not a continuation byte.
|
|
||||||
}
|
|
||||||
return t.lookupValue(uint32(i), c3), 4
|
|
||||||
}
|
|
||||||
// Illegal rune
|
|
||||||
return 0, 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// lookup{{if eq .SourceType "string"}}String{{end}}Unsafe returns the trie value for the first UTF-8 encoding in s.
|
|
||||||
// s must start with a full and valid UTF-8 encoded rune.
|
|
||||||
func (t *{{.Name}}Trie) lookup{{if eq .SourceType "string"}}String{{end}}Unsafe(s {{.SourceType}}) {{.ValueType}} {
|
|
||||||
c0 := s[0]
|
|
||||||
if c0 < 0x80 { // is ASCII
|
|
||||||
return {{.ASCIIBlock}}[c0]
|
|
||||||
}
|
|
||||||
i := {{.StarterBlock}}[c0]
|
|
||||||
if c0 < 0xE0 { // 2-byte UTF-8
|
|
||||||
return t.lookupValue(uint32(i), s[1])
|
|
||||||
}
|
|
||||||
i = {{.Name}}Index[uint32(i)<<6+uint32(s[1])]
|
|
||||||
if c0 < 0xF0 { // 3-byte UTF-8
|
|
||||||
return t.lookupValue(uint32(i), s[2])
|
|
||||||
}
|
|
||||||
i = {{.Name}}Index[uint32(i)<<6+uint32(s[2])]
|
|
||||||
if c0 < 0xF8 { // 4-byte UTF-8
|
|
||||||
return t.lookupValue(uint32(i), s[3])
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
`
|
|
494
vendor/golang.org/x/text/internal/triegen/triegen.go
generated
vendored
494
vendor/golang.org/x/text/internal/triegen/triegen.go
generated
vendored
|
@ -1,494 +0,0 @@
|
||||||
// Copyright 2014 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 triegen implements a code generator for a trie for associating
|
|
||||||
// unsigned integer values with UTF-8 encoded runes.
|
|
||||||
//
|
|
||||||
// Many of the go.text packages use tries for storing per-rune information. A
|
|
||||||
// trie is especially useful if many of the runes have the same value. If this
|
|
||||||
// is the case, many blocks can be expected to be shared allowing for
|
|
||||||
// information on many runes to be stored in little space.
|
|
||||||
//
|
|
||||||
// As most of the lookups are done directly on []byte slices, the tries use the
|
|
||||||
// UTF-8 bytes directly for the lookup. This saves a conversion from UTF-8 to
|
|
||||||
// runes and contributes a little bit to better performance. It also naturally
|
|
||||||
// provides a fast path for ASCII.
|
|
||||||
//
|
|
||||||
// Space is also an issue. There are many code points defined in Unicode and as
|
|
||||||
// a result tables can get quite large. So every byte counts. The triegen
|
|
||||||
// package automatically chooses the smallest integer values to represent the
|
|
||||||
// tables. Compacters allow further compression of the trie by allowing for
|
|
||||||
// alternative representations of individual trie blocks.
|
|
||||||
//
|
|
||||||
// triegen allows generating multiple tries as a single structure. This is
|
|
||||||
// useful when, for example, one wants to generate tries for several languages
|
|
||||||
// that have a lot of values in common. Some existing libraries for
|
|
||||||
// internationalization store all per-language data as a dynamically loadable
|
|
||||||
// chunk. The go.text packages are designed with the assumption that the user
|
|
||||||
// typically wants to compile in support for all supported languages, in line
|
|
||||||
// with the approach common to Go to create a single standalone binary. The
|
|
||||||
// multi-root trie approach can give significant storage savings in this
|
|
||||||
// scenario.
|
|
||||||
//
|
|
||||||
// triegen generates both tables and code. The code is optimized to use the
|
|
||||||
// automatically chosen data types. The following code is generated for a Trie
|
|
||||||
// or multiple Tries named "foo":
|
|
||||||
// - type fooTrie
|
|
||||||
// The trie type.
|
|
||||||
//
|
|
||||||
// - func newFooTrie(x int) *fooTrie
|
|
||||||
// Trie constructor, where x is the index of the trie passed to Gen.
|
|
||||||
//
|
|
||||||
// - func (t *fooTrie) lookup(s []byte) (v uintX, sz int)
|
|
||||||
// The lookup method, where uintX is automatically chosen.
|
|
||||||
//
|
|
||||||
// - func lookupString, lookupUnsafe and lookupStringUnsafe
|
|
||||||
// Variants of the above.
|
|
||||||
//
|
|
||||||
// - var fooValues and fooIndex and any tables generated by Compacters.
|
|
||||||
// The core trie data.
|
|
||||||
//
|
|
||||||
// - var fooTrieHandles
|
|
||||||
// Indexes of starter blocks in case of multiple trie roots.
|
|
||||||
//
|
|
||||||
// It is recommended that users test the generated trie by checking the returned
|
|
||||||
// value for every rune. Such exhaustive tests are possible as the the number of
|
|
||||||
// runes in Unicode is limited.
|
|
||||||
package triegen // import "golang.org/x/text/internal/triegen"
|
|
||||||
|
|
||||||
// TODO: Arguably, the internally optimized data types would not have to be
|
|
||||||
// exposed in the generated API. We could also investigate not generating the
|
|
||||||
// code, but using it through a package. We would have to investigate the impact
|
|
||||||
// on performance of making such change, though. For packages like unicode/norm,
|
|
||||||
// small changes like this could tank performance.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
"hash/crc64"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
// builder builds a set of tries for associating values with runes. The set of
|
|
||||||
// tries can share common index and value blocks.
|
|
||||||
type builder struct {
|
|
||||||
Name string
|
|
||||||
|
|
||||||
// ValueType is the type of the trie values looked up.
|
|
||||||
ValueType string
|
|
||||||
|
|
||||||
// ValueSize is the byte size of the ValueType.
|
|
||||||
ValueSize int
|
|
||||||
|
|
||||||
// IndexType is the type of trie index values used for all UTF-8 bytes of
|
|
||||||
// a rune except the last one.
|
|
||||||
IndexType string
|
|
||||||
|
|
||||||
// IndexSize is the byte size of the IndexType.
|
|
||||||
IndexSize int
|
|
||||||
|
|
||||||
// SourceType is used when generating the lookup functions. If the user
|
|
||||||
// requests StringSupport, all lookup functions will be generated for
|
|
||||||
// string input as well.
|
|
||||||
SourceType string
|
|
||||||
|
|
||||||
Trie []*Trie
|
|
||||||
|
|
||||||
IndexBlocks []*node
|
|
||||||
ValueBlocks [][]uint64
|
|
||||||
Compactions []compaction
|
|
||||||
Checksum uint64
|
|
||||||
|
|
||||||
ASCIIBlock string
|
|
||||||
StarterBlock string
|
|
||||||
|
|
||||||
indexBlockIdx map[uint64]int
|
|
||||||
valueBlockIdx map[uint64]nodeIndex
|
|
||||||
asciiBlockIdx map[uint64]int
|
|
||||||
|
|
||||||
// Stats are used to fill out the template.
|
|
||||||
Stats struct {
|
|
||||||
NValueEntries int
|
|
||||||
NValueBytes int
|
|
||||||
NIndexEntries int
|
|
||||||
NIndexBytes int
|
|
||||||
NHandleBytes int
|
|
||||||
}
|
|
||||||
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
// A nodeIndex encodes the index of a node, which is defined by the compaction
|
|
||||||
// which stores it and an index within the compaction. For internal nodes, the
|
|
||||||
// compaction is always 0.
|
|
||||||
type nodeIndex struct {
|
|
||||||
compaction int
|
|
||||||
index int
|
|
||||||
}
|
|
||||||
|
|
||||||
// compaction keeps track of stats used for the compaction.
|
|
||||||
type compaction struct {
|
|
||||||
c Compacter
|
|
||||||
blocks []*node
|
|
||||||
maxHandle uint32
|
|
||||||
totalSize int
|
|
||||||
|
|
||||||
// Used by template-based generator and thus exported.
|
|
||||||
Cutoff uint32
|
|
||||||
Offset uint32
|
|
||||||
Handler string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *builder) setError(err error) {
|
|
||||||
if b.err == nil {
|
|
||||||
b.err = err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// An Option can be passed to Gen.
|
|
||||||
type Option func(b *builder) error
|
|
||||||
|
|
||||||
// Compact configures the trie generator to use the given Compacter.
|
|
||||||
func Compact(c Compacter) Option {
|
|
||||||
return func(b *builder) error {
|
|
||||||
b.Compactions = append(b.Compactions, compaction{
|
|
||||||
c: c,
|
|
||||||
Handler: c.Handler() + "(n, b)"})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gen writes Go code for a shared trie lookup structure to w for the given
|
|
||||||
// Tries. The generated trie type will be called nameTrie. newNameTrie(x) will
|
|
||||||
// return the *nameTrie for tries[x]. A value can be looked up by using one of
|
|
||||||
// the various lookup methods defined on nameTrie. It returns the table size of
|
|
||||||
// the generated trie.
|
|
||||||
func Gen(w io.Writer, name string, tries []*Trie, opts ...Option) (sz int, err error) {
|
|
||||||
// The index contains two dummy blocks, followed by the zero block. The zero
|
|
||||||
// block is at offset 0x80, so that the offset for the zero block for
|
|
||||||
// continuation bytes is 0.
|
|
||||||
b := &builder{
|
|
||||||
Name: name,
|
|
||||||
Trie: tries,
|
|
||||||
IndexBlocks: []*node{{}, {}, {}},
|
|
||||||
Compactions: []compaction{{
|
|
||||||
Handler: name + "Values[n<<6+uint32(b)]",
|
|
||||||
}},
|
|
||||||
// The 0 key in indexBlockIdx and valueBlockIdx is the hash of the zero
|
|
||||||
// block.
|
|
||||||
indexBlockIdx: map[uint64]int{0: 0},
|
|
||||||
valueBlockIdx: map[uint64]nodeIndex{0: {}},
|
|
||||||
asciiBlockIdx: map[uint64]int{},
|
|
||||||
}
|
|
||||||
b.Compactions[0].c = (*simpleCompacter)(b)
|
|
||||||
|
|
||||||
for _, f := range opts {
|
|
||||||
if err := f(b); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
b.build()
|
|
||||||
if b.err != nil {
|
|
||||||
return 0, b.err
|
|
||||||
}
|
|
||||||
if err = b.print(w); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return b.Size(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Trie represents a single root node of a trie. A builder may build several
|
|
||||||
// overlapping tries at once.
|
|
||||||
type Trie struct {
|
|
||||||
root *node
|
|
||||||
|
|
||||||
hiddenTrie
|
|
||||||
}
|
|
||||||
|
|
||||||
// hiddenTrie contains values we want to be visible to the template generator,
|
|
||||||
// but hidden from the API documentation.
|
|
||||||
type hiddenTrie struct {
|
|
||||||
Name string
|
|
||||||
Checksum uint64
|
|
||||||
ASCIIIndex int
|
|
||||||
StarterIndex int
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewTrie returns a new trie root.
|
|
||||||
func NewTrie(name string) *Trie {
|
|
||||||
return &Trie{
|
|
||||||
&node{
|
|
||||||
children: make([]*node, blockSize),
|
|
||||||
values: make([]uint64, utf8.RuneSelf),
|
|
||||||
},
|
|
||||||
hiddenTrie{Name: name},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gen is a convenience wrapper around the Gen func passing t as the only trie
|
|
||||||
// and uses the name passed to NewTrie. It returns the size of the generated
|
|
||||||
// tables.
|
|
||||||
func (t *Trie) Gen(w io.Writer, opts ...Option) (sz int, err error) {
|
|
||||||
return Gen(w, t.Name, []*Trie{t}, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// node is a node of the intermediate trie structure.
|
|
||||||
type node struct {
|
|
||||||
// children holds this node's children. It is always of length 64.
|
|
||||||
// A child node may be nil.
|
|
||||||
children []*node
|
|
||||||
|
|
||||||
// values contains the values of this node. If it is non-nil, this node is
|
|
||||||
// either a root or leaf node:
|
|
||||||
// For root nodes, len(values) == 128 and it maps the bytes in [0x00, 0x7F].
|
|
||||||
// For leaf nodes, len(values) == 64 and it maps the bytes in [0x80, 0xBF].
|
|
||||||
values []uint64
|
|
||||||
|
|
||||||
index nodeIndex
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert associates value with the given rune. Insert will panic if a non-zero
|
|
||||||
// value is passed for an invalid rune.
|
|
||||||
func (t *Trie) Insert(r rune, value uint64) {
|
|
||||||
if value == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s := string(r)
|
|
||||||
if []rune(s)[0] != r && value != 0 {
|
|
||||||
// Note: The UCD tables will always assign what amounts to a zero value
|
|
||||||
// to a surrogate. Allowing a zero value for an illegal rune allows
|
|
||||||
// users to iterate over [0..MaxRune] without having to explicitly
|
|
||||||
// exclude surrogates, which would be tedious.
|
|
||||||
panic(fmt.Sprintf("triegen: non-zero value for invalid rune %U", r))
|
|
||||||
}
|
|
||||||
if len(s) == 1 {
|
|
||||||
// It is a root node value (ASCII).
|
|
||||||
t.root.values[s[0]] = value
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
n := t.root
|
|
||||||
for ; len(s) > 1; s = s[1:] {
|
|
||||||
if n.children == nil {
|
|
||||||
n.children = make([]*node, blockSize)
|
|
||||||
}
|
|
||||||
p := s[0] % blockSize
|
|
||||||
c := n.children[p]
|
|
||||||
if c == nil {
|
|
||||||
c = &node{}
|
|
||||||
n.children[p] = c
|
|
||||||
}
|
|
||||||
if len(s) > 2 && c.values != nil {
|
|
||||||
log.Fatalf("triegen: insert(%U): found internal node with values", r)
|
|
||||||
}
|
|
||||||
n = c
|
|
||||||
}
|
|
||||||
if n.values == nil {
|
|
||||||
n.values = make([]uint64, blockSize)
|
|
||||||
}
|
|
||||||
if n.children != nil {
|
|
||||||
log.Fatalf("triegen: insert(%U): found leaf node that also has child nodes", r)
|
|
||||||
}
|
|
||||||
n.values[s[0]-0x80] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
// Size returns the number of bytes the generated trie will take to store. It
|
|
||||||
// needs to be exported as it is used in the templates.
|
|
||||||
func (b *builder) Size() int {
|
|
||||||
// Index blocks.
|
|
||||||
sz := len(b.IndexBlocks) * blockSize * b.IndexSize
|
|
||||||
|
|
||||||
// Skip the first compaction, which represents the normal value blocks, as
|
|
||||||
// its totalSize does not account for the ASCII blocks, which are managed
|
|
||||||
// separately.
|
|
||||||
sz += len(b.ValueBlocks) * blockSize * b.ValueSize
|
|
||||||
for _, c := range b.Compactions[1:] {
|
|
||||||
sz += c.totalSize
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: this computation does not account for the fixed overhead of a using
|
|
||||||
// a compaction, either code or data. As for data, though, the typical
|
|
||||||
// overhead of data is in the order of bytes (2 bytes for cases). Further,
|
|
||||||
// the savings of using a compaction should anyway be substantial for it to
|
|
||||||
// be worth it.
|
|
||||||
|
|
||||||
// For multi-root tries, we also need to account for the handles.
|
|
||||||
if len(b.Trie) > 1 {
|
|
||||||
sz += 2 * b.IndexSize * len(b.Trie)
|
|
||||||
}
|
|
||||||
return sz
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *builder) build() {
|
|
||||||
// Compute the sizes of the values.
|
|
||||||
var vmax uint64
|
|
||||||
for _, t := range b.Trie {
|
|
||||||
vmax = maxValue(t.root, vmax)
|
|
||||||
}
|
|
||||||
b.ValueType, b.ValueSize = getIntType(vmax)
|
|
||||||
|
|
||||||
// Compute all block allocations.
|
|
||||||
// TODO: first compute the ASCII blocks for all tries and then the other
|
|
||||||
// nodes. ASCII blocks are more restricted in placement, as they require two
|
|
||||||
// blocks to be placed consecutively. Processing them first may improve
|
|
||||||
// sharing (at least one zero block can be expected to be saved.)
|
|
||||||
for _, t := range b.Trie {
|
|
||||||
b.Checksum += b.buildTrie(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute the offsets for all the Compacters.
|
|
||||||
offset := uint32(0)
|
|
||||||
for i := range b.Compactions {
|
|
||||||
c := &b.Compactions[i]
|
|
||||||
c.Offset = offset
|
|
||||||
offset += c.maxHandle + 1
|
|
||||||
c.Cutoff = offset
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute the sizes of indexes.
|
|
||||||
// TODO: different byte positions could have different sizes. So far we have
|
|
||||||
// not found a case where this is beneficial.
|
|
||||||
imax := uint64(b.Compactions[len(b.Compactions)-1].Cutoff)
|
|
||||||
for _, ib := range b.IndexBlocks {
|
|
||||||
if x := uint64(ib.index.index); x > imax {
|
|
||||||
imax = x
|
|
||||||
}
|
|
||||||
}
|
|
||||||
b.IndexType, b.IndexSize = getIntType(imax)
|
|
||||||
}
|
|
||||||
|
|
||||||
func maxValue(n *node, max uint64) uint64 {
|
|
||||||
if n == nil {
|
|
||||||
return max
|
|
||||||
}
|
|
||||||
for _, c := range n.children {
|
|
||||||
max = maxValue(c, max)
|
|
||||||
}
|
|
||||||
for _, v := range n.values {
|
|
||||||
if max < v {
|
|
||||||
max = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return max
|
|
||||||
}
|
|
||||||
|
|
||||||
func getIntType(v uint64) (string, int) {
|
|
||||||
switch {
|
|
||||||
case v < 1<<8:
|
|
||||||
return "uint8", 1
|
|
||||||
case v < 1<<16:
|
|
||||||
return "uint16", 2
|
|
||||||
case v < 1<<32:
|
|
||||||
return "uint32", 4
|
|
||||||
}
|
|
||||||
return "uint64", 8
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
blockSize = 64
|
|
||||||
|
|
||||||
// Subtract two blocks to offset 0x80, the first continuation byte.
|
|
||||||
blockOffset = 2
|
|
||||||
|
|
||||||
// Subtract three blocks to offset 0xC0, the first non-ASCII starter.
|
|
||||||
rootBlockOffset = 3
|
|
||||||
)
|
|
||||||
|
|
||||||
var crcTable = crc64.MakeTable(crc64.ISO)
|
|
||||||
|
|
||||||
func (b *builder) buildTrie(t *Trie) uint64 {
|
|
||||||
n := t.root
|
|
||||||
|
|
||||||
// Get the ASCII offset. For the first trie, the ASCII block will be at
|
|
||||||
// position 0.
|
|
||||||
hasher := crc64.New(crcTable)
|
|
||||||
binary.Write(hasher, binary.BigEndian, n.values)
|
|
||||||
hash := hasher.Sum64()
|
|
||||||
|
|
||||||
v, ok := b.asciiBlockIdx[hash]
|
|
||||||
if !ok {
|
|
||||||
v = len(b.ValueBlocks)
|
|
||||||
b.asciiBlockIdx[hash] = v
|
|
||||||
|
|
||||||
b.ValueBlocks = append(b.ValueBlocks, n.values[:blockSize], n.values[blockSize:])
|
|
||||||
if v == 0 {
|
|
||||||
// Add the zero block at position 2 so that it will be assigned a
|
|
||||||
// zero reference in the lookup blocks.
|
|
||||||
// TODO: always do this? This would allow us to remove a check from
|
|
||||||
// the trie lookup, but at the expense of extra space. Analyze
|
|
||||||
// performance for unicode/norm.
|
|
||||||
b.ValueBlocks = append(b.ValueBlocks, make([]uint64, blockSize))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t.ASCIIIndex = v
|
|
||||||
|
|
||||||
// Compute remaining offsets.
|
|
||||||
t.Checksum = b.computeOffsets(n, true)
|
|
||||||
// We already subtracted the normal blockOffset from the index. Subtract the
|
|
||||||
// difference for starter bytes.
|
|
||||||
t.StarterIndex = n.index.index - (rootBlockOffset - blockOffset)
|
|
||||||
return t.Checksum
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *builder) computeOffsets(n *node, root bool) uint64 {
|
|
||||||
// For the first trie, the root lookup block will be at position 3, which is
|
|
||||||
// the offset for UTF-8 non-ASCII starter bytes.
|
|
||||||
first := len(b.IndexBlocks) == rootBlockOffset
|
|
||||||
if first {
|
|
||||||
b.IndexBlocks = append(b.IndexBlocks, n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// We special-case the cases where all values recursively are 0. This allows
|
|
||||||
// for the use of a zero block to which all such values can be directed.
|
|
||||||
hash := uint64(0)
|
|
||||||
if n.children != nil || n.values != nil {
|
|
||||||
hasher := crc64.New(crcTable)
|
|
||||||
for _, c := range n.children {
|
|
||||||
var v uint64
|
|
||||||
if c != nil {
|
|
||||||
v = b.computeOffsets(c, false)
|
|
||||||
}
|
|
||||||
binary.Write(hasher, binary.BigEndian, v)
|
|
||||||
}
|
|
||||||
binary.Write(hasher, binary.BigEndian, n.values)
|
|
||||||
hash = hasher.Sum64()
|
|
||||||
}
|
|
||||||
|
|
||||||
if first {
|
|
||||||
b.indexBlockIdx[hash] = rootBlockOffset - blockOffset
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compacters don't apply to internal nodes.
|
|
||||||
if n.children != nil {
|
|
||||||
v, ok := b.indexBlockIdx[hash]
|
|
||||||
if !ok {
|
|
||||||
v = len(b.IndexBlocks) - blockOffset
|
|
||||||
b.IndexBlocks = append(b.IndexBlocks, n)
|
|
||||||
b.indexBlockIdx[hash] = v
|
|
||||||
}
|
|
||||||
n.index = nodeIndex{0, v}
|
|
||||||
} else {
|
|
||||||
h, ok := b.valueBlockIdx[hash]
|
|
||||||
if !ok {
|
|
||||||
bestI, bestSize := 0, blockSize*b.ValueSize
|
|
||||||
for i, c := range b.Compactions[1:] {
|
|
||||||
if sz, ok := c.c.Size(n.values); ok && bestSize > sz {
|
|
||||||
bestI, bestSize = i+1, sz
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c := &b.Compactions[bestI]
|
|
||||||
c.totalSize += bestSize
|
|
||||||
v := c.c.Store(n.values)
|
|
||||||
if c.maxHandle < v {
|
|
||||||
c.maxHandle = v
|
|
||||||
}
|
|
||||||
h = nodeIndex{bestI, int(v)}
|
|
||||||
b.valueBlockIdx[hash] = h
|
|
||||||
}
|
|
||||||
n.index = h
|
|
||||||
}
|
|
||||||
return hash
|
|
||||||
}
|
|
376
vendor/golang.org/x/text/internal/ucd/ucd.go
generated
vendored
376
vendor/golang.org/x/text/internal/ucd/ucd.go
generated
vendored
|
@ -1,376 +0,0 @@
|
||||||
// Copyright 2014 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 ucd provides a parser for Unicode Character Database files, the
|
|
||||||
// format of which is defined in http://www.unicode.org/reports/tr44/. See
|
|
||||||
// http://www.unicode.org/Public/UCD/latest/ucd/ for example files.
|
|
||||||
//
|
|
||||||
// It currently does not support substitutions of missing fields.
|
|
||||||
package ucd // import "golang.org/x/text/internal/ucd"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// UnicodeData.txt fields.
|
|
||||||
const (
|
|
||||||
CodePoint = iota
|
|
||||||
Name
|
|
||||||
GeneralCategory
|
|
||||||
CanonicalCombiningClass
|
|
||||||
BidiClass
|
|
||||||
DecompMapping
|
|
||||||
DecimalValue
|
|
||||||
DigitValue
|
|
||||||
NumericValue
|
|
||||||
BidiMirrored
|
|
||||||
Unicode1Name
|
|
||||||
ISOComment
|
|
||||||
SimpleUppercaseMapping
|
|
||||||
SimpleLowercaseMapping
|
|
||||||
SimpleTitlecaseMapping
|
|
||||||
)
|
|
||||||
|
|
||||||
// Parse calls f for each entry in the given reader of a UCD file. It will close
|
|
||||||
// the reader upon return. It will call log.Fatal if any error occurred.
|
|
||||||
//
|
|
||||||
// This implements the most common usage pattern of using Parser.
|
|
||||||
func Parse(r io.ReadCloser, f func(p *Parser)) {
|
|
||||||
defer r.Close()
|
|
||||||
|
|
||||||
p := New(r)
|
|
||||||
for p.Next() {
|
|
||||||
f(p)
|
|
||||||
}
|
|
||||||
if err := p.Err(); err != nil {
|
|
||||||
r.Close() // os.Exit will cause defers not to be called.
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// An Option is used to configure a Parser.
|
|
||||||
type Option func(p *Parser)
|
|
||||||
|
|
||||||
func keepRanges(p *Parser) {
|
|
||||||
p.keepRanges = true
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
// KeepRanges prevents the expansion of ranges. The raw ranges can be
|
|
||||||
// obtained by calling Range(0) on the parser.
|
|
||||||
KeepRanges Option = keepRanges
|
|
||||||
)
|
|
||||||
|
|
||||||
// The Part option register a handler for lines starting with a '@'. The text
|
|
||||||
// after a '@' is available as the first field. Comments are handled as usual.
|
|
||||||
func Part(f func(p *Parser)) Option {
|
|
||||||
return func(p *Parser) {
|
|
||||||
p.partHandler = f
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The CommentHandler option passes comments that are on a line by itself to
|
|
||||||
// a given handler.
|
|
||||||
func CommentHandler(f func(s string)) Option {
|
|
||||||
return func(p *Parser) {
|
|
||||||
p.commentHandler = f
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Parser parses Unicode Character Database (UCD) files.
|
|
||||||
type Parser struct {
|
|
||||||
scanner *bufio.Scanner
|
|
||||||
|
|
||||||
keepRanges bool // Don't expand rune ranges in field 0.
|
|
||||||
|
|
||||||
err error
|
|
||||||
comment []byte
|
|
||||||
field [][]byte
|
|
||||||
// parsedRange is needed in case Range(0) is called more than once for one
|
|
||||||
// field. In some cases this requires scanning ahead.
|
|
||||||
parsedRange bool
|
|
||||||
rangeStart, rangeEnd rune
|
|
||||||
|
|
||||||
partHandler func(p *Parser)
|
|
||||||
commentHandler func(s string)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Parser) setError(err error) {
|
|
||||||
if p.err == nil {
|
|
||||||
p.err = err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Parser) getField(i int) []byte {
|
|
||||||
if i >= len(p.field) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return p.field[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Err returns a non-nil error if any error occurred during parsing.
|
|
||||||
func (p *Parser) Err() error {
|
|
||||||
return p.err
|
|
||||||
}
|
|
||||||
|
|
||||||
// New returns a Parser for the given Reader.
|
|
||||||
func New(r io.Reader, o ...Option) *Parser {
|
|
||||||
p := &Parser{
|
|
||||||
scanner: bufio.NewScanner(r),
|
|
||||||
}
|
|
||||||
for _, f := range o {
|
|
||||||
f(p)
|
|
||||||
}
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next parses the next line in the file. It returns true if a line was parsed
|
|
||||||
// and false if it reached the end of the file.
|
|
||||||
func (p *Parser) Next() bool {
|
|
||||||
if !p.keepRanges && p.rangeStart < p.rangeEnd {
|
|
||||||
p.rangeStart++
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
p.comment = nil
|
|
||||||
p.field = p.field[:0]
|
|
||||||
p.parsedRange = false
|
|
||||||
|
|
||||||
for p.scanner.Scan() {
|
|
||||||
b := p.scanner.Bytes()
|
|
||||||
if len(b) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if b[0] == '#' {
|
|
||||||
if p.commentHandler != nil {
|
|
||||||
p.commentHandler(strings.TrimSpace(string(b[1:])))
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse line
|
|
||||||
if i := bytes.IndexByte(b, '#'); i != -1 {
|
|
||||||
p.comment = bytes.TrimSpace(b[i+1:])
|
|
||||||
b = b[:i]
|
|
||||||
}
|
|
||||||
if b[0] == '@' {
|
|
||||||
if p.partHandler != nil {
|
|
||||||
p.field = append(p.field, bytes.TrimSpace(b[1:]))
|
|
||||||
p.partHandler(p)
|
|
||||||
p.field = p.field[:0]
|
|
||||||
}
|
|
||||||
p.comment = nil
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
i := bytes.IndexByte(b, ';')
|
|
||||||
if i == -1 {
|
|
||||||
p.field = append(p.field, bytes.TrimSpace(b))
|
|
||||||
break
|
|
||||||
}
|
|
||||||
p.field = append(p.field, bytes.TrimSpace(b[:i]))
|
|
||||||
b = b[i+1:]
|
|
||||||
}
|
|
||||||
if !p.keepRanges {
|
|
||||||
p.rangeStart, p.rangeEnd = p.getRange(0)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
p.setError(p.scanner.Err())
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseRune(b []byte) (rune, error) {
|
|
||||||
if len(b) > 2 && b[0] == 'U' && b[1] == '+' {
|
|
||||||
b = b[2:]
|
|
||||||
}
|
|
||||||
x, err := strconv.ParseUint(string(b), 16, 32)
|
|
||||||
return rune(x), err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Parser) parseRune(b []byte) rune {
|
|
||||||
x, err := parseRune(b)
|
|
||||||
p.setError(err)
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rune parses and returns field i as a rune.
|
|
||||||
func (p *Parser) Rune(i int) rune {
|
|
||||||
if i > 0 || p.keepRanges {
|
|
||||||
return p.parseRune(p.getField(i))
|
|
||||||
}
|
|
||||||
return p.rangeStart
|
|
||||||
}
|
|
||||||
|
|
||||||
// Runes interprets and returns field i as a sequence of runes.
|
|
||||||
func (p *Parser) Runes(i int) (runes []rune) {
|
|
||||||
add := func(b []byte) {
|
|
||||||
if b = bytes.TrimSpace(b); len(b) > 0 {
|
|
||||||
runes = append(runes, p.parseRune(b))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for b := p.getField(i); ; {
|
|
||||||
i := bytes.IndexByte(b, ' ')
|
|
||||||
if i == -1 {
|
|
||||||
add(b)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
add(b[:i])
|
|
||||||
b = b[i+1:]
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
errIncorrectLegacyRange = errors.New("ucd: unmatched <* First>")
|
|
||||||
|
|
||||||
// reRange matches one line of a legacy rune range.
|
|
||||||
reRange = regexp.MustCompile("^([0-9A-F]*);<([^,]*), ([^>]*)>(.*)$")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Range parses and returns field i as a rune range. A range is inclusive at
|
|
||||||
// both ends. If the field only has one rune, first and last will be identical.
|
|
||||||
// It supports the legacy format for ranges used in UnicodeData.txt.
|
|
||||||
func (p *Parser) Range(i int) (first, last rune) {
|
|
||||||
if !p.keepRanges {
|
|
||||||
return p.rangeStart, p.rangeStart
|
|
||||||
}
|
|
||||||
return p.getRange(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Parser) getRange(i int) (first, last rune) {
|
|
||||||
b := p.getField(i)
|
|
||||||
if k := bytes.Index(b, []byte("..")); k != -1 {
|
|
||||||
return p.parseRune(b[:k]), p.parseRune(b[k+2:])
|
|
||||||
}
|
|
||||||
// The first field may not be a rune, in which case we may ignore any error
|
|
||||||
// and set the range as 0..0.
|
|
||||||
x, err := parseRune(b)
|
|
||||||
if err != nil {
|
|
||||||
// Disable range parsing henceforth. This ensures that an error will be
|
|
||||||
// returned if the user subsequently will try to parse this field as
|
|
||||||
// a Rune.
|
|
||||||
p.keepRanges = true
|
|
||||||
}
|
|
||||||
// Special case for UnicodeData that was retained for backwards compatibility.
|
|
||||||
if i == 0 && len(p.field) > 1 && bytes.HasSuffix(p.field[1], []byte("First>")) {
|
|
||||||
if p.parsedRange {
|
|
||||||
return p.rangeStart, p.rangeEnd
|
|
||||||
}
|
|
||||||
mf := reRange.FindStringSubmatch(p.scanner.Text())
|
|
||||||
if mf == nil || !p.scanner.Scan() {
|
|
||||||
p.setError(errIncorrectLegacyRange)
|
|
||||||
return x, x
|
|
||||||
}
|
|
||||||
// Using Bytes would be more efficient here, but Text is a lot easier
|
|
||||||
// and this is not a frequent case.
|
|
||||||
ml := reRange.FindStringSubmatch(p.scanner.Text())
|
|
||||||
if ml == nil || mf[2] != ml[2] || ml[3] != "Last" || mf[4] != ml[4] {
|
|
||||||
p.setError(errIncorrectLegacyRange)
|
|
||||||
return x, x
|
|
||||||
}
|
|
||||||
p.rangeStart, p.rangeEnd = x, p.parseRune(p.scanner.Bytes()[:len(ml[1])])
|
|
||||||
p.parsedRange = true
|
|
||||||
return p.rangeStart, p.rangeEnd
|
|
||||||
}
|
|
||||||
return x, x
|
|
||||||
}
|
|
||||||
|
|
||||||
// bools recognizes all valid UCD boolean values.
|
|
||||||
var bools = map[string]bool{
|
|
||||||
"": false,
|
|
||||||
"N": false,
|
|
||||||
"No": false,
|
|
||||||
"F": false,
|
|
||||||
"False": false,
|
|
||||||
"Y": true,
|
|
||||||
"Yes": true,
|
|
||||||
"T": true,
|
|
||||||
"True": true,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bool parses and returns field i as a boolean value.
|
|
||||||
func (p *Parser) Bool(i int) bool {
|
|
||||||
b := p.getField(i)
|
|
||||||
for s, v := range bools {
|
|
||||||
if bstrEq(b, s) {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.setError(strconv.ErrSyntax)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int parses and returns field i as an integer value.
|
|
||||||
func (p *Parser) Int(i int) int {
|
|
||||||
x, err := strconv.ParseInt(string(p.getField(i)), 10, 64)
|
|
||||||
p.setError(err)
|
|
||||||
return int(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uint parses and returns field i as an unsigned integer value.
|
|
||||||
func (p *Parser) Uint(i int) uint {
|
|
||||||
x, err := strconv.ParseUint(string(p.getField(i)), 10, 64)
|
|
||||||
p.setError(err)
|
|
||||||
return uint(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Float parses and returns field i as a decimal value.
|
|
||||||
func (p *Parser) Float(i int) float64 {
|
|
||||||
x, err := strconv.ParseFloat(string(p.getField(i)), 64)
|
|
||||||
p.setError(err)
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
|
|
||||||
// String parses and returns field i as a string value.
|
|
||||||
func (p *Parser) String(i int) string {
|
|
||||||
return string(p.getField(i))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Strings parses and returns field i as a space-separated list of strings.
|
|
||||||
func (p *Parser) Strings(i int) []string {
|
|
||||||
ss := strings.Split(string(p.getField(i)), " ")
|
|
||||||
for i, s := range ss {
|
|
||||||
ss[i] = strings.TrimSpace(s)
|
|
||||||
}
|
|
||||||
return ss
|
|
||||||
}
|
|
||||||
|
|
||||||
// Comment returns the comments for the current line.
|
|
||||||
func (p *Parser) Comment() string {
|
|
||||||
return string(p.comment)
|
|
||||||
}
|
|
||||||
|
|
||||||
var errUndefinedEnum = errors.New("ucd: undefined enum value")
|
|
||||||
|
|
||||||
// Enum interprets and returns field i as a value that must be one of the values
|
|
||||||
// in enum.
|
|
||||||
func (p *Parser) Enum(i int, enum ...string) string {
|
|
||||||
b := p.getField(i)
|
|
||||||
for _, s := range enum {
|
|
||||||
if bstrEq(b, s) {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.setError(errUndefinedEnum)
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func bstrEq(b []byte, s string) bool {
|
|
||||||
if len(b) != len(s) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for i, c := range b {
|
|
||||||
if c != s[i] {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
133
vendor/golang.org/x/text/unicode/bidi/gen.go
generated
vendored
133
vendor/golang.org/x/text/unicode/bidi/gen.go
generated
vendored
|
@ -1,133 +0,0 @@
|
||||||
// Copyright 2015 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 ignore
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"golang.org/x/text/internal/gen"
|
|
||||||
"golang.org/x/text/internal/triegen"
|
|
||||||
"golang.org/x/text/internal/ucd"
|
|
||||||
)
|
|
||||||
|
|
||||||
var outputFile = flag.String("out", "tables.go", "output file")
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
gen.Init()
|
|
||||||
gen.Repackage("gen_trieval.go", "trieval.go", "bidi")
|
|
||||||
gen.Repackage("gen_ranges.go", "ranges_test.go", "bidi")
|
|
||||||
|
|
||||||
genTables()
|
|
||||||
}
|
|
||||||
|
|
||||||
// bidiClass names and codes taken from class "bc" in
|
|
||||||
// http://www.unicode.org/Public/8.0.0/ucd/PropertyValueAliases.txt
|
|
||||||
var bidiClass = map[string]Class{
|
|
||||||
"AL": AL, // ArabicLetter
|
|
||||||
"AN": AN, // ArabicNumber
|
|
||||||
"B": B, // ParagraphSeparator
|
|
||||||
"BN": BN, // BoundaryNeutral
|
|
||||||
"CS": CS, // CommonSeparator
|
|
||||||
"EN": EN, // EuropeanNumber
|
|
||||||
"ES": ES, // EuropeanSeparator
|
|
||||||
"ET": ET, // EuropeanTerminator
|
|
||||||
"L": L, // LeftToRight
|
|
||||||
"NSM": NSM, // NonspacingMark
|
|
||||||
"ON": ON, // OtherNeutral
|
|
||||||
"R": R, // RightToLeft
|
|
||||||
"S": S, // SegmentSeparator
|
|
||||||
"WS": WS, // WhiteSpace
|
|
||||||
|
|
||||||
"FSI": Control,
|
|
||||||
"PDF": Control,
|
|
||||||
"PDI": Control,
|
|
||||||
"LRE": Control,
|
|
||||||
"LRI": Control,
|
|
||||||
"LRO": Control,
|
|
||||||
"RLE": Control,
|
|
||||||
"RLI": Control,
|
|
||||||
"RLO": Control,
|
|
||||||
}
|
|
||||||
|
|
||||||
func genTables() {
|
|
||||||
if numClass > 0x0F {
|
|
||||||
log.Fatalf("Too many Class constants (%#x > 0x0F).", numClass)
|
|
||||||
}
|
|
||||||
w := gen.NewCodeWriter()
|
|
||||||
defer w.WriteGoFile(*outputFile, "bidi")
|
|
||||||
|
|
||||||
gen.WriteUnicodeVersion(w)
|
|
||||||
|
|
||||||
t := triegen.NewTrie("bidi")
|
|
||||||
|
|
||||||
// Build data about bracket mapping. These bits need to be or-ed with
|
|
||||||
// any other bits.
|
|
||||||
orMask := map[rune]uint64{}
|
|
||||||
|
|
||||||
xorMap := map[rune]int{}
|
|
||||||
xorMasks := []rune{0} // First value is no-op.
|
|
||||||
|
|
||||||
ucd.Parse(gen.OpenUCDFile("BidiBrackets.txt"), func(p *ucd.Parser) {
|
|
||||||
r1 := p.Rune(0)
|
|
||||||
r2 := p.Rune(1)
|
|
||||||
xor := r1 ^ r2
|
|
||||||
if _, ok := xorMap[xor]; !ok {
|
|
||||||
xorMap[xor] = len(xorMasks)
|
|
||||||
xorMasks = append(xorMasks, xor)
|
|
||||||
}
|
|
||||||
entry := uint64(xorMap[xor]) << xorMaskShift
|
|
||||||
switch p.String(2) {
|
|
||||||
case "o":
|
|
||||||
entry |= openMask
|
|
||||||
case "c", "n":
|
|
||||||
default:
|
|
||||||
log.Fatalf("Unknown bracket class %q.", p.String(2))
|
|
||||||
}
|
|
||||||
orMask[r1] = entry
|
|
||||||
})
|
|
||||||
|
|
||||||
w.WriteComment(`
|
|
||||||
xorMasks contains masks to be xor-ed with brackets to get the reverse
|
|
||||||
version.`)
|
|
||||||
w.WriteVar("xorMasks", xorMasks)
|
|
||||||
|
|
||||||
done := map[rune]bool{}
|
|
||||||
|
|
||||||
insert := func(r rune, c Class) {
|
|
||||||
if !done[r] {
|
|
||||||
t.Insert(r, orMask[r]|uint64(c))
|
|
||||||
done[r] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert the derived BiDi properties.
|
|
||||||
ucd.Parse(gen.OpenUCDFile("extracted/DerivedBidiClass.txt"), func(p *ucd.Parser) {
|
|
||||||
r := p.Rune(0)
|
|
||||||
class, ok := bidiClass[p.String(1)]
|
|
||||||
if !ok {
|
|
||||||
log.Fatalf("%U: Unknown BiDi class %q", r, p.String(1))
|
|
||||||
}
|
|
||||||
insert(r, class)
|
|
||||||
})
|
|
||||||
visitDefaults(insert)
|
|
||||||
|
|
||||||
// TODO: use sparse blocks. This would reduce table size considerably
|
|
||||||
// from the looks of it.
|
|
||||||
|
|
||||||
sz, err := t.Gen(w)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
w.Size += sz
|
|
||||||
}
|
|
||||||
|
|
||||||
// dummy values to make methods in gen_common compile. The real versions
|
|
||||||
// will be generated by this file to tables.go.
|
|
||||||
var (
|
|
||||||
xorMasks []rune
|
|
||||||
)
|
|
57
vendor/golang.org/x/text/unicode/bidi/gen_ranges.go
generated
vendored
57
vendor/golang.org/x/text/unicode/bidi/gen_ranges.go
generated
vendored
|
@ -1,57 +0,0 @@
|
||||||
// Copyright 2015 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 ignore
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"unicode"
|
|
||||||
|
|
||||||
"golang.org/x/text/internal/gen"
|
|
||||||
"golang.org/x/text/internal/ucd"
|
|
||||||
"golang.org/x/text/unicode/rangetable"
|
|
||||||
)
|
|
||||||
|
|
||||||
// These tables are hand-extracted from:
|
|
||||||
// http://www.unicode.org/Public/8.0.0/ucd/extracted/DerivedBidiClass.txt
|
|
||||||
func visitDefaults(fn func(r rune, c Class)) {
|
|
||||||
// first write default values for ranges listed above.
|
|
||||||
visitRunes(fn, AL, []rune{
|
|
||||||
0x0600, 0x07BF, // Arabic
|
|
||||||
0x08A0, 0x08FF, // Arabic Extended-A
|
|
||||||
0xFB50, 0xFDCF, // Arabic Presentation Forms
|
|
||||||
0xFDF0, 0xFDFF,
|
|
||||||
0xFE70, 0xFEFF,
|
|
||||||
0x0001EE00, 0x0001EEFF, // Arabic Mathematical Alpha Symbols
|
|
||||||
})
|
|
||||||
visitRunes(fn, R, []rune{
|
|
||||||
0x0590, 0x05FF, // Hebrew
|
|
||||||
0x07C0, 0x089F, // Nko et al.
|
|
||||||
0xFB1D, 0xFB4F,
|
|
||||||
0x00010800, 0x00010FFF, // Cypriot Syllabary et. al.
|
|
||||||
0x0001E800, 0x0001EDFF,
|
|
||||||
0x0001EF00, 0x0001EFFF,
|
|
||||||
})
|
|
||||||
visitRunes(fn, ET, []rune{ // European Terminator
|
|
||||||
0x20A0, 0x20Cf, // Currency symbols
|
|
||||||
})
|
|
||||||
rangetable.Visit(unicode.Noncharacter_Code_Point, func(r rune) {
|
|
||||||
fn(r, BN) // Boundary Neutral
|
|
||||||
})
|
|
||||||
ucd.Parse(gen.OpenUCDFile("DerivedCoreProperties.txt"), func(p *ucd.Parser) {
|
|
||||||
if p.String(1) == "Default_Ignorable_Code_Point" {
|
|
||||||
fn(p.Rune(0), BN) // Boundary Neutral
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func visitRunes(fn func(r rune, c Class), c Class, runes []rune) {
|
|
||||||
for i := 0; i < len(runes); i += 2 {
|
|
||||||
lo, hi := runes[i], runes[i+1]
|
|
||||||
for j := lo; j <= hi; j++ {
|
|
||||||
fn(j, c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
64
vendor/golang.org/x/text/unicode/bidi/gen_trieval.go
generated
vendored
64
vendor/golang.org/x/text/unicode/bidi/gen_trieval.go
generated
vendored
|
@ -1,64 +0,0 @@
|
||||||
// Copyright 2015 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 ignore
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
// Class is the Unicode BiDi class. Each rune has a single class.
|
|
||||||
type Class uint
|
|
||||||
|
|
||||||
const (
|
|
||||||
L Class = iota // LeftToRight
|
|
||||||
R // RightToLeft
|
|
||||||
EN // EuropeanNumber
|
|
||||||
ES // EuropeanSeparator
|
|
||||||
ET // EuropeanTerminator
|
|
||||||
AN // ArabicNumber
|
|
||||||
CS // CommonSeparator
|
|
||||||
B // ParagraphSeparator
|
|
||||||
S // SegmentSeparator
|
|
||||||
WS // WhiteSpace
|
|
||||||
ON // OtherNeutral
|
|
||||||
BN // BoundaryNeutral
|
|
||||||
NSM // NonspacingMark
|
|
||||||
AL // ArabicLetter
|
|
||||||
Control // Control LRO - PDI
|
|
||||||
|
|
||||||
numClass
|
|
||||||
|
|
||||||
LRO // LeftToRightOverride
|
|
||||||
RLO // RightToLeftOverride
|
|
||||||
LRE // LeftToRightEmbedding
|
|
||||||
RLE // RightToLeftEmbedding
|
|
||||||
PDF // PopDirectionalFormat
|
|
||||||
LRI // LeftToRightIsolate
|
|
||||||
RLI // RightToLeftIsolate
|
|
||||||
FSI // FirstStrongIsolate
|
|
||||||
PDI // PopDirectionalIsolate
|
|
||||||
|
|
||||||
unknownClass = ^Class(0)
|
|
||||||
)
|
|
||||||
|
|
||||||
var controlToClass = map[rune]Class{
|
|
||||||
0x202D: LRO, // LeftToRightOverride,
|
|
||||||
0x202E: RLO, // RightToLeftOverride,
|
|
||||||
0x202A: LRE, // LeftToRightEmbedding,
|
|
||||||
0x202B: RLE, // RightToLeftEmbedding,
|
|
||||||
0x202C: PDF, // PopDirectionalFormat,
|
|
||||||
0x2066: LRI, // LeftToRightIsolate,
|
|
||||||
0x2067: RLI, // RightToLeftIsolate,
|
|
||||||
0x2068: FSI, // FirstStrongIsolate,
|
|
||||||
0x2069: PDI, // PopDirectionalIsolate,
|
|
||||||
}
|
|
||||||
|
|
||||||
// A trie entry has the following bits:
|
|
||||||
// 7..5 XOR mask for brackets
|
|
||||||
// 4 1: Bracket open, 0: Bracket close
|
|
||||||
// 3..0 Class type
|
|
||||||
|
|
||||||
const (
|
|
||||||
openMask = 0x10
|
|
||||||
xorMaskShift = 5
|
|
||||||
)
|
|
100
vendor/golang.org/x/text/unicode/cldr/base.go
generated
vendored
100
vendor/golang.org/x/text/unicode/cldr/base.go
generated
vendored
|
@ -1,100 +0,0 @@
|
||||||
// Copyright 2013 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 cldr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/xml"
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Elem is implemented by every XML element.
|
|
||||||
type Elem interface {
|
|
||||||
setEnclosing(Elem)
|
|
||||||
setName(string)
|
|
||||||
enclosing() Elem
|
|
||||||
|
|
||||||
GetCommon() *Common
|
|
||||||
}
|
|
||||||
|
|
||||||
type hidden struct {
|
|
||||||
CharData string `xml:",chardata"`
|
|
||||||
Alias *struct {
|
|
||||||
Common
|
|
||||||
Source string `xml:"source,attr"`
|
|
||||||
Path string `xml:"path,attr"`
|
|
||||||
} `xml:"alias"`
|
|
||||||
Def *struct {
|
|
||||||
Common
|
|
||||||
Choice string `xml:"choice,attr,omitempty"`
|
|
||||||
Type string `xml:"type,attr,omitempty"`
|
|
||||||
} `xml:"default"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Common holds several of the most common attributes and sub elements
|
|
||||||
// of an XML element.
|
|
||||||
type Common struct {
|
|
||||||
XMLName xml.Name
|
|
||||||
name string
|
|
||||||
enclElem Elem
|
|
||||||
Type string `xml:"type,attr,omitempty"`
|
|
||||||
Reference string `xml:"reference,attr,omitempty"`
|
|
||||||
Alt string `xml:"alt,attr,omitempty"`
|
|
||||||
ValidSubLocales string `xml:"validSubLocales,attr,omitempty"`
|
|
||||||
Draft string `xml:"draft,attr,omitempty"`
|
|
||||||
hidden
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default returns the default type to select from the enclosed list
|
|
||||||
// or "" if no default value is specified.
|
|
||||||
func (e *Common) Default() string {
|
|
||||||
if e.Def == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
if e.Def.Choice != "" {
|
|
||||||
return e.Def.Choice
|
|
||||||
} else if e.Def.Type != "" {
|
|
||||||
// Type is still used by the default element in collation.
|
|
||||||
return e.Def.Type
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCommon returns e. It is provided such that Common implements Elem.
|
|
||||||
func (e *Common) GetCommon() *Common {
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
// Data returns the character data accumulated for this element.
|
|
||||||
func (e *Common) Data() string {
|
|
||||||
e.CharData = charRe.ReplaceAllStringFunc(e.CharData, replaceUnicode)
|
|
||||||
return e.CharData
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Common) setName(s string) {
|
|
||||||
e.name = s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Common) enclosing() Elem {
|
|
||||||
return e.enclElem
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Common) setEnclosing(en Elem) {
|
|
||||||
e.enclElem = en
|
|
||||||
}
|
|
||||||
|
|
||||||
// Escape characters that can be escaped without further escaping the string.
|
|
||||||
var charRe = regexp.MustCompile(`&#x[0-9a-fA-F]*;|\\u[0-9a-fA-F]{4}|\\U[0-9a-fA-F]{8}|\\x[0-9a-fA-F]{2}|\\[0-7]{3}|\\[abtnvfr]`)
|
|
||||||
|
|
||||||
// replaceUnicode converts hexadecimal Unicode codepoint notations to a one-rune string.
|
|
||||||
// It assumes the input string is correctly formatted.
|
|
||||||
func replaceUnicode(s string) string {
|
|
||||||
if s[1] == '#' {
|
|
||||||
r, _ := strconv.ParseInt(s[3:len(s)-1], 16, 32)
|
|
||||||
return string(r)
|
|
||||||
}
|
|
||||||
r, _, _, _ := strconv.UnquoteChar(s, 0)
|
|
||||||
return string(r)
|
|
||||||
}
|
|
130
vendor/golang.org/x/text/unicode/cldr/cldr.go
generated
vendored
130
vendor/golang.org/x/text/unicode/cldr/cldr.go
generated
vendored
|
@ -1,130 +0,0 @@
|
||||||
// Copyright 2013 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.
|
|
||||||
|
|
||||||
//go:generate go run makexml.go -output xml.go
|
|
||||||
|
|
||||||
// Package cldr provides a parser for LDML and related XML formats.
|
|
||||||
// This package is intended to be used by the table generation tools
|
|
||||||
// for the various internationalization-related packages.
|
|
||||||
// As the XML types are generated from the CLDR DTD, and as the CLDR standard
|
|
||||||
// is periodically amended, this package may change considerably over time.
|
|
||||||
// This mostly means that data may appear and disappear between versions.
|
|
||||||
// That is, old code should keep compiling for newer versions, but data
|
|
||||||
// may have moved or changed.
|
|
||||||
// CLDR version 22 is the first version supported by this package.
|
|
||||||
// Older versions may not work.
|
|
||||||
package cldr // import "golang.org/x/text/unicode/cldr"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CLDR provides access to parsed data of the Unicode Common Locale Data Repository.
|
|
||||||
type CLDR struct {
|
|
||||||
parent map[string][]string
|
|
||||||
locale map[string]*LDML
|
|
||||||
resolved map[string]*LDML
|
|
||||||
bcp47 *LDMLBCP47
|
|
||||||
supp *SupplementalData
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeCLDR() *CLDR {
|
|
||||||
return &CLDR{
|
|
||||||
parent: make(map[string][]string),
|
|
||||||
locale: make(map[string]*LDML),
|
|
||||||
resolved: make(map[string]*LDML),
|
|
||||||
bcp47: &LDMLBCP47{},
|
|
||||||
supp: &SupplementalData{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BCP47 returns the parsed BCP47 LDML data. If no such data was parsed, nil is returned.
|
|
||||||
func (cldr *CLDR) BCP47() *LDMLBCP47 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draft indicates the draft level of an element.
|
|
||||||
type Draft int
|
|
||||||
|
|
||||||
const (
|
|
||||||
Approved Draft = iota
|
|
||||||
Contributed
|
|
||||||
Provisional
|
|
||||||
Unconfirmed
|
|
||||||
)
|
|
||||||
|
|
||||||
var drafts = []string{"unconfirmed", "provisional", "contributed", "approved", ""}
|
|
||||||
|
|
||||||
// ParseDraft returns the Draft value corresponding to the given string. The
|
|
||||||
// empty string corresponds to Approved.
|
|
||||||
func ParseDraft(level string) (Draft, error) {
|
|
||||||
if level == "" {
|
|
||||||
return Approved, nil
|
|
||||||
}
|
|
||||||
for i, s := range drafts {
|
|
||||||
if level == s {
|
|
||||||
return Unconfirmed - Draft(i), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Approved, fmt.Errorf("cldr: unknown draft level %q", level)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d Draft) String() string {
|
|
||||||
return drafts[len(drafts)-1-int(d)]
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetDraftLevel sets which draft levels to include in the evaluated LDML.
|
|
||||||
// Any draft element for which the draft level is higher than lev will be excluded.
|
|
||||||
// If multiple draft levels are available for a single element, the one with the
|
|
||||||
// lowest draft level will be selected, unless preferDraft is true, in which case
|
|
||||||
// the highest draft will be chosen.
|
|
||||||
// It is assumed that the underlying LDML is canonicalized.
|
|
||||||
func (cldr *CLDR) SetDraftLevel(lev Draft, preferDraft bool) {
|
|
||||||
// TODO: implement
|
|
||||||
cldr.resolved = make(map[string]*LDML)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RawLDML returns the LDML XML for id in unresolved form.
|
|
||||||
// id must be one of the strings returned by Locales.
|
|
||||||
func (cldr *CLDR) RawLDML(loc string) *LDML {
|
|
||||||
return cldr.locale[loc]
|
|
||||||
}
|
|
||||||
|
|
||||||
// LDML returns the fully resolved LDML XML for loc, which must be one of
|
|
||||||
// the strings returned by Locales.
|
|
||||||
func (cldr *CLDR) LDML(loc string) (*LDML, error) {
|
|
||||||
return cldr.resolve(loc)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Supplemental returns the parsed supplemental data. If no such data was parsed,
|
|
||||||
// nil is returned.
|
|
||||||
func (cldr *CLDR) Supplemental() *SupplementalData {
|
|
||||||
return cldr.supp
|
|
||||||
}
|
|
||||||
|
|
||||||
// Locales returns the locales for which there exist files.
|
|
||||||
// Valid sublocales for which there is no file are not included.
|
|
||||||
// The root locale is always sorted first.
|
|
||||||
func (cldr *CLDR) Locales() []string {
|
|
||||||
loc := []string{"root"}
|
|
||||||
hasRoot := false
|
|
||||||
for l, _ := range cldr.locale {
|
|
||||||
if l == "root" {
|
|
||||||
hasRoot = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
loc = append(loc, l)
|
|
||||||
}
|
|
||||||
sort.Strings(loc[1:])
|
|
||||||
if !hasRoot {
|
|
||||||
return loc[1:]
|
|
||||||
}
|
|
||||||
return loc
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get fills in the fields of x based on the XPath path.
|
|
||||||
func Get(e Elem, path string) (res Elem, err error) {
|
|
||||||
return walkXPath(e, path)
|
|
||||||
}
|
|
359
vendor/golang.org/x/text/unicode/cldr/collate.go
generated
vendored
359
vendor/golang.org/x/text/unicode/cldr/collate.go
generated
vendored
|
@ -1,359 +0,0 @@
|
||||||
// Copyright 2013 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 cldr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"encoding/xml"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
// RuleProcessor can be passed to Collator's Process method, which
|
|
||||||
// parses the rules and calls the respective method for each rule found.
|
|
||||||
type RuleProcessor interface {
|
|
||||||
Reset(anchor string, before int) error
|
|
||||||
Insert(level int, str, context, extend string) error
|
|
||||||
Index(id string)
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
// cldrIndex is a Unicode-reserved sentinel value used to mark the start
|
|
||||||
// of a grouping within an index.
|
|
||||||
// We ignore any rule that starts with this rune.
|
|
||||||
// See http://unicode.org/reports/tr35/#Collation_Elements for details.
|
|
||||||
cldrIndex = "\uFDD0"
|
|
||||||
|
|
||||||
// specialAnchor is the format in which to represent logical reset positions,
|
|
||||||
// such as "first tertiary ignorable".
|
|
||||||
specialAnchor = "<%s/>"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Process parses the rules for the tailorings of this collation
|
|
||||||
// and calls the respective methods of p for each rule found.
|
|
||||||
func (c Collation) Process(p RuleProcessor) (err error) {
|
|
||||||
if len(c.Cr) > 0 {
|
|
||||||
if len(c.Cr) > 1 {
|
|
||||||
return fmt.Errorf("multiple cr elements, want 0 or 1")
|
|
||||||
}
|
|
||||||
return processRules(p, c.Cr[0].Data())
|
|
||||||
}
|
|
||||||
if c.Rules.Any != nil {
|
|
||||||
return c.processXML(p)
|
|
||||||
}
|
|
||||||
return errors.New("no tailoring data")
|
|
||||||
}
|
|
||||||
|
|
||||||
// processRules parses rules in the Collation Rule Syntax defined in
|
|
||||||
// http://www.unicode.org/reports/tr35/tr35-collation.html#Collation_Tailorings.
|
|
||||||
func processRules(p RuleProcessor, s string) (err error) {
|
|
||||||
chk := func(s string, e error) string {
|
|
||||||
if err == nil {
|
|
||||||
err = e
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
i := 0 // Save the line number for use after the loop.
|
|
||||||
scanner := bufio.NewScanner(strings.NewReader(s))
|
|
||||||
for ; scanner.Scan() && err == nil; i++ {
|
|
||||||
for s := skipSpace(scanner.Text()); s != "" && s[0] != '#'; s = skipSpace(s) {
|
|
||||||
level := 5
|
|
||||||
var ch byte
|
|
||||||
switch ch, s = s[0], s[1:]; ch {
|
|
||||||
case '&': // followed by <anchor> or '[' <key> ']'
|
|
||||||
if s = skipSpace(s); consume(&s, '[') {
|
|
||||||
s = chk(parseSpecialAnchor(p, s))
|
|
||||||
} else {
|
|
||||||
s = chk(parseAnchor(p, 0, s))
|
|
||||||
}
|
|
||||||
case '<': // sort relation '<'{1,4}, optionally followed by '*'.
|
|
||||||
for level = 1; consume(&s, '<'); level++ {
|
|
||||||
}
|
|
||||||
if level > 4 {
|
|
||||||
err = fmt.Errorf("level %d > 4", level)
|
|
||||||
}
|
|
||||||
fallthrough
|
|
||||||
case '=': // identity relation, optionally followed by *.
|
|
||||||
if consume(&s, '*') {
|
|
||||||
s = chk(parseSequence(p, level, s))
|
|
||||||
} else {
|
|
||||||
s = chk(parseOrder(p, level, s))
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
chk("", fmt.Errorf("illegal operator %q", ch))
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if chk("", scanner.Err()); err != nil {
|
|
||||||
return fmt.Errorf("%d: %v", i, err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseSpecialAnchor parses the anchor syntax which is either of the form
|
|
||||||
// ['before' <level>] <anchor>
|
|
||||||
// or
|
|
||||||
// [<label>]
|
|
||||||
// The starting should already be consumed.
|
|
||||||
func parseSpecialAnchor(p RuleProcessor, s string) (tail string, err error) {
|
|
||||||
i := strings.IndexByte(s, ']')
|
|
||||||
if i == -1 {
|
|
||||||
return "", errors.New("unmatched bracket")
|
|
||||||
}
|
|
||||||
a := strings.TrimSpace(s[:i])
|
|
||||||
s = s[i+1:]
|
|
||||||
if strings.HasPrefix(a, "before ") {
|
|
||||||
l, err := strconv.ParseUint(skipSpace(a[len("before "):]), 10, 3)
|
|
||||||
if err != nil {
|
|
||||||
return s, err
|
|
||||||
}
|
|
||||||
return parseAnchor(p, int(l), s)
|
|
||||||
}
|
|
||||||
return s, p.Reset(fmt.Sprintf(specialAnchor, a), 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseAnchor(p RuleProcessor, level int, s string) (tail string, err error) {
|
|
||||||
anchor, s, err := scanString(s)
|
|
||||||
if err != nil {
|
|
||||||
return s, err
|
|
||||||
}
|
|
||||||
return s, p.Reset(anchor, level)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseOrder(p RuleProcessor, level int, s string) (tail string, err error) {
|
|
||||||
var value, context, extend string
|
|
||||||
if value, s, err = scanString(s); err != nil {
|
|
||||||
return s, err
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(value, cldrIndex) {
|
|
||||||
p.Index(value[len(cldrIndex):])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if consume(&s, '|') {
|
|
||||||
if context, s, err = scanString(s); err != nil {
|
|
||||||
return s, errors.New("missing string after context")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if consume(&s, '/') {
|
|
||||||
if extend, s, err = scanString(s); err != nil {
|
|
||||||
return s, errors.New("missing string after extension")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s, p.Insert(level, value, context, extend)
|
|
||||||
}
|
|
||||||
|
|
||||||
// scanString scans a single input string.
|
|
||||||
func scanString(s string) (str, tail string, err error) {
|
|
||||||
if s = skipSpace(s); s == "" {
|
|
||||||
return s, s, errors.New("missing string")
|
|
||||||
}
|
|
||||||
buf := [16]byte{} // small but enough to hold most cases.
|
|
||||||
value := buf[:0]
|
|
||||||
for s != "" {
|
|
||||||
if consume(&s, '\'') {
|
|
||||||
i := strings.IndexByte(s, '\'')
|
|
||||||
if i == -1 {
|
|
||||||
return "", "", errors.New(`unmatched single quote`)
|
|
||||||
}
|
|
||||||
if i == 0 {
|
|
||||||
value = append(value, '\'')
|
|
||||||
} else {
|
|
||||||
value = append(value, s[:i]...)
|
|
||||||
}
|
|
||||||
s = s[i+1:]
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
r, sz := utf8.DecodeRuneInString(s)
|
|
||||||
if unicode.IsSpace(r) || strings.ContainsRune("&<=#", r) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
value = append(value, s[:sz]...)
|
|
||||||
s = s[sz:]
|
|
||||||
}
|
|
||||||
return string(value), skipSpace(s), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseSequence(p RuleProcessor, level int, s string) (tail string, err error) {
|
|
||||||
if s = skipSpace(s); s == "" {
|
|
||||||
return s, errors.New("empty sequence")
|
|
||||||
}
|
|
||||||
last := rune(0)
|
|
||||||
for s != "" {
|
|
||||||
r, sz := utf8.DecodeRuneInString(s)
|
|
||||||
s = s[sz:]
|
|
||||||
|
|
||||||
if r == '-' {
|
|
||||||
// We have a range. The first element was already written.
|
|
||||||
if last == 0 {
|
|
||||||
return s, errors.New("range without starter value")
|
|
||||||
}
|
|
||||||
r, sz = utf8.DecodeRuneInString(s)
|
|
||||||
s = s[sz:]
|
|
||||||
if r == utf8.RuneError || r < last {
|
|
||||||
return s, fmt.Errorf("invalid range %q-%q", last, r)
|
|
||||||
}
|
|
||||||
for i := last + 1; i <= r; i++ {
|
|
||||||
if err := p.Insert(level, string(i), "", ""); err != nil {
|
|
||||||
return s, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
last = 0
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if unicode.IsSpace(r) || unicode.IsPunct(r) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// normal case
|
|
||||||
if err := p.Insert(level, string(r), "", ""); err != nil {
|
|
||||||
return s, err
|
|
||||||
}
|
|
||||||
last = r
|
|
||||||
}
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func skipSpace(s string) string {
|
|
||||||
return strings.TrimLeftFunc(s, unicode.IsSpace)
|
|
||||||
}
|
|
||||||
|
|
||||||
// consumes returns whether the next byte is ch. If so, it gobbles it by
|
|
||||||
// updating s.
|
|
||||||
func consume(s *string, ch byte) (ok bool) {
|
|
||||||
if *s == "" || (*s)[0] != ch {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
*s = (*s)[1:]
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// The following code parses Collation rules of CLDR version 24 and before.
|
|
||||||
|
|
||||||
var lmap = map[byte]int{
|
|
||||||
'p': 1,
|
|
||||||
's': 2,
|
|
||||||
't': 3,
|
|
||||||
'i': 5,
|
|
||||||
}
|
|
||||||
|
|
||||||
type rulesElem struct {
|
|
||||||
Rules struct {
|
|
||||||
Common
|
|
||||||
Any []*struct {
|
|
||||||
XMLName xml.Name
|
|
||||||
rule
|
|
||||||
} `xml:",any"`
|
|
||||||
} `xml:"rules"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type rule struct {
|
|
||||||
Value string `xml:",chardata"`
|
|
||||||
Before string `xml:"before,attr"`
|
|
||||||
Any []*struct {
|
|
||||||
XMLName xml.Name
|
|
||||||
rule
|
|
||||||
} `xml:",any"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var emptyValueError = errors.New("cldr: empty rule value")
|
|
||||||
|
|
||||||
func (r *rule) value() (string, error) {
|
|
||||||
// Convert hexadecimal Unicode codepoint notation to a string.
|
|
||||||
s := charRe.ReplaceAllStringFunc(r.Value, replaceUnicode)
|
|
||||||
r.Value = s
|
|
||||||
if s == "" {
|
|
||||||
if len(r.Any) != 1 {
|
|
||||||
return "", emptyValueError
|
|
||||||
}
|
|
||||||
r.Value = fmt.Sprintf(specialAnchor, r.Any[0].XMLName.Local)
|
|
||||||
r.Any = nil
|
|
||||||
} else if len(r.Any) != 0 {
|
|
||||||
return "", fmt.Errorf("cldr: XML elements found in collation rule: %v", r.Any)
|
|
||||||
}
|
|
||||||
return r.Value, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r rule) process(p RuleProcessor, name, context, extend string) error {
|
|
||||||
v, err := r.value()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
switch name {
|
|
||||||
case "p", "s", "t", "i":
|
|
||||||
if strings.HasPrefix(v, cldrIndex) {
|
|
||||||
p.Index(v[len(cldrIndex):])
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err := p.Insert(lmap[name[0]], v, context, extend); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case "pc", "sc", "tc", "ic":
|
|
||||||
level := lmap[name[0]]
|
|
||||||
for _, s := range v {
|
|
||||||
if err := p.Insert(level, string(s), context, extend); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("cldr: unsupported tag: %q", name)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// processXML parses the format of CLDR versions 24 and older.
|
|
||||||
func (c Collation) processXML(p RuleProcessor) (err error) {
|
|
||||||
// Collation is generated and defined in xml.go.
|
|
||||||
var v string
|
|
||||||
for _, r := range c.Rules.Any {
|
|
||||||
switch r.XMLName.Local {
|
|
||||||
case "reset":
|
|
||||||
level := 0
|
|
||||||
switch r.Before {
|
|
||||||
case "primary", "1":
|
|
||||||
level = 1
|
|
||||||
case "secondary", "2":
|
|
||||||
level = 2
|
|
||||||
case "tertiary", "3":
|
|
||||||
level = 3
|
|
||||||
case "":
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("cldr: unknown level %q", r.Before)
|
|
||||||
}
|
|
||||||
v, err = r.value()
|
|
||||||
if err == nil {
|
|
||||||
err = p.Reset(v, level)
|
|
||||||
}
|
|
||||||
case "x":
|
|
||||||
var context, extend string
|
|
||||||
for _, r1 := range r.Any {
|
|
||||||
v, err = r1.value()
|
|
||||||
switch r1.XMLName.Local {
|
|
||||||
case "context":
|
|
||||||
context = v
|
|
||||||
case "extend":
|
|
||||||
extend = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, r1 := range r.Any {
|
|
||||||
if t := r1.XMLName.Local; t == "context" || t == "extend" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
r1.rule.process(p, r1.XMLName.Local, context, extend)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
err = r.rule.process(p, r.XMLName.Local, "", "")
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
171
vendor/golang.org/x/text/unicode/cldr/decode.go
generated
vendored
171
vendor/golang.org/x/text/unicode/cldr/decode.go
generated
vendored
|
@ -1,171 +0,0 @@
|
||||||
// Copyright 2013 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 cldr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"archive/zip"
|
|
||||||
"bytes"
|
|
||||||
"encoding/xml"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"regexp"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A Decoder loads an archive of CLDR data.
|
|
||||||
type Decoder struct {
|
|
||||||
dirFilter []string
|
|
||||||
sectionFilter []string
|
|
||||||
loader Loader
|
|
||||||
cldr *CLDR
|
|
||||||
curLocale string
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetSectionFilter takes a list top-level LDML element names to which
|
|
||||||
// evaluation of LDML should be limited. It automatically calls SetDirFilter.
|
|
||||||
func (d *Decoder) SetSectionFilter(filter ...string) {
|
|
||||||
d.sectionFilter = filter
|
|
||||||
// TODO: automatically set dir filter
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetDirFilter limits the loading of LDML XML files of the specied directories.
|
|
||||||
// Note that sections may be split across directories differently for different CLDR versions.
|
|
||||||
// For more robust code, use SetSectionFilter.
|
|
||||||
func (d *Decoder) SetDirFilter(dir ...string) {
|
|
||||||
d.dirFilter = dir
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Loader provides access to the files of a CLDR archive.
|
|
||||||
type Loader interface {
|
|
||||||
Len() int
|
|
||||||
Path(i int) string
|
|
||||||
Reader(i int) (io.ReadCloser, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
var fileRe = regexp.MustCompile(".*/(.*)/(.*)\\.xml")
|
|
||||||
|
|
||||||
// Decode loads and decodes the files represented by l.
|
|
||||||
func (d *Decoder) Decode(l Loader) (cldr *CLDR, err error) {
|
|
||||||
d.cldr = makeCLDR()
|
|
||||||
for i := 0; i < l.Len(); i++ {
|
|
||||||
fname := l.Path(i)
|
|
||||||
if m := fileRe.FindStringSubmatch(fname); m != nil {
|
|
||||||
if len(d.dirFilter) > 0 && !in(d.dirFilter, m[1]) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var r io.Reader
|
|
||||||
if r, err = l.Reader(i); err == nil {
|
|
||||||
err = d.decode(m[1], m[2], r)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
d.cldr.finalize(d.sectionFilter)
|
|
||||||
return d.cldr, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Decoder) decode(dir, id string, r io.Reader) error {
|
|
||||||
var v interface{}
|
|
||||||
var l *LDML
|
|
||||||
cldr := d.cldr
|
|
||||||
switch {
|
|
||||||
case dir == "supplemental":
|
|
||||||
v = cldr.supp
|
|
||||||
case dir == "transforms":
|
|
||||||
return nil
|
|
||||||
case dir == "bcp47":
|
|
||||||
v = cldr.bcp47
|
|
||||||
case dir == "validity":
|
|
||||||
return nil
|
|
||||||
default:
|
|
||||||
ok := false
|
|
||||||
if v, ok = cldr.locale[id]; !ok {
|
|
||||||
l = &LDML{}
|
|
||||||
v, cldr.locale[id] = l, l
|
|
||||||
}
|
|
||||||
}
|
|
||||||
x := xml.NewDecoder(r)
|
|
||||||
if err := x.Decode(v); err != nil {
|
|
||||||
log.Printf("%s/%s: %v", dir, id, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if l != nil {
|
|
||||||
if l.Identity == nil {
|
|
||||||
return fmt.Errorf("%s/%s: missing identity element", dir, id)
|
|
||||||
}
|
|
||||||
// TODO: verify when CLDR bug http://unicode.org/cldr/trac/ticket/8970
|
|
||||||
// is resolved.
|
|
||||||
// path := strings.Split(id, "_")
|
|
||||||
// if lang := l.Identity.Language.Type; lang != path[0] {
|
|
||||||
// return fmt.Errorf("%s/%s: language was %s; want %s", dir, id, lang, path[0])
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type pathLoader []string
|
|
||||||
|
|
||||||
func makePathLoader(path string) (pl pathLoader, err error) {
|
|
||||||
err = filepath.Walk(path, func(path string, _ os.FileInfo, err error) error {
|
|
||||||
pl = append(pl, path)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
return pl, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pl pathLoader) Len() int {
|
|
||||||
return len(pl)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pl pathLoader) Path(i int) string {
|
|
||||||
return pl[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pl pathLoader) Reader(i int) (io.ReadCloser, error) {
|
|
||||||
return os.Open(pl[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodePath loads CLDR data from the given path.
|
|
||||||
func (d *Decoder) DecodePath(path string) (cldr *CLDR, err error) {
|
|
||||||
loader, err := makePathLoader(path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return d.Decode(loader)
|
|
||||||
}
|
|
||||||
|
|
||||||
type zipLoader struct {
|
|
||||||
r *zip.Reader
|
|
||||||
}
|
|
||||||
|
|
||||||
func (zl zipLoader) Len() int {
|
|
||||||
return len(zl.r.File)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (zl zipLoader) Path(i int) string {
|
|
||||||
return zl.r.File[i].Name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (zl zipLoader) Reader(i int) (io.ReadCloser, error) {
|
|
||||||
return zl.r.File[i].Open()
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeZip loads CLDR data from the zip archive for which r is the source.
|
|
||||||
func (d *Decoder) DecodeZip(r io.Reader) (cldr *CLDR, err error) {
|
|
||||||
buffer, err := ioutil.ReadAll(r)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
archive, err := zip.NewReader(bytes.NewReader(buffer), int64(len(buffer)))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return d.Decode(zipLoader{archive})
|
|
||||||
}
|
|
400
vendor/golang.org/x/text/unicode/cldr/makexml.go
generated
vendored
400
vendor/golang.org/x/text/unicode/cldr/makexml.go
generated
vendored
|
@ -1,400 +0,0 @@
|
||||||
// Copyright 2013 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 ignore
|
|
||||||
|
|
||||||
// This tool generates types for the various XML formats of CLDR.
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"archive/zip"
|
|
||||||
"bytes"
|
|
||||||
"encoding/xml"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"golang.org/x/text/internal/gen"
|
|
||||||
)
|
|
||||||
|
|
||||||
var outputFile = flag.String("output", "xml.go", "output file name")
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
r := gen.OpenCLDRCoreZip()
|
|
||||||
buffer, err := ioutil.ReadAll(r)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Could not read zip file")
|
|
||||||
}
|
|
||||||
r.Close()
|
|
||||||
z, err := zip.NewReader(bytes.NewReader(buffer), int64(len(buffer)))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Could not read zip archive: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
|
||||||
|
|
||||||
version := gen.CLDRVersion()
|
|
||||||
|
|
||||||
for _, dtd := range files {
|
|
||||||
for _, f := range z.File {
|
|
||||||
if strings.HasSuffix(f.Name, dtd.file+".dtd") {
|
|
||||||
r, err := f.Open()
|
|
||||||
failOnError(err)
|
|
||||||
|
|
||||||
b := makeBuilder(&buf, dtd)
|
|
||||||
b.parseDTD(r)
|
|
||||||
b.resolve(b.index[dtd.top[0]])
|
|
||||||
b.write()
|
|
||||||
if b.version != "" && version != b.version {
|
|
||||||
println(f.Name)
|
|
||||||
log.Fatalf("main: inconsistent versions: found %s; want %s", b.version, version)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Fprintln(&buf, "// Version is the version of CLDR from which the XML definitions are generated.")
|
|
||||||
fmt.Fprintf(&buf, "const Version = %q\n", version)
|
|
||||||
|
|
||||||
gen.WriteGoFile(*outputFile, "cldr", buf.Bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
func failOnError(err error) {
|
|
||||||
if err != nil {
|
|
||||||
log.New(os.Stderr, "", log.Lshortfile).Output(2, err.Error())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// configuration data per DTD type
|
|
||||||
type dtd struct {
|
|
||||||
file string // base file name
|
|
||||||
root string // Go name of the root XML element
|
|
||||||
top []string // create a different type for this section
|
|
||||||
|
|
||||||
skipElem []string // hard-coded or deprecated elements
|
|
||||||
skipAttr []string // attributes to exclude
|
|
||||||
predefined []string // hard-coded elements exist of the form <name>Elem
|
|
||||||
forceRepeat []string // elements to make slices despite DTD
|
|
||||||
}
|
|
||||||
|
|
||||||
var files = []dtd{
|
|
||||||
{
|
|
||||||
file: "ldmlBCP47",
|
|
||||||
root: "LDMLBCP47",
|
|
||||||
top: []string{"ldmlBCP47"},
|
|
||||||
skipElem: []string{
|
|
||||||
"cldrVersion", // deprecated, not used
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
file: "ldmlSupplemental",
|
|
||||||
root: "SupplementalData",
|
|
||||||
top: []string{"supplementalData"},
|
|
||||||
skipElem: []string{
|
|
||||||
"cldrVersion", // deprecated, not used
|
|
||||||
},
|
|
||||||
forceRepeat: []string{
|
|
||||||
"plurals", // data defined in plurals.xml and ordinals.xml
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
file: "ldml",
|
|
||||||
root: "LDML",
|
|
||||||
top: []string{
|
|
||||||
"ldml", "collation", "calendar", "timeZoneNames", "localeDisplayNames", "numbers",
|
|
||||||
},
|
|
||||||
skipElem: []string{
|
|
||||||
"cp", // not used anywhere
|
|
||||||
"special", // not used anywhere
|
|
||||||
"fallback", // deprecated, not used
|
|
||||||
"alias", // in Common
|
|
||||||
"default", // in Common
|
|
||||||
},
|
|
||||||
skipAttr: []string{
|
|
||||||
"hiraganaQuarternary", // typo in DTD, correct version included as well
|
|
||||||
},
|
|
||||||
predefined: []string{"rules"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var comments = map[string]string{
|
|
||||||
"ldmlBCP47": `
|
|
||||||
// LDMLBCP47 holds information on allowable values for various variables in LDML.
|
|
||||||
`,
|
|
||||||
"supplementalData": `
|
|
||||||
// SupplementalData holds information relevant for internationalization
|
|
||||||
// and proper use of CLDR, but that is not contained in the locale hierarchy.
|
|
||||||
`,
|
|
||||||
"ldml": `
|
|
||||||
// LDML is the top-level type for locale-specific data.
|
|
||||||
`,
|
|
||||||
"collation": `
|
|
||||||
// Collation contains rules that specify a certain sort-order,
|
|
||||||
// as a tailoring of the root order.
|
|
||||||
// The parsed rules are obtained by passing a RuleProcessor to Collation's
|
|
||||||
// Process method.
|
|
||||||
`,
|
|
||||||
"calendar": `
|
|
||||||
// Calendar specifies the fields used for formatting and parsing dates and times.
|
|
||||||
// The month and quarter names are identified numerically, starting at 1.
|
|
||||||
// The day (of the week) names are identified with short strings, since there is
|
|
||||||
// no universally-accepted numeric designation.
|
|
||||||
`,
|
|
||||||
"dates": `
|
|
||||||
// Dates contains information regarding the format and parsing of dates and times.
|
|
||||||
`,
|
|
||||||
"localeDisplayNames": `
|
|
||||||
// LocaleDisplayNames specifies localized display names for for scripts, languages,
|
|
||||||
// countries, currencies, and variants.
|
|
||||||
`,
|
|
||||||
"numbers": `
|
|
||||||
// Numbers supplies information for formatting and parsing numbers and currencies.
|
|
||||||
`,
|
|
||||||
}
|
|
||||||
|
|
||||||
type element struct {
|
|
||||||
name string // XML element name
|
|
||||||
category string // elements contained by this element
|
|
||||||
signature string // category + attrKey*
|
|
||||||
|
|
||||||
attr []*attribute // attributes supported by this element.
|
|
||||||
sub []struct { // parsed and evaluated sub elements of this element.
|
|
||||||
e *element
|
|
||||||
repeat bool // true if the element needs to be a slice
|
|
||||||
}
|
|
||||||
|
|
||||||
resolved bool // prevent multiple resolutions of this element.
|
|
||||||
}
|
|
||||||
|
|
||||||
type attribute struct {
|
|
||||||
name string
|
|
||||||
key string
|
|
||||||
list []string
|
|
||||||
|
|
||||||
tag string // Go tag
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
reHead = regexp.MustCompile(` *(\w+) +([\w\-]+)`)
|
|
||||||
reAttr = regexp.MustCompile(` *(\w+) *(?:(\w+)|\(([\w\- \|]+)\)) *(?:#([A-Z]*) *(?:\"([\.\d+])\")?)? *("[\w\-:]*")?`)
|
|
||||||
reElem = regexp.MustCompile(`^ *(EMPTY|ANY|\(.*\)[\*\+\?]?) *$`)
|
|
||||||
reToken = regexp.MustCompile(`\w\-`)
|
|
||||||
)
|
|
||||||
|
|
||||||
// builder is used to read in the DTD files from CLDR and generate Go code
|
|
||||||
// to be used with the encoding/xml package.
|
|
||||||
type builder struct {
|
|
||||||
w io.Writer
|
|
||||||
index map[string]*element
|
|
||||||
elem []*element
|
|
||||||
info dtd
|
|
||||||
version string
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeBuilder(w io.Writer, d dtd) builder {
|
|
||||||
return builder{
|
|
||||||
w: w,
|
|
||||||
index: make(map[string]*element),
|
|
||||||
elem: []*element{},
|
|
||||||
info: d,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseDTD parses a DTD file.
|
|
||||||
func (b *builder) parseDTD(r io.Reader) {
|
|
||||||
for d := xml.NewDecoder(r); ; {
|
|
||||||
t, err := d.Token()
|
|
||||||
if t == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
failOnError(err)
|
|
||||||
dir, ok := t.(xml.Directive)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
m := reHead.FindSubmatch(dir)
|
|
||||||
dir = dir[len(m[0]):]
|
|
||||||
ename := string(m[2])
|
|
||||||
el, elementFound := b.index[ename]
|
|
||||||
switch string(m[1]) {
|
|
||||||
case "ELEMENT":
|
|
||||||
if elementFound {
|
|
||||||
log.Fatal("parseDTD: duplicate entry for element %q", ename)
|
|
||||||
}
|
|
||||||
m := reElem.FindSubmatch(dir)
|
|
||||||
if m == nil {
|
|
||||||
log.Fatalf("parseDTD: invalid element %q", string(dir))
|
|
||||||
}
|
|
||||||
if len(m[0]) != len(dir) {
|
|
||||||
log.Fatal("parseDTD: invalid element %q", string(dir), len(dir), len(m[0]), string(m[0]))
|
|
||||||
}
|
|
||||||
s := string(m[1])
|
|
||||||
el = &element{
|
|
||||||
name: ename,
|
|
||||||
category: s,
|
|
||||||
}
|
|
||||||
b.index[ename] = el
|
|
||||||
case "ATTLIST":
|
|
||||||
if !elementFound {
|
|
||||||
log.Fatalf("parseDTD: unknown element %q", ename)
|
|
||||||
}
|
|
||||||
s := string(dir)
|
|
||||||
m := reAttr.FindStringSubmatch(s)
|
|
||||||
if m == nil {
|
|
||||||
log.Fatal(fmt.Errorf("parseDTD: invalid attribute %q", string(dir)))
|
|
||||||
}
|
|
||||||
if m[4] == "FIXED" {
|
|
||||||
b.version = m[5]
|
|
||||||
} else {
|
|
||||||
switch m[1] {
|
|
||||||
case "draft", "references", "alt", "validSubLocales", "standard" /* in Common */ :
|
|
||||||
case "type", "choice":
|
|
||||||
default:
|
|
||||||
el.attr = append(el.attr, &attribute{
|
|
||||||
name: m[1],
|
|
||||||
key: s,
|
|
||||||
list: reToken.FindAllString(m[3], -1),
|
|
||||||
})
|
|
||||||
el.signature = fmt.Sprintf("%s=%s+%s", el.signature, m[1], m[2])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var reCat = regexp.MustCompile(`[ ,\|]*(?:(\(|\)|\#?[\w_-]+)([\*\+\?]?))?`)
|
|
||||||
|
|
||||||
// resolve takes a parsed element and converts it into structured data
|
|
||||||
// that can be used to generate the XML code.
|
|
||||||
func (b *builder) resolve(e *element) {
|
|
||||||
if e.resolved {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
b.elem = append(b.elem, e)
|
|
||||||
e.resolved = true
|
|
||||||
s := e.category
|
|
||||||
found := make(map[string]bool)
|
|
||||||
sequenceStart := []int{}
|
|
||||||
for len(s) > 0 {
|
|
||||||
m := reCat.FindStringSubmatch(s)
|
|
||||||
if m == nil {
|
|
||||||
log.Fatalf("%s: invalid category string %q", e.name, s)
|
|
||||||
}
|
|
||||||
repeat := m[2] == "*" || m[2] == "+" || in(b.info.forceRepeat, m[1])
|
|
||||||
switch m[1] {
|
|
||||||
case "":
|
|
||||||
case "(":
|
|
||||||
sequenceStart = append(sequenceStart, len(e.sub))
|
|
||||||
case ")":
|
|
||||||
if len(sequenceStart) == 0 {
|
|
||||||
log.Fatalf("%s: unmatched closing parenthesis", e.name)
|
|
||||||
}
|
|
||||||
for i := sequenceStart[len(sequenceStart)-1]; i < len(e.sub); i++ {
|
|
||||||
e.sub[i].repeat = e.sub[i].repeat || repeat
|
|
||||||
}
|
|
||||||
sequenceStart = sequenceStart[:len(sequenceStart)-1]
|
|
||||||
default:
|
|
||||||
if in(b.info.skipElem, m[1]) {
|
|
||||||
} else if sub, ok := b.index[m[1]]; ok {
|
|
||||||
if !found[sub.name] {
|
|
||||||
e.sub = append(e.sub, struct {
|
|
||||||
e *element
|
|
||||||
repeat bool
|
|
||||||
}{sub, repeat})
|
|
||||||
found[sub.name] = true
|
|
||||||
b.resolve(sub)
|
|
||||||
}
|
|
||||||
} else if m[1] == "#PCDATA" || m[1] == "ANY" {
|
|
||||||
} else if m[1] != "EMPTY" {
|
|
||||||
log.Fatalf("resolve:%s: element %q not found", e.name, m[1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s = s[len(m[0]):]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// return true if s is contained in set.
|
|
||||||
func in(set []string, s string) bool {
|
|
||||||
for _, v := range set {
|
|
||||||
if v == s {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
var repl = strings.NewReplacer("-", " ", "_", " ")
|
|
||||||
|
|
||||||
// title puts the first character or each character following '_' in title case and
|
|
||||||
// removes all occurrences of '_'.
|
|
||||||
func title(s string) string {
|
|
||||||
return strings.Replace(strings.Title(repl.Replace(s)), " ", "", -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// writeElem generates Go code for a single element, recursively.
|
|
||||||
func (b *builder) writeElem(tab int, e *element) {
|
|
||||||
p := func(f string, x ...interface{}) {
|
|
||||||
f = strings.Replace(f, "\n", "\n"+strings.Repeat("\t", tab), -1)
|
|
||||||
fmt.Fprintf(b.w, f, x...)
|
|
||||||
}
|
|
||||||
if len(e.sub) == 0 && len(e.attr) == 0 {
|
|
||||||
p("Common")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p("struct {")
|
|
||||||
tab++
|
|
||||||
p("\nCommon")
|
|
||||||
for _, attr := range e.attr {
|
|
||||||
if !in(b.info.skipAttr, attr.name) {
|
|
||||||
p("\n%s string `xml:\"%s,attr\"`", title(attr.name), attr.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, sub := range e.sub {
|
|
||||||
if in(b.info.predefined, sub.e.name) {
|
|
||||||
p("\n%sElem", sub.e.name)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if in(b.info.skipElem, sub.e.name) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
p("\n%s ", title(sub.e.name))
|
|
||||||
if sub.repeat {
|
|
||||||
p("[]")
|
|
||||||
}
|
|
||||||
p("*")
|
|
||||||
if in(b.info.top, sub.e.name) {
|
|
||||||
p(title(sub.e.name))
|
|
||||||
} else {
|
|
||||||
b.writeElem(tab, sub.e)
|
|
||||||
}
|
|
||||||
p(" `xml:\"%s\"`", sub.e.name)
|
|
||||||
}
|
|
||||||
tab--
|
|
||||||
p("\n}")
|
|
||||||
}
|
|
||||||
|
|
||||||
// write generates the Go XML code.
|
|
||||||
func (b *builder) write() {
|
|
||||||
for i, name := range b.info.top {
|
|
||||||
e := b.index[name]
|
|
||||||
if e != nil {
|
|
||||||
fmt.Fprintf(b.w, comments[name])
|
|
||||||
name := title(e.name)
|
|
||||||
if i == 0 {
|
|
||||||
name = b.info.root
|
|
||||||
}
|
|
||||||
fmt.Fprintf(b.w, "type %s ", name)
|
|
||||||
b.writeElem(0, e)
|
|
||||||
fmt.Fprint(b.w, "\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
602
vendor/golang.org/x/text/unicode/cldr/resolve.go
generated
vendored
602
vendor/golang.org/x/text/unicode/cldr/resolve.go
generated
vendored
|
@ -1,602 +0,0 @@
|
||||||
// Copyright 2013 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 cldr
|
|
||||||
|
|
||||||
// This file implements the various inheritance constructs defined by LDML.
|
|
||||||
// See http://www.unicode.org/reports/tr35/#Inheritance_and_Validity
|
|
||||||
// for more details.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"reflect"
|
|
||||||
"regexp"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// fieldIter iterates over fields in a struct. It includes
|
|
||||||
// fields of embedded structs.
|
|
||||||
type fieldIter struct {
|
|
||||||
v reflect.Value
|
|
||||||
index, n []int
|
|
||||||
}
|
|
||||||
|
|
||||||
func iter(v reflect.Value) fieldIter {
|
|
||||||
if v.Kind() != reflect.Struct {
|
|
||||||
log.Panicf("value %v must be a struct", v)
|
|
||||||
}
|
|
||||||
i := fieldIter{
|
|
||||||
v: v,
|
|
||||||
index: []int{0},
|
|
||||||
n: []int{v.NumField()},
|
|
||||||
}
|
|
||||||
i.descent()
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *fieldIter) descent() {
|
|
||||||
for f := i.field(); f.Anonymous && f.Type.NumField() > 0; f = i.field() {
|
|
||||||
i.index = append(i.index, 0)
|
|
||||||
i.n = append(i.n, f.Type.NumField())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *fieldIter) done() bool {
|
|
||||||
return len(i.index) == 1 && i.index[0] >= i.n[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
func skip(f reflect.StructField) bool {
|
|
||||||
return !f.Anonymous && (f.Name[0] < 'A' || f.Name[0] > 'Z')
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *fieldIter) next() {
|
|
||||||
for {
|
|
||||||
k := len(i.index) - 1
|
|
||||||
i.index[k]++
|
|
||||||
if i.index[k] < i.n[k] {
|
|
||||||
if !skip(i.field()) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if k == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
i.index = i.index[:k]
|
|
||||||
i.n = i.n[:k]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i.descent()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *fieldIter) value() reflect.Value {
|
|
||||||
return i.v.FieldByIndex(i.index)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *fieldIter) field() reflect.StructField {
|
|
||||||
return i.v.Type().FieldByIndex(i.index)
|
|
||||||
}
|
|
||||||
|
|
||||||
type visitor func(v reflect.Value) error
|
|
||||||
|
|
||||||
var stopDescent = fmt.Errorf("do not recurse")
|
|
||||||
|
|
||||||
func (f visitor) visit(x interface{}) error {
|
|
||||||
return f.visitRec(reflect.ValueOf(x))
|
|
||||||
}
|
|
||||||
|
|
||||||
// visit recursively calls f on all nodes in v.
|
|
||||||
func (f visitor) visitRec(v reflect.Value) error {
|
|
||||||
if v.Kind() == reflect.Ptr {
|
|
||||||
if v.IsNil() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return f.visitRec(v.Elem())
|
|
||||||
}
|
|
||||||
if err := f(v); err != nil {
|
|
||||||
if err == stopDescent {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Struct:
|
|
||||||
for i := iter(v); !i.done(); i.next() {
|
|
||||||
if err := f.visitRec(i.value()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case reflect.Slice:
|
|
||||||
for i := 0; i < v.Len(); i++ {
|
|
||||||
if err := f.visitRec(v.Index(i)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getPath is used for error reporting purposes only.
|
|
||||||
func getPath(e Elem) string {
|
|
||||||
if e == nil {
|
|
||||||
return "<nil>"
|
|
||||||
}
|
|
||||||
if e.enclosing() == nil {
|
|
||||||
return e.GetCommon().name
|
|
||||||
}
|
|
||||||
if e.GetCommon().Type == "" {
|
|
||||||
return fmt.Sprintf("%s.%s", getPath(e.enclosing()), e.GetCommon().name)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s.%s[type=%s]", getPath(e.enclosing()), e.GetCommon().name, e.GetCommon().Type)
|
|
||||||
}
|
|
||||||
|
|
||||||
// xmlName returns the xml name of the element or attribute
|
|
||||||
func xmlName(f reflect.StructField) (name string, attr bool) {
|
|
||||||
tags := strings.Split(f.Tag.Get("xml"), ",")
|
|
||||||
for _, s := range tags {
|
|
||||||
attr = attr || s == "attr"
|
|
||||||
}
|
|
||||||
return tags[0], attr
|
|
||||||
}
|
|
||||||
|
|
||||||
func findField(v reflect.Value, key string) (reflect.Value, error) {
|
|
||||||
v = reflect.Indirect(v)
|
|
||||||
for i := iter(v); !i.done(); i.next() {
|
|
||||||
if n, _ := xmlName(i.field()); n == key {
|
|
||||||
return i.value(), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return reflect.Value{}, fmt.Errorf("cldr: no field %q in element %#v", key, v.Interface())
|
|
||||||
}
|
|
||||||
|
|
||||||
var xpathPart = regexp.MustCompile(`(\pL+)(?:\[@(\pL+)='([\w-]+)'\])?`)
|
|
||||||
|
|
||||||
func walkXPath(e Elem, path string) (res Elem, err error) {
|
|
||||||
for _, c := range strings.Split(path, "/") {
|
|
||||||
if c == ".." {
|
|
||||||
if e = e.enclosing(); e == nil {
|
|
||||||
panic("path ..")
|
|
||||||
return nil, fmt.Errorf(`cldr: ".." moves past root in path %q`, path)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
} else if c == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
m := xpathPart.FindStringSubmatch(c)
|
|
||||||
if len(m) == 0 || len(m[0]) != len(c) {
|
|
||||||
return nil, fmt.Errorf("cldr: syntax error in path component %q", c)
|
|
||||||
}
|
|
||||||
v, err := findField(reflect.ValueOf(e), m[1])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Slice:
|
|
||||||
i := 0
|
|
||||||
if m[2] != "" || v.Len() > 1 {
|
|
||||||
if m[2] == "" {
|
|
||||||
m[2] = "type"
|
|
||||||
if m[3] = e.GetCommon().Default(); m[3] == "" {
|
|
||||||
return nil, fmt.Errorf("cldr: type selector or default value needed for element %s", m[1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for ; i < v.Len(); i++ {
|
|
||||||
vi := v.Index(i)
|
|
||||||
key, err := findField(vi.Elem(), m[2])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
key = reflect.Indirect(key)
|
|
||||||
if key.Kind() == reflect.String && key.String() == m[3] {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if i == v.Len() || v.Index(i).IsNil() {
|
|
||||||
return nil, fmt.Errorf("no %s found with %s==%s", m[1], m[2], m[3])
|
|
||||||
}
|
|
||||||
e = v.Index(i).Interface().(Elem)
|
|
||||||
case reflect.Ptr:
|
|
||||||
if v.IsNil() {
|
|
||||||
return nil, fmt.Errorf("cldr: element %q not found within element %q", m[1], e.GetCommon().name)
|
|
||||||
}
|
|
||||||
var ok bool
|
|
||||||
if e, ok = v.Interface().(Elem); !ok {
|
|
||||||
return nil, fmt.Errorf("cldr: %q is not an XML element", m[1])
|
|
||||||
} else if m[2] != "" || m[3] != "" {
|
|
||||||
return nil, fmt.Errorf("cldr: no type selector allowed for element %s", m[1])
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("cldr: %q is not an XML element", m[1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return e, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
const absPrefix = "//ldml/"
|
|
||||||
|
|
||||||
func (cldr *CLDR) resolveAlias(e Elem, src, path string) (res Elem, err error) {
|
|
||||||
if src != "locale" {
|
|
||||||
if !strings.HasPrefix(path, absPrefix) {
|
|
||||||
return nil, fmt.Errorf("cldr: expected absolute path, found %q", path)
|
|
||||||
}
|
|
||||||
path = path[len(absPrefix):]
|
|
||||||
if e, err = cldr.resolve(src); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return walkXPath(e, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cldr *CLDR) resolveAndMergeAlias(e Elem) error {
|
|
||||||
alias := e.GetCommon().Alias
|
|
||||||
if alias == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
a, err := cldr.resolveAlias(e, alias.Source, alias.Path)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("%v: error evaluating path %q: %v", getPath(e), alias.Path, err)
|
|
||||||
}
|
|
||||||
// Ensure alias node was already evaluated. TODO: avoid double evaluation.
|
|
||||||
err = cldr.resolveAndMergeAlias(a)
|
|
||||||
v := reflect.ValueOf(e).Elem()
|
|
||||||
for i := iter(reflect.ValueOf(a).Elem()); !i.done(); i.next() {
|
|
||||||
if vv := i.value(); vv.Kind() != reflect.Ptr || !vv.IsNil() {
|
|
||||||
if _, attr := xmlName(i.field()); !attr {
|
|
||||||
v.FieldByIndex(i.index).Set(vv)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cldr *CLDR) aliasResolver() visitor {
|
|
||||||
return func(v reflect.Value) (err error) {
|
|
||||||
if e, ok := v.Addr().Interface().(Elem); ok {
|
|
||||||
err = cldr.resolveAndMergeAlias(e)
|
|
||||||
if err == nil && blocking[e.GetCommon().name] {
|
|
||||||
return stopDescent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// elements within blocking elements do not inherit.
|
|
||||||
// Taken from CLDR's supplementalMetaData.xml.
|
|
||||||
var blocking = map[string]bool{
|
|
||||||
"identity": true,
|
|
||||||
"supplementalData": true,
|
|
||||||
"cldrTest": true,
|
|
||||||
"collation": true,
|
|
||||||
"transform": true,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Distinguishing attributes affect inheritance; two elements with different
|
|
||||||
// distinguishing attributes are treated as different for purposes of inheritance,
|
|
||||||
// except when such attributes occur in the indicated elements.
|
|
||||||
// Taken from CLDR's supplementalMetaData.xml.
|
|
||||||
var distinguishing = map[string][]string{
|
|
||||||
"key": nil,
|
|
||||||
"request_id": nil,
|
|
||||||
"id": nil,
|
|
||||||
"registry": nil,
|
|
||||||
"alt": nil,
|
|
||||||
"iso4217": nil,
|
|
||||||
"iso3166": nil,
|
|
||||||
"mzone": nil,
|
|
||||||
"from": nil,
|
|
||||||
"to": nil,
|
|
||||||
"type": []string{
|
|
||||||
"abbreviationFallback",
|
|
||||||
"default",
|
|
||||||
"mapping",
|
|
||||||
"measurementSystem",
|
|
||||||
"preferenceOrdering",
|
|
||||||
},
|
|
||||||
"numberSystem": nil,
|
|
||||||
}
|
|
||||||
|
|
||||||
func in(set []string, s string) bool {
|
|
||||||
for _, v := range set {
|
|
||||||
if v == s {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// attrKey computes a key based on the distinguishable attributes of
|
|
||||||
// an element and it's values.
|
|
||||||
func attrKey(v reflect.Value, exclude ...string) string {
|
|
||||||
parts := []string{}
|
|
||||||
ename := v.Interface().(Elem).GetCommon().name
|
|
||||||
v = v.Elem()
|
|
||||||
for i := iter(v); !i.done(); i.next() {
|
|
||||||
if name, attr := xmlName(i.field()); attr {
|
|
||||||
if except, ok := distinguishing[name]; ok && !in(exclude, name) && !in(except, ename) {
|
|
||||||
v := i.value()
|
|
||||||
if v.Kind() == reflect.Ptr {
|
|
||||||
v = v.Elem()
|
|
||||||
}
|
|
||||||
if v.IsValid() {
|
|
||||||
parts = append(parts, fmt.Sprintf("%s=%s", name, v.String()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sort.Strings(parts)
|
|
||||||
return strings.Join(parts, ";")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Key returns a key for e derived from all distinguishing attributes
|
|
||||||
// except those specified by exclude.
|
|
||||||
func Key(e Elem, exclude ...string) string {
|
|
||||||
return attrKey(reflect.ValueOf(e), exclude...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// linkEnclosing sets the enclosing element as well as the name
|
|
||||||
// for all sub-elements of child, recursively.
|
|
||||||
func linkEnclosing(parent, child Elem) {
|
|
||||||
child.setEnclosing(parent)
|
|
||||||
v := reflect.ValueOf(child).Elem()
|
|
||||||
for i := iter(v); !i.done(); i.next() {
|
|
||||||
vf := i.value()
|
|
||||||
if vf.Kind() == reflect.Slice {
|
|
||||||
for j := 0; j < vf.Len(); j++ {
|
|
||||||
linkEnclosing(child, vf.Index(j).Interface().(Elem))
|
|
||||||
}
|
|
||||||
} else if vf.Kind() == reflect.Ptr && !vf.IsNil() && vf.Elem().Kind() == reflect.Struct {
|
|
||||||
linkEnclosing(child, vf.Interface().(Elem))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func setNames(e Elem, name string) {
|
|
||||||
e.setName(name)
|
|
||||||
v := reflect.ValueOf(e).Elem()
|
|
||||||
for i := iter(v); !i.done(); i.next() {
|
|
||||||
vf := i.value()
|
|
||||||
name, _ = xmlName(i.field())
|
|
||||||
if vf.Kind() == reflect.Slice {
|
|
||||||
for j := 0; j < vf.Len(); j++ {
|
|
||||||
setNames(vf.Index(j).Interface().(Elem), name)
|
|
||||||
}
|
|
||||||
} else if vf.Kind() == reflect.Ptr && !vf.IsNil() && vf.Elem().Kind() == reflect.Struct {
|
|
||||||
setNames(vf.Interface().(Elem), name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// deepCopy copies elements of v recursively. All elements of v that may
|
|
||||||
// be modified by inheritance are explicitly copied.
|
|
||||||
func deepCopy(v reflect.Value) reflect.Value {
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Ptr:
|
|
||||||
if v.IsNil() || v.Elem().Kind() != reflect.Struct {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
nv := reflect.New(v.Elem().Type())
|
|
||||||
nv.Elem().Set(v.Elem())
|
|
||||||
deepCopyRec(nv.Elem(), v.Elem())
|
|
||||||
return nv
|
|
||||||
case reflect.Slice:
|
|
||||||
nv := reflect.MakeSlice(v.Type(), v.Len(), v.Len())
|
|
||||||
for i := 0; i < v.Len(); i++ {
|
|
||||||
deepCopyRec(nv.Index(i), v.Index(i))
|
|
||||||
}
|
|
||||||
return nv
|
|
||||||
}
|
|
||||||
panic("deepCopy: must be called with pointer or slice")
|
|
||||||
}
|
|
||||||
|
|
||||||
// deepCopyRec is only called by deepCopy.
|
|
||||||
func deepCopyRec(nv, v reflect.Value) {
|
|
||||||
if v.Kind() == reflect.Struct {
|
|
||||||
t := v.Type()
|
|
||||||
for i := 0; i < v.NumField(); i++ {
|
|
||||||
if name, attr := xmlName(t.Field(i)); name != "" && !attr {
|
|
||||||
deepCopyRec(nv.Field(i), v.Field(i))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
nv.Set(deepCopy(v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// newNode is used to insert a missing node during inheritance.
|
|
||||||
func (cldr *CLDR) newNode(v, enc reflect.Value) reflect.Value {
|
|
||||||
n := reflect.New(v.Type())
|
|
||||||
for i := iter(v); !i.done(); i.next() {
|
|
||||||
if name, attr := xmlName(i.field()); name == "" || attr {
|
|
||||||
n.Elem().FieldByIndex(i.index).Set(i.value())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
n.Interface().(Elem).GetCommon().setEnclosing(enc.Addr().Interface().(Elem))
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
// v, parent must be pointers to struct
|
|
||||||
func (cldr *CLDR) inheritFields(v, parent reflect.Value) (res reflect.Value, err error) {
|
|
||||||
t := v.Type()
|
|
||||||
nv := reflect.New(t)
|
|
||||||
nv.Elem().Set(v)
|
|
||||||
for i := iter(v); !i.done(); i.next() {
|
|
||||||
vf := i.value()
|
|
||||||
f := i.field()
|
|
||||||
name, attr := xmlName(f)
|
|
||||||
if name == "" || attr {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
pf := parent.FieldByIndex(i.index)
|
|
||||||
if blocking[name] {
|
|
||||||
if vf.IsNil() {
|
|
||||||
vf = pf
|
|
||||||
}
|
|
||||||
nv.Elem().FieldByIndex(i.index).Set(deepCopy(vf))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
switch f.Type.Kind() {
|
|
||||||
case reflect.Ptr:
|
|
||||||
if f.Type.Elem().Kind() == reflect.Struct {
|
|
||||||
if !vf.IsNil() {
|
|
||||||
if vf, err = cldr.inheritStructPtr(vf, pf); err != nil {
|
|
||||||
return reflect.Value{}, err
|
|
||||||
}
|
|
||||||
vf.Interface().(Elem).setEnclosing(nv.Interface().(Elem))
|
|
||||||
nv.Elem().FieldByIndex(i.index).Set(vf)
|
|
||||||
} else if !pf.IsNil() {
|
|
||||||
n := cldr.newNode(pf.Elem(), v)
|
|
||||||
if vf, err = cldr.inheritStructPtr(n, pf); err != nil {
|
|
||||||
return reflect.Value{}, err
|
|
||||||
}
|
|
||||||
vf.Interface().(Elem).setEnclosing(nv.Interface().(Elem))
|
|
||||||
nv.Elem().FieldByIndex(i.index).Set(vf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case reflect.Slice:
|
|
||||||
vf, err := cldr.inheritSlice(nv.Elem(), vf, pf)
|
|
||||||
if err != nil {
|
|
||||||
return reflect.Zero(t), err
|
|
||||||
}
|
|
||||||
nv.Elem().FieldByIndex(i.index).Set(vf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nv, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func root(e Elem) *LDML {
|
|
||||||
for ; e.enclosing() != nil; e = e.enclosing() {
|
|
||||||
}
|
|
||||||
return e.(*LDML)
|
|
||||||
}
|
|
||||||
|
|
||||||
// inheritStructPtr first merges possible aliases in with v and then inherits
|
|
||||||
// any underspecified elements from parent.
|
|
||||||
func (cldr *CLDR) inheritStructPtr(v, parent reflect.Value) (r reflect.Value, err error) {
|
|
||||||
if !v.IsNil() {
|
|
||||||
e := v.Interface().(Elem).GetCommon()
|
|
||||||
alias := e.Alias
|
|
||||||
if alias == nil && !parent.IsNil() {
|
|
||||||
alias = parent.Interface().(Elem).GetCommon().Alias
|
|
||||||
}
|
|
||||||
if alias != nil {
|
|
||||||
a, err := cldr.resolveAlias(v.Interface().(Elem), alias.Source, alias.Path)
|
|
||||||
if a != nil {
|
|
||||||
if v, err = cldr.inheritFields(v.Elem(), reflect.ValueOf(a).Elem()); err != nil {
|
|
||||||
return reflect.Value{}, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !parent.IsNil() {
|
|
||||||
return cldr.inheritFields(v.Elem(), parent.Elem())
|
|
||||||
}
|
|
||||||
} else if parent.IsNil() {
|
|
||||||
panic("should not reach here")
|
|
||||||
}
|
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must be slice of struct pointers.
|
|
||||||
func (cldr *CLDR) inheritSlice(enc, v, parent reflect.Value) (res reflect.Value, err error) {
|
|
||||||
t := v.Type()
|
|
||||||
index := make(map[string]reflect.Value)
|
|
||||||
if !v.IsNil() {
|
|
||||||
for i := 0; i < v.Len(); i++ {
|
|
||||||
vi := v.Index(i)
|
|
||||||
key := attrKey(vi)
|
|
||||||
index[key] = vi
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !parent.IsNil() {
|
|
||||||
for i := 0; i < parent.Len(); i++ {
|
|
||||||
vi := parent.Index(i)
|
|
||||||
key := attrKey(vi)
|
|
||||||
if w, ok := index[key]; ok {
|
|
||||||
index[key], err = cldr.inheritStructPtr(w, vi)
|
|
||||||
} else {
|
|
||||||
n := cldr.newNode(vi.Elem(), enc)
|
|
||||||
index[key], err = cldr.inheritStructPtr(n, vi)
|
|
||||||
}
|
|
||||||
index[key].Interface().(Elem).setEnclosing(enc.Addr().Interface().(Elem))
|
|
||||||
if err != nil {
|
|
||||||
return v, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
keys := make([]string, 0, len(index))
|
|
||||||
for k, _ := range index {
|
|
||||||
keys = append(keys, k)
|
|
||||||
}
|
|
||||||
sort.Strings(keys)
|
|
||||||
sl := reflect.MakeSlice(t, len(index), len(index))
|
|
||||||
for i, k := range keys {
|
|
||||||
sl.Index(i).Set(index[k])
|
|
||||||
}
|
|
||||||
return sl, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parentLocale(loc string) string {
|
|
||||||
parts := strings.Split(loc, "_")
|
|
||||||
if len(parts) == 1 {
|
|
||||||
return "root"
|
|
||||||
}
|
|
||||||
parts = parts[:len(parts)-1]
|
|
||||||
key := strings.Join(parts, "_")
|
|
||||||
return key
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cldr *CLDR) resolve(loc string) (res *LDML, err error) {
|
|
||||||
if r := cldr.resolved[loc]; r != nil {
|
|
||||||
return r, nil
|
|
||||||
}
|
|
||||||
x := cldr.RawLDML(loc)
|
|
||||||
if x == nil {
|
|
||||||
return nil, fmt.Errorf("cldr: unknown locale %q", loc)
|
|
||||||
}
|
|
||||||
var v reflect.Value
|
|
||||||
if loc == "root" {
|
|
||||||
x = deepCopy(reflect.ValueOf(x)).Interface().(*LDML)
|
|
||||||
linkEnclosing(nil, x)
|
|
||||||
err = cldr.aliasResolver().visit(x)
|
|
||||||
} else {
|
|
||||||
key := parentLocale(loc)
|
|
||||||
var parent *LDML
|
|
||||||
for ; cldr.locale[key] == nil; key = parentLocale(key) {
|
|
||||||
}
|
|
||||||
if parent, err = cldr.resolve(key); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
v, err = cldr.inheritFields(reflect.ValueOf(x).Elem(), reflect.ValueOf(parent).Elem())
|
|
||||||
x = v.Interface().(*LDML)
|
|
||||||
linkEnclosing(nil, x)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
cldr.resolved[loc] = x
|
|
||||||
return x, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// finalize finalizes the initialization of the raw LDML structs. It also
|
|
||||||
// removed unwanted fields, as specified by filter, so that they will not
|
|
||||||
// be unnecessarily evaluated.
|
|
||||||
func (cldr *CLDR) finalize(filter []string) {
|
|
||||||
for _, x := range cldr.locale {
|
|
||||||
if filter != nil {
|
|
||||||
v := reflect.ValueOf(x).Elem()
|
|
||||||
t := v.Type()
|
|
||||||
for i := 0; i < v.NumField(); i++ {
|
|
||||||
f := t.Field(i)
|
|
||||||
name, _ := xmlName(f)
|
|
||||||
if name != "" && name != "identity" && !in(filter, name) {
|
|
||||||
v.Field(i).Set(reflect.Zero(f.Type))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
linkEnclosing(nil, x) // for resolving aliases and paths
|
|
||||||
setNames(x, "ldml")
|
|
||||||
}
|
|
||||||
}
|
|
144
vendor/golang.org/x/text/unicode/cldr/slice.go
generated
vendored
144
vendor/golang.org/x/text/unicode/cldr/slice.go
generated
vendored
|
@ -1,144 +0,0 @@
|
||||||
// Copyright 2013 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 cldr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Slice provides utilities for modifying slices of elements.
|
|
||||||
// It can be wrapped around any slice of which the element type implements
|
|
||||||
// interface Elem.
|
|
||||||
type Slice struct {
|
|
||||||
ptr reflect.Value
|
|
||||||
typ reflect.Type
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value returns the reflect.Value of the underlying slice.
|
|
||||||
func (s *Slice) Value() reflect.Value {
|
|
||||||
return s.ptr.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeSlice wraps a pointer to a slice of Elems.
|
|
||||||
// It replaces the array pointed to by the slice so that subsequent modifications
|
|
||||||
// do not alter the data in a CLDR type.
|
|
||||||
// It panics if an incorrect type is passed.
|
|
||||||
func MakeSlice(slicePtr interface{}) Slice {
|
|
||||||
ptr := reflect.ValueOf(slicePtr)
|
|
||||||
if ptr.Kind() != reflect.Ptr {
|
|
||||||
panic(fmt.Sprintf("MakeSlice: argument must be pointer to slice, found %v", ptr.Type()))
|
|
||||||
}
|
|
||||||
sl := ptr.Elem()
|
|
||||||
if sl.Kind() != reflect.Slice {
|
|
||||||
panic(fmt.Sprintf("MakeSlice: argument must point to a slice, found %v", sl.Type()))
|
|
||||||
}
|
|
||||||
intf := reflect.TypeOf((*Elem)(nil)).Elem()
|
|
||||||
if !sl.Type().Elem().Implements(intf) {
|
|
||||||
panic(fmt.Sprintf("MakeSlice: element type of slice (%v) does not implement Elem", sl.Type().Elem()))
|
|
||||||
}
|
|
||||||
nsl := reflect.MakeSlice(sl.Type(), sl.Len(), sl.Len())
|
|
||||||
reflect.Copy(nsl, sl)
|
|
||||||
sl.Set(nsl)
|
|
||||||
return Slice{
|
|
||||||
ptr: ptr,
|
|
||||||
typ: sl.Type().Elem().Elem(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s Slice) indexForAttr(a string) []int {
|
|
||||||
for i := iter(reflect.Zero(s.typ)); !i.done(); i.next() {
|
|
||||||
if n, _ := xmlName(i.field()); n == a {
|
|
||||||
return i.index
|
|
||||||
}
|
|
||||||
}
|
|
||||||
panic(fmt.Sprintf("MakeSlice: no attribute %q for type %v", a, s.typ))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter filters s to only include elements for which fn returns true.
|
|
||||||
func (s Slice) Filter(fn func(e Elem) bool) {
|
|
||||||
k := 0
|
|
||||||
sl := s.Value()
|
|
||||||
for i := 0; i < sl.Len(); i++ {
|
|
||||||
vi := sl.Index(i)
|
|
||||||
if fn(vi.Interface().(Elem)) {
|
|
||||||
sl.Index(k).Set(vi)
|
|
||||||
k++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sl.Set(sl.Slice(0, k))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Group finds elements in s for which fn returns the same value and groups
|
|
||||||
// them in a new Slice.
|
|
||||||
func (s Slice) Group(fn func(e Elem) string) []Slice {
|
|
||||||
m := make(map[string][]reflect.Value)
|
|
||||||
sl := s.Value()
|
|
||||||
for i := 0; i < sl.Len(); i++ {
|
|
||||||
vi := sl.Index(i)
|
|
||||||
key := fn(vi.Interface().(Elem))
|
|
||||||
m[key] = append(m[key], vi)
|
|
||||||
}
|
|
||||||
keys := []string{}
|
|
||||||
for k, _ := range m {
|
|
||||||
keys = append(keys, k)
|
|
||||||
}
|
|
||||||
sort.Strings(keys)
|
|
||||||
res := []Slice{}
|
|
||||||
for _, k := range keys {
|
|
||||||
nsl := reflect.New(sl.Type())
|
|
||||||
nsl.Elem().Set(reflect.Append(nsl.Elem(), m[k]...))
|
|
||||||
res = append(res, MakeSlice(nsl.Interface()))
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
// SelectAnyOf filters s to contain only elements for which attr matches
|
|
||||||
// any of the values.
|
|
||||||
func (s Slice) SelectAnyOf(attr string, values ...string) {
|
|
||||||
index := s.indexForAttr(attr)
|
|
||||||
s.Filter(func(e Elem) bool {
|
|
||||||
vf := reflect.ValueOf(e).Elem().FieldByIndex(index)
|
|
||||||
return in(values, vf.String())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// SelectOnePerGroup filters s to include at most one element e per group of
|
|
||||||
// elements matching Key(attr), where e has an attribute a that matches any
|
|
||||||
// the values in v.
|
|
||||||
// If more than one element in a group matches a value in v preference
|
|
||||||
// is given to the element that matches the first value in v.
|
|
||||||
func (s Slice) SelectOnePerGroup(a string, v []string) {
|
|
||||||
index := s.indexForAttr(a)
|
|
||||||
grouped := s.Group(func(e Elem) string { return Key(e, a) })
|
|
||||||
sl := s.Value()
|
|
||||||
sl.Set(sl.Slice(0, 0))
|
|
||||||
for _, g := range grouped {
|
|
||||||
e := reflect.Value{}
|
|
||||||
found := len(v)
|
|
||||||
gsl := g.Value()
|
|
||||||
for i := 0; i < gsl.Len(); i++ {
|
|
||||||
vi := gsl.Index(i).Elem().FieldByIndex(index)
|
|
||||||
j := 0
|
|
||||||
for ; j < len(v) && v[j] != vi.String(); j++ {
|
|
||||||
}
|
|
||||||
if j < found {
|
|
||||||
found = j
|
|
||||||
e = gsl.Index(i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if found < len(v) {
|
|
||||||
sl.Set(reflect.Append(sl, e))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SelectDraft drops all elements from the list with a draft level smaller than d
|
|
||||||
// and selects the highest draft level of the remaining.
|
|
||||||
// This method assumes that the input CLDR is canonicalized.
|
|
||||||
func (s Slice) SelectDraft(d Draft) {
|
|
||||||
s.SelectOnePerGroup("draft", drafts[len(drafts)-2-int(d):])
|
|
||||||
}
|
|
1456
vendor/golang.org/x/text/unicode/cldr/xml.go
generated
vendored
1456
vendor/golang.org/x/text/unicode/cldr/xml.go
generated
vendored
File diff suppressed because it is too large
Load diff
978
vendor/golang.org/x/text/unicode/norm/maketables.go
generated
vendored
978
vendor/golang.org/x/text/unicode/norm/maketables.go
generated
vendored
|
@ -1,978 +0,0 @@
|
||||||
// Copyright 2011 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 ignore
|
|
||||||
|
|
||||||
// Normalization table generator.
|
|
||||||
// Data read from the web.
|
|
||||||
// See forminfo.go for a description of the trie values associated with each rune.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"golang.org/x/text/internal/gen"
|
|
||||||
"golang.org/x/text/internal/triegen"
|
|
||||||
"golang.org/x/text/internal/ucd"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
gen.Init()
|
|
||||||
loadUnicodeData()
|
|
||||||
compactCCC()
|
|
||||||
loadCompositionExclusions()
|
|
||||||
completeCharFields(FCanonical)
|
|
||||||
completeCharFields(FCompatibility)
|
|
||||||
computeNonStarterCounts()
|
|
||||||
verifyComputed()
|
|
||||||
printChars()
|
|
||||||
if *test {
|
|
||||||
testDerived()
|
|
||||||
printTestdata()
|
|
||||||
} else {
|
|
||||||
makeTables()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
tablelist = flag.String("tables",
|
|
||||||
"all",
|
|
||||||
"comma-separated list of which tables to generate; "+
|
|
||||||
"can be 'decomp', 'recomp', 'info' and 'all'")
|
|
||||||
test = flag.Bool("test",
|
|
||||||
false,
|
|
||||||
"test existing tables against DerivedNormalizationProps and generate test data for regression testing")
|
|
||||||
verbose = flag.Bool("verbose",
|
|
||||||
false,
|
|
||||||
"write data to stdout as it is parsed")
|
|
||||||
)
|
|
||||||
|
|
||||||
const MaxChar = 0x10FFFF // anything above this shouldn't exist
|
|
||||||
|
|
||||||
// Quick Check properties of runes allow us to quickly
|
|
||||||
// determine whether a rune may occur in a normal form.
|
|
||||||
// For a given normal form, a rune may be guaranteed to occur
|
|
||||||
// verbatim (QC=Yes), may or may not combine with another
|
|
||||||
// rune (QC=Maybe), or may not occur (QC=No).
|
|
||||||
type QCResult int
|
|
||||||
|
|
||||||
const (
|
|
||||||
QCUnknown QCResult = iota
|
|
||||||
QCYes
|
|
||||||
QCNo
|
|
||||||
QCMaybe
|
|
||||||
)
|
|
||||||
|
|
||||||
func (r QCResult) String() string {
|
|
||||||
switch r {
|
|
||||||
case QCYes:
|
|
||||||
return "Yes"
|
|
||||||
case QCNo:
|
|
||||||
return "No"
|
|
||||||
case QCMaybe:
|
|
||||||
return "Maybe"
|
|
||||||
}
|
|
||||||
return "***UNKNOWN***"
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
FCanonical = iota // NFC or NFD
|
|
||||||
FCompatibility // NFKC or NFKD
|
|
||||||
FNumberOfFormTypes
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
MComposed = iota // NFC or NFKC
|
|
||||||
MDecomposed // NFD or NFKD
|
|
||||||
MNumberOfModes
|
|
||||||
)
|
|
||||||
|
|
||||||
// This contains only the properties we're interested in.
|
|
||||||
type Char struct {
|
|
||||||
name string
|
|
||||||
codePoint rune // if zero, this index is not a valid code point.
|
|
||||||
ccc uint8 // canonical combining class
|
|
||||||
origCCC uint8
|
|
||||||
excludeInComp bool // from CompositionExclusions.txt
|
|
||||||
compatDecomp bool // it has a compatibility expansion
|
|
||||||
|
|
||||||
nTrailingNonStarters uint8
|
|
||||||
nLeadingNonStarters uint8 // must be equal to trailing if non-zero
|
|
||||||
|
|
||||||
forms [FNumberOfFormTypes]FormInfo // For FCanonical and FCompatibility
|
|
||||||
|
|
||||||
state State
|
|
||||||
}
|
|
||||||
|
|
||||||
var chars = make([]Char, MaxChar+1)
|
|
||||||
var cccMap = make(map[uint8]uint8)
|
|
||||||
|
|
||||||
func (c Char) String() string {
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
|
|
||||||
fmt.Fprintf(buf, "%U [%s]:\n", c.codePoint, c.name)
|
|
||||||
fmt.Fprintf(buf, " ccc: %v\n", c.ccc)
|
|
||||||
fmt.Fprintf(buf, " excludeInComp: %v\n", c.excludeInComp)
|
|
||||||
fmt.Fprintf(buf, " compatDecomp: %v\n", c.compatDecomp)
|
|
||||||
fmt.Fprintf(buf, " state: %v\n", c.state)
|
|
||||||
fmt.Fprintf(buf, " NFC:\n")
|
|
||||||
fmt.Fprint(buf, c.forms[FCanonical])
|
|
||||||
fmt.Fprintf(buf, " NFKC:\n")
|
|
||||||
fmt.Fprint(buf, c.forms[FCompatibility])
|
|
||||||
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// In UnicodeData.txt, some ranges are marked like this:
|
|
||||||
// 3400;<CJK Ideograph Extension A, First>;Lo;0;L;;;;;N;;;;;
|
|
||||||
// 4DB5;<CJK Ideograph Extension A, Last>;Lo;0;L;;;;;N;;;;;
|
|
||||||
// parseCharacter keeps a state variable indicating the weirdness.
|
|
||||||
type State int
|
|
||||||
|
|
||||||
const (
|
|
||||||
SNormal State = iota // known to be zero for the type
|
|
||||||
SFirst
|
|
||||||
SLast
|
|
||||||
SMissing
|
|
||||||
)
|
|
||||||
|
|
||||||
var lastChar = rune('\u0000')
|
|
||||||
|
|
||||||
func (c Char) isValid() bool {
|
|
||||||
return c.codePoint != 0 && c.state != SMissing
|
|
||||||
}
|
|
||||||
|
|
||||||
type FormInfo struct {
|
|
||||||
quickCheck [MNumberOfModes]QCResult // index: MComposed or MDecomposed
|
|
||||||
verified [MNumberOfModes]bool // index: MComposed or MDecomposed
|
|
||||||
|
|
||||||
combinesForward bool // May combine with rune on the right
|
|
||||||
combinesBackward bool // May combine with rune on the left
|
|
||||||
isOneWay bool // Never appears in result
|
|
||||||
inDecomp bool // Some decompositions result in this char.
|
|
||||||
decomp Decomposition
|
|
||||||
expandedDecomp Decomposition
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f FormInfo) String() string {
|
|
||||||
buf := bytes.NewBuffer(make([]byte, 0))
|
|
||||||
|
|
||||||
fmt.Fprintf(buf, " quickCheck[C]: %v\n", f.quickCheck[MComposed])
|
|
||||||
fmt.Fprintf(buf, " quickCheck[D]: %v\n", f.quickCheck[MDecomposed])
|
|
||||||
fmt.Fprintf(buf, " cmbForward: %v\n", f.combinesForward)
|
|
||||||
fmt.Fprintf(buf, " cmbBackward: %v\n", f.combinesBackward)
|
|
||||||
fmt.Fprintf(buf, " isOneWay: %v\n", f.isOneWay)
|
|
||||||
fmt.Fprintf(buf, " inDecomp: %v\n", f.inDecomp)
|
|
||||||
fmt.Fprintf(buf, " decomposition: %X\n", f.decomp)
|
|
||||||
fmt.Fprintf(buf, " expandedDecomp: %X\n", f.expandedDecomp)
|
|
||||||
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
type Decomposition []rune
|
|
||||||
|
|
||||||
func parseDecomposition(s string, skipfirst bool) (a []rune, err error) {
|
|
||||||
decomp := strings.Split(s, " ")
|
|
||||||
if len(decomp) > 0 && skipfirst {
|
|
||||||
decomp = decomp[1:]
|
|
||||||
}
|
|
||||||
for _, d := range decomp {
|
|
||||||
point, err := strconv.ParseUint(d, 16, 64)
|
|
||||||
if err != nil {
|
|
||||||
return a, err
|
|
||||||
}
|
|
||||||
a = append(a, rune(point))
|
|
||||||
}
|
|
||||||
return a, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadUnicodeData() {
|
|
||||||
f := gen.OpenUCDFile("UnicodeData.txt")
|
|
||||||
defer f.Close()
|
|
||||||
p := ucd.New(f)
|
|
||||||
for p.Next() {
|
|
||||||
r := p.Rune(ucd.CodePoint)
|
|
||||||
char := &chars[r]
|
|
||||||
|
|
||||||
char.ccc = uint8(p.Uint(ucd.CanonicalCombiningClass))
|
|
||||||
decmap := p.String(ucd.DecompMapping)
|
|
||||||
|
|
||||||
exp, err := parseDecomposition(decmap, false)
|
|
||||||
isCompat := false
|
|
||||||
if err != nil {
|
|
||||||
if len(decmap) > 0 {
|
|
||||||
exp, err = parseDecomposition(decmap, true)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf(`%U: bad decomp |%v|: "%s"`, r, decmap, err)
|
|
||||||
}
|
|
||||||
isCompat = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
char.name = p.String(ucd.Name)
|
|
||||||
char.codePoint = r
|
|
||||||
char.forms[FCompatibility].decomp = exp
|
|
||||||
if !isCompat {
|
|
||||||
char.forms[FCanonical].decomp = exp
|
|
||||||
} else {
|
|
||||||
char.compatDecomp = true
|
|
||||||
}
|
|
||||||
if len(decmap) > 0 {
|
|
||||||
char.forms[FCompatibility].decomp = exp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := p.Err(); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// compactCCC converts the sparse set of CCC values to a continguous one,
|
|
||||||
// reducing the number of bits needed from 8 to 6.
|
|
||||||
func compactCCC() {
|
|
||||||
m := make(map[uint8]uint8)
|
|
||||||
for i := range chars {
|
|
||||||
c := &chars[i]
|
|
||||||
m[c.ccc] = 0
|
|
||||||
}
|
|
||||||
cccs := []int{}
|
|
||||||
for v, _ := range m {
|
|
||||||
cccs = append(cccs, int(v))
|
|
||||||
}
|
|
||||||
sort.Ints(cccs)
|
|
||||||
for i, c := range cccs {
|
|
||||||
cccMap[uint8(i)] = uint8(c)
|
|
||||||
m[uint8(c)] = uint8(i)
|
|
||||||
}
|
|
||||||
for i := range chars {
|
|
||||||
c := &chars[i]
|
|
||||||
c.origCCC = c.ccc
|
|
||||||
c.ccc = m[c.ccc]
|
|
||||||
}
|
|
||||||
if len(m) >= 1<<6 {
|
|
||||||
log.Fatalf("too many difference CCC values: %d >= 64", len(m))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CompositionExclusions.txt has form:
|
|
||||||
// 0958 # ...
|
|
||||||
// See http://unicode.org/reports/tr44/ for full explanation
|
|
||||||
func loadCompositionExclusions() {
|
|
||||||
f := gen.OpenUCDFile("CompositionExclusions.txt")
|
|
||||||
defer f.Close()
|
|
||||||
p := ucd.New(f)
|
|
||||||
for p.Next() {
|
|
||||||
c := &chars[p.Rune(0)]
|
|
||||||
if c.excludeInComp {
|
|
||||||
log.Fatalf("%U: Duplicate entry in exclusions.", c.codePoint)
|
|
||||||
}
|
|
||||||
c.excludeInComp = true
|
|
||||||
}
|
|
||||||
if e := p.Err(); e != nil {
|
|
||||||
log.Fatal(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// hasCompatDecomp returns true if any of the recursive
|
|
||||||
// decompositions contains a compatibility expansion.
|
|
||||||
// In this case, the character may not occur in NFK*.
|
|
||||||
func hasCompatDecomp(r rune) bool {
|
|
||||||
c := &chars[r]
|
|
||||||
if c.compatDecomp {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
for _, d := range c.forms[FCompatibility].decomp {
|
|
||||||
if hasCompatDecomp(d) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hangul related constants.
|
|
||||||
const (
|
|
||||||
HangulBase = 0xAC00
|
|
||||||
HangulEnd = 0xD7A4 // hangulBase + Jamo combinations (19 * 21 * 28)
|
|
||||||
|
|
||||||
JamoLBase = 0x1100
|
|
||||||
JamoLEnd = 0x1113
|
|
||||||
JamoVBase = 0x1161
|
|
||||||
JamoVEnd = 0x1176
|
|
||||||
JamoTBase = 0x11A8
|
|
||||||
JamoTEnd = 0x11C3
|
|
||||||
|
|
||||||
JamoLVTCount = 19 * 21 * 28
|
|
||||||
JamoTCount = 28
|
|
||||||
)
|
|
||||||
|
|
||||||
func isHangul(r rune) bool {
|
|
||||||
return HangulBase <= r && r < HangulEnd
|
|
||||||
}
|
|
||||||
|
|
||||||
func isHangulWithoutJamoT(r rune) bool {
|
|
||||||
if !isHangul(r) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
r -= HangulBase
|
|
||||||
return r < JamoLVTCount && r%JamoTCount == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func ccc(r rune) uint8 {
|
|
||||||
return chars[r].ccc
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert a rune in a buffer, ordered by Canonical Combining Class.
|
|
||||||
func insertOrdered(b Decomposition, r rune) Decomposition {
|
|
||||||
n := len(b)
|
|
||||||
b = append(b, 0)
|
|
||||||
cc := ccc(r)
|
|
||||||
if cc > 0 {
|
|
||||||
// Use bubble sort.
|
|
||||||
for ; n > 0; n-- {
|
|
||||||
if ccc(b[n-1]) <= cc {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
b[n] = b[n-1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
b[n] = r
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recursively decompose.
|
|
||||||
func decomposeRecursive(form int, r rune, d Decomposition) Decomposition {
|
|
||||||
dcomp := chars[r].forms[form].decomp
|
|
||||||
if len(dcomp) == 0 {
|
|
||||||
return insertOrdered(d, r)
|
|
||||||
}
|
|
||||||
for _, c := range dcomp {
|
|
||||||
d = decomposeRecursive(form, c, d)
|
|
||||||
}
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
|
|
||||||
func completeCharFields(form int) {
|
|
||||||
// Phase 0: pre-expand decomposition.
|
|
||||||
for i := range chars {
|
|
||||||
f := &chars[i].forms[form]
|
|
||||||
if len(f.decomp) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
exp := make(Decomposition, 0)
|
|
||||||
for _, c := range f.decomp {
|
|
||||||
exp = decomposeRecursive(form, c, exp)
|
|
||||||
}
|
|
||||||
f.expandedDecomp = exp
|
|
||||||
}
|
|
||||||
|
|
||||||
// Phase 1: composition exclusion, mark decomposition.
|
|
||||||
for i := range chars {
|
|
||||||
c := &chars[i]
|
|
||||||
f := &c.forms[form]
|
|
||||||
|
|
||||||
// Marks script-specific exclusions and version restricted.
|
|
||||||
f.isOneWay = c.excludeInComp
|
|
||||||
|
|
||||||
// Singletons
|
|
||||||
f.isOneWay = f.isOneWay || len(f.decomp) == 1
|
|
||||||
|
|
||||||
// Non-starter decompositions
|
|
||||||
if len(f.decomp) > 1 {
|
|
||||||
chk := c.ccc != 0 || chars[f.decomp[0]].ccc != 0
|
|
||||||
f.isOneWay = f.isOneWay || chk
|
|
||||||
}
|
|
||||||
|
|
||||||
// Runes that decompose into more than two runes.
|
|
||||||
f.isOneWay = f.isOneWay || len(f.decomp) > 2
|
|
||||||
|
|
||||||
if form == FCompatibility {
|
|
||||||
f.isOneWay = f.isOneWay || hasCompatDecomp(c.codePoint)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, r := range f.decomp {
|
|
||||||
chars[r].forms[form].inDecomp = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Phase 2: forward and backward combining.
|
|
||||||
for i := range chars {
|
|
||||||
c := &chars[i]
|
|
||||||
f := &c.forms[form]
|
|
||||||
|
|
||||||
if !f.isOneWay && len(f.decomp) == 2 {
|
|
||||||
f0 := &chars[f.decomp[0]].forms[form]
|
|
||||||
f1 := &chars[f.decomp[1]].forms[form]
|
|
||||||
if !f0.isOneWay {
|
|
||||||
f0.combinesForward = true
|
|
||||||
}
|
|
||||||
if !f1.isOneWay {
|
|
||||||
f1.combinesBackward = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if isHangulWithoutJamoT(rune(i)) {
|
|
||||||
f.combinesForward = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Phase 3: quick check values.
|
|
||||||
for i := range chars {
|
|
||||||
c := &chars[i]
|
|
||||||
f := &c.forms[form]
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case len(f.decomp) > 0:
|
|
||||||
f.quickCheck[MDecomposed] = QCNo
|
|
||||||
case isHangul(rune(i)):
|
|
||||||
f.quickCheck[MDecomposed] = QCNo
|
|
||||||
default:
|
|
||||||
f.quickCheck[MDecomposed] = QCYes
|
|
||||||
}
|
|
||||||
switch {
|
|
||||||
case f.isOneWay:
|
|
||||||
f.quickCheck[MComposed] = QCNo
|
|
||||||
case (i & 0xffff00) == JamoLBase:
|
|
||||||
f.quickCheck[MComposed] = QCYes
|
|
||||||
if JamoLBase <= i && i < JamoLEnd {
|
|
||||||
f.combinesForward = true
|
|
||||||
}
|
|
||||||
if JamoVBase <= i && i < JamoVEnd {
|
|
||||||
f.quickCheck[MComposed] = QCMaybe
|
|
||||||
f.combinesBackward = true
|
|
||||||
f.combinesForward = true
|
|
||||||
}
|
|
||||||
if JamoTBase <= i && i < JamoTEnd {
|
|
||||||
f.quickCheck[MComposed] = QCMaybe
|
|
||||||
f.combinesBackward = true
|
|
||||||
}
|
|
||||||
case !f.combinesBackward:
|
|
||||||
f.quickCheck[MComposed] = QCYes
|
|
||||||
default:
|
|
||||||
f.quickCheck[MComposed] = QCMaybe
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func computeNonStarterCounts() {
|
|
||||||
// Phase 4: leading and trailing non-starter count
|
|
||||||
for i := range chars {
|
|
||||||
c := &chars[i]
|
|
||||||
|
|
||||||
runes := []rune{rune(i)}
|
|
||||||
// We always use FCompatibility so that the CGJ insertion points do not
|
|
||||||
// change for repeated normalizations with different forms.
|
|
||||||
if exp := c.forms[FCompatibility].expandedDecomp; len(exp) > 0 {
|
|
||||||
runes = exp
|
|
||||||
}
|
|
||||||
// We consider runes that combine backwards to be non-starters for the
|
|
||||||
// purpose of Stream-Safe Text Processing.
|
|
||||||
for _, r := range runes {
|
|
||||||
if cr := &chars[r]; cr.ccc == 0 && !cr.forms[FCompatibility].combinesBackward {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
c.nLeadingNonStarters++
|
|
||||||
}
|
|
||||||
for i := len(runes) - 1; i >= 0; i-- {
|
|
||||||
if cr := &chars[runes[i]]; cr.ccc == 0 && !cr.forms[FCompatibility].combinesBackward {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
c.nTrailingNonStarters++
|
|
||||||
}
|
|
||||||
if c.nTrailingNonStarters > 3 {
|
|
||||||
log.Fatalf("%U: Decomposition with more than 3 (%d) trailing modifiers (%U)", i, c.nTrailingNonStarters, runes)
|
|
||||||
}
|
|
||||||
|
|
||||||
if isHangul(rune(i)) {
|
|
||||||
c.nTrailingNonStarters = 2
|
|
||||||
if isHangulWithoutJamoT(rune(i)) {
|
|
||||||
c.nTrailingNonStarters = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if l, t := c.nLeadingNonStarters, c.nTrailingNonStarters; l > 0 && l != t {
|
|
||||||
log.Fatalf("%U: number of leading and trailing non-starters should be equal (%d vs %d)", i, l, t)
|
|
||||||
}
|
|
||||||
if t := c.nTrailingNonStarters; t > 3 {
|
|
||||||
log.Fatalf("%U: number of trailing non-starters is %d > 3", t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func printBytes(w io.Writer, b []byte, name string) {
|
|
||||||
fmt.Fprintf(w, "// %s: %d bytes\n", name, len(b))
|
|
||||||
fmt.Fprintf(w, "var %s = [...]byte {", name)
|
|
||||||
for i, c := range b {
|
|
||||||
switch {
|
|
||||||
case i%64 == 0:
|
|
||||||
fmt.Fprintf(w, "\n// Bytes %x - %x\n", i, i+63)
|
|
||||||
case i%8 == 0:
|
|
||||||
fmt.Fprintf(w, "\n")
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, "0x%.2X, ", c)
|
|
||||||
}
|
|
||||||
fmt.Fprint(w, "\n}\n\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
// See forminfo.go for format.
|
|
||||||
func makeEntry(f *FormInfo, c *Char) uint16 {
|
|
||||||
e := uint16(0)
|
|
||||||
if r := c.codePoint; HangulBase <= r && r < HangulEnd {
|
|
||||||
e |= 0x40
|
|
||||||
}
|
|
||||||
if f.combinesForward {
|
|
||||||
e |= 0x20
|
|
||||||
}
|
|
||||||
if f.quickCheck[MDecomposed] == QCNo {
|
|
||||||
e |= 0x4
|
|
||||||
}
|
|
||||||
switch f.quickCheck[MComposed] {
|
|
||||||
case QCYes:
|
|
||||||
case QCNo:
|
|
||||||
e |= 0x10
|
|
||||||
case QCMaybe:
|
|
||||||
e |= 0x18
|
|
||||||
default:
|
|
||||||
log.Fatalf("Illegal quickcheck value %v.", f.quickCheck[MComposed])
|
|
||||||
}
|
|
||||||
e |= uint16(c.nTrailingNonStarters)
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
// decompSet keeps track of unique decompositions, grouped by whether
|
|
||||||
// the decomposition is followed by a trailing and/or leading CCC.
|
|
||||||
type decompSet [7]map[string]bool
|
|
||||||
|
|
||||||
const (
|
|
||||||
normalDecomp = iota
|
|
||||||
firstMulti
|
|
||||||
firstCCC
|
|
||||||
endMulti
|
|
||||||
firstLeadingCCC
|
|
||||||
firstCCCZeroExcept
|
|
||||||
firstStarterWithNLead
|
|
||||||
lastDecomp
|
|
||||||
)
|
|
||||||
|
|
||||||
var cname = []string{"firstMulti", "firstCCC", "endMulti", "firstLeadingCCC", "firstCCCZeroExcept", "firstStarterWithNLead", "lastDecomp"}
|
|
||||||
|
|
||||||
func makeDecompSet() decompSet {
|
|
||||||
m := decompSet{}
|
|
||||||
for i := range m {
|
|
||||||
m[i] = make(map[string]bool)
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
func (m *decompSet) insert(key int, s string) {
|
|
||||||
m[key][s] = true
|
|
||||||
}
|
|
||||||
|
|
||||||
func printCharInfoTables(w io.Writer) int {
|
|
||||||
mkstr := func(r rune, f *FormInfo) (int, string) {
|
|
||||||
d := f.expandedDecomp
|
|
||||||
s := string([]rune(d))
|
|
||||||
if max := 1 << 6; len(s) >= max {
|
|
||||||
const msg = "%U: too many bytes in decomposition: %d >= %d"
|
|
||||||
log.Fatalf(msg, r, len(s), max)
|
|
||||||
}
|
|
||||||
head := uint8(len(s))
|
|
||||||
if f.quickCheck[MComposed] != QCYes {
|
|
||||||
head |= 0x40
|
|
||||||
}
|
|
||||||
if f.combinesForward {
|
|
||||||
head |= 0x80
|
|
||||||
}
|
|
||||||
s = string([]byte{head}) + s
|
|
||||||
|
|
||||||
lccc := ccc(d[0])
|
|
||||||
tccc := ccc(d[len(d)-1])
|
|
||||||
cc := ccc(r)
|
|
||||||
if cc != 0 && lccc == 0 && tccc == 0 {
|
|
||||||
log.Fatalf("%U: trailing and leading ccc are 0 for non-zero ccc %d", r, cc)
|
|
||||||
}
|
|
||||||
if tccc < lccc && lccc != 0 {
|
|
||||||
const msg = "%U: lccc (%d) must be <= tcc (%d)"
|
|
||||||
log.Fatalf(msg, r, lccc, tccc)
|
|
||||||
}
|
|
||||||
index := normalDecomp
|
|
||||||
nTrail := chars[r].nTrailingNonStarters
|
|
||||||
if tccc > 0 || lccc > 0 || nTrail > 0 {
|
|
||||||
tccc <<= 2
|
|
||||||
tccc |= nTrail
|
|
||||||
s += string([]byte{tccc})
|
|
||||||
index = endMulti
|
|
||||||
for _, r := range d[1:] {
|
|
||||||
if ccc(r) == 0 {
|
|
||||||
index = firstCCC
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if lccc > 0 {
|
|
||||||
s += string([]byte{lccc})
|
|
||||||
if index == firstCCC {
|
|
||||||
log.Fatalf("%U: multi-segment decomposition not supported for decompositions with leading CCC != 0", r)
|
|
||||||
}
|
|
||||||
index = firstLeadingCCC
|
|
||||||
}
|
|
||||||
if cc != lccc {
|
|
||||||
if cc != 0 {
|
|
||||||
log.Fatalf("%U: for lccc != ccc, expected ccc to be 0; was %d", r, cc)
|
|
||||||
}
|
|
||||||
index = firstCCCZeroExcept
|
|
||||||
}
|
|
||||||
} else if len(d) > 1 {
|
|
||||||
index = firstMulti
|
|
||||||
}
|
|
||||||
return index, s
|
|
||||||
}
|
|
||||||
|
|
||||||
decompSet := makeDecompSet()
|
|
||||||
const nLeadStr = "\x00\x01" // 0-byte length and tccc with nTrail.
|
|
||||||
decompSet.insert(firstStarterWithNLead, nLeadStr)
|
|
||||||
|
|
||||||
// Store the uniqued decompositions in a byte buffer,
|
|
||||||
// preceded by their byte length.
|
|
||||||
for _, c := range chars {
|
|
||||||
for _, f := range c.forms {
|
|
||||||
if len(f.expandedDecomp) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if f.combinesBackward {
|
|
||||||
log.Fatalf("%U: combinesBackward and decompose", c.codePoint)
|
|
||||||
}
|
|
||||||
index, s := mkstr(c.codePoint, &f)
|
|
||||||
decompSet.insert(index, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
decompositions := bytes.NewBuffer(make([]byte, 0, 10000))
|
|
||||||
size := 0
|
|
||||||
positionMap := make(map[string]uint16)
|
|
||||||
decompositions.WriteString("\000")
|
|
||||||
fmt.Fprintln(w, "const (")
|
|
||||||
for i, m := range decompSet {
|
|
||||||
sa := []string{}
|
|
||||||
for s := range m {
|
|
||||||
sa = append(sa, s)
|
|
||||||
}
|
|
||||||
sort.Strings(sa)
|
|
||||||
for _, s := range sa {
|
|
||||||
p := decompositions.Len()
|
|
||||||
decompositions.WriteString(s)
|
|
||||||
positionMap[s] = uint16(p)
|
|
||||||
}
|
|
||||||
if cname[i] != "" {
|
|
||||||
fmt.Fprintf(w, "%s = 0x%X\n", cname[i], decompositions.Len())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Fprintln(w, "maxDecomp = 0x8000")
|
|
||||||
fmt.Fprintln(w, ")")
|
|
||||||
b := decompositions.Bytes()
|
|
||||||
printBytes(w, b, "decomps")
|
|
||||||
size += len(b)
|
|
||||||
|
|
||||||
varnames := []string{"nfc", "nfkc"}
|
|
||||||
for i := 0; i < FNumberOfFormTypes; i++ {
|
|
||||||
trie := triegen.NewTrie(varnames[i])
|
|
||||||
|
|
||||||
for r, c := range chars {
|
|
||||||
f := c.forms[i]
|
|
||||||
d := f.expandedDecomp
|
|
||||||
if len(d) != 0 {
|
|
||||||
_, key := mkstr(c.codePoint, &f)
|
|
||||||
trie.Insert(rune(r), uint64(positionMap[key]))
|
|
||||||
if c.ccc != ccc(d[0]) {
|
|
||||||
// We assume the lead ccc of a decomposition !=0 in this case.
|
|
||||||
if ccc(d[0]) == 0 {
|
|
||||||
log.Fatalf("Expected leading CCC to be non-zero; ccc is %d", c.ccc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if c.nLeadingNonStarters > 0 && len(f.expandedDecomp) == 0 && c.ccc == 0 && !f.combinesBackward {
|
|
||||||
// Handle cases where it can't be detected that the nLead should be equal
|
|
||||||
// to nTrail.
|
|
||||||
trie.Insert(c.codePoint, uint64(positionMap[nLeadStr]))
|
|
||||||
} else if v := makeEntry(&f, &c)<<8 | uint16(c.ccc); v != 0 {
|
|
||||||
trie.Insert(c.codePoint, uint64(0x8000|v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sz, err := trie.Gen(w, triegen.Compact(&normCompacter{name: varnames[i]}))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
size += sz
|
|
||||||
}
|
|
||||||
return size
|
|
||||||
}
|
|
||||||
|
|
||||||
func contains(sa []string, s string) bool {
|
|
||||||
for _, a := range sa {
|
|
||||||
if a == s {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeTables() {
|
|
||||||
w := &bytes.Buffer{}
|
|
||||||
|
|
||||||
size := 0
|
|
||||||
if *tablelist == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
list := strings.Split(*tablelist, ",")
|
|
||||||
if *tablelist == "all" {
|
|
||||||
list = []string{"recomp", "info"}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute maximum decomposition size.
|
|
||||||
max := 0
|
|
||||||
for _, c := range chars {
|
|
||||||
if n := len(string(c.forms[FCompatibility].expandedDecomp)); n > max {
|
|
||||||
max = n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintln(w, "const (")
|
|
||||||
fmt.Fprintln(w, "\t// Version is the Unicode edition from which the tables are derived.")
|
|
||||||
fmt.Fprintf(w, "\tVersion = %q\n", gen.UnicodeVersion())
|
|
||||||
fmt.Fprintln(w)
|
|
||||||
fmt.Fprintln(w, "\t// MaxTransformChunkSize indicates the maximum number of bytes that Transform")
|
|
||||||
fmt.Fprintln(w, "\t// may need to write atomically for any Form. Making a destination buffer at")
|
|
||||||
fmt.Fprintln(w, "\t// least this size ensures that Transform can always make progress and that")
|
|
||||||
fmt.Fprintln(w, "\t// the user does not need to grow the buffer on an ErrShortDst.")
|
|
||||||
fmt.Fprintf(w, "\tMaxTransformChunkSize = %d+maxNonStarters*4\n", len(string(0x034F))+max)
|
|
||||||
fmt.Fprintln(w, ")\n")
|
|
||||||
|
|
||||||
// Print the CCC remap table.
|
|
||||||
size += len(cccMap)
|
|
||||||
fmt.Fprintf(w, "var ccc = [%d]uint8{", len(cccMap))
|
|
||||||
for i := 0; i < len(cccMap); i++ {
|
|
||||||
if i%8 == 0 {
|
|
||||||
fmt.Fprintln(w)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, "%3d, ", cccMap[uint8(i)])
|
|
||||||
}
|
|
||||||
fmt.Fprintln(w, "\n}\n")
|
|
||||||
|
|
||||||
if contains(list, "info") {
|
|
||||||
size += printCharInfoTables(w)
|
|
||||||
}
|
|
||||||
|
|
||||||
if contains(list, "recomp") {
|
|
||||||
// Note that we use 32 bit keys, instead of 64 bit.
|
|
||||||
// This clips the bits of three entries, but we know
|
|
||||||
// this won't cause a collision. The compiler will catch
|
|
||||||
// any changes made to UnicodeData.txt that introduces
|
|
||||||
// a collision.
|
|
||||||
// Note that the recomposition map for NFC and NFKC
|
|
||||||
// are identical.
|
|
||||||
|
|
||||||
// Recomposition map
|
|
||||||
nrentries := 0
|
|
||||||
for _, c := range chars {
|
|
||||||
f := c.forms[FCanonical]
|
|
||||||
if !f.isOneWay && len(f.decomp) > 0 {
|
|
||||||
nrentries++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sz := nrentries * 8
|
|
||||||
size += sz
|
|
||||||
fmt.Fprintf(w, "// recompMap: %d bytes (entries only)\n", sz)
|
|
||||||
fmt.Fprintln(w, "var recompMap = map[uint32]rune{")
|
|
||||||
for i, c := range chars {
|
|
||||||
f := c.forms[FCanonical]
|
|
||||||
d := f.decomp
|
|
||||||
if !f.isOneWay && len(d) > 0 {
|
|
||||||
key := uint32(uint16(d[0]))<<16 + uint32(uint16(d[1]))
|
|
||||||
fmt.Fprintf(w, "0x%.8X: 0x%.4X,\n", key, i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, "}\n\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintf(w, "// Total size of tables: %dKB (%d bytes)\n", (size+512)/1024, size)
|
|
||||||
gen.WriteGoFile("tables.go", "norm", w.Bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
func printChars() {
|
|
||||||
if *verbose {
|
|
||||||
for _, c := range chars {
|
|
||||||
if !c.isValid() || c.state == SMissing {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fmt.Println(c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// verifyComputed does various consistency tests.
|
|
||||||
func verifyComputed() {
|
|
||||||
for i, c := range chars {
|
|
||||||
for _, f := range c.forms {
|
|
||||||
isNo := (f.quickCheck[MDecomposed] == QCNo)
|
|
||||||
if (len(f.decomp) > 0) != isNo && !isHangul(rune(i)) {
|
|
||||||
log.Fatalf("%U: NF*D QC must be No if rune decomposes", i)
|
|
||||||
}
|
|
||||||
|
|
||||||
isMaybe := f.quickCheck[MComposed] == QCMaybe
|
|
||||||
if f.combinesBackward != isMaybe {
|
|
||||||
log.Fatalf("%U: NF*C QC must be Maybe if combinesBackward", i)
|
|
||||||
}
|
|
||||||
if len(f.decomp) > 0 && f.combinesForward && isMaybe {
|
|
||||||
log.Fatalf("%U: NF*C QC must be Yes or No if combinesForward and decomposes", i)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(f.expandedDecomp) != 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if a, b := c.nLeadingNonStarters > 0, (c.ccc > 0 || f.combinesBackward); a != b {
|
|
||||||
// We accept these runes to be treated differently (it only affects
|
|
||||||
// segment breaking in iteration, most likely on improper use), but
|
|
||||||
// reconsider if more characters are added.
|
|
||||||
// U+FF9E HALFWIDTH KATAKANA VOICED SOUND MARK;Lm;0;L;<narrow> 3099;;;;N;;;;;
|
|
||||||
// U+FF9F HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK;Lm;0;L;<narrow> 309A;;;;N;;;;;
|
|
||||||
// U+3133 HANGUL LETTER KIYEOK-SIOS;Lo;0;L;<compat> 11AA;;;;N;HANGUL LETTER GIYEOG SIOS;;;;
|
|
||||||
// U+318E HANGUL LETTER ARAEAE;Lo;0;L;<compat> 11A1;;;;N;HANGUL LETTER ALAE AE;;;;
|
|
||||||
// U+FFA3 HALFWIDTH HANGUL LETTER KIYEOK-SIOS;Lo;0;L;<narrow> 3133;;;;N;HALFWIDTH HANGUL LETTER GIYEOG SIOS;;;;
|
|
||||||
// U+FFDC HALFWIDTH HANGUL LETTER I;Lo;0;L;<narrow> 3163;;;;N;;;;;
|
|
||||||
if i != 0xFF9E && i != 0xFF9F && !(0x3133 <= i && i <= 0x318E) && !(0xFFA3 <= i && i <= 0xFFDC) {
|
|
||||||
log.Fatalf("%U: nLead was %v; want %v", i, a, b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
nfc := c.forms[FCanonical]
|
|
||||||
nfkc := c.forms[FCompatibility]
|
|
||||||
if nfc.combinesBackward != nfkc.combinesBackward {
|
|
||||||
log.Fatalf("%U: Cannot combine combinesBackward\n", c.codePoint)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use values in DerivedNormalizationProps.txt to compare against the
|
|
||||||
// values we computed.
|
|
||||||
// DerivedNormalizationProps.txt has form:
|
|
||||||
// 00C0..00C5 ; NFD_QC; N # ...
|
|
||||||
// 0374 ; NFD_QC; N # ...
|
|
||||||
// See http://unicode.org/reports/tr44/ for full explanation
|
|
||||||
func testDerived() {
|
|
||||||
f := gen.OpenUCDFile("DerivedNormalizationProps.txt")
|
|
||||||
defer f.Close()
|
|
||||||
p := ucd.New(f)
|
|
||||||
for p.Next() {
|
|
||||||
r := p.Rune(0)
|
|
||||||
c := &chars[r]
|
|
||||||
|
|
||||||
var ftype, mode int
|
|
||||||
qt := p.String(1)
|
|
||||||
switch qt {
|
|
||||||
case "NFC_QC":
|
|
||||||
ftype, mode = FCanonical, MComposed
|
|
||||||
case "NFD_QC":
|
|
||||||
ftype, mode = FCanonical, MDecomposed
|
|
||||||
case "NFKC_QC":
|
|
||||||
ftype, mode = FCompatibility, MComposed
|
|
||||||
case "NFKD_QC":
|
|
||||||
ftype, mode = FCompatibility, MDecomposed
|
|
||||||
default:
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var qr QCResult
|
|
||||||
switch p.String(2) {
|
|
||||||
case "Y":
|
|
||||||
qr = QCYes
|
|
||||||
case "N":
|
|
||||||
qr = QCNo
|
|
||||||
case "M":
|
|
||||||
qr = QCMaybe
|
|
||||||
default:
|
|
||||||
log.Fatalf(`Unexpected quick check value "%s"`, p.String(2))
|
|
||||||
}
|
|
||||||
if got := c.forms[ftype].quickCheck[mode]; got != qr {
|
|
||||||
log.Printf("%U: FAILED %s (was %v need %v)\n", r, qt, got, qr)
|
|
||||||
}
|
|
||||||
c.forms[ftype].verified[mode] = true
|
|
||||||
}
|
|
||||||
if err := p.Err(); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
// Any unspecified value must be QCYes. Verify this.
|
|
||||||
for i, c := range chars {
|
|
||||||
for j, fd := range c.forms {
|
|
||||||
for k, qr := range fd.quickCheck {
|
|
||||||
if !fd.verified[k] && qr != QCYes {
|
|
||||||
m := "%U: FAIL F:%d M:%d (was %v need Yes) %s\n"
|
|
||||||
log.Printf(m, i, j, k, qr, c.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var testHeader = `const (
|
|
||||||
Yes = iota
|
|
||||||
No
|
|
||||||
Maybe
|
|
||||||
)
|
|
||||||
|
|
||||||
type formData struct {
|
|
||||||
qc uint8
|
|
||||||
combinesForward bool
|
|
||||||
decomposition string
|
|
||||||
}
|
|
||||||
|
|
||||||
type runeData struct {
|
|
||||||
r rune
|
|
||||||
ccc uint8
|
|
||||||
nLead uint8
|
|
||||||
nTrail uint8
|
|
||||||
f [2]formData // 0: canonical; 1: compatibility
|
|
||||||
}
|
|
||||||
|
|
||||||
func f(qc uint8, cf bool, dec string) [2]formData {
|
|
||||||
return [2]formData{{qc, cf, dec}, {qc, cf, dec}}
|
|
||||||
}
|
|
||||||
|
|
||||||
func g(qc, qck uint8, cf, cfk bool, d, dk string) [2]formData {
|
|
||||||
return [2]formData{{qc, cf, d}, {qck, cfk, dk}}
|
|
||||||
}
|
|
||||||
|
|
||||||
var testData = []runeData{
|
|
||||||
`
|
|
||||||
|
|
||||||
func printTestdata() {
|
|
||||||
type lastInfo struct {
|
|
||||||
ccc uint8
|
|
||||||
nLead uint8
|
|
||||||
nTrail uint8
|
|
||||||
f string
|
|
||||||
}
|
|
||||||
|
|
||||||
last := lastInfo{}
|
|
||||||
w := &bytes.Buffer{}
|
|
||||||
fmt.Fprintf(w, testHeader)
|
|
||||||
for r, c := range chars {
|
|
||||||
f := c.forms[FCanonical]
|
|
||||||
qc, cf, d := f.quickCheck[MComposed], f.combinesForward, string(f.expandedDecomp)
|
|
||||||
f = c.forms[FCompatibility]
|
|
||||||
qck, cfk, dk := f.quickCheck[MComposed], f.combinesForward, string(f.expandedDecomp)
|
|
||||||
s := ""
|
|
||||||
if d == dk && qc == qck && cf == cfk {
|
|
||||||
s = fmt.Sprintf("f(%s, %v, %q)", qc, cf, d)
|
|
||||||
} else {
|
|
||||||
s = fmt.Sprintf("g(%s, %s, %v, %v, %q, %q)", qc, qck, cf, cfk, d, dk)
|
|
||||||
}
|
|
||||||
current := lastInfo{c.ccc, c.nLeadingNonStarters, c.nTrailingNonStarters, s}
|
|
||||||
if last != current {
|
|
||||||
fmt.Fprintf(w, "\t{0x%x, %d, %d, %d, %s},\n", r, c.origCCC, c.nLeadingNonStarters, c.nTrailingNonStarters, s)
|
|
||||||
last = current
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Fprintln(w, "}")
|
|
||||||
gen.WriteGoFile("data_test.go", "norm", w.Bytes())
|
|
||||||
}
|
|
117
vendor/golang.org/x/text/unicode/norm/triegen.go
generated
vendored
117
vendor/golang.org/x/text/unicode/norm/triegen.go
generated
vendored
|
@ -1,117 +0,0 @@
|
||||||
// Copyright 2011 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 ignore
|
|
||||||
|
|
||||||
// Trie table generator.
|
|
||||||
// Used by make*tables tools to generate a go file with trie data structures
|
|
||||||
// for mapping UTF-8 to a 16-bit value. All but the last byte in a UTF-8 byte
|
|
||||||
// sequence are used to lookup offsets in the index table to be used for the
|
|
||||||
// next byte. The last byte is used to index into a table with 16-bit values.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
const maxSparseEntries = 16
|
|
||||||
|
|
||||||
type normCompacter struct {
|
|
||||||
sparseBlocks [][]uint64
|
|
||||||
sparseOffset []uint16
|
|
||||||
sparseCount int
|
|
||||||
name string
|
|
||||||
}
|
|
||||||
|
|
||||||
func mostFrequentStride(a []uint64) int {
|
|
||||||
counts := make(map[int]int)
|
|
||||||
var v int
|
|
||||||
for _, x := range a {
|
|
||||||
if stride := int(x) - v; v != 0 && stride >= 0 {
|
|
||||||
counts[stride]++
|
|
||||||
}
|
|
||||||
v = int(x)
|
|
||||||
}
|
|
||||||
var maxs, maxc int
|
|
||||||
for stride, cnt := range counts {
|
|
||||||
if cnt > maxc || (cnt == maxc && stride < maxs) {
|
|
||||||
maxs, maxc = stride, cnt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return maxs
|
|
||||||
}
|
|
||||||
|
|
||||||
func countSparseEntries(a []uint64) int {
|
|
||||||
stride := mostFrequentStride(a)
|
|
||||||
var v, count int
|
|
||||||
for _, tv := range a {
|
|
||||||
if int(tv)-v != stride {
|
|
||||||
if tv != 0 {
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
v = int(tv)
|
|
||||||
}
|
|
||||||
return count
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *normCompacter) Size(v []uint64) (sz int, ok bool) {
|
|
||||||
if n := countSparseEntries(v); n <= maxSparseEntries {
|
|
||||||
return (n+1)*4 + 2, true
|
|
||||||
}
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *normCompacter) Store(v []uint64) uint32 {
|
|
||||||
h := uint32(len(c.sparseOffset))
|
|
||||||
c.sparseBlocks = append(c.sparseBlocks, v)
|
|
||||||
c.sparseOffset = append(c.sparseOffset, uint16(c.sparseCount))
|
|
||||||
c.sparseCount += countSparseEntries(v) + 1
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *normCompacter) Handler() string {
|
|
||||||
return c.name + "Sparse.lookup"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *normCompacter) Print(w io.Writer) (retErr error) {
|
|
||||||
p := func(f string, x ...interface{}) {
|
|
||||||
if _, err := fmt.Fprintf(w, f, x...); retErr == nil && err != nil {
|
|
||||||
retErr = err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ls := len(c.sparseBlocks)
|
|
||||||
p("// %sSparseOffset: %d entries, %d bytes\n", c.name, ls, ls*2)
|
|
||||||
p("var %sSparseOffset = %#v\n\n", c.name, c.sparseOffset)
|
|
||||||
|
|
||||||
ns := c.sparseCount
|
|
||||||
p("// %sSparseValues: %d entries, %d bytes\n", c.name, ns, ns*4)
|
|
||||||
p("var %sSparseValues = [%d]valueRange {", c.name, ns)
|
|
||||||
for i, b := range c.sparseBlocks {
|
|
||||||
p("\n// Block %#x, offset %#x", i, c.sparseOffset[i])
|
|
||||||
var v int
|
|
||||||
stride := mostFrequentStride(b)
|
|
||||||
n := countSparseEntries(b)
|
|
||||||
p("\n{value:%#04x,lo:%#02x},", stride, uint8(n))
|
|
||||||
for i, nv := range b {
|
|
||||||
if int(nv)-v != stride {
|
|
||||||
if v != 0 {
|
|
||||||
p(",hi:%#02x},", 0x80+i-1)
|
|
||||||
}
|
|
||||||
if nv != 0 {
|
|
||||||
p("\n{value:%#04x,lo:%#02x", nv, 0x80+i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
v = int(nv)
|
|
||||||
}
|
|
||||||
if v != 0 {
|
|
||||||
p(",hi:%#02x},", 0x80+len(b)-1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p("\n}\n\n")
|
|
||||||
return
|
|
||||||
}
|
|
113
vendor/golang.org/x/text/unicode/rangetable/gen.go
generated
vendored
113
vendor/golang.org/x/text/unicode/rangetable/gen.go
generated
vendored
|
@ -1,113 +0,0 @@
|
||||||
// Copyright 2015 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 ignore
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
|
|
||||||
"golang.org/x/text/internal/gen"
|
|
||||||
"golang.org/x/text/internal/ucd"
|
|
||||||
"golang.org/x/text/unicode/rangetable"
|
|
||||||
)
|
|
||||||
|
|
||||||
var versionList = flag.String("versions", "",
|
|
||||||
"list of versions for which to generate RangeTables")
|
|
||||||
|
|
||||||
const bootstrapMessage = `No versions specified.
|
|
||||||
To bootstrap the code generation, run:
|
|
||||||
go run gen.go --versions=4.1.0,5.0.0,6.0.0,6.1.0,6.2.0,6.3.0,7.0.0
|
|
||||||
|
|
||||||
and ensure that the latest versions are included by checking:
|
|
||||||
http://www.unicode.org/Public/`
|
|
||||||
|
|
||||||
func getVersions() []string {
|
|
||||||
if *versionList == "" {
|
|
||||||
log.Fatal(bootstrapMessage)
|
|
||||||
}
|
|
||||||
|
|
||||||
versions := strings.Split(*versionList, ",")
|
|
||||||
sort.Strings(versions)
|
|
||||||
|
|
||||||
// Ensure that at least the current version is included.
|
|
||||||
for _, v := range versions {
|
|
||||||
if v == gen.UnicodeVersion() {
|
|
||||||
return versions
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
versions = append(versions, gen.UnicodeVersion())
|
|
||||||
sort.Strings(versions)
|
|
||||||
return versions
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
gen.Init()
|
|
||||||
|
|
||||||
versions := getVersions()
|
|
||||||
|
|
||||||
w := &bytes.Buffer{}
|
|
||||||
|
|
||||||
fmt.Fprintf(w, "//go:generate go run gen.go --versions=%s\n\n", strings.Join(versions, ","))
|
|
||||||
fmt.Fprintf(w, "import \"unicode\"\n\n")
|
|
||||||
|
|
||||||
vstr := func(s string) string { return strings.Replace(s, ".", "_", -1) }
|
|
||||||
|
|
||||||
fmt.Fprintf(w, "var assigned = map[string]*unicode.RangeTable{\n")
|
|
||||||
for _, v := range versions {
|
|
||||||
fmt.Fprintf(w, "\t%q: assigned%s,\n", v, vstr(v))
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, "}\n\n")
|
|
||||||
|
|
||||||
var size int
|
|
||||||
for _, v := range versions {
|
|
||||||
assigned := []rune{}
|
|
||||||
|
|
||||||
r := gen.Open("http://www.unicode.org/Public/", "", v+"/ucd/UnicodeData.txt")
|
|
||||||
ucd.Parse(r, func(p *ucd.Parser) {
|
|
||||||
assigned = append(assigned, p.Rune(0))
|
|
||||||
})
|
|
||||||
|
|
||||||
rt := rangetable.New(assigned...)
|
|
||||||
sz := int(reflect.TypeOf(unicode.RangeTable{}).Size())
|
|
||||||
sz += int(reflect.TypeOf(unicode.Range16{}).Size()) * len(rt.R16)
|
|
||||||
sz += int(reflect.TypeOf(unicode.Range32{}).Size()) * len(rt.R32)
|
|
||||||
|
|
||||||
fmt.Fprintf(w, "// size %d bytes (%d KiB)\n", sz, sz/1024)
|
|
||||||
fmt.Fprintf(w, "var assigned%s = ", vstr(v))
|
|
||||||
print(w, rt)
|
|
||||||
|
|
||||||
size += sz
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintf(w, "// Total size %d bytes (%d KiB)\n", size, size/1024)
|
|
||||||
|
|
||||||
gen.WriteGoFile("tables.go", "rangetable", w.Bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
func print(w io.Writer, rt *unicode.RangeTable) {
|
|
||||||
fmt.Fprintln(w, "&unicode.RangeTable{")
|
|
||||||
fmt.Fprintln(w, "\tR16: []unicode.Range16{")
|
|
||||||
for _, r := range rt.R16 {
|
|
||||||
fmt.Fprintf(w, "\t\t{%#04x, %#04x, %d},\n", r.Lo, r.Hi, r.Stride)
|
|
||||||
}
|
|
||||||
fmt.Fprintln(w, "\t},")
|
|
||||||
fmt.Fprintln(w, "\tR32: []unicode.Range32{")
|
|
||||||
for _, r := range rt.R32 {
|
|
||||||
fmt.Fprintf(w, "\t\t{%#08x, %#08x, %d},\n", r.Lo, r.Hi, r.Stride)
|
|
||||||
}
|
|
||||||
fmt.Fprintln(w, "\t},")
|
|
||||||
fmt.Fprintf(w, "\tLatinOffset: %d,\n", rt.LatinOffset)
|
|
||||||
fmt.Fprintf(w, "}\n\n")
|
|
||||||
}
|
|
260
vendor/golang.org/x/text/unicode/rangetable/merge.go
generated
vendored
260
vendor/golang.org/x/text/unicode/rangetable/merge.go
generated
vendored
|
@ -1,260 +0,0 @@
|
||||||
// Copyright 2015 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 rangetable
|
|
||||||
|
|
||||||
import (
|
|
||||||
"unicode"
|
|
||||||
)
|
|
||||||
|
|
||||||
// atEnd is used to mark a completed iteration.
|
|
||||||
const atEnd = unicode.MaxRune + 1
|
|
||||||
|
|
||||||
// Merge returns a new RangeTable that is the union of the given tables.
|
|
||||||
// It can also be used to compact user-created RangeTables. The entries in
|
|
||||||
// R16 and R32 for any given RangeTable should be sorted and non-overlapping.
|
|
||||||
//
|
|
||||||
// A lookup in the resulting table can be several times faster than using In
|
|
||||||
// directly on the ranges. Merge is an expensive operation, however, and only
|
|
||||||
// makes sense if one intends to use the result for more than a couple of
|
|
||||||
// hundred lookups.
|
|
||||||
func Merge(ranges ...*unicode.RangeTable) *unicode.RangeTable {
|
|
||||||
rt := &unicode.RangeTable{}
|
|
||||||
if len(ranges) == 0 {
|
|
||||||
return rt
|
|
||||||
}
|
|
||||||
|
|
||||||
iter := tablesIter(make([]tableIndex, len(ranges)))
|
|
||||||
|
|
||||||
for i, t := range ranges {
|
|
||||||
iter[i] = tableIndex{t, 0, atEnd}
|
|
||||||
if len(t.R16) > 0 {
|
|
||||||
iter[i].next = rune(t.R16[0].Lo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if r0 := iter.next16(); r0.Stride != 0 {
|
|
||||||
for {
|
|
||||||
r1 := iter.next16()
|
|
||||||
if r1.Stride == 0 {
|
|
||||||
rt.R16 = append(rt.R16, r0)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
stride := r1.Lo - r0.Hi
|
|
||||||
if (r1.Lo == r1.Hi || stride == r1.Stride) && (r0.Lo == r0.Hi || stride == r0.Stride) {
|
|
||||||
// Fully merge the next range into the previous one.
|
|
||||||
r0.Hi, r0.Stride = r1.Hi, stride
|
|
||||||
continue
|
|
||||||
} else if stride == r0.Stride {
|
|
||||||
// Move the first element of r1 to r0. This may eliminate an
|
|
||||||
// entry.
|
|
||||||
r0.Hi = r1.Lo
|
|
||||||
r0.Stride = stride
|
|
||||||
r1.Lo = r1.Lo + r1.Stride
|
|
||||||
if r1.Lo > r1.Hi {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rt.R16 = append(rt.R16, r0)
|
|
||||||
r0 = r1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, t := range ranges {
|
|
||||||
iter[i] = tableIndex{t, 0, atEnd}
|
|
||||||
if len(t.R32) > 0 {
|
|
||||||
iter[i].next = rune(t.R32[0].Lo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if r0 := iter.next32(); r0.Stride != 0 {
|
|
||||||
for {
|
|
||||||
r1 := iter.next32()
|
|
||||||
if r1.Stride == 0 {
|
|
||||||
rt.R32 = append(rt.R32, r0)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
stride := r1.Lo - r0.Hi
|
|
||||||
if (r1.Lo == r1.Hi || stride == r1.Stride) && (r0.Lo == r0.Hi || stride == r0.Stride) {
|
|
||||||
// Fully merge the next range into the previous one.
|
|
||||||
r0.Hi, r0.Stride = r1.Hi, stride
|
|
||||||
continue
|
|
||||||
} else if stride == r0.Stride {
|
|
||||||
// Move the first element of r1 to r0. This may eliminate an
|
|
||||||
// entry.
|
|
||||||
r0.Hi = r1.Lo
|
|
||||||
r1.Lo = r1.Lo + r1.Stride
|
|
||||||
if r1.Lo > r1.Hi {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rt.R32 = append(rt.R32, r0)
|
|
||||||
r0 = r1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < len(rt.R16) && rt.R16[i].Hi <= unicode.MaxLatin1; i++ {
|
|
||||||
rt.LatinOffset = i + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return rt
|
|
||||||
}
|
|
||||||
|
|
||||||
type tableIndex struct {
|
|
||||||
t *unicode.RangeTable
|
|
||||||
p uint32
|
|
||||||
next rune
|
|
||||||
}
|
|
||||||
|
|
||||||
type tablesIter []tableIndex
|
|
||||||
|
|
||||||
// sortIter does an insertion sort using the next field of tableIndex. Insertion
|
|
||||||
// sort is a good sorting algorithm for this case.
|
|
||||||
func sortIter(t []tableIndex) {
|
|
||||||
for i := range t {
|
|
||||||
for j := i; j > 0 && t[j-1].next > t[j].next; j-- {
|
|
||||||
t[j], t[j-1] = t[j-1], t[j]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// next16 finds the ranged to be added to the table. If ranges overlap between
|
|
||||||
// multiple tables it clips the result to a non-overlapping range if the
|
|
||||||
// elements are not fully subsumed. It returns a zero range if there are no more
|
|
||||||
// ranges.
|
|
||||||
func (ti tablesIter) next16() unicode.Range16 {
|
|
||||||
sortIter(ti)
|
|
||||||
|
|
||||||
t0 := ti[0]
|
|
||||||
if t0.next == atEnd {
|
|
||||||
return unicode.Range16{}
|
|
||||||
}
|
|
||||||
r0 := t0.t.R16[t0.p]
|
|
||||||
r0.Lo = uint16(t0.next)
|
|
||||||
|
|
||||||
// We restrict the Hi of the current range if it overlaps with another range.
|
|
||||||
for i := range ti {
|
|
||||||
tn := ti[i]
|
|
||||||
// Since our tableIndices are sorted by next, we can break if the there
|
|
||||||
// is no overlap. The first value of a next range can always be merged
|
|
||||||
// into the current one, so we can break in case of equality as well.
|
|
||||||
if rune(r0.Hi) <= tn.next {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
rn := tn.t.R16[tn.p]
|
|
||||||
rn.Lo = uint16(tn.next)
|
|
||||||
|
|
||||||
// Limit r0.Hi based on next ranges in list, but allow it to overlap
|
|
||||||
// with ranges as long as it subsumes it.
|
|
||||||
m := (rn.Lo - r0.Lo) % r0.Stride
|
|
||||||
if m == 0 && (rn.Stride == r0.Stride || rn.Lo == rn.Hi) {
|
|
||||||
// Overlap, take the min of the two Hi values: for simplicity's sake
|
|
||||||
// we only process one range at a time.
|
|
||||||
if r0.Hi > rn.Hi {
|
|
||||||
r0.Hi = rn.Hi
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Not a compatible stride. Set to the last possible value before
|
|
||||||
// rn.Lo, but ensure there is at least one value.
|
|
||||||
if x := rn.Lo - m; r0.Lo <= x {
|
|
||||||
r0.Hi = x
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the next values for each table.
|
|
||||||
for i := range ti {
|
|
||||||
tn := &ti[i]
|
|
||||||
if rune(r0.Hi) < tn.next {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
rn := tn.t.R16[tn.p]
|
|
||||||
stride := rune(rn.Stride)
|
|
||||||
tn.next += stride * (1 + ((rune(r0.Hi) - tn.next) / stride))
|
|
||||||
if rune(rn.Hi) < tn.next {
|
|
||||||
if tn.p++; int(tn.p) == len(tn.t.R16) {
|
|
||||||
tn.next = atEnd
|
|
||||||
} else {
|
|
||||||
tn.next = rune(tn.t.R16[tn.p].Lo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if r0.Lo == r0.Hi {
|
|
||||||
r0.Stride = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return r0
|
|
||||||
}
|
|
||||||
|
|
||||||
// next32 finds the ranged to be added to the table. If ranges overlap between
|
|
||||||
// multiple tables it clips the result to a non-overlapping range if the
|
|
||||||
// elements are not fully subsumed. It returns a zero range if there are no more
|
|
||||||
// ranges.
|
|
||||||
func (ti tablesIter) next32() unicode.Range32 {
|
|
||||||
sortIter(ti)
|
|
||||||
|
|
||||||
t0 := ti[0]
|
|
||||||
if t0.next == atEnd {
|
|
||||||
return unicode.Range32{}
|
|
||||||
}
|
|
||||||
r0 := t0.t.R32[t0.p]
|
|
||||||
r0.Lo = uint32(t0.next)
|
|
||||||
|
|
||||||
// We restrict the Hi of the current range if it overlaps with another range.
|
|
||||||
for i := range ti {
|
|
||||||
tn := ti[i]
|
|
||||||
// Since our tableIndices are sorted by next, we can break if the there
|
|
||||||
// is no overlap. The first value of a next range can always be merged
|
|
||||||
// into the current one, so we can break in case of equality as well.
|
|
||||||
if rune(r0.Hi) <= tn.next {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
rn := tn.t.R32[tn.p]
|
|
||||||
rn.Lo = uint32(tn.next)
|
|
||||||
|
|
||||||
// Limit r0.Hi based on next ranges in list, but allow it to overlap
|
|
||||||
// with ranges as long as it subsumes it.
|
|
||||||
m := (rn.Lo - r0.Lo) % r0.Stride
|
|
||||||
if m == 0 && (rn.Stride == r0.Stride || rn.Lo == rn.Hi) {
|
|
||||||
// Overlap, take the min of the two Hi values: for simplicity's sake
|
|
||||||
// we only process one range at a time.
|
|
||||||
if r0.Hi > rn.Hi {
|
|
||||||
r0.Hi = rn.Hi
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Not a compatible stride. Set to the last possible value before
|
|
||||||
// rn.Lo, but ensure there is at least one value.
|
|
||||||
if x := rn.Lo - m; r0.Lo <= x {
|
|
||||||
r0.Hi = x
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the next values for each table.
|
|
||||||
for i := range ti {
|
|
||||||
tn := &ti[i]
|
|
||||||
if rune(r0.Hi) < tn.next {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
rn := tn.t.R32[tn.p]
|
|
||||||
stride := rune(rn.Stride)
|
|
||||||
tn.next += stride * (1 + ((rune(r0.Hi) - tn.next) / stride))
|
|
||||||
if rune(rn.Hi) < tn.next {
|
|
||||||
if tn.p++; int(tn.p) == len(tn.t.R32) {
|
|
||||||
tn.next = atEnd
|
|
||||||
} else {
|
|
||||||
tn.next = rune(tn.t.R32[tn.p].Lo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if r0.Lo == r0.Hi {
|
|
||||||
r0.Stride = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return r0
|
|
||||||
}
|
|
70
vendor/golang.org/x/text/unicode/rangetable/rangetable.go
generated
vendored
70
vendor/golang.org/x/text/unicode/rangetable/rangetable.go
generated
vendored
|
@ -1,70 +0,0 @@
|
||||||
// Copyright 2015 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 rangetable provides utilities for creating and inspecting
|
|
||||||
// unicode.RangeTables.
|
|
||||||
package rangetable
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sort"
|
|
||||||
"unicode"
|
|
||||||
)
|
|
||||||
|
|
||||||
// New creates a RangeTable from the given runes, which may contain duplicates.
|
|
||||||
func New(r ...rune) *unicode.RangeTable {
|
|
||||||
if len(r) == 0 {
|
|
||||||
return &unicode.RangeTable{}
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Sort(byRune(r))
|
|
||||||
|
|
||||||
// Remove duplicates.
|
|
||||||
k := 1
|
|
||||||
for i := 1; i < len(r); i++ {
|
|
||||||
if r[k-1] != r[i] {
|
|
||||||
r[k] = r[i]
|
|
||||||
k++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var rt unicode.RangeTable
|
|
||||||
for _, r := range r[:k] {
|
|
||||||
if r <= 0xFFFF {
|
|
||||||
rt.R16 = append(rt.R16, unicode.Range16{Lo: uint16(r), Hi: uint16(r), Stride: 1})
|
|
||||||
} else {
|
|
||||||
rt.R32 = append(rt.R32, unicode.Range32{Lo: uint32(r), Hi: uint32(r), Stride: 1})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optimize RangeTable.
|
|
||||||
return Merge(&rt)
|
|
||||||
}
|
|
||||||
|
|
||||||
type byRune []rune
|
|
||||||
|
|
||||||
func (r byRune) Len() int { return len(r) }
|
|
||||||
func (r byRune) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
|
|
||||||
func (r byRune) Less(i, j int) bool { return r[i] < r[j] }
|
|
||||||
|
|
||||||
// Visit visits all runes in the given RangeTable in order, calling fn for each.
|
|
||||||
func Visit(rt *unicode.RangeTable, fn func(rune)) {
|
|
||||||
for _, r16 := range rt.R16 {
|
|
||||||
for r := rune(r16.Lo); r <= rune(r16.Hi); r += rune(r16.Stride) {
|
|
||||||
fn(r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, r32 := range rt.R32 {
|
|
||||||
for r := rune(r32.Lo); r <= rune(r32.Hi); r += rune(r32.Stride) {
|
|
||||||
fn(r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assigned returns a RangeTable with all assigned code points for a given
|
|
||||||
// Unicode version. This includes graphic, format, control, and private-use
|
|
||||||
// characters. It returns nil if the data for the given version is not
|
|
||||||
// available.
|
|
||||||
func Assigned(version string) *unicode.RangeTable {
|
|
||||||
return assigned[version]
|
|
||||||
}
|
|
5735
vendor/golang.org/x/text/unicode/rangetable/tables.go
generated
vendored
5735
vendor/golang.org/x/text/unicode/rangetable/tables.go
generated
vendored
File diff suppressed because it is too large
Load diff
115
vendor/golang.org/x/text/width/gen.go
generated
vendored
115
vendor/golang.org/x/text/width/gen.go
generated
vendored
|
@ -1,115 +0,0 @@
|
||||||
// Copyright 2015 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 ignore
|
|
||||||
|
|
||||||
// This program generates the trie for width operations. The generated table
|
|
||||||
// includes width category information as well as the normalization mappings.
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"math"
|
|
||||||
"unicode/utf8"
|
|
||||||
|
|
||||||
"golang.org/x/text/internal/gen"
|
|
||||||
"golang.org/x/text/internal/triegen"
|
|
||||||
)
|
|
||||||
|
|
||||||
// See gen_common.go for flags.
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
gen.Init()
|
|
||||||
genTables()
|
|
||||||
genTests()
|
|
||||||
gen.Repackage("gen_trieval.go", "trieval.go", "width")
|
|
||||||
gen.Repackage("gen_common.go", "common_test.go", "width")
|
|
||||||
}
|
|
||||||
|
|
||||||
func genTables() {
|
|
||||||
t := triegen.NewTrie("width")
|
|
||||||
// fold and inverse mappings. See mapComment for a description of the format
|
|
||||||
// of each entry. Add dummy value to make an index of 0 mean no mapping.
|
|
||||||
inverse := [][4]byte{{}}
|
|
||||||
mapping := map[[4]byte]int{[4]byte{}: 0}
|
|
||||||
|
|
||||||
getWidthData(func(r rune, tag elem, alt rune) {
|
|
||||||
idx := 0
|
|
||||||
if alt != 0 {
|
|
||||||
var buf [4]byte
|
|
||||||
buf[0] = byte(utf8.EncodeRune(buf[1:], alt))
|
|
||||||
s := string(r)
|
|
||||||
buf[buf[0]] ^= s[len(s)-1]
|
|
||||||
var ok bool
|
|
||||||
if idx, ok = mapping[buf]; !ok {
|
|
||||||
idx = len(mapping)
|
|
||||||
if idx > math.MaxUint8 {
|
|
||||||
log.Fatalf("Index %d does not fit in a byte.", idx)
|
|
||||||
}
|
|
||||||
mapping[buf] = idx
|
|
||||||
inverse = append(inverse, buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t.Insert(r, uint64(tag|elem(idx)))
|
|
||||||
})
|
|
||||||
|
|
||||||
w := &bytes.Buffer{}
|
|
||||||
gen.WriteUnicodeVersion(w)
|
|
||||||
|
|
||||||
sz, err := t.Gen(w)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sz += writeMappings(w, inverse)
|
|
||||||
|
|
||||||
fmt.Fprintf(w, "// Total table size %d bytes (%dKiB)\n", sz, sz/1024)
|
|
||||||
|
|
||||||
gen.WriteGoFile(*outputFile, "width", w.Bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
const inverseDataComment = `
|
|
||||||
// inverseData contains 4-byte entries of the following format:
|
|
||||||
// <length> <modified UTF-8-encoded rune> <0 padding>
|
|
||||||
// The last byte of the UTF-8-encoded rune is xor-ed with the last byte of the
|
|
||||||
// UTF-8 encoding of the original rune. Mappings often have the following
|
|
||||||
// pattern:
|
|
||||||
// A -> A (U+FF21 -> U+0041)
|
|
||||||
// B -> B (U+FF22 -> U+0042)
|
|
||||||
// ...
|
|
||||||
// By xor-ing the last byte the same entry can be shared by many mappings. This
|
|
||||||
// reduces the total number of distinct entries by about two thirds.
|
|
||||||
// The resulting entry for the aforementioned mappings is
|
|
||||||
// { 0x01, 0xE0, 0x00, 0x00 }
|
|
||||||
// Using this entry to map U+FF21 (UTF-8 [EF BC A1]), we get
|
|
||||||
// E0 ^ A1 = 41.
|
|
||||||
// Similarly, for U+FF22 (UTF-8 [EF BC A2]), we get
|
|
||||||
// E0 ^ A2 = 42.
|
|
||||||
// Note that because of the xor-ing, the byte sequence stored in the entry is
|
|
||||||
// not valid UTF-8.`
|
|
||||||
|
|
||||||
func writeMappings(w io.Writer, data [][4]byte) int {
|
|
||||||
fmt.Fprintln(w, inverseDataComment)
|
|
||||||
fmt.Fprintf(w, "var inverseData = [%d][4]byte{\n", len(data))
|
|
||||||
for _, x := range data {
|
|
||||||
fmt.Fprintf(w, "{ 0x%02x, 0x%02x, 0x%02x, 0x%02x },\n", x[0], x[1], x[2], x[3])
|
|
||||||
}
|
|
||||||
fmt.Fprintln(w, "}")
|
|
||||||
return len(data) * 4
|
|
||||||
}
|
|
||||||
|
|
||||||
func genTests() {
|
|
||||||
w := &bytes.Buffer{}
|
|
||||||
fmt.Fprintf(w, "\nvar mapRunes = map[rune]struct{r rune; e elem}{\n")
|
|
||||||
getWidthData(func(r rune, tag elem, alt rune) {
|
|
||||||
if alt != 0 {
|
|
||||||
fmt.Fprintf(w, "\t0x%X: {0x%X, 0x%X},\n", r, alt, tag)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
fmt.Fprintln(w, "}")
|
|
||||||
gen.WriteGoFile("runes_test.go", "width", w.Bytes())
|
|
||||||
}
|
|
96
vendor/golang.org/x/text/width/gen_common.go
generated
vendored
96
vendor/golang.org/x/text/width/gen_common.go
generated
vendored
|
@ -1,96 +0,0 @@
|
||||||
// Copyright 2015 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 ignore
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
// This code is shared between the main code generator and the test code.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"log"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"golang.org/x/text/internal/gen"
|
|
||||||
"golang.org/x/text/internal/ucd"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
outputFile = flag.String("out", "tables.go", "output file")
|
|
||||||
)
|
|
||||||
|
|
||||||
var typeMap = map[string]elem{
|
|
||||||
"A": tagAmbiguous,
|
|
||||||
"N": tagNeutral,
|
|
||||||
"Na": tagNarrow,
|
|
||||||
"W": tagWide,
|
|
||||||
"F": tagFullwidth,
|
|
||||||
"H": tagHalfwidth,
|
|
||||||
}
|
|
||||||
|
|
||||||
// getWidthData calls f for every entry for which it is defined.
|
|
||||||
//
|
|
||||||
// f may be called multiple times for the same rune. The last call to f is the
|
|
||||||
// correct value. f is not called for all runes. The default tag type is
|
|
||||||
// Neutral.
|
|
||||||
func getWidthData(f func(r rune, tag elem, alt rune)) {
|
|
||||||
// Set the default values for Unified Ideographs. In line with Annex 11,
|
|
||||||
// we encode full ranges instead of the defined runes in Unified_Ideograph.
|
|
||||||
for _, b := range []struct{ lo, hi rune }{
|
|
||||||
{0x4E00, 0x9FFF}, // the CJK Unified Ideographs block,
|
|
||||||
{0x3400, 0x4DBF}, // the CJK Unified Ideographs Externsion A block,
|
|
||||||
{0xF900, 0xFAFF}, // the CJK Compatibility Ideographs block,
|
|
||||||
{0x20000, 0x2FFFF}, // the Supplementary Ideographic Plane,
|
|
||||||
{0x30000, 0x3FFFF}, // the Tertiary Ideographic Plane,
|
|
||||||
} {
|
|
||||||
for r := b.lo; r <= b.hi; r++ {
|
|
||||||
f(r, tagWide, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inverse := map[rune]rune{}
|
|
||||||
maps := map[string]bool{
|
|
||||||
"<wide>": true,
|
|
||||||
"<narrow>": true,
|
|
||||||
}
|
|
||||||
|
|
||||||
// We cannot reuse package norm's decomposition, as we need an unexpanded
|
|
||||||
// decomposition. We make use of the opportunity to verify that the
|
|
||||||
// decomposition type is as expected.
|
|
||||||
ucd.Parse(gen.OpenUCDFile("UnicodeData.txt"), func(p *ucd.Parser) {
|
|
||||||
r := p.Rune(0)
|
|
||||||
s := strings.SplitN(p.String(ucd.DecompMapping), " ", 2)
|
|
||||||
if !maps[s[0]] {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
x, err := strconv.ParseUint(s[1], 16, 32)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Error parsing rune %q", s[1])
|
|
||||||
}
|
|
||||||
if inverse[r] != 0 || inverse[rune(x)] != 0 {
|
|
||||||
log.Fatalf("Circular dependency in mapping between %U and %U", r, x)
|
|
||||||
}
|
|
||||||
inverse[r] = rune(x)
|
|
||||||
inverse[rune(x)] = r
|
|
||||||
})
|
|
||||||
|
|
||||||
// <rune range>;<type>
|
|
||||||
ucd.Parse(gen.OpenUCDFile("EastAsianWidth.txt"), func(p *ucd.Parser) {
|
|
||||||
tag, ok := typeMap[p.String(1)]
|
|
||||||
if !ok {
|
|
||||||
log.Fatalf("Unknown width type %q", p.String(1))
|
|
||||||
}
|
|
||||||
r := p.Rune(0)
|
|
||||||
alt, ok := inverse[r]
|
|
||||||
if tag == tagFullwidth || tag == tagHalfwidth && r != wonSign {
|
|
||||||
tag |= tagNeedsFold
|
|
||||||
if !ok {
|
|
||||||
log.Fatalf("Narrow or wide rune %U has no decomposition", r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
f(r, tag, alt)
|
|
||||||
})
|
|
||||||
}
|
|
34
vendor/golang.org/x/text/width/gen_trieval.go
generated
vendored
34
vendor/golang.org/x/text/width/gen_trieval.go
generated
vendored
|
@ -1,34 +0,0 @@
|
||||||
// Copyright 2015 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 ignore
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
// elem is an entry of the width trie. The high byte is used to encode the type
|
|
||||||
// of the rune. The low byte is used to store the index to a mapping entry in
|
|
||||||
// the inverseData array.
|
|
||||||
type elem uint16
|
|
||||||
|
|
||||||
const (
|
|
||||||
tagNeutral elem = iota << typeShift
|
|
||||||
tagAmbiguous
|
|
||||||
tagWide
|
|
||||||
tagNarrow
|
|
||||||
tagFullwidth
|
|
||||||
tagHalfwidth
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
numTypeBits = 3
|
|
||||||
typeShift = 16 - numTypeBits
|
|
||||||
|
|
||||||
// tagNeedsFold is true for all fullwidth and halfwidth runes except for
|
|
||||||
// the Won sign U+20A9.
|
|
||||||
tagNeedsFold = 0x1000
|
|
||||||
|
|
||||||
// The Korean Won sign is halfwidth, but SHOULD NOT be mapped to a wide
|
|
||||||
// variant.
|
|
||||||
wonSign rune = 0x20A9
|
|
||||||
)
|
|
Loading…
Reference in a new issue