mirror of
https://github.com/Luzifer/badge-gen.git
synced 2024-11-09 22:00:05 +00:00
Update Godeps
This commit is contained in:
parent
f14dfcdc87
commit
53d540170f
111 changed files with 28225 additions and 11 deletions
77
Godeps/Godeps.json
generated
77
Godeps/Godeps.json
generated
|
@ -1,35 +1,90 @@
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/Luzifer/badge-gen",
|
"ImportPath": "github.com/Luzifer/badge-gen",
|
||||||
"GoVersion": "go1.4.2",
|
"GoVersion": "go1.6",
|
||||||
|
"GodepVersion": "v62",
|
||||||
"Deps": [
|
"Deps": [
|
||||||
{
|
{
|
||||||
"ImportPath": "code.google.com/p/freetype-go/freetype/truetype",
|
"ImportPath": "github.com/Luzifer/rconfig",
|
||||||
"Comment": "release-85",
|
"Comment": "v1.1.0",
|
||||||
"Rev": "46c3056cafbb4da11c4087a892c7d2bfa4224a8f"
|
"Rev": "c27bd3a64b5b19556914d9fec69922cf3852d585"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/alecthomas/template",
|
"ImportPath": "github.com/golang/freetype/raster",
|
||||||
"Rev": "c08b835da7d1f28aa5b914921b914071be8d2d5c"
|
"Comment": "release-131-g38b4c39",
|
||||||
|
"Rev": "38b4c392adc5eed94207994c4848fff99f4ac234"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/golang/freetype/truetype",
|
||||||
|
"Comment": "release-131-g38b4c39",
|
||||||
|
"Rev": "38b4c392adc5eed94207994c4848fff99f4ac234"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/gorilla/context",
|
"ImportPath": "github.com/gorilla/context",
|
||||||
"Rev": "14f550f51af52180c2eefed15e5fd18d63c0a64a"
|
"Rev": "1c83b3eabd45b6d76072b66b746c20815fb2872d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/gorilla/mux",
|
"ImportPath": "github.com/gorilla/mux",
|
||||||
"Rev": "660d31f8602b95058fed6833debf113e85350868"
|
"Rev": "49c024275504f0341e5a9971eb7ba7fa3dc7af40"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/spf13/pflag",
|
||||||
|
"Rev": "367864438f1b1a3c7db4da06a2f55b144e6784e0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/tdewolff/buffer",
|
"ImportPath": "github.com/tdewolff/buffer",
|
||||||
"Rev": "8ff0a82a07bd36d4cf1e8728015553e67d720100"
|
"Comment": "v1.0.0-6-g0edfcb7",
|
||||||
|
"Rev": "0edfcb7b750146ff879e95831de2ef53605a5cb5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/tdewolff/minify",
|
"ImportPath": "github.com/tdewolff/minify",
|
||||||
"Rev": "a974e906cf4a3d88031daea7ce70211c3ad2cbb9"
|
"Comment": "v2.0.0-85-g28aac1f",
|
||||||
|
"Rev": "28aac1f92d928dfb63dd0258a3b2248a020e86da"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/tdewolff/minify/css",
|
||||||
|
"Comment": "v2.0.0-85-g28aac1f",
|
||||||
|
"Rev": "28aac1f92d928dfb63dd0258a3b2248a020e86da"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/tdewolff/minify/svg",
|
||||||
|
"Comment": "v2.0.0-85-g28aac1f",
|
||||||
|
"Rev": "28aac1f92d928dfb63dd0258a3b2248a020e86da"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/tdewolff/parse",
|
"ImportPath": "github.com/tdewolff/parse",
|
||||||
"Rev": "faf467f7e305130ee8d98bcfa6fa87d35c35e67b"
|
"Comment": "v2.0.0-2-g34d5c11",
|
||||||
|
"Rev": "34d5c1160d4503da4b456e5094609f2331d6dde3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/tdewolff/parse/css",
|
||||||
|
"Comment": "v2.0.0-2-g34d5c11",
|
||||||
|
"Rev": "34d5c1160d4503da4b456e5094609f2331d6dde3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/tdewolff/parse/svg",
|
||||||
|
"Comment": "v2.0.0-2-g34d5c11",
|
||||||
|
"Rev": "34d5c1160d4503da4b456e5094609f2331d6dde3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/tdewolff/parse/xml",
|
||||||
|
"Comment": "v2.0.0-2-g34d5c11",
|
||||||
|
"Rev": "34d5c1160d4503da4b456e5094609f2331d6dde3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/tdewolff/strconv",
|
||||||
|
"Rev": "3e8091f4417ebaaa3910da63a45ea394ebbfb0e3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/image/font",
|
||||||
|
"Rev": "97680175a5267bb8b31f1923e7a66df98013b11a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/image/math/fixed",
|
||||||
|
"Rev": "97680175a5267bb8b31f1923e7a66df98013b11a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "gopkg.in/yaml.v2",
|
||||||
|
"Rev": "53feefa2559fb8dfa8d81baad31be332c97d6c77"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
5
Godeps/Readme
generated
Normal file
5
Godeps/Readme
generated
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
This directory tree is generated automatically by godep.
|
||||||
|
|
||||||
|
Please do not edit.
|
||||||
|
|
||||||
|
See https://github.com/tools/godep for more information.
|
8
vendor/github.com/Luzifer/rconfig/.travis.yml
generated
vendored
Normal file
8
vendor/github.com/Luzifer/rconfig/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.4
|
||||||
|
- 1.5
|
||||||
|
- tip
|
||||||
|
|
||||||
|
script: go test -v -race -cover ./...
|
5
vendor/github.com/Luzifer/rconfig/History.md
generated
vendored
Normal file
5
vendor/github.com/Luzifer/rconfig/History.md
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# 1.1.0 / 2016-06-28
|
||||||
|
|
||||||
|
* Support time.Duration config parameters
|
||||||
|
* Added goreportcard badge
|
||||||
|
* Added testcase for using bool with ENV and default
|
13
vendor/github.com/Luzifer/rconfig/LICENSE
generated
vendored
Normal file
13
vendor/github.com/Luzifer/rconfig/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
Copyright 2015 Knut Ahlers <knut@ahlers.me>
|
||||||
|
|
||||||
|
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.
|
94
vendor/github.com/Luzifer/rconfig/README.md
generated
vendored
Normal file
94
vendor/github.com/Luzifer/rconfig/README.md
generated
vendored
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
[![Build Status](https://travis-ci.org/Luzifer/rconfig.svg?branch=master)](https://travis-ci.org/Luzifer/rconfig)
|
||||||
|
[![License: Apache v2.0](https://badge.luzifer.io/v1/badge?color=5d79b5&title=license&text=Apache+v2.0)](http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
[![Documentation](https://badge.luzifer.io/v1/badge?title=godoc&text=reference)](https://godoc.org/github.com/Luzifer/rconfig)
|
||||||
|
[![Go Report](http://goreportcard.com/badge/Luzifer/rconfig)](http://goreportcard.com/report/Luzifer/rconfig)
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
> Package rconfig implements a CLI configuration reader with struct-embedded defaults, environment variables and posix compatible flag parsing using the [pflag](https://github.com/spf13/pflag) library.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Install by running:
|
||||||
|
|
||||||
|
```
|
||||||
|
go get -u github.com/Luzifer/rconfig
|
||||||
|
```
|
||||||
|
|
||||||
|
OR fetch a specific version:
|
||||||
|
|
||||||
|
```
|
||||||
|
go get -u gopkg.in/luzifer/rconfig.v1
|
||||||
|
```
|
||||||
|
|
||||||
|
Run tests by running:
|
||||||
|
|
||||||
|
```
|
||||||
|
go test -v -race -cover github.com/Luzifer/rconfig
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
As a first step define a struct holding your configuration:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type config struct {
|
||||||
|
Username string `default:"unknown" flag:"user" description:"Your name"`
|
||||||
|
Details struct {
|
||||||
|
Age int `default:"25" flag:"age" env:"age" description:"Your age"`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Next create an instance of that struct and let `rconfig` fill that config:
|
||||||
|
|
||||||
|
```go
|
||||||
|
var cfg config
|
||||||
|
func init() {
|
||||||
|
cfg = config{}
|
||||||
|
rconfig.Parse(&cfg)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You're ready to access your configuration:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func main() {
|
||||||
|
fmt.Printf("Hello %s, happy birthday for your %dth birthday.",
|
||||||
|
cfg.Username,
|
||||||
|
cfg.Details.Age)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Provide variable defaults by using a file
|
||||||
|
|
||||||
|
Given you have a file `~/.myapp.yml` containing some secrets or usernames (for the example below username is assumed to be "luzifer") as a default configuration for your application you can use this source code to load the defaults from that file using the `vardefault` tag in your configuration struct.
|
||||||
|
|
||||||
|
The order of the directives (lower number = higher precedence):
|
||||||
|
|
||||||
|
1. Flags provided in command line
|
||||||
|
1. Environment variables
|
||||||
|
1. Variable defaults (`vardefault` tag in the struct)
|
||||||
|
1. `default` tag in the struct
|
||||||
|
|
||||||
|
```go
|
||||||
|
type config struct {
|
||||||
|
Username string `vardefault:"username" flag:"username" description:"Your username"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var cfg = config{}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rconfig.SetVariableDefaults(rconfig.VarDefaultsFromYAMLFile("~/.myapp.yml"))
|
||||||
|
rconfig.Parse(&cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Printf("Username = %s", cfg.Username)
|
||||||
|
// Output: Username = luzifer
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## More info
|
||||||
|
|
||||||
|
You can see the full reference documentation of the rconfig package [at godoc.org](https://godoc.org/github.com/Luzifer/rconfig), or through go's standard documentation system by running `godoc -http=:6060` and browsing to [http://localhost:6060/pkg/github.com/Luzifer/rconfig](http://localhost:6060/pkg/github.com/Luzifer/rconfig) after installation.
|
338
vendor/github.com/Luzifer/rconfig/config.go
generated
vendored
Normal file
338
vendor/github.com/Luzifer/rconfig/config.go
generated
vendored
Normal file
|
@ -0,0 +1,338 @@
|
||||||
|
// Package rconfig implements a CLI configuration reader with struct-embedded
|
||||||
|
// defaults, environment variables and posix compatible flag parsing using
|
||||||
|
// the pflag library.
|
||||||
|
package rconfig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
fs *pflag.FlagSet
|
||||||
|
variableDefaults map[string]string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
variableDefaults = make(map[string]string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse takes the pointer to a struct filled with variables which should be read
|
||||||
|
// from ENV, default or flag. The precedence in this is flag > ENV > default. So
|
||||||
|
// if a flag is specified on the CLI it will overwrite the ENV and otherwise ENV
|
||||||
|
// overwrites the default specified.
|
||||||
|
//
|
||||||
|
// For your configuration struct you can use the following struct-tags to control
|
||||||
|
// the behavior of rconfig:
|
||||||
|
//
|
||||||
|
// default: Set a default value
|
||||||
|
// vardefault: Read the default value from the variable defaults
|
||||||
|
// env: Read the value from this environment variable
|
||||||
|
// flag: Flag to read in format "long,short" (for example "listen,l")
|
||||||
|
// description: A help text for Usage output to guide your users
|
||||||
|
//
|
||||||
|
// The format you need to specify those values you can see in the example to this
|
||||||
|
// function.
|
||||||
|
//
|
||||||
|
func Parse(config interface{}) error {
|
||||||
|
return parse(config, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Args returns the non-flag command-line arguments.
|
||||||
|
func Args() []string {
|
||||||
|
return fs.Args()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage prints a basic usage with the corresponding defaults for the flags to
|
||||||
|
// os.Stdout. The defaults are derived from the `default` struct-tag and the ENV.
|
||||||
|
func Usage() {
|
||||||
|
if fs != nil && fs.Parsed() {
|
||||||
|
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
|
||||||
|
fs.PrintDefaults()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetVariableDefaults presets the parser with a map of default values to be used
|
||||||
|
// when specifying the vardefault tag
|
||||||
|
func SetVariableDefaults(defaults map[string]string) {
|
||||||
|
variableDefaults = defaults
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse(in interface{}, args []string) error {
|
||||||
|
if args == nil {
|
||||||
|
args = os.Args
|
||||||
|
}
|
||||||
|
|
||||||
|
fs = pflag.NewFlagSet(os.Args[0], pflag.ExitOnError)
|
||||||
|
if err := execTags(in, fs); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fs.Parse(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
func execTags(in interface{}, fs *pflag.FlagSet) error {
|
||||||
|
if reflect.TypeOf(in).Kind() != reflect.Ptr {
|
||||||
|
return errors.New("Calling parser with non-pointer")
|
||||||
|
}
|
||||||
|
|
||||||
|
if reflect.ValueOf(in).Elem().Kind() != reflect.Struct {
|
||||||
|
return errors.New("Calling parser with pointer to non-struct")
|
||||||
|
}
|
||||||
|
|
||||||
|
st := reflect.ValueOf(in).Elem()
|
||||||
|
for i := 0; i < st.NumField(); i++ {
|
||||||
|
valField := st.Field(i)
|
||||||
|
typeField := st.Type().Field(i)
|
||||||
|
|
||||||
|
if typeField.Tag.Get("default") == "" && typeField.Tag.Get("env") == "" && typeField.Tag.Get("flag") == "" && typeField.Type.Kind() != reflect.Struct {
|
||||||
|
// None of our supported tags is present and it's not a sub-struct
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
value := varDefault(typeField.Tag.Get("vardefault"), typeField.Tag.Get("default"))
|
||||||
|
value = envDefault(typeField.Tag.Get("env"), value)
|
||||||
|
parts := strings.Split(typeField.Tag.Get("flag"), ",")
|
||||||
|
|
||||||
|
switch typeField.Type {
|
||||||
|
case reflect.TypeOf(time.Duration(0)):
|
||||||
|
v, err := time.ParseDuration(value)
|
||||||
|
if err != nil {
|
||||||
|
if value == "" {
|
||||||
|
v = time.Duration(0)
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if typeField.Tag.Get("flag") != "" {
|
||||||
|
if len(parts) == 1 {
|
||||||
|
fs.DurationVar(valField.Addr().Interface().(*time.Duration), parts[0], v, typeField.Tag.Get("description"))
|
||||||
|
} else {
|
||||||
|
fs.DurationVarP(valField.Addr().Interface().(*time.Duration), parts[0], parts[1], v, typeField.Tag.Get("description"))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
valField.Set(reflect.ValueOf(v))
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch typeField.Type.Kind() {
|
||||||
|
case reflect.String:
|
||||||
|
if typeField.Tag.Get("flag") != "" {
|
||||||
|
if len(parts) == 1 {
|
||||||
|
fs.StringVar(valField.Addr().Interface().(*string), parts[0], value, typeField.Tag.Get("description"))
|
||||||
|
} else {
|
||||||
|
fs.StringVarP(valField.Addr().Interface().(*string), parts[0], parts[1], value, typeField.Tag.Get("description"))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
valField.SetString(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
case reflect.Bool:
|
||||||
|
v := value == "true"
|
||||||
|
if typeField.Tag.Get("flag") != "" {
|
||||||
|
if len(parts) == 1 {
|
||||||
|
fs.BoolVar(valField.Addr().Interface().(*bool), parts[0], v, typeField.Tag.Get("description"))
|
||||||
|
} else {
|
||||||
|
fs.BoolVarP(valField.Addr().Interface().(*bool), parts[0], parts[1], v, typeField.Tag.Get("description"))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
valField.SetBool(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int32, reflect.Int64:
|
||||||
|
vt, err := strconv.ParseInt(value, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
if value == "" {
|
||||||
|
vt = 0
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if typeField.Tag.Get("flag") != "" {
|
||||||
|
registerFlagInt(typeField.Type.Kind(), fs, valField.Addr().Interface(), parts, vt, typeField.Tag.Get("description"))
|
||||||
|
} else {
|
||||||
|
valField.SetInt(vt)
|
||||||
|
}
|
||||||
|
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
vt, err := strconv.ParseUint(value, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
if value == "" {
|
||||||
|
vt = 0
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if typeField.Tag.Get("flag") != "" {
|
||||||
|
registerFlagUint(typeField.Type.Kind(), fs, valField.Addr().Interface(), parts, vt, typeField.Tag.Get("description"))
|
||||||
|
} else {
|
||||||
|
valField.SetUint(vt)
|
||||||
|
}
|
||||||
|
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
vt, err := strconv.ParseFloat(value, 64)
|
||||||
|
if err != nil {
|
||||||
|
if value == "" {
|
||||||
|
vt = 0.0
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if typeField.Tag.Get("flag") != "" {
|
||||||
|
registerFlagFloat(typeField.Type.Kind(), fs, valField.Addr().Interface(), parts, vt, typeField.Tag.Get("description"))
|
||||||
|
} else {
|
||||||
|
valField.SetFloat(vt)
|
||||||
|
}
|
||||||
|
|
||||||
|
case reflect.Struct:
|
||||||
|
if err := execTags(valField.Addr().Interface(), fs); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
case reflect.Slice:
|
||||||
|
switch typeField.Type.Elem().Kind() {
|
||||||
|
case reflect.Int:
|
||||||
|
def := []int{}
|
||||||
|
for _, v := range strings.Split(value, ",") {
|
||||||
|
it, err := strconv.ParseInt(strings.TrimSpace(v), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
def = append(def, int(it))
|
||||||
|
}
|
||||||
|
if len(parts) == 1 {
|
||||||
|
fs.IntSliceVar(valField.Addr().Interface().(*[]int), parts[0], def, typeField.Tag.Get("description"))
|
||||||
|
} else {
|
||||||
|
fs.IntSliceVarP(valField.Addr().Interface().(*[]int), parts[0], parts[1], def, typeField.Tag.Get("description"))
|
||||||
|
}
|
||||||
|
case reflect.String:
|
||||||
|
del := typeField.Tag.Get("delimiter")
|
||||||
|
if len(del) == 0 {
|
||||||
|
del = ","
|
||||||
|
}
|
||||||
|
def := strings.Split(value, del)
|
||||||
|
if len(parts) == 1 {
|
||||||
|
fs.StringSliceVar(valField.Addr().Interface().(*[]string), parts[0], def, typeField.Tag.Get("description"))
|
||||||
|
} else {
|
||||||
|
fs.StringSliceVarP(valField.Addr().Interface().(*[]string), parts[0], parts[1], def, typeField.Tag.Get("description"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerFlagFloat(t reflect.Kind, fs *pflag.FlagSet, field interface{}, parts []string, vt float64, desc string) {
|
||||||
|
switch t {
|
||||||
|
case reflect.Float32:
|
||||||
|
if len(parts) == 1 {
|
||||||
|
fs.Float32Var(field.(*float32), parts[0], float32(vt), desc)
|
||||||
|
} else {
|
||||||
|
fs.Float32VarP(field.(*float32), parts[0], parts[1], float32(vt), desc)
|
||||||
|
}
|
||||||
|
case reflect.Float64:
|
||||||
|
if len(parts) == 1 {
|
||||||
|
fs.Float64Var(field.(*float64), parts[0], float64(vt), desc)
|
||||||
|
} else {
|
||||||
|
fs.Float64VarP(field.(*float64), parts[0], parts[1], float64(vt), desc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerFlagInt(t reflect.Kind, fs *pflag.FlagSet, field interface{}, parts []string, vt int64, desc string) {
|
||||||
|
switch t {
|
||||||
|
case reflect.Int:
|
||||||
|
if len(parts) == 1 {
|
||||||
|
fs.IntVar(field.(*int), parts[0], int(vt), desc)
|
||||||
|
} else {
|
||||||
|
fs.IntVarP(field.(*int), parts[0], parts[1], int(vt), desc)
|
||||||
|
}
|
||||||
|
case reflect.Int8:
|
||||||
|
if len(parts) == 1 {
|
||||||
|
fs.Int8Var(field.(*int8), parts[0], int8(vt), desc)
|
||||||
|
} else {
|
||||||
|
fs.Int8VarP(field.(*int8), parts[0], parts[1], int8(vt), desc)
|
||||||
|
}
|
||||||
|
case reflect.Int32:
|
||||||
|
if len(parts) == 1 {
|
||||||
|
fs.Int32Var(field.(*int32), parts[0], int32(vt), desc)
|
||||||
|
} else {
|
||||||
|
fs.Int32VarP(field.(*int32), parts[0], parts[1], int32(vt), desc)
|
||||||
|
}
|
||||||
|
case reflect.Int64:
|
||||||
|
if len(parts) == 1 {
|
||||||
|
fs.Int64Var(field.(*int64), parts[0], int64(vt), desc)
|
||||||
|
} else {
|
||||||
|
fs.Int64VarP(field.(*int64), parts[0], parts[1], int64(vt), desc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerFlagUint(t reflect.Kind, fs *pflag.FlagSet, field interface{}, parts []string, vt uint64, desc string) {
|
||||||
|
switch t {
|
||||||
|
case reflect.Uint:
|
||||||
|
if len(parts) == 1 {
|
||||||
|
fs.UintVar(field.(*uint), parts[0], uint(vt), desc)
|
||||||
|
} else {
|
||||||
|
fs.UintVarP(field.(*uint), parts[0], parts[1], uint(vt), desc)
|
||||||
|
}
|
||||||
|
case reflect.Uint8:
|
||||||
|
if len(parts) == 1 {
|
||||||
|
fs.Uint8Var(field.(*uint8), parts[0], uint8(vt), desc)
|
||||||
|
} else {
|
||||||
|
fs.Uint8VarP(field.(*uint8), parts[0], parts[1], uint8(vt), desc)
|
||||||
|
}
|
||||||
|
case reflect.Uint16:
|
||||||
|
if len(parts) == 1 {
|
||||||
|
fs.Uint16Var(field.(*uint16), parts[0], uint16(vt), desc)
|
||||||
|
} else {
|
||||||
|
fs.Uint16VarP(field.(*uint16), parts[0], parts[1], uint16(vt), desc)
|
||||||
|
}
|
||||||
|
case reflect.Uint32:
|
||||||
|
if len(parts) == 1 {
|
||||||
|
fs.Uint32Var(field.(*uint32), parts[0], uint32(vt), desc)
|
||||||
|
} else {
|
||||||
|
fs.Uint32VarP(field.(*uint32), parts[0], parts[1], uint32(vt), desc)
|
||||||
|
}
|
||||||
|
case reflect.Uint64:
|
||||||
|
if len(parts) == 1 {
|
||||||
|
fs.Uint64Var(field.(*uint64), parts[0], uint64(vt), desc)
|
||||||
|
} else {
|
||||||
|
fs.Uint64VarP(field.(*uint64), parts[0], parts[1], uint64(vt), desc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func envDefault(env, def string) string {
|
||||||
|
value := def
|
||||||
|
|
||||||
|
if env != "" {
|
||||||
|
if e := os.Getenv(env); e != "" {
|
||||||
|
value = e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
func varDefault(name, def string) string {
|
||||||
|
value := def
|
||||||
|
|
||||||
|
if name != "" {
|
||||||
|
if v, ok := variableDefaults[name]; ok {
|
||||||
|
value = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
27
vendor/github.com/Luzifer/rconfig/vardefault_providers.go
generated
vendored
Normal file
27
vendor/github.com/Luzifer/rconfig/vardefault_providers.go
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
package rconfig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// VarDefaultsFromYAMLFile reads contents of a file and calls VarDefaultsFromYAML
|
||||||
|
func VarDefaultsFromYAMLFile(filename string) map[string]string {
|
||||||
|
data, err := ioutil.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return make(map[string]string)
|
||||||
|
}
|
||||||
|
|
||||||
|
return VarDefaultsFromYAML(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// VarDefaultsFromYAML creates a vardefaults map from YAML raw data
|
||||||
|
func VarDefaultsFromYAML(in []byte) map[string]string {
|
||||||
|
out := make(map[string]string)
|
||||||
|
err := yaml.Unmarshal(in, &out)
|
||||||
|
if err != nil {
|
||||||
|
return make(map[string]string)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
12
vendor/github.com/golang/freetype/LICENSE
generated
vendored
Normal file
12
vendor/github.com/golang/freetype/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
Use of the Freetype-Go software is subject to your choice of exactly one of
|
||||||
|
the following two licenses:
|
||||||
|
* The FreeType License, which is similar to the original BSD license with
|
||||||
|
an advertising clause, or
|
||||||
|
* The GNU General Public License (GPL), version 2 or later.
|
||||||
|
|
||||||
|
The text of these licenses are available in the licenses/ftl.txt and the
|
||||||
|
licenses/gpl.txt files respectively. They are also available at
|
||||||
|
http://freetype.sourceforge.net/license.html
|
||||||
|
|
||||||
|
The Luxi fonts in the testdata directory are licensed separately. See the
|
||||||
|
testdata/COPYING file for details.
|
245
vendor/github.com/golang/freetype/raster/geom.go
generated
vendored
Normal file
245
vendor/github.com/golang/freetype/raster/geom.go
generated
vendored
Normal file
|
@ -0,0 +1,245 @@
|
||||||
|
// Copyright 2010 The Freetype-Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by your choice of either the
|
||||||
|
// FreeType License or the GNU General Public License version 2 (or
|
||||||
|
// any later version), both of which can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package raster
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
|
||||||
|
"golang.org/x/image/math/fixed"
|
||||||
|
)
|
||||||
|
|
||||||
|
// maxAbs returns the maximum of abs(a) and abs(b).
|
||||||
|
func maxAbs(a, b fixed.Int26_6) fixed.Int26_6 {
|
||||||
|
if a < 0 {
|
||||||
|
a = -a
|
||||||
|
}
|
||||||
|
if b < 0 {
|
||||||
|
b = -b
|
||||||
|
}
|
||||||
|
if a < b {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// pNeg returns the vector -p, or equivalently p rotated by 180 degrees.
|
||||||
|
func pNeg(p fixed.Point26_6) fixed.Point26_6 {
|
||||||
|
return fixed.Point26_6{-p.X, -p.Y}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pDot returns the dot product p·q.
|
||||||
|
func pDot(p fixed.Point26_6, q fixed.Point26_6) fixed.Int52_12 {
|
||||||
|
px, py := int64(p.X), int64(p.Y)
|
||||||
|
qx, qy := int64(q.X), int64(q.Y)
|
||||||
|
return fixed.Int52_12(px*qx + py*qy)
|
||||||
|
}
|
||||||
|
|
||||||
|
// pLen returns the length of the vector p.
|
||||||
|
func pLen(p fixed.Point26_6) fixed.Int26_6 {
|
||||||
|
// TODO(nigeltao): use fixed point math.
|
||||||
|
x := float64(p.X)
|
||||||
|
y := float64(p.Y)
|
||||||
|
return fixed.Int26_6(math.Sqrt(x*x + y*y))
|
||||||
|
}
|
||||||
|
|
||||||
|
// pNorm returns the vector p normalized to the given length, or zero if p is
|
||||||
|
// degenerate.
|
||||||
|
func pNorm(p fixed.Point26_6, length fixed.Int26_6) fixed.Point26_6 {
|
||||||
|
d := pLen(p)
|
||||||
|
if d == 0 {
|
||||||
|
return fixed.Point26_6{}
|
||||||
|
}
|
||||||
|
s, t := int64(length), int64(d)
|
||||||
|
x := int64(p.X) * s / t
|
||||||
|
y := int64(p.Y) * s / t
|
||||||
|
return fixed.Point26_6{fixed.Int26_6(x), fixed.Int26_6(y)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pRot45CW returns the vector p rotated clockwise by 45 degrees.
|
||||||
|
//
|
||||||
|
// Note that the Y-axis grows downwards, so {1, 0}.Rot45CW is {1/√2, 1/√2}.
|
||||||
|
func pRot45CW(p fixed.Point26_6) fixed.Point26_6 {
|
||||||
|
// 181/256 is approximately 1/√2, or sin(π/4).
|
||||||
|
px, py := int64(p.X), int64(p.Y)
|
||||||
|
qx := (+px - py) * 181 / 256
|
||||||
|
qy := (+px + py) * 181 / 256
|
||||||
|
return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pRot90CW returns the vector p rotated clockwise by 90 degrees.
|
||||||
|
//
|
||||||
|
// Note that the Y-axis grows downwards, so {1, 0}.Rot90CW is {0, 1}.
|
||||||
|
func pRot90CW(p fixed.Point26_6) fixed.Point26_6 {
|
||||||
|
return fixed.Point26_6{-p.Y, p.X}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pRot135CW returns the vector p rotated clockwise by 135 degrees.
|
||||||
|
//
|
||||||
|
// Note that the Y-axis grows downwards, so {1, 0}.Rot135CW is {-1/√2, 1/√2}.
|
||||||
|
func pRot135CW(p fixed.Point26_6) fixed.Point26_6 {
|
||||||
|
// 181/256 is approximately 1/√2, or sin(π/4).
|
||||||
|
px, py := int64(p.X), int64(p.Y)
|
||||||
|
qx := (-px - py) * 181 / 256
|
||||||
|
qy := (+px - py) * 181 / 256
|
||||||
|
return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pRot45CCW returns the vector p rotated counter-clockwise by 45 degrees.
|
||||||
|
//
|
||||||
|
// Note that the Y-axis grows downwards, so {1, 0}.Rot45CCW is {1/√2, -1/√2}.
|
||||||
|
func pRot45CCW(p fixed.Point26_6) fixed.Point26_6 {
|
||||||
|
// 181/256 is approximately 1/√2, or sin(π/4).
|
||||||
|
px, py := int64(p.X), int64(p.Y)
|
||||||
|
qx := (+px + py) * 181 / 256
|
||||||
|
qy := (-px + py) * 181 / 256
|
||||||
|
return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pRot90CCW returns the vector p rotated counter-clockwise by 90 degrees.
|
||||||
|
//
|
||||||
|
// Note that the Y-axis grows downwards, so {1, 0}.Rot90CCW is {0, -1}.
|
||||||
|
func pRot90CCW(p fixed.Point26_6) fixed.Point26_6 {
|
||||||
|
return fixed.Point26_6{p.Y, -p.X}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pRot135CCW returns the vector p rotated counter-clockwise by 135 degrees.
|
||||||
|
//
|
||||||
|
// Note that the Y-axis grows downwards, so {1, 0}.Rot135CCW is {-1/√2, -1/√2}.
|
||||||
|
func pRot135CCW(p fixed.Point26_6) fixed.Point26_6 {
|
||||||
|
// 181/256 is approximately 1/√2, or sin(π/4).
|
||||||
|
px, py := int64(p.X), int64(p.Y)
|
||||||
|
qx := (-px + py) * 181 / 256
|
||||||
|
qy := (-px - py) * 181 / 256
|
||||||
|
return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// An Adder accumulates points on a curve.
|
||||||
|
type Adder interface {
|
||||||
|
// Start starts a new curve at the given point.
|
||||||
|
Start(a fixed.Point26_6)
|
||||||
|
// Add1 adds a linear segment to the current curve.
|
||||||
|
Add1(b fixed.Point26_6)
|
||||||
|
// Add2 adds a quadratic segment to the current curve.
|
||||||
|
Add2(b, c fixed.Point26_6)
|
||||||
|
// Add3 adds a cubic segment to the current curve.
|
||||||
|
Add3(b, c, d fixed.Point26_6)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Path is a sequence of curves, and a curve is a start point followed by a
|
||||||
|
// sequence of linear, quadratic or cubic segments.
|
||||||
|
type Path []fixed.Int26_6
|
||||||
|
|
||||||
|
// String returns a human-readable representation of a Path.
|
||||||
|
func (p Path) String() string {
|
||||||
|
s := ""
|
||||||
|
for i := 0; i < len(p); {
|
||||||
|
if i != 0 {
|
||||||
|
s += " "
|
||||||
|
}
|
||||||
|
switch p[i] {
|
||||||
|
case 0:
|
||||||
|
s += "S0" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+3]))
|
||||||
|
i += 4
|
||||||
|
case 1:
|
||||||
|
s += "A1" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+3]))
|
||||||
|
i += 4
|
||||||
|
case 2:
|
||||||
|
s += "A2" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+5]))
|
||||||
|
i += 6
|
||||||
|
case 3:
|
||||||
|
s += "A3" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+7]))
|
||||||
|
i += 8
|
||||||
|
default:
|
||||||
|
panic("freetype/raster: bad path")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear cancels any previous calls to p.Start or p.AddXxx.
|
||||||
|
func (p *Path) Clear() {
|
||||||
|
*p = (*p)[:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start starts a new curve at the given point.
|
||||||
|
func (p *Path) Start(a fixed.Point26_6) {
|
||||||
|
*p = append(*p, 0, a.X, a.Y, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add1 adds a linear segment to the current curve.
|
||||||
|
func (p *Path) Add1(b fixed.Point26_6) {
|
||||||
|
*p = append(*p, 1, b.X, b.Y, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add2 adds a quadratic segment to the current curve.
|
||||||
|
func (p *Path) Add2(b, c fixed.Point26_6) {
|
||||||
|
*p = append(*p, 2, b.X, b.Y, c.X, c.Y, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add3 adds a cubic segment to the current curve.
|
||||||
|
func (p *Path) Add3(b, c, d fixed.Point26_6) {
|
||||||
|
*p = append(*p, 3, b.X, b.Y, c.X, c.Y, d.X, d.Y, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddPath adds the Path q to p.
|
||||||
|
func (p *Path) AddPath(q Path) {
|
||||||
|
*p = append(*p, q...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddStroke adds a stroked Path.
|
||||||
|
func (p *Path) AddStroke(q Path, width fixed.Int26_6, cr Capper, jr Joiner) {
|
||||||
|
Stroke(p, q, width, cr, jr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// firstPoint returns the first point in a non-empty Path.
|
||||||
|
func (p Path) firstPoint() fixed.Point26_6 {
|
||||||
|
return fixed.Point26_6{p[1], p[2]}
|
||||||
|
}
|
||||||
|
|
||||||
|
// lastPoint returns the last point in a non-empty Path.
|
||||||
|
func (p Path) lastPoint() fixed.Point26_6 {
|
||||||
|
return fixed.Point26_6{p[len(p)-3], p[len(p)-2]}
|
||||||
|
}
|
||||||
|
|
||||||
|
// addPathReversed adds q reversed to p.
|
||||||
|
// For example, if q consists of a linear segment from A to B followed by a
|
||||||
|
// quadratic segment from B to C to D, then the values of q looks like:
|
||||||
|
// index: 01234567890123
|
||||||
|
// value: 0AA01BB12CCDD2
|
||||||
|
// So, when adding q backwards to p, we want to Add2(C, B) followed by Add1(A).
|
||||||
|
func addPathReversed(p Adder, q Path) {
|
||||||
|
if len(q) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
i := len(q) - 1
|
||||||
|
for {
|
||||||
|
switch q[i] {
|
||||||
|
case 0:
|
||||||
|
return
|
||||||
|
case 1:
|
||||||
|
i -= 4
|
||||||
|
p.Add1(
|
||||||
|
fixed.Point26_6{q[i-2], q[i-1]},
|
||||||
|
)
|
||||||
|
case 2:
|
||||||
|
i -= 6
|
||||||
|
p.Add2(
|
||||||
|
fixed.Point26_6{q[i+2], q[i+3]},
|
||||||
|
fixed.Point26_6{q[i-2], q[i-1]},
|
||||||
|
)
|
||||||
|
case 3:
|
||||||
|
i -= 8
|
||||||
|
p.Add3(
|
||||||
|
fixed.Point26_6{q[i+4], q[i+5]},
|
||||||
|
fixed.Point26_6{q[i+2], q[i+3]},
|
||||||
|
fixed.Point26_6{q[i-2], q[i-1]},
|
||||||
|
)
|
||||||
|
default:
|
||||||
|
panic("freetype/raster: bad path")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
287
vendor/github.com/golang/freetype/raster/paint.go
generated
vendored
Normal file
287
vendor/github.com/golang/freetype/raster/paint.go
generated
vendored
Normal file
|
@ -0,0 +1,287 @@
|
||||||
|
// Copyright 2010 The Freetype-Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by your choice of either the
|
||||||
|
// FreeType License or the GNU General Public License version 2 (or
|
||||||
|
// any later version), both of which can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package raster
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"image/draw"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Span is a horizontal segment of pixels with constant alpha. X0 is an
|
||||||
|
// inclusive bound and X1 is exclusive, the same as for slices. A fully opaque
|
||||||
|
// Span has Alpha == 0xffff.
|
||||||
|
type Span struct {
|
||||||
|
Y, X0, X1 int
|
||||||
|
Alpha uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Painter knows how to paint a batch of Spans. Rasterization may involve
|
||||||
|
// Painting multiple batches, and done will be true for the final batch. The
|
||||||
|
// Spans' Y values are monotonically increasing during a rasterization. Paint
|
||||||
|
// may use all of ss as scratch space during the call.
|
||||||
|
type Painter interface {
|
||||||
|
Paint(ss []Span, done bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The PainterFunc type adapts an ordinary function to the Painter interface.
|
||||||
|
type PainterFunc func(ss []Span, done bool)
|
||||||
|
|
||||||
|
// Paint just delegates the call to f.
|
||||||
|
func (f PainterFunc) Paint(ss []Span, done bool) { f(ss, done) }
|
||||||
|
|
||||||
|
// An AlphaOverPainter is a Painter that paints Spans onto a *image.Alpha using
|
||||||
|
// the Over Porter-Duff composition operator.
|
||||||
|
type AlphaOverPainter struct {
|
||||||
|
Image *image.Alpha
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paint satisfies the Painter interface.
|
||||||
|
func (r AlphaOverPainter) Paint(ss []Span, done bool) {
|
||||||
|
b := r.Image.Bounds()
|
||||||
|
for _, s := range ss {
|
||||||
|
if s.Y < b.Min.Y {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if s.Y >= b.Max.Y {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if s.X0 < b.Min.X {
|
||||||
|
s.X0 = b.Min.X
|
||||||
|
}
|
||||||
|
if s.X1 > b.Max.X {
|
||||||
|
s.X1 = b.Max.X
|
||||||
|
}
|
||||||
|
if s.X0 >= s.X1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
base := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride - r.Image.Rect.Min.X
|
||||||
|
p := r.Image.Pix[base+s.X0 : base+s.X1]
|
||||||
|
a := int(s.Alpha >> 8)
|
||||||
|
for i, c := range p {
|
||||||
|
v := int(c)
|
||||||
|
p[i] = uint8((v*255 + (255-v)*a) / 255)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAlphaOverPainter creates a new AlphaOverPainter for the given image.
|
||||||
|
func NewAlphaOverPainter(m *image.Alpha) AlphaOverPainter {
|
||||||
|
return AlphaOverPainter{m}
|
||||||
|
}
|
||||||
|
|
||||||
|
// An AlphaSrcPainter is a Painter that paints Spans onto a *image.Alpha using
|
||||||
|
// the Src Porter-Duff composition operator.
|
||||||
|
type AlphaSrcPainter struct {
|
||||||
|
Image *image.Alpha
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paint satisfies the Painter interface.
|
||||||
|
func (r AlphaSrcPainter) Paint(ss []Span, done bool) {
|
||||||
|
b := r.Image.Bounds()
|
||||||
|
for _, s := range ss {
|
||||||
|
if s.Y < b.Min.Y {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if s.Y >= b.Max.Y {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if s.X0 < b.Min.X {
|
||||||
|
s.X0 = b.Min.X
|
||||||
|
}
|
||||||
|
if s.X1 > b.Max.X {
|
||||||
|
s.X1 = b.Max.X
|
||||||
|
}
|
||||||
|
if s.X0 >= s.X1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
base := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride - r.Image.Rect.Min.X
|
||||||
|
p := r.Image.Pix[base+s.X0 : base+s.X1]
|
||||||
|
color := uint8(s.Alpha >> 8)
|
||||||
|
for i := range p {
|
||||||
|
p[i] = color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAlphaSrcPainter creates a new AlphaSrcPainter for the given image.
|
||||||
|
func NewAlphaSrcPainter(m *image.Alpha) AlphaSrcPainter {
|
||||||
|
return AlphaSrcPainter{m}
|
||||||
|
}
|
||||||
|
|
||||||
|
// An RGBAPainter is a Painter that paints Spans onto a *image.RGBA.
|
||||||
|
type RGBAPainter struct {
|
||||||
|
// Image is the image to compose onto.
|
||||||
|
Image *image.RGBA
|
||||||
|
// Op is the Porter-Duff composition operator.
|
||||||
|
Op draw.Op
|
||||||
|
// cr, cg, cb and ca are the 16-bit color to paint the spans.
|
||||||
|
cr, cg, cb, ca uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paint satisfies the Painter interface.
|
||||||
|
func (r *RGBAPainter) Paint(ss []Span, done bool) {
|
||||||
|
b := r.Image.Bounds()
|
||||||
|
for _, s := range ss {
|
||||||
|
if s.Y < b.Min.Y {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if s.Y >= b.Max.Y {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if s.X0 < b.Min.X {
|
||||||
|
s.X0 = b.Min.X
|
||||||
|
}
|
||||||
|
if s.X1 > b.Max.X {
|
||||||
|
s.X1 = b.Max.X
|
||||||
|
}
|
||||||
|
if s.X0 >= s.X1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// This code mimics drawGlyphOver in $GOROOT/src/image/draw/draw.go.
|
||||||
|
ma := s.Alpha
|
||||||
|
const m = 1<<16 - 1
|
||||||
|
i0 := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride + (s.X0-r.Image.Rect.Min.X)*4
|
||||||
|
i1 := i0 + (s.X1-s.X0)*4
|
||||||
|
if r.Op == draw.Over {
|
||||||
|
for i := i0; i < i1; i += 4 {
|
||||||
|
dr := uint32(r.Image.Pix[i+0])
|
||||||
|
dg := uint32(r.Image.Pix[i+1])
|
||||||
|
db := uint32(r.Image.Pix[i+2])
|
||||||
|
da := uint32(r.Image.Pix[i+3])
|
||||||
|
a := (m - (r.ca * ma / m)) * 0x101
|
||||||
|
r.Image.Pix[i+0] = uint8((dr*a + r.cr*ma) / m >> 8)
|
||||||
|
r.Image.Pix[i+1] = uint8((dg*a + r.cg*ma) / m >> 8)
|
||||||
|
r.Image.Pix[i+2] = uint8((db*a + r.cb*ma) / m >> 8)
|
||||||
|
r.Image.Pix[i+3] = uint8((da*a + r.ca*ma) / m >> 8)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for i := i0; i < i1; i += 4 {
|
||||||
|
r.Image.Pix[i+0] = uint8(r.cr * ma / m >> 8)
|
||||||
|
r.Image.Pix[i+1] = uint8(r.cg * ma / m >> 8)
|
||||||
|
r.Image.Pix[i+2] = uint8(r.cb * ma / m >> 8)
|
||||||
|
r.Image.Pix[i+3] = uint8(r.ca * ma / m >> 8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetColor sets the color to paint the spans.
|
||||||
|
func (r *RGBAPainter) SetColor(c color.Color) {
|
||||||
|
r.cr, r.cg, r.cb, r.ca = c.RGBA()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRGBAPainter creates a new RGBAPainter for the given image.
|
||||||
|
func NewRGBAPainter(m *image.RGBA) *RGBAPainter {
|
||||||
|
return &RGBAPainter{Image: m}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A MonochromePainter wraps another Painter, quantizing each Span's alpha to
|
||||||
|
// be either fully opaque or fully transparent.
|
||||||
|
type MonochromePainter struct {
|
||||||
|
Painter Painter
|
||||||
|
y, x0, x1 int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paint delegates to the wrapped Painter after quantizing each Span's alpha
|
||||||
|
// value and merging adjacent fully opaque Spans.
|
||||||
|
func (m *MonochromePainter) Paint(ss []Span, done bool) {
|
||||||
|
// We compact the ss slice, discarding any Spans whose alpha quantizes to zero.
|
||||||
|
j := 0
|
||||||
|
for _, s := range ss {
|
||||||
|
if s.Alpha >= 0x8000 {
|
||||||
|
if m.y == s.Y && m.x1 == s.X0 {
|
||||||
|
m.x1 = s.X1
|
||||||
|
} else {
|
||||||
|
ss[j] = Span{m.y, m.x0, m.x1, 1<<16 - 1}
|
||||||
|
j++
|
||||||
|
m.y, m.x0, m.x1 = s.Y, s.X0, s.X1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if done {
|
||||||
|
// Flush the accumulated Span.
|
||||||
|
finalSpan := Span{m.y, m.x0, m.x1, 1<<16 - 1}
|
||||||
|
if j < len(ss) {
|
||||||
|
ss[j] = finalSpan
|
||||||
|
j++
|
||||||
|
m.Painter.Paint(ss[:j], true)
|
||||||
|
} else if j == len(ss) {
|
||||||
|
m.Painter.Paint(ss, false)
|
||||||
|
if cap(ss) > 0 {
|
||||||
|
ss = ss[:1]
|
||||||
|
} else {
|
||||||
|
ss = make([]Span, 1)
|
||||||
|
}
|
||||||
|
ss[0] = finalSpan
|
||||||
|
m.Painter.Paint(ss, true)
|
||||||
|
} else {
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
// Reset the accumulator, so that this Painter can be re-used.
|
||||||
|
m.y, m.x0, m.x1 = 0, 0, 0
|
||||||
|
} else {
|
||||||
|
m.Painter.Paint(ss[:j], false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMonochromePainter creates a new MonochromePainter that wraps the given
|
||||||
|
// Painter.
|
||||||
|
func NewMonochromePainter(p Painter) *MonochromePainter {
|
||||||
|
return &MonochromePainter{Painter: p}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A GammaCorrectionPainter wraps another Painter, performing gamma-correction
|
||||||
|
// on each Span's alpha value.
|
||||||
|
type GammaCorrectionPainter struct {
|
||||||
|
// Painter is the wrapped Painter.
|
||||||
|
Painter Painter
|
||||||
|
// a is the precomputed alpha values for linear interpolation, with fully
|
||||||
|
// opaque == 0xffff.
|
||||||
|
a [256]uint16
|
||||||
|
// gammaIsOne is whether gamma correction is a no-op.
|
||||||
|
gammaIsOne bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paint delegates to the wrapped Painter after performing gamma-correction on
|
||||||
|
// each Span.
|
||||||
|
func (g *GammaCorrectionPainter) Paint(ss []Span, done bool) {
|
||||||
|
if !g.gammaIsOne {
|
||||||
|
const n = 0x101
|
||||||
|
for i, s := range ss {
|
||||||
|
if s.Alpha == 0 || s.Alpha == 0xffff {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
p, q := s.Alpha/n, s.Alpha%n
|
||||||
|
// The resultant alpha is a linear interpolation of g.a[p] and g.a[p+1].
|
||||||
|
a := uint32(g.a[p])*(n-q) + uint32(g.a[p+1])*q
|
||||||
|
ss[i].Alpha = (a + n/2) / n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.Painter.Paint(ss, done)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetGamma sets the gamma value.
|
||||||
|
func (g *GammaCorrectionPainter) SetGamma(gamma float64) {
|
||||||
|
g.gammaIsOne = gamma == 1
|
||||||
|
if g.gammaIsOne {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := 0; i < 256; i++ {
|
||||||
|
a := float64(i) / 0xff
|
||||||
|
a = math.Pow(a, gamma)
|
||||||
|
g.a[i] = uint16(0xffff * a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGammaCorrectionPainter creates a new GammaCorrectionPainter that wraps
|
||||||
|
// the given Painter.
|
||||||
|
func NewGammaCorrectionPainter(p Painter, gamma float64) *GammaCorrectionPainter {
|
||||||
|
g := &GammaCorrectionPainter{Painter: p}
|
||||||
|
g.SetGamma(gamma)
|
||||||
|
return g
|
||||||
|
}
|
601
vendor/github.com/golang/freetype/raster/raster.go
generated
vendored
Normal file
601
vendor/github.com/golang/freetype/raster/raster.go
generated
vendored
Normal file
|
@ -0,0 +1,601 @@
|
||||||
|
// Copyright 2010 The Freetype-Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by your choice of either the
|
||||||
|
// FreeType License or the GNU General Public License version 2 (or
|
||||||
|
// any later version), both of which can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package raster provides an anti-aliasing 2-D rasterizer.
|
||||||
|
//
|
||||||
|
// It is part of the larger Freetype suite of font-related packages, but the
|
||||||
|
// raster package is not specific to font rasterization, and can be used
|
||||||
|
// standalone without any other Freetype package.
|
||||||
|
//
|
||||||
|
// Rasterization is done by the same area/coverage accumulation algorithm as
|
||||||
|
// the Freetype "smooth" module, and the Anti-Grain Geometry library. A
|
||||||
|
// description of the area/coverage algorithm is at
|
||||||
|
// http://projects.tuxee.net/cl-vectors/section-the-cl-aa-algorithm
|
||||||
|
package raster
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"golang.org/x/image/math/fixed"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A cell is part of a linked list (for a given yi co-ordinate) of accumulated
|
||||||
|
// area/coverage for the pixel at (xi, yi).
|
||||||
|
type cell struct {
|
||||||
|
xi int
|
||||||
|
area, cover int
|
||||||
|
next int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Rasterizer struct {
|
||||||
|
// If false, the default behavior is to use the even-odd winding fill
|
||||||
|
// rule during Rasterize.
|
||||||
|
UseNonZeroWinding bool
|
||||||
|
// An offset (in pixels) to the painted spans.
|
||||||
|
Dx, Dy int
|
||||||
|
|
||||||
|
// The width of the Rasterizer. The height is implicit in len(cellIndex).
|
||||||
|
width int
|
||||||
|
// splitScaleN is the scaling factor used to determine how many times
|
||||||
|
// to decompose a quadratic or cubic segment into a linear approximation.
|
||||||
|
splitScale2, splitScale3 int
|
||||||
|
|
||||||
|
// The current pen position.
|
||||||
|
a fixed.Point26_6
|
||||||
|
// The current cell and its area/coverage being accumulated.
|
||||||
|
xi, yi int
|
||||||
|
area, cover int
|
||||||
|
|
||||||
|
// Saved cells.
|
||||||
|
cell []cell
|
||||||
|
// Linked list of cells, one per row.
|
||||||
|
cellIndex []int
|
||||||
|
// Buffers.
|
||||||
|
cellBuf [256]cell
|
||||||
|
cellIndexBuf [64]int
|
||||||
|
spanBuf [64]Span
|
||||||
|
}
|
||||||
|
|
||||||
|
// findCell returns the index in r.cell for the cell corresponding to
|
||||||
|
// (r.xi, r.yi). The cell is created if necessary.
|
||||||
|
func (r *Rasterizer) findCell() int {
|
||||||
|
if r.yi < 0 || r.yi >= len(r.cellIndex) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
xi := r.xi
|
||||||
|
if xi < 0 {
|
||||||
|
xi = -1
|
||||||
|
} else if xi > r.width {
|
||||||
|
xi = r.width
|
||||||
|
}
|
||||||
|
i, prev := r.cellIndex[r.yi], -1
|
||||||
|
for i != -1 && r.cell[i].xi <= xi {
|
||||||
|
if r.cell[i].xi == xi {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
i, prev = r.cell[i].next, i
|
||||||
|
}
|
||||||
|
c := len(r.cell)
|
||||||
|
if c == cap(r.cell) {
|
||||||
|
buf := make([]cell, c, 4*c)
|
||||||
|
copy(buf, r.cell)
|
||||||
|
r.cell = buf[0 : c+1]
|
||||||
|
} else {
|
||||||
|
r.cell = r.cell[0 : c+1]
|
||||||
|
}
|
||||||
|
r.cell[c] = cell{xi, 0, 0, i}
|
||||||
|
if prev == -1 {
|
||||||
|
r.cellIndex[r.yi] = c
|
||||||
|
} else {
|
||||||
|
r.cell[prev].next = c
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// saveCell saves any accumulated r.area/r.cover for (r.xi, r.yi).
|
||||||
|
func (r *Rasterizer) saveCell() {
|
||||||
|
if r.area != 0 || r.cover != 0 {
|
||||||
|
i := r.findCell()
|
||||||
|
if i != -1 {
|
||||||
|
r.cell[i].area += r.area
|
||||||
|
r.cell[i].cover += r.cover
|
||||||
|
}
|
||||||
|
r.area = 0
|
||||||
|
r.cover = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setCell sets the (xi, yi) cell that r is accumulating area/coverage for.
|
||||||
|
func (r *Rasterizer) setCell(xi, yi int) {
|
||||||
|
if r.xi != xi || r.yi != yi {
|
||||||
|
r.saveCell()
|
||||||
|
r.xi, r.yi = xi, yi
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// scan accumulates area/coverage for the yi'th scanline, going from
|
||||||
|
// x0 to x1 in the horizontal direction (in 26.6 fixed point co-ordinates)
|
||||||
|
// and from y0f to y1f fractional vertical units within that scanline.
|
||||||
|
func (r *Rasterizer) scan(yi int, x0, y0f, x1, y1f fixed.Int26_6) {
|
||||||
|
// Break the 26.6 fixed point X co-ordinates into integral and fractional parts.
|
||||||
|
x0i := int(x0) / 64
|
||||||
|
x0f := x0 - fixed.Int26_6(64*x0i)
|
||||||
|
x1i := int(x1) / 64
|
||||||
|
x1f := x1 - fixed.Int26_6(64*x1i)
|
||||||
|
|
||||||
|
// A perfectly horizontal scan.
|
||||||
|
if y0f == y1f {
|
||||||
|
r.setCell(x1i, yi)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dx, dy := x1-x0, y1f-y0f
|
||||||
|
// A single cell scan.
|
||||||
|
if x0i == x1i {
|
||||||
|
r.area += int((x0f + x1f) * dy)
|
||||||
|
r.cover += int(dy)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// There are at least two cells. Apart from the first and last cells,
|
||||||
|
// all intermediate cells go through the full width of the cell,
|
||||||
|
// or 64 units in 26.6 fixed point format.
|
||||||
|
var (
|
||||||
|
p, q, edge0, edge1 fixed.Int26_6
|
||||||
|
xiDelta int
|
||||||
|
)
|
||||||
|
if dx > 0 {
|
||||||
|
p, q = (64-x0f)*dy, dx
|
||||||
|
edge0, edge1, xiDelta = 0, 64, 1
|
||||||
|
} else {
|
||||||
|
p, q = x0f*dy, -dx
|
||||||
|
edge0, edge1, xiDelta = 64, 0, -1
|
||||||
|
}
|
||||||
|
yDelta, yRem := p/q, p%q
|
||||||
|
if yRem < 0 {
|
||||||
|
yDelta -= 1
|
||||||
|
yRem += q
|
||||||
|
}
|
||||||
|
// Do the first cell.
|
||||||
|
xi, y := x0i, y0f
|
||||||
|
r.area += int((x0f + edge1) * yDelta)
|
||||||
|
r.cover += int(yDelta)
|
||||||
|
xi, y = xi+xiDelta, y+yDelta
|
||||||
|
r.setCell(xi, yi)
|
||||||
|
if xi != x1i {
|
||||||
|
// Do all the intermediate cells.
|
||||||
|
p = 64 * (y1f - y + yDelta)
|
||||||
|
fullDelta, fullRem := p/q, p%q
|
||||||
|
if fullRem < 0 {
|
||||||
|
fullDelta -= 1
|
||||||
|
fullRem += q
|
||||||
|
}
|
||||||
|
yRem -= q
|
||||||
|
for xi != x1i {
|
||||||
|
yDelta = fullDelta
|
||||||
|
yRem += fullRem
|
||||||
|
if yRem >= 0 {
|
||||||
|
yDelta += 1
|
||||||
|
yRem -= q
|
||||||
|
}
|
||||||
|
r.area += int(64 * yDelta)
|
||||||
|
r.cover += int(yDelta)
|
||||||
|
xi, y = xi+xiDelta, y+yDelta
|
||||||
|
r.setCell(xi, yi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Do the last cell.
|
||||||
|
yDelta = y1f - y
|
||||||
|
r.area += int((edge0 + x1f) * yDelta)
|
||||||
|
r.cover += int(yDelta)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start starts a new curve at the given point.
|
||||||
|
func (r *Rasterizer) Start(a fixed.Point26_6) {
|
||||||
|
r.setCell(int(a.X/64), int(a.Y/64))
|
||||||
|
r.a = a
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add1 adds a linear segment to the current curve.
|
||||||
|
func (r *Rasterizer) Add1(b fixed.Point26_6) {
|
||||||
|
x0, y0 := r.a.X, r.a.Y
|
||||||
|
x1, y1 := b.X, b.Y
|
||||||
|
dx, dy := x1-x0, y1-y0
|
||||||
|
// Break the 26.6 fixed point Y co-ordinates into integral and fractional
|
||||||
|
// parts.
|
||||||
|
y0i := int(y0) / 64
|
||||||
|
y0f := y0 - fixed.Int26_6(64*y0i)
|
||||||
|
y1i := int(y1) / 64
|
||||||
|
y1f := y1 - fixed.Int26_6(64*y1i)
|
||||||
|
|
||||||
|
if y0i == y1i {
|
||||||
|
// There is only one scanline.
|
||||||
|
r.scan(y0i, x0, y0f, x1, y1f)
|
||||||
|
|
||||||
|
} else if dx == 0 {
|
||||||
|
// This is a vertical line segment. We avoid calling r.scan and instead
|
||||||
|
// manipulate r.area and r.cover directly.
|
||||||
|
var (
|
||||||
|
edge0, edge1 fixed.Int26_6
|
||||||
|
yiDelta int
|
||||||
|
)
|
||||||
|
if dy > 0 {
|
||||||
|
edge0, edge1, yiDelta = 0, 64, 1
|
||||||
|
} else {
|
||||||
|
edge0, edge1, yiDelta = 64, 0, -1
|
||||||
|
}
|
||||||
|
x0i, yi := int(x0)/64, y0i
|
||||||
|
x0fTimes2 := (int(x0) - (64 * x0i)) * 2
|
||||||
|
// Do the first pixel.
|
||||||
|
dcover := int(edge1 - y0f)
|
||||||
|
darea := int(x0fTimes2 * dcover)
|
||||||
|
r.area += darea
|
||||||
|
r.cover += dcover
|
||||||
|
yi += yiDelta
|
||||||
|
r.setCell(x0i, yi)
|
||||||
|
// Do all the intermediate pixels.
|
||||||
|
dcover = int(edge1 - edge0)
|
||||||
|
darea = int(x0fTimes2 * dcover)
|
||||||
|
for yi != y1i {
|
||||||
|
r.area += darea
|
||||||
|
r.cover += dcover
|
||||||
|
yi += yiDelta
|
||||||
|
r.setCell(x0i, yi)
|
||||||
|
}
|
||||||
|
// Do the last pixel.
|
||||||
|
dcover = int(y1f - edge0)
|
||||||
|
darea = int(x0fTimes2 * dcover)
|
||||||
|
r.area += darea
|
||||||
|
r.cover += dcover
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// There are at least two scanlines. Apart from the first and last
|
||||||
|
// scanlines, all intermediate scanlines go through the full height of
|
||||||
|
// the row, or 64 units in 26.6 fixed point format.
|
||||||
|
var (
|
||||||
|
p, q, edge0, edge1 fixed.Int26_6
|
||||||
|
yiDelta int
|
||||||
|
)
|
||||||
|
if dy > 0 {
|
||||||
|
p, q = (64-y0f)*dx, dy
|
||||||
|
edge0, edge1, yiDelta = 0, 64, 1
|
||||||
|
} else {
|
||||||
|
p, q = y0f*dx, -dy
|
||||||
|
edge0, edge1, yiDelta = 64, 0, -1
|
||||||
|
}
|
||||||
|
xDelta, xRem := p/q, p%q
|
||||||
|
if xRem < 0 {
|
||||||
|
xDelta -= 1
|
||||||
|
xRem += q
|
||||||
|
}
|
||||||
|
// Do the first scanline.
|
||||||
|
x, yi := x0, y0i
|
||||||
|
r.scan(yi, x, y0f, x+xDelta, edge1)
|
||||||
|
x, yi = x+xDelta, yi+yiDelta
|
||||||
|
r.setCell(int(x)/64, yi)
|
||||||
|
if yi != y1i {
|
||||||
|
// Do all the intermediate scanlines.
|
||||||
|
p = 64 * dx
|
||||||
|
fullDelta, fullRem := p/q, p%q
|
||||||
|
if fullRem < 0 {
|
||||||
|
fullDelta -= 1
|
||||||
|
fullRem += q
|
||||||
|
}
|
||||||
|
xRem -= q
|
||||||
|
for yi != y1i {
|
||||||
|
xDelta = fullDelta
|
||||||
|
xRem += fullRem
|
||||||
|
if xRem >= 0 {
|
||||||
|
xDelta += 1
|
||||||
|
xRem -= q
|
||||||
|
}
|
||||||
|
r.scan(yi, x, edge0, x+xDelta, edge1)
|
||||||
|
x, yi = x+xDelta, yi+yiDelta
|
||||||
|
r.setCell(int(x)/64, yi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Do the last scanline.
|
||||||
|
r.scan(yi, x, edge0, x1, y1f)
|
||||||
|
}
|
||||||
|
// The next lineTo starts from b.
|
||||||
|
r.a = b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add2 adds a quadratic segment to the current curve.
|
||||||
|
func (r *Rasterizer) Add2(b, c fixed.Point26_6) {
|
||||||
|
// Calculate nSplit (the number of recursive decompositions) based on how
|
||||||
|
// 'curvy' it is. Specifically, how much the middle point b deviates from
|
||||||
|
// (a+c)/2.
|
||||||
|
dev := maxAbs(r.a.X-2*b.X+c.X, r.a.Y-2*b.Y+c.Y) / fixed.Int26_6(r.splitScale2)
|
||||||
|
nsplit := 0
|
||||||
|
for dev > 0 {
|
||||||
|
dev /= 4
|
||||||
|
nsplit++
|
||||||
|
}
|
||||||
|
// dev is 32-bit, and nsplit++ every time we shift off 2 bits, so maxNsplit
|
||||||
|
// is 16.
|
||||||
|
const maxNsplit = 16
|
||||||
|
if nsplit > maxNsplit {
|
||||||
|
panic("freetype/raster: Add2 nsplit too large: " + strconv.Itoa(nsplit))
|
||||||
|
}
|
||||||
|
// Recursively decompose the curve nSplit levels deep.
|
||||||
|
var (
|
||||||
|
pStack [2*maxNsplit + 3]fixed.Point26_6
|
||||||
|
sStack [maxNsplit + 1]int
|
||||||
|
i int
|
||||||
|
)
|
||||||
|
sStack[0] = nsplit
|
||||||
|
pStack[0] = c
|
||||||
|
pStack[1] = b
|
||||||
|
pStack[2] = r.a
|
||||||
|
for i >= 0 {
|
||||||
|
s := sStack[i]
|
||||||
|
p := pStack[2*i:]
|
||||||
|
if s > 0 {
|
||||||
|
// Split the quadratic curve p[:3] into an equivalent set of two
|
||||||
|
// shorter curves: p[:3] and p[2:5]. The new p[4] is the old p[2],
|
||||||
|
// and p[0] is unchanged.
|
||||||
|
mx := p[1].X
|
||||||
|
p[4].X = p[2].X
|
||||||
|
p[3].X = (p[4].X + mx) / 2
|
||||||
|
p[1].X = (p[0].X + mx) / 2
|
||||||
|
p[2].X = (p[1].X + p[3].X) / 2
|
||||||
|
my := p[1].Y
|
||||||
|
p[4].Y = p[2].Y
|
||||||
|
p[3].Y = (p[4].Y + my) / 2
|
||||||
|
p[1].Y = (p[0].Y + my) / 2
|
||||||
|
p[2].Y = (p[1].Y + p[3].Y) / 2
|
||||||
|
// The two shorter curves have one less split to do.
|
||||||
|
sStack[i] = s - 1
|
||||||
|
sStack[i+1] = s - 1
|
||||||
|
i++
|
||||||
|
} else {
|
||||||
|
// Replace the level-0 quadratic with a two-linear-piece
|
||||||
|
// approximation.
|
||||||
|
midx := (p[0].X + 2*p[1].X + p[2].X) / 4
|
||||||
|
midy := (p[0].Y + 2*p[1].Y + p[2].Y) / 4
|
||||||
|
r.Add1(fixed.Point26_6{midx, midy})
|
||||||
|
r.Add1(p[0])
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add3 adds a cubic segment to the current curve.
|
||||||
|
func (r *Rasterizer) Add3(b, c, d fixed.Point26_6) {
|
||||||
|
// Calculate nSplit (the number of recursive decompositions) based on how
|
||||||
|
// 'curvy' it is.
|
||||||
|
dev2 := maxAbs(r.a.X-3*(b.X+c.X)+d.X, r.a.Y-3*(b.Y+c.Y)+d.Y) / fixed.Int26_6(r.splitScale2)
|
||||||
|
dev3 := maxAbs(r.a.X-2*b.X+d.X, r.a.Y-2*b.Y+d.Y) / fixed.Int26_6(r.splitScale3)
|
||||||
|
nsplit := 0
|
||||||
|
for dev2 > 0 || dev3 > 0 {
|
||||||
|
dev2 /= 8
|
||||||
|
dev3 /= 4
|
||||||
|
nsplit++
|
||||||
|
}
|
||||||
|
// devN is 32-bit, and nsplit++ every time we shift off 2 bits, so
|
||||||
|
// maxNsplit is 16.
|
||||||
|
const maxNsplit = 16
|
||||||
|
if nsplit > maxNsplit {
|
||||||
|
panic("freetype/raster: Add3 nsplit too large: " + strconv.Itoa(nsplit))
|
||||||
|
}
|
||||||
|
// Recursively decompose the curve nSplit levels deep.
|
||||||
|
var (
|
||||||
|
pStack [3*maxNsplit + 4]fixed.Point26_6
|
||||||
|
sStack [maxNsplit + 1]int
|
||||||
|
i int
|
||||||
|
)
|
||||||
|
sStack[0] = nsplit
|
||||||
|
pStack[0] = d
|
||||||
|
pStack[1] = c
|
||||||
|
pStack[2] = b
|
||||||
|
pStack[3] = r.a
|
||||||
|
for i >= 0 {
|
||||||
|
s := sStack[i]
|
||||||
|
p := pStack[3*i:]
|
||||||
|
if s > 0 {
|
||||||
|
// Split the cubic curve p[:4] into an equivalent set of two
|
||||||
|
// shorter curves: p[:4] and p[3:7]. The new p[6] is the old p[3],
|
||||||
|
// and p[0] is unchanged.
|
||||||
|
m01x := (p[0].X + p[1].X) / 2
|
||||||
|
m12x := (p[1].X + p[2].X) / 2
|
||||||
|
m23x := (p[2].X + p[3].X) / 2
|
||||||
|
p[6].X = p[3].X
|
||||||
|
p[5].X = m23x
|
||||||
|
p[1].X = m01x
|
||||||
|
p[2].X = (m01x + m12x) / 2
|
||||||
|
p[4].X = (m12x + m23x) / 2
|
||||||
|
p[3].X = (p[2].X + p[4].X) / 2
|
||||||
|
m01y := (p[0].Y + p[1].Y) / 2
|
||||||
|
m12y := (p[1].Y + p[2].Y) / 2
|
||||||
|
m23y := (p[2].Y + p[3].Y) / 2
|
||||||
|
p[6].Y = p[3].Y
|
||||||
|
p[5].Y = m23y
|
||||||
|
p[1].Y = m01y
|
||||||
|
p[2].Y = (m01y + m12y) / 2
|
||||||
|
p[4].Y = (m12y + m23y) / 2
|
||||||
|
p[3].Y = (p[2].Y + p[4].Y) / 2
|
||||||
|
// The two shorter curves have one less split to do.
|
||||||
|
sStack[i] = s - 1
|
||||||
|
sStack[i+1] = s - 1
|
||||||
|
i++
|
||||||
|
} else {
|
||||||
|
// Replace the level-0 cubic with a two-linear-piece approximation.
|
||||||
|
midx := (p[0].X + 3*(p[1].X+p[2].X) + p[3].X) / 8
|
||||||
|
midy := (p[0].Y + 3*(p[1].Y+p[2].Y) + p[3].Y) / 8
|
||||||
|
r.Add1(fixed.Point26_6{midx, midy})
|
||||||
|
r.Add1(p[0])
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddPath adds the given Path.
|
||||||
|
func (r *Rasterizer) AddPath(p Path) {
|
||||||
|
for i := 0; i < len(p); {
|
||||||
|
switch p[i] {
|
||||||
|
case 0:
|
||||||
|
r.Start(
|
||||||
|
fixed.Point26_6{p[i+1], p[i+2]},
|
||||||
|
)
|
||||||
|
i += 4
|
||||||
|
case 1:
|
||||||
|
r.Add1(
|
||||||
|
fixed.Point26_6{p[i+1], p[i+2]},
|
||||||
|
)
|
||||||
|
i += 4
|
||||||
|
case 2:
|
||||||
|
r.Add2(
|
||||||
|
fixed.Point26_6{p[i+1], p[i+2]},
|
||||||
|
fixed.Point26_6{p[i+3], p[i+4]},
|
||||||
|
)
|
||||||
|
i += 6
|
||||||
|
case 3:
|
||||||
|
r.Add3(
|
||||||
|
fixed.Point26_6{p[i+1], p[i+2]},
|
||||||
|
fixed.Point26_6{p[i+3], p[i+4]},
|
||||||
|
fixed.Point26_6{p[i+5], p[i+6]},
|
||||||
|
)
|
||||||
|
i += 8
|
||||||
|
default:
|
||||||
|
panic("freetype/raster: bad path")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddStroke adds a stroked Path.
|
||||||
|
func (r *Rasterizer) AddStroke(q Path, width fixed.Int26_6, cr Capper, jr Joiner) {
|
||||||
|
Stroke(r, q, width, cr, jr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// areaToAlpha converts an area value to a uint32 alpha value. A completely
|
||||||
|
// filled pixel corresponds to an area of 64*64*2, and an alpha of 0xffff. The
|
||||||
|
// conversion of area values greater than this depends on the winding rule:
|
||||||
|
// even-odd or non-zero.
|
||||||
|
func (r *Rasterizer) areaToAlpha(area int) uint32 {
|
||||||
|
// The C Freetype implementation (version 2.3.12) does "alpha := area>>1"
|
||||||
|
// without the +1. Round-to-nearest gives a more symmetric result than
|
||||||
|
// round-down. The C implementation also returns 8-bit alpha, not 16-bit
|
||||||
|
// alpha.
|
||||||
|
a := (area + 1) >> 1
|
||||||
|
if a < 0 {
|
||||||
|
a = -a
|
||||||
|
}
|
||||||
|
alpha := uint32(a)
|
||||||
|
if r.UseNonZeroWinding {
|
||||||
|
if alpha > 0x0fff {
|
||||||
|
alpha = 0x0fff
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alpha &= 0x1fff
|
||||||
|
if alpha > 0x1000 {
|
||||||
|
alpha = 0x2000 - alpha
|
||||||
|
} else if alpha == 0x1000 {
|
||||||
|
alpha = 0x0fff
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// alpha is now in the range [0x0000, 0x0fff]. Convert that 12-bit alpha to
|
||||||
|
// 16-bit alpha.
|
||||||
|
return alpha<<4 | alpha>>8
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rasterize converts r's accumulated curves into Spans for p. The Spans passed
|
||||||
|
// to p are non-overlapping, and sorted by Y and then X. They all have non-zero
|
||||||
|
// width (and 0 <= X0 < X1 <= r.width) and non-zero A, except for the final
|
||||||
|
// Span, which has Y, X0, X1 and A all equal to zero.
|
||||||
|
func (r *Rasterizer) Rasterize(p Painter) {
|
||||||
|
r.saveCell()
|
||||||
|
s := 0
|
||||||
|
for yi := 0; yi < len(r.cellIndex); yi++ {
|
||||||
|
xi, cover := 0, 0
|
||||||
|
for c := r.cellIndex[yi]; c != -1; c = r.cell[c].next {
|
||||||
|
if cover != 0 && r.cell[c].xi > xi {
|
||||||
|
alpha := r.areaToAlpha(cover * 64 * 2)
|
||||||
|
if alpha != 0 {
|
||||||
|
xi0, xi1 := xi, r.cell[c].xi
|
||||||
|
if xi0 < 0 {
|
||||||
|
xi0 = 0
|
||||||
|
}
|
||||||
|
if xi1 >= r.width {
|
||||||
|
xi1 = r.width
|
||||||
|
}
|
||||||
|
if xi0 < xi1 {
|
||||||
|
r.spanBuf[s] = Span{yi + r.Dy, xi0 + r.Dx, xi1 + r.Dx, alpha}
|
||||||
|
s++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cover += r.cell[c].cover
|
||||||
|
alpha := r.areaToAlpha(cover*64*2 - r.cell[c].area)
|
||||||
|
xi = r.cell[c].xi + 1
|
||||||
|
if alpha != 0 {
|
||||||
|
xi0, xi1 := r.cell[c].xi, xi
|
||||||
|
if xi0 < 0 {
|
||||||
|
xi0 = 0
|
||||||
|
}
|
||||||
|
if xi1 >= r.width {
|
||||||
|
xi1 = r.width
|
||||||
|
}
|
||||||
|
if xi0 < xi1 {
|
||||||
|
r.spanBuf[s] = Span{yi + r.Dy, xi0 + r.Dx, xi1 + r.Dx, alpha}
|
||||||
|
s++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if s > len(r.spanBuf)-2 {
|
||||||
|
p.Paint(r.spanBuf[:s], false)
|
||||||
|
s = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.Paint(r.spanBuf[:s], true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear cancels any previous calls to r.Start or r.AddXxx.
|
||||||
|
func (r *Rasterizer) Clear() {
|
||||||
|
r.a = fixed.Point26_6{}
|
||||||
|
r.xi = 0
|
||||||
|
r.yi = 0
|
||||||
|
r.area = 0
|
||||||
|
r.cover = 0
|
||||||
|
r.cell = r.cell[:0]
|
||||||
|
for i := 0; i < len(r.cellIndex); i++ {
|
||||||
|
r.cellIndex[i] = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBounds sets the maximum width and height of the rasterized image and
|
||||||
|
// calls Clear. The width and height are in pixels, not fixed.Int26_6 units.
|
||||||
|
func (r *Rasterizer) SetBounds(width, height int) {
|
||||||
|
if width < 0 {
|
||||||
|
width = 0
|
||||||
|
}
|
||||||
|
if height < 0 {
|
||||||
|
height = 0
|
||||||
|
}
|
||||||
|
// Use the same ssN heuristic as the C Freetype (version 2.4.0)
|
||||||
|
// implementation.
|
||||||
|
ss2, ss3 := 32, 16
|
||||||
|
if width > 24 || height > 24 {
|
||||||
|
ss2, ss3 = 2*ss2, 2*ss3
|
||||||
|
if width > 120 || height > 120 {
|
||||||
|
ss2, ss3 = 2*ss2, 2*ss3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.width = width
|
||||||
|
r.splitScale2 = ss2
|
||||||
|
r.splitScale3 = ss3
|
||||||
|
r.cell = r.cellBuf[:0]
|
||||||
|
if height > len(r.cellIndexBuf) {
|
||||||
|
r.cellIndex = make([]int, height)
|
||||||
|
} else {
|
||||||
|
r.cellIndex = r.cellIndexBuf[:height]
|
||||||
|
}
|
||||||
|
r.Clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRasterizer creates a new Rasterizer with the given bounds.
|
||||||
|
func NewRasterizer(width, height int) *Rasterizer {
|
||||||
|
r := new(Rasterizer)
|
||||||
|
r.SetBounds(width, height)
|
||||||
|
return r
|
||||||
|
}
|
483
vendor/github.com/golang/freetype/raster/stroke.go
generated
vendored
Normal file
483
vendor/github.com/golang/freetype/raster/stroke.go
generated
vendored
Normal file
|
@ -0,0 +1,483 @@
|
||||||
|
// Copyright 2010 The Freetype-Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by your choice of either the
|
||||||
|
// FreeType License or the GNU General Public License version 2 (or
|
||||||
|
// any later version), both of which can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package raster
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/image/math/fixed"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Two points are considered practically equal if the square of the distance
|
||||||
|
// between them is less than one quarter (i.e. 1024 / 4096).
|
||||||
|
const epsilon = fixed.Int52_12(1024)
|
||||||
|
|
||||||
|
// A Capper signifies how to begin or end a stroked path.
|
||||||
|
type Capper interface {
|
||||||
|
// Cap adds a cap to p given a pivot point and the normal vector of a
|
||||||
|
// terminal segment. The normal's length is half of the stroke width.
|
||||||
|
Cap(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The CapperFunc type adapts an ordinary function to be a Capper.
|
||||||
|
type CapperFunc func(Adder, fixed.Int26_6, fixed.Point26_6, fixed.Point26_6)
|
||||||
|
|
||||||
|
func (f CapperFunc) Cap(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6) {
|
||||||
|
f(p, halfWidth, pivot, n1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Joiner signifies how to join interior nodes of a stroked path.
|
||||||
|
type Joiner interface {
|
||||||
|
// Join adds a join to the two sides of a stroked path given a pivot
|
||||||
|
// point and the normal vectors of the trailing and leading segments.
|
||||||
|
// Both normals have length equal to half of the stroke width.
|
||||||
|
Join(lhs, rhs Adder, halfWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The JoinerFunc type adapts an ordinary function to be a Joiner.
|
||||||
|
type JoinerFunc func(lhs, rhs Adder, halfWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6)
|
||||||
|
|
||||||
|
func (f JoinerFunc) Join(lhs, rhs Adder, halfWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6) {
|
||||||
|
f(lhs, rhs, halfWidth, pivot, n0, n1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoundCapper adds round caps to a stroked path.
|
||||||
|
var RoundCapper Capper = CapperFunc(roundCapper)
|
||||||
|
|
||||||
|
func roundCapper(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6) {
|
||||||
|
// The cubic Bézier approximation to a circle involves the magic number
|
||||||
|
// (√2 - 1) * 4/3, which is approximately 35/64.
|
||||||
|
const k = 35
|
||||||
|
e := pRot90CCW(n1)
|
||||||
|
side := pivot.Add(e)
|
||||||
|
start, end := pivot.Sub(n1), pivot.Add(n1)
|
||||||
|
d, e := n1.Mul(k), e.Mul(k)
|
||||||
|
p.Add3(start.Add(e), side.Sub(d), side)
|
||||||
|
p.Add3(side.Add(d), end.Add(e), end)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ButtCapper adds butt caps to a stroked path.
|
||||||
|
var ButtCapper Capper = CapperFunc(buttCapper)
|
||||||
|
|
||||||
|
func buttCapper(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6) {
|
||||||
|
p.Add1(pivot.Add(n1))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SquareCapper adds square caps to a stroked path.
|
||||||
|
var SquareCapper Capper = CapperFunc(squareCapper)
|
||||||
|
|
||||||
|
func squareCapper(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6) {
|
||||||
|
e := pRot90CCW(n1)
|
||||||
|
side := pivot.Add(e)
|
||||||
|
p.Add1(side.Sub(n1))
|
||||||
|
p.Add1(side.Add(n1))
|
||||||
|
p.Add1(pivot.Add(n1))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoundJoiner adds round joins to a stroked path.
|
||||||
|
var RoundJoiner Joiner = JoinerFunc(roundJoiner)
|
||||||
|
|
||||||
|
func roundJoiner(lhs, rhs Adder, haflWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6) {
|
||||||
|
dot := pDot(pRot90CW(n0), n1)
|
||||||
|
if dot >= 0 {
|
||||||
|
addArc(lhs, pivot, n0, n1)
|
||||||
|
rhs.Add1(pivot.Sub(n1))
|
||||||
|
} else {
|
||||||
|
lhs.Add1(pivot.Add(n1))
|
||||||
|
addArc(rhs, pivot, pNeg(n0), pNeg(n1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BevelJoiner adds bevel joins to a stroked path.
|
||||||
|
var BevelJoiner Joiner = JoinerFunc(bevelJoiner)
|
||||||
|
|
||||||
|
func bevelJoiner(lhs, rhs Adder, haflWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6) {
|
||||||
|
lhs.Add1(pivot.Add(n1))
|
||||||
|
rhs.Add1(pivot.Sub(n1))
|
||||||
|
}
|
||||||
|
|
||||||
|
// addArc adds a circular arc from pivot+n0 to pivot+n1 to p. The shorter of
|
||||||
|
// the two possible arcs is taken, i.e. the one spanning <= 180 degrees. The
|
||||||
|
// two vectors n0 and n1 must be of equal length.
|
||||||
|
func addArc(p Adder, pivot, n0, n1 fixed.Point26_6) {
|
||||||
|
// r2 is the square of the length of n0.
|
||||||
|
r2 := pDot(n0, n0)
|
||||||
|
if r2 < epsilon {
|
||||||
|
// The arc radius is so small that we collapse to a straight line.
|
||||||
|
p.Add1(pivot.Add(n1))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// We approximate the arc by 0, 1, 2 or 3 45-degree quadratic segments plus
|
||||||
|
// a final quadratic segment from s to n1. Each 45-degree segment has
|
||||||
|
// control points {1, 0}, {1, tan(π/8)} and {1/√2, 1/√2} suitably scaled,
|
||||||
|
// rotated and translated. tan(π/8) is approximately 27/64.
|
||||||
|
const tpo8 = 27
|
||||||
|
var s fixed.Point26_6
|
||||||
|
// We determine which octant the angle between n0 and n1 is in via three
|
||||||
|
// dot products. m0, m1 and m2 are n0 rotated clockwise by 45, 90 and 135
|
||||||
|
// degrees.
|
||||||
|
m0 := pRot45CW(n0)
|
||||||
|
m1 := pRot90CW(n0)
|
||||||
|
m2 := pRot90CW(m0)
|
||||||
|
if pDot(m1, n1) >= 0 {
|
||||||
|
if pDot(n0, n1) >= 0 {
|
||||||
|
if pDot(m2, n1) <= 0 {
|
||||||
|
// n1 is between 0 and 45 degrees clockwise of n0.
|
||||||
|
s = n0
|
||||||
|
} else {
|
||||||
|
// n1 is between 45 and 90 degrees clockwise of n0.
|
||||||
|
p.Add2(pivot.Add(n0).Add(m1.Mul(tpo8)), pivot.Add(m0))
|
||||||
|
s = m0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pm1, n0t := pivot.Add(m1), n0.Mul(tpo8)
|
||||||
|
p.Add2(pivot.Add(n0).Add(m1.Mul(tpo8)), pivot.Add(m0))
|
||||||
|
p.Add2(pm1.Add(n0t), pm1)
|
||||||
|
if pDot(m0, n1) >= 0 {
|
||||||
|
// n1 is between 90 and 135 degrees clockwise of n0.
|
||||||
|
s = m1
|
||||||
|
} else {
|
||||||
|
// n1 is between 135 and 180 degrees clockwise of n0.
|
||||||
|
p.Add2(pm1.Sub(n0t), pivot.Add(m2))
|
||||||
|
s = m2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if pDot(n0, n1) >= 0 {
|
||||||
|
if pDot(m0, n1) >= 0 {
|
||||||
|
// n1 is between 0 and 45 degrees counter-clockwise of n0.
|
||||||
|
s = n0
|
||||||
|
} else {
|
||||||
|
// n1 is between 45 and 90 degrees counter-clockwise of n0.
|
||||||
|
p.Add2(pivot.Add(n0).Sub(m1.Mul(tpo8)), pivot.Sub(m2))
|
||||||
|
s = pNeg(m2)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pm1, n0t := pivot.Sub(m1), n0.Mul(tpo8)
|
||||||
|
p.Add2(pivot.Add(n0).Sub(m1.Mul(tpo8)), pivot.Sub(m2))
|
||||||
|
p.Add2(pm1.Add(n0t), pm1)
|
||||||
|
if pDot(m2, n1) <= 0 {
|
||||||
|
// n1 is between 90 and 135 degrees counter-clockwise of n0.
|
||||||
|
s = pNeg(m1)
|
||||||
|
} else {
|
||||||
|
// n1 is between 135 and 180 degrees counter-clockwise of n0.
|
||||||
|
p.Add2(pm1.Sub(n0t), pivot.Sub(m0))
|
||||||
|
s = pNeg(m0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The final quadratic segment has two endpoints s and n1 and the middle
|
||||||
|
// control point is a multiple of s.Add(n1), i.e. it is on the angle
|
||||||
|
// bisector of those two points. The multiple ranges between 128/256 and
|
||||||
|
// 150/256 as the angle between s and n1 ranges between 0 and 45 degrees.
|
||||||
|
//
|
||||||
|
// When the angle is 0 degrees (i.e. s and n1 are coincident) then
|
||||||
|
// s.Add(n1) is twice s and so the middle control point of the degenerate
|
||||||
|
// quadratic segment should be half s.Add(n1), and half = 128/256.
|
||||||
|
//
|
||||||
|
// When the angle is 45 degrees then 150/256 is the ratio of the lengths of
|
||||||
|
// the two vectors {1, tan(π/8)} and {1 + 1/√2, 1/√2}.
|
||||||
|
//
|
||||||
|
// d is the normalized dot product between s and n1. Since the angle ranges
|
||||||
|
// between 0 and 45 degrees then d ranges between 256/256 and 181/256.
|
||||||
|
d := 256 * pDot(s, n1) / r2
|
||||||
|
multiple := fixed.Int26_6(150-(150-128)*(d-181)/(256-181)) >> 2
|
||||||
|
p.Add2(pivot.Add(s.Add(n1).Mul(multiple)), pivot.Add(n1))
|
||||||
|
}
|
||||||
|
|
||||||
|
// midpoint returns the midpoint of two Points.
|
||||||
|
func midpoint(a, b fixed.Point26_6) fixed.Point26_6 {
|
||||||
|
return fixed.Point26_6{(a.X + b.X) / 2, (a.Y + b.Y) / 2}
|
||||||
|
}
|
||||||
|
|
||||||
|
// angleGreaterThan45 returns whether the angle between two vectors is more
|
||||||
|
// than 45 degrees.
|
||||||
|
func angleGreaterThan45(v0, v1 fixed.Point26_6) bool {
|
||||||
|
v := pRot45CCW(v0)
|
||||||
|
return pDot(v, v1) < 0 || pDot(pRot90CW(v), v1) < 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// interpolate returns the point (1-t)*a + t*b.
|
||||||
|
func interpolate(a, b fixed.Point26_6, t fixed.Int52_12) fixed.Point26_6 {
|
||||||
|
s := 1<<12 - t
|
||||||
|
x := s*fixed.Int52_12(a.X) + t*fixed.Int52_12(b.X)
|
||||||
|
y := s*fixed.Int52_12(a.Y) + t*fixed.Int52_12(b.Y)
|
||||||
|
return fixed.Point26_6{fixed.Int26_6(x >> 12), fixed.Int26_6(y >> 12)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// curviest2 returns the value of t for which the quadratic parametric curve
|
||||||
|
// (1-t)²*a + 2*t*(1-t).b + t²*c has maximum curvature.
|
||||||
|
//
|
||||||
|
// The curvature of the parametric curve f(t) = (x(t), y(t)) is
|
||||||
|
// |x′y″-y′x″| / (x′²+y′²)^(3/2).
|
||||||
|
//
|
||||||
|
// Let d = b-a and e = c-2*b+a, so that f′(t) = 2*d+2*e*t and f″(t) = 2*e.
|
||||||
|
// The curvature's numerator is (2*dx+2*ex*t)*(2*ey)-(2*dy+2*ey*t)*(2*ex),
|
||||||
|
// which simplifies to 4*dx*ey-4*dy*ex, which is constant with respect to t.
|
||||||
|
//
|
||||||
|
// Thus, curvature is extreme where the denominator is extreme, i.e. where
|
||||||
|
// (x′²+y′²) is extreme. The first order condition is that
|
||||||
|
// 2*x′*x″+2*y′*y″ = 0, or (dx+ex*t)*ex + (dy+ey*t)*ey = 0.
|
||||||
|
// Solving for t gives t = -(dx*ex+dy*ey) / (ex*ex+ey*ey).
|
||||||
|
func curviest2(a, b, c fixed.Point26_6) fixed.Int52_12 {
|
||||||
|
dx := int64(b.X - a.X)
|
||||||
|
dy := int64(b.Y - a.Y)
|
||||||
|
ex := int64(c.X - 2*b.X + a.X)
|
||||||
|
ey := int64(c.Y - 2*b.Y + a.Y)
|
||||||
|
if ex == 0 && ey == 0 {
|
||||||
|
return 2048
|
||||||
|
}
|
||||||
|
return fixed.Int52_12(-4096 * (dx*ex + dy*ey) / (ex*ex + ey*ey))
|
||||||
|
}
|
||||||
|
|
||||||
|
// A stroker holds state for stroking a path.
|
||||||
|
type stroker struct {
|
||||||
|
// p is the destination that records the stroked path.
|
||||||
|
p Adder
|
||||||
|
// u is the half-width of the stroke.
|
||||||
|
u fixed.Int26_6
|
||||||
|
// cr and jr specify how to end and connect path segments.
|
||||||
|
cr Capper
|
||||||
|
jr Joiner
|
||||||
|
// r is the reverse path. Stroking a path involves constructing two
|
||||||
|
// parallel paths 2*u apart. The first path is added immediately to p,
|
||||||
|
// the second path is accumulated in r and eventually added in reverse.
|
||||||
|
r Path
|
||||||
|
// a is the most recent segment point. anorm is the segment normal of
|
||||||
|
// length u at that point.
|
||||||
|
a, anorm fixed.Point26_6
|
||||||
|
}
|
||||||
|
|
||||||
|
// addNonCurvy2 adds a quadratic segment to the stroker, where the segment
|
||||||
|
// defined by (k.a, b, c) achieves maximum curvature at either k.a or c.
|
||||||
|
func (k *stroker) addNonCurvy2(b, c fixed.Point26_6) {
|
||||||
|
// We repeatedly divide the segment at its middle until it is straight
|
||||||
|
// enough to approximate the stroke by just translating the control points.
|
||||||
|
// ds and ps are stacks of depths and points. t is the top of the stack.
|
||||||
|
const maxDepth = 5
|
||||||
|
var (
|
||||||
|
ds [maxDepth + 1]int
|
||||||
|
ps [2*maxDepth + 3]fixed.Point26_6
|
||||||
|
t int
|
||||||
|
)
|
||||||
|
// Initially the ps stack has one quadratic segment of depth zero.
|
||||||
|
ds[0] = 0
|
||||||
|
ps[2] = k.a
|
||||||
|
ps[1] = b
|
||||||
|
ps[0] = c
|
||||||
|
anorm := k.anorm
|
||||||
|
var cnorm fixed.Point26_6
|
||||||
|
|
||||||
|
for {
|
||||||
|
depth := ds[t]
|
||||||
|
a := ps[2*t+2]
|
||||||
|
b := ps[2*t+1]
|
||||||
|
c := ps[2*t+0]
|
||||||
|
ab := b.Sub(a)
|
||||||
|
bc := c.Sub(b)
|
||||||
|
abIsSmall := pDot(ab, ab) < fixed.Int52_12(1<<12)
|
||||||
|
bcIsSmall := pDot(bc, bc) < fixed.Int52_12(1<<12)
|
||||||
|
if abIsSmall && bcIsSmall {
|
||||||
|
// Approximate the segment by a circular arc.
|
||||||
|
cnorm = pRot90CCW(pNorm(bc, k.u))
|
||||||
|
mac := midpoint(a, c)
|
||||||
|
addArc(k.p, mac, anorm, cnorm)
|
||||||
|
addArc(&k.r, mac, pNeg(anorm), pNeg(cnorm))
|
||||||
|
} else if depth < maxDepth && angleGreaterThan45(ab, bc) {
|
||||||
|
// Divide the segment in two and push both halves on the stack.
|
||||||
|
mab := midpoint(a, b)
|
||||||
|
mbc := midpoint(b, c)
|
||||||
|
t++
|
||||||
|
ds[t+0] = depth + 1
|
||||||
|
ds[t-1] = depth + 1
|
||||||
|
ps[2*t+2] = a
|
||||||
|
ps[2*t+1] = mab
|
||||||
|
ps[2*t+0] = midpoint(mab, mbc)
|
||||||
|
ps[2*t-1] = mbc
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
// Translate the control points.
|
||||||
|
bnorm := pRot90CCW(pNorm(c.Sub(a), k.u))
|
||||||
|
cnorm = pRot90CCW(pNorm(bc, k.u))
|
||||||
|
k.p.Add2(b.Add(bnorm), c.Add(cnorm))
|
||||||
|
k.r.Add2(b.Sub(bnorm), c.Sub(cnorm))
|
||||||
|
}
|
||||||
|
if t == 0 {
|
||||||
|
k.a, k.anorm = c, cnorm
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t--
|
||||||
|
anorm = cnorm
|
||||||
|
}
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add1 adds a linear segment to the stroker.
|
||||||
|
func (k *stroker) Add1(b fixed.Point26_6) {
|
||||||
|
bnorm := pRot90CCW(pNorm(b.Sub(k.a), k.u))
|
||||||
|
if len(k.r) == 0 {
|
||||||
|
k.p.Start(k.a.Add(bnorm))
|
||||||
|
k.r.Start(k.a.Sub(bnorm))
|
||||||
|
} else {
|
||||||
|
k.jr.Join(k.p, &k.r, k.u, k.a, k.anorm, bnorm)
|
||||||
|
}
|
||||||
|
k.p.Add1(b.Add(bnorm))
|
||||||
|
k.r.Add1(b.Sub(bnorm))
|
||||||
|
k.a, k.anorm = b, bnorm
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add2 adds a quadratic segment to the stroker.
|
||||||
|
func (k *stroker) Add2(b, c fixed.Point26_6) {
|
||||||
|
ab := b.Sub(k.a)
|
||||||
|
bc := c.Sub(b)
|
||||||
|
abnorm := pRot90CCW(pNorm(ab, k.u))
|
||||||
|
if len(k.r) == 0 {
|
||||||
|
k.p.Start(k.a.Add(abnorm))
|
||||||
|
k.r.Start(k.a.Sub(abnorm))
|
||||||
|
} else {
|
||||||
|
k.jr.Join(k.p, &k.r, k.u, k.a, k.anorm, abnorm)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Approximate nearly-degenerate quadratics by linear segments.
|
||||||
|
abIsSmall := pDot(ab, ab) < epsilon
|
||||||
|
bcIsSmall := pDot(bc, bc) < epsilon
|
||||||
|
if abIsSmall || bcIsSmall {
|
||||||
|
acnorm := pRot90CCW(pNorm(c.Sub(k.a), k.u))
|
||||||
|
k.p.Add1(c.Add(acnorm))
|
||||||
|
k.r.Add1(c.Sub(acnorm))
|
||||||
|
k.a, k.anorm = c, acnorm
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// The quadratic segment (k.a, b, c) has a point of maximum curvature.
|
||||||
|
// If this occurs at an end point, we process the segment as a whole.
|
||||||
|
t := curviest2(k.a, b, c)
|
||||||
|
if t <= 0 || 4096 <= t {
|
||||||
|
k.addNonCurvy2(b, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, we perform a de Casteljau decomposition at the point of
|
||||||
|
// maximum curvature and process the two straighter parts.
|
||||||
|
mab := interpolate(k.a, b, t)
|
||||||
|
mbc := interpolate(b, c, t)
|
||||||
|
mabc := interpolate(mab, mbc, t)
|
||||||
|
|
||||||
|
// If the vectors ab and bc are close to being in opposite directions,
|
||||||
|
// then the decomposition can become unstable, so we approximate the
|
||||||
|
// quadratic segment by two linear segments joined by an arc.
|
||||||
|
bcnorm := pRot90CCW(pNorm(bc, k.u))
|
||||||
|
if pDot(abnorm, bcnorm) < -fixed.Int52_12(k.u)*fixed.Int52_12(k.u)*2047/2048 {
|
||||||
|
pArc := pDot(abnorm, bc) < 0
|
||||||
|
|
||||||
|
k.p.Add1(mabc.Add(abnorm))
|
||||||
|
if pArc {
|
||||||
|
z := pRot90CW(abnorm)
|
||||||
|
addArc(k.p, mabc, abnorm, z)
|
||||||
|
addArc(k.p, mabc, z, bcnorm)
|
||||||
|
}
|
||||||
|
k.p.Add1(mabc.Add(bcnorm))
|
||||||
|
k.p.Add1(c.Add(bcnorm))
|
||||||
|
|
||||||
|
k.r.Add1(mabc.Sub(abnorm))
|
||||||
|
if !pArc {
|
||||||
|
z := pRot90CW(abnorm)
|
||||||
|
addArc(&k.r, mabc, pNeg(abnorm), z)
|
||||||
|
addArc(&k.r, mabc, z, pNeg(bcnorm))
|
||||||
|
}
|
||||||
|
k.r.Add1(mabc.Sub(bcnorm))
|
||||||
|
k.r.Add1(c.Sub(bcnorm))
|
||||||
|
|
||||||
|
k.a, k.anorm = c, bcnorm
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the decomposed parts.
|
||||||
|
k.addNonCurvy2(mab, mabc)
|
||||||
|
k.addNonCurvy2(mbc, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add3 adds a cubic segment to the stroker.
|
||||||
|
func (k *stroker) Add3(b, c, d fixed.Point26_6) {
|
||||||
|
panic("freetype/raster: stroke unimplemented for cubic segments")
|
||||||
|
}
|
||||||
|
|
||||||
|
// stroke adds the stroked Path q to p, where q consists of exactly one curve.
|
||||||
|
func (k *stroker) stroke(q Path) {
|
||||||
|
// Stroking is implemented by deriving two paths each k.u apart from q.
|
||||||
|
// The left-hand-side path is added immediately to k.p; the right-hand-side
|
||||||
|
// path is accumulated in k.r. Once we've finished adding the LHS to k.p,
|
||||||
|
// we add the RHS in reverse order.
|
||||||
|
k.r = make(Path, 0, len(q))
|
||||||
|
k.a = fixed.Point26_6{q[1], q[2]}
|
||||||
|
for i := 4; i < len(q); {
|
||||||
|
switch q[i] {
|
||||||
|
case 1:
|
||||||
|
k.Add1(
|
||||||
|
fixed.Point26_6{q[i+1], q[i+2]},
|
||||||
|
)
|
||||||
|
i += 4
|
||||||
|
case 2:
|
||||||
|
k.Add2(
|
||||||
|
fixed.Point26_6{q[i+1], q[i+2]},
|
||||||
|
fixed.Point26_6{q[i+3], q[i+4]},
|
||||||
|
)
|
||||||
|
i += 6
|
||||||
|
case 3:
|
||||||
|
k.Add3(
|
||||||
|
fixed.Point26_6{q[i+1], q[i+2]},
|
||||||
|
fixed.Point26_6{q[i+3], q[i+4]},
|
||||||
|
fixed.Point26_6{q[i+5], q[i+6]},
|
||||||
|
)
|
||||||
|
i += 8
|
||||||
|
default:
|
||||||
|
panic("freetype/raster: bad path")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(k.r) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// TODO(nigeltao): if q is a closed curve then we should join the first and
|
||||||
|
// last segments instead of capping them.
|
||||||
|
k.cr.Cap(k.p, k.u, q.lastPoint(), pNeg(k.anorm))
|
||||||
|
addPathReversed(k.p, k.r)
|
||||||
|
pivot := q.firstPoint()
|
||||||
|
k.cr.Cap(k.p, k.u, pivot, pivot.Sub(fixed.Point26_6{k.r[1], k.r[2]}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stroke adds q stroked with the given width to p. The result is typically
|
||||||
|
// self-intersecting and should be rasterized with UseNonZeroWinding.
|
||||||
|
// cr and jr may be nil, which defaults to a RoundCapper or RoundJoiner.
|
||||||
|
func Stroke(p Adder, q Path, width fixed.Int26_6, cr Capper, jr Joiner) {
|
||||||
|
if len(q) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if cr == nil {
|
||||||
|
cr = RoundCapper
|
||||||
|
}
|
||||||
|
if jr == nil {
|
||||||
|
jr = RoundJoiner
|
||||||
|
}
|
||||||
|
if q[0] != 0 {
|
||||||
|
panic("freetype/raster: bad path")
|
||||||
|
}
|
||||||
|
s := stroker{p: p, u: width / 2, cr: cr, jr: jr}
|
||||||
|
i := 0
|
||||||
|
for j := 4; j < len(q); {
|
||||||
|
switch q[j] {
|
||||||
|
case 0:
|
||||||
|
s.stroke(q[i:j])
|
||||||
|
i, j = j, j+4
|
||||||
|
case 1:
|
||||||
|
j += 4
|
||||||
|
case 2:
|
||||||
|
j += 6
|
||||||
|
case 3:
|
||||||
|
j += 8
|
||||||
|
default:
|
||||||
|
panic("freetype/raster: bad path")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.stroke(q[i:])
|
||||||
|
}
|
507
vendor/github.com/golang/freetype/truetype/face.go
generated
vendored
Normal file
507
vendor/github.com/golang/freetype/truetype/face.go
generated
vendored
Normal file
|
@ -0,0 +1,507 @@
|
||||||
|
// Copyright 2015 The Freetype-Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by your choice of either the
|
||||||
|
// FreeType License or the GNU General Public License version 2 (or
|
||||||
|
// any later version), both of which can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package truetype
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"math"
|
||||||
|
|
||||||
|
"github.com/golang/freetype/raster"
|
||||||
|
"golang.org/x/image/font"
|
||||||
|
"golang.org/x/image/math/fixed"
|
||||||
|
)
|
||||||
|
|
||||||
|
func powerOf2(i int) bool {
|
||||||
|
return i != 0 && (i&(i-1)) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Options are optional arguments to NewFace.
|
||||||
|
type Options struct {
|
||||||
|
// Size is the font size in points, as in "a 10 point font size".
|
||||||
|
//
|
||||||
|
// A zero value means to use a 12 point font size.
|
||||||
|
Size float64
|
||||||
|
|
||||||
|
// DPI is the dots-per-inch resolution.
|
||||||
|
//
|
||||||
|
// A zero value means to use 72 DPI.
|
||||||
|
DPI float64
|
||||||
|
|
||||||
|
// Hinting is how to quantize the glyph nodes.
|
||||||
|
//
|
||||||
|
// A zero value means to use no hinting.
|
||||||
|
Hinting font.Hinting
|
||||||
|
|
||||||
|
// GlyphCacheEntries is the number of entries in the glyph mask image
|
||||||
|
// cache.
|
||||||
|
//
|
||||||
|
// If non-zero, it must be a power of 2.
|
||||||
|
//
|
||||||
|
// A zero value means to use 512 entries.
|
||||||
|
GlyphCacheEntries int
|
||||||
|
|
||||||
|
// SubPixelsX is the number of sub-pixel locations a glyph's dot is
|
||||||
|
// quantized to, in the horizontal direction. For example, a value of 8
|
||||||
|
// means that the dot is quantized to 1/8th of a pixel. This quantization
|
||||||
|
// only affects the glyph mask image, not its bounding box or advance
|
||||||
|
// width. A higher value gives a more faithful glyph image, but reduces the
|
||||||
|
// effectiveness of the glyph cache.
|
||||||
|
//
|
||||||
|
// If non-zero, it must be a power of 2, and be between 1 and 64 inclusive.
|
||||||
|
//
|
||||||
|
// A zero value means to use 4 sub-pixel locations.
|
||||||
|
SubPixelsX int
|
||||||
|
|
||||||
|
// SubPixelsY is the number of sub-pixel locations a glyph's dot is
|
||||||
|
// quantized to, in the vertical direction. For example, a value of 8
|
||||||
|
// means that the dot is quantized to 1/8th of a pixel. This quantization
|
||||||
|
// only affects the glyph mask image, not its bounding box or advance
|
||||||
|
// width. A higher value gives a more faithful glyph image, but reduces the
|
||||||
|
// effectiveness of the glyph cache.
|
||||||
|
//
|
||||||
|
// If non-zero, it must be a power of 2, and be between 1 and 64 inclusive.
|
||||||
|
//
|
||||||
|
// A zero value means to use 1 sub-pixel location.
|
||||||
|
SubPixelsY int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) size() float64 {
|
||||||
|
if o != nil && o.Size > 0 {
|
||||||
|
return o.Size
|
||||||
|
}
|
||||||
|
return 12
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) dpi() float64 {
|
||||||
|
if o != nil && o.DPI > 0 {
|
||||||
|
return o.DPI
|
||||||
|
}
|
||||||
|
return 72
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) hinting() font.Hinting {
|
||||||
|
if o != nil {
|
||||||
|
switch o.Hinting {
|
||||||
|
case font.HintingVertical, font.HintingFull:
|
||||||
|
// TODO: support vertical hinting.
|
||||||
|
return font.HintingFull
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return font.HintingNone
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) glyphCacheEntries() int {
|
||||||
|
if o != nil && powerOf2(o.GlyphCacheEntries) {
|
||||||
|
return o.GlyphCacheEntries
|
||||||
|
}
|
||||||
|
// 512 is 128 * 4 * 1, which lets us cache 128 glyphs at 4 * 1 subpixel
|
||||||
|
// locations in the X and Y direction.
|
||||||
|
return 512
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) subPixelsX() (value uint32, halfQuantum, mask fixed.Int26_6) {
|
||||||
|
if o != nil {
|
||||||
|
switch o.SubPixelsX {
|
||||||
|
case 1, 2, 4, 8, 16, 32, 64:
|
||||||
|
return subPixels(o.SubPixelsX)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// This default value of 4 isn't based on anything scientific, merely as
|
||||||
|
// small a number as possible that looks almost as good as no quantization,
|
||||||
|
// or returning subPixels(64).
|
||||||
|
return subPixels(4)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) subPixelsY() (value uint32, halfQuantum, mask fixed.Int26_6) {
|
||||||
|
if o != nil {
|
||||||
|
switch o.SubPixelsX {
|
||||||
|
case 1, 2, 4, 8, 16, 32, 64:
|
||||||
|
return subPixels(o.SubPixelsX)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// This default value of 1 isn't based on anything scientific, merely that
|
||||||
|
// vertical sub-pixel glyph rendering is pretty rare. Baseline locations
|
||||||
|
// can usually afford to snap to the pixel grid, so the vertical direction
|
||||||
|
// doesn't have the deal with the horizontal's fractional advance widths.
|
||||||
|
return subPixels(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// subPixels returns q and the bias and mask that leads to q quantized
|
||||||
|
// sub-pixel locations per full pixel.
|
||||||
|
//
|
||||||
|
// For example, q == 4 leads to a bias of 8 and a mask of 0xfffffff0, or -16,
|
||||||
|
// because we want to round fractions of fixed.Int26_6 as:
|
||||||
|
// - 0 to 7 rounds to 0.
|
||||||
|
// - 8 to 23 rounds to 16.
|
||||||
|
// - 24 to 39 rounds to 32.
|
||||||
|
// - 40 to 55 rounds to 48.
|
||||||
|
// - 56 to 63 rounds to 64.
|
||||||
|
// which means to add 8 and then bitwise-and with -16, in two's complement
|
||||||
|
// representation.
|
||||||
|
//
|
||||||
|
// When q == 1, we want bias == 32 and mask == -64.
|
||||||
|
// When q == 2, we want bias == 16 and mask == -32.
|
||||||
|
// When q == 4, we want bias == 8 and mask == -16.
|
||||||
|
// ...
|
||||||
|
// When q == 64, we want bias == 0 and mask == -1. (The no-op case).
|
||||||
|
// The pattern is clear.
|
||||||
|
func subPixels(q int) (value uint32, bias, mask fixed.Int26_6) {
|
||||||
|
return uint32(q), 32 / fixed.Int26_6(q), -64 / fixed.Int26_6(q)
|
||||||
|
}
|
||||||
|
|
||||||
|
// glyphCacheEntry caches the arguments and return values of rasterize.
|
||||||
|
type glyphCacheEntry struct {
|
||||||
|
key glyphCacheKey
|
||||||
|
val glyphCacheVal
|
||||||
|
}
|
||||||
|
|
||||||
|
type glyphCacheKey struct {
|
||||||
|
index Index
|
||||||
|
fx, fy uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
type glyphCacheVal struct {
|
||||||
|
advanceWidth fixed.Int26_6
|
||||||
|
offset image.Point
|
||||||
|
gw int
|
||||||
|
gh int
|
||||||
|
}
|
||||||
|
|
||||||
|
type indexCacheEntry struct {
|
||||||
|
rune rune
|
||||||
|
index Index
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFace returns a new font.Face for the given Font.
|
||||||
|
func NewFace(f *Font, opts *Options) font.Face {
|
||||||
|
a := &face{
|
||||||
|
f: f,
|
||||||
|
hinting: opts.hinting(),
|
||||||
|
scale: fixed.Int26_6(0.5 + (opts.size() * opts.dpi() * 64 / 72)),
|
||||||
|
glyphCache: make([]glyphCacheEntry, opts.glyphCacheEntries()),
|
||||||
|
}
|
||||||
|
a.subPixelX, a.subPixelBiasX, a.subPixelMaskX = opts.subPixelsX()
|
||||||
|
a.subPixelY, a.subPixelBiasY, a.subPixelMaskY = opts.subPixelsY()
|
||||||
|
|
||||||
|
// Fill the cache with invalid entries. Valid glyph cache entries have fx
|
||||||
|
// and fy in the range [0, 64). Valid index cache entries have rune >= 0.
|
||||||
|
for i := range a.glyphCache {
|
||||||
|
a.glyphCache[i].key.fy = 0xff
|
||||||
|
}
|
||||||
|
for i := range a.indexCache {
|
||||||
|
a.indexCache[i].rune = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the rasterizer's bounds to be big enough to handle the largest glyph.
|
||||||
|
b := f.Bounds(a.scale)
|
||||||
|
xmin := +int(b.Min.X) >> 6
|
||||||
|
ymin := -int(b.Max.Y) >> 6
|
||||||
|
xmax := +int(b.Max.X+63) >> 6
|
||||||
|
ymax := -int(b.Min.Y-63) >> 6
|
||||||
|
a.maxw = xmax - xmin
|
||||||
|
a.maxh = ymax - ymin
|
||||||
|
a.masks = image.NewAlpha(image.Rect(0, 0, a.maxw, a.maxh*len(a.glyphCache)))
|
||||||
|
a.r.SetBounds(a.maxw, a.maxh)
|
||||||
|
a.p = facePainter{a}
|
||||||
|
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
type face struct {
|
||||||
|
f *Font
|
||||||
|
hinting font.Hinting
|
||||||
|
scale fixed.Int26_6
|
||||||
|
subPixelX uint32
|
||||||
|
subPixelBiasX fixed.Int26_6
|
||||||
|
subPixelMaskX fixed.Int26_6
|
||||||
|
subPixelY uint32
|
||||||
|
subPixelBiasY fixed.Int26_6
|
||||||
|
subPixelMaskY fixed.Int26_6
|
||||||
|
masks *image.Alpha
|
||||||
|
glyphCache []glyphCacheEntry
|
||||||
|
r raster.Rasterizer
|
||||||
|
p raster.Painter
|
||||||
|
paintOffset int
|
||||||
|
maxw int
|
||||||
|
maxh int
|
||||||
|
glyphBuf GlyphBuf
|
||||||
|
indexCache [indexCacheLen]indexCacheEntry
|
||||||
|
|
||||||
|
// TODO: clip rectangle?
|
||||||
|
}
|
||||||
|
|
||||||
|
const indexCacheLen = 256
|
||||||
|
|
||||||
|
func (a *face) index(r rune) Index {
|
||||||
|
const mask = indexCacheLen - 1
|
||||||
|
c := &a.indexCache[r&mask]
|
||||||
|
if c.rune == r {
|
||||||
|
return c.index
|
||||||
|
}
|
||||||
|
i := a.f.Index(r)
|
||||||
|
c.rune = r
|
||||||
|
c.index = i
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close satisfies the font.Face interface.
|
||||||
|
func (a *face) Close() error { return nil }
|
||||||
|
|
||||||
|
// Metrics satisfies the font.Face interface.
|
||||||
|
func (a *face) Metrics() font.Metrics {
|
||||||
|
scale := float64(a.scale)
|
||||||
|
fupe := float64(a.f.FUnitsPerEm())
|
||||||
|
return font.Metrics{
|
||||||
|
Height: a.scale,
|
||||||
|
Ascent: fixed.Int26_6(math.Ceil(scale * float64(+a.f.ascent) / fupe)),
|
||||||
|
Descent: fixed.Int26_6(math.Ceil(scale * float64(-a.f.descent) / fupe)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kern satisfies the font.Face interface.
|
||||||
|
func (a *face) Kern(r0, r1 rune) fixed.Int26_6 {
|
||||||
|
i0 := a.index(r0)
|
||||||
|
i1 := a.index(r1)
|
||||||
|
kern := a.f.Kern(a.scale, i0, i1)
|
||||||
|
if a.hinting != font.HintingNone {
|
||||||
|
kern = (kern + 32) &^ 63
|
||||||
|
}
|
||||||
|
return kern
|
||||||
|
}
|
||||||
|
|
||||||
|
// Glyph satisfies the font.Face interface.
|
||||||
|
func (a *face) Glyph(dot fixed.Point26_6, r rune) (
|
||||||
|
dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) {
|
||||||
|
|
||||||
|
// Quantize to the sub-pixel granularity.
|
||||||
|
dotX := (dot.X + a.subPixelBiasX) & a.subPixelMaskX
|
||||||
|
dotY := (dot.Y + a.subPixelBiasY) & a.subPixelMaskY
|
||||||
|
|
||||||
|
// Split the coordinates into their integer and fractional parts.
|
||||||
|
ix, fx := int(dotX>>6), dotX&0x3f
|
||||||
|
iy, fy := int(dotY>>6), dotY&0x3f
|
||||||
|
|
||||||
|
index := a.index(r)
|
||||||
|
cIndex := uint32(index)
|
||||||
|
cIndex = cIndex*a.subPixelX - uint32(fx/a.subPixelMaskX)
|
||||||
|
cIndex = cIndex*a.subPixelY - uint32(fy/a.subPixelMaskY)
|
||||||
|
cIndex &= uint32(len(a.glyphCache) - 1)
|
||||||
|
a.paintOffset = a.maxh * int(cIndex)
|
||||||
|
k := glyphCacheKey{
|
||||||
|
index: index,
|
||||||
|
fx: uint8(fx),
|
||||||
|
fy: uint8(fy),
|
||||||
|
}
|
||||||
|
var v glyphCacheVal
|
||||||
|
if a.glyphCache[cIndex].key != k {
|
||||||
|
var ok bool
|
||||||
|
v, ok = a.rasterize(index, fx, fy)
|
||||||
|
if !ok {
|
||||||
|
return image.Rectangle{}, nil, image.Point{}, 0, false
|
||||||
|
}
|
||||||
|
a.glyphCache[cIndex] = glyphCacheEntry{k, v}
|
||||||
|
} else {
|
||||||
|
v = a.glyphCache[cIndex].val
|
||||||
|
}
|
||||||
|
|
||||||
|
dr.Min = image.Point{
|
||||||
|
X: ix + v.offset.X,
|
||||||
|
Y: iy + v.offset.Y,
|
||||||
|
}
|
||||||
|
dr.Max = image.Point{
|
||||||
|
X: dr.Min.X + v.gw,
|
||||||
|
Y: dr.Min.Y + v.gh,
|
||||||
|
}
|
||||||
|
return dr, a.masks, image.Point{Y: a.paintOffset}, v.advanceWidth, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *face) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) {
|
||||||
|
if err := a.glyphBuf.Load(a.f, a.scale, a.index(r), a.hinting); err != nil {
|
||||||
|
return fixed.Rectangle26_6{}, 0, false
|
||||||
|
}
|
||||||
|
xmin := +a.glyphBuf.Bounds.Min.X
|
||||||
|
ymin := -a.glyphBuf.Bounds.Max.Y
|
||||||
|
xmax := +a.glyphBuf.Bounds.Max.X
|
||||||
|
ymax := -a.glyphBuf.Bounds.Min.Y
|
||||||
|
if xmin > xmax || ymin > ymax {
|
||||||
|
return fixed.Rectangle26_6{}, 0, false
|
||||||
|
}
|
||||||
|
return fixed.Rectangle26_6{
|
||||||
|
Min: fixed.Point26_6{
|
||||||
|
X: xmin,
|
||||||
|
Y: ymin,
|
||||||
|
},
|
||||||
|
Max: fixed.Point26_6{
|
||||||
|
X: xmax,
|
||||||
|
Y: ymax,
|
||||||
|
},
|
||||||
|
}, a.glyphBuf.AdvanceWidth, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *face) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) {
|
||||||
|
if err := a.glyphBuf.Load(a.f, a.scale, a.index(r), a.hinting); err != nil {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
return a.glyphBuf.AdvanceWidth, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// rasterize returns the advance width, integer-pixel offset to render at, and
|
||||||
|
// the width and height of the given glyph at the given sub-pixel offsets.
|
||||||
|
//
|
||||||
|
// The 26.6 fixed point arguments fx and fy must be in the range [0, 1).
|
||||||
|
func (a *face) rasterize(index Index, fx, fy fixed.Int26_6) (v glyphCacheVal, ok bool) {
|
||||||
|
if err := a.glyphBuf.Load(a.f, a.scale, index, a.hinting); err != nil {
|
||||||
|
return glyphCacheVal{}, false
|
||||||
|
}
|
||||||
|
// Calculate the integer-pixel bounds for the glyph.
|
||||||
|
xmin := int(fx+a.glyphBuf.Bounds.Min.X) >> 6
|
||||||
|
ymin := int(fy-a.glyphBuf.Bounds.Max.Y) >> 6
|
||||||
|
xmax := int(fx+a.glyphBuf.Bounds.Max.X+0x3f) >> 6
|
||||||
|
ymax := int(fy-a.glyphBuf.Bounds.Min.Y+0x3f) >> 6
|
||||||
|
if xmin > xmax || ymin > ymax {
|
||||||
|
return glyphCacheVal{}, false
|
||||||
|
}
|
||||||
|
// A TrueType's glyph's nodes can have negative co-ordinates, but the
|
||||||
|
// rasterizer clips anything left of x=0 or above y=0. xmin and ymin are
|
||||||
|
// the pixel offsets, based on the font's FUnit metrics, that let a
|
||||||
|
// negative co-ordinate in TrueType space be non-negative in rasterizer
|
||||||
|
// space. xmin and ymin are typically <= 0.
|
||||||
|
fx -= fixed.Int26_6(xmin << 6)
|
||||||
|
fy -= fixed.Int26_6(ymin << 6)
|
||||||
|
// Rasterize the glyph's vectors.
|
||||||
|
a.r.Clear()
|
||||||
|
pixOffset := a.paintOffset * a.maxw
|
||||||
|
clear(a.masks.Pix[pixOffset : pixOffset+a.maxw*a.maxh])
|
||||||
|
e0 := 0
|
||||||
|
for _, e1 := range a.glyphBuf.Ends {
|
||||||
|
a.drawContour(a.glyphBuf.Points[e0:e1], fx, fy)
|
||||||
|
e0 = e1
|
||||||
|
}
|
||||||
|
a.r.Rasterize(a.p)
|
||||||
|
return glyphCacheVal{
|
||||||
|
a.glyphBuf.AdvanceWidth,
|
||||||
|
image.Point{xmin, ymin},
|
||||||
|
xmax - xmin,
|
||||||
|
ymax - ymin,
|
||||||
|
}, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func clear(pix []byte) {
|
||||||
|
for i := range pix {
|
||||||
|
pix[i] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// drawContour draws the given closed contour with the given offset.
|
||||||
|
func (a *face) drawContour(ps []Point, dx, dy fixed.Int26_6) {
|
||||||
|
if len(ps) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// The low bit of each point's Flags value is whether the point is on the
|
||||||
|
// curve. Truetype fonts only have quadratic Bézier curves, not cubics.
|
||||||
|
// Thus, two consecutive off-curve points imply an on-curve point in the
|
||||||
|
// middle of those two.
|
||||||
|
//
|
||||||
|
// See http://chanae.walon.org/pub/ttf/ttf_glyphs.htm for more details.
|
||||||
|
|
||||||
|
// ps[0] is a truetype.Point measured in FUnits and positive Y going
|
||||||
|
// upwards. start is the same thing measured in fixed point units and
|
||||||
|
// positive Y going downwards, and offset by (dx, dy).
|
||||||
|
start := fixed.Point26_6{
|
||||||
|
X: dx + ps[0].X,
|
||||||
|
Y: dy - ps[0].Y,
|
||||||
|
}
|
||||||
|
var others []Point
|
||||||
|
if ps[0].Flags&0x01 != 0 {
|
||||||
|
others = ps[1:]
|
||||||
|
} else {
|
||||||
|
last := fixed.Point26_6{
|
||||||
|
X: dx + ps[len(ps)-1].X,
|
||||||
|
Y: dy - ps[len(ps)-1].Y,
|
||||||
|
}
|
||||||
|
if ps[len(ps)-1].Flags&0x01 != 0 {
|
||||||
|
start = last
|
||||||
|
others = ps[:len(ps)-1]
|
||||||
|
} else {
|
||||||
|
start = fixed.Point26_6{
|
||||||
|
X: (start.X + last.X) / 2,
|
||||||
|
Y: (start.Y + last.Y) / 2,
|
||||||
|
}
|
||||||
|
others = ps
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a.r.Start(start)
|
||||||
|
q0, on0 := start, true
|
||||||
|
for _, p := range others {
|
||||||
|
q := fixed.Point26_6{
|
||||||
|
X: dx + p.X,
|
||||||
|
Y: dy - p.Y,
|
||||||
|
}
|
||||||
|
on := p.Flags&0x01 != 0
|
||||||
|
if on {
|
||||||
|
if on0 {
|
||||||
|
a.r.Add1(q)
|
||||||
|
} else {
|
||||||
|
a.r.Add2(q0, q)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if on0 {
|
||||||
|
// No-op.
|
||||||
|
} else {
|
||||||
|
mid := fixed.Point26_6{
|
||||||
|
X: (q0.X + q.X) / 2,
|
||||||
|
Y: (q0.Y + q.Y) / 2,
|
||||||
|
}
|
||||||
|
a.r.Add2(q0, mid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
q0, on0 = q, on
|
||||||
|
}
|
||||||
|
// Close the curve.
|
||||||
|
if on0 {
|
||||||
|
a.r.Add1(start)
|
||||||
|
} else {
|
||||||
|
a.r.Add2(q0, start)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// facePainter is like a raster.AlphaSrcPainter, with an additional Y offset
|
||||||
|
// (face.paintOffset) to the painted spans.
|
||||||
|
type facePainter struct {
|
||||||
|
a *face
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p facePainter) Paint(ss []raster.Span, done bool) {
|
||||||
|
m := p.a.masks
|
||||||
|
b := m.Bounds()
|
||||||
|
b.Min.Y = p.a.paintOffset
|
||||||
|
b.Max.Y = p.a.paintOffset + p.a.maxh
|
||||||
|
for _, s := range ss {
|
||||||
|
s.Y += p.a.paintOffset
|
||||||
|
if s.Y < b.Min.Y {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if s.Y >= b.Max.Y {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if s.X0 < b.Min.X {
|
||||||
|
s.X0 = b.Min.X
|
||||||
|
}
|
||||||
|
if s.X1 > b.Max.X {
|
||||||
|
s.X1 = b.Max.X
|
||||||
|
}
|
||||||
|
if s.X0 >= s.X1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
base := (s.Y-m.Rect.Min.Y)*m.Stride - m.Rect.Min.X
|
||||||
|
p := m.Pix[base+s.X0 : base+s.X1]
|
||||||
|
color := uint8(s.Alpha >> 8)
|
||||||
|
for i := range p {
|
||||||
|
p[i] = color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
517
vendor/github.com/golang/freetype/truetype/glyph.go
generated
vendored
Normal file
517
vendor/github.com/golang/freetype/truetype/glyph.go
generated
vendored
Normal file
|
@ -0,0 +1,517 @@
|
||||||
|
// Copyright 2010 The Freetype-Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by your choice of either the
|
||||||
|
// FreeType License or the GNU General Public License version 2 (or
|
||||||
|
// any later version), both of which can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package truetype
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/image/font"
|
||||||
|
"golang.org/x/image/math/fixed"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: implement VerticalHinting.
|
||||||
|
|
||||||
|
// A Point is a co-ordinate pair plus whether it is 'on' a contour or an 'off'
|
||||||
|
// control point.
|
||||||
|
type Point struct {
|
||||||
|
X, Y fixed.Int26_6
|
||||||
|
// The Flags' LSB means whether or not this Point is 'on' the contour.
|
||||||
|
// Other bits are reserved for internal use.
|
||||||
|
Flags uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// A GlyphBuf holds a glyph's contours. A GlyphBuf can be re-used to load a
|
||||||
|
// series of glyphs from a Font.
|
||||||
|
type GlyphBuf struct {
|
||||||
|
// AdvanceWidth is the glyph's advance width.
|
||||||
|
AdvanceWidth fixed.Int26_6
|
||||||
|
// Bounds is the glyph's bounding box.
|
||||||
|
Bounds fixed.Rectangle26_6
|
||||||
|
// Points contains all Points from all contours of the glyph. If hinting
|
||||||
|
// was used to load a glyph then Unhinted contains those Points before they
|
||||||
|
// were hinted, and InFontUnits contains those Points before they were
|
||||||
|
// hinted and scaled.
|
||||||
|
Points, Unhinted, InFontUnits []Point
|
||||||
|
// Ends is the point indexes of the end point of each contour. The length
|
||||||
|
// of Ends is the number of contours in the glyph. The i'th contour
|
||||||
|
// consists of points Points[Ends[i-1]:Ends[i]], where Ends[-1] is
|
||||||
|
// interpreted to mean zero.
|
||||||
|
Ends []int
|
||||||
|
|
||||||
|
font *Font
|
||||||
|
scale fixed.Int26_6
|
||||||
|
hinting font.Hinting
|
||||||
|
hinter hinter
|
||||||
|
// phantomPoints are the co-ordinates of the synthetic phantom points
|
||||||
|
// used for hinting and bounding box calculations.
|
||||||
|
phantomPoints [4]Point
|
||||||
|
// pp1x is the X co-ordinate of the first phantom point. The '1' is
|
||||||
|
// using 1-based indexing; pp1x is almost always phantomPoints[0].X.
|
||||||
|
// TODO: eliminate this and consistently use phantomPoints[0].X.
|
||||||
|
pp1x fixed.Int26_6
|
||||||
|
// metricsSet is whether the glyph's metrics have been set yet. For a
|
||||||
|
// compound glyph, a sub-glyph may override the outer glyph's metrics.
|
||||||
|
metricsSet bool
|
||||||
|
// tmp is a scratch buffer.
|
||||||
|
tmp []Point
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flags for decoding a glyph's contours. These flags are documented at
|
||||||
|
// http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html.
|
||||||
|
const (
|
||||||
|
flagOnCurve = 1 << iota
|
||||||
|
flagXShortVector
|
||||||
|
flagYShortVector
|
||||||
|
flagRepeat
|
||||||
|
flagPositiveXShortVector
|
||||||
|
flagPositiveYShortVector
|
||||||
|
|
||||||
|
// The remaining flags are for internal use.
|
||||||
|
flagTouchedX
|
||||||
|
flagTouchedY
|
||||||
|
)
|
||||||
|
|
||||||
|
// The same flag bits (0x10 and 0x20) are overloaded to have two meanings,
|
||||||
|
// dependent on the value of the flag{X,Y}ShortVector bits.
|
||||||
|
const (
|
||||||
|
flagThisXIsSame = flagPositiveXShortVector
|
||||||
|
flagThisYIsSame = flagPositiveYShortVector
|
||||||
|
)
|
||||||
|
|
||||||
|
// Load loads a glyph's contours from a Font, overwriting any previously loaded
|
||||||
|
// contours for this GlyphBuf. scale is the number of 26.6 fixed point units in
|
||||||
|
// 1 em, i is the glyph index, and h is the hinting policy.
|
||||||
|
func (g *GlyphBuf) Load(f *Font, scale fixed.Int26_6, i Index, h font.Hinting) error {
|
||||||
|
g.Points = g.Points[:0]
|
||||||
|
g.Unhinted = g.Unhinted[:0]
|
||||||
|
g.InFontUnits = g.InFontUnits[:0]
|
||||||
|
g.Ends = g.Ends[:0]
|
||||||
|
g.font = f
|
||||||
|
g.hinting = h
|
||||||
|
g.scale = scale
|
||||||
|
g.pp1x = 0
|
||||||
|
g.phantomPoints = [4]Point{}
|
||||||
|
g.metricsSet = false
|
||||||
|
|
||||||
|
if h != font.HintingNone {
|
||||||
|
if err := g.hinter.init(f, scale); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := g.load(0, i, true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// TODO: this selection of either g.pp1x or g.phantomPoints[0].X isn't ideal,
|
||||||
|
// and should be cleaned up once we have all the testScaling tests passing,
|
||||||
|
// plus additional tests for Freetype-Go's bounding boxes matching C Freetype's.
|
||||||
|
pp1x := g.pp1x
|
||||||
|
if h != font.HintingNone {
|
||||||
|
pp1x = g.phantomPoints[0].X
|
||||||
|
}
|
||||||
|
if pp1x != 0 {
|
||||||
|
for i := range g.Points {
|
||||||
|
g.Points[i].X -= pp1x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
advanceWidth := g.phantomPoints[1].X - g.phantomPoints[0].X
|
||||||
|
if h != font.HintingNone {
|
||||||
|
if len(f.hdmx) >= 8 {
|
||||||
|
if n := u32(f.hdmx, 4); n > 3+uint32(i) {
|
||||||
|
for hdmx := f.hdmx[8:]; uint32(len(hdmx)) >= n; hdmx = hdmx[n:] {
|
||||||
|
if fixed.Int26_6(hdmx[0]) == scale>>6 {
|
||||||
|
advanceWidth = fixed.Int26_6(hdmx[2+i]) << 6
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
advanceWidth = (advanceWidth + 32) &^ 63
|
||||||
|
}
|
||||||
|
g.AdvanceWidth = advanceWidth
|
||||||
|
|
||||||
|
// Set g.Bounds to the 'control box', which is the bounding box of the
|
||||||
|
// Bézier curves' control points. This is easier to calculate, no smaller
|
||||||
|
// than and often equal to the tightest possible bounding box of the curves
|
||||||
|
// themselves. This approach is what C Freetype does. We can't just scale
|
||||||
|
// the nominal bounding box in the glyf data as the hinting process and
|
||||||
|
// phantom point adjustment may move points outside of that box.
|
||||||
|
if len(g.Points) == 0 {
|
||||||
|
g.Bounds = fixed.Rectangle26_6{}
|
||||||
|
} else {
|
||||||
|
p := g.Points[0]
|
||||||
|
g.Bounds.Min.X = p.X
|
||||||
|
g.Bounds.Max.X = p.X
|
||||||
|
g.Bounds.Min.Y = p.Y
|
||||||
|
g.Bounds.Max.Y = p.Y
|
||||||
|
for _, p := range g.Points[1:] {
|
||||||
|
if g.Bounds.Min.X > p.X {
|
||||||
|
g.Bounds.Min.X = p.X
|
||||||
|
} else if g.Bounds.Max.X < p.X {
|
||||||
|
g.Bounds.Max.X = p.X
|
||||||
|
}
|
||||||
|
if g.Bounds.Min.Y > p.Y {
|
||||||
|
g.Bounds.Min.Y = p.Y
|
||||||
|
} else if g.Bounds.Max.Y < p.Y {
|
||||||
|
g.Bounds.Max.Y = p.Y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Snap the box to the grid, if hinting is on.
|
||||||
|
if h != font.HintingNone {
|
||||||
|
g.Bounds.Min.X &^= 63
|
||||||
|
g.Bounds.Min.Y &^= 63
|
||||||
|
g.Bounds.Max.X += 63
|
||||||
|
g.Bounds.Max.X &^= 63
|
||||||
|
g.Bounds.Max.Y += 63
|
||||||
|
g.Bounds.Max.Y &^= 63
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GlyphBuf) load(recursion uint32, i Index, useMyMetrics bool) (err error) {
|
||||||
|
// The recursion limit here is arbitrary, but defends against malformed glyphs.
|
||||||
|
if recursion >= 32 {
|
||||||
|
return UnsupportedError("excessive compound glyph recursion")
|
||||||
|
}
|
||||||
|
// Find the relevant slice of g.font.glyf.
|
||||||
|
var g0, g1 uint32
|
||||||
|
if g.font.locaOffsetFormat == locaOffsetFormatShort {
|
||||||
|
g0 = 2 * uint32(u16(g.font.loca, 2*int(i)))
|
||||||
|
g1 = 2 * uint32(u16(g.font.loca, 2*int(i)+2))
|
||||||
|
} else {
|
||||||
|
g0 = u32(g.font.loca, 4*int(i))
|
||||||
|
g1 = u32(g.font.loca, 4*int(i)+4)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode the contour count and nominal bounding box, from the first
|
||||||
|
// 10 bytes of the glyf data. boundsYMin and boundsXMax, at offsets 4
|
||||||
|
// and 6, are unused.
|
||||||
|
glyf, ne, boundsXMin, boundsYMax := []byte(nil), 0, fixed.Int26_6(0), fixed.Int26_6(0)
|
||||||
|
if g0+10 <= g1 {
|
||||||
|
glyf = g.font.glyf[g0:g1]
|
||||||
|
ne = int(int16(u16(glyf, 0)))
|
||||||
|
boundsXMin = fixed.Int26_6(int16(u16(glyf, 2)))
|
||||||
|
boundsYMax = fixed.Int26_6(int16(u16(glyf, 8)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the phantom points.
|
||||||
|
uhm, pp1x := g.font.unscaledHMetric(i), fixed.Int26_6(0)
|
||||||
|
uvm := g.font.unscaledVMetric(i, boundsYMax)
|
||||||
|
g.phantomPoints = [4]Point{
|
||||||
|
{X: boundsXMin - uhm.LeftSideBearing},
|
||||||
|
{X: boundsXMin - uhm.LeftSideBearing + uhm.AdvanceWidth},
|
||||||
|
{X: uhm.AdvanceWidth / 2, Y: boundsYMax + uvm.TopSideBearing},
|
||||||
|
{X: uhm.AdvanceWidth / 2, Y: boundsYMax + uvm.TopSideBearing - uvm.AdvanceHeight},
|
||||||
|
}
|
||||||
|
if len(glyf) == 0 {
|
||||||
|
g.addPhantomsAndScale(len(g.Points), len(g.Points), true, true)
|
||||||
|
copy(g.phantomPoints[:], g.Points[len(g.Points)-4:])
|
||||||
|
g.Points = g.Points[:len(g.Points)-4]
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load and hint the contours.
|
||||||
|
if ne < 0 {
|
||||||
|
if ne != -1 {
|
||||||
|
// http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html says that
|
||||||
|
// "the values -2, -3, and so forth, are reserved for future use."
|
||||||
|
return UnsupportedError("negative number of contours")
|
||||||
|
}
|
||||||
|
pp1x = g.font.scale(g.scale * (boundsXMin - uhm.LeftSideBearing))
|
||||||
|
if err := g.loadCompound(recursion, uhm, i, glyf, useMyMetrics); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
np0, ne0 := len(g.Points), len(g.Ends)
|
||||||
|
program := g.loadSimple(glyf, ne)
|
||||||
|
g.addPhantomsAndScale(np0, np0, true, true)
|
||||||
|
pp1x = g.Points[len(g.Points)-4].X
|
||||||
|
if g.hinting != font.HintingNone {
|
||||||
|
if len(program) != 0 {
|
||||||
|
err := g.hinter.run(
|
||||||
|
program,
|
||||||
|
g.Points[np0:],
|
||||||
|
g.Unhinted[np0:],
|
||||||
|
g.InFontUnits[np0:],
|
||||||
|
g.Ends[ne0:],
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Drop the four phantom points.
|
||||||
|
g.InFontUnits = g.InFontUnits[:len(g.InFontUnits)-4]
|
||||||
|
g.Unhinted = g.Unhinted[:len(g.Unhinted)-4]
|
||||||
|
}
|
||||||
|
if useMyMetrics {
|
||||||
|
copy(g.phantomPoints[:], g.Points[len(g.Points)-4:])
|
||||||
|
}
|
||||||
|
g.Points = g.Points[:len(g.Points)-4]
|
||||||
|
if np0 != 0 {
|
||||||
|
// The hinting program expects the []Ends values to be indexed
|
||||||
|
// relative to the inner glyph, not the outer glyph, so we delay
|
||||||
|
// adding np0 until after the hinting program (if any) has run.
|
||||||
|
for i := ne0; i < len(g.Ends); i++ {
|
||||||
|
g.Ends[i] += np0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if useMyMetrics && !g.metricsSet {
|
||||||
|
g.metricsSet = true
|
||||||
|
g.pp1x = pp1x
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadOffset is the initial offset for loadSimple and loadCompound. The first
|
||||||
|
// 10 bytes are the number of contours and the bounding box.
|
||||||
|
const loadOffset = 10
|
||||||
|
|
||||||
|
func (g *GlyphBuf) loadSimple(glyf []byte, ne int) (program []byte) {
|
||||||
|
offset := loadOffset
|
||||||
|
for i := 0; i < ne; i++ {
|
||||||
|
g.Ends = append(g.Ends, 1+int(u16(glyf, offset)))
|
||||||
|
offset += 2
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note the TrueType hinting instructions.
|
||||||
|
instrLen := int(u16(glyf, offset))
|
||||||
|
offset += 2
|
||||||
|
program = glyf[offset : offset+instrLen]
|
||||||
|
offset += instrLen
|
||||||
|
|
||||||
|
np0 := len(g.Points)
|
||||||
|
np1 := np0 + int(g.Ends[len(g.Ends)-1])
|
||||||
|
|
||||||
|
// Decode the flags.
|
||||||
|
for i := np0; i < np1; {
|
||||||
|
c := uint32(glyf[offset])
|
||||||
|
offset++
|
||||||
|
g.Points = append(g.Points, Point{Flags: c})
|
||||||
|
i++
|
||||||
|
if c&flagRepeat != 0 {
|
||||||
|
count := glyf[offset]
|
||||||
|
offset++
|
||||||
|
for ; count > 0; count-- {
|
||||||
|
g.Points = append(g.Points, Point{Flags: c})
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode the co-ordinates.
|
||||||
|
var x int16
|
||||||
|
for i := np0; i < np1; i++ {
|
||||||
|
f := g.Points[i].Flags
|
||||||
|
if f&flagXShortVector != 0 {
|
||||||
|
dx := int16(glyf[offset])
|
||||||
|
offset++
|
||||||
|
if f&flagPositiveXShortVector == 0 {
|
||||||
|
x -= dx
|
||||||
|
} else {
|
||||||
|
x += dx
|
||||||
|
}
|
||||||
|
} else if f&flagThisXIsSame == 0 {
|
||||||
|
x += int16(u16(glyf, offset))
|
||||||
|
offset += 2
|
||||||
|
}
|
||||||
|
g.Points[i].X = fixed.Int26_6(x)
|
||||||
|
}
|
||||||
|
var y int16
|
||||||
|
for i := np0; i < np1; i++ {
|
||||||
|
f := g.Points[i].Flags
|
||||||
|
if f&flagYShortVector != 0 {
|
||||||
|
dy := int16(glyf[offset])
|
||||||
|
offset++
|
||||||
|
if f&flagPositiveYShortVector == 0 {
|
||||||
|
y -= dy
|
||||||
|
} else {
|
||||||
|
y += dy
|
||||||
|
}
|
||||||
|
} else if f&flagThisYIsSame == 0 {
|
||||||
|
y += int16(u16(glyf, offset))
|
||||||
|
offset += 2
|
||||||
|
}
|
||||||
|
g.Points[i].Y = fixed.Int26_6(y)
|
||||||
|
}
|
||||||
|
|
||||||
|
return program
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GlyphBuf) loadCompound(recursion uint32, uhm HMetric, i Index,
|
||||||
|
glyf []byte, useMyMetrics bool) error {
|
||||||
|
|
||||||
|
// Flags for decoding a compound glyph. These flags are documented at
|
||||||
|
// http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html.
|
||||||
|
const (
|
||||||
|
flagArg1And2AreWords = 1 << iota
|
||||||
|
flagArgsAreXYValues
|
||||||
|
flagRoundXYToGrid
|
||||||
|
flagWeHaveAScale
|
||||||
|
flagUnused
|
||||||
|
flagMoreComponents
|
||||||
|
flagWeHaveAnXAndYScale
|
||||||
|
flagWeHaveATwoByTwo
|
||||||
|
flagWeHaveInstructions
|
||||||
|
flagUseMyMetrics
|
||||||
|
flagOverlapCompound
|
||||||
|
)
|
||||||
|
np0, ne0 := len(g.Points), len(g.Ends)
|
||||||
|
offset := loadOffset
|
||||||
|
for {
|
||||||
|
flags := u16(glyf, offset)
|
||||||
|
component := Index(u16(glyf, offset+2))
|
||||||
|
dx, dy, transform, hasTransform := fixed.Int26_6(0), fixed.Int26_6(0), [4]int16{}, false
|
||||||
|
if flags&flagArg1And2AreWords != 0 {
|
||||||
|
dx = fixed.Int26_6(int16(u16(glyf, offset+4)))
|
||||||
|
dy = fixed.Int26_6(int16(u16(glyf, offset+6)))
|
||||||
|
offset += 8
|
||||||
|
} else {
|
||||||
|
dx = fixed.Int26_6(int16(int8(glyf[offset+4])))
|
||||||
|
dy = fixed.Int26_6(int16(int8(glyf[offset+5])))
|
||||||
|
offset += 6
|
||||||
|
}
|
||||||
|
if flags&flagArgsAreXYValues == 0 {
|
||||||
|
return UnsupportedError("compound glyph transform vector")
|
||||||
|
}
|
||||||
|
if flags&(flagWeHaveAScale|flagWeHaveAnXAndYScale|flagWeHaveATwoByTwo) != 0 {
|
||||||
|
hasTransform = true
|
||||||
|
switch {
|
||||||
|
case flags&flagWeHaveAScale != 0:
|
||||||
|
transform[0] = int16(u16(glyf, offset+0))
|
||||||
|
transform[3] = transform[0]
|
||||||
|
offset += 2
|
||||||
|
case flags&flagWeHaveAnXAndYScale != 0:
|
||||||
|
transform[0] = int16(u16(glyf, offset+0))
|
||||||
|
transform[3] = int16(u16(glyf, offset+2))
|
||||||
|
offset += 4
|
||||||
|
case flags&flagWeHaveATwoByTwo != 0:
|
||||||
|
transform[0] = int16(u16(glyf, offset+0))
|
||||||
|
transform[1] = int16(u16(glyf, offset+2))
|
||||||
|
transform[2] = int16(u16(glyf, offset+4))
|
||||||
|
transform[3] = int16(u16(glyf, offset+6))
|
||||||
|
offset += 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
savedPP := g.phantomPoints
|
||||||
|
np0 := len(g.Points)
|
||||||
|
componentUMM := useMyMetrics && (flags&flagUseMyMetrics != 0)
|
||||||
|
if err := g.load(recursion+1, component, componentUMM); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if flags&flagUseMyMetrics == 0 {
|
||||||
|
g.phantomPoints = savedPP
|
||||||
|
}
|
||||||
|
if hasTransform {
|
||||||
|
for j := np0; j < len(g.Points); j++ {
|
||||||
|
p := &g.Points[j]
|
||||||
|
newX := 0 +
|
||||||
|
fixed.Int26_6((int64(p.X)*int64(transform[0])+1<<13)>>14) +
|
||||||
|
fixed.Int26_6((int64(p.Y)*int64(transform[2])+1<<13)>>14)
|
||||||
|
newY := 0 +
|
||||||
|
fixed.Int26_6((int64(p.X)*int64(transform[1])+1<<13)>>14) +
|
||||||
|
fixed.Int26_6((int64(p.Y)*int64(transform[3])+1<<13)>>14)
|
||||||
|
p.X, p.Y = newX, newY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dx = g.font.scale(g.scale * dx)
|
||||||
|
dy = g.font.scale(g.scale * dy)
|
||||||
|
if flags&flagRoundXYToGrid != 0 {
|
||||||
|
dx = (dx + 32) &^ 63
|
||||||
|
dy = (dy + 32) &^ 63
|
||||||
|
}
|
||||||
|
for j := np0; j < len(g.Points); j++ {
|
||||||
|
p := &g.Points[j]
|
||||||
|
p.X += dx
|
||||||
|
p.Y += dy
|
||||||
|
}
|
||||||
|
// TODO: also adjust g.InFontUnits and g.Unhinted?
|
||||||
|
if flags&flagMoreComponents == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
instrLen := 0
|
||||||
|
if g.hinting != font.HintingNone && offset+2 <= len(glyf) {
|
||||||
|
instrLen = int(u16(glyf, offset))
|
||||||
|
offset += 2
|
||||||
|
}
|
||||||
|
|
||||||
|
g.addPhantomsAndScale(np0, len(g.Points), false, instrLen > 0)
|
||||||
|
points, ends := g.Points[np0:], g.Ends[ne0:]
|
||||||
|
g.Points = g.Points[:len(g.Points)-4]
|
||||||
|
for j := range points {
|
||||||
|
points[j].Flags &^= flagTouchedX | flagTouchedY
|
||||||
|
}
|
||||||
|
|
||||||
|
if instrLen == 0 {
|
||||||
|
if !g.metricsSet {
|
||||||
|
copy(g.phantomPoints[:], points[len(points)-4:])
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hint the compound glyph.
|
||||||
|
program := glyf[offset : offset+instrLen]
|
||||||
|
// Temporarily adjust the ends to be relative to this compound glyph.
|
||||||
|
if np0 != 0 {
|
||||||
|
for i := range ends {
|
||||||
|
ends[i] -= np0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Hinting instructions of a composite glyph completely refer to the
|
||||||
|
// (already) hinted subglyphs.
|
||||||
|
g.tmp = append(g.tmp[:0], points...)
|
||||||
|
if err := g.hinter.run(program, points, g.tmp, g.tmp, ends); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if np0 != 0 {
|
||||||
|
for i := range ends {
|
||||||
|
ends[i] += np0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !g.metricsSet {
|
||||||
|
copy(g.phantomPoints[:], points[len(points)-4:])
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GlyphBuf) addPhantomsAndScale(np0, np1 int, simple, adjust bool) {
|
||||||
|
// Add the four phantom points.
|
||||||
|
g.Points = append(g.Points, g.phantomPoints[:]...)
|
||||||
|
// Scale the points.
|
||||||
|
if simple && g.hinting != font.HintingNone {
|
||||||
|
g.InFontUnits = append(g.InFontUnits, g.Points[np1:]...)
|
||||||
|
}
|
||||||
|
for i := np1; i < len(g.Points); i++ {
|
||||||
|
p := &g.Points[i]
|
||||||
|
p.X = g.font.scale(g.scale * p.X)
|
||||||
|
p.Y = g.font.scale(g.scale * p.Y)
|
||||||
|
}
|
||||||
|
if g.hinting == font.HintingNone {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Round the 1st phantom point to the grid, shifting all other points equally.
|
||||||
|
// Note that "all other points" starts from np0, not np1.
|
||||||
|
// TODO: delete this adjustment and the np0/np1 distinction, when
|
||||||
|
// we update the compatibility tests to C Freetype 2.5.3.
|
||||||
|
// See http://git.savannah.gnu.org/cgit/freetype/freetype2.git/commit/?id=05c786d990390a7ca18e62962641dac740bacb06
|
||||||
|
if adjust {
|
||||||
|
pp1x := g.Points[len(g.Points)-4].X
|
||||||
|
if dx := ((pp1x + 32) &^ 63) - pp1x; dx != 0 {
|
||||||
|
for i := np0; i < len(g.Points); i++ {
|
||||||
|
g.Points[i].X += dx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if simple {
|
||||||
|
g.Unhinted = append(g.Unhinted, g.Points[np1:]...)
|
||||||
|
}
|
||||||
|
// Round the 2nd and 4th phantom point to the grid.
|
||||||
|
p := &g.Points[len(g.Points)-3]
|
||||||
|
p.X = (p.X + 32) &^ 63
|
||||||
|
p = &g.Points[len(g.Points)-1]
|
||||||
|
p.Y = (p.Y + 32) &^ 63
|
||||||
|
}
|
1770
vendor/github.com/golang/freetype/truetype/hint.go
generated
vendored
Normal file
1770
vendor/github.com/golang/freetype/truetype/hint.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
289
vendor/github.com/golang/freetype/truetype/opcodes.go
generated
vendored
Normal file
289
vendor/github.com/golang/freetype/truetype/opcodes.go
generated
vendored
Normal file
|
@ -0,0 +1,289 @@
|
||||||
|
// Copyright 2012 The Freetype-Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by your choice of either the
|
||||||
|
// FreeType License or the GNU General Public License version 2 (or
|
||||||
|
// any later version), both of which can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package truetype
|
||||||
|
|
||||||
|
// The Truetype opcodes are summarized at
|
||||||
|
// https://developer.apple.com/fonts/TTRefMan/RM07/appendixA.html
|
||||||
|
|
||||||
|
const (
|
||||||
|
opSVTCA0 = 0x00 // Set freedom and projection Vectors To Coordinate Axis
|
||||||
|
opSVTCA1 = 0x01 // .
|
||||||
|
opSPVTCA0 = 0x02 // Set Projection Vector To Coordinate Axis
|
||||||
|
opSPVTCA1 = 0x03 // .
|
||||||
|
opSFVTCA0 = 0x04 // Set Freedom Vector to Coordinate Axis
|
||||||
|
opSFVTCA1 = 0x05 // .
|
||||||
|
opSPVTL0 = 0x06 // Set Projection Vector To Line
|
||||||
|
opSPVTL1 = 0x07 // .
|
||||||
|
opSFVTL0 = 0x08 // Set Freedom Vector To Line
|
||||||
|
opSFVTL1 = 0x09 // .
|
||||||
|
opSPVFS = 0x0a // Set Projection Vector From Stack
|
||||||
|
opSFVFS = 0x0b // Set Freedom Vector From Stack
|
||||||
|
opGPV = 0x0c // Get Projection Vector
|
||||||
|
opGFV = 0x0d // Get Freedom Vector
|
||||||
|
opSFVTPV = 0x0e // Set Freedom Vector To Projection Vector
|
||||||
|
opISECT = 0x0f // moves point p to the InterSECTion of two lines
|
||||||
|
opSRP0 = 0x10 // Set Reference Point 0
|
||||||
|
opSRP1 = 0x11 // Set Reference Point 1
|
||||||
|
opSRP2 = 0x12 // Set Reference Point 2
|
||||||
|
opSZP0 = 0x13 // Set Zone Pointer 0
|
||||||
|
opSZP1 = 0x14 // Set Zone Pointer 1
|
||||||
|
opSZP2 = 0x15 // Set Zone Pointer 2
|
||||||
|
opSZPS = 0x16 // Set Zone PointerS
|
||||||
|
opSLOOP = 0x17 // Set LOOP variable
|
||||||
|
opRTG = 0x18 // Round To Grid
|
||||||
|
opRTHG = 0x19 // Round To Half Grid
|
||||||
|
opSMD = 0x1a // Set Minimum Distance
|
||||||
|
opELSE = 0x1b // ELSE clause
|
||||||
|
opJMPR = 0x1c // JuMP Relative
|
||||||
|
opSCVTCI = 0x1d // Set Control Value Table Cut-In
|
||||||
|
opSSWCI = 0x1e // Set Single Width Cut-In
|
||||||
|
opSSW = 0x1f // Set Single Width
|
||||||
|
opDUP = 0x20 // DUPlicate top stack element
|
||||||
|
opPOP = 0x21 // POP top stack element
|
||||||
|
opCLEAR = 0x22 // CLEAR the stack
|
||||||
|
opSWAP = 0x23 // SWAP the top two elements on the stack
|
||||||
|
opDEPTH = 0x24 // DEPTH of the stack
|
||||||
|
opCINDEX = 0x25 // Copy the INDEXed element to the top of the stack
|
||||||
|
opMINDEX = 0x26 // Move the INDEXed element to the top of the stack
|
||||||
|
opALIGNPTS = 0x27 // ALIGN PoinTS
|
||||||
|
op_0x28 = 0x28 // deprecated
|
||||||
|
opUTP = 0x29 // UnTouch Point
|
||||||
|
opLOOPCALL = 0x2a // LOOP and CALL function
|
||||||
|
opCALL = 0x2b // CALL function
|
||||||
|
opFDEF = 0x2c // Function DEFinition
|
||||||
|
opENDF = 0x2d // END Function definition
|
||||||
|
opMDAP0 = 0x2e // Move Direct Absolute Point
|
||||||
|
opMDAP1 = 0x2f // .
|
||||||
|
opIUP0 = 0x30 // Interpolate Untouched Points through the outline
|
||||||
|
opIUP1 = 0x31 // .
|
||||||
|
opSHP0 = 0x32 // SHift Point using reference point
|
||||||
|
opSHP1 = 0x33 // .
|
||||||
|
opSHC0 = 0x34 // SHift Contour using reference point
|
||||||
|
opSHC1 = 0x35 // .
|
||||||
|
opSHZ0 = 0x36 // SHift Zone using reference point
|
||||||
|
opSHZ1 = 0x37 // .
|
||||||
|
opSHPIX = 0x38 // SHift point by a PIXel amount
|
||||||
|
opIP = 0x39 // Interpolate Point
|
||||||
|
opMSIRP0 = 0x3a // Move Stack Indirect Relative Point
|
||||||
|
opMSIRP1 = 0x3b // .
|
||||||
|
opALIGNRP = 0x3c // ALIGN to Reference Point
|
||||||
|
opRTDG = 0x3d // Round To Double Grid
|
||||||
|
opMIAP0 = 0x3e // Move Indirect Absolute Point
|
||||||
|
opMIAP1 = 0x3f // .
|
||||||
|
opNPUSHB = 0x40 // PUSH N Bytes
|
||||||
|
opNPUSHW = 0x41 // PUSH N Words
|
||||||
|
opWS = 0x42 // Write Store
|
||||||
|
opRS = 0x43 // Read Store
|
||||||
|
opWCVTP = 0x44 // Write Control Value Table in Pixel units
|
||||||
|
opRCVT = 0x45 // Read Control Value Table entry
|
||||||
|
opGC0 = 0x46 // Get Coordinate projected onto the projection vector
|
||||||
|
opGC1 = 0x47 // .
|
||||||
|
opSCFS = 0x48 // Sets Coordinate From the Stack using projection vector and freedom vector
|
||||||
|
opMD0 = 0x49 // Measure Distance
|
||||||
|
opMD1 = 0x4a // .
|
||||||
|
opMPPEM = 0x4b // Measure Pixels Per EM
|
||||||
|
opMPS = 0x4c // Measure Point Size
|
||||||
|
opFLIPON = 0x4d // set the auto FLIP Boolean to ON
|
||||||
|
opFLIPOFF = 0x4e // set the auto FLIP Boolean to OFF
|
||||||
|
opDEBUG = 0x4f // DEBUG call
|
||||||
|
opLT = 0x50 // Less Than
|
||||||
|
opLTEQ = 0x51 // Less Than or EQual
|
||||||
|
opGT = 0x52 // Greater Than
|
||||||
|
opGTEQ = 0x53 // Greater Than or EQual
|
||||||
|
opEQ = 0x54 // EQual
|
||||||
|
opNEQ = 0x55 // Not EQual
|
||||||
|
opODD = 0x56 // ODD
|
||||||
|
opEVEN = 0x57 // EVEN
|
||||||
|
opIF = 0x58 // IF test
|
||||||
|
opEIF = 0x59 // End IF
|
||||||
|
opAND = 0x5a // logical AND
|
||||||
|
opOR = 0x5b // logical OR
|
||||||
|
opNOT = 0x5c // logical NOT
|
||||||
|
opDELTAP1 = 0x5d // DELTA exception P1
|
||||||
|
opSDB = 0x5e // Set Delta Base in the graphics state
|
||||||
|
opSDS = 0x5f // Set Delta Shift in the graphics state
|
||||||
|
opADD = 0x60 // ADD
|
||||||
|
opSUB = 0x61 // SUBtract
|
||||||
|
opDIV = 0x62 // DIVide
|
||||||
|
opMUL = 0x63 // MULtiply
|
||||||
|
opABS = 0x64 // ABSolute value
|
||||||
|
opNEG = 0x65 // NEGate
|
||||||
|
opFLOOR = 0x66 // FLOOR
|
||||||
|
opCEILING = 0x67 // CEILING
|
||||||
|
opROUND00 = 0x68 // ROUND value
|
||||||
|
opROUND01 = 0x69 // .
|
||||||
|
opROUND10 = 0x6a // .
|
||||||
|
opROUND11 = 0x6b // .
|
||||||
|
opNROUND00 = 0x6c // No ROUNDing of value
|
||||||
|
opNROUND01 = 0x6d // .
|
||||||
|
opNROUND10 = 0x6e // .
|
||||||
|
opNROUND11 = 0x6f // .
|
||||||
|
opWCVTF = 0x70 // Write Control Value Table in Funits
|
||||||
|
opDELTAP2 = 0x71 // DELTA exception P2
|
||||||
|
opDELTAP3 = 0x72 // DELTA exception P3
|
||||||
|
opDELTAC1 = 0x73 // DELTA exception C1
|
||||||
|
opDELTAC2 = 0x74 // DELTA exception C2
|
||||||
|
opDELTAC3 = 0x75 // DELTA exception C3
|
||||||
|
opSROUND = 0x76 // Super ROUND
|
||||||
|
opS45ROUND = 0x77 // Super ROUND 45 degrees
|
||||||
|
opJROT = 0x78 // Jump Relative On True
|
||||||
|
opJROF = 0x79 // Jump Relative On False
|
||||||
|
opROFF = 0x7a // Round OFF
|
||||||
|
op_0x7b = 0x7b // deprecated
|
||||||
|
opRUTG = 0x7c // Round Up To Grid
|
||||||
|
opRDTG = 0x7d // Round Down To Grid
|
||||||
|
opSANGW = 0x7e // Set ANGle Weight
|
||||||
|
opAA = 0x7f // Adjust Angle
|
||||||
|
opFLIPPT = 0x80 // FLIP PoinT
|
||||||
|
opFLIPRGON = 0x81 // FLIP RanGe ON
|
||||||
|
opFLIPRGOFF = 0x82 // FLIP RanGe OFF
|
||||||
|
op_0x83 = 0x83 // deprecated
|
||||||
|
op_0x84 = 0x84 // deprecated
|
||||||
|
opSCANCTRL = 0x85 // SCAN conversion ConTRoL
|
||||||
|
opSDPVTL0 = 0x86 // Set Dual Projection Vector To Line
|
||||||
|
opSDPVTL1 = 0x87 // .
|
||||||
|
opGETINFO = 0x88 // GET INFOrmation
|
||||||
|
opIDEF = 0x89 // Instruction DEFinition
|
||||||
|
opROLL = 0x8a // ROLL the top three stack elements
|
||||||
|
opMAX = 0x8b // MAXimum of top two stack elements
|
||||||
|
opMIN = 0x8c // MINimum of top two stack elements
|
||||||
|
opSCANTYPE = 0x8d // SCANTYPE
|
||||||
|
opINSTCTRL = 0x8e // INSTRuction execution ConTRoL
|
||||||
|
op_0x8f = 0x8f
|
||||||
|
op_0x90 = 0x90
|
||||||
|
op_0x91 = 0x91
|
||||||
|
op_0x92 = 0x92
|
||||||
|
op_0x93 = 0x93
|
||||||
|
op_0x94 = 0x94
|
||||||
|
op_0x95 = 0x95
|
||||||
|
op_0x96 = 0x96
|
||||||
|
op_0x97 = 0x97
|
||||||
|
op_0x98 = 0x98
|
||||||
|
op_0x99 = 0x99
|
||||||
|
op_0x9a = 0x9a
|
||||||
|
op_0x9b = 0x9b
|
||||||
|
op_0x9c = 0x9c
|
||||||
|
op_0x9d = 0x9d
|
||||||
|
op_0x9e = 0x9e
|
||||||
|
op_0x9f = 0x9f
|
||||||
|
op_0xa0 = 0xa0
|
||||||
|
op_0xa1 = 0xa1
|
||||||
|
op_0xa2 = 0xa2
|
||||||
|
op_0xa3 = 0xa3
|
||||||
|
op_0xa4 = 0xa4
|
||||||
|
op_0xa5 = 0xa5
|
||||||
|
op_0xa6 = 0xa6
|
||||||
|
op_0xa7 = 0xa7
|
||||||
|
op_0xa8 = 0xa8
|
||||||
|
op_0xa9 = 0xa9
|
||||||
|
op_0xaa = 0xaa
|
||||||
|
op_0xab = 0xab
|
||||||
|
op_0xac = 0xac
|
||||||
|
op_0xad = 0xad
|
||||||
|
op_0xae = 0xae
|
||||||
|
op_0xaf = 0xaf
|
||||||
|
opPUSHB000 = 0xb0 // PUSH Bytes
|
||||||
|
opPUSHB001 = 0xb1 // .
|
||||||
|
opPUSHB010 = 0xb2 // .
|
||||||
|
opPUSHB011 = 0xb3 // .
|
||||||
|
opPUSHB100 = 0xb4 // .
|
||||||
|
opPUSHB101 = 0xb5 // .
|
||||||
|
opPUSHB110 = 0xb6 // .
|
||||||
|
opPUSHB111 = 0xb7 // .
|
||||||
|
opPUSHW000 = 0xb8 // PUSH Words
|
||||||
|
opPUSHW001 = 0xb9 // .
|
||||||
|
opPUSHW010 = 0xba // .
|
||||||
|
opPUSHW011 = 0xbb // .
|
||||||
|
opPUSHW100 = 0xbc // .
|
||||||
|
opPUSHW101 = 0xbd // .
|
||||||
|
opPUSHW110 = 0xbe // .
|
||||||
|
opPUSHW111 = 0xbf // .
|
||||||
|
opMDRP00000 = 0xc0 // Move Direct Relative Point
|
||||||
|
opMDRP00001 = 0xc1 // .
|
||||||
|
opMDRP00010 = 0xc2 // .
|
||||||
|
opMDRP00011 = 0xc3 // .
|
||||||
|
opMDRP00100 = 0xc4 // .
|
||||||
|
opMDRP00101 = 0xc5 // .
|
||||||
|
opMDRP00110 = 0xc6 // .
|
||||||
|
opMDRP00111 = 0xc7 // .
|
||||||
|
opMDRP01000 = 0xc8 // .
|
||||||
|
opMDRP01001 = 0xc9 // .
|
||||||
|
opMDRP01010 = 0xca // .
|
||||||
|
opMDRP01011 = 0xcb // .
|
||||||
|
opMDRP01100 = 0xcc // .
|
||||||
|
opMDRP01101 = 0xcd // .
|
||||||
|
opMDRP01110 = 0xce // .
|
||||||
|
opMDRP01111 = 0xcf // .
|
||||||
|
opMDRP10000 = 0xd0 // .
|
||||||
|
opMDRP10001 = 0xd1 // .
|
||||||
|
opMDRP10010 = 0xd2 // .
|
||||||
|
opMDRP10011 = 0xd3 // .
|
||||||
|
opMDRP10100 = 0xd4 // .
|
||||||
|
opMDRP10101 = 0xd5 // .
|
||||||
|
opMDRP10110 = 0xd6 // .
|
||||||
|
opMDRP10111 = 0xd7 // .
|
||||||
|
opMDRP11000 = 0xd8 // .
|
||||||
|
opMDRP11001 = 0xd9 // .
|
||||||
|
opMDRP11010 = 0xda // .
|
||||||
|
opMDRP11011 = 0xdb // .
|
||||||
|
opMDRP11100 = 0xdc // .
|
||||||
|
opMDRP11101 = 0xdd // .
|
||||||
|
opMDRP11110 = 0xde // .
|
||||||
|
opMDRP11111 = 0xdf // .
|
||||||
|
opMIRP00000 = 0xe0 // Move Indirect Relative Point
|
||||||
|
opMIRP00001 = 0xe1 // .
|
||||||
|
opMIRP00010 = 0xe2 // .
|
||||||
|
opMIRP00011 = 0xe3 // .
|
||||||
|
opMIRP00100 = 0xe4 // .
|
||||||
|
opMIRP00101 = 0xe5 // .
|
||||||
|
opMIRP00110 = 0xe6 // .
|
||||||
|
opMIRP00111 = 0xe7 // .
|
||||||
|
opMIRP01000 = 0xe8 // .
|
||||||
|
opMIRP01001 = 0xe9 // .
|
||||||
|
opMIRP01010 = 0xea // .
|
||||||
|
opMIRP01011 = 0xeb // .
|
||||||
|
opMIRP01100 = 0xec // .
|
||||||
|
opMIRP01101 = 0xed // .
|
||||||
|
opMIRP01110 = 0xee // .
|
||||||
|
opMIRP01111 = 0xef // .
|
||||||
|
opMIRP10000 = 0xf0 // .
|
||||||
|
opMIRP10001 = 0xf1 // .
|
||||||
|
opMIRP10010 = 0xf2 // .
|
||||||
|
opMIRP10011 = 0xf3 // .
|
||||||
|
opMIRP10100 = 0xf4 // .
|
||||||
|
opMIRP10101 = 0xf5 // .
|
||||||
|
opMIRP10110 = 0xf6 // .
|
||||||
|
opMIRP10111 = 0xf7 // .
|
||||||
|
opMIRP11000 = 0xf8 // .
|
||||||
|
opMIRP11001 = 0xf9 // .
|
||||||
|
opMIRP11010 = 0xfa // .
|
||||||
|
opMIRP11011 = 0xfb // .
|
||||||
|
opMIRP11100 = 0xfc // .
|
||||||
|
opMIRP11101 = 0xfd // .
|
||||||
|
opMIRP11110 = 0xfe // .
|
||||||
|
opMIRP11111 = 0xff // .
|
||||||
|
)
|
||||||
|
|
||||||
|
// popCount is the number of stack elements that each opcode pops.
|
||||||
|
var popCount = [256]uint8{
|
||||||
|
// 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f
|
||||||
|
0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 5, // 0x00 - 0x0f
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, // 0x10 - 0x1f
|
||||||
|
1, 1, 0, 2, 0, 1, 1, 2, 0, 1, 2, 1, 1, 0, 1, 1, // 0x20 - 0x2f
|
||||||
|
0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 2, 2, 0, 0, 2, 2, // 0x30 - 0x3f
|
||||||
|
0, 0, 2, 1, 2, 1, 1, 1, 2, 2, 2, 0, 0, 0, 0, 0, // 0x40 - 0x4f
|
||||||
|
2, 2, 2, 2, 2, 2, 1, 1, 1, 0, 2, 2, 1, 1, 1, 1, // 0x50 - 0x5f
|
||||||
|
2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 - 0x6f
|
||||||
|
2, 1, 1, 1, 1, 1, 1, 1, 2, 2, 0, 0, 0, 0, 1, 1, // 0x70 - 0x7f
|
||||||
|
0, 2, 2, 0, 0, 1, 2, 2, 1, 1, 3, 2, 2, 1, 2, 0, // 0x80 - 0x8f
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90 - 0x9f
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xa0 - 0xaf
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xb0 - 0xbf
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xc0 - 0xcf
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xd0 - 0xdf
|
||||||
|
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xe0 - 0xef
|
||||||
|
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xf0 - 0xff
|
||||||
|
}
|
643
vendor/github.com/golang/freetype/truetype/truetype.go
generated
vendored
Normal file
643
vendor/github.com/golang/freetype/truetype/truetype.go
generated
vendored
Normal file
|
@ -0,0 +1,643 @@
|
||||||
|
// Copyright 2010 The Freetype-Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by your choice of either the
|
||||||
|
// FreeType License or the GNU General Public License version 2 (or
|
||||||
|
// any later version), both of which can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package truetype provides a parser for the TTF and TTC file formats.
|
||||||
|
// Those formats are documented at http://developer.apple.com/fonts/TTRefMan/
|
||||||
|
// and http://www.microsoft.com/typography/otspec/
|
||||||
|
//
|
||||||
|
// Some of a font's methods provide lengths or co-ordinates, e.g. bounds, font
|
||||||
|
// metrics and control points. All these methods take a scale parameter, which
|
||||||
|
// is the number of pixels in 1 em, expressed as a 26.6 fixed point value. For
|
||||||
|
// example, if 1 em is 10 pixels then scale is fixed.I(10), which is equal to
|
||||||
|
// fixed.Int26_6(10 << 6).
|
||||||
|
//
|
||||||
|
// To measure a TrueType font in ideal FUnit space, use scale equal to
|
||||||
|
// font.FUnitsPerEm().
|
||||||
|
package truetype
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"golang.org/x/image/math/fixed"
|
||||||
|
)
|
||||||
|
|
||||||
|
// An Index is a Font's index of a rune.
|
||||||
|
type Index uint16
|
||||||
|
|
||||||
|
// A NameID identifies a name table entry.
|
||||||
|
//
|
||||||
|
// See https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6name.html
|
||||||
|
type NameID uint16
|
||||||
|
|
||||||
|
const (
|
||||||
|
NameIDCopyright NameID = 0
|
||||||
|
NameIDFontFamily = 1
|
||||||
|
NameIDFontSubfamily = 2
|
||||||
|
NameIDUniqueSubfamilyID = 3
|
||||||
|
NameIDFontFullName = 4
|
||||||
|
NameIDNameTableVersion = 5
|
||||||
|
NameIDPostscriptName = 6
|
||||||
|
NameIDTrademarkNotice = 7
|
||||||
|
NameIDManufacturerName = 8
|
||||||
|
NameIDDesignerName = 9
|
||||||
|
NameIDFontDescription = 10
|
||||||
|
NameIDFontVendorURL = 11
|
||||||
|
NameIDFontDesignerURL = 12
|
||||||
|
NameIDFontLicense = 13
|
||||||
|
NameIDFontLicenseURL = 14
|
||||||
|
NameIDPreferredFamily = 16
|
||||||
|
NameIDPreferredSubfamily = 17
|
||||||
|
NameIDCompatibleName = 18
|
||||||
|
NameIDSampleText = 19
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// A 32-bit encoding consists of a most-significant 16-bit Platform ID and a
|
||||||
|
// least-significant 16-bit Platform Specific ID. The magic numbers are
|
||||||
|
// specified at https://www.microsoft.com/typography/otspec/name.htm
|
||||||
|
unicodeEncoding = 0x00000003 // PID = 0 (Unicode), PSID = 3 (Unicode 2.0)
|
||||||
|
microsoftSymbolEncoding = 0x00030000 // PID = 3 (Microsoft), PSID = 0 (Symbol)
|
||||||
|
microsoftUCS2Encoding = 0x00030001 // PID = 3 (Microsoft), PSID = 1 (UCS-2)
|
||||||
|
microsoftUCS4Encoding = 0x0003000a // PID = 3 (Microsoft), PSID = 10 (UCS-4)
|
||||||
|
)
|
||||||
|
|
||||||
|
// An HMetric holds the horizontal metrics of a single glyph.
|
||||||
|
type HMetric struct {
|
||||||
|
AdvanceWidth, LeftSideBearing fixed.Int26_6
|
||||||
|
}
|
||||||
|
|
||||||
|
// A VMetric holds the vertical metrics of a single glyph.
|
||||||
|
type VMetric struct {
|
||||||
|
AdvanceHeight, TopSideBearing fixed.Int26_6
|
||||||
|
}
|
||||||
|
|
||||||
|
// A FormatError reports that the input is not a valid TrueType font.
|
||||||
|
type FormatError string
|
||||||
|
|
||||||
|
func (e FormatError) Error() string {
|
||||||
|
return "freetype: invalid TrueType format: " + string(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// An UnsupportedError reports that the input uses a valid but unimplemented
|
||||||
|
// TrueType feature.
|
||||||
|
type UnsupportedError string
|
||||||
|
|
||||||
|
func (e UnsupportedError) Error() string {
|
||||||
|
return "freetype: unsupported TrueType feature: " + string(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// u32 returns the big-endian uint32 at b[i:].
|
||||||
|
func u32(b []byte, i int) uint32 {
|
||||||
|
return uint32(b[i])<<24 | uint32(b[i+1])<<16 | uint32(b[i+2])<<8 | uint32(b[i+3])
|
||||||
|
}
|
||||||
|
|
||||||
|
// u16 returns the big-endian uint16 at b[i:].
|
||||||
|
func u16(b []byte, i int) uint16 {
|
||||||
|
return uint16(b[i])<<8 | uint16(b[i+1])
|
||||||
|
}
|
||||||
|
|
||||||
|
// readTable returns a slice of the TTF data given by a table's directory entry.
|
||||||
|
func readTable(ttf []byte, offsetLength []byte) ([]byte, error) {
|
||||||
|
offset := int(u32(offsetLength, 0))
|
||||||
|
if offset < 0 {
|
||||||
|
return nil, FormatError(fmt.Sprintf("offset too large: %d", uint32(offset)))
|
||||||
|
}
|
||||||
|
length := int(u32(offsetLength, 4))
|
||||||
|
if length < 0 {
|
||||||
|
return nil, FormatError(fmt.Sprintf("length too large: %d", uint32(length)))
|
||||||
|
}
|
||||||
|
end := offset + length
|
||||||
|
if end < 0 || end > len(ttf) {
|
||||||
|
return nil, FormatError(fmt.Sprintf("offset + length too large: %d", uint32(offset)+uint32(length)))
|
||||||
|
}
|
||||||
|
return ttf[offset:end], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseSubtables returns the offset and platformID of the best subtable in
|
||||||
|
// table, where best favors a Unicode cmap encoding, and failing that, a
|
||||||
|
// Microsoft cmap encoding. offset is the offset of the first subtable in
|
||||||
|
// table, and size is the size of each subtable.
|
||||||
|
//
|
||||||
|
// If pred is non-nil, then only subtables that satisfy that predicate will be
|
||||||
|
// considered.
|
||||||
|
func parseSubtables(table []byte, name string, offset, size int, pred func([]byte) bool) (
|
||||||
|
bestOffset int, bestPID uint32, retErr error) {
|
||||||
|
|
||||||
|
if len(table) < 4 {
|
||||||
|
return 0, 0, FormatError(name + " too short")
|
||||||
|
}
|
||||||
|
nSubtables := int(u16(table, 2))
|
||||||
|
if len(table) < size*nSubtables+offset {
|
||||||
|
return 0, 0, FormatError(name + " too short")
|
||||||
|
}
|
||||||
|
ok := false
|
||||||
|
for i := 0; i < nSubtables; i, offset = i+1, offset+size {
|
||||||
|
if pred != nil && !pred(table[offset:]) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// We read the 16-bit Platform ID and 16-bit Platform Specific ID as a single uint32.
|
||||||
|
// All values are big-endian.
|
||||||
|
pidPsid := u32(table, offset)
|
||||||
|
// We prefer the Unicode cmap encoding. Failing to find that, we fall
|
||||||
|
// back onto the Microsoft cmap encoding.
|
||||||
|
if pidPsid == unicodeEncoding {
|
||||||
|
bestOffset, bestPID, ok = offset, pidPsid>>16, true
|
||||||
|
break
|
||||||
|
|
||||||
|
} else if pidPsid == microsoftSymbolEncoding ||
|
||||||
|
pidPsid == microsoftUCS2Encoding ||
|
||||||
|
pidPsid == microsoftUCS4Encoding {
|
||||||
|
|
||||||
|
bestOffset, bestPID, ok = offset, pidPsid>>16, true
|
||||||
|
// We don't break out of the for loop, so that Unicode can override Microsoft.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return 0, 0, UnsupportedError(name + " encoding")
|
||||||
|
}
|
||||||
|
return bestOffset, bestPID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
locaOffsetFormatUnknown int = iota
|
||||||
|
locaOffsetFormatShort
|
||||||
|
locaOffsetFormatLong
|
||||||
|
)
|
||||||
|
|
||||||
|
// A cm holds a parsed cmap entry.
|
||||||
|
type cm struct {
|
||||||
|
start, end, delta, offset uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Font represents a Truetype font.
|
||||||
|
type Font struct {
|
||||||
|
// Tables sliced from the TTF data. The different tables are documented
|
||||||
|
// at http://developer.apple.com/fonts/TTRefMan/RM06/Chap6.html
|
||||||
|
cmap, cvt, fpgm, glyf, hdmx, head, hhea, hmtx, kern, loca, maxp, name, os2, prep, vmtx []byte
|
||||||
|
|
||||||
|
cmapIndexes []byte
|
||||||
|
|
||||||
|
// Cached values derived from the raw ttf data.
|
||||||
|
cm []cm
|
||||||
|
locaOffsetFormat int
|
||||||
|
nGlyph, nHMetric, nKern int
|
||||||
|
fUnitsPerEm int32
|
||||||
|
ascent int32 // In FUnits.
|
||||||
|
descent int32 // In FUnits; typically negative.
|
||||||
|
bounds fixed.Rectangle26_6 // In FUnits.
|
||||||
|
// Values from the maxp section.
|
||||||
|
maxTwilightPoints, maxStorage, maxFunctionDefs, maxStackElements uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Font) parseCmap() error {
|
||||||
|
const (
|
||||||
|
cmapFormat4 = 4
|
||||||
|
cmapFormat12 = 12
|
||||||
|
languageIndependent = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
offset, _, err := parseSubtables(f.cmap, "cmap", 4, 8, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
offset = int(u32(f.cmap, offset+4))
|
||||||
|
if offset <= 0 || offset > len(f.cmap) {
|
||||||
|
return FormatError("bad cmap offset")
|
||||||
|
}
|
||||||
|
|
||||||
|
cmapFormat := u16(f.cmap, offset)
|
||||||
|
switch cmapFormat {
|
||||||
|
case cmapFormat4:
|
||||||
|
language := u16(f.cmap, offset+4)
|
||||||
|
if language != languageIndependent {
|
||||||
|
return UnsupportedError(fmt.Sprintf("language: %d", language))
|
||||||
|
}
|
||||||
|
segCountX2 := int(u16(f.cmap, offset+6))
|
||||||
|
if segCountX2%2 == 1 {
|
||||||
|
return FormatError(fmt.Sprintf("bad segCountX2: %d", segCountX2))
|
||||||
|
}
|
||||||
|
segCount := segCountX2 / 2
|
||||||
|
offset += 14
|
||||||
|
f.cm = make([]cm, segCount)
|
||||||
|
for i := 0; i < segCount; i++ {
|
||||||
|
f.cm[i].end = uint32(u16(f.cmap, offset))
|
||||||
|
offset += 2
|
||||||
|
}
|
||||||
|
offset += 2
|
||||||
|
for i := 0; i < segCount; i++ {
|
||||||
|
f.cm[i].start = uint32(u16(f.cmap, offset))
|
||||||
|
offset += 2
|
||||||
|
}
|
||||||
|
for i := 0; i < segCount; i++ {
|
||||||
|
f.cm[i].delta = uint32(u16(f.cmap, offset))
|
||||||
|
offset += 2
|
||||||
|
}
|
||||||
|
for i := 0; i < segCount; i++ {
|
||||||
|
f.cm[i].offset = uint32(u16(f.cmap, offset))
|
||||||
|
offset += 2
|
||||||
|
}
|
||||||
|
f.cmapIndexes = f.cmap[offset:]
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case cmapFormat12:
|
||||||
|
if u16(f.cmap, offset+2) != 0 {
|
||||||
|
return FormatError(fmt.Sprintf("cmap format: % x", f.cmap[offset:offset+4]))
|
||||||
|
}
|
||||||
|
length := u32(f.cmap, offset+4)
|
||||||
|
language := u32(f.cmap, offset+8)
|
||||||
|
if language != languageIndependent {
|
||||||
|
return UnsupportedError(fmt.Sprintf("language: %d", language))
|
||||||
|
}
|
||||||
|
nGroups := u32(f.cmap, offset+12)
|
||||||
|
if length != 12*nGroups+16 {
|
||||||
|
return FormatError("inconsistent cmap length")
|
||||||
|
}
|
||||||
|
offset += 16
|
||||||
|
f.cm = make([]cm, nGroups)
|
||||||
|
for i := uint32(0); i < nGroups; i++ {
|
||||||
|
f.cm[i].start = u32(f.cmap, offset+0)
|
||||||
|
f.cm[i].end = u32(f.cmap, offset+4)
|
||||||
|
f.cm[i].delta = u32(f.cmap, offset+8) - f.cm[i].start
|
||||||
|
offset += 12
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return UnsupportedError(fmt.Sprintf("cmap format: %d", cmapFormat))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Font) parseHead() error {
|
||||||
|
if len(f.head) != 54 {
|
||||||
|
return FormatError(fmt.Sprintf("bad head length: %d", len(f.head)))
|
||||||
|
}
|
||||||
|
f.fUnitsPerEm = int32(u16(f.head, 18))
|
||||||
|
f.bounds.Min.X = fixed.Int26_6(int16(u16(f.head, 36)))
|
||||||
|
f.bounds.Min.Y = fixed.Int26_6(int16(u16(f.head, 38)))
|
||||||
|
f.bounds.Max.X = fixed.Int26_6(int16(u16(f.head, 40)))
|
||||||
|
f.bounds.Max.Y = fixed.Int26_6(int16(u16(f.head, 42)))
|
||||||
|
switch i := u16(f.head, 50); i {
|
||||||
|
case 0:
|
||||||
|
f.locaOffsetFormat = locaOffsetFormatShort
|
||||||
|
case 1:
|
||||||
|
f.locaOffsetFormat = locaOffsetFormatLong
|
||||||
|
default:
|
||||||
|
return FormatError(fmt.Sprintf("bad indexToLocFormat: %d", i))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Font) parseHhea() error {
|
||||||
|
if len(f.hhea) != 36 {
|
||||||
|
return FormatError(fmt.Sprintf("bad hhea length: %d", len(f.hhea)))
|
||||||
|
}
|
||||||
|
f.ascent = int32(int16(u16(f.hhea, 4)))
|
||||||
|
f.descent = int32(int16(u16(f.hhea, 6)))
|
||||||
|
f.nHMetric = int(u16(f.hhea, 34))
|
||||||
|
if 4*f.nHMetric+2*(f.nGlyph-f.nHMetric) != len(f.hmtx) {
|
||||||
|
return FormatError(fmt.Sprintf("bad hmtx length: %d", len(f.hmtx)))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Font) parseKern() error {
|
||||||
|
// Apple's TrueType documentation (http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html) says:
|
||||||
|
// "Previous versions of the 'kern' table defined both the version and nTables fields in the header
|
||||||
|
// as UInt16 values and not UInt32 values. Use of the older format on the Mac OS is discouraged
|
||||||
|
// (although AAT can sense an old kerning table and still make correct use of it). Microsoft
|
||||||
|
// Windows still uses the older format for the 'kern' table and will not recognize the newer one.
|
||||||
|
// Fonts targeted for the Mac OS only should use the new format; fonts targeted for both the Mac OS
|
||||||
|
// and Windows should use the old format."
|
||||||
|
// Since we expect that almost all fonts aim to be Windows-compatible, we only parse the "older" format,
|
||||||
|
// just like the C Freetype implementation.
|
||||||
|
if len(f.kern) == 0 {
|
||||||
|
if f.nKern != 0 {
|
||||||
|
return FormatError("bad kern table length")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if len(f.kern) < 18 {
|
||||||
|
return FormatError("kern data too short")
|
||||||
|
}
|
||||||
|
version, offset := u16(f.kern, 0), 2
|
||||||
|
if version != 0 {
|
||||||
|
return UnsupportedError(fmt.Sprintf("kern version: %d", version))
|
||||||
|
}
|
||||||
|
n, offset := u16(f.kern, offset), offset+2
|
||||||
|
if n != 1 {
|
||||||
|
return UnsupportedError(fmt.Sprintf("kern nTables: %d", n))
|
||||||
|
}
|
||||||
|
offset += 2
|
||||||
|
length, offset := int(u16(f.kern, offset)), offset+2
|
||||||
|
coverage, offset := u16(f.kern, offset), offset+2
|
||||||
|
if coverage != 0x0001 {
|
||||||
|
// We only support horizontal kerning.
|
||||||
|
return UnsupportedError(fmt.Sprintf("kern coverage: 0x%04x", coverage))
|
||||||
|
}
|
||||||
|
f.nKern, offset = int(u16(f.kern, offset)), offset+2
|
||||||
|
if 6*f.nKern != length-14 {
|
||||||
|
return FormatError("bad kern table length")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Font) parseMaxp() error {
|
||||||
|
if len(f.maxp) != 32 {
|
||||||
|
return FormatError(fmt.Sprintf("bad maxp length: %d", len(f.maxp)))
|
||||||
|
}
|
||||||
|
f.nGlyph = int(u16(f.maxp, 4))
|
||||||
|
f.maxTwilightPoints = u16(f.maxp, 16)
|
||||||
|
f.maxStorage = u16(f.maxp, 18)
|
||||||
|
f.maxFunctionDefs = u16(f.maxp, 20)
|
||||||
|
f.maxStackElements = u16(f.maxp, 24)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// scale returns x divided by f.fUnitsPerEm, rounded to the nearest integer.
|
||||||
|
func (f *Font) scale(x fixed.Int26_6) fixed.Int26_6 {
|
||||||
|
if x >= 0 {
|
||||||
|
x += fixed.Int26_6(f.fUnitsPerEm) / 2
|
||||||
|
} else {
|
||||||
|
x -= fixed.Int26_6(f.fUnitsPerEm) / 2
|
||||||
|
}
|
||||||
|
return x / fixed.Int26_6(f.fUnitsPerEm)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bounds returns the union of a Font's glyphs' bounds.
|
||||||
|
func (f *Font) Bounds(scale fixed.Int26_6) fixed.Rectangle26_6 {
|
||||||
|
b := f.bounds
|
||||||
|
b.Min.X = f.scale(scale * b.Min.X)
|
||||||
|
b.Min.Y = f.scale(scale * b.Min.Y)
|
||||||
|
b.Max.X = f.scale(scale * b.Max.X)
|
||||||
|
b.Max.Y = f.scale(scale * b.Max.Y)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUnitsPerEm returns the number of FUnits in a Font's em-square's side.
|
||||||
|
func (f *Font) FUnitsPerEm() int32 {
|
||||||
|
return f.fUnitsPerEm
|
||||||
|
}
|
||||||
|
|
||||||
|
// Index returns a Font's index for the given rune.
|
||||||
|
func (f *Font) Index(x rune) Index {
|
||||||
|
c := uint32(x)
|
||||||
|
for i, j := 0, len(f.cm); i < j; {
|
||||||
|
h := i + (j-i)/2
|
||||||
|
cm := &f.cm[h]
|
||||||
|
if c < cm.start {
|
||||||
|
j = h
|
||||||
|
} else if cm.end < c {
|
||||||
|
i = h + 1
|
||||||
|
} else if cm.offset == 0 {
|
||||||
|
return Index(c + cm.delta)
|
||||||
|
} else {
|
||||||
|
offset := int(cm.offset) + 2*(h-len(f.cm)+int(c-cm.start))
|
||||||
|
return Index(u16(f.cmapIndexes, offset))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the Font's name value for the given NameID. It returns "" if
|
||||||
|
// there was an error, or if that name was not found.
|
||||||
|
func (f *Font) Name(id NameID) string {
|
||||||
|
x, platformID, err := parseSubtables(f.name, "name", 6, 12, func(b []byte) bool {
|
||||||
|
return NameID(u16(b, 6)) == id
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
offset, length := u16(f.name, 4)+u16(f.name, x+10), u16(f.name, x+8)
|
||||||
|
// Return the ASCII value of the encoded string.
|
||||||
|
// The string is encoded as UTF-16 on non-Apple platformIDs; Apple is platformID 1.
|
||||||
|
src := f.name[offset : offset+length]
|
||||||
|
var dst []byte
|
||||||
|
if platformID != 1 { // UTF-16.
|
||||||
|
if len(src)&1 != 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
dst = make([]byte, len(src)/2)
|
||||||
|
for i := range dst {
|
||||||
|
dst[i] = printable(u16(src, 2*i))
|
||||||
|
}
|
||||||
|
} else { // ASCII.
|
||||||
|
dst = make([]byte, len(src))
|
||||||
|
for i, c := range src {
|
||||||
|
dst[i] = printable(uint16(c))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string(dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
func printable(r uint16) byte {
|
||||||
|
if 0x20 <= r && r < 0x7f {
|
||||||
|
return byte(r)
|
||||||
|
}
|
||||||
|
return '?'
|
||||||
|
}
|
||||||
|
|
||||||
|
// unscaledHMetric returns the unscaled horizontal metrics for the glyph with
|
||||||
|
// the given index.
|
||||||
|
func (f *Font) unscaledHMetric(i Index) (h HMetric) {
|
||||||
|
j := int(i)
|
||||||
|
if j < 0 || f.nGlyph <= j {
|
||||||
|
return HMetric{}
|
||||||
|
}
|
||||||
|
if j >= f.nHMetric {
|
||||||
|
p := 4 * (f.nHMetric - 1)
|
||||||
|
return HMetric{
|
||||||
|
AdvanceWidth: fixed.Int26_6(u16(f.hmtx, p)),
|
||||||
|
LeftSideBearing: fixed.Int26_6(int16(u16(f.hmtx, p+2*(j-f.nHMetric)+4))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return HMetric{
|
||||||
|
AdvanceWidth: fixed.Int26_6(u16(f.hmtx, 4*j)),
|
||||||
|
LeftSideBearing: fixed.Int26_6(int16(u16(f.hmtx, 4*j+2))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HMetric returns the horizontal metrics for the glyph with the given index.
|
||||||
|
func (f *Font) HMetric(scale fixed.Int26_6, i Index) HMetric {
|
||||||
|
h := f.unscaledHMetric(i)
|
||||||
|
h.AdvanceWidth = f.scale(scale * h.AdvanceWidth)
|
||||||
|
h.LeftSideBearing = f.scale(scale * h.LeftSideBearing)
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
// unscaledVMetric returns the unscaled vertical metrics for the glyph with
|
||||||
|
// the given index. yMax is the top of the glyph's bounding box.
|
||||||
|
func (f *Font) unscaledVMetric(i Index, yMax fixed.Int26_6) (v VMetric) {
|
||||||
|
j := int(i)
|
||||||
|
if j < 0 || f.nGlyph <= j {
|
||||||
|
return VMetric{}
|
||||||
|
}
|
||||||
|
if 4*j+4 <= len(f.vmtx) {
|
||||||
|
return VMetric{
|
||||||
|
AdvanceHeight: fixed.Int26_6(u16(f.vmtx, 4*j)),
|
||||||
|
TopSideBearing: fixed.Int26_6(int16(u16(f.vmtx, 4*j+2))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The OS/2 table has grown over time.
|
||||||
|
// https://developer.apple.com/fonts/TTRefMan/RM06/Chap6OS2.html
|
||||||
|
// says that it was originally 68 bytes. Optional fields, including
|
||||||
|
// the ascender and descender, are described at
|
||||||
|
// http://www.microsoft.com/typography/otspec/os2.htm
|
||||||
|
if len(f.os2) >= 72 {
|
||||||
|
sTypoAscender := fixed.Int26_6(int16(u16(f.os2, 68)))
|
||||||
|
sTypoDescender := fixed.Int26_6(int16(u16(f.os2, 70)))
|
||||||
|
return VMetric{
|
||||||
|
AdvanceHeight: sTypoAscender - sTypoDescender,
|
||||||
|
TopSideBearing: sTypoAscender - yMax,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return VMetric{
|
||||||
|
AdvanceHeight: fixed.Int26_6(f.fUnitsPerEm),
|
||||||
|
TopSideBearing: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// VMetric returns the vertical metrics for the glyph with the given index.
|
||||||
|
func (f *Font) VMetric(scale fixed.Int26_6, i Index) VMetric {
|
||||||
|
// TODO: should 0 be bounds.YMax?
|
||||||
|
v := f.unscaledVMetric(i, 0)
|
||||||
|
v.AdvanceHeight = f.scale(scale * v.AdvanceHeight)
|
||||||
|
v.TopSideBearing = f.scale(scale * v.TopSideBearing)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kern returns the horizontal adjustment for the given glyph pair. A positive
|
||||||
|
// kern means to move the glyphs further apart.
|
||||||
|
func (f *Font) Kern(scale fixed.Int26_6, i0, i1 Index) fixed.Int26_6 {
|
||||||
|
if f.nKern == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
g := uint32(i0)<<16 | uint32(i1)
|
||||||
|
lo, hi := 0, f.nKern
|
||||||
|
for lo < hi {
|
||||||
|
i := (lo + hi) / 2
|
||||||
|
ig := u32(f.kern, 18+6*i)
|
||||||
|
if ig < g {
|
||||||
|
lo = i + 1
|
||||||
|
} else if ig > g {
|
||||||
|
hi = i
|
||||||
|
} else {
|
||||||
|
return f.scale(scale * fixed.Int26_6(int16(u16(f.kern, 22+6*i))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse returns a new Font for the given TTF or TTC data.
|
||||||
|
//
|
||||||
|
// For TrueType Collections, the first font in the collection is parsed.
|
||||||
|
func Parse(ttf []byte) (font *Font, err error) {
|
||||||
|
return parse(ttf, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse(ttf []byte, offset int) (font *Font, err error) {
|
||||||
|
if len(ttf)-offset < 12 {
|
||||||
|
err = FormatError("TTF data is too short")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
originalOffset := offset
|
||||||
|
magic, offset := u32(ttf, offset), offset+4
|
||||||
|
switch magic {
|
||||||
|
case 0x00010000:
|
||||||
|
// No-op.
|
||||||
|
case 0x74746366: // "ttcf" as a big-endian uint32.
|
||||||
|
if originalOffset != 0 {
|
||||||
|
err = FormatError("recursive TTC")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ttcVersion, offset := u32(ttf, offset), offset+4
|
||||||
|
if ttcVersion != 0x00010000 {
|
||||||
|
// TODO: support TTC version 2.0, once I have such a .ttc file to test with.
|
||||||
|
err = FormatError("bad TTC version")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
numFonts, offset := int(u32(ttf, offset)), offset+4
|
||||||
|
if numFonts <= 0 {
|
||||||
|
err = FormatError("bad number of TTC fonts")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(ttf[offset:])/4 < numFonts {
|
||||||
|
err = FormatError("TTC offset table is too short")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// TODO: provide an API to select which font in a TrueType collection to return,
|
||||||
|
// not just the first one. This may require an API to parse a TTC's name tables,
|
||||||
|
// so users of this package can select the font in a TTC by name.
|
||||||
|
offset = int(u32(ttf, offset))
|
||||||
|
if offset <= 0 || offset > len(ttf) {
|
||||||
|
err = FormatError("bad TTC offset")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return parse(ttf, offset)
|
||||||
|
default:
|
||||||
|
err = FormatError("bad TTF version")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n, offset := int(u16(ttf, offset)), offset+2
|
||||||
|
if len(ttf) < 16*n+12 {
|
||||||
|
err = FormatError("TTF data is too short")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
f := new(Font)
|
||||||
|
// Assign the table slices.
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
x := 16*i + 12
|
||||||
|
switch string(ttf[x : x+4]) {
|
||||||
|
case "cmap":
|
||||||
|
f.cmap, err = readTable(ttf, ttf[x+8:x+16])
|
||||||
|
case "cvt ":
|
||||||
|
f.cvt, err = readTable(ttf, ttf[x+8:x+16])
|
||||||
|
case "fpgm":
|
||||||
|
f.fpgm, err = readTable(ttf, ttf[x+8:x+16])
|
||||||
|
case "glyf":
|
||||||
|
f.glyf, err = readTable(ttf, ttf[x+8:x+16])
|
||||||
|
case "hdmx":
|
||||||
|
f.hdmx, err = readTable(ttf, ttf[x+8:x+16])
|
||||||
|
case "head":
|
||||||
|
f.head, err = readTable(ttf, ttf[x+8:x+16])
|
||||||
|
case "hhea":
|
||||||
|
f.hhea, err = readTable(ttf, ttf[x+8:x+16])
|
||||||
|
case "hmtx":
|
||||||
|
f.hmtx, err = readTable(ttf, ttf[x+8:x+16])
|
||||||
|
case "kern":
|
||||||
|
f.kern, err = readTable(ttf, ttf[x+8:x+16])
|
||||||
|
case "loca":
|
||||||
|
f.loca, err = readTable(ttf, ttf[x+8:x+16])
|
||||||
|
case "maxp":
|
||||||
|
f.maxp, err = readTable(ttf, ttf[x+8:x+16])
|
||||||
|
case "name":
|
||||||
|
f.name, err = readTable(ttf, ttf[x+8:x+16])
|
||||||
|
case "OS/2":
|
||||||
|
f.os2, err = readTable(ttf, ttf[x+8:x+16])
|
||||||
|
case "prep":
|
||||||
|
f.prep, err = readTable(ttf, ttf[x+8:x+16])
|
||||||
|
case "vmtx":
|
||||||
|
f.vmtx, err = readTable(ttf, ttf[x+8:x+16])
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Parse and sanity-check the TTF data.
|
||||||
|
if err = f.parseHead(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = f.parseMaxp(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = f.parseCmap(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = f.parseKern(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = f.parseHhea(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
font = f
|
||||||
|
return
|
||||||
|
}
|
8
vendor/github.com/gorilla/context/.travis.yml
generated
vendored
Normal file
8
vendor/github.com/gorilla/context/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
language: go
|
||||||
|
sudo: false
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.3
|
||||||
|
- 1.4
|
||||||
|
- 1.5
|
||||||
|
- tip
|
27
vendor/github.com/gorilla/context/LICENSE
generated
vendored
Normal file
27
vendor/github.com/gorilla/context/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
7
vendor/github.com/gorilla/context/README.md
generated
vendored
Normal file
7
vendor/github.com/gorilla/context/README.md
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
context
|
||||||
|
=======
|
||||||
|
[![Build Status](https://travis-ci.org/gorilla/context.png?branch=master)](https://travis-ci.org/gorilla/context)
|
||||||
|
|
||||||
|
gorilla/context is a general purpose registry for global request variables.
|
||||||
|
|
||||||
|
Read the full documentation here: http://www.gorillatoolkit.org/pkg/context
|
143
vendor/github.com/gorilla/context/context.go
generated
vendored
Normal file
143
vendor/github.com/gorilla/context/context.go
generated
vendored
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
// Copyright 2012 The Gorilla 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 context
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
mutex sync.RWMutex
|
||||||
|
data = make(map[*http.Request]map[interface{}]interface{})
|
||||||
|
datat = make(map[*http.Request]int64)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Set stores a value for a given key in a given request.
|
||||||
|
func Set(r *http.Request, key, val interface{}) {
|
||||||
|
mutex.Lock()
|
||||||
|
if data[r] == nil {
|
||||||
|
data[r] = make(map[interface{}]interface{})
|
||||||
|
datat[r] = time.Now().Unix()
|
||||||
|
}
|
||||||
|
data[r][key] = val
|
||||||
|
mutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns a value stored for a given key in a given request.
|
||||||
|
func Get(r *http.Request, key interface{}) interface{} {
|
||||||
|
mutex.RLock()
|
||||||
|
if ctx := data[r]; ctx != nil {
|
||||||
|
value := ctx[key]
|
||||||
|
mutex.RUnlock()
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
mutex.RUnlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOk returns stored value and presence state like multi-value return of map access.
|
||||||
|
func GetOk(r *http.Request, key interface{}) (interface{}, bool) {
|
||||||
|
mutex.RLock()
|
||||||
|
if _, ok := data[r]; ok {
|
||||||
|
value, ok := data[r][key]
|
||||||
|
mutex.RUnlock()
|
||||||
|
return value, ok
|
||||||
|
}
|
||||||
|
mutex.RUnlock()
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAll returns all stored values for the request as a map. Nil is returned for invalid requests.
|
||||||
|
func GetAll(r *http.Request) map[interface{}]interface{} {
|
||||||
|
mutex.RLock()
|
||||||
|
if context, ok := data[r]; ok {
|
||||||
|
result := make(map[interface{}]interface{}, len(context))
|
||||||
|
for k, v := range context {
|
||||||
|
result[k] = v
|
||||||
|
}
|
||||||
|
mutex.RUnlock()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
mutex.RUnlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllOk returns all stored values for the request as a map and a boolean value that indicates if
|
||||||
|
// the request was registered.
|
||||||
|
func GetAllOk(r *http.Request) (map[interface{}]interface{}, bool) {
|
||||||
|
mutex.RLock()
|
||||||
|
context, ok := data[r]
|
||||||
|
result := make(map[interface{}]interface{}, len(context))
|
||||||
|
for k, v := range context {
|
||||||
|
result[k] = v
|
||||||
|
}
|
||||||
|
mutex.RUnlock()
|
||||||
|
return result, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete removes a value stored for a given key in a given request.
|
||||||
|
func Delete(r *http.Request, key interface{}) {
|
||||||
|
mutex.Lock()
|
||||||
|
if data[r] != nil {
|
||||||
|
delete(data[r], key)
|
||||||
|
}
|
||||||
|
mutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear removes all values stored for a given request.
|
||||||
|
//
|
||||||
|
// This is usually called by a handler wrapper to clean up request
|
||||||
|
// variables at the end of a request lifetime. See ClearHandler().
|
||||||
|
func Clear(r *http.Request) {
|
||||||
|
mutex.Lock()
|
||||||
|
clear(r)
|
||||||
|
mutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear is Clear without the lock.
|
||||||
|
func clear(r *http.Request) {
|
||||||
|
delete(data, r)
|
||||||
|
delete(datat, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Purge removes request data stored for longer than maxAge, in seconds.
|
||||||
|
// It returns the amount of requests removed.
|
||||||
|
//
|
||||||
|
// If maxAge <= 0, all request data is removed.
|
||||||
|
//
|
||||||
|
// This is only used for sanity check: in case context cleaning was not
|
||||||
|
// properly set some request data can be kept forever, consuming an increasing
|
||||||
|
// amount of memory. In case this is detected, Purge() must be called
|
||||||
|
// periodically until the problem is fixed.
|
||||||
|
func Purge(maxAge int) int {
|
||||||
|
mutex.Lock()
|
||||||
|
count := 0
|
||||||
|
if maxAge <= 0 {
|
||||||
|
count = len(data)
|
||||||
|
data = make(map[*http.Request]map[interface{}]interface{})
|
||||||
|
datat = make(map[*http.Request]int64)
|
||||||
|
} else {
|
||||||
|
min := time.Now().Unix() - int64(maxAge)
|
||||||
|
for r := range data {
|
||||||
|
if datat[r] < min {
|
||||||
|
clear(r)
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mutex.Unlock()
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearHandler wraps an http.Handler and clears request values at the end
|
||||||
|
// of a request lifetime.
|
||||||
|
func ClearHandler(h http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
defer Clear(r)
|
||||||
|
h.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
82
vendor/github.com/gorilla/context/doc.go
generated
vendored
Normal file
82
vendor/github.com/gorilla/context/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
// Copyright 2012 The Gorilla 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 context stores values shared during a request lifetime.
|
||||||
|
|
||||||
|
For example, a router can set variables extracted from the URL and later
|
||||||
|
application handlers can access those values, or it can be used to store
|
||||||
|
sessions values to be saved at the end of a request. There are several
|
||||||
|
others common uses.
|
||||||
|
|
||||||
|
The idea was posted by Brad Fitzpatrick to the go-nuts mailing list:
|
||||||
|
|
||||||
|
http://groups.google.com/group/golang-nuts/msg/e2d679d303aa5d53
|
||||||
|
|
||||||
|
Here's the basic usage: first define the keys that you will need. The key
|
||||||
|
type is interface{} so a key can be of any type that supports equality.
|
||||||
|
Here we define a key using a custom int type to avoid name collisions:
|
||||||
|
|
||||||
|
package foo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gorilla/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type key int
|
||||||
|
|
||||||
|
const MyKey key = 0
|
||||||
|
|
||||||
|
Then set a variable. Variables are bound to an http.Request object, so you
|
||||||
|
need a request instance to set a value:
|
||||||
|
|
||||||
|
context.Set(r, MyKey, "bar")
|
||||||
|
|
||||||
|
The application can later access the variable using the same key you provided:
|
||||||
|
|
||||||
|
func MyHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// val is "bar".
|
||||||
|
val := context.Get(r, foo.MyKey)
|
||||||
|
|
||||||
|
// returns ("bar", true)
|
||||||
|
val, ok := context.GetOk(r, foo.MyKey)
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
And that's all about the basic usage. We discuss some other ideas below.
|
||||||
|
|
||||||
|
Any type can be stored in the context. To enforce a given type, make the key
|
||||||
|
private and wrap Get() and Set() to accept and return values of a specific
|
||||||
|
type:
|
||||||
|
|
||||||
|
type key int
|
||||||
|
|
||||||
|
const mykey key = 0
|
||||||
|
|
||||||
|
// GetMyKey returns a value for this package from the request values.
|
||||||
|
func GetMyKey(r *http.Request) SomeType {
|
||||||
|
if rv := context.Get(r, mykey); rv != nil {
|
||||||
|
return rv.(SomeType)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMyKey sets a value for this package in the request values.
|
||||||
|
func SetMyKey(r *http.Request, val SomeType) {
|
||||||
|
context.Set(r, mykey, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
Variables must be cleared at the end of a request, to remove all values
|
||||||
|
that were stored. This can be done in an http.Handler, after a request was
|
||||||
|
served. Just call Clear() passing the request:
|
||||||
|
|
||||||
|
context.Clear(r)
|
||||||
|
|
||||||
|
...or use ClearHandler(), which conveniently wraps an http.Handler to clear
|
||||||
|
variables at the end of a request lifetime.
|
||||||
|
|
||||||
|
The Routers from the packages gorilla/mux and gorilla/pat call Clear()
|
||||||
|
so if you are using either of them you don't need to clear the context manually.
|
||||||
|
*/
|
||||||
|
package context
|
8
vendor/github.com/gorilla/mux/.travis.yml
generated
vendored
Normal file
8
vendor/github.com/gorilla/mux/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
language: go
|
||||||
|
sudo: false
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.3
|
||||||
|
- 1.4
|
||||||
|
- 1.5
|
||||||
|
- tip
|
27
vendor/github.com/gorilla/mux/LICENSE
generated
vendored
Normal file
27
vendor/github.com/gorilla/mux/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
235
vendor/github.com/gorilla/mux/README.md
generated
vendored
Normal file
235
vendor/github.com/gorilla/mux/README.md
generated
vendored
Normal file
|
@ -0,0 +1,235 @@
|
||||||
|
mux
|
||||||
|
===
|
||||||
|
[![GoDoc](https://godoc.org/github.com/gorilla/mux?status.svg)](https://godoc.org/github.com/gorilla/mux)
|
||||||
|
[![Build Status](https://travis-ci.org/gorilla/mux.png?branch=master)](https://travis-ci.org/gorilla/mux)
|
||||||
|
|
||||||
|
Package gorilla/mux implements a request router and dispatcher.
|
||||||
|
|
||||||
|
The name mux stands for "HTTP request multiplexer". Like the standard
|
||||||
|
http.ServeMux, mux.Router matches incoming requests against a list of
|
||||||
|
registered routes and calls a handler for the route that matches the URL
|
||||||
|
or other conditions. The main features are:
|
||||||
|
|
||||||
|
* Requests can be matched based on URL host, path, path prefix, schemes,
|
||||||
|
header and query values, HTTP methods or using custom matchers.
|
||||||
|
* URL hosts and paths can have variables with an optional regular
|
||||||
|
expression.
|
||||||
|
* Registered URLs can be built, or "reversed", which helps maintaining
|
||||||
|
references to resources.
|
||||||
|
* Routes can be used as subrouters: nested routes are only tested if the
|
||||||
|
parent route matches. This is useful to define groups of routes that
|
||||||
|
share common conditions like a host, a path prefix or other repeated
|
||||||
|
attributes. As a bonus, this optimizes request matching.
|
||||||
|
* It implements the http.Handler interface so it is compatible with the
|
||||||
|
standard http.ServeMux.
|
||||||
|
|
||||||
|
Let's start registering a couple of URL paths and handlers:
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
r := mux.NewRouter()
|
||||||
|
r.HandleFunc("/", HomeHandler)
|
||||||
|
r.HandleFunc("/products", ProductsHandler)
|
||||||
|
r.HandleFunc("/articles", ArticlesHandler)
|
||||||
|
http.Handle("/", r)
|
||||||
|
}
|
||||||
|
|
||||||
|
Here we register three routes mapping URL paths to handlers. This is
|
||||||
|
equivalent to how http.HandleFunc() works: if an incoming request URL matches
|
||||||
|
one of the paths, the corresponding handler is called passing
|
||||||
|
(http.ResponseWriter, *http.Request) as parameters.
|
||||||
|
|
||||||
|
Paths can have variables. They are defined using the format {name} or
|
||||||
|
{name:pattern}. If a regular expression pattern is not defined, the matched
|
||||||
|
variable will be anything until the next slash. For example:
|
||||||
|
|
||||||
|
r := mux.NewRouter()
|
||||||
|
r.HandleFunc("/products/{key}", ProductHandler)
|
||||||
|
r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler)
|
||||||
|
r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)
|
||||||
|
|
||||||
|
The names are used to create a map of route variables which can be retrieved
|
||||||
|
calling mux.Vars():
|
||||||
|
|
||||||
|
vars := mux.Vars(request)
|
||||||
|
category := vars["category"]
|
||||||
|
|
||||||
|
And this is all you need to know about the basic usage. More advanced options
|
||||||
|
are explained below.
|
||||||
|
|
||||||
|
Routes can also be restricted to a domain or subdomain. Just define a host
|
||||||
|
pattern to be matched. They can also have variables:
|
||||||
|
|
||||||
|
r := mux.NewRouter()
|
||||||
|
// Only matches if domain is "www.example.com".
|
||||||
|
r.Host("www.example.com")
|
||||||
|
// Matches a dynamic subdomain.
|
||||||
|
r.Host("{subdomain:[a-z]+}.domain.com")
|
||||||
|
|
||||||
|
There are several other matchers that can be added. To match path prefixes:
|
||||||
|
|
||||||
|
r.PathPrefix("/products/")
|
||||||
|
|
||||||
|
...or HTTP methods:
|
||||||
|
|
||||||
|
r.Methods("GET", "POST")
|
||||||
|
|
||||||
|
...or URL schemes:
|
||||||
|
|
||||||
|
r.Schemes("https")
|
||||||
|
|
||||||
|
...or header values:
|
||||||
|
|
||||||
|
r.Headers("X-Requested-With", "XMLHttpRequest")
|
||||||
|
|
||||||
|
...or query values:
|
||||||
|
|
||||||
|
r.Queries("key", "value")
|
||||||
|
|
||||||
|
...or to use a custom matcher function:
|
||||||
|
|
||||||
|
r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool {
|
||||||
|
return r.ProtoMajor == 0
|
||||||
|
})
|
||||||
|
|
||||||
|
...and finally, it is possible to combine several matchers in a single route:
|
||||||
|
|
||||||
|
r.HandleFunc("/products", ProductsHandler).
|
||||||
|
Host("www.example.com").
|
||||||
|
Methods("GET").
|
||||||
|
Schemes("http")
|
||||||
|
|
||||||
|
Setting the same matching conditions again and again can be boring, so we have
|
||||||
|
a way to group several routes that share the same requirements.
|
||||||
|
We call it "subrouting".
|
||||||
|
|
||||||
|
For example, let's say we have several URLs that should only match when the
|
||||||
|
host is `www.example.com`. Create a route for that host and get a "subrouter"
|
||||||
|
from it:
|
||||||
|
|
||||||
|
r := mux.NewRouter()
|
||||||
|
s := r.Host("www.example.com").Subrouter()
|
||||||
|
|
||||||
|
Then register routes in the subrouter:
|
||||||
|
|
||||||
|
s.HandleFunc("/products/", ProductsHandler)
|
||||||
|
s.HandleFunc("/products/{key}", ProductHandler)
|
||||||
|
s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
|
||||||
|
|
||||||
|
The three URL paths we registered above will only be tested if the domain is
|
||||||
|
`www.example.com`, because the subrouter is tested first. This is not
|
||||||
|
only convenient, but also optimizes request matching. You can create
|
||||||
|
subrouters combining any attribute matchers accepted by a route.
|
||||||
|
|
||||||
|
Subrouters can be used to create domain or path "namespaces": you define
|
||||||
|
subrouters in a central place and then parts of the app can register its
|
||||||
|
paths relatively to a given subrouter.
|
||||||
|
|
||||||
|
There's one more thing about subroutes. When a subrouter has a path prefix,
|
||||||
|
the inner routes use it as base for their paths:
|
||||||
|
|
||||||
|
r := mux.NewRouter()
|
||||||
|
s := r.PathPrefix("/products").Subrouter()
|
||||||
|
// "/products/"
|
||||||
|
s.HandleFunc("/", ProductsHandler)
|
||||||
|
// "/products/{key}/"
|
||||||
|
s.HandleFunc("/{key}/", ProductHandler)
|
||||||
|
// "/products/{key}/details"
|
||||||
|
s.HandleFunc("/{key}/details", ProductDetailsHandler)
|
||||||
|
|
||||||
|
Now let's see how to build registered URLs.
|
||||||
|
|
||||||
|
Routes can be named. All routes that define a name can have their URLs built,
|
||||||
|
or "reversed". We define a name calling Name() on a route. For example:
|
||||||
|
|
||||||
|
r := mux.NewRouter()
|
||||||
|
r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
|
||||||
|
Name("article")
|
||||||
|
|
||||||
|
To build a URL, get the route and call the URL() method, passing a sequence of
|
||||||
|
key/value pairs for the route variables. For the previous route, we would do:
|
||||||
|
|
||||||
|
url, err := r.Get("article").URL("category", "technology", "id", "42")
|
||||||
|
|
||||||
|
...and the result will be a url.URL with the following path:
|
||||||
|
|
||||||
|
"/articles/technology/42"
|
||||||
|
|
||||||
|
This also works for host variables:
|
||||||
|
|
||||||
|
r := mux.NewRouter()
|
||||||
|
r.Host("{subdomain}.domain.com").
|
||||||
|
Path("/articles/{category}/{id:[0-9]+}").
|
||||||
|
HandlerFunc(ArticleHandler).
|
||||||
|
Name("article")
|
||||||
|
|
||||||
|
// url.String() will be "http://news.domain.com/articles/technology/42"
|
||||||
|
url, err := r.Get("article").URL("subdomain", "news",
|
||||||
|
"category", "technology",
|
||||||
|
"id", "42")
|
||||||
|
|
||||||
|
All variables defined in the route are required, and their values must
|
||||||
|
conform to the corresponding patterns. These requirements guarantee that a
|
||||||
|
generated URL will always match a registered route -- the only exception is
|
||||||
|
for explicitly defined "build-only" routes which never match.
|
||||||
|
|
||||||
|
Regex support also exists for matching Headers within a route. For example, we could do:
|
||||||
|
|
||||||
|
r.HeadersRegexp("Content-Type", "application/(text|json)")
|
||||||
|
|
||||||
|
...and the route will match both requests with a Content-Type of `application/json` as well as
|
||||||
|
`application/text`
|
||||||
|
|
||||||
|
There's also a way to build only the URL host or path for a route:
|
||||||
|
use the methods URLHost() or URLPath() instead. For the previous route,
|
||||||
|
we would do:
|
||||||
|
|
||||||
|
// "http://news.domain.com/"
|
||||||
|
host, err := r.Get("article").URLHost("subdomain", "news")
|
||||||
|
|
||||||
|
// "/articles/technology/42"
|
||||||
|
path, err := r.Get("article").URLPath("category", "technology", "id", "42")
|
||||||
|
|
||||||
|
And if you use subrouters, host and path defined separately can be built
|
||||||
|
as well:
|
||||||
|
|
||||||
|
r := mux.NewRouter()
|
||||||
|
s := r.Host("{subdomain}.domain.com").Subrouter()
|
||||||
|
s.Path("/articles/{category}/{id:[0-9]+}").
|
||||||
|
HandlerFunc(ArticleHandler).
|
||||||
|
Name("article")
|
||||||
|
|
||||||
|
// "http://news.domain.com/articles/technology/42"
|
||||||
|
url, err := r.Get("article").URL("subdomain", "news",
|
||||||
|
"category", "technology",
|
||||||
|
"id", "42")
|
||||||
|
|
||||||
|
## Full Example
|
||||||
|
|
||||||
|
Here's a complete, runnable example of a small mux based server:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
)
|
||||||
|
|
||||||
|
func YourHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Write([]byte("Gorilla!\n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
r := mux.NewRouter()
|
||||||
|
// Routes consist of a path and a handler function.
|
||||||
|
r.HandleFunc("/", YourHandler)
|
||||||
|
|
||||||
|
// Bind to a port and pass our router in
|
||||||
|
http.ListenAndServe(":8000", r)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
BSD licensed. See the LICENSE file for details.
|
206
vendor/github.com/gorilla/mux/doc.go
generated
vendored
Normal file
206
vendor/github.com/gorilla/mux/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,206 @@
|
||||||
|
// Copyright 2012 The Gorilla 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 gorilla/mux implements a request router and dispatcher.
|
||||||
|
|
||||||
|
The name mux stands for "HTTP request multiplexer". Like the standard
|
||||||
|
http.ServeMux, mux.Router matches incoming requests against a list of
|
||||||
|
registered routes and calls a handler for the route that matches the URL
|
||||||
|
or other conditions. The main features are:
|
||||||
|
|
||||||
|
* Requests can be matched based on URL host, path, path prefix, schemes,
|
||||||
|
header and query values, HTTP methods or using custom matchers.
|
||||||
|
* URL hosts and paths can have variables with an optional regular
|
||||||
|
expression.
|
||||||
|
* Registered URLs can be built, or "reversed", which helps maintaining
|
||||||
|
references to resources.
|
||||||
|
* Routes can be used as subrouters: nested routes are only tested if the
|
||||||
|
parent route matches. This is useful to define groups of routes that
|
||||||
|
share common conditions like a host, a path prefix or other repeated
|
||||||
|
attributes. As a bonus, this optimizes request matching.
|
||||||
|
* It implements the http.Handler interface so it is compatible with the
|
||||||
|
standard http.ServeMux.
|
||||||
|
|
||||||
|
Let's start registering a couple of URL paths and handlers:
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
r := mux.NewRouter()
|
||||||
|
r.HandleFunc("/", HomeHandler)
|
||||||
|
r.HandleFunc("/products", ProductsHandler)
|
||||||
|
r.HandleFunc("/articles", ArticlesHandler)
|
||||||
|
http.Handle("/", r)
|
||||||
|
}
|
||||||
|
|
||||||
|
Here we register three routes mapping URL paths to handlers. This is
|
||||||
|
equivalent to how http.HandleFunc() works: if an incoming request URL matches
|
||||||
|
one of the paths, the corresponding handler is called passing
|
||||||
|
(http.ResponseWriter, *http.Request) as parameters.
|
||||||
|
|
||||||
|
Paths can have variables. They are defined using the format {name} or
|
||||||
|
{name:pattern}. If a regular expression pattern is not defined, the matched
|
||||||
|
variable will be anything until the next slash. For example:
|
||||||
|
|
||||||
|
r := mux.NewRouter()
|
||||||
|
r.HandleFunc("/products/{key}", ProductHandler)
|
||||||
|
r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler)
|
||||||
|
r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)
|
||||||
|
|
||||||
|
The names are used to create a map of route variables which can be retrieved
|
||||||
|
calling mux.Vars():
|
||||||
|
|
||||||
|
vars := mux.Vars(request)
|
||||||
|
category := vars["category"]
|
||||||
|
|
||||||
|
And this is all you need to know about the basic usage. More advanced options
|
||||||
|
are explained below.
|
||||||
|
|
||||||
|
Routes can also be restricted to a domain or subdomain. Just define a host
|
||||||
|
pattern to be matched. They can also have variables:
|
||||||
|
|
||||||
|
r := mux.NewRouter()
|
||||||
|
// Only matches if domain is "www.example.com".
|
||||||
|
r.Host("www.example.com")
|
||||||
|
// Matches a dynamic subdomain.
|
||||||
|
r.Host("{subdomain:[a-z]+}.domain.com")
|
||||||
|
|
||||||
|
There are several other matchers that can be added. To match path prefixes:
|
||||||
|
|
||||||
|
r.PathPrefix("/products/")
|
||||||
|
|
||||||
|
...or HTTP methods:
|
||||||
|
|
||||||
|
r.Methods("GET", "POST")
|
||||||
|
|
||||||
|
...or URL schemes:
|
||||||
|
|
||||||
|
r.Schemes("https")
|
||||||
|
|
||||||
|
...or header values:
|
||||||
|
|
||||||
|
r.Headers("X-Requested-With", "XMLHttpRequest")
|
||||||
|
|
||||||
|
...or query values:
|
||||||
|
|
||||||
|
r.Queries("key", "value")
|
||||||
|
|
||||||
|
...or to use a custom matcher function:
|
||||||
|
|
||||||
|
r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool {
|
||||||
|
return r.ProtoMajor == 0
|
||||||
|
})
|
||||||
|
|
||||||
|
...and finally, it is possible to combine several matchers in a single route:
|
||||||
|
|
||||||
|
r.HandleFunc("/products", ProductsHandler).
|
||||||
|
Host("www.example.com").
|
||||||
|
Methods("GET").
|
||||||
|
Schemes("http")
|
||||||
|
|
||||||
|
Setting the same matching conditions again and again can be boring, so we have
|
||||||
|
a way to group several routes that share the same requirements.
|
||||||
|
We call it "subrouting".
|
||||||
|
|
||||||
|
For example, let's say we have several URLs that should only match when the
|
||||||
|
host is "www.example.com". Create a route for that host and get a "subrouter"
|
||||||
|
from it:
|
||||||
|
|
||||||
|
r := mux.NewRouter()
|
||||||
|
s := r.Host("www.example.com").Subrouter()
|
||||||
|
|
||||||
|
Then register routes in the subrouter:
|
||||||
|
|
||||||
|
s.HandleFunc("/products/", ProductsHandler)
|
||||||
|
s.HandleFunc("/products/{key}", ProductHandler)
|
||||||
|
s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
|
||||||
|
|
||||||
|
The three URL paths we registered above will only be tested if the domain is
|
||||||
|
"www.example.com", because the subrouter is tested first. This is not
|
||||||
|
only convenient, but also optimizes request matching. You can create
|
||||||
|
subrouters combining any attribute matchers accepted by a route.
|
||||||
|
|
||||||
|
Subrouters can be used to create domain or path "namespaces": you define
|
||||||
|
subrouters in a central place and then parts of the app can register its
|
||||||
|
paths relatively to a given subrouter.
|
||||||
|
|
||||||
|
There's one more thing about subroutes. When a subrouter has a path prefix,
|
||||||
|
the inner routes use it as base for their paths:
|
||||||
|
|
||||||
|
r := mux.NewRouter()
|
||||||
|
s := r.PathPrefix("/products").Subrouter()
|
||||||
|
// "/products/"
|
||||||
|
s.HandleFunc("/", ProductsHandler)
|
||||||
|
// "/products/{key}/"
|
||||||
|
s.HandleFunc("/{key}/", ProductHandler)
|
||||||
|
// "/products/{key}/details"
|
||||||
|
s.HandleFunc("/{key}/details", ProductDetailsHandler)
|
||||||
|
|
||||||
|
Now let's see how to build registered URLs.
|
||||||
|
|
||||||
|
Routes can be named. All routes that define a name can have their URLs built,
|
||||||
|
or "reversed". We define a name calling Name() on a route. For example:
|
||||||
|
|
||||||
|
r := mux.NewRouter()
|
||||||
|
r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
|
||||||
|
Name("article")
|
||||||
|
|
||||||
|
To build a URL, get the route and call the URL() method, passing a sequence of
|
||||||
|
key/value pairs for the route variables. For the previous route, we would do:
|
||||||
|
|
||||||
|
url, err := r.Get("article").URL("category", "technology", "id", "42")
|
||||||
|
|
||||||
|
...and the result will be a url.URL with the following path:
|
||||||
|
|
||||||
|
"/articles/technology/42"
|
||||||
|
|
||||||
|
This also works for host variables:
|
||||||
|
|
||||||
|
r := mux.NewRouter()
|
||||||
|
r.Host("{subdomain}.domain.com").
|
||||||
|
Path("/articles/{category}/{id:[0-9]+}").
|
||||||
|
HandlerFunc(ArticleHandler).
|
||||||
|
Name("article")
|
||||||
|
|
||||||
|
// url.String() will be "http://news.domain.com/articles/technology/42"
|
||||||
|
url, err := r.Get("article").URL("subdomain", "news",
|
||||||
|
"category", "technology",
|
||||||
|
"id", "42")
|
||||||
|
|
||||||
|
All variables defined in the route are required, and their values must
|
||||||
|
conform to the corresponding patterns. These requirements guarantee that a
|
||||||
|
generated URL will always match a registered route -- the only exception is
|
||||||
|
for explicitly defined "build-only" routes which never match.
|
||||||
|
|
||||||
|
Regex support also exists for matching Headers within a route. For example, we could do:
|
||||||
|
|
||||||
|
r.HeadersRegexp("Content-Type", "application/(text|json)")
|
||||||
|
|
||||||
|
...and the route will match both requests with a Content-Type of `application/json` as well as
|
||||||
|
`application/text`
|
||||||
|
|
||||||
|
There's also a way to build only the URL host or path for a route:
|
||||||
|
use the methods URLHost() or URLPath() instead. For the previous route,
|
||||||
|
we would do:
|
||||||
|
|
||||||
|
// "http://news.domain.com/"
|
||||||
|
host, err := r.Get("article").URLHost("subdomain", "news")
|
||||||
|
|
||||||
|
// "/articles/technology/42"
|
||||||
|
path, err := r.Get("article").URLPath("category", "technology", "id", "42")
|
||||||
|
|
||||||
|
And if you use subrouters, host and path defined separately can be built
|
||||||
|
as well:
|
||||||
|
|
||||||
|
r := mux.NewRouter()
|
||||||
|
s := r.Host("{subdomain}.domain.com").Subrouter()
|
||||||
|
s.Path("/articles/{category}/{id:[0-9]+}").
|
||||||
|
HandlerFunc(ArticleHandler).
|
||||||
|
Name("article")
|
||||||
|
|
||||||
|
// "http://news.domain.com/articles/technology/42"
|
||||||
|
url, err := r.Get("article").URL("subdomain", "news",
|
||||||
|
"category", "technology",
|
||||||
|
"id", "42")
|
||||||
|
*/
|
||||||
|
package mux
|
469
vendor/github.com/gorilla/mux/mux.go
generated
vendored
Normal file
469
vendor/github.com/gorilla/mux/mux.go
generated
vendored
Normal file
|
@ -0,0 +1,469 @@
|
||||||
|
// Copyright 2012 The Gorilla 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 mux
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"path"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/gorilla/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewRouter returns a new router instance.
|
||||||
|
func NewRouter() *Router {
|
||||||
|
return &Router{namedRoutes: make(map[string]*Route), KeepContext: false}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Router registers routes to be matched and dispatches a handler.
|
||||||
|
//
|
||||||
|
// It implements the http.Handler interface, so it can be registered to serve
|
||||||
|
// requests:
|
||||||
|
//
|
||||||
|
// var router = mux.NewRouter()
|
||||||
|
//
|
||||||
|
// func main() {
|
||||||
|
// http.Handle("/", router)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Or, for Google App Engine, register it in a init() function:
|
||||||
|
//
|
||||||
|
// func init() {
|
||||||
|
// http.Handle("/", router)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// This will send all incoming requests to the router.
|
||||||
|
type Router struct {
|
||||||
|
// Configurable Handler to be used when no route matches.
|
||||||
|
NotFoundHandler http.Handler
|
||||||
|
// Parent route, if this is a subrouter.
|
||||||
|
parent parentRoute
|
||||||
|
// Routes to be matched, in order.
|
||||||
|
routes []*Route
|
||||||
|
// Routes by name for URL building.
|
||||||
|
namedRoutes map[string]*Route
|
||||||
|
// See Router.StrictSlash(). This defines the flag for new routes.
|
||||||
|
strictSlash bool
|
||||||
|
// If true, do not clear the request context after handling the request
|
||||||
|
KeepContext bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match matches registered routes against the request.
|
||||||
|
func (r *Router) Match(req *http.Request, match *RouteMatch) bool {
|
||||||
|
for _, route := range r.routes {
|
||||||
|
if route.Match(req, match) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeHTTP dispatches the handler registered in the matched route.
|
||||||
|
//
|
||||||
|
// When there is a match, the route variables can be retrieved calling
|
||||||
|
// mux.Vars(request).
|
||||||
|
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
|
// Clean path to canonical form and redirect.
|
||||||
|
if p := cleanPath(req.URL.Path); p != req.URL.Path {
|
||||||
|
|
||||||
|
// Added 3 lines (Philip Schlump) - It was droping the query string and #whatever from query.
|
||||||
|
// This matches with fix in go 1.2 r.c. 4 for same problem. Go Issue:
|
||||||
|
// http://code.google.com/p/go/issues/detail?id=5252
|
||||||
|
url := *req.URL
|
||||||
|
url.Path = p
|
||||||
|
p = url.String()
|
||||||
|
|
||||||
|
w.Header().Set("Location", p)
|
||||||
|
w.WriteHeader(http.StatusMovedPermanently)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var match RouteMatch
|
||||||
|
var handler http.Handler
|
||||||
|
if r.Match(req, &match) {
|
||||||
|
handler = match.Handler
|
||||||
|
setVars(req, match.Vars)
|
||||||
|
setCurrentRoute(req, match.Route)
|
||||||
|
}
|
||||||
|
if handler == nil {
|
||||||
|
handler = r.NotFoundHandler
|
||||||
|
if handler == nil {
|
||||||
|
handler = http.NotFoundHandler()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !r.KeepContext {
|
||||||
|
defer context.Clear(req)
|
||||||
|
}
|
||||||
|
handler.ServeHTTP(w, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns a route registered with the given name.
|
||||||
|
func (r *Router) Get(name string) *Route {
|
||||||
|
return r.getNamedRoutes()[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRoute returns a route registered with the given name. This method
|
||||||
|
// was renamed to Get() and remains here for backwards compatibility.
|
||||||
|
func (r *Router) GetRoute(name string) *Route {
|
||||||
|
return r.getNamedRoutes()[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
// StrictSlash defines the trailing slash behavior for new routes. The initial
|
||||||
|
// value is false.
|
||||||
|
//
|
||||||
|
// When true, if the route path is "/path/", accessing "/path" will redirect
|
||||||
|
// to the former and vice versa. In other words, your application will always
|
||||||
|
// see the path as specified in the route.
|
||||||
|
//
|
||||||
|
// When false, if the route path is "/path", accessing "/path/" will not match
|
||||||
|
// this route and vice versa.
|
||||||
|
//
|
||||||
|
// Special case: when a route sets a path prefix using the PathPrefix() method,
|
||||||
|
// strict slash is ignored for that route because the redirect behavior can't
|
||||||
|
// be determined from a prefix alone. However, any subrouters created from that
|
||||||
|
// route inherit the original StrictSlash setting.
|
||||||
|
func (r *Router) StrictSlash(value bool) *Router {
|
||||||
|
r.strictSlash = value
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// parentRoute
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// getNamedRoutes returns the map where named routes are registered.
|
||||||
|
func (r *Router) getNamedRoutes() map[string]*Route {
|
||||||
|
if r.namedRoutes == nil {
|
||||||
|
if r.parent != nil {
|
||||||
|
r.namedRoutes = r.parent.getNamedRoutes()
|
||||||
|
} else {
|
||||||
|
r.namedRoutes = make(map[string]*Route)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r.namedRoutes
|
||||||
|
}
|
||||||
|
|
||||||
|
// getRegexpGroup returns regexp definitions from the parent route, if any.
|
||||||
|
func (r *Router) getRegexpGroup() *routeRegexpGroup {
|
||||||
|
if r.parent != nil {
|
||||||
|
return r.parent.getRegexpGroup()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Router) buildVars(m map[string]string) map[string]string {
|
||||||
|
if r.parent != nil {
|
||||||
|
m = r.parent.buildVars(m)
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Route factories
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// NewRoute registers an empty route.
|
||||||
|
func (r *Router) NewRoute() *Route {
|
||||||
|
route := &Route{parent: r, strictSlash: r.strictSlash}
|
||||||
|
r.routes = append(r.routes, route)
|
||||||
|
return route
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle registers a new route with a matcher for the URL path.
|
||||||
|
// See Route.Path() and Route.Handler().
|
||||||
|
func (r *Router) Handle(path string, handler http.Handler) *Route {
|
||||||
|
return r.NewRoute().Path(path).Handler(handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleFunc registers a new route with a matcher for the URL path.
|
||||||
|
// See Route.Path() and Route.HandlerFunc().
|
||||||
|
func (r *Router) HandleFunc(path string, f func(http.ResponseWriter,
|
||||||
|
*http.Request)) *Route {
|
||||||
|
return r.NewRoute().Path(path).HandlerFunc(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Headers registers a new route with a matcher for request header values.
|
||||||
|
// See Route.Headers().
|
||||||
|
func (r *Router) Headers(pairs ...string) *Route {
|
||||||
|
return r.NewRoute().Headers(pairs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Host registers a new route with a matcher for the URL host.
|
||||||
|
// See Route.Host().
|
||||||
|
func (r *Router) Host(tpl string) *Route {
|
||||||
|
return r.NewRoute().Host(tpl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatcherFunc registers a new route with a custom matcher function.
|
||||||
|
// See Route.MatcherFunc().
|
||||||
|
func (r *Router) MatcherFunc(f MatcherFunc) *Route {
|
||||||
|
return r.NewRoute().MatcherFunc(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Methods registers a new route with a matcher for HTTP methods.
|
||||||
|
// See Route.Methods().
|
||||||
|
func (r *Router) Methods(methods ...string) *Route {
|
||||||
|
return r.NewRoute().Methods(methods...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path registers a new route with a matcher for the URL path.
|
||||||
|
// See Route.Path().
|
||||||
|
func (r *Router) Path(tpl string) *Route {
|
||||||
|
return r.NewRoute().Path(tpl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PathPrefix registers a new route with a matcher for the URL path prefix.
|
||||||
|
// See Route.PathPrefix().
|
||||||
|
func (r *Router) PathPrefix(tpl string) *Route {
|
||||||
|
return r.NewRoute().PathPrefix(tpl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Queries registers a new route with a matcher for URL query values.
|
||||||
|
// See Route.Queries().
|
||||||
|
func (r *Router) Queries(pairs ...string) *Route {
|
||||||
|
return r.NewRoute().Queries(pairs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schemes registers a new route with a matcher for URL schemes.
|
||||||
|
// See Route.Schemes().
|
||||||
|
func (r *Router) Schemes(schemes ...string) *Route {
|
||||||
|
return r.NewRoute().Schemes(schemes...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildVars registers a new route with a custom function for modifying
|
||||||
|
// route variables before building a URL.
|
||||||
|
func (r *Router) BuildVarsFunc(f BuildVarsFunc) *Route {
|
||||||
|
return r.NewRoute().BuildVarsFunc(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Walk walks the router and all its sub-routers, calling walkFn for each route
|
||||||
|
// in the tree. The routes are walked in the order they were added. Sub-routers
|
||||||
|
// are explored depth-first.
|
||||||
|
func (r *Router) Walk(walkFn WalkFunc) error {
|
||||||
|
return r.walk(walkFn, []*Route{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SkipRouter is used as a return value from WalkFuncs to indicate that the
|
||||||
|
// router that walk is about to descend down to should be skipped.
|
||||||
|
var SkipRouter = errors.New("skip this router")
|
||||||
|
|
||||||
|
// WalkFunc is the type of the function called for each route visited by Walk.
|
||||||
|
// At every invocation, it is given the current route, and the current router,
|
||||||
|
// and a list of ancestor routes that lead to the current route.
|
||||||
|
type WalkFunc func(route *Route, router *Router, ancestors []*Route) error
|
||||||
|
|
||||||
|
func (r *Router) walk(walkFn WalkFunc, ancestors []*Route) error {
|
||||||
|
for _, t := range r.routes {
|
||||||
|
if t.regexp == nil || t.regexp.path == nil || t.regexp.path.template == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err := walkFn(t, r, ancestors)
|
||||||
|
if err == SkipRouter {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, sr := range t.matchers {
|
||||||
|
if h, ok := sr.(*Router); ok {
|
||||||
|
err := h.walk(walkFn, ancestors)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if h, ok := t.handler.(*Router); ok {
|
||||||
|
ancestors = append(ancestors, t)
|
||||||
|
err := h.walk(walkFn, ancestors)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ancestors = ancestors[:len(ancestors)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Context
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// RouteMatch stores information about a matched route.
|
||||||
|
type RouteMatch struct {
|
||||||
|
Route *Route
|
||||||
|
Handler http.Handler
|
||||||
|
Vars map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
type contextKey int
|
||||||
|
|
||||||
|
const (
|
||||||
|
varsKey contextKey = iota
|
||||||
|
routeKey
|
||||||
|
)
|
||||||
|
|
||||||
|
// Vars returns the route variables for the current request, if any.
|
||||||
|
func Vars(r *http.Request) map[string]string {
|
||||||
|
if rv := context.Get(r, varsKey); rv != nil {
|
||||||
|
return rv.(map[string]string)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CurrentRoute returns the matched route for the current request, if any.
|
||||||
|
// This only works when called inside the handler of the matched route
|
||||||
|
// because the matched route is stored in the request context which is cleared
|
||||||
|
// after the handler returns, unless the KeepContext option is set on the
|
||||||
|
// Router.
|
||||||
|
func CurrentRoute(r *http.Request) *Route {
|
||||||
|
if rv := context.Get(r, routeKey); rv != nil {
|
||||||
|
return rv.(*Route)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setVars(r *http.Request, val interface{}) {
|
||||||
|
context.Set(r, varsKey, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setCurrentRoute(r *http.Request, val interface{}) {
|
||||||
|
context.Set(r, routeKey, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Helpers
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// cleanPath returns the canonical path for p, eliminating . and .. elements.
|
||||||
|
// Borrowed from the net/http package.
|
||||||
|
func cleanPath(p string) string {
|
||||||
|
if p == "" {
|
||||||
|
return "/"
|
||||||
|
}
|
||||||
|
if p[0] != '/' {
|
||||||
|
p = "/" + p
|
||||||
|
}
|
||||||
|
np := path.Clean(p)
|
||||||
|
// path.Clean removes trailing slash except for root;
|
||||||
|
// put the trailing slash back if necessary.
|
||||||
|
if p[len(p)-1] == '/' && np != "/" {
|
||||||
|
np += "/"
|
||||||
|
}
|
||||||
|
return np
|
||||||
|
}
|
||||||
|
|
||||||
|
// uniqueVars returns an error if two slices contain duplicated strings.
|
||||||
|
func uniqueVars(s1, s2 []string) error {
|
||||||
|
for _, v1 := range s1 {
|
||||||
|
for _, v2 := range s2 {
|
||||||
|
if v1 == v2 {
|
||||||
|
return fmt.Errorf("mux: duplicated route variable %q", v2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkPairs(pairs ...string) (int, error) {
|
||||||
|
length := len(pairs)
|
||||||
|
if length%2 != 0 {
|
||||||
|
return length, fmt.Errorf(
|
||||||
|
"mux: number of parameters must be multiple of 2, got %v", pairs)
|
||||||
|
}
|
||||||
|
return length, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// mapFromPairs converts variadic string parameters to a string map.
|
||||||
|
func mapFromPairsToString(pairs ...string) (map[string]string, error) {
|
||||||
|
length, err := checkPairs(pairs...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
m := make(map[string]string, length/2)
|
||||||
|
for i := 0; i < length; i += 2 {
|
||||||
|
m[pairs[i]] = pairs[i+1]
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapFromPairsToRegex(pairs ...string) (map[string]*regexp.Regexp, error) {
|
||||||
|
length, err := checkPairs(pairs...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
m := make(map[string]*regexp.Regexp, length/2)
|
||||||
|
for i := 0; i < length; i += 2 {
|
||||||
|
regex, err := regexp.Compile(pairs[i+1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
m[pairs[i]] = regex
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// matchInArray returns true if the given string value is in the array.
|
||||||
|
func matchInArray(arr []string, value string) bool {
|
||||||
|
for _, v := range arr {
|
||||||
|
if v == value {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// matchMapWithString returns true if the given key/value pairs exist in a given map.
|
||||||
|
func matchMapWithString(toCheck map[string]string, toMatch map[string][]string, canonicalKey bool) bool {
|
||||||
|
for k, v := range toCheck {
|
||||||
|
// Check if key exists.
|
||||||
|
if canonicalKey {
|
||||||
|
k = http.CanonicalHeaderKey(k)
|
||||||
|
}
|
||||||
|
if values := toMatch[k]; values == nil {
|
||||||
|
return false
|
||||||
|
} else if v != "" {
|
||||||
|
// If value was defined as an empty string we only check that the
|
||||||
|
// key exists. Otherwise we also check for equality.
|
||||||
|
valueExists := false
|
||||||
|
for _, value := range values {
|
||||||
|
if v == value {
|
||||||
|
valueExists = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !valueExists {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// matchMapWithRegex returns true if the given key/value pairs exist in a given map compiled against
|
||||||
|
// the given regex
|
||||||
|
func matchMapWithRegex(toCheck map[string]*regexp.Regexp, toMatch map[string][]string, canonicalKey bool) bool {
|
||||||
|
for k, v := range toCheck {
|
||||||
|
// Check if key exists.
|
||||||
|
if canonicalKey {
|
||||||
|
k = http.CanonicalHeaderKey(k)
|
||||||
|
}
|
||||||
|
if values := toMatch[k]; values == nil {
|
||||||
|
return false
|
||||||
|
} else if v != nil {
|
||||||
|
// If value was defined as an empty string we only check that the
|
||||||
|
// key exists. Otherwise we also check for equality.
|
||||||
|
valueExists := false
|
||||||
|
for _, value := range values {
|
||||||
|
if v.MatchString(value) {
|
||||||
|
valueExists = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !valueExists {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
317
vendor/github.com/gorilla/mux/regexp.go
generated
vendored
Normal file
317
vendor/github.com/gorilla/mux/regexp.go
generated
vendored
Normal file
|
@ -0,0 +1,317 @@
|
||||||
|
// Copyright 2012 The Gorilla 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 mux
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// newRouteRegexp parses a route template and returns a routeRegexp,
|
||||||
|
// used to match a host, a path or a query string.
|
||||||
|
//
|
||||||
|
// It will extract named variables, assemble a regexp to be matched, create
|
||||||
|
// a "reverse" template to build URLs and compile regexps to validate variable
|
||||||
|
// values used in URL building.
|
||||||
|
//
|
||||||
|
// Previously we accepted only Python-like identifiers for variable
|
||||||
|
// names ([a-zA-Z_][a-zA-Z0-9_]*), but currently the only restriction is that
|
||||||
|
// name and pattern can't be empty, and names can't contain a colon.
|
||||||
|
func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash bool) (*routeRegexp, error) {
|
||||||
|
// Check if it is well-formed.
|
||||||
|
idxs, errBraces := braceIndices(tpl)
|
||||||
|
if errBraces != nil {
|
||||||
|
return nil, errBraces
|
||||||
|
}
|
||||||
|
// Backup the original.
|
||||||
|
template := tpl
|
||||||
|
// Now let's parse it.
|
||||||
|
defaultPattern := "[^/]+"
|
||||||
|
if matchQuery {
|
||||||
|
defaultPattern = "[^?&]*"
|
||||||
|
} else if matchHost {
|
||||||
|
defaultPattern = "[^.]+"
|
||||||
|
matchPrefix = false
|
||||||
|
}
|
||||||
|
// Only match strict slash if not matching
|
||||||
|
if matchPrefix || matchHost || matchQuery {
|
||||||
|
strictSlash = false
|
||||||
|
}
|
||||||
|
// Set a flag for strictSlash.
|
||||||
|
endSlash := false
|
||||||
|
if strictSlash && strings.HasSuffix(tpl, "/") {
|
||||||
|
tpl = tpl[:len(tpl)-1]
|
||||||
|
endSlash = true
|
||||||
|
}
|
||||||
|
varsN := make([]string, len(idxs)/2)
|
||||||
|
varsR := make([]*regexp.Regexp, len(idxs)/2)
|
||||||
|
pattern := bytes.NewBufferString("")
|
||||||
|
pattern.WriteByte('^')
|
||||||
|
reverse := bytes.NewBufferString("")
|
||||||
|
var end int
|
||||||
|
var err error
|
||||||
|
for i := 0; i < len(idxs); i += 2 {
|
||||||
|
// Set all values we are interested in.
|
||||||
|
raw := tpl[end:idxs[i]]
|
||||||
|
end = idxs[i+1]
|
||||||
|
parts := strings.SplitN(tpl[idxs[i]+1:end-1], ":", 2)
|
||||||
|
name := parts[0]
|
||||||
|
patt := defaultPattern
|
||||||
|
if len(parts) == 2 {
|
||||||
|
patt = parts[1]
|
||||||
|
}
|
||||||
|
// Name or pattern can't be empty.
|
||||||
|
if name == "" || patt == "" {
|
||||||
|
return nil, fmt.Errorf("mux: missing name or pattern in %q",
|
||||||
|
tpl[idxs[i]:end])
|
||||||
|
}
|
||||||
|
// Build the regexp pattern.
|
||||||
|
varIdx := i / 2
|
||||||
|
fmt.Fprintf(pattern, "%s(?P<%s>%s)", regexp.QuoteMeta(raw), varGroupName(varIdx), patt)
|
||||||
|
// Build the reverse template.
|
||||||
|
fmt.Fprintf(reverse, "%s%%s", raw)
|
||||||
|
|
||||||
|
// Append variable name and compiled pattern.
|
||||||
|
varsN[varIdx] = name
|
||||||
|
varsR[varIdx], err = regexp.Compile(fmt.Sprintf("^%s$", patt))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Add the remaining.
|
||||||
|
raw := tpl[end:]
|
||||||
|
pattern.WriteString(regexp.QuoteMeta(raw))
|
||||||
|
if strictSlash {
|
||||||
|
pattern.WriteString("[/]?")
|
||||||
|
}
|
||||||
|
if matchQuery {
|
||||||
|
// Add the default pattern if the query value is empty
|
||||||
|
if queryVal := strings.SplitN(template, "=", 2)[1]; queryVal == "" {
|
||||||
|
pattern.WriteString(defaultPattern)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !matchPrefix {
|
||||||
|
pattern.WriteByte('$')
|
||||||
|
}
|
||||||
|
reverse.WriteString(raw)
|
||||||
|
if endSlash {
|
||||||
|
reverse.WriteByte('/')
|
||||||
|
}
|
||||||
|
// Compile full regexp.
|
||||||
|
reg, errCompile := regexp.Compile(pattern.String())
|
||||||
|
if errCompile != nil {
|
||||||
|
return nil, errCompile
|
||||||
|
}
|
||||||
|
// Done!
|
||||||
|
return &routeRegexp{
|
||||||
|
template: template,
|
||||||
|
matchHost: matchHost,
|
||||||
|
matchQuery: matchQuery,
|
||||||
|
strictSlash: strictSlash,
|
||||||
|
regexp: reg,
|
||||||
|
reverse: reverse.String(),
|
||||||
|
varsN: varsN,
|
||||||
|
varsR: varsR,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// routeRegexp stores a regexp to match a host or path and information to
|
||||||
|
// collect and validate route variables.
|
||||||
|
type routeRegexp struct {
|
||||||
|
// The unmodified template.
|
||||||
|
template string
|
||||||
|
// True for host match, false for path or query string match.
|
||||||
|
matchHost bool
|
||||||
|
// True for query string match, false for path and host match.
|
||||||
|
matchQuery bool
|
||||||
|
// The strictSlash value defined on the route, but disabled if PathPrefix was used.
|
||||||
|
strictSlash bool
|
||||||
|
// Expanded regexp.
|
||||||
|
regexp *regexp.Regexp
|
||||||
|
// Reverse template.
|
||||||
|
reverse string
|
||||||
|
// Variable names.
|
||||||
|
varsN []string
|
||||||
|
// Variable regexps (validators).
|
||||||
|
varsR []*regexp.Regexp
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match matches the regexp against the URL host or path.
|
||||||
|
func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool {
|
||||||
|
if !r.matchHost {
|
||||||
|
if r.matchQuery {
|
||||||
|
return r.matchQueryString(req)
|
||||||
|
} else {
|
||||||
|
return r.regexp.MatchString(req.URL.Path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r.regexp.MatchString(getHost(req))
|
||||||
|
}
|
||||||
|
|
||||||
|
// url builds a URL part using the given values.
|
||||||
|
func (r *routeRegexp) url(values map[string]string) (string, error) {
|
||||||
|
urlValues := make([]interface{}, len(r.varsN))
|
||||||
|
for k, v := range r.varsN {
|
||||||
|
value, ok := values[v]
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("mux: missing route variable %q", v)
|
||||||
|
}
|
||||||
|
urlValues[k] = value
|
||||||
|
}
|
||||||
|
rv := fmt.Sprintf(r.reverse, urlValues...)
|
||||||
|
if !r.regexp.MatchString(rv) {
|
||||||
|
// The URL is checked against the full regexp, instead of checking
|
||||||
|
// individual variables. This is faster but to provide a good error
|
||||||
|
// message, we check individual regexps if the URL doesn't match.
|
||||||
|
for k, v := range r.varsN {
|
||||||
|
if !r.varsR[k].MatchString(values[v]) {
|
||||||
|
return "", fmt.Errorf(
|
||||||
|
"mux: variable %q doesn't match, expected %q", values[v],
|
||||||
|
r.varsR[k].String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rv, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getUrlQuery returns a single query parameter from a request URL.
|
||||||
|
// For a URL with foo=bar&baz=ding, we return only the relevant key
|
||||||
|
// value pair for the routeRegexp.
|
||||||
|
func (r *routeRegexp) getUrlQuery(req *http.Request) string {
|
||||||
|
if !r.matchQuery {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
templateKey := strings.SplitN(r.template, "=", 2)[0]
|
||||||
|
for key, vals := range req.URL.Query() {
|
||||||
|
if key == templateKey && len(vals) > 0 {
|
||||||
|
return key + "=" + vals[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *routeRegexp) matchQueryString(req *http.Request) bool {
|
||||||
|
return r.regexp.MatchString(r.getUrlQuery(req))
|
||||||
|
}
|
||||||
|
|
||||||
|
// braceIndices returns the first level curly brace indices from a string.
|
||||||
|
// It returns an error in case of unbalanced braces.
|
||||||
|
func braceIndices(s string) ([]int, error) {
|
||||||
|
var level, idx int
|
||||||
|
idxs := make([]int, 0)
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
switch s[i] {
|
||||||
|
case '{':
|
||||||
|
if level++; level == 1 {
|
||||||
|
idx = i
|
||||||
|
}
|
||||||
|
case '}':
|
||||||
|
if level--; level == 0 {
|
||||||
|
idxs = append(idxs, idx, i+1)
|
||||||
|
} else if level < 0 {
|
||||||
|
return nil, fmt.Errorf("mux: unbalanced braces in %q", s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if level != 0 {
|
||||||
|
return nil, fmt.Errorf("mux: unbalanced braces in %q", s)
|
||||||
|
}
|
||||||
|
return idxs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// varGroupName builds a capturing group name for the indexed variable.
|
||||||
|
func varGroupName(idx int) string {
|
||||||
|
return "v" + strconv.Itoa(idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// routeRegexpGroup
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// routeRegexpGroup groups the route matchers that carry variables.
|
||||||
|
type routeRegexpGroup struct {
|
||||||
|
host *routeRegexp
|
||||||
|
path *routeRegexp
|
||||||
|
queries []*routeRegexp
|
||||||
|
}
|
||||||
|
|
||||||
|
// setMatch extracts the variables from the URL once a route matches.
|
||||||
|
func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route) {
|
||||||
|
// Store host variables.
|
||||||
|
if v.host != nil {
|
||||||
|
hostVars := v.host.regexp.FindStringSubmatch(getHost(req))
|
||||||
|
if hostVars != nil {
|
||||||
|
subexpNames := v.host.regexp.SubexpNames()
|
||||||
|
varName := 0
|
||||||
|
for i, name := range subexpNames[1:] {
|
||||||
|
if name != "" && name == varGroupName(varName) {
|
||||||
|
m.Vars[v.host.varsN[varName]] = hostVars[i+1]
|
||||||
|
varName++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Store path variables.
|
||||||
|
if v.path != nil {
|
||||||
|
pathVars := v.path.regexp.FindStringSubmatch(req.URL.Path)
|
||||||
|
if pathVars != nil {
|
||||||
|
subexpNames := v.path.regexp.SubexpNames()
|
||||||
|
varName := 0
|
||||||
|
for i, name := range subexpNames[1:] {
|
||||||
|
if name != "" && name == varGroupName(varName) {
|
||||||
|
m.Vars[v.path.varsN[varName]] = pathVars[i+1]
|
||||||
|
varName++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check if we should redirect.
|
||||||
|
if v.path.strictSlash {
|
||||||
|
p1 := strings.HasSuffix(req.URL.Path, "/")
|
||||||
|
p2 := strings.HasSuffix(v.path.template, "/")
|
||||||
|
if p1 != p2 {
|
||||||
|
u, _ := url.Parse(req.URL.String())
|
||||||
|
if p1 {
|
||||||
|
u.Path = u.Path[:len(u.Path)-1]
|
||||||
|
} else {
|
||||||
|
u.Path += "/"
|
||||||
|
}
|
||||||
|
m.Handler = http.RedirectHandler(u.String(), 301)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Store query string variables.
|
||||||
|
for _, q := range v.queries {
|
||||||
|
queryVars := q.regexp.FindStringSubmatch(q.getUrlQuery(req))
|
||||||
|
if queryVars != nil {
|
||||||
|
subexpNames := q.regexp.SubexpNames()
|
||||||
|
varName := 0
|
||||||
|
for i, name := range subexpNames[1:] {
|
||||||
|
if name != "" && name == varGroupName(varName) {
|
||||||
|
m.Vars[q.varsN[varName]] = queryVars[i+1]
|
||||||
|
varName++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getHost tries its best to return the request host.
|
||||||
|
func getHost(r *http.Request) string {
|
||||||
|
if r.URL.IsAbs() {
|
||||||
|
return r.URL.Host
|
||||||
|
}
|
||||||
|
host := r.Host
|
||||||
|
// Slice off any port information.
|
||||||
|
if i := strings.Index(host, ":"); i != -1 {
|
||||||
|
host = host[:i]
|
||||||
|
}
|
||||||
|
return host
|
||||||
|
|
||||||
|
}
|
595
vendor/github.com/gorilla/mux/route.go
generated
vendored
Normal file
595
vendor/github.com/gorilla/mux/route.go
generated
vendored
Normal file
|
@ -0,0 +1,595 @@
|
||||||
|
// Copyright 2012 The Gorilla 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 mux
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Route stores information to match a request and build URLs.
|
||||||
|
type Route struct {
|
||||||
|
// Parent where the route was registered (a Router).
|
||||||
|
parent parentRoute
|
||||||
|
// Request handler for the route.
|
||||||
|
handler http.Handler
|
||||||
|
// List of matchers.
|
||||||
|
matchers []matcher
|
||||||
|
// Manager for the variables from host and path.
|
||||||
|
regexp *routeRegexpGroup
|
||||||
|
// If true, when the path pattern is "/path/", accessing "/path" will
|
||||||
|
// redirect to the former and vice versa.
|
||||||
|
strictSlash bool
|
||||||
|
// If true, this route never matches: it is only used to build URLs.
|
||||||
|
buildOnly bool
|
||||||
|
// The name used to build URLs.
|
||||||
|
name string
|
||||||
|
// Error resulted from building a route.
|
||||||
|
err error
|
||||||
|
|
||||||
|
buildVarsFunc BuildVarsFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match matches the route against the request.
|
||||||
|
func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
|
||||||
|
if r.buildOnly || r.err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Match everything.
|
||||||
|
for _, m := range r.matchers {
|
||||||
|
if matched := m.Match(req, match); !matched {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Yay, we have a match. Let's collect some info about it.
|
||||||
|
if match.Route == nil {
|
||||||
|
match.Route = r
|
||||||
|
}
|
||||||
|
if match.Handler == nil {
|
||||||
|
match.Handler = r.handler
|
||||||
|
}
|
||||||
|
if match.Vars == nil {
|
||||||
|
match.Vars = make(map[string]string)
|
||||||
|
}
|
||||||
|
// Set variables.
|
||||||
|
if r.regexp != nil {
|
||||||
|
r.regexp.setMatch(req, match, r)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Route attributes
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// GetError returns an error resulted from building the route, if any.
|
||||||
|
func (r *Route) GetError() error {
|
||||||
|
return r.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildOnly sets the route to never match: it is only used to build URLs.
|
||||||
|
func (r *Route) BuildOnly() *Route {
|
||||||
|
r.buildOnly = true
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handler --------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Handler sets a handler for the route.
|
||||||
|
func (r *Route) Handler(handler http.Handler) *Route {
|
||||||
|
if r.err == nil {
|
||||||
|
r.handler = handler
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandlerFunc sets a handler function for the route.
|
||||||
|
func (r *Route) HandlerFunc(f func(http.ResponseWriter, *http.Request)) *Route {
|
||||||
|
return r.Handler(http.HandlerFunc(f))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHandler returns the handler for the route, if any.
|
||||||
|
func (r *Route) GetHandler() http.Handler {
|
||||||
|
return r.handler
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Name sets the name for the route, used to build URLs.
|
||||||
|
// If the name was registered already it will be overwritten.
|
||||||
|
func (r *Route) Name(name string) *Route {
|
||||||
|
if r.name != "" {
|
||||||
|
r.err = fmt.Errorf("mux: route already has name %q, can't set %q",
|
||||||
|
r.name, name)
|
||||||
|
}
|
||||||
|
if r.err == nil {
|
||||||
|
r.name = name
|
||||||
|
r.getNamedRoutes()[name] = r
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetName returns the name for the route, if any.
|
||||||
|
func (r *Route) GetName() string {
|
||||||
|
return r.name
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Matchers
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// matcher types try to match a request.
|
||||||
|
type matcher interface {
|
||||||
|
Match(*http.Request, *RouteMatch) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// addMatcher adds a matcher to the route.
|
||||||
|
func (r *Route) addMatcher(m matcher) *Route {
|
||||||
|
if r.err == nil {
|
||||||
|
r.matchers = append(r.matchers, m)
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// addRegexpMatcher adds a host or path matcher and builder to a route.
|
||||||
|
func (r *Route) addRegexpMatcher(tpl string, matchHost, matchPrefix, matchQuery bool) error {
|
||||||
|
if r.err != nil {
|
||||||
|
return r.err
|
||||||
|
}
|
||||||
|
r.regexp = r.getRegexpGroup()
|
||||||
|
if !matchHost && !matchQuery {
|
||||||
|
if len(tpl) == 0 || tpl[0] != '/' {
|
||||||
|
return fmt.Errorf("mux: path must start with a slash, got %q", tpl)
|
||||||
|
}
|
||||||
|
if r.regexp.path != nil {
|
||||||
|
tpl = strings.TrimRight(r.regexp.path.template, "/") + tpl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rr, err := newRouteRegexp(tpl, matchHost, matchPrefix, matchQuery, r.strictSlash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, q := range r.regexp.queries {
|
||||||
|
if err = uniqueVars(rr.varsN, q.varsN); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if matchHost {
|
||||||
|
if r.regexp.path != nil {
|
||||||
|
if err = uniqueVars(rr.varsN, r.regexp.path.varsN); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.regexp.host = rr
|
||||||
|
} else {
|
||||||
|
if r.regexp.host != nil {
|
||||||
|
if err = uniqueVars(rr.varsN, r.regexp.host.varsN); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if matchQuery {
|
||||||
|
r.regexp.queries = append(r.regexp.queries, rr)
|
||||||
|
} else {
|
||||||
|
r.regexp.path = rr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.addMatcher(rr)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Headers --------------------------------------------------------------------
|
||||||
|
|
||||||
|
// headerMatcher matches the request against header values.
|
||||||
|
type headerMatcher map[string]string
|
||||||
|
|
||||||
|
func (m headerMatcher) Match(r *http.Request, match *RouteMatch) bool {
|
||||||
|
return matchMapWithString(m, r.Header, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Headers adds a matcher for request header values.
|
||||||
|
// It accepts a sequence of key/value pairs to be matched. For example:
|
||||||
|
//
|
||||||
|
// r := mux.NewRouter()
|
||||||
|
// r.Headers("Content-Type", "application/json",
|
||||||
|
// "X-Requested-With", "XMLHttpRequest")
|
||||||
|
//
|
||||||
|
// The above route will only match if both request header values match.
|
||||||
|
// If the value is an empty string, it will match any value if the key is set.
|
||||||
|
func (r *Route) Headers(pairs ...string) *Route {
|
||||||
|
if r.err == nil {
|
||||||
|
var headers map[string]string
|
||||||
|
headers, r.err = mapFromPairsToString(pairs...)
|
||||||
|
return r.addMatcher(headerMatcher(headers))
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// headerRegexMatcher matches the request against the route given a regex for the header
|
||||||
|
type headerRegexMatcher map[string]*regexp.Regexp
|
||||||
|
|
||||||
|
func (m headerRegexMatcher) Match(r *http.Request, match *RouteMatch) bool {
|
||||||
|
return matchMapWithRegex(m, r.Header, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regular expressions can be used with headers as well.
|
||||||
|
// It accepts a sequence of key/value pairs, where the value has regex support. For example
|
||||||
|
// r := mux.NewRouter()
|
||||||
|
// r.HeadersRegexp("Content-Type", "application/(text|json)",
|
||||||
|
// "X-Requested-With", "XMLHttpRequest")
|
||||||
|
//
|
||||||
|
// The above route will only match if both the request header matches both regular expressions.
|
||||||
|
// It the value is an empty string, it will match any value if the key is set.
|
||||||
|
func (r *Route) HeadersRegexp(pairs ...string) *Route {
|
||||||
|
if r.err == nil {
|
||||||
|
var headers map[string]*regexp.Regexp
|
||||||
|
headers, r.err = mapFromPairsToRegex(pairs...)
|
||||||
|
return r.addMatcher(headerRegexMatcher(headers))
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Host -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Host adds a matcher for the URL host.
|
||||||
|
// It accepts a template with zero or more URL variables enclosed by {}.
|
||||||
|
// Variables can define an optional regexp pattern to be matched:
|
||||||
|
//
|
||||||
|
// - {name} matches anything until the next dot.
|
||||||
|
//
|
||||||
|
// - {name:pattern} matches the given regexp pattern.
|
||||||
|
//
|
||||||
|
// For example:
|
||||||
|
//
|
||||||
|
// r := mux.NewRouter()
|
||||||
|
// r.Host("www.example.com")
|
||||||
|
// r.Host("{subdomain}.domain.com")
|
||||||
|
// r.Host("{subdomain:[a-z]+}.domain.com")
|
||||||
|
//
|
||||||
|
// Variable names must be unique in a given route. They can be retrieved
|
||||||
|
// calling mux.Vars(request).
|
||||||
|
func (r *Route) Host(tpl string) *Route {
|
||||||
|
r.err = r.addRegexpMatcher(tpl, true, false, false)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatcherFunc ----------------------------------------------------------------
|
||||||
|
|
||||||
|
// MatcherFunc is the function signature used by custom matchers.
|
||||||
|
type MatcherFunc func(*http.Request, *RouteMatch) bool
|
||||||
|
|
||||||
|
func (m MatcherFunc) Match(r *http.Request, match *RouteMatch) bool {
|
||||||
|
return m(r, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatcherFunc adds a custom function to be used as request matcher.
|
||||||
|
func (r *Route) MatcherFunc(f MatcherFunc) *Route {
|
||||||
|
return r.addMatcher(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Methods --------------------------------------------------------------------
|
||||||
|
|
||||||
|
// methodMatcher matches the request against HTTP methods.
|
||||||
|
type methodMatcher []string
|
||||||
|
|
||||||
|
func (m methodMatcher) Match(r *http.Request, match *RouteMatch) bool {
|
||||||
|
return matchInArray(m, r.Method)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Methods adds a matcher for HTTP methods.
|
||||||
|
// It accepts a sequence of one or more methods to be matched, e.g.:
|
||||||
|
// "GET", "POST", "PUT".
|
||||||
|
func (r *Route) Methods(methods ...string) *Route {
|
||||||
|
for k, v := range methods {
|
||||||
|
methods[k] = strings.ToUpper(v)
|
||||||
|
}
|
||||||
|
return r.addMatcher(methodMatcher(methods))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Path adds a matcher for the URL path.
|
||||||
|
// It accepts a template with zero or more URL variables enclosed by {}. The
|
||||||
|
// template must start with a "/".
|
||||||
|
// Variables can define an optional regexp pattern to be matched:
|
||||||
|
//
|
||||||
|
// - {name} matches anything until the next slash.
|
||||||
|
//
|
||||||
|
// - {name:pattern} matches the given regexp pattern.
|
||||||
|
//
|
||||||
|
// For example:
|
||||||
|
//
|
||||||
|
// r := mux.NewRouter()
|
||||||
|
// r.Path("/products/").Handler(ProductsHandler)
|
||||||
|
// r.Path("/products/{key}").Handler(ProductsHandler)
|
||||||
|
// r.Path("/articles/{category}/{id:[0-9]+}").
|
||||||
|
// Handler(ArticleHandler)
|
||||||
|
//
|
||||||
|
// Variable names must be unique in a given route. They can be retrieved
|
||||||
|
// calling mux.Vars(request).
|
||||||
|
func (r *Route) Path(tpl string) *Route {
|
||||||
|
r.err = r.addRegexpMatcher(tpl, false, false, false)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// PathPrefix -----------------------------------------------------------------
|
||||||
|
|
||||||
|
// PathPrefix adds a matcher for the URL path prefix. This matches if the given
|
||||||
|
// template is a prefix of the full URL path. See Route.Path() for details on
|
||||||
|
// the tpl argument.
|
||||||
|
//
|
||||||
|
// Note that it does not treat slashes specially ("/foobar/" will be matched by
|
||||||
|
// the prefix "/foo") so you may want to use a trailing slash here.
|
||||||
|
//
|
||||||
|
// Also note that the setting of Router.StrictSlash() has no effect on routes
|
||||||
|
// with a PathPrefix matcher.
|
||||||
|
func (r *Route) PathPrefix(tpl string) *Route {
|
||||||
|
r.err = r.addRegexpMatcher(tpl, false, true, false)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Queries adds a matcher for URL query values.
|
||||||
|
// It accepts a sequence of key/value pairs. Values may define variables.
|
||||||
|
// For example:
|
||||||
|
//
|
||||||
|
// r := mux.NewRouter()
|
||||||
|
// r.Queries("foo", "bar", "id", "{id:[0-9]+}")
|
||||||
|
//
|
||||||
|
// The above route will only match if the URL contains the defined queries
|
||||||
|
// values, e.g.: ?foo=bar&id=42.
|
||||||
|
//
|
||||||
|
// It the value is an empty string, it will match any value if the key is set.
|
||||||
|
//
|
||||||
|
// Variables can define an optional regexp pattern to be matched:
|
||||||
|
//
|
||||||
|
// - {name} matches anything until the next slash.
|
||||||
|
//
|
||||||
|
// - {name:pattern} matches the given regexp pattern.
|
||||||
|
func (r *Route) Queries(pairs ...string) *Route {
|
||||||
|
length := len(pairs)
|
||||||
|
if length%2 != 0 {
|
||||||
|
r.err = fmt.Errorf(
|
||||||
|
"mux: number of parameters must be multiple of 2, got %v", pairs)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for i := 0; i < length; i += 2 {
|
||||||
|
if r.err = r.addRegexpMatcher(pairs[i]+"="+pairs[i+1], false, false, true); r.err != nil {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schemes --------------------------------------------------------------------
|
||||||
|
|
||||||
|
// schemeMatcher matches the request against URL schemes.
|
||||||
|
type schemeMatcher []string
|
||||||
|
|
||||||
|
func (m schemeMatcher) Match(r *http.Request, match *RouteMatch) bool {
|
||||||
|
return matchInArray(m, r.URL.Scheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schemes adds a matcher for URL schemes.
|
||||||
|
// It accepts a sequence of schemes to be matched, e.g.: "http", "https".
|
||||||
|
func (r *Route) Schemes(schemes ...string) *Route {
|
||||||
|
for k, v := range schemes {
|
||||||
|
schemes[k] = strings.ToLower(v)
|
||||||
|
}
|
||||||
|
return r.addMatcher(schemeMatcher(schemes))
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildVarsFunc --------------------------------------------------------------
|
||||||
|
|
||||||
|
// BuildVarsFunc is the function signature used by custom build variable
|
||||||
|
// functions (which can modify route variables before a route's URL is built).
|
||||||
|
type BuildVarsFunc func(map[string]string) map[string]string
|
||||||
|
|
||||||
|
// BuildVarsFunc adds a custom function to be used to modify build variables
|
||||||
|
// before a route's URL is built.
|
||||||
|
func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route {
|
||||||
|
r.buildVarsFunc = f
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subrouter ------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Subrouter creates a subrouter for the route.
|
||||||
|
//
|
||||||
|
// It will test the inner routes only if the parent route matched. For example:
|
||||||
|
//
|
||||||
|
// r := mux.NewRouter()
|
||||||
|
// s := r.Host("www.example.com").Subrouter()
|
||||||
|
// s.HandleFunc("/products/", ProductsHandler)
|
||||||
|
// s.HandleFunc("/products/{key}", ProductHandler)
|
||||||
|
// s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
|
||||||
|
//
|
||||||
|
// Here, the routes registered in the subrouter won't be tested if the host
|
||||||
|
// doesn't match.
|
||||||
|
func (r *Route) Subrouter() *Router {
|
||||||
|
router := &Router{parent: r, strictSlash: r.strictSlash}
|
||||||
|
r.addMatcher(router)
|
||||||
|
return router
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// URL building
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// URL builds a URL for the route.
|
||||||
|
//
|
||||||
|
// It accepts a sequence of key/value pairs for the route variables. For
|
||||||
|
// example, given this route:
|
||||||
|
//
|
||||||
|
// r := mux.NewRouter()
|
||||||
|
// r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
|
||||||
|
// Name("article")
|
||||||
|
//
|
||||||
|
// ...a URL for it can be built using:
|
||||||
|
//
|
||||||
|
// url, err := r.Get("article").URL("category", "technology", "id", "42")
|
||||||
|
//
|
||||||
|
// ...which will return an url.URL with the following path:
|
||||||
|
//
|
||||||
|
// "/articles/technology/42"
|
||||||
|
//
|
||||||
|
// This also works for host variables:
|
||||||
|
//
|
||||||
|
// r := mux.NewRouter()
|
||||||
|
// r.Host("{subdomain}.domain.com").
|
||||||
|
// HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
|
||||||
|
// Name("article")
|
||||||
|
//
|
||||||
|
// // url.String() will be "http://news.domain.com/articles/technology/42"
|
||||||
|
// url, err := r.Get("article").URL("subdomain", "news",
|
||||||
|
// "category", "technology",
|
||||||
|
// "id", "42")
|
||||||
|
//
|
||||||
|
// All variables defined in the route are required, and their values must
|
||||||
|
// conform to the corresponding patterns.
|
||||||
|
func (r *Route) URL(pairs ...string) (*url.URL, error) {
|
||||||
|
if r.err != nil {
|
||||||
|
return nil, r.err
|
||||||
|
}
|
||||||
|
if r.regexp == nil {
|
||||||
|
return nil, errors.New("mux: route doesn't have a host or path")
|
||||||
|
}
|
||||||
|
values, err := r.prepareVars(pairs...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var scheme, host, path string
|
||||||
|
if r.regexp.host != nil {
|
||||||
|
// Set a default scheme.
|
||||||
|
scheme = "http"
|
||||||
|
if host, err = r.regexp.host.url(values); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if r.regexp.path != nil {
|
||||||
|
if path, err = r.regexp.path.url(values); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &url.URL{
|
||||||
|
Scheme: scheme,
|
||||||
|
Host: host,
|
||||||
|
Path: path,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// URLHost builds the host part of the URL for a route. See Route.URL().
|
||||||
|
//
|
||||||
|
// The route must have a host defined.
|
||||||
|
func (r *Route) URLHost(pairs ...string) (*url.URL, error) {
|
||||||
|
if r.err != nil {
|
||||||
|
return nil, r.err
|
||||||
|
}
|
||||||
|
if r.regexp == nil || r.regexp.host == nil {
|
||||||
|
return nil, errors.New("mux: route doesn't have a host")
|
||||||
|
}
|
||||||
|
values, err := r.prepareVars(pairs...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
host, err := r.regexp.host.url(values)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &url.URL{
|
||||||
|
Scheme: "http",
|
||||||
|
Host: host,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// URLPath builds the path part of the URL for a route. See Route.URL().
|
||||||
|
//
|
||||||
|
// The route must have a path defined.
|
||||||
|
func (r *Route) URLPath(pairs ...string) (*url.URL, error) {
|
||||||
|
if r.err != nil {
|
||||||
|
return nil, r.err
|
||||||
|
}
|
||||||
|
if r.regexp == nil || r.regexp.path == nil {
|
||||||
|
return nil, errors.New("mux: route doesn't have a path")
|
||||||
|
}
|
||||||
|
values, err := r.prepareVars(pairs...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
path, err := r.regexp.path.url(values)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &url.URL{
|
||||||
|
Path: path,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepareVars converts the route variable pairs into a map. If the route has a
|
||||||
|
// BuildVarsFunc, it is invoked.
|
||||||
|
func (r *Route) prepareVars(pairs ...string) (map[string]string, error) {
|
||||||
|
m, err := mapFromPairsToString(pairs...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return r.buildVars(m), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Route) buildVars(m map[string]string) map[string]string {
|
||||||
|
if r.parent != nil {
|
||||||
|
m = r.parent.buildVars(m)
|
||||||
|
}
|
||||||
|
if r.buildVarsFunc != nil {
|
||||||
|
m = r.buildVarsFunc(m)
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// parentRoute
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// parentRoute allows routes to know about parent host and path definitions.
|
||||||
|
type parentRoute interface {
|
||||||
|
getNamedRoutes() map[string]*Route
|
||||||
|
getRegexpGroup() *routeRegexpGroup
|
||||||
|
buildVars(map[string]string) map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// getNamedRoutes returns the map where named routes are registered.
|
||||||
|
func (r *Route) getNamedRoutes() map[string]*Route {
|
||||||
|
if r.parent == nil {
|
||||||
|
// During tests router is not always set.
|
||||||
|
r.parent = NewRouter()
|
||||||
|
}
|
||||||
|
return r.parent.getNamedRoutes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// getRegexpGroup returns regexp definitions from this route.
|
||||||
|
func (r *Route) getRegexpGroup() *routeRegexpGroup {
|
||||||
|
if r.regexp == nil {
|
||||||
|
if r.parent == nil {
|
||||||
|
// During tests router is not always set.
|
||||||
|
r.parent = NewRouter()
|
||||||
|
}
|
||||||
|
regexp := r.parent.getRegexpGroup()
|
||||||
|
if regexp == nil {
|
||||||
|
r.regexp = new(routeRegexpGroup)
|
||||||
|
} else {
|
||||||
|
// Copy.
|
||||||
|
r.regexp = &routeRegexpGroup{
|
||||||
|
host: regexp.host,
|
||||||
|
path: regexp.path,
|
||||||
|
queries: regexp.queries,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r.regexp
|
||||||
|
}
|
17
vendor/github.com/spf13/pflag/.travis.yml
generated
vendored
Normal file
17
vendor/github.com/spf13/pflag/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
sudo: false
|
||||||
|
|
||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.5
|
||||||
|
- 1.6
|
||||||
|
- tip
|
||||||
|
|
||||||
|
install:
|
||||||
|
- go get github.com/golang/lint/golint
|
||||||
|
- export PATH=$GOPATH/bin:$PATH
|
||||||
|
- go install ./...
|
||||||
|
|
||||||
|
script:
|
||||||
|
- verify/all.sh -v
|
||||||
|
- go test ./...
|
28
vendor/github.com/spf13/pflag/LICENSE
generated
vendored
Normal file
28
vendor/github.com/spf13/pflag/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
Copyright (c) 2012 Alex Ogier. All rights reserved.
|
||||||
|
Copyright (c) 2012 The Go Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
275
vendor/github.com/spf13/pflag/README.md
generated
vendored
Normal file
275
vendor/github.com/spf13/pflag/README.md
generated
vendored
Normal file
|
@ -0,0 +1,275 @@
|
||||||
|
[![Build Status](https://travis-ci.org/spf13/pflag.svg?branch=master)](https://travis-ci.org/spf13/pflag)
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
pflag is a drop-in replacement for Go's flag package, implementing
|
||||||
|
POSIX/GNU-style --flags.
|
||||||
|
|
||||||
|
pflag is compatible with the [GNU extensions to the POSIX recommendations
|
||||||
|
for command-line options][1]. For a more precise description, see the
|
||||||
|
"Command-line flag syntax" section below.
|
||||||
|
|
||||||
|
[1]: http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html
|
||||||
|
|
||||||
|
pflag is available under the same style of BSD license as the Go language,
|
||||||
|
which can be found in the LICENSE file.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
pflag is available using the standard `go get` command.
|
||||||
|
|
||||||
|
Install by running:
|
||||||
|
|
||||||
|
go get github.com/spf13/pflag
|
||||||
|
|
||||||
|
Run tests by running:
|
||||||
|
|
||||||
|
go test github.com/spf13/pflag
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
pflag is a drop-in replacement of Go's native flag package. If you import
|
||||||
|
pflag under the name "flag" then all code should continue to function
|
||||||
|
with no changes.
|
||||||
|
|
||||||
|
``` go
|
||||||
|
import flag "github.com/spf13/pflag"
|
||||||
|
```
|
||||||
|
|
||||||
|
There is one exception to this: if you directly instantiate the Flag struct
|
||||||
|
there is one more field "Shorthand" that you will need to set.
|
||||||
|
Most code never instantiates this struct directly, and instead uses
|
||||||
|
functions such as String(), BoolVar(), and Var(), and is therefore
|
||||||
|
unaffected.
|
||||||
|
|
||||||
|
Define flags using flag.String(), Bool(), Int(), etc.
|
||||||
|
|
||||||
|
This declares an integer flag, -flagname, stored in the pointer ip, with type *int.
|
||||||
|
|
||||||
|
``` go
|
||||||
|
var ip *int = flag.Int("flagname", 1234, "help message for flagname")
|
||||||
|
```
|
||||||
|
|
||||||
|
If you like, you can bind the flag to a variable using the Var() functions.
|
||||||
|
|
||||||
|
``` go
|
||||||
|
var flagvar int
|
||||||
|
func init() {
|
||||||
|
flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Or you can create custom flags that satisfy the Value interface (with
|
||||||
|
pointer receivers) and couple them to flag parsing by
|
||||||
|
|
||||||
|
``` go
|
||||||
|
flag.Var(&flagVal, "name", "help message for flagname")
|
||||||
|
```
|
||||||
|
|
||||||
|
For such flags, the default value is just the initial value of the variable.
|
||||||
|
|
||||||
|
After all flags are defined, call
|
||||||
|
|
||||||
|
``` go
|
||||||
|
flag.Parse()
|
||||||
|
```
|
||||||
|
|
||||||
|
to parse the command line into the defined flags.
|
||||||
|
|
||||||
|
Flags may then be used directly. If you're using the flags themselves,
|
||||||
|
they are all pointers; if you bind to variables, they're values.
|
||||||
|
|
||||||
|
``` go
|
||||||
|
fmt.Println("ip has value ", *ip)
|
||||||
|
fmt.Println("flagvar has value ", flagvar)
|
||||||
|
```
|
||||||
|
|
||||||
|
There are helpers function to get values later if you have the FlagSet but
|
||||||
|
it was difficult to keep up with all of the flag pointers in your code.
|
||||||
|
If you have a pflag.FlagSet with a flag called 'flagname' of type int you
|
||||||
|
can use GetInt() to get the int value. But notice that 'flagname' must exist
|
||||||
|
and it must be an int. GetString("flagname") will fail.
|
||||||
|
|
||||||
|
``` go
|
||||||
|
i, err := flagset.GetInt("flagname")
|
||||||
|
```
|
||||||
|
|
||||||
|
After parsing, the arguments after the flag are available as the
|
||||||
|
slice flag.Args() or individually as flag.Arg(i).
|
||||||
|
The arguments are indexed from 0 through flag.NArg()-1.
|
||||||
|
|
||||||
|
The pflag package also defines some new functions that are not in flag,
|
||||||
|
that give one-letter shorthands for flags. You can use these by appending
|
||||||
|
'P' to the name of any function that defines a flag.
|
||||||
|
|
||||||
|
``` go
|
||||||
|
var ip = flag.IntP("flagname", "f", 1234, "help message")
|
||||||
|
var flagvar bool
|
||||||
|
func init() {
|
||||||
|
flag.BoolVarP("boolname", "b", true, "help message")
|
||||||
|
}
|
||||||
|
flag.VarP(&flagVar, "varname", "v", 1234, "help message")
|
||||||
|
```
|
||||||
|
|
||||||
|
Shorthand letters can be used with single dashes on the command line.
|
||||||
|
Boolean shorthand flags can be combined with other shorthand flags.
|
||||||
|
|
||||||
|
The default set of command-line flags is controlled by
|
||||||
|
top-level functions. The FlagSet type allows one to define
|
||||||
|
independent sets of flags, such as to implement subcommands
|
||||||
|
in a command-line interface. The methods of FlagSet are
|
||||||
|
analogous to the top-level functions for the command-line
|
||||||
|
flag set.
|
||||||
|
|
||||||
|
## Setting no option default values for flags
|
||||||
|
|
||||||
|
After you create a flag it is possible to set the pflag.NoOptDefVal for
|
||||||
|
the given flag. Doing this changes the meaning of the flag slightly. If
|
||||||
|
a flag has a NoOptDefVal and the flag is set on the command line without
|
||||||
|
an option the flag will be set to the NoOptDefVal. For example given:
|
||||||
|
|
||||||
|
``` go
|
||||||
|
var ip = flag.IntP("flagname", "f", 1234, "help message")
|
||||||
|
flag.Lookup("flagname").NoOptDefVal = "4321"
|
||||||
|
```
|
||||||
|
|
||||||
|
Would result in something like
|
||||||
|
|
||||||
|
| Parsed Arguments | Resulting Value |
|
||||||
|
| ------------- | ------------- |
|
||||||
|
| --flagname=1357 | ip=1357 |
|
||||||
|
| --flagname | ip=4321 |
|
||||||
|
| [nothing] | ip=1234 |
|
||||||
|
|
||||||
|
## Command line flag syntax
|
||||||
|
|
||||||
|
```
|
||||||
|
--flag // boolean flags, or flags with no option default values
|
||||||
|
--flag x // only on flags without a default value
|
||||||
|
--flag=x
|
||||||
|
```
|
||||||
|
|
||||||
|
Unlike the flag package, a single dash before an option means something
|
||||||
|
different than a double dash. Single dashes signify a series of shorthand
|
||||||
|
letters for flags. All but the last shorthand letter must be boolean flags
|
||||||
|
or a flag with a default value
|
||||||
|
|
||||||
|
```
|
||||||
|
// boolean or flags where the 'no option default value' is set
|
||||||
|
-f
|
||||||
|
-f=true
|
||||||
|
-abc
|
||||||
|
but
|
||||||
|
-b true is INVALID
|
||||||
|
|
||||||
|
// non-boolean and flags without a 'no option default value'
|
||||||
|
-n 1234
|
||||||
|
-n=1234
|
||||||
|
-n1234
|
||||||
|
|
||||||
|
// mixed
|
||||||
|
-abcs "hello"
|
||||||
|
-absd="hello"
|
||||||
|
-abcs1234
|
||||||
|
```
|
||||||
|
|
||||||
|
Flag parsing stops after the terminator "--". Unlike the flag package,
|
||||||
|
flags can be interspersed with arguments anywhere on the command line
|
||||||
|
before this terminator.
|
||||||
|
|
||||||
|
Integer flags accept 1234, 0664, 0x1234 and may be negative.
|
||||||
|
Boolean flags (in their long form) accept 1, 0, t, f, true, false,
|
||||||
|
TRUE, FALSE, True, False.
|
||||||
|
Duration flags accept any input valid for time.ParseDuration.
|
||||||
|
|
||||||
|
## Mutating or "Normalizing" Flag names
|
||||||
|
|
||||||
|
It is possible to set a custom flag name 'normalization function.' It allows flag names to be mutated both when created in the code and when used on the command line to some 'normalized' form. The 'normalized' form is used for comparison. Two examples of using the custom normalization func follow.
|
||||||
|
|
||||||
|
**Example #1**: You want -, _, and . in flags to compare the same. aka --my-flag == --my_flag == --my.flag
|
||||||
|
|
||||||
|
``` go
|
||||||
|
func wordSepNormalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName {
|
||||||
|
from := []string{"-", "_"}
|
||||||
|
to := "."
|
||||||
|
for _, sep := range from {
|
||||||
|
name = strings.Replace(name, sep, to, -1)
|
||||||
|
}
|
||||||
|
return pflag.NormalizedName(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
myFlagSet.SetNormalizeFunc(wordSepNormalizeFunc)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example #2**: You want to alias two flags. aka --old-flag-name == --new-flag-name
|
||||||
|
|
||||||
|
``` go
|
||||||
|
func aliasNormalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName {
|
||||||
|
switch name {
|
||||||
|
case "old-flag-name":
|
||||||
|
name = "new-flag-name"
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return pflag.NormalizedName(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
myFlagSet.SetNormalizeFunc(aliasNormalizeFunc)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Deprecating a flag or its shorthand
|
||||||
|
It is possible to deprecate a flag, or just its shorthand. Deprecating a flag/shorthand hides it from help text and prints a usage message when the deprecated flag/shorthand is used.
|
||||||
|
|
||||||
|
**Example #1**: You want to deprecate a flag named "badflag" as well as inform the users what flag they should use instead.
|
||||||
|
```go
|
||||||
|
// deprecate a flag by specifying its name and a usage message
|
||||||
|
flags.MarkDeprecated("badflag", "please use --good-flag instead")
|
||||||
|
```
|
||||||
|
This hides "badflag" from help text, and prints `Flag --badflag has been deprecated, please use --good-flag instead` when "badflag" is used.
|
||||||
|
|
||||||
|
**Example #2**: You want to keep a flag name "noshorthandflag" but deprecate its shortname "n".
|
||||||
|
```go
|
||||||
|
// deprecate a flag shorthand by specifying its flag name and a usage message
|
||||||
|
flags.MarkShorthandDeprecated("noshorthandflag", "please use --noshorthandflag only")
|
||||||
|
```
|
||||||
|
This hides the shortname "n" from help text, and prints `Flag shorthand -n has been deprecated, please use --noshorthandflag only` when the shorthand "n" is used.
|
||||||
|
|
||||||
|
Note that usage message is essential here, and it should not be empty.
|
||||||
|
|
||||||
|
## Hidden flags
|
||||||
|
It is possible to mark a flag as hidden, meaning it will still function as normal, however will not show up in usage/help text.
|
||||||
|
|
||||||
|
**Example**: You have a flag named "secretFlag" that you need for internal use only and don't want it showing up in help text, or for its usage text to be available.
|
||||||
|
```go
|
||||||
|
// hide a flag by specifying its name
|
||||||
|
flags.MarkHidden("secretFlag")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Supporting Go flags when using pflag
|
||||||
|
In order to support flags defined using Go's `flag` package, they must be added to the `pflag` flagset. This is usually necessary
|
||||||
|
to support flags defined by third-party dependencies (e.g. `golang/glog`).
|
||||||
|
|
||||||
|
**Example**: You want to add the Go flags to the `CommandLine` flagset
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
goflag "flag"
|
||||||
|
flag "github.com/spf13/pflag"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ip *int = flag.Int("flagname", 1234, "help message for flagname")
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.CommandLine.AddGoFlagSet(goflag.CommandLine)
|
||||||
|
flag.Parse()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## More info
|
||||||
|
|
||||||
|
You can see the full reference documentation of the pflag package
|
||||||
|
[at godoc.org][3], or through go's standard documentation system by
|
||||||
|
running `godoc -http=:6060` and browsing to
|
||||||
|
[http://localhost:6060/pkg/github.com/ogier/pflag][2] after
|
||||||
|
installation.
|
||||||
|
|
||||||
|
[2]: http://localhost:6060/pkg/github.com/ogier/pflag
|
||||||
|
[3]: http://godoc.org/github.com/ogier/pflag
|
97
vendor/github.com/spf13/pflag/bool.go
generated
vendored
Normal file
97
vendor/github.com/spf13/pflag/bool.go
generated
vendored
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
package pflag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// optional interface to indicate boolean flags that can be
|
||||||
|
// supplied without "=value" text
|
||||||
|
type boolFlag interface {
|
||||||
|
Value
|
||||||
|
IsBoolFlag() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- bool Value
|
||||||
|
type boolValue bool
|
||||||
|
|
||||||
|
func newBoolValue(val bool, p *bool) *boolValue {
|
||||||
|
*p = val
|
||||||
|
return (*boolValue)(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *boolValue) Set(s string) error {
|
||||||
|
v, err := strconv.ParseBool(s)
|
||||||
|
*b = boolValue(v)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *boolValue) Type() string {
|
||||||
|
return "bool"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *boolValue) String() string { return fmt.Sprintf("%v", *b) }
|
||||||
|
|
||||||
|
func (b *boolValue) IsBoolFlag() bool { return true }
|
||||||
|
|
||||||
|
func boolConv(sval string) (interface{}, error) {
|
||||||
|
return strconv.ParseBool(sval)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBool return the bool value of a flag with the given name
|
||||||
|
func (f *FlagSet) GetBool(name string) (bool, error) {
|
||||||
|
val, err := f.getFlagType(name, "bool", boolConv)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return val.(bool), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoolVar defines a bool flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to a bool variable in which to store the value of the flag.
|
||||||
|
func (f *FlagSet) BoolVar(p *bool, name string, value bool, usage string) {
|
||||||
|
f.BoolVarP(p, name, "", value, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoolVarP is like BoolVar, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) BoolVarP(p *bool, name, shorthand string, value bool, usage string) {
|
||||||
|
flag := f.VarPF(newBoolValue(value, p), name, shorthand, usage)
|
||||||
|
flag.NoOptDefVal = "true"
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoolVar defines a bool flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to a bool variable in which to store the value of the flag.
|
||||||
|
func BoolVar(p *bool, name string, value bool, usage string) {
|
||||||
|
BoolVarP(p, name, "", value, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoolVarP is like BoolVar, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func BoolVarP(p *bool, name, shorthand string, value bool, usage string) {
|
||||||
|
flag := CommandLine.VarPF(newBoolValue(value, p), name, shorthand, usage)
|
||||||
|
flag.NoOptDefVal = "true"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bool defines a bool flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of a bool variable that stores the value of the flag.
|
||||||
|
func (f *FlagSet) Bool(name string, value bool, usage string) *bool {
|
||||||
|
return f.BoolP(name, "", value, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoolP is like Bool, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) BoolP(name, shorthand string, value bool, usage string) *bool {
|
||||||
|
p := new(bool)
|
||||||
|
f.BoolVarP(p, name, shorthand, value, usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bool defines a bool flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of a bool variable that stores the value of the flag.
|
||||||
|
func Bool(name string, value bool, usage string) *bool {
|
||||||
|
return BoolP(name, "", value, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoolP is like Bool, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func BoolP(name, shorthand string, value bool, usage string) *bool {
|
||||||
|
b := CommandLine.BoolP(name, shorthand, value, usage)
|
||||||
|
return b
|
||||||
|
}
|
97
vendor/github.com/spf13/pflag/count.go
generated
vendored
Normal file
97
vendor/github.com/spf13/pflag/count.go
generated
vendored
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
package pflag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// -- count Value
|
||||||
|
type countValue int
|
||||||
|
|
||||||
|
func newCountValue(val int, p *int) *countValue {
|
||||||
|
*p = val
|
||||||
|
return (*countValue)(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *countValue) Set(s string) error {
|
||||||
|
v, err := strconv.ParseInt(s, 0, 64)
|
||||||
|
// -1 means that no specific value was passed, so increment
|
||||||
|
if v == -1 {
|
||||||
|
*i = countValue(*i + 1)
|
||||||
|
} else {
|
||||||
|
*i = countValue(v)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *countValue) Type() string {
|
||||||
|
return "count"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *countValue) String() string { return fmt.Sprintf("%v", *i) }
|
||||||
|
|
||||||
|
func countConv(sval string) (interface{}, error) {
|
||||||
|
i, err := strconv.Atoi(sval)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCount return the int value of a flag with the given name
|
||||||
|
func (f *FlagSet) GetCount(name string) (int, error) {
|
||||||
|
val, err := f.getFlagType(name, "count", countConv)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return val.(int), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CountVar defines a count flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to an int variable in which to store the value of the flag.
|
||||||
|
// A count flag will add 1 to its value evey time it is found on the command line
|
||||||
|
func (f *FlagSet) CountVar(p *int, name string, usage string) {
|
||||||
|
f.CountVarP(p, name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CountVarP is like CountVar only take a shorthand for the flag name.
|
||||||
|
func (f *FlagSet) CountVarP(p *int, name, shorthand string, usage string) {
|
||||||
|
flag := f.VarPF(newCountValue(0, p), name, shorthand, usage)
|
||||||
|
flag.NoOptDefVal = "-1"
|
||||||
|
}
|
||||||
|
|
||||||
|
// CountVar like CountVar only the flag is placed on the CommandLine instead of a given flag set
|
||||||
|
func CountVar(p *int, name string, usage string) {
|
||||||
|
CommandLine.CountVar(p, name, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CountVarP is like CountVar only take a shorthand for the flag name.
|
||||||
|
func CountVarP(p *int, name, shorthand string, usage string) {
|
||||||
|
CommandLine.CountVarP(p, name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count defines a count flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of an int variable that stores the value of the flag.
|
||||||
|
// A count flag will add 1 to its value evey time it is found on the command line
|
||||||
|
func (f *FlagSet) Count(name string, usage string) *int {
|
||||||
|
p := new(int)
|
||||||
|
f.CountVarP(p, name, "", usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// CountP is like Count only takes a shorthand for the flag name.
|
||||||
|
func (f *FlagSet) CountP(name, shorthand string, usage string) *int {
|
||||||
|
p := new(int)
|
||||||
|
f.CountVarP(p, name, shorthand, usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count like Count only the flag is placed on the CommandLine isntead of a given flag set
|
||||||
|
func Count(name string, usage string) *int {
|
||||||
|
return CommandLine.CountP(name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CountP is like Count only takes a shorthand for the flag name.
|
||||||
|
func CountP(name, shorthand string, usage string) *int {
|
||||||
|
return CommandLine.CountP(name, shorthand, usage)
|
||||||
|
}
|
86
vendor/github.com/spf13/pflag/duration.go
generated
vendored
Normal file
86
vendor/github.com/spf13/pflag/duration.go
generated
vendored
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
package pflag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// -- time.Duration Value
|
||||||
|
type durationValue time.Duration
|
||||||
|
|
||||||
|
func newDurationValue(val time.Duration, p *time.Duration) *durationValue {
|
||||||
|
*p = val
|
||||||
|
return (*durationValue)(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *durationValue) Set(s string) error {
|
||||||
|
v, err := time.ParseDuration(s)
|
||||||
|
*d = durationValue(v)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *durationValue) Type() string {
|
||||||
|
return "duration"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *durationValue) String() string { return (*time.Duration)(d).String() }
|
||||||
|
|
||||||
|
func durationConv(sval string) (interface{}, error) {
|
||||||
|
return time.ParseDuration(sval)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDuration return the duration value of a flag with the given name
|
||||||
|
func (f *FlagSet) GetDuration(name string) (time.Duration, error) {
|
||||||
|
val, err := f.getFlagType(name, "duration", durationConv)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return val.(time.Duration), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DurationVar defines a time.Duration flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to a time.Duration variable in which to store the value of the flag.
|
||||||
|
func (f *FlagSet) DurationVar(p *time.Duration, name string, value time.Duration, usage string) {
|
||||||
|
f.VarP(newDurationValue(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DurationVarP is like DurationVar, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) DurationVarP(p *time.Duration, name, shorthand string, value time.Duration, usage string) {
|
||||||
|
f.VarP(newDurationValue(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DurationVar defines a time.Duration flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to a time.Duration variable in which to store the value of the flag.
|
||||||
|
func DurationVar(p *time.Duration, name string, value time.Duration, usage string) {
|
||||||
|
CommandLine.VarP(newDurationValue(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DurationVarP is like DurationVar, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func DurationVarP(p *time.Duration, name, shorthand string, value time.Duration, usage string) {
|
||||||
|
CommandLine.VarP(newDurationValue(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Duration defines a time.Duration flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of a time.Duration variable that stores the value of the flag.
|
||||||
|
func (f *FlagSet) Duration(name string, value time.Duration, usage string) *time.Duration {
|
||||||
|
p := new(time.Duration)
|
||||||
|
f.DurationVarP(p, name, "", value, usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// DurationP is like Duration, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) DurationP(name, shorthand string, value time.Duration, usage string) *time.Duration {
|
||||||
|
p := new(time.Duration)
|
||||||
|
f.DurationVarP(p, name, shorthand, value, usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Duration defines a time.Duration flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of a time.Duration variable that stores the value of the flag.
|
||||||
|
func Duration(name string, value time.Duration, usage string) *time.Duration {
|
||||||
|
return CommandLine.DurationP(name, "", value, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DurationP is like Duration, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func DurationP(name, shorthand string, value time.Duration, usage string) *time.Duration {
|
||||||
|
return CommandLine.DurationP(name, shorthand, value, usage)
|
||||||
|
}
|
934
vendor/github.com/spf13/pflag/flag.go
generated
vendored
Normal file
934
vendor/github.com/spf13/pflag/flag.go
generated
vendored
Normal file
|
@ -0,0 +1,934 @@
|
||||||
|
// Copyright 2009 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 pflag is a drop-in replacement for Go's flag package, implementing
|
||||||
|
POSIX/GNU-style --flags.
|
||||||
|
|
||||||
|
pflag is compatible with the GNU extensions to the POSIX recommendations
|
||||||
|
for command-line options. See
|
||||||
|
http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
pflag is a drop-in replacement of Go's native flag package. If you import
|
||||||
|
pflag under the name "flag" then all code should continue to function
|
||||||
|
with no changes.
|
||||||
|
|
||||||
|
import flag "github.com/ogier/pflag"
|
||||||
|
|
||||||
|
There is one exception to this: if you directly instantiate the Flag struct
|
||||||
|
there is one more field "Shorthand" that you will need to set.
|
||||||
|
Most code never instantiates this struct directly, and instead uses
|
||||||
|
functions such as String(), BoolVar(), and Var(), and is therefore
|
||||||
|
unaffected.
|
||||||
|
|
||||||
|
Define flags using flag.String(), Bool(), Int(), etc.
|
||||||
|
|
||||||
|
This declares an integer flag, -flagname, stored in the pointer ip, with type *int.
|
||||||
|
var ip = flag.Int("flagname", 1234, "help message for flagname")
|
||||||
|
If you like, you can bind the flag to a variable using the Var() functions.
|
||||||
|
var flagvar int
|
||||||
|
func init() {
|
||||||
|
flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname")
|
||||||
|
}
|
||||||
|
Or you can create custom flags that satisfy the Value interface (with
|
||||||
|
pointer receivers) and couple them to flag parsing by
|
||||||
|
flag.Var(&flagVal, "name", "help message for flagname")
|
||||||
|
For such flags, the default value is just the initial value of the variable.
|
||||||
|
|
||||||
|
After all flags are defined, call
|
||||||
|
flag.Parse()
|
||||||
|
to parse the command line into the defined flags.
|
||||||
|
|
||||||
|
Flags may then be used directly. If you're using the flags themselves,
|
||||||
|
they are all pointers; if you bind to variables, they're values.
|
||||||
|
fmt.Println("ip has value ", *ip)
|
||||||
|
fmt.Println("flagvar has value ", flagvar)
|
||||||
|
|
||||||
|
After parsing, the arguments after the flag are available as the
|
||||||
|
slice flag.Args() or individually as flag.Arg(i).
|
||||||
|
The arguments are indexed from 0 through flag.NArg()-1.
|
||||||
|
|
||||||
|
The pflag package also defines some new functions that are not in flag,
|
||||||
|
that give one-letter shorthands for flags. You can use these by appending
|
||||||
|
'P' to the name of any function that defines a flag.
|
||||||
|
var ip = flag.IntP("flagname", "f", 1234, "help message")
|
||||||
|
var flagvar bool
|
||||||
|
func init() {
|
||||||
|
flag.BoolVarP("boolname", "b", true, "help message")
|
||||||
|
}
|
||||||
|
flag.VarP(&flagVar, "varname", "v", 1234, "help message")
|
||||||
|
Shorthand letters can be used with single dashes on the command line.
|
||||||
|
Boolean shorthand flags can be combined with other shorthand flags.
|
||||||
|
|
||||||
|
Command line flag syntax:
|
||||||
|
--flag // boolean flags only
|
||||||
|
--flag=x
|
||||||
|
|
||||||
|
Unlike the flag package, a single dash before an option means something
|
||||||
|
different than a double dash. Single dashes signify a series of shorthand
|
||||||
|
letters for flags. All but the last shorthand letter must be boolean flags.
|
||||||
|
// boolean flags
|
||||||
|
-f
|
||||||
|
-abc
|
||||||
|
// non-boolean flags
|
||||||
|
-n 1234
|
||||||
|
-Ifile
|
||||||
|
// mixed
|
||||||
|
-abcs "hello"
|
||||||
|
-abcn1234
|
||||||
|
|
||||||
|
Flag parsing stops after the terminator "--". Unlike the flag package,
|
||||||
|
flags can be interspersed with arguments anywhere on the command line
|
||||||
|
before this terminator.
|
||||||
|
|
||||||
|
Integer flags accept 1234, 0664, 0x1234 and may be negative.
|
||||||
|
Boolean flags (in their long form) accept 1, 0, t, f, true, false,
|
||||||
|
TRUE, FALSE, True, False.
|
||||||
|
Duration flags accept any input valid for time.ParseDuration.
|
||||||
|
|
||||||
|
The default set of command-line flags is controlled by
|
||||||
|
top-level functions. The FlagSet type allows one to define
|
||||||
|
independent sets of flags, such as to implement subcommands
|
||||||
|
in a command-line interface. The methods of FlagSet are
|
||||||
|
analogous to the top-level functions for the command-line
|
||||||
|
flag set.
|
||||||
|
*/
|
||||||
|
package pflag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrHelp is the error returned if the flag -help is invoked but no such flag is defined.
|
||||||
|
var ErrHelp = errors.New("pflag: help requested")
|
||||||
|
|
||||||
|
// ErrorHandling defines how to handle flag parsing errors.
|
||||||
|
type ErrorHandling int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ContinueOnError will return an err from Parse() if an error is found
|
||||||
|
ContinueOnError ErrorHandling = iota
|
||||||
|
// ExitOnError will call os.Exit(2) if an error is found when parsing
|
||||||
|
ExitOnError
|
||||||
|
// PanicOnError will panic() if an error is found when parsing flags
|
||||||
|
PanicOnError
|
||||||
|
)
|
||||||
|
|
||||||
|
// NormalizedName is a flag name that has been normalized according to rules
|
||||||
|
// for the FlagSet (e.g. making '-' and '_' equivalent).
|
||||||
|
type NormalizedName string
|
||||||
|
|
||||||
|
// A FlagSet represents a set of defined flags.
|
||||||
|
type FlagSet struct {
|
||||||
|
// Usage is the function called when an error occurs while parsing flags.
|
||||||
|
// The field is a function (not a method) that may be changed to point to
|
||||||
|
// a custom error handler.
|
||||||
|
Usage func()
|
||||||
|
|
||||||
|
name string
|
||||||
|
parsed bool
|
||||||
|
actual map[NormalizedName]*Flag
|
||||||
|
formal map[NormalizedName]*Flag
|
||||||
|
shorthands map[byte]*Flag
|
||||||
|
args []string // arguments after flags
|
||||||
|
argsLenAtDash int // len(args) when a '--' was located when parsing, or -1 if no --
|
||||||
|
exitOnError bool // does the program exit if there's an error?
|
||||||
|
errorHandling ErrorHandling
|
||||||
|
output io.Writer // nil means stderr; use out() accessor
|
||||||
|
interspersed bool // allow interspersed option/non-option args
|
||||||
|
normalizeNameFunc func(f *FlagSet, name string) NormalizedName
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Flag represents the state of a flag.
|
||||||
|
type Flag struct {
|
||||||
|
Name string // name as it appears on command line
|
||||||
|
Shorthand string // one-letter abbreviated flag
|
||||||
|
Usage string // help message
|
||||||
|
Value Value // value as set
|
||||||
|
DefValue string // default value (as text); for usage message
|
||||||
|
Changed bool // If the user set the value (or if left to default)
|
||||||
|
NoOptDefVal string //default value (as text); if the flag is on the command line without any options
|
||||||
|
Deprecated string // If this flag is deprecated, this string is the new or now thing to use
|
||||||
|
Hidden bool // used by cobra.Command to allow flags to be hidden from help/usage text
|
||||||
|
ShorthandDeprecated string // If the shorthand of this flag is deprecated, this string is the new or now thing to use
|
||||||
|
Annotations map[string][]string // used by cobra.Command bash autocomple code
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value is the interface to the dynamic value stored in a flag.
|
||||||
|
// (The default value is represented as a string.)
|
||||||
|
type Value interface {
|
||||||
|
String() string
|
||||||
|
Set(string) error
|
||||||
|
Type() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// sortFlags returns the flags as a slice in lexicographical sorted order.
|
||||||
|
func sortFlags(flags map[NormalizedName]*Flag) []*Flag {
|
||||||
|
list := make(sort.StringSlice, len(flags))
|
||||||
|
i := 0
|
||||||
|
for k := range flags {
|
||||||
|
list[i] = string(k)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
list.Sort()
|
||||||
|
result := make([]*Flag, len(list))
|
||||||
|
for i, name := range list {
|
||||||
|
result[i] = flags[NormalizedName(name)]
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNormalizeFunc allows you to add a function which can translate flag names.
|
||||||
|
// Flags added to the FlagSet will be translated and then when anything tries to
|
||||||
|
// look up the flag that will also be translated. So it would be possible to create
|
||||||
|
// a flag named "getURL" and have it translated to "geturl". A user could then pass
|
||||||
|
// "--getUrl" which may also be translated to "geturl" and everything will work.
|
||||||
|
func (f *FlagSet) SetNormalizeFunc(n func(f *FlagSet, name string) NormalizedName) {
|
||||||
|
f.normalizeNameFunc = n
|
||||||
|
for k, v := range f.formal {
|
||||||
|
delete(f.formal, k)
|
||||||
|
nname := f.normalizeFlagName(string(k))
|
||||||
|
f.formal[nname] = v
|
||||||
|
v.Name = string(nname)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNormalizeFunc returns the previously set NormalizeFunc of a function which
|
||||||
|
// does no translation, if not set previously.
|
||||||
|
func (f *FlagSet) GetNormalizeFunc() func(f *FlagSet, name string) NormalizedName {
|
||||||
|
if f.normalizeNameFunc != nil {
|
||||||
|
return f.normalizeNameFunc
|
||||||
|
}
|
||||||
|
return func(f *FlagSet, name string) NormalizedName { return NormalizedName(name) }
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FlagSet) normalizeFlagName(name string) NormalizedName {
|
||||||
|
n := f.GetNormalizeFunc()
|
||||||
|
return n(f, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FlagSet) out() io.Writer {
|
||||||
|
if f.output == nil {
|
||||||
|
return os.Stderr
|
||||||
|
}
|
||||||
|
return f.output
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetOutput sets the destination for usage and error messages.
|
||||||
|
// If output is nil, os.Stderr is used.
|
||||||
|
func (f *FlagSet) SetOutput(output io.Writer) {
|
||||||
|
f.output = output
|
||||||
|
}
|
||||||
|
|
||||||
|
// VisitAll visits the flags in lexicographical order, calling fn for each.
|
||||||
|
// It visits all flags, even those not set.
|
||||||
|
func (f *FlagSet) VisitAll(fn func(*Flag)) {
|
||||||
|
for _, flag := range sortFlags(f.formal) {
|
||||||
|
fn(flag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasFlags returns a bool to indicate if the FlagSet has any flags definied.
|
||||||
|
func (f *FlagSet) HasFlags() bool {
|
||||||
|
return len(f.formal) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasAvailableFlags returns a bool to indicate if the FlagSet has any flags
|
||||||
|
// definied that are not hidden or deprecated.
|
||||||
|
func (f *FlagSet) HasAvailableFlags() bool {
|
||||||
|
for _, flag := range f.formal {
|
||||||
|
if !flag.Hidden && len(flag.Deprecated) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// VisitAll visits the command-line flags in lexicographical order, calling
|
||||||
|
// fn for each. It visits all flags, even those not set.
|
||||||
|
func VisitAll(fn func(*Flag)) {
|
||||||
|
CommandLine.VisitAll(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Visit visits the flags in lexicographical order, calling fn for each.
|
||||||
|
// It visits only those flags that have been set.
|
||||||
|
func (f *FlagSet) Visit(fn func(*Flag)) {
|
||||||
|
for _, flag := range sortFlags(f.actual) {
|
||||||
|
fn(flag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Visit visits the command-line flags in lexicographical order, calling fn
|
||||||
|
// for each. It visits only those flags that have been set.
|
||||||
|
func Visit(fn func(*Flag)) {
|
||||||
|
CommandLine.Visit(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lookup returns the Flag structure of the named flag, returning nil if none exists.
|
||||||
|
func (f *FlagSet) Lookup(name string) *Flag {
|
||||||
|
return f.lookup(f.normalizeFlagName(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
// lookup returns the Flag structure of the named flag, returning nil if none exists.
|
||||||
|
func (f *FlagSet) lookup(name NormalizedName) *Flag {
|
||||||
|
return f.formal[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
// func to return a given type for a given flag name
|
||||||
|
func (f *FlagSet) getFlagType(name string, ftype string, convFunc func(sval string) (interface{}, error)) (interface{}, error) {
|
||||||
|
flag := f.Lookup(name)
|
||||||
|
if flag == nil {
|
||||||
|
err := fmt.Errorf("flag accessed but not defined: %s", name)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if flag.Value.Type() != ftype {
|
||||||
|
err := fmt.Errorf("trying to get %s value of flag of type %s", ftype, flag.Value.Type())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sval := flag.Value.String()
|
||||||
|
result, err := convFunc(sval)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArgsLenAtDash will return the length of f.Args at the moment when a -- was
|
||||||
|
// found during arg parsing. This allows your program to know which args were
|
||||||
|
// before the -- and which came after.
|
||||||
|
func (f *FlagSet) ArgsLenAtDash() int {
|
||||||
|
return f.argsLenAtDash
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarkDeprecated indicated that a flag is deprecated in your program. It will
|
||||||
|
// continue to function but will not show up in help or usage messages. Using
|
||||||
|
// this flag will also print the given usageMessage.
|
||||||
|
func (f *FlagSet) MarkDeprecated(name string, usageMessage string) error {
|
||||||
|
flag := f.Lookup(name)
|
||||||
|
if flag == nil {
|
||||||
|
return fmt.Errorf("flag %q does not exist", name)
|
||||||
|
}
|
||||||
|
if len(usageMessage) == 0 {
|
||||||
|
return fmt.Errorf("deprecated message for flag %q must be set", name)
|
||||||
|
}
|
||||||
|
flag.Deprecated = usageMessage
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarkShorthandDeprecated will mark the shorthand of a flag deprecated in your
|
||||||
|
// program. It will continue to function but will not show up in help or usage
|
||||||
|
// messages. Using this flag will also print the given usageMessage.
|
||||||
|
func (f *FlagSet) MarkShorthandDeprecated(name string, usageMessage string) error {
|
||||||
|
flag := f.Lookup(name)
|
||||||
|
if flag == nil {
|
||||||
|
return fmt.Errorf("flag %q does not exist", name)
|
||||||
|
}
|
||||||
|
if len(usageMessage) == 0 {
|
||||||
|
return fmt.Errorf("deprecated message for flag %q must be set", name)
|
||||||
|
}
|
||||||
|
flag.ShorthandDeprecated = usageMessage
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarkHidden sets a flag to 'hidden' in your program. It will continue to
|
||||||
|
// function but will not show up in help or usage messages.
|
||||||
|
func (f *FlagSet) MarkHidden(name string) error {
|
||||||
|
flag := f.Lookup(name)
|
||||||
|
if flag == nil {
|
||||||
|
return fmt.Errorf("flag %q does not exist", name)
|
||||||
|
}
|
||||||
|
flag.Hidden = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lookup returns the Flag structure of the named command-line flag,
|
||||||
|
// returning nil if none exists.
|
||||||
|
func Lookup(name string) *Flag {
|
||||||
|
return CommandLine.Lookup(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets the value of the named flag.
|
||||||
|
func (f *FlagSet) Set(name, value string) error {
|
||||||
|
normalName := f.normalizeFlagName(name)
|
||||||
|
flag, ok := f.formal[normalName]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("no such flag -%v", name)
|
||||||
|
}
|
||||||
|
err := flag.Value.Set(value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if f.actual == nil {
|
||||||
|
f.actual = make(map[NormalizedName]*Flag)
|
||||||
|
}
|
||||||
|
f.actual[normalName] = flag
|
||||||
|
flag.Changed = true
|
||||||
|
if len(flag.Deprecated) > 0 {
|
||||||
|
fmt.Fprintf(os.Stderr, "Flag --%s has been deprecated, %s\n", flag.Name, flag.Deprecated)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAnnotation allows one to set arbitrary annotations on a flag in the FlagSet.
|
||||||
|
// This is sometimes used by spf13/cobra programs which want to generate additional
|
||||||
|
// bash completion information.
|
||||||
|
func (f *FlagSet) SetAnnotation(name, key string, values []string) error {
|
||||||
|
normalName := f.normalizeFlagName(name)
|
||||||
|
flag, ok := f.formal[normalName]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("no such flag -%v", name)
|
||||||
|
}
|
||||||
|
if flag.Annotations == nil {
|
||||||
|
flag.Annotations = map[string][]string{}
|
||||||
|
}
|
||||||
|
flag.Annotations[key] = values
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Changed returns true if the flag was explicitly set during Parse() and false
|
||||||
|
// otherwise
|
||||||
|
func (f *FlagSet) Changed(name string) bool {
|
||||||
|
flag := f.Lookup(name)
|
||||||
|
// If a flag doesn't exist, it wasn't changed....
|
||||||
|
if flag == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return flag.Changed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets the value of the named command-line flag.
|
||||||
|
func Set(name, value string) error {
|
||||||
|
return CommandLine.Set(name, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintDefaults prints, to standard error unless configured
|
||||||
|
// otherwise, the default values of all defined flags in the set.
|
||||||
|
func (f *FlagSet) PrintDefaults() {
|
||||||
|
usages := f.FlagUsages()
|
||||||
|
fmt.Fprintf(f.out(), "%s", usages)
|
||||||
|
}
|
||||||
|
|
||||||
|
// isZeroValue guesses whether the string represents the zero
|
||||||
|
// value for a flag. It is not accurate but in practice works OK.
|
||||||
|
func isZeroValue(value string) bool {
|
||||||
|
switch value {
|
||||||
|
case "false":
|
||||||
|
return true
|
||||||
|
case "<nil>":
|
||||||
|
return true
|
||||||
|
case "":
|
||||||
|
return true
|
||||||
|
case "0":
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnquoteUsage extracts a back-quoted name from the usage
|
||||||
|
// string for a flag and returns it and the un-quoted usage.
|
||||||
|
// Given "a `name` to show" it returns ("name", "a name to show").
|
||||||
|
// If there are no back quotes, the name is an educated guess of the
|
||||||
|
// type of the flag's value, or the empty string if the flag is boolean.
|
||||||
|
func UnquoteUsage(flag *Flag) (name string, usage string) {
|
||||||
|
// Look for a back-quoted name, but avoid the strings package.
|
||||||
|
usage = flag.Usage
|
||||||
|
for i := 0; i < len(usage); i++ {
|
||||||
|
if usage[i] == '`' {
|
||||||
|
for j := i + 1; j < len(usage); j++ {
|
||||||
|
if usage[j] == '`' {
|
||||||
|
name = usage[i+1 : j]
|
||||||
|
usage = usage[:i] + name + usage[j+1:]
|
||||||
|
return name, usage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break // Only one back quote; use type name.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// No explicit name, so use type if we can find one.
|
||||||
|
name = "value"
|
||||||
|
switch flag.Value.(type) {
|
||||||
|
case boolFlag:
|
||||||
|
name = ""
|
||||||
|
case *durationValue:
|
||||||
|
name = "duration"
|
||||||
|
case *float64Value:
|
||||||
|
name = "float"
|
||||||
|
case *intValue, *int64Value:
|
||||||
|
name = "int"
|
||||||
|
case *stringValue:
|
||||||
|
name = "string"
|
||||||
|
case *uintValue, *uint64Value:
|
||||||
|
name = "uint"
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlagUsages Returns a string containing the usage information for all flags in
|
||||||
|
// the FlagSet
|
||||||
|
func (f *FlagSet) FlagUsages() string {
|
||||||
|
x := new(bytes.Buffer)
|
||||||
|
|
||||||
|
lines := make([]string, 0, len(f.formal))
|
||||||
|
|
||||||
|
maxlen := 0
|
||||||
|
f.VisitAll(func(flag *Flag) {
|
||||||
|
if len(flag.Deprecated) > 0 || flag.Hidden {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
line := ""
|
||||||
|
if len(flag.Shorthand) > 0 && len(flag.ShorthandDeprecated) == 0 {
|
||||||
|
line = fmt.Sprintf(" -%s, --%s", flag.Shorthand, flag.Name)
|
||||||
|
} else {
|
||||||
|
line = fmt.Sprintf(" --%s", flag.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
varname, usage := UnquoteUsage(flag)
|
||||||
|
if len(varname) > 0 {
|
||||||
|
line += " " + varname
|
||||||
|
}
|
||||||
|
if len(flag.NoOptDefVal) > 0 {
|
||||||
|
switch flag.Value.Type() {
|
||||||
|
case "string":
|
||||||
|
line += fmt.Sprintf("[=%q]", flag.NoOptDefVal)
|
||||||
|
case "bool":
|
||||||
|
if flag.NoOptDefVal != "true" {
|
||||||
|
line += fmt.Sprintf("[=%s]", flag.NoOptDefVal)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
line += fmt.Sprintf("[=%s]", flag.NoOptDefVal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This special character will be replaced with spacing once the
|
||||||
|
// correct alignment is calculated
|
||||||
|
line += "\x00"
|
||||||
|
if len(line) > maxlen {
|
||||||
|
maxlen = len(line)
|
||||||
|
}
|
||||||
|
|
||||||
|
line += usage
|
||||||
|
if !isZeroValue(flag.DefValue) {
|
||||||
|
if flag.Value.Type() == "string" {
|
||||||
|
line += fmt.Sprintf(" (default %q)", flag.DefValue)
|
||||||
|
} else {
|
||||||
|
line += fmt.Sprintf(" (default %s)", flag.DefValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lines = append(lines, line)
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, line := range lines {
|
||||||
|
sidx := strings.Index(line, "\x00")
|
||||||
|
spacing := strings.Repeat(" ", maxlen-sidx)
|
||||||
|
fmt.Fprintln(x, line[:sidx], spacing, line[sidx+1:])
|
||||||
|
}
|
||||||
|
|
||||||
|
return x.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintDefaults prints to standard error the default values of all defined command-line flags.
|
||||||
|
func PrintDefaults() {
|
||||||
|
CommandLine.PrintDefaults()
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaultUsage is the default function to print a usage message.
|
||||||
|
func defaultUsage(f *FlagSet) {
|
||||||
|
fmt.Fprintf(f.out(), "Usage of %s:\n", f.name)
|
||||||
|
f.PrintDefaults()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Usage is not just defaultUsage(CommandLine)
|
||||||
|
// because it serves (via godoc flag Usage) as the example
|
||||||
|
// for how to write your own usage function.
|
||||||
|
|
||||||
|
// Usage prints to standard error a usage message documenting all defined command-line flags.
|
||||||
|
// The function is a variable that may be changed to point to a custom function.
|
||||||
|
// By default it prints a simple header and calls PrintDefaults; for details about the
|
||||||
|
// format of the output and how to control it, see the documentation for PrintDefaults.
|
||||||
|
var Usage = func() {
|
||||||
|
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
|
||||||
|
PrintDefaults()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NFlag returns the number of flags that have been set.
|
||||||
|
func (f *FlagSet) NFlag() int { return len(f.actual) }
|
||||||
|
|
||||||
|
// NFlag returns the number of command-line flags that have been set.
|
||||||
|
func NFlag() int { return len(CommandLine.actual) }
|
||||||
|
|
||||||
|
// Arg returns the i'th argument. Arg(0) is the first remaining argument
|
||||||
|
// after flags have been processed.
|
||||||
|
func (f *FlagSet) Arg(i int) string {
|
||||||
|
if i < 0 || i >= len(f.args) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return f.args[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arg returns the i'th command-line argument. Arg(0) is the first remaining argument
|
||||||
|
// after flags have been processed.
|
||||||
|
func Arg(i int) string {
|
||||||
|
return CommandLine.Arg(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NArg is the number of arguments remaining after flags have been processed.
|
||||||
|
func (f *FlagSet) NArg() int { return len(f.args) }
|
||||||
|
|
||||||
|
// NArg is the number of arguments remaining after flags have been processed.
|
||||||
|
func NArg() int { return len(CommandLine.args) }
|
||||||
|
|
||||||
|
// Args returns the non-flag arguments.
|
||||||
|
func (f *FlagSet) Args() []string { return f.args }
|
||||||
|
|
||||||
|
// Args returns the non-flag command-line arguments.
|
||||||
|
func Args() []string { return CommandLine.args }
|
||||||
|
|
||||||
|
// Var defines a flag with the specified name and usage string. The type and
|
||||||
|
// value of the flag are represented by the first argument, of type Value, which
|
||||||
|
// typically holds a user-defined implementation of Value. For instance, the
|
||||||
|
// caller could create a flag that turns a comma-separated string into a slice
|
||||||
|
// of strings by giving the slice the methods of Value; in particular, Set would
|
||||||
|
// decompose the comma-separated string into the slice.
|
||||||
|
func (f *FlagSet) Var(value Value, name string, usage string) {
|
||||||
|
f.VarP(value, name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// VarPF is like VarP, but returns the flag created
|
||||||
|
func (f *FlagSet) VarPF(value Value, name, shorthand, usage string) *Flag {
|
||||||
|
// Remember the default value as a string; it won't change.
|
||||||
|
flag := &Flag{
|
||||||
|
Name: name,
|
||||||
|
Shorthand: shorthand,
|
||||||
|
Usage: usage,
|
||||||
|
Value: value,
|
||||||
|
DefValue: value.String(),
|
||||||
|
}
|
||||||
|
f.AddFlag(flag)
|
||||||
|
return flag
|
||||||
|
}
|
||||||
|
|
||||||
|
// VarP is like Var, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) VarP(value Value, name, shorthand, usage string) {
|
||||||
|
_ = f.VarPF(value, name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFlag will add the flag to the FlagSet
|
||||||
|
func (f *FlagSet) AddFlag(flag *Flag) {
|
||||||
|
// Call normalizeFlagName function only once
|
||||||
|
normalizedFlagName := f.normalizeFlagName(flag.Name)
|
||||||
|
|
||||||
|
_, alreadythere := f.formal[normalizedFlagName]
|
||||||
|
if alreadythere {
|
||||||
|
msg := fmt.Sprintf("%s flag redefined: %s", f.name, flag.Name)
|
||||||
|
fmt.Fprintln(f.out(), msg)
|
||||||
|
panic(msg) // Happens only if flags are declared with identical names
|
||||||
|
}
|
||||||
|
if f.formal == nil {
|
||||||
|
f.formal = make(map[NormalizedName]*Flag)
|
||||||
|
}
|
||||||
|
|
||||||
|
flag.Name = string(normalizedFlagName)
|
||||||
|
f.formal[normalizedFlagName] = flag
|
||||||
|
|
||||||
|
if len(flag.Shorthand) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(flag.Shorthand) > 1 {
|
||||||
|
fmt.Fprintf(f.out(), "%s shorthand more than ASCII character: %s\n", f.name, flag.Shorthand)
|
||||||
|
panic("shorthand is more than one character")
|
||||||
|
}
|
||||||
|
if f.shorthands == nil {
|
||||||
|
f.shorthands = make(map[byte]*Flag)
|
||||||
|
}
|
||||||
|
c := flag.Shorthand[0]
|
||||||
|
old, alreadythere := f.shorthands[c]
|
||||||
|
if alreadythere {
|
||||||
|
fmt.Fprintf(f.out(), "%s shorthand reused: %q for %s already used for %s\n", f.name, c, flag.Name, old.Name)
|
||||||
|
panic("shorthand redefinition")
|
||||||
|
}
|
||||||
|
f.shorthands[c] = flag
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFlagSet adds one FlagSet to another. If a flag is already present in f
|
||||||
|
// the flag from newSet will be ignored
|
||||||
|
func (f *FlagSet) AddFlagSet(newSet *FlagSet) {
|
||||||
|
if newSet == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
newSet.VisitAll(func(flag *Flag) {
|
||||||
|
if f.Lookup(flag.Name) == nil {
|
||||||
|
f.AddFlag(flag)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Var defines a flag with the specified name and usage string. The type and
|
||||||
|
// value of the flag are represented by the first argument, of type Value, which
|
||||||
|
// typically holds a user-defined implementation of Value. For instance, the
|
||||||
|
// caller could create a flag that turns a comma-separated string into a slice
|
||||||
|
// of strings by giving the slice the methods of Value; in particular, Set would
|
||||||
|
// decompose the comma-separated string into the slice.
|
||||||
|
func Var(value Value, name string, usage string) {
|
||||||
|
CommandLine.VarP(value, name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// VarP is like Var, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func VarP(value Value, name, shorthand, usage string) {
|
||||||
|
CommandLine.VarP(value, name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// failf prints to standard error a formatted error and usage message and
|
||||||
|
// returns the error.
|
||||||
|
func (f *FlagSet) failf(format string, a ...interface{}) error {
|
||||||
|
err := fmt.Errorf(format, a...)
|
||||||
|
fmt.Fprintln(f.out(), err)
|
||||||
|
f.usage()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// usage calls the Usage method for the flag set, or the usage function if
|
||||||
|
// the flag set is CommandLine.
|
||||||
|
func (f *FlagSet) usage() {
|
||||||
|
if f == CommandLine {
|
||||||
|
Usage()
|
||||||
|
} else if f.Usage == nil {
|
||||||
|
defaultUsage(f)
|
||||||
|
} else {
|
||||||
|
f.Usage()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FlagSet) setFlag(flag *Flag, value string, origArg string) error {
|
||||||
|
if err := flag.Value.Set(value); err != nil {
|
||||||
|
return f.failf("invalid argument %q for %s: %v", value, origArg, err)
|
||||||
|
}
|
||||||
|
// mark as visited for Visit()
|
||||||
|
if f.actual == nil {
|
||||||
|
f.actual = make(map[NormalizedName]*Flag)
|
||||||
|
}
|
||||||
|
f.actual[f.normalizeFlagName(flag.Name)] = flag
|
||||||
|
flag.Changed = true
|
||||||
|
if len(flag.Deprecated) > 0 {
|
||||||
|
fmt.Fprintf(os.Stderr, "Flag --%s has been deprecated, %s\n", flag.Name, flag.Deprecated)
|
||||||
|
}
|
||||||
|
if len(flag.ShorthandDeprecated) > 0 && containsShorthand(origArg, flag.Shorthand) {
|
||||||
|
fmt.Fprintf(os.Stderr, "Flag shorthand -%s has been deprecated, %s\n", flag.Shorthand, flag.ShorthandDeprecated)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func containsShorthand(arg, shorthand string) bool {
|
||||||
|
// filter out flags --<flag_name>
|
||||||
|
if strings.HasPrefix(arg, "-") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
arg = strings.SplitN(arg, "=", 2)[0]
|
||||||
|
return strings.Contains(arg, shorthand)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FlagSet) parseLongArg(s string, args []string) (a []string, err error) {
|
||||||
|
a = args
|
||||||
|
name := s[2:]
|
||||||
|
if len(name) == 0 || name[0] == '-' || name[0] == '=' {
|
||||||
|
err = f.failf("bad flag syntax: %s", s)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
split := strings.SplitN(name, "=", 2)
|
||||||
|
name = split[0]
|
||||||
|
flag, alreadythere := f.formal[f.normalizeFlagName(name)]
|
||||||
|
if !alreadythere {
|
||||||
|
if name == "help" { // special case for nice help message.
|
||||||
|
f.usage()
|
||||||
|
return a, ErrHelp
|
||||||
|
}
|
||||||
|
err = f.failf("unknown flag: --%s", name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var value string
|
||||||
|
if len(split) == 2 {
|
||||||
|
// '--flag=arg'
|
||||||
|
value = split[1]
|
||||||
|
} else if len(flag.NoOptDefVal) > 0 {
|
||||||
|
// '--flag' (arg was optional)
|
||||||
|
value = flag.NoOptDefVal
|
||||||
|
} else if len(a) > 0 {
|
||||||
|
// '--flag arg'
|
||||||
|
value = a[0]
|
||||||
|
a = a[1:]
|
||||||
|
} else {
|
||||||
|
// '--flag' (arg was required)
|
||||||
|
err = f.failf("flag needs an argument: %s", s)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = f.setFlag(flag, value, s)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FlagSet) parseSingleShortArg(shorthands string, args []string) (outShorts string, outArgs []string, err error) {
|
||||||
|
if strings.HasPrefix(shorthands, "test.") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
outArgs = args
|
||||||
|
outShorts = shorthands[1:]
|
||||||
|
c := shorthands[0]
|
||||||
|
|
||||||
|
flag, alreadythere := f.shorthands[c]
|
||||||
|
if !alreadythere {
|
||||||
|
if c == 'h' { // special case for nice help message.
|
||||||
|
f.usage()
|
||||||
|
err = ErrHelp
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//TODO continue on error
|
||||||
|
err = f.failf("unknown shorthand flag: %q in -%s", c, shorthands)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var value string
|
||||||
|
if len(shorthands) > 2 && shorthands[1] == '=' {
|
||||||
|
value = shorthands[2:]
|
||||||
|
outShorts = ""
|
||||||
|
} else if len(flag.NoOptDefVal) > 0 {
|
||||||
|
value = flag.NoOptDefVal
|
||||||
|
} else if len(shorthands) > 1 {
|
||||||
|
value = shorthands[1:]
|
||||||
|
outShorts = ""
|
||||||
|
} else if len(args) > 0 {
|
||||||
|
value = args[0]
|
||||||
|
outArgs = args[1:]
|
||||||
|
} else {
|
||||||
|
err = f.failf("flag needs an argument: %q in -%s", c, shorthands)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = f.setFlag(flag, value, shorthands)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FlagSet) parseShortArg(s string, args []string) (a []string, err error) {
|
||||||
|
a = args
|
||||||
|
shorthands := s[1:]
|
||||||
|
|
||||||
|
for len(shorthands) > 0 {
|
||||||
|
shorthands, a, err = f.parseSingleShortArg(shorthands, args)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FlagSet) parseArgs(args []string) (err error) {
|
||||||
|
for len(args) > 0 {
|
||||||
|
s := args[0]
|
||||||
|
args = args[1:]
|
||||||
|
if len(s) == 0 || s[0] != '-' || len(s) == 1 {
|
||||||
|
if !f.interspersed {
|
||||||
|
f.args = append(f.args, s)
|
||||||
|
f.args = append(f.args, args...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
f.args = append(f.args, s)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if s[1] == '-' {
|
||||||
|
if len(s) == 2 { // "--" terminates the flags
|
||||||
|
f.argsLenAtDash = len(f.args)
|
||||||
|
f.args = append(f.args, args...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
args, err = f.parseLongArg(s, args)
|
||||||
|
} else {
|
||||||
|
args, err = f.parseShortArg(s, args)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse parses flag definitions from the argument list, which should not
|
||||||
|
// include the command name. Must be called after all flags in the FlagSet
|
||||||
|
// are defined and before flags are accessed by the program.
|
||||||
|
// The return value will be ErrHelp if -help was set but not defined.
|
||||||
|
func (f *FlagSet) Parse(arguments []string) error {
|
||||||
|
f.parsed = true
|
||||||
|
f.args = make([]string, 0, len(arguments))
|
||||||
|
err := f.parseArgs(arguments)
|
||||||
|
if err != nil {
|
||||||
|
switch f.errorHandling {
|
||||||
|
case ContinueOnError:
|
||||||
|
return err
|
||||||
|
case ExitOnError:
|
||||||
|
os.Exit(2)
|
||||||
|
case PanicOnError:
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parsed reports whether f.Parse has been called.
|
||||||
|
func (f *FlagSet) Parsed() bool {
|
||||||
|
return f.parsed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse parses the command-line flags from os.Args[1:]. Must be called
|
||||||
|
// after all flags are defined and before flags are accessed by the program.
|
||||||
|
func Parse() {
|
||||||
|
// Ignore errors; CommandLine is set for ExitOnError.
|
||||||
|
CommandLine.Parse(os.Args[1:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetInterspersed sets whether to support interspersed option/non-option arguments.
|
||||||
|
func SetInterspersed(interspersed bool) {
|
||||||
|
CommandLine.SetInterspersed(interspersed)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parsed returns true if the command-line flags have been parsed.
|
||||||
|
func Parsed() bool {
|
||||||
|
return CommandLine.Parsed()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommandLine is the default set of command-line flags, parsed from os.Args.
|
||||||
|
var CommandLine = NewFlagSet(os.Args[0], ExitOnError)
|
||||||
|
|
||||||
|
// NewFlagSet returns a new, empty flag set with the specified name and
|
||||||
|
// error handling property.
|
||||||
|
func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet {
|
||||||
|
f := &FlagSet{
|
||||||
|
name: name,
|
||||||
|
errorHandling: errorHandling,
|
||||||
|
argsLenAtDash: -1,
|
||||||
|
interspersed: true,
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetInterspersed sets whether to support interspersed option/non-option arguments.
|
||||||
|
func (f *FlagSet) SetInterspersed(interspersed bool) {
|
||||||
|
f.interspersed = interspersed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init sets the name and error handling property for a flag set.
|
||||||
|
// By default, the zero FlagSet uses an empty name and the
|
||||||
|
// ContinueOnError error handling policy.
|
||||||
|
func (f *FlagSet) Init(name string, errorHandling ErrorHandling) {
|
||||||
|
f.name = name
|
||||||
|
f.errorHandling = errorHandling
|
||||||
|
f.argsLenAtDash = -1
|
||||||
|
}
|
91
vendor/github.com/spf13/pflag/float32.go
generated
vendored
Normal file
91
vendor/github.com/spf13/pflag/float32.go
generated
vendored
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
package pflag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// -- float32 Value
|
||||||
|
type float32Value float32
|
||||||
|
|
||||||
|
func newFloat32Value(val float32, p *float32) *float32Value {
|
||||||
|
*p = val
|
||||||
|
return (*float32Value)(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *float32Value) Set(s string) error {
|
||||||
|
v, err := strconv.ParseFloat(s, 32)
|
||||||
|
*f = float32Value(v)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *float32Value) Type() string {
|
||||||
|
return "float32"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *float32Value) String() string { return fmt.Sprintf("%v", *f) }
|
||||||
|
|
||||||
|
func float32Conv(sval string) (interface{}, error) {
|
||||||
|
v, err := strconv.ParseFloat(sval, 32)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return float32(v), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFloat32 return the float32 value of a flag with the given name
|
||||||
|
func (f *FlagSet) GetFloat32(name string) (float32, error) {
|
||||||
|
val, err := f.getFlagType(name, "float32", float32Conv)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return val.(float32), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float32Var defines a float32 flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to a float32 variable in which to store the value of the flag.
|
||||||
|
func (f *FlagSet) Float32Var(p *float32, name string, value float32, usage string) {
|
||||||
|
f.VarP(newFloat32Value(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float32VarP is like Float32Var, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) Float32VarP(p *float32, name, shorthand string, value float32, usage string) {
|
||||||
|
f.VarP(newFloat32Value(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float32Var defines a float32 flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to a float32 variable in which to store the value of the flag.
|
||||||
|
func Float32Var(p *float32, name string, value float32, usage string) {
|
||||||
|
CommandLine.VarP(newFloat32Value(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float32VarP is like Float32Var, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func Float32VarP(p *float32, name, shorthand string, value float32, usage string) {
|
||||||
|
CommandLine.VarP(newFloat32Value(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float32 defines a float32 flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of a float32 variable that stores the value of the flag.
|
||||||
|
func (f *FlagSet) Float32(name string, value float32, usage string) *float32 {
|
||||||
|
p := new(float32)
|
||||||
|
f.Float32VarP(p, name, "", value, usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float32P is like Float32, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) Float32P(name, shorthand string, value float32, usage string) *float32 {
|
||||||
|
p := new(float32)
|
||||||
|
f.Float32VarP(p, name, shorthand, value, usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float32 defines a float32 flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of a float32 variable that stores the value of the flag.
|
||||||
|
func Float32(name string, value float32, usage string) *float32 {
|
||||||
|
return CommandLine.Float32P(name, "", value, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float32P is like Float32, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func Float32P(name, shorthand string, value float32, usage string) *float32 {
|
||||||
|
return CommandLine.Float32P(name, shorthand, value, usage)
|
||||||
|
}
|
87
vendor/github.com/spf13/pflag/float64.go
generated
vendored
Normal file
87
vendor/github.com/spf13/pflag/float64.go
generated
vendored
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
package pflag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// -- float64 Value
|
||||||
|
type float64Value float64
|
||||||
|
|
||||||
|
func newFloat64Value(val float64, p *float64) *float64Value {
|
||||||
|
*p = val
|
||||||
|
return (*float64Value)(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *float64Value) Set(s string) error {
|
||||||
|
v, err := strconv.ParseFloat(s, 64)
|
||||||
|
*f = float64Value(v)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *float64Value) Type() string {
|
||||||
|
return "float64"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *float64Value) String() string { return fmt.Sprintf("%v", *f) }
|
||||||
|
|
||||||
|
func float64Conv(sval string) (interface{}, error) {
|
||||||
|
return strconv.ParseFloat(sval, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFloat64 return the float64 value of a flag with the given name
|
||||||
|
func (f *FlagSet) GetFloat64(name string) (float64, error) {
|
||||||
|
val, err := f.getFlagType(name, "float64", float64Conv)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return val.(float64), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64Var defines a float64 flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to a float64 variable in which to store the value of the flag.
|
||||||
|
func (f *FlagSet) Float64Var(p *float64, name string, value float64, usage string) {
|
||||||
|
f.VarP(newFloat64Value(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64VarP is like Float64Var, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) Float64VarP(p *float64, name, shorthand string, value float64, usage string) {
|
||||||
|
f.VarP(newFloat64Value(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64Var defines a float64 flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to a float64 variable in which to store the value of the flag.
|
||||||
|
func Float64Var(p *float64, name string, value float64, usage string) {
|
||||||
|
CommandLine.VarP(newFloat64Value(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64VarP is like Float64Var, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func Float64VarP(p *float64, name, shorthand string, value float64, usage string) {
|
||||||
|
CommandLine.VarP(newFloat64Value(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64 defines a float64 flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of a float64 variable that stores the value of the flag.
|
||||||
|
func (f *FlagSet) Float64(name string, value float64, usage string) *float64 {
|
||||||
|
p := new(float64)
|
||||||
|
f.Float64VarP(p, name, "", value, usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64P is like Float64, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) Float64P(name, shorthand string, value float64, usage string) *float64 {
|
||||||
|
p := new(float64)
|
||||||
|
f.Float64VarP(p, name, shorthand, value, usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64 defines a float64 flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of a float64 variable that stores the value of the flag.
|
||||||
|
func Float64(name string, value float64, usage string) *float64 {
|
||||||
|
return CommandLine.Float64P(name, "", value, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64P is like Float64, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func Float64P(name, shorthand string, value float64, usage string) *float64 {
|
||||||
|
return CommandLine.Float64P(name, shorthand, value, usage)
|
||||||
|
}
|
104
vendor/github.com/spf13/pflag/golangflag.go
generated
vendored
Normal file
104
vendor/github.com/spf13/pflag/golangflag.go
generated
vendored
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
// Copyright 2009 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 pflag
|
||||||
|
|
||||||
|
import (
|
||||||
|
goflag "flag"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = fmt.Print
|
||||||
|
|
||||||
|
// flagValueWrapper implements pflag.Value around a flag.Value. The main
|
||||||
|
// difference here is the addition of the Type method that returns a string
|
||||||
|
// name of the type. As this is generally unknown, we approximate that with
|
||||||
|
// reflection.
|
||||||
|
type flagValueWrapper struct {
|
||||||
|
inner goflag.Value
|
||||||
|
flagType string
|
||||||
|
}
|
||||||
|
|
||||||
|
// We are just copying the boolFlag interface out of goflag as that is what
|
||||||
|
// they use to decide if a flag should get "true" when no arg is given.
|
||||||
|
type goBoolFlag interface {
|
||||||
|
goflag.Value
|
||||||
|
IsBoolFlag() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func wrapFlagValue(v goflag.Value) Value {
|
||||||
|
// If the flag.Value happens to also be a pflag.Value, just use it directly.
|
||||||
|
if pv, ok := v.(Value); ok {
|
||||||
|
return pv
|
||||||
|
}
|
||||||
|
|
||||||
|
pv := &flagValueWrapper{
|
||||||
|
inner: v,
|
||||||
|
}
|
||||||
|
|
||||||
|
t := reflect.TypeOf(v)
|
||||||
|
if t.Kind() == reflect.Interface || t.Kind() == reflect.Ptr {
|
||||||
|
t = t.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
pv.flagType = strings.TrimSuffix(t.Name(), "Value")
|
||||||
|
return pv
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *flagValueWrapper) String() string {
|
||||||
|
return v.inner.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *flagValueWrapper) Set(s string) error {
|
||||||
|
return v.inner.Set(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *flagValueWrapper) Type() string {
|
||||||
|
return v.flagType
|
||||||
|
}
|
||||||
|
|
||||||
|
// PFlagFromGoFlag will return a *pflag.Flag given a *flag.Flag
|
||||||
|
// If the *flag.Flag.Name was a single character (ex: `v`) it will be accessiblei
|
||||||
|
// with both `-v` and `--v` in flags. If the golang flag was more than a single
|
||||||
|
// character (ex: `verbose`) it will only be accessible via `--verbose`
|
||||||
|
func PFlagFromGoFlag(goflag *goflag.Flag) *Flag {
|
||||||
|
// Remember the default value as a string; it won't change.
|
||||||
|
flag := &Flag{
|
||||||
|
Name: goflag.Name,
|
||||||
|
Usage: goflag.Usage,
|
||||||
|
Value: wrapFlagValue(goflag.Value),
|
||||||
|
// Looks like golang flags don't set DefValue correctly :-(
|
||||||
|
//DefValue: goflag.DefValue,
|
||||||
|
DefValue: goflag.Value.String(),
|
||||||
|
}
|
||||||
|
// Ex: if the golang flag was -v, allow both -v and --v to work
|
||||||
|
if len(flag.Name) == 1 {
|
||||||
|
flag.Shorthand = flag.Name
|
||||||
|
}
|
||||||
|
if fv, ok := goflag.Value.(goBoolFlag); ok && fv.IsBoolFlag() {
|
||||||
|
flag.NoOptDefVal = "true"
|
||||||
|
}
|
||||||
|
return flag
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddGoFlag will add the given *flag.Flag to the pflag.FlagSet
|
||||||
|
func (f *FlagSet) AddGoFlag(goflag *goflag.Flag) {
|
||||||
|
if f.Lookup(goflag.Name) != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
newflag := PFlagFromGoFlag(goflag)
|
||||||
|
f.AddFlag(newflag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddGoFlagSet will add the given *flag.FlagSet to the pflag.FlagSet
|
||||||
|
func (f *FlagSet) AddGoFlagSet(newSet *goflag.FlagSet) {
|
||||||
|
if newSet == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
newSet.VisitAll(func(goflag *goflag.Flag) {
|
||||||
|
f.AddGoFlag(goflag)
|
||||||
|
})
|
||||||
|
}
|
87
vendor/github.com/spf13/pflag/int.go
generated
vendored
Normal file
87
vendor/github.com/spf13/pflag/int.go
generated
vendored
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
package pflag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// -- int Value
|
||||||
|
type intValue int
|
||||||
|
|
||||||
|
func newIntValue(val int, p *int) *intValue {
|
||||||
|
*p = val
|
||||||
|
return (*intValue)(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *intValue) Set(s string) error {
|
||||||
|
v, err := strconv.ParseInt(s, 0, 64)
|
||||||
|
*i = intValue(v)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *intValue) Type() string {
|
||||||
|
return "int"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *intValue) String() string { return fmt.Sprintf("%v", *i) }
|
||||||
|
|
||||||
|
func intConv(sval string) (interface{}, error) {
|
||||||
|
return strconv.Atoi(sval)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInt return the int value of a flag with the given name
|
||||||
|
func (f *FlagSet) GetInt(name string) (int, error) {
|
||||||
|
val, err := f.getFlagType(name, "int", intConv)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return val.(int), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntVar defines an int flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to an int variable in which to store the value of the flag.
|
||||||
|
func (f *FlagSet) IntVar(p *int, name string, value int, usage string) {
|
||||||
|
f.VarP(newIntValue(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntVarP is like IntVar, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) IntVarP(p *int, name, shorthand string, value int, usage string) {
|
||||||
|
f.VarP(newIntValue(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntVar defines an int flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to an int variable in which to store the value of the flag.
|
||||||
|
func IntVar(p *int, name string, value int, usage string) {
|
||||||
|
CommandLine.VarP(newIntValue(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntVarP is like IntVar, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func IntVarP(p *int, name, shorthand string, value int, usage string) {
|
||||||
|
CommandLine.VarP(newIntValue(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int defines an int flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of an int variable that stores the value of the flag.
|
||||||
|
func (f *FlagSet) Int(name string, value int, usage string) *int {
|
||||||
|
p := new(int)
|
||||||
|
f.IntVarP(p, name, "", value, usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntP is like Int, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) IntP(name, shorthand string, value int, usage string) *int {
|
||||||
|
p := new(int)
|
||||||
|
f.IntVarP(p, name, shorthand, value, usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int defines an int flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of an int variable that stores the value of the flag.
|
||||||
|
func Int(name string, value int, usage string) *int {
|
||||||
|
return CommandLine.IntP(name, "", value, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntP is like Int, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func IntP(name, shorthand string, value int, usage string) *int {
|
||||||
|
return CommandLine.IntP(name, shorthand, value, usage)
|
||||||
|
}
|
91
vendor/github.com/spf13/pflag/int32.go
generated
vendored
Normal file
91
vendor/github.com/spf13/pflag/int32.go
generated
vendored
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
package pflag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// -- int32 Value
|
||||||
|
type int32Value int32
|
||||||
|
|
||||||
|
func newInt32Value(val int32, p *int32) *int32Value {
|
||||||
|
*p = val
|
||||||
|
return (*int32Value)(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *int32Value) Set(s string) error {
|
||||||
|
v, err := strconv.ParseInt(s, 0, 32)
|
||||||
|
*i = int32Value(v)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *int32Value) Type() string {
|
||||||
|
return "int32"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *int32Value) String() string { return fmt.Sprintf("%v", *i) }
|
||||||
|
|
||||||
|
func int32Conv(sval string) (interface{}, error) {
|
||||||
|
v, err := strconv.ParseInt(sval, 0, 32)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return int32(v), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInt32 return the int32 value of a flag with the given name
|
||||||
|
func (f *FlagSet) GetInt32(name string) (int32, error) {
|
||||||
|
val, err := f.getFlagType(name, "int32", int32Conv)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return val.(int32), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int32Var defines an int32 flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to an int32 variable in which to store the value of the flag.
|
||||||
|
func (f *FlagSet) Int32Var(p *int32, name string, value int32, usage string) {
|
||||||
|
f.VarP(newInt32Value(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int32VarP is like Int32Var, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) Int32VarP(p *int32, name, shorthand string, value int32, usage string) {
|
||||||
|
f.VarP(newInt32Value(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int32Var defines an int32 flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to an int32 variable in which to store the value of the flag.
|
||||||
|
func Int32Var(p *int32, name string, value int32, usage string) {
|
||||||
|
CommandLine.VarP(newInt32Value(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int32VarP is like Int32Var, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func Int32VarP(p *int32, name, shorthand string, value int32, usage string) {
|
||||||
|
CommandLine.VarP(newInt32Value(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int32 defines an int32 flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of an int32 variable that stores the value of the flag.
|
||||||
|
func (f *FlagSet) Int32(name string, value int32, usage string) *int32 {
|
||||||
|
p := new(int32)
|
||||||
|
f.Int32VarP(p, name, "", value, usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int32P is like Int32, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) Int32P(name, shorthand string, value int32, usage string) *int32 {
|
||||||
|
p := new(int32)
|
||||||
|
f.Int32VarP(p, name, shorthand, value, usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int32 defines an int32 flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of an int32 variable that stores the value of the flag.
|
||||||
|
func Int32(name string, value int32, usage string) *int32 {
|
||||||
|
return CommandLine.Int32P(name, "", value, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int32P is like Int32, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func Int32P(name, shorthand string, value int32, usage string) *int32 {
|
||||||
|
return CommandLine.Int32P(name, shorthand, value, usage)
|
||||||
|
}
|
87
vendor/github.com/spf13/pflag/int64.go
generated
vendored
Normal file
87
vendor/github.com/spf13/pflag/int64.go
generated
vendored
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
package pflag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// -- int64 Value
|
||||||
|
type int64Value int64
|
||||||
|
|
||||||
|
func newInt64Value(val int64, p *int64) *int64Value {
|
||||||
|
*p = val
|
||||||
|
return (*int64Value)(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *int64Value) Set(s string) error {
|
||||||
|
v, err := strconv.ParseInt(s, 0, 64)
|
||||||
|
*i = int64Value(v)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *int64Value) Type() string {
|
||||||
|
return "int64"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *int64Value) String() string { return fmt.Sprintf("%v", *i) }
|
||||||
|
|
||||||
|
func int64Conv(sval string) (interface{}, error) {
|
||||||
|
return strconv.ParseInt(sval, 0, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInt64 return the int64 value of a flag with the given name
|
||||||
|
func (f *FlagSet) GetInt64(name string) (int64, error) {
|
||||||
|
val, err := f.getFlagType(name, "int64", int64Conv)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return val.(int64), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64Var defines an int64 flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to an int64 variable in which to store the value of the flag.
|
||||||
|
func (f *FlagSet) Int64Var(p *int64, name string, value int64, usage string) {
|
||||||
|
f.VarP(newInt64Value(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64VarP is like Int64Var, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) Int64VarP(p *int64, name, shorthand string, value int64, usage string) {
|
||||||
|
f.VarP(newInt64Value(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64Var defines an int64 flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to an int64 variable in which to store the value of the flag.
|
||||||
|
func Int64Var(p *int64, name string, value int64, usage string) {
|
||||||
|
CommandLine.VarP(newInt64Value(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64VarP is like Int64Var, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func Int64VarP(p *int64, name, shorthand string, value int64, usage string) {
|
||||||
|
CommandLine.VarP(newInt64Value(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64 defines an int64 flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of an int64 variable that stores the value of the flag.
|
||||||
|
func (f *FlagSet) Int64(name string, value int64, usage string) *int64 {
|
||||||
|
p := new(int64)
|
||||||
|
f.Int64VarP(p, name, "", value, usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64P is like Int64, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) Int64P(name, shorthand string, value int64, usage string) *int64 {
|
||||||
|
p := new(int64)
|
||||||
|
f.Int64VarP(p, name, shorthand, value, usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64 defines an int64 flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of an int64 variable that stores the value of the flag.
|
||||||
|
func Int64(name string, value int64, usage string) *int64 {
|
||||||
|
return CommandLine.Int64P(name, "", value, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64P is like Int64, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func Int64P(name, shorthand string, value int64, usage string) *int64 {
|
||||||
|
return CommandLine.Int64P(name, shorthand, value, usage)
|
||||||
|
}
|
91
vendor/github.com/spf13/pflag/int8.go
generated
vendored
Normal file
91
vendor/github.com/spf13/pflag/int8.go
generated
vendored
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
package pflag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// -- int8 Value
|
||||||
|
type int8Value int8
|
||||||
|
|
||||||
|
func newInt8Value(val int8, p *int8) *int8Value {
|
||||||
|
*p = val
|
||||||
|
return (*int8Value)(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *int8Value) Set(s string) error {
|
||||||
|
v, err := strconv.ParseInt(s, 0, 8)
|
||||||
|
*i = int8Value(v)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *int8Value) Type() string {
|
||||||
|
return "int8"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *int8Value) String() string { return fmt.Sprintf("%v", *i) }
|
||||||
|
|
||||||
|
func int8Conv(sval string) (interface{}, error) {
|
||||||
|
v, err := strconv.ParseInt(sval, 0, 8)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return int8(v), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInt8 return the int8 value of a flag with the given name
|
||||||
|
func (f *FlagSet) GetInt8(name string) (int8, error) {
|
||||||
|
val, err := f.getFlagType(name, "int8", int8Conv)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return val.(int8), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int8Var defines an int8 flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to an int8 variable in which to store the value of the flag.
|
||||||
|
func (f *FlagSet) Int8Var(p *int8, name string, value int8, usage string) {
|
||||||
|
f.VarP(newInt8Value(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int8VarP is like Int8Var, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) Int8VarP(p *int8, name, shorthand string, value int8, usage string) {
|
||||||
|
f.VarP(newInt8Value(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int8Var defines an int8 flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to an int8 variable in which to store the value of the flag.
|
||||||
|
func Int8Var(p *int8, name string, value int8, usage string) {
|
||||||
|
CommandLine.VarP(newInt8Value(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int8VarP is like Int8Var, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func Int8VarP(p *int8, name, shorthand string, value int8, usage string) {
|
||||||
|
CommandLine.VarP(newInt8Value(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int8 defines an int8 flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of an int8 variable that stores the value of the flag.
|
||||||
|
func (f *FlagSet) Int8(name string, value int8, usage string) *int8 {
|
||||||
|
p := new(int8)
|
||||||
|
f.Int8VarP(p, name, "", value, usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int8P is like Int8, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) Int8P(name, shorthand string, value int8, usage string) *int8 {
|
||||||
|
p := new(int8)
|
||||||
|
f.Int8VarP(p, name, shorthand, value, usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int8 defines an int8 flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of an int8 variable that stores the value of the flag.
|
||||||
|
func Int8(name string, value int8, usage string) *int8 {
|
||||||
|
return CommandLine.Int8P(name, "", value, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int8P is like Int8, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func Int8P(name, shorthand string, value int8, usage string) *int8 {
|
||||||
|
return CommandLine.Int8P(name, shorthand, value, usage)
|
||||||
|
}
|
128
vendor/github.com/spf13/pflag/int_slice.go
generated
vendored
Normal file
128
vendor/github.com/spf13/pflag/int_slice.go
generated
vendored
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
package pflag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// -- intSlice Value
|
||||||
|
type intSliceValue struct {
|
||||||
|
value *[]int
|
||||||
|
changed bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newIntSliceValue(val []int, p *[]int) *intSliceValue {
|
||||||
|
isv := new(intSliceValue)
|
||||||
|
isv.value = p
|
||||||
|
*isv.value = val
|
||||||
|
return isv
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *intSliceValue) Set(val string) error {
|
||||||
|
ss := strings.Split(val, ",")
|
||||||
|
out := make([]int, len(ss))
|
||||||
|
for i, d := range ss {
|
||||||
|
var err error
|
||||||
|
out[i], err = strconv.Atoi(d)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if !s.changed {
|
||||||
|
*s.value = out
|
||||||
|
} else {
|
||||||
|
*s.value = append(*s.value, out...)
|
||||||
|
}
|
||||||
|
s.changed = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *intSliceValue) Type() string {
|
||||||
|
return "intSlice"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *intSliceValue) String() string {
|
||||||
|
out := make([]string, len(*s.value))
|
||||||
|
for i, d := range *s.value {
|
||||||
|
out[i] = fmt.Sprintf("%d", d)
|
||||||
|
}
|
||||||
|
return "[" + strings.Join(out, ",") + "]"
|
||||||
|
}
|
||||||
|
|
||||||
|
func intSliceConv(val string) (interface{}, error) {
|
||||||
|
val = strings.Trim(val, "[]")
|
||||||
|
// Empty string would cause a slice with one (empty) entry
|
||||||
|
if len(val) == 0 {
|
||||||
|
return []int{}, nil
|
||||||
|
}
|
||||||
|
ss := strings.Split(val, ",")
|
||||||
|
out := make([]int, len(ss))
|
||||||
|
for i, d := range ss {
|
||||||
|
var err error
|
||||||
|
out[i], err = strconv.Atoi(d)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIntSlice return the []int value of a flag with the given name
|
||||||
|
func (f *FlagSet) GetIntSlice(name string) ([]int, error) {
|
||||||
|
val, err := f.getFlagType(name, "intSlice", intSliceConv)
|
||||||
|
if err != nil {
|
||||||
|
return []int{}, err
|
||||||
|
}
|
||||||
|
return val.([]int), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntSliceVar defines a intSlice flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to a []int variable in which to store the value of the flag.
|
||||||
|
func (f *FlagSet) IntSliceVar(p *[]int, name string, value []int, usage string) {
|
||||||
|
f.VarP(newIntSliceValue(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntSliceVarP is like IntSliceVar, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) IntSliceVarP(p *[]int, name, shorthand string, value []int, usage string) {
|
||||||
|
f.VarP(newIntSliceValue(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntSliceVar defines a int[] flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to a int[] variable in which to store the value of the flag.
|
||||||
|
func IntSliceVar(p *[]int, name string, value []int, usage string) {
|
||||||
|
CommandLine.VarP(newIntSliceValue(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntSliceVarP is like IntSliceVar, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func IntSliceVarP(p *[]int, name, shorthand string, value []int, usage string) {
|
||||||
|
CommandLine.VarP(newIntSliceValue(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntSlice defines a []int flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of a []int variable that stores the value of the flag.
|
||||||
|
func (f *FlagSet) IntSlice(name string, value []int, usage string) *[]int {
|
||||||
|
p := []int{}
|
||||||
|
f.IntSliceVarP(&p, name, "", value, usage)
|
||||||
|
return &p
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntSliceP is like IntSlice, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) IntSliceP(name, shorthand string, value []int, usage string) *[]int {
|
||||||
|
p := []int{}
|
||||||
|
f.IntSliceVarP(&p, name, shorthand, value, usage)
|
||||||
|
return &p
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntSlice defines a []int flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of a []int variable that stores the value of the flag.
|
||||||
|
func IntSlice(name string, value []int, usage string) *[]int {
|
||||||
|
return CommandLine.IntSliceP(name, "", value, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntSliceP is like IntSlice, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func IntSliceP(name, shorthand string, value []int, usage string) *[]int {
|
||||||
|
return CommandLine.IntSliceP(name, shorthand, value, usage)
|
||||||
|
}
|
96
vendor/github.com/spf13/pflag/ip.go
generated
vendored
Normal file
96
vendor/github.com/spf13/pflag/ip.go
generated
vendored
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
package pflag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = strings.TrimSpace
|
||||||
|
|
||||||
|
// -- net.IP value
|
||||||
|
type ipValue net.IP
|
||||||
|
|
||||||
|
func newIPValue(val net.IP, p *net.IP) *ipValue {
|
||||||
|
*p = val
|
||||||
|
return (*ipValue)(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ipValue) String() string { return net.IP(*i).String() }
|
||||||
|
func (i *ipValue) Set(s string) error {
|
||||||
|
ip := net.ParseIP(strings.TrimSpace(s))
|
||||||
|
if ip == nil {
|
||||||
|
return fmt.Errorf("failed to parse IP: %q", s)
|
||||||
|
}
|
||||||
|
*i = ipValue(ip)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ipValue) Type() string {
|
||||||
|
return "ip"
|
||||||
|
}
|
||||||
|
|
||||||
|
func ipConv(sval string) (interface{}, error) {
|
||||||
|
ip := net.ParseIP(sval)
|
||||||
|
if ip != nil {
|
||||||
|
return ip, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("invalid string being converted to IP address: %s", sval)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIP return the net.IP value of a flag with the given name
|
||||||
|
func (f *FlagSet) GetIP(name string) (net.IP, error) {
|
||||||
|
val, err := f.getFlagType(name, "ip", ipConv)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return val.(net.IP), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPVar defines an net.IP flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to an net.IP variable in which to store the value of the flag.
|
||||||
|
func (f *FlagSet) IPVar(p *net.IP, name string, value net.IP, usage string) {
|
||||||
|
f.VarP(newIPValue(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPVarP is like IPVar, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) IPVarP(p *net.IP, name, shorthand string, value net.IP, usage string) {
|
||||||
|
f.VarP(newIPValue(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPVar defines an net.IP flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to an net.IP variable in which to store the value of the flag.
|
||||||
|
func IPVar(p *net.IP, name string, value net.IP, usage string) {
|
||||||
|
CommandLine.VarP(newIPValue(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPVarP is like IPVar, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func IPVarP(p *net.IP, name, shorthand string, value net.IP, usage string) {
|
||||||
|
CommandLine.VarP(newIPValue(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IP defines an net.IP flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of an net.IP variable that stores the value of the flag.
|
||||||
|
func (f *FlagSet) IP(name string, value net.IP, usage string) *net.IP {
|
||||||
|
p := new(net.IP)
|
||||||
|
f.IPVarP(p, name, "", value, usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPP is like IP, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) IPP(name, shorthand string, value net.IP, usage string) *net.IP {
|
||||||
|
p := new(net.IP)
|
||||||
|
f.IPVarP(p, name, shorthand, value, usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// IP defines an net.IP flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of an net.IP variable that stores the value of the flag.
|
||||||
|
func IP(name string, value net.IP, usage string) *net.IP {
|
||||||
|
return CommandLine.IPP(name, "", value, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPP is like IP, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func IPP(name, shorthand string, value net.IP, usage string) *net.IP {
|
||||||
|
return CommandLine.IPP(name, shorthand, value, usage)
|
||||||
|
}
|
122
vendor/github.com/spf13/pflag/ipmask.go
generated
vendored
Normal file
122
vendor/github.com/spf13/pflag/ipmask.go
generated
vendored
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
package pflag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// -- net.IPMask value
|
||||||
|
type ipMaskValue net.IPMask
|
||||||
|
|
||||||
|
func newIPMaskValue(val net.IPMask, p *net.IPMask) *ipMaskValue {
|
||||||
|
*p = val
|
||||||
|
return (*ipMaskValue)(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ipMaskValue) String() string { return net.IPMask(*i).String() }
|
||||||
|
func (i *ipMaskValue) Set(s string) error {
|
||||||
|
ip := ParseIPv4Mask(s)
|
||||||
|
if ip == nil {
|
||||||
|
return fmt.Errorf("failed to parse IP mask: %q", s)
|
||||||
|
}
|
||||||
|
*i = ipMaskValue(ip)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ipMaskValue) Type() string {
|
||||||
|
return "ipMask"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseIPv4Mask written in IP form (e.g. 255.255.255.0).
|
||||||
|
// This function should really belong to the net package.
|
||||||
|
func ParseIPv4Mask(s string) net.IPMask {
|
||||||
|
mask := net.ParseIP(s)
|
||||||
|
if mask == nil {
|
||||||
|
if len(s) != 8 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// net.IPMask.String() actually outputs things like ffffff00
|
||||||
|
// so write a horrible parser for that as well :-(
|
||||||
|
m := []int{}
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
b := "0x" + s[2*i:2*i+2]
|
||||||
|
d, err := strconv.ParseInt(b, 0, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
m = append(m, int(d))
|
||||||
|
}
|
||||||
|
s := fmt.Sprintf("%d.%d.%d.%d", m[0], m[1], m[2], m[3])
|
||||||
|
mask = net.ParseIP(s)
|
||||||
|
if mask == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return net.IPv4Mask(mask[12], mask[13], mask[14], mask[15])
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseIPv4Mask(sval string) (interface{}, error) {
|
||||||
|
mask := ParseIPv4Mask(sval)
|
||||||
|
if mask == nil {
|
||||||
|
return nil, fmt.Errorf("unable to parse %s as net.IPMask", sval)
|
||||||
|
}
|
||||||
|
return mask, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIPv4Mask return the net.IPv4Mask value of a flag with the given name
|
||||||
|
func (f *FlagSet) GetIPv4Mask(name string) (net.IPMask, error) {
|
||||||
|
val, err := f.getFlagType(name, "ipMask", parseIPv4Mask)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return val.(net.IPMask), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPMaskVar defines an net.IPMask flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to an net.IPMask variable in which to store the value of the flag.
|
||||||
|
func (f *FlagSet) IPMaskVar(p *net.IPMask, name string, value net.IPMask, usage string) {
|
||||||
|
f.VarP(newIPMaskValue(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPMaskVarP is like IPMaskVar, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) IPMaskVarP(p *net.IPMask, name, shorthand string, value net.IPMask, usage string) {
|
||||||
|
f.VarP(newIPMaskValue(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPMaskVar defines an net.IPMask flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to an net.IPMask variable in which to store the value of the flag.
|
||||||
|
func IPMaskVar(p *net.IPMask, name string, value net.IPMask, usage string) {
|
||||||
|
CommandLine.VarP(newIPMaskValue(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPMaskVarP is like IPMaskVar, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func IPMaskVarP(p *net.IPMask, name, shorthand string, value net.IPMask, usage string) {
|
||||||
|
CommandLine.VarP(newIPMaskValue(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPMask defines an net.IPMask flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of an net.IPMask variable that stores the value of the flag.
|
||||||
|
func (f *FlagSet) IPMask(name string, value net.IPMask, usage string) *net.IPMask {
|
||||||
|
p := new(net.IPMask)
|
||||||
|
f.IPMaskVarP(p, name, "", value, usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPMaskP is like IPMask, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) IPMaskP(name, shorthand string, value net.IPMask, usage string) *net.IPMask {
|
||||||
|
p := new(net.IPMask)
|
||||||
|
f.IPMaskVarP(p, name, shorthand, value, usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPMask defines an net.IPMask flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of an net.IPMask variable that stores the value of the flag.
|
||||||
|
func IPMask(name string, value net.IPMask, usage string) *net.IPMask {
|
||||||
|
return CommandLine.IPMaskP(name, "", value, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPMaskP is like IP, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func IPMaskP(name, shorthand string, value net.IPMask, usage string) *net.IPMask {
|
||||||
|
return CommandLine.IPMaskP(name, shorthand, value, usage)
|
||||||
|
}
|
100
vendor/github.com/spf13/pflag/ipnet.go
generated
vendored
Normal file
100
vendor/github.com/spf13/pflag/ipnet.go
generated
vendored
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
package pflag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IPNet adapts net.IPNet for use as a flag.
|
||||||
|
type ipNetValue net.IPNet
|
||||||
|
|
||||||
|
func (ipnet ipNetValue) String() string {
|
||||||
|
n := net.IPNet(ipnet)
|
||||||
|
return n.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ipnet *ipNetValue) Set(value string) error {
|
||||||
|
_, n, err := net.ParseCIDR(strings.TrimSpace(value))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*ipnet = ipNetValue(*n)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ipNetValue) Type() string {
|
||||||
|
return "ipNet"
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = strings.TrimSpace
|
||||||
|
|
||||||
|
func newIPNetValue(val net.IPNet, p *net.IPNet) *ipNetValue {
|
||||||
|
*p = val
|
||||||
|
return (*ipNetValue)(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ipNetConv(sval string) (interface{}, error) {
|
||||||
|
_, n, err := net.ParseCIDR(strings.TrimSpace(sval))
|
||||||
|
if err == nil {
|
||||||
|
return *n, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("invalid string being converted to IPNet: %s", sval)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIPNet return the net.IPNet value of a flag with the given name
|
||||||
|
func (f *FlagSet) GetIPNet(name string) (net.IPNet, error) {
|
||||||
|
val, err := f.getFlagType(name, "ipNet", ipNetConv)
|
||||||
|
if err != nil {
|
||||||
|
return net.IPNet{}, err
|
||||||
|
}
|
||||||
|
return val.(net.IPNet), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPNetVar defines an net.IPNet flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to an net.IPNet variable in which to store the value of the flag.
|
||||||
|
func (f *FlagSet) IPNetVar(p *net.IPNet, name string, value net.IPNet, usage string) {
|
||||||
|
f.VarP(newIPNetValue(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPNetVarP is like IPNetVar, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) IPNetVarP(p *net.IPNet, name, shorthand string, value net.IPNet, usage string) {
|
||||||
|
f.VarP(newIPNetValue(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPNetVar defines an net.IPNet flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to an net.IPNet variable in which to store the value of the flag.
|
||||||
|
func IPNetVar(p *net.IPNet, name string, value net.IPNet, usage string) {
|
||||||
|
CommandLine.VarP(newIPNetValue(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPNetVarP is like IPNetVar, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func IPNetVarP(p *net.IPNet, name, shorthand string, value net.IPNet, usage string) {
|
||||||
|
CommandLine.VarP(newIPNetValue(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPNet defines an net.IPNet flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of an net.IPNet variable that stores the value of the flag.
|
||||||
|
func (f *FlagSet) IPNet(name string, value net.IPNet, usage string) *net.IPNet {
|
||||||
|
p := new(net.IPNet)
|
||||||
|
f.IPNetVarP(p, name, "", value, usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPNetP is like IPNet, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) IPNetP(name, shorthand string, value net.IPNet, usage string) *net.IPNet {
|
||||||
|
p := new(net.IPNet)
|
||||||
|
f.IPNetVarP(p, name, shorthand, value, usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPNet defines an net.IPNet flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of an net.IPNet variable that stores the value of the flag.
|
||||||
|
func IPNet(name string, value net.IPNet, usage string) *net.IPNet {
|
||||||
|
return CommandLine.IPNetP(name, "", value, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPNetP is like IPNet, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func IPNetP(name, shorthand string, value net.IPNet, usage string) *net.IPNet {
|
||||||
|
return CommandLine.IPNetP(name, shorthand, value, usage)
|
||||||
|
}
|
82
vendor/github.com/spf13/pflag/string.go
generated
vendored
Normal file
82
vendor/github.com/spf13/pflag/string.go
generated
vendored
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
package pflag
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// -- string Value
|
||||||
|
type stringValue string
|
||||||
|
|
||||||
|
func newStringValue(val string, p *string) *stringValue {
|
||||||
|
*p = val
|
||||||
|
return (*stringValue)(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stringValue) Set(val string) error {
|
||||||
|
*s = stringValue(val)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (s *stringValue) Type() string {
|
||||||
|
return "string"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stringValue) String() string { return fmt.Sprintf("%s", *s) }
|
||||||
|
|
||||||
|
func stringConv(sval string) (interface{}, error) {
|
||||||
|
return sval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetString return the string value of a flag with the given name
|
||||||
|
func (f *FlagSet) GetString(name string) (string, error) {
|
||||||
|
val, err := f.getFlagType(name, "string", stringConv)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return val.(string), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringVar defines a string flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to a string variable in which to store the value of the flag.
|
||||||
|
func (f *FlagSet) StringVar(p *string, name string, value string, usage string) {
|
||||||
|
f.VarP(newStringValue(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringVarP is like StringVar, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) StringVarP(p *string, name, shorthand string, value string, usage string) {
|
||||||
|
f.VarP(newStringValue(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringVar defines a string flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to a string variable in which to store the value of the flag.
|
||||||
|
func StringVar(p *string, name string, value string, usage string) {
|
||||||
|
CommandLine.VarP(newStringValue(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringVarP is like StringVar, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func StringVarP(p *string, name, shorthand string, value string, usage string) {
|
||||||
|
CommandLine.VarP(newStringValue(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// String defines a string flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of a string variable that stores the value of the flag.
|
||||||
|
func (f *FlagSet) String(name string, value string, usage string) *string {
|
||||||
|
p := new(string)
|
||||||
|
f.StringVarP(p, name, "", value, usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringP is like String, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) StringP(name, shorthand string, value string, usage string) *string {
|
||||||
|
p := new(string)
|
||||||
|
f.StringVarP(p, name, shorthand, value, usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// String defines a string flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of a string variable that stores the value of the flag.
|
||||||
|
func String(name string, value string, usage string) *string {
|
||||||
|
return CommandLine.StringP(name, "", value, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringP is like String, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func StringP(name, shorthand string, value string, usage string) *string {
|
||||||
|
return CommandLine.StringP(name, shorthand, value, usage)
|
||||||
|
}
|
111
vendor/github.com/spf13/pflag/string_slice.go
generated
vendored
Normal file
111
vendor/github.com/spf13/pflag/string_slice.go
generated
vendored
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
package pflag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/csv"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = fmt.Fprint
|
||||||
|
|
||||||
|
// -- stringSlice Value
|
||||||
|
type stringSliceValue struct {
|
||||||
|
value *[]string
|
||||||
|
changed bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newStringSliceValue(val []string, p *[]string) *stringSliceValue {
|
||||||
|
ssv := new(stringSliceValue)
|
||||||
|
ssv.value = p
|
||||||
|
*ssv.value = val
|
||||||
|
return ssv
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stringSliceValue) Set(val string) error {
|
||||||
|
stringReader := strings.NewReader(val)
|
||||||
|
csvReader := csv.NewReader(stringReader)
|
||||||
|
v, err := csvReader.Read()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !s.changed {
|
||||||
|
*s.value = v
|
||||||
|
} else {
|
||||||
|
*s.value = append(*s.value, v...)
|
||||||
|
}
|
||||||
|
s.changed = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stringSliceValue) Type() string {
|
||||||
|
return "stringSlice"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stringSliceValue) String() string { return "[" + strings.Join(*s.value, ",") + "]" }
|
||||||
|
|
||||||
|
func stringSliceConv(sval string) (interface{}, error) {
|
||||||
|
sval = strings.Trim(sval, "[]")
|
||||||
|
// An empty string would cause a slice with one (empty) string
|
||||||
|
if len(sval) == 0 {
|
||||||
|
return []string{}, nil
|
||||||
|
}
|
||||||
|
v := strings.Split(sval, ",")
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStringSlice return the []string value of a flag with the given name
|
||||||
|
func (f *FlagSet) GetStringSlice(name string) ([]string, error) {
|
||||||
|
val, err := f.getFlagType(name, "stringSlice", stringSliceConv)
|
||||||
|
if err != nil {
|
||||||
|
return []string{}, err
|
||||||
|
}
|
||||||
|
return val.([]string), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringSliceVar defines a string flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to a []string variable in which to store the value of the flag.
|
||||||
|
func (f *FlagSet) StringSliceVar(p *[]string, name string, value []string, usage string) {
|
||||||
|
f.VarP(newStringSliceValue(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringSliceVarP is like StringSliceVar, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) StringSliceVarP(p *[]string, name, shorthand string, value []string, usage string) {
|
||||||
|
f.VarP(newStringSliceValue(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringSliceVar defines a string flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to a []string variable in which to store the value of the flag.
|
||||||
|
func StringSliceVar(p *[]string, name string, value []string, usage string) {
|
||||||
|
CommandLine.VarP(newStringSliceValue(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringSliceVarP is like StringSliceVar, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func StringSliceVarP(p *[]string, name, shorthand string, value []string, usage string) {
|
||||||
|
CommandLine.VarP(newStringSliceValue(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringSlice defines a string flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of a []string variable that stores the value of the flag.
|
||||||
|
func (f *FlagSet) StringSlice(name string, value []string, usage string) *[]string {
|
||||||
|
p := []string{}
|
||||||
|
f.StringSliceVarP(&p, name, "", value, usage)
|
||||||
|
return &p
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringSliceP is like StringSlice, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) StringSliceP(name, shorthand string, value []string, usage string) *[]string {
|
||||||
|
p := []string{}
|
||||||
|
f.StringSliceVarP(&p, name, shorthand, value, usage)
|
||||||
|
return &p
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringSlice defines a string flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of a []string variable that stores the value of the flag.
|
||||||
|
func StringSlice(name string, value []string, usage string) *[]string {
|
||||||
|
return CommandLine.StringSliceP(name, "", value, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringSliceP is like StringSlice, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func StringSliceP(name, shorthand string, value []string, usage string) *[]string {
|
||||||
|
return CommandLine.StringSliceP(name, shorthand, value, usage)
|
||||||
|
}
|
91
vendor/github.com/spf13/pflag/uint.go
generated
vendored
Normal file
91
vendor/github.com/spf13/pflag/uint.go
generated
vendored
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
package pflag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// -- uint Value
|
||||||
|
type uintValue uint
|
||||||
|
|
||||||
|
func newUintValue(val uint, p *uint) *uintValue {
|
||||||
|
*p = val
|
||||||
|
return (*uintValue)(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *uintValue) Set(s string) error {
|
||||||
|
v, err := strconv.ParseUint(s, 0, 64)
|
||||||
|
*i = uintValue(v)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *uintValue) Type() string {
|
||||||
|
return "uint"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *uintValue) String() string { return fmt.Sprintf("%v", *i) }
|
||||||
|
|
||||||
|
func uintConv(sval string) (interface{}, error) {
|
||||||
|
v, err := strconv.ParseUint(sval, 0, 0)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return uint(v), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUint return the uint value of a flag with the given name
|
||||||
|
func (f *FlagSet) GetUint(name string) (uint, error) {
|
||||||
|
val, err := f.getFlagType(name, "uint", uintConv)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return val.(uint), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UintVar defines a uint flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to a uint variable in which to store the value of the flag.
|
||||||
|
func (f *FlagSet) UintVar(p *uint, name string, value uint, usage string) {
|
||||||
|
f.VarP(newUintValue(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UintVarP is like UintVar, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) UintVarP(p *uint, name, shorthand string, value uint, usage string) {
|
||||||
|
f.VarP(newUintValue(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UintVar defines a uint flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to a uint variable in which to store the value of the flag.
|
||||||
|
func UintVar(p *uint, name string, value uint, usage string) {
|
||||||
|
CommandLine.VarP(newUintValue(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UintVarP is like UintVar, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func UintVarP(p *uint, name, shorthand string, value uint, usage string) {
|
||||||
|
CommandLine.VarP(newUintValue(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint defines a uint flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of a uint variable that stores the value of the flag.
|
||||||
|
func (f *FlagSet) Uint(name string, value uint, usage string) *uint {
|
||||||
|
p := new(uint)
|
||||||
|
f.UintVarP(p, name, "", value, usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// UintP is like Uint, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) UintP(name, shorthand string, value uint, usage string) *uint {
|
||||||
|
p := new(uint)
|
||||||
|
f.UintVarP(p, name, shorthand, value, usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint defines a uint flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of a uint variable that stores the value of the flag.
|
||||||
|
func Uint(name string, value uint, usage string) *uint {
|
||||||
|
return CommandLine.UintP(name, "", value, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UintP is like Uint, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func UintP(name, shorthand string, value uint, usage string) *uint {
|
||||||
|
return CommandLine.UintP(name, shorthand, value, usage)
|
||||||
|
}
|
89
vendor/github.com/spf13/pflag/uint16.go
generated
vendored
Normal file
89
vendor/github.com/spf13/pflag/uint16.go
generated
vendored
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
package pflag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// -- uint16 value
|
||||||
|
type uint16Value uint16
|
||||||
|
|
||||||
|
func newUint16Value(val uint16, p *uint16) *uint16Value {
|
||||||
|
*p = val
|
||||||
|
return (*uint16Value)(p)
|
||||||
|
}
|
||||||
|
func (i *uint16Value) String() string { return fmt.Sprintf("%d", *i) }
|
||||||
|
func (i *uint16Value) Set(s string) error {
|
||||||
|
v, err := strconv.ParseUint(s, 0, 16)
|
||||||
|
*i = uint16Value(v)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *uint16Value) Type() string {
|
||||||
|
return "uint16"
|
||||||
|
}
|
||||||
|
|
||||||
|
func uint16Conv(sval string) (interface{}, error) {
|
||||||
|
v, err := strconv.ParseUint(sval, 0, 16)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return uint16(v), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUint16 return the uint16 value of a flag with the given name
|
||||||
|
func (f *FlagSet) GetUint16(name string) (uint16, error) {
|
||||||
|
val, err := f.getFlagType(name, "uint16", uint16Conv)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return val.(uint16), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint16Var defines a uint flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to a uint variable in which to store the value of the flag.
|
||||||
|
func (f *FlagSet) Uint16Var(p *uint16, name string, value uint16, usage string) {
|
||||||
|
f.VarP(newUint16Value(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint16VarP is like Uint16Var, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) Uint16VarP(p *uint16, name, shorthand string, value uint16, usage string) {
|
||||||
|
f.VarP(newUint16Value(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint16Var defines a uint flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to a uint variable in which to store the value of the flag.
|
||||||
|
func Uint16Var(p *uint16, name string, value uint16, usage string) {
|
||||||
|
CommandLine.VarP(newUint16Value(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint16VarP is like Uint16Var, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func Uint16VarP(p *uint16, name, shorthand string, value uint16, usage string) {
|
||||||
|
CommandLine.VarP(newUint16Value(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint16 defines a uint flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of a uint variable that stores the value of the flag.
|
||||||
|
func (f *FlagSet) Uint16(name string, value uint16, usage string) *uint16 {
|
||||||
|
p := new(uint16)
|
||||||
|
f.Uint16VarP(p, name, "", value, usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint16P is like Uint16, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) Uint16P(name, shorthand string, value uint16, usage string) *uint16 {
|
||||||
|
p := new(uint16)
|
||||||
|
f.Uint16VarP(p, name, shorthand, value, usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint16 defines a uint flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of a uint variable that stores the value of the flag.
|
||||||
|
func Uint16(name string, value uint16, usage string) *uint16 {
|
||||||
|
return CommandLine.Uint16P(name, "", value, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint16P is like Uint16, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func Uint16P(name, shorthand string, value uint16, usage string) *uint16 {
|
||||||
|
return CommandLine.Uint16P(name, shorthand, value, usage)
|
||||||
|
}
|
89
vendor/github.com/spf13/pflag/uint32.go
generated
vendored
Normal file
89
vendor/github.com/spf13/pflag/uint32.go
generated
vendored
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
package pflag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// -- uint16 value
|
||||||
|
type uint32Value uint32
|
||||||
|
|
||||||
|
func newUint32Value(val uint32, p *uint32) *uint32Value {
|
||||||
|
*p = val
|
||||||
|
return (*uint32Value)(p)
|
||||||
|
}
|
||||||
|
func (i *uint32Value) String() string { return fmt.Sprintf("%d", *i) }
|
||||||
|
func (i *uint32Value) Set(s string) error {
|
||||||
|
v, err := strconv.ParseUint(s, 0, 32)
|
||||||
|
*i = uint32Value(v)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *uint32Value) Type() string {
|
||||||
|
return "uint32"
|
||||||
|
}
|
||||||
|
|
||||||
|
func uint32Conv(sval string) (interface{}, error) {
|
||||||
|
v, err := strconv.ParseUint(sval, 0, 32)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return uint32(v), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUint32 return the uint32 value of a flag with the given name
|
||||||
|
func (f *FlagSet) GetUint32(name string) (uint32, error) {
|
||||||
|
val, err := f.getFlagType(name, "uint32", uint32Conv)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return val.(uint32), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint32Var defines a uint32 flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to a uint32 variable in which to store the value of the flag.
|
||||||
|
func (f *FlagSet) Uint32Var(p *uint32, name string, value uint32, usage string) {
|
||||||
|
f.VarP(newUint32Value(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint32VarP is like Uint32Var, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) Uint32VarP(p *uint32, name, shorthand string, value uint32, usage string) {
|
||||||
|
f.VarP(newUint32Value(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint32Var defines a uint32 flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to a uint32 variable in which to store the value of the flag.
|
||||||
|
func Uint32Var(p *uint32, name string, value uint32, usage string) {
|
||||||
|
CommandLine.VarP(newUint32Value(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint32VarP is like Uint32Var, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func Uint32VarP(p *uint32, name, shorthand string, value uint32, usage string) {
|
||||||
|
CommandLine.VarP(newUint32Value(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint32 defines a uint32 flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of a uint32 variable that stores the value of the flag.
|
||||||
|
func (f *FlagSet) Uint32(name string, value uint32, usage string) *uint32 {
|
||||||
|
p := new(uint32)
|
||||||
|
f.Uint32VarP(p, name, "", value, usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint32P is like Uint32, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) Uint32P(name, shorthand string, value uint32, usage string) *uint32 {
|
||||||
|
p := new(uint32)
|
||||||
|
f.Uint32VarP(p, name, shorthand, value, usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint32 defines a uint32 flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of a uint32 variable that stores the value of the flag.
|
||||||
|
func Uint32(name string, value uint32, usage string) *uint32 {
|
||||||
|
return CommandLine.Uint32P(name, "", value, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint32P is like Uint32, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func Uint32P(name, shorthand string, value uint32, usage string) *uint32 {
|
||||||
|
return CommandLine.Uint32P(name, shorthand, value, usage)
|
||||||
|
}
|
91
vendor/github.com/spf13/pflag/uint64.go
generated
vendored
Normal file
91
vendor/github.com/spf13/pflag/uint64.go
generated
vendored
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
package pflag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// -- uint64 Value
|
||||||
|
type uint64Value uint64
|
||||||
|
|
||||||
|
func newUint64Value(val uint64, p *uint64) *uint64Value {
|
||||||
|
*p = val
|
||||||
|
return (*uint64Value)(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *uint64Value) Set(s string) error {
|
||||||
|
v, err := strconv.ParseUint(s, 0, 64)
|
||||||
|
*i = uint64Value(v)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *uint64Value) Type() string {
|
||||||
|
return "uint64"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *uint64Value) String() string { return fmt.Sprintf("%v", *i) }
|
||||||
|
|
||||||
|
func uint64Conv(sval string) (interface{}, error) {
|
||||||
|
v, err := strconv.ParseUint(sval, 0, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return uint64(v), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUint64 return the uint64 value of a flag with the given name
|
||||||
|
func (f *FlagSet) GetUint64(name string) (uint64, error) {
|
||||||
|
val, err := f.getFlagType(name, "uint64", uint64Conv)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return val.(uint64), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint64Var defines a uint64 flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to a uint64 variable in which to store the value of the flag.
|
||||||
|
func (f *FlagSet) Uint64Var(p *uint64, name string, value uint64, usage string) {
|
||||||
|
f.VarP(newUint64Value(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint64VarP is like Uint64Var, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) Uint64VarP(p *uint64, name, shorthand string, value uint64, usage string) {
|
||||||
|
f.VarP(newUint64Value(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint64Var defines a uint64 flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to a uint64 variable in which to store the value of the flag.
|
||||||
|
func Uint64Var(p *uint64, name string, value uint64, usage string) {
|
||||||
|
CommandLine.VarP(newUint64Value(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint64VarP is like Uint64Var, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func Uint64VarP(p *uint64, name, shorthand string, value uint64, usage string) {
|
||||||
|
CommandLine.VarP(newUint64Value(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint64 defines a uint64 flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of a uint64 variable that stores the value of the flag.
|
||||||
|
func (f *FlagSet) Uint64(name string, value uint64, usage string) *uint64 {
|
||||||
|
p := new(uint64)
|
||||||
|
f.Uint64VarP(p, name, "", value, usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint64P is like Uint64, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) Uint64P(name, shorthand string, value uint64, usage string) *uint64 {
|
||||||
|
p := new(uint64)
|
||||||
|
f.Uint64VarP(p, name, shorthand, value, usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint64 defines a uint64 flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of a uint64 variable that stores the value of the flag.
|
||||||
|
func Uint64(name string, value uint64, usage string) *uint64 {
|
||||||
|
return CommandLine.Uint64P(name, "", value, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint64P is like Uint64, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func Uint64P(name, shorthand string, value uint64, usage string) *uint64 {
|
||||||
|
return CommandLine.Uint64P(name, shorthand, value, usage)
|
||||||
|
}
|
91
vendor/github.com/spf13/pflag/uint8.go
generated
vendored
Normal file
91
vendor/github.com/spf13/pflag/uint8.go
generated
vendored
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
package pflag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// -- uint8 Value
|
||||||
|
type uint8Value uint8
|
||||||
|
|
||||||
|
func newUint8Value(val uint8, p *uint8) *uint8Value {
|
||||||
|
*p = val
|
||||||
|
return (*uint8Value)(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *uint8Value) Set(s string) error {
|
||||||
|
v, err := strconv.ParseUint(s, 0, 8)
|
||||||
|
*i = uint8Value(v)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *uint8Value) Type() string {
|
||||||
|
return "uint8"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *uint8Value) String() string { return fmt.Sprintf("%v", *i) }
|
||||||
|
|
||||||
|
func uint8Conv(sval string) (interface{}, error) {
|
||||||
|
v, err := strconv.ParseUint(sval, 0, 8)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return uint8(v), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUint8 return the uint8 value of a flag with the given name
|
||||||
|
func (f *FlagSet) GetUint8(name string) (uint8, error) {
|
||||||
|
val, err := f.getFlagType(name, "uint8", uint8Conv)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return val.(uint8), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint8Var defines a uint8 flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to a uint8 variable in which to store the value of the flag.
|
||||||
|
func (f *FlagSet) Uint8Var(p *uint8, name string, value uint8, usage string) {
|
||||||
|
f.VarP(newUint8Value(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint8VarP is like Uint8Var, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) Uint8VarP(p *uint8, name, shorthand string, value uint8, usage string) {
|
||||||
|
f.VarP(newUint8Value(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint8Var defines a uint8 flag with specified name, default value, and usage string.
|
||||||
|
// The argument p points to a uint8 variable in which to store the value of the flag.
|
||||||
|
func Uint8Var(p *uint8, name string, value uint8, usage string) {
|
||||||
|
CommandLine.VarP(newUint8Value(value, p), name, "", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint8VarP is like Uint8Var, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func Uint8VarP(p *uint8, name, shorthand string, value uint8, usage string) {
|
||||||
|
CommandLine.VarP(newUint8Value(value, p), name, shorthand, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint8 defines a uint8 flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of a uint8 variable that stores the value of the flag.
|
||||||
|
func (f *FlagSet) Uint8(name string, value uint8, usage string) *uint8 {
|
||||||
|
p := new(uint8)
|
||||||
|
f.Uint8VarP(p, name, "", value, usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint8P is like Uint8, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func (f *FlagSet) Uint8P(name, shorthand string, value uint8, usage string) *uint8 {
|
||||||
|
p := new(uint8)
|
||||||
|
f.Uint8VarP(p, name, shorthand, value, usage)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint8 defines a uint8 flag with specified name, default value, and usage string.
|
||||||
|
// The return value is the address of a uint8 variable that stores the value of the flag.
|
||||||
|
func Uint8(name string, value uint8, usage string) *uint8 {
|
||||||
|
return CommandLine.Uint8P(name, "", value, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint8P is like Uint8, but accepts a shorthand letter that can be used after a single dash.
|
||||||
|
func Uint8P(name, shorthand string, value uint8, usage string) *uint8 {
|
||||||
|
return CommandLine.Uint8P(name, shorthand, value, usage)
|
||||||
|
}
|
22
vendor/github.com/tdewolff/buffer/LICENSE.md
generated
vendored
Normal file
22
vendor/github.com/tdewolff/buffer/LICENSE.md
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
Copyright (c) 2015 Taco de Wolff
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use,
|
||||||
|
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following
|
||||||
|
conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
42
vendor/github.com/tdewolff/buffer/README.md
generated
vendored
Normal file
42
vendor/github.com/tdewolff/buffer/README.md
generated
vendored
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
# Buffer [![GoDoc](http://godoc.org/github.com/tdewolff/buffer?status.svg)](http://godoc.org/github.com/tdewolff/buffer)
|
||||||
|
|
||||||
|
This package contains several buffer types used in https://github.com/tdewolff/parse for example.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
Run the following command
|
||||||
|
|
||||||
|
go get github.com/tdewolff/buffer
|
||||||
|
|
||||||
|
or add the following import and run the project with `go get`
|
||||||
|
``` go
|
||||||
|
import "github.com/tdewolff/buffer"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Reader
|
||||||
|
Reader is a wrapper around a `[]byte` that implements the `io.Reader` interface. It is a much thinner layer than `bytes.Buffer` provides and is therefore faster.
|
||||||
|
|
||||||
|
## Writer
|
||||||
|
Writer is a buffer that implements the `io.Writer` interface. It is a much thinner layer than `bytes.Buffer` provides and is therefore faster. It will expand the buffer when needed.
|
||||||
|
|
||||||
|
The reset functionality allows for better memory reuse. After calling `Reset`, it will overwrite the current buffer and thus reduce allocations.
|
||||||
|
|
||||||
|
## Shifter
|
||||||
|
Shifter is a read buffer specifically for building lexers. It reads in chunks from an `io.Reader` and allows to keep track two positions: the start and end position. The start position is the beginning of the current token being parsed, the end position is being moved forward until a valid token is found. Calling `Shift` will collapse the positions to the end and return the parsed `[]byte`.
|
||||||
|
|
||||||
|
Moving the end position can go through `Move(int)` which also accepts negative integers or `MoveTo(int)` where the integer will be the new length of the selected bytes. `MoveTo(int)` is useful when you saved a previous position through `Pos() int` and want to return to that position.
|
||||||
|
|
||||||
|
`Peek(int) byte` will peek forward (relative to the end position, ie. the position set with Move/MoveTo) and return the byte at that location. `PeekRune(int) (rune, int)` returns UTF-8 runes and its length at the given **byte** position. Consecutive calls to Peek **may invalidate previously returned byte slices**. So if you need to use the content of a byte slice after the next call to `Peek(int) byte`, it needs to be copied in principal (see exception below).
|
||||||
|
|
||||||
|
`Bytes() []byte` will return the currently selected bytes, `Skip()` will collapse the selection. `Shift() []byte` is a combination of `Bytes() []byte` and `Skip()`.
|
||||||
|
|
||||||
|
When the internal `io.Reader` returned an error, `Err() error` will return that error (even if subsequent peeks are still possible). If `Peek(int) byte` returns `0` when an error occurred. `IsEOF() bool` is a faster alternative than `Err() == io.EOF`, if it returns true it means the internal buffer will not be reallocated/overwritten. So returned byte slices need not be copied for use after subsequent `Peek(int) byte` calls. When the `io.Reader` provides the `Bytes() []byte` function (which `Reader` does in this package), it will use that buffer instead and thus `IsEOF()` returns always `true` (ie. copying returned slices is not needed).
|
||||||
|
|
||||||
|
## Lexer
|
||||||
|
Lexer is an improvement over Shifter in that it does not need the returned byte slices to be copied. Instead you can call `ShiftLen() int`, which returns the number of bytes that have been shifted since the previous call to `ShiftLen`, and use that to specify how many bytes need to be freed up from the buffer. Calling `Free(n int)` frees up `n` bytes from the internal buffer(s). It holds an array of buffers to accomodate for keeping everything in-memory. If you don't need to keep returned byte slices around, call `Free(ShiftLen())` after every `Shift` call.
|
||||||
|
|
||||||
|
The `MoveTo(int)` function has been renamed to `Rewind(int)` to fit its meaning better. Also `Bytes() []byte` has been renamed to `Lexeme() []byte` for the same reason.
|
||||||
|
|
||||||
|
## License
|
||||||
|
Released under the [MIT license](LICENSE.md).
|
||||||
|
|
||||||
|
[1]: http://golang.org/ "Go Language"
|
15
vendor/github.com/tdewolff/buffer/buffer.go
generated
vendored
Normal file
15
vendor/github.com/tdewolff/buffer/buffer.go
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
/*
|
||||||
|
Package buffer contains buffer and wrapper types for byte slices. It is useful for writing lexers or other high-performance byte slice handling.
|
||||||
|
|
||||||
|
The `Reader` and `Writer` types implement the `io.Reader` and `io.Writer` respectively and provide a thinner and faster interface than `bytes.Buffer`.
|
||||||
|
The `Shifter` type is useful for building lexers because it keeps track of the start and end position of a byte selection, and shifts the bytes whenever a valid token is found.
|
||||||
|
The `Lexer` is however an improved version of `Shifter`, allowing zero-copy for the parser by using a (kind of) ring buffer underneath.
|
||||||
|
*/
|
||||||
|
package buffer
|
||||||
|
|
||||||
|
// defaultBufSize specifies the default initial length of internal buffers.
|
||||||
|
var defaultBufSize = 4096
|
||||||
|
|
||||||
|
// MinBuf specifies the default initial length of internal buffers.
|
||||||
|
// Solely here to support old versions of parse.
|
||||||
|
var MinBuf = defaultBufSize
|
221
vendor/github.com/tdewolff/buffer/lexer.go
generated
vendored
Normal file
221
vendor/github.com/tdewolff/buffer/lexer.go
generated
vendored
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
package buffer
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
type block struct {
|
||||||
|
buf []byte
|
||||||
|
next int // index in pool plus one
|
||||||
|
active bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type bufferPool struct {
|
||||||
|
pool []block
|
||||||
|
head int // index in pool plus one
|
||||||
|
tail int // index in pool plus one
|
||||||
|
|
||||||
|
pos int // byte pos in tail
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *bufferPool) swap(oldBuf []byte, size int) []byte {
|
||||||
|
// find new buffer that can be reused
|
||||||
|
swap := -1
|
||||||
|
for i := 0; i < len(z.pool); i++ {
|
||||||
|
if !z.pool[i].active && size <= cap(z.pool[i].buf) {
|
||||||
|
swap = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if swap == -1 { // no free buffer found for reuse
|
||||||
|
if z.tail == 0 && z.pos >= len(oldBuf) && size <= cap(oldBuf) { // but we can reuse the current buffer!
|
||||||
|
z.pos -= len(oldBuf)
|
||||||
|
return oldBuf[:0]
|
||||||
|
}
|
||||||
|
// allocate new
|
||||||
|
z.pool = append(z.pool, block{make([]byte, 0, size), 0, true})
|
||||||
|
swap = len(z.pool) - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
newBuf := z.pool[swap].buf
|
||||||
|
|
||||||
|
// put current buffer into pool
|
||||||
|
z.pool[swap] = block{oldBuf, 0, true}
|
||||||
|
if z.head != 0 {
|
||||||
|
z.pool[z.head-1].next = swap + 1
|
||||||
|
}
|
||||||
|
z.head = swap + 1
|
||||||
|
if z.tail == 0 {
|
||||||
|
z.tail = swap + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return newBuf[:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *bufferPool) free(n int) {
|
||||||
|
z.pos += n
|
||||||
|
// move the tail over to next buffers
|
||||||
|
for z.tail != 0 && z.pos >= len(z.pool[z.tail-1].buf) {
|
||||||
|
z.pos -= len(z.pool[z.tail-1].buf)
|
||||||
|
newTail := z.pool[z.tail-1].next
|
||||||
|
z.pool[z.tail-1].active = false // after this, any thread may pick up the inactive buffer, so it can't be used anymore
|
||||||
|
z.tail = newTail
|
||||||
|
}
|
||||||
|
if z.tail == 0 {
|
||||||
|
z.head = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lexer is a buffered reader that allows peeking forward and shifting, taking an io.Reader.
|
||||||
|
// It keeps data in-memory until Free, taking a byte length, is called to move beyond the data.
|
||||||
|
type Lexer struct {
|
||||||
|
r io.Reader
|
||||||
|
err error
|
||||||
|
|
||||||
|
pool bufferPool
|
||||||
|
|
||||||
|
buf []byte
|
||||||
|
start int // index in buf
|
||||||
|
pos int // index in buf
|
||||||
|
prevStart int
|
||||||
|
|
||||||
|
free int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLexer returns a new Lexer for a given io.Reader with a 4kB estimated buffer size.
|
||||||
|
// If the io.Reader implements Bytes, that buffer is used instead.
|
||||||
|
func NewLexer(r io.Reader) *Lexer {
|
||||||
|
return NewLexerSize(r, defaultBufSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLexerSize returns a new Lexer for a given io.Reader and estimated required buffer size.
|
||||||
|
// If the io.Reader implements Bytes, that buffer is used instead.
|
||||||
|
func NewLexerSize(r io.Reader, size int) *Lexer {
|
||||||
|
// if reader has the bytes in memory already, use that instead
|
||||||
|
if buffer, ok := r.(interface {
|
||||||
|
Bytes() []byte
|
||||||
|
}); ok {
|
||||||
|
return &Lexer{
|
||||||
|
err: io.EOF,
|
||||||
|
buf: buffer.Bytes(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &Lexer{
|
||||||
|
r: r,
|
||||||
|
buf: make([]byte, 0, size),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Lexer) read(pos int) byte {
|
||||||
|
if z.err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// free unused bytes
|
||||||
|
z.pool.free(z.free)
|
||||||
|
z.free = 0
|
||||||
|
|
||||||
|
// get new buffer
|
||||||
|
c := cap(z.buf)
|
||||||
|
p := pos - z.start + 1
|
||||||
|
if 2*p > c { // if the token is larger than half the buffer, increase buffer size
|
||||||
|
c = 2*c + p
|
||||||
|
}
|
||||||
|
d := len(z.buf) - z.start
|
||||||
|
buf := z.pool.swap(z.buf[:z.start], c)
|
||||||
|
copy(buf[:d], z.buf[z.start:]) // copy the left-overs (unfinished token) from the old buffer
|
||||||
|
|
||||||
|
// read in new data for the rest of the buffer
|
||||||
|
var n int
|
||||||
|
n, z.err = z.r.Read(buf[d:cap(buf)])
|
||||||
|
pos -= z.start
|
||||||
|
z.pos -= z.start
|
||||||
|
z.start, z.buf = 0, buf[:d+n]
|
||||||
|
if pos >= d+n {
|
||||||
|
if z.err == nil {
|
||||||
|
z.err = io.EOF
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return z.buf[pos]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err returns the error returned from io.Reader. It may still return valid bytes for a while though.
|
||||||
|
func (z *Lexer) Err() error {
|
||||||
|
if z.err == io.EOF && z.pos < len(z.buf) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return z.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free frees up bytes of length n from previously shifted tokens.
|
||||||
|
// Each call to Shift should at one point be followed by a call to Free with a length returned by ShiftLen.
|
||||||
|
func (z *Lexer) Free(n int) {
|
||||||
|
z.free += n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Peek returns the ith byte relative to the end position and possibly does an allocation.
|
||||||
|
// Peek returns zero when an error has occurred, Err returns the error.
|
||||||
|
// TODO: inline function
|
||||||
|
func (z *Lexer) Peek(pos int) byte {
|
||||||
|
pos += z.pos
|
||||||
|
if uint(pos) < uint(len(z.buf)) { // uint for BCE
|
||||||
|
return z.buf[pos]
|
||||||
|
}
|
||||||
|
return z.read(pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PeekRune returns the rune and rune length of the ith byte relative to the end position.
|
||||||
|
func (z *Lexer) PeekRune(pos int) (rune, int) {
|
||||||
|
// from unicode/utf8
|
||||||
|
c := z.Peek(pos)
|
||||||
|
if c < 0xC0 {
|
||||||
|
return rune(c), 1
|
||||||
|
} else if c < 0xE0 {
|
||||||
|
return rune(c&0x1F)<<6 | rune(z.Peek(pos+1)&0x3F), 2
|
||||||
|
} else if c < 0xF0 {
|
||||||
|
return rune(c&0x0F)<<12 | rune(z.Peek(pos+1)&0x3F)<<6 | rune(z.Peek(pos+2)&0x3F), 3
|
||||||
|
}
|
||||||
|
return rune(c&0x07)<<18 | rune(z.Peek(pos+1)&0x3F)<<12 | rune(z.Peek(pos+2)&0x3F)<<6 | rune(z.Peek(pos+3)&0x3F), 4
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move advances the position.
|
||||||
|
func (z *Lexer) Move(n int) {
|
||||||
|
z.pos += n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pos returns a mark to which can be rewinded.
|
||||||
|
func (z *Lexer) Pos() int {
|
||||||
|
return z.pos - z.start
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rewind rewinds the position to the given position.
|
||||||
|
func (z *Lexer) Rewind(pos int) {
|
||||||
|
z.pos = z.start + pos
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lexeme returns the bytes of the current selection.
|
||||||
|
func (z *Lexer) Lexeme() []byte {
|
||||||
|
return z.buf[z.start:z.pos]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip collapses the position to the end of the selection.
|
||||||
|
func (z *Lexer) Skip() {
|
||||||
|
z.start = z.pos
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shift returns the bytes of the current selection and collapses the position to the end of the selection.
|
||||||
|
// It also returns the number of bytes we moved since the last call to Shift. This can be used in calls to Free.
|
||||||
|
func (z *Lexer) Shift() []byte {
|
||||||
|
if z.pos > len(z.buf) { // make sure we peeked at least as much as we shift
|
||||||
|
z.read(z.pos - 1)
|
||||||
|
}
|
||||||
|
b := z.buf[z.start:z.pos]
|
||||||
|
z.start = z.pos
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShiftLen returns the number of bytes moved since the last call to ShiftLen. This can be used in calls to Free because it takes into account multiple Shifts or Skips.
|
||||||
|
func (z *Lexer) ShiftLen() int {
|
||||||
|
n := z.start - z.prevStart
|
||||||
|
z.prevStart = z.start
|
||||||
|
return n
|
||||||
|
}
|
39
vendor/github.com/tdewolff/buffer/reader.go
generated
vendored
Normal file
39
vendor/github.com/tdewolff/buffer/reader.go
generated
vendored
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
package buffer
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
// Reader implements an io.Reader over a byte slice.
|
||||||
|
type Reader struct {
|
||||||
|
buf []byte
|
||||||
|
pos int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewReader returns a new Reader for a given byte slice.
|
||||||
|
func NewReader(buf []byte) *Reader {
|
||||||
|
return &Reader{
|
||||||
|
buf: buf,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads bytes into the given byte slice and returns the number of bytes read and an error if occurred.
|
||||||
|
func (r *Reader) Read(b []byte) (n int, err error) {
|
||||||
|
if len(b) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
if r.pos >= len(r.buf) {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
n = copy(b, r.buf[r.pos:])
|
||||||
|
r.pos += n
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes returns the underlying byte slice.
|
||||||
|
func (r *Reader) Bytes() []byte {
|
||||||
|
return r.buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset resets the position of the read pointer to the beginning of the underlying byte slice
|
||||||
|
func (r *Reader) Reset() {
|
||||||
|
r.pos = 0
|
||||||
|
}
|
144
vendor/github.com/tdewolff/buffer/shifter.go
generated
vendored
Normal file
144
vendor/github.com/tdewolff/buffer/shifter.go
generated
vendored
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
package buffer
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
// Shifter is a buffered reader that allows peeking forward and shifting, taking an io.Reader.
|
||||||
|
type Shifter struct {
|
||||||
|
r io.Reader
|
||||||
|
err error
|
||||||
|
eof bool
|
||||||
|
|
||||||
|
buf []byte
|
||||||
|
pos int
|
||||||
|
end int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewShifter returns a new Shifter for a given io.Reader with a 4kB estimated buffer size.
|
||||||
|
// If the io.Reader implements Bytes, that buffer is used instead.
|
||||||
|
func NewShifter(r io.Reader) *Shifter {
|
||||||
|
return NewShifterSize(r, defaultBufSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewShifterSize returns a new Shifter for a given io.Reader and estimated required buffer size.
|
||||||
|
// If the io.Reader implements Bytes, that buffer is used instead.
|
||||||
|
func NewShifterSize(r io.Reader, size int) *Shifter {
|
||||||
|
// If reader has the bytes in memory already, use that instead!
|
||||||
|
if buffer, ok := r.(interface {
|
||||||
|
Bytes() []byte
|
||||||
|
}); ok {
|
||||||
|
return &Shifter{
|
||||||
|
err: io.EOF,
|
||||||
|
eof: true,
|
||||||
|
buf: buffer.Bytes(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
z := &Shifter{
|
||||||
|
r: r,
|
||||||
|
buf: make([]byte, 0, size),
|
||||||
|
}
|
||||||
|
z.Peek(0)
|
||||||
|
return z
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err returns the error returned from io.Reader. It may still return valid bytes for a while though.
|
||||||
|
func (z *Shifter) Err() error {
|
||||||
|
if z.eof && z.end < len(z.buf) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return z.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEOF returns true when it has encountered EOF meaning that it has loaded the last data in memory (ie. previously returned byte slice will not be overwritten by Peek).
|
||||||
|
// Calling IsEOF is faster than checking Err() == io.EOF.
|
||||||
|
func (z *Shifter) IsEOF() bool {
|
||||||
|
return z.eof
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Shifter) read(end int) byte {
|
||||||
|
if z.err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// reallocate a new buffer (possibly larger)
|
||||||
|
c := cap(z.buf)
|
||||||
|
d := len(z.buf) - z.pos
|
||||||
|
var buf []byte
|
||||||
|
if 2*d > c {
|
||||||
|
buf = make([]byte, d, 2*c+end-z.pos)
|
||||||
|
} else {
|
||||||
|
buf = z.buf[:d]
|
||||||
|
}
|
||||||
|
copy(buf, z.buf[z.pos:])
|
||||||
|
|
||||||
|
// read in to fill the buffer till capacity
|
||||||
|
var n int
|
||||||
|
n, z.err = z.r.Read(buf[d:cap(buf)])
|
||||||
|
z.eof = (z.err == io.EOF)
|
||||||
|
end -= z.pos
|
||||||
|
z.end -= z.pos
|
||||||
|
z.pos, z.buf = 0, buf[:d+n]
|
||||||
|
if n == 0 {
|
||||||
|
if z.err == nil {
|
||||||
|
z.err = io.EOF
|
||||||
|
z.eof = true
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return z.buf[end]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Peek returns the ith byte relative to the end position and possibly does an allocation. Calling Peek may invalidate previous returned byte slices by Bytes or Shift, unless IsEOF returns true.
|
||||||
|
// Peek returns zero when an error has occurred, Err returns the error.
|
||||||
|
func (z *Shifter) Peek(end int) byte {
|
||||||
|
end += z.end
|
||||||
|
if end >= len(z.buf) {
|
||||||
|
return z.read(end)
|
||||||
|
}
|
||||||
|
return z.buf[end]
|
||||||
|
}
|
||||||
|
|
||||||
|
// PeekRune returns the rune and rune length of the ith byte relative to the end position.
|
||||||
|
func (z *Shifter) PeekRune(i int) (rune, int) {
|
||||||
|
// from unicode/utf8
|
||||||
|
c := z.Peek(i)
|
||||||
|
if c < 0xC0 {
|
||||||
|
return rune(c), 1
|
||||||
|
} else if c < 0xE0 {
|
||||||
|
return rune(c&0x1F)<<6 | rune(z.Peek(i+1)&0x3F), 2
|
||||||
|
} else if c < 0xF0 {
|
||||||
|
return rune(c&0x0F)<<12 | rune(z.Peek(i+1)&0x3F)<<6 | rune(z.Peek(i+2)&0x3F), 3
|
||||||
|
}
|
||||||
|
return rune(c&0x07)<<18 | rune(z.Peek(i+1)&0x3F)<<12 | rune(z.Peek(i+2)&0x3F)<<6 | rune(z.Peek(i+3)&0x3F), 4
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move advances the end position.
|
||||||
|
func (z *Shifter) Move(n int) {
|
||||||
|
z.end += n
|
||||||
|
}
|
||||||
|
|
||||||
|
// MoveTo sets the end position.
|
||||||
|
func (z *Shifter) MoveTo(n int) {
|
||||||
|
z.end = z.pos + n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pos returns the end position.
|
||||||
|
func (z *Shifter) Pos() int {
|
||||||
|
return z.end - z.pos
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes returns the bytes of the current selection.
|
||||||
|
func (z *Shifter) Bytes() []byte {
|
||||||
|
return z.buf[z.pos:z.end]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shift returns the bytes of the current selection and collapses the position to the end.
|
||||||
|
func (z *Shifter) Shift() []byte {
|
||||||
|
b := z.buf[z.pos:z.end]
|
||||||
|
z.pos = z.end
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip collapses the position to the end.
|
||||||
|
func (z *Shifter) Skip() {
|
||||||
|
z.pos = z.end
|
||||||
|
}
|
41
vendor/github.com/tdewolff/buffer/writer.go
generated
vendored
Normal file
41
vendor/github.com/tdewolff/buffer/writer.go
generated
vendored
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
package buffer
|
||||||
|
|
||||||
|
// Writer implements an io.Writer over a byte slice.
|
||||||
|
type Writer struct {
|
||||||
|
buf []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWriter returns a new Writer for a given byte slice.
|
||||||
|
func NewWriter(buf []byte) *Writer {
|
||||||
|
return &Writer{
|
||||||
|
buf: buf,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes bytes from the given byte slice and returns the number of bytes written and an error if occurred. When err != nil, n == 0.
|
||||||
|
func (w *Writer) Write(b []byte) (int, error) {
|
||||||
|
n := len(b)
|
||||||
|
end := len(w.buf)
|
||||||
|
if end+n > cap(w.buf) {
|
||||||
|
buf := make([]byte, end, 2*cap(w.buf)+n)
|
||||||
|
copy(buf, w.buf)
|
||||||
|
w.buf = buf
|
||||||
|
}
|
||||||
|
w.buf = w.buf[:end+n]
|
||||||
|
return copy(w.buf[end:], b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the length of the underlying byte slice.
|
||||||
|
func (w *Writer) Len() int {
|
||||||
|
return len(w.buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes returns the underlying byte slice.
|
||||||
|
func (w *Writer) Bytes() []byte {
|
||||||
|
return w.buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset empties and reuses the current buffer. Subsequent writes will overwrite the buffer, so any reference to the underlying slice is invalidated after this call.
|
||||||
|
func (w *Writer) Reset() {
|
||||||
|
w.buf = w.buf[:0]
|
||||||
|
}
|
3
vendor/github.com/tdewolff/minify/.travis.yml
generated
vendored
Normal file
3
vendor/github.com/tdewolff/minify/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
language: go
|
||||||
|
script:
|
||||||
|
- go test -v ./...
|
22
vendor/github.com/tdewolff/minify/LICENSE.md
generated
vendored
Normal file
22
vendor/github.com/tdewolff/minify/LICENSE.md
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
Copyright (c) 2015 Taco de Wolff
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use,
|
||||||
|
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following
|
||||||
|
conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
536
vendor/github.com/tdewolff/minify/README.md
generated
vendored
Normal file
536
vendor/github.com/tdewolff/minify/README.md
generated
vendored
Normal file
|
@ -0,0 +1,536 @@
|
||||||
|
# Minify [![Build Status](https://travis-ci.org/tdewolff/minify.svg?branch=master)](https://travis-ci.org/tdewolff/minify) [![GoDoc](http://godoc.org/github.com/tdewolff/minify?status.svg)](http://godoc.org/github.com/tdewolff/minify) [![Join the chat at https://gitter.im/tdewolff/minify](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/tdewolff/minify?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
|
|
||||||
|
**I will be away for 5 months, starting May. v2 should be the preferred stable release to use. Master has some new changes for SVG that haven't yet endured the test of time, bug reports are appreciated.**
|
||||||
|
|
||||||
|
Minify is a minifier package written in [Go][1]. It has build-in HTML5, CSS3, JS, JSON, SVG and XML minifiers and provides an interface to implement any minifier. Minification is the process of removing bytes from a file (such as whitespace) without changing its output and therefore speeding up transmission over the internet. The implemented minifiers are high performance and streaming (which implies O(n)).
|
||||||
|
|
||||||
|
It associates minification functions with mimetypes, allowing embedded resources (like CSS or JS in HTML files) to be minified too. The user can add any mime-based implementation. Users can also implement a mimetype using an external command (like the ClosureCompiler, UglifyCSS, ...). It is possible to pass parameters through the mediatype to specify the charset for example.
|
||||||
|
|
||||||
|
[Demo](http://go.tacodewolff.nl/) · [CLI](https://github.com/tdewolff/minify/tree/master/cmd/minify) · [Download](https://dl.equinox.io/tdewolff/minify/stable)
|
||||||
|
|
||||||
|
#### Table of Contents
|
||||||
|
|
||||||
|
- [Minify](#minify---)
|
||||||
|
- [Prologue](#prologue)
|
||||||
|
- [Installation](#installation)
|
||||||
|
- [API stability](#api-stability)
|
||||||
|
- [Testing](#testing)
|
||||||
|
- [HTML](#html--)
|
||||||
|
- [Whitespace removal](#whitespace-removal)
|
||||||
|
- [CSS](#css--)
|
||||||
|
- [JS](#js--)
|
||||||
|
- [JSON](#json--)
|
||||||
|
- [SVG](#svg--)
|
||||||
|
- [XML](#xml--)
|
||||||
|
- [Usage](#usage)
|
||||||
|
- [New](#new)
|
||||||
|
- [From reader](#from-reader)
|
||||||
|
- [From bytes](#from-bytes)
|
||||||
|
- [From string](#from-string)
|
||||||
|
- [Custom minifier](#custom-minifier)
|
||||||
|
- [Mediatypes](#mediatypes)
|
||||||
|
- [Examples](#examples)
|
||||||
|
- [Common minifiers](#common-minifiers)
|
||||||
|
- [Custom minifier](#custom-minifier-1)
|
||||||
|
- [ResponseWriter](#responsewriter)
|
||||||
|
- [Templates](#templates)
|
||||||
|
- [License](#license)
|
||||||
|
|
||||||
|
#### Status
|
||||||
|
|
||||||
|
* CSS: **fully implemented**
|
||||||
|
* HTML: **fully implemented**
|
||||||
|
* JS: basic JSmin-like implementation
|
||||||
|
* JSON: **fully implemented**
|
||||||
|
* SVG: partially implemented; in development
|
||||||
|
* XML: **fully implemented**
|
||||||
|
|
||||||
|
## Prologue
|
||||||
|
Minifiers or bindings to minifiers exist in almost all programming languages. Some implementations are merely using several regular-expressions to trim whitespace and comments (even though regex for parsing HTML/XML is ill-advised, for a good read see [Regular Expressions: Now You Have Two Problems](http://blog.codinghorror.com/regular-expressions-now-you-have-two-problems/)). Some implementations are much more profound, such as the [YUI Compressor](http://yui.github.io/yuicompressor/) and [Google Closure Compiler](https://github.com/google/closure-compiler) for JS. As most existing implementations either use Java or JavaScript and don't focus on performance, they are pretty slow. And loading the whole file into memory is bad for really large files (or impossible for infinite streams).
|
||||||
|
|
||||||
|
This minifier proves to be that fast and extensive minifier that can handle HTML and any other filetype it may contain (CSS, JS, ...). It streams the input and output and can minify files concurrently.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
Run the following command
|
||||||
|
|
||||||
|
go get github.com/tdewolff/minify
|
||||||
|
|
||||||
|
or add the following imports and run the project with `go get`
|
||||||
|
``` go
|
||||||
|
import (
|
||||||
|
"github.com/tdewolff/minify"
|
||||||
|
"github.com/tdewolff/minify/css"
|
||||||
|
"github.com/tdewolff/minify/html"
|
||||||
|
"github.com/tdewolff/minify/js"
|
||||||
|
"github.com/tdewolff/minify/json"
|
||||||
|
"github.com/tdewolff/minify/svg"
|
||||||
|
"github.com/tdewolff/minify/xml"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
## API stability
|
||||||
|
There is no guarantee for absolute stability, but I take issues and bugs seriously and don't take API changes lightly. The library will be maintained in a compatible way unless vital bugs prevent me from doing so. There has been one API change after v1 which added options support and I took the opportunity to push through some more API clean up as well. There are no plans whatsoever for future API changes.
|
||||||
|
|
||||||
|
- minify-v1.0.0 depends on parse-v1.0.0
|
||||||
|
- minify-v1.1.0 depends on parse-v1.1.0
|
||||||
|
- minify-v2.0.0 depends on parse-v2.0.0
|
||||||
|
- minify-tip will always compile with my other packages on tip
|
||||||
|
|
||||||
|
The API differences between v1 and v2 are listed below. If `m := minify.New()` and `w` and `r` are your writer and reader respectfully, then **v1** → **v2**:
|
||||||
|
- `minify.Bytes(m, ...)` → `m.Bytes(...)`
|
||||||
|
- `minify.String(m, ...)` → `m.String(...)`
|
||||||
|
- `html.Minify(m, "text/html", w, r)` → `html.Minify(m, w, r, nil)` also for `css`, `js`, ...
|
||||||
|
- `css.Minify(m, "text/css;inline=1", w, r)` → `css.Minify(m, w, r, map[string]string{"inline":"1"})`
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
For all subpackages and the imported `parse` and `buffer` packages, test coverage of 100% is pursued. Besides full coverage, the minifiers are [fuzz tested](https://github.com/tdewolff/fuzz) using [github.com/dvyukov/go-fuzz](http://www.github.com/dvyukov/go-fuzz), see [the wiki](https://github.com/tdewolff/minify/wiki) for the most important bugs found by fuzz testing. Furthermore am I working on adding visual testing to ensure that minification doesn't change anything visually. By using the WebKit browser to render the original and minified pages we can check whether any pixel is different.
|
||||||
|
|
||||||
|
These tests ensure that everything works as intended, the code does not crash (whatever the input) and that it doesn't change the final result visually. If you still encounter a bug, please report [here](https://github.com/tdewolff/minify/issues)!
|
||||||
|
|
||||||
|
## HTML
|
||||||
|
|
||||||
|
HTML (with JS and CSS) minification typically runs at about 40MB/s ~= 140GB/h, depending on the composition of the file.
|
||||||
|
|
||||||
|
Website | Original | Minified | Ratio | Time<sup>*</sup>
|
||||||
|
------- | -------- | -------- | ----- | -----------------------
|
||||||
|
[Amazon](http://www.amazon.com/) | 463kB | **414kB** | 90% | 11ms
|
||||||
|
[BBC](http://www.bbc.com/) | 113kB | **96kB** | 85% | 3ms
|
||||||
|
[StackOverflow](http://stackoverflow.com/) | 201kB | **182kB** | 91% | 5ms
|
||||||
|
[Wikipedia](http://en.wikipedia.org/wiki/President_of_the_United_States) | 435kB | **410kB** | 94%<sup>**</sup> | 10ms
|
||||||
|
|
||||||
|
<sup>*</sup>These times are measured on my home computer which is an average development computer. The duration varies a lot but it's important to see it's in the 10ms range! The benchmark uses all the minifiers and excludes reading from and writing to the file from the measurement.
|
||||||
|
|
||||||
|
<sup>**</sup>Is already somewhat minified, so this doesn't reflect the full potential of this minifier.
|
||||||
|
|
||||||
|
The HTML5 minifier uses these minifications:
|
||||||
|
|
||||||
|
- strip unnecessary whitespace and otherwise collapse it to one space (or newline if it originally contained a newline)
|
||||||
|
- strip superfluous quotes, or uses single/double quotes whichever requires fewer escapes
|
||||||
|
- strip default attribute values and attribute boolean values
|
||||||
|
- strip some empty attributes
|
||||||
|
- strip unrequired tags (`html`, `head`, `body`, ...)
|
||||||
|
- strip unrequired end tags (`tr`, `td`, `li`, ... and often `p`)
|
||||||
|
- strip default protocols (`http:`, `https:` and `javascript:`)
|
||||||
|
- strip comments (except conditional comments)
|
||||||
|
- shorten `doctype` and `meta` charset
|
||||||
|
- lowercase tags, attributes and some values to enhance gzip compression
|
||||||
|
|
||||||
|
Options:
|
||||||
|
|
||||||
|
- `KeepDefaultAttrVals` do not remove default attribute value such as `<script type="text/javascript">`
|
||||||
|
- `KeepWhitespace` do not remove whitespace between inline tags but still collapse multiple whitespace characters into one
|
||||||
|
|
||||||
|
After recent benchmarking and profiling it became really fast and minifies pages in the 10ms range, making it viable for on-the-fly minification.
|
||||||
|
|
||||||
|
However, be careful when doing on-the-fly minification. Minification typically trims off 10% and does this at worst around about 20MB/s. This means users have to download slower than 2MB/s to make on-the-fly minification worthwhile. This may or may not apply in your situation. Rather use caching!
|
||||||
|
|
||||||
|
### Whitespace removal
|
||||||
|
The whitespace removal mechanism collapses all sequences of whitespace (spaces, newlines, tabs) to a single space. If the sequence contained a newline or carriage return it will collapse into a newline character instead. It trims all text parts (in between tags) depending on whether it was preceded by a space from a previous piece of text and whether it is followed up by a block element or an inline element. In the former case we can omit spaces while for inline elements whitespace has significance.
|
||||||
|
|
||||||
|
Make sure your HTML doesn't depend on whitespace between `block` elements that have been changed to `inline` or `inline-block` elements using CSS. Your layout *should not* depend on those whitespaces as the minifier will remove them. An example is a menu consisting of multiple `<li>` that have `display:inline-block` applied and have whitespace in between them. It is bad practise to rely on whitespace for element positioning anyways!
|
||||||
|
|
||||||
|
## CSS
|
||||||
|
|
||||||
|
Minification typically runs at about 25MB/s ~= 90GB/h.
|
||||||
|
|
||||||
|
Library | Original | Minified | Ratio | Time<sup>*</sup>
|
||||||
|
------- | -------- | -------- | ----- | -----------------------
|
||||||
|
[Bootstrap](http://getbootstrap.com/) | 134kB | **111kB** | 83% | 5ms
|
||||||
|
[Gumby](http://gumbyframework.com/) | 182kB | **167kB** | 90% | 7ms
|
||||||
|
|
||||||
|
<sup>*</sup>The benchmark excludes the time reading from and writing to a file from the measurement.
|
||||||
|
|
||||||
|
The CSS minifier will only use safe minifications:
|
||||||
|
|
||||||
|
- remove comments and unnecessary whitespace
|
||||||
|
- remove trailing semicolons
|
||||||
|
- optimize `margin`, `padding` and `border-width` number of sides
|
||||||
|
- shorten numbers by removing unnecessary `+` and zeros and rewriting with/without exponent
|
||||||
|
- remove dimension and percentage for zero values
|
||||||
|
- remove quotes for URLs
|
||||||
|
- remove quotes for font families and make lowercase
|
||||||
|
- rewrite hex colors to/from color names, or to 3 digit hex
|
||||||
|
- rewrite `rgb(`, `rgba(`, `hsl(` and `hsla(` colors to hex or name
|
||||||
|
- replace `normal` and `bold` by numbers for `font-weight` and `font`
|
||||||
|
- replace `none` → `0` for `border`, `background` and `outline`
|
||||||
|
- lowercase all identifiers except classes, IDs and URLs to enhance gzip compression
|
||||||
|
- shorten MS alpha function
|
||||||
|
- rewrite data URIs with base64 or ASCII whichever is shorter
|
||||||
|
- calls minifier for data URI mediatypes, thus you can compress embedded SVG files if you have that minifier attached
|
||||||
|
|
||||||
|
It does purposely not use the following techniques:
|
||||||
|
|
||||||
|
- (partially) merge rulesets
|
||||||
|
- (partially) split rulesets
|
||||||
|
- collapse multiple declarations when main declaration is defined within a ruleset (don't put `font-weight` within an already existing `font`, too complex)
|
||||||
|
- remove overwritten properties in ruleset (this not always overwrites it, for example with `!important`)
|
||||||
|
- rewrite properties into one ruleset if possible (like `margin-top`, `margin-right`, `margin-bottom` and `margin-left` → `margin`)
|
||||||
|
- put nested ID selector at the front (`body > div#elem p` → `#elem p`)
|
||||||
|
- rewrite attribute selectors for IDs and classes (`div[id=a]` → `div#a`)
|
||||||
|
- put space after pseudo-selectors (IE6 is old, move on!)
|
||||||
|
|
||||||
|
It's great that so many other tools make comparison tables: [CSS Minifier Comparison](http://www.codenothing.com/benchmarks/css-compressor-3.0/full.html), [CSS minifiers comparison](http://www.phpied.com/css-minifiers-comparison/) and [CleanCSS tests](http://goalsmashers.github.io/css-minification-benchmark/). From the last link, this CSS minifier is almost without doubt the fastest and has near-perfect minification rates. It falls short with the purposely not implemented and often unsafe techniques, so that's fine.
|
||||||
|
|
||||||
|
## JS
|
||||||
|
|
||||||
|
The JS minifier is pretty basic. It removes comments, whitespace and line breaks whenever it can. It employs all the rules that [JSMin](http://www.crockford.com/javascript/jsmin.html) does too, but has additional improvements. For example the prefix-postfix bug is fixed.
|
||||||
|
|
||||||
|
Minification typically runs at about 45MB/s ~= 160GB/h. Common speeds of PHP and JS implementations are about 100-300kB/s (see [Uglify2](http://lisperator.net/uglifyjs/), [Adventures in PHP web asset minimization](https://www.happyassassin.net/2014/12/29/adventures-in-php-web-asset-minimization/)).
|
||||||
|
|
||||||
|
Library | Original | Minified | Ratio | Time<sup>*</sup>
|
||||||
|
------- | -------- | -------- | ----- | -----------------------
|
||||||
|
[ACE](https://github.com/ajaxorg/ace-builds) | 630kB | **442kB** | 70% | 14ms
|
||||||
|
[jQuery](http://jquery.com/download/) | 242kB | **130kB** | 54% | 5ms
|
||||||
|
[jQuery UI](http://jqueryui.com/download/) | 459kB | **300kB** | 65% | 11ms
|
||||||
|
[Moment](http://momentjs.com/) | 97kB | **51kB** | 52% | 2ms
|
||||||
|
|
||||||
|
<sup>*</sup>The benchmark excludes the time reading from and writing to a file from the measurement.
|
||||||
|
|
||||||
|
TODO:
|
||||||
|
- shorten local variables / function parameters names
|
||||||
|
- precise semicolon and newline omission
|
||||||
|
|
||||||
|
## JSON
|
||||||
|
|
||||||
|
Minification typically runs at about 95MB/s ~= 340GB/h. It shaves off about 15% of filesize for common indented JSON such as generated by [JSON Generator](http://www.json-generator.com/).
|
||||||
|
|
||||||
|
The JSON minifier only removes whitespace, which is the only thing that can be left out.
|
||||||
|
|
||||||
|
## SVG
|
||||||
|
|
||||||
|
Minification typically runs at about 15MB/s ~= 55GB/h. Performance improvement are due.
|
||||||
|
|
||||||
|
The SVG minifier uses these minifications:
|
||||||
|
|
||||||
|
- trim and collapse whitespace between all tags
|
||||||
|
- strip comments, empty `doctype`, XML prelude, `metadata`
|
||||||
|
- strip SVG version
|
||||||
|
- strip CDATA sections wherever possible
|
||||||
|
- collapse tags with no content to a void tag
|
||||||
|
- collapse empty container tags (`g`, `svg`, ...)
|
||||||
|
- minify style tag and attributes with the CSS minifier
|
||||||
|
- minify colors
|
||||||
|
- shorten lengths and numbers and remove default `px` unit
|
||||||
|
- shorten `path` data
|
||||||
|
- convert `rect`, `line`, `polygon`, `polyline` to `path`
|
||||||
|
- use relative or absolute positions in path data whichever is shorter
|
||||||
|
|
||||||
|
TODO:
|
||||||
|
- convert attributes to style attribute whenever shorter
|
||||||
|
- merge path data? (same style and no intersection -- the latter is difficult)
|
||||||
|
- truncate decimals
|
||||||
|
|
||||||
|
## XML
|
||||||
|
|
||||||
|
Minification typically runs at about 70MB/s ~= 250GB/h.
|
||||||
|
|
||||||
|
The XML minifier uses these minifications:
|
||||||
|
|
||||||
|
- strip unnecessary whitespace and otherwise collapse it to one space (or newline if it originally contained a newline)
|
||||||
|
- strip comments
|
||||||
|
- collapse tags with no content to a void tag
|
||||||
|
- strip CDATA sections wherever possible
|
||||||
|
|
||||||
|
Options:
|
||||||
|
|
||||||
|
- `KeepWhitespace` do not remove whitespace between inline tags but still collapse multiple whitespace characters into one
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
Any input stream is being buffered by the minification functions. This is how the underlying buffer package inherently works to ensure high performance. The output stream however is not buffer. It is wise to preallocate a buffer as big as the input to which the output is written, or otherwise use `bufio` to buffer to a streaming writer.
|
||||||
|
|
||||||
|
### New
|
||||||
|
Retrieve a minifier struct which holds a map of mediatype → minifier functions.
|
||||||
|
``` go
|
||||||
|
m := minify.New()
|
||||||
|
```
|
||||||
|
|
||||||
|
The following loads all provided minifiers.
|
||||||
|
``` go
|
||||||
|
m := minify.New()
|
||||||
|
m.AddFunc("text/css", css.Minify)
|
||||||
|
m.AddFunc("text/html", html.Minify)
|
||||||
|
m.AddFunc("text/javascript", js.Minify)
|
||||||
|
m.AddFunc("image/svg+xml", svg.Minify)
|
||||||
|
m.AddFuncRegexp(regexp.MustCompile("[/+]json$"), json.Minify)
|
||||||
|
m.AddFuncRegexp(regexp.MustCompile("[/+]xml$"), xml.Minify)
|
||||||
|
```
|
||||||
|
|
||||||
|
You can set options to several minifiers.
|
||||||
|
``` go
|
||||||
|
m.Add("text/html", &html.Minifier{
|
||||||
|
KeepDefaultAttrVals: true,
|
||||||
|
KeepWhitespace: true,
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### From reader
|
||||||
|
Minify from an `io.Reader` to an `io.Writer` for a specific mediatype.
|
||||||
|
``` go
|
||||||
|
if err := m.Minify(mediatype, w, r); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Minify formats directly from an `io.Reader` to an `io.Writer`. The `params map[string]string` would contain the mediatype parameters, pass `nil` if non-existent.
|
||||||
|
``` go
|
||||||
|
if err := css.Minify(m, w, r, params); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := html.Minify(m, w, r, params); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := js.Minify(m, w, r, params); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Minify(m, w, r, params); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := svg.Minify(m, w, r, params); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := xml.Minify(m, w, r, params); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### From bytes
|
||||||
|
Minify from and to a `[]byte` for a specific mediatype.
|
||||||
|
``` go
|
||||||
|
b, err = m.Bytes(mediatype, b)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### From string
|
||||||
|
Minify from and to a `string` for a specific mediatype.
|
||||||
|
``` go
|
||||||
|
s, err = m.String(mediatype, s)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### From reader
|
||||||
|
Get a minifying reader for a specific mediatype.
|
||||||
|
``` go
|
||||||
|
mr := m.Reader(mediatype, r)
|
||||||
|
if _, err := mr.Read(b); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### From writer
|
||||||
|
Get a minifying writer for a specific mediatype. Must be explicitly closed because it uses an `io.Pipe` underneath.
|
||||||
|
``` go
|
||||||
|
mw := m.Writer(mediatype, w)
|
||||||
|
mw.Write([]byte("input"))
|
||||||
|
if err := mw.Close(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom minifier
|
||||||
|
Add a minifier for a specific mimetype.
|
||||||
|
``` go
|
||||||
|
type CustomMinifier struct {
|
||||||
|
KeepLineBreaks bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CustomMinifier) Minify(m *minify.M, w io.Writer, r io.Reader, params map[string]string) error {
|
||||||
|
// ...
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
m.Add(mimetype, &CustomMinifier{KeepLineBreaks: true})
|
||||||
|
// or
|
||||||
|
m.AddRegexp(regexp.MustCompile("/x-custom$"), &CustomMinifier{KeepLineBreaks: true})
|
||||||
|
```
|
||||||
|
|
||||||
|
Add a minify function for a specific mimetype.
|
||||||
|
``` go
|
||||||
|
m.AddFunc(mimetype, func(m *minify.M, w io.Writer, r io.Reader, params map[string]string) error {
|
||||||
|
// ...
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
m.AddFuncRegexp(regexp.MustCompile("/x-custom$"), func(m *minify.M, w io.Writer, r io.Reader, params map[string]string) error {
|
||||||
|
// ...
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
Add a command `cmd` with arguments `args` for a specific mimetype.
|
||||||
|
``` go
|
||||||
|
m.AddCmd(mimetype, exec.Command(cmd, args...))
|
||||||
|
m.AddCmdRegexp(regexp.MustCompile("/x-custom$"), exec.Command(cmd, args...))
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mediatypes
|
||||||
|
Using the `params map[string]string` argument one can pass parameters to the minifier such as seen in mediatypes (`type/subtype; key1=val2; key2=val2`). Examples are the encoding or charset of the data. Calling `Minify` will split the mimetype and parameters for the minifiers for you, but `MinifyMimetype` can be used if you already have them split up.
|
||||||
|
|
||||||
|
Minifiers can also be added using a regular expression. For example a minifier with `image/.*` will match any image mime.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
### Common minifiers
|
||||||
|
Basic example that minifies from stdin to stdout and loads the default HTML, CSS and JS minifiers. Optionally, one can enable `java -jar build/compiler.jar` to run for JS (for example the [ClosureCompiler](https://code.google.com/p/closure-compiler/)). Note that reading the file into a buffer first and writing to a pre-allocated buffer would be faster (but would disable streaming).
|
||||||
|
``` go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
|
||||||
|
"github.com/tdewolff/minify"
|
||||||
|
"github.com/tdewolff/minify/css"
|
||||||
|
"github.com/tdewolff/minify/html"
|
||||||
|
"github.com/tdewolff/minify/js"
|
||||||
|
"github.com/tdewolff/minify/json"
|
||||||
|
"github.com/tdewolff/minify/svg"
|
||||||
|
"github.com/tdewolff/minify/xml"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
m := minify.New()
|
||||||
|
m.AddFunc("text/css", css.Minify)
|
||||||
|
m.AddFunc("text/html", html.Minify)
|
||||||
|
m.AddFunc("text/javascript", js.Minify)
|
||||||
|
m.AddFunc("image/svg+xml", svg.Minify)
|
||||||
|
m.AddFuncRegexp(regexp.MustCompile("[/+]json$"), json.Minify)
|
||||||
|
m.AddFuncRegexp(regexp.MustCompile("[/+]xml$"), xml.Minify)
|
||||||
|
|
||||||
|
// Or use the following for better minification of JS but lower speed:
|
||||||
|
// m.AddCmd("text/javascript", exec.Command("java", "-jar", "build/compiler.jar"))
|
||||||
|
|
||||||
|
if err := m.Minify("text/html", os.Stdout, os.Stdin); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom minifier
|
||||||
|
Custom minifier showing an example that implements the minifier function interface. Within a custom minifier, it is possible to call any minifier function (through `m minify.Minifier`) recursively when dealing with embedded resources.
|
||||||
|
``` go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/tdewolff/minify"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
m := minify.New()
|
||||||
|
m.AddFunc("text/plain", func(m *minify.M, w io.Writer, r io.Reader, _ map[string]string) error {
|
||||||
|
// remove newlines and spaces
|
||||||
|
rb := bufio.NewReader(r)
|
||||||
|
for {
|
||||||
|
line, err := rb.ReadString('\n')
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, errws := io.WriteString(w, strings.Replace(line, " ", "", -1)); errws != nil {
|
||||||
|
return errws
|
||||||
|
}
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
in := "Because my coffee was too cold, I heated it in the microwave."
|
||||||
|
out, err := m.String("text/plain", in)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println(out)
|
||||||
|
// Output: Becausemycoffeewastoocold,Iheateditinthemicrowave.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### ResponseWriter
|
||||||
|
ResponseWriter example which returns a ResponseWriter that minifies the content and then writes to the original ResponseWriter. Any write after applying this filter will be minified.
|
||||||
|
``` go
|
||||||
|
type MinifyResponseWriter struct {
|
||||||
|
http.ResponseWriter
|
||||||
|
io.WriteCloser
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MinifyResponseWriter) Write(b []byte) (int, error) {
|
||||||
|
return m.WriteCloser.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MinifyResponseWriter must be closed explicitly by calling site.
|
||||||
|
func MinifyFilter(mediatype string, res http.ResponseWriter) MinifyResponseWriter {
|
||||||
|
m := minify.New()
|
||||||
|
// add minfiers
|
||||||
|
|
||||||
|
mw := m.Writer(mediatype, res)
|
||||||
|
return MinifyResponseWriter{res, mw}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
``` go
|
||||||
|
// Usage
|
||||||
|
func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
w = MinifyFilter("text/html", w)
|
||||||
|
io.WriteString(w, "<p class="message"> This HTTP response will be minified. </p>")
|
||||||
|
// Output: <p class=message>This HTTP response will be minified.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Templates
|
||||||
|
|
||||||
|
Here's an example of a replacement for `template.ParseFiles` from `template/html`, which automatically minifies each template before parsing it.
|
||||||
|
|
||||||
|
Be aware that minifying templates will work in most cases but not all. Because the HTML minifier only works for valid HTML5, your template must be valid HTML5 of itself. Template tags are parsed as regular text by the minifier.
|
||||||
|
|
||||||
|
``` go
|
||||||
|
func compileTemplates(filenames ...string) (*template.Template, error) {
|
||||||
|
m := minify.New()
|
||||||
|
m.AddFunc("text/html", html.Minify)
|
||||||
|
|
||||||
|
var tmpl *template.Template
|
||||||
|
for _, filename := range filenames {
|
||||||
|
name := filepath.Base(filename)
|
||||||
|
if tmpl == nil {
|
||||||
|
tmpl = template.New(name)
|
||||||
|
} else {
|
||||||
|
tmpl = tmpl.New(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := ioutil.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
mb, err := m.Bytes("text/html", b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tmpl.Parse(string(mb))
|
||||||
|
}
|
||||||
|
return tmpl, nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
|
||||||
|
``` go
|
||||||
|
templates := template.MustCompile(compileTemplates("view.html", "home.html"))
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
Released under the [MIT license](LICENSE.md).
|
||||||
|
|
||||||
|
[1]: http://golang.org/ "Go Language"
|
338
vendor/github.com/tdewolff/minify/common.go
generated
vendored
Normal file
338
vendor/github.com/tdewolff/minify/common.go
generated
vendored
Normal file
|
@ -0,0 +1,338 @@
|
||||||
|
package minify
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/tdewolff/parse"
|
||||||
|
"github.com/tdewolff/strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Epsilon is the closest number to zero that is not considered to be zero.
|
||||||
|
var Epsilon = 0.00001
|
||||||
|
|
||||||
|
// ContentType minifies a given mediatype by removing all whitespace.
|
||||||
|
func ContentType(b []byte) []byte {
|
||||||
|
j := 0
|
||||||
|
start := 0
|
||||||
|
inString := false
|
||||||
|
for i, c := range b {
|
||||||
|
if !inString && parse.IsWhitespace(c) {
|
||||||
|
if start != 0 {
|
||||||
|
j += copy(b[j:], b[start:i])
|
||||||
|
} else {
|
||||||
|
j += i
|
||||||
|
}
|
||||||
|
start = i + 1
|
||||||
|
} else if c == '"' {
|
||||||
|
inString = !inString
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if start != 0 {
|
||||||
|
j += copy(b[j:], b[start:])
|
||||||
|
return parse.ToLower(b[:j])
|
||||||
|
}
|
||||||
|
return parse.ToLower(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DataURI minifies a data URI and calls a minifier by the specified mediatype. Specifications: https://www.ietf.org/rfc/rfc2397.txt.
|
||||||
|
func DataURI(m *M, dataURI []byte) []byte {
|
||||||
|
if mediatype, data, err := parse.DataURI(dataURI); err == nil {
|
||||||
|
dataURI, _ = m.Bytes(string(mediatype), data)
|
||||||
|
base64Len := len(";base64") + base64.StdEncoding.EncodedLen(len(dataURI))
|
||||||
|
asciiLen := len(dataURI)
|
||||||
|
for _, c := range dataURI {
|
||||||
|
if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '-' || c == '_' || c == '.' || c == '~' || c == ' ' {
|
||||||
|
asciiLen++
|
||||||
|
} else {
|
||||||
|
asciiLen += 2
|
||||||
|
}
|
||||||
|
if asciiLen > base64Len {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if asciiLen > base64Len {
|
||||||
|
encoded := make([]byte, base64Len-len(";base64"))
|
||||||
|
base64.StdEncoding.Encode(encoded, dataURI)
|
||||||
|
dataURI = encoded
|
||||||
|
mediatype = append(mediatype, []byte(";base64")...)
|
||||||
|
} else {
|
||||||
|
dataURI = []byte(url.QueryEscape(string(dataURI)))
|
||||||
|
dataURI = bytes.Replace(dataURI, []byte("\""), []byte("\\\""), -1)
|
||||||
|
}
|
||||||
|
if len("text/plain") <= len(mediatype) && parse.EqualFold(mediatype[:len("text/plain")], []byte("text/plain")) {
|
||||||
|
mediatype = mediatype[len("text/plain"):]
|
||||||
|
}
|
||||||
|
for i := 0; i+len(";charset=us-ascii") <= len(mediatype); i++ {
|
||||||
|
// must start with semicolon and be followed by end of mediatype or semicolon
|
||||||
|
if mediatype[i] == ';' && parse.EqualFold(mediatype[i+1:i+len(";charset=us-ascii")], []byte("charset=us-ascii")) && (i+len(";charset=us-ascii") >= len(mediatype) || mediatype[i+len(";charset=us-ascii")] == ';') {
|
||||||
|
mediatype = append(mediatype[:i], mediatype[i+len(";charset=us-ascii"):]...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dataURI = append(append(append([]byte("data:"), mediatype...), ','), dataURI...)
|
||||||
|
}
|
||||||
|
return dataURI
|
||||||
|
}
|
||||||
|
|
||||||
|
const MaxInt = int(^uint(0) >> 1)
|
||||||
|
const MinInt = -MaxInt - 1
|
||||||
|
|
||||||
|
// Number minifies a given byte slice containing a number (see parse.Number) and removes superfluous characters.
|
||||||
|
func Number(num []byte, prec int) []byte {
|
||||||
|
// omit first + and register mantissa start and end, whether it's negative and the exponent
|
||||||
|
neg := false
|
||||||
|
start := 0
|
||||||
|
dot := -1
|
||||||
|
end := len(num)
|
||||||
|
origExp := 0
|
||||||
|
if 0 < end && (num[0] == '+' || num[0] == '-') {
|
||||||
|
if num[0] == '-' {
|
||||||
|
neg = true
|
||||||
|
}
|
||||||
|
start++
|
||||||
|
}
|
||||||
|
for i, c := range num[start:] {
|
||||||
|
if c == '.' {
|
||||||
|
dot = start + i
|
||||||
|
} else if c == 'e' || c == 'E' {
|
||||||
|
end = start + i
|
||||||
|
i += start + 1
|
||||||
|
if i < len(num) && num[i] == '+' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if tmpOrigExp, n := strconv.ParseInt(num[i:]); n > 0 && tmpOrigExp >= int64(MinInt) && tmpOrigExp <= int64(MaxInt) {
|
||||||
|
// range checks for when int is 32 bit
|
||||||
|
origExp = int(tmpOrigExp)
|
||||||
|
} else {
|
||||||
|
return num
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if dot == -1 {
|
||||||
|
dot = end
|
||||||
|
}
|
||||||
|
|
||||||
|
// trim leading zeros but leave at least one digit
|
||||||
|
for start < end-1 && num[start] == '0' {
|
||||||
|
start++
|
||||||
|
}
|
||||||
|
// trim trailing zeros
|
||||||
|
i := end - 1
|
||||||
|
for ; i > dot; i-- {
|
||||||
|
if num[i] != '0' {
|
||||||
|
end = i + 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if i == dot {
|
||||||
|
end = dot
|
||||||
|
if start == end {
|
||||||
|
num[start] = '0'
|
||||||
|
return num[start : start+1]
|
||||||
|
}
|
||||||
|
} else if start == end-1 && num[start] == '0' {
|
||||||
|
return num[start:end]
|
||||||
|
}
|
||||||
|
|
||||||
|
// n is the number of significant digits
|
||||||
|
// normExp would be the exponent if it were normalised (0.1 <= f < 1)
|
||||||
|
n := 0
|
||||||
|
normExp := 0
|
||||||
|
if dot == start {
|
||||||
|
for i = dot + 1; i < end; i++ {
|
||||||
|
if num[i] != '0' {
|
||||||
|
n = end - i
|
||||||
|
normExp = dot - i + 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if dot == end {
|
||||||
|
normExp = end - start
|
||||||
|
for i = end - 1; i >= start; i-- {
|
||||||
|
if num[i] != '0' {
|
||||||
|
n = i + 1 - start
|
||||||
|
end = i + 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
n = end - start - 1
|
||||||
|
normExp = dot - start
|
||||||
|
}
|
||||||
|
|
||||||
|
if origExp < 0 && (normExp < MinInt-origExp || normExp-n < MinInt-origExp) || origExp > 0 && (normExp > MaxInt-origExp || normExp-n > MaxInt-origExp) {
|
||||||
|
return num
|
||||||
|
}
|
||||||
|
normExp += origExp
|
||||||
|
|
||||||
|
// intExp would be the exponent if it were an integer
|
||||||
|
intExp := normExp - n
|
||||||
|
lenIntExp := 1
|
||||||
|
if intExp <= -10 || intExp >= 10 {
|
||||||
|
lenIntExp = strconv.LenInt(int64(intExp))
|
||||||
|
}
|
||||||
|
|
||||||
|
// there are three cases to consider when printing the number
|
||||||
|
// case 1: without decimals and with an exponent (large numbers)
|
||||||
|
// case 2: with decimals and without an exponent (around zero)
|
||||||
|
// case 3: without decimals and with a negative exponent (small numbers)
|
||||||
|
if normExp >= n {
|
||||||
|
// case 1
|
||||||
|
if dot < end {
|
||||||
|
if dot == start {
|
||||||
|
start = end - n
|
||||||
|
} else {
|
||||||
|
// TODO: copy the other part if shorter?
|
||||||
|
//fmt.Println("COPY1", end-dot-1)
|
||||||
|
copy(num[dot:], num[dot+1:end])
|
||||||
|
end--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if normExp >= n+3 {
|
||||||
|
num[end] = 'e'
|
||||||
|
end++
|
||||||
|
for i := end + lenIntExp - 1; i >= end; i-- {
|
||||||
|
num[i] = byte(intExp%10) + '0'
|
||||||
|
intExp /= 10
|
||||||
|
}
|
||||||
|
end += lenIntExp
|
||||||
|
} else if normExp == n+2 {
|
||||||
|
num[end] = '0'
|
||||||
|
num[end+1] = '0'
|
||||||
|
end += 2
|
||||||
|
} else if normExp == n+1 {
|
||||||
|
num[end] = '0'
|
||||||
|
end++
|
||||||
|
}
|
||||||
|
} else if normExp >= -lenIntExp-1 {
|
||||||
|
// case 2
|
||||||
|
zeroes := -normExp
|
||||||
|
newDot := 0
|
||||||
|
if zeroes > 0 {
|
||||||
|
// dot placed at the front and add zeroes
|
||||||
|
newDot = end - n - zeroes - 1
|
||||||
|
if newDot != dot {
|
||||||
|
d := start - newDot
|
||||||
|
if d > 0 {
|
||||||
|
if dot < end {
|
||||||
|
// copy original digits behind the dot backwards
|
||||||
|
//fmt.Println("COPY2", end-dot-1)
|
||||||
|
copy(num[dot+1+d:], num[dot+1:end])
|
||||||
|
if dot > start {
|
||||||
|
// copy original digits before the dot backwards
|
||||||
|
//fmt.Println("COPY3a", dot-start)
|
||||||
|
copy(num[start+d+1:], num[start:dot])
|
||||||
|
}
|
||||||
|
} else if dot > start {
|
||||||
|
// copy original digits before the dot backwards
|
||||||
|
//fmt.Println("COPY3b", dot-start)
|
||||||
|
copy(num[start+d:], num[start:dot])
|
||||||
|
}
|
||||||
|
newDot = start
|
||||||
|
end += d
|
||||||
|
} else {
|
||||||
|
start += -d
|
||||||
|
}
|
||||||
|
num[newDot] = '.'
|
||||||
|
for i := 0; i < zeroes; i++ {
|
||||||
|
num[newDot+1+i] = '0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// placed in the middle
|
||||||
|
if dot == start {
|
||||||
|
// TODO: try if placing at the end reduces copying
|
||||||
|
// when there are zeroes after the dot
|
||||||
|
dot = end - n - 1
|
||||||
|
start = dot
|
||||||
|
} else if dot >= end {
|
||||||
|
// TODO: try if placing at the start reduces copying
|
||||||
|
// when input has no dot in it
|
||||||
|
dot = end
|
||||||
|
end++
|
||||||
|
}
|
||||||
|
newDot = start + normExp
|
||||||
|
if newDot > dot {
|
||||||
|
// copy digits forwards
|
||||||
|
//fmt.Println("COPY4", newDot-dot)
|
||||||
|
copy(num[dot:], num[dot+1:newDot+1])
|
||||||
|
} else if newDot < dot {
|
||||||
|
// copy digits backwards
|
||||||
|
//fmt.Println("COPY5", dot-newDot)
|
||||||
|
copy(num[newDot+1:], num[newDot:dot])
|
||||||
|
}
|
||||||
|
num[newDot] = '.'
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply precision
|
||||||
|
dot = newDot
|
||||||
|
if prec > -1 && dot+1+prec < end {
|
||||||
|
end = dot + 1 + prec
|
||||||
|
inc := num[end] >= '5'
|
||||||
|
if inc || num[end-1] == '0' {
|
||||||
|
for i := end - 1; i > start; i-- {
|
||||||
|
if i == dot {
|
||||||
|
end--
|
||||||
|
} else if inc {
|
||||||
|
if num[i] == '9' {
|
||||||
|
end--
|
||||||
|
} else {
|
||||||
|
num[i]++
|
||||||
|
inc = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else if i > dot && num[i] == '0' {
|
||||||
|
end--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if dot == start && end == start+1 {
|
||||||
|
if inc {
|
||||||
|
num[start] = '1'
|
||||||
|
} else {
|
||||||
|
num[start] = '0'
|
||||||
|
}
|
||||||
|
} else if inc {
|
||||||
|
if num[start] == '9' {
|
||||||
|
num[start] = '0'
|
||||||
|
copy(num[start+1:], num[start:end])
|
||||||
|
end++
|
||||||
|
num[start] = '1'
|
||||||
|
} else {
|
||||||
|
num[start]++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// case 3
|
||||||
|
if dot < end {
|
||||||
|
if dot == start {
|
||||||
|
//fmt.Println("COPY6", n)
|
||||||
|
copy(num[start:], num[end-n:end])
|
||||||
|
end = start + n
|
||||||
|
} else {
|
||||||
|
//fmt.Println("COPY7", end-dot-1)
|
||||||
|
copy(num[dot:], num[dot+1:end])
|
||||||
|
end--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
num[end] = 'e'
|
||||||
|
num[end+1] = '-'
|
||||||
|
end += 2
|
||||||
|
intExp = -intExp
|
||||||
|
for i := end + lenIntExp - 1; i >= end; i-- {
|
||||||
|
num[i] = byte(intExp%10) + '0'
|
||||||
|
intExp /= 10
|
||||||
|
}
|
||||||
|
end += lenIntExp
|
||||||
|
}
|
||||||
|
|
||||||
|
if neg {
|
||||||
|
start--
|
||||||
|
num[start] = '-'
|
||||||
|
}
|
||||||
|
return num[start:end]
|
||||||
|
}
|
517
vendor/github.com/tdewolff/minify/css/css.go
generated
vendored
Normal file
517
vendor/github.com/tdewolff/minify/css/css.go
generated
vendored
Normal file
|
@ -0,0 +1,517 @@
|
||||||
|
// Package css minifies CSS3 following the specifications at http://www.w3.org/TR/css-syntax-3/.
|
||||||
|
package css
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/tdewolff/minify"
|
||||||
|
"github.com/tdewolff/parse"
|
||||||
|
"github.com/tdewolff/parse/css"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
spaceBytes = []byte(" ")
|
||||||
|
colonBytes = []byte(":")
|
||||||
|
semicolonBytes = []byte(";")
|
||||||
|
leftBracketBytes = []byte("{")
|
||||||
|
rightBracketBytes = []byte("}")
|
||||||
|
zeroBytes = []byte("0")
|
||||||
|
msfilterBytes = []byte("-ms-filter")
|
||||||
|
backgroundNoneBytes = []byte("0 0")
|
||||||
|
)
|
||||||
|
|
||||||
|
type cssMinifier struct {
|
||||||
|
m *minify.M
|
||||||
|
w io.Writer
|
||||||
|
p *css.Parser
|
||||||
|
o *Minifier
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Minifier is a CSS minifier.
|
||||||
|
type Minifier struct {
|
||||||
|
Decimals int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minify minifies CSS data, it reads from r and writes to w.
|
||||||
|
func Minify(m *minify.M, w io.Writer, r io.Reader, params map[string]string) error {
|
||||||
|
return (&Minifier{Decimals: -1}).Minify(m, w, r, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minify minifies CSS data, it reads from r and writes to w.
|
||||||
|
func (o *Minifier) Minify(m *minify.M, w io.Writer, r io.Reader, params map[string]string) error {
|
||||||
|
isInline := params != nil && params["inline"] == "1"
|
||||||
|
c := &cssMinifier{
|
||||||
|
m: m,
|
||||||
|
w: w,
|
||||||
|
p: css.NewParser(r, isInline),
|
||||||
|
o: o,
|
||||||
|
}
|
||||||
|
if err := c.minifyGrammar(); err != nil && err != io.EOF {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cssMinifier) minifyGrammar() error {
|
||||||
|
semicolonQueued := false
|
||||||
|
for {
|
||||||
|
gt, _, data := c.p.Next()
|
||||||
|
if gt == css.ErrorGrammar {
|
||||||
|
return c.p.Err()
|
||||||
|
} else if gt == css.EndAtRuleGrammar || gt == css.EndRulesetGrammar {
|
||||||
|
if _, err := c.w.Write(rightBracketBytes); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
semicolonQueued = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if semicolonQueued {
|
||||||
|
if _, err := c.w.Write(semicolonBytes); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
semicolonQueued = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if gt == css.AtRuleGrammar {
|
||||||
|
if _, err := c.w.Write(data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, val := range c.p.Values() {
|
||||||
|
if _, err := c.w.Write(val.Data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
semicolonQueued = true
|
||||||
|
} else if gt == css.BeginAtRuleGrammar {
|
||||||
|
if _, err := c.w.Write(data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, val := range c.p.Values() {
|
||||||
|
if _, err := c.w.Write(val.Data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, err := c.w.Write(leftBracketBytes); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else if gt == css.BeginRulesetGrammar {
|
||||||
|
if err := c.minifySelectors(data, c.p.Values()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := c.w.Write(leftBracketBytes); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else if gt == css.DeclarationGrammar {
|
||||||
|
if _, err := c.w.Write(data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := c.w.Write(colonBytes); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := c.minifyDeclaration(data, c.p.Values()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
semicolonQueued = true
|
||||||
|
} else if gt == css.CommentGrammar {
|
||||||
|
if len(data) > 5 && data[1] == '*' && data[2] == '!' {
|
||||||
|
if _, err := c.w.Write(data[:3]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
comment := parse.TrimWhitespace(parse.ReplaceMultipleWhitespace(data[3 : len(data)-2]))
|
||||||
|
if _, err := c.w.Write(comment); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := c.w.Write(data[len(data)-2:]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if _, err := c.w.Write(data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cssMinifier) minifySelectors(property []byte, values []css.Token) error {
|
||||||
|
inAttr := false
|
||||||
|
isClass := false
|
||||||
|
for _, val := range c.p.Values() {
|
||||||
|
if !inAttr {
|
||||||
|
if val.TokenType == css.IdentToken {
|
||||||
|
if !isClass {
|
||||||
|
parse.ToLower(val.Data)
|
||||||
|
}
|
||||||
|
isClass = false
|
||||||
|
} else if val.TokenType == css.DelimToken && val.Data[0] == '.' {
|
||||||
|
isClass = true
|
||||||
|
} else if val.TokenType == css.LeftBracketToken {
|
||||||
|
inAttr = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if val.TokenType == css.StringToken && len(val.Data) > 2 {
|
||||||
|
s := val.Data[1 : len(val.Data)-1]
|
||||||
|
if css.IsIdent([]byte(s)) {
|
||||||
|
if _, err := c.w.Write(s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else if val.TokenType == css.RightBracketToken {
|
||||||
|
inAttr = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, err := c.w.Write(val.Data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cssMinifier) minifyDeclaration(property []byte, values []css.Token) error {
|
||||||
|
if len(values) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
prop := css.ToHash(property)
|
||||||
|
inProgid := false
|
||||||
|
for i, value := range values {
|
||||||
|
if inProgid {
|
||||||
|
if value.TokenType == css.FunctionToken {
|
||||||
|
inProgid = false
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
} else if value.TokenType == css.IdentToken && css.ToHash(value.Data) == css.Progid {
|
||||||
|
inProgid = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
value.TokenType, value.Data = c.shortenToken(prop, value.TokenType, value.Data)
|
||||||
|
if prop == css.Font || prop == css.Font_Family || prop == css.Font_Weight {
|
||||||
|
if value.TokenType == css.IdentToken && (prop == css.Font || prop == css.Font_Weight) {
|
||||||
|
val := css.ToHash(value.Data)
|
||||||
|
if val == css.Normal && prop == css.Font_Weight {
|
||||||
|
// normal could also be specified for font-variant, not just font-weight
|
||||||
|
value.TokenType = css.NumberToken
|
||||||
|
value.Data = []byte("400")
|
||||||
|
} else if val == css.Bold {
|
||||||
|
value.TokenType = css.NumberToken
|
||||||
|
value.Data = []byte("700")
|
||||||
|
}
|
||||||
|
} else if value.TokenType == css.StringToken && (prop == css.Font || prop == css.Font_Family) && len(value.Data) > 2 {
|
||||||
|
unquote := true
|
||||||
|
parse.ToLower(value.Data)
|
||||||
|
s := value.Data[1 : len(value.Data)-1]
|
||||||
|
if len(s) > 0 {
|
||||||
|
for _, split := range bytes.Split(s, spaceBytes) {
|
||||||
|
val := css.ToHash(split)
|
||||||
|
// if len is zero, it contains two consecutive spaces
|
||||||
|
if val == css.Inherit || val == css.Serif || val == css.Sans_Serif || val == css.Monospace || val == css.Fantasy || val == css.Cursive || val == css.Initial || val == css.Default ||
|
||||||
|
len(split) == 0 || !css.IsIdent(split) {
|
||||||
|
unquote = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if unquote {
|
||||||
|
value.Data = s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if prop == css.Outline || prop == css.Border || prop == css.Border_Bottom || prop == css.Border_Left || prop == css.Border_Right || prop == css.Border_Top {
|
||||||
|
if css.ToHash(value.Data) == css.None {
|
||||||
|
value.TokenType = css.NumberToken
|
||||||
|
value.Data = zeroBytes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
values[i].TokenType, values[i].Data = value.TokenType, value.Data
|
||||||
|
}
|
||||||
|
|
||||||
|
important := false
|
||||||
|
if len(values) > 2 && values[len(values)-2].TokenType == css.DelimToken && values[len(values)-2].Data[0] == '!' && css.ToHash(values[len(values)-1].Data) == css.Important {
|
||||||
|
values = values[:len(values)-2]
|
||||||
|
important = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(values) == 1 {
|
||||||
|
if prop == css.Background && css.ToHash(values[0].Data) == css.None {
|
||||||
|
values[0].Data = backgroundNoneBytes
|
||||||
|
} else if bytes.Equal(property, msfilterBytes) {
|
||||||
|
alpha := []byte("progid:DXImageTransform.Microsoft.Alpha(Opacity=")
|
||||||
|
if values[0].TokenType == css.StringToken && bytes.HasPrefix(values[0].Data[1:len(values[0].Data)-1], alpha) {
|
||||||
|
values[0].Data = append(append([]byte{values[0].Data[0]}, []byte("alpha(opacity=")...), values[0].Data[1+len(alpha):]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if prop == css.Margin || prop == css.Padding || prop == css.Border_Width {
|
||||||
|
if (values[0].TokenType == css.NumberToken || values[0].TokenType == css.DimensionToken || values[0].TokenType == css.PercentageToken) && (len(values)+1)%2 == 0 {
|
||||||
|
valid := true
|
||||||
|
for i := 1; i < len(values); i += 2 {
|
||||||
|
if values[i].TokenType != css.WhitespaceToken || values[i+1].TokenType != css.NumberToken && values[i+1].TokenType != css.DimensionToken && values[i+1].TokenType != css.PercentageToken {
|
||||||
|
valid = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if valid {
|
||||||
|
n := (len(values) + 1) / 2
|
||||||
|
if n == 2 {
|
||||||
|
if bytes.Equal(values[0].Data, values[2].Data) {
|
||||||
|
values = values[:1]
|
||||||
|
}
|
||||||
|
} else if n == 3 {
|
||||||
|
if bytes.Equal(values[0].Data, values[2].Data) && bytes.Equal(values[0].Data, values[4].Data) {
|
||||||
|
values = values[:1]
|
||||||
|
} else if bytes.Equal(values[0].Data, values[4].Data) {
|
||||||
|
values = values[:3]
|
||||||
|
}
|
||||||
|
} else if n == 4 {
|
||||||
|
if bytes.Equal(values[0].Data, values[2].Data) && bytes.Equal(values[0].Data, values[4].Data) && bytes.Equal(values[0].Data, values[6].Data) {
|
||||||
|
values = values[:1]
|
||||||
|
} else if bytes.Equal(values[0].Data, values[4].Data) && bytes.Equal(values[2].Data, values[6].Data) {
|
||||||
|
values = values[:3]
|
||||||
|
} else if bytes.Equal(values[2].Data, values[6].Data) {
|
||||||
|
values = values[:5]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if prop == css.Filter && len(values) == 11 {
|
||||||
|
if bytes.Equal(values[0].Data, []byte("progid")) &&
|
||||||
|
values[1].TokenType == css.ColonToken &&
|
||||||
|
bytes.Equal(values[2].Data, []byte("DXImageTransform")) &&
|
||||||
|
values[3].Data[0] == '.' &&
|
||||||
|
bytes.Equal(values[4].Data, []byte("Microsoft")) &&
|
||||||
|
values[5].Data[0] == '.' &&
|
||||||
|
bytes.Equal(values[6].Data, []byte("Alpha(")) &&
|
||||||
|
bytes.Equal(parse.ToLower(values[7].Data), []byte("opacity")) &&
|
||||||
|
values[8].Data[0] == '=' &&
|
||||||
|
values[10].Data[0] == ')' {
|
||||||
|
values = values[6:]
|
||||||
|
values[0].Data = []byte("alpha(")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(values); i++ {
|
||||||
|
if values[i].TokenType == css.FunctionToken {
|
||||||
|
n, err := c.minifyFunction(values[i:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i += n - 1
|
||||||
|
} else if _, err := c.w.Write(values[i].Data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if important {
|
||||||
|
if _, err := c.w.Write([]byte("!important")); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cssMinifier) minifyFunction(values []css.Token) (int, error) {
|
||||||
|
n := 1
|
||||||
|
simple := true
|
||||||
|
for i, value := range values[1:] {
|
||||||
|
if value.TokenType == css.RightParenthesisToken {
|
||||||
|
n++
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if i%2 == 0 && (value.TokenType != css.NumberToken && value.TokenType != css.PercentageToken) || (i%2 == 1 && value.TokenType != css.CommaToken) {
|
||||||
|
simple = false
|
||||||
|
}
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
values = values[:n]
|
||||||
|
if simple && (n-1)%2 == 0 {
|
||||||
|
fun := css.ToHash(values[0].Data[:len(values[0].Data)-1])
|
||||||
|
nArgs := (n - 1) / 2
|
||||||
|
if (fun == css.Rgba || fun == css.Hsla) && nArgs == 4 {
|
||||||
|
d, _ := strconv.ParseFloat(string(values[7].Data), 32) // can never fail because if simple == true than this is a NumberToken or PercentageToken
|
||||||
|
if d-1.0 > -minify.Epsilon {
|
||||||
|
if fun == css.Rgba {
|
||||||
|
values[0].Data = []byte("rgb(")
|
||||||
|
fun = css.Rgb
|
||||||
|
} else {
|
||||||
|
values[0].Data = []byte("hsl(")
|
||||||
|
fun = css.Hsl
|
||||||
|
}
|
||||||
|
values = values[:len(values)-2]
|
||||||
|
values[len(values)-1].Data = []byte(")")
|
||||||
|
nArgs = 3
|
||||||
|
} else if d < minify.Epsilon {
|
||||||
|
values[0].Data = []byte("transparent")
|
||||||
|
values = values[:1]
|
||||||
|
fun = 0
|
||||||
|
nArgs = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if fun == css.Rgb && nArgs == 3 {
|
||||||
|
var err [3]error
|
||||||
|
rgb := [3]byte{}
|
||||||
|
for j := 0; j < 3; j++ {
|
||||||
|
val := values[j*2+1]
|
||||||
|
if val.TokenType == css.NumberToken {
|
||||||
|
var d int64
|
||||||
|
d, err[j] = strconv.ParseInt(string(val.Data), 10, 32)
|
||||||
|
if d < 0 {
|
||||||
|
d = 0
|
||||||
|
} else if d > 255 {
|
||||||
|
d = 255
|
||||||
|
}
|
||||||
|
rgb[j] = byte(d)
|
||||||
|
} else if val.TokenType == css.PercentageToken {
|
||||||
|
var d float64
|
||||||
|
d, err[j] = strconv.ParseFloat(string(val.Data[:len(val.Data)-1]), 32)
|
||||||
|
if d < 0.0 {
|
||||||
|
d = 0.0
|
||||||
|
} else if d > 100.0 {
|
||||||
|
d = 100.0
|
||||||
|
}
|
||||||
|
rgb[j] = byte((d / 100.0 * 255.0) + 0.5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err[0] == nil && err[1] == nil && err[2] == nil {
|
||||||
|
val := make([]byte, 7)
|
||||||
|
val[0] = '#'
|
||||||
|
hex.Encode(val[1:], rgb[:])
|
||||||
|
parse.ToLower(val)
|
||||||
|
if s, ok := ShortenColorHex[string(val)]; ok {
|
||||||
|
if _, err := c.w.Write(s); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if len(val) == 7 && val[1] == val[2] && val[3] == val[4] && val[5] == val[6] {
|
||||||
|
val[2] = val[3]
|
||||||
|
val[3] = val[5]
|
||||||
|
val = val[:4]
|
||||||
|
}
|
||||||
|
if _, err := c.w.Write(val); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
} else if fun == css.Hsl && nArgs == 3 {
|
||||||
|
if values[1].TokenType == css.NumberToken && values[3].TokenType == css.PercentageToken && values[5].TokenType == css.PercentageToken {
|
||||||
|
h, err1 := strconv.ParseFloat(string(values[1].Data), 32)
|
||||||
|
s, err2 := strconv.ParseFloat(string(values[3].Data[:len(values[3].Data)-1]), 32)
|
||||||
|
l, err3 := strconv.ParseFloat(string(values[5].Data[:len(values[5].Data)-1]), 32)
|
||||||
|
if err1 == nil && err2 == nil && err3 == nil {
|
||||||
|
r, g, b := css.HSL2RGB(h/360.0, s/100.0, l/100.0)
|
||||||
|
rgb := []byte{byte((r * 255.0) + 0.5), byte((g * 255.0) + 0.5), byte((b * 255.0) + 0.5)}
|
||||||
|
val := make([]byte, 7)
|
||||||
|
val[0] = '#'
|
||||||
|
hex.Encode(val[1:], rgb[:])
|
||||||
|
parse.ToLower(val)
|
||||||
|
if s, ok := ShortenColorHex[string(val)]; ok {
|
||||||
|
if _, err := c.w.Write(s); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if len(val) == 7 && val[1] == val[2] && val[3] == val[4] && val[5] == val[6] {
|
||||||
|
val[2] = val[3]
|
||||||
|
val[3] = val[5]
|
||||||
|
val = val[:4]
|
||||||
|
}
|
||||||
|
if _, err := c.w.Write(val); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, value := range values {
|
||||||
|
if _, err := c.w.Write(value.Data); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cssMinifier) shortenToken(prop css.Hash, tt css.TokenType, data []byte) (css.TokenType, []byte) {
|
||||||
|
if tt == css.NumberToken || tt == css.PercentageToken || tt == css.DimensionToken {
|
||||||
|
if tt == css.NumberToken && (prop == css.Z_Index || prop == css.Counter_Increment || prop == css.Counter_Reset || prop == css.Orphans || prop == css.Widows) {
|
||||||
|
return tt, data // integers
|
||||||
|
}
|
||||||
|
n := len(data)
|
||||||
|
if tt == css.PercentageToken {
|
||||||
|
n--
|
||||||
|
} else if tt == css.DimensionToken {
|
||||||
|
n = parse.Number(data)
|
||||||
|
}
|
||||||
|
dim := data[n:]
|
||||||
|
data = minify.Number(data[:n], c.o.Decimals)
|
||||||
|
if len(data) != 1 || data[0] != '0' {
|
||||||
|
if tt == css.PercentageToken {
|
||||||
|
data = append(data, '%')
|
||||||
|
} else if tt == css.DimensionToken {
|
||||||
|
parse.ToLower(dim)
|
||||||
|
data = append(data, dim...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if tt == css.IdentToken {
|
||||||
|
parse.ToLower(data)
|
||||||
|
if hex, ok := ShortenColorName[css.ToHash(data)]; ok {
|
||||||
|
tt = css.HashToken
|
||||||
|
data = hex
|
||||||
|
}
|
||||||
|
} else if tt == css.HashToken {
|
||||||
|
parse.ToLower(data)
|
||||||
|
if ident, ok := ShortenColorHex[string(data)]; ok {
|
||||||
|
tt = css.IdentToken
|
||||||
|
data = ident
|
||||||
|
} else if len(data) == 7 && data[1] == data[2] && data[3] == data[4] && data[5] == data[6] {
|
||||||
|
tt = css.HashToken
|
||||||
|
data[2] = data[3]
|
||||||
|
data[3] = data[5]
|
||||||
|
data = data[:4]
|
||||||
|
}
|
||||||
|
} else if tt == css.StringToken {
|
||||||
|
// remove any \\\r\n \\\r \\\n
|
||||||
|
for i := 1; i < len(data)-2; i++ {
|
||||||
|
if data[i] == '\\' && (data[i+1] == '\n' || data[i+1] == '\r') {
|
||||||
|
// encountered first replacee, now start to move bytes to the front
|
||||||
|
j := i + 2
|
||||||
|
if data[i+1] == '\r' && len(data) > i+2 && data[i+2] == '\n' {
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
for ; j < len(data); j++ {
|
||||||
|
if data[j] == '\\' && len(data) > j+1 && (data[j+1] == '\n' || data[j+1] == '\r') {
|
||||||
|
if data[j+1] == '\r' && len(data) > j+2 && data[j+2] == '\n' {
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
j++
|
||||||
|
} else {
|
||||||
|
data[i] = data[j]
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data = data[:i]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if tt == css.URLToken {
|
||||||
|
parse.ToLower(data[:3])
|
||||||
|
if len(data) > 10 {
|
||||||
|
uri := data[4 : len(data)-1]
|
||||||
|
delim := byte('"')
|
||||||
|
if uri[0] == '\'' || uri[0] == '"' {
|
||||||
|
delim = uri[0]
|
||||||
|
uri = uri[1 : len(uri)-1]
|
||||||
|
}
|
||||||
|
uri = minify.DataURI(c.m, uri)
|
||||||
|
if css.IsURLUnquoted(uri) {
|
||||||
|
data = append(append([]byte("url("), uri...), ')')
|
||||||
|
} else {
|
||||||
|
data = append(append(append([]byte("url("), delim), uri...), delim, ')')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tt, data
|
||||||
|
}
|
143
vendor/github.com/tdewolff/minify/css/table.go
generated
vendored
Normal file
143
vendor/github.com/tdewolff/minify/css/table.go
generated
vendored
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
package css
|
||||||
|
|
||||||
|
import "github.com/tdewolff/parse/css"
|
||||||
|
|
||||||
|
// Uses http://www.w3.org/TR/2010/PR-css3-color-20101028/ for colors
|
||||||
|
|
||||||
|
// ShortenColorHex maps a color hexcode to its shorter name
|
||||||
|
var ShortenColorHex = map[string][]byte{
|
||||||
|
"#000080": []byte("navy"),
|
||||||
|
"#008000": []byte("green"),
|
||||||
|
"#008080": []byte("teal"),
|
||||||
|
"#4b0082": []byte("indigo"),
|
||||||
|
"#800000": []byte("maroon"),
|
||||||
|
"#800080": []byte("purple"),
|
||||||
|
"#808000": []byte("olive"),
|
||||||
|
"#808080": []byte("gray"),
|
||||||
|
"#a0522d": []byte("sienna"),
|
||||||
|
"#a52a2a": []byte("brown"),
|
||||||
|
"#c0c0c0": []byte("silver"),
|
||||||
|
"#cd853f": []byte("peru"),
|
||||||
|
"#d2b48c": []byte("tan"),
|
||||||
|
"#da70d6": []byte("orchid"),
|
||||||
|
"#dda0dd": []byte("plum"),
|
||||||
|
"#ee82ee": []byte("violet"),
|
||||||
|
"#f0e68c": []byte("khaki"),
|
||||||
|
"#f0ffff": []byte("azure"),
|
||||||
|
"#f5deb3": []byte("wheat"),
|
||||||
|
"#f5f5dc": []byte("beige"),
|
||||||
|
"#fa8072": []byte("salmon"),
|
||||||
|
"#faf0e6": []byte("linen"),
|
||||||
|
"#ff6347": []byte("tomato"),
|
||||||
|
"#ff7f50": []byte("coral"),
|
||||||
|
"#ffa500": []byte("orange"),
|
||||||
|
"#ffc0cb": []byte("pink"),
|
||||||
|
"#ffd700": []byte("gold"),
|
||||||
|
"#ffe4c4": []byte("bisque"),
|
||||||
|
"#fffafa": []byte("snow"),
|
||||||
|
"#fffff0": []byte("ivory"),
|
||||||
|
"#ff0000": []byte("red"),
|
||||||
|
"#f00": []byte("red"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShortenColorName maps a color name to its shorter hexcode
|
||||||
|
var ShortenColorName = map[css.Hash][]byte{
|
||||||
|
css.Black: []byte("#000"),
|
||||||
|
css.Darkblue: []byte("#00008b"),
|
||||||
|
css.Mediumblue: []byte("#0000cd"),
|
||||||
|
css.Darkgreen: []byte("#006400"),
|
||||||
|
css.Darkcyan: []byte("#008b8b"),
|
||||||
|
css.Deepskyblue: []byte("#00bfff"),
|
||||||
|
css.Darkturquoise: []byte("#00ced1"),
|
||||||
|
css.Mediumspringgreen: []byte("#00fa9a"),
|
||||||
|
css.Springgreen: []byte("#00ff7f"),
|
||||||
|
css.Midnightblue: []byte("#191970"),
|
||||||
|
css.Dodgerblue: []byte("#1e90ff"),
|
||||||
|
css.Lightseagreen: []byte("#20b2aa"),
|
||||||
|
css.Forestgreen: []byte("#228b22"),
|
||||||
|
css.Seagreen: []byte("#2e8b57"),
|
||||||
|
css.Darkslategray: []byte("#2f4f4f"),
|
||||||
|
css.Limegreen: []byte("#32cd32"),
|
||||||
|
css.Mediumseagreen: []byte("#3cb371"),
|
||||||
|
css.Turquoise: []byte("#40e0d0"),
|
||||||
|
css.Royalblue: []byte("#4169e1"),
|
||||||
|
css.Steelblue: []byte("#4682b4"),
|
||||||
|
css.Darkslateblue: []byte("#483d8b"),
|
||||||
|
css.Mediumturquoise: []byte("#48d1cc"),
|
||||||
|
css.Darkolivegreen: []byte("#556b2f"),
|
||||||
|
css.Cadetblue: []byte("#5f9ea0"),
|
||||||
|
css.Cornflowerblue: []byte("#6495ed"),
|
||||||
|
css.Mediumaquamarine: []byte("#66cdaa"),
|
||||||
|
css.Slateblue: []byte("#6a5acd"),
|
||||||
|
css.Olivedrab: []byte("#6b8e23"),
|
||||||
|
css.Slategray: []byte("#708090"),
|
||||||
|
css.Lightslateblue: []byte("#789"),
|
||||||
|
css.Mediumslateblue: []byte("#7b68ee"),
|
||||||
|
css.Lawngreen: []byte("#7cfc00"),
|
||||||
|
css.Chartreuse: []byte("#7fff00"),
|
||||||
|
css.Aquamarine: []byte("#7fffd4"),
|
||||||
|
css.Lightskyblue: []byte("#87cefa"),
|
||||||
|
css.Blueviolet: []byte("#8a2be2"),
|
||||||
|
css.Darkmagenta: []byte("#8b008b"),
|
||||||
|
css.Saddlebrown: []byte("#8b4513"),
|
||||||
|
css.Darkseagreen: []byte("#8fbc8f"),
|
||||||
|
css.Lightgreen: []byte("#90ee90"),
|
||||||
|
css.Mediumpurple: []byte("#9370db"),
|
||||||
|
css.Darkviolet: []byte("#9400d3"),
|
||||||
|
css.Palegreen: []byte("#98fb98"),
|
||||||
|
css.Darkorchid: []byte("#9932cc"),
|
||||||
|
css.Yellowgreen: []byte("#9acd32"),
|
||||||
|
css.Darkgray: []byte("#a9a9a9"),
|
||||||
|
css.Lightblue: []byte("#add8e6"),
|
||||||
|
css.Greenyellow: []byte("#adff2f"),
|
||||||
|
css.Paleturquoise: []byte("#afeeee"),
|
||||||
|
css.Lightsteelblue: []byte("#b0c4de"),
|
||||||
|
css.Powderblue: []byte("#b0e0e6"),
|
||||||
|
css.Firebrick: []byte("#b22222"),
|
||||||
|
css.Darkgoldenrod: []byte("#b8860b"),
|
||||||
|
css.Mediumorchid: []byte("#ba55d3"),
|
||||||
|
css.Rosybrown: []byte("#bc8f8f"),
|
||||||
|
css.Darkkhaki: []byte("#bdb76b"),
|
||||||
|
css.Mediumvioletred: []byte("#c71585"),
|
||||||
|
css.Indianred: []byte("#cd5c5c"),
|
||||||
|
css.Chocolate: []byte("#d2691e"),
|
||||||
|
css.Lightgray: []byte("#d3d3d3"),
|
||||||
|
css.Goldenrod: []byte("#daa520"),
|
||||||
|
css.Palevioletred: []byte("#db7093"),
|
||||||
|
css.Gainsboro: []byte("#dcdcdc"),
|
||||||
|
css.Burlywood: []byte("#deb887"),
|
||||||
|
css.Lightcyan: []byte("#e0ffff"),
|
||||||
|
css.Lavender: []byte("#e6e6fa"),
|
||||||
|
css.Darksalmon: []byte("#e9967a"),
|
||||||
|
css.Palegoldenrod: []byte("#eee8aa"),
|
||||||
|
css.Lightcoral: []byte("#f08080"),
|
||||||
|
css.Aliceblue: []byte("#f0f8ff"),
|
||||||
|
css.Honeydew: []byte("#f0fff0"),
|
||||||
|
css.Sandybrown: []byte("#f4a460"),
|
||||||
|
css.Whitesmoke: []byte("#f5f5f5"),
|
||||||
|
css.Mintcream: []byte("#f5fffa"),
|
||||||
|
css.Ghostwhite: []byte("#f8f8ff"),
|
||||||
|
css.Antiquewhite: []byte("#faebd7"),
|
||||||
|
css.Lightgoldenrodyellow: []byte("#fafad2"),
|
||||||
|
css.Fuchsia: []byte("#f0f"),
|
||||||
|
css.Magenta: []byte("#f0f"),
|
||||||
|
css.Deeppink: []byte("#ff1493"),
|
||||||
|
css.Orangered: []byte("#ff4500"),
|
||||||
|
css.Darkorange: []byte("#ff8c00"),
|
||||||
|
css.Lightsalmon: []byte("#ffa07a"),
|
||||||
|
css.Lightpink: []byte("#ffb6c1"),
|
||||||
|
css.Peachpuff: []byte("#ffdab9"),
|
||||||
|
css.Navajowhite: []byte("#ffdead"),
|
||||||
|
css.Moccasin: []byte("#ffe4b5"),
|
||||||
|
css.Mistyrose: []byte("#ffe4e1"),
|
||||||
|
css.Blanchedalmond: []byte("#ffebcd"),
|
||||||
|
css.Papayawhip: []byte("#ffefd5"),
|
||||||
|
css.Lavenderblush: []byte("#fff0f5"),
|
||||||
|
css.Seashell: []byte("#fff5ee"),
|
||||||
|
css.Cornsilk: []byte("#fff8dc"),
|
||||||
|
css.Lemonchiffon: []byte("#fffacd"),
|
||||||
|
css.Floralwhite: []byte("#fffaf0"),
|
||||||
|
css.Yellow: []byte("#ff0"),
|
||||||
|
css.Lightyellow: []byte("#ffffe0"),
|
||||||
|
css.White: []byte("#fff"),
|
||||||
|
}
|
198
vendor/github.com/tdewolff/minify/minify.go
generated
vendored
Normal file
198
vendor/github.com/tdewolff/minify/minify.go
generated
vendored
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
// Package minify relates MIME type to minifiers. Several minifiers are provided in the subpackages.
|
||||||
|
package minify
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net/url"
|
||||||
|
"os/exec"
|
||||||
|
"regexp"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/tdewolff/buffer"
|
||||||
|
"github.com/tdewolff/parse"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrNotExist is returned when no minifier exists for a given mimetype.
|
||||||
|
var ErrNotExist = errors.New("minifier does not exist for mimetype")
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
type minifierFunc func(*M, io.Writer, io.Reader, map[string]string) error
|
||||||
|
|
||||||
|
func (f minifierFunc) Minify(m *M, w io.Writer, r io.Reader, params map[string]string) error {
|
||||||
|
return f(m, w, r, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minifier is the interface for minifiers.
|
||||||
|
// The *M parameter is used for minifying embedded resources, such as JS within HTML.
|
||||||
|
type Minifier interface {
|
||||||
|
Minify(*M, io.Writer, io.Reader, map[string]string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
type patternMinifier struct {
|
||||||
|
pattern *regexp.Regexp
|
||||||
|
Minifier
|
||||||
|
}
|
||||||
|
|
||||||
|
type cmdMinifier struct {
|
||||||
|
cmd *exec.Cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cmdMinifier) Minify(_ *M, w io.Writer, r io.Reader, _ map[string]string) error {
|
||||||
|
cmd := &exec.Cmd{}
|
||||||
|
*cmd = *c.cmd // concurrency safety
|
||||||
|
cmd.Stdout = w
|
||||||
|
cmd.Stdin = r
|
||||||
|
return cmd.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// M holds a map of mimetype => function to allow recursive minifier calls of the minifier functions.
|
||||||
|
type M struct {
|
||||||
|
literal map[string]Minifier
|
||||||
|
pattern []patternMinifier
|
||||||
|
|
||||||
|
URL *url.URL
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new M.
|
||||||
|
func New() *M {
|
||||||
|
return &M{
|
||||||
|
map[string]Minifier{},
|
||||||
|
[]patternMinifier{},
|
||||||
|
nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds a minifier to the mimetype => function map (unsafe for concurrent use).
|
||||||
|
func (m *M) Add(mimetype string, minifier Minifier) {
|
||||||
|
m.literal[mimetype] = minifier
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFunc adds a minify function to the mimetype => function map (unsafe for concurrent use).
|
||||||
|
func (m *M) AddFunc(mimetype string, minifier minifierFunc) {
|
||||||
|
m.literal[mimetype] = minifier
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRegexp adds a minifier to the mimetype => function map (unsafe for concurrent use).
|
||||||
|
func (m *M) AddRegexp(pattern *regexp.Regexp, minifier Minifier) {
|
||||||
|
m.pattern = append(m.pattern, patternMinifier{pattern, minifier})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFuncRegexp adds a minify function to the mimetype => function map (unsafe for concurrent use).
|
||||||
|
func (m *M) AddFuncRegexp(pattern *regexp.Regexp, minifier minifierFunc) {
|
||||||
|
m.pattern = append(m.pattern, patternMinifier{pattern, minifier})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddCmd adds a minify function to the mimetype => function map (unsafe for concurrent use) that executes a command to process the minification.
|
||||||
|
// It allows the use of external tools like ClosureCompiler, UglifyCSS, etc. for a specific mimetype.
|
||||||
|
func (m *M) AddCmd(mimetype string, cmd *exec.Cmd) {
|
||||||
|
m.literal[mimetype] = &cmdMinifier{cmd}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddCmdRegexp adds a minify function to the mimetype => function map (unsafe for concurrent use) that executes a command to process the minification.
|
||||||
|
// It allows the use of external tools like ClosureCompiler, UglifyCSS, etc. for a specific mimetype regular expression.
|
||||||
|
func (m *M) AddCmdRegexp(pattern *regexp.Regexp, cmd *exec.Cmd) {
|
||||||
|
m.pattern = append(m.pattern, patternMinifier{pattern, &cmdMinifier{cmd}})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minify minifies the content of a Reader and writes it to a Writer (safe for concurrent use).
|
||||||
|
// An error is returned when no such mimetype exists (ErrNotExist) or when an error occurred in the minifier function.
|
||||||
|
// Mediatype may take the form of 'text/plain', 'text/*', '*/*' or 'text/plain; charset=UTF-8; version=2.0'.
|
||||||
|
func (m *M) Minify(mediatype string, w io.Writer, r io.Reader) error {
|
||||||
|
mimetype, params := parse.Mediatype([]byte(mediatype))
|
||||||
|
return m.MinifyMimetype(mimetype, w, r, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MinifyMimetype minifies the content of a Reader and writes it to a Writer (safe for concurrent use).
|
||||||
|
// It is a lower level version of Minify and requires the mediatype to be split up into mimetype and parameters.
|
||||||
|
// It is mostly used internally by minifiers because it is faster (no need to convert a byte-slice to string and vice versa).
|
||||||
|
func (m *M) MinifyMimetype(mimetype []byte, w io.Writer, r io.Reader, params map[string]string) error {
|
||||||
|
err := ErrNotExist
|
||||||
|
if minifier, ok := m.literal[string(mimetype)]; ok { // string conversion is optimized away
|
||||||
|
err = minifier.Minify(m, w, r, params)
|
||||||
|
} else {
|
||||||
|
for _, minifier := range m.pattern {
|
||||||
|
if minifier.pattern.Match(mimetype) {
|
||||||
|
err = minifier.Minify(m, w, r, params)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes minifies an array of bytes (safe for concurrent use). When an error occurs it return the original array and the error.
|
||||||
|
// It returns an error when no such mimetype exists (ErrNotExist) or any error occurred in the minifier function.
|
||||||
|
func (m *M) Bytes(mediatype string, v []byte) ([]byte, error) {
|
||||||
|
out := buffer.NewWriter(make([]byte, 0, len(v)))
|
||||||
|
if err := m.Minify(mediatype, out, buffer.NewReader(v)); err != nil {
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
return out.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// String minifies a string (safe for concurrent use). When an error occurs it return the original string and the error.
|
||||||
|
// It returns an error when no such mimetype exists (ErrNotExist) or any error occurred in the minifier function.
|
||||||
|
func (m *M) String(mediatype string, v string) (string, error) {
|
||||||
|
out := buffer.NewWriter(make([]byte, 0, len(v)))
|
||||||
|
if err := m.Minify(mediatype, out, buffer.NewReader([]byte(v))); err != nil {
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
return string(out.Bytes()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reader wraps a Reader interface and minifies the stream.
|
||||||
|
// Errors from the minifier are returned by the reader.
|
||||||
|
func (m *M) Reader(mediatype string, r io.Reader) io.Reader {
|
||||||
|
pr, pw := io.Pipe()
|
||||||
|
go func() {
|
||||||
|
if err := m.Minify(mediatype, pw, r); err != nil {
|
||||||
|
pw.CloseWithError(err)
|
||||||
|
} else {
|
||||||
|
pw.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return pr
|
||||||
|
}
|
||||||
|
|
||||||
|
// minifyWriter makes sure that errors from the minifier are passed down through Close.
|
||||||
|
// It also makes sure that all data has been written on calling Close, it flushes.
|
||||||
|
type minifyWriter struct {
|
||||||
|
pw *io.PipeWriter
|
||||||
|
wg sync.WaitGroup
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mw *minifyWriter) Write(b []byte) (int, error) {
|
||||||
|
return mw.pw.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mw *minifyWriter) Close() error {
|
||||||
|
mw.pw.Close()
|
||||||
|
mw.wg.Wait()
|
||||||
|
return mw.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writer wraps a Writer interface and minifies the stream.
|
||||||
|
// Errors from the minifier are returned by the writer.
|
||||||
|
// The writer must be closed explicitly.
|
||||||
|
func (m *M) Writer(mediatype string, w io.Writer) io.WriteCloser {
|
||||||
|
pr, pw := io.Pipe()
|
||||||
|
mw := &minifyWriter{pw, sync.WaitGroup{}, nil}
|
||||||
|
mw.wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
if err := m.Minify(mediatype, w, pr); err != nil {
|
||||||
|
mw.err = err
|
||||||
|
pr.CloseWithError(err)
|
||||||
|
} else {
|
||||||
|
pr.Close()
|
||||||
|
}
|
||||||
|
mw.wg.Done()
|
||||||
|
}()
|
||||||
|
return mw
|
||||||
|
}
|
134
vendor/github.com/tdewolff/minify/svg/buffer.go
generated
vendored
Normal file
134
vendor/github.com/tdewolff/minify/svg/buffer.go
generated
vendored
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
package svg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/tdewolff/parse"
|
||||||
|
"github.com/tdewolff/parse/svg"
|
||||||
|
"github.com/tdewolff/parse/xml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Token is a single token unit with an attribute value (if given) and hash of the data.
|
||||||
|
type Token struct {
|
||||||
|
xml.TokenType
|
||||||
|
Hash svg.Hash
|
||||||
|
Data []byte
|
||||||
|
Text []byte
|
||||||
|
AttrVal []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// TokenBuffer is a buffer that allows for token look-ahead.
|
||||||
|
type TokenBuffer struct {
|
||||||
|
l *xml.Lexer
|
||||||
|
|
||||||
|
buf []Token
|
||||||
|
pos int
|
||||||
|
|
||||||
|
attrBuffer []*Token
|
||||||
|
prevN int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTokenBuffer returns a new TokenBuffer.
|
||||||
|
func NewTokenBuffer(l *xml.Lexer) *TokenBuffer {
|
||||||
|
return &TokenBuffer{
|
||||||
|
l: l,
|
||||||
|
buf: make([]Token, 0, 8),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *TokenBuffer) read(t *Token) {
|
||||||
|
t.TokenType, t.Data = z.l.Next()
|
||||||
|
t.Text = z.l.Text()
|
||||||
|
if t.TokenType == xml.AttributeToken {
|
||||||
|
t.AttrVal = z.l.AttrVal()
|
||||||
|
if len(t.AttrVal) > 1 && (t.AttrVal[0] == '"' || t.AttrVal[0] == '\'') {
|
||||||
|
t.AttrVal = parse.ReplaceMultipleWhitespace(parse.TrimWhitespace(t.AttrVal[1 : len(t.AttrVal)-1])) // quotes will be readded in attribute loop if necessary
|
||||||
|
}
|
||||||
|
t.Hash = svg.ToHash(t.Text)
|
||||||
|
} else if t.TokenType == xml.StartTagToken || t.TokenType == xml.EndTagToken {
|
||||||
|
t.AttrVal = nil
|
||||||
|
t.Hash = svg.ToHash(t.Text)
|
||||||
|
} else {
|
||||||
|
t.AttrVal = nil
|
||||||
|
t.Hash = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Peek returns the ith element and possibly does an allocation.
|
||||||
|
// Peeking past an error will panic.
|
||||||
|
func (z *TokenBuffer) Peek(pos int) *Token {
|
||||||
|
pos += z.pos
|
||||||
|
if pos >= len(z.buf) {
|
||||||
|
if len(z.buf) > 0 && z.buf[len(z.buf)-1].TokenType == xml.ErrorToken {
|
||||||
|
return &z.buf[len(z.buf)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
c := cap(z.buf)
|
||||||
|
d := len(z.buf) - z.pos
|
||||||
|
p := pos - z.pos + 1 // required peek length
|
||||||
|
var buf []Token
|
||||||
|
if 2*p > c {
|
||||||
|
buf = make([]Token, 0, 2*c+p)
|
||||||
|
} else {
|
||||||
|
buf = z.buf
|
||||||
|
}
|
||||||
|
copy(buf[:d], z.buf[z.pos:])
|
||||||
|
|
||||||
|
buf = buf[:p]
|
||||||
|
pos -= z.pos
|
||||||
|
for i := d; i < p; i++ {
|
||||||
|
z.read(&buf[i])
|
||||||
|
if buf[i].TokenType == xml.ErrorToken {
|
||||||
|
buf = buf[:i+1]
|
||||||
|
pos = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
z.pos, z.buf = 0, buf
|
||||||
|
}
|
||||||
|
return &z.buf[pos]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shift returns the first element and advances position.
|
||||||
|
func (z *TokenBuffer) Shift() *Token {
|
||||||
|
z.l.Free(z.prevN)
|
||||||
|
if z.pos >= len(z.buf) {
|
||||||
|
t := &z.buf[:1][0]
|
||||||
|
z.read(t)
|
||||||
|
z.prevN = len(t.Data)
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
t := &z.buf[z.pos]
|
||||||
|
z.pos++
|
||||||
|
z.prevN = len(t.Data)
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attributes extracts the gives attribute hashes from a tag.
|
||||||
|
// It returns in the same order pointers to the requested token data or nil.
|
||||||
|
func (z *TokenBuffer) Attributes(hashes ...svg.Hash) ([]*Token, *Token) {
|
||||||
|
n := 0
|
||||||
|
for {
|
||||||
|
if t := z.Peek(n); t.TokenType != xml.AttributeToken {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
if len(hashes) > cap(z.attrBuffer) {
|
||||||
|
z.attrBuffer = make([]*Token, len(hashes))
|
||||||
|
} else {
|
||||||
|
z.attrBuffer = z.attrBuffer[:len(hashes)]
|
||||||
|
for i := range z.attrBuffer {
|
||||||
|
z.attrBuffer[i] = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var replacee *Token
|
||||||
|
for i := z.pos; i < z.pos+n; i++ {
|
||||||
|
attr := &z.buf[i]
|
||||||
|
for j, hash := range hashes {
|
||||||
|
if hash == attr.Hash {
|
||||||
|
z.attrBuffer[j] = attr
|
||||||
|
replacee = attr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return z.attrBuffer, replacee
|
||||||
|
}
|
269
vendor/github.com/tdewolff/minify/svg/pathdata.go
generated
vendored
Normal file
269
vendor/github.com/tdewolff/minify/svg/pathdata.go
generated
vendored
Normal file
|
@ -0,0 +1,269 @@
|
||||||
|
package svg
|
||||||
|
|
||||||
|
import (
|
||||||
|
strconvStdlib "strconv"
|
||||||
|
|
||||||
|
"github.com/tdewolff/minify"
|
||||||
|
"github.com/tdewolff/parse"
|
||||||
|
"github.com/tdewolff/strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PathData struct {
|
||||||
|
o *Minifier
|
||||||
|
|
||||||
|
x, y float64
|
||||||
|
coords [][]byte
|
||||||
|
coordFloats []float64
|
||||||
|
|
||||||
|
state PathDataState
|
||||||
|
curBuffer []byte
|
||||||
|
altBuffer []byte
|
||||||
|
coordBuffer []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type PathDataState struct {
|
||||||
|
cmd byte
|
||||||
|
prevDigit bool
|
||||||
|
prevDigitIsInt bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPathData(o *Minifier) *PathData {
|
||||||
|
return &PathData{o: o}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PathData) ShortenPathData(b []byte) []byte {
|
||||||
|
var x0, y0 float64
|
||||||
|
var cmd byte
|
||||||
|
|
||||||
|
p.x, p.y = 0.0, 0.0
|
||||||
|
p.coords = p.coords[:0]
|
||||||
|
p.coordFloats = p.coordFloats[:0]
|
||||||
|
p.state = PathDataState{}
|
||||||
|
|
||||||
|
j := 0
|
||||||
|
for i := 0; i < len(b); i++ {
|
||||||
|
c := b[i]
|
||||||
|
if c == ' ' || c == ',' || c == '\n' || c == '\r' || c == '\t' {
|
||||||
|
continue
|
||||||
|
} else if c >= 'A' && (cmd == 0 || cmd != c || c == 'M' || c == 'm') { // any command
|
||||||
|
if cmd != 0 {
|
||||||
|
j += p.copyInstruction(b[j:], cmd)
|
||||||
|
if cmd == 'M' || cmd == 'm' {
|
||||||
|
x0 = p.x
|
||||||
|
y0 = p.y
|
||||||
|
} else if cmd == 'Z' || cmd == 'z' {
|
||||||
|
p.x = x0
|
||||||
|
p.y = y0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cmd = c
|
||||||
|
p.coords = p.coords[:0]
|
||||||
|
p.coordFloats = p.coordFloats[:0]
|
||||||
|
} else if n := parse.Number(b[i:]); n > 0 {
|
||||||
|
f, _ := strconv.ParseFloat(b[i : i+n])
|
||||||
|
p.coords = append(p.coords, b[i:i+n])
|
||||||
|
p.coordFloats = append(p.coordFloats, f)
|
||||||
|
i += n - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cmd != 0 {
|
||||||
|
j += p.copyInstruction(b[j:], cmd)
|
||||||
|
}
|
||||||
|
return b[:j]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PathData) copyInstruction(b []byte, cmd byte) int {
|
||||||
|
n := len(p.coords)
|
||||||
|
if n == 0 {
|
||||||
|
if cmd == 'Z' || cmd == 'z' {
|
||||||
|
b[0] = 'z'
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
isRelCmd := cmd >= 'a'
|
||||||
|
|
||||||
|
// get new cursor coordinates
|
||||||
|
di := 0
|
||||||
|
if (cmd == 'M' || cmd == 'm' || cmd == 'L' || cmd == 'l' || cmd == 'T' || cmd == 't') && n%2 == 0 {
|
||||||
|
di = 2
|
||||||
|
// reprint M always, as the first pair is a move but subsequent pairs are L
|
||||||
|
if cmd == 'M' || cmd == 'm' {
|
||||||
|
p.state.cmd = byte(0)
|
||||||
|
}
|
||||||
|
} else if cmd == 'H' || cmd == 'h' || cmd == 'V' || cmd == 'v' {
|
||||||
|
di = 1
|
||||||
|
} else if (cmd == 'S' || cmd == 's' || cmd == 'Q' || cmd == 'q') && n%4 == 0 {
|
||||||
|
di = 4
|
||||||
|
} else if (cmd == 'C' || cmd == 'c') && n%6 == 0 {
|
||||||
|
di = 6
|
||||||
|
} else if (cmd == 'A' || cmd == 'a') && n%7 == 0 {
|
||||||
|
di = 7
|
||||||
|
} else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
j := 0
|
||||||
|
origCmd := cmd
|
||||||
|
ax, ay := 0.0, 0.0
|
||||||
|
for i := 0; i < n; i += di {
|
||||||
|
// subsequent coordinate pairs for M are really L
|
||||||
|
if i > 0 && (origCmd == 'M' || origCmd == 'm') {
|
||||||
|
origCmd = 'L' + (origCmd - 'M')
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = origCmd
|
||||||
|
coords := p.coords[i : i+di]
|
||||||
|
coordFloats := p.coordFloats[i : i+di]
|
||||||
|
|
||||||
|
if cmd == 'H' || cmd == 'h' {
|
||||||
|
ax = coordFloats[di-1]
|
||||||
|
if isRelCmd {
|
||||||
|
ay = 0
|
||||||
|
} else {
|
||||||
|
ay = p.y
|
||||||
|
}
|
||||||
|
} else if cmd == 'V' || cmd == 'v' {
|
||||||
|
if isRelCmd {
|
||||||
|
ax = 0
|
||||||
|
} else {
|
||||||
|
ax = p.x
|
||||||
|
}
|
||||||
|
ay = coordFloats[di-1]
|
||||||
|
} else {
|
||||||
|
ax = coordFloats[di-2]
|
||||||
|
ay = coordFloats[di-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// switch from L to H or V whenever possible
|
||||||
|
if cmd == 'L' || cmd == 'l' {
|
||||||
|
if isRelCmd {
|
||||||
|
if coordFloats[0] == 0 {
|
||||||
|
cmd = 'v'
|
||||||
|
coords = coords[1:]
|
||||||
|
coordFloats = coordFloats[1:]
|
||||||
|
} else if coordFloats[1] == 0 {
|
||||||
|
cmd = 'h'
|
||||||
|
coords = coords[:1]
|
||||||
|
coordFloats = coordFloats[:1]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if coordFloats[0] == p.x {
|
||||||
|
cmd = 'V'
|
||||||
|
coords = coords[1:]
|
||||||
|
coordFloats = coordFloats[1:]
|
||||||
|
} else if coordFloats[1] == p.y {
|
||||||
|
cmd = 'H'
|
||||||
|
coords = coords[:1]
|
||||||
|
coordFloats = coordFloats[:1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// make a current and alternated path with absolute/relative altered
|
||||||
|
var curState, altState PathDataState
|
||||||
|
curState = p.shortenCurPosInstruction(cmd, coords)
|
||||||
|
if isRelCmd {
|
||||||
|
altState = p.shortenAltPosInstruction(cmd-'a'+'A', coordFloats, p.x, p.y)
|
||||||
|
} else {
|
||||||
|
altState = p.shortenAltPosInstruction(cmd-'A'+'a', coordFloats, -p.x, -p.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
// choose shortest, relative or absolute path?
|
||||||
|
if len(p.altBuffer) < len(p.curBuffer) {
|
||||||
|
j += copy(b[j:], p.altBuffer)
|
||||||
|
p.state = altState
|
||||||
|
} else {
|
||||||
|
j += copy(b[j:], p.curBuffer)
|
||||||
|
p.state = curState
|
||||||
|
}
|
||||||
|
|
||||||
|
if isRelCmd {
|
||||||
|
p.x += ax
|
||||||
|
p.y += ay
|
||||||
|
} else {
|
||||||
|
p.x = ax
|
||||||
|
p.y = ay
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return j
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PathData) shortenCurPosInstruction(cmd byte, coords [][]byte) PathDataState {
|
||||||
|
state := p.state
|
||||||
|
p.curBuffer = p.curBuffer[:0]
|
||||||
|
if cmd != state.cmd && !(state.cmd == 'M' && cmd == 'L' || state.cmd == 'm' && cmd == 'l') {
|
||||||
|
p.curBuffer = append(p.curBuffer, cmd)
|
||||||
|
state.cmd = cmd
|
||||||
|
state.prevDigit = false
|
||||||
|
state.prevDigitIsInt = false
|
||||||
|
}
|
||||||
|
for _, coord := range coords {
|
||||||
|
coord = minify.Number(coord, p.o.Decimals)
|
||||||
|
state.copyNumber(&p.curBuffer, coord)
|
||||||
|
}
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PathData) shortenAltPosInstruction(cmd byte, coordFloats []float64, x, y float64) PathDataState {
|
||||||
|
state := p.state
|
||||||
|
p.altBuffer = p.altBuffer[:0]
|
||||||
|
if cmd != state.cmd && !(state.cmd == 'M' && cmd == 'L' || state.cmd == 'm' && cmd == 'l') {
|
||||||
|
p.altBuffer = append(p.altBuffer, cmd)
|
||||||
|
state.cmd = cmd
|
||||||
|
state.prevDigit = false
|
||||||
|
state.prevDigitIsInt = false
|
||||||
|
}
|
||||||
|
for i, f := range coordFloats {
|
||||||
|
if cmd == 'L' || cmd == 'l' || cmd == 'C' || cmd == 'c' || cmd == 'S' || cmd == 's' || cmd == 'Q' || cmd == 'q' || cmd == 'T' || cmd == 't' || cmd == 'M' || cmd == 'm' {
|
||||||
|
if i%2 == 0 {
|
||||||
|
f += x
|
||||||
|
} else {
|
||||||
|
f += y
|
||||||
|
}
|
||||||
|
} else if cmd == 'H' || cmd == 'h' {
|
||||||
|
f += x
|
||||||
|
} else if cmd == 'V' || cmd == 'v' {
|
||||||
|
f += y
|
||||||
|
} else if cmd == 'A' || cmd == 'a' {
|
||||||
|
if i%7 == 5 {
|
||||||
|
f += x
|
||||||
|
} else if i%7 == 6 {
|
||||||
|
f += y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p.coordBuffer = strconvStdlib.AppendFloat(p.coordBuffer[:0], f, 'g', -1, 64)
|
||||||
|
coord := minify.Number(p.coordBuffer, p.o.Decimals)
|
||||||
|
state.copyNumber(&p.altBuffer, coord)
|
||||||
|
}
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
func (state *PathDataState) copyNumber(buffer *[]byte, coord []byte) {
|
||||||
|
if state.prevDigit && (coord[0] >= '0' && coord[0] <= '9' || coord[0] == '.' && state.prevDigitIsInt) {
|
||||||
|
if coord[0] == '0' && !state.prevDigitIsInt {
|
||||||
|
// if the first digit is zero, it is always just zero
|
||||||
|
*buffer = append(*buffer, '.', '0')
|
||||||
|
// prevDigit and prevDigitIsInt stay true
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
*buffer = append(*buffer, ' ')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.prevDigit = true
|
||||||
|
state.prevDigitIsInt = true
|
||||||
|
if len(coord) > 2 && coord[len(coord)-2] == '0' && coord[len(coord)-1] == '0' {
|
||||||
|
coord[len(coord)-2] = 'e'
|
||||||
|
coord[len(coord)-1] = '2'
|
||||||
|
state.prevDigitIsInt = false
|
||||||
|
} else {
|
||||||
|
for _, c := range coord {
|
||||||
|
if c == '.' || c == 'e' || c == 'E' {
|
||||||
|
state.prevDigitIsInt = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*buffer = append(*buffer, coord...)
|
||||||
|
}
|
427
vendor/github.com/tdewolff/minify/svg/svg.go
generated
vendored
Normal file
427
vendor/github.com/tdewolff/minify/svg/svg.go
generated
vendored
Normal file
|
@ -0,0 +1,427 @@
|
||||||
|
// Package svg minifies SVG1.1 following the specifications at http://www.w3.org/TR/SVG11/.
|
||||||
|
package svg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/tdewolff/buffer"
|
||||||
|
"github.com/tdewolff/minify"
|
||||||
|
minifyCSS "github.com/tdewolff/minify/css"
|
||||||
|
"github.com/tdewolff/parse"
|
||||||
|
"github.com/tdewolff/parse/css"
|
||||||
|
"github.com/tdewolff/parse/svg"
|
||||||
|
"github.com/tdewolff/parse/xml"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
voidBytes = []byte("/>")
|
||||||
|
isBytes = []byte("=")
|
||||||
|
spaceBytes = []byte(" ")
|
||||||
|
cdataEndBytes = []byte("]]>")
|
||||||
|
pathBytes = []byte("<path")
|
||||||
|
dBytes = []byte("d")
|
||||||
|
zeroBytes = []byte("0")
|
||||||
|
cssMimeBytes = []byte("text/css")
|
||||||
|
)
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Minifier is an SVG minifier.
|
||||||
|
type Minifier struct {
|
||||||
|
Decimals int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minify minifies SVG data, it reads from r and writes to w.
|
||||||
|
func Minify(m *minify.M, w io.Writer, r io.Reader, params map[string]string) error {
|
||||||
|
return (&Minifier{Decimals: -1}).Minify(m, w, r, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minify minifies SVG data, it reads from r and writes to w.
|
||||||
|
func (o *Minifier) Minify(m *minify.M, w io.Writer, r io.Reader, _ map[string]string) error {
|
||||||
|
var tag svg.Hash
|
||||||
|
defaultStyleType := cssMimeBytes
|
||||||
|
defaultStyleParams := map[string]string(nil)
|
||||||
|
defaultInlineStyleParams := map[string]string{"inline": "1"}
|
||||||
|
|
||||||
|
p := NewPathData(o)
|
||||||
|
minifyBuffer := buffer.NewWriter(make([]byte, 0, 64))
|
||||||
|
attrByteBuffer := make([]byte, 0, 64)
|
||||||
|
gStack := make([]bool, 0)
|
||||||
|
|
||||||
|
l := xml.NewLexer(r)
|
||||||
|
tb := NewTokenBuffer(l)
|
||||||
|
for {
|
||||||
|
t := *tb.Shift()
|
||||||
|
SWITCH:
|
||||||
|
switch t.TokenType {
|
||||||
|
case xml.ErrorToken:
|
||||||
|
if l.Err() == io.EOF {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return l.Err()
|
||||||
|
case xml.DOCTYPEToken:
|
||||||
|
if len(t.Text) > 0 && t.Text[len(t.Text)-1] == ']' {
|
||||||
|
if _, err := w.Write(t.Data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case xml.TextToken:
|
||||||
|
t.Data = parse.ReplaceMultipleWhitespace(parse.TrimWhitespace(t.Data))
|
||||||
|
if tag == svg.Style && len(t.Data) > 0 {
|
||||||
|
if err := m.MinifyMimetype(defaultStyleType, w, buffer.NewReader(t.Data), defaultStyleParams); err != nil {
|
||||||
|
if err != minify.ErrNotExist {
|
||||||
|
return err
|
||||||
|
} else if _, err := w.Write(t.Data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if _, err := w.Write(t.Data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case xml.CDATAToken:
|
||||||
|
if tag == svg.Style {
|
||||||
|
minifyBuffer.Reset()
|
||||||
|
if err := m.MinifyMimetype(defaultStyleType, minifyBuffer, buffer.NewReader(t.Text), defaultStyleParams); err == nil {
|
||||||
|
t.Data = append(t.Data[:9], minifyBuffer.Bytes()...)
|
||||||
|
t.Text = t.Data[9:]
|
||||||
|
t.Data = append(t.Data, cdataEndBytes...)
|
||||||
|
} else if err != minify.ErrNotExist {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var useText bool
|
||||||
|
if t.Text, useText = xml.EscapeCDATAVal(&attrByteBuffer, t.Text); useText {
|
||||||
|
t.Text = parse.ReplaceMultipleWhitespace(parse.TrimWhitespace(t.Text))
|
||||||
|
if _, err := w.Write(t.Text); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else if _, err := w.Write(t.Data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case xml.StartTagPIToken:
|
||||||
|
for {
|
||||||
|
if t := *tb.Shift(); t.TokenType == xml.StartTagClosePIToken || t.TokenType == xml.ErrorToken {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case xml.StartTagToken:
|
||||||
|
tag = t.Hash
|
||||||
|
if containerTagMap[tag] { // skip empty containers
|
||||||
|
i := 0
|
||||||
|
for {
|
||||||
|
next := tb.Peek(i)
|
||||||
|
i++
|
||||||
|
if next.TokenType == xml.EndTagToken && next.Hash == tag || next.TokenType == xml.StartTagCloseVoidToken || next.TokenType == xml.ErrorToken {
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
tb.Shift()
|
||||||
|
}
|
||||||
|
break SWITCH
|
||||||
|
} else if next.TokenType != xml.AttributeToken && next.TokenType != xml.StartTagCloseToken {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if tag == svg.G {
|
||||||
|
if tb.Peek(0).TokenType == xml.StartTagCloseToken {
|
||||||
|
gStack = append(gStack, false)
|
||||||
|
tb.Shift()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
gStack = append(gStack, true)
|
||||||
|
}
|
||||||
|
} else if tag == svg.Metadata {
|
||||||
|
skipTag(tb, tag)
|
||||||
|
break
|
||||||
|
} else if tag == svg.Line {
|
||||||
|
o.shortenLine(tb, &t, p)
|
||||||
|
} else if tag == svg.Rect && !o.shortenRect(tb, &t, p) {
|
||||||
|
skipTag(tb, tag)
|
||||||
|
break
|
||||||
|
} else if tag == svg.Polygon || tag == svg.Polyline {
|
||||||
|
o.shortenPoly(tb, &t, p)
|
||||||
|
}
|
||||||
|
if _, err := w.Write(t.Data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case xml.AttributeToken:
|
||||||
|
if len(t.AttrVal) == 0 || t.Text == nil { // data is nil when attribute has been removed
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
attr := t.Hash
|
||||||
|
val := t.AttrVal
|
||||||
|
if n, m := parse.Dimension(val); n+m == len(val) { // TODO: inefficient, temporary measure
|
||||||
|
val, _ = o.shortenDimension(val)
|
||||||
|
}
|
||||||
|
if attr == svg.Xml_Space && parse.Equal(val, []byte("preserve")) ||
|
||||||
|
tag == svg.Svg && (attr == svg.Version && parse.Equal(val, []byte("1.1")) ||
|
||||||
|
attr == svg.X && parse.Equal(val, []byte("0")) ||
|
||||||
|
attr == svg.Y && parse.Equal(val, []byte("0")) ||
|
||||||
|
attr == svg.Width && parse.Equal(val, []byte("100%")) ||
|
||||||
|
attr == svg.Height && parse.Equal(val, []byte("100%")) ||
|
||||||
|
attr == svg.PreserveAspectRatio && parse.Equal(val, []byte("xMidYMid meet")) ||
|
||||||
|
attr == svg.BaseProfile && parse.Equal(val, []byte("none")) ||
|
||||||
|
attr == svg.ContentScriptType && parse.Equal(val, []byte("application/ecmascript")) ||
|
||||||
|
attr == svg.ContentStyleType && parse.Equal(val, []byte("text/css"))) ||
|
||||||
|
tag == svg.Style && attr == svg.Type && parse.Equal(val, []byte("text/css")) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := w.Write(spaceBytes); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := w.Write(t.Text); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := w.Write(isBytes); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if tag == svg.Svg && attr == svg.ContentStyleType {
|
||||||
|
val = minify.ContentType(val)
|
||||||
|
defaultStyleType = val
|
||||||
|
} else if attr == svg.Style {
|
||||||
|
minifyBuffer.Reset()
|
||||||
|
if err := m.MinifyMimetype(defaultStyleType, minifyBuffer, buffer.NewReader(val), defaultInlineStyleParams); err == nil {
|
||||||
|
val = minifyBuffer.Bytes()
|
||||||
|
} else if err != minify.ErrNotExist {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else if attr == svg.D {
|
||||||
|
val = p.ShortenPathData(val)
|
||||||
|
} else if attr == svg.ViewBox {
|
||||||
|
j := 0
|
||||||
|
newVal := val[:0]
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
if i != 0 {
|
||||||
|
if j >= len(val) || val[j] != ' ' && val[j] != ',' {
|
||||||
|
newVal = append(newVal, val[j:]...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
newVal = append(newVal, ' ')
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
if dim, n := o.shortenDimension(val[j:]); n > 0 {
|
||||||
|
newVal = append(newVal, dim...)
|
||||||
|
j += n
|
||||||
|
} else {
|
||||||
|
newVal = append(newVal, val[j:]...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val = newVal
|
||||||
|
} else if colorAttrMap[attr] && len(val) > 0 {
|
||||||
|
parse.ToLower(val)
|
||||||
|
if val[0] == '#' {
|
||||||
|
if name, ok := minifyCSS.ShortenColorHex[string(val)]; ok {
|
||||||
|
val = name
|
||||||
|
} else if len(val) == 7 && val[1] == val[2] && val[3] == val[4] && val[5] == val[6] {
|
||||||
|
val[2] = val[3]
|
||||||
|
val[3] = val[5]
|
||||||
|
val = val[:4]
|
||||||
|
}
|
||||||
|
} else if hex, ok := minifyCSS.ShortenColorName[css.ToHash(val)]; ok {
|
||||||
|
val = hex
|
||||||
|
} else if len(val) > 5 && parse.Equal(val[:4], []byte("rgb(")) && val[len(val)-1] == ')' {
|
||||||
|
// TODO: handle rgb(x, y, z) and hsl(x, y, z)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// prefer single or double quotes depending on what occurs more often in value
|
||||||
|
val = xml.EscapeAttrVal(&attrByteBuffer, val)
|
||||||
|
if _, err := w.Write(val); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case xml.StartTagCloseToken:
|
||||||
|
next := tb.Peek(0)
|
||||||
|
skipExtra := false
|
||||||
|
if next.TokenType == xml.TextToken && parse.IsAllWhitespace(next.Data) {
|
||||||
|
next = tb.Peek(1)
|
||||||
|
skipExtra = true
|
||||||
|
}
|
||||||
|
if next.TokenType == xml.EndTagToken {
|
||||||
|
// collapse empty tags to single void tag
|
||||||
|
tb.Shift()
|
||||||
|
if skipExtra {
|
||||||
|
tb.Shift()
|
||||||
|
}
|
||||||
|
if _, err := w.Write(voidBytes); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if _, err := w.Write(t.Data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case xml.StartTagCloseVoidToken:
|
||||||
|
tag = 0
|
||||||
|
if _, err := w.Write(t.Data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case xml.EndTagToken:
|
||||||
|
tag = 0
|
||||||
|
if t.Hash == svg.G && len(gStack) > 0 {
|
||||||
|
if !gStack[len(gStack)-1] {
|
||||||
|
gStack = gStack[:len(gStack)-1]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
gStack = gStack[:len(gStack)-1]
|
||||||
|
}
|
||||||
|
if len(t.Data) > 3+len(t.Text) {
|
||||||
|
t.Data[2+len(t.Text)] = '>'
|
||||||
|
t.Data = t.Data[:3+len(t.Text)]
|
||||||
|
}
|
||||||
|
if _, err := w.Write(t.Data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Minifier) shortenDimension(b []byte) ([]byte, int) {
|
||||||
|
if n, m := parse.Dimension(b); n > 0 {
|
||||||
|
unit := b[n : n+m]
|
||||||
|
b = minify.Number(b[:n], o.Decimals)
|
||||||
|
if len(b) != 1 || b[0] != '0' {
|
||||||
|
if m == 2 && unit[0] == 'p' && unit[1] == 'x' {
|
||||||
|
unit = nil
|
||||||
|
} else if m > 1 { // only percentage is length 1
|
||||||
|
parse.ToLower(unit)
|
||||||
|
}
|
||||||
|
b = append(b, unit...)
|
||||||
|
}
|
||||||
|
return b, n + m
|
||||||
|
}
|
||||||
|
return b, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Minifier) shortenLine(tb *TokenBuffer, t *Token, p *PathData) {
|
||||||
|
x1, y1, x2, y2 := zeroBytes, zeroBytes, zeroBytes, zeroBytes
|
||||||
|
if attrs, replacee := tb.Attributes(svg.X1, svg.Y1, svg.X2, svg.Y2); replacee != nil {
|
||||||
|
if attrs[0] != nil {
|
||||||
|
x1 = minify.Number(attrs[0].AttrVal, o.Decimals)
|
||||||
|
attrs[0].Text = nil
|
||||||
|
}
|
||||||
|
if attrs[1] != nil {
|
||||||
|
y1 = minify.Number(attrs[1].AttrVal, o.Decimals)
|
||||||
|
attrs[1].Text = nil
|
||||||
|
}
|
||||||
|
if attrs[2] != nil {
|
||||||
|
x2 = minify.Number(attrs[2].AttrVal, o.Decimals)
|
||||||
|
attrs[2].Text = nil
|
||||||
|
}
|
||||||
|
if attrs[3] != nil {
|
||||||
|
y2 = minify.Number(attrs[3].AttrVal, o.Decimals)
|
||||||
|
attrs[3].Text = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
d := make([]byte, 0, 5+len(x1)+len(y1)+len(x2)+len(y2))
|
||||||
|
d = append(d, 'M')
|
||||||
|
d = append(d, x1...)
|
||||||
|
d = append(d, ' ')
|
||||||
|
d = append(d, y1...)
|
||||||
|
d = append(d, 'L')
|
||||||
|
d = append(d, x2...)
|
||||||
|
d = append(d, ' ')
|
||||||
|
d = append(d, y2...)
|
||||||
|
d = append(d, 'z')
|
||||||
|
d = p.ShortenPathData(d)
|
||||||
|
|
||||||
|
t.Data = pathBytes
|
||||||
|
replacee.Text = dBytes
|
||||||
|
replacee.AttrVal = d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Minifier) shortenRect(tb *TokenBuffer, t *Token, p *PathData) bool {
|
||||||
|
if attrs, replacee := tb.Attributes(svg.X, svg.Y, svg.Width, svg.Height, svg.Rx, svg.Ry); replacee != nil && attrs[4] == nil && attrs[5] == nil {
|
||||||
|
x, y, w, h := zeroBytes, zeroBytes, zeroBytes, zeroBytes
|
||||||
|
if attrs[0] != nil {
|
||||||
|
x = minify.Number(attrs[0].AttrVal, o.Decimals)
|
||||||
|
attrs[0].Text = nil
|
||||||
|
}
|
||||||
|
if attrs[1] != nil {
|
||||||
|
y = minify.Number(attrs[1].AttrVal, o.Decimals)
|
||||||
|
attrs[1].Text = nil
|
||||||
|
}
|
||||||
|
if attrs[2] != nil {
|
||||||
|
w = minify.Number(attrs[2].AttrVal, o.Decimals)
|
||||||
|
attrs[2].Text = nil
|
||||||
|
}
|
||||||
|
if attrs[3] != nil {
|
||||||
|
h = minify.Number(attrs[3].AttrVal, o.Decimals)
|
||||||
|
attrs[3].Text = nil
|
||||||
|
}
|
||||||
|
if len(w) == 0 || w[0] == '0' || len(h) == 0 || h[0] == '0' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
d := make([]byte, 0, 6+2*len(x)+len(y)+len(w)+len(h))
|
||||||
|
d = append(d, 'M')
|
||||||
|
d = append(d, x...)
|
||||||
|
d = append(d, ' ')
|
||||||
|
d = append(d, y...)
|
||||||
|
d = append(d, 'h')
|
||||||
|
d = append(d, w...)
|
||||||
|
d = append(d, 'v')
|
||||||
|
d = append(d, h...)
|
||||||
|
d = append(d, 'H')
|
||||||
|
d = append(d, x...)
|
||||||
|
d = append(d, 'z')
|
||||||
|
d = p.ShortenPathData(d)
|
||||||
|
|
||||||
|
t.Data = pathBytes
|
||||||
|
replacee.Text = dBytes
|
||||||
|
replacee.AttrVal = d
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Minifier) shortenPoly(tb *TokenBuffer, t *Token, p *PathData) {
|
||||||
|
if attrs, replacee := tb.Attributes(svg.Points); replacee != nil && attrs[0] != nil {
|
||||||
|
points := attrs[0].AttrVal
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
for i < len(points) && !(points[i] == ' ' || points[i] == ',' || points[i] == '\n' || points[i] == '\r' || points[i] == '\t') {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
for i < len(points) && (points[i] == ' ' || points[i] == ',' || points[i] == '\n' || points[i] == '\r' || points[i] == '\t') {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
for i < len(points) && !(points[i] == ' ' || points[i] == ',' || points[i] == '\n' || points[i] == '\r' || points[i] == '\t') {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
endMoveTo := i
|
||||||
|
for i < len(points) && (points[i] == ' ' || points[i] == ',' || points[i] == '\n' || points[i] == '\r' || points[i] == '\t') {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
startLineTo := i
|
||||||
|
|
||||||
|
if i == len(points) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
d := make([]byte, 0, len(points)+3)
|
||||||
|
d = append(d, 'M')
|
||||||
|
d = append(d, points[:endMoveTo]...)
|
||||||
|
d = append(d, 'L')
|
||||||
|
d = append(d, points[startLineTo:]...)
|
||||||
|
if t.Hash == svg.Polygon {
|
||||||
|
d = append(d, 'z')
|
||||||
|
}
|
||||||
|
d = p.ShortenPathData(d)
|
||||||
|
|
||||||
|
t.Data = pathBytes
|
||||||
|
replacee.Text = dBytes
|
||||||
|
replacee.AttrVal = d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
func skipTag(tb *TokenBuffer, tag svg.Hash) {
|
||||||
|
for {
|
||||||
|
if t := *tb.Shift(); (t.TokenType == xml.EndTagToken || t.TokenType == xml.StartTagCloseVoidToken) && t.Hash == tag || t.TokenType == xml.ErrorToken {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
97
vendor/github.com/tdewolff/minify/svg/table.go
generated
vendored
Normal file
97
vendor/github.com/tdewolff/minify/svg/table.go
generated
vendored
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
package svg
|
||||||
|
|
||||||
|
import "github.com/tdewolff/parse/svg"
|
||||||
|
|
||||||
|
var containerTagMap = map[svg.Hash]bool{
|
||||||
|
svg.A: true,
|
||||||
|
svg.Defs: true,
|
||||||
|
svg.G: true,
|
||||||
|
svg.Marker: true,
|
||||||
|
svg.Mask: true,
|
||||||
|
svg.Missing_Glyph: true,
|
||||||
|
svg.Pattern: true,
|
||||||
|
svg.Svg: true,
|
||||||
|
svg.Switch: true,
|
||||||
|
svg.Symbol: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
var colorAttrMap = map[svg.Hash]bool{
|
||||||
|
svg.Color: true,
|
||||||
|
svg.Fill: true,
|
||||||
|
svg.Stroke: true,
|
||||||
|
svg.Stop_Color: true,
|
||||||
|
svg.Flood_Color: true,
|
||||||
|
svg.Lighting_Color: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// var styleAttrMap = map[svg.Hash]bool{
|
||||||
|
// svg.Font: true,
|
||||||
|
// svg.Font_Family: true,
|
||||||
|
// svg.Font_Size: true,
|
||||||
|
// svg.Font_Size_Adjust: true,
|
||||||
|
// svg.Font_Stretch: true,
|
||||||
|
// svg.Font_Style: true,
|
||||||
|
// svg.Font_Variant: true,
|
||||||
|
// svg.Font_Weight: true,
|
||||||
|
// svg.Direction: true,
|
||||||
|
// svg.Letter_Spacing: true,
|
||||||
|
// svg.Text_Decoration: true,
|
||||||
|
// svg.Unicode_Bidi: true,
|
||||||
|
// svg.White_Space: true,
|
||||||
|
// svg.Word_Spacing: true,
|
||||||
|
// svg.Clip: true,
|
||||||
|
// svg.Color: true,
|
||||||
|
// svg.Cursor: true,
|
||||||
|
// svg.Display: true,
|
||||||
|
// svg.Overflow: true,
|
||||||
|
// svg.Visibility: true,
|
||||||
|
// svg.Clip_Path: true,
|
||||||
|
// svg.Clip_Rule: true,
|
||||||
|
// svg.Mask: true,
|
||||||
|
// svg.Opacity: true,
|
||||||
|
// svg.Enable_Background: true,
|
||||||
|
// svg.Filter: true,
|
||||||
|
// svg.Flood_Color: true,
|
||||||
|
// svg.Flood_Opacity: true,
|
||||||
|
// svg.Lighting_Color: true,
|
||||||
|
// svg.Solid_Color: true,
|
||||||
|
// svg.Solid_Opacity: true,
|
||||||
|
// svg.Stop_Color: true,
|
||||||
|
// svg.Stop_Opacity: true,
|
||||||
|
// svg.Pointer_Events: true,
|
||||||
|
// svg.Buffered_Rendering: true,
|
||||||
|
// svg.Color_Interpolation: true,
|
||||||
|
// svg.Color_Interpolation_Filters: true,
|
||||||
|
// svg.Color_Profile: true,
|
||||||
|
// svg.Color_Rendering: true,
|
||||||
|
// svg.Fill: true,
|
||||||
|
// svg.Fill_Opacity: true,
|
||||||
|
// svg.Fill_Rule: true,
|
||||||
|
// svg.Image_Rendering: true,
|
||||||
|
// svg.Marker: true,
|
||||||
|
// svg.Marker_End: true,
|
||||||
|
// svg.Marker_Mid: true,
|
||||||
|
// svg.Marker_Start: true,
|
||||||
|
// svg.Shape_Rendering: true,
|
||||||
|
// svg.Stroke: true,
|
||||||
|
// svg.Stroke_Dasharray: true,
|
||||||
|
// svg.Stroke_Dashoffset: true,
|
||||||
|
// svg.Stroke_Linecap: true,
|
||||||
|
// svg.Stroke_Linejoin: true,
|
||||||
|
// svg.Stroke_Miterlimit: true,
|
||||||
|
// svg.Stroke_Opacity: true,
|
||||||
|
// svg.Stroke_Width: true,
|
||||||
|
// svg.Paint_Order: true,
|
||||||
|
// svg.Vector_Effect: true,
|
||||||
|
// svg.Viewport_Fill: true,
|
||||||
|
// svg.Viewport_Fill_Opacity: true,
|
||||||
|
// svg.Text_Rendering: true,
|
||||||
|
// svg.Alignment_Baseline: true,
|
||||||
|
// svg.Baseline_Shift: true,
|
||||||
|
// svg.Dominant_Baseline: true,
|
||||||
|
// svg.Glyph_Orientation_Horizontal: true,
|
||||||
|
// svg.Glyph_Orientation_Vertical: true,
|
||||||
|
// svg.Kerning: true,
|
||||||
|
// svg.Text_Anchor: true,
|
||||||
|
// svg.Writing_Mode: true,
|
||||||
|
// }
|
3
vendor/github.com/tdewolff/parse/.travis.yml
generated
vendored
Normal file
3
vendor/github.com/tdewolff/parse/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
language: go
|
||||||
|
script:
|
||||||
|
- go test -v ./...
|
22
vendor/github.com/tdewolff/parse/LICENSE.md
generated
vendored
Normal file
22
vendor/github.com/tdewolff/parse/LICENSE.md
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
Copyright (c) 2015 Taco de Wolff
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use,
|
||||||
|
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following
|
||||||
|
conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
38
vendor/github.com/tdewolff/parse/README.md
generated
vendored
Normal file
38
vendor/github.com/tdewolff/parse/README.md
generated
vendored
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
# Parse [![Build Status](https://travis-ci.org/tdewolff/parse.svg?branch=master)](https://travis-ci.org/tdewolff/parse) [![GoDoc](http://godoc.org/github.com/tdewolff/parse?status.svg)](http://godoc.org/github.com/tdewolff/parse)
|
||||||
|
|
||||||
|
This package contains several lexers and parsers written in [Go][1]. All subpackages are built to be streaming, high performance and to be in accordance with the official (latest) specifications.
|
||||||
|
|
||||||
|
The lexers are implemented using `buffer.Lexer` in https://github.com/tdewolff/buffer and the parsers work on top of the lexers. Some subpackages have hashes defined (using [Hasher](https://github.com/tdewolff/hasher)) that speed up common byte-slice comparisons.
|
||||||
|
|
||||||
|
## CSS
|
||||||
|
This package is a CSS3 lexer and parser. Both follow the specification at [CSS Syntax Module Level 3](http://www.w3.org/TR/css-syntax-3/). The lexer takes an io.Reader and converts it into tokens until the EOF. The parser returns a parse tree of the full io.Reader input stream, but the low-level `Next` function can be used for stream parsing to returns grammar units until the EOF.
|
||||||
|
|
||||||
|
[See README here](https://github.com/tdewolff/parse/tree/master/css).
|
||||||
|
|
||||||
|
## HTML
|
||||||
|
This package is an HTML5 lexer. It follows the specification at [The HTML syntax](http://www.w3.org/TR/html5/syntax.html). The lexer takes an io.Reader and converts it into tokens until the EOF.
|
||||||
|
|
||||||
|
[See README here](https://github.com/tdewolff/parse/tree/master/html).
|
||||||
|
|
||||||
|
## JS
|
||||||
|
This package is a JS lexer (ECMA-262, edition 6.0). It follows the specification at [ECMAScript Language Specification](http://www.ecma-international.org/ecma-262/6.0/). The lexer takes an io.Reader and converts it into tokens until the EOF.
|
||||||
|
|
||||||
|
[See README here](https://github.com/tdewolff/parse/tree/master/js).
|
||||||
|
|
||||||
|
## JSON
|
||||||
|
This package is a JSON parser (ECMA-404). It follows the specification at [JSON](http://json.org/). The parser takes an io.Reader and converts it into tokens until the EOF.
|
||||||
|
|
||||||
|
[See README here](https://github.com/tdewolff/parse/tree/master/json).
|
||||||
|
|
||||||
|
## SVG
|
||||||
|
This package contains common hashes for SVG1.1 tags and attributes.
|
||||||
|
|
||||||
|
## XML
|
||||||
|
This package is an XML1.0 lexer. It follows the specification at [Extensible Markup Language (XML) 1.0 (Fifth Edition)](http://www.w3.org/TR/xml/). The lexer takes an io.Reader and converts it into tokens until the EOF.
|
||||||
|
|
||||||
|
[See README here](https://github.com/tdewolff/parse/tree/master/xml).
|
||||||
|
|
||||||
|
## License
|
||||||
|
Released under the [MIT license](LICENSE.md).
|
||||||
|
|
||||||
|
[1]: http://golang.org/ "Go Language"
|
230
vendor/github.com/tdewolff/parse/common.go
generated
vendored
Normal file
230
vendor/github.com/tdewolff/parse/common.go
generated
vendored
Normal file
|
@ -0,0 +1,230 @@
|
||||||
|
// Package parse contains a collection of parsers for various formats in its subpackages.
|
||||||
|
package parse
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrBadDataURI is returned by DataURI when the byte slice does not start with 'data:' or is too short.
|
||||||
|
var ErrBadDataURI = errors.New("not a data URI")
|
||||||
|
|
||||||
|
// Number returns the number of bytes that parse as a number of the regex format (+|-)?([0-9]+(\.[0-9]+)?|\.[0-9]+)((e|E)(+|-)?[0-9]+)?.
|
||||||
|
func Number(b []byte) int {
|
||||||
|
if len(b) == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
i := 0
|
||||||
|
if b[i] == '+' || b[i] == '-' {
|
||||||
|
i++
|
||||||
|
if i >= len(b) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
firstDigit := (b[i] >= '0' && b[i] <= '9')
|
||||||
|
if firstDigit {
|
||||||
|
i++
|
||||||
|
for i < len(b) && b[i] >= '0' && b[i] <= '9' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if i < len(b) && b[i] == '.' {
|
||||||
|
i++
|
||||||
|
if i < len(b) && b[i] >= '0' && b[i] <= '9' {
|
||||||
|
i++
|
||||||
|
for i < len(b) && b[i] >= '0' && b[i] <= '9' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
} else if firstDigit {
|
||||||
|
// . could belong to the next token
|
||||||
|
i--
|
||||||
|
return i
|
||||||
|
} else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
} else if !firstDigit {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
iOld := i
|
||||||
|
if i < len(b) && (b[i] == 'e' || b[i] == 'E') {
|
||||||
|
i++
|
||||||
|
if i < len(b) && (b[i] == '+' || b[i] == '-') {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i >= len(b) || b[i] < '0' || b[i] > '9' {
|
||||||
|
// e could belong to next token
|
||||||
|
return iOld
|
||||||
|
}
|
||||||
|
for i < len(b) && b[i] >= '0' && b[i] <= '9' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dimension parses a byte-slice and returns the length of the number and its unit.
|
||||||
|
func Dimension(b []byte) (int, int) {
|
||||||
|
num := Number(b)
|
||||||
|
if num == 0 || num == len(b) {
|
||||||
|
return num, 0
|
||||||
|
} else if b[num] == '%' {
|
||||||
|
return num, 1
|
||||||
|
} else if b[num] >= 'a' && b[num] <= 'z' || b[num] >= 'A' && b[num] <= 'Z' {
|
||||||
|
i := num + 1
|
||||||
|
for i < len(b) && (b[i] >= 'a' && b[i] <= 'z' || b[i] >= 'A' && b[i] <= 'Z') {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return num, i - num
|
||||||
|
}
|
||||||
|
return num, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mediatype parses a given mediatype and splits the mimetype from the parameters.
|
||||||
|
// It works similar to mime.ParseMediaType but is faster.
|
||||||
|
func Mediatype(b []byte) ([]byte, map[string]string) {
|
||||||
|
i := 0
|
||||||
|
for i < len(b) && b[i] == ' ' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
b = b[i:]
|
||||||
|
n := len(b)
|
||||||
|
mimetype := b
|
||||||
|
var params map[string]string
|
||||||
|
for i := 3; i < n; i++ { // mimetype is at least three characters long
|
||||||
|
if b[i] == ';' || b[i] == ' ' {
|
||||||
|
mimetype = b[:i]
|
||||||
|
if b[i] == ' ' {
|
||||||
|
i++
|
||||||
|
for i < n && b[i] == ' ' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i < n && b[i] != ';' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
params = map[string]string{}
|
||||||
|
s := string(b)
|
||||||
|
PARAM:
|
||||||
|
i++
|
||||||
|
for i < n && s[i] == ' ' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
start := i
|
||||||
|
for i < n && s[i] != '=' && s[i] != ';' && s[i] != ' ' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
key := s[start:i]
|
||||||
|
for i < n && s[i] == ' ' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i < n && s[i] == '=' {
|
||||||
|
i++
|
||||||
|
for i < n && s[i] == ' ' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
start = i
|
||||||
|
for i < n && s[i] != ';' && s[i] != ' ' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
start = i
|
||||||
|
}
|
||||||
|
params[key] = s[start:i]
|
||||||
|
for i < n && s[i] == ' ' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i < n && s[i] == ';' {
|
||||||
|
goto PARAM
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mimetype, params
|
||||||
|
}
|
||||||
|
|
||||||
|
// DataURI parses the given data URI and returns the mediatype, data and ok.
|
||||||
|
func DataURI(dataURI []byte) ([]byte, []byte, error) {
|
||||||
|
if len(dataURI) > 5 && Equal(dataURI[:5], []byte("data:")) {
|
||||||
|
dataURI = dataURI[5:]
|
||||||
|
inBase64 := false
|
||||||
|
var mediatype []byte
|
||||||
|
i := 0
|
||||||
|
for j := 0; j < len(dataURI); j++ {
|
||||||
|
c := dataURI[j]
|
||||||
|
if c == '=' || c == ';' || c == ',' {
|
||||||
|
if c != '=' && Equal(TrimWhitespace(dataURI[i:j]), []byte("base64")) {
|
||||||
|
if len(mediatype) > 0 {
|
||||||
|
mediatype = mediatype[:len(mediatype)-1]
|
||||||
|
}
|
||||||
|
inBase64 = true
|
||||||
|
i = j
|
||||||
|
} else if c != ',' {
|
||||||
|
mediatype = append(append(mediatype, TrimWhitespace(dataURI[i:j])...), c)
|
||||||
|
i = j + 1
|
||||||
|
} else {
|
||||||
|
mediatype = append(mediatype, TrimWhitespace(dataURI[i:j])...)
|
||||||
|
}
|
||||||
|
if c == ',' {
|
||||||
|
if len(mediatype) == 0 || mediatype[0] == ';' {
|
||||||
|
mediatype = []byte("text/plain")
|
||||||
|
}
|
||||||
|
data := dataURI[j+1:]
|
||||||
|
if inBase64 {
|
||||||
|
decoded := make([]byte, base64.StdEncoding.DecodedLen(len(data)))
|
||||||
|
n, err := base64.StdEncoding.Decode(decoded, data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
data = decoded[:n]
|
||||||
|
} else if unescaped, err := url.QueryUnescape(string(data)); err == nil {
|
||||||
|
data = []byte(unescaped)
|
||||||
|
}
|
||||||
|
return mediatype, data, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil, ErrBadDataURI
|
||||||
|
}
|
||||||
|
|
||||||
|
// QuoteEntity parses the given byte slice and returns the quote that got matched (' or ") and its entity length.
|
||||||
|
func QuoteEntity(b []byte) (quote byte, n int) {
|
||||||
|
if len(b) < 5 || b[0] != '&' {
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
if b[1] == '#' {
|
||||||
|
if b[2] == 'x' {
|
||||||
|
i := 3
|
||||||
|
for i < len(b) && b[i] == '0' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i+2 < len(b) && b[i] == '2' && b[i+2] == ';' {
|
||||||
|
if b[i+1] == '2' {
|
||||||
|
return '"', i + 3 // "
|
||||||
|
} else if b[i+1] == '7' {
|
||||||
|
return '\'', i + 3 // '
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
i := 2
|
||||||
|
for i < len(b) && b[i] == '0' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i+2 < len(b) && b[i] == '3' && b[i+2] == ';' {
|
||||||
|
if b[i+1] == '4' {
|
||||||
|
return '"', i + 3 // "
|
||||||
|
} else if b[i+1] == '9' {
|
||||||
|
return '\'', i + 3 // '
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if len(b) >= 6 && b[5] == ';' {
|
||||||
|
if EqualFold(b[1:5], []byte{'q', 'u', 'o', 't'}) {
|
||||||
|
return '"', 6 // "
|
||||||
|
} else if EqualFold(b[1:5], []byte{'a', 'p', 'o', 's'}) {
|
||||||
|
return '\'', 6 // '
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, 0
|
||||||
|
}
|
171
vendor/github.com/tdewolff/parse/css/README.md
generated
vendored
Normal file
171
vendor/github.com/tdewolff/parse/css/README.md
generated
vendored
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
# CSS [![GoDoc](http://godoc.org/github.com/tdewolff/parse/css?status.svg)](http://godoc.org/github.com/tdewolff/parse/css) [![GoCover](http://gocover.io/_badge/github.com/tdewolff/parse/css)](http://gocover.io/github.com/tdewolff/parse/css)
|
||||||
|
|
||||||
|
This package is a CSS3 lexer and parser written in [Go][1]. Both follow the specification at [CSS Syntax Module Level 3](http://www.w3.org/TR/css-syntax-3/). The lexer takes an io.Reader and converts it into tokens until the EOF. The parser returns a parse tree of the full io.Reader input stream, but the low-level `Next` function can be used for stream parsing to returns grammar units until the EOF.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
Run the following command
|
||||||
|
|
||||||
|
go get github.com/tdewolff/parse/css
|
||||||
|
|
||||||
|
or add the following import and run project with `go get`
|
||||||
|
|
||||||
|
import "github.com/tdewolff/parse/css"
|
||||||
|
|
||||||
|
## Lexer
|
||||||
|
### Usage
|
||||||
|
The following initializes a new Lexer with io.Reader `r`:
|
||||||
|
``` go
|
||||||
|
l := css.NewLexer(r)
|
||||||
|
```
|
||||||
|
|
||||||
|
To tokenize until EOF an error, use:
|
||||||
|
``` go
|
||||||
|
for {
|
||||||
|
tt, text := l.Next()
|
||||||
|
switch tt {
|
||||||
|
case css.ErrorToken:
|
||||||
|
// error or EOF set in l.Err()
|
||||||
|
return
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
All tokens (see [CSS Syntax Module Level 3](http://www.w3.org/TR/css3-syntax/)):
|
||||||
|
``` go
|
||||||
|
ErrorToken // non-official token, returned when errors occur
|
||||||
|
IdentToken
|
||||||
|
FunctionToken // rgb( rgba( ...
|
||||||
|
AtKeywordToken // @abc
|
||||||
|
HashToken // #abc
|
||||||
|
StringToken
|
||||||
|
BadStringToken
|
||||||
|
UrlToken // url(
|
||||||
|
BadUrlToken
|
||||||
|
DelimToken // any unmatched character
|
||||||
|
NumberToken // 5
|
||||||
|
PercentageToken // 5%
|
||||||
|
DimensionToken // 5em
|
||||||
|
UnicodeRangeToken
|
||||||
|
IncludeMatchToken // ~=
|
||||||
|
DashMatchToken // |=
|
||||||
|
PrefixMatchToken // ^=
|
||||||
|
SuffixMatchToken // $=
|
||||||
|
SubstringMatchToken // *=
|
||||||
|
ColumnToken // ||
|
||||||
|
WhitespaceToken
|
||||||
|
CDOToken // <!--
|
||||||
|
CDCToken // -->
|
||||||
|
ColonToken
|
||||||
|
SemicolonToken
|
||||||
|
CommaToken
|
||||||
|
BracketToken // ( ) [ ] { }, all bracket tokens use this, Data() can distinguish between the brackets
|
||||||
|
CommentToken // non-official token
|
||||||
|
```
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
``` go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/tdewolff/parse/css"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Tokenize CSS3 from stdin.
|
||||||
|
func main() {
|
||||||
|
l := css.NewLexer(os.Stdin)
|
||||||
|
for {
|
||||||
|
tt, text := l.Next()
|
||||||
|
switch tt {
|
||||||
|
case css.ErrorToken:
|
||||||
|
if l.Err() != io.EOF {
|
||||||
|
fmt.Println("Error on line", l.Line(), ":", l.Err())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
case css.IdentToken:
|
||||||
|
fmt.Println("Identifier", string(text))
|
||||||
|
case css.NumberToken:
|
||||||
|
fmt.Println("Number", string(text))
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Parser
|
||||||
|
### Usage
|
||||||
|
The following creates a new Parser.
|
||||||
|
``` go
|
||||||
|
// false because this is the content of an inline style attribute
|
||||||
|
p := css.NewParser(bytes.NewBufferString("color: red;"), false)
|
||||||
|
```
|
||||||
|
|
||||||
|
To iterate over the stylesheet, use:
|
||||||
|
``` go
|
||||||
|
for {
|
||||||
|
gt, _, data := p.Next()
|
||||||
|
if gt == css.ErrorGrammar {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
All grammar units returned by `Next`:
|
||||||
|
``` go
|
||||||
|
ErrorGrammar
|
||||||
|
AtRuleGrammar
|
||||||
|
EndAtRuleGrammar
|
||||||
|
RulesetGrammar
|
||||||
|
EndRulesetGrammar
|
||||||
|
DeclarationGrammar
|
||||||
|
TokenGrammar
|
||||||
|
```
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
``` go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/tdewolff/parse/css"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// false because this is the content of an inline style attribute
|
||||||
|
p := css.NewParser(bytes.NewBufferString("color: red;"), false)
|
||||||
|
out := ""
|
||||||
|
for {
|
||||||
|
gt, _, data := p.Next()
|
||||||
|
if gt == css.ErrorGrammar {
|
||||||
|
break
|
||||||
|
} else if gt == css.AtRuleGrammar || gt == css.BeginAtRuleGrammar || gt == css.BeginRulesetGrammar || gt == css.DeclarationGrammar {
|
||||||
|
out += string(data)
|
||||||
|
if gt == css.DeclarationGrammar {
|
||||||
|
out += ":"
|
||||||
|
}
|
||||||
|
for _, val := range p.Values() {
|
||||||
|
out += string(val.Data)
|
||||||
|
}
|
||||||
|
if gt == css.BeginAtRuleGrammar || gt == css.BeginRulesetGrammar {
|
||||||
|
out += "{"
|
||||||
|
} else if gt == css.AtRuleGrammar || gt == css.DeclarationGrammar {
|
||||||
|
out += ";"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out += string(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
Released under the [MIT license](https://github.com/tdewolff/parse/blob/master/LICENSE.md).
|
||||||
|
|
||||||
|
[1]: http://golang.org/ "Go Language"
|
676
vendor/github.com/tdewolff/parse/css/hash.go
generated
vendored
Normal file
676
vendor/github.com/tdewolff/parse/css/hash.go
generated
vendored
Normal file
|
@ -0,0 +1,676 @@
|
||||||
|
package css
|
||||||
|
|
||||||
|
// generated by hasher -file hash.go -type Hash; DO NOT EDIT, except for adding more constants to the list and rerun go generate
|
||||||
|
|
||||||
|
// uses github.com/tdewolff/hasher
|
||||||
|
//go:generate hasher -type=Hash -file=hash.go
|
||||||
|
|
||||||
|
// Hash defines perfect hashes for a predefined list of strings
|
||||||
|
type Hash uint32
|
||||||
|
|
||||||
|
// Unique hash definitions to be used instead of strings
|
||||||
|
const (
|
||||||
|
Accelerator Hash = 0x47f0b // accelerator
|
||||||
|
Aliceblue Hash = 0x52509 // aliceblue
|
||||||
|
Alpha Hash = 0x5af05 // alpha
|
||||||
|
Antiquewhite Hash = 0x45c0c // antiquewhite
|
||||||
|
Aquamarine Hash = 0x7020a // aquamarine
|
||||||
|
Azimuth Hash = 0x5b307 // azimuth
|
||||||
|
Background Hash = 0xa // background
|
||||||
|
Background_Attachment Hash = 0x3a15 // background-attachment
|
||||||
|
Background_Color Hash = 0x11c10 // background-color
|
||||||
|
Background_Image Hash = 0x99210 // background-image
|
||||||
|
Background_Position Hash = 0x13 // background-position
|
||||||
|
Background_Position_X Hash = 0x80815 // background-position-x
|
||||||
|
Background_Position_Y Hash = 0x15 // background-position-y
|
||||||
|
Background_Repeat Hash = 0x1511 // background-repeat
|
||||||
|
Behavior Hash = 0x3108 // behavior
|
||||||
|
Black Hash = 0x6005 // black
|
||||||
|
Blanchedalmond Hash = 0x650e // blanchedalmond
|
||||||
|
Blueviolet Hash = 0x52a0a // blueviolet
|
||||||
|
Bold Hash = 0x7a04 // bold
|
||||||
|
Border Hash = 0x8506 // border
|
||||||
|
Border_Bottom Hash = 0x850d // border-bottom
|
||||||
|
Border_Bottom_Color Hash = 0x8513 // border-bottom-color
|
||||||
|
Border_Bottom_Style Hash = 0xbe13 // border-bottom-style
|
||||||
|
Border_Bottom_Width Hash = 0xe113 // border-bottom-width
|
||||||
|
Border_Collapse Hash = 0x1020f // border-collapse
|
||||||
|
Border_Color Hash = 0x1350c // border-color
|
||||||
|
Border_Left Hash = 0x15c0b // border-left
|
||||||
|
Border_Left_Color Hash = 0x15c11 // border-left-color
|
||||||
|
Border_Left_Style Hash = 0x17911 // border-left-style
|
||||||
|
Border_Left_Width Hash = 0x18a11 // border-left-width
|
||||||
|
Border_Right Hash = 0x19b0c // border-right
|
||||||
|
Border_Right_Color Hash = 0x19b12 // border-right-color
|
||||||
|
Border_Right_Style Hash = 0x1ad12 // border-right-style
|
||||||
|
Border_Right_Width Hash = 0x1bf12 // border-right-width
|
||||||
|
Border_Spacing Hash = 0x1d10e // border-spacing
|
||||||
|
Border_Style Hash = 0x1f40c // border-style
|
||||||
|
Border_Top Hash = 0x2000a // border-top
|
||||||
|
Border_Top_Color Hash = 0x20010 // border-top-color
|
||||||
|
Border_Top_Style Hash = 0x21010 // border-top-style
|
||||||
|
Border_Top_Width Hash = 0x22010 // border-top-width
|
||||||
|
Border_Width Hash = 0x2300c // border-width
|
||||||
|
Bottom Hash = 0x8c06 // bottom
|
||||||
|
Burlywood Hash = 0x23c09 // burlywood
|
||||||
|
Cadetblue Hash = 0x25809 // cadetblue
|
||||||
|
Caption_Side Hash = 0x2610c // caption-side
|
||||||
|
Charset Hash = 0x44207 // charset
|
||||||
|
Chartreuse Hash = 0x2730a // chartreuse
|
||||||
|
Chocolate Hash = 0x27d09 // chocolate
|
||||||
|
Clear Hash = 0x2ab05 // clear
|
||||||
|
Clip Hash = 0x2b004 // clip
|
||||||
|
Color Hash = 0x9305 // color
|
||||||
|
Content Hash = 0x2e507 // content
|
||||||
|
Cornflowerblue Hash = 0x2ff0e // cornflowerblue
|
||||||
|
Cornsilk Hash = 0x30d08 // cornsilk
|
||||||
|
Counter_Increment Hash = 0x31511 // counter-increment
|
||||||
|
Counter_Reset Hash = 0x3540d // counter-reset
|
||||||
|
Cue Hash = 0x36103 // cue
|
||||||
|
Cue_After Hash = 0x36109 // cue-after
|
||||||
|
Cue_Before Hash = 0x36a0a // cue-before
|
||||||
|
Cursive Hash = 0x37b07 // cursive
|
||||||
|
Cursor Hash = 0x38e06 // cursor
|
||||||
|
Darkblue Hash = 0x7208 // darkblue
|
||||||
|
Darkcyan Hash = 0x7d08 // darkcyan
|
||||||
|
Darkgoldenrod Hash = 0x2440d // darkgoldenrod
|
||||||
|
Darkgray Hash = 0x25008 // darkgray
|
||||||
|
Darkgreen Hash = 0x79209 // darkgreen
|
||||||
|
Darkkhaki Hash = 0x88509 // darkkhaki
|
||||||
|
Darkmagenta Hash = 0x4f40b // darkmagenta
|
||||||
|
Darkolivegreen Hash = 0x7210e // darkolivegreen
|
||||||
|
Darkorange Hash = 0x7860a // darkorange
|
||||||
|
Darkorchid Hash = 0x87c0a // darkorchid
|
||||||
|
Darksalmon Hash = 0x8c00a // darksalmon
|
||||||
|
Darkseagreen Hash = 0x9240c // darkseagreen
|
||||||
|
Darkslateblue Hash = 0x3940d // darkslateblue
|
||||||
|
Darkslategray Hash = 0x3a10d // darkslategray
|
||||||
|
Darkturquoise Hash = 0x3ae0d // darkturquoise
|
||||||
|
Darkviolet Hash = 0x3bb0a // darkviolet
|
||||||
|
Deeppink Hash = 0x26b08 // deeppink
|
||||||
|
Deepskyblue Hash = 0x8930b // deepskyblue
|
||||||
|
Default Hash = 0x57b07 // default
|
||||||
|
Direction Hash = 0x9f109 // direction
|
||||||
|
Display Hash = 0x3c507 // display
|
||||||
|
Document Hash = 0x3d308 // document
|
||||||
|
Dodgerblue Hash = 0x3db0a // dodgerblue
|
||||||
|
Elevation Hash = 0x4a009 // elevation
|
||||||
|
Empty_Cells Hash = 0x4c20b // empty-cells
|
||||||
|
Fantasy Hash = 0x5ce07 // fantasy
|
||||||
|
Filter Hash = 0x59806 // filter
|
||||||
|
Firebrick Hash = 0x3e509 // firebrick
|
||||||
|
Float Hash = 0x3ee05 // float
|
||||||
|
Floralwhite Hash = 0x3f30b // floralwhite
|
||||||
|
Font Hash = 0xd804 // font
|
||||||
|
Font_Face Hash = 0xd809 // font-face
|
||||||
|
Font_Family Hash = 0x41d0b // font-family
|
||||||
|
Font_Size Hash = 0x42809 // font-size
|
||||||
|
Font_Size_Adjust Hash = 0x42810 // font-size-adjust
|
||||||
|
Font_Stretch Hash = 0x4380c // font-stretch
|
||||||
|
Font_Style Hash = 0x4490a // font-style
|
||||||
|
Font_Variant Hash = 0x4530c // font-variant
|
||||||
|
Font_Weight Hash = 0x46e0b // font-weight
|
||||||
|
Forestgreen Hash = 0x3700b // forestgreen
|
||||||
|
Fuchsia Hash = 0x47907 // fuchsia
|
||||||
|
Gainsboro Hash = 0x14c09 // gainsboro
|
||||||
|
Ghostwhite Hash = 0x1de0a // ghostwhite
|
||||||
|
Goldenrod Hash = 0x24809 // goldenrod
|
||||||
|
Greenyellow Hash = 0x7960b // greenyellow
|
||||||
|
Height Hash = 0x68506 // height
|
||||||
|
Honeydew Hash = 0x5b908 // honeydew
|
||||||
|
Hsl Hash = 0xf303 // hsl
|
||||||
|
Hsla Hash = 0xf304 // hsla
|
||||||
|
Ime_Mode Hash = 0x88d08 // ime-mode
|
||||||
|
Import Hash = 0x4e306 // import
|
||||||
|
Important Hash = 0x4e309 // important
|
||||||
|
Include_Source Hash = 0x7f20e // include-source
|
||||||
|
Indianred Hash = 0x4ec09 // indianred
|
||||||
|
Inherit Hash = 0x51907 // inherit
|
||||||
|
Initial Hash = 0x52007 // initial
|
||||||
|
Keyframes Hash = 0x40109 // keyframes
|
||||||
|
Lavender Hash = 0xf508 // lavender
|
||||||
|
Lavenderblush Hash = 0xf50d // lavenderblush
|
||||||
|
Lawngreen Hash = 0x4da09 // lawngreen
|
||||||
|
Layer_Background_Color Hash = 0x11616 // layer-background-color
|
||||||
|
Layer_Background_Image Hash = 0x98c16 // layer-background-image
|
||||||
|
Layout_Flow Hash = 0x5030b // layout-flow
|
||||||
|
Layout_Grid Hash = 0x53f0b // layout-grid
|
||||||
|
Layout_Grid_Char Hash = 0x53f10 // layout-grid-char
|
||||||
|
Layout_Grid_Char_Spacing Hash = 0x53f18 // layout-grid-char-spacing
|
||||||
|
Layout_Grid_Line Hash = 0x55710 // layout-grid-line
|
||||||
|
Layout_Grid_Mode Hash = 0x56d10 // layout-grid-mode
|
||||||
|
Layout_Grid_Type Hash = 0x58210 // layout-grid-type
|
||||||
|
Left Hash = 0x16304 // left
|
||||||
|
Lemonchiffon Hash = 0xcf0c // lemonchiffon
|
||||||
|
Letter_Spacing Hash = 0x5310e // letter-spacing
|
||||||
|
Lightblue Hash = 0x59e09 // lightblue
|
||||||
|
Lightcoral Hash = 0x5a70a // lightcoral
|
||||||
|
Lightcyan Hash = 0x5d509 // lightcyan
|
||||||
|
Lightgoldenrodyellow Hash = 0x5de14 // lightgoldenrodyellow
|
||||||
|
Lightgray Hash = 0x60509 // lightgray
|
||||||
|
Lightgreen Hash = 0x60e0a // lightgreen
|
||||||
|
Lightpink Hash = 0x61809 // lightpink
|
||||||
|
Lightsalmon Hash = 0x6210b // lightsalmon
|
||||||
|
Lightseagreen Hash = 0x62c0d // lightseagreen
|
||||||
|
Lightskyblue Hash = 0x6390c // lightskyblue
|
||||||
|
Lightslateblue Hash = 0x6450e // lightslateblue
|
||||||
|
Lightsteelblue Hash = 0x6530e // lightsteelblue
|
||||||
|
Lightyellow Hash = 0x6610b // lightyellow
|
||||||
|
Limegreen Hash = 0x67709 // limegreen
|
||||||
|
Line_Break Hash = 0x5630a // line-break
|
||||||
|
Line_Height Hash = 0x6800b // line-height
|
||||||
|
List_Style Hash = 0x68b0a // list-style
|
||||||
|
List_Style_Image Hash = 0x68b10 // list-style-image
|
||||||
|
List_Style_Position Hash = 0x69b13 // list-style-position
|
||||||
|
List_Style_Type Hash = 0x6ae0f // list-style-type
|
||||||
|
Magenta Hash = 0x4f807 // magenta
|
||||||
|
Margin Hash = 0x2c006 // margin
|
||||||
|
Margin_Bottom Hash = 0x2c00d // margin-bottom
|
||||||
|
Margin_Left Hash = 0x2cc0b // margin-left
|
||||||
|
Margin_Right Hash = 0x3320c // margin-right
|
||||||
|
Margin_Top Hash = 0x7cd0a // margin-top
|
||||||
|
Marker_Offset Hash = 0x6bd0d // marker-offset
|
||||||
|
Marks Hash = 0x6ca05 // marks
|
||||||
|
Max_Height Hash = 0x6e90a // max-height
|
||||||
|
Max_Width Hash = 0x6f309 // max-width
|
||||||
|
Media Hash = 0xa1405 // media
|
||||||
|
Mediumaquamarine Hash = 0x6fc10 // mediumaquamarine
|
||||||
|
Mediumblue Hash = 0x70c0a // mediumblue
|
||||||
|
Mediumorchid Hash = 0x7160c // mediumorchid
|
||||||
|
Mediumpurple Hash = 0x72f0c // mediumpurple
|
||||||
|
Mediumseagreen Hash = 0x73b0e // mediumseagreen
|
||||||
|
Mediumslateblue Hash = 0x7490f // mediumslateblue
|
||||||
|
Mediumspringgreen Hash = 0x75811 // mediumspringgreen
|
||||||
|
Mediumturquoise Hash = 0x7690f // mediumturquoise
|
||||||
|
Mediumvioletred Hash = 0x7780f // mediumvioletred
|
||||||
|
Midnightblue Hash = 0x7a60c // midnightblue
|
||||||
|
Min_Height Hash = 0x7b20a // min-height
|
||||||
|
Min_Width Hash = 0x7bc09 // min-width
|
||||||
|
Mintcream Hash = 0x7c509 // mintcream
|
||||||
|
Mistyrose Hash = 0x7e309 // mistyrose
|
||||||
|
Moccasin Hash = 0x7ec08 // moccasin
|
||||||
|
Monospace Hash = 0x8c709 // monospace
|
||||||
|
Namespace Hash = 0x49809 // namespace
|
||||||
|
Navajowhite Hash = 0x4a80b // navajowhite
|
||||||
|
None Hash = 0x4bf04 // none
|
||||||
|
Normal Hash = 0x4d506 // normal
|
||||||
|
Olivedrab Hash = 0x80009 // olivedrab
|
||||||
|
Orangered Hash = 0x78a09 // orangered
|
||||||
|
Orphans Hash = 0x48807 // orphans
|
||||||
|
Outline Hash = 0x81d07 // outline
|
||||||
|
Outline_Color Hash = 0x81d0d // outline-color
|
||||||
|
Outline_Style Hash = 0x82a0d // outline-style
|
||||||
|
Outline_Width Hash = 0x8370d // outline-width
|
||||||
|
Overflow Hash = 0x2db08 // overflow
|
||||||
|
Overflow_X Hash = 0x2db0a // overflow-x
|
||||||
|
Overflow_Y Hash = 0x8440a // overflow-y
|
||||||
|
Padding Hash = 0x2b307 // padding
|
||||||
|
Padding_Bottom Hash = 0x2b30e // padding-bottom
|
||||||
|
Padding_Left Hash = 0x5f90c // padding-left
|
||||||
|
Padding_Right Hash = 0x7d60d // padding-right
|
||||||
|
Padding_Top Hash = 0x8d90b // padding-top
|
||||||
|
Page Hash = 0x84e04 // page
|
||||||
|
Page_Break_After Hash = 0x8e310 // page-break-after
|
||||||
|
Page_Break_Before Hash = 0x84e11 // page-break-before
|
||||||
|
Page_Break_Inside Hash = 0x85f11 // page-break-inside
|
||||||
|
Palegoldenrod Hash = 0x8700d // palegoldenrod
|
||||||
|
Palegreen Hash = 0x89e09 // palegreen
|
||||||
|
Paleturquoise Hash = 0x8a70d // paleturquoise
|
||||||
|
Palevioletred Hash = 0x8b40d // palevioletred
|
||||||
|
Papayawhip Hash = 0x8d00a // papayawhip
|
||||||
|
Pause Hash = 0x8f305 // pause
|
||||||
|
Pause_After Hash = 0x8f30b // pause-after
|
||||||
|
Pause_Before Hash = 0x8fe0c // pause-before
|
||||||
|
Peachpuff Hash = 0x59009 // peachpuff
|
||||||
|
Pitch Hash = 0x90a05 // pitch
|
||||||
|
Pitch_Range Hash = 0x90a0b // pitch-range
|
||||||
|
Play_During Hash = 0x3c80b // play-during
|
||||||
|
Position Hash = 0xb08 // position
|
||||||
|
Powderblue Hash = 0x9150a // powderblue
|
||||||
|
Progid Hash = 0x91f06 // progid
|
||||||
|
Quotes Hash = 0x93006 // quotes
|
||||||
|
Rgb Hash = 0x3803 // rgb
|
||||||
|
Rgba Hash = 0x3804 // rgba
|
||||||
|
Richness Hash = 0x9708 // richness
|
||||||
|
Right Hash = 0x1a205 // right
|
||||||
|
Rosybrown Hash = 0x15309 // rosybrown
|
||||||
|
Royalblue Hash = 0xb509 // royalblue
|
||||||
|
Ruby_Align Hash = 0x12b0a // ruby-align
|
||||||
|
Ruby_Overhang Hash = 0x1400d // ruby-overhang
|
||||||
|
Ruby_Position Hash = 0x16c0d // ruby-position
|
||||||
|
Saddlebrown Hash = 0x48e0b // saddlebrown
|
||||||
|
Sandybrown Hash = 0x4cc0a // sandybrown
|
||||||
|
Sans_Serif Hash = 0x5c50a // sans-serif
|
||||||
|
Scrollbar_3d_Light_Color Hash = 0x9e18 // scrollbar-3d-light-color
|
||||||
|
Scrollbar_Arrow_Color Hash = 0x29615 // scrollbar-arrow-color
|
||||||
|
Scrollbar_Base_Color Hash = 0x40914 // scrollbar-base-color
|
||||||
|
Scrollbar_Dark_Shadow_Color Hash = 0x6ce1b // scrollbar-dark-shadow-color
|
||||||
|
Scrollbar_Face_Color Hash = 0x93514 // scrollbar-face-color
|
||||||
|
Scrollbar_Highlight_Color Hash = 0x9ce19 // scrollbar-highlight-color
|
||||||
|
Scrollbar_Shadow_Color Hash = 0x94916 // scrollbar-shadow-color
|
||||||
|
Scrollbar_Track_Color Hash = 0x95f15 // scrollbar-track-color
|
||||||
|
Seagreen Hash = 0x63108 // seagreen
|
||||||
|
Seashell Hash = 0x10f08 // seashell
|
||||||
|
Serif Hash = 0x5ca05 // serif
|
||||||
|
Size Hash = 0x42d04 // size
|
||||||
|
Slateblue Hash = 0x39809 // slateblue
|
||||||
|
Slategray Hash = 0x3a509 // slategray
|
||||||
|
Speak Hash = 0x97405 // speak
|
||||||
|
Speak_Header Hash = 0x9740c // speak-header
|
||||||
|
Speak_Numeral Hash = 0x9800d // speak-numeral
|
||||||
|
Speak_Punctuation Hash = 0x9a211 // speak-punctuation
|
||||||
|
Speech_Rate Hash = 0x9b30b // speech-rate
|
||||||
|
Springgreen Hash = 0x75e0b // springgreen
|
||||||
|
Steelblue Hash = 0x65809 // steelblue
|
||||||
|
Stress Hash = 0x29106 // stress
|
||||||
|
Supports Hash = 0x9c708 // supports
|
||||||
|
Table_Layout Hash = 0x4fd0c // table-layout
|
||||||
|
Text_Align Hash = 0x2840a // text-align
|
||||||
|
Text_Align_Last Hash = 0x2840f // text-align-last
|
||||||
|
Text_Autospace Hash = 0x1e60e // text-autospace
|
||||||
|
Text_Decoration Hash = 0x4b10f // text-decoration
|
||||||
|
Text_Indent Hash = 0x9bc0b // text-indent
|
||||||
|
Text_Justify Hash = 0x250c // text-justify
|
||||||
|
Text_Kashida_Space Hash = 0x4e12 // text-kashida-space
|
||||||
|
Text_Overflow Hash = 0x2d60d // text-overflow
|
||||||
|
Text_Shadow Hash = 0x2eb0b // text-shadow
|
||||||
|
Text_Transform Hash = 0x3250e // text-transform
|
||||||
|
Text_Underline_Position Hash = 0x33d17 // text-underline-position
|
||||||
|
Top Hash = 0x20703 // top
|
||||||
|
Turquoise Hash = 0x3b209 // turquoise
|
||||||
|
Unicode_Bidi Hash = 0x9e70c // unicode-bidi
|
||||||
|
Vertical_Align Hash = 0x3800e // vertical-align
|
||||||
|
Visibility Hash = 0x9fa0a // visibility
|
||||||
|
Voice_Family Hash = 0xa040c // voice-family
|
||||||
|
Volume Hash = 0xa1006 // volume
|
||||||
|
White Hash = 0x1e305 // white
|
||||||
|
White_Space Hash = 0x4630b // white-space
|
||||||
|
Whitesmoke Hash = 0x3f90a // whitesmoke
|
||||||
|
Widows Hash = 0x5c006 // widows
|
||||||
|
Width Hash = 0xef05 // width
|
||||||
|
Word_Break Hash = 0x2f50a // word-break
|
||||||
|
Word_Spacing Hash = 0x50d0c // word-spacing
|
||||||
|
Word_Wrap Hash = 0x5f109 // word-wrap
|
||||||
|
Writing_Mode Hash = 0x66b0c // writing-mode
|
||||||
|
Yellow Hash = 0x5ec06 // yellow
|
||||||
|
Yellowgreen Hash = 0x79b0b // yellowgreen
|
||||||
|
Z_Index Hash = 0xa1907 // z-index
|
||||||
|
)
|
||||||
|
|
||||||
|
// String returns the hash' name.
|
||||||
|
func (i Hash) String() string {
|
||||||
|
start := uint32(i >> 8)
|
||||||
|
n := uint32(i & 0xff)
|
||||||
|
if start+n > uint32(len(_Hash_text)) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return _Hash_text[start : start+n]
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToHash returns the hash whose name is s. It returns zero if there is no
|
||||||
|
// such hash. It is case sensitive.
|
||||||
|
func ToHash(s []byte) Hash {
|
||||||
|
if len(s) == 0 || len(s) > _Hash_maxLen {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
h := uint32(_Hash_hash0)
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
h ^= uint32(s[i])
|
||||||
|
h *= 16777619
|
||||||
|
}
|
||||||
|
if i := _Hash_table[h&uint32(len(_Hash_table)-1)]; int(i&0xff) == len(s) {
|
||||||
|
t := _Hash_text[i>>8 : i>>8+i&0xff]
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
if t[i] != s[i] {
|
||||||
|
goto NEXT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
NEXT:
|
||||||
|
if i := _Hash_table[(h>>16)&uint32(len(_Hash_table)-1)]; int(i&0xff) == len(s) {
|
||||||
|
t := _Hash_text[i>>8 : i>>8+i&0xff]
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
if t[i] != s[i] {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
const _Hash_hash0 = 0x700e0976
|
||||||
|
const _Hash_maxLen = 27
|
||||||
|
const _Hash_text = "background-position-ybackground-repeatext-justifybehaviorgba" +
|
||||||
|
"ckground-attachmentext-kashida-spaceblackblanchedalmondarkbl" +
|
||||||
|
"ueboldarkcyanborder-bottom-colorichnesscrollbar-3d-light-col" +
|
||||||
|
"oroyalblueborder-bottom-stylemonchiffont-faceborder-bottom-w" +
|
||||||
|
"idthslavenderblushborder-collapseashellayer-background-color" +
|
||||||
|
"uby-alignborder-coloruby-overhangainsborosybrownborder-left-" +
|
||||||
|
"coloruby-positionborder-left-styleborder-left-widthborder-ri" +
|
||||||
|
"ght-colorborder-right-styleborder-right-widthborder-spacingh" +
|
||||||
|
"ostwhitext-autospaceborder-styleborder-top-colorborder-top-s" +
|
||||||
|
"tyleborder-top-widthborder-widthburlywoodarkgoldenrodarkgray" +
|
||||||
|
"cadetbluecaption-sideeppinkchartreusechocolatext-align-lastr" +
|
||||||
|
"esscrollbar-arrow-colorclearclipadding-bottomargin-bottomarg" +
|
||||||
|
"in-leftext-overflow-xcontentext-shadoword-breakcornflowerblu" +
|
||||||
|
"ecornsilkcounter-incrementext-transformargin-rightext-underl" +
|
||||||
|
"ine-positioncounter-resetcue-aftercue-beforestgreencursivert" +
|
||||||
|
"ical-aligncursordarkslatebluedarkslategraydarkturquoisedarkv" +
|
||||||
|
"ioletdisplay-duringdocumentdodgerbluefirebrickfloatfloralwhi" +
|
||||||
|
"tesmokeyframescrollbar-base-colorfont-familyfont-size-adjust" +
|
||||||
|
"font-stretcharsetfont-stylefont-variantiquewhite-spacefont-w" +
|
||||||
|
"eightfuchsiacceleratorphansaddlebrownamespacelevationavajowh" +
|
||||||
|
"itext-decorationonempty-cellsandybrownormalawngreenimportant" +
|
||||||
|
"indianredarkmagentable-layout-floword-spacinginheritinitiali" +
|
||||||
|
"cebluevioletter-spacinglayout-grid-char-spacinglayout-grid-l" +
|
||||||
|
"ine-breaklayout-grid-modefaultlayout-grid-typeachpuffilterli" +
|
||||||
|
"ghtbluelightcoralphazimuthoneydewidowsans-serifantasylightcy" +
|
||||||
|
"anlightgoldenrodyelloword-wrapadding-leftlightgraylightgreen" +
|
||||||
|
"lightpinklightsalmonlightseagreenlightskybluelightslatebluel" +
|
||||||
|
"ightsteelbluelightyellowriting-modelimegreenline-heightlist-" +
|
||||||
|
"style-imagelist-style-positionlist-style-typemarker-offsetma" +
|
||||||
|
"rkscrollbar-dark-shadow-colormax-heightmax-widthmediumaquama" +
|
||||||
|
"rinemediumbluemediumorchidarkolivegreenmediumpurplemediumsea" +
|
||||||
|
"greenmediumslatebluemediumspringgreenmediumturquoisemediumvi" +
|
||||||
|
"oletredarkorangeredarkgreenyellowgreenmidnightbluemin-height" +
|
||||||
|
"min-widthmintcreamargin-topadding-rightmistyrosemoccasinclud" +
|
||||||
|
"e-sourceolivedrabackground-position-xoutline-coloroutline-st" +
|
||||||
|
"yleoutline-widthoverflow-ypage-break-beforepage-break-inside" +
|
||||||
|
"palegoldenrodarkorchidarkkhakime-modeepskybluepalegreenpalet" +
|
||||||
|
"urquoisepalevioletredarksalmonospacepapayawhipadding-topage-" +
|
||||||
|
"break-afterpause-afterpause-beforepitch-rangepowderblueprogi" +
|
||||||
|
"darkseagreenquotescrollbar-face-colorscrollbar-shadow-colors" +
|
||||||
|
"crollbar-track-colorspeak-headerspeak-numeralayer-background" +
|
||||||
|
"-imagespeak-punctuationspeech-ratext-indentsupportscrollbar-" +
|
||||||
|
"highlight-colorunicode-bidirectionvisibilityvoice-familyvolu" +
|
||||||
|
"mediaz-index"
|
||||||
|
|
||||||
|
var _Hash_table = [1 << 9]Hash{
|
||||||
|
0x0: 0x4cc0a, // sandybrown
|
||||||
|
0x1: 0x20703, // top
|
||||||
|
0x4: 0xb509, // royalblue
|
||||||
|
0x6: 0x4b10f, // text-decoration
|
||||||
|
0xb: 0x5030b, // layout-flow
|
||||||
|
0xc: 0x11c10, // background-color
|
||||||
|
0xd: 0x8c06, // bottom
|
||||||
|
0x10: 0x62c0d, // lightseagreen
|
||||||
|
0x11: 0x8930b, // deepskyblue
|
||||||
|
0x12: 0x39809, // slateblue
|
||||||
|
0x13: 0x4c20b, // empty-cells
|
||||||
|
0x14: 0x2b004, // clip
|
||||||
|
0x15: 0x70c0a, // mediumblue
|
||||||
|
0x16: 0x49809, // namespace
|
||||||
|
0x18: 0x2c00d, // margin-bottom
|
||||||
|
0x1a: 0x1350c, // border-color
|
||||||
|
0x1b: 0x5b908, // honeydew
|
||||||
|
0x1d: 0x2300c, // border-width
|
||||||
|
0x1e: 0x9740c, // speak-header
|
||||||
|
0x1f: 0x8b40d, // palevioletred
|
||||||
|
0x20: 0x1d10e, // border-spacing
|
||||||
|
0x22: 0x2b307, // padding
|
||||||
|
0x23: 0x3320c, // margin-right
|
||||||
|
0x27: 0x7bc09, // min-width
|
||||||
|
0x29: 0x60509, // lightgray
|
||||||
|
0x2a: 0x6610b, // lightyellow
|
||||||
|
0x2c: 0x8e310, // page-break-after
|
||||||
|
0x2d: 0x2e507, // content
|
||||||
|
0x30: 0x250c, // text-justify
|
||||||
|
0x32: 0x2840f, // text-align-last
|
||||||
|
0x34: 0x93514, // scrollbar-face-color
|
||||||
|
0x35: 0x40109, // keyframes
|
||||||
|
0x37: 0x4f807, // magenta
|
||||||
|
0x38: 0x3a509, // slategray
|
||||||
|
0x3a: 0x99210, // background-image
|
||||||
|
0x3c: 0x7f20e, // include-source
|
||||||
|
0x3d: 0x65809, // steelblue
|
||||||
|
0x3e: 0x81d0d, // outline-color
|
||||||
|
0x40: 0x1020f, // border-collapse
|
||||||
|
0x41: 0xf508, // lavender
|
||||||
|
0x42: 0x9c708, // supports
|
||||||
|
0x44: 0x6800b, // line-height
|
||||||
|
0x45: 0x9a211, // speak-punctuation
|
||||||
|
0x46: 0x9fa0a, // visibility
|
||||||
|
0x47: 0x2ab05, // clear
|
||||||
|
0x4b: 0x52a0a, // blueviolet
|
||||||
|
0x4e: 0x57b07, // default
|
||||||
|
0x50: 0x6bd0d, // marker-offset
|
||||||
|
0x52: 0x31511, // counter-increment
|
||||||
|
0x53: 0x6450e, // lightslateblue
|
||||||
|
0x54: 0x10f08, // seashell
|
||||||
|
0x56: 0x16c0d, // ruby-position
|
||||||
|
0x57: 0x82a0d, // outline-style
|
||||||
|
0x58: 0x63108, // seagreen
|
||||||
|
0x59: 0x9305, // color
|
||||||
|
0x5c: 0x2610c, // caption-side
|
||||||
|
0x5d: 0x68506, // height
|
||||||
|
0x5e: 0x7490f, // mediumslateblue
|
||||||
|
0x5f: 0x8fe0c, // pause-before
|
||||||
|
0x60: 0xcf0c, // lemonchiffon
|
||||||
|
0x63: 0x37b07, // cursive
|
||||||
|
0x66: 0x4a80b, // navajowhite
|
||||||
|
0x67: 0xa040c, // voice-family
|
||||||
|
0x68: 0x2440d, // darkgoldenrod
|
||||||
|
0x69: 0x3e509, // firebrick
|
||||||
|
0x6a: 0x4490a, // font-style
|
||||||
|
0x6b: 0x9f109, // direction
|
||||||
|
0x6d: 0x7860a, // darkorange
|
||||||
|
0x6f: 0x4530c, // font-variant
|
||||||
|
0x70: 0x2c006, // margin
|
||||||
|
0x71: 0x84e11, // page-break-before
|
||||||
|
0x73: 0x2d60d, // text-overflow
|
||||||
|
0x74: 0x4e12, // text-kashida-space
|
||||||
|
0x75: 0x30d08, // cornsilk
|
||||||
|
0x76: 0x46e0b, // font-weight
|
||||||
|
0x77: 0x42d04, // size
|
||||||
|
0x78: 0x53f0b, // layout-grid
|
||||||
|
0x79: 0x8d90b, // padding-top
|
||||||
|
0x7a: 0x44207, // charset
|
||||||
|
0x7d: 0x7e309, // mistyrose
|
||||||
|
0x7e: 0x5b307, // azimuth
|
||||||
|
0x7f: 0x8f30b, // pause-after
|
||||||
|
0x84: 0x38e06, // cursor
|
||||||
|
0x85: 0xf303, // hsl
|
||||||
|
0x86: 0x5310e, // letter-spacing
|
||||||
|
0x8b: 0x3d308, // document
|
||||||
|
0x8d: 0x36109, // cue-after
|
||||||
|
0x8f: 0x36a0a, // cue-before
|
||||||
|
0x91: 0x5ce07, // fantasy
|
||||||
|
0x94: 0x1400d, // ruby-overhang
|
||||||
|
0x95: 0x2b30e, // padding-bottom
|
||||||
|
0x9a: 0x59e09, // lightblue
|
||||||
|
0x9c: 0x8c00a, // darksalmon
|
||||||
|
0x9d: 0x42810, // font-size-adjust
|
||||||
|
0x9e: 0x61809, // lightpink
|
||||||
|
0xa0: 0x9240c, // darkseagreen
|
||||||
|
0xa2: 0x85f11, // page-break-inside
|
||||||
|
0xa4: 0x24809, // goldenrod
|
||||||
|
0xa6: 0xa1405, // media
|
||||||
|
0xa7: 0x53f18, // layout-grid-char-spacing
|
||||||
|
0xa9: 0x4e309, // important
|
||||||
|
0xaa: 0x7b20a, // min-height
|
||||||
|
0xb0: 0x15c11, // border-left-color
|
||||||
|
0xb1: 0x84e04, // page
|
||||||
|
0xb2: 0x98c16, // layer-background-image
|
||||||
|
0xb5: 0x55710, // layout-grid-line
|
||||||
|
0xb6: 0x1511, // background-repeat
|
||||||
|
0xb7: 0x8513, // border-bottom-color
|
||||||
|
0xb9: 0x25008, // darkgray
|
||||||
|
0xbb: 0x5f90c, // padding-left
|
||||||
|
0xbc: 0x1a205, // right
|
||||||
|
0xc0: 0x40914, // scrollbar-base-color
|
||||||
|
0xc1: 0x6530e, // lightsteelblue
|
||||||
|
0xc2: 0xef05, // width
|
||||||
|
0xc5: 0x3b209, // turquoise
|
||||||
|
0xc8: 0x3ee05, // float
|
||||||
|
0xca: 0x12b0a, // ruby-align
|
||||||
|
0xcb: 0xb08, // position
|
||||||
|
0xcc: 0x7cd0a, // margin-top
|
||||||
|
0xce: 0x2cc0b, // margin-left
|
||||||
|
0xcf: 0x2eb0b, // text-shadow
|
||||||
|
0xd0: 0x2f50a, // word-break
|
||||||
|
0xd4: 0x3f90a, // whitesmoke
|
||||||
|
0xd6: 0x33d17, // text-underline-position
|
||||||
|
0xd7: 0x1bf12, // border-right-width
|
||||||
|
0xd8: 0x80009, // olivedrab
|
||||||
|
0xd9: 0x89e09, // palegreen
|
||||||
|
0xdb: 0x4e306, // import
|
||||||
|
0xdc: 0x6ca05, // marks
|
||||||
|
0xdd: 0x3bb0a, // darkviolet
|
||||||
|
0xde: 0x13, // background-position
|
||||||
|
0xe0: 0x6fc10, // mediumaquamarine
|
||||||
|
0xe1: 0x7a04, // bold
|
||||||
|
0xe2: 0x7690f, // mediumturquoise
|
||||||
|
0xe4: 0x8700d, // palegoldenrod
|
||||||
|
0xe5: 0x4f40b, // darkmagenta
|
||||||
|
0xe6: 0x15309, // rosybrown
|
||||||
|
0xe7: 0x18a11, // border-left-width
|
||||||
|
0xe8: 0x88509, // darkkhaki
|
||||||
|
0xea: 0x650e, // blanchedalmond
|
||||||
|
0xeb: 0x52007, // initial
|
||||||
|
0xec: 0x6ce1b, // scrollbar-dark-shadow-color
|
||||||
|
0xee: 0x48e0b, // saddlebrown
|
||||||
|
0xef: 0x8a70d, // paleturquoise
|
||||||
|
0xf1: 0x19b12, // border-right-color
|
||||||
|
0xf3: 0x1e305, // white
|
||||||
|
0xf7: 0x9ce19, // scrollbar-highlight-color
|
||||||
|
0xf9: 0x56d10, // layout-grid-mode
|
||||||
|
0xfc: 0x1f40c, // border-style
|
||||||
|
0xfe: 0x69b13, // list-style-position
|
||||||
|
0x100: 0x11616, // layer-background-color
|
||||||
|
0x102: 0x58210, // layout-grid-type
|
||||||
|
0x103: 0x15c0b, // border-left
|
||||||
|
0x104: 0x2db08, // overflow
|
||||||
|
0x105: 0x7a60c, // midnightblue
|
||||||
|
0x10b: 0x2840a, // text-align
|
||||||
|
0x10e: 0x21010, // border-top-style
|
||||||
|
0x110: 0x5de14, // lightgoldenrodyellow
|
||||||
|
0x114: 0x8506, // border
|
||||||
|
0x119: 0xd804, // font
|
||||||
|
0x11c: 0x7020a, // aquamarine
|
||||||
|
0x11d: 0x60e0a, // lightgreen
|
||||||
|
0x11e: 0x5ec06, // yellow
|
||||||
|
0x120: 0x97405, // speak
|
||||||
|
0x121: 0x4630b, // white-space
|
||||||
|
0x123: 0x3940d, // darkslateblue
|
||||||
|
0x125: 0x1e60e, // text-autospace
|
||||||
|
0x128: 0xf50d, // lavenderblush
|
||||||
|
0x12c: 0x6210b, // lightsalmon
|
||||||
|
0x12d: 0x51907, // inherit
|
||||||
|
0x131: 0x87c0a, // darkorchid
|
||||||
|
0x132: 0x2000a, // border-top
|
||||||
|
0x133: 0x3c80b, // play-during
|
||||||
|
0x137: 0x22010, // border-top-width
|
||||||
|
0x139: 0x48807, // orphans
|
||||||
|
0x13a: 0x41d0b, // font-family
|
||||||
|
0x13d: 0x3db0a, // dodgerblue
|
||||||
|
0x13f: 0x8d00a, // papayawhip
|
||||||
|
0x140: 0x8f305, // pause
|
||||||
|
0x143: 0x2ff0e, // cornflowerblue
|
||||||
|
0x144: 0x3c507, // display
|
||||||
|
0x146: 0x52509, // aliceblue
|
||||||
|
0x14a: 0x7208, // darkblue
|
||||||
|
0x14b: 0x3108, // behavior
|
||||||
|
0x14c: 0x3540d, // counter-reset
|
||||||
|
0x14d: 0x7960b, // greenyellow
|
||||||
|
0x14e: 0x75811, // mediumspringgreen
|
||||||
|
0x14f: 0x9150a, // powderblue
|
||||||
|
0x150: 0x53f10, // layout-grid-char
|
||||||
|
0x158: 0x81d07, // outline
|
||||||
|
0x159: 0x23c09, // burlywood
|
||||||
|
0x15b: 0xe113, // border-bottom-width
|
||||||
|
0x15c: 0x4bf04, // none
|
||||||
|
0x15e: 0x36103, // cue
|
||||||
|
0x15f: 0x4fd0c, // table-layout
|
||||||
|
0x160: 0x90a0b, // pitch-range
|
||||||
|
0x161: 0xa1907, // z-index
|
||||||
|
0x162: 0x29106, // stress
|
||||||
|
0x163: 0x80815, // background-position-x
|
||||||
|
0x165: 0x4d506, // normal
|
||||||
|
0x167: 0x72f0c, // mediumpurple
|
||||||
|
0x169: 0x5a70a, // lightcoral
|
||||||
|
0x16c: 0x6e90a, // max-height
|
||||||
|
0x16d: 0x3804, // rgba
|
||||||
|
0x16e: 0x68b10, // list-style-image
|
||||||
|
0x170: 0x26b08, // deeppink
|
||||||
|
0x173: 0x91f06, // progid
|
||||||
|
0x175: 0x75e0b, // springgreen
|
||||||
|
0x176: 0x3700b, // forestgreen
|
||||||
|
0x179: 0x7ec08, // moccasin
|
||||||
|
0x17a: 0x7780f, // mediumvioletred
|
||||||
|
0x17e: 0x9bc0b, // text-indent
|
||||||
|
0x181: 0x6ae0f, // list-style-type
|
||||||
|
0x182: 0x14c09, // gainsboro
|
||||||
|
0x183: 0x3ae0d, // darkturquoise
|
||||||
|
0x184: 0x3a10d, // darkslategray
|
||||||
|
0x189: 0x2db0a, // overflow-x
|
||||||
|
0x18b: 0x93006, // quotes
|
||||||
|
0x18c: 0x3a15, // background-attachment
|
||||||
|
0x18f: 0x19b0c, // border-right
|
||||||
|
0x191: 0x6005, // black
|
||||||
|
0x192: 0x79b0b, // yellowgreen
|
||||||
|
0x194: 0x59009, // peachpuff
|
||||||
|
0x197: 0x3f30b, // floralwhite
|
||||||
|
0x19c: 0x7210e, // darkolivegreen
|
||||||
|
0x19d: 0x5f109, // word-wrap
|
||||||
|
0x19e: 0x17911, // border-left-style
|
||||||
|
0x1a0: 0x9b30b, // speech-rate
|
||||||
|
0x1a1: 0x8370d, // outline-width
|
||||||
|
0x1a2: 0x9e70c, // unicode-bidi
|
||||||
|
0x1a3: 0x68b0a, // list-style
|
||||||
|
0x1a4: 0x90a05, // pitch
|
||||||
|
0x1a5: 0x95f15, // scrollbar-track-color
|
||||||
|
0x1a6: 0x47907, // fuchsia
|
||||||
|
0x1a8: 0x3800e, // vertical-align
|
||||||
|
0x1ad: 0x5af05, // alpha
|
||||||
|
0x1ae: 0x6f309, // max-width
|
||||||
|
0x1af: 0x9708, // richness
|
||||||
|
0x1b0: 0x3803, // rgb
|
||||||
|
0x1b1: 0x7d60d, // padding-right
|
||||||
|
0x1b2: 0x29615, // scrollbar-arrow-color
|
||||||
|
0x1b3: 0x16304, // left
|
||||||
|
0x1b5: 0x4a009, // elevation
|
||||||
|
0x1b6: 0x5630a, // line-break
|
||||||
|
0x1ba: 0x27d09, // chocolate
|
||||||
|
0x1bb: 0x9800d, // speak-numeral
|
||||||
|
0x1bd: 0x47f0b, // accelerator
|
||||||
|
0x1be: 0x67709, // limegreen
|
||||||
|
0x1c1: 0x7d08, // darkcyan
|
||||||
|
0x1c3: 0x6390c, // lightskyblue
|
||||||
|
0x1c5: 0x5c50a, // sans-serif
|
||||||
|
0x1c6: 0x850d, // border-bottom
|
||||||
|
0x1c7: 0xa, // background
|
||||||
|
0x1c8: 0xa1006, // volume
|
||||||
|
0x1ca: 0x66b0c, // writing-mode
|
||||||
|
0x1cb: 0x9e18, // scrollbar-3d-light-color
|
||||||
|
0x1cc: 0x5c006, // widows
|
||||||
|
0x1cf: 0x42809, // font-size
|
||||||
|
0x1d0: 0x15, // background-position-y
|
||||||
|
0x1d1: 0x5d509, // lightcyan
|
||||||
|
0x1d4: 0x4ec09, // indianred
|
||||||
|
0x1d7: 0x1de0a, // ghostwhite
|
||||||
|
0x1db: 0x78a09, // orangered
|
||||||
|
0x1dc: 0x45c0c, // antiquewhite
|
||||||
|
0x1dd: 0x4da09, // lawngreen
|
||||||
|
0x1df: 0x73b0e, // mediumseagreen
|
||||||
|
0x1e0: 0x20010, // border-top-color
|
||||||
|
0x1e2: 0xf304, // hsla
|
||||||
|
0x1e4: 0x3250e, // text-transform
|
||||||
|
0x1e6: 0x7160c, // mediumorchid
|
||||||
|
0x1e9: 0x8c709, // monospace
|
||||||
|
0x1ec: 0x94916, // scrollbar-shadow-color
|
||||||
|
0x1ed: 0x79209, // darkgreen
|
||||||
|
0x1ef: 0x25809, // cadetblue
|
||||||
|
0x1f0: 0x59806, // filter
|
||||||
|
0x1f1: 0x1ad12, // border-right-style
|
||||||
|
0x1f6: 0x8440a, // overflow-y
|
||||||
|
0x1f7: 0xd809, // font-face
|
||||||
|
0x1f8: 0x50d0c, // word-spacing
|
||||||
|
0x1fa: 0xbe13, // border-bottom-style
|
||||||
|
0x1fb: 0x4380c, // font-stretch
|
||||||
|
0x1fc: 0x7c509, // mintcream
|
||||||
|
0x1fd: 0x88d08, // ime-mode
|
||||||
|
0x1fe: 0x2730a, // chartreuse
|
||||||
|
0x1ff: 0x5ca05, // serif
|
||||||
|
}
|
687
vendor/github.com/tdewolff/parse/css/lex.go
generated
vendored
Normal file
687
vendor/github.com/tdewolff/parse/css/lex.go
generated
vendored
Normal file
|
@ -0,0 +1,687 @@
|
||||||
|
// Package css is a CSS3 lexer and parser following the specifications at http://www.w3.org/TR/css-syntax-3/.
|
||||||
|
package css
|
||||||
|
|
||||||
|
// TODO: \uFFFD replacement character for NULL bytes in strings for example, or atleast don't end the string early
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/tdewolff/buffer"
|
||||||
|
"github.com/tdewolff/parse"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TokenType determines the type of token, eg. a number or a semicolon.
|
||||||
|
type TokenType uint32
|
||||||
|
|
||||||
|
// TokenType values.
|
||||||
|
const (
|
||||||
|
ErrorToken TokenType = iota // extra token when errors occur
|
||||||
|
IdentToken
|
||||||
|
FunctionToken // rgb( rgba( ...
|
||||||
|
AtKeywordToken // @abc
|
||||||
|
HashToken // #abc
|
||||||
|
StringToken
|
||||||
|
BadStringToken
|
||||||
|
URLToken
|
||||||
|
BadURLToken
|
||||||
|
DelimToken // any unmatched character
|
||||||
|
NumberToken // 5
|
||||||
|
PercentageToken // 5%
|
||||||
|
DimensionToken // 5em
|
||||||
|
UnicodeRangeToken // U+554A
|
||||||
|
IncludeMatchToken // ~=
|
||||||
|
DashMatchToken // |=
|
||||||
|
PrefixMatchToken // ^=
|
||||||
|
SuffixMatchToken // $=
|
||||||
|
SubstringMatchToken // *=
|
||||||
|
ColumnToken // ||
|
||||||
|
WhitespaceToken // space \t \r \n \f
|
||||||
|
CDOToken // <!--
|
||||||
|
CDCToken // -->
|
||||||
|
ColonToken // :
|
||||||
|
SemicolonToken // ;
|
||||||
|
CommaToken // ,
|
||||||
|
LeftBracketToken // [
|
||||||
|
RightBracketToken // ]
|
||||||
|
LeftParenthesisToken // (
|
||||||
|
RightParenthesisToken // )
|
||||||
|
LeftBraceToken // {
|
||||||
|
RightBraceToken // }
|
||||||
|
CommentToken // extra token for comments
|
||||||
|
EmptyToken
|
||||||
|
)
|
||||||
|
|
||||||
|
// String returns the string representation of a TokenType.
|
||||||
|
func (tt TokenType) String() string {
|
||||||
|
switch tt {
|
||||||
|
case ErrorToken:
|
||||||
|
return "Error"
|
||||||
|
case IdentToken:
|
||||||
|
return "Ident"
|
||||||
|
case FunctionToken:
|
||||||
|
return "Function"
|
||||||
|
case AtKeywordToken:
|
||||||
|
return "AtKeyword"
|
||||||
|
case HashToken:
|
||||||
|
return "Hash"
|
||||||
|
case StringToken:
|
||||||
|
return "String"
|
||||||
|
case BadStringToken:
|
||||||
|
return "BadString"
|
||||||
|
case URLToken:
|
||||||
|
return "URL"
|
||||||
|
case BadURLToken:
|
||||||
|
return "BadURL"
|
||||||
|
case DelimToken:
|
||||||
|
return "Delim"
|
||||||
|
case NumberToken:
|
||||||
|
return "Number"
|
||||||
|
case PercentageToken:
|
||||||
|
return "Percentage"
|
||||||
|
case DimensionToken:
|
||||||
|
return "Dimension"
|
||||||
|
case UnicodeRangeToken:
|
||||||
|
return "UnicodeRange"
|
||||||
|
case IncludeMatchToken:
|
||||||
|
return "IncludeMatch"
|
||||||
|
case DashMatchToken:
|
||||||
|
return "DashMatch"
|
||||||
|
case PrefixMatchToken:
|
||||||
|
return "PrefixMatch"
|
||||||
|
case SuffixMatchToken:
|
||||||
|
return "SuffixMatch"
|
||||||
|
case SubstringMatchToken:
|
||||||
|
return "SubstringMatch"
|
||||||
|
case ColumnToken:
|
||||||
|
return "Column"
|
||||||
|
case WhitespaceToken:
|
||||||
|
return "Whitespace"
|
||||||
|
case CDOToken:
|
||||||
|
return "CDO"
|
||||||
|
case CDCToken:
|
||||||
|
return "CDC"
|
||||||
|
case ColonToken:
|
||||||
|
return "Colon"
|
||||||
|
case SemicolonToken:
|
||||||
|
return "Semicolon"
|
||||||
|
case CommaToken:
|
||||||
|
return "Comma"
|
||||||
|
case LeftBracketToken:
|
||||||
|
return "LeftBracket"
|
||||||
|
case RightBracketToken:
|
||||||
|
return "RightBracket"
|
||||||
|
case LeftParenthesisToken:
|
||||||
|
return "LeftParenthesis"
|
||||||
|
case RightParenthesisToken:
|
||||||
|
return "RightParenthesis"
|
||||||
|
case LeftBraceToken:
|
||||||
|
return "LeftBrace"
|
||||||
|
case RightBraceToken:
|
||||||
|
return "RightBrace"
|
||||||
|
case CommentToken:
|
||||||
|
return "Comment"
|
||||||
|
case EmptyToken:
|
||||||
|
return "Empty"
|
||||||
|
}
|
||||||
|
return "Invalid(" + strconv.Itoa(int(tt)) + ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Lexer is the state for the lexer.
|
||||||
|
type Lexer struct {
|
||||||
|
r *buffer.Lexer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLexer returns a new Lexer for a given io.Reader.
|
||||||
|
func NewLexer(r io.Reader) *Lexer {
|
||||||
|
return &Lexer{
|
||||||
|
buffer.NewLexer(r),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err returns the error encountered during lexing, this is often io.EOF but also other errors can be returned.
|
||||||
|
func (l Lexer) Err() error {
|
||||||
|
return l.r.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free frees up bytes of length n from previously shifted tokens.
|
||||||
|
func (l *Lexer) Free(n int) {
|
||||||
|
l.r.Free(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next returns the next Token. It returns ErrorToken when an error was encountered. Using Err() one can retrieve the error message.
|
||||||
|
func (l *Lexer) Next() (TokenType, []byte) {
|
||||||
|
switch l.r.Peek(0) {
|
||||||
|
case ' ', '\t', '\n', '\r', '\f':
|
||||||
|
l.r.Move(1)
|
||||||
|
for l.consumeWhitespace() {
|
||||||
|
}
|
||||||
|
return WhitespaceToken, l.r.Shift()
|
||||||
|
case ':':
|
||||||
|
l.r.Move(1)
|
||||||
|
return ColonToken, l.r.Shift()
|
||||||
|
case ';':
|
||||||
|
l.r.Move(1)
|
||||||
|
return SemicolonToken, l.r.Shift()
|
||||||
|
case ',':
|
||||||
|
l.r.Move(1)
|
||||||
|
return CommaToken, l.r.Shift()
|
||||||
|
case '(', ')', '[', ']', '{', '}':
|
||||||
|
if t := l.consumeBracket(); t != ErrorToken {
|
||||||
|
return t, l.r.Shift()
|
||||||
|
}
|
||||||
|
case '#':
|
||||||
|
if l.consumeHashToken() {
|
||||||
|
return HashToken, l.r.Shift()
|
||||||
|
}
|
||||||
|
case '"', '\'':
|
||||||
|
if t := l.consumeString(); t != ErrorToken {
|
||||||
|
return t, l.r.Shift()
|
||||||
|
}
|
||||||
|
case '.', '+':
|
||||||
|
if t := l.consumeNumeric(); t != ErrorToken {
|
||||||
|
return t, l.r.Shift()
|
||||||
|
}
|
||||||
|
case '-':
|
||||||
|
if t := l.consumeNumeric(); t != ErrorToken {
|
||||||
|
return t, l.r.Shift()
|
||||||
|
} else if t := l.consumeIdentlike(); t != ErrorToken {
|
||||||
|
return t, l.r.Shift()
|
||||||
|
} else if l.consumeCDCToken() {
|
||||||
|
return CDCToken, l.r.Shift()
|
||||||
|
}
|
||||||
|
case '@':
|
||||||
|
if l.consumeAtKeywordToken() {
|
||||||
|
return AtKeywordToken, l.r.Shift()
|
||||||
|
}
|
||||||
|
case '$', '*', '^', '~':
|
||||||
|
if t := l.consumeMatch(); t != ErrorToken {
|
||||||
|
return t, l.r.Shift()
|
||||||
|
}
|
||||||
|
case '/':
|
||||||
|
if l.consumeComment() {
|
||||||
|
return CommentToken, l.r.Shift()
|
||||||
|
}
|
||||||
|
case '<':
|
||||||
|
if l.consumeCDOToken() {
|
||||||
|
return CDOToken, l.r.Shift()
|
||||||
|
}
|
||||||
|
case '\\':
|
||||||
|
if t := l.consumeIdentlike(); t != ErrorToken {
|
||||||
|
return t, l.r.Shift()
|
||||||
|
}
|
||||||
|
case 'u', 'U':
|
||||||
|
if l.consumeUnicodeRangeToken() {
|
||||||
|
return UnicodeRangeToken, l.r.Shift()
|
||||||
|
} else if t := l.consumeIdentlike(); t != ErrorToken {
|
||||||
|
return t, l.r.Shift()
|
||||||
|
}
|
||||||
|
case '|':
|
||||||
|
if t := l.consumeMatch(); t != ErrorToken {
|
||||||
|
return t, l.r.Shift()
|
||||||
|
} else if l.consumeColumnToken() {
|
||||||
|
return ColumnToken, l.r.Shift()
|
||||||
|
}
|
||||||
|
case 0:
|
||||||
|
if l.Err() != nil {
|
||||||
|
return ErrorToken, nil
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if t := l.consumeNumeric(); t != ErrorToken {
|
||||||
|
return t, l.r.Shift()
|
||||||
|
} else if t := l.consumeIdentlike(); t != ErrorToken {
|
||||||
|
return t, l.r.Shift()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// can't be rune because consumeIdentlike consumes that as an identifier
|
||||||
|
l.r.Move(1)
|
||||||
|
return DelimToken, l.r.Shift()
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/*
|
||||||
|
The following functions follow the railroad diagrams in http://www.w3.org/TR/css3-syntax/
|
||||||
|
*/
|
||||||
|
|
||||||
|
func (l *Lexer) consumeByte(c byte) bool {
|
||||||
|
if l.r.Peek(0) == c {
|
||||||
|
l.r.Move(1)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Lexer) consumeComment() bool {
|
||||||
|
if l.r.Peek(0) != '/' || l.r.Peek(1) != '*' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
l.r.Move(2)
|
||||||
|
for {
|
||||||
|
c := l.r.Peek(0)
|
||||||
|
if c == 0 && l.r.Err() != nil {
|
||||||
|
break
|
||||||
|
} else if c == '*' && l.r.Peek(1) == '/' {
|
||||||
|
l.r.Move(2)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
l.r.Move(1)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Lexer) consumeNewline() bool {
|
||||||
|
c := l.r.Peek(0)
|
||||||
|
if c == '\n' || c == '\f' {
|
||||||
|
l.r.Move(1)
|
||||||
|
return true
|
||||||
|
} else if c == '\r' {
|
||||||
|
if l.r.Peek(1) == '\n' {
|
||||||
|
l.r.Move(2)
|
||||||
|
} else {
|
||||||
|
l.r.Move(1)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Lexer) consumeWhitespace() bool {
|
||||||
|
c := l.r.Peek(0)
|
||||||
|
if c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' {
|
||||||
|
l.r.Move(1)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Lexer) consumeDigit() bool {
|
||||||
|
c := l.r.Peek(0)
|
||||||
|
if c >= '0' && c <= '9' {
|
||||||
|
l.r.Move(1)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Lexer) consumeHexDigit() bool {
|
||||||
|
c := l.r.Peek(0)
|
||||||
|
if (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') {
|
||||||
|
l.r.Move(1)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Lexer) consumeEscape() bool {
|
||||||
|
if l.r.Peek(0) != '\\' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
mark := l.r.Pos()
|
||||||
|
l.r.Move(1)
|
||||||
|
if l.consumeNewline() {
|
||||||
|
l.r.Rewind(mark)
|
||||||
|
return false
|
||||||
|
} else if l.consumeHexDigit() {
|
||||||
|
for k := 1; k < 6; k++ {
|
||||||
|
if !l.consumeHexDigit() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l.consumeWhitespace()
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
c := l.r.Peek(0)
|
||||||
|
if c >= 0xC0 {
|
||||||
|
_, n := l.r.PeekRune(0)
|
||||||
|
l.r.Move(n)
|
||||||
|
return true
|
||||||
|
} else if c == 0 && l.r.Err() != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l.r.Move(1)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Lexer) consumeIdentToken() bool {
|
||||||
|
mark := l.r.Pos()
|
||||||
|
if l.r.Peek(0) == '-' {
|
||||||
|
l.r.Move(1)
|
||||||
|
}
|
||||||
|
c := l.r.Peek(0)
|
||||||
|
if !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c >= 0x80) {
|
||||||
|
if c != '\\' || !l.consumeEscape() {
|
||||||
|
l.r.Rewind(mark)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
l.r.Move(1)
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
c := l.r.Peek(0)
|
||||||
|
if !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c == '-' || c >= 0x80) {
|
||||||
|
if c != '\\' || !l.consumeEscape() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
l.r.Move(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Lexer) consumeAtKeywordToken() bool {
|
||||||
|
// expect to be on an '@'
|
||||||
|
l.r.Move(1)
|
||||||
|
if !l.consumeIdentToken() {
|
||||||
|
l.r.Move(-1)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Lexer) consumeHashToken() bool {
|
||||||
|
// expect to be on a '#'
|
||||||
|
mark := l.r.Pos()
|
||||||
|
l.r.Move(1)
|
||||||
|
c := l.r.Peek(0)
|
||||||
|
if !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c == '-' || c >= 0x80) {
|
||||||
|
if c != '\\' || !l.consumeEscape() {
|
||||||
|
l.r.Rewind(mark)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
l.r.Move(1)
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
c := l.r.Peek(0)
|
||||||
|
if !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c == '-' || c >= 0x80) {
|
||||||
|
if c != '\\' || !l.consumeEscape() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
l.r.Move(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Lexer) consumeNumberToken() bool {
|
||||||
|
mark := l.r.Pos()
|
||||||
|
c := l.r.Peek(0)
|
||||||
|
if c == '+' || c == '-' {
|
||||||
|
l.r.Move(1)
|
||||||
|
}
|
||||||
|
firstDigit := l.consumeDigit()
|
||||||
|
if firstDigit {
|
||||||
|
for l.consumeDigit() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if l.r.Peek(0) == '.' {
|
||||||
|
l.r.Move(1)
|
||||||
|
if l.consumeDigit() {
|
||||||
|
for l.consumeDigit() {
|
||||||
|
}
|
||||||
|
} else if firstDigit {
|
||||||
|
// . could belong to the next token
|
||||||
|
l.r.Move(-1)
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
l.r.Rewind(mark)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else if !firstDigit {
|
||||||
|
l.r.Rewind(mark)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
mark = l.r.Pos()
|
||||||
|
c = l.r.Peek(0)
|
||||||
|
if c == 'e' || c == 'E' {
|
||||||
|
l.r.Move(1)
|
||||||
|
c = l.r.Peek(0)
|
||||||
|
if c == '+' || c == '-' {
|
||||||
|
l.r.Move(1)
|
||||||
|
}
|
||||||
|
if !l.consumeDigit() {
|
||||||
|
// e could belong to next token
|
||||||
|
l.r.Rewind(mark)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for l.consumeDigit() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Lexer) consumeUnicodeRangeToken() bool {
|
||||||
|
c := l.r.Peek(0)
|
||||||
|
if (c != 'u' && c != 'U') || l.r.Peek(1) != '+' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
mark := l.r.Pos()
|
||||||
|
l.r.Move(2)
|
||||||
|
if l.consumeHexDigit() {
|
||||||
|
// consume up to 6 hexDigits
|
||||||
|
k := 1
|
||||||
|
for ; k < 6; k++ {
|
||||||
|
if !l.consumeHexDigit() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// either a minus or a quenstion mark or the end is expected
|
||||||
|
if l.consumeByte('-') {
|
||||||
|
// consume another up to 6 hexDigits
|
||||||
|
if l.consumeHexDigit() {
|
||||||
|
for k := 1; k < 6; k++ {
|
||||||
|
if !l.consumeHexDigit() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
l.r.Rewind(mark)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// could be filled up to 6 characters with question marks or else regular hexDigits
|
||||||
|
if l.consumeByte('?') {
|
||||||
|
k++
|
||||||
|
for ; k < 6; k++ {
|
||||||
|
if !l.consumeByte('?') {
|
||||||
|
l.r.Rewind(mark)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// consume 6 question marks
|
||||||
|
for k := 0; k < 6; k++ {
|
||||||
|
if !l.consumeByte('?') {
|
||||||
|
l.r.Rewind(mark)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Lexer) consumeColumnToken() bool {
|
||||||
|
if l.r.Peek(0) == '|' && l.r.Peek(1) == '|' {
|
||||||
|
l.r.Move(2)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Lexer) consumeCDOToken() bool {
|
||||||
|
if l.r.Peek(0) == '<' && l.r.Peek(1) == '!' && l.r.Peek(2) == '-' && l.r.Peek(3) == '-' {
|
||||||
|
l.r.Move(4)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Lexer) consumeCDCToken() bool {
|
||||||
|
if l.r.Peek(0) == '-' && l.r.Peek(1) == '-' && l.r.Peek(2) == '>' {
|
||||||
|
l.r.Move(3)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// consumeMatch consumes any MatchToken.
|
||||||
|
func (l *Lexer) consumeMatch() TokenType {
|
||||||
|
if l.r.Peek(1) == '=' {
|
||||||
|
switch l.r.Peek(0) {
|
||||||
|
case '~':
|
||||||
|
l.r.Move(2)
|
||||||
|
return IncludeMatchToken
|
||||||
|
case '|':
|
||||||
|
l.r.Move(2)
|
||||||
|
return DashMatchToken
|
||||||
|
case '^':
|
||||||
|
l.r.Move(2)
|
||||||
|
return PrefixMatchToken
|
||||||
|
case '$':
|
||||||
|
l.r.Move(2)
|
||||||
|
return SuffixMatchToken
|
||||||
|
case '*':
|
||||||
|
l.r.Move(2)
|
||||||
|
return SubstringMatchToken
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ErrorToken
|
||||||
|
}
|
||||||
|
|
||||||
|
// consumeBracket consumes any bracket token.
|
||||||
|
func (l *Lexer) consumeBracket() TokenType {
|
||||||
|
switch l.r.Peek(0) {
|
||||||
|
case '(':
|
||||||
|
l.r.Move(1)
|
||||||
|
return LeftParenthesisToken
|
||||||
|
case ')':
|
||||||
|
l.r.Move(1)
|
||||||
|
return RightParenthesisToken
|
||||||
|
case '[':
|
||||||
|
l.r.Move(1)
|
||||||
|
return LeftBracketToken
|
||||||
|
case ']':
|
||||||
|
l.r.Move(1)
|
||||||
|
return RightBracketToken
|
||||||
|
case '{':
|
||||||
|
l.r.Move(1)
|
||||||
|
return LeftBraceToken
|
||||||
|
case '}':
|
||||||
|
l.r.Move(1)
|
||||||
|
return RightBraceToken
|
||||||
|
}
|
||||||
|
return ErrorToken
|
||||||
|
}
|
||||||
|
|
||||||
|
// consumeNumeric consumes NumberToken, PercentageToken or DimensionToken.
|
||||||
|
func (l *Lexer) consumeNumeric() TokenType {
|
||||||
|
if l.consumeNumberToken() {
|
||||||
|
if l.consumeByte('%') {
|
||||||
|
return PercentageToken
|
||||||
|
} else if l.consumeIdentToken() {
|
||||||
|
return DimensionToken
|
||||||
|
}
|
||||||
|
return NumberToken
|
||||||
|
}
|
||||||
|
return ErrorToken
|
||||||
|
}
|
||||||
|
|
||||||
|
// consumeString consumes a string and may return BadStringToken when a newline is encountered.
|
||||||
|
func (l *Lexer) consumeString() TokenType {
|
||||||
|
// assume to be on " or '
|
||||||
|
delim := l.r.Peek(0)
|
||||||
|
l.r.Move(1)
|
||||||
|
for {
|
||||||
|
c := l.r.Peek(0)
|
||||||
|
if c == 0 && l.r.Err() != nil {
|
||||||
|
break
|
||||||
|
} else if c == '\n' || c == '\r' || c == '\f' {
|
||||||
|
l.r.Move(1)
|
||||||
|
return BadStringToken
|
||||||
|
} else if c == delim {
|
||||||
|
l.r.Move(1)
|
||||||
|
break
|
||||||
|
} else if c == '\\' {
|
||||||
|
if !l.consumeEscape() {
|
||||||
|
l.r.Move(1)
|
||||||
|
l.consumeNewline()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
l.r.Move(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return StringToken
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Lexer) consumeUnquotedURL() bool {
|
||||||
|
for {
|
||||||
|
c := l.r.Peek(0)
|
||||||
|
if c == 0 && l.r.Err() != nil || c == ')' {
|
||||||
|
break
|
||||||
|
} else if c == '"' || c == '\'' || c == '(' || c == '\\' || c == ' ' || c <= 0x1F || c == 0x7F {
|
||||||
|
if c != '\\' || !l.consumeEscape() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
l.r.Move(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// consumeRemnantsBadUrl consumes bytes of a BadUrlToken so that normal tokenization may continue.
|
||||||
|
func (l *Lexer) consumeRemnantsBadURL() {
|
||||||
|
for {
|
||||||
|
if l.consumeByte(')') || l.Err() != nil {
|
||||||
|
break
|
||||||
|
} else if !l.consumeEscape() {
|
||||||
|
l.r.Move(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// consumeIdentlike consumes IdentToken, FunctionToken or UrlToken.
|
||||||
|
func (l *Lexer) consumeIdentlike() TokenType {
|
||||||
|
if l.consumeIdentToken() {
|
||||||
|
if l.r.Peek(0) != '(' {
|
||||||
|
return IdentToken
|
||||||
|
} else if !parse.EqualFold(bytes.Replace(l.r.Lexeme(), []byte{'\\'}, nil, -1), []byte{'u', 'r', 'l'}) {
|
||||||
|
l.r.Move(1)
|
||||||
|
return FunctionToken
|
||||||
|
}
|
||||||
|
l.r.Move(1)
|
||||||
|
|
||||||
|
// consume url
|
||||||
|
for l.consumeWhitespace() {
|
||||||
|
}
|
||||||
|
if c := l.r.Peek(0); c == '"' || c == '\'' {
|
||||||
|
if l.consumeString() == BadStringToken {
|
||||||
|
l.consumeRemnantsBadURL()
|
||||||
|
return BadURLToken
|
||||||
|
}
|
||||||
|
} else if !l.consumeUnquotedURL() && !l.consumeWhitespace() {
|
||||||
|
l.consumeRemnantsBadURL()
|
||||||
|
return BadURLToken
|
||||||
|
}
|
||||||
|
for l.consumeWhitespace() {
|
||||||
|
}
|
||||||
|
if !l.consumeByte(')') && l.Err() != io.EOF {
|
||||||
|
l.consumeRemnantsBadURL()
|
||||||
|
return BadURLToken
|
||||||
|
}
|
||||||
|
return URLToken
|
||||||
|
}
|
||||||
|
return ErrorToken
|
||||||
|
}
|
370
vendor/github.com/tdewolff/parse/css/parse.go
generated
vendored
Normal file
370
vendor/github.com/tdewolff/parse/css/parse.go
generated
vendored
Normal file
|
@ -0,0 +1,370 @@
|
||||||
|
package css
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/tdewolff/parse"
|
||||||
|
)
|
||||||
|
|
||||||
|
var wsBytes = []byte(" ")
|
||||||
|
var endBytes = []byte("}")
|
||||||
|
var emptyBytes = []byte("")
|
||||||
|
|
||||||
|
// ErrBadQualifiedRule is returned when a qualitied rule is expected, but an error or EOF happened earlier.
|
||||||
|
var ErrBadQualifiedRule = errors.New("unexpected ending in qualified rule, expected left brace token")
|
||||||
|
|
||||||
|
// ErrBadDeclaration is returned when a declaration is expected but the colon token is lacking.
|
||||||
|
var ErrBadDeclaration = errors.New("unexpected token in declaration, expected colon token")
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// GrammarType determines the type of grammar.
|
||||||
|
type GrammarType uint32
|
||||||
|
|
||||||
|
// GrammarType values.
|
||||||
|
const (
|
||||||
|
ErrorGrammar GrammarType = iota // extra token when errors occur
|
||||||
|
CommentGrammar
|
||||||
|
AtRuleGrammar
|
||||||
|
BeginAtRuleGrammar
|
||||||
|
EndAtRuleGrammar
|
||||||
|
BeginRulesetGrammar
|
||||||
|
EndRulesetGrammar
|
||||||
|
DeclarationGrammar
|
||||||
|
TokenGrammar
|
||||||
|
)
|
||||||
|
|
||||||
|
// String returns the string representation of a GrammarType.
|
||||||
|
func (tt GrammarType) String() string {
|
||||||
|
switch tt {
|
||||||
|
case ErrorGrammar:
|
||||||
|
return "Error"
|
||||||
|
case CommentGrammar:
|
||||||
|
return "Comment"
|
||||||
|
case AtRuleGrammar:
|
||||||
|
return "AtRule"
|
||||||
|
case BeginAtRuleGrammar:
|
||||||
|
return "BeginAtRule"
|
||||||
|
case EndAtRuleGrammar:
|
||||||
|
return "EndAtRule"
|
||||||
|
case BeginRulesetGrammar:
|
||||||
|
return "BeginRuleset"
|
||||||
|
case EndRulesetGrammar:
|
||||||
|
return "EndRuleset"
|
||||||
|
case DeclarationGrammar:
|
||||||
|
return "Declaration"
|
||||||
|
case TokenGrammar:
|
||||||
|
return "Token"
|
||||||
|
}
|
||||||
|
return "Invalid(" + strconv.Itoa(int(tt)) + ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// State is the state function the parser currently is in.
|
||||||
|
type State func() GrammarType
|
||||||
|
|
||||||
|
// Token is a single TokenType and its associated data.
|
||||||
|
type Token struct {
|
||||||
|
TokenType
|
||||||
|
Data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parser is the state for the parser.
|
||||||
|
type Parser struct {
|
||||||
|
l *Lexer
|
||||||
|
state []State
|
||||||
|
err error
|
||||||
|
|
||||||
|
buf []Token
|
||||||
|
level int
|
||||||
|
|
||||||
|
tt TokenType
|
||||||
|
data []byte
|
||||||
|
prevWS bool
|
||||||
|
prevEnd bool
|
||||||
|
n int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewParser returns a new CSS parser from an io.Reader. isInline specifies whether this is an inline style attribute.
|
||||||
|
func NewParser(r io.Reader, isInline bool) *Parser {
|
||||||
|
l := NewLexer(r)
|
||||||
|
p := &Parser{
|
||||||
|
l: l,
|
||||||
|
}
|
||||||
|
if isInline {
|
||||||
|
p.state = []State{p.parseDeclarationList}
|
||||||
|
} else {
|
||||||
|
p.state = []State{p.parseStylesheet}
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err returns the error encountered during parsing, this is often io.EOF but also other errors can be returned.
|
||||||
|
func (p *Parser) Err() error {
|
||||||
|
if p.err != nil {
|
||||||
|
return p.err
|
||||||
|
}
|
||||||
|
return p.l.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next returns the next Grammar. It returns ErrorGrammar when an error was encountered. Using Err() one can retrieve the error message.
|
||||||
|
func (p *Parser) Next() (GrammarType, TokenType, []byte) {
|
||||||
|
p.l.Free(p.n)
|
||||||
|
p.n = 0
|
||||||
|
|
||||||
|
if p.prevEnd {
|
||||||
|
p.tt, p.data = RightBraceToken, endBytes
|
||||||
|
p.prevEnd = false
|
||||||
|
} else {
|
||||||
|
p.tt, p.data = p.popToken(true)
|
||||||
|
}
|
||||||
|
gt := p.state[len(p.state)-1]()
|
||||||
|
return gt, p.tt, p.data
|
||||||
|
}
|
||||||
|
|
||||||
|
// Values returns a slice of Tokens for the last Grammar. Only AtRuleGrammar, BeginAtRuleGrammar, BeginRulesetGrammar and Declaration will return the at-rule components, ruleset selector and declaration values respectively.
|
||||||
|
func (p *Parser) Values() []Token {
|
||||||
|
return p.buf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) popToken(allowComment bool) (TokenType, []byte) {
|
||||||
|
p.prevWS = false
|
||||||
|
tt, data := p.l.Next()
|
||||||
|
p.n += len(data)
|
||||||
|
for tt == WhitespaceToken || tt == CommentToken {
|
||||||
|
if tt == WhitespaceToken {
|
||||||
|
p.prevWS = true
|
||||||
|
} else if allowComment && len(p.state) == 1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
tt, data = p.l.Next()
|
||||||
|
p.n += len(data)
|
||||||
|
}
|
||||||
|
return tt, data
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) initBuf() {
|
||||||
|
p.buf = p.buf[:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) pushBuf(tt TokenType, data []byte) {
|
||||||
|
p.buf = append(p.buf, Token{tt, data})
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
func (p *Parser) parseStylesheet() GrammarType {
|
||||||
|
if p.tt == CDOToken || p.tt == CDCToken {
|
||||||
|
return TokenGrammar
|
||||||
|
} else if p.tt == AtKeywordToken {
|
||||||
|
return p.parseAtRule()
|
||||||
|
} else if p.tt == CommentToken {
|
||||||
|
return CommentGrammar
|
||||||
|
} else if p.tt == ErrorToken {
|
||||||
|
return ErrorGrammar
|
||||||
|
}
|
||||||
|
return p.parseQualifiedRule()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parseDeclarationList() GrammarType {
|
||||||
|
if p.tt == CommentToken {
|
||||||
|
p.tt, p.data = p.popToken(false)
|
||||||
|
}
|
||||||
|
for p.tt == SemicolonToken {
|
||||||
|
p.tt, p.data = p.popToken(false)
|
||||||
|
}
|
||||||
|
if p.tt == ErrorToken {
|
||||||
|
return ErrorGrammar
|
||||||
|
} else if p.tt == AtKeywordToken {
|
||||||
|
return p.parseAtRule()
|
||||||
|
} else if p.tt == IdentToken {
|
||||||
|
return p.parseDeclaration()
|
||||||
|
} else if p.tt == DelimToken && p.data[0] == '*' { // CSS hack
|
||||||
|
p.tt, p.data = p.popToken(false)
|
||||||
|
if p.tt == IdentToken {
|
||||||
|
p.data = append([]byte("*"), p.data...)
|
||||||
|
return p.parseDeclaration()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// parse error
|
||||||
|
for p.tt != SemicolonToken && p.tt != ErrorToken {
|
||||||
|
p.tt, p.data = p.popToken(false)
|
||||||
|
}
|
||||||
|
return p.state[len(p.state)-1]()
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
func (p *Parser) parseAtRule() GrammarType {
|
||||||
|
p.initBuf()
|
||||||
|
parse.ToLower(p.data)
|
||||||
|
atRuleName := p.data
|
||||||
|
if len(atRuleName) > 0 && atRuleName[1] == '-' {
|
||||||
|
if i := bytes.IndexByte(atRuleName[2:], '-'); i != -1 {
|
||||||
|
atRuleName = atRuleName[i+2:] // skip vendor specific prefix
|
||||||
|
}
|
||||||
|
}
|
||||||
|
atRule := ToHash(atRuleName[1:])
|
||||||
|
|
||||||
|
first := true
|
||||||
|
skipWS := false
|
||||||
|
for {
|
||||||
|
tt, data := p.popToken(false)
|
||||||
|
if tt == LeftBraceToken && p.level == 0 {
|
||||||
|
if atRule == Font_Face || atRule == Page {
|
||||||
|
p.state = append(p.state, p.parseAtRuleDeclarationList)
|
||||||
|
} else if atRule == Document || atRule == Keyframes || atRule == Media || atRule == Supports {
|
||||||
|
p.state = append(p.state, p.parseAtRuleRuleList)
|
||||||
|
} else {
|
||||||
|
p.state = append(p.state, p.parseAtRuleUnknown)
|
||||||
|
}
|
||||||
|
return BeginAtRuleGrammar
|
||||||
|
} else if (tt == SemicolonToken || tt == RightBraceToken) && p.level == 0 || tt == ErrorToken {
|
||||||
|
p.prevEnd = (tt == RightBraceToken)
|
||||||
|
return AtRuleGrammar
|
||||||
|
} else if tt == LeftParenthesisToken || tt == LeftBraceToken || tt == LeftBracketToken || tt == FunctionToken {
|
||||||
|
p.level++
|
||||||
|
} else if tt == RightParenthesisToken || tt == RightBraceToken || tt == RightBracketToken {
|
||||||
|
p.level--
|
||||||
|
}
|
||||||
|
if first {
|
||||||
|
if tt == LeftParenthesisToken || tt == LeftBracketToken {
|
||||||
|
p.prevWS = false
|
||||||
|
}
|
||||||
|
first = false
|
||||||
|
}
|
||||||
|
if len(data) == 1 && (data[0] == ',' || data[0] == ':') {
|
||||||
|
skipWS = true
|
||||||
|
} else if p.prevWS && !skipWS && tt != RightParenthesisToken {
|
||||||
|
p.pushBuf(WhitespaceToken, wsBytes)
|
||||||
|
} else {
|
||||||
|
skipWS = false
|
||||||
|
}
|
||||||
|
if tt == LeftParenthesisToken {
|
||||||
|
skipWS = true
|
||||||
|
}
|
||||||
|
p.pushBuf(tt, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parseAtRuleRuleList() GrammarType {
|
||||||
|
if p.tt == RightBraceToken || p.tt == ErrorToken {
|
||||||
|
p.state = p.state[:len(p.state)-1]
|
||||||
|
return EndAtRuleGrammar
|
||||||
|
} else if p.tt == AtKeywordToken {
|
||||||
|
return p.parseAtRule()
|
||||||
|
} else {
|
||||||
|
return p.parseQualifiedRule()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parseAtRuleDeclarationList() GrammarType {
|
||||||
|
for p.tt == SemicolonToken {
|
||||||
|
p.tt, p.data = p.popToken(false)
|
||||||
|
}
|
||||||
|
if p.tt == RightBraceToken || p.tt == ErrorToken {
|
||||||
|
p.state = p.state[:len(p.state)-1]
|
||||||
|
return EndAtRuleGrammar
|
||||||
|
}
|
||||||
|
return p.parseDeclarationList()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parseAtRuleUnknown() GrammarType {
|
||||||
|
if p.tt == RightBraceToken && p.level == 0 || p.tt == ErrorToken {
|
||||||
|
p.state = p.state[:len(p.state)-1]
|
||||||
|
return EndAtRuleGrammar
|
||||||
|
}
|
||||||
|
if p.tt == LeftParenthesisToken || p.tt == LeftBraceToken || p.tt == LeftBracketToken || p.tt == FunctionToken {
|
||||||
|
p.level++
|
||||||
|
} else if p.tt == RightParenthesisToken || p.tt == RightBraceToken || p.tt == RightBracketToken {
|
||||||
|
p.level--
|
||||||
|
}
|
||||||
|
return TokenGrammar
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parseQualifiedRule() GrammarType {
|
||||||
|
p.initBuf()
|
||||||
|
first := true
|
||||||
|
inAttrSel := false
|
||||||
|
skipWS := true
|
||||||
|
var tt TokenType
|
||||||
|
var data []byte
|
||||||
|
for {
|
||||||
|
if first {
|
||||||
|
tt, data = p.tt, p.data
|
||||||
|
p.tt = WhitespaceToken
|
||||||
|
p.data = emptyBytes
|
||||||
|
first = false
|
||||||
|
} else {
|
||||||
|
tt, data = p.popToken(false)
|
||||||
|
}
|
||||||
|
if tt == LeftBraceToken && p.level == 0 {
|
||||||
|
p.state = append(p.state, p.parseQualifiedRuleDeclarationList)
|
||||||
|
return BeginRulesetGrammar
|
||||||
|
} else if tt == ErrorToken {
|
||||||
|
p.err = ErrBadQualifiedRule
|
||||||
|
return ErrorGrammar
|
||||||
|
} else if tt == LeftParenthesisToken || tt == LeftBraceToken || tt == LeftBracketToken || tt == FunctionToken {
|
||||||
|
p.level++
|
||||||
|
} else if tt == RightParenthesisToken || tt == RightBraceToken || tt == RightBracketToken {
|
||||||
|
p.level--
|
||||||
|
}
|
||||||
|
if len(data) == 1 && (data[0] == ',' || data[0] == '>' || data[0] == '+' || data[0] == '~') {
|
||||||
|
skipWS = true
|
||||||
|
} else if p.prevWS && !skipWS && !inAttrSel {
|
||||||
|
p.pushBuf(WhitespaceToken, wsBytes)
|
||||||
|
} else {
|
||||||
|
skipWS = false
|
||||||
|
}
|
||||||
|
if tt == LeftBracketToken {
|
||||||
|
inAttrSel = true
|
||||||
|
} else if tt == RightBracketToken {
|
||||||
|
inAttrSel = false
|
||||||
|
}
|
||||||
|
p.pushBuf(tt, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parseQualifiedRuleDeclarationList() GrammarType {
|
||||||
|
for p.tt == SemicolonToken {
|
||||||
|
p.tt, p.data = p.popToken(false)
|
||||||
|
}
|
||||||
|
if p.tt == RightBraceToken || p.tt == ErrorToken {
|
||||||
|
p.state = p.state[:len(p.state)-1]
|
||||||
|
return EndRulesetGrammar
|
||||||
|
}
|
||||||
|
return p.parseDeclarationList()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parseDeclaration() GrammarType {
|
||||||
|
p.initBuf()
|
||||||
|
parse.ToLower(p.data)
|
||||||
|
if tt, _ := p.popToken(false); tt != ColonToken {
|
||||||
|
p.err = ErrBadDeclaration
|
||||||
|
return ErrorGrammar
|
||||||
|
}
|
||||||
|
skipWS := true
|
||||||
|
for {
|
||||||
|
tt, data := p.popToken(false)
|
||||||
|
if (tt == SemicolonToken || tt == RightBraceToken) && p.level == 0 || tt == ErrorToken {
|
||||||
|
p.prevEnd = (tt == RightBraceToken)
|
||||||
|
return DeclarationGrammar
|
||||||
|
} else if tt == LeftParenthesisToken || tt == LeftBraceToken || tt == LeftBracketToken || tt == FunctionToken {
|
||||||
|
p.level++
|
||||||
|
} else if tt == RightParenthesisToken || tt == RightBraceToken || tt == RightBracketToken {
|
||||||
|
p.level--
|
||||||
|
}
|
||||||
|
if len(data) == 1 && (data[0] == ',' || data[0] == '/' || data[0] == ':' || data[0] == '!' || data[0] == '=') {
|
||||||
|
skipWS = true
|
||||||
|
} else if p.prevWS && !skipWS {
|
||||||
|
p.pushBuf(WhitespaceToken, wsBytes)
|
||||||
|
} else {
|
||||||
|
skipWS = false
|
||||||
|
}
|
||||||
|
p.pushBuf(tt, data)
|
||||||
|
}
|
||||||
|
}
|
45
vendor/github.com/tdewolff/parse/css/util.go
generated
vendored
Normal file
45
vendor/github.com/tdewolff/parse/css/util.go
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
package css
|
||||||
|
|
||||||
|
import "bytes"
|
||||||
|
|
||||||
|
// IsIdent returns true if the bytes are a valid identifier.
|
||||||
|
func IsIdent(b []byte) bool {
|
||||||
|
l := NewLexer(bytes.NewBuffer(b))
|
||||||
|
l.consumeIdentToken()
|
||||||
|
return l.r.Pos() == len(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsURLUnquoted returns true if the bytes are a valid unquoted URL.
|
||||||
|
func IsURLUnquoted(b []byte) bool {
|
||||||
|
l := NewLexer(bytes.NewBuffer(b))
|
||||||
|
l.consumeUnquotedURL()
|
||||||
|
return l.r.Pos() == len(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HSL2RGB converts HSL to RGB with all of range [0,1]
|
||||||
|
// from http://www.w3.org/TR/css3-color/#hsl-color
|
||||||
|
func HSL2RGB(h, s, l float64) (float64, float64, float64) {
|
||||||
|
m2 := l * (s + 1)
|
||||||
|
if l > 0.5 {
|
||||||
|
m2 = l + s - l*s
|
||||||
|
}
|
||||||
|
m1 := l*2 - m2
|
||||||
|
return hue2rgb(m1, m2, h+1.0/3.0), hue2rgb(m1, m2, h), hue2rgb(m1, m2, h-1.0/3.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func hue2rgb(m1, m2, h float64) float64 {
|
||||||
|
if h < 0.0 {
|
||||||
|
h += 1.0
|
||||||
|
}
|
||||||
|
if h > 1.0 {
|
||||||
|
h -= 1.0
|
||||||
|
}
|
||||||
|
if h*6.0 < 1.0 {
|
||||||
|
return m1 + (m2-m1)*h*6.0
|
||||||
|
} else if h*2.0 < 1.0 {
|
||||||
|
return m2
|
||||||
|
} else if h*3.0 < 2.0 {
|
||||||
|
return m1 + (m2-m1)*(2.0/3.0-h)*6.0
|
||||||
|
}
|
||||||
|
return m1
|
||||||
|
}
|
285
vendor/github.com/tdewolff/parse/svg/hash.go
generated
vendored
Normal file
285
vendor/github.com/tdewolff/parse/svg/hash.go
generated
vendored
Normal file
|
@ -0,0 +1,285 @@
|
||||||
|
package svg
|
||||||
|
|
||||||
|
// generated by hasher -type=Hash -file=hash.go; DO NOT EDIT, except for adding more constants to the list and rerun go generate
|
||||||
|
|
||||||
|
// uses github.com/tdewolff/hasher
|
||||||
|
//go:generate hasher -type=Hash -file=hash.go
|
||||||
|
|
||||||
|
// Hash defines perfect hashes for a predefined list of strings
|
||||||
|
type Hash uint32
|
||||||
|
|
||||||
|
// Unique hash definitions to be used instead of strings
|
||||||
|
const (
|
||||||
|
A Hash = 0x101 // a
|
||||||
|
Alignment_Baseline Hash = 0x2e12 // alignment-baseline
|
||||||
|
BaseProfile Hash = 0xb // baseProfile
|
||||||
|
Baseline_Shift Hash = 0x380e // baseline-shift
|
||||||
|
Buffered_Rendering Hash = 0x5212 // buffered-rendering
|
||||||
|
Clip Hash = 0x6404 // clip
|
||||||
|
Clip_Path Hash = 0x6409 // clip-path
|
||||||
|
Clip_Rule Hash = 0x8009 // clip-rule
|
||||||
|
Color Hash = 0xd805 // color
|
||||||
|
Color_Interpolation Hash = 0xd813 // color-interpolation
|
||||||
|
Color_Interpolation_Filters Hash = 0xd81b // color-interpolation-filters
|
||||||
|
Color_Profile Hash = 0x1ea0d // color-profile
|
||||||
|
Color_Rendering Hash = 0x2110f // color-rendering
|
||||||
|
ContentScriptType Hash = 0xa011 // contentScriptType
|
||||||
|
ContentStyleType Hash = 0xb110 // contentStyleType
|
||||||
|
Cursor Hash = 0xc106 // cursor
|
||||||
|
D Hash = 0x5901 // d
|
||||||
|
Defs Hash = 0x34b04 // defs
|
||||||
|
Direction Hash = 0x2f109 // direction
|
||||||
|
Display Hash = 0x9807 // display
|
||||||
|
Dominant_Baseline Hash = 0x18511 // dominant-baseline
|
||||||
|
Enable_Background Hash = 0x8811 // enable-background
|
||||||
|
Fill Hash = 0xc904 // fill
|
||||||
|
Fill_Opacity Hash = 0x31f0c // fill-opacity
|
||||||
|
Fill_Rule Hash = 0xc909 // fill-rule
|
||||||
|
Filter Hash = 0xec06 // filter
|
||||||
|
Flood_Color Hash = 0xd20b // flood-color
|
||||||
|
Flood_Opacity Hash = 0x1010d // flood-opacity
|
||||||
|
Font Hash = 0x11004 // font
|
||||||
|
Font_Family Hash = 0x1100b // font-family
|
||||||
|
Font_Size Hash = 0x11b09 // font-size
|
||||||
|
Font_Size_Adjust Hash = 0x11b10 // font-size-adjust
|
||||||
|
Font_Stretch Hash = 0x1330c // font-stretch
|
||||||
|
Font_Style Hash = 0x13f0a // font-style
|
||||||
|
Font_Variant Hash = 0x1490c // font-variant
|
||||||
|
Font_Weight Hash = 0x1620b // font-weight
|
||||||
|
G Hash = 0x1601 // g
|
||||||
|
Glyph_Orientation_Horizontal Hash = 0x1c61c // glyph-orientation-horizontal
|
||||||
|
Glyph_Orientation_Vertical Hash = 0x161a // glyph-orientation-vertical
|
||||||
|
Height Hash = 0x6c06 // height
|
||||||
|
Image_Rendering Hash = 0x16d0f // image-rendering
|
||||||
|
Kerning Hash = 0x1af07 // kerning
|
||||||
|
Letter_Spacing Hash = 0x90e // letter-spacing
|
||||||
|
Lighting_Color Hash = 0x1e10e // lighting-color
|
||||||
|
Line Hash = 0x3c04 // line
|
||||||
|
Marker Hash = 0x17c06 // marker
|
||||||
|
Marker_End Hash = 0x17c0a // marker-end
|
||||||
|
Marker_Mid Hash = 0x1960a // marker-mid
|
||||||
|
Marker_Start Hash = 0x1a00c // marker-start
|
||||||
|
Mask Hash = 0x1ac04 // mask
|
||||||
|
Metadata Hash = 0x1b608 // metadata
|
||||||
|
Missing_Glyph Hash = 0x1be0d // missing-glyph
|
||||||
|
Opacity Hash = 0x10707 // opacity
|
||||||
|
Overflow Hash = 0x25308 // overflow
|
||||||
|
Paint_Order Hash = 0x2930b // paint-order
|
||||||
|
Path Hash = 0x6904 // path
|
||||||
|
Pattern Hash = 0x1f707 // pattern
|
||||||
|
Pointer_Events Hash = 0x1fe0e // pointer-events
|
||||||
|
Points Hash = 0x22006 // points
|
||||||
|
Polygon Hash = 0x23207 // polygon
|
||||||
|
Polyline Hash = 0x23908 // polyline
|
||||||
|
PreserveAspectRatio Hash = 0x24113 // preserveAspectRatio
|
||||||
|
Rect Hash = 0x2f304 // rect
|
||||||
|
Rx Hash = 0x4f02 // rx
|
||||||
|
Ry Hash = 0xc602 // ry
|
||||||
|
Shape_Rendering Hash = 0xf20f // shape-rendering
|
||||||
|
Solid_Color Hash = 0x20b0b // solid-color
|
||||||
|
Solid_Opacity Hash = 0x2250d // solid-opacity
|
||||||
|
Stop_Color Hash = 0x1290a // stop-color
|
||||||
|
Stop_Opacity Hash = 0x34e0c // stop-opacity
|
||||||
|
Stroke Hash = 0x26506 // stroke
|
||||||
|
Stroke_Dasharray Hash = 0x26510 // stroke-dasharray
|
||||||
|
Stroke_Dashoffset Hash = 0x27511 // stroke-dashoffset
|
||||||
|
Stroke_Linecap Hash = 0x2860e // stroke-linecap
|
||||||
|
Stroke_Linejoin Hash = 0x29e0f // stroke-linejoin
|
||||||
|
Stroke_Miterlimit Hash = 0x2ad11 // stroke-miterlimit
|
||||||
|
Stroke_Opacity Hash = 0x2be0e // stroke-opacity
|
||||||
|
Stroke_Width Hash = 0x2cc0c // stroke-width
|
||||||
|
Style Hash = 0x14405 // style
|
||||||
|
Svg Hash = 0x2d803 // svg
|
||||||
|
Switch Hash = 0x2db06 // switch
|
||||||
|
Symbol Hash = 0x2e106 // symbol
|
||||||
|
Text_Anchor Hash = 0x450b // text-anchor
|
||||||
|
Text_Decoration Hash = 0x710f // text-decoration
|
||||||
|
Text_Rendering Hash = 0x1540e // text-rendering
|
||||||
|
Type Hash = 0x10c04 // type
|
||||||
|
Unicode_Bidi Hash = 0x2e70c // unicode-bidi
|
||||||
|
Vector_Effect Hash = 0x2fa0d // vector-effect
|
||||||
|
Version Hash = 0x30707 // version
|
||||||
|
ViewBox Hash = 0x30e07 // viewBox
|
||||||
|
Viewport_Fill Hash = 0x3160d // viewport-fill
|
||||||
|
Viewport_Fill_Opacity Hash = 0x31615 // viewport-fill-opacity
|
||||||
|
Visibility Hash = 0x32b0a // visibility
|
||||||
|
White_Space Hash = 0x25a0b // white-space
|
||||||
|
Width Hash = 0x2d305 // width
|
||||||
|
Word_Spacing Hash = 0x3350c // word-spacing
|
||||||
|
Writing_Mode Hash = 0x3410c // writing-mode
|
||||||
|
X Hash = 0x4701 // x
|
||||||
|
X1 Hash = 0x5002 // x1
|
||||||
|
X2 Hash = 0x31402 // x2
|
||||||
|
Xml_Space Hash = 0x35a09 // xml:space
|
||||||
|
Y Hash = 0x1801 // y
|
||||||
|
Y1 Hash = 0x9e02 // y1
|
||||||
|
Y2 Hash = 0xc702 // y2
|
||||||
|
)
|
||||||
|
|
||||||
|
// String returns the hash' name.
|
||||||
|
func (i Hash) String() string {
|
||||||
|
start := uint32(i >> 8)
|
||||||
|
n := uint32(i & 0xff)
|
||||||
|
if start+n > uint32(len(_Hash_text)) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return _Hash_text[start : start+n]
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToHash returns the hash whose name is s. It returns zero if there is no
|
||||||
|
// such hash. It is case sensitive.
|
||||||
|
func ToHash(s []byte) Hash {
|
||||||
|
if len(s) == 0 || len(s) > _Hash_maxLen {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
h := uint32(_Hash_hash0)
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
h ^= uint32(s[i])
|
||||||
|
h *= 16777619
|
||||||
|
}
|
||||||
|
if i := _Hash_table[h&uint32(len(_Hash_table)-1)]; int(i&0xff) == len(s) {
|
||||||
|
t := _Hash_text[i>>8 : i>>8+i&0xff]
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
if t[i] != s[i] {
|
||||||
|
goto NEXT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
NEXT:
|
||||||
|
if i := _Hash_table[(h>>16)&uint32(len(_Hash_table)-1)]; int(i&0xff) == len(s) {
|
||||||
|
t := _Hash_text[i>>8 : i>>8+i&0xff]
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
if t[i] != s[i] {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
const _Hash_hash0 = 0xf7ffa4d4
|
||||||
|
const _Hash_maxLen = 28
|
||||||
|
const _Hash_text = "baseProfiletter-spacinglyph-orientation-verticalignment-base" +
|
||||||
|
"line-shiftext-anchorx1buffered-renderingclip-patheightext-de" +
|
||||||
|
"corationclip-rulenable-backgroundisplay1contentScriptTypecon" +
|
||||||
|
"tentStyleTypecursory2fill-ruleflood-color-interpolation-filt" +
|
||||||
|
"ershape-renderingflood-opacitypefont-familyfont-size-adjusto" +
|
||||||
|
"p-colorfont-stretchfont-stylefont-variantext-renderingfont-w" +
|
||||||
|
"eightimage-renderingmarker-endominant-baselinemarker-midmark" +
|
||||||
|
"er-startmaskerningmetadatamissing-glyph-orientation-horizont" +
|
||||||
|
"alighting-color-profilepatternpointer-eventsolid-color-rende" +
|
||||||
|
"ringpointsolid-opacitypolygonpolylinepreserveAspectRatioverf" +
|
||||||
|
"lowhite-spacestroke-dasharraystroke-dashoffsetstroke-linecap" +
|
||||||
|
"aint-orderstroke-linejoinstroke-miterlimitstroke-opacitystro" +
|
||||||
|
"ke-widthsvgswitchsymbolunicode-bidirectionvector-effectversi" +
|
||||||
|
"onviewBox2viewport-fill-opacityvisibilityword-spacingwriting" +
|
||||||
|
"-modefstop-opacityxml:space"
|
||||||
|
|
||||||
|
var _Hash_table = [1 << 7]Hash{
|
||||||
|
0x0: 0x18511, // dominant-baseline
|
||||||
|
0x1: 0x1b608, // metadata
|
||||||
|
0x2: 0x11b10, // font-size-adjust
|
||||||
|
0x3: 0x11004, // font
|
||||||
|
0x4: 0x2d305, // width
|
||||||
|
0x5: 0x34b04, // defs
|
||||||
|
0x6: 0x450b, // text-anchor
|
||||||
|
0x7: 0x2fa0d, // vector-effect
|
||||||
|
0x8: 0x6904, // path
|
||||||
|
0x9: 0x13f0a, // font-style
|
||||||
|
0xa: 0x2d803, // svg
|
||||||
|
0xb: 0x2ad11, // stroke-miterlimit
|
||||||
|
0xc: 0x1c61c, // glyph-orientation-horizontal
|
||||||
|
0xd: 0x17c06, // marker
|
||||||
|
0xe: 0xa011, // contentScriptType
|
||||||
|
0xf: 0x1490c, // font-variant
|
||||||
|
0x10: 0x2e106, // symbol
|
||||||
|
0x11: 0x1fe0e, // pointer-events
|
||||||
|
0x12: 0x9e02, // y1
|
||||||
|
0x13: 0x1be0d, // missing-glyph
|
||||||
|
0x14: 0x1010d, // flood-opacity
|
||||||
|
0x16: 0x3410c, // writing-mode
|
||||||
|
0x18: 0x3350c, // word-spacing
|
||||||
|
0x1b: 0xc106, // cursor
|
||||||
|
0x1d: 0x4f02, // rx
|
||||||
|
0x1e: 0x34e0c, // stop-opacity
|
||||||
|
0x21: 0xec06, // filter
|
||||||
|
0x22: 0x2f304, // rect
|
||||||
|
0x25: 0x2e70c, // unicode-bidi
|
||||||
|
0x26: 0x8811, // enable-background
|
||||||
|
0x27: 0x6c06, // height
|
||||||
|
0x28: 0x26510, // stroke-dasharray
|
||||||
|
0x2a: 0xd805, // color
|
||||||
|
0x2c: 0x31402, // x2
|
||||||
|
0x2e: 0x17c0a, // marker-end
|
||||||
|
0x2f: 0x5002, // x1
|
||||||
|
0x30: 0x23908, // polyline
|
||||||
|
0x31: 0xc602, // ry
|
||||||
|
0x32: 0x25308, // overflow
|
||||||
|
0x33: 0x1ac04, // mask
|
||||||
|
0x34: 0x3160d, // viewport-fill
|
||||||
|
0x35: 0x31f0c, // fill-opacity
|
||||||
|
0x36: 0x5212, // buffered-rendering
|
||||||
|
0x38: 0xd81b, // color-interpolation-filters
|
||||||
|
0x39: 0x1540e, // text-rendering
|
||||||
|
0x3a: 0x23207, // polygon
|
||||||
|
0x3b: 0x710f, // text-decoration
|
||||||
|
0x3c: 0x10c04, // type
|
||||||
|
0x3d: 0x11b09, // font-size
|
||||||
|
0x3e: 0x1a00c, // marker-start
|
||||||
|
0x3f: 0x1ea0d, // color-profile
|
||||||
|
0x40: 0x22006, // points
|
||||||
|
0x41: 0x1330c, // font-stretch
|
||||||
|
0x42: 0x1290a, // stop-color
|
||||||
|
0x43: 0x9807, // display
|
||||||
|
0x44: 0x4701, // x
|
||||||
|
0x45: 0x20b0b, // solid-color
|
||||||
|
0x46: 0x3c04, // line
|
||||||
|
0x47: 0x6409, // clip-path
|
||||||
|
0x48: 0x30707, // version
|
||||||
|
0x49: 0x1601, // g
|
||||||
|
0x4b: 0x8009, // clip-rule
|
||||||
|
0x4d: 0x2db06, // switch
|
||||||
|
0x4f: 0x10707, // opacity
|
||||||
|
0x50: 0xd813, // color-interpolation
|
||||||
|
0x51: 0x2cc0c, // stroke-width
|
||||||
|
0x52: 0x6404, // clip
|
||||||
|
0x53: 0x2110f, // color-rendering
|
||||||
|
0x54: 0x26506, // stroke
|
||||||
|
0x55: 0x2f109, // direction
|
||||||
|
0x56: 0x1e10e, // lighting-color
|
||||||
|
0x57: 0x1801, // y
|
||||||
|
0x59: 0x14405, // style
|
||||||
|
0x5a: 0x380e, // baseline-shift
|
||||||
|
0x5c: 0xc904, // fill
|
||||||
|
0x5d: 0xf20f, // shape-rendering
|
||||||
|
0x5f: 0x2860e, // stroke-linecap
|
||||||
|
0x60: 0x27511, // stroke-dashoffset
|
||||||
|
0x61: 0x2930b, // paint-order
|
||||||
|
0x63: 0x2250d, // solid-opacity
|
||||||
|
0x64: 0x2e12, // alignment-baseline
|
||||||
|
0x65: 0x25a0b, // white-space
|
||||||
|
0x66: 0x1f707, // pattern
|
||||||
|
0x67: 0x2be0e, // stroke-opacity
|
||||||
|
0x68: 0x32b0a, // visibility
|
||||||
|
0x6a: 0x30e07, // viewBox
|
||||||
|
0x6b: 0x90e, // letter-spacing
|
||||||
|
0x6c: 0x1100b, // font-family
|
||||||
|
0x6d: 0xb110, // contentStyleType
|
||||||
|
0x6f: 0x101, // a
|
||||||
|
0x70: 0x5901, // d
|
||||||
|
0x71: 0x29e0f, // stroke-linejoin
|
||||||
|
0x72: 0x31615, // viewport-fill-opacity
|
||||||
|
0x73: 0xb, // baseProfile
|
||||||
|
0x74: 0x16d0f, // image-rendering
|
||||||
|
0x75: 0x24113, // preserveAspectRatio
|
||||||
|
0x76: 0xd20b, // flood-color
|
||||||
|
0x77: 0x1960a, // marker-mid
|
||||||
|
0x78: 0x1af07, // kerning
|
||||||
|
0x7a: 0x1620b, // font-weight
|
||||||
|
0x7b: 0x161a, // glyph-orientation-vertical
|
||||||
|
0x7d: 0x35a09, // xml:space
|
||||||
|
0x7e: 0xc909, // fill-rule
|
||||||
|
0x7f: 0xc702, // y2
|
||||||
|
}
|
160
vendor/github.com/tdewolff/parse/util.go
generated
vendored
Normal file
160
vendor/github.com/tdewolff/parse/util.go
generated
vendored
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
package parse
|
||||||
|
|
||||||
|
// Copy returns a copy of the given byte slice.
|
||||||
|
func Copy(src []byte) (dst []byte) {
|
||||||
|
dst = make([]byte, len(src))
|
||||||
|
copy(dst, src)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToLower converts all characters in the byte slice from A-Z to a-z.
|
||||||
|
func ToLower(src []byte) []byte {
|
||||||
|
for i, c := range src {
|
||||||
|
if c >= 'A' && c <= 'Z' {
|
||||||
|
src[i] = c + ('a' - 'A')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return src
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal returns true when s matches the target.
|
||||||
|
func Equal(s, target []byte) bool {
|
||||||
|
if len(s) != len(target) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, c := range target {
|
||||||
|
if s[i] != c {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// EqualFold returns true when s matches case-insensitively the targetLower (which must be lowercase).
|
||||||
|
func EqualFold(s, targetLower []byte) bool {
|
||||||
|
if len(s) != len(targetLower) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, c := range targetLower {
|
||||||
|
if s[i] != c && (c < 'A' && c > 'Z' || s[i]+('a'-'A') != c) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var whitespaceTable = [256]bool{
|
||||||
|
// ASCII
|
||||||
|
false, false, false, false, false, false, false, false,
|
||||||
|
false, true, true, false, true, true, false, false, // tab, new line, form feed, carriage return
|
||||||
|
false, false, false, false, false, false, false, false,
|
||||||
|
false, false, false, false, false, false, false, false,
|
||||||
|
|
||||||
|
true, false, false, false, false, false, false, false, // space
|
||||||
|
false, false, false, false, false, false, false, false,
|
||||||
|
false, false, false, false, false, false, false, false,
|
||||||
|
false, false, false, false, false, false, false, false,
|
||||||
|
|
||||||
|
false, false, false, false, false, false, false, false,
|
||||||
|
false, false, false, false, false, false, false, false,
|
||||||
|
false, false, false, false, false, false, false, false,
|
||||||
|
false, false, false, false, false, false, false, false,
|
||||||
|
|
||||||
|
false, false, false, false, false, false, false, false,
|
||||||
|
false, false, false, false, false, false, false, false,
|
||||||
|
false, false, false, false, false, false, false, false,
|
||||||
|
false, false, false, false, false, false, false, false,
|
||||||
|
|
||||||
|
// non-ASCII
|
||||||
|
false, false, false, false, false, false, false, false,
|
||||||
|
false, false, false, false, false, false, false, false,
|
||||||
|
false, false, false, false, false, false, false, false,
|
||||||
|
false, false, false, false, false, false, false, false,
|
||||||
|
|
||||||
|
false, false, false, false, false, false, false, false,
|
||||||
|
false, false, false, false, false, false, false, false,
|
||||||
|
false, false, false, false, false, false, false, false,
|
||||||
|
false, false, false, false, false, false, false, false,
|
||||||
|
|
||||||
|
false, false, false, false, false, false, false, false,
|
||||||
|
false, false, false, false, false, false, false, false,
|
||||||
|
false, false, false, false, false, false, false, false,
|
||||||
|
false, false, false, false, false, false, false, false,
|
||||||
|
|
||||||
|
false, false, false, false, false, false, false, false,
|
||||||
|
false, false, false, false, false, false, false, false,
|
||||||
|
false, false, false, false, false, false, false, false,
|
||||||
|
false, false, false, false, false, false, false, false,
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsWhitespace returns true for space, \n, \r, \t, \f.
|
||||||
|
func IsWhitespace(c byte) bool {
|
||||||
|
return whitespaceTable[c]
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAllWhitespace returns true when the entire byte slice consists of space, \n, \r, \t, \f.
|
||||||
|
func IsAllWhitespace(b []byte) bool {
|
||||||
|
for _, c := range b {
|
||||||
|
if !IsWhitespace(c) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrimWhitespace removes any leading and trailing whitespace characters.
|
||||||
|
func TrimWhitespace(b []byte) []byte {
|
||||||
|
n := len(b)
|
||||||
|
start := n
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
if !IsWhitespace(b[i]) {
|
||||||
|
start = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end := n
|
||||||
|
for i := n - 1; i >= start; i-- {
|
||||||
|
if !IsWhitespace(b[i]) {
|
||||||
|
end = i + 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b[start:end]
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplaceMultipleWhitespace replaces character series of space, \n, \t, \f, \r into a single space or newline (when the serie contained a \n or \r).
|
||||||
|
func ReplaceMultipleWhitespace(b []byte) []byte {
|
||||||
|
j := 0
|
||||||
|
prevWS := false
|
||||||
|
hasNewline := false
|
||||||
|
for i, c := range b {
|
||||||
|
if IsWhitespace(c) {
|
||||||
|
prevWS = true
|
||||||
|
if c == '\n' || c == '\r' {
|
||||||
|
hasNewline = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if prevWS {
|
||||||
|
prevWS = false
|
||||||
|
if hasNewline {
|
||||||
|
hasNewline = false
|
||||||
|
b[j] = '\n'
|
||||||
|
} else {
|
||||||
|
b[j] = ' '
|
||||||
|
}
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
b[j] = b[i]
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if prevWS {
|
||||||
|
if hasNewline {
|
||||||
|
b[j] = '\n'
|
||||||
|
} else {
|
||||||
|
b[j] = ' '
|
||||||
|
}
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
return b[:j]
|
||||||
|
}
|
101
vendor/github.com/tdewolff/parse/xml/README.md
generated
vendored
Normal file
101
vendor/github.com/tdewolff/parse/xml/README.md
generated
vendored
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
# XML [![GoDoc](http://godoc.org/github.com/tdewolff/parse/xml?status.svg)](http://godoc.org/github.com/tdewolff/parse/xml) [![GoCover](http://gocover.io/_badge/github.com/tdewolff/parse/xml)](http://gocover.io/github.com/tdewolff/parse/xml)
|
||||||
|
|
||||||
|
This package is an XML lexer written in [Go][1]. It follows the specification at [Extensible Markup Language (XML) 1.0 (Fifth Edition)](http://www.w3.org/TR/REC-xml/). The lexer takes an io.Reader and converts it into tokens until the EOF.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
Run the following command
|
||||||
|
|
||||||
|
go get github.com/tdewolff/parse/xml
|
||||||
|
|
||||||
|
or add the following import and run project with `go get`
|
||||||
|
|
||||||
|
import "github.com/tdewolff/parse/xml"
|
||||||
|
|
||||||
|
## Lexer
|
||||||
|
### Usage
|
||||||
|
The following initializes a new Lexer with io.Reader `r`:
|
||||||
|
``` go
|
||||||
|
l := xml.NewLexer(r)
|
||||||
|
```
|
||||||
|
|
||||||
|
To tokenize until EOF an error, use:
|
||||||
|
``` go
|
||||||
|
for {
|
||||||
|
tt, data := l.Next()
|
||||||
|
switch tt {
|
||||||
|
case xml.ErrorToken:
|
||||||
|
// error or EOF set in l.Err()
|
||||||
|
return
|
||||||
|
case xml.StartTagToken:
|
||||||
|
// ...
|
||||||
|
for {
|
||||||
|
ttAttr, dataAttr := l.Next()
|
||||||
|
if ttAttr != xml.AttributeToken {
|
||||||
|
// handle StartTagCloseToken/StartTagCloseVoidToken/StartTagClosePIToken
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
case xml.EndTagToken:
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
All tokens:
|
||||||
|
``` go
|
||||||
|
ErrorToken TokenType = iota // extra token when errors occur
|
||||||
|
CommentToken
|
||||||
|
CDATAToken
|
||||||
|
StartTagToken
|
||||||
|
StartTagCloseToken
|
||||||
|
StartTagCloseVoidToken
|
||||||
|
StartTagClosePIToken
|
||||||
|
EndTagToken
|
||||||
|
AttributeToken
|
||||||
|
TextToken
|
||||||
|
```
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
``` go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/tdewolff/parse/xml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Tokenize XML from stdin.
|
||||||
|
func main() {
|
||||||
|
l := xml.NewLexer(os.Stdin)
|
||||||
|
for {
|
||||||
|
tt, data := l.Next()
|
||||||
|
switch tt {
|
||||||
|
case xml.ErrorToken:
|
||||||
|
if l.Err() != io.EOF {
|
||||||
|
fmt.Println("Error on line", l.Line(), ":", l.Err())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
case xml.StartTagToken:
|
||||||
|
fmt.Println("Tag", string(data))
|
||||||
|
for {
|
||||||
|
ttAttr, dataAttr := l.Next()
|
||||||
|
if ttAttr != xml.AttributeToken {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
key := dataAttr
|
||||||
|
val := l.AttrVal()
|
||||||
|
fmt.Println("Attribute", string(key), "=", string(val))
|
||||||
|
}
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
Released under the [MIT license](https://github.com/tdewolff/parse/blob/master/LICENSE.md).
|
||||||
|
|
||||||
|
[1]: http://golang.org/ "Go Language"
|
338
vendor/github.com/tdewolff/parse/xml/lex.go
generated
vendored
Normal file
338
vendor/github.com/tdewolff/parse/xml/lex.go
generated
vendored
Normal file
|
@ -0,0 +1,338 @@
|
||||||
|
// Package xml is an XML1.0 lexer following the specifications at http://www.w3.org/TR/xml/.
|
||||||
|
package xml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/tdewolff/buffer"
|
||||||
|
)
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// TokenType determines the type of token, eg. a number or a semicolon.
|
||||||
|
type TokenType uint32
|
||||||
|
|
||||||
|
// TokenType values.
|
||||||
|
const (
|
||||||
|
ErrorToken TokenType = iota // extra token when errors occur
|
||||||
|
CommentToken
|
||||||
|
DOCTYPEToken
|
||||||
|
CDATAToken
|
||||||
|
StartTagToken
|
||||||
|
StartTagPIToken
|
||||||
|
StartTagCloseToken
|
||||||
|
StartTagCloseVoidToken
|
||||||
|
StartTagClosePIToken
|
||||||
|
EndTagToken
|
||||||
|
AttributeToken
|
||||||
|
TextToken
|
||||||
|
)
|
||||||
|
|
||||||
|
// String returns the string representation of a TokenType.
|
||||||
|
func (tt TokenType) String() string {
|
||||||
|
switch tt {
|
||||||
|
case ErrorToken:
|
||||||
|
return "Error"
|
||||||
|
case CommentToken:
|
||||||
|
return "Comment"
|
||||||
|
case DOCTYPEToken:
|
||||||
|
return "DOCTYPE"
|
||||||
|
case CDATAToken:
|
||||||
|
return "CDATA"
|
||||||
|
case StartTagToken:
|
||||||
|
return "StartTag"
|
||||||
|
case StartTagPIToken:
|
||||||
|
return "StartTagPI"
|
||||||
|
case StartTagCloseToken:
|
||||||
|
return "StartTagClose"
|
||||||
|
case StartTagCloseVoidToken:
|
||||||
|
return "StartTagCloseVoid"
|
||||||
|
case StartTagClosePIToken:
|
||||||
|
return "StartTagClosePI"
|
||||||
|
case EndTagToken:
|
||||||
|
return "EndTag"
|
||||||
|
case AttributeToken:
|
||||||
|
return "Attribute"
|
||||||
|
case TextToken:
|
||||||
|
return "Text"
|
||||||
|
}
|
||||||
|
return "Invalid(" + strconv.Itoa(int(tt)) + ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Lexer is the state for the lexer.
|
||||||
|
type Lexer struct {
|
||||||
|
r *buffer.Lexer
|
||||||
|
inTag bool
|
||||||
|
|
||||||
|
text []byte
|
||||||
|
attrVal []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLexer returns a new Lexer for a given io.Reader.
|
||||||
|
func NewLexer(r io.Reader) *Lexer {
|
||||||
|
return &Lexer{
|
||||||
|
r: buffer.NewLexer(r),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err returns the error encountered during lexing, this is often io.EOF but also other errors can be returned.
|
||||||
|
func (l *Lexer) Err() error {
|
||||||
|
return l.r.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free frees up bytes of length n from previously shifted tokens.
|
||||||
|
func (l *Lexer) Free(n int) {
|
||||||
|
l.r.Free(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next returns the next Token. It returns ErrorToken when an error was encountered. Using Err() one can retrieve the error message.
|
||||||
|
func (l *Lexer) Next() (TokenType, []byte) {
|
||||||
|
l.text = nil
|
||||||
|
var c byte
|
||||||
|
if l.inTag {
|
||||||
|
l.attrVal = nil
|
||||||
|
for { // before attribute name state
|
||||||
|
if c = l.r.Peek(0); c == ' ' || c == '\t' || c == '\n' || c == '\r' {
|
||||||
|
l.r.Move(1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if c == 0 {
|
||||||
|
return ErrorToken, nil
|
||||||
|
} else if c != '>' && (c != '/' && c != '?' || l.r.Peek(1) != '>') {
|
||||||
|
return AttributeToken, l.shiftAttribute()
|
||||||
|
}
|
||||||
|
start := l.r.Pos()
|
||||||
|
l.inTag = false
|
||||||
|
if c == '/' {
|
||||||
|
l.r.Move(2)
|
||||||
|
l.text = l.r.Lexeme()[start:]
|
||||||
|
return StartTagCloseVoidToken, l.r.Shift()
|
||||||
|
} else if c == '?' {
|
||||||
|
l.r.Move(2)
|
||||||
|
l.text = l.r.Lexeme()[start:]
|
||||||
|
return StartTagClosePIToken, l.r.Shift()
|
||||||
|
} else {
|
||||||
|
l.r.Move(1)
|
||||||
|
l.text = l.r.Lexeme()[start:]
|
||||||
|
return StartTagCloseToken, l.r.Shift()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
c = l.r.Peek(0)
|
||||||
|
if c == '<' {
|
||||||
|
if l.r.Pos() > 0 {
|
||||||
|
return TextToken, l.r.Shift()
|
||||||
|
}
|
||||||
|
c = l.r.Peek(1)
|
||||||
|
if c == '/' {
|
||||||
|
l.r.Move(2)
|
||||||
|
return EndTagToken, l.shiftEndTag()
|
||||||
|
} else if c == '!' {
|
||||||
|
l.r.Move(2)
|
||||||
|
if l.at('-', '-') {
|
||||||
|
l.r.Move(2)
|
||||||
|
return CommentToken, l.shiftCommentText()
|
||||||
|
} else if l.at('[', 'C', 'D', 'A', 'T', 'A', '[') {
|
||||||
|
l.r.Move(7)
|
||||||
|
return CDATAToken, l.shiftCDATAText()
|
||||||
|
} else if l.at('D', 'O', 'C', 'T', 'Y', 'P', 'E') {
|
||||||
|
l.r.Move(8)
|
||||||
|
return DOCTYPEToken, l.shiftDOCTYPEText()
|
||||||
|
}
|
||||||
|
l.r.Move(-2)
|
||||||
|
} else if c == '?' {
|
||||||
|
l.r.Move(2)
|
||||||
|
l.inTag = true
|
||||||
|
return StartTagPIToken, l.shiftStartTag()
|
||||||
|
}
|
||||||
|
l.r.Move(1)
|
||||||
|
l.inTag = true
|
||||||
|
return StartTagToken, l.shiftStartTag()
|
||||||
|
} else if c == 0 {
|
||||||
|
if l.r.Pos() > 0 {
|
||||||
|
return TextToken, l.r.Shift()
|
||||||
|
}
|
||||||
|
return ErrorToken, nil
|
||||||
|
}
|
||||||
|
l.r.Move(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Text returns the textual representation of a token. This excludes delimiters and additional leading/trailing characters.
|
||||||
|
func (l *Lexer) Text() []byte {
|
||||||
|
return l.text
|
||||||
|
}
|
||||||
|
|
||||||
|
// AttrVal returns the attribute value when an AttributeToken was returned from Next.
|
||||||
|
func (l *Lexer) AttrVal() []byte {
|
||||||
|
return l.attrVal
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// The following functions follow the specifications at http://www.w3.org/html/wg/drafts/html/master/syntax.html
|
||||||
|
|
||||||
|
func (l *Lexer) shiftDOCTYPEText() []byte {
|
||||||
|
inString := false
|
||||||
|
inBrackets := false
|
||||||
|
for {
|
||||||
|
c := l.r.Peek(0)
|
||||||
|
if c == '"' {
|
||||||
|
inString = !inString
|
||||||
|
} else if (c == '[' || c == ']') && !inString {
|
||||||
|
inBrackets = (c == '[')
|
||||||
|
} else if c == '>' && !inString && !inBrackets {
|
||||||
|
l.text = l.r.Lexeme()[9:]
|
||||||
|
l.r.Move(1)
|
||||||
|
return l.r.Shift()
|
||||||
|
} else if c == 0 {
|
||||||
|
l.text = l.r.Lexeme()[9:]
|
||||||
|
return l.r.Shift()
|
||||||
|
}
|
||||||
|
l.r.Move(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Lexer) shiftCDATAText() []byte {
|
||||||
|
for {
|
||||||
|
c := l.r.Peek(0)
|
||||||
|
if c == ']' && l.r.Peek(1) == ']' && l.r.Peek(2) == '>' {
|
||||||
|
l.text = l.r.Lexeme()[9:]
|
||||||
|
l.r.Move(3)
|
||||||
|
return l.r.Shift()
|
||||||
|
} else if c == 0 {
|
||||||
|
l.text = l.r.Lexeme()[9:]
|
||||||
|
return l.r.Shift()
|
||||||
|
}
|
||||||
|
l.r.Move(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Lexer) shiftCommentText() []byte {
|
||||||
|
for {
|
||||||
|
c := l.r.Peek(0)
|
||||||
|
if c == '-' && l.r.Peek(1) == '-' && l.r.Peek(2) == '>' {
|
||||||
|
l.text = l.r.Lexeme()[4:]
|
||||||
|
l.r.Move(3)
|
||||||
|
return l.r.Shift()
|
||||||
|
} else if c == 0 {
|
||||||
|
return l.r.Shift()
|
||||||
|
}
|
||||||
|
l.r.Move(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Lexer) shiftStartTag() []byte {
|
||||||
|
nameStart := l.r.Pos()
|
||||||
|
for {
|
||||||
|
if c := l.r.Peek(0); c == ' ' || c == '>' || (c == '/' || c == '?') && l.r.Peek(1) == '>' || c == '\t' || c == '\n' || c == '\r' || c == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
l.r.Move(1)
|
||||||
|
}
|
||||||
|
l.text = l.r.Lexeme()[nameStart:]
|
||||||
|
return l.r.Shift()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Lexer) shiftAttribute() []byte {
|
||||||
|
nameStart := l.r.Pos()
|
||||||
|
var c byte
|
||||||
|
for { // attribute name state
|
||||||
|
if c = l.r.Peek(0); c == ' ' || c == '=' || c == '>' || (c == '/' || c == '?') && l.r.Peek(1) == '>' || c == '\t' || c == '\n' || c == '\r' || c == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
l.r.Move(1)
|
||||||
|
}
|
||||||
|
nameEnd := l.r.Pos()
|
||||||
|
for { // after attribute name state
|
||||||
|
if c = l.r.Peek(0); c == ' ' || c == '\t' || c == '\n' || c == '\r' {
|
||||||
|
l.r.Move(1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if c == '=' {
|
||||||
|
l.r.Move(1)
|
||||||
|
for { // before attribute value state
|
||||||
|
if c = l.r.Peek(0); c == ' ' || c == '\t' || c == '\n' || c == '\r' {
|
||||||
|
l.r.Move(1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
attrPos := l.r.Pos()
|
||||||
|
delim := c
|
||||||
|
if delim == '"' || delim == '\'' { // attribute value single- and double-quoted state
|
||||||
|
l.r.Move(1)
|
||||||
|
for {
|
||||||
|
c = l.r.Peek(0)
|
||||||
|
if c == delim {
|
||||||
|
l.r.Move(1)
|
||||||
|
break
|
||||||
|
} else if c == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
l.r.Move(1)
|
||||||
|
if c == '\t' || c == '\n' || c == '\r' {
|
||||||
|
l.r.Lexeme()[l.r.Pos()-1] = ' '
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { // attribute value unquoted state
|
||||||
|
for {
|
||||||
|
if c = l.r.Peek(0); c == ' ' || c == '>' || (c == '/' || c == '?') && l.r.Peek(1) == '>' || c == '\t' || c == '\n' || c == '\r' || c == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
l.r.Move(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l.attrVal = l.r.Lexeme()[attrPos:]
|
||||||
|
} else {
|
||||||
|
l.r.Rewind(nameEnd)
|
||||||
|
l.attrVal = nil
|
||||||
|
}
|
||||||
|
l.text = l.r.Lexeme()[nameStart:nameEnd]
|
||||||
|
return l.r.Shift()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Lexer) shiftEndTag() []byte {
|
||||||
|
for {
|
||||||
|
c := l.r.Peek(0)
|
||||||
|
if c == '>' {
|
||||||
|
l.text = l.r.Lexeme()[2:]
|
||||||
|
l.r.Move(1)
|
||||||
|
break
|
||||||
|
} else if c == 0 {
|
||||||
|
l.text = l.r.Lexeme()[2:]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
l.r.Move(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
end := len(l.text)
|
||||||
|
for end > 0 {
|
||||||
|
if c := l.text[end-1]; c == ' ' || c == '\t' || c == '\n' || c == '\r' {
|
||||||
|
end--
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
l.text = l.text[:end]
|
||||||
|
return l.r.Shift()
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
func (l *Lexer) at(b ...byte) bool {
|
||||||
|
for i, c := range b {
|
||||||
|
if l.r.Peek(i) != c {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
108
vendor/github.com/tdewolff/parse/xml/util.go
generated
vendored
Normal file
108
vendor/github.com/tdewolff/parse/xml/util.go
generated
vendored
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
package xml
|
||||||
|
|
||||||
|
import "github.com/tdewolff/parse"
|
||||||
|
|
||||||
|
var (
|
||||||
|
ltEntityBytes = []byte("<")
|
||||||
|
ampEntityBytes = []byte("&")
|
||||||
|
singleQuoteEntityBytes = []byte("'")
|
||||||
|
doubleQuoteEntityBytes = []byte(""")
|
||||||
|
)
|
||||||
|
|
||||||
|
// EscapeAttrVal returns the escape attribute value bytes without quotes.
|
||||||
|
func EscapeAttrVal(buf *[]byte, b []byte) []byte {
|
||||||
|
singles := 0
|
||||||
|
doubles := 0
|
||||||
|
for i, c := range b {
|
||||||
|
if c == '&' {
|
||||||
|
if quote, n := parse.QuoteEntity(b[i:]); n > 0 {
|
||||||
|
if quote == '"' {
|
||||||
|
doubles++
|
||||||
|
} else {
|
||||||
|
singles++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if c == '"' {
|
||||||
|
doubles++
|
||||||
|
} else if c == '\'' {
|
||||||
|
singles++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
n := len(b) + 2
|
||||||
|
var quote byte
|
||||||
|
var escapedQuote []byte
|
||||||
|
if doubles > singles {
|
||||||
|
n += singles * 4
|
||||||
|
quote = '\''
|
||||||
|
escapedQuote = singleQuoteEntityBytes
|
||||||
|
} else {
|
||||||
|
n += doubles * 4
|
||||||
|
quote = '"'
|
||||||
|
escapedQuote = doubleQuoteEntityBytes
|
||||||
|
}
|
||||||
|
if n > cap(*buf) {
|
||||||
|
*buf = make([]byte, 0, n) // maximum size, not actual size
|
||||||
|
}
|
||||||
|
t := (*buf)[:n] // maximum size, not actual size
|
||||||
|
t[0] = quote
|
||||||
|
j := 1
|
||||||
|
start := 0
|
||||||
|
for i, c := range b {
|
||||||
|
if c == '&' {
|
||||||
|
if entityQuote, n := parse.QuoteEntity(b[i:]); n > 0 {
|
||||||
|
j += copy(t[j:], b[start:i])
|
||||||
|
if entityQuote != quote {
|
||||||
|
t[j] = entityQuote
|
||||||
|
j++
|
||||||
|
} else {
|
||||||
|
j += copy(t[j:], escapedQuote)
|
||||||
|
}
|
||||||
|
start = i + n
|
||||||
|
}
|
||||||
|
} else if c == quote {
|
||||||
|
j += copy(t[j:], b[start:i])
|
||||||
|
j += copy(t[j:], escapedQuote)
|
||||||
|
start = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
j += copy(t[j:], b[start:])
|
||||||
|
t[j] = quote
|
||||||
|
return t[:j+1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// EscapeCDATAVal returns the escaped text bytes.
|
||||||
|
func EscapeCDATAVal(buf *[]byte, b []byte) ([]byte, bool) {
|
||||||
|
n := 0
|
||||||
|
for _, c := range b {
|
||||||
|
if c == '<' || c == '&' {
|
||||||
|
if c == '<' {
|
||||||
|
n += 3 // <
|
||||||
|
} else {
|
||||||
|
n += 4 // &
|
||||||
|
}
|
||||||
|
if n > len("<![CDATA[]]>") {
|
||||||
|
return b, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(b)+n > cap(*buf) {
|
||||||
|
*buf = make([]byte, 0, len(b)+n)
|
||||||
|
}
|
||||||
|
t := (*buf)[:len(b)+n]
|
||||||
|
j := 0
|
||||||
|
start := 0
|
||||||
|
for i, c := range b {
|
||||||
|
if c == '<' {
|
||||||
|
j += copy(t[j:], b[start:i])
|
||||||
|
j += copy(t[j:], ltEntityBytes)
|
||||||
|
start = i + 1
|
||||||
|
} else if c == '&' {
|
||||||
|
j += copy(t[j:], b[start:i])
|
||||||
|
j += copy(t[j:], ampEntityBytes)
|
||||||
|
start = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
j += copy(t[j:], b[start:])
|
||||||
|
return t[:j], true
|
||||||
|
}
|
22
vendor/github.com/tdewolff/strconv/LICENSE.md
generated
vendored
Normal file
22
vendor/github.com/tdewolff/strconv/LICENSE.md
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
Copyright (c) 2015 Taco de Wolff
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use,
|
||||||
|
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following
|
||||||
|
conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
10
vendor/github.com/tdewolff/strconv/README.md
generated
vendored
Normal file
10
vendor/github.com/tdewolff/strconv/README.md
generated
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# Strconv [![GoDoc](http://godoc.org/github.com/tdewolff/strconv?status.svg)](http://godoc.org/github.com/tdewolff/strconv)
|
||||||
|
|
||||||
|
This package contains string conversion function and is written in [Go][1]. It is much alike the standard library's strconv package, but it is specifically tailored for the performance needs within the minify package.
|
||||||
|
|
||||||
|
For example, the floating-point to string conversion function is approximately twice as fast as the standard library, but it is not as precise.
|
||||||
|
|
||||||
|
## License
|
||||||
|
Released under the [MIT license](LICENSE.md).
|
||||||
|
|
||||||
|
[1]: http://golang.org/ "Go Language"
|
251
vendor/github.com/tdewolff/strconv/float.go
generated
vendored
Normal file
251
vendor/github.com/tdewolff/strconv/float.go
generated
vendored
Normal file
|
@ -0,0 +1,251 @@
|
||||||
|
package strconv
|
||||||
|
|
||||||
|
import "math"
|
||||||
|
|
||||||
|
var float64pow10 = []float64{
|
||||||
|
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
|
||||||
|
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
|
||||||
|
1e20, 1e21, 1e22,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float parses a byte-slice and returns the float it represents.
|
||||||
|
// If an invalid character is encountered, it will stop there.
|
||||||
|
func ParseFloat(b []byte) (float64, int) {
|
||||||
|
i := 0
|
||||||
|
neg := false
|
||||||
|
if i < len(b) && (b[i] == '+' || b[i] == '-') {
|
||||||
|
neg = b[i] == '-'
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
dot := -1
|
||||||
|
trunk := -1
|
||||||
|
n := uint64(0)
|
||||||
|
for ; i < len(b); i++ {
|
||||||
|
c := b[i]
|
||||||
|
if c >= '0' && c <= '9' {
|
||||||
|
if trunk == -1 {
|
||||||
|
if n > math.MaxUint64/10 {
|
||||||
|
trunk = i
|
||||||
|
} else {
|
||||||
|
n *= 10
|
||||||
|
n += uint64(c - '0')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if dot == -1 && c == '.' {
|
||||||
|
dot = i
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f := float64(n)
|
||||||
|
if neg {
|
||||||
|
f = -f
|
||||||
|
}
|
||||||
|
|
||||||
|
mantExp := int64(0)
|
||||||
|
if dot != -1 {
|
||||||
|
if trunk == -1 {
|
||||||
|
trunk = i
|
||||||
|
}
|
||||||
|
mantExp = int64(trunk - dot - 1)
|
||||||
|
} else if trunk != -1 {
|
||||||
|
mantExp = int64(trunk - i)
|
||||||
|
}
|
||||||
|
expExp := int64(0)
|
||||||
|
if i < len(b) && (b[i] == 'e' || b[i] == 'E') {
|
||||||
|
i++
|
||||||
|
if e, expLen := ParseInt(b[i:]); expLen > 0 {
|
||||||
|
expExp = e
|
||||||
|
i += expLen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exp := expExp - mantExp
|
||||||
|
|
||||||
|
// copied from strconv/atof.go
|
||||||
|
if exp == 0 {
|
||||||
|
return f, i
|
||||||
|
} else if exp > 0 && exp <= 15+22 { // int * 10^k
|
||||||
|
// If exponent is big but number of digits is not,
|
||||||
|
// can move a few zeros into the integer part.
|
||||||
|
if exp > 22 {
|
||||||
|
f *= float64pow10[exp-22]
|
||||||
|
exp = 22
|
||||||
|
}
|
||||||
|
if f <= 1e15 && f >= -1e15 {
|
||||||
|
return f * float64pow10[exp], i
|
||||||
|
}
|
||||||
|
} else if exp < 0 && exp >= -22 { // int / 10^k
|
||||||
|
return f / float64pow10[-exp], i
|
||||||
|
}
|
||||||
|
f *= math.Pow10(int(-mantExp))
|
||||||
|
return f * math.Pow10(int(expExp)), i
|
||||||
|
}
|
||||||
|
|
||||||
|
const log2 = 0.301029995
|
||||||
|
const int64maxlen = 18
|
||||||
|
|
||||||
|
func float64exp(f float64) int {
|
||||||
|
exp2 := 0
|
||||||
|
if f != 0.0 {
|
||||||
|
x := math.Float64bits(f)
|
||||||
|
exp2 = int(x>>(64-11-1))&0x7FF - 1023 + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
exp10 := float64(exp2) * log2
|
||||||
|
if exp10 < 0 {
|
||||||
|
exp10 -= 1.0
|
||||||
|
}
|
||||||
|
return int(exp10)
|
||||||
|
}
|
||||||
|
|
||||||
|
func AppendFloat(b []byte, f float64, prec int) ([]byte, bool) {
|
||||||
|
if math.IsNaN(f) || math.IsInf(f, 0) {
|
||||||
|
return b, false
|
||||||
|
} else if prec >= int64maxlen {
|
||||||
|
return b, false
|
||||||
|
}
|
||||||
|
|
||||||
|
neg := false
|
||||||
|
if f < 0.0 {
|
||||||
|
f = -f
|
||||||
|
neg = true
|
||||||
|
}
|
||||||
|
if prec == -1 {
|
||||||
|
prec = int64maxlen - 1
|
||||||
|
}
|
||||||
|
prec -= float64exp(f) // number of digits in front of the dot
|
||||||
|
f *= math.Pow10(prec)
|
||||||
|
|
||||||
|
// calculate mantissa and exponent
|
||||||
|
mant := int64(f)
|
||||||
|
mantLen := LenInt(mant)
|
||||||
|
mantExp := mantLen - prec - 1
|
||||||
|
if mant == 0 {
|
||||||
|
return append(b, '0'), true
|
||||||
|
}
|
||||||
|
|
||||||
|
// expLen is zero for positive exponents, because positive exponents are determined later on in the big conversion loop
|
||||||
|
exp := 0
|
||||||
|
expLen := 0
|
||||||
|
if mantExp > 0 {
|
||||||
|
// positive exponent is determined in the loop below
|
||||||
|
// but if we initially decreased the exponent to fit in an integer, we can't set the new exponent in the loop alone,
|
||||||
|
// since the number of zeros at the end determines the positive exponent in the loop, and we just artificially lost zeros
|
||||||
|
if prec < 0 {
|
||||||
|
exp = mantExp
|
||||||
|
}
|
||||||
|
expLen = 1 + LenInt(int64(exp)) // e + digits
|
||||||
|
} else if mantExp < -3 {
|
||||||
|
exp = mantExp
|
||||||
|
expLen = 2 + LenInt(int64(exp)) // e + minus + digits
|
||||||
|
} else if mantExp < -1 {
|
||||||
|
mantLen += -mantExp - 1 // extra zero between dot and first digit
|
||||||
|
}
|
||||||
|
|
||||||
|
// reserve space in b
|
||||||
|
i := len(b)
|
||||||
|
maxLen := 1 + mantLen + expLen // dot + mantissa digits + exponent
|
||||||
|
if neg {
|
||||||
|
maxLen++
|
||||||
|
}
|
||||||
|
if i+maxLen > cap(b) {
|
||||||
|
b = append(b, make([]byte, maxLen)...)
|
||||||
|
} else {
|
||||||
|
b = b[:i+maxLen]
|
||||||
|
}
|
||||||
|
|
||||||
|
// write to string representation
|
||||||
|
if neg {
|
||||||
|
b[i] = '-'
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
// big conversion loop, start at the end and move to the front
|
||||||
|
// initially print trailing zeros and remove them later on
|
||||||
|
// for example if the first non-zero digit is three positions in front of the dot, it will overwrite the zeros with a positive exponent
|
||||||
|
zero := true
|
||||||
|
last := i + mantLen // right-most position of digit that is non-zero + dot
|
||||||
|
dot := last - prec - exp // position of dot
|
||||||
|
j := last
|
||||||
|
for mant > 0 {
|
||||||
|
if j == dot {
|
||||||
|
b[j] = '.'
|
||||||
|
j--
|
||||||
|
}
|
||||||
|
newMant := mant / 10
|
||||||
|
digit := mant - 10*newMant
|
||||||
|
if zero && digit > 0 {
|
||||||
|
// first non-zero digit, if we are still behind the dot we can trim the end to this position
|
||||||
|
// otherwise trim to the dot (including the dot)
|
||||||
|
if j > dot {
|
||||||
|
i = j + 1
|
||||||
|
// decrease negative exponent further to get rid of dot
|
||||||
|
if exp < 0 {
|
||||||
|
newExp := exp - (j - dot)
|
||||||
|
// getting rid of the dot shouldn't lower the exponent to more digits (e.g. -9 -> -10)
|
||||||
|
if LenInt(int64(newExp)) == LenInt(int64(exp)) {
|
||||||
|
exp = newExp
|
||||||
|
dot = j
|
||||||
|
j--
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
i = dot
|
||||||
|
}
|
||||||
|
last = j
|
||||||
|
zero = false
|
||||||
|
}
|
||||||
|
b[j] = '0' + byte(digit)
|
||||||
|
j--
|
||||||
|
mant = newMant
|
||||||
|
}
|
||||||
|
|
||||||
|
if j > dot {
|
||||||
|
// extra zeros behind the dot
|
||||||
|
for j > dot {
|
||||||
|
b[j] = '0'
|
||||||
|
j--
|
||||||
|
}
|
||||||
|
b[j] = '.'
|
||||||
|
} else if last+3 < dot {
|
||||||
|
// add positive exponent because we have 3 or more zeros in front of the dot
|
||||||
|
i = last + 1
|
||||||
|
exp = dot - last - 1
|
||||||
|
} else if j == dot {
|
||||||
|
// handle 0.1
|
||||||
|
b[j] = '.'
|
||||||
|
}
|
||||||
|
|
||||||
|
// exponent
|
||||||
|
if exp != 0 {
|
||||||
|
if exp == 1 {
|
||||||
|
b[i] = '0'
|
||||||
|
i++
|
||||||
|
} else if exp == 2 {
|
||||||
|
b[i] = '0'
|
||||||
|
b[i+1] = '0'
|
||||||
|
i += 2
|
||||||
|
} else {
|
||||||
|
b[i] = 'e'
|
||||||
|
i++
|
||||||
|
if exp < 0 {
|
||||||
|
b[i] = '-'
|
||||||
|
i++
|
||||||
|
exp = -exp
|
||||||
|
}
|
||||||
|
i += LenInt(int64(exp))
|
||||||
|
j := i
|
||||||
|
for exp > 0 {
|
||||||
|
newExp := exp / 10
|
||||||
|
digit := exp - 10*newExp
|
||||||
|
j--
|
||||||
|
b[j] = '0' + byte(digit)
|
||||||
|
exp = newExp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b[:i], true
|
||||||
|
}
|
78
vendor/github.com/tdewolff/strconv/int.go
generated
vendored
Normal file
78
vendor/github.com/tdewolff/strconv/int.go
generated
vendored
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
package strconv
|
||||||
|
|
||||||
|
import "math"
|
||||||
|
|
||||||
|
// Int parses a byte-slice and returns the integer it represents.
|
||||||
|
// If an invalid character is encountered, it will stop there.
|
||||||
|
func ParseInt(b []byte) (int64, int) {
|
||||||
|
i := 0
|
||||||
|
neg := false
|
||||||
|
if len(b) > 0 && (b[0] == '+' || b[0] == '-') {
|
||||||
|
neg = b[0] == '-'
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
n := uint64(0)
|
||||||
|
for i < len(b) {
|
||||||
|
c := b[i]
|
||||||
|
if n > math.MaxUint64/10 {
|
||||||
|
return 0, 0
|
||||||
|
} else if c >= '0' && c <= '9' {
|
||||||
|
n *= 10
|
||||||
|
n += uint64(c - '0')
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if !neg && n > uint64(math.MaxInt64) || n > uint64(math.MaxInt64)+1 {
|
||||||
|
return 0, 0
|
||||||
|
} else if neg {
|
||||||
|
return -int64(n), i
|
||||||
|
}
|
||||||
|
return int64(n), i
|
||||||
|
}
|
||||||
|
|
||||||
|
func LenInt(i int64) int {
|
||||||
|
if i < 0 {
|
||||||
|
i = -i
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case i < 10:
|
||||||
|
return 1
|
||||||
|
case i < 100:
|
||||||
|
return 2
|
||||||
|
case i < 1000:
|
||||||
|
return 3
|
||||||
|
case i < 10000:
|
||||||
|
return 4
|
||||||
|
case i < 100000:
|
||||||
|
return 5
|
||||||
|
case i < 1000000:
|
||||||
|
return 6
|
||||||
|
case i < 10000000:
|
||||||
|
return 7
|
||||||
|
case i < 100000000:
|
||||||
|
return 8
|
||||||
|
case i < 1000000000:
|
||||||
|
return 9
|
||||||
|
case i < 10000000000:
|
||||||
|
return 10
|
||||||
|
case i < 100000000000:
|
||||||
|
return 11
|
||||||
|
case i < 1000000000000:
|
||||||
|
return 12
|
||||||
|
case i < 10000000000000:
|
||||||
|
return 13
|
||||||
|
case i < 100000000000000:
|
||||||
|
return 14
|
||||||
|
case i < 1000000000000000:
|
||||||
|
return 15
|
||||||
|
case i < 10000000000000000:
|
||||||
|
return 16
|
||||||
|
case i < 100000000000000000:
|
||||||
|
return 17
|
||||||
|
case i < 1000000000000000000:
|
||||||
|
return 18
|
||||||
|
}
|
||||||
|
return 19
|
||||||
|
}
|
27
vendor/golang.org/x/image/LICENSE
generated
vendored
Normal file
27
vendor/golang.org/x/image/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
22
vendor/golang.org/x/image/PATENTS
generated
vendored
Normal file
22
vendor/golang.org/x/image/PATENTS
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
Additional IP Rights Grant (Patents)
|
||||||
|
|
||||||
|
"This implementation" means the copyrightable works distributed by
|
||||||
|
Google as part of the Go project.
|
||||||
|
|
||||||
|
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||||
|
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||||
|
patent license to make, have made, use, offer to sell, sell, import,
|
||||||
|
transfer and otherwise run, modify and propagate the contents of this
|
||||||
|
implementation of Go, where such license applies only to those patent
|
||||||
|
claims, both currently owned or controlled by Google and acquired in
|
||||||
|
the future, licensable by Google that are necessarily infringed by this
|
||||||
|
implementation of Go. This grant does not include claims that would be
|
||||||
|
infringed only as a consequence of further modification of this
|
||||||
|
implementation. If you or your agent or exclusive licensee institute or
|
||||||
|
order or agree to the institution of patent litigation against any
|
||||||
|
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||||
|
that this implementation of Go or any code incorporated within this
|
||||||
|
implementation of Go constitutes direct or contributory patent
|
||||||
|
infringement, or inducement of patent infringement, then any patent
|
||||||
|
rights granted to you under this License for this implementation of Go
|
||||||
|
shall terminate as of the date such litigation is filed.
|
281
vendor/golang.org/x/image/font/font.go
generated
vendored
Normal file
281
vendor/golang.org/x/image/font/font.go
generated
vendored
Normal file
|
@ -0,0 +1,281 @@
|
||||||
|
// 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 font defines an interface for font faces, for drawing text on an
|
||||||
|
// image.
|
||||||
|
//
|
||||||
|
// Other packages provide font face implementations. For example, a truetype
|
||||||
|
// package would provide one based on .ttf font files.
|
||||||
|
package font
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/draw"
|
||||||
|
"io"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"golang.org/x/image/math/fixed"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: who is responsible for caches (glyph images, glyph indices, kerns)?
|
||||||
|
// The Drawer or the Face?
|
||||||
|
|
||||||
|
// Face is a font face. Its glyphs are often derived from a font file, such as
|
||||||
|
// "Comic_Sans_MS.ttf", but a face has a specific size, style, weight and
|
||||||
|
// hinting. For example, the 12pt and 18pt versions of Comic Sans are two
|
||||||
|
// different faces, even if derived from the same font file.
|
||||||
|
//
|
||||||
|
// A Face is not safe for concurrent use by multiple goroutines, as its methods
|
||||||
|
// may re-use implementation-specific caches and mask image buffers.
|
||||||
|
//
|
||||||
|
// To create a Face, look to other packages that implement specific font file
|
||||||
|
// formats.
|
||||||
|
type Face interface {
|
||||||
|
io.Closer
|
||||||
|
|
||||||
|
// Glyph returns the draw.DrawMask parameters (dr, mask, maskp) to draw r's
|
||||||
|
// glyph at the sub-pixel destination location dot, and that glyph's
|
||||||
|
// advance width.
|
||||||
|
//
|
||||||
|
// It returns !ok if the face does not contain a glyph for r.
|
||||||
|
//
|
||||||
|
// The contents of the mask image returned by one Glyph call may change
|
||||||
|
// after the next Glyph call. Callers that want to cache the mask must make
|
||||||
|
// a copy.
|
||||||
|
Glyph(dot fixed.Point26_6, r rune) (
|
||||||
|
dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool)
|
||||||
|
|
||||||
|
// GlyphBounds returns the bounding box of r's glyph, drawn at a dot equal
|
||||||
|
// to the origin, and that glyph's advance width.
|
||||||
|
//
|
||||||
|
// It returns !ok if the face does not contain a glyph for r.
|
||||||
|
//
|
||||||
|
// The glyph's ascent and descent equal -bounds.Min.Y and +bounds.Max.Y. A
|
||||||
|
// visual depiction of what these metrics are is at
|
||||||
|
// https://developer.apple.com/library/mac/documentation/TextFonts/Conceptual/CocoaTextArchitecture/Art/glyph_metrics_2x.png
|
||||||
|
GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool)
|
||||||
|
|
||||||
|
// GlyphAdvance returns the advance width of r's glyph.
|
||||||
|
//
|
||||||
|
// It returns !ok if the face does not contain a glyph for r.
|
||||||
|
GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool)
|
||||||
|
|
||||||
|
// Kern returns the horizontal adjustment for the kerning pair (r0, r1). A
|
||||||
|
// positive kern means to move the glyphs further apart.
|
||||||
|
Kern(r0, r1 rune) fixed.Int26_6
|
||||||
|
|
||||||
|
// Metrics returns the metrics for this Face.
|
||||||
|
Metrics() Metrics
|
||||||
|
|
||||||
|
// TODO: ColoredGlyph for various emoji?
|
||||||
|
// TODO: Ligatures? Shaping?
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metrics holds the metrics for a Face. A visual depiction is at
|
||||||
|
// https://developer.apple.com/library/mac/documentation/TextFonts/Conceptual/CocoaTextArchitecture/Art/glyph_metrics_2x.png
|
||||||
|
type Metrics struct {
|
||||||
|
// Height is the recommended amount of vertical space between two lines of
|
||||||
|
// text.
|
||||||
|
Height fixed.Int26_6
|
||||||
|
|
||||||
|
// Ascent is the distance from the top of a line to its baseline.
|
||||||
|
Ascent fixed.Int26_6
|
||||||
|
|
||||||
|
// Descent is the distance from the bottom of a line to its baseline. The
|
||||||
|
// value is typically positive, even though a descender goes below the
|
||||||
|
// baseline.
|
||||||
|
Descent fixed.Int26_6
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drawer draws text on a destination image.
|
||||||
|
//
|
||||||
|
// A Drawer is not safe for concurrent use by multiple goroutines, since its
|
||||||
|
// Face is not.
|
||||||
|
type Drawer struct {
|
||||||
|
// Dst is the destination image.
|
||||||
|
Dst draw.Image
|
||||||
|
// Src is the source image.
|
||||||
|
Src image.Image
|
||||||
|
// Face provides the glyph mask images.
|
||||||
|
Face Face
|
||||||
|
// Dot is the baseline location to draw the next glyph. The majority of the
|
||||||
|
// affected pixels will be above and to the right of the dot, but some may
|
||||||
|
// be below or to the left. For example, drawing a 'j' in an italic face
|
||||||
|
// may affect pixels below and to the left of the dot.
|
||||||
|
Dot fixed.Point26_6
|
||||||
|
|
||||||
|
// TODO: Clip image.Image?
|
||||||
|
// TODO: SrcP image.Point for Src images other than *image.Uniform? How
|
||||||
|
// does it get updated during DrawString?
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: should DrawString return the last rune drawn, so the next DrawString
|
||||||
|
// call can kern beforehand? Or should that be the responsibility of the caller
|
||||||
|
// if they really want to do that, since they have to explicitly shift d.Dot
|
||||||
|
// anyway? What if ligatures span more than two runes? What if grapheme
|
||||||
|
// clusters span multiple runes?
|
||||||
|
//
|
||||||
|
// TODO: do we assume that the input is in any particular Unicode Normalization
|
||||||
|
// Form?
|
||||||
|
//
|
||||||
|
// TODO: have DrawRunes(s []rune)? DrawRuneReader(io.RuneReader)?? If we take
|
||||||
|
// io.RuneReader, we can't assume that we can rewind the stream.
|
||||||
|
//
|
||||||
|
// TODO: how does this work with line breaking: drawing text up until a
|
||||||
|
// vertical line? Should DrawString return the number of runes drawn?
|
||||||
|
|
||||||
|
// DrawBytes draws s at the dot and advances the dot's location.
|
||||||
|
func (d *Drawer) DrawBytes(s []byte) {
|
||||||
|
prevC := rune(-1)
|
||||||
|
for len(s) > 0 {
|
||||||
|
c, size := utf8.DecodeRune(s)
|
||||||
|
s = s[size:]
|
||||||
|
if prevC >= 0 {
|
||||||
|
d.Dot.X += d.Face.Kern(prevC, c)
|
||||||
|
}
|
||||||
|
dr, mask, maskp, advance, ok := d.Face.Glyph(d.Dot, c)
|
||||||
|
if !ok {
|
||||||
|
// TODO: is falling back on the U+FFFD glyph the responsibility of
|
||||||
|
// the Drawer or the Face?
|
||||||
|
// TODO: set prevC = '\ufffd'?
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
draw.DrawMask(d.Dst, dr, d.Src, image.Point{}, mask, maskp, draw.Over)
|
||||||
|
d.Dot.X += advance
|
||||||
|
prevC = c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DrawString draws s at the dot and advances the dot's location.
|
||||||
|
func (d *Drawer) DrawString(s string) {
|
||||||
|
prevC := rune(-1)
|
||||||
|
for _, c := range s {
|
||||||
|
if prevC >= 0 {
|
||||||
|
d.Dot.X += d.Face.Kern(prevC, c)
|
||||||
|
}
|
||||||
|
dr, mask, maskp, advance, ok := d.Face.Glyph(d.Dot, c)
|
||||||
|
if !ok {
|
||||||
|
// TODO: is falling back on the U+FFFD glyph the responsibility of
|
||||||
|
// the Drawer or the Face?
|
||||||
|
// TODO: set prevC = '\ufffd'?
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
draw.DrawMask(d.Dst, dr, d.Src, image.Point{}, mask, maskp, draw.Over)
|
||||||
|
d.Dot.X += advance
|
||||||
|
prevC = c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MeasureBytes returns how far dot would advance by drawing s.
|
||||||
|
func (d *Drawer) MeasureBytes(s []byte) (advance fixed.Int26_6) {
|
||||||
|
return MeasureBytes(d.Face, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MeasureString returns how far dot would advance by drawing s.
|
||||||
|
func (d *Drawer) MeasureString(s string) (advance fixed.Int26_6) {
|
||||||
|
return MeasureString(d.Face, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MeasureBytes returns how far dot would advance by drawing s with f.
|
||||||
|
func MeasureBytes(f Face, s []byte) (advance fixed.Int26_6) {
|
||||||
|
prevC := rune(-1)
|
||||||
|
for len(s) > 0 {
|
||||||
|
c, size := utf8.DecodeRune(s)
|
||||||
|
s = s[size:]
|
||||||
|
if prevC >= 0 {
|
||||||
|
advance += f.Kern(prevC, c)
|
||||||
|
}
|
||||||
|
a, ok := f.GlyphAdvance(c)
|
||||||
|
if !ok {
|
||||||
|
// TODO: is falling back on the U+FFFD glyph the responsibility of
|
||||||
|
// the Drawer or the Face?
|
||||||
|
// TODO: set prevC = '\ufffd'?
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
advance += a
|
||||||
|
prevC = c
|
||||||
|
}
|
||||||
|
return advance
|
||||||
|
}
|
||||||
|
|
||||||
|
// MeasureString returns how far dot would advance by drawing s with f.
|
||||||
|
func MeasureString(f Face, s string) (advance fixed.Int26_6) {
|
||||||
|
prevC := rune(-1)
|
||||||
|
for _, c := range s {
|
||||||
|
if prevC >= 0 {
|
||||||
|
advance += f.Kern(prevC, c)
|
||||||
|
}
|
||||||
|
a, ok := f.GlyphAdvance(c)
|
||||||
|
if !ok {
|
||||||
|
// TODO: is falling back on the U+FFFD glyph the responsibility of
|
||||||
|
// the Drawer or the Face?
|
||||||
|
// TODO: set prevC = '\ufffd'?
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
advance += a
|
||||||
|
prevC = c
|
||||||
|
}
|
||||||
|
return advance
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hinting selects how to quantize a vector font's glyph nodes.
|
||||||
|
//
|
||||||
|
// Not all fonts support hinting.
|
||||||
|
type Hinting int
|
||||||
|
|
||||||
|
const (
|
||||||
|
HintingNone Hinting = iota
|
||||||
|
HintingVertical
|
||||||
|
HintingFull
|
||||||
|
)
|
||||||
|
|
||||||
|
// Stretch selects a normal, condensed, or expanded face.
|
||||||
|
//
|
||||||
|
// Not all fonts support stretches.
|
||||||
|
type Stretch int
|
||||||
|
|
||||||
|
const (
|
||||||
|
StretchUltraCondensed Stretch = -4
|
||||||
|
StretchExtraCondensed Stretch = -3
|
||||||
|
StretchCondensed Stretch = -2
|
||||||
|
StretchSemiCondensed Stretch = -1
|
||||||
|
StretchNormal Stretch = +0
|
||||||
|
StretchSemiExpanded Stretch = +1
|
||||||
|
StretchExpanded Stretch = +2
|
||||||
|
StretchExtraExpanded Stretch = +3
|
||||||
|
StretchUltraExpanded Stretch = +4
|
||||||
|
)
|
||||||
|
|
||||||
|
// Style selects a normal, italic, or oblique face.
|
||||||
|
//
|
||||||
|
// Not all fonts support styles.
|
||||||
|
type Style int
|
||||||
|
|
||||||
|
const (
|
||||||
|
StyleNormal Style = iota
|
||||||
|
StyleItalic
|
||||||
|
StyleOblique
|
||||||
|
)
|
||||||
|
|
||||||
|
// Weight selects a normal, light or bold face.
|
||||||
|
//
|
||||||
|
// Not all fonts support weights.
|
||||||
|
//
|
||||||
|
// The named Weight constants (e.g. WeightBold) correspond to CSS' common
|
||||||
|
// weight names (e.g. "Bold"), but the numerical values differ, so that in Go,
|
||||||
|
// the zero value means to use a normal weight. For the CSS names and values,
|
||||||
|
// see https://developer.mozilla.org/en/docs/Web/CSS/font-weight
|
||||||
|
type Weight int
|
||||||
|
|
||||||
|
const (
|
||||||
|
WeightThin Weight = -3 // CSS font-weight value 100.
|
||||||
|
WeightExtraLight Weight = -2 // CSS font-weight value 200.
|
||||||
|
WeightLight Weight = -1 // CSS font-weight value 300.
|
||||||
|
WeightNormal Weight = +0 // CSS font-weight value 400.
|
||||||
|
WeightMedium Weight = +1 // CSS font-weight value 500.
|
||||||
|
WeightSemiBold Weight = +2 // CSS font-weight value 600.
|
||||||
|
WeightBold Weight = +3 // CSS font-weight value 700.
|
||||||
|
WeightExtraBold Weight = +4 // CSS font-weight value 800.
|
||||||
|
WeightBlack Weight = +5 // CSS font-weight value 900.
|
||||||
|
)
|
202
vendor/golang.org/x/image/math/fixed/fixed.go
generated
vendored
Normal file
202
vendor/golang.org/x/image/math/fixed/fixed.go
generated
vendored
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
// 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 fixed implements fixed-point integer types.
|
||||||
|
package fixed
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: implement fmt.Formatter for %f and %g.
|
||||||
|
|
||||||
|
// I returns the integer value i as an Int26_6.
|
||||||
|
//
|
||||||
|
// For example, passing the integer value 2 yields Int26_6(128).
|
||||||
|
func I(i int) Int26_6 {
|
||||||
|
return Int26_6(i << 6)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int26_6 is a signed 26.6 fixed-point number.
|
||||||
|
//
|
||||||
|
// The integer part ranges from -33554432 to 33554431, inclusive. The
|
||||||
|
// fractional part has 6 bits of precision.
|
||||||
|
//
|
||||||
|
// For example, the number one-and-a-quarter is Int26_6(1<<6 + 1<<4).
|
||||||
|
type Int26_6 int32
|
||||||
|
|
||||||
|
// String returns a human-readable representation of a 26.6 fixed-point number.
|
||||||
|
//
|
||||||
|
// For example, the number one-and-a-quarter becomes "1:16".
|
||||||
|
func (x Int26_6) String() string {
|
||||||
|
const shift, mask = 6, 1<<6 - 1
|
||||||
|
if x >= 0 {
|
||||||
|
return fmt.Sprintf("%d:%02d", int32(x>>shift), int32(x&mask))
|
||||||
|
}
|
||||||
|
x = -x
|
||||||
|
if x >= 0 {
|
||||||
|
return fmt.Sprintf("-%d:%02d", int32(x>>shift), int32(x&mask))
|
||||||
|
}
|
||||||
|
return "-33554432:00" // The minimum value is -(1<<25).
|
||||||
|
}
|
||||||
|
|
||||||
|
// Floor returns the greatest integer value less than or equal to x.
|
||||||
|
//
|
||||||
|
// Its return type is int, not Int26_6.
|
||||||
|
func (x Int26_6) Floor() int { return int((x + 0x00) >> 6) }
|
||||||
|
|
||||||
|
// Round returns the nearest integer value to x. Ties are rounded up.
|
||||||
|
//
|
||||||
|
// Its return type is int, not Int26_6.
|
||||||
|
func (x Int26_6) Round() int { return int((x + 0x20) >> 6) }
|
||||||
|
|
||||||
|
// Ceil returns the least integer value greater than or equal to x.
|
||||||
|
//
|
||||||
|
// Its return type is int, not Int26_6.
|
||||||
|
func (x Int26_6) Ceil() int { return int((x + 0x3f) >> 6) }
|
||||||
|
|
||||||
|
// Int52_12 is a signed 52.12 fixed-point number.
|
||||||
|
//
|
||||||
|
// The integer part ranges from -2251799813685248 to 2251799813685247,
|
||||||
|
// inclusive. The fractional part has 12 bits of precision.
|
||||||
|
//
|
||||||
|
// For example, the number one-and-a-quarter is Int52_12(1<<12 + 1<<10).
|
||||||
|
type Int52_12 int64
|
||||||
|
|
||||||
|
// String returns a human-readable representation of a 52.12 fixed-point
|
||||||
|
// number.
|
||||||
|
//
|
||||||
|
// For example, the number one-and-a-quarter becomes "1:1024".
|
||||||
|
func (x Int52_12) String() string {
|
||||||
|
const shift, mask = 12, 1<<12 - 1
|
||||||
|
if x >= 0 {
|
||||||
|
return fmt.Sprintf("%d:%04d", int64(x>>shift), int64(x&mask))
|
||||||
|
}
|
||||||
|
x = -x
|
||||||
|
if x >= 0 {
|
||||||
|
return fmt.Sprintf("-%d:%04d", int64(x>>shift), int64(x&mask))
|
||||||
|
}
|
||||||
|
return "-2251799813685248:0000" // The minimum value is -(1<<51).
|
||||||
|
}
|
||||||
|
|
||||||
|
// Floor returns the greatest integer value less than or equal to x.
|
||||||
|
//
|
||||||
|
// Its return type is int, not Int52_12.
|
||||||
|
func (x Int52_12) Floor() int { return int((x + 0x000) >> 12) }
|
||||||
|
|
||||||
|
// Round returns the nearest integer value to x. Ties are rounded up.
|
||||||
|
//
|
||||||
|
// Its return type is int, not Int52_12.
|
||||||
|
func (x Int52_12) Round() int { return int((x + 0x800) >> 12) }
|
||||||
|
|
||||||
|
// Ceil returns the least integer value greater than or equal to x.
|
||||||
|
//
|
||||||
|
// Its return type is int, not Int52_12.
|
||||||
|
func (x Int52_12) Ceil() int { return int((x + 0xfff) >> 12) }
|
||||||
|
|
||||||
|
// P returns the integer values x and y as a Point26_6.
|
||||||
|
//
|
||||||
|
// For example, passing the integer values (2, -3) yields Point26_6{128, -192}.
|
||||||
|
func P(x, y int) Point26_6 {
|
||||||
|
return Point26_6{Int26_6(x << 6), Int26_6(y << 6)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Point26_6 is a 26.6 fixed-point coordinate pair.
|
||||||
|
//
|
||||||
|
// It is analogous to the image.Point type in the standard library.
|
||||||
|
type Point26_6 struct {
|
||||||
|
X, Y Int26_6
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add returns the vector p+q.
|
||||||
|
func (p Point26_6) Add(q Point26_6) Point26_6 {
|
||||||
|
return Point26_6{p.X + q.X, p.Y + q.Y}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub returns the vector p-q.
|
||||||
|
func (p Point26_6) Sub(q Point26_6) Point26_6 {
|
||||||
|
return Point26_6{p.X - q.X, p.Y - q.Y}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mul returns the vector p*k.
|
||||||
|
func (p Point26_6) Mul(k Int26_6) Point26_6 {
|
||||||
|
return Point26_6{p.X * k / 64, p.Y * k / 64}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Div returns the vector p/k.
|
||||||
|
func (p Point26_6) Div(k Int26_6) Point26_6 {
|
||||||
|
return Point26_6{p.X * 64 / k, p.Y * 64 / k}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Point52_12 is a 52.12 fixed-point coordinate pair.
|
||||||
|
//
|
||||||
|
// It is analogous to the image.Point type in the standard library.
|
||||||
|
type Point52_12 struct {
|
||||||
|
X, Y Int52_12
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add returns the vector p+q.
|
||||||
|
func (p Point52_12) Add(q Point52_12) Point52_12 {
|
||||||
|
return Point52_12{p.X + q.X, p.Y + q.Y}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub returns the vector p-q.
|
||||||
|
func (p Point52_12) Sub(q Point52_12) Point52_12 {
|
||||||
|
return Point52_12{p.X - q.X, p.Y - q.Y}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mul returns the vector p*k.
|
||||||
|
func (p Point52_12) Mul(k Int52_12) Point52_12 {
|
||||||
|
return Point52_12{p.X * k / 4096, p.Y * k / 4096}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Div returns the vector p/k.
|
||||||
|
func (p Point52_12) Div(k Int52_12) Point52_12 {
|
||||||
|
return Point52_12{p.X * 4096 / k, p.Y * 4096 / k}
|
||||||
|
}
|
||||||
|
|
||||||
|
// R returns the integer values minX, minY, maxX, maxY as a Rectangle26_6.
|
||||||
|
//
|
||||||
|
// For example, passing the integer values (0, 1, 2, 3) yields
|
||||||
|
// Rectangle26_6{Point26_6{0, 64}, Point26_6{128, 192}}.
|
||||||
|
//
|
||||||
|
// Like the image.Rect function in the standard library, the returned rectangle
|
||||||
|
// has minimum and maximum coordinates swapped if necessary so that it is
|
||||||
|
// well-formed.
|
||||||
|
func R(minX, minY, maxX, maxY int) Rectangle26_6 {
|
||||||
|
if minX > maxX {
|
||||||
|
minX, maxX = maxX, minX
|
||||||
|
}
|
||||||
|
if minY > maxY {
|
||||||
|
minY, maxY = maxY, minY
|
||||||
|
}
|
||||||
|
return Rectangle26_6{
|
||||||
|
Point26_6{
|
||||||
|
Int26_6(minX << 6),
|
||||||
|
Int26_6(minY << 6),
|
||||||
|
},
|
||||||
|
Point26_6{
|
||||||
|
Int26_6(maxX << 6),
|
||||||
|
Int26_6(maxY << 6),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rectangle26_6 is a 26.6 fixed-point coordinate rectangle. The Min bound is
|
||||||
|
// inclusive and the Max bound is exclusive. It is well-formed if Min.X <=
|
||||||
|
// Max.X and likewise for Y.
|
||||||
|
//
|
||||||
|
// It is analogous to the image.Rectangle type in the standard library.
|
||||||
|
type Rectangle26_6 struct {
|
||||||
|
Min, Max Point26_6
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rectangle52_12 is a 52.12 fixed-point coordinate rectangle. The Min bound is
|
||||||
|
// inclusive and the Max bound is exclusive. It is well-formed if Min.X <=
|
||||||
|
// Max.X and likewise for Y.
|
||||||
|
//
|
||||||
|
// It is analogous to the image.Rectangle type in the standard library.
|
||||||
|
type Rectangle52_12 struct {
|
||||||
|
Min, Max Point52_12
|
||||||
|
}
|
188
vendor/gopkg.in/yaml.v2/LICENSE
generated
vendored
Normal file
188
vendor/gopkg.in/yaml.v2/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,188 @@
|
||||||
|
|
||||||
|
Copyright (c) 2011-2014 - Canonical Inc.
|
||||||
|
|
||||||
|
This software is licensed under the LGPLv3, included below.
|
||||||
|
|
||||||
|
As a special exception to the GNU Lesser General Public License version 3
|
||||||
|
("LGPL3"), the copyright holders of this Library give you permission to
|
||||||
|
convey to a third party a Combined Work that links statically or dynamically
|
||||||
|
to this Library without providing any Minimal Corresponding Source or
|
||||||
|
Minimal Application Code as set out in 4d or providing the installation
|
||||||
|
information set out in section 4e, provided that you comply with the other
|
||||||
|
provisions of LGPL3 and provided that you meet, for the Application the
|
||||||
|
terms and conditions of the license(s) which apply to the Application.
|
||||||
|
|
||||||
|
Except as stated in this special exception, the provisions of LGPL3 will
|
||||||
|
continue to comply in full to this Library. If you modify this Library, you
|
||||||
|
may apply this exception to your version of this Library, but you are not
|
||||||
|
obliged to do so. If you do not wish to do so, delete this exception
|
||||||
|
statement from your version. This exception does not (and cannot) modify any
|
||||||
|
license terms which apply to the Application, with which you must still
|
||||||
|
comply.
|
||||||
|
|
||||||
|
|
||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
|
||||||
|
This version of the GNU Lesser General Public License incorporates
|
||||||
|
the terms and conditions of version 3 of the GNU General Public
|
||||||
|
License, supplemented by the additional permissions listed below.
|
||||||
|
|
||||||
|
0. Additional Definitions.
|
||||||
|
|
||||||
|
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||||
|
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||||
|
General Public License.
|
||||||
|
|
||||||
|
"The Library" refers to a covered work governed by this License,
|
||||||
|
other than an Application or a Combined Work as defined below.
|
||||||
|
|
||||||
|
An "Application" is any work that makes use of an interface provided
|
||||||
|
by the Library, but which is not otherwise based on the Library.
|
||||||
|
Defining a subclass of a class defined by the Library is deemed a mode
|
||||||
|
of using an interface provided by the Library.
|
||||||
|
|
||||||
|
A "Combined Work" is a work produced by combining or linking an
|
||||||
|
Application with the Library. The particular version of the Library
|
||||||
|
with which the Combined Work was made is also called the "Linked
|
||||||
|
Version".
|
||||||
|
|
||||||
|
The "Minimal Corresponding Source" for a Combined Work means the
|
||||||
|
Corresponding Source for the Combined Work, excluding any source code
|
||||||
|
for portions of the Combined Work that, considered in isolation, are
|
||||||
|
based on the Application, and not on the Linked Version.
|
||||||
|
|
||||||
|
The "Corresponding Application Code" for a Combined Work means the
|
||||||
|
object code and/or source code for the Application, including any data
|
||||||
|
and utility programs needed for reproducing the Combined Work from the
|
||||||
|
Application, but excluding the System Libraries of the Combined Work.
|
||||||
|
|
||||||
|
1. Exception to Section 3 of the GNU GPL.
|
||||||
|
|
||||||
|
You may convey a covered work under sections 3 and 4 of this License
|
||||||
|
without being bound by section 3 of the GNU GPL.
|
||||||
|
|
||||||
|
2. Conveying Modified Versions.
|
||||||
|
|
||||||
|
If you modify a copy of the Library, and, in your modifications, a
|
||||||
|
facility refers to a function or data to be supplied by an Application
|
||||||
|
that uses the facility (other than as an argument passed when the
|
||||||
|
facility is invoked), then you may convey a copy of the modified
|
||||||
|
version:
|
||||||
|
|
||||||
|
a) under this License, provided that you make a good faith effort to
|
||||||
|
ensure that, in the event an Application does not supply the
|
||||||
|
function or data, the facility still operates, and performs
|
||||||
|
whatever part of its purpose remains meaningful, or
|
||||||
|
|
||||||
|
b) under the GNU GPL, with none of the additional permissions of
|
||||||
|
this License applicable to that copy.
|
||||||
|
|
||||||
|
3. Object Code Incorporating Material from Library Header Files.
|
||||||
|
|
||||||
|
The object code form of an Application may incorporate material from
|
||||||
|
a header file that is part of the Library. You may convey such object
|
||||||
|
code under terms of your choice, provided that, if the incorporated
|
||||||
|
material is not limited to numerical parameters, data structure
|
||||||
|
layouts and accessors, or small macros, inline functions and templates
|
||||||
|
(ten or fewer lines in length), you do both of the following:
|
||||||
|
|
||||||
|
a) Give prominent notice with each copy of the object code that the
|
||||||
|
Library is used in it and that the Library and its use are
|
||||||
|
covered by this License.
|
||||||
|
|
||||||
|
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||||
|
document.
|
||||||
|
|
||||||
|
4. Combined Works.
|
||||||
|
|
||||||
|
You may convey a Combined Work under terms of your choice that,
|
||||||
|
taken together, effectively do not restrict modification of the
|
||||||
|
portions of the Library contained in the Combined Work and reverse
|
||||||
|
engineering for debugging such modifications, if you also do each of
|
||||||
|
the following:
|
||||||
|
|
||||||
|
a) Give prominent notice with each copy of the Combined Work that
|
||||||
|
the Library is used in it and that the Library and its use are
|
||||||
|
covered by this License.
|
||||||
|
|
||||||
|
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||||
|
document.
|
||||||
|
|
||||||
|
c) For a Combined Work that displays copyright notices during
|
||||||
|
execution, include the copyright notice for the Library among
|
||||||
|
these notices, as well as a reference directing the user to the
|
||||||
|
copies of the GNU GPL and this license document.
|
||||||
|
|
||||||
|
d) Do one of the following:
|
||||||
|
|
||||||
|
0) Convey the Minimal Corresponding Source under the terms of this
|
||||||
|
License, and the Corresponding Application Code in a form
|
||||||
|
suitable for, and under terms that permit, the user to
|
||||||
|
recombine or relink the Application with a modified version of
|
||||||
|
the Linked Version to produce a modified Combined Work, in the
|
||||||
|
manner specified by section 6 of the GNU GPL for conveying
|
||||||
|
Corresponding Source.
|
||||||
|
|
||||||
|
1) Use a suitable shared library mechanism for linking with the
|
||||||
|
Library. A suitable mechanism is one that (a) uses at run time
|
||||||
|
a copy of the Library already present on the user's computer
|
||||||
|
system, and (b) will operate properly with a modified version
|
||||||
|
of the Library that is interface-compatible with the Linked
|
||||||
|
Version.
|
||||||
|
|
||||||
|
e) Provide Installation Information, but only if you would otherwise
|
||||||
|
be required to provide such information under section 6 of the
|
||||||
|
GNU GPL, and only to the extent that such information is
|
||||||
|
necessary to install and execute a modified version of the
|
||||||
|
Combined Work produced by recombining or relinking the
|
||||||
|
Application with a modified version of the Linked Version. (If
|
||||||
|
you use option 4d0, the Installation Information must accompany
|
||||||
|
the Minimal Corresponding Source and Corresponding Application
|
||||||
|
Code. If you use option 4d1, you must provide the Installation
|
||||||
|
Information in the manner specified by section 6 of the GNU GPL
|
||||||
|
for conveying Corresponding Source.)
|
||||||
|
|
||||||
|
5. Combined Libraries.
|
||||||
|
|
||||||
|
You may place library facilities that are a work based on the
|
||||||
|
Library side by side in a single library together with other library
|
||||||
|
facilities that are not Applications and are not covered by this
|
||||||
|
License, and convey such a combined library under terms of your
|
||||||
|
choice, if you do both of the following:
|
||||||
|
|
||||||
|
a) Accompany the combined library with a copy of the same work based
|
||||||
|
on the Library, uncombined with any other library facilities,
|
||||||
|
conveyed under the terms of this License.
|
||||||
|
|
||||||
|
b) Give prominent notice with the combined library that part of it
|
||||||
|
is a work based on the Library, and explaining where to find the
|
||||||
|
accompanying uncombined form of the same work.
|
||||||
|
|
||||||
|
6. Revised Versions of the GNU Lesser General Public License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the GNU Lesser General Public License from time to time. Such new
|
||||||
|
versions will be similar in spirit to the present version, but may
|
||||||
|
differ in detail to address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Library as you received it specifies that a certain numbered version
|
||||||
|
of the GNU Lesser General Public License "or any later version"
|
||||||
|
applies to it, you have the option of following the terms and
|
||||||
|
conditions either of that published version or of any later version
|
||||||
|
published by the Free Software Foundation. If the Library as you
|
||||||
|
received it does not specify a version number of the GNU Lesser
|
||||||
|
General Public License, you may choose any version of the GNU Lesser
|
||||||
|
General Public License ever published by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Library as you received it specifies that a proxy can decide
|
||||||
|
whether future versions of the GNU Lesser General Public License shall
|
||||||
|
apply, that proxy's public statement of acceptance of any version is
|
||||||
|
permanent authorization for you to choose that version for the
|
||||||
|
Library.
|
31
vendor/gopkg.in/yaml.v2/LICENSE.libyaml
generated
vendored
Normal file
31
vendor/gopkg.in/yaml.v2/LICENSE.libyaml
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
The following files were ported to Go from C files of libyaml, and thus
|
||||||
|
are still covered by their original copyright and license:
|
||||||
|
|
||||||
|
apic.go
|
||||||
|
emitterc.go
|
||||||
|
parserc.go
|
||||||
|
readerc.go
|
||||||
|
scannerc.go
|
||||||
|
writerc.go
|
||||||
|
yamlh.go
|
||||||
|
yamlprivateh.go
|
||||||
|
|
||||||
|
Copyright (c) 2006 Kirill Simonov
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
131
vendor/gopkg.in/yaml.v2/README.md
generated
vendored
Normal file
131
vendor/gopkg.in/yaml.v2/README.md
generated
vendored
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
# YAML support for the Go language
|
||||||
|
|
||||||
|
Introduction
|
||||||
|
------------
|
||||||
|
|
||||||
|
The yaml package enables Go programs to comfortably encode and decode YAML
|
||||||
|
values. It was developed within [Canonical](https://www.canonical.com) as
|
||||||
|
part of the [juju](https://juju.ubuntu.com) project, and is based on a
|
||||||
|
pure Go port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML)
|
||||||
|
C library to parse and generate YAML data quickly and reliably.
|
||||||
|
|
||||||
|
Compatibility
|
||||||
|
-------------
|
||||||
|
|
||||||
|
The yaml package supports most of YAML 1.1 and 1.2, including support for
|
||||||
|
anchors, tags, map merging, etc. Multi-document unmarshalling is not yet
|
||||||
|
implemented, and base-60 floats from YAML 1.1 are purposefully not
|
||||||
|
supported since they're a poor design and are gone in YAML 1.2.
|
||||||
|
|
||||||
|
Installation and usage
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
The import path for the package is *gopkg.in/yaml.v2*.
|
||||||
|
|
||||||
|
To install it, run:
|
||||||
|
|
||||||
|
go get gopkg.in/yaml.v2
|
||||||
|
|
||||||
|
API documentation
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
If opened in a browser, the import path itself leads to the API documentation:
|
||||||
|
|
||||||
|
* [https://gopkg.in/yaml.v2](https://gopkg.in/yaml.v2)
|
||||||
|
|
||||||
|
API stability
|
||||||
|
-------------
|
||||||
|
|
||||||
|
The package API for yaml v2 will remain stable as described in [gopkg.in](https://gopkg.in).
|
||||||
|
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
|
||||||
|
The yaml package is licensed under the LGPL with an exception that allows it to be linked statically. Please see the LICENSE file for details.
|
||||||
|
|
||||||
|
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
|
||||||
|
```Go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var data = `
|
||||||
|
a: Easy!
|
||||||
|
b:
|
||||||
|
c: 2
|
||||||
|
d: [3, 4]
|
||||||
|
`
|
||||||
|
|
||||||
|
type T struct {
|
||||||
|
A string
|
||||||
|
B struct {
|
||||||
|
RenamedC int `yaml:"c"`
|
||||||
|
D []int `yaml:",flow"`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
t := T{}
|
||||||
|
|
||||||
|
err := yaml.Unmarshal([]byte(data), &t)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("error: %v", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("--- t:\n%v\n\n", t)
|
||||||
|
|
||||||
|
d, err := yaml.Marshal(&t)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("error: %v", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("--- t dump:\n%s\n\n", string(d))
|
||||||
|
|
||||||
|
m := make(map[interface{}]interface{})
|
||||||
|
|
||||||
|
err = yaml.Unmarshal([]byte(data), &m)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("error: %v", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("--- m:\n%v\n\n", m)
|
||||||
|
|
||||||
|
d, err = yaml.Marshal(&m)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("error: %v", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("--- m dump:\n%s\n\n", string(d))
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This example will generate the following output:
|
||||||
|
|
||||||
|
```
|
||||||
|
--- t:
|
||||||
|
{Easy! {2 [3 4]}}
|
||||||
|
|
||||||
|
--- t dump:
|
||||||
|
a: Easy!
|
||||||
|
b:
|
||||||
|
c: 2
|
||||||
|
d: [3, 4]
|
||||||
|
|
||||||
|
|
||||||
|
--- m:
|
||||||
|
map[a:Easy! b:map[c:2 d:[3 4]]]
|
||||||
|
|
||||||
|
--- m dump:
|
||||||
|
a: Easy!
|
||||||
|
b:
|
||||||
|
c: 2
|
||||||
|
d:
|
||||||
|
- 3
|
||||||
|
- 4
|
||||||
|
```
|
||||||
|
|
742
vendor/gopkg.in/yaml.v2/apic.go
generated
vendored
Normal file
742
vendor/gopkg.in/yaml.v2/apic.go
generated
vendored
Normal file
|
@ -0,0 +1,742 @@
|
||||||
|
package yaml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func yaml_insert_token(parser *yaml_parser_t, pos int, token *yaml_token_t) {
|
||||||
|
//fmt.Println("yaml_insert_token", "pos:", pos, "typ:", token.typ, "head:", parser.tokens_head, "len:", len(parser.tokens))
|
||||||
|
|
||||||
|
// Check if we can move the queue at the beginning of the buffer.
|
||||||
|
if parser.tokens_head > 0 && len(parser.tokens) == cap(parser.tokens) {
|
||||||
|
if parser.tokens_head != len(parser.tokens) {
|
||||||
|
copy(parser.tokens, parser.tokens[parser.tokens_head:])
|
||||||
|
}
|
||||||
|
parser.tokens = parser.tokens[:len(parser.tokens)-parser.tokens_head]
|
||||||
|
parser.tokens_head = 0
|
||||||
|
}
|
||||||
|
parser.tokens = append(parser.tokens, *token)
|
||||||
|
if pos < 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
copy(parser.tokens[parser.tokens_head+pos+1:], parser.tokens[parser.tokens_head+pos:])
|
||||||
|
parser.tokens[parser.tokens_head+pos] = *token
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new parser object.
|
||||||
|
func yaml_parser_initialize(parser *yaml_parser_t) bool {
|
||||||
|
*parser = yaml_parser_t{
|
||||||
|
raw_buffer: make([]byte, 0, input_raw_buffer_size),
|
||||||
|
buffer: make([]byte, 0, input_buffer_size),
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroy a parser object.
|
||||||
|
func yaml_parser_delete(parser *yaml_parser_t) {
|
||||||
|
*parser = yaml_parser_t{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String read handler.
|
||||||
|
func yaml_string_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) {
|
||||||
|
if parser.input_pos == len(parser.input) {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
n = copy(buffer, parser.input[parser.input_pos:])
|
||||||
|
parser.input_pos += n
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// File read handler.
|
||||||
|
func yaml_file_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) {
|
||||||
|
return parser.input_file.Read(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a string input.
|
||||||
|
func yaml_parser_set_input_string(parser *yaml_parser_t, input []byte) {
|
||||||
|
if parser.read_handler != nil {
|
||||||
|
panic("must set the input source only once")
|
||||||
|
}
|
||||||
|
parser.read_handler = yaml_string_read_handler
|
||||||
|
parser.input = input
|
||||||
|
parser.input_pos = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a file input.
|
||||||
|
func yaml_parser_set_input_file(parser *yaml_parser_t, file *os.File) {
|
||||||
|
if parser.read_handler != nil {
|
||||||
|
panic("must set the input source only once")
|
||||||
|
}
|
||||||
|
parser.read_handler = yaml_file_read_handler
|
||||||
|
parser.input_file = file
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the source encoding.
|
||||||
|
func yaml_parser_set_encoding(parser *yaml_parser_t, encoding yaml_encoding_t) {
|
||||||
|
if parser.encoding != yaml_ANY_ENCODING {
|
||||||
|
panic("must set the encoding only once")
|
||||||
|
}
|
||||||
|
parser.encoding = encoding
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new emitter object.
|
||||||
|
func yaml_emitter_initialize(emitter *yaml_emitter_t) bool {
|
||||||
|
*emitter = yaml_emitter_t{
|
||||||
|
buffer: make([]byte, output_buffer_size),
|
||||||
|
raw_buffer: make([]byte, 0, output_raw_buffer_size),
|
||||||
|
states: make([]yaml_emitter_state_t, 0, initial_stack_size),
|
||||||
|
events: make([]yaml_event_t, 0, initial_queue_size),
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroy an emitter object.
|
||||||
|
func yaml_emitter_delete(emitter *yaml_emitter_t) {
|
||||||
|
*emitter = yaml_emitter_t{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String write handler.
|
||||||
|
func yaml_string_write_handler(emitter *yaml_emitter_t, buffer []byte) error {
|
||||||
|
*emitter.output_buffer = append(*emitter.output_buffer, buffer...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// File write handler.
|
||||||
|
func yaml_file_write_handler(emitter *yaml_emitter_t, buffer []byte) error {
|
||||||
|
_, err := emitter.output_file.Write(buffer)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a string output.
|
||||||
|
func yaml_emitter_set_output_string(emitter *yaml_emitter_t, output_buffer *[]byte) {
|
||||||
|
if emitter.write_handler != nil {
|
||||||
|
panic("must set the output target only once")
|
||||||
|
}
|
||||||
|
emitter.write_handler = yaml_string_write_handler
|
||||||
|
emitter.output_buffer = output_buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a file output.
|
||||||
|
func yaml_emitter_set_output_file(emitter *yaml_emitter_t, file io.Writer) {
|
||||||
|
if emitter.write_handler != nil {
|
||||||
|
panic("must set the output target only once")
|
||||||
|
}
|
||||||
|
emitter.write_handler = yaml_file_write_handler
|
||||||
|
emitter.output_file = file
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the output encoding.
|
||||||
|
func yaml_emitter_set_encoding(emitter *yaml_emitter_t, encoding yaml_encoding_t) {
|
||||||
|
if emitter.encoding != yaml_ANY_ENCODING {
|
||||||
|
panic("must set the output encoding only once")
|
||||||
|
}
|
||||||
|
emitter.encoding = encoding
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the canonical output style.
|
||||||
|
func yaml_emitter_set_canonical(emitter *yaml_emitter_t, canonical bool) {
|
||||||
|
emitter.canonical = canonical
|
||||||
|
}
|
||||||
|
|
||||||
|
//// Set the indentation increment.
|
||||||
|
func yaml_emitter_set_indent(emitter *yaml_emitter_t, indent int) {
|
||||||
|
if indent < 2 || indent > 9 {
|
||||||
|
indent = 2
|
||||||
|
}
|
||||||
|
emitter.best_indent = indent
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the preferred line width.
|
||||||
|
func yaml_emitter_set_width(emitter *yaml_emitter_t, width int) {
|
||||||
|
if width < 0 {
|
||||||
|
width = -1
|
||||||
|
}
|
||||||
|
emitter.best_width = width
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set if unescaped non-ASCII characters are allowed.
|
||||||
|
func yaml_emitter_set_unicode(emitter *yaml_emitter_t, unicode bool) {
|
||||||
|
emitter.unicode = unicode
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the preferred line break character.
|
||||||
|
func yaml_emitter_set_break(emitter *yaml_emitter_t, line_break yaml_break_t) {
|
||||||
|
emitter.line_break = line_break
|
||||||
|
}
|
||||||
|
|
||||||
|
///*
|
||||||
|
// * Destroy a token object.
|
||||||
|
// */
|
||||||
|
//
|
||||||
|
//YAML_DECLARE(void)
|
||||||
|
//yaml_token_delete(yaml_token_t *token)
|
||||||
|
//{
|
||||||
|
// assert(token); // Non-NULL token object expected.
|
||||||
|
//
|
||||||
|
// switch (token.type)
|
||||||
|
// {
|
||||||
|
// case YAML_TAG_DIRECTIVE_TOKEN:
|
||||||
|
// yaml_free(token.data.tag_directive.handle);
|
||||||
|
// yaml_free(token.data.tag_directive.prefix);
|
||||||
|
// break;
|
||||||
|
//
|
||||||
|
// case YAML_ALIAS_TOKEN:
|
||||||
|
// yaml_free(token.data.alias.value);
|
||||||
|
// break;
|
||||||
|
//
|
||||||
|
// case YAML_ANCHOR_TOKEN:
|
||||||
|
// yaml_free(token.data.anchor.value);
|
||||||
|
// break;
|
||||||
|
//
|
||||||
|
// case YAML_TAG_TOKEN:
|
||||||
|
// yaml_free(token.data.tag.handle);
|
||||||
|
// yaml_free(token.data.tag.suffix);
|
||||||
|
// break;
|
||||||
|
//
|
||||||
|
// case YAML_SCALAR_TOKEN:
|
||||||
|
// yaml_free(token.data.scalar.value);
|
||||||
|
// break;
|
||||||
|
//
|
||||||
|
// default:
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// memset(token, 0, sizeof(yaml_token_t));
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
///*
|
||||||
|
// * Check if a string is a valid UTF-8 sequence.
|
||||||
|
// *
|
||||||
|
// * Check 'reader.c' for more details on UTF-8 encoding.
|
||||||
|
// */
|
||||||
|
//
|
||||||
|
//static int
|
||||||
|
//yaml_check_utf8(yaml_char_t *start, size_t length)
|
||||||
|
//{
|
||||||
|
// yaml_char_t *end = start+length;
|
||||||
|
// yaml_char_t *pointer = start;
|
||||||
|
//
|
||||||
|
// while (pointer < end) {
|
||||||
|
// unsigned char octet;
|
||||||
|
// unsigned int width;
|
||||||
|
// unsigned int value;
|
||||||
|
// size_t k;
|
||||||
|
//
|
||||||
|
// octet = pointer[0];
|
||||||
|
// width = (octet & 0x80) == 0x00 ? 1 :
|
||||||
|
// (octet & 0xE0) == 0xC0 ? 2 :
|
||||||
|
// (octet & 0xF0) == 0xE0 ? 3 :
|
||||||
|
// (octet & 0xF8) == 0xF0 ? 4 : 0;
|
||||||
|
// value = (octet & 0x80) == 0x00 ? octet & 0x7F :
|
||||||
|
// (octet & 0xE0) == 0xC0 ? octet & 0x1F :
|
||||||
|
// (octet & 0xF0) == 0xE0 ? octet & 0x0F :
|
||||||
|
// (octet & 0xF8) == 0xF0 ? octet & 0x07 : 0;
|
||||||
|
// if (!width) return 0;
|
||||||
|
// if (pointer+width > end) return 0;
|
||||||
|
// for (k = 1; k < width; k ++) {
|
||||||
|
// octet = pointer[k];
|
||||||
|
// if ((octet & 0xC0) != 0x80) return 0;
|
||||||
|
// value = (value << 6) + (octet & 0x3F);
|
||||||
|
// }
|
||||||
|
// if (!((width == 1) ||
|
||||||
|
// (width == 2 && value >= 0x80) ||
|
||||||
|
// (width == 3 && value >= 0x800) ||
|
||||||
|
// (width == 4 && value >= 0x10000))) return 0;
|
||||||
|
//
|
||||||
|
// pointer += width;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return 1;
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
|
||||||
|
// Create STREAM-START.
|
||||||
|
func yaml_stream_start_event_initialize(event *yaml_event_t, encoding yaml_encoding_t) bool {
|
||||||
|
*event = yaml_event_t{
|
||||||
|
typ: yaml_STREAM_START_EVENT,
|
||||||
|
encoding: encoding,
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create STREAM-END.
|
||||||
|
func yaml_stream_end_event_initialize(event *yaml_event_t) bool {
|
||||||
|
*event = yaml_event_t{
|
||||||
|
typ: yaml_STREAM_END_EVENT,
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create DOCUMENT-START.
|
||||||
|
func yaml_document_start_event_initialize(event *yaml_event_t, version_directive *yaml_version_directive_t,
|
||||||
|
tag_directives []yaml_tag_directive_t, implicit bool) bool {
|
||||||
|
*event = yaml_event_t{
|
||||||
|
typ: yaml_DOCUMENT_START_EVENT,
|
||||||
|
version_directive: version_directive,
|
||||||
|
tag_directives: tag_directives,
|
||||||
|
implicit: implicit,
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create DOCUMENT-END.
|
||||||
|
func yaml_document_end_event_initialize(event *yaml_event_t, implicit bool) bool {
|
||||||
|
*event = yaml_event_t{
|
||||||
|
typ: yaml_DOCUMENT_END_EVENT,
|
||||||
|
implicit: implicit,
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
///*
|
||||||
|
// * Create ALIAS.
|
||||||
|
// */
|
||||||
|
//
|
||||||
|
//YAML_DECLARE(int)
|
||||||
|
//yaml_alias_event_initialize(event *yaml_event_t, anchor *yaml_char_t)
|
||||||
|
//{
|
||||||
|
// mark yaml_mark_t = { 0, 0, 0 }
|
||||||
|
// anchor_copy *yaml_char_t = NULL
|
||||||
|
//
|
||||||
|
// assert(event) // Non-NULL event object is expected.
|
||||||
|
// assert(anchor) // Non-NULL anchor is expected.
|
||||||
|
//
|
||||||
|
// if (!yaml_check_utf8(anchor, strlen((char *)anchor))) return 0
|
||||||
|
//
|
||||||
|
// anchor_copy = yaml_strdup(anchor)
|
||||||
|
// if (!anchor_copy)
|
||||||
|
// return 0
|
||||||
|
//
|
||||||
|
// ALIAS_EVENT_INIT(*event, anchor_copy, mark, mark)
|
||||||
|
//
|
||||||
|
// return 1
|
||||||
|
//}
|
||||||
|
|
||||||
|
// Create SCALAR.
|
||||||
|
func yaml_scalar_event_initialize(event *yaml_event_t, anchor, tag, value []byte, plain_implicit, quoted_implicit bool, style yaml_scalar_style_t) bool {
|
||||||
|
*event = yaml_event_t{
|
||||||
|
typ: yaml_SCALAR_EVENT,
|
||||||
|
anchor: anchor,
|
||||||
|
tag: tag,
|
||||||
|
value: value,
|
||||||
|
implicit: plain_implicit,
|
||||||
|
quoted_implicit: quoted_implicit,
|
||||||
|
style: yaml_style_t(style),
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create SEQUENCE-START.
|
||||||
|
func yaml_sequence_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_sequence_style_t) bool {
|
||||||
|
*event = yaml_event_t{
|
||||||
|
typ: yaml_SEQUENCE_START_EVENT,
|
||||||
|
anchor: anchor,
|
||||||
|
tag: tag,
|
||||||
|
implicit: implicit,
|
||||||
|
style: yaml_style_t(style),
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create SEQUENCE-END.
|
||||||
|
func yaml_sequence_end_event_initialize(event *yaml_event_t) bool {
|
||||||
|
*event = yaml_event_t{
|
||||||
|
typ: yaml_SEQUENCE_END_EVENT,
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create MAPPING-START.
|
||||||
|
func yaml_mapping_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_mapping_style_t) bool {
|
||||||
|
*event = yaml_event_t{
|
||||||
|
typ: yaml_MAPPING_START_EVENT,
|
||||||
|
anchor: anchor,
|
||||||
|
tag: tag,
|
||||||
|
implicit: implicit,
|
||||||
|
style: yaml_style_t(style),
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create MAPPING-END.
|
||||||
|
func yaml_mapping_end_event_initialize(event *yaml_event_t) bool {
|
||||||
|
*event = yaml_event_t{
|
||||||
|
typ: yaml_MAPPING_END_EVENT,
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroy an event object.
|
||||||
|
func yaml_event_delete(event *yaml_event_t) {
|
||||||
|
*event = yaml_event_t{}
|
||||||
|
}
|
||||||
|
|
||||||
|
///*
|
||||||
|
// * Create a document object.
|
||||||
|
// */
|
||||||
|
//
|
||||||
|
//YAML_DECLARE(int)
|
||||||
|
//yaml_document_initialize(document *yaml_document_t,
|
||||||
|
// version_directive *yaml_version_directive_t,
|
||||||
|
// tag_directives_start *yaml_tag_directive_t,
|
||||||
|
// tag_directives_end *yaml_tag_directive_t,
|
||||||
|
// start_implicit int, end_implicit int)
|
||||||
|
//{
|
||||||
|
// struct {
|
||||||
|
// error yaml_error_type_t
|
||||||
|
// } context
|
||||||
|
// struct {
|
||||||
|
// start *yaml_node_t
|
||||||
|
// end *yaml_node_t
|
||||||
|
// top *yaml_node_t
|
||||||
|
// } nodes = { NULL, NULL, NULL }
|
||||||
|
// version_directive_copy *yaml_version_directive_t = NULL
|
||||||
|
// struct {
|
||||||
|
// start *yaml_tag_directive_t
|
||||||
|
// end *yaml_tag_directive_t
|
||||||
|
// top *yaml_tag_directive_t
|
||||||
|
// } tag_directives_copy = { NULL, NULL, NULL }
|
||||||
|
// value yaml_tag_directive_t = { NULL, NULL }
|
||||||
|
// mark yaml_mark_t = { 0, 0, 0 }
|
||||||
|
//
|
||||||
|
// assert(document) // Non-NULL document object is expected.
|
||||||
|
// assert((tag_directives_start && tag_directives_end) ||
|
||||||
|
// (tag_directives_start == tag_directives_end))
|
||||||
|
// // Valid tag directives are expected.
|
||||||
|
//
|
||||||
|
// if (!STACK_INIT(&context, nodes, INITIAL_STACK_SIZE)) goto error
|
||||||
|
//
|
||||||
|
// if (version_directive) {
|
||||||
|
// version_directive_copy = yaml_malloc(sizeof(yaml_version_directive_t))
|
||||||
|
// if (!version_directive_copy) goto error
|
||||||
|
// version_directive_copy.major = version_directive.major
|
||||||
|
// version_directive_copy.minor = version_directive.minor
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (tag_directives_start != tag_directives_end) {
|
||||||
|
// tag_directive *yaml_tag_directive_t
|
||||||
|
// if (!STACK_INIT(&context, tag_directives_copy, INITIAL_STACK_SIZE))
|
||||||
|
// goto error
|
||||||
|
// for (tag_directive = tag_directives_start
|
||||||
|
// tag_directive != tag_directives_end; tag_directive ++) {
|
||||||
|
// assert(tag_directive.handle)
|
||||||
|
// assert(tag_directive.prefix)
|
||||||
|
// if (!yaml_check_utf8(tag_directive.handle,
|
||||||
|
// strlen((char *)tag_directive.handle)))
|
||||||
|
// goto error
|
||||||
|
// if (!yaml_check_utf8(tag_directive.prefix,
|
||||||
|
// strlen((char *)tag_directive.prefix)))
|
||||||
|
// goto error
|
||||||
|
// value.handle = yaml_strdup(tag_directive.handle)
|
||||||
|
// value.prefix = yaml_strdup(tag_directive.prefix)
|
||||||
|
// if (!value.handle || !value.prefix) goto error
|
||||||
|
// if (!PUSH(&context, tag_directives_copy, value))
|
||||||
|
// goto error
|
||||||
|
// value.handle = NULL
|
||||||
|
// value.prefix = NULL
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// DOCUMENT_INIT(*document, nodes.start, nodes.end, version_directive_copy,
|
||||||
|
// tag_directives_copy.start, tag_directives_copy.top,
|
||||||
|
// start_implicit, end_implicit, mark, mark)
|
||||||
|
//
|
||||||
|
// return 1
|
||||||
|
//
|
||||||
|
//error:
|
||||||
|
// STACK_DEL(&context, nodes)
|
||||||
|
// yaml_free(version_directive_copy)
|
||||||
|
// while (!STACK_EMPTY(&context, tag_directives_copy)) {
|
||||||
|
// value yaml_tag_directive_t = POP(&context, tag_directives_copy)
|
||||||
|
// yaml_free(value.handle)
|
||||||
|
// yaml_free(value.prefix)
|
||||||
|
// }
|
||||||
|
// STACK_DEL(&context, tag_directives_copy)
|
||||||
|
// yaml_free(value.handle)
|
||||||
|
// yaml_free(value.prefix)
|
||||||
|
//
|
||||||
|
// return 0
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
///*
|
||||||
|
// * Destroy a document object.
|
||||||
|
// */
|
||||||
|
//
|
||||||
|
//YAML_DECLARE(void)
|
||||||
|
//yaml_document_delete(document *yaml_document_t)
|
||||||
|
//{
|
||||||
|
// struct {
|
||||||
|
// error yaml_error_type_t
|
||||||
|
// } context
|
||||||
|
// tag_directive *yaml_tag_directive_t
|
||||||
|
//
|
||||||
|
// context.error = YAML_NO_ERROR // Eliminate a compliler warning.
|
||||||
|
//
|
||||||
|
// assert(document) // Non-NULL document object is expected.
|
||||||
|
//
|
||||||
|
// while (!STACK_EMPTY(&context, document.nodes)) {
|
||||||
|
// node yaml_node_t = POP(&context, document.nodes)
|
||||||
|
// yaml_free(node.tag)
|
||||||
|
// switch (node.type) {
|
||||||
|
// case YAML_SCALAR_NODE:
|
||||||
|
// yaml_free(node.data.scalar.value)
|
||||||
|
// break
|
||||||
|
// case YAML_SEQUENCE_NODE:
|
||||||
|
// STACK_DEL(&context, node.data.sequence.items)
|
||||||
|
// break
|
||||||
|
// case YAML_MAPPING_NODE:
|
||||||
|
// STACK_DEL(&context, node.data.mapping.pairs)
|
||||||
|
// break
|
||||||
|
// default:
|
||||||
|
// assert(0) // Should not happen.
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// STACK_DEL(&context, document.nodes)
|
||||||
|
//
|
||||||
|
// yaml_free(document.version_directive)
|
||||||
|
// for (tag_directive = document.tag_directives.start
|
||||||
|
// tag_directive != document.tag_directives.end
|
||||||
|
// tag_directive++) {
|
||||||
|
// yaml_free(tag_directive.handle)
|
||||||
|
// yaml_free(tag_directive.prefix)
|
||||||
|
// }
|
||||||
|
// yaml_free(document.tag_directives.start)
|
||||||
|
//
|
||||||
|
// memset(document, 0, sizeof(yaml_document_t))
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
///**
|
||||||
|
// * Get a document node.
|
||||||
|
// */
|
||||||
|
//
|
||||||
|
//YAML_DECLARE(yaml_node_t *)
|
||||||
|
//yaml_document_get_node(document *yaml_document_t, index int)
|
||||||
|
//{
|
||||||
|
// assert(document) // Non-NULL document object is expected.
|
||||||
|
//
|
||||||
|
// if (index > 0 && document.nodes.start + index <= document.nodes.top) {
|
||||||
|
// return document.nodes.start + index - 1
|
||||||
|
// }
|
||||||
|
// return NULL
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
///**
|
||||||
|
// * Get the root object.
|
||||||
|
// */
|
||||||
|
//
|
||||||
|
//YAML_DECLARE(yaml_node_t *)
|
||||||
|
//yaml_document_get_root_node(document *yaml_document_t)
|
||||||
|
//{
|
||||||
|
// assert(document) // Non-NULL document object is expected.
|
||||||
|
//
|
||||||
|
// if (document.nodes.top != document.nodes.start) {
|
||||||
|
// return document.nodes.start
|
||||||
|
// }
|
||||||
|
// return NULL
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
///*
|
||||||
|
// * Add a scalar node to a document.
|
||||||
|
// */
|
||||||
|
//
|
||||||
|
//YAML_DECLARE(int)
|
||||||
|
//yaml_document_add_scalar(document *yaml_document_t,
|
||||||
|
// tag *yaml_char_t, value *yaml_char_t, length int,
|
||||||
|
// style yaml_scalar_style_t)
|
||||||
|
//{
|
||||||
|
// struct {
|
||||||
|
// error yaml_error_type_t
|
||||||
|
// } context
|
||||||
|
// mark yaml_mark_t = { 0, 0, 0 }
|
||||||
|
// tag_copy *yaml_char_t = NULL
|
||||||
|
// value_copy *yaml_char_t = NULL
|
||||||
|
// node yaml_node_t
|
||||||
|
//
|
||||||
|
// assert(document) // Non-NULL document object is expected.
|
||||||
|
// assert(value) // Non-NULL value is expected.
|
||||||
|
//
|
||||||
|
// if (!tag) {
|
||||||
|
// tag = (yaml_char_t *)YAML_DEFAULT_SCALAR_TAG
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error
|
||||||
|
// tag_copy = yaml_strdup(tag)
|
||||||
|
// if (!tag_copy) goto error
|
||||||
|
//
|
||||||
|
// if (length < 0) {
|
||||||
|
// length = strlen((char *)value)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (!yaml_check_utf8(value, length)) goto error
|
||||||
|
// value_copy = yaml_malloc(length+1)
|
||||||
|
// if (!value_copy) goto error
|
||||||
|
// memcpy(value_copy, value, length)
|
||||||
|
// value_copy[length] = '\0'
|
||||||
|
//
|
||||||
|
// SCALAR_NODE_INIT(node, tag_copy, value_copy, length, style, mark, mark)
|
||||||
|
// if (!PUSH(&context, document.nodes, node)) goto error
|
||||||
|
//
|
||||||
|
// return document.nodes.top - document.nodes.start
|
||||||
|
//
|
||||||
|
//error:
|
||||||
|
// yaml_free(tag_copy)
|
||||||
|
// yaml_free(value_copy)
|
||||||
|
//
|
||||||
|
// return 0
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
///*
|
||||||
|
// * Add a sequence node to a document.
|
||||||
|
// */
|
||||||
|
//
|
||||||
|
//YAML_DECLARE(int)
|
||||||
|
//yaml_document_add_sequence(document *yaml_document_t,
|
||||||
|
// tag *yaml_char_t, style yaml_sequence_style_t)
|
||||||
|
//{
|
||||||
|
// struct {
|
||||||
|
// error yaml_error_type_t
|
||||||
|
// } context
|
||||||
|
// mark yaml_mark_t = { 0, 0, 0 }
|
||||||
|
// tag_copy *yaml_char_t = NULL
|
||||||
|
// struct {
|
||||||
|
// start *yaml_node_item_t
|
||||||
|
// end *yaml_node_item_t
|
||||||
|
// top *yaml_node_item_t
|
||||||
|
// } items = { NULL, NULL, NULL }
|
||||||
|
// node yaml_node_t
|
||||||
|
//
|
||||||
|
// assert(document) // Non-NULL document object is expected.
|
||||||
|
//
|
||||||
|
// if (!tag) {
|
||||||
|
// tag = (yaml_char_t *)YAML_DEFAULT_SEQUENCE_TAG
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error
|
||||||
|
// tag_copy = yaml_strdup(tag)
|
||||||
|
// if (!tag_copy) goto error
|
||||||
|
//
|
||||||
|
// if (!STACK_INIT(&context, items, INITIAL_STACK_SIZE)) goto error
|
||||||
|
//
|
||||||
|
// SEQUENCE_NODE_INIT(node, tag_copy, items.start, items.end,
|
||||||
|
// style, mark, mark)
|
||||||
|
// if (!PUSH(&context, document.nodes, node)) goto error
|
||||||
|
//
|
||||||
|
// return document.nodes.top - document.nodes.start
|
||||||
|
//
|
||||||
|
//error:
|
||||||
|
// STACK_DEL(&context, items)
|
||||||
|
// yaml_free(tag_copy)
|
||||||
|
//
|
||||||
|
// return 0
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
///*
|
||||||
|
// * Add a mapping node to a document.
|
||||||
|
// */
|
||||||
|
//
|
||||||
|
//YAML_DECLARE(int)
|
||||||
|
//yaml_document_add_mapping(document *yaml_document_t,
|
||||||
|
// tag *yaml_char_t, style yaml_mapping_style_t)
|
||||||
|
//{
|
||||||
|
// struct {
|
||||||
|
// error yaml_error_type_t
|
||||||
|
// } context
|
||||||
|
// mark yaml_mark_t = { 0, 0, 0 }
|
||||||
|
// tag_copy *yaml_char_t = NULL
|
||||||
|
// struct {
|
||||||
|
// start *yaml_node_pair_t
|
||||||
|
// end *yaml_node_pair_t
|
||||||
|
// top *yaml_node_pair_t
|
||||||
|
// } pairs = { NULL, NULL, NULL }
|
||||||
|
// node yaml_node_t
|
||||||
|
//
|
||||||
|
// assert(document) // Non-NULL document object is expected.
|
||||||
|
//
|
||||||
|
// if (!tag) {
|
||||||
|
// tag = (yaml_char_t *)YAML_DEFAULT_MAPPING_TAG
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error
|
||||||
|
// tag_copy = yaml_strdup(tag)
|
||||||
|
// if (!tag_copy) goto error
|
||||||
|
//
|
||||||
|
// if (!STACK_INIT(&context, pairs, INITIAL_STACK_SIZE)) goto error
|
||||||
|
//
|
||||||
|
// MAPPING_NODE_INIT(node, tag_copy, pairs.start, pairs.end,
|
||||||
|
// style, mark, mark)
|
||||||
|
// if (!PUSH(&context, document.nodes, node)) goto error
|
||||||
|
//
|
||||||
|
// return document.nodes.top - document.nodes.start
|
||||||
|
//
|
||||||
|
//error:
|
||||||
|
// STACK_DEL(&context, pairs)
|
||||||
|
// yaml_free(tag_copy)
|
||||||
|
//
|
||||||
|
// return 0
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
///*
|
||||||
|
// * Append an item to a sequence node.
|
||||||
|
// */
|
||||||
|
//
|
||||||
|
//YAML_DECLARE(int)
|
||||||
|
//yaml_document_append_sequence_item(document *yaml_document_t,
|
||||||
|
// sequence int, item int)
|
||||||
|
//{
|
||||||
|
// struct {
|
||||||
|
// error yaml_error_type_t
|
||||||
|
// } context
|
||||||
|
//
|
||||||
|
// assert(document) // Non-NULL document is required.
|
||||||
|
// assert(sequence > 0
|
||||||
|
// && document.nodes.start + sequence <= document.nodes.top)
|
||||||
|
// // Valid sequence id is required.
|
||||||
|
// assert(document.nodes.start[sequence-1].type == YAML_SEQUENCE_NODE)
|
||||||
|
// // A sequence node is required.
|
||||||
|
// assert(item > 0 && document.nodes.start + item <= document.nodes.top)
|
||||||
|
// // Valid item id is required.
|
||||||
|
//
|
||||||
|
// if (!PUSH(&context,
|
||||||
|
// document.nodes.start[sequence-1].data.sequence.items, item))
|
||||||
|
// return 0
|
||||||
|
//
|
||||||
|
// return 1
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
///*
|
||||||
|
// * Append a pair of a key and a value to a mapping node.
|
||||||
|
// */
|
||||||
|
//
|
||||||
|
//YAML_DECLARE(int)
|
||||||
|
//yaml_document_append_mapping_pair(document *yaml_document_t,
|
||||||
|
// mapping int, key int, value int)
|
||||||
|
//{
|
||||||
|
// struct {
|
||||||
|
// error yaml_error_type_t
|
||||||
|
// } context
|
||||||
|
//
|
||||||
|
// pair yaml_node_pair_t
|
||||||
|
//
|
||||||
|
// assert(document) // Non-NULL document is required.
|
||||||
|
// assert(mapping > 0
|
||||||
|
// && document.nodes.start + mapping <= document.nodes.top)
|
||||||
|
// // Valid mapping id is required.
|
||||||
|
// assert(document.nodes.start[mapping-1].type == YAML_MAPPING_NODE)
|
||||||
|
// // A mapping node is required.
|
||||||
|
// assert(key > 0 && document.nodes.start + key <= document.nodes.top)
|
||||||
|
// // Valid key id is required.
|
||||||
|
// assert(value > 0 && document.nodes.start + value <= document.nodes.top)
|
||||||
|
// // Valid value id is required.
|
||||||
|
//
|
||||||
|
// pair.key = key
|
||||||
|
// pair.value = value
|
||||||
|
//
|
||||||
|
// if (!PUSH(&context,
|
||||||
|
// document.nodes.start[mapping-1].data.mapping.pairs, pair))
|
||||||
|
// return 0
|
||||||
|
//
|
||||||
|
// return 1
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//
|
683
vendor/gopkg.in/yaml.v2/decode.go
generated
vendored
Normal file
683
vendor/gopkg.in/yaml.v2/decode.go
generated
vendored
Normal file
|
@ -0,0 +1,683 @@
|
||||||
|
package yaml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
documentNode = 1 << iota
|
||||||
|
mappingNode
|
||||||
|
sequenceNode
|
||||||
|
scalarNode
|
||||||
|
aliasNode
|
||||||
|
)
|
||||||
|
|
||||||
|
type node struct {
|
||||||
|
kind int
|
||||||
|
line, column int
|
||||||
|
tag string
|
||||||
|
value string
|
||||||
|
implicit bool
|
||||||
|
children []*node
|
||||||
|
anchors map[string]*node
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Parser, produces a node tree out of a libyaml event stream.
|
||||||
|
|
||||||
|
type parser struct {
|
||||||
|
parser yaml_parser_t
|
||||||
|
event yaml_event_t
|
||||||
|
doc *node
|
||||||
|
}
|
||||||
|
|
||||||
|
func newParser(b []byte) *parser {
|
||||||
|
p := parser{}
|
||||||
|
if !yaml_parser_initialize(&p.parser) {
|
||||||
|
panic("failed to initialize YAML emitter")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(b) == 0 {
|
||||||
|
b = []byte{'\n'}
|
||||||
|
}
|
||||||
|
|
||||||
|
yaml_parser_set_input_string(&p.parser, b)
|
||||||
|
|
||||||
|
p.skip()
|
||||||
|
if p.event.typ != yaml_STREAM_START_EVENT {
|
||||||
|
panic("expected stream start event, got " + strconv.Itoa(int(p.event.typ)))
|
||||||
|
}
|
||||||
|
p.skip()
|
||||||
|
return &p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) destroy() {
|
||||||
|
if p.event.typ != yaml_NO_EVENT {
|
||||||
|
yaml_event_delete(&p.event)
|
||||||
|
}
|
||||||
|
yaml_parser_delete(&p.parser)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) skip() {
|
||||||
|
if p.event.typ != yaml_NO_EVENT {
|
||||||
|
if p.event.typ == yaml_STREAM_END_EVENT {
|
||||||
|
failf("attempted to go past the end of stream; corrupted value?")
|
||||||
|
}
|
||||||
|
yaml_event_delete(&p.event)
|
||||||
|
}
|
||||||
|
if !yaml_parser_parse(&p.parser, &p.event) {
|
||||||
|
p.fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) fail() {
|
||||||
|
var where string
|
||||||
|
var line int
|
||||||
|
if p.parser.problem_mark.line != 0 {
|
||||||
|
line = p.parser.problem_mark.line
|
||||||
|
} else if p.parser.context_mark.line != 0 {
|
||||||
|
line = p.parser.context_mark.line
|
||||||
|
}
|
||||||
|
if line != 0 {
|
||||||
|
where = "line " + strconv.Itoa(line) + ": "
|
||||||
|
}
|
||||||
|
var msg string
|
||||||
|
if len(p.parser.problem) > 0 {
|
||||||
|
msg = p.parser.problem
|
||||||
|
} else {
|
||||||
|
msg = "unknown problem parsing YAML content"
|
||||||
|
}
|
||||||
|
failf("%s%s", where, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) anchor(n *node, anchor []byte) {
|
||||||
|
if anchor != nil {
|
||||||
|
p.doc.anchors[string(anchor)] = n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) parse() *node {
|
||||||
|
switch p.event.typ {
|
||||||
|
case yaml_SCALAR_EVENT:
|
||||||
|
return p.scalar()
|
||||||
|
case yaml_ALIAS_EVENT:
|
||||||
|
return p.alias()
|
||||||
|
case yaml_MAPPING_START_EVENT:
|
||||||
|
return p.mapping()
|
||||||
|
case yaml_SEQUENCE_START_EVENT:
|
||||||
|
return p.sequence()
|
||||||
|
case yaml_DOCUMENT_START_EVENT:
|
||||||
|
return p.document()
|
||||||
|
case yaml_STREAM_END_EVENT:
|
||||||
|
// Happens when attempting to decode an empty buffer.
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
panic("attempted to parse unknown event: " + strconv.Itoa(int(p.event.typ)))
|
||||||
|
}
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) node(kind int) *node {
|
||||||
|
return &node{
|
||||||
|
kind: kind,
|
||||||
|
line: p.event.start_mark.line,
|
||||||
|
column: p.event.start_mark.column,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) document() *node {
|
||||||
|
n := p.node(documentNode)
|
||||||
|
n.anchors = make(map[string]*node)
|
||||||
|
p.doc = n
|
||||||
|
p.skip()
|
||||||
|
n.children = append(n.children, p.parse())
|
||||||
|
if p.event.typ != yaml_DOCUMENT_END_EVENT {
|
||||||
|
panic("expected end of document event but got " + strconv.Itoa(int(p.event.typ)))
|
||||||
|
}
|
||||||
|
p.skip()
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) alias() *node {
|
||||||
|
n := p.node(aliasNode)
|
||||||
|
n.value = string(p.event.anchor)
|
||||||
|
p.skip()
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) scalar() *node {
|
||||||
|
n := p.node(scalarNode)
|
||||||
|
n.value = string(p.event.value)
|
||||||
|
n.tag = string(p.event.tag)
|
||||||
|
n.implicit = p.event.implicit
|
||||||
|
p.anchor(n, p.event.anchor)
|
||||||
|
p.skip()
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) sequence() *node {
|
||||||
|
n := p.node(sequenceNode)
|
||||||
|
p.anchor(n, p.event.anchor)
|
||||||
|
p.skip()
|
||||||
|
for p.event.typ != yaml_SEQUENCE_END_EVENT {
|
||||||
|
n.children = append(n.children, p.parse())
|
||||||
|
}
|
||||||
|
p.skip()
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) mapping() *node {
|
||||||
|
n := p.node(mappingNode)
|
||||||
|
p.anchor(n, p.event.anchor)
|
||||||
|
p.skip()
|
||||||
|
for p.event.typ != yaml_MAPPING_END_EVENT {
|
||||||
|
n.children = append(n.children, p.parse(), p.parse())
|
||||||
|
}
|
||||||
|
p.skip()
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Decoder, unmarshals a node into a provided value.
|
||||||
|
|
||||||
|
type decoder struct {
|
||||||
|
doc *node
|
||||||
|
aliases map[string]bool
|
||||||
|
mapType reflect.Type
|
||||||
|
terrors []string
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
mapItemType = reflect.TypeOf(MapItem{})
|
||||||
|
durationType = reflect.TypeOf(time.Duration(0))
|
||||||
|
defaultMapType = reflect.TypeOf(map[interface{}]interface{}{})
|
||||||
|
ifaceType = defaultMapType.Elem()
|
||||||
|
)
|
||||||
|
|
||||||
|
func newDecoder() *decoder {
|
||||||
|
d := &decoder{mapType: defaultMapType}
|
||||||
|
d.aliases = make(map[string]bool)
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *decoder) terror(n *node, tag string, out reflect.Value) {
|
||||||
|
if n.tag != "" {
|
||||||
|
tag = n.tag
|
||||||
|
}
|
||||||
|
value := n.value
|
||||||
|
if tag != yaml_SEQ_TAG && tag != yaml_MAP_TAG {
|
||||||
|
if len(value) > 10 {
|
||||||
|
value = " `" + value[:7] + "...`"
|
||||||
|
} else {
|
||||||
|
value = " `" + value + "`"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.terrors = append(d.terrors, fmt.Sprintf("line %d: cannot unmarshal %s%s into %s", n.line+1, shortTag(tag), value, out.Type()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *decoder) callUnmarshaler(n *node, u Unmarshaler) (good bool) {
|
||||||
|
terrlen := len(d.terrors)
|
||||||
|
err := u.UnmarshalYAML(func(v interface{}) (err error) {
|
||||||
|
defer handleErr(&err)
|
||||||
|
d.unmarshal(n, reflect.ValueOf(v))
|
||||||
|
if len(d.terrors) > terrlen {
|
||||||
|
issues := d.terrors[terrlen:]
|
||||||
|
d.terrors = d.terrors[:terrlen]
|
||||||
|
return &TypeError{issues}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if e, ok := err.(*TypeError); ok {
|
||||||
|
d.terrors = append(d.terrors, e.Errors...)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
fail(err)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// d.prepare initializes and dereferences pointers and calls UnmarshalYAML
|
||||||
|
// if a value is found to implement it.
|
||||||
|
// It returns the initialized and dereferenced out value, whether
|
||||||
|
// unmarshalling was already done by UnmarshalYAML, and if so whether
|
||||||
|
// its types unmarshalled appropriately.
|
||||||
|
//
|
||||||
|
// If n holds a null value, prepare returns before doing anything.
|
||||||
|
func (d *decoder) prepare(n *node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) {
|
||||||
|
if n.tag == yaml_NULL_TAG || n.kind == scalarNode && n.tag == "" && (n.value == "null" || n.value == "") {
|
||||||
|
return out, false, false
|
||||||
|
}
|
||||||
|
again := true
|
||||||
|
for again {
|
||||||
|
again = false
|
||||||
|
if out.Kind() == reflect.Ptr {
|
||||||
|
if out.IsNil() {
|
||||||
|
out.Set(reflect.New(out.Type().Elem()))
|
||||||
|
}
|
||||||
|
out = out.Elem()
|
||||||
|
again = true
|
||||||
|
}
|
||||||
|
if out.CanAddr() {
|
||||||
|
if u, ok := out.Addr().Interface().(Unmarshaler); ok {
|
||||||
|
good = d.callUnmarshaler(n, u)
|
||||||
|
return out, true, good
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out, false, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) {
|
||||||
|
switch n.kind {
|
||||||
|
case documentNode:
|
||||||
|
return d.document(n, out)
|
||||||
|
case aliasNode:
|
||||||
|
return d.alias(n, out)
|
||||||
|
}
|
||||||
|
out, unmarshaled, good := d.prepare(n, out)
|
||||||
|
if unmarshaled {
|
||||||
|
return good
|
||||||
|
}
|
||||||
|
switch n.kind {
|
||||||
|
case scalarNode:
|
||||||
|
good = d.scalar(n, out)
|
||||||
|
case mappingNode:
|
||||||
|
good = d.mapping(n, out)
|
||||||
|
case sequenceNode:
|
||||||
|
good = d.sequence(n, out)
|
||||||
|
default:
|
||||||
|
panic("internal error: unknown node kind: " + strconv.Itoa(n.kind))
|
||||||
|
}
|
||||||
|
return good
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *decoder) document(n *node, out reflect.Value) (good bool) {
|
||||||
|
if len(n.children) == 1 {
|
||||||
|
d.doc = n
|
||||||
|
d.unmarshal(n.children[0], out)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *decoder) alias(n *node, out reflect.Value) (good bool) {
|
||||||
|
an, ok := d.doc.anchors[n.value]
|
||||||
|
if !ok {
|
||||||
|
failf("unknown anchor '%s' referenced", n.value)
|
||||||
|
}
|
||||||
|
if d.aliases[n.value] {
|
||||||
|
failf("anchor '%s' value contains itself", n.value)
|
||||||
|
}
|
||||||
|
d.aliases[n.value] = true
|
||||||
|
good = d.unmarshal(an, out)
|
||||||
|
delete(d.aliases, n.value)
|
||||||
|
return good
|
||||||
|
}
|
||||||
|
|
||||||
|
var zeroValue reflect.Value
|
||||||
|
|
||||||
|
func resetMap(out reflect.Value) {
|
||||||
|
for _, k := range out.MapKeys() {
|
||||||
|
out.SetMapIndex(k, zeroValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *decoder) scalar(n *node, out reflect.Value) (good bool) {
|
||||||
|
var tag string
|
||||||
|
var resolved interface{}
|
||||||
|
if n.tag == "" && !n.implicit {
|
||||||
|
tag = yaml_STR_TAG
|
||||||
|
resolved = n.value
|
||||||
|
} else {
|
||||||
|
tag, resolved = resolve(n.tag, n.value)
|
||||||
|
if tag == yaml_BINARY_TAG {
|
||||||
|
data, err := base64.StdEncoding.DecodeString(resolved.(string))
|
||||||
|
if err != nil {
|
||||||
|
failf("!!binary value contains invalid base64 data")
|
||||||
|
}
|
||||||
|
resolved = string(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if resolved == nil {
|
||||||
|
if out.Kind() == reflect.Map && !out.CanAddr() {
|
||||||
|
resetMap(out)
|
||||||
|
} else {
|
||||||
|
out.Set(reflect.Zero(out.Type()))
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if s, ok := resolved.(string); ok && out.CanAddr() {
|
||||||
|
if u, ok := out.Addr().Interface().(encoding.TextUnmarshaler); ok {
|
||||||
|
err := u.UnmarshalText([]byte(s))
|
||||||
|
if err != nil {
|
||||||
|
fail(err)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch out.Kind() {
|
||||||
|
case reflect.String:
|
||||||
|
if tag == yaml_BINARY_TAG {
|
||||||
|
out.SetString(resolved.(string))
|
||||||
|
good = true
|
||||||
|
} else if resolved != nil {
|
||||||
|
out.SetString(n.value)
|
||||||
|
good = true
|
||||||
|
}
|
||||||
|
case reflect.Interface:
|
||||||
|
if resolved == nil {
|
||||||
|
out.Set(reflect.Zero(out.Type()))
|
||||||
|
} else {
|
||||||
|
out.Set(reflect.ValueOf(resolved))
|
||||||
|
}
|
||||||
|
good = true
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
switch resolved := resolved.(type) {
|
||||||
|
case int:
|
||||||
|
if !out.OverflowInt(int64(resolved)) {
|
||||||
|
out.SetInt(int64(resolved))
|
||||||
|
good = true
|
||||||
|
}
|
||||||
|
case int64:
|
||||||
|
if !out.OverflowInt(resolved) {
|
||||||
|
out.SetInt(resolved)
|
||||||
|
good = true
|
||||||
|
}
|
||||||
|
case uint64:
|
||||||
|
if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) {
|
||||||
|
out.SetInt(int64(resolved))
|
||||||
|
good = true
|
||||||
|
}
|
||||||
|
case float64:
|
||||||
|
if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) {
|
||||||
|
out.SetInt(int64(resolved))
|
||||||
|
good = true
|
||||||
|
}
|
||||||
|
case string:
|
||||||
|
if out.Type() == durationType {
|
||||||
|
d, err := time.ParseDuration(resolved)
|
||||||
|
if err == nil {
|
||||||
|
out.SetInt(int64(d))
|
||||||
|
good = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||||
|
switch resolved := resolved.(type) {
|
||||||
|
case int:
|
||||||
|
if resolved >= 0 && !out.OverflowUint(uint64(resolved)) {
|
||||||
|
out.SetUint(uint64(resolved))
|
||||||
|
good = true
|
||||||
|
}
|
||||||
|
case int64:
|
||||||
|
if resolved >= 0 && !out.OverflowUint(uint64(resolved)) {
|
||||||
|
out.SetUint(uint64(resolved))
|
||||||
|
good = true
|
||||||
|
}
|
||||||
|
case uint64:
|
||||||
|
if !out.OverflowUint(uint64(resolved)) {
|
||||||
|
out.SetUint(uint64(resolved))
|
||||||
|
good = true
|
||||||
|
}
|
||||||
|
case float64:
|
||||||
|
if resolved <= math.MaxUint64 && !out.OverflowUint(uint64(resolved)) {
|
||||||
|
out.SetUint(uint64(resolved))
|
||||||
|
good = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Bool:
|
||||||
|
switch resolved := resolved.(type) {
|
||||||
|
case bool:
|
||||||
|
out.SetBool(resolved)
|
||||||
|
good = true
|
||||||
|
}
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
switch resolved := resolved.(type) {
|
||||||
|
case int:
|
||||||
|
out.SetFloat(float64(resolved))
|
||||||
|
good = true
|
||||||
|
case int64:
|
||||||
|
out.SetFloat(float64(resolved))
|
||||||
|
good = true
|
||||||
|
case uint64:
|
||||||
|
out.SetFloat(float64(resolved))
|
||||||
|
good = true
|
||||||
|
case float64:
|
||||||
|
out.SetFloat(resolved)
|
||||||
|
good = true
|
||||||
|
}
|
||||||
|
case reflect.Ptr:
|
||||||
|
if out.Type().Elem() == reflect.TypeOf(resolved) {
|
||||||
|
// TODO DOes this make sense? When is out a Ptr except when decoding a nil value?
|
||||||
|
elem := reflect.New(out.Type().Elem())
|
||||||
|
elem.Elem().Set(reflect.ValueOf(resolved))
|
||||||
|
out.Set(elem)
|
||||||
|
good = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !good {
|
||||||
|
d.terror(n, tag, out)
|
||||||
|
}
|
||||||
|
return good
|
||||||
|
}
|
||||||
|
|
||||||
|
func settableValueOf(i interface{}) reflect.Value {
|
||||||
|
v := reflect.ValueOf(i)
|
||||||
|
sv := reflect.New(v.Type()).Elem()
|
||||||
|
sv.Set(v)
|
||||||
|
return sv
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *decoder) sequence(n *node, out reflect.Value) (good bool) {
|
||||||
|
l := len(n.children)
|
||||||
|
|
||||||
|
var iface reflect.Value
|
||||||
|
switch out.Kind() {
|
||||||
|
case reflect.Slice:
|
||||||
|
out.Set(reflect.MakeSlice(out.Type(), l, l))
|
||||||
|
case reflect.Interface:
|
||||||
|
// No type hints. Will have to use a generic sequence.
|
||||||
|
iface = out
|
||||||
|
out = settableValueOf(make([]interface{}, l))
|
||||||
|
default:
|
||||||
|
d.terror(n, yaml_SEQ_TAG, out)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
et := out.Type().Elem()
|
||||||
|
|
||||||
|
j := 0
|
||||||
|
for i := 0; i < l; i++ {
|
||||||
|
e := reflect.New(et).Elem()
|
||||||
|
if ok := d.unmarshal(n.children[i], e); ok {
|
||||||
|
out.Index(j).Set(e)
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.Set(out.Slice(0, j))
|
||||||
|
if iface.IsValid() {
|
||||||
|
iface.Set(out)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *decoder) mapping(n *node, out reflect.Value) (good bool) {
|
||||||
|
switch out.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
return d.mappingStruct(n, out)
|
||||||
|
case reflect.Slice:
|
||||||
|
return d.mappingSlice(n, out)
|
||||||
|
case reflect.Map:
|
||||||
|
// okay
|
||||||
|
case reflect.Interface:
|
||||||
|
if d.mapType.Kind() == reflect.Map {
|
||||||
|
iface := out
|
||||||
|
out = reflect.MakeMap(d.mapType)
|
||||||
|
iface.Set(out)
|
||||||
|
} else {
|
||||||
|
slicev := reflect.New(d.mapType).Elem()
|
||||||
|
if !d.mappingSlice(n, slicev) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
out.Set(slicev)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
d.terror(n, yaml_MAP_TAG, out)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
outt := out.Type()
|
||||||
|
kt := outt.Key()
|
||||||
|
et := outt.Elem()
|
||||||
|
|
||||||
|
mapType := d.mapType
|
||||||
|
if outt.Key() == ifaceType && outt.Elem() == ifaceType {
|
||||||
|
d.mapType = outt
|
||||||
|
}
|
||||||
|
|
||||||
|
if out.IsNil() {
|
||||||
|
out.Set(reflect.MakeMap(outt))
|
||||||
|
}
|
||||||
|
l := len(n.children)
|
||||||
|
for i := 0; i < l; i += 2 {
|
||||||
|
if isMerge(n.children[i]) {
|
||||||
|
d.merge(n.children[i+1], out)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
k := reflect.New(kt).Elem()
|
||||||
|
if d.unmarshal(n.children[i], k) {
|
||||||
|
kkind := k.Kind()
|
||||||
|
if kkind == reflect.Interface {
|
||||||
|
kkind = k.Elem().Kind()
|
||||||
|
}
|
||||||
|
if kkind == reflect.Map || kkind == reflect.Slice {
|
||||||
|
failf("invalid map key: %#v", k.Interface())
|
||||||
|
}
|
||||||
|
e := reflect.New(et).Elem()
|
||||||
|
if d.unmarshal(n.children[i+1], e) {
|
||||||
|
out.SetMapIndex(k, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.mapType = mapType
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *decoder) mappingSlice(n *node, out reflect.Value) (good bool) {
|
||||||
|
outt := out.Type()
|
||||||
|
if outt.Elem() != mapItemType {
|
||||||
|
d.terror(n, yaml_MAP_TAG, out)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
mapType := d.mapType
|
||||||
|
d.mapType = outt
|
||||||
|
|
||||||
|
var slice []MapItem
|
||||||
|
var l = len(n.children)
|
||||||
|
for i := 0; i < l; i += 2 {
|
||||||
|
if isMerge(n.children[i]) {
|
||||||
|
d.merge(n.children[i+1], out)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
item := MapItem{}
|
||||||
|
k := reflect.ValueOf(&item.Key).Elem()
|
||||||
|
if d.unmarshal(n.children[i], k) {
|
||||||
|
v := reflect.ValueOf(&item.Value).Elem()
|
||||||
|
if d.unmarshal(n.children[i+1], v) {
|
||||||
|
slice = append(slice, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.Set(reflect.ValueOf(slice))
|
||||||
|
d.mapType = mapType
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) {
|
||||||
|
sinfo, err := getStructInfo(out.Type())
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
name := settableValueOf("")
|
||||||
|
l := len(n.children)
|
||||||
|
|
||||||
|
var inlineMap reflect.Value
|
||||||
|
var elemType reflect.Type
|
||||||
|
if sinfo.InlineMap != -1 {
|
||||||
|
inlineMap = out.Field(sinfo.InlineMap)
|
||||||
|
inlineMap.Set(reflect.New(inlineMap.Type()).Elem())
|
||||||
|
elemType = inlineMap.Type().Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < l; i += 2 {
|
||||||
|
ni := n.children[i]
|
||||||
|
if isMerge(ni) {
|
||||||
|
d.merge(n.children[i+1], out)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !d.unmarshal(ni, name) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if info, ok := sinfo.FieldsMap[name.String()]; ok {
|
||||||
|
var field reflect.Value
|
||||||
|
if info.Inline == nil {
|
||||||
|
field = out.Field(info.Num)
|
||||||
|
} else {
|
||||||
|
field = out.FieldByIndex(info.Inline)
|
||||||
|
}
|
||||||
|
d.unmarshal(n.children[i+1], field)
|
||||||
|
} else if sinfo.InlineMap != -1 {
|
||||||
|
if inlineMap.IsNil() {
|
||||||
|
inlineMap.Set(reflect.MakeMap(inlineMap.Type()))
|
||||||
|
}
|
||||||
|
value := reflect.New(elemType).Elem()
|
||||||
|
d.unmarshal(n.children[i+1], value)
|
||||||
|
inlineMap.SetMapIndex(name, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func failWantMap() {
|
||||||
|
failf("map merge requires map or sequence of maps as the value")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *decoder) merge(n *node, out reflect.Value) {
|
||||||
|
switch n.kind {
|
||||||
|
case mappingNode:
|
||||||
|
d.unmarshal(n, out)
|
||||||
|
case aliasNode:
|
||||||
|
an, ok := d.doc.anchors[n.value]
|
||||||
|
if ok && an.kind != mappingNode {
|
||||||
|
failWantMap()
|
||||||
|
}
|
||||||
|
d.unmarshal(n, out)
|
||||||
|
case sequenceNode:
|
||||||
|
// Step backwards as earlier nodes take precedence.
|
||||||
|
for i := len(n.children) - 1; i >= 0; i-- {
|
||||||
|
ni := n.children[i]
|
||||||
|
if ni.kind == aliasNode {
|
||||||
|
an, ok := d.doc.anchors[ni.value]
|
||||||
|
if ok && an.kind != mappingNode {
|
||||||
|
failWantMap()
|
||||||
|
}
|
||||||
|
} else if ni.kind != mappingNode {
|
||||||
|
failWantMap()
|
||||||
|
}
|
||||||
|
d.unmarshal(ni, out)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
failWantMap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isMerge(n *node) bool {
|
||||||
|
return n.kind == scalarNode && n.value == "<<" && (n.implicit == true || n.tag == yaml_MERGE_TAG)
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue