mirror of https://github.com/etcd-io/dbtester.git
vendor: update
This commit is contained in:
parent
ce2d57ad02
commit
65754b40c5
|
|
@ -0,0 +1,20 @@
|
|||
Copyright (C) 2013 Blake Mizerany
|
||||
|
||||
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.
|
||||
|
|
@ -133,7 +133,7 @@ func (s *Stream) Query(q float64) float64 {
|
|||
if l == 0 {
|
||||
return 0
|
||||
}
|
||||
i := int(float64(l) * q)
|
||||
i := int(math.Ceil(float64(l) * q))
|
||||
if i > 0 {
|
||||
i -= 1
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -97,7 +97,12 @@ func (c *Client) Close() error {
|
|||
func (c *Client) Ctx() context.Context { return c.ctx }
|
||||
|
||||
// Endpoints lists the registered endpoints for the client.
|
||||
func (c *Client) Endpoints() []string { return c.cfg.Endpoints }
|
||||
func (c *Client) Endpoints() (eps []string) {
|
||||
// copy the slice; protect original endpoints from being changed
|
||||
eps = make([]string, len(c.cfg.Endpoints))
|
||||
copy(eps, c.cfg.Endpoints)
|
||||
return
|
||||
}
|
||||
|
||||
// SetEndpoints updates client's endpoints.
|
||||
func (c *Client) SetEndpoints(eps ...string) {
|
||||
|
|
|
|||
|
|
@ -138,15 +138,24 @@ func BigIBytes(s *big.Int) string {
|
|||
// ParseBigBytes("42mib") -> 44040192, nil
|
||||
func ParseBigBytes(s string) (*big.Int, error) {
|
||||
lastDigit := 0
|
||||
hasComma := false
|
||||
for _, r := range s {
|
||||
if !(unicode.IsDigit(r) || r == '.') {
|
||||
if !(unicode.IsDigit(r) || r == '.' || r == ',') {
|
||||
break
|
||||
}
|
||||
if r == ',' {
|
||||
hasComma = true
|
||||
}
|
||||
lastDigit++
|
||||
}
|
||||
|
||||
num := s[:lastDigit]
|
||||
if hasComma {
|
||||
num = strings.Replace(num, ",", "", -1)
|
||||
}
|
||||
|
||||
val := &big.Rat{}
|
||||
_, err := fmt.Sscanf(s[:lastDigit], "%f", val)
|
||||
_, err := fmt.Sscanf(num, "%f", val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,14 +109,23 @@ func IBytes(s uint64) string {
|
|||
// ParseBytes("42mib") -> 44040192, nil
|
||||
func ParseBytes(s string) (uint64, error) {
|
||||
lastDigit := 0
|
||||
hasComma := false
|
||||
for _, r := range s {
|
||||
if !(unicode.IsDigit(r) || r == '.') {
|
||||
if !(unicode.IsDigit(r) || r == '.' || r == ',') {
|
||||
break
|
||||
}
|
||||
if r == ',' {
|
||||
hasComma = true
|
||||
}
|
||||
lastDigit++
|
||||
}
|
||||
|
||||
f, err := strconv.ParseFloat(s[:lastDigit], 64)
|
||||
num := s[:lastDigit]
|
||||
if hasComma {
|
||||
num = strings.Replace(num, ",", "", -1)
|
||||
}
|
||||
|
||||
f, err := strconv.ParseFloat(num, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ func Comma(v int64) string {
|
|||
// Commaf produces a string form of the given number in base 10 with
|
||||
// commas after every three orders of magnitude.
|
||||
//
|
||||
// e.g. Comma(834142.32) -> 834,142.32
|
||||
// e.g. Commaf(834142.32) -> 834,142.32
|
||||
func Commaf(v float64) string {
|
||||
buf := &bytes.Buffer{}
|
||||
if v < 0 {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
// +build go1.6
|
||||
|
||||
package humanize
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math/big"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// BigCommaf produces a string form of the given big.Float in base 10
|
||||
// with commas after every three orders of magnitude.
|
||||
func BigCommaf(v *big.Float) string {
|
||||
buf := &bytes.Buffer{}
|
||||
if v.Sign() < 0 {
|
||||
buf.Write([]byte{'-'})
|
||||
v.Abs(v)
|
||||
}
|
||||
|
||||
comma := []byte{','}
|
||||
|
||||
parts := strings.Split(v.Text('f', -1), ".")
|
||||
pos := 0
|
||||
if len(parts[0])%3 != 0 {
|
||||
pos += len(parts[0]) % 3
|
||||
buf.WriteString(parts[0][:pos])
|
||||
buf.Write(comma)
|
||||
}
|
||||
for ; pos < len(parts[0]); pos += 3 {
|
||||
buf.WriteString(parts[0][pos : pos+3])
|
||||
buf.Write(comma)
|
||||
}
|
||||
buf.Truncate(buf.Len() - 1)
|
||||
|
||||
if len(parts) > 1 {
|
||||
buf.Write([]byte{'.'})
|
||||
buf.WriteString(parts[1])
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
|
@ -41,7 +41,7 @@ func revfmap(in map[float64]string) map[string]float64 {
|
|||
var riParseRegex *regexp.Regexp
|
||||
|
||||
func init() {
|
||||
ri := `^([0-9.]+)\s?([`
|
||||
ri := `^([\-0-9.]+)\s?([`
|
||||
for _, v := range siPrefixTable {
|
||||
ri += v
|
||||
}
|
||||
|
|
@ -61,18 +61,21 @@ func ComputeSI(input float64) (float64, string) {
|
|||
if input == 0 {
|
||||
return 0, ""
|
||||
}
|
||||
exponent := math.Floor(logn(input, 10))
|
||||
mag := math.Abs(input)
|
||||
exponent := math.Floor(logn(mag, 10))
|
||||
exponent = math.Floor(exponent/3) * 3
|
||||
|
||||
value := input / math.Pow(10, exponent)
|
||||
value := mag / math.Pow(10, exponent)
|
||||
|
||||
// Handle special case where value is exactly 1000.0
|
||||
// Should return 1M instead of 1000k
|
||||
if value == 1000.0 {
|
||||
exponent += 3
|
||||
value = input / math.Pow(10, exponent)
|
||||
value = mag / math.Pow(10, exponent)
|
||||
}
|
||||
|
||||
value = math.Copysign(value, input)
|
||||
|
||||
prefix := siPrefixTable[exponent]
|
||||
return value, prefix
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,9 +9,7 @@ import (
|
|||
|
||||
// Seconds-based time units
|
||||
const (
|
||||
Minute = 60
|
||||
Hour = 60 * Minute
|
||||
Day = 24 * Hour
|
||||
Day = 24 * time.Hour
|
||||
Week = 7 * Day
|
||||
Month = 30 * Day
|
||||
Year = 12 * Month
|
||||
|
|
@ -25,18 +23,35 @@ func Time(then time.Time) string {
|
|||
return RelTime(then, time.Now(), "ago", "from now")
|
||||
}
|
||||
|
||||
var magnitudes = []struct {
|
||||
d int64
|
||||
format string
|
||||
divby int64
|
||||
}{
|
||||
{1, "now", 1},
|
||||
{2, "1 second %s", 1},
|
||||
{Minute, "%d seconds %s", 1},
|
||||
{2 * Minute, "1 minute %s", 1},
|
||||
{Hour, "%d minutes %s", Minute},
|
||||
{2 * Hour, "1 hour %s", 1},
|
||||
{Day, "%d hours %s", Hour},
|
||||
// A RelTimeMagnitude struct contains a relative time point at which
|
||||
// the relative format of time will switch to a new format string. A
|
||||
// slice of these in ascending order by their "D" field is passed to
|
||||
// CustomRelTime to format durations.
|
||||
//
|
||||
// The Format field is a string that may contain a "%s" which will be
|
||||
// replaced with the appropriate signed label (e.g. "ago" or "from
|
||||
// now") and a "%d" that will be replaced by the quantity.
|
||||
//
|
||||
// The DivBy field is the amount of time the time difference must be
|
||||
// divided by in order to display correctly.
|
||||
//
|
||||
// e.g. if D is 2*time.Minute and you want to display "%d minutes %s"
|
||||
// DivBy should be time.Minute so whatever the duration is will be
|
||||
// expressed in minutes.
|
||||
type RelTimeMagnitude struct {
|
||||
D time.Duration
|
||||
Format string
|
||||
DivBy time.Duration
|
||||
}
|
||||
|
||||
var defaultMagnitudes = []RelTimeMagnitude{
|
||||
{time.Second, "now", time.Second},
|
||||
{2 * time.Second, "1 second %s", 1},
|
||||
{time.Minute, "%d seconds %s", time.Second},
|
||||
{2 * time.Minute, "1 minute %s", 1},
|
||||
{time.Hour, "%d minutes %s", time.Minute},
|
||||
{2 * time.Hour, "1 hour %s", 1},
|
||||
{Day, "%d hours %s", time.Hour},
|
||||
{2 * Day, "1 day %s", 1},
|
||||
{Week, "%d days %s", Day},
|
||||
{2 * Week, "1 week %s", 1},
|
||||
|
|
@ -57,35 +72,46 @@ var magnitudes = []struct {
|
|||
//
|
||||
// RelTime(timeInPast, timeInFuture, "earlier", "later") -> "3 weeks earlier"
|
||||
func RelTime(a, b time.Time, albl, blbl string) string {
|
||||
lbl := albl
|
||||
diff := b.Unix() - a.Unix()
|
||||
return CustomRelTime(a, b, albl, blbl, defaultMagnitudes)
|
||||
}
|
||||
|
||||
after := a.After(b)
|
||||
if after {
|
||||
// CustomRelTime formats a time into a relative string.
|
||||
//
|
||||
// It takes two times two labels and a table of relative time formats.
|
||||
// In addition to the generic time delta string (e.g. 5 minutes), the
|
||||
// labels are used applied so that the label corresponding to the
|
||||
// smaller time is applied.
|
||||
func CustomRelTime(a, b time.Time, albl, blbl string, magnitudes []RelTimeMagnitude) string {
|
||||
lbl := albl
|
||||
diff := b.Sub(a)
|
||||
|
||||
if a.After(b) {
|
||||
lbl = blbl
|
||||
diff = a.Unix() - b.Unix()
|
||||
diff = a.Sub(b)
|
||||
}
|
||||
|
||||
n := sort.Search(len(magnitudes), func(i int) bool {
|
||||
return magnitudes[i].d > diff
|
||||
return magnitudes[i].D >= diff
|
||||
})
|
||||
|
||||
if n >= len(magnitudes) {
|
||||
n = len(magnitudes) - 1
|
||||
}
|
||||
mag := magnitudes[n]
|
||||
args := []interface{}{}
|
||||
escaped := false
|
||||
for _, ch := range mag.format {
|
||||
for _, ch := range mag.Format {
|
||||
if escaped {
|
||||
switch ch {
|
||||
case '%':
|
||||
case 's':
|
||||
args = append(args, lbl)
|
||||
case 'd':
|
||||
args = append(args, diff/mag.divby)
|
||||
args = append(args, diff/mag.DivBy)
|
||||
}
|
||||
escaped = false
|
||||
} else {
|
||||
escaped = ch == '%'
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf(mag.format, args...)
|
||||
return fmt.Sprintf(mag.Format, args...)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
|
|
@ -73,7 +74,8 @@ type AgentServiceCheck struct {
|
|||
HTTP string `json:",omitempty"`
|
||||
TCP string `json:",omitempty"`
|
||||
Status string `json:",omitempty"`
|
||||
TLSSkipVerify string `json:",omitempty"`
|
||||
Notes string `json:",omitempty"`
|
||||
TLSSkipVerify bool `json:",omitempty"`
|
||||
|
||||
// In Consul 0.7 and later, checks that are associated with a service
|
||||
// may also contain this optional DeregisterCriticalServiceAfter field,
|
||||
|
|
@ -115,6 +117,17 @@ func (a *Agent) Self() (map[string]map[string]interface{}, error) {
|
|||
return out, nil
|
||||
}
|
||||
|
||||
// Reload triggers a configuration reload for the agent we are connected to.
|
||||
func (a *Agent) Reload() error {
|
||||
r := a.c.newRequest("PUT", "/v1/agent/reload")
|
||||
_, resp, err := requireOK(a.c.doRequest(r))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// NodeName is used to get the node name of the agent
|
||||
func (a *Agent) NodeName() (string, error) {
|
||||
if a.nodeName != "" {
|
||||
|
|
@ -346,6 +359,17 @@ func (a *Agent) Join(addr string, wan bool) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Leave is used to have the agent gracefully leave the cluster and shutdown
|
||||
func (a *Agent) Leave() error {
|
||||
r := a.c.newRequest("PUT", "/v1/agent/leave")
|
||||
_, resp, err := requireOK(a.c.doRequest(r))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// ForceLeave is used to have the agent eject a failed node
|
||||
func (a *Agent) ForceLeave(node string) error {
|
||||
r := a.c.newRequest("PUT", "/v1/agent/force-leave/"+node)
|
||||
|
|
@ -410,3 +434,38 @@ func (a *Agent) DisableNodeMaintenance() error {
|
|||
resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Monitor returns a channel which will receive streaming logs from the agent
|
||||
// Providing a non-nil stopCh can be used to close the connection and stop the
|
||||
// log stream
|
||||
func (a *Agent) Monitor(loglevel string, stopCh chan struct{}, q *QueryOptions) (chan string, error) {
|
||||
r := a.c.newRequest("GET", "/v1/agent/monitor")
|
||||
r.setQueryOptions(q)
|
||||
if loglevel != "" {
|
||||
r.params.Add("loglevel", loglevel)
|
||||
}
|
||||
_, resp, err := requireOK(a.c.doRequest(r))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logCh := make(chan string, 64)
|
||||
go func() {
|
||||
defer resp.Body.Close()
|
||||
|
||||
scanner := bufio.NewScanner(resp.Body)
|
||||
for {
|
||||
select {
|
||||
case <-stopCh:
|
||||
close(logCh)
|
||||
return
|
||||
default:
|
||||
}
|
||||
if scanner.Scan() {
|
||||
logCh <- scanner.Text()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return logCh, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,28 @@ import (
|
|||
"github.com/hashicorp/go-cleanhttp"
|
||||
)
|
||||
|
||||
const (
|
||||
// HTTPAddrEnvName defines an environment variable name which sets
|
||||
// the HTTP address if there is no -http-addr specified.
|
||||
HTTPAddrEnvName = "CONSUL_HTTP_ADDR"
|
||||
|
||||
// HTTPTokenEnvName defines an environment variable name which sets
|
||||
// the HTTP token.
|
||||
HTTPTokenEnvName = "CONSUL_HTTP_TOKEN"
|
||||
|
||||
// HTTPAuthEnvName defines an environment variable name which sets
|
||||
// the HTTP authentication header.
|
||||
HTTPAuthEnvName = "CONSUL_HTTP_AUTH"
|
||||
|
||||
// HTTPSSLEnvName defines an environment variable name which sets
|
||||
// whether or not to use HTTPS.
|
||||
HTTPSSLEnvName = "CONSUL_HTTP_SSL"
|
||||
|
||||
// HTTPSSLVerifyEnvName defines an environment variable name which sets
|
||||
// whether or not to disable certificate checking.
|
||||
HTTPSSLVerifyEnvName = "CONSUL_HTTP_SSL_VERIFY"
|
||||
)
|
||||
|
||||
// QueryOptions are used to parameterize a query
|
||||
type QueryOptions struct {
|
||||
// Providing a datacenter overwrites the DC provided
|
||||
|
|
@ -181,15 +203,15 @@ func defaultConfig(transportFn func() *http.Transport) *Config {
|
|||
},
|
||||
}
|
||||
|
||||
if addr := os.Getenv("CONSUL_HTTP_ADDR"); addr != "" {
|
||||
if addr := os.Getenv(HTTPAddrEnvName); addr != "" {
|
||||
config.Address = addr
|
||||
}
|
||||
|
||||
if token := os.Getenv("CONSUL_HTTP_TOKEN"); token != "" {
|
||||
if token := os.Getenv(HTTPTokenEnvName); token != "" {
|
||||
config.Token = token
|
||||
}
|
||||
|
||||
if auth := os.Getenv("CONSUL_HTTP_AUTH"); auth != "" {
|
||||
if auth := os.Getenv(HTTPAuthEnvName); auth != "" {
|
||||
var username, password string
|
||||
if strings.Contains(auth, ":") {
|
||||
split := strings.SplitN(auth, ":", 2)
|
||||
|
|
@ -205,10 +227,10 @@ func defaultConfig(transportFn func() *http.Transport) *Config {
|
|||
}
|
||||
}
|
||||
|
||||
if ssl := os.Getenv("CONSUL_HTTP_SSL"); ssl != "" {
|
||||
if ssl := os.Getenv(HTTPSSLEnvName); ssl != "" {
|
||||
enabled, err := strconv.ParseBool(ssl)
|
||||
if err != nil {
|
||||
log.Printf("[WARN] client: could not parse CONSUL_HTTP_SSL: %s", err)
|
||||
log.Printf("[WARN] client: could not parse %s: %s", HTTPSSLEnvName, err)
|
||||
}
|
||||
|
||||
if enabled {
|
||||
|
|
@ -216,10 +238,10 @@ func defaultConfig(transportFn func() *http.Transport) *Config {
|
|||
}
|
||||
}
|
||||
|
||||
if verify := os.Getenv("CONSUL_HTTP_SSL_VERIFY"); verify != "" {
|
||||
if verify := os.Getenv(HTTPSSLVerifyEnvName); verify != "" {
|
||||
doVerify, err := strconv.ParseBool(verify)
|
||||
if err != nil {
|
||||
log.Printf("[WARN] client: could not parse CONSUL_HTTP_SSL_VERIFY: %s", err)
|
||||
log.Printf("[WARN] client: could not parse %s: %s", HTTPSSLVerifyEnvName, err)
|
||||
}
|
||||
|
||||
if !doVerify {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ type CatalogService struct {
|
|||
ServiceTags []string
|
||||
ServicePort int
|
||||
ServiceEnableTagOverride bool
|
||||
CreateIndex uint64
|
||||
ModifyIndex uint64
|
||||
}
|
||||
|
||||
type CatalogNode struct {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package api
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -11,6 +12,15 @@ const (
|
|||
HealthPassing = "passing"
|
||||
HealthWarning = "warning"
|
||||
HealthCritical = "critical"
|
||||
HealthMaint = "maintenance"
|
||||
)
|
||||
|
||||
const (
|
||||
// NodeMaint is the special key set by a node in maintenance mode.
|
||||
NodeMaint = "_node_maintenance"
|
||||
|
||||
// ServiceMaintPrefix is the prefix for a service in maintenance mode.
|
||||
ServiceMaintPrefix = "_service_maintenance:"
|
||||
)
|
||||
|
||||
// HealthCheck is used to represent a single check
|
||||
|
|
@ -25,11 +35,56 @@ type HealthCheck struct {
|
|||
ServiceName string
|
||||
}
|
||||
|
||||
// HealthChecks is a collection of HealthCheck structs.
|
||||
type HealthChecks []*HealthCheck
|
||||
|
||||
// AggregatedStatus returns the "best" status for the list of health checks.
|
||||
// Because a given entry may have many service and node-level health checks
|
||||
// attached, this function determines the best representative of the status as
|
||||
// as single string using the following heuristic:
|
||||
//
|
||||
// maintenance > critical > warning > passing
|
||||
//
|
||||
func (c HealthChecks) AggregatedStatus() string {
|
||||
var passing, warning, critical, maintenance bool
|
||||
for _, check := range c {
|
||||
id := string(check.CheckID)
|
||||
if id == NodeMaint || strings.HasPrefix(id, ServiceMaintPrefix) {
|
||||
maintenance = true
|
||||
continue
|
||||
}
|
||||
|
||||
switch check.Status {
|
||||
case HealthPassing:
|
||||
passing = true
|
||||
case HealthWarning:
|
||||
warning = true
|
||||
case HealthCritical:
|
||||
critical = true
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case maintenance:
|
||||
return HealthMaint
|
||||
case critical:
|
||||
return HealthCritical
|
||||
case warning:
|
||||
return HealthWarning
|
||||
case passing:
|
||||
return HealthPassing
|
||||
default:
|
||||
return HealthPassing
|
||||
}
|
||||
}
|
||||
|
||||
// ServiceEntry is used for the health service endpoint
|
||||
type ServiceEntry struct {
|
||||
Node *Node
|
||||
Service *AgentService
|
||||
Checks []*HealthCheck
|
||||
Checks HealthChecks
|
||||
}
|
||||
|
||||
// Health can be used to query the Health endpoints
|
||||
|
|
@ -43,7 +98,7 @@ func (c *Client) Health() *Health {
|
|||
}
|
||||
|
||||
// Node is used to query for checks belonging to a given node
|
||||
func (h *Health) Node(node string, q *QueryOptions) ([]*HealthCheck, *QueryMeta, error) {
|
||||
func (h *Health) Node(node string, q *QueryOptions) (HealthChecks, *QueryMeta, error) {
|
||||
r := h.c.newRequest("GET", "/v1/health/node/"+node)
|
||||
r.setQueryOptions(q)
|
||||
rtt, resp, err := requireOK(h.c.doRequest(r))
|
||||
|
|
@ -56,7 +111,7 @@ func (h *Health) Node(node string, q *QueryOptions) ([]*HealthCheck, *QueryMeta,
|
|||
parseQueryMeta(resp, qm)
|
||||
qm.RequestTime = rtt
|
||||
|
||||
var out []*HealthCheck
|
||||
var out HealthChecks
|
||||
if err := decodeBody(resp, &out); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
@ -64,7 +119,7 @@ func (h *Health) Node(node string, q *QueryOptions) ([]*HealthCheck, *QueryMeta,
|
|||
}
|
||||
|
||||
// Checks is used to return the checks associated with a service
|
||||
func (h *Health) Checks(service string, q *QueryOptions) ([]*HealthCheck, *QueryMeta, error) {
|
||||
func (h *Health) Checks(service string, q *QueryOptions) (HealthChecks, *QueryMeta, error) {
|
||||
r := h.c.newRequest("GET", "/v1/health/checks/"+service)
|
||||
r.setQueryOptions(q)
|
||||
rtt, resp, err := requireOK(h.c.doRequest(r))
|
||||
|
|
@ -77,7 +132,7 @@ func (h *Health) Checks(service string, q *QueryOptions) ([]*HealthCheck, *Query
|
|||
parseQueryMeta(resp, qm)
|
||||
qm.RequestTime = rtt
|
||||
|
||||
var out []*HealthCheck
|
||||
var out HealthChecks
|
||||
if err := decodeBody(resp, &out); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
@ -115,7 +170,7 @@ func (h *Health) Service(service, tag string, passingOnly bool, q *QueryOptions)
|
|||
|
||||
// State is used to retrieve all the checks in a given state.
|
||||
// The wildcard "any" state can also be used for all checks.
|
||||
func (h *Health) State(state string, q *QueryOptions) ([]*HealthCheck, *QueryMeta, error) {
|
||||
func (h *Health) State(state string, q *QueryOptions) (HealthChecks, *QueryMeta, error) {
|
||||
switch state {
|
||||
case HealthAny:
|
||||
case HealthWarning:
|
||||
|
|
@ -136,7 +191,7 @@ func (h *Health) State(state string, q *QueryOptions) ([]*HealthCheck, *QueryMet
|
|||
parseQueryMeta(resp, qm)
|
||||
qm.RequestTime = rtt
|
||||
|
||||
var out []*HealthCheck
|
||||
var out HealthChecks
|
||||
if err := decodeBody(resp, &out); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,21 +50,21 @@ type KVOp string
|
|||
|
||||
const (
|
||||
KVSet KVOp = "set"
|
||||
KVDelete = "delete"
|
||||
KVDeleteCAS = "delete-cas"
|
||||
KVDeleteTree = "delete-tree"
|
||||
KVCAS = "cas"
|
||||
KVLock = "lock"
|
||||
KVUnlock = "unlock"
|
||||
KVGet = "get"
|
||||
KVGetTree = "get-tree"
|
||||
KVCheckSession = "check-session"
|
||||
KVCheckIndex = "check-index"
|
||||
KVDelete KVOp = "delete"
|
||||
KVDeleteCAS KVOp = "delete-cas"
|
||||
KVDeleteTree KVOp = "delete-tree"
|
||||
KVCAS KVOp = "cas"
|
||||
KVLock KVOp = "lock"
|
||||
KVUnlock KVOp = "unlock"
|
||||
KVGet KVOp = "get"
|
||||
KVGetTree KVOp = "get-tree"
|
||||
KVCheckSession KVOp = "check-session"
|
||||
KVCheckIndex KVOp = "check-index"
|
||||
)
|
||||
|
||||
// KVTxnOp defines a single operation inside a transaction.
|
||||
type KVTxnOp struct {
|
||||
Verb string
|
||||
Verb KVOp
|
||||
Key string
|
||||
Value []byte
|
||||
Flags uint64
|
||||
|
|
|
|||
|
|
@ -43,6 +43,26 @@ type RaftConfiguration struct {
|
|||
Index uint64
|
||||
}
|
||||
|
||||
// keyringRequest is used for performing Keyring operations
|
||||
type keyringRequest struct {
|
||||
Key string
|
||||
}
|
||||
|
||||
// KeyringResponse is returned when listing the gossip encryption keys
|
||||
type KeyringResponse struct {
|
||||
// Whether this response is for a WAN ring
|
||||
WAN bool
|
||||
|
||||
// The datacenter name this request corresponds to
|
||||
Datacenter string
|
||||
|
||||
// A map of the encryption keys to the number of nodes they're installed on
|
||||
Keys map[string]int
|
||||
|
||||
// The total number of nodes in this ring
|
||||
NumNodes int
|
||||
}
|
||||
|
||||
// RaftGetConfiguration is used to query the current Raft peer set.
|
||||
func (op *Operator) RaftGetConfiguration(q *QueryOptions) (*RaftConfiguration, error) {
|
||||
r := op.c.newRequest("GET", "/v1/operator/raft/configuration")
|
||||
|
|
@ -79,3 +99,65 @@ func (op *Operator) RaftRemovePeerByAddress(address string, q *WriteOptions) err
|
|||
resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// KeyringInstall is used to install a new gossip encryption key into the cluster
|
||||
func (op *Operator) KeyringInstall(key string, q *WriteOptions) error {
|
||||
r := op.c.newRequest("POST", "/v1/operator/keyring")
|
||||
r.setWriteOptions(q)
|
||||
r.obj = keyringRequest{
|
||||
Key: key,
|
||||
}
|
||||
_, resp, err := requireOK(op.c.doRequest(r))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// KeyringList is used to list the gossip keys installed in the cluster
|
||||
func (op *Operator) KeyringList(q *QueryOptions) ([]*KeyringResponse, error) {
|
||||
r := op.c.newRequest("GET", "/v1/operator/keyring")
|
||||
r.setQueryOptions(q)
|
||||
_, resp, err := requireOK(op.c.doRequest(r))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var out []*KeyringResponse
|
||||
if err := decodeBody(resp, &out); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// KeyringRemove is used to remove a gossip encryption key from the cluster
|
||||
func (op *Operator) KeyringRemove(key string, q *WriteOptions) error {
|
||||
r := op.c.newRequest("DELETE", "/v1/operator/keyring")
|
||||
r.setWriteOptions(q)
|
||||
r.obj = keyringRequest{
|
||||
Key: key,
|
||||
}
|
||||
_, resp, err := requireOK(op.c.doRequest(r))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// KeyringUse is used to change the active gossip encryption key
|
||||
func (op *Operator) KeyringUse(key string, q *WriteOptions) error {
|
||||
r := op.c.newRequest("PUT", "/v1/operator/keyring")
|
||||
r.setWriteOptions(q)
|
||||
r.obj = keyringRequest{
|
||||
Key: key,
|
||||
}
|
||||
_, resp, err := requireOK(op.c.doRequest(r))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ type Flattener interface {
|
|||
MoveTo(x, y float64)
|
||||
// LineTo Draw a line from the current position to the point (x, y)
|
||||
LineTo(x, y float64)
|
||||
// LineJoin add the most recent starting point to close the path to create a polygon
|
||||
// LineJoin use Round, Bevel or miter to join points
|
||||
LineJoin()
|
||||
// Close add the most recent starting point to close the path to create a polygon
|
||||
Close()
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
package draw2dbase
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
|
||||
|
|
@ -132,6 +133,10 @@ func (gc *StackGraphicContext) BeginPath() {
|
|||
gc.Current.Path.Clear()
|
||||
}
|
||||
|
||||
func (gc *StackGraphicContext) GetPath() draw2d.Path {
|
||||
return *gc.Current.Path.Copy()
|
||||
}
|
||||
|
||||
func (gc *StackGraphicContext) IsEmpty() bool {
|
||||
return gc.Current.Path.IsEmpty()
|
||||
}
|
||||
|
|
@ -191,3 +196,8 @@ func (gc *StackGraphicContext) Restore() {
|
|||
oldContext.Previous = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (gc *StackGraphicContext) GetFontName() string {
|
||||
fontData := gc.Current.FontData
|
||||
return fmt.Sprintf("%s:%d:%d:%d", fontData.Name, fontData.Family, fontData.Style, gc.Current.FontSize)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
package draw2dbase
|
||||
|
||||
import "github.com/llgcode/draw2d"
|
||||
|
||||
var glyphCache map[string]map[rune]*Glyph
|
||||
|
||||
func init() {
|
||||
glyphCache = make(map[string]map[rune]*Glyph)
|
||||
}
|
||||
|
||||
// FetchGlyph fetches a glyph from the cache, calling renderGlyph first if it doesn't already exist
|
||||
func FetchGlyph(gc draw2d.GraphicContext, fontName string, chr rune) *Glyph {
|
||||
if glyphCache[fontName] == nil {
|
||||
glyphCache[fontName] = make(map[rune]*Glyph, 60)
|
||||
}
|
||||
if glyphCache[fontName][chr] == nil {
|
||||
glyphCache[fontName][chr] = renderGlyph(gc, fontName, chr)
|
||||
}
|
||||
return glyphCache[fontName][chr].Copy()
|
||||
}
|
||||
|
||||
// renderGlyph renders a glyph then caches and returns it
|
||||
func renderGlyph(gc draw2d.GraphicContext, fontName string, chr rune) *Glyph {
|
||||
gc.Save()
|
||||
defer gc.Restore()
|
||||
gc.BeginPath()
|
||||
width := gc.CreateStringPath(string(chr), 0, 0)
|
||||
path := gc.GetPath()
|
||||
return &Glyph{
|
||||
path: &path,
|
||||
Width: width,
|
||||
}
|
||||
}
|
||||
|
||||
// Glyph represents a rune which has been converted to a Path and width
|
||||
type Glyph struct {
|
||||
// path represents a glyph, it is always at (0, 0)
|
||||
path *draw2d.Path
|
||||
// Width of the glyph
|
||||
Width float64
|
||||
}
|
||||
|
||||
func (g *Glyph) Copy() *Glyph {
|
||||
return &Glyph{
|
||||
path: g.path.Copy(),
|
||||
Width: g.Width,
|
||||
}
|
||||
}
|
||||
|
||||
// Fill copies a glyph from the cache, and fills it
|
||||
func (g *Glyph) Fill(gc draw2d.GraphicContext, x, y float64) float64 {
|
||||
gc.Save()
|
||||
gc.BeginPath()
|
||||
gc.Translate(x, y)
|
||||
gc.Fill(g.path)
|
||||
gc.Restore()
|
||||
return g.Width
|
||||
}
|
||||
|
||||
// Stroke fetches a glyph from the cache, and strokes it
|
||||
func (g *Glyph) Stroke(gc draw2d.GraphicContext, x, y float64) float64 {
|
||||
gc.Save()
|
||||
gc.BeginPath()
|
||||
gc.Translate(x, y)
|
||||
gc.Stroke(g.path)
|
||||
gc.Restore()
|
||||
return g.Width
|
||||
}
|
||||
|
|
@ -117,27 +117,57 @@ func (gc *GraphicContext) DrawImage(img image.Image) {
|
|||
}
|
||||
|
||||
// FillString draws the text at point (0, 0)
|
||||
func (gc *GraphicContext) FillString(text string) (cursor float64) {
|
||||
func (gc *GraphicContext) FillString(text string) (width float64) {
|
||||
return gc.FillStringAt(text, 0, 0)
|
||||
}
|
||||
|
||||
// FillStringAt draws the text at the specified point (x, y)
|
||||
func (gc *GraphicContext) FillStringAt(text string, x, y float64) (cursor float64) {
|
||||
width := gc.CreateStringPath(text, x, y)
|
||||
gc.Fill()
|
||||
return width
|
||||
func (gc *GraphicContext) FillStringAt(text string, x, y float64) (width float64) {
|
||||
f, err := gc.loadCurrentFont()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return 0.0
|
||||
}
|
||||
startx := x
|
||||
prev, hasPrev := truetype.Index(0), false
|
||||
fontName := gc.GetFontName()
|
||||
for _, r := range text {
|
||||
index := f.Index(r)
|
||||
if hasPrev {
|
||||
x += fUnitsToFloat64(f.Kern(fixed.Int26_6(gc.Current.Scale), prev, index))
|
||||
}
|
||||
glyph := draw2dbase.FetchGlyph(gc, fontName, r)
|
||||
x += glyph.Fill(gc, x, y)
|
||||
prev, hasPrev = index, true
|
||||
}
|
||||
return x - startx
|
||||
}
|
||||
|
||||
// StrokeString draws the contour of the text at point (0, 0)
|
||||
func (gc *GraphicContext) StrokeString(text string) (cursor float64) {
|
||||
func (gc *GraphicContext) StrokeString(text string) (width float64) {
|
||||
return gc.StrokeStringAt(text, 0, 0)
|
||||
}
|
||||
|
||||
// StrokeStringAt draws the contour of the text at point (x, y)
|
||||
func (gc *GraphicContext) StrokeStringAt(text string, x, y float64) (cursor float64) {
|
||||
width := gc.CreateStringPath(text, x, y)
|
||||
gc.Stroke()
|
||||
return width
|
||||
func (gc *GraphicContext) StrokeStringAt(text string, x, y float64) (width float64) {
|
||||
f, err := gc.loadCurrentFont()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return 0.0
|
||||
}
|
||||
startx := x
|
||||
prev, hasPrev := truetype.Index(0), false
|
||||
fontName := gc.GetFontName()
|
||||
for _, r := range text {
|
||||
index := f.Index(r)
|
||||
if hasPrev {
|
||||
x += fUnitsToFloat64(f.Kern(fixed.Int26_6(gc.Current.Scale), prev, index))
|
||||
}
|
||||
glyph := draw2dbase.FetchGlyph(gc, fontName, r)
|
||||
x += glyph.Stroke(gc, x, y)
|
||||
prev, hasPrev = index, true
|
||||
}
|
||||
return x - startx
|
||||
}
|
||||
|
||||
func (gc *GraphicContext) loadCurrentFont() (*truetype.Font, error) {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ type GraphicContext interface {
|
|||
PathBuilder
|
||||
// BeginPath creates a new path
|
||||
BeginPath()
|
||||
// GetPath copies the current path, then returns it
|
||||
GetPath() Path
|
||||
// GetMatrixTransform returns the current transformation matrix
|
||||
GetMatrixTransform() Matrix
|
||||
// SetMatrixTransform sets the current transformation matrix
|
||||
|
|
@ -48,6 +50,8 @@ type GraphicContext interface {
|
|||
SetFontData(fontData FontData)
|
||||
// GetFontData gets the current FontData
|
||||
GetFontData() FontData
|
||||
// GetFontName gets the current FontData as a string
|
||||
GetFontName() string
|
||||
// DrawImage draws the raster image in the current canvas
|
||||
DrawImage(image image.Image)
|
||||
// Save the context and push it to the context stack
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Yasuhiro Matsumoto
|
||||
|
||||
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.
|
||||
|
|
@ -1,7 +1,12 @@
|
|||
package runewidth
|
||||
|
||||
var EastAsianWidth = IsEastAsian()
|
||||
var DefaultCondition = &Condition{EastAsianWidth}
|
||||
var (
|
||||
// EastAsianWidth will be set true if the current locale is CJK
|
||||
EastAsianWidth = IsEastAsian()
|
||||
|
||||
// DefaultCondition is a condition in current locale
|
||||
DefaultCondition = &Condition{EastAsianWidth}
|
||||
)
|
||||
|
||||
type interval struct {
|
||||
first rune
|
||||
|
|
@ -302,10 +307,12 @@ var ctypes = []intervalType{
|
|||
{0x100000, 0x10FFFE, ambiguous},
|
||||
}
|
||||
|
||||
// Condition have flag EastAsianWidth whether the current locale is CJK or not.
|
||||
type Condition struct {
|
||||
EastAsianWidth bool
|
||||
}
|
||||
|
||||
// NewCondition return new instance of Condition which is current locale.
|
||||
func NewCondition() *Condition {
|
||||
return &Condition{EastAsianWidth}
|
||||
}
|
||||
|
|
@ -344,6 +351,7 @@ func (c *Condition) RuneWidth(r rune) int {
|
|||
return 1
|
||||
}
|
||||
|
||||
// StringWidth return width as you can see
|
||||
func (c *Condition) StringWidth(s string) (width int) {
|
||||
for _, r := range []rune(s) {
|
||||
width += c.RuneWidth(r)
|
||||
|
|
@ -351,6 +359,7 @@ func (c *Condition) StringWidth(s string) (width int) {
|
|||
return width
|
||||
}
|
||||
|
||||
// Truncate return string truncated with w cells
|
||||
func (c *Condition) Truncate(s string, w int, tail string) string {
|
||||
if c.StringWidth(s) <= w {
|
||||
return s
|
||||
|
|
@ -370,6 +379,7 @@ func (c *Condition) Truncate(s string, w int, tail string) string {
|
|||
return string(r[0:i]) + tail
|
||||
}
|
||||
|
||||
// Wrap return string wrapped with w cells
|
||||
func (c *Condition) Wrap(s string, w int) string {
|
||||
width := 0
|
||||
out := ""
|
||||
|
|
@ -392,6 +402,7 @@ func (c *Condition) Wrap(s string, w int) string {
|
|||
return out
|
||||
}
|
||||
|
||||
// FillLeft return string filled in left by spaces in w cells
|
||||
func (c *Condition) FillLeft(s string, w int) string {
|
||||
width := c.StringWidth(s)
|
||||
count := w - width
|
||||
|
|
@ -405,6 +416,7 @@ func (c *Condition) FillLeft(s string, w int) string {
|
|||
return s
|
||||
}
|
||||
|
||||
// FillRight return string filled in left by spaces in w cells
|
||||
func (c *Condition) FillRight(s string, w int) string {
|
||||
width := c.StringWidth(s)
|
||||
count := w - width
|
||||
|
|
@ -438,27 +450,32 @@ func IsAmbiguousWidth(r rune) bool {
|
|||
return ct(r) == ambiguous
|
||||
}
|
||||
|
||||
// IsAmbiguousWidth returns whether is ambiguous width or not.
|
||||
// IsNeutralWidth returns whether is neutral width or not.
|
||||
func IsNeutralWidth(r rune) bool {
|
||||
return ct(r) == neutral
|
||||
}
|
||||
|
||||
// StringWidth return width as you can see
|
||||
func StringWidth(s string) (width int) {
|
||||
return DefaultCondition.StringWidth(s)
|
||||
}
|
||||
|
||||
// Truncate return string truncated with w cells
|
||||
func Truncate(s string, w int, tail string) string {
|
||||
return DefaultCondition.Truncate(s, w, tail)
|
||||
}
|
||||
|
||||
// Wrap return string wrapped with w cells
|
||||
func Wrap(s string, w int) string {
|
||||
return DefaultCondition.Wrap(s, w)
|
||||
}
|
||||
|
||||
// FillLeft return string filled in left by spaces in w cells
|
||||
func FillLeft(s string, w int) string {
|
||||
return DefaultCondition.FillLeft(s, w)
|
||||
}
|
||||
|
||||
// FillRight return string filled in left by spaces in w cells
|
||||
func FillRight(s string, w int) string {
|
||||
return DefaultCondition.FillRight(s, w)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,20 +10,25 @@ import (
|
|||
|
||||
var reLoc = regexp.MustCompile(`^[a-z][a-z][a-z]?(?:_[A-Z][A-Z])?\.(.+)`)
|
||||
|
||||
func IsEastAsian() bool {
|
||||
locale := os.Getenv("LC_CTYPE")
|
||||
if locale == "" {
|
||||
locale = os.Getenv("LANG")
|
||||
}
|
||||
|
||||
// ignore C locale
|
||||
if locale == "POSIX" || locale == "C" {
|
||||
return false
|
||||
}
|
||||
if len(locale) > 1 && locale[0] == 'C' && (locale[1] == '.' || locale[1] == '-') {
|
||||
return false
|
||||
}
|
||||
var mblenTable = map[string]int{
|
||||
"utf-8": 6,
|
||||
"utf8": 6,
|
||||
"jis": 8,
|
||||
"eucjp": 3,
|
||||
"euckr": 2,
|
||||
"euccn": 2,
|
||||
"sjis": 2,
|
||||
"cp932": 2,
|
||||
"cp51932": 2,
|
||||
"cp936": 2,
|
||||
"cp949": 2,
|
||||
"cp950": 2,
|
||||
"big5": 2,
|
||||
"gbk": 2,
|
||||
"gb2312": 2,
|
||||
}
|
||||
|
||||
func isEastAsian(locale string) bool {
|
||||
charset := strings.ToLower(locale)
|
||||
r := reLoc.FindStringSubmatch(locale)
|
||||
if len(r) == 2 {
|
||||
|
|
@ -40,26 +45,11 @@ func IsEastAsian() bool {
|
|||
break
|
||||
}
|
||||
}
|
||||
|
||||
mbc_max := 1
|
||||
switch charset {
|
||||
case "utf-8", "utf8":
|
||||
mbc_max = 6
|
||||
case "jis":
|
||||
mbc_max = 8
|
||||
case "eucjp":
|
||||
mbc_max = 3
|
||||
case "euckr", "euccn":
|
||||
mbc_max = 2
|
||||
case "sjis", "cp932", "cp51932", "cp936", "cp949", "cp950":
|
||||
mbc_max = 2
|
||||
case "big5":
|
||||
mbc_max = 2
|
||||
case "gbk", "gb2312":
|
||||
mbc_max = 2
|
||||
max := 1
|
||||
if m, ok := mblenTable[charset]; ok {
|
||||
max = m
|
||||
}
|
||||
|
||||
if mbc_max > 1 && (charset[0] != 'u' ||
|
||||
if max > 1 && (charset[0] != 'u' ||
|
||||
strings.HasPrefix(locale, "ja") ||
|
||||
strings.HasPrefix(locale, "ko") ||
|
||||
strings.HasPrefix(locale, "zh")) {
|
||||
|
|
@ -67,3 +57,21 @@ func IsEastAsian() bool {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsEastAsian return true if the current locale is CJK
|
||||
func IsEastAsian() bool {
|
||||
locale := os.Getenv("LC_CTYPE")
|
||||
if locale == "" {
|
||||
locale = os.Getenv("LANG")
|
||||
}
|
||||
|
||||
// ignore C locale
|
||||
if locale == "POSIX" || locale == "C" {
|
||||
return false
|
||||
}
|
||||
if len(locale) > 1 && locale[0] == 'C' && (locale[1] == '.' || locale[1] == '-') {
|
||||
return false
|
||||
}
|
||||
|
||||
return isEastAsian(locale)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ var (
|
|||
procGetConsoleOutputCP = kernel32.NewProc("GetConsoleOutputCP")
|
||||
)
|
||||
|
||||
// IsEastAsian return true if the current locale is CJK
|
||||
func IsEastAsian() bool {
|
||||
r1, _, _ := procGetConsoleOutputCP.Call()
|
||||
if r1 == 0 {
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@
|
|||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
|
|
@ -186,7 +186,7 @@
|
|||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2013 Matt T. Proud
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
Copyright 2012 Matt T. Proud (matt.proud@gmail.com)
|
||||
|
|
@ -38,7 +38,7 @@ var errInvalidVarint = errors.New("invalid varint32 encountered")
|
|||
func ReadDelimited(r io.Reader, m proto.Message) (n int, err error) {
|
||||
// Per AbstractParser#parsePartialDelimitedFrom with
|
||||
// CodedInputStream#readRawVarint32.
|
||||
headerBuf := make([]byte, binary.MaxVarintLen32)
|
||||
var headerBuf [binary.MaxVarintLen32]byte
|
||||
var bytesRead, varIntBytes int
|
||||
var messageLength uint64
|
||||
for varIntBytes == 0 { // i.e. no varint has been decoded yet.
|
||||
|
|
|
|||
|
|
@ -33,8 +33,8 @@ func WriteDelimited(w io.Writer, m proto.Message) (n int, err error) {
|
|||
return 0, err
|
||||
}
|
||||
|
||||
buf := make([]byte, binary.MaxVarintLen32)
|
||||
encodedLength := binary.PutUvarint(buf, uint64(len(buffer)))
|
||||
var buf [binary.MaxVarintLen32]byte
|
||||
encodedLength := binary.PutUvarint(buf[:], uint64(len(buffer)))
|
||||
|
||||
sync, err := w.Write(buf[:encodedLength])
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -46,10 +46,7 @@ func ResponseFormat(h http.Header) Format {
|
|||
return FmtUnknown
|
||||
}
|
||||
|
||||
const (
|
||||
textType = "text/plain"
|
||||
jsonType = "application/json"
|
||||
)
|
||||
const textType = "text/plain"
|
||||
|
||||
switch mediatype {
|
||||
case ProtoType:
|
||||
|
|
@ -66,22 +63,6 @@ func ResponseFormat(h http.Header) Format {
|
|||
return FmtUnknown
|
||||
}
|
||||
return FmtText
|
||||
|
||||
case jsonType:
|
||||
var prometheusAPIVersion string
|
||||
|
||||
if params["schema"] == "prometheus/telemetry" && params["version"] != "" {
|
||||
prometheusAPIVersion = params["version"]
|
||||
} else {
|
||||
prometheusAPIVersion = h.Get("X-Prometheus-API-Version")
|
||||
}
|
||||
|
||||
switch prometheusAPIVersion {
|
||||
case "0.0.2", "":
|
||||
return fmtJSON2
|
||||
default:
|
||||
return FmtUnknown
|
||||
}
|
||||
}
|
||||
|
||||
return FmtUnknown
|
||||
|
|
@ -93,8 +74,6 @@ func NewDecoder(r io.Reader, format Format) Decoder {
|
|||
switch format {
|
||||
case FmtProtoDelim:
|
||||
return &protoDecoder{r: r}
|
||||
case fmtJSON2:
|
||||
return newJSON2Decoder(r)
|
||||
}
|
||||
return &textDecoder{r: r}
|
||||
}
|
||||
|
|
@ -107,10 +86,32 @@ type protoDecoder struct {
|
|||
// Decode implements the Decoder interface.
|
||||
func (d *protoDecoder) Decode(v *dto.MetricFamily) error {
|
||||
_, err := pbutil.ReadDelimited(d.r, v)
|
||||
return err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !model.IsValidMetricName(model.LabelValue(v.GetName())) {
|
||||
return fmt.Errorf("invalid metric name %q", v.GetName())
|
||||
}
|
||||
for _, m := range v.GetMetric() {
|
||||
if m == nil {
|
||||
continue
|
||||
}
|
||||
for _, l := range m.GetLabel() {
|
||||
if l == nil {
|
||||
continue
|
||||
}
|
||||
if !model.LabelValue(l.GetValue()).IsValid() {
|
||||
return fmt.Errorf("invalid label value %q", l.GetValue())
|
||||
}
|
||||
if !model.LabelName(l.GetName()).IsValid() {
|
||||
return fmt.Errorf("invalid label name %q", l.GetName())
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// textDecoder implements the Decoder interface for the text protcol.
|
||||
// textDecoder implements the Decoder interface for the text protocol.
|
||||
type textDecoder struct {
|
||||
r io.Reader
|
||||
p TextParser
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ import (
|
|||
"io"
|
||||
"net/http"
|
||||
|
||||
"bitbucket.org/ww/goautoneg"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/matttproud/golang_protobuf_extensions/pbutil"
|
||||
"github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -29,9 +29,6 @@ const (
|
|||
FmtProtoDelim Format = ProtoFmt + ` encoding=delimited`
|
||||
FmtProtoText Format = ProtoFmt + ` encoding=text`
|
||||
FmtProtoCompact Format = ProtoFmt + ` encoding=compact-text`
|
||||
|
||||
// fmtJSON2 is hidden as it is deprecated.
|
||||
fmtJSON2 Format = `application/json; version=0.0.2`
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ import "bytes"
|
|||
|
||||
// Fuzz text metric parser with with github.com/dvyukov/go-fuzz:
|
||||
//
|
||||
// go-fuzz-build github.com/prometheus/client_golang/text
|
||||
// go-fuzz -bin text-fuzz.zip -workdir fuzz
|
||||
// go-fuzz-build github.com/prometheus/common/expfmt
|
||||
// go-fuzz -bin expfmt-fuzz.zip -workdir fuzz
|
||||
//
|
||||
// Further input samples should go in the folder fuzz/corpus.
|
||||
func Fuzz(in []byte) int {
|
||||
|
|
|
|||
|
|
@ -1,162 +0,0 @@
|
|||
// Copyright 2015 The Prometheus Authors
|
||||
// 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.
|
||||
|
||||
package expfmt
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
|
||||
"github.com/prometheus/common/model"
|
||||
)
|
||||
|
||||
type json2Decoder struct {
|
||||
dec *json.Decoder
|
||||
fams []*dto.MetricFamily
|
||||
}
|
||||
|
||||
func newJSON2Decoder(r io.Reader) Decoder {
|
||||
return &json2Decoder{
|
||||
dec: json.NewDecoder(r),
|
||||
}
|
||||
}
|
||||
|
||||
type histogram002 struct {
|
||||
Labels model.LabelSet `json:"labels"`
|
||||
Values map[string]float64 `json:"value"`
|
||||
}
|
||||
|
||||
type counter002 struct {
|
||||
Labels model.LabelSet `json:"labels"`
|
||||
Value float64 `json:"value"`
|
||||
}
|
||||
|
||||
func protoLabelSet(base, ext model.LabelSet) []*dto.LabelPair {
|
||||
labels := base.Clone().Merge(ext)
|
||||
delete(labels, model.MetricNameLabel)
|
||||
|
||||
names := make([]string, 0, len(labels))
|
||||
for ln := range labels {
|
||||
names = append(names, string(ln))
|
||||
}
|
||||
sort.Strings(names)
|
||||
|
||||
pairs := make([]*dto.LabelPair, 0, len(labels))
|
||||
|
||||
for _, ln := range names {
|
||||
lv := labels[model.LabelName(ln)]
|
||||
|
||||
pairs = append(pairs, &dto.LabelPair{
|
||||
Name: proto.String(ln),
|
||||
Value: proto.String(string(lv)),
|
||||
})
|
||||
}
|
||||
|
||||
return pairs
|
||||
}
|
||||
|
||||
func (d *json2Decoder) more() error {
|
||||
var entities []struct {
|
||||
BaseLabels model.LabelSet `json:"baseLabels"`
|
||||
Docstring string `json:"docstring"`
|
||||
Metric struct {
|
||||
Type string `json:"type"`
|
||||
Values json.RawMessage `json:"value"`
|
||||
} `json:"metric"`
|
||||
}
|
||||
|
||||
if err := d.dec.Decode(&entities); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, e := range entities {
|
||||
f := &dto.MetricFamily{
|
||||
Name: proto.String(string(e.BaseLabels[model.MetricNameLabel])),
|
||||
Help: proto.String(e.Docstring),
|
||||
Type: dto.MetricType_UNTYPED.Enum(),
|
||||
Metric: []*dto.Metric{},
|
||||
}
|
||||
|
||||
d.fams = append(d.fams, f)
|
||||
|
||||
switch e.Metric.Type {
|
||||
case "counter", "gauge":
|
||||
var values []counter002
|
||||
|
||||
if err := json.Unmarshal(e.Metric.Values, &values); err != nil {
|
||||
return fmt.Errorf("could not extract %s value: %s", e.Metric.Type, err)
|
||||
}
|
||||
|
||||
for _, ctr := range values {
|
||||
f.Metric = append(f.Metric, &dto.Metric{
|
||||
Label: protoLabelSet(e.BaseLabels, ctr.Labels),
|
||||
Untyped: &dto.Untyped{
|
||||
Value: proto.Float64(ctr.Value),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
case "histogram":
|
||||
var values []histogram002
|
||||
|
||||
if err := json.Unmarshal(e.Metric.Values, &values); err != nil {
|
||||
return fmt.Errorf("could not extract %s value: %s", e.Metric.Type, err)
|
||||
}
|
||||
|
||||
for _, hist := range values {
|
||||
quants := make([]string, 0, len(values))
|
||||
for q := range hist.Values {
|
||||
quants = append(quants, q)
|
||||
}
|
||||
|
||||
sort.Strings(quants)
|
||||
|
||||
for _, q := range quants {
|
||||
value := hist.Values[q]
|
||||
// The correct label is "quantile" but to not break old expressions
|
||||
// this remains "percentile"
|
||||
hist.Labels["percentile"] = model.LabelValue(q)
|
||||
|
||||
f.Metric = append(f.Metric, &dto.Metric{
|
||||
Label: protoLabelSet(e.BaseLabels, hist.Labels),
|
||||
Untyped: &dto.Untyped{
|
||||
Value: proto.Float64(value),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unknown metric type %q", e.Metric.Type)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode implements the Decoder interface.
|
||||
func (d *json2Decoder) Decode(v *dto.MetricFamily) error {
|
||||
if len(d.fams) == 0 {
|
||||
if err := d.more(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
*v = *d.fams[0]
|
||||
d.fams = d.fams[1:]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -14,7 +14,6 @@
|
|||
package expfmt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
|
|
@ -26,9 +25,12 @@ import (
|
|||
|
||||
// MetricFamilyToText converts a MetricFamily proto message into text format and
|
||||
// writes the resulting lines to 'out'. It returns the number of bytes written
|
||||
// and any error encountered. This function does not perform checks on the
|
||||
// content of the metric and label names, i.e. invalid metric or label names
|
||||
// and any error encountered. The output will have the same order as the input,
|
||||
// no further sorting is performed. Furthermore, this function assumes the input
|
||||
// is already sanitized and does not perform any sanity checks. If the input
|
||||
// contains duplicate metrics or invalid metric or label names, the conversion
|
||||
// will result in invalid text format output.
|
||||
//
|
||||
// This method fulfills the type 'prometheus.encoder'.
|
||||
func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (int, error) {
|
||||
var written int
|
||||
|
|
@ -285,21 +287,17 @@ func labelPairsToText(
|
|||
return written, nil
|
||||
}
|
||||
|
||||
var (
|
||||
escape = strings.NewReplacer("\\", `\\`, "\n", `\n`)
|
||||
escapeWithDoubleQuote = strings.NewReplacer("\\", `\\`, "\n", `\n`, "\"", `\"`)
|
||||
)
|
||||
|
||||
// escapeString replaces '\' by '\\', new line character by '\n', and - if
|
||||
// includeDoubleQuote is true - '"' by '\"'.
|
||||
func escapeString(v string, includeDoubleQuote bool) string {
|
||||
result := bytes.NewBuffer(make([]byte, 0, len(v)))
|
||||
for _, c := range v {
|
||||
switch {
|
||||
case c == '\\':
|
||||
result.WriteString(`\\`)
|
||||
case includeDoubleQuote && c == '"':
|
||||
result.WriteString(`\"`)
|
||||
case c == '\n':
|
||||
result.WriteString(`\n`)
|
||||
default:
|
||||
result.WriteRune(c)
|
||||
}
|
||||
if includeDoubleQuote {
|
||||
return escapeWithDoubleQuote.Replace(v)
|
||||
}
|
||||
return result.String()
|
||||
|
||||
return escape.Replace(v)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ func (e ParseError) Error() string {
|
|||
}
|
||||
|
||||
// TextParser is used to parse the simple and flat text-based exchange format. Its
|
||||
// nil value is ready to use.
|
||||
// zero value is ready to use.
|
||||
type TextParser struct {
|
||||
metricFamiliesByName map[string]*dto.MetricFamily
|
||||
buf *bufio.Reader // Where the parsed input is read through.
|
||||
|
|
@ -108,6 +108,13 @@ func (p *TextParser) TextToMetricFamilies(in io.Reader) (map[string]*dto.MetricF
|
|||
delete(p.metricFamiliesByName, k)
|
||||
}
|
||||
}
|
||||
// If p.err is io.EOF now, we have run into a premature end of the input
|
||||
// stream. Turn this error into something nicer and more
|
||||
// meaningful. (io.EOF is often used as a signal for the legitimate end
|
||||
// of an input stream.)
|
||||
if p.err == io.EOF {
|
||||
p.parseError("unexpected end of input stream")
|
||||
}
|
||||
return p.metricFamiliesByName, p.err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,8 +35,9 @@ type Alert struct {
|
|||
Annotations LabelSet `json:"annotations"`
|
||||
|
||||
// The known time range for this alert. Both ends are optional.
|
||||
StartsAt time.Time `json:"startsAt,omitempty"`
|
||||
EndsAt time.Time `json:"endsAt,omitempty"`
|
||||
StartsAt time.Time `json:"startsAt,omitempty"`
|
||||
EndsAt time.Time `json:"endsAt,omitempty"`
|
||||
GeneratorURL string `json:"generatorURL"`
|
||||
}
|
||||
|
||||
// Name returns the name of the alert. It is equivalent to the "alertname" label.
|
||||
|
|
@ -60,10 +61,16 @@ func (a *Alert) String() string {
|
|||
|
||||
// Resolved returns true iff the activity interval ended in the past.
|
||||
func (a *Alert) Resolved() bool {
|
||||
return a.ResolvedAt(time.Now())
|
||||
}
|
||||
|
||||
// ResolvedAt returns true off the activity interval ended before
|
||||
// the given timestamp.
|
||||
func (a *Alert) ResolvedAt(ts time.Time) bool {
|
||||
if a.EndsAt.IsZero() {
|
||||
return false
|
||||
}
|
||||
return !a.EndsAt.After(time.Now())
|
||||
return !a.EndsAt.After(ts)
|
||||
}
|
||||
|
||||
// Status returns the status of the alert.
|
||||
|
|
@ -74,6 +81,26 @@ func (a *Alert) Status() AlertStatus {
|
|||
return AlertFiring
|
||||
}
|
||||
|
||||
// Validate checks whether the alert data is inconsistent.
|
||||
func (a *Alert) Validate() error {
|
||||
if a.StartsAt.IsZero() {
|
||||
return fmt.Errorf("start time missing")
|
||||
}
|
||||
if !a.EndsAt.IsZero() && a.EndsAt.Before(a.StartsAt) {
|
||||
return fmt.Errorf("start time must be before end time")
|
||||
}
|
||||
if err := a.Labels.Validate(); err != nil {
|
||||
return fmt.Errorf("invalid label set: %s", err)
|
||||
}
|
||||
if len(a.Labels) == 0 {
|
||||
return fmt.Errorf("at least one label pair required")
|
||||
}
|
||||
if err := a.Annotations.Validate(); err != nil {
|
||||
return fmt.Errorf("invalid annotations: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Alert is a list of alerts that can be sorted in chronological order.
|
||||
type Alerts []*Alert
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright 2015 The Prometheus Authors
|
||||
// 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.
|
||||
|
||||
package model
|
||||
|
||||
// Inline and byte-free variant of hash/fnv's fnv64a.
|
||||
|
||||
const (
|
||||
offset64 = 14695981039346656037
|
||||
prime64 = 1099511628211
|
||||
)
|
||||
|
||||
// hashNew initializies a new fnv64a hash value.
|
||||
func hashNew() uint64 {
|
||||
return offset64
|
||||
}
|
||||
|
||||
// hashAdd adds a string to a fnv64a hash value, returning the updated hash.
|
||||
func hashAdd(h uint64, s string) uint64 {
|
||||
for i := 0; i < len(s); i++ {
|
||||
h ^= uint64(s[i])
|
||||
h *= prime64
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
// hashAddByte adds a byte to a fnv64a hash value, returning the updated hash.
|
||||
func hashAddByte(h uint64, b byte) uint64 {
|
||||
h ^= uint64(b)
|
||||
h *= prime64
|
||||
return h
|
||||
}
|
||||
|
|
@ -17,8 +17,8 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -80,20 +80,37 @@ const (
|
|||
QuantileLabel = "quantile"
|
||||
)
|
||||
|
||||
// LabelNameRE is a regular expression matching valid label names.
|
||||
// LabelNameRE is a regular expression matching valid label names. Note that the
|
||||
// IsValid method of LabelName performs the same check but faster than a match
|
||||
// with this regular expression.
|
||||
var LabelNameRE = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9_]*$")
|
||||
|
||||
// A LabelName is a key for a LabelSet or Metric. It has a value associated
|
||||
// therewith.
|
||||
type LabelName string
|
||||
|
||||
// IsValid is true iff the label name matches the pattern of LabelNameRE. This
|
||||
// method, however, does not use LabelNameRE for the check but a much faster
|
||||
// hardcoded implementation.
|
||||
func (ln LabelName) IsValid() bool {
|
||||
if len(ln) == 0 {
|
||||
return false
|
||||
}
|
||||
for i, b := range ln {
|
||||
if !((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_' || (b >= '0' && b <= '9' && i > 0)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
func (ln *LabelName) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
var s string
|
||||
if err := unmarshal(&s); err != nil {
|
||||
return err
|
||||
}
|
||||
if !LabelNameRE.MatchString(s) {
|
||||
if !LabelName(s).IsValid() {
|
||||
return fmt.Errorf("%q is not a valid label name", s)
|
||||
}
|
||||
*ln = LabelName(s)
|
||||
|
|
@ -106,7 +123,7 @@ func (ln *LabelName) UnmarshalJSON(b []byte) error {
|
|||
if err := json.Unmarshal(b, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
if !LabelNameRE.MatchString(s) {
|
||||
if !LabelName(s).IsValid() {
|
||||
return fmt.Errorf("%q is not a valid label name", s)
|
||||
}
|
||||
*ln = LabelName(s)
|
||||
|
|
@ -139,6 +156,11 @@ func (l LabelNames) String() string {
|
|||
// A LabelValue is an associated value for a LabelName.
|
||||
type LabelValue string
|
||||
|
||||
// IsValid returns true iff the string is a valid UTF8.
|
||||
func (lv LabelValue) IsValid() bool {
|
||||
return utf8.ValidString(string(lv))
|
||||
}
|
||||
|
||||
// LabelValues is a sortable LabelValue slice. It implements sort.Interface.
|
||||
type LabelValues []LabelValue
|
||||
|
||||
|
|
@ -147,7 +169,7 @@ func (l LabelValues) Len() int {
|
|||
}
|
||||
|
||||
func (l LabelValues) Less(i, j int) bool {
|
||||
return sort.StringsAreSorted([]string{string(l[i]), string(l[j])})
|
||||
return string(l[i]) < string(l[j])
|
||||
}
|
||||
|
||||
func (l LabelValues) Swap(i, j int) {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,21 @@ import (
|
|||
// match.
|
||||
type LabelSet map[LabelName]LabelValue
|
||||
|
||||
// Validate checks whether all names and values in the label set
|
||||
// are valid.
|
||||
func (ls LabelSet) Validate() error {
|
||||
for ln, lv := range ls {
|
||||
if !ln.IsValid() {
|
||||
return fmt.Errorf("invalid name %q", ln)
|
||||
}
|
||||
if !lv.IsValid() {
|
||||
return fmt.Errorf("invalid value %q", lv)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Equal returns true iff both label sets have exactly the same key/value pairs.
|
||||
func (ls LabelSet) Equal(o LabelSet) bool {
|
||||
if len(ls) != len(o) {
|
||||
return false
|
||||
|
|
@ -90,6 +105,7 @@ func (ls LabelSet) Before(o LabelSet) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// Clone returns a copy of the label set.
|
||||
func (ls LabelSet) Clone() LabelSet {
|
||||
lsn := make(LabelSet, len(ls))
|
||||
for ln, lv := range ls {
|
||||
|
|
@ -144,7 +160,7 @@ func (l *LabelSet) UnmarshalJSON(b []byte) error {
|
|||
// LabelName as a string and does not call its UnmarshalJSON method.
|
||||
// Thus, we have to replicate the behavior here.
|
||||
for ln := range m {
|
||||
if !LabelNameRE.MatchString(string(ln)) {
|
||||
if !ln.IsValid() {
|
||||
return fmt.Errorf("%q is not a valid label name", ln)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,11 +15,18 @@ package model
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var separator = []byte{0}
|
||||
var (
|
||||
separator = []byte{0}
|
||||
// MetricNameRE is a regular expression matching valid metric
|
||||
// names. Note that the IsValidMetricName function performs the same
|
||||
// check but faster than a match with this regular expression.
|
||||
MetricNameRE = regexp.MustCompile(`^[a-zA-Z_:][a-zA-Z0-9_:]*$`)
|
||||
)
|
||||
|
||||
// A Metric is similar to a LabelSet, but the key difference is that a Metric is
|
||||
// a singleton and refers to one and only one stream of samples.
|
||||
|
|
@ -79,3 +86,18 @@ func (m Metric) Fingerprint() Fingerprint {
|
|||
func (m Metric) FastFingerprint() Fingerprint {
|
||||
return LabelSet(m).FastFingerprint()
|
||||
}
|
||||
|
||||
// IsValidMetricName returns true iff name matches the pattern of MetricNameRE.
|
||||
// This function, however, does not use MetricNameRE for the check but a much
|
||||
// faster hardcoded implementation.
|
||||
func IsValidMetricName(n LabelValue) bool {
|
||||
if len(n) == 0 {
|
||||
return false
|
||||
}
|
||||
for i, b := range n {
|
||||
if !((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_' || b == ':' || (b >= '0' && b <= '9' && i > 0)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,5 +12,5 @@
|
|||
// limitations under the License.
|
||||
|
||||
// Package model contains common data structures that are shared across
|
||||
// Prometheus componenets and libraries.
|
||||
// Prometheus components and libraries.
|
||||
package model
|
||||
|
|
|
|||
|
|
@ -14,11 +14,7 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"hash"
|
||||
"hash/fnv"
|
||||
"sort"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// SeparatorByte is a byte that cannot occur in valid UTF-8 sequences and is
|
||||
|
|
@ -28,30 +24,9 @@ const SeparatorByte byte = 255
|
|||
|
||||
var (
|
||||
// cache the signature of an empty label set.
|
||||
emptyLabelSignature = fnv.New64a().Sum64()
|
||||
|
||||
hashAndBufPool sync.Pool
|
||||
emptyLabelSignature = hashNew()
|
||||
)
|
||||
|
||||
type hashAndBuf struct {
|
||||
h hash.Hash64
|
||||
b bytes.Buffer
|
||||
}
|
||||
|
||||
func getHashAndBuf() *hashAndBuf {
|
||||
hb := hashAndBufPool.Get()
|
||||
if hb == nil {
|
||||
return &hashAndBuf{h: fnv.New64a()}
|
||||
}
|
||||
return hb.(*hashAndBuf)
|
||||
}
|
||||
|
||||
func putHashAndBuf(hb *hashAndBuf) {
|
||||
hb.h.Reset()
|
||||
hb.b.Reset()
|
||||
hashAndBufPool.Put(hb)
|
||||
}
|
||||
|
||||
// LabelsToSignature returns a quasi-unique signature (i.e., fingerprint) for a
|
||||
// given label set. (Collisions are possible but unlikely if the number of label
|
||||
// sets the function is applied to is small.)
|
||||
|
|
@ -66,18 +41,14 @@ func LabelsToSignature(labels map[string]string) uint64 {
|
|||
}
|
||||
sort.Strings(labelNames)
|
||||
|
||||
hb := getHashAndBuf()
|
||||
defer putHashAndBuf(hb)
|
||||
|
||||
sum := hashNew()
|
||||
for _, labelName := range labelNames {
|
||||
hb.b.WriteString(labelName)
|
||||
hb.b.WriteByte(SeparatorByte)
|
||||
hb.b.WriteString(labels[labelName])
|
||||
hb.b.WriteByte(SeparatorByte)
|
||||
hb.h.Write(hb.b.Bytes())
|
||||
hb.b.Reset()
|
||||
sum = hashAdd(sum, labelName)
|
||||
sum = hashAddByte(sum, SeparatorByte)
|
||||
sum = hashAdd(sum, labels[labelName])
|
||||
sum = hashAddByte(sum, SeparatorByte)
|
||||
}
|
||||
return hb.h.Sum64()
|
||||
return sum
|
||||
}
|
||||
|
||||
// labelSetToFingerprint works exactly as LabelsToSignature but takes a LabelSet as
|
||||
|
|
@ -93,18 +64,14 @@ func labelSetToFingerprint(ls LabelSet) Fingerprint {
|
|||
}
|
||||
sort.Sort(labelNames)
|
||||
|
||||
hb := getHashAndBuf()
|
||||
defer putHashAndBuf(hb)
|
||||
|
||||
sum := hashNew()
|
||||
for _, labelName := range labelNames {
|
||||
hb.b.WriteString(string(labelName))
|
||||
hb.b.WriteByte(SeparatorByte)
|
||||
hb.b.WriteString(string(ls[labelName]))
|
||||
hb.b.WriteByte(SeparatorByte)
|
||||
hb.h.Write(hb.b.Bytes())
|
||||
hb.b.Reset()
|
||||
sum = hashAdd(sum, string(labelName))
|
||||
sum = hashAddByte(sum, SeparatorByte)
|
||||
sum = hashAdd(sum, string(ls[labelName]))
|
||||
sum = hashAddByte(sum, SeparatorByte)
|
||||
}
|
||||
return Fingerprint(hb.h.Sum64())
|
||||
return Fingerprint(sum)
|
||||
}
|
||||
|
||||
// labelSetToFastFingerprint works similar to labelSetToFingerprint but uses a
|
||||
|
|
@ -116,17 +83,12 @@ func labelSetToFastFingerprint(ls LabelSet) Fingerprint {
|
|||
}
|
||||
|
||||
var result uint64
|
||||
hb := getHashAndBuf()
|
||||
defer putHashAndBuf(hb)
|
||||
|
||||
for labelName, labelValue := range ls {
|
||||
hb.b.WriteString(string(labelName))
|
||||
hb.b.WriteByte(SeparatorByte)
|
||||
hb.b.WriteString(string(labelValue))
|
||||
hb.h.Write(hb.b.Bytes())
|
||||
result ^= hb.h.Sum64()
|
||||
hb.h.Reset()
|
||||
hb.b.Reset()
|
||||
sum := hashNew()
|
||||
sum = hashAdd(sum, string(labelName))
|
||||
sum = hashAddByte(sum, SeparatorByte)
|
||||
sum = hashAdd(sum, string(labelValue))
|
||||
result ^= sum
|
||||
}
|
||||
return Fingerprint(result)
|
||||
}
|
||||
|
|
@ -136,24 +98,20 @@ func labelSetToFastFingerprint(ls LabelSet) Fingerprint {
|
|||
// specified LabelNames into the signature calculation. The labels passed in
|
||||
// will be sorted by this function.
|
||||
func SignatureForLabels(m Metric, labels ...LabelName) uint64 {
|
||||
if len(m) == 0 || len(labels) == 0 {
|
||||
if len(labels) == 0 {
|
||||
return emptyLabelSignature
|
||||
}
|
||||
|
||||
sort.Sort(LabelNames(labels))
|
||||
|
||||
hb := getHashAndBuf()
|
||||
defer putHashAndBuf(hb)
|
||||
|
||||
sum := hashNew()
|
||||
for _, label := range labels {
|
||||
hb.b.WriteString(string(label))
|
||||
hb.b.WriteByte(SeparatorByte)
|
||||
hb.b.WriteString(string(m[label]))
|
||||
hb.b.WriteByte(SeparatorByte)
|
||||
hb.h.Write(hb.b.Bytes())
|
||||
hb.b.Reset()
|
||||
sum = hashAdd(sum, string(label))
|
||||
sum = hashAddByte(sum, SeparatorByte)
|
||||
sum = hashAdd(sum, string(m[label]))
|
||||
sum = hashAddByte(sum, SeparatorByte)
|
||||
}
|
||||
return hb.h.Sum64()
|
||||
return sum
|
||||
}
|
||||
|
||||
// SignatureWithoutLabels works like LabelsToSignature but takes a Metric as
|
||||
|
|
@ -175,16 +133,12 @@ func SignatureWithoutLabels(m Metric, labels map[LabelName]struct{}) uint64 {
|
|||
}
|
||||
sort.Sort(labelNames)
|
||||
|
||||
hb := getHashAndBuf()
|
||||
defer putHashAndBuf(hb)
|
||||
|
||||
sum := hashNew()
|
||||
for _, labelName := range labelNames {
|
||||
hb.b.WriteString(string(labelName))
|
||||
hb.b.WriteByte(SeparatorByte)
|
||||
hb.b.WriteString(string(m[labelName]))
|
||||
hb.b.WriteByte(SeparatorByte)
|
||||
hb.h.Write(hb.b.Bytes())
|
||||
hb.b.Reset()
|
||||
sum = hashAdd(sum, string(labelName))
|
||||
sum = hashAddByte(sum, SeparatorByte)
|
||||
sum = hashAdd(sum, string(m[labelName]))
|
||||
sum = hashAddByte(sum, SeparatorByte)
|
||||
}
|
||||
return hb.h.Sum64()
|
||||
return sum
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,6 +44,21 @@ func (m *Matcher) UnmarshalJSON(b []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Validate returns true iff all fields of the matcher have valid values.
|
||||
func (m *Matcher) Validate() error {
|
||||
if !m.Name.IsValid() {
|
||||
return fmt.Errorf("invalid name %q", m.Name)
|
||||
}
|
||||
if m.IsRegex {
|
||||
if _, err := regexp.Compile(m.Value); err != nil {
|
||||
return fmt.Errorf("invalid regular expression %q", m.Value)
|
||||
}
|
||||
} else if !LabelValue(m.Value).IsValid() || len(m.Value) == 0 {
|
||||
return fmt.Errorf("invalid value %q", m.Value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Silence defines the representation of a silence definiton
|
||||
// in the Prometheus eco-system.
|
||||
type Silence struct {
|
||||
|
|
@ -58,3 +73,34 @@ type Silence struct {
|
|||
CreatedBy string `json:"createdBy"`
|
||||
Comment string `json:"comment,omitempty"`
|
||||
}
|
||||
|
||||
// Validate returns true iff all fields of the silence have valid values.
|
||||
func (s *Silence) Validate() error {
|
||||
if len(s.Matchers) == 0 {
|
||||
return fmt.Errorf("at least one matcher required")
|
||||
}
|
||||
for _, m := range s.Matchers {
|
||||
if err := m.Validate(); err != nil {
|
||||
return fmt.Errorf("invalid matcher: %s", err)
|
||||
}
|
||||
}
|
||||
if s.StartsAt.IsZero() {
|
||||
return fmt.Errorf("start time missing")
|
||||
}
|
||||
if s.EndsAt.IsZero() {
|
||||
return fmt.Errorf("end time missing")
|
||||
}
|
||||
if s.EndsAt.Before(s.StartsAt) {
|
||||
return fmt.Errorf("start time must be before end time")
|
||||
}
|
||||
if s.CreatedBy == "" {
|
||||
return fmt.Errorf("creator information missing")
|
||||
}
|
||||
if s.Comment == "" {
|
||||
return fmt.Errorf("comment missing")
|
||||
}
|
||||
if s.CreatedAt.IsZero() {
|
||||
return fmt.Errorf("creation timestamp missing")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -163,51 +163,70 @@ func (t *Time) UnmarshalJSON(b []byte) error {
|
|||
// This type should not propagate beyond the scope of input/output processing.
|
||||
type Duration time.Duration
|
||||
|
||||
var durationRE = regexp.MustCompile("^([0-9]+)(y|w|d|h|m|s|ms)$")
|
||||
|
||||
// StringToDuration parses a string into a time.Duration, assuming that a year
|
||||
// a day always has 24h.
|
||||
// always has 365d, a week always has 7d, and a day always has 24h.
|
||||
func ParseDuration(durationStr string) (Duration, error) {
|
||||
matches := durationRE.FindStringSubmatch(durationStr)
|
||||
if len(matches) != 3 {
|
||||
return 0, fmt.Errorf("not a valid duration string: %q", durationStr)
|
||||
}
|
||||
durSeconds, _ := strconv.Atoi(matches[1])
|
||||
dur := time.Duration(durSeconds) * time.Second
|
||||
unit := matches[2]
|
||||
switch unit {
|
||||
var (
|
||||
n, _ = strconv.Atoi(matches[1])
|
||||
dur = time.Duration(n) * time.Millisecond
|
||||
)
|
||||
switch unit := matches[2]; unit {
|
||||
case "y":
|
||||
dur *= 1000 * 60 * 60 * 24 * 365
|
||||
case "w":
|
||||
dur *= 1000 * 60 * 60 * 24 * 7
|
||||
case "d":
|
||||
dur *= 60 * 60 * 24
|
||||
dur *= 1000 * 60 * 60 * 24
|
||||
case "h":
|
||||
dur *= 60 * 60
|
||||
dur *= 1000 * 60 * 60
|
||||
case "m":
|
||||
dur *= 60
|
||||
dur *= 1000 * 60
|
||||
case "s":
|
||||
dur *= 1
|
||||
dur *= 1000
|
||||
case "ms":
|
||||
// Value already correct
|
||||
default:
|
||||
return 0, fmt.Errorf("invalid time unit in duration string: %q", unit)
|
||||
}
|
||||
return Duration(dur), nil
|
||||
}
|
||||
|
||||
var durationRE = regexp.MustCompile("^([0-9]+)([ywdhms]+)$")
|
||||
|
||||
func (d Duration) String() string {
|
||||
seconds := int64(time.Duration(d) / time.Second)
|
||||
var (
|
||||
ms = int64(time.Duration(d) / time.Millisecond)
|
||||
unit = "ms"
|
||||
)
|
||||
factors := map[string]int64{
|
||||
"d": 60 * 60 * 24,
|
||||
"h": 60 * 60,
|
||||
"m": 60,
|
||||
"s": 1,
|
||||
"y": 1000 * 60 * 60 * 24 * 365,
|
||||
"w": 1000 * 60 * 60 * 24 * 7,
|
||||
"d": 1000 * 60 * 60 * 24,
|
||||
"h": 1000 * 60 * 60,
|
||||
"m": 1000 * 60,
|
||||
"s": 1000,
|
||||
"ms": 1,
|
||||
}
|
||||
unit := "s"
|
||||
|
||||
switch int64(0) {
|
||||
case seconds % factors["d"]:
|
||||
case ms % factors["y"]:
|
||||
unit = "y"
|
||||
case ms % factors["w"]:
|
||||
unit = "w"
|
||||
case ms % factors["d"]:
|
||||
unit = "d"
|
||||
case seconds % factors["h"]:
|
||||
case ms % factors["h"]:
|
||||
unit = "h"
|
||||
case seconds % factors["m"]:
|
||||
case ms % factors["m"]:
|
||||
unit = "m"
|
||||
case ms % factors["s"]:
|
||||
unit = "s"
|
||||
}
|
||||
return fmt.Sprintf("%v%v", seconds/factors[unit], unit)
|
||||
return fmt.Sprintf("%v%v", ms/factors[unit], unit)
|
||||
}
|
||||
|
||||
// MarshalYAML implements the yaml.Marshaler interface.
|
||||
|
|
|
|||
|
|
@ -16,11 +16,28 @@ package model
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
// ZeroSamplePair is the pseudo zero-value of SamplePair used to signal a
|
||||
// non-existing sample pair. It is a SamplePair with timestamp Earliest and
|
||||
// value 0.0. Note that the natural zero value of SamplePair has a timestamp
|
||||
// of 0, which is possible to appear in a real SamplePair and thus not
|
||||
// suitable to signal a non-existing SamplePair.
|
||||
ZeroSamplePair = SamplePair{Timestamp: Earliest}
|
||||
|
||||
// ZeroSample is the pseudo zero-value of Sample used to signal a
|
||||
// non-existing sample. It is a Sample with timestamp Earliest, value 0.0,
|
||||
// and metric nil. Note that the natural zero value of Sample has a timestamp
|
||||
// of 0, which is possible to appear in a real Sample and thus not suitable
|
||||
// to signal a non-existing Sample.
|
||||
ZeroSample = Sample{Timestamp: Earliest}
|
||||
)
|
||||
|
||||
// A SampleValue is a representation of a value for a given sample at a given
|
||||
// time.
|
||||
type SampleValue float64
|
||||
|
|
@ -43,8 +60,14 @@ func (v *SampleValue) UnmarshalJSON(b []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Equal returns true if the value of v and o is equal or if both are NaN. Note
|
||||
// that v==o is false if both are NaN. If you want the conventional float
|
||||
// behavior, use == to compare two SampleValues.
|
||||
func (v SampleValue) Equal(o SampleValue) bool {
|
||||
return v == o
|
||||
if v == o {
|
||||
return true
|
||||
}
|
||||
return math.IsNaN(float64(v)) && math.IsNaN(float64(o))
|
||||
}
|
||||
|
||||
func (v SampleValue) String() string {
|
||||
|
|
@ -77,9 +100,9 @@ func (s *SamplePair) UnmarshalJSON(b []byte) error {
|
|||
}
|
||||
|
||||
// Equal returns true if this SamplePair and o have equal Values and equal
|
||||
// Timestamps.
|
||||
// Timestamps. The sematics of Value equality is defined by SampleValue.Equal.
|
||||
func (s *SamplePair) Equal(o *SamplePair) bool {
|
||||
return s == o || (s.Value == o.Value && s.Timestamp.Equal(o.Timestamp))
|
||||
return s == o || (s.Value.Equal(o.Value) && s.Timestamp.Equal(o.Timestamp))
|
||||
}
|
||||
|
||||
func (s SamplePair) String() string {
|
||||
|
|
@ -93,7 +116,8 @@ type Sample struct {
|
|||
Timestamp Time `json:"timestamp"`
|
||||
}
|
||||
|
||||
// Equal compares first the metrics, then the timestamp, then the value.
|
||||
// Equal compares first the metrics, then the timestamp, then the value. The
|
||||
// sematics of value equality is defined by SampleValue.Equal.
|
||||
func (s *Sample) Equal(o *Sample) bool {
|
||||
if s == o {
|
||||
return true
|
||||
|
|
@ -105,7 +129,7 @@ func (s *Sample) Equal(o *Sample) bool {
|
|||
if !s.Timestamp.Equal(o.Timestamp) {
|
||||
return false
|
||||
}
|
||||
if s.Value != o.Value {
|
||||
if s.Value.Equal(o.Value) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,14 +27,7 @@ func NewFS(mountPoint string) (FS, error) {
|
|||
return FS(mountPoint), nil
|
||||
}
|
||||
|
||||
func (fs FS) stat(p string) (os.FileInfo, error) {
|
||||
return os.Stat(path.Join(string(fs), p))
|
||||
}
|
||||
|
||||
func (fs FS) open(p string) (*os.File, error) {
|
||||
return os.Open(path.Join(string(fs), p))
|
||||
}
|
||||
|
||||
func (fs FS) readlink(p string) (string, error) {
|
||||
return os.Readlink(path.Join(string(fs), p))
|
||||
// Path returns the path of the given subsystem relative to the procfs root.
|
||||
func (fs FS) Path(p ...string) string {
|
||||
return path.Join(append([]string{string(fs)}, p...)...)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
|
@ -58,7 +59,7 @@ func NewIPVSStats() (IPVSStats, error) {
|
|||
|
||||
// NewIPVSStats reads the IPVS statistics from the specified `proc` filesystem.
|
||||
func (fs FS) NewIPVSStats() (IPVSStats, error) {
|
||||
file, err := fs.open("net/ip_vs_stats")
|
||||
file, err := os.Open(fs.Path("net/ip_vs_stats"))
|
||||
if err != nil {
|
||||
return IPVSStats{}, err
|
||||
}
|
||||
|
|
@ -127,7 +128,7 @@ func NewIPVSBackendStatus() ([]IPVSBackendStatus, error) {
|
|||
|
||||
// NewIPVSBackendStatus reads and returns the status of all (virtual,real) server pairs from the specified `proc` filesystem.
|
||||
func (fs FS) NewIPVSBackendStatus() ([]IPVSBackendStatus, error) {
|
||||
file, err := fs.open("net/ip_vs")
|
||||
file, err := os.Open(fs.Path("net/ip_vs"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package procfs
|
|||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
|
@ -32,36 +31,22 @@ type MDStat struct {
|
|||
|
||||
// ParseMDStat parses an mdstat-file and returns a struct with the relevant infos.
|
||||
func (fs FS) ParseMDStat() (mdstates []MDStat, err error) {
|
||||
mdStatusFilePath := path.Join(string(fs), "mdstat")
|
||||
mdStatusFilePath := fs.Path("mdstat")
|
||||
content, err := ioutil.ReadFile(mdStatusFilePath)
|
||||
if err != nil {
|
||||
return []MDStat{}, fmt.Errorf("error parsing %s: %s", mdStatusFilePath, err)
|
||||
}
|
||||
|
||||
mdStatusFile := string(content)
|
||||
|
||||
lines := strings.Split(mdStatusFile, "\n")
|
||||
var currentMD string
|
||||
|
||||
// Each md has at least the deviceline, statusline and one empty line afterwards
|
||||
// so we will have probably something of the order len(lines)/3 devices
|
||||
// so we use that for preallocation.
|
||||
estimateMDs := len(lines) / 3
|
||||
mdStates := make([]MDStat, 0, estimateMDs)
|
||||
|
||||
mdStates := []MDStat{}
|
||||
lines := strings.Split(string(content), "\n")
|
||||
for i, l := range lines {
|
||||
if l == "" {
|
||||
// Skip entirely empty lines.
|
||||
continue
|
||||
}
|
||||
|
||||
if l[0] == ' ' {
|
||||
// Those lines are not the beginning of a md-section.
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(l, "Personalities") || strings.HasPrefix(l, "unused") {
|
||||
// We aren't interested in lines with general info.
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -69,32 +54,30 @@ func (fs FS) ParseMDStat() (mdstates []MDStat, err error) {
|
|||
if len(mainLine) < 3 {
|
||||
return mdStates, fmt.Errorf("error parsing mdline: %s", l)
|
||||
}
|
||||
currentMD = mainLine[0] // name of md-device
|
||||
activityState := mainLine[2] // activity status of said md-device
|
||||
mdName := mainLine[0]
|
||||
activityState := mainLine[2]
|
||||
|
||||
if len(lines) <= i+3 {
|
||||
return mdStates, fmt.Errorf("error parsing %s: entry for %s has fewer lines than expected", mdStatusFilePath, currentMD)
|
||||
return mdStates, fmt.Errorf(
|
||||
"error parsing %s: too few lines for md device %s",
|
||||
mdStatusFilePath,
|
||||
mdName,
|
||||
)
|
||||
}
|
||||
|
||||
active, total, size, err := evalStatusline(lines[i+1]) // parse statusline, always present
|
||||
active, total, size, err := evalStatusline(lines[i+1])
|
||||
if err != nil {
|
||||
return mdStates, fmt.Errorf("error parsing %s: %s", mdStatusFilePath, err)
|
||||
}
|
||||
|
||||
//
|
||||
// Now get the number of synced blocks.
|
||||
//
|
||||
|
||||
// Get the line number of the syncing-line.
|
||||
var j int
|
||||
if strings.Contains(lines[i+2], "bitmap") { // then skip the bitmap line
|
||||
// j is the line number of the syncing-line.
|
||||
j := i + 2
|
||||
if strings.Contains(lines[i+2], "bitmap") { // skip bitmap line
|
||||
j = i + 3
|
||||
} else {
|
||||
j = i + 2
|
||||
}
|
||||
|
||||
// If device is syncing at the moment, get the number of currently synced bytes,
|
||||
// otherwise that number equals the size of the device.
|
||||
// If device is syncing at the moment, get the number of currently
|
||||
// synced bytes, otherwise that number equals the size of the device.
|
||||
syncedBlocks := size
|
||||
if strings.Contains(lines[j], "recovery") || strings.Contains(lines[j], "resync") {
|
||||
syncedBlocks, err = evalBuildline(lines[j])
|
||||
|
|
@ -103,8 +86,14 @@ func (fs FS) ParseMDStat() (mdstates []MDStat, err error) {
|
|||
}
|
||||
}
|
||||
|
||||
mdStates = append(mdStates, MDStat{currentMD, activityState, active, total, size, syncedBlocks})
|
||||
|
||||
mdStates = append(mdStates, MDStat{
|
||||
Name: mdName,
|
||||
ActivityState: activityState,
|
||||
DisksActive: active,
|
||||
DisksTotal: total,
|
||||
BlocksTotal: size,
|
||||
BlocksSynced: syncedBlocks,
|
||||
})
|
||||
}
|
||||
|
||||
return mdStates, nil
|
||||
|
|
@ -112,47 +101,38 @@ func (fs FS) ParseMDStat() (mdstates []MDStat, err error) {
|
|||
|
||||
func evalStatusline(statusline string) (active, total, size int64, err error) {
|
||||
matches := statuslineRE.FindStringSubmatch(statusline)
|
||||
|
||||
// +1 to make it more obvious that the whole string containing the info is also returned as matches[0].
|
||||
if len(matches) != 3+1 {
|
||||
return 0, 0, 0, fmt.Errorf("unexpected number matches found in statusline: %s", statusline)
|
||||
if len(matches) != 4 {
|
||||
return 0, 0, 0, fmt.Errorf("unexpected statusline: %s", statusline)
|
||||
}
|
||||
|
||||
size, err = strconv.ParseInt(matches[1], 10, 64)
|
||||
if err != nil {
|
||||
return 0, 0, 0, fmt.Errorf("%s in statusline: %s", err, statusline)
|
||||
return 0, 0, 0, fmt.Errorf("unexpected statusline %s: %s", statusline, err)
|
||||
}
|
||||
|
||||
total, err = strconv.ParseInt(matches[2], 10, 64)
|
||||
if err != nil {
|
||||
return 0, 0, 0, fmt.Errorf("%s in statusline: %s", err, statusline)
|
||||
return 0, 0, 0, fmt.Errorf("unexpected statusline %s: %s", statusline, err)
|
||||
}
|
||||
|
||||
active, err = strconv.ParseInt(matches[3], 10, 64)
|
||||
if err != nil {
|
||||
return 0, 0, 0, fmt.Errorf("%s in statusline: %s", err, statusline)
|
||||
return 0, 0, 0, fmt.Errorf("unexpected statusline %s: %s", statusline, err)
|
||||
}
|
||||
|
||||
return active, total, size, nil
|
||||
}
|
||||
|
||||
// Gets the size that has already been synced out of the sync-line.
|
||||
func evalBuildline(buildline string) (int64, error) {
|
||||
func evalBuildline(buildline string) (syncedBlocks int64, err error) {
|
||||
matches := buildlineRE.FindStringSubmatch(buildline)
|
||||
|
||||
// +1 to make it more obvious that the whole string containing the info is also returned as matches[0].
|
||||
if len(matches) < 1+1 {
|
||||
return 0, fmt.Errorf("too few matches found in buildline: %s", buildline)
|
||||
if len(matches) != 2 {
|
||||
return 0, fmt.Errorf("unexpected buildline: %s", buildline)
|
||||
}
|
||||
|
||||
if len(matches) > 1+1 {
|
||||
return 0, fmt.Errorf("too many matches found in buildline: %s", buildline)
|
||||
}
|
||||
|
||||
syncedSize, err := strconv.ParseInt(matches[1], 10, 64)
|
||||
syncedBlocks, err = strconv.ParseInt(matches[1], 10, 64)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("%s in buildline: %s", err, buildline)
|
||||
}
|
||||
|
||||
return syncedSize, nil
|
||||
return syncedBlocks, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import (
|
|||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
|
@ -42,7 +41,7 @@ func NewProc(pid int) (Proc, error) {
|
|||
return fs.NewProc(pid)
|
||||
}
|
||||
|
||||
// AllProcs returns a list of all currently avaible processes under /proc.
|
||||
// AllProcs returns a list of all currently available processes under /proc.
|
||||
func AllProcs() (Procs, error) {
|
||||
fs, err := NewFS(DefaultMountPoint)
|
||||
if err != nil {
|
||||
|
|
@ -53,7 +52,7 @@ func AllProcs() (Procs, error) {
|
|||
|
||||
// Self returns a process for the current process.
|
||||
func (fs FS) Self() (Proc, error) {
|
||||
p, err := fs.readlink("self")
|
||||
p, err := os.Readlink(fs.Path("self"))
|
||||
if err != nil {
|
||||
return Proc{}, err
|
||||
}
|
||||
|
|
@ -66,15 +65,15 @@ func (fs FS) Self() (Proc, error) {
|
|||
|
||||
// NewProc returns a process for the given pid.
|
||||
func (fs FS) NewProc(pid int) (Proc, error) {
|
||||
if _, err := fs.stat(strconv.Itoa(pid)); err != nil {
|
||||
if _, err := os.Stat(fs.Path(strconv.Itoa(pid))); err != nil {
|
||||
return Proc{}, err
|
||||
}
|
||||
return Proc{PID: pid, fs: fs}, nil
|
||||
}
|
||||
|
||||
// AllProcs returns a list of all currently avaible processes.
|
||||
// AllProcs returns a list of all currently available processes.
|
||||
func (fs FS) AllProcs() (Procs, error) {
|
||||
d, err := fs.open("")
|
||||
d, err := os.Open(fs.Path())
|
||||
if err != nil {
|
||||
return Procs{}, err
|
||||
}
|
||||
|
|
@ -99,7 +98,7 @@ func (fs FS) AllProcs() (Procs, error) {
|
|||
|
||||
// CmdLine returns the command line of a process.
|
||||
func (p Proc) CmdLine() ([]string, error) {
|
||||
f, err := p.open("cmdline")
|
||||
f, err := os.Open(p.path("cmdline"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -117,10 +116,25 @@ func (p Proc) CmdLine() ([]string, error) {
|
|||
return strings.Split(string(data[:len(data)-1]), string(byte(0))), nil
|
||||
}
|
||||
|
||||
// Comm returns the command name of a process.
|
||||
func (p Proc) Comm() (string, error) {
|
||||
f, err := os.Open(p.path("comm"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
data, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return strings.TrimSpace(string(data)), nil
|
||||
}
|
||||
|
||||
// Executable returns the absolute path of the executable command of a process.
|
||||
func (p Proc) Executable() (string, error) {
|
||||
exe, err := p.readlink("exe")
|
||||
|
||||
exe, err := os.Readlink(p.path("exe"))
|
||||
if os.IsNotExist(err) {
|
||||
return "", nil
|
||||
}
|
||||
|
|
@ -158,7 +172,7 @@ func (p Proc) FileDescriptorTargets() ([]string, error) {
|
|||
targets := make([]string, len(names))
|
||||
|
||||
for i, name := range names {
|
||||
target, err := p.readlink("fd/" + name)
|
||||
target, err := os.Readlink(p.path("fd", name))
|
||||
if err == nil {
|
||||
targets[i] = target
|
||||
}
|
||||
|
|
@ -179,7 +193,7 @@ func (p Proc) FileDescriptorsLen() (int, error) {
|
|||
}
|
||||
|
||||
func (p Proc) fileDescriptors() ([]string, error) {
|
||||
d, err := p.open("fd")
|
||||
d, err := os.Open(p.path("fd"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -193,10 +207,6 @@ func (p Proc) fileDescriptors() ([]string, error) {
|
|||
return names, nil
|
||||
}
|
||||
|
||||
func (p Proc) open(pa string) (*os.File, error) {
|
||||
return p.fs.open(path.Join(strconv.Itoa(p.PID), pa))
|
||||
}
|
||||
|
||||
func (p Proc) readlink(pa string) (string, error) {
|
||||
return p.fs.readlink(path.Join(strconv.Itoa(p.PID), pa))
|
||||
func (p Proc) path(pa ...string) string {
|
||||
return p.fs.Path(append([]string{strconv.Itoa(p.PID)}, pa...)...)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package procfs
|
|||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
// ProcIO models the content of /proc/<pid>/io.
|
||||
|
|
@ -29,7 +30,7 @@ type ProcIO struct {
|
|||
func (p Proc) NewIO() (ProcIO, error) {
|
||||
pio := ProcIO{}
|
||||
|
||||
f, err := p.open("io")
|
||||
f, err := os.Open(p.path("io"))
|
||||
if err != nil {
|
||||
return pio, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,29 +3,56 @@ package procfs
|
|||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// ProcLimits represents the soft limits for each of the process's resource
|
||||
// limits.
|
||||
// limits. For more information see getrlimit(2):
|
||||
// http://man7.org/linux/man-pages/man2/getrlimit.2.html.
|
||||
type ProcLimits struct {
|
||||
CPUTime int
|
||||
FileSize int
|
||||
DataSize int
|
||||
StackSize int
|
||||
CoreFileSize int
|
||||
ResidentSet int
|
||||
Processes int
|
||||
OpenFiles int
|
||||
LockedMemory int
|
||||
AddressSpace int
|
||||
FileLocks int
|
||||
PendingSignals int
|
||||
MsqqueueSize int
|
||||
NicePriority int
|
||||
// CPU time limit in seconds.
|
||||
CPUTime int
|
||||
// Maximum size of files that the process may create.
|
||||
FileSize int
|
||||
// Maximum size of the process's data segment (initialized data,
|
||||
// uninitialized data, and heap).
|
||||
DataSize int
|
||||
// Maximum size of the process stack in bytes.
|
||||
StackSize int
|
||||
// Maximum size of a core file.
|
||||
CoreFileSize int
|
||||
// Limit of the process's resident set in pages.
|
||||
ResidentSet int
|
||||
// Maximum number of processes that can be created for the real user ID of
|
||||
// the calling process.
|
||||
Processes int
|
||||
// Value one greater than the maximum file descriptor number that can be
|
||||
// opened by this process.
|
||||
OpenFiles int
|
||||
// Maximum number of bytes of memory that may be locked into RAM.
|
||||
LockedMemory int
|
||||
// Maximum size of the process's virtual memory address space in bytes.
|
||||
AddressSpace int
|
||||
// Limit on the combined number of flock(2) locks and fcntl(2) leases that
|
||||
// this process may establish.
|
||||
FileLocks int
|
||||
// Limit of signals that may be queued for the real user ID of the calling
|
||||
// process.
|
||||
PendingSignals int
|
||||
// Limit on the number of bytes that can be allocated for POSIX message
|
||||
// queues for the real user ID of the calling process.
|
||||
MsqqueueSize int
|
||||
// Limit of the nice priority set using setpriority(2) or nice(2).
|
||||
NicePriority int
|
||||
// Limit of the real-time priority set using sched_setscheduler(2) or
|
||||
// sched_setparam(2).
|
||||
RealtimePriority int
|
||||
RealtimeTimeout int
|
||||
// Limit (in microseconds) on the amount of CPU time that a process
|
||||
// scheduled under a real-time scheduling policy may consume without making
|
||||
// a blocking system call.
|
||||
RealtimeTimeout int
|
||||
}
|
||||
|
||||
const (
|
||||
|
|
@ -39,7 +66,7 @@ var (
|
|||
|
||||
// NewLimits returns the current soft limits of the process.
|
||||
func (p Proc) NewLimits() (ProcLimits, error) {
|
||||
f, err := p.open("limits")
|
||||
f, err := os.Open(p.path("limits"))
|
||||
if err != nil {
|
||||
return ProcLimits{}, err
|
||||
}
|
||||
|
|
@ -60,7 +87,7 @@ func (p Proc) NewLimits() (ProcLimits, error) {
|
|||
case "Max cpu time":
|
||||
l.CPUTime, err = parseInt(fields[1])
|
||||
case "Max file size":
|
||||
l.FileLocks, err = parseInt(fields[1])
|
||||
l.FileSize, err = parseInt(fields[1])
|
||||
case "Max data size":
|
||||
l.DataSize, err = parseInt(fields[1])
|
||||
case "Max stack size":
|
||||
|
|
@ -90,7 +117,6 @@ func (p Proc) NewLimits() (ProcLimits, error) {
|
|||
case "Max realtime timeout":
|
||||
l.RealtimeTimeout, err = parseInt(fields[1])
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return ProcLimits{}, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,15 +7,15 @@ import (
|
|||
"os"
|
||||
)
|
||||
|
||||
// Originally, this USER_HZ value was dynamically retrieved via a sysconf call which
|
||||
// required cgo. However, that caused a lot of problems regarding
|
||||
// Originally, this USER_HZ value was dynamically retrieved via a sysconf call
|
||||
// which required cgo. However, that caused a lot of problems regarding
|
||||
// cross-compilation. Alternatives such as running a binary to determine the
|
||||
// value, or trying to derive it in some other way were all problematic.
|
||||
// After much research it was determined that USER_HZ is actually hardcoded to
|
||||
// 100 on all Go-supported platforms as of the time of this writing. This is
|
||||
// why we decided to hardcode it here as well. It is not impossible that there
|
||||
// could be systems with exceptions, but they should be very exotic edge cases,
|
||||
// and in that case, the worst outcome will be two misreported metrics.
|
||||
// value, or trying to derive it in some other way were all problematic. After
|
||||
// much research it was determined that USER_HZ is actually hardcoded to 100 on
|
||||
// all Go-supported platforms as of the time of this writing. This is why we
|
||||
// decided to hardcode it here as well. It is not impossible that there could
|
||||
// be systems with exceptions, but they should be very exotic edge cases, and
|
||||
// in that case, the worst outcome will be two misreported metrics.
|
||||
//
|
||||
// See also the following discussions:
|
||||
//
|
||||
|
|
@ -91,7 +91,7 @@ type ProcStat struct {
|
|||
|
||||
// NewStat returns the current status information of the process.
|
||||
func (p Proc) NewStat() (ProcStat, error) {
|
||||
f, err := p.open("stat")
|
||||
f, err := os.Open(p.path("stat"))
|
||||
if err != nil {
|
||||
return ProcStat{}, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package procfs
|
|||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
|
@ -25,7 +26,7 @@ func NewStat() (Stat, error) {
|
|||
|
||||
// NewStat returns an information about current kernel/system statistics.
|
||||
func (fs FS) NewStat() (Stat, error) {
|
||||
f, err := fs.open("stat")
|
||||
f, err := os.Open(fs.Path("stat"))
|
||||
if err != nil {
|
||||
return Stat{}, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,10 +64,11 @@ Rich Feature Set includes:
|
|||
- Never silently skip data when decoding.
|
||||
User decides whether to return an error or silently skip data when keys or indexes
|
||||
in the data stream do not map to fields in the struct.
|
||||
- Detect and error when encoding a cyclic reference (instead of stack overflow shutdown)
|
||||
- Encode/Decode from/to chan types (for iterative streaming support)
|
||||
- Drop-in replacement for encoding/json. `json:` key in struct tag supported.
|
||||
- Provides a RPC Server and Client Codec for net/rpc communication protocol.
|
||||
- Handle unique idiosynchracies of codecs e.g.
|
||||
- Handle unique idiosyncrasies of codecs e.g.
|
||||
- For messagepack, configure how ambiguities in handling raw bytes are resolved
|
||||
- For messagepack, provide rpc server/client codec to support
|
||||
msgpack-rpc protocol defined at:
|
||||
|
|
@ -171,6 +172,8 @@ package codec
|
|||
|
||||
// TODO:
|
||||
//
|
||||
// - optimization for codecgen:
|
||||
// if len of entity is <= 3 words, then support a value receiver for encode.
|
||||
// - (En|De)coder should store an error when it occurs.
|
||||
// Until reset, subsequent calls return that error that was stored.
|
||||
// This means that free panics must go away.
|
||||
|
|
@ -178,16 +181,19 @@ package codec
|
|||
// - Decoding using a chan is good, but incurs concurrency costs.
|
||||
// This is because there's no fast way to use a channel without it
|
||||
// having to switch goroutines constantly.
|
||||
// Callback pattern is still the best. Maybe cnsider supporting something like:
|
||||
// Callback pattern is still the best. Maybe consider supporting something like:
|
||||
// type X struct {
|
||||
// Name string
|
||||
// Ys []Y
|
||||
// Ys chan <- Y
|
||||
// Ys func(interface{}) -> call this interface for each entry in there.
|
||||
// Ys func(Y) -> call this function for each entry
|
||||
// }
|
||||
// - Consider adding a isZeroer interface { isZero() bool }
|
||||
// It is used within isEmpty, for omitEmpty support.
|
||||
// - Consider making Handle used AS-IS within the encoding/decoding session.
|
||||
// This means that we don't cache Handle information within the (En|De)coder,
|
||||
// except we really need it at Reset(...)
|
||||
// - Handle recursive types during encoding/decoding?
|
||||
// - Consider adding math/big support
|
||||
// - Consider reducing the size of the generated functions:
|
||||
// Maybe use one loop, and put the conditionals in the loop.
|
||||
// for ... { if cLen > 0 { if j == cLen { break } } else if dd.CheckBreak() { break } }
|
||||
|
|
|
|||
|
|
@ -348,6 +348,13 @@ func (d *bincDecDriver) readNextBd() {
|
|||
d.bdRead = true
|
||||
}
|
||||
|
||||
func (d *bincDecDriver) uncacheRead() {
|
||||
if d.bdRead {
|
||||
d.r.unreadn1()
|
||||
d.bdRead = false
|
||||
}
|
||||
}
|
||||
|
||||
func (d *bincDecDriver) ContainerType() (vt valueType) {
|
||||
if d.vd == bincVdSpecial && d.vs == bincSpNil {
|
||||
return valueTypeNil
|
||||
|
|
@ -705,7 +712,7 @@ func (d *bincDecDriver) decStringAndBytes(bs []byte, withString, zerocopy bool)
|
|||
}
|
||||
|
||||
func (d *bincDecDriver) DecodeString() (s string) {
|
||||
// DecodeBytes does not accomodate symbols, whose impl stores string version in map.
|
||||
// DecodeBytes does not accommodate symbols, whose impl stores string version in map.
|
||||
// Use decStringAndBytes directly.
|
||||
// return string(d.DecodeBytes(d.b[:], true, true))
|
||||
_, s = d.decStringAndBytes(d.b[:], true, true)
|
||||
|
|
@ -908,10 +915,14 @@ func (h *BincHandle) newDecDriver(d *Decoder) decDriver {
|
|||
|
||||
func (e *bincEncDriver) reset() {
|
||||
e.w = e.e.w
|
||||
e.s = 0
|
||||
e.m = nil
|
||||
}
|
||||
|
||||
func (d *bincDecDriver) reset() {
|
||||
d.r = d.d.r
|
||||
d.s = nil
|
||||
d.bd, d.bdRead, d.vd, d.vs = 0, false, 0, 0
|
||||
}
|
||||
|
||||
var _ decDriver = (*bincDecDriver)(nil)
|
||||
|
|
|
|||
|
|
@ -188,6 +188,13 @@ func (d *cborDecDriver) readNextBd() {
|
|||
d.bdRead = true
|
||||
}
|
||||
|
||||
func (d *cborDecDriver) uncacheRead() {
|
||||
if d.bdRead {
|
||||
d.r.unreadn1()
|
||||
d.bdRead = false
|
||||
}
|
||||
}
|
||||
|
||||
func (d *cborDecDriver) ContainerType() (vt valueType) {
|
||||
if d.bd == cborBdNil {
|
||||
return valueTypeNil
|
||||
|
|
@ -508,7 +515,7 @@ func (d *cborDecDriver) DecodeNaked() {
|
|||
n.v = valueTypeExt
|
||||
n.u = d.decUint()
|
||||
n.l = nil
|
||||
d.bdRead = false
|
||||
// d.bdRead = false
|
||||
// d.d.decode(&re.Value) // handled by decode itself.
|
||||
// decodeFurther = true
|
||||
default:
|
||||
|
|
@ -578,6 +585,7 @@ func (e *cborEncDriver) reset() {
|
|||
|
||||
func (d *cborDecDriver) reset() {
|
||||
d.r = d.d.r
|
||||
d.bd, d.bdRead = 0, false
|
||||
}
|
||||
|
||||
var _ decDriver = (*cborDecDriver)(nil)
|
||||
|
|
|
|||
|
|
@ -91,10 +91,12 @@ type decDriver interface {
|
|||
uncacheRead()
|
||||
}
|
||||
|
||||
type decNoSeparator struct{}
|
||||
type decNoSeparator struct {
|
||||
}
|
||||
|
||||
func (_ decNoSeparator) ReadEnd() {}
|
||||
func (_ decNoSeparator) uncacheRead() {}
|
||||
func (_ decNoSeparator) ReadEnd() {}
|
||||
|
||||
// func (_ decNoSeparator) uncacheRead() {}
|
||||
|
||||
type DecodeOptions struct {
|
||||
// MapType specifies type to use during schema-less decoding of a map in the stream.
|
||||
|
|
@ -161,6 +163,15 @@ type DecodeOptions struct {
|
|||
// Note: Handles will be smart when using the intern functionality.
|
||||
// So everything will not be interned.
|
||||
InternString bool
|
||||
|
||||
// PreferArrayOverSlice controls whether to decode to an array or a slice.
|
||||
//
|
||||
// This only impacts decoding into a nil interface{}.
|
||||
// Consequently, it has no effect on codecgen.
|
||||
//
|
||||
// *Note*: This only applies if using go1.5 and above,
|
||||
// as it requires reflect.ArrayOf support which was absent before go1.5.
|
||||
PreferArrayOverSlice bool
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
|
|
@ -433,6 +444,10 @@ func (f *decFnInfo) rawExt(rv reflect.Value) {
|
|||
f.d.d.DecodeExt(rv.Addr().Interface(), 0, nil)
|
||||
}
|
||||
|
||||
func (f *decFnInfo) raw(rv reflect.Value) {
|
||||
rv.SetBytes(f.d.raw())
|
||||
}
|
||||
|
||||
func (f *decFnInfo) ext(rv reflect.Value) {
|
||||
f.d.d.DecodeExt(rv.Addr().Interface(), f.xfTag, f.xfFn)
|
||||
}
|
||||
|
|
@ -583,14 +598,16 @@ func (f *decFnInfo) kInterfaceNaked() (rvn reflect.Value) {
|
|||
if d.mtid == 0 || d.mtid == mapIntfIntfTypId {
|
||||
l := len(n.ms)
|
||||
n.ms = append(n.ms, nil)
|
||||
d.decode(&n.ms[l])
|
||||
rvn = reflect.ValueOf(&n.ms[l]).Elem()
|
||||
var v2 interface{} = &n.ms[l]
|
||||
d.decode(v2)
|
||||
rvn = reflect.ValueOf(v2).Elem()
|
||||
n.ms = n.ms[:l]
|
||||
} else if d.mtid == mapStrIntfTypId { // for json performance
|
||||
l := len(n.ns)
|
||||
n.ns = append(n.ns, nil)
|
||||
d.decode(&n.ns[l])
|
||||
rvn = reflect.ValueOf(&n.ns[l]).Elem()
|
||||
var v2 interface{} = &n.ns[l]
|
||||
d.decode(v2)
|
||||
rvn = reflect.ValueOf(v2).Elem()
|
||||
n.ns = n.ns[:l]
|
||||
} else {
|
||||
rvn = reflect.New(d.h.MapType).Elem()
|
||||
|
|
@ -601,9 +618,13 @@ func (f *decFnInfo) kInterfaceNaked() (rvn reflect.Value) {
|
|||
if d.stid == 0 || d.stid == intfSliceTypId {
|
||||
l := len(n.ss)
|
||||
n.ss = append(n.ss, nil)
|
||||
d.decode(&n.ss[l])
|
||||
rvn = reflect.ValueOf(&n.ss[l]).Elem()
|
||||
var v2 interface{} = &n.ss[l]
|
||||
d.decode(v2)
|
||||
n.ss = n.ss[:l]
|
||||
rvn = reflect.ValueOf(v2).Elem()
|
||||
if reflectArrayOfSupported && d.stid == 0 && d.h.PreferArrayOverSlice {
|
||||
rvn = reflectArrayOf(rvn)
|
||||
}
|
||||
} else {
|
||||
rvn = reflect.New(d.h.SliceType).Elem()
|
||||
d.decodeValue(rvn, nil)
|
||||
|
|
@ -615,9 +636,9 @@ func (f *decFnInfo) kInterfaceNaked() (rvn reflect.Value) {
|
|||
l := len(n.is)
|
||||
n.is = append(n.is, nil)
|
||||
v2 := &n.is[l]
|
||||
n.is = n.is[:l]
|
||||
d.decode(v2)
|
||||
v = *v2
|
||||
n.is = n.is[:l]
|
||||
}
|
||||
bfn := d.h.getExtForTag(tag)
|
||||
if bfn == nil {
|
||||
|
|
@ -1166,7 +1187,7 @@ type decRtidFn struct {
|
|||
// primitives are being decoded.
|
||||
//
|
||||
// maps and arrays are not handled by this mechanism.
|
||||
// However, RawExt is, and we accomodate for extensions that decode
|
||||
// However, RawExt is, and we accommodate for extensions that decode
|
||||
// RawExt from DecodeNaked, but need to decode the value subsequently.
|
||||
// kInterfaceNaked and swallow, which call DecodeNaked, handle this caveat.
|
||||
//
|
||||
|
|
@ -1453,8 +1474,8 @@ func (d *Decoder) swallow() {
|
|||
l := len(n.is)
|
||||
n.is = append(n.is, nil)
|
||||
v2 := &n.is[l]
|
||||
n.is = n.is[:l]
|
||||
d.decode(v2)
|
||||
n.is = n.is[:l]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1504,6 +1525,8 @@ func (d *Decoder) decode(iv interface{}) {
|
|||
*v = 0
|
||||
case *[]uint8:
|
||||
*v = nil
|
||||
case *Raw:
|
||||
*v = nil
|
||||
case reflect.Value:
|
||||
if v.Kind() != reflect.Ptr || v.IsNil() {
|
||||
d.errNotValidPtrValue(v)
|
||||
|
|
@ -1543,7 +1566,6 @@ func (d *Decoder) decode(iv interface{}) {
|
|||
d.decodeValueNotNil(v.Elem(), nil)
|
||||
|
||||
case *string:
|
||||
|
||||
*v = d.d.DecodeString()
|
||||
case *bool:
|
||||
*v = d.d.DecodeBool()
|
||||
|
|
@ -1574,6 +1596,9 @@ func (d *Decoder) decode(iv interface{}) {
|
|||
case *[]uint8:
|
||||
*v = d.d.DecodeBytes(*v, false, false)
|
||||
|
||||
case *Raw:
|
||||
*v = d.raw()
|
||||
|
||||
case *interface{}:
|
||||
d.decodeValueNotNil(reflect.ValueOf(iv).Elem(), nil)
|
||||
|
||||
|
|
@ -1695,6 +1720,8 @@ func (d *Decoder) getDecFn(rt reflect.Type, checkFastpath, checkCodecSelfer bool
|
|||
fn.f = (*decFnInfo).selferUnmarshal
|
||||
} else if rtid == rawExtTypId {
|
||||
fn.f = (*decFnInfo).rawExt
|
||||
} else if rtid == rawTypId {
|
||||
fn.f = (*decFnInfo).raw
|
||||
} else if d.d.IsBuiltinType(rtid) {
|
||||
fn.f = (*decFnInfo).builtin
|
||||
} else if xfFn := d.h.getExt(rtid); xfFn != nil {
|
||||
|
|
@ -1793,12 +1820,13 @@ func (d *Decoder) getDecFn(rt reflect.Type, checkFastpath, checkCodecSelfer bool
|
|||
}
|
||||
|
||||
func (d *Decoder) structFieldNotFound(index int, rvkencname string) {
|
||||
// NOTE: rvkencname may be a stringView, so don't pass it to another function.
|
||||
if d.h.ErrorIfNoField {
|
||||
if index >= 0 {
|
||||
d.errorf("no matching struct field found when decoding stream array at index %v", index)
|
||||
return
|
||||
} else if rvkencname != "" {
|
||||
d.errorf("no matching struct field found when decoding stream map with key %s", rvkencname)
|
||||
d.errorf("no matching struct field found when decoding stream map with key " + rvkencname)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
@ -1862,6 +1890,7 @@ func (d *Decoder) intern(s string) {
|
|||
}
|
||||
}
|
||||
|
||||
// nextValueBytes returns the next value in the stream as a set of bytes.
|
||||
func (d *Decoder) nextValueBytes() []byte {
|
||||
d.d.uncacheRead()
|
||||
d.r.track()
|
||||
|
|
@ -1869,6 +1898,15 @@ func (d *Decoder) nextValueBytes() []byte {
|
|||
return d.r.stopTrack()
|
||||
}
|
||||
|
||||
func (d *Decoder) raw() []byte {
|
||||
// ensure that this is not a view into the bytes
|
||||
// i.e. make new copy always.
|
||||
bs := d.nextValueBytes()
|
||||
bs2 := make([]byte, len(bs))
|
||||
copy(bs2, bs)
|
||||
return bs2
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
|
||||
// decSliceHelper assists when decoding into a slice, from a map or an array in the stream.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
|
||||
// Use of this source code is governed by a MIT license found in the LICENSE file.
|
||||
|
||||
// +build go1.5
|
||||
|
||||
package codec
|
||||
|
||||
import "reflect"
|
||||
|
||||
const reflectArrayOfSupported = true
|
||||
|
||||
func reflectArrayOf(rvn reflect.Value) (rvn2 reflect.Value) {
|
||||
rvn2 = reflect.New(reflect.ArrayOf(rvn.Len(), intfTyp)).Elem()
|
||||
reflect.Copy(rvn2, rvn)
|
||||
return
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
|
||||
// Use of this source code is governed by a MIT license found in the LICENSE file.
|
||||
|
||||
// +build !go1.5
|
||||
|
||||
package codec
|
||||
|
||||
import "reflect"
|
||||
|
||||
const reflectArrayOfSupported = false
|
||||
|
||||
func reflectArrayOf(rvn reflect.Value) (rvn2 reflect.Value) {
|
||||
panic("reflect.ArrayOf unsupported")
|
||||
}
|
||||
|
|
@ -110,6 +110,28 @@ type EncodeOptions struct {
|
|||
//
|
||||
Canonical bool
|
||||
|
||||
// CheckCircularRef controls whether we check for circular references
|
||||
// and error fast during an encode.
|
||||
//
|
||||
// If enabled, an error is received if a pointer to a struct
|
||||
// references itself either directly or through one of its fields (iteratively).
|
||||
//
|
||||
// This is opt-in, as there may be a performance hit to checking circular references.
|
||||
CheckCircularRef bool
|
||||
|
||||
// RecursiveEmptyCheck controls whether we descend into interfaces, structs and pointers
|
||||
// when checking if a value is empty.
|
||||
//
|
||||
// Note that this may make OmitEmpty more expensive, as it incurs a lot more reflect calls.
|
||||
RecursiveEmptyCheck bool
|
||||
|
||||
// Raw controls whether we encode Raw values.
|
||||
// This is a "dangerous" option and must be explicitly set.
|
||||
// If set, we blindly encode Raw values as-is, without checking
|
||||
// if they are a correct representation of a value in that format.
|
||||
// If unset, we error out.
|
||||
Raw bool
|
||||
|
||||
// AsSymbols defines what should be encoded as symbols.
|
||||
//
|
||||
// Encoding as symbols can reduce the encoded size significantly.
|
||||
|
|
@ -132,13 +154,16 @@ type simpleIoEncWriterWriter struct {
|
|||
w io.Writer
|
||||
bw io.ByteWriter
|
||||
sw ioEncStringWriter
|
||||
bs [1]byte
|
||||
}
|
||||
|
||||
func (o *simpleIoEncWriterWriter) WriteByte(c byte) (err error) {
|
||||
if o.bw != nil {
|
||||
return o.bw.WriteByte(c)
|
||||
}
|
||||
_, err = o.w.Write([]byte{c})
|
||||
// _, err = o.w.Write([]byte{c})
|
||||
o.bs[0] = c
|
||||
_, err = o.w.Write(o.bs[:])
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -210,45 +235,57 @@ type bytesEncWriter struct {
|
|||
}
|
||||
|
||||
func (z *bytesEncWriter) writeb(s []byte) {
|
||||
if len(s) > 0 {
|
||||
c := z.grow(len(s))
|
||||
copy(z.b[c:], s)
|
||||
if len(s) == 0 {
|
||||
return
|
||||
}
|
||||
oc, a := z.growNoAlloc(len(s))
|
||||
if a {
|
||||
z.growAlloc(len(s), oc)
|
||||
}
|
||||
copy(z.b[oc:], s)
|
||||
}
|
||||
|
||||
func (z *bytesEncWriter) writestr(s string) {
|
||||
if len(s) > 0 {
|
||||
c := z.grow(len(s))
|
||||
copy(z.b[c:], s)
|
||||
if len(s) == 0 {
|
||||
return
|
||||
}
|
||||
oc, a := z.growNoAlloc(len(s))
|
||||
if a {
|
||||
z.growAlloc(len(s), oc)
|
||||
}
|
||||
copy(z.b[oc:], s)
|
||||
}
|
||||
|
||||
func (z *bytesEncWriter) writen1(b1 byte) {
|
||||
c := z.grow(1)
|
||||
z.b[c] = b1
|
||||
oc, a := z.growNoAlloc(1)
|
||||
if a {
|
||||
z.growAlloc(1, oc)
|
||||
}
|
||||
z.b[oc] = b1
|
||||
}
|
||||
|
||||
func (z *bytesEncWriter) writen2(b1 byte, b2 byte) {
|
||||
c := z.grow(2)
|
||||
z.b[c] = b1
|
||||
z.b[c+1] = b2
|
||||
oc, a := z.growNoAlloc(2)
|
||||
if a {
|
||||
z.growAlloc(2, oc)
|
||||
}
|
||||
z.b[oc+1] = b2
|
||||
z.b[oc] = b1
|
||||
}
|
||||
|
||||
func (z *bytesEncWriter) atEndOfEncode() {
|
||||
*(z.out) = z.b[:z.c]
|
||||
}
|
||||
|
||||
func (z *bytesEncWriter) grow(n int) (oldcursor int) {
|
||||
// have a growNoalloc(n int), which can be inlined.
|
||||
// if allocation is needed, then call growAlloc(n int)
|
||||
|
||||
func (z *bytesEncWriter) growNoAlloc(n int) (oldcursor int, allocNeeded bool) {
|
||||
oldcursor = z.c
|
||||
z.c = oldcursor + n
|
||||
z.c = z.c + n
|
||||
if z.c > len(z.b) {
|
||||
if z.c > cap(z.b) {
|
||||
// appendslice logic (if cap < 1024, *2, else *1.25): more expensive. many copy calls.
|
||||
// bytes.Buffer model (2*cap + n): much better
|
||||
// bs := make([]byte, 2*cap(z.b)+n)
|
||||
bs := make([]byte, growCap(cap(z.b), 1, n))
|
||||
copy(bs, z.b[:oldcursor])
|
||||
z.b = bs
|
||||
allocNeeded = true
|
||||
} else {
|
||||
z.b = z.b[:cap(z.b)]
|
||||
}
|
||||
|
|
@ -256,6 +293,15 @@ func (z *bytesEncWriter) grow(n int) (oldcursor int) {
|
|||
return
|
||||
}
|
||||
|
||||
func (z *bytesEncWriter) growAlloc(n int, oldcursor int) {
|
||||
// appendslice logic (if cap < 1024, *2, else *1.25): more expensive. many copy calls.
|
||||
// bytes.Buffer model (2*cap + n): much better
|
||||
// bs := make([]byte, 2*cap(z.b)+n)
|
||||
bs := make([]byte, growCap(cap(z.b), 1, n))
|
||||
copy(bs, z.b[:oldcursor])
|
||||
z.b = bs
|
||||
}
|
||||
|
||||
// ---------------------------------------------
|
||||
|
||||
type encFnInfo struct {
|
||||
|
|
@ -270,6 +316,10 @@ func (f *encFnInfo) builtin(rv reflect.Value) {
|
|||
f.e.e.EncodeBuiltin(f.ti.rtid, rv.Interface())
|
||||
}
|
||||
|
||||
func (f *encFnInfo) raw(rv reflect.Value) {
|
||||
f.e.raw(rv.Interface().(Raw))
|
||||
}
|
||||
|
||||
func (f *encFnInfo) rawExt(rv reflect.Value) {
|
||||
// rev := rv.Interface().(RawExt)
|
||||
// f.e.e.EncodeRawExt(&rev, f.e)
|
||||
|
|
@ -296,7 +346,7 @@ func (f *encFnInfo) getValueForMarshalInterface(rv reflect.Value, indir int8) (v
|
|||
v = rv.Interface()
|
||||
} else if indir == -1 {
|
||||
// If a non-pointer was passed to Encode(), then that value is not addressable.
|
||||
// Take addr if addresable, else copy value to an addressable value.
|
||||
// Take addr if addressable, else copy value to an addressable value.
|
||||
if rv.CanAddr() {
|
||||
v = rv.Addr().Interface()
|
||||
} else {
|
||||
|
|
@ -464,7 +514,7 @@ func (f *encFnInfo) kSlice(rv reflect.Value) {
|
|||
for j := 0; j < l; j++ {
|
||||
if cr != nil {
|
||||
if ti.mbs {
|
||||
if l%2 == 0 {
|
||||
if j%2 == 0 {
|
||||
cr.sendContainerState(containerMapKey)
|
||||
} else {
|
||||
cr.sendContainerState(containerMapValue)
|
||||
|
|
@ -503,7 +553,7 @@ func (f *encFnInfo) kStruct(rv reflect.Value) {
|
|||
newlen := len(fti.sfi)
|
||||
|
||||
// Use sync.Pool to reduce allocating slices unnecessarily.
|
||||
// The cost of the occasional locking is less than the cost of new allocation.
|
||||
// The cost of sync.Pool is less than the cost of new allocation.
|
||||
pool, poolv, fkvs := encStructPoolGet(newlen)
|
||||
|
||||
// if toMap, use the sorted array. If toArray, use unsorted array (to match sequence in struct)
|
||||
|
|
@ -512,25 +562,20 @@ func (f *encFnInfo) kStruct(rv reflect.Value) {
|
|||
}
|
||||
newlen = 0
|
||||
var kv stringRv
|
||||
recur := e.h.RecursiveEmptyCheck
|
||||
for _, si := range tisfi {
|
||||
kv.r = si.field(rv, false)
|
||||
// if si.i != -1 {
|
||||
// rvals[newlen] = rv.Field(int(si.i))
|
||||
// } else {
|
||||
// rvals[newlen] = rv.FieldByIndex(si.is)
|
||||
// }
|
||||
if toMap {
|
||||
if si.omitEmpty && isEmptyValue(kv.r) {
|
||||
if si.omitEmpty && isEmptyValue(kv.r, recur, recur) {
|
||||
continue
|
||||
}
|
||||
kv.v = si.encName
|
||||
} else {
|
||||
// use the zero value.
|
||||
// if a reference or struct, set to nil (so you do not output too much)
|
||||
if si.omitEmpty && isEmptyValue(kv.r) {
|
||||
if si.omitEmpty && isEmptyValue(kv.r, recur, recur) {
|
||||
switch kv.r.Kind() {
|
||||
case reflect.Struct, reflect.Interface, reflect.Ptr, reflect.Array,
|
||||
reflect.Map, reflect.Slice:
|
||||
case reflect.Struct, reflect.Interface, reflect.Ptr, reflect.Array, reflect.Map, reflect.Slice:
|
||||
kv.r = reflect.Value{} //encode as nil
|
||||
}
|
||||
}
|
||||
|
|
@ -541,7 +586,7 @@ func (f *encFnInfo) kStruct(rv reflect.Value) {
|
|||
|
||||
// debugf(">>>> kStruct: newlen: %v", newlen)
|
||||
// sep := !e.be
|
||||
ee := e.e //don't dereference everytime
|
||||
ee := e.e //don't dereference every time
|
||||
|
||||
if toMap {
|
||||
ee.EncodeMapStart(newlen)
|
||||
|
|
@ -596,13 +641,15 @@ func (f *encFnInfo) kStruct(rv reflect.Value) {
|
|||
// f.e.encodeValue(rv.Elem())
|
||||
// }
|
||||
|
||||
func (f *encFnInfo) kInterface(rv reflect.Value) {
|
||||
if rv.IsNil() {
|
||||
f.e.e.EncodeNil()
|
||||
return
|
||||
}
|
||||
f.e.encodeValue(rv.Elem(), nil)
|
||||
}
|
||||
// func (f *encFnInfo) kInterface(rv reflect.Value) {
|
||||
// println("kInterface called")
|
||||
// debug.PrintStack()
|
||||
// if rv.IsNil() {
|
||||
// f.e.e.EncodeNil()
|
||||
// return
|
||||
// }
|
||||
// f.e.encodeValue(rv.Elem(), nil)
|
||||
// }
|
||||
|
||||
func (f *encFnInfo) kMap(rv reflect.Value) {
|
||||
ee := f.e.e
|
||||
|
|
@ -877,6 +924,7 @@ type Encoder struct {
|
|||
// as the handler MAY need to do some coordination.
|
||||
w encWriter
|
||||
s []encRtidFn
|
||||
ci set
|
||||
be bool // is binary encoding
|
||||
js bool // is json handle
|
||||
|
||||
|
|
@ -925,7 +973,7 @@ func newEncoder(h Handle) *Encoder {
|
|||
|
||||
// Reset the Encoder with a new output stream.
|
||||
//
|
||||
// This accomodates using the state of the Encoder,
|
||||
// This accommodates using the state of the Encoder,
|
||||
// where it has "cached" information about sub-engines.
|
||||
func (e *Encoder) Reset(w io.Writer) {
|
||||
ww, ok := w.(ioEncWriterWriter)
|
||||
|
|
@ -1032,20 +1080,6 @@ func (e *Encoder) MustEncode(v interface{}) {
|
|||
e.w.atEndOfEncode()
|
||||
}
|
||||
|
||||
// comment out these (Must)Write methods. They were only put there to support cbor.
|
||||
// However, users already have access to the streams, and can write directly.
|
||||
//
|
||||
// // Write allows users write to the Encoder stream directly.
|
||||
// func (e *Encoder) Write(bs []byte) (err error) {
|
||||
// defer panicToErr(&err)
|
||||
// e.w.writeb(bs)
|
||||
// return
|
||||
// }
|
||||
// // MustWrite is like write, but panics if unable to Write.
|
||||
// func (e *Encoder) MustWrite(bs []byte) {
|
||||
// e.w.writeb(bs)
|
||||
// }
|
||||
|
||||
func (e *Encoder) encode(iv interface{}) {
|
||||
// if ics, ok := iv.(Selfer); ok {
|
||||
// ics.CodecEncodeSelf(e)
|
||||
|
|
@ -1057,7 +1091,8 @@ func (e *Encoder) encode(iv interface{}) {
|
|||
e.e.EncodeNil()
|
||||
case Selfer:
|
||||
v.CodecEncodeSelf(e)
|
||||
|
||||
case Raw:
|
||||
e.raw(v)
|
||||
case reflect.Value:
|
||||
e.encodeValue(v, nil)
|
||||
|
||||
|
|
@ -1133,20 +1168,23 @@ func (e *Encoder) encode(iv interface{}) {
|
|||
}
|
||||
}
|
||||
|
||||
func (e *Encoder) encodeI(iv interface{}, checkFastpath, checkCodecSelfer bool) {
|
||||
if rv, proceed := e.preEncodeValue(reflect.ValueOf(iv)); proceed {
|
||||
rt := rv.Type()
|
||||
rtid := reflect.ValueOf(rt).Pointer()
|
||||
fn := e.getEncFn(rtid, rt, checkFastpath, checkCodecSelfer)
|
||||
fn.f(&fn.i, rv)
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Encoder) preEncodeValue(rv reflect.Value) (rv2 reflect.Value, proceed bool) {
|
||||
func (e *Encoder) preEncodeValue(rv reflect.Value) (rv2 reflect.Value, sptr uintptr, proceed bool) {
|
||||
// use a goto statement instead of a recursive function for ptr/interface.
|
||||
TOP:
|
||||
switch rv.Kind() {
|
||||
case reflect.Ptr, reflect.Interface:
|
||||
case reflect.Ptr:
|
||||
if rv.IsNil() {
|
||||
e.e.EncodeNil()
|
||||
return
|
||||
}
|
||||
rv = rv.Elem()
|
||||
if e.h.CheckCircularRef && rv.Kind() == reflect.Struct {
|
||||
// TODO: Movable pointers will be an issue here. Future problem.
|
||||
sptr = rv.UnsafeAddr()
|
||||
break TOP
|
||||
}
|
||||
goto TOP
|
||||
case reflect.Interface:
|
||||
if rv.IsNil() {
|
||||
e.e.EncodeNil()
|
||||
return
|
||||
|
|
@ -1163,18 +1201,40 @@ TOP:
|
|||
return
|
||||
}
|
||||
|
||||
return rv, true
|
||||
proceed = true
|
||||
rv2 = rv
|
||||
return
|
||||
}
|
||||
|
||||
func (e *Encoder) doEncodeValue(rv reflect.Value, fn *encFn, sptr uintptr,
|
||||
checkFastpath, checkCodecSelfer bool) {
|
||||
if sptr != 0 {
|
||||
if (&e.ci).add(sptr) {
|
||||
e.errorf("circular reference found: # %d", sptr)
|
||||
}
|
||||
}
|
||||
if fn == nil {
|
||||
rt := rv.Type()
|
||||
rtid := reflect.ValueOf(rt).Pointer()
|
||||
// fn = e.getEncFn(rtid, rt, true, true)
|
||||
fn = e.getEncFn(rtid, rt, checkFastpath, checkCodecSelfer)
|
||||
}
|
||||
fn.f(&fn.i, rv)
|
||||
if sptr != 0 {
|
||||
(&e.ci).remove(sptr)
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Encoder) encodeI(iv interface{}, checkFastpath, checkCodecSelfer bool) {
|
||||
if rv, sptr, proceed := e.preEncodeValue(reflect.ValueOf(iv)); proceed {
|
||||
e.doEncodeValue(rv, nil, sptr, checkFastpath, checkCodecSelfer)
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Encoder) encodeValue(rv reflect.Value, fn *encFn) {
|
||||
// if a valid fn is passed, it MUST BE for the dereferenced type of rv
|
||||
if rv, proceed := e.preEncodeValue(rv); proceed {
|
||||
if fn == nil {
|
||||
rt := rv.Type()
|
||||
rtid := reflect.ValueOf(rt).Pointer()
|
||||
fn = e.getEncFn(rtid, rt, true, true)
|
||||
}
|
||||
fn.f(&fn.i, rv)
|
||||
if rv, sptr, proceed := e.preEncodeValue(rv); proceed {
|
||||
e.doEncodeValue(rv, fn, sptr, true, true)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1217,6 +1277,8 @@ func (e *Encoder) getEncFn(rtid uintptr, rt reflect.Type, checkFastpath, checkCo
|
|||
|
||||
if checkCodecSelfer && ti.cs {
|
||||
fn.f = (*encFnInfo).selferMarshal
|
||||
} else if rtid == rawTypId {
|
||||
fn.f = (*encFnInfo).raw
|
||||
} else if rtid == rawExtTypId {
|
||||
fn.f = (*encFnInfo).rawExt
|
||||
} else if e.e.IsBuiltinType(rtid) {
|
||||
|
|
@ -1234,7 +1296,7 @@ func (e *Encoder) getEncFn(rtid uintptr, rt reflect.Type, checkFastpath, checkCo
|
|||
} else {
|
||||
rk := rt.Kind()
|
||||
if fastpathEnabled && checkFastpath && (rk == reflect.Map || rk == reflect.Slice) {
|
||||
if rt.PkgPath() == "" {
|
||||
if rt.PkgPath() == "" { // un-named slice or map
|
||||
if idx := fastpathAV.index(rtid); idx != -1 {
|
||||
fn.f = fastpathAV[idx].encfn
|
||||
}
|
||||
|
|
@ -1284,10 +1346,11 @@ func (e *Encoder) getEncFn(rtid uintptr, rt reflect.Type, checkFastpath, checkCo
|
|||
fn.f = (*encFnInfo).kSlice
|
||||
case reflect.Struct:
|
||||
fn.f = (*encFnInfo).kStruct
|
||||
// reflect.Ptr and reflect.Interface are handled already by preEncodeValue
|
||||
// case reflect.Ptr:
|
||||
// fn.f = (*encFnInfo).kPtr
|
||||
case reflect.Interface:
|
||||
fn.f = (*encFnInfo).kInterface
|
||||
// case reflect.Interface:
|
||||
// fn.f = (*encFnInfo).kInterface
|
||||
case reflect.Map:
|
||||
fn.f = (*encFnInfo).kMap
|
||||
default:
|
||||
|
|
@ -1320,6 +1383,18 @@ func (e *Encoder) asis(v []byte) {
|
|||
}
|
||||
}
|
||||
|
||||
func (e *Encoder) raw(vv Raw) {
|
||||
v := []byte(vv)
|
||||
if !e.h.Raw {
|
||||
e.errorf("Raw values cannot be encoded: %v", v)
|
||||
}
|
||||
if e.as == nil {
|
||||
e.w.writeb(v)
|
||||
} else {
|
||||
e.as.EncodeAsis(v)
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Encoder) errorf(format string, params ...interface{}) {
|
||||
err := fmt.Errorf(format, params...)
|
||||
panic(err)
|
||||
|
|
@ -1353,25 +1428,6 @@ func encStructPoolGet(newlen int) (p *sync.Pool, v interface{}, s []stringRv) {
|
|||
// panic(errors.New("encStructPoolLen must be equal to 4")) // defensive, in case it is changed
|
||||
// }
|
||||
// idxpool := newlen / 8
|
||||
|
||||
// if pool == nil {
|
||||
// fkvs = make([]stringRv, newlen)
|
||||
// } else {
|
||||
// poolv = pool.Get()
|
||||
// switch vv := poolv.(type) {
|
||||
// case *[8]stringRv:
|
||||
// fkvs = vv[:newlen]
|
||||
// case *[16]stringRv:
|
||||
// fkvs = vv[:newlen]
|
||||
// case *[32]stringRv:
|
||||
// fkvs = vv[:newlen]
|
||||
// case *[64]stringRv:
|
||||
// fkvs = vv[:newlen]
|
||||
// case *[128]stringRv:
|
||||
// fkvs = vv[:newlen]
|
||||
// }
|
||||
// }
|
||||
|
||||
if newlen <= 8 {
|
||||
p = &encStructPool[0]
|
||||
v = p.Get()
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ package codec
|
|||
// Currently support
|
||||
// - slice of all builtin types,
|
||||
// - map of all builtin types to string or interface value
|
||||
// - symetrical maps of all builtin types (e.g. str-str, uint8-uint8)
|
||||
// - symmetrical maps of all builtin types (e.g. str-str, uint8-uint8)
|
||||
// This should provide adequate "typical" implementations.
|
||||
//
|
||||
// Note that fast track decode functions must handle values for which an address cannot be obtained.
|
||||
|
|
@ -38,6 +38,8 @@ import (
|
|||
"sort"
|
||||
)
|
||||
|
||||
const fastpathEnabled = true
|
||||
|
||||
const fastpathCheckNilFalse = false // for reflect
|
||||
const fastpathCheckNilTrue = true // for type switch
|
||||
|
||||
|
|
@ -81,9 +83,6 @@ var fastpathAV fastpathA
|
|||
|
||||
// due to possible initialization loop error, make fastpath in an init()
|
||||
func init() {
|
||||
if !fastpathEnabled {
|
||||
return
|
||||
}
|
||||
i := 0
|
||||
fn := func(v interface{}, fe func(*encFnInfo, reflect.Value), fd func(*decFnInfo, reflect.Value)) (f fastpathE) {
|
||||
xrt := reflect.TypeOf(v)
|
||||
|
|
@ -373,9 +372,6 @@ func init() {
|
|||
|
||||
// -- -- fast path type switch
|
||||
func fastpathEncodeTypeSwitch(iv interface{}, e *Encoder) bool {
|
||||
if !fastpathEnabled {
|
||||
return false
|
||||
}
|
||||
switch v := iv.(type) {
|
||||
|
||||
case []interface{}:
|
||||
|
|
@ -1741,9 +1737,6 @@ func fastpathEncodeTypeSwitch(iv interface{}, e *Encoder) bool {
|
|||
}
|
||||
|
||||
func fastpathEncodeTypeSwitchSlice(iv interface{}, e *Encoder) bool {
|
||||
if !fastpathEnabled {
|
||||
return false
|
||||
}
|
||||
switch v := iv.(type) {
|
||||
|
||||
case []interface{}:
|
||||
|
|
@ -1829,9 +1822,6 @@ func fastpathEncodeTypeSwitchSlice(iv interface{}, e *Encoder) bool {
|
|||
}
|
||||
|
||||
func fastpathEncodeTypeSwitchMap(iv interface{}, e *Encoder) bool {
|
||||
if !fastpathEnabled {
|
||||
return false
|
||||
}
|
||||
switch v := iv.(type) {
|
||||
|
||||
case map[interface{}]interface{}:
|
||||
|
|
@ -3124,7 +3114,11 @@ func fastpathEncodeTypeSwitchMap(iv interface{}, e *Encoder) bool {
|
|||
// -- -- fast path functions
|
||||
|
||||
func (f *encFnInfo) fastpathEncSliceIntfR(rv reflect.Value) {
|
||||
fastpathTV.EncSliceIntfV(rv.Interface().([]interface{}), fastpathCheckNilFalse, f.e)
|
||||
if f.ti.mbs {
|
||||
fastpathTV.EncAsMapSliceIntfV(rv.Interface().([]interface{}), fastpathCheckNilFalse, f.e)
|
||||
} else {
|
||||
fastpathTV.EncSliceIntfV(rv.Interface().([]interface{}), fastpathCheckNilFalse, f.e)
|
||||
}
|
||||
}
|
||||
func (_ fastpathT) EncSliceIntfV(v []interface{}, checkNil bool, e *Encoder) {
|
||||
ee := e.e
|
||||
|
|
@ -3145,8 +3139,39 @@ func (_ fastpathT) EncSliceIntfV(v []interface{}, checkNil bool, e *Encoder) {
|
|||
}
|
||||
}
|
||||
|
||||
func (_ fastpathT) EncAsMapSliceIntfV(v []interface{}, checkNil bool, e *Encoder) {
|
||||
ee := e.e
|
||||
cr := e.cr
|
||||
if checkNil && v == nil {
|
||||
ee.EncodeNil()
|
||||
return
|
||||
}
|
||||
if len(v)%2 == 1 {
|
||||
e.errorf("mapBySlice requires even slice length, but got %v", len(v))
|
||||
return
|
||||
}
|
||||
ee.EncodeMapStart(len(v) / 2)
|
||||
for j, v2 := range v {
|
||||
if cr != nil {
|
||||
if j%2 == 0 {
|
||||
cr.sendContainerState(containerMapKey)
|
||||
} else {
|
||||
cr.sendContainerState(containerMapValue)
|
||||
}
|
||||
}
|
||||
e.encode(v2)
|
||||
}
|
||||
if cr != nil {
|
||||
cr.sendContainerState(containerMapEnd)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *encFnInfo) fastpathEncSliceStringR(rv reflect.Value) {
|
||||
fastpathTV.EncSliceStringV(rv.Interface().([]string), fastpathCheckNilFalse, f.e)
|
||||
if f.ti.mbs {
|
||||
fastpathTV.EncAsMapSliceStringV(rv.Interface().([]string), fastpathCheckNilFalse, f.e)
|
||||
} else {
|
||||
fastpathTV.EncSliceStringV(rv.Interface().([]string), fastpathCheckNilFalse, f.e)
|
||||
}
|
||||
}
|
||||
func (_ fastpathT) EncSliceStringV(v []string, checkNil bool, e *Encoder) {
|
||||
ee := e.e
|
||||
|
|
@ -3167,8 +3192,39 @@ func (_ fastpathT) EncSliceStringV(v []string, checkNil bool, e *Encoder) {
|
|||
}
|
||||
}
|
||||
|
||||
func (_ fastpathT) EncAsMapSliceStringV(v []string, checkNil bool, e *Encoder) {
|
||||
ee := e.e
|
||||
cr := e.cr
|
||||
if checkNil && v == nil {
|
||||
ee.EncodeNil()
|
||||
return
|
||||
}
|
||||
if len(v)%2 == 1 {
|
||||
e.errorf("mapBySlice requires even slice length, but got %v", len(v))
|
||||
return
|
||||
}
|
||||
ee.EncodeMapStart(len(v) / 2)
|
||||
for j, v2 := range v {
|
||||
if cr != nil {
|
||||
if j%2 == 0 {
|
||||
cr.sendContainerState(containerMapKey)
|
||||
} else {
|
||||
cr.sendContainerState(containerMapValue)
|
||||
}
|
||||
}
|
||||
ee.EncodeString(c_UTF8, v2)
|
||||
}
|
||||
if cr != nil {
|
||||
cr.sendContainerState(containerMapEnd)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *encFnInfo) fastpathEncSliceFloat32R(rv reflect.Value) {
|
||||
fastpathTV.EncSliceFloat32V(rv.Interface().([]float32), fastpathCheckNilFalse, f.e)
|
||||
if f.ti.mbs {
|
||||
fastpathTV.EncAsMapSliceFloat32V(rv.Interface().([]float32), fastpathCheckNilFalse, f.e)
|
||||
} else {
|
||||
fastpathTV.EncSliceFloat32V(rv.Interface().([]float32), fastpathCheckNilFalse, f.e)
|
||||
}
|
||||
}
|
||||
func (_ fastpathT) EncSliceFloat32V(v []float32, checkNil bool, e *Encoder) {
|
||||
ee := e.e
|
||||
|
|
@ -3189,8 +3245,39 @@ func (_ fastpathT) EncSliceFloat32V(v []float32, checkNil bool, e *Encoder) {
|
|||
}
|
||||
}
|
||||
|
||||
func (_ fastpathT) EncAsMapSliceFloat32V(v []float32, checkNil bool, e *Encoder) {
|
||||
ee := e.e
|
||||
cr := e.cr
|
||||
if checkNil && v == nil {
|
||||
ee.EncodeNil()
|
||||
return
|
||||
}
|
||||
if len(v)%2 == 1 {
|
||||
e.errorf("mapBySlice requires even slice length, but got %v", len(v))
|
||||
return
|
||||
}
|
||||
ee.EncodeMapStart(len(v) / 2)
|
||||
for j, v2 := range v {
|
||||
if cr != nil {
|
||||
if j%2 == 0 {
|
||||
cr.sendContainerState(containerMapKey)
|
||||
} else {
|
||||
cr.sendContainerState(containerMapValue)
|
||||
}
|
||||
}
|
||||
ee.EncodeFloat32(v2)
|
||||
}
|
||||
if cr != nil {
|
||||
cr.sendContainerState(containerMapEnd)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *encFnInfo) fastpathEncSliceFloat64R(rv reflect.Value) {
|
||||
fastpathTV.EncSliceFloat64V(rv.Interface().([]float64), fastpathCheckNilFalse, f.e)
|
||||
if f.ti.mbs {
|
||||
fastpathTV.EncAsMapSliceFloat64V(rv.Interface().([]float64), fastpathCheckNilFalse, f.e)
|
||||
} else {
|
||||
fastpathTV.EncSliceFloat64V(rv.Interface().([]float64), fastpathCheckNilFalse, f.e)
|
||||
}
|
||||
}
|
||||
func (_ fastpathT) EncSliceFloat64V(v []float64, checkNil bool, e *Encoder) {
|
||||
ee := e.e
|
||||
|
|
@ -3211,8 +3298,39 @@ func (_ fastpathT) EncSliceFloat64V(v []float64, checkNil bool, e *Encoder) {
|
|||
}
|
||||
}
|
||||
|
||||
func (_ fastpathT) EncAsMapSliceFloat64V(v []float64, checkNil bool, e *Encoder) {
|
||||
ee := e.e
|
||||
cr := e.cr
|
||||
if checkNil && v == nil {
|
||||
ee.EncodeNil()
|
||||
return
|
||||
}
|
||||
if len(v)%2 == 1 {
|
||||
e.errorf("mapBySlice requires even slice length, but got %v", len(v))
|
||||
return
|
||||
}
|
||||
ee.EncodeMapStart(len(v) / 2)
|
||||
for j, v2 := range v {
|
||||
if cr != nil {
|
||||
if j%2 == 0 {
|
||||
cr.sendContainerState(containerMapKey)
|
||||
} else {
|
||||
cr.sendContainerState(containerMapValue)
|
||||
}
|
||||
}
|
||||
ee.EncodeFloat64(v2)
|
||||
}
|
||||
if cr != nil {
|
||||
cr.sendContainerState(containerMapEnd)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *encFnInfo) fastpathEncSliceUintR(rv reflect.Value) {
|
||||
fastpathTV.EncSliceUintV(rv.Interface().([]uint), fastpathCheckNilFalse, f.e)
|
||||
if f.ti.mbs {
|
||||
fastpathTV.EncAsMapSliceUintV(rv.Interface().([]uint), fastpathCheckNilFalse, f.e)
|
||||
} else {
|
||||
fastpathTV.EncSliceUintV(rv.Interface().([]uint), fastpathCheckNilFalse, f.e)
|
||||
}
|
||||
}
|
||||
func (_ fastpathT) EncSliceUintV(v []uint, checkNil bool, e *Encoder) {
|
||||
ee := e.e
|
||||
|
|
@ -3233,8 +3351,39 @@ func (_ fastpathT) EncSliceUintV(v []uint, checkNil bool, e *Encoder) {
|
|||
}
|
||||
}
|
||||
|
||||
func (_ fastpathT) EncAsMapSliceUintV(v []uint, checkNil bool, e *Encoder) {
|
||||
ee := e.e
|
||||
cr := e.cr
|
||||
if checkNil && v == nil {
|
||||
ee.EncodeNil()
|
||||
return
|
||||
}
|
||||
if len(v)%2 == 1 {
|
||||
e.errorf("mapBySlice requires even slice length, but got %v", len(v))
|
||||
return
|
||||
}
|
||||
ee.EncodeMapStart(len(v) / 2)
|
||||
for j, v2 := range v {
|
||||
if cr != nil {
|
||||
if j%2 == 0 {
|
||||
cr.sendContainerState(containerMapKey)
|
||||
} else {
|
||||
cr.sendContainerState(containerMapValue)
|
||||
}
|
||||
}
|
||||
ee.EncodeUint(uint64(v2))
|
||||
}
|
||||
if cr != nil {
|
||||
cr.sendContainerState(containerMapEnd)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *encFnInfo) fastpathEncSliceUint16R(rv reflect.Value) {
|
||||
fastpathTV.EncSliceUint16V(rv.Interface().([]uint16), fastpathCheckNilFalse, f.e)
|
||||
if f.ti.mbs {
|
||||
fastpathTV.EncAsMapSliceUint16V(rv.Interface().([]uint16), fastpathCheckNilFalse, f.e)
|
||||
} else {
|
||||
fastpathTV.EncSliceUint16V(rv.Interface().([]uint16), fastpathCheckNilFalse, f.e)
|
||||
}
|
||||
}
|
||||
func (_ fastpathT) EncSliceUint16V(v []uint16, checkNil bool, e *Encoder) {
|
||||
ee := e.e
|
||||
|
|
@ -3255,8 +3404,39 @@ func (_ fastpathT) EncSliceUint16V(v []uint16, checkNil bool, e *Encoder) {
|
|||
}
|
||||
}
|
||||
|
||||
func (_ fastpathT) EncAsMapSliceUint16V(v []uint16, checkNil bool, e *Encoder) {
|
||||
ee := e.e
|
||||
cr := e.cr
|
||||
if checkNil && v == nil {
|
||||
ee.EncodeNil()
|
||||
return
|
||||
}
|
||||
if len(v)%2 == 1 {
|
||||
e.errorf("mapBySlice requires even slice length, but got %v", len(v))
|
||||
return
|
||||
}
|
||||
ee.EncodeMapStart(len(v) / 2)
|
||||
for j, v2 := range v {
|
||||
if cr != nil {
|
||||
if j%2 == 0 {
|
||||
cr.sendContainerState(containerMapKey)
|
||||
} else {
|
||||
cr.sendContainerState(containerMapValue)
|
||||
}
|
||||
}
|
||||
ee.EncodeUint(uint64(v2))
|
||||
}
|
||||
if cr != nil {
|
||||
cr.sendContainerState(containerMapEnd)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *encFnInfo) fastpathEncSliceUint32R(rv reflect.Value) {
|
||||
fastpathTV.EncSliceUint32V(rv.Interface().([]uint32), fastpathCheckNilFalse, f.e)
|
||||
if f.ti.mbs {
|
||||
fastpathTV.EncAsMapSliceUint32V(rv.Interface().([]uint32), fastpathCheckNilFalse, f.e)
|
||||
} else {
|
||||
fastpathTV.EncSliceUint32V(rv.Interface().([]uint32), fastpathCheckNilFalse, f.e)
|
||||
}
|
||||
}
|
||||
func (_ fastpathT) EncSliceUint32V(v []uint32, checkNil bool, e *Encoder) {
|
||||
ee := e.e
|
||||
|
|
@ -3277,8 +3457,39 @@ func (_ fastpathT) EncSliceUint32V(v []uint32, checkNil bool, e *Encoder) {
|
|||
}
|
||||
}
|
||||
|
||||
func (_ fastpathT) EncAsMapSliceUint32V(v []uint32, checkNil bool, e *Encoder) {
|
||||
ee := e.e
|
||||
cr := e.cr
|
||||
if checkNil && v == nil {
|
||||
ee.EncodeNil()
|
||||
return
|
||||
}
|
||||
if len(v)%2 == 1 {
|
||||
e.errorf("mapBySlice requires even slice length, but got %v", len(v))
|
||||
return
|
||||
}
|
||||
ee.EncodeMapStart(len(v) / 2)
|
||||
for j, v2 := range v {
|
||||
if cr != nil {
|
||||
if j%2 == 0 {
|
||||
cr.sendContainerState(containerMapKey)
|
||||
} else {
|
||||
cr.sendContainerState(containerMapValue)
|
||||
}
|
||||
}
|
||||
ee.EncodeUint(uint64(v2))
|
||||
}
|
||||
if cr != nil {
|
||||
cr.sendContainerState(containerMapEnd)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *encFnInfo) fastpathEncSliceUint64R(rv reflect.Value) {
|
||||
fastpathTV.EncSliceUint64V(rv.Interface().([]uint64), fastpathCheckNilFalse, f.e)
|
||||
if f.ti.mbs {
|
||||
fastpathTV.EncAsMapSliceUint64V(rv.Interface().([]uint64), fastpathCheckNilFalse, f.e)
|
||||
} else {
|
||||
fastpathTV.EncSliceUint64V(rv.Interface().([]uint64), fastpathCheckNilFalse, f.e)
|
||||
}
|
||||
}
|
||||
func (_ fastpathT) EncSliceUint64V(v []uint64, checkNil bool, e *Encoder) {
|
||||
ee := e.e
|
||||
|
|
@ -3299,8 +3510,39 @@ func (_ fastpathT) EncSliceUint64V(v []uint64, checkNil bool, e *Encoder) {
|
|||
}
|
||||
}
|
||||
|
||||
func (_ fastpathT) EncAsMapSliceUint64V(v []uint64, checkNil bool, e *Encoder) {
|
||||
ee := e.e
|
||||
cr := e.cr
|
||||
if checkNil && v == nil {
|
||||
ee.EncodeNil()
|
||||
return
|
||||
}
|
||||
if len(v)%2 == 1 {
|
||||
e.errorf("mapBySlice requires even slice length, but got %v", len(v))
|
||||
return
|
||||
}
|
||||
ee.EncodeMapStart(len(v) / 2)
|
||||
for j, v2 := range v {
|
||||
if cr != nil {
|
||||
if j%2 == 0 {
|
||||
cr.sendContainerState(containerMapKey)
|
||||
} else {
|
||||
cr.sendContainerState(containerMapValue)
|
||||
}
|
||||
}
|
||||
ee.EncodeUint(uint64(v2))
|
||||
}
|
||||
if cr != nil {
|
||||
cr.sendContainerState(containerMapEnd)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *encFnInfo) fastpathEncSliceUintptrR(rv reflect.Value) {
|
||||
fastpathTV.EncSliceUintptrV(rv.Interface().([]uintptr), fastpathCheckNilFalse, f.e)
|
||||
if f.ti.mbs {
|
||||
fastpathTV.EncAsMapSliceUintptrV(rv.Interface().([]uintptr), fastpathCheckNilFalse, f.e)
|
||||
} else {
|
||||
fastpathTV.EncSliceUintptrV(rv.Interface().([]uintptr), fastpathCheckNilFalse, f.e)
|
||||
}
|
||||
}
|
||||
func (_ fastpathT) EncSliceUintptrV(v []uintptr, checkNil bool, e *Encoder) {
|
||||
ee := e.e
|
||||
|
|
@ -3321,8 +3563,39 @@ func (_ fastpathT) EncSliceUintptrV(v []uintptr, checkNil bool, e *Encoder) {
|
|||
}
|
||||
}
|
||||
|
||||
func (_ fastpathT) EncAsMapSliceUintptrV(v []uintptr, checkNil bool, e *Encoder) {
|
||||
ee := e.e
|
||||
cr := e.cr
|
||||
if checkNil && v == nil {
|
||||
ee.EncodeNil()
|
||||
return
|
||||
}
|
||||
if len(v)%2 == 1 {
|
||||
e.errorf("mapBySlice requires even slice length, but got %v", len(v))
|
||||
return
|
||||
}
|
||||
ee.EncodeMapStart(len(v) / 2)
|
||||
for j, v2 := range v {
|
||||
if cr != nil {
|
||||
if j%2 == 0 {
|
||||
cr.sendContainerState(containerMapKey)
|
||||
} else {
|
||||
cr.sendContainerState(containerMapValue)
|
||||
}
|
||||
}
|
||||
e.encode(v2)
|
||||
}
|
||||
if cr != nil {
|
||||
cr.sendContainerState(containerMapEnd)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *encFnInfo) fastpathEncSliceIntR(rv reflect.Value) {
|
||||
fastpathTV.EncSliceIntV(rv.Interface().([]int), fastpathCheckNilFalse, f.e)
|
||||
if f.ti.mbs {
|
||||
fastpathTV.EncAsMapSliceIntV(rv.Interface().([]int), fastpathCheckNilFalse, f.e)
|
||||
} else {
|
||||
fastpathTV.EncSliceIntV(rv.Interface().([]int), fastpathCheckNilFalse, f.e)
|
||||
}
|
||||
}
|
||||
func (_ fastpathT) EncSliceIntV(v []int, checkNil bool, e *Encoder) {
|
||||
ee := e.e
|
||||
|
|
@ -3343,8 +3616,39 @@ func (_ fastpathT) EncSliceIntV(v []int, checkNil bool, e *Encoder) {
|
|||
}
|
||||
}
|
||||
|
||||
func (_ fastpathT) EncAsMapSliceIntV(v []int, checkNil bool, e *Encoder) {
|
||||
ee := e.e
|
||||
cr := e.cr
|
||||
if checkNil && v == nil {
|
||||
ee.EncodeNil()
|
||||
return
|
||||
}
|
||||
if len(v)%2 == 1 {
|
||||
e.errorf("mapBySlice requires even slice length, but got %v", len(v))
|
||||
return
|
||||
}
|
||||
ee.EncodeMapStart(len(v) / 2)
|
||||
for j, v2 := range v {
|
||||
if cr != nil {
|
||||
if j%2 == 0 {
|
||||
cr.sendContainerState(containerMapKey)
|
||||
} else {
|
||||
cr.sendContainerState(containerMapValue)
|
||||
}
|
||||
}
|
||||
ee.EncodeInt(int64(v2))
|
||||
}
|
||||
if cr != nil {
|
||||
cr.sendContainerState(containerMapEnd)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *encFnInfo) fastpathEncSliceInt8R(rv reflect.Value) {
|
||||
fastpathTV.EncSliceInt8V(rv.Interface().([]int8), fastpathCheckNilFalse, f.e)
|
||||
if f.ti.mbs {
|
||||
fastpathTV.EncAsMapSliceInt8V(rv.Interface().([]int8), fastpathCheckNilFalse, f.e)
|
||||
} else {
|
||||
fastpathTV.EncSliceInt8V(rv.Interface().([]int8), fastpathCheckNilFalse, f.e)
|
||||
}
|
||||
}
|
||||
func (_ fastpathT) EncSliceInt8V(v []int8, checkNil bool, e *Encoder) {
|
||||
ee := e.e
|
||||
|
|
@ -3365,8 +3669,39 @@ func (_ fastpathT) EncSliceInt8V(v []int8, checkNil bool, e *Encoder) {
|
|||
}
|
||||
}
|
||||
|
||||
func (_ fastpathT) EncAsMapSliceInt8V(v []int8, checkNil bool, e *Encoder) {
|
||||
ee := e.e
|
||||
cr := e.cr
|
||||
if checkNil && v == nil {
|
||||
ee.EncodeNil()
|
||||
return
|
||||
}
|
||||
if len(v)%2 == 1 {
|
||||
e.errorf("mapBySlice requires even slice length, but got %v", len(v))
|
||||
return
|
||||
}
|
||||
ee.EncodeMapStart(len(v) / 2)
|
||||
for j, v2 := range v {
|
||||
if cr != nil {
|
||||
if j%2 == 0 {
|
||||
cr.sendContainerState(containerMapKey)
|
||||
} else {
|
||||
cr.sendContainerState(containerMapValue)
|
||||
}
|
||||
}
|
||||
ee.EncodeInt(int64(v2))
|
||||
}
|
||||
if cr != nil {
|
||||
cr.sendContainerState(containerMapEnd)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *encFnInfo) fastpathEncSliceInt16R(rv reflect.Value) {
|
||||
fastpathTV.EncSliceInt16V(rv.Interface().([]int16), fastpathCheckNilFalse, f.e)
|
||||
if f.ti.mbs {
|
||||
fastpathTV.EncAsMapSliceInt16V(rv.Interface().([]int16), fastpathCheckNilFalse, f.e)
|
||||
} else {
|
||||
fastpathTV.EncSliceInt16V(rv.Interface().([]int16), fastpathCheckNilFalse, f.e)
|
||||
}
|
||||
}
|
||||
func (_ fastpathT) EncSliceInt16V(v []int16, checkNil bool, e *Encoder) {
|
||||
ee := e.e
|
||||
|
|
@ -3387,8 +3722,39 @@ func (_ fastpathT) EncSliceInt16V(v []int16, checkNil bool, e *Encoder) {
|
|||
}
|
||||
}
|
||||
|
||||
func (_ fastpathT) EncAsMapSliceInt16V(v []int16, checkNil bool, e *Encoder) {
|
||||
ee := e.e
|
||||
cr := e.cr
|
||||
if checkNil && v == nil {
|
||||
ee.EncodeNil()
|
||||
return
|
||||
}
|
||||
if len(v)%2 == 1 {
|
||||
e.errorf("mapBySlice requires even slice length, but got %v", len(v))
|
||||
return
|
||||
}
|
||||
ee.EncodeMapStart(len(v) / 2)
|
||||
for j, v2 := range v {
|
||||
if cr != nil {
|
||||
if j%2 == 0 {
|
||||
cr.sendContainerState(containerMapKey)
|
||||
} else {
|
||||
cr.sendContainerState(containerMapValue)
|
||||
}
|
||||
}
|
||||
ee.EncodeInt(int64(v2))
|
||||
}
|
||||
if cr != nil {
|
||||
cr.sendContainerState(containerMapEnd)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *encFnInfo) fastpathEncSliceInt32R(rv reflect.Value) {
|
||||
fastpathTV.EncSliceInt32V(rv.Interface().([]int32), fastpathCheckNilFalse, f.e)
|
||||
if f.ti.mbs {
|
||||
fastpathTV.EncAsMapSliceInt32V(rv.Interface().([]int32), fastpathCheckNilFalse, f.e)
|
||||
} else {
|
||||
fastpathTV.EncSliceInt32V(rv.Interface().([]int32), fastpathCheckNilFalse, f.e)
|
||||
}
|
||||
}
|
||||
func (_ fastpathT) EncSliceInt32V(v []int32, checkNil bool, e *Encoder) {
|
||||
ee := e.e
|
||||
|
|
@ -3409,8 +3775,39 @@ func (_ fastpathT) EncSliceInt32V(v []int32, checkNil bool, e *Encoder) {
|
|||
}
|
||||
}
|
||||
|
||||
func (_ fastpathT) EncAsMapSliceInt32V(v []int32, checkNil bool, e *Encoder) {
|
||||
ee := e.e
|
||||
cr := e.cr
|
||||
if checkNil && v == nil {
|
||||
ee.EncodeNil()
|
||||
return
|
||||
}
|
||||
if len(v)%2 == 1 {
|
||||
e.errorf("mapBySlice requires even slice length, but got %v", len(v))
|
||||
return
|
||||
}
|
||||
ee.EncodeMapStart(len(v) / 2)
|
||||
for j, v2 := range v {
|
||||
if cr != nil {
|
||||
if j%2 == 0 {
|
||||
cr.sendContainerState(containerMapKey)
|
||||
} else {
|
||||
cr.sendContainerState(containerMapValue)
|
||||
}
|
||||
}
|
||||
ee.EncodeInt(int64(v2))
|
||||
}
|
||||
if cr != nil {
|
||||
cr.sendContainerState(containerMapEnd)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *encFnInfo) fastpathEncSliceInt64R(rv reflect.Value) {
|
||||
fastpathTV.EncSliceInt64V(rv.Interface().([]int64), fastpathCheckNilFalse, f.e)
|
||||
if f.ti.mbs {
|
||||
fastpathTV.EncAsMapSliceInt64V(rv.Interface().([]int64), fastpathCheckNilFalse, f.e)
|
||||
} else {
|
||||
fastpathTV.EncSliceInt64V(rv.Interface().([]int64), fastpathCheckNilFalse, f.e)
|
||||
}
|
||||
}
|
||||
func (_ fastpathT) EncSliceInt64V(v []int64, checkNil bool, e *Encoder) {
|
||||
ee := e.e
|
||||
|
|
@ -3431,8 +3828,39 @@ func (_ fastpathT) EncSliceInt64V(v []int64, checkNil bool, e *Encoder) {
|
|||
}
|
||||
}
|
||||
|
||||
func (_ fastpathT) EncAsMapSliceInt64V(v []int64, checkNil bool, e *Encoder) {
|
||||
ee := e.e
|
||||
cr := e.cr
|
||||
if checkNil && v == nil {
|
||||
ee.EncodeNil()
|
||||
return
|
||||
}
|
||||
if len(v)%2 == 1 {
|
||||
e.errorf("mapBySlice requires even slice length, but got %v", len(v))
|
||||
return
|
||||
}
|
||||
ee.EncodeMapStart(len(v) / 2)
|
||||
for j, v2 := range v {
|
||||
if cr != nil {
|
||||
if j%2 == 0 {
|
||||
cr.sendContainerState(containerMapKey)
|
||||
} else {
|
||||
cr.sendContainerState(containerMapValue)
|
||||
}
|
||||
}
|
||||
ee.EncodeInt(int64(v2))
|
||||
}
|
||||
if cr != nil {
|
||||
cr.sendContainerState(containerMapEnd)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *encFnInfo) fastpathEncSliceBoolR(rv reflect.Value) {
|
||||
fastpathTV.EncSliceBoolV(rv.Interface().([]bool), fastpathCheckNilFalse, f.e)
|
||||
if f.ti.mbs {
|
||||
fastpathTV.EncAsMapSliceBoolV(rv.Interface().([]bool), fastpathCheckNilFalse, f.e)
|
||||
} else {
|
||||
fastpathTV.EncSliceBoolV(rv.Interface().([]bool), fastpathCheckNilFalse, f.e)
|
||||
}
|
||||
}
|
||||
func (_ fastpathT) EncSliceBoolV(v []bool, checkNil bool, e *Encoder) {
|
||||
ee := e.e
|
||||
|
|
@ -3453,6 +3881,33 @@ func (_ fastpathT) EncSliceBoolV(v []bool, checkNil bool, e *Encoder) {
|
|||
}
|
||||
}
|
||||
|
||||
func (_ fastpathT) EncAsMapSliceBoolV(v []bool, checkNil bool, e *Encoder) {
|
||||
ee := e.e
|
||||
cr := e.cr
|
||||
if checkNil && v == nil {
|
||||
ee.EncodeNil()
|
||||
return
|
||||
}
|
||||
if len(v)%2 == 1 {
|
||||
e.errorf("mapBySlice requires even slice length, but got %v", len(v))
|
||||
return
|
||||
}
|
||||
ee.EncodeMapStart(len(v) / 2)
|
||||
for j, v2 := range v {
|
||||
if cr != nil {
|
||||
if j%2 == 0 {
|
||||
cr.sendContainerState(containerMapKey)
|
||||
} else {
|
||||
cr.sendContainerState(containerMapValue)
|
||||
}
|
||||
}
|
||||
ee.EncodeBool(v2)
|
||||
}
|
||||
if cr != nil {
|
||||
cr.sendContainerState(containerMapEnd)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *encFnInfo) fastpathEncMapIntfIntfR(rv reflect.Value) {
|
||||
fastpathTV.EncMapIntfIntfV(rv.Interface().(map[interface{}]interface{}), fastpathCheckNilFalse, f.e)
|
||||
}
|
||||
|
|
@ -15489,9 +15944,6 @@ func (_ fastpathT) EncMapBoolBoolV(v map[bool]bool, checkNil bool, e *Encoder) {
|
|||
|
||||
// -- -- fast path type switch
|
||||
func fastpathDecodeTypeSwitch(iv interface{}, d *Decoder) bool {
|
||||
if !fastpathEnabled {
|
||||
return false
|
||||
}
|
||||
switch v := iv.(type) {
|
||||
|
||||
case []interface{}:
|
||||
|
|
@ -17712,7 +18164,7 @@ func (_ fastpathT) DecSliceIntfV(v []interface{}, checkNil bool, canChange bool,
|
|||
changed = true
|
||||
}
|
||||
slh.End()
|
||||
return
|
||||
return v, changed
|
||||
}
|
||||
|
||||
if containerLenS > 0 {
|
||||
|
|
@ -17771,7 +18223,7 @@ func (_ fastpathT) DecSliceIntfV(v []interface{}, checkNil bool, canChange bool,
|
|||
changed = true
|
||||
}
|
||||
slh.End()
|
||||
return
|
||||
return v, changed
|
||||
}
|
||||
if cap(v) == 0 {
|
||||
v = make([]interface{}, 1, 4)
|
||||
|
|
@ -17846,7 +18298,7 @@ func (_ fastpathT) DecSliceStringV(v []string, checkNil bool, canChange bool, d
|
|||
changed = true
|
||||
}
|
||||
slh.End()
|
||||
return
|
||||
return v, changed
|
||||
}
|
||||
|
||||
if containerLenS > 0 {
|
||||
|
|
@ -17905,7 +18357,7 @@ func (_ fastpathT) DecSliceStringV(v []string, checkNil bool, canChange bool, d
|
|||
changed = true
|
||||
}
|
||||
slh.End()
|
||||
return
|
||||
return v, changed
|
||||
}
|
||||
if cap(v) == 0 {
|
||||
v = make([]string, 1, 4)
|
||||
|
|
@ -17979,7 +18431,7 @@ func (_ fastpathT) DecSliceFloat32V(v []float32, checkNil bool, canChange bool,
|
|||
changed = true
|
||||
}
|
||||
slh.End()
|
||||
return
|
||||
return v, changed
|
||||
}
|
||||
|
||||
if containerLenS > 0 {
|
||||
|
|
@ -18038,7 +18490,7 @@ func (_ fastpathT) DecSliceFloat32V(v []float32, checkNil bool, canChange bool,
|
|||
changed = true
|
||||
}
|
||||
slh.End()
|
||||
return
|
||||
return v, changed
|
||||
}
|
||||
if cap(v) == 0 {
|
||||
v = make([]float32, 1, 4)
|
||||
|
|
@ -18112,7 +18564,7 @@ func (_ fastpathT) DecSliceFloat64V(v []float64, checkNil bool, canChange bool,
|
|||
changed = true
|
||||
}
|
||||
slh.End()
|
||||
return
|
||||
return v, changed
|
||||
}
|
||||
|
||||
if containerLenS > 0 {
|
||||
|
|
@ -18171,7 +18623,7 @@ func (_ fastpathT) DecSliceFloat64V(v []float64, checkNil bool, canChange bool,
|
|||
changed = true
|
||||
}
|
||||
slh.End()
|
||||
return
|
||||
return v, changed
|
||||
}
|
||||
if cap(v) == 0 {
|
||||
v = make([]float64, 1, 4)
|
||||
|
|
@ -18245,7 +18697,7 @@ func (_ fastpathT) DecSliceUintV(v []uint, checkNil bool, canChange bool, d *Dec
|
|||
changed = true
|
||||
}
|
||||
slh.End()
|
||||
return
|
||||
return v, changed
|
||||
}
|
||||
|
||||
if containerLenS > 0 {
|
||||
|
|
@ -18304,7 +18756,7 @@ func (_ fastpathT) DecSliceUintV(v []uint, checkNil bool, canChange bool, d *Dec
|
|||
changed = true
|
||||
}
|
||||
slh.End()
|
||||
return
|
||||
return v, changed
|
||||
}
|
||||
if cap(v) == 0 {
|
||||
v = make([]uint, 1, 4)
|
||||
|
|
@ -18378,7 +18830,7 @@ func (_ fastpathT) DecSliceUint16V(v []uint16, checkNil bool, canChange bool, d
|
|||
changed = true
|
||||
}
|
||||
slh.End()
|
||||
return
|
||||
return v, changed
|
||||
}
|
||||
|
||||
if containerLenS > 0 {
|
||||
|
|
@ -18437,7 +18889,7 @@ func (_ fastpathT) DecSliceUint16V(v []uint16, checkNil bool, canChange bool, d
|
|||
changed = true
|
||||
}
|
||||
slh.End()
|
||||
return
|
||||
return v, changed
|
||||
}
|
||||
if cap(v) == 0 {
|
||||
v = make([]uint16, 1, 4)
|
||||
|
|
@ -18511,7 +18963,7 @@ func (_ fastpathT) DecSliceUint32V(v []uint32, checkNil bool, canChange bool, d
|
|||
changed = true
|
||||
}
|
||||
slh.End()
|
||||
return
|
||||
return v, changed
|
||||
}
|
||||
|
||||
if containerLenS > 0 {
|
||||
|
|
@ -18570,7 +19022,7 @@ func (_ fastpathT) DecSliceUint32V(v []uint32, checkNil bool, canChange bool, d
|
|||
changed = true
|
||||
}
|
||||
slh.End()
|
||||
return
|
||||
return v, changed
|
||||
}
|
||||
if cap(v) == 0 {
|
||||
v = make([]uint32, 1, 4)
|
||||
|
|
@ -18644,7 +19096,7 @@ func (_ fastpathT) DecSliceUint64V(v []uint64, checkNil bool, canChange bool, d
|
|||
changed = true
|
||||
}
|
||||
slh.End()
|
||||
return
|
||||
return v, changed
|
||||
}
|
||||
|
||||
if containerLenS > 0 {
|
||||
|
|
@ -18703,7 +19155,7 @@ func (_ fastpathT) DecSliceUint64V(v []uint64, checkNil bool, canChange bool, d
|
|||
changed = true
|
||||
}
|
||||
slh.End()
|
||||
return
|
||||
return v, changed
|
||||
}
|
||||
if cap(v) == 0 {
|
||||
v = make([]uint64, 1, 4)
|
||||
|
|
@ -18777,7 +19229,7 @@ func (_ fastpathT) DecSliceUintptrV(v []uintptr, checkNil bool, canChange bool,
|
|||
changed = true
|
||||
}
|
||||
slh.End()
|
||||
return
|
||||
return v, changed
|
||||
}
|
||||
|
||||
if containerLenS > 0 {
|
||||
|
|
@ -18836,7 +19288,7 @@ func (_ fastpathT) DecSliceUintptrV(v []uintptr, checkNil bool, canChange bool,
|
|||
changed = true
|
||||
}
|
||||
slh.End()
|
||||
return
|
||||
return v, changed
|
||||
}
|
||||
if cap(v) == 0 {
|
||||
v = make([]uintptr, 1, 4)
|
||||
|
|
@ -18910,7 +19362,7 @@ func (_ fastpathT) DecSliceIntV(v []int, checkNil bool, canChange bool, d *Decod
|
|||
changed = true
|
||||
}
|
||||
slh.End()
|
||||
return
|
||||
return v, changed
|
||||
}
|
||||
|
||||
if containerLenS > 0 {
|
||||
|
|
@ -18969,7 +19421,7 @@ func (_ fastpathT) DecSliceIntV(v []int, checkNil bool, canChange bool, d *Decod
|
|||
changed = true
|
||||
}
|
||||
slh.End()
|
||||
return
|
||||
return v, changed
|
||||
}
|
||||
if cap(v) == 0 {
|
||||
v = make([]int, 1, 4)
|
||||
|
|
@ -19043,7 +19495,7 @@ func (_ fastpathT) DecSliceInt8V(v []int8, checkNil bool, canChange bool, d *Dec
|
|||
changed = true
|
||||
}
|
||||
slh.End()
|
||||
return
|
||||
return v, changed
|
||||
}
|
||||
|
||||
if containerLenS > 0 {
|
||||
|
|
@ -19102,7 +19554,7 @@ func (_ fastpathT) DecSliceInt8V(v []int8, checkNil bool, canChange bool, d *Dec
|
|||
changed = true
|
||||
}
|
||||
slh.End()
|
||||
return
|
||||
return v, changed
|
||||
}
|
||||
if cap(v) == 0 {
|
||||
v = make([]int8, 1, 4)
|
||||
|
|
@ -19176,7 +19628,7 @@ func (_ fastpathT) DecSliceInt16V(v []int16, checkNil bool, canChange bool, d *D
|
|||
changed = true
|
||||
}
|
||||
slh.End()
|
||||
return
|
||||
return v, changed
|
||||
}
|
||||
|
||||
if containerLenS > 0 {
|
||||
|
|
@ -19235,7 +19687,7 @@ func (_ fastpathT) DecSliceInt16V(v []int16, checkNil bool, canChange bool, d *D
|
|||
changed = true
|
||||
}
|
||||
slh.End()
|
||||
return
|
||||
return v, changed
|
||||
}
|
||||
if cap(v) == 0 {
|
||||
v = make([]int16, 1, 4)
|
||||
|
|
@ -19309,7 +19761,7 @@ func (_ fastpathT) DecSliceInt32V(v []int32, checkNil bool, canChange bool, d *D
|
|||
changed = true
|
||||
}
|
||||
slh.End()
|
||||
return
|
||||
return v, changed
|
||||
}
|
||||
|
||||
if containerLenS > 0 {
|
||||
|
|
@ -19368,7 +19820,7 @@ func (_ fastpathT) DecSliceInt32V(v []int32, checkNil bool, canChange bool, d *D
|
|||
changed = true
|
||||
}
|
||||
slh.End()
|
||||
return
|
||||
return v, changed
|
||||
}
|
||||
if cap(v) == 0 {
|
||||
v = make([]int32, 1, 4)
|
||||
|
|
@ -19442,7 +19894,7 @@ func (_ fastpathT) DecSliceInt64V(v []int64, checkNil bool, canChange bool, d *D
|
|||
changed = true
|
||||
}
|
||||
slh.End()
|
||||
return
|
||||
return v, changed
|
||||
}
|
||||
|
||||
if containerLenS > 0 {
|
||||
|
|
@ -19501,7 +19953,7 @@ func (_ fastpathT) DecSliceInt64V(v []int64, checkNil bool, canChange bool, d *D
|
|||
changed = true
|
||||
}
|
||||
slh.End()
|
||||
return
|
||||
return v, changed
|
||||
}
|
||||
if cap(v) == 0 {
|
||||
v = make([]int64, 1, 4)
|
||||
|
|
@ -19575,7 +20027,7 @@ func (_ fastpathT) DecSliceBoolV(v []bool, checkNil bool, canChange bool, d *Dec
|
|||
changed = true
|
||||
}
|
||||
slh.End()
|
||||
return
|
||||
return v, changed
|
||||
}
|
||||
|
||||
if containerLenS > 0 {
|
||||
|
|
@ -19634,7 +20086,7 @@ func (_ fastpathT) DecSliceBoolV(v []bool, checkNil bool, canChange bool, d *Dec
|
|||
changed = true
|
||||
}
|
||||
slh.End()
|
||||
return
|
||||
return v, changed
|
||||
}
|
||||
if cap(v) == 0 {
|
||||
v = make([]bool, 1, 4)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ package codec
|
|||
|
||||
import "reflect"
|
||||
|
||||
const fastpathEnabled = false
|
||||
|
||||
// The generated fast-path code is very large, and adds a few seconds to the build time.
|
||||
// This causes test execution, execution of small tools which use codec, etc
|
||||
// to take a long time.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// //+build ignore
|
||||
// // +build ignore
|
||||
|
||||
// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
|
||||
// Use of this source code is governed by a MIT license found in the LICENSE file.
|
||||
|
|
@ -17,7 +17,7 @@ import (
|
|||
|
||||
// This file is used to generate helper code for codecgen.
|
||||
// The values here i.e. genHelper(En|De)coder are not to be used directly by
|
||||
// library users. They WILL change continously and without notice.
|
||||
// library users. They WILL change continuously and without notice.
|
||||
//
|
||||
// To help enforce this, we create an unexported type with exported members.
|
||||
// The only way to get the type is via the one exported type that we control (somewhat).
|
||||
|
|
@ -83,6 +83,11 @@ func (f genHelperEncoder) EncBinaryMarshal(iv encoding.BinaryMarshaler) {
|
|||
f.e.marshal(bs, fnerr, false, c_RAW)
|
||||
}
|
||||
|
||||
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||
func (f genHelperEncoder) EncRaw(iv Raw) {
|
||||
f.e.raw(iv)
|
||||
}
|
||||
|
||||
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||
func (f genHelperEncoder) TimeRtidIfBinc() uintptr {
|
||||
if _, ok := f.e.hh.(*BincHandle); ok {
|
||||
|
|
@ -191,6 +196,11 @@ func (f genHelperDecoder) DecBinaryUnmarshal(bm encoding.BinaryUnmarshaler) {
|
|||
}
|
||||
}
|
||||
|
||||
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||
func (f genHelperDecoder) DecRaw() []byte {
|
||||
return f.d.raw()
|
||||
}
|
||||
|
||||
// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
|
||||
func (f genHelperDecoder) TimeRtidIfBinc() uintptr {
|
||||
if _, ok := f.d.hh.(*BincHandle); ok {
|
||||
|
|
|
|||
|
|
@ -68,8 +68,9 @@ z.DecSendContainerState(codecSelfer_containerMapEnd{{ .Sfx }})
|
|||
|
||||
const genDecListTmpl = `
|
||||
{{var "v"}} := {{if not isArray}}*{{end}}{{ .Varname }}
|
||||
{{var "h"}}, {{var "l"}} := z.DecSliceHelperStart() {{/* // helper, containerLenS */}}
|
||||
{{var "h"}}, {{var "l"}} := z.DecSliceHelperStart() {{/* // helper, containerLenS */}}{{if not isArray}}
|
||||
var {{var "c"}} bool {{/* // changed */}}
|
||||
_ = {{var "c"}}{{end}}
|
||||
if {{var "l"}} == 0 {
|
||||
{{if isSlice }}if {{var "v"}} == nil {
|
||||
{{var "v"}} = []{{ .Typ }}{}
|
||||
|
|
@ -95,6 +96,8 @@ if {{var "l"}} == 0 {
|
|||
}
|
||||
{{ else }} var {{var "rr"}}, {{var "rl"}} int {{/* // num2read, length of slice/array/chan */}}
|
||||
var {{var "rt"}} bool {{/* truncated */}}
|
||||
_, _ = {{var "rl"}}, {{var "rt"}}
|
||||
{{var "rr"}} = {{var "l"}} // len({{var "v"}})
|
||||
if {{var "l"}} > cap({{var "v"}}) {
|
||||
{{if isArray }}z.DecArrayCannotExpand(len({{var "v"}}), {{var "l"}})
|
||||
{{ else }}{{if not .Immutable }}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import (
|
|||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
|
|
@ -21,11 +20,14 @@ import (
|
|||
"sync"
|
||||
"text/template"
|
||||
"time"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// ---------------------------------------------------
|
||||
// codecgen supports the full cycle of reflection-based codec:
|
||||
// - RawExt
|
||||
// - Raw
|
||||
// - Builtins
|
||||
// - Extensions
|
||||
// - (Binary|Text|JSON)(Unm|M)arshal
|
||||
|
|
@ -76,7 +78,7 @@ import (
|
|||
// codecgen will panic if the file was generated with an old version of the library in use.
|
||||
//
|
||||
// Note:
|
||||
// It was a concious decision to have gen.go always explicitly call EncodeNil or TryDecodeAsNil.
|
||||
// It was a conscious decision to have gen.go always explicitly call EncodeNil or TryDecodeAsNil.
|
||||
// This way, there isn't a function call overhead just to see that we should not enter a block of code.
|
||||
|
||||
// GenVersion is the current version of codecgen.
|
||||
|
|
@ -124,6 +126,7 @@ var (
|
|||
genExpectArrayOrMapErr = errors.New("unexpected type. Expecting array/map/slice")
|
||||
genBase64enc = base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789__")
|
||||
genQNameRegex = regexp.MustCompile(`[A-Za-z_.]+`)
|
||||
genCheckVendor bool
|
||||
)
|
||||
|
||||
// genRunner holds some state used during a Gen run.
|
||||
|
|
@ -162,6 +165,16 @@ type genRunner struct {
|
|||
//
|
||||
// Library users: *DO NOT USE IT DIRECTLY. IT WILL CHANGE CONTINOUSLY WITHOUT NOTICE.*
|
||||
func Gen(w io.Writer, buildTags, pkgName, uid string, useUnsafe bool, ti *TypeInfos, typ ...reflect.Type) {
|
||||
// trim out all types which already implement Selfer
|
||||
typ2 := make([]reflect.Type, 0, len(typ))
|
||||
for _, t := range typ {
|
||||
if reflect.PtrTo(t).Implements(selferTyp) || t.Implements(selferTyp) {
|
||||
continue
|
||||
}
|
||||
typ2 = append(typ2, t)
|
||||
}
|
||||
typ = typ2
|
||||
|
||||
if len(typ) == 0 {
|
||||
return
|
||||
}
|
||||
|
|
@ -199,7 +212,7 @@ func Gen(w io.Writer, buildTags, pkgName, uid string, useUnsafe bool, ti *TypeIn
|
|||
x.genRefPkgs(t)
|
||||
}
|
||||
if buildTags != "" {
|
||||
x.line("//+build " + buildTags)
|
||||
x.line("// +build " + buildTags)
|
||||
x.line("")
|
||||
}
|
||||
x.line(`
|
||||
|
|
@ -266,6 +279,7 @@ func Gen(w io.Writer, buildTags, pkgName, uid string, useUnsafe bool, ti *TypeIn
|
|||
x.line("type " + x.hn + " struct{}")
|
||||
x.line("")
|
||||
|
||||
x.varsfxreset()
|
||||
x.line("func init() {")
|
||||
x.linef("if %sGenVersion != %v {", x.cpfx, GenVersion)
|
||||
x.line("_, file, _, _ := runtime.Caller(0)")
|
||||
|
|
@ -309,6 +323,7 @@ func Gen(w io.Writer, buildTags, pkgName, uid string, useUnsafe bool, ti *TypeIn
|
|||
for _, t := range x.ts {
|
||||
rtid := reflect.ValueOf(t).Pointer()
|
||||
// generate enc functions for all these slice/map types.
|
||||
x.varsfxreset()
|
||||
x.linef("func (x %s) enc%s(v %s%s, e *%sEncoder) {", x.hn, x.genMethodNameT(t), x.arr2str(t, "*"), x.genTypeName(t), x.cpfx)
|
||||
x.genRequiredMethodVars(true)
|
||||
switch t.Kind() {
|
||||
|
|
@ -323,6 +338,7 @@ func Gen(w io.Writer, buildTags, pkgName, uid string, useUnsafe bool, ti *TypeIn
|
|||
x.line("")
|
||||
|
||||
// generate dec functions for all these slice/map types.
|
||||
x.varsfxreset()
|
||||
x.linef("func (x %s) dec%s(v *%s, d *%sDecoder) {", x.hn, x.genMethodNameT(t), x.genTypeName(t), x.cpfx)
|
||||
x.genRequiredMethodVars(false)
|
||||
switch t.Kind() {
|
||||
|
|
@ -377,7 +393,7 @@ func (x *genRunner) genRefPkgs(t reflect.Type) {
|
|||
x.imn[tpkg] = tpkg
|
||||
} else {
|
||||
x.imc++
|
||||
x.imn[tpkg] = "pkg" + strconv.FormatUint(x.imc, 10) + "_" + tpkg[idx+1:]
|
||||
x.imn[tpkg] = "pkg" + strconv.FormatUint(x.imc, 10) + "_" + genGoIdentifier(tpkg[idx+1:], false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -408,6 +424,10 @@ func (x *genRunner) varsfx() string {
|
|||
return strconv.FormatUint(x.c, 10)
|
||||
}
|
||||
|
||||
func (x *genRunner) varsfxreset() {
|
||||
x.c = 0
|
||||
}
|
||||
|
||||
func (x *genRunner) out(s string) {
|
||||
if _, err := io.WriteString(x.w, s); err != nil {
|
||||
panic(err)
|
||||
|
|
@ -494,6 +514,7 @@ func (x *genRunner) selfer(encode bool) {
|
|||
// always make decode use a pointer receiver,
|
||||
// and structs always use a ptr receiver (encode|decode)
|
||||
isptr := !encode || t.Kind() == reflect.Struct
|
||||
x.varsfxreset()
|
||||
fnSigPfx := "func (x "
|
||||
if isptr {
|
||||
fnSigPfx += "*"
|
||||
|
|
@ -566,9 +587,28 @@ func (x *genRunner) xtraSM(varname string, encode bool, t reflect.Type) {
|
|||
} else {
|
||||
x.linef("h.dec%s((*%s)(%s), d)", x.genMethodNameT(t), x.genTypeName(t), varname)
|
||||
}
|
||||
if _, ok := x.tm[t]; !ok {
|
||||
x.tm[t] = struct{}{}
|
||||
x.ts = append(x.ts, t)
|
||||
x.registerXtraT(t)
|
||||
}
|
||||
|
||||
func (x *genRunner) registerXtraT(t reflect.Type) {
|
||||
// recursively register the types
|
||||
if _, ok := x.tm[t]; ok {
|
||||
return
|
||||
}
|
||||
var tkey reflect.Type
|
||||
switch t.Kind() {
|
||||
case reflect.Chan, reflect.Slice, reflect.Array:
|
||||
case reflect.Map:
|
||||
tkey = t.Key()
|
||||
default:
|
||||
return
|
||||
}
|
||||
x.tm[t] = struct{}{}
|
||||
x.ts = append(x.ts, t)
|
||||
// check if this refers to any xtra types eg. a slice of array: add the array
|
||||
x.registerXtraT(t.Elem())
|
||||
if tkey != nil {
|
||||
x.registerXtraT(tkey)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -608,22 +648,33 @@ func (x *genRunner) encVar(varname string, t reflect.Type) {
|
|||
|
||||
}
|
||||
|
||||
// enc will encode a variable (varname) of type T,
|
||||
// except t is of kind reflect.Struct or reflect.Array, wherein varname is of type *T (to prevent copying)
|
||||
// enc will encode a variable (varname) of type t,
|
||||
// except t is of kind reflect.Struct or reflect.Array, wherein varname is of type ptrTo(T) (to prevent copying)
|
||||
func (x *genRunner) enc(varname string, t reflect.Type) {
|
||||
// varName here must be to a pointer to a struct/array, or to a value directly.
|
||||
rtid := reflect.ValueOf(t).Pointer()
|
||||
// We call CodecEncodeSelf if one of the following are honored:
|
||||
// - the type already implements Selfer, call that
|
||||
// - the type has a Selfer implementation just created, use that
|
||||
// - the type is in the list of the ones we will generate for, but it is not currently being generated
|
||||
|
||||
mi := x.varsfx()
|
||||
tptr := reflect.PtrTo(t)
|
||||
tk := t.Kind()
|
||||
if x.checkForSelfer(t, varname) {
|
||||
if t.Implements(selferTyp) || (tptr.Implements(selferTyp) && (tk == reflect.Array || tk == reflect.Struct)) {
|
||||
x.line(varname + ".CodecEncodeSelf(e)")
|
||||
return
|
||||
if tk == reflect.Array || tk == reflect.Struct { // varname is of type *T
|
||||
if tptr.Implements(selferTyp) || t.Implements(selferTyp) {
|
||||
x.line(varname + ".CodecEncodeSelf(e)")
|
||||
return
|
||||
}
|
||||
} else { // varname is of type T
|
||||
if t.Implements(selferTyp) {
|
||||
x.line(varname + ".CodecEncodeSelf(e)")
|
||||
return
|
||||
} else if tptr.Implements(selferTyp) {
|
||||
x.linef("%ssf%s := &%s", genTempVarPfx, mi, varname)
|
||||
x.linef("%ssf%s.CodecEncodeSelf(e)", genTempVarPfx, mi)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := x.te[rtid]; ok {
|
||||
|
|
@ -651,14 +702,17 @@ func (x *genRunner) enc(varname string, t reflect.Type) {
|
|||
}
|
||||
|
||||
// check if
|
||||
// - type is RawExt
|
||||
// - type is RawExt, Raw
|
||||
// - the type implements (Text|JSON|Binary)(Unm|M)arshal
|
||||
mi := x.varsfx()
|
||||
x.linef("%sm%s := z.EncBinary()", genTempVarPfx, mi)
|
||||
x.linef("_ = %sm%s", genTempVarPfx, mi)
|
||||
x.line("if false {") //start if block
|
||||
defer func() { x.line("}") }() //end if block
|
||||
|
||||
if t == rawTyp {
|
||||
x.linef("} else { z.EncRaw(%v)", varname)
|
||||
return
|
||||
}
|
||||
if t == rawExtTyp {
|
||||
x.linef("} else { r.EncodeRawExt(%v, e)", varname)
|
||||
return
|
||||
|
|
@ -676,15 +730,31 @@ func (x *genRunner) enc(varname string, t reflect.Type) {
|
|||
// first check if extensions are configued, before doing the interface conversion
|
||||
x.linef("} else if z.HasExtensions() && z.EncExt(%s) {", varname)
|
||||
}
|
||||
if t.Implements(binaryMarshalerTyp) || tptr.Implements(binaryMarshalerTyp) {
|
||||
x.linef("} else if %sm%s { z.EncBinaryMarshal(%v) ", genTempVarPfx, mi, varname)
|
||||
if tk == reflect.Array || tk == reflect.Struct { // varname is of type *T
|
||||
if t.Implements(binaryMarshalerTyp) || tptr.Implements(binaryMarshalerTyp) {
|
||||
x.linef("} else if %sm%s { z.EncBinaryMarshal(%v) ", genTempVarPfx, mi, varname)
|
||||
}
|
||||
if t.Implements(jsonMarshalerTyp) || tptr.Implements(jsonMarshalerTyp) {
|
||||
x.linef("} else if !%sm%s && z.IsJSONHandle() { z.EncJSONMarshal(%v) ", genTempVarPfx, mi, varname)
|
||||
} else if t.Implements(textMarshalerTyp) || tptr.Implements(textMarshalerTyp) {
|
||||
x.linef("} else if !%sm%s { z.EncTextMarshal(%v) ", genTempVarPfx, mi, varname)
|
||||
}
|
||||
} else { // varname is of type T
|
||||
if t.Implements(binaryMarshalerTyp) {
|
||||
x.linef("} else if %sm%s { z.EncBinaryMarshal(%v) ", genTempVarPfx, mi, varname)
|
||||
} else if tptr.Implements(binaryMarshalerTyp) {
|
||||
x.linef("} else if %sm%s { z.EncBinaryMarshal(&%v) ", genTempVarPfx, mi, varname)
|
||||
}
|
||||
if t.Implements(jsonMarshalerTyp) {
|
||||
x.linef("} else if !%sm%s && z.IsJSONHandle() { z.EncJSONMarshal(%v) ", genTempVarPfx, mi, varname)
|
||||
} else if tptr.Implements(jsonMarshalerTyp) {
|
||||
x.linef("} else if !%sm%s && z.IsJSONHandle() { z.EncJSONMarshal(&%v) ", genTempVarPfx, mi, varname)
|
||||
} else if t.Implements(textMarshalerTyp) {
|
||||
x.linef("} else if !%sm%s { z.EncTextMarshal(%v) ", genTempVarPfx, mi, varname)
|
||||
} else if tptr.Implements(textMarshalerTyp) {
|
||||
x.linef("} else if !%sm%s { z.EncTextMarshal(&%v) ", genTempVarPfx, mi, varname)
|
||||
}
|
||||
}
|
||||
if t.Implements(jsonMarshalerTyp) || tptr.Implements(jsonMarshalerTyp) {
|
||||
x.linef("} else if !%sm%s && z.IsJSONHandle() { z.EncJSONMarshal(%v) ", genTempVarPfx, mi, varname)
|
||||
} else if t.Implements(textMarshalerTyp) || tptr.Implements(textMarshalerTyp) {
|
||||
x.linef("} else if !%sm%s { z.EncTextMarshal(%v) ", genTempVarPfx, mi, varname)
|
||||
}
|
||||
|
||||
x.line("} else {")
|
||||
|
||||
switch t.Kind() {
|
||||
|
|
@ -922,6 +992,14 @@ func (x *genRunner) encStruct(varname string, rtid uintptr, t reflect.Type) {
|
|||
}
|
||||
|
||||
func (x *genRunner) encListFallback(varname string, t reflect.Type) {
|
||||
if t.AssignableTo(uint8SliceTyp) {
|
||||
x.linef("r.EncodeStringBytes(codecSelferC_RAW%s, []byte(%s))", x.xs, varname)
|
||||
return
|
||||
}
|
||||
if t.Kind() == reflect.Array && t.Elem().Kind() == reflect.Uint8 {
|
||||
x.linef("r.EncodeStringBytes(codecSelferC_RAW%s, ([%v]byte(%s))[:])", x.xs, t.Len(), varname)
|
||||
return
|
||||
}
|
||||
i := x.varsfx()
|
||||
g := genTempVarPfx
|
||||
x.line("r.EncodeArrayStart(len(" + varname + "))")
|
||||
|
|
@ -1020,6 +1098,8 @@ func (x *genRunner) decVar(varname string, t reflect.Type, canBeNil bool) {
|
|||
}
|
||||
}
|
||||
|
||||
// dec will decode a variable (varname) of type ptrTo(t).
|
||||
// t is always a basetype (i.e. not of kind reflect.Ptr).
|
||||
func (x *genRunner) dec(varname string, t reflect.Type) {
|
||||
// assumptions:
|
||||
// - the varname is to a pointer already. No need to take address of it
|
||||
|
|
@ -1056,7 +1136,7 @@ func (x *genRunner) dec(varname string, t reflect.Type) {
|
|||
}
|
||||
|
||||
// check if
|
||||
// - type is RawExt
|
||||
// - type is Raw, RawExt
|
||||
// - the type implements (Text|JSON|Binary)(Unm|M)arshal
|
||||
mi := x.varsfx()
|
||||
x.linef("%sm%s := z.DecBinary()", genTempVarPfx, mi)
|
||||
|
|
@ -1064,6 +1144,10 @@ func (x *genRunner) dec(varname string, t reflect.Type) {
|
|||
x.line("if false {") //start if block
|
||||
defer func() { x.line("}") }() //end if block
|
||||
|
||||
if t == rawTyp {
|
||||
x.linef("} else { *%v = z.DecRaw()", varname)
|
||||
return
|
||||
}
|
||||
if t == rawExtTyp {
|
||||
x.linef("} else { r.DecodeExt(%v, 0, nil)", varname)
|
||||
return
|
||||
|
|
@ -1189,59 +1273,49 @@ func (x *genRunner) dec(varname string, t reflect.Type) {
|
|||
}
|
||||
|
||||
func (x *genRunner) decTryAssignPrimitive(varname string, t reflect.Type) (tryAsPtr bool) {
|
||||
// We have to use the actual type name when doing a direct assignment.
|
||||
// We don't have the luxury of casting the pointer to the underlying type.
|
||||
//
|
||||
// Consequently, in the situation of a
|
||||
// type Message int32
|
||||
// var x Message
|
||||
// var i int32 = 32
|
||||
// x = i // this will bomb
|
||||
// x = Message(i) // this will work
|
||||
// *((*int32)(&x)) = i // this will work
|
||||
//
|
||||
// Consequently, we replace:
|
||||
// case reflect.Uint32: x.line(varname + " = uint32(r.DecodeUint(32))")
|
||||
// with:
|
||||
// case reflect.Uint32: x.line(varname + " = " + genTypeNamePrim(t, x.tc) + "(r.DecodeUint(32))")
|
||||
// This should only be used for exact primitives (ie un-named types).
|
||||
// Named types may be implementations of Selfer, Unmarshaler, etc.
|
||||
// They should be handled by dec(...)
|
||||
|
||||
xfn := func(t reflect.Type) string {
|
||||
return x.genTypeNamePrim(t)
|
||||
if t.Name() != "" {
|
||||
tryAsPtr = true
|
||||
return
|
||||
}
|
||||
|
||||
switch t.Kind() {
|
||||
case reflect.Int:
|
||||
x.linef("%s = %s(r.DecodeInt(codecSelferBitsize%s))", varname, xfn(t), x.xs)
|
||||
x.linef("%s = r.DecodeInt(codecSelferBitsize%s)", varname, x.xs)
|
||||
case reflect.Int8:
|
||||
x.linef("%s = %s(r.DecodeInt(8))", varname, xfn(t))
|
||||
x.linef("%s = r.DecodeInt(8)", varname)
|
||||
case reflect.Int16:
|
||||
x.linef("%s = %s(r.DecodeInt(16))", varname, xfn(t))
|
||||
x.linef("%s = r.DecodeInt(16)", varname)
|
||||
case reflect.Int32:
|
||||
x.linef("%s = %s(r.DecodeInt(32))", varname, xfn(t))
|
||||
x.linef("%s = r.DecodeInt(32)", varname)
|
||||
case reflect.Int64:
|
||||
x.linef("%s = %s(r.DecodeInt(64))", varname, xfn(t))
|
||||
x.linef("%s = r.DecodeInt(64)", varname)
|
||||
|
||||
case reflect.Uint:
|
||||
x.linef("%s = %s(r.DecodeUint(codecSelferBitsize%s))", varname, xfn(t), x.xs)
|
||||
x.linef("%s = r.DecodeUint(codecSelferBitsize%s)", varname, x.xs)
|
||||
case reflect.Uint8:
|
||||
x.linef("%s = %s(r.DecodeUint(8))", varname, xfn(t))
|
||||
x.linef("%s = r.DecodeUint(8)", varname)
|
||||
case reflect.Uint16:
|
||||
x.linef("%s = %s(r.DecodeUint(16))", varname, xfn(t))
|
||||
x.linef("%s = r.DecodeUint(16)", varname)
|
||||
case reflect.Uint32:
|
||||
x.linef("%s = %s(r.DecodeUint(32))", varname, xfn(t))
|
||||
x.linef("%s = r.DecodeUint(32)", varname)
|
||||
case reflect.Uint64:
|
||||
x.linef("%s = %s(r.DecodeUint(64))", varname, xfn(t))
|
||||
x.linef("%s = r.DecodeUint(64)", varname)
|
||||
case reflect.Uintptr:
|
||||
x.linef("%s = %s(r.DecodeUint(codecSelferBitsize%s))", varname, xfn(t), x.xs)
|
||||
x.linef("%s = r.DecodeUint(codecSelferBitsize%s)", varname, x.xs)
|
||||
|
||||
case reflect.Float32:
|
||||
x.linef("%s = %s(r.DecodeFloat(true))", varname, xfn(t))
|
||||
x.linef("%s = r.DecodeFloat(true)", varname)
|
||||
case reflect.Float64:
|
||||
x.linef("%s = %s(r.DecodeFloat(false))", varname, xfn(t))
|
||||
x.linef("%s = r.DecodeFloat(false)", varname)
|
||||
|
||||
case reflect.Bool:
|
||||
x.linef("%s = %s(r.DecodeBool())", varname, xfn(t))
|
||||
x.linef("%s = r.DecodeBool()", varname)
|
||||
case reflect.String:
|
||||
x.linef("%s = %s(r.DecodeString())", varname, xfn(t))
|
||||
x.linef("%s = r.DecodeString()", varname)
|
||||
default:
|
||||
tryAsPtr = true
|
||||
}
|
||||
|
|
@ -1249,6 +1323,14 @@ func (x *genRunner) decTryAssignPrimitive(varname string, t reflect.Type) (tryAs
|
|||
}
|
||||
|
||||
func (x *genRunner) decListFallback(varname string, rtid uintptr, t reflect.Type) {
|
||||
if t.AssignableTo(uint8SliceTyp) {
|
||||
x.line("*" + varname + " = r.DecodeBytes(*((*[]byte)(" + varname + ")), false, false)")
|
||||
return
|
||||
}
|
||||
if t.Kind() == reflect.Array && t.Elem().Kind() == reflect.Uint8 {
|
||||
x.linef("r.DecodeBytes( ((*[%s]byte)(%s))[:], false, true)", t.Len(), varname)
|
||||
return
|
||||
}
|
||||
type tstruc struct {
|
||||
TempVar string
|
||||
Rand string
|
||||
|
|
@ -1364,7 +1446,7 @@ func (x *genRunner) decStructMapSwitch(kName string, varname string, rtid uintpt
|
|||
if si.i != -1 {
|
||||
t2 = t.Field(int(si.i))
|
||||
} else {
|
||||
//we must accomodate anonymous fields, where the embedded field is a nil pointer in the value.
|
||||
//we must accommodate anonymous fields, where the embedded field is a nil pointer in the value.
|
||||
// t2 = t.FieldByIndex(si.is)
|
||||
t2typ := t
|
||||
varname3 := varname
|
||||
|
|
@ -1452,7 +1534,7 @@ func (x *genRunner) decStructArray(varname, lenvarname, breakString string, rtid
|
|||
if si.i != -1 {
|
||||
t2 = t.Field(int(si.i))
|
||||
} else {
|
||||
//we must accomodate anonymous fields, where the embedded field is a nil pointer in the value.
|
||||
//we must accommodate anonymous fields, where the embedded field is a nil pointer in the value.
|
||||
// t2 = t.FieldByIndex(si.is)
|
||||
t2typ := t
|
||||
varname3 := varname
|
||||
|
|
@ -1569,8 +1651,6 @@ func (x *genV) MethodNamePfx(prefix string, prim bool) string {
|
|||
|
||||
}
|
||||
|
||||
var genCheckVendor = os.Getenv("GO15VENDOREXPERIMENT") == "1"
|
||||
|
||||
// genImportPath returns import path of a non-predeclared named typed, or an empty string otherwise.
|
||||
//
|
||||
// This handles the misbehaviour that occurs when 1.5-style vendoring is enabled,
|
||||
|
|
@ -1592,6 +1672,26 @@ func genImportPath(t reflect.Type) (s string) {
|
|||
return
|
||||
}
|
||||
|
||||
// A go identifier is (letter|_)[letter|number|_]*
|
||||
func genGoIdentifier(s string, checkFirstChar bool) string {
|
||||
b := make([]byte, 0, len(s))
|
||||
t := make([]byte, 4)
|
||||
var n int
|
||||
for i, r := range s {
|
||||
if checkFirstChar && i == 0 && !unicode.IsLetter(r) {
|
||||
b = append(b, '_')
|
||||
}
|
||||
// r must be unicode_letter, unicode_digit or _
|
||||
if unicode.IsLetter(r) || unicode.IsDigit(r) {
|
||||
n = utf8.EncodeRune(t, r)
|
||||
b = append(b, t[:n]...)
|
||||
} else {
|
||||
b = append(b, '_')
|
||||
}
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func genNonPtr(t reflect.Type) reflect.Type {
|
||||
for t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
|
|
@ -1601,7 +1701,7 @@ func genNonPtr(t reflect.Type) reflect.Type {
|
|||
|
||||
func genTitleCaseName(s string) string {
|
||||
switch s {
|
||||
case "interface{}":
|
||||
case "interface{}", "interface {}":
|
||||
return "Intf"
|
||||
default:
|
||||
return strings.ToUpper(s[0:1]) + s[1:]
|
||||
|
|
@ -1704,7 +1804,7 @@ func (x genInternal) FastpathLen() (l int) {
|
|||
|
||||
func genInternalZeroValue(s string) string {
|
||||
switch s {
|
||||
case "interface{}":
|
||||
case "interface{}", "interface {}":
|
||||
return "nil"
|
||||
case "bool":
|
||||
return "false"
|
||||
|
|
@ -1856,7 +1956,7 @@ func genInternalInit() {
|
|||
}
|
||||
var gt genInternal
|
||||
|
||||
// For each slice or map type, there must be a (symetrical) Encode and Decode fast-path function
|
||||
// For each slice or map type, there must be a (symmetrical) Encode and Decode fast-path function
|
||||
for _, s := range types {
|
||||
gt.Values = append(gt.Values, genV{Primitive: s, Size: mapvaltypes2[s]})
|
||||
if s != "uint8" { // do not generate fast path for slice of bytes. Treat specially already.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
|
||||
// Use of this source code is governed by a MIT license found in the LICENSE file.
|
||||
|
||||
// +build go1.5,!go1.6
|
||||
|
||||
package codec
|
||||
|
||||
import "os"
|
||||
|
||||
func init() {
|
||||
genCheckVendor = os.Getenv("GO15VENDOREXPERIMENT") == "1"
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
|
||||
// Use of this source code is governed by a MIT license found in the LICENSE file.
|
||||
|
||||
// +build go1.6
|
||||
|
||||
package codec
|
||||
|
||||
import "os"
|
||||
|
||||
func init() {
|
||||
genCheckVendor = os.Getenv("GO15VENDOREXPERIMENT") != "0"
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
|
||||
// Use of this source code is governed by a MIT license found in the LICENSE file.
|
||||
|
||||
// +build go1.7
|
||||
|
||||
package codec
|
||||
|
||||
func init() {
|
||||
genCheckVendor = true
|
||||
}
|
||||
|
|
@ -38,10 +38,6 @@ package codec
|
|||
// a length prefix, or if it used explicit breaks. If length-prefixed, we assume that
|
||||
// it has to be binary, and we do not even try to read separators.
|
||||
//
|
||||
// The only codec that may suffer (slightly) is cbor, and only when decoding indefinite-length.
|
||||
// It may suffer because we treat it like a text-based codec, and read separators.
|
||||
// However, this read is a no-op and the cost is insignificant.
|
||||
//
|
||||
// Philosophy
|
||||
// ------------
|
||||
// On decode, this codec will update containers appropriately:
|
||||
|
|
@ -137,17 +133,6 @@ const (
|
|||
// Note that this will always cause rpc tests to fail, since they need io.EOF sent via panic.
|
||||
recoverPanicToErr = true
|
||||
|
||||
// Fast path functions try to create a fast path encode or decode implementation
|
||||
// for common maps and slices, by by-passing reflection altogether.
|
||||
fastpathEnabled = true
|
||||
|
||||
// if checkStructForEmptyValue, check structs fields to see if an empty value.
|
||||
// This could be an expensive call, so possibly disable it.
|
||||
checkStructForEmptyValue = false
|
||||
|
||||
// if derefForIsEmptyValue, deref pointers and interfaces when checking isEmptyValue
|
||||
derefForIsEmptyValue = false
|
||||
|
||||
// if resetSliceElemToZeroValue, then on decoding a slice, reset the element to a zero value first.
|
||||
// Only concern is that, if the slice already contained some garbage, we will decode into that garbage.
|
||||
// The chances of this are slim, so leave this "optimization".
|
||||
|
|
@ -155,8 +140,10 @@ const (
|
|||
resetSliceElemToZeroValue bool = false
|
||||
)
|
||||
|
||||
var oneByteArr = [1]byte{0}
|
||||
var zeroByteSlice = oneByteArr[:0:0]
|
||||
var (
|
||||
oneByteArr = [1]byte{0}
|
||||
zeroByteSlice = oneByteArr[:0:0]
|
||||
)
|
||||
|
||||
type charEncoding uint8
|
||||
|
||||
|
|
@ -215,6 +202,41 @@ const (
|
|||
containerArrayEnd
|
||||
)
|
||||
|
||||
// sfiIdx used for tracking where a (field/enc)Name is seen in a []*structFieldInfo
|
||||
type sfiIdx struct {
|
||||
name string
|
||||
index int
|
||||
}
|
||||
|
||||
// do not recurse if a containing type refers to an embedded type
|
||||
// which refers back to its containing type (via a pointer).
|
||||
// The second time this back-reference happens, break out,
|
||||
// so as not to cause an infinite loop.
|
||||
const rgetMaxRecursion = 2
|
||||
|
||||
// Anecdotally, we believe most types have <= 12 fields.
|
||||
// Java's PMD rules set TooManyFields threshold to 15.
|
||||
const rgetPoolTArrayLen = 12
|
||||
|
||||
type rgetT struct {
|
||||
fNames []string
|
||||
encNames []string
|
||||
etypes []uintptr
|
||||
sfis []*structFieldInfo
|
||||
}
|
||||
|
||||
type rgetPoolT struct {
|
||||
fNames [rgetPoolTArrayLen]string
|
||||
encNames [rgetPoolTArrayLen]string
|
||||
etypes [rgetPoolTArrayLen]uintptr
|
||||
sfis [rgetPoolTArrayLen]*structFieldInfo
|
||||
sfiidx [rgetPoolTArrayLen]sfiIdx
|
||||
}
|
||||
|
||||
var rgetPool = sync.Pool{
|
||||
New: func() interface{} { return new(rgetPoolT) },
|
||||
}
|
||||
|
||||
type containerStateRecv interface {
|
||||
sendContainerState(containerState)
|
||||
}
|
||||
|
|
@ -240,6 +262,7 @@ var (
|
|||
stringTyp = reflect.TypeOf("")
|
||||
timeTyp = reflect.TypeOf(time.Time{})
|
||||
rawExtTyp = reflect.TypeOf(RawExt{})
|
||||
rawTyp = reflect.TypeOf(Raw{})
|
||||
uint8SliceTyp = reflect.TypeOf([]uint8(nil))
|
||||
|
||||
mapBySliceTyp = reflect.TypeOf((*MapBySlice)(nil)).Elem()
|
||||
|
|
@ -257,6 +280,7 @@ var (
|
|||
|
||||
uint8SliceTypId = reflect.ValueOf(uint8SliceTyp).Pointer()
|
||||
rawExtTypId = reflect.ValueOf(rawExtTyp).Pointer()
|
||||
rawTypId = reflect.ValueOf(rawTyp).Pointer()
|
||||
intfTypId = reflect.ValueOf(intfTyp).Pointer()
|
||||
timeTypId = reflect.ValueOf(timeTyp).Pointer()
|
||||
stringTypId = reflect.ValueOf(stringTyp).Pointer()
|
||||
|
|
@ -337,6 +361,11 @@ type Handle interface {
|
|||
isBinary() bool
|
||||
}
|
||||
|
||||
// Raw represents raw formatted bytes.
|
||||
// We "blindly" store it during encode and store the raw bytes during decode.
|
||||
// Note: it is dangerous during encode, so we may gate the behaviour behind an Encode flag which must be explicitly set.
|
||||
type Raw []byte
|
||||
|
||||
// RawExt represents raw unprocessed extension data.
|
||||
// Some codecs will decode extension data as a *RawExt if there is no registered extension for the tag.
|
||||
//
|
||||
|
|
@ -347,7 +376,7 @@ type RawExt struct {
|
|||
// Data is used by codecs (e.g. binc, msgpack, simple) which do custom serialization of the types
|
||||
Data []byte
|
||||
// Value represents the extension, if Data is nil.
|
||||
// Value is used by codecs (e.g. cbor) which use the format to do custom serialization of the types.
|
||||
// Value is used by codecs (e.g. cbor, json) which use the format to do custom serialization of the types.
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
|
|
@ -525,7 +554,7 @@ func (o *extHandle) AddExt(
|
|||
func (o *extHandle) SetExt(rt reflect.Type, tag uint64, ext Ext) (err error) {
|
||||
// o is a pointer, because we may need to initialize it
|
||||
if rt.PkgPath() == "" || rt.Kind() == reflect.Interface {
|
||||
err = fmt.Errorf("codec.Handle.AddExt: Takes named type, especially not a pointer or interface: %T",
|
||||
err = fmt.Errorf("codec.Handle.AddExt: Takes named type, not a pointer or interface: %T",
|
||||
reflect.Zero(rt).Interface())
|
||||
return
|
||||
}
|
||||
|
|
@ -568,7 +597,8 @@ func (o extHandle) getExtForTag(tag uint64) *extTypeTagFn {
|
|||
}
|
||||
|
||||
type structFieldInfo struct {
|
||||
encName string // encode name
|
||||
encName string // encode name
|
||||
fieldName string // field name
|
||||
|
||||
// only one of 'i' or 'is' can be set. If 'i' is -1, then 'is' has been set.
|
||||
|
||||
|
|
@ -714,6 +744,7 @@ type typeInfo struct {
|
|||
}
|
||||
|
||||
func (ti *typeInfo) indexForEncName(name string) int {
|
||||
// NOTE: name may be a stringView, so don't pass it to another function.
|
||||
//tisfi := ti.sfi
|
||||
const binarySearchThreshold = 16
|
||||
if sfilen := len(ti.sfi); sfilen < binarySearchThreshold {
|
||||
|
|
@ -828,19 +859,19 @@ func (x *TypeInfos) get(rtid uintptr, rt reflect.Type) (pti *typeInfo) {
|
|||
}
|
||||
|
||||
if rt.Kind() == reflect.Struct {
|
||||
var siInfo *structFieldInfo
|
||||
var omitEmpty bool
|
||||
if f, ok := rt.FieldByName(structInfoFieldName); ok {
|
||||
siInfo = parseStructFieldInfo(structInfoFieldName, x.structTag(f.Tag))
|
||||
siInfo := parseStructFieldInfo(structInfoFieldName, x.structTag(f.Tag))
|
||||
ti.toArray = siInfo.toArray
|
||||
omitEmpty = siInfo.omitEmpty
|
||||
}
|
||||
sfip := make([]*structFieldInfo, 0, rt.NumField())
|
||||
x.rget(rt, nil, make(map[string]bool, 16), &sfip, siInfo)
|
||||
|
||||
ti.sfip = make([]*structFieldInfo, len(sfip))
|
||||
ti.sfi = make([]*structFieldInfo, len(sfip))
|
||||
copy(ti.sfip, sfip)
|
||||
sort.Sort(sfiSortedByEncName(sfip))
|
||||
copy(ti.sfi, sfip)
|
||||
pi := rgetPool.Get()
|
||||
pv := pi.(*rgetPoolT)
|
||||
pv.etypes[0] = ti.baseId
|
||||
vv := rgetT{pv.fNames[:0], pv.encNames[:0], pv.etypes[:1], pv.sfis[:0]}
|
||||
x.rget(rt, rtid, omitEmpty, nil, &vv)
|
||||
ti.sfip, ti.sfi = rgetResolveSFI(vv.sfis, pv.sfiidx[:0])
|
||||
rgetPool.Put(pi)
|
||||
}
|
||||
// sfi = sfip
|
||||
|
||||
|
|
@ -853,17 +884,30 @@ func (x *TypeInfos) get(rtid uintptr, rt reflect.Type) (pti *typeInfo) {
|
|||
return
|
||||
}
|
||||
|
||||
func (x *TypeInfos) rget(rt reflect.Type, indexstack []int, fnameToHastag map[string]bool,
|
||||
sfi *[]*structFieldInfo, siInfo *structFieldInfo,
|
||||
func (x *TypeInfos) rget(rt reflect.Type, rtid uintptr, omitEmpty bool,
|
||||
indexstack []int, pv *rgetT,
|
||||
) {
|
||||
for j := 0; j < rt.NumField(); j++ {
|
||||
// Read up fields and store how to access the value.
|
||||
//
|
||||
// It uses go's rules for message selectors,
|
||||
// which say that the field with the shallowest depth is selected.
|
||||
//
|
||||
// Note: we consciously use slices, not a map, to simulate a set.
|
||||
// Typically, types have < 16 fields,
|
||||
// and iteration using equals is faster than maps there
|
||||
|
||||
LOOP:
|
||||
for j, jlen := 0, rt.NumField(); j < jlen; j++ {
|
||||
f := rt.Field(j)
|
||||
fkind := f.Type.Kind()
|
||||
// skip if a func type, or is unexported, or structTag value == "-"
|
||||
if fkind == reflect.Func {
|
||||
continue
|
||||
switch fkind {
|
||||
case reflect.Func, reflect.Complex64, reflect.Complex128, reflect.UnsafePointer:
|
||||
continue LOOP
|
||||
}
|
||||
// if r1, _ := utf8.DecodeRuneInString(f.Name); r1 == utf8.RuneError || !unicode.IsUpper(r1) {
|
||||
|
||||
// if r1, _ := utf8.DecodeRuneInString(f.Name);
|
||||
// r1 == utf8.RuneError || !unicode.IsUpper(r1) {
|
||||
if f.PkgPath != "" && !f.Anonymous { // unexported, not embedded
|
||||
continue
|
||||
}
|
||||
|
|
@ -872,7 +916,8 @@ func (x *TypeInfos) rget(rt reflect.Type, indexstack []int, fnameToHastag map[st
|
|||
continue
|
||||
}
|
||||
var si *structFieldInfo
|
||||
// if anonymous and no struct tag (or it's blank), and a struct (or pointer to struct), inline it.
|
||||
// if anonymous and no struct tag (or it's blank),
|
||||
// and a struct (or pointer to struct), inline it.
|
||||
if f.Anonymous && fkind != reflect.Interface {
|
||||
doInline := stag == ""
|
||||
if !doInline {
|
||||
|
|
@ -886,11 +931,31 @@ func (x *TypeInfos) rget(rt reflect.Type, indexstack []int, fnameToHastag map[st
|
|||
ft = ft.Elem()
|
||||
}
|
||||
if ft.Kind() == reflect.Struct {
|
||||
indexstack2 := make([]int, len(indexstack)+1, len(indexstack)+4)
|
||||
copy(indexstack2, indexstack)
|
||||
indexstack2[len(indexstack)] = j
|
||||
// indexstack2 := append(append(make([]int, 0, len(indexstack)+4), indexstack...), j)
|
||||
x.rget(ft, indexstack2, fnameToHastag, sfi, siInfo)
|
||||
// if etypes contains this, don't call rget again (as fields are already seen here)
|
||||
ftid := reflect.ValueOf(ft).Pointer()
|
||||
// We cannot recurse forever, but we need to track other field depths.
|
||||
// So - we break if we see a type twice (not the first time).
|
||||
// This should be sufficient to handle an embedded type that refers to its
|
||||
// owning type, which then refers to its embedded type.
|
||||
processIt := true
|
||||
numk := 0
|
||||
for _, k := range pv.etypes {
|
||||
if k == ftid {
|
||||
numk++
|
||||
if numk == rgetMaxRecursion {
|
||||
processIt = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if processIt {
|
||||
pv.etypes = append(pv.etypes, ftid)
|
||||
indexstack2 := make([]int, len(indexstack)+1)
|
||||
copy(indexstack2, indexstack)
|
||||
indexstack2[len(indexstack)] = j
|
||||
// indexstack2 := append(append(make([]int, 0, len(indexstack)+4), indexstack...), j)
|
||||
x.rget(ft, ftid, omitEmpty, indexstack2, pv)
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
|
@ -901,36 +966,86 @@ func (x *TypeInfos) rget(rt reflect.Type, indexstack []int, fnameToHastag map[st
|
|||
continue
|
||||
}
|
||||
|
||||
// do not let fields with same name in embedded structs override field at higher level.
|
||||
// this must be done after anonymous check, to allow anonymous field
|
||||
// still include their child fields
|
||||
if _, ok := fnameToHastag[f.Name]; ok {
|
||||
continue
|
||||
}
|
||||
if f.Name == "" {
|
||||
panic(noFieldNameToStructFieldInfoErr)
|
||||
}
|
||||
|
||||
pv.fNames = append(pv.fNames, f.Name)
|
||||
|
||||
if si == nil {
|
||||
si = parseStructFieldInfo(f.Name, stag)
|
||||
} else if si.encName == "" {
|
||||
si.encName = f.Name
|
||||
}
|
||||
si.fieldName = f.Name
|
||||
|
||||
pv.encNames = append(pv.encNames, si.encName)
|
||||
|
||||
// si.ikind = int(f.Type.Kind())
|
||||
if len(indexstack) == 0 {
|
||||
si.i = int16(j)
|
||||
} else {
|
||||
si.i = -1
|
||||
si.is = append(append(make([]int, 0, len(indexstack)+4), indexstack...), j)
|
||||
si.is = make([]int, len(indexstack)+1)
|
||||
copy(si.is, indexstack)
|
||||
si.is[len(indexstack)] = j
|
||||
// si.is = append(append(make([]int, 0, len(indexstack)+4), indexstack...), j)
|
||||
}
|
||||
|
||||
if siInfo != nil {
|
||||
if siInfo.omitEmpty {
|
||||
si.omitEmpty = true
|
||||
if omitEmpty {
|
||||
si.omitEmpty = true
|
||||
}
|
||||
pv.sfis = append(pv.sfis, si)
|
||||
}
|
||||
}
|
||||
|
||||
// resolves the struct field info got from a call to rget.
|
||||
// Returns a trimmed, unsorted and sorted []*structFieldInfo.
|
||||
func rgetResolveSFI(x []*structFieldInfo, pv []sfiIdx) (y, z []*structFieldInfo) {
|
||||
var n int
|
||||
for i, v := range x {
|
||||
xn := v.encName //TODO: fieldName or encName? use encName for now.
|
||||
var found bool
|
||||
for j, k := range pv {
|
||||
if k.name == xn {
|
||||
// one of them must be reset to nil, and the index updated appropriately to the other one
|
||||
if len(v.is) == len(x[k.index].is) {
|
||||
} else if len(v.is) < len(x[k.index].is) {
|
||||
pv[j].index = i
|
||||
if x[k.index] != nil {
|
||||
x[k.index] = nil
|
||||
n++
|
||||
}
|
||||
} else {
|
||||
if x[i] != nil {
|
||||
x[i] = nil
|
||||
n++
|
||||
}
|
||||
}
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
*sfi = append(*sfi, si)
|
||||
fnameToHastag[f.Name] = stag != ""
|
||||
if !found {
|
||||
pv = append(pv, sfiIdx{xn, i})
|
||||
}
|
||||
}
|
||||
|
||||
// remove all the nils
|
||||
y = make([]*structFieldInfo, len(x)-n)
|
||||
n = 0
|
||||
for _, v := range x {
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
y[n] = v
|
||||
n++
|
||||
}
|
||||
|
||||
z = make([]*structFieldInfo, len(y))
|
||||
copy(z, y)
|
||||
sort.Sort(sfiSortedByEncName(z))
|
||||
return
|
||||
}
|
||||
|
||||
func panicToErr(err *error) {
|
||||
|
|
@ -1127,3 +1242,73 @@ type bytesISlice []bytesI
|
|||
func (p bytesISlice) Len() int { return len(p) }
|
||||
func (p bytesISlice) Less(i, j int) bool { return bytes.Compare(p[i].v, p[j].v) == -1 }
|
||||
func (p bytesISlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||
|
||||
// -----------------
|
||||
|
||||
type set []uintptr
|
||||
|
||||
func (s *set) add(v uintptr) (exists bool) {
|
||||
// e.ci is always nil, or len >= 1
|
||||
// defer func() { fmt.Printf("$$$$$$$$$$$ cirRef Add: %v, exists: %v\n", v, exists) }()
|
||||
x := *s
|
||||
if x == nil {
|
||||
x = make([]uintptr, 1, 8)
|
||||
x[0] = v
|
||||
*s = x
|
||||
return
|
||||
}
|
||||
// typically, length will be 1. make this perform.
|
||||
if len(x) == 1 {
|
||||
if j := x[0]; j == 0 {
|
||||
x[0] = v
|
||||
} else if j == v {
|
||||
exists = true
|
||||
} else {
|
||||
x = append(x, v)
|
||||
*s = x
|
||||
}
|
||||
return
|
||||
}
|
||||
// check if it exists
|
||||
for _, j := range x {
|
||||
if j == v {
|
||||
exists = true
|
||||
return
|
||||
}
|
||||
}
|
||||
// try to replace a "deleted" slot
|
||||
for i, j := range x {
|
||||
if j == 0 {
|
||||
x[i] = v
|
||||
return
|
||||
}
|
||||
}
|
||||
// if unable to replace deleted slot, just append it.
|
||||
x = append(x, v)
|
||||
*s = x
|
||||
return
|
||||
}
|
||||
|
||||
func (s *set) remove(v uintptr) (exists bool) {
|
||||
// defer func() { fmt.Printf("$$$$$$$$$$$ cirRef Rm: %v, exists: %v\n", v, exists) }()
|
||||
x := *s
|
||||
if len(x) == 0 {
|
||||
return
|
||||
}
|
||||
if len(x) == 1 {
|
||||
if x[0] == v {
|
||||
x[0] = 0
|
||||
}
|
||||
return
|
||||
}
|
||||
for i, j := range x {
|
||||
if j == v {
|
||||
exists = true
|
||||
x[i] = 0 // set it to 0, as way to delete it.
|
||||
// copy(x[i:], x[i+1:])
|
||||
// x = x[:len(x)-1]
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,8 +70,8 @@ func hIsEmptyValue(v reflect.Value, deref, checkStruct bool) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func isEmptyValue(v reflect.Value) bool {
|
||||
return hIsEmptyValue(v, derefForIsEmptyValue, checkStructForEmptyValue)
|
||||
func isEmptyValue(v reflect.Value, deref, checkStruct bool) bool {
|
||||
return hIsEmptyValue(v, deref, checkStruct)
|
||||
}
|
||||
|
||||
func pruneSignExt(v []byte, pos bool) (n int) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//+build !unsafe
|
||||
// +build !unsafe
|
||||
|
||||
// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
|
||||
// Use of this source code is governed by a MIT license found in the LICENSE file.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//+build unsafe
|
||||
// +build unsafe
|
||||
|
||||
// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
|
||||
// Use of this source code is governed by a MIT license found in the LICENSE file.
|
||||
|
|
@ -16,7 +16,7 @@ type unsafeString struct {
|
|||
Len int
|
||||
}
|
||||
|
||||
type unsafeBytes struct {
|
||||
type unsafeSlice struct {
|
||||
Data uintptr
|
||||
Len int
|
||||
Cap int
|
||||
|
|
@ -29,8 +29,10 @@ func stringView(v []byte) string {
|
|||
if len(v) == 0 {
|
||||
return ""
|
||||
}
|
||||
x := unsafeString{uintptr(unsafe.Pointer(&v[0])), len(v)}
|
||||
return *(*string)(unsafe.Pointer(&x))
|
||||
|
||||
bx := (*unsafeSlice)(unsafe.Pointer(&v))
|
||||
sx := unsafeString{bx.Data, bx.Len}
|
||||
return *(*string)(unsafe.Pointer(&sx))
|
||||
}
|
||||
|
||||
// bytesView returns a view of the string as a []byte.
|
||||
|
|
@ -40,6 +42,8 @@ func bytesView(v string) []byte {
|
|||
if len(v) == 0 {
|
||||
return zeroByteSlice
|
||||
}
|
||||
x := unsafeBytes{uintptr(unsafe.Pointer(&v)), len(v), len(v)}
|
||||
return *(*[]byte)(unsafe.Pointer(&x))
|
||||
|
||||
sx := (*unsafeString)(unsafe.Pointer(&v))
|
||||
bx := unsafeSlice{sx.Data, sx.Len, sx.Len}
|
||||
return *(*[]byte)(unsafe.Pointer(&bx))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,18 +43,23 @@ import (
|
|||
|
||||
//--------------------------------
|
||||
|
||||
var jsonLiterals = [...]byte{'t', 'r', 'u', 'e', 'f', 'a', 'l', 's', 'e', 'n', 'u', 'l', 'l'}
|
||||
var (
|
||||
jsonLiterals = [...]byte{'t', 'r', 'u', 'e', 'f', 'a', 'l', 's', 'e', 'n', 'u', 'l', 'l'}
|
||||
|
||||
var jsonFloat64Pow10 = [...]float64{
|
||||
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
|
||||
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
|
||||
1e20, 1e21, 1e22,
|
||||
}
|
||||
jsonFloat64Pow10 = [...]float64{
|
||||
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
|
||||
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
|
||||
1e20, 1e21, 1e22,
|
||||
}
|
||||
|
||||
var jsonUint64Pow10 = [...]uint64{
|
||||
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
|
||||
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
|
||||
}
|
||||
jsonUint64Pow10 = [...]uint64{
|
||||
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
|
||||
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
|
||||
}
|
||||
|
||||
// jsonTabs and jsonSpaces are used as caches for indents
|
||||
jsonTabs, jsonSpaces string
|
||||
)
|
||||
|
||||
const (
|
||||
// jsonUnreadAfterDecNum controls whether we unread after decoding a number.
|
||||
|
|
@ -85,8 +90,23 @@ const (
|
|||
jsonNumUintMaxVal = 1<<uint64(64) - 1
|
||||
|
||||
// jsonNumDigitsUint64Largest = 19
|
||||
|
||||
jsonSpacesOrTabsLen = 128
|
||||
)
|
||||
|
||||
func init() {
|
||||
var bs [jsonSpacesOrTabsLen]byte
|
||||
for i := 0; i < jsonSpacesOrTabsLen; i++ {
|
||||
bs[i] = ' '
|
||||
}
|
||||
jsonSpaces = string(bs[:])
|
||||
|
||||
for i := 0; i < jsonSpacesOrTabsLen; i++ {
|
||||
bs[i] = '\t'
|
||||
}
|
||||
jsonTabs = string(bs[:])
|
||||
}
|
||||
|
||||
type jsonEncDriver struct {
|
||||
e *Encoder
|
||||
w encWriter
|
||||
|
|
@ -94,30 +114,76 @@ type jsonEncDriver struct {
|
|||
b [64]byte // scratch
|
||||
bs []byte // scratch
|
||||
se setExtWrapper
|
||||
ds string // indent string
|
||||
dl uint16 // indent level
|
||||
dt bool // indent using tabs
|
||||
d bool // indent
|
||||
c containerState
|
||||
noBuiltInTypes
|
||||
}
|
||||
|
||||
// indent is done as below:
|
||||
// - newline and indent are added before each mapKey or arrayElem
|
||||
// - newline and indent are added before each ending,
|
||||
// except there was no entry (so we can have {} or [])
|
||||
|
||||
func (e *jsonEncDriver) sendContainerState(c containerState) {
|
||||
// determine whether to output separators
|
||||
if c == containerMapKey {
|
||||
if e.c != containerMapStart {
|
||||
e.w.writen1(',')
|
||||
}
|
||||
if e.d {
|
||||
e.writeIndent()
|
||||
}
|
||||
} else if c == containerMapValue {
|
||||
e.w.writen1(':')
|
||||
if e.d {
|
||||
e.w.writen2(':', ' ')
|
||||
} else {
|
||||
e.w.writen1(':')
|
||||
}
|
||||
} else if c == containerMapEnd {
|
||||
if e.d {
|
||||
e.dl--
|
||||
if e.c != containerMapStart {
|
||||
e.writeIndent()
|
||||
}
|
||||
}
|
||||
e.w.writen1('}')
|
||||
} else if c == containerArrayElem {
|
||||
if e.c != containerArrayStart {
|
||||
e.w.writen1(',')
|
||||
}
|
||||
if e.d {
|
||||
e.writeIndent()
|
||||
}
|
||||
} else if c == containerArrayEnd {
|
||||
if e.d {
|
||||
e.dl--
|
||||
if e.c != containerArrayStart {
|
||||
e.writeIndent()
|
||||
}
|
||||
}
|
||||
e.w.writen1(']')
|
||||
}
|
||||
e.c = c
|
||||
}
|
||||
|
||||
func (e *jsonEncDriver) writeIndent() {
|
||||
e.w.writen1('\n')
|
||||
if x := len(e.ds) * int(e.dl); x <= jsonSpacesOrTabsLen {
|
||||
if e.dt {
|
||||
e.w.writestr(jsonTabs[:x])
|
||||
} else {
|
||||
e.w.writestr(jsonSpaces[:x])
|
||||
}
|
||||
} else {
|
||||
for i := uint16(0); i < e.dl; i++ {
|
||||
e.w.writestr(e.ds)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (e *jsonEncDriver) EncodeNil() {
|
||||
e.w.writeb(jsonLiterals[9:13]) // null
|
||||
}
|
||||
|
|
@ -131,19 +197,39 @@ func (e *jsonEncDriver) EncodeBool(b bool) {
|
|||
}
|
||||
|
||||
func (e *jsonEncDriver) EncodeFloat32(f float32) {
|
||||
e.w.writeb(strconv.AppendFloat(e.b[:0], float64(f), 'E', -1, 32))
|
||||
e.encodeFloat(float64(f), 32)
|
||||
}
|
||||
|
||||
func (e *jsonEncDriver) EncodeFloat64(f float64) {
|
||||
// e.w.writestr(strconv.FormatFloat(f, 'E', -1, 64))
|
||||
e.w.writeb(strconv.AppendFloat(e.b[:0], f, 'E', -1, 64))
|
||||
e.encodeFloat(f, 64)
|
||||
}
|
||||
|
||||
func (e *jsonEncDriver) encodeFloat(f float64, numbits int) {
|
||||
x := strconv.AppendFloat(e.b[:0], f, 'G', -1, numbits)
|
||||
e.w.writeb(x)
|
||||
if bytes.IndexByte(x, 'E') == -1 && bytes.IndexByte(x, '.') == -1 {
|
||||
e.w.writen2('.', '0')
|
||||
}
|
||||
}
|
||||
|
||||
func (e *jsonEncDriver) EncodeInt(v int64) {
|
||||
if x := e.h.IntegerAsString; x == 'A' || x == 'L' && (v > 1<<53 || v < -(1<<53)) {
|
||||
e.w.writen1('"')
|
||||
e.w.writeb(strconv.AppendInt(e.b[:0], v, 10))
|
||||
e.w.writen1('"')
|
||||
return
|
||||
}
|
||||
e.w.writeb(strconv.AppendInt(e.b[:0], v, 10))
|
||||
}
|
||||
|
||||
func (e *jsonEncDriver) EncodeUint(v uint64) {
|
||||
if x := e.h.IntegerAsString; x == 'A' || x == 'L' && v > 1<<53 {
|
||||
e.w.writen1('"')
|
||||
e.w.writeb(strconv.AppendUint(e.b[:0], v, 10))
|
||||
e.w.writen1('"')
|
||||
return
|
||||
}
|
||||
e.w.writeb(strconv.AppendUint(e.b[:0], v, 10))
|
||||
}
|
||||
|
||||
|
|
@ -165,11 +251,17 @@ func (e *jsonEncDriver) EncodeRawExt(re *RawExt, en *Encoder) {
|
|||
}
|
||||
|
||||
func (e *jsonEncDriver) EncodeArrayStart(length int) {
|
||||
if e.d {
|
||||
e.dl++
|
||||
}
|
||||
e.w.writen1('[')
|
||||
e.c = containerArrayStart
|
||||
}
|
||||
|
||||
func (e *jsonEncDriver) EncodeMapStart(length int) {
|
||||
if e.d {
|
||||
e.dl++
|
||||
}
|
||||
e.w.writen1('{')
|
||||
e.c = containerMapStart
|
||||
}
|
||||
|
|
@ -564,6 +656,11 @@ func (d *jsonDecDriver) decNum(storeBytes bool) {
|
|||
d.tok = b
|
||||
}
|
||||
b := d.tok
|
||||
var str bool
|
||||
if b == '"' {
|
||||
str = true
|
||||
b = d.r.readn1()
|
||||
}
|
||||
if !(b == '+' || b == '-' || b == '.' || (b >= '0' && b <= '9')) {
|
||||
d.d.errorf("json: decNum: got first char '%c'", b)
|
||||
return
|
||||
|
|
@ -578,6 +675,10 @@ func (d *jsonDecDriver) decNum(storeBytes bool) {
|
|||
n.reset()
|
||||
d.bs = d.bs[:0]
|
||||
|
||||
if str && storeBytes {
|
||||
d.bs = append(d.bs, '"')
|
||||
}
|
||||
|
||||
// The format of a number is as below:
|
||||
// parsing: sign? digit* dot? digit* e? sign? digit*
|
||||
// states: 0 1* 2 3* 4 5* 6 7
|
||||
|
|
@ -668,6 +769,14 @@ LOOP:
|
|||
default:
|
||||
break LOOP
|
||||
}
|
||||
case '"':
|
||||
if str {
|
||||
if storeBytes {
|
||||
d.bs = append(d.bs, '"')
|
||||
}
|
||||
b, eof = r.readn1eof()
|
||||
}
|
||||
break LOOP
|
||||
default:
|
||||
break LOOP
|
||||
}
|
||||
|
|
@ -822,6 +931,11 @@ func (d *jsonDecDriver) DecodeBytes(bs []byte, isstring, zerocopy bool) (bsOut [
|
|||
if isstring {
|
||||
return d.bs
|
||||
}
|
||||
// if appendStringAsBytes returned a zero-len slice, then treat as nil.
|
||||
// This should only happen for null, and "".
|
||||
if len(d.bs) == 0 {
|
||||
return nil
|
||||
}
|
||||
bs0 := d.bs
|
||||
slen := base64.StdEncoding.DecodedLen(len(bs0))
|
||||
if slen <= cap(bs) {
|
||||
|
|
@ -859,6 +973,14 @@ func (d *jsonDecDriver) appendStringAsBytes() {
|
|||
}
|
||||
d.tok = b
|
||||
}
|
||||
|
||||
// handle null as a string
|
||||
if d.tok == 'n' {
|
||||
d.readStrIdx(10, 13) // ull
|
||||
d.bs = d.bs[:0]
|
||||
return
|
||||
}
|
||||
|
||||
if d.tok != '"' {
|
||||
d.d.errorf("json: expect char '%c' but got char '%c'", '"', d.tok)
|
||||
}
|
||||
|
|
@ -1033,6 +1155,24 @@ type JsonHandle struct {
|
|||
// RawBytesExt, if configured, is used to encode and decode raw bytes in a custom way.
|
||||
// If not configured, raw bytes are encoded to/from base64 text.
|
||||
RawBytesExt InterfaceExt
|
||||
|
||||
// Indent indicates how a value is encoded.
|
||||
// - If positive, indent by that number of spaces.
|
||||
// - If negative, indent by that number of tabs.
|
||||
Indent int8
|
||||
|
||||
// IntegerAsString controls how integers (signed and unsigned) are encoded.
|
||||
//
|
||||
// Per the JSON Spec, JSON numbers are 64-bit floating point numbers.
|
||||
// Consequently, integers > 2^53 cannot be represented as a JSON number without losing precision.
|
||||
// This can be mitigated by configuring how to encode integers.
|
||||
//
|
||||
// IntegerAsString interpretes the following values:
|
||||
// - if 'L', then encode integers > 2^53 as a json string.
|
||||
// - if 'A', then encode all integers as a json string
|
||||
// containing the exact integer representation as a decimal.
|
||||
// - else encode all integers as a json number (default)
|
||||
IntegerAsString uint8
|
||||
}
|
||||
|
||||
func (h *JsonHandle) SetInterfaceExt(rt reflect.Type, tag uint64, ext InterfaceExt) (err error) {
|
||||
|
|
@ -1040,26 +1180,48 @@ func (h *JsonHandle) SetInterfaceExt(rt reflect.Type, tag uint64, ext InterfaceE
|
|||
}
|
||||
|
||||
func (h *JsonHandle) newEncDriver(e *Encoder) encDriver {
|
||||
hd := jsonEncDriver{e: e, w: e.w, h: h}
|
||||
hd := jsonEncDriver{e: e, h: h}
|
||||
hd.bs = hd.b[:0]
|
||||
hd.se.i = h.RawBytesExt
|
||||
|
||||
hd.reset()
|
||||
|
||||
return &hd
|
||||
}
|
||||
|
||||
func (h *JsonHandle) newDecDriver(d *Decoder) decDriver {
|
||||
// d := jsonDecDriver{r: r.(*bytesDecReader), h: h}
|
||||
hd := jsonDecDriver{d: d, r: d.r, h: h}
|
||||
hd := jsonDecDriver{d: d, h: h}
|
||||
hd.bs = hd.b[:0]
|
||||
hd.se.i = h.RawBytesExt
|
||||
hd.reset()
|
||||
return &hd
|
||||
}
|
||||
|
||||
func (e *jsonEncDriver) reset() {
|
||||
e.w = e.e.w
|
||||
e.se.i = e.h.RawBytesExt
|
||||
if e.bs != nil {
|
||||
e.bs = e.bs[:0]
|
||||
}
|
||||
e.d, e.dt, e.dl, e.ds = false, false, 0, ""
|
||||
e.c = 0
|
||||
if e.h.Indent > 0 {
|
||||
e.d = true
|
||||
e.ds = jsonSpaces[:e.h.Indent]
|
||||
} else if e.h.Indent < 0 {
|
||||
e.d = true
|
||||
e.dt = true
|
||||
e.ds = jsonTabs[:-(e.h.Indent)]
|
||||
}
|
||||
}
|
||||
|
||||
func (d *jsonDecDriver) reset() {
|
||||
d.r = d.d.r
|
||||
d.se.i = d.h.RawBytesExt
|
||||
if d.bs != nil {
|
||||
d.bs = d.bs[:0]
|
||||
}
|
||||
d.c, d.tok = 0, 0
|
||||
d.n.reset()
|
||||
}
|
||||
|
||||
var jsonEncodeTerminate = []byte{' '}
|
||||
|
|
|
|||
|
|
@ -374,7 +374,7 @@ func (d *msgpackDecDriver) DecodeNaked() {
|
|||
}
|
||||
if n.v == valueTypeUint && d.h.SignedInteger {
|
||||
n.v = valueTypeInt
|
||||
n.i = int64(n.v)
|
||||
n.i = int64(n.u)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
@ -561,6 +561,13 @@ func (d *msgpackDecDriver) readNextBd() {
|
|||
d.bdRead = true
|
||||
}
|
||||
|
||||
func (d *msgpackDecDriver) uncacheRead() {
|
||||
if d.bdRead {
|
||||
d.r.unreadn1()
|
||||
d.bdRead = false
|
||||
}
|
||||
}
|
||||
|
||||
func (d *msgpackDecDriver) ContainerType() (vt valueType) {
|
||||
bd := d.bd
|
||||
if bd == mpNil {
|
||||
|
|
@ -729,6 +736,7 @@ func (e *msgpackEncDriver) reset() {
|
|||
|
||||
func (d *msgpackDecDriver) reset() {
|
||||
d.r = d.d.r
|
||||
d.bd, d.bdRead = 0, false
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ type Rpc interface {
|
|||
}
|
||||
|
||||
// RpcCodecBuffered allows access to the underlying bufio.Reader/Writer
|
||||
// used by the rpc connection. It accomodates use-cases where the connection
|
||||
// used by the rpc connection. It accommodates use-cases where the connection
|
||||
// should be used by rpc and non-rpc functions, e.g. streaming a file after
|
||||
// sending an rpc response.
|
||||
type RpcCodecBuffered interface {
|
||||
|
|
|
|||
|
|
@ -166,6 +166,13 @@ func (d *simpleDecDriver) readNextBd() {
|
|||
d.bdRead = true
|
||||
}
|
||||
|
||||
func (d *simpleDecDriver) uncacheRead() {
|
||||
if d.bdRead {
|
||||
d.r.unreadn1()
|
||||
d.bdRead = false
|
||||
}
|
||||
}
|
||||
|
||||
func (d *simpleDecDriver) ContainerType() (vt valueType) {
|
||||
if d.bd == simpleVdNil {
|
||||
return valueTypeNil
|
||||
|
|
@ -474,7 +481,7 @@ func (d *simpleDecDriver) DecodeNaked() {
|
|||
// SimpleHandle is a Handle for a very simple encoding format.
|
||||
//
|
||||
// simple is a simplistic codec similar to binc, but not as compact.
|
||||
// - Encoding of a value is always preceeded by the descriptor byte (bd)
|
||||
// - Encoding of a value is always preceded by the descriptor byte (bd)
|
||||
// - True, false, nil are encoded fully in 1 byte (the descriptor)
|
||||
// - Integers (intXXX, uintXXX) are encoded in 1, 2, 4 or 8 bytes (plus a descriptor byte).
|
||||
// There are positive (uintXXX and intXXX >= 0) and negative (intXXX < 0) integers.
|
||||
|
|
@ -512,6 +519,7 @@ func (e *simpleEncDriver) reset() {
|
|||
|
||||
func (d *simpleDecDriver) reset() {
|
||||
d.r = d.d.r
|
||||
d.bd, d.bdRead = 0, false
|
||||
}
|
||||
|
||||
var _ decDriver = (*simpleDecDriver)(nil)
|
||||
|
|
|
|||
|
|
@ -5,11 +5,22 @@ package codec
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
timeDigits = [...]byte{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}
|
||||
timeDigits = [...]byte{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}
|
||||
timeExtEncFn = func(rv reflect.Value) (bs []byte, err error) {
|
||||
defer panicToErr(&err)
|
||||
bs = timeExt{}.WriteExt(rv.Interface())
|
||||
return
|
||||
}
|
||||
timeExtDecFn = func(rv reflect.Value, bs []byte) (err error) {
|
||||
defer panicToErr(&err)
|
||||
timeExt{}.ReadExt(rv.Interface(), bs)
|
||||
return
|
||||
}
|
||||
)
|
||||
|
||||
type timeExt struct{}
|
||||
|
|
|
|||
|
|
@ -56,6 +56,11 @@ func (x Int26_6) Round() int { return int((x + 0x20) >> 6) }
|
|||
// Its return type is int, not Int26_6.
|
||||
func (x Int26_6) Ceil() int { return int((x + 0x3f) >> 6) }
|
||||
|
||||
// Mul returns x*y in 26.6 fixed-point arithmetic.
|
||||
func (x Int26_6) Mul(y Int26_6) Int26_6 {
|
||||
return Int26_6((int64(x)*int64(y) + 1<<5) >> 6)
|
||||
}
|
||||
|
||||
// Int52_12 is a signed 52.12 fixed-point number.
|
||||
//
|
||||
// The integer part ranges from -2251799813685248 to 2251799813685247,
|
||||
|
|
@ -95,6 +100,39 @@ func (x Int52_12) Round() int { return int((x + 0x800) >> 12) }
|
|||
// Its return type is int, not Int52_12.
|
||||
func (x Int52_12) Ceil() int { return int((x + 0xfff) >> 12) }
|
||||
|
||||
// Mul returns x*y in 52.12 fixed-point arithmetic.
|
||||
func (x Int52_12) Mul(y Int52_12) Int52_12 {
|
||||
const M, N = 52, 12
|
||||
lo, hi := muli64(int64(x), int64(y))
|
||||
ret := Int52_12(hi<<M | lo>>N)
|
||||
ret += Int52_12((lo >> (N - 1)) & 1) // Round to nearest, instead of rounding down.
|
||||
return ret
|
||||
}
|
||||
|
||||
// muli64 multiplies two int64 values, returning the 128-bit signed integer
|
||||
// result as two uint64 values.
|
||||
//
|
||||
// This implementation is similar to $GOROOT/src/runtime/softfloat64.go's mullu
|
||||
// function, which is in turn adapted from Hacker's Delight.
|
||||
func muli64(u, v int64) (lo, hi uint64) {
|
||||
const (
|
||||
s = 32
|
||||
mask = 1<<s - 1
|
||||
)
|
||||
|
||||
u1 := uint64(u >> s)
|
||||
u0 := uint64(u & mask)
|
||||
v1 := uint64(v >> s)
|
||||
v0 := uint64(v & mask)
|
||||
|
||||
w0 := u0 * v0
|
||||
t := u1*v0 + w0>>s
|
||||
w1 := t & mask
|
||||
w2 := uint64(int64(t) >> s)
|
||||
w1 += u0 * v1
|
||||
return uint64(u) * uint64(v), u1*v1 + w2 + uint64(int64(w1)>>s)
|
||||
}
|
||||
|
||||
// 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}.
|
||||
|
|
|
|||
|
|
@ -118,8 +118,9 @@ func (d *decoder) ifdUint(p []byte) (u []uint, err error) {
|
|||
}
|
||||
|
||||
// parseIFD decides whether the the IFD entry in p is "interesting" and
|
||||
// stows away the data in the decoder.
|
||||
func (d *decoder) parseIFD(p []byte) error {
|
||||
// stows away the data in the decoder. It returns the tag number of the
|
||||
// entry and an error, if any.
|
||||
func (d *decoder) parseIFD(p []byte) (int, error) {
|
||||
tag := d.byteOrder.Uint16(p[0:2])
|
||||
switch tag {
|
||||
case tBitsPerSample,
|
||||
|
|
@ -138,17 +139,17 @@ func (d *decoder) parseIFD(p []byte) error {
|
|||
tImageWidth:
|
||||
val, err := d.ifdUint(p)
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
d.features[int(tag)] = val
|
||||
case tColorMap:
|
||||
val, err := d.ifdUint(p)
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
numcolors := len(val) / 3
|
||||
if len(val)%3 != 0 || numcolors <= 0 || numcolors > 256 {
|
||||
return FormatError("bad ColorMap length")
|
||||
return 0, FormatError("bad ColorMap length")
|
||||
}
|
||||
d.palette = make([]color.Color, numcolors)
|
||||
for i := 0; i < numcolors; i++ {
|
||||
|
|
@ -166,15 +167,15 @@ func (d *decoder) parseIFD(p []byte) error {
|
|||
// must terminate the import process gracefully.
|
||||
val, err := d.ifdUint(p)
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
for _, v := range val {
|
||||
if v != 1 {
|
||||
return UnsupportedError("sample format")
|
||||
return 0, UnsupportedError("sample format")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return int(tag), nil
|
||||
}
|
||||
|
||||
// readBits reads n bits from the internal buffer starting at the current offset.
|
||||
|
|
@ -428,10 +429,16 @@ func newDecoder(r io.Reader) (*decoder, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
prevTag := -1
|
||||
for i := 0; i < len(p); i += ifdLen {
|
||||
if err := d.parseIFD(p[i : i+ifdLen]); err != nil {
|
||||
tag, err := d.parseIFD(p[i : i+ifdLen])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tag <= prevTag {
|
||||
return nil, FormatError("tags are not sorted in ascending order")
|
||||
}
|
||||
prevTag = tag
|
||||
}
|
||||
|
||||
d.config.Width = int(d.firstVal(tImageWidth))
|
||||
|
|
|
|||
|
|
@ -28,7 +28,8 @@ import (
|
|||
// See https://cloud.google.com/appengine/docs/flexible/custom-runtimes#health_check_requests
|
||||
// for details on how to do your own health checking.
|
||||
//
|
||||
// Main is not yet supported on App Engine Standard.
|
||||
// On App Engine Standard it ensures the server has started and is prepared to
|
||||
// receive requests.
|
||||
//
|
||||
// Main never returns.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -32,7 +32,8 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
apiPath = "/rpc_http"
|
||||
apiPath = "/rpc_http"
|
||||
defaultTicketSuffix = "/default.20150612t184001.0"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -60,6 +61,9 @@ var (
|
|||
Dial: limitDial,
|
||||
},
|
||||
}
|
||||
|
||||
defaultTicketOnce sync.Once
|
||||
defaultTicket string
|
||||
)
|
||||
|
||||
func apiURL() *url.URL {
|
||||
|
|
@ -266,6 +270,24 @@ func WithContext(parent netcontext.Context, req *http.Request) netcontext.Contex
|
|||
return withContext(parent, c)
|
||||
}
|
||||
|
||||
// DefaultTicket returns a ticket used for background context or dev_appserver.
|
||||
func DefaultTicket() string {
|
||||
defaultTicketOnce.Do(func() {
|
||||
if IsDevAppServer() {
|
||||
defaultTicket = "testapp" + defaultTicketSuffix
|
||||
return
|
||||
}
|
||||
appID := partitionlessAppID()
|
||||
escAppID := strings.Replace(strings.Replace(appID, ":", "_", -1), ".", "_", -1)
|
||||
majVersion := VersionID(nil)
|
||||
if i := strings.Index(majVersion, "."); i > 0 {
|
||||
majVersion = majVersion[:i]
|
||||
}
|
||||
defaultTicket = fmt.Sprintf("%s/%s.%s.%s", escAppID, ModuleName(nil), majVersion, InstanceID())
|
||||
})
|
||||
return defaultTicket
|
||||
}
|
||||
|
||||
func BackgroundContext() netcontext.Context {
|
||||
ctxs.Lock()
|
||||
defer ctxs.Unlock()
|
||||
|
|
@ -275,13 +297,7 @@ func BackgroundContext() netcontext.Context {
|
|||
}
|
||||
|
||||
// Compute background security ticket.
|
||||
appID := partitionlessAppID()
|
||||
escAppID := strings.Replace(strings.Replace(appID, ":", "_", -1), ".", "_", -1)
|
||||
majVersion := VersionID(nil)
|
||||
if i := strings.Index(majVersion, "."); i > 0 {
|
||||
majVersion = majVersion[:i]
|
||||
}
|
||||
ticket := fmt.Sprintf("%s/%s.%s.%s", escAppID, ModuleName(nil), majVersion, InstanceID())
|
||||
ticket := DefaultTicket()
|
||||
|
||||
ctxs.bg = &context{
|
||||
req: &http.Request{
|
||||
|
|
@ -475,6 +491,16 @@ func Call(ctx netcontext.Context, service, method string, in, out proto.Message)
|
|||
}
|
||||
|
||||
ticket := c.req.Header.Get(ticketHeader)
|
||||
// Use a test ticket under test environment.
|
||||
if ticket == "" {
|
||||
if appid := ctx.Value(&appIDOverrideKey); appid != nil {
|
||||
ticket = appid.(string) + defaultTicketSuffix
|
||||
}
|
||||
}
|
||||
// Fall back to use background ticket when the request ticket is not available in Flex or dev_appserver.
|
||||
if ticket == "" {
|
||||
ticket = DefaultTicket()
|
||||
}
|
||||
req := &remotepb.Request{
|
||||
ServiceName: &service,
|
||||
Method: &method,
|
||||
|
|
@ -550,6 +576,9 @@ var logLevelName = map[int64]string{
|
|||
}
|
||||
|
||||
func logf(c *context, level int64, format string, args ...interface{}) {
|
||||
if c == nil {
|
||||
panic("not an App Engine context")
|
||||
}
|
||||
s := fmt.Sprintf(format, args...)
|
||||
s = strings.TrimRight(s, "\n") // Remove any trailing newline characters.
|
||||
c.addLogLine(&logpb.UserAppLogLine{
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
netcontext "golang.org/x/net/context"
|
||||
)
|
||||
|
|
@ -84,3 +86,31 @@ func Logf(ctx netcontext.Context, level int64, format string, args ...interface{
|
|||
func NamespacedContext(ctx netcontext.Context, namespace string) netcontext.Context {
|
||||
return withNamespace(ctx, namespace)
|
||||
}
|
||||
|
||||
// SetTestEnv sets the env variables for testing background ticket in Flex.
|
||||
func SetTestEnv() func() {
|
||||
var environ = []struct {
|
||||
key, value string
|
||||
}{
|
||||
{"GAE_LONG_APP_ID", "my-app-id"},
|
||||
{"GAE_MINOR_VERSION", "067924799508853122"},
|
||||
{"GAE_MODULE_INSTANCE", "0"},
|
||||
{"GAE_MODULE_NAME", "default"},
|
||||
{"GAE_MODULE_VERSION", "20150612t184001"},
|
||||
}
|
||||
|
||||
for _, v := range environ {
|
||||
old := os.Getenv(v.key)
|
||||
os.Setenv(v.key, v.value)
|
||||
v.value = old
|
||||
}
|
||||
return func() { // Restore old environment after the test completes.
|
||||
for _, v := range environ {
|
||||
if v.value == "" {
|
||||
os.Unsetenv(v.key)
|
||||
continue
|
||||
}
|
||||
os.Setenv(v.key, v.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue