Using godeps for Heroku
This commit is contained in:
parent
9c6c65a036
commit
f77045f80b
34
Godeps/Godeps.json
generated
Normal file
34
Godeps/Godeps.json
generated
Normal file
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"ImportPath": "github.com/MichaelOultram/mumbledj",
|
||||
"GoVersion": "go1.4.2",
|
||||
"Deps": [
|
||||
{
|
||||
"ImportPath": "code.google.com/p/gcfg",
|
||||
"Rev": "c2d3050044d05357eaf6c3547249ba57c5e235cb"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/golang/protobuf/proto",
|
||||
"Rev": "0f7a9caded1fb3c9cc5a9b4bcf2ff633cc8ae644"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/jmoiron/jsonq",
|
||||
"Rev": "7c27c8eb9f6831555a4209f6a7d579159e766a3c"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/layeh/gopus",
|
||||
"Rev": "2f86fa22bc209cc0ccbc6418dfbad9199e3dbc78"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/layeh/gumble/gumble",
|
||||
"Rev": "c9fcce8fc4b71c7c53a5d3d9d48a1e001ad19a19"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/layeh/gumble/gumble_ffmpeg",
|
||||
"Rev": "c9fcce8fc4b71c7c53a5d3d9d48a1e001ad19a19"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/layeh/gumble/gumbleutil",
|
||||
"Rev": "c9fcce8fc4b71c7c53a5d3d9d48a1e001ad19a19"
|
||||
}
|
||||
]
|
||||
}
|
5
Godeps/Readme
generated
Normal file
5
Godeps/Readme
generated
Normal file
|
@ -0,0 +1,5 @@
|
|||
This directory tree is generated automatically by godep.
|
||||
|
||||
Please do not edit.
|
||||
|
||||
See https://github.com/tools/godep for more information.
|
2
Godeps/_workspace/.gitignore
generated
vendored
Normal file
2
Godeps/_workspace/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/pkg
|
||||
/bin
|
57
Godeps/_workspace/src/code.google.com/p/gcfg/LICENSE
generated
vendored
Normal file
57
Godeps/_workspace/src/code.google.com/p/gcfg/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
Copyright (c) 2012 Péter Surányi. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
Portions of gcfg's source code have been derived from Go, and are
|
||||
covered by the following license:
|
||||
----------------------------------------------------------------------
|
||||
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
7
Godeps/_workspace/src/code.google.com/p/gcfg/README
generated
vendored
Normal file
7
Godeps/_workspace/src/code.google.com/p/gcfg/README
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
Gcfg reads INI-style configuration files into Go structs;
|
||||
supports user-defined types and subsections.
|
||||
|
||||
Project page: https://code.google.com/p/gcfg
|
||||
Package docs: http://godoc.org/code.google.com/p/gcfg
|
||||
|
||||
My other projects: https://speter.net
|
118
Godeps/_workspace/src/code.google.com/p/gcfg/doc.go
generated
vendored
Normal file
118
Godeps/_workspace/src/code.google.com/p/gcfg/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,118 @@
|
|||
// Package gcfg reads "INI-style" text-based configuration files with
|
||||
// "name=value" pairs grouped into sections (gcfg files).
|
||||
//
|
||||
// This package is still a work in progress; see the sections below for planned
|
||||
// changes.
|
||||
//
|
||||
// Syntax
|
||||
//
|
||||
// The syntax is based on that used by git config:
|
||||
// http://git-scm.com/docs/git-config#_syntax .
|
||||
// There are some (planned) differences compared to the git config format:
|
||||
// - improve data portability:
|
||||
// - must be encoded in UTF-8 (for now) and must not contain the 0 byte
|
||||
// - include and "path" type is not supported
|
||||
// (path type may be implementable as a user-defined type)
|
||||
// - internationalization
|
||||
// - section and variable names can contain unicode letters, unicode digits
|
||||
// (as defined in http://golang.org/ref/spec#Characters ) and hyphens
|
||||
// (U+002D), starting with a unicode letter
|
||||
// - disallow potentially ambiguous or misleading definitions:
|
||||
// - `[sec.sub]` format is not allowed (deprecated in gitconfig)
|
||||
// - `[sec ""]` is not allowed
|
||||
// - use `[sec]` for section name "sec" and empty subsection name
|
||||
// - (planned) within a single file, definitions must be contiguous for each:
|
||||
// - section: '[secA]' -> '[secB]' -> '[secA]' is an error
|
||||
// - subsection: '[sec "A"]' -> '[sec "B"]' -> '[sec "A"]' is an error
|
||||
// - multivalued variable: 'multi=a' -> 'other=x' -> 'multi=b' is an error
|
||||
//
|
||||
// Data structure
|
||||
//
|
||||
// The functions in this package read values into a user-defined struct.
|
||||
// Each section corresponds to a struct field in the config struct, and each
|
||||
// variable in a section corresponds to a data field in the section struct.
|
||||
// The mapping of each section or variable name to fields is done either based
|
||||
// on the "gcfg" struct tag or by matching the name of the section or variable,
|
||||
// ignoring case. In the latter case, hyphens '-' in section and variable names
|
||||
// correspond to underscores '_' in field names.
|
||||
// Fields must be exported; to use a section or variable name starting with a
|
||||
// letter that is neither upper- or lower-case, prefix the field name with 'X'.
|
||||
// (See https://code.google.com/p/go/issues/detail?id=5763#c4 .)
|
||||
//
|
||||
// For sections with subsections, the corresponding field in config must be a
|
||||
// map, rather than a struct, with string keys and pointer-to-struct values.
|
||||
// Values for subsection variables are stored in the map with the subsection
|
||||
// name used as the map key.
|
||||
// (Note that unlike section and variable names, subsection names are case
|
||||
// sensitive.)
|
||||
// When using a map, and there is a section with the same section name but
|
||||
// without a subsection name, its values are stored with the empty string used
|
||||
// as the key.
|
||||
//
|
||||
// The functions in this package panic if config is not a pointer to a struct,
|
||||
// or when a field is not of a suitable type (either a struct or a map with
|
||||
// string keys and pointer-to-struct values).
|
||||
//
|
||||
// Parsing of values
|
||||
//
|
||||
// The section structs in the config struct may contain single-valued or
|
||||
// multi-valued variables. Variables of unnamed slice type (that is, a type
|
||||
// starting with `[]`) are treated as multi-value; all others (including named
|
||||
// slice types) are treated as single-valued variables.
|
||||
//
|
||||
// Single-valued variables are handled based on the type as follows.
|
||||
// Unnamed pointer types (that is, types starting with `*`) are dereferenced,
|
||||
// and if necessary, a new instance is allocated.
|
||||
//
|
||||
// For types implementing the encoding.TextUnmarshaler interface, the
|
||||
// UnmarshalText method is used to set the value. Implementing this method is
|
||||
// the recommended way for parsing user-defined types.
|
||||
//
|
||||
// For fields of string kind, the value string is assigned to the field, after
|
||||
// unquoting and unescaping as needed.
|
||||
// For fields of bool kind, the field is set to true if the value is "true",
|
||||
// "yes", "on" or "1", and set to false if the value is "false", "no", "off" or
|
||||
// "0", ignoring case. In addition, single-valued bool fields can be specified
|
||||
// with a "blank" value (variable name without equals sign and value); in such
|
||||
// case the value is set to true.
|
||||
//
|
||||
// Predefined integer types [u]int(|8|16|32|64) and big.Int are parsed as
|
||||
// decimal or hexadecimal (if having '0x' prefix). (This is to prevent
|
||||
// unintuitively handling zero-padded numbers as octal.) Other types having
|
||||
// [u]int* as the underlying type, such as os.FileMode and uintptr allow
|
||||
// decimal, hexadecimal, or octal values.
|
||||
// Parsing mode for integer types can be overridden using the struct tag option
|
||||
// ",int=mode" where mode is a combination of the 'd', 'h', and 'o' characters
|
||||
// (each standing for decimal, hexadecimal, and octal, respectively.)
|
||||
//
|
||||
// All other types are parsed using fmt.Sscanf with the "%v" verb.
|
||||
//
|
||||
// For multi-valued variables, each individual value is parsed as above and
|
||||
// appended to the slice. If the first value is specified as a "blank" value
|
||||
// (variable name without equals sign and value), a new slice is allocated;
|
||||
// that is any values previously set in the slice will be ignored.
|
||||
//
|
||||
// The types subpackage for provides helpers for parsing "enum-like" and integer
|
||||
// types.
|
||||
//
|
||||
// TODO
|
||||
//
|
||||
// The following is a list of changes under consideration:
|
||||
// - documentation
|
||||
// - self-contained syntax documentation
|
||||
// - more practical examples
|
||||
// - move TODOs to issue tracker (eventually)
|
||||
// - syntax
|
||||
// - reconsider valid escape sequences
|
||||
// (gitconfig doesn't support \r in value, \t in subsection name, etc.)
|
||||
// - reading / parsing gcfg files
|
||||
// - define internal representation structure
|
||||
// - support multiple inputs (readers, strings, files)
|
||||
// - support declaring encoding (?)
|
||||
// - support varying fields sets for subsections (?)
|
||||
// - writing gcfg files
|
||||
// - error handling
|
||||
// - make error context accessible programmatically?
|
||||
// - limit input size?
|
||||
//
|
||||
package gcfg
|
132
Godeps/_workspace/src/code.google.com/p/gcfg/example_test.go
generated
vendored
Normal file
132
Godeps/_workspace/src/code.google.com/p/gcfg/example_test.go
generated
vendored
Normal file
|
@ -0,0 +1,132 @@
|
|||
package gcfg_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
import "code.google.com/p/gcfg"
|
||||
|
||||
func ExampleReadStringInto() {
|
||||
cfgStr := `; Comment line
|
||||
[section]
|
||||
name=value # comment`
|
||||
cfg := struct {
|
||||
Section struct {
|
||||
Name string
|
||||
}
|
||||
}{}
|
||||
err := gcfg.ReadStringInto(&cfg, cfgStr)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to parse gcfg data: %s", err)
|
||||
}
|
||||
fmt.Println(cfg.Section.Name)
|
||||
// Output: value
|
||||
}
|
||||
|
||||
func ExampleReadStringInto_bool() {
|
||||
cfgStr := `; Comment line
|
||||
[section]
|
||||
switch=on`
|
||||
cfg := struct {
|
||||
Section struct {
|
||||
Switch bool
|
||||
}
|
||||
}{}
|
||||
err := gcfg.ReadStringInto(&cfg, cfgStr)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to parse gcfg data: %s", err)
|
||||
}
|
||||
fmt.Println(cfg.Section.Switch)
|
||||
// Output: true
|
||||
}
|
||||
|
||||
func ExampleReadStringInto_hyphens() {
|
||||
cfgStr := `; Comment line
|
||||
[section-name]
|
||||
variable-name=value # comment`
|
||||
cfg := struct {
|
||||
Section_Name struct {
|
||||
Variable_Name string
|
||||
}
|
||||
}{}
|
||||
err := gcfg.ReadStringInto(&cfg, cfgStr)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to parse gcfg data: %s", err)
|
||||
}
|
||||
fmt.Println(cfg.Section_Name.Variable_Name)
|
||||
// Output: value
|
||||
}
|
||||
|
||||
func ExampleReadStringInto_tags() {
|
||||
cfgStr := `; Comment line
|
||||
[section]
|
||||
var-name=value # comment`
|
||||
cfg := struct {
|
||||
Section struct {
|
||||
FieldName string `gcfg:"var-name"`
|
||||
}
|
||||
}{}
|
||||
err := gcfg.ReadStringInto(&cfg, cfgStr)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to parse gcfg data: %s", err)
|
||||
}
|
||||
fmt.Println(cfg.Section.FieldName)
|
||||
// Output: value
|
||||
}
|
||||
|
||||
func ExampleReadStringInto_subsections() {
|
||||
cfgStr := `; Comment line
|
||||
[profile "A"]
|
||||
color = white
|
||||
|
||||
[profile "B"]
|
||||
color = black
|
||||
`
|
||||
cfg := struct {
|
||||
Profile map[string]*struct {
|
||||
Color string
|
||||
}
|
||||
}{}
|
||||
err := gcfg.ReadStringInto(&cfg, cfgStr)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to parse gcfg data: %s", err)
|
||||
}
|
||||
fmt.Printf("%s %s\n", cfg.Profile["A"].Color, cfg.Profile["B"].Color)
|
||||
// Output: white black
|
||||
}
|
||||
|
||||
func ExampleReadStringInto_multivalue() {
|
||||
cfgStr := `; Comment line
|
||||
[section]
|
||||
multi=value1
|
||||
multi=value2`
|
||||
cfg := struct {
|
||||
Section struct {
|
||||
Multi []string
|
||||
}
|
||||
}{}
|
||||
err := gcfg.ReadStringInto(&cfg, cfgStr)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to parse gcfg data: %s", err)
|
||||
}
|
||||
fmt.Println(cfg.Section.Multi)
|
||||
// Output: [value1 value2]
|
||||
}
|
||||
|
||||
func ExampleReadStringInto_unicode() {
|
||||
cfgStr := `; Comment line
|
||||
[甲]
|
||||
乙=丙 # comment`
|
||||
cfg := struct {
|
||||
X甲 struct {
|
||||
X乙 string
|
||||
}
|
||||
}{}
|
||||
err := gcfg.ReadStringInto(&cfg, cfgStr)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to parse gcfg data: %s", err)
|
||||
}
|
||||
fmt.Println(cfg.X甲.X乙)
|
||||
// Output: 丙
|
||||
}
|
7
Godeps/_workspace/src/code.google.com/p/gcfg/go1_0.go
generated
vendored
Normal file
7
Godeps/_workspace/src/code.google.com/p/gcfg/go1_0.go
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
// +build !go1.2
|
||||
|
||||
package gcfg
|
||||
|
||||
type textUnmarshaler interface {
|
||||
UnmarshalText(text []byte) error
|
||||
}
|
9
Godeps/_workspace/src/code.google.com/p/gcfg/go1_2.go
generated
vendored
Normal file
9
Godeps/_workspace/src/code.google.com/p/gcfg/go1_2.go
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
// +build go1.2
|
||||
|
||||
package gcfg
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
)
|
||||
|
||||
type textUnmarshaler encoding.TextUnmarshaler
|
63
Godeps/_workspace/src/code.google.com/p/gcfg/issues_test.go
generated
vendored
Normal file
63
Godeps/_workspace/src/code.google.com/p/gcfg/issues_test.go
generated
vendored
Normal file
|
@ -0,0 +1,63 @@
|
|||
package gcfg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type Config1 struct {
|
||||
Section struct {
|
||||
Int int
|
||||
BigInt big.Int
|
||||
}
|
||||
}
|
||||
|
||||
var testsIssue1 = []struct {
|
||||
cfg string
|
||||
typename string
|
||||
}{
|
||||
{"[section]\nint=X", "int"},
|
||||
{"[section]\nint=", "int"},
|
||||
{"[section]\nint=1A", "int"},
|
||||
{"[section]\nbigint=X", "big.Int"},
|
||||
{"[section]\nbigint=", "big.Int"},
|
||||
{"[section]\nbigint=1A", "big.Int"},
|
||||
}
|
||||
|
||||
// Value parse error should:
|
||||
// - include plain type name
|
||||
// - not include reflect internals
|
||||
func TestIssue1(t *testing.T) {
|
||||
for i, tt := range testsIssue1 {
|
||||
var c Config1
|
||||
err := ReadStringInto(&c, tt.cfg)
|
||||
switch {
|
||||
case err == nil:
|
||||
t.Errorf("%d fail: got ok; wanted error", i)
|
||||
case !strings.Contains(err.Error(), tt.typename):
|
||||
t.Errorf("%d fail: error message doesn't contain type name %q: %v",
|
||||
i, tt.typename, err)
|
||||
case strings.Contains(err.Error(), "reflect"):
|
||||
t.Errorf("%d fail: error message includes reflect internals: %v",
|
||||
i, err)
|
||||
default:
|
||||
t.Logf("%d pass: %v", i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type confIssue2 struct{ Main struct{ Foo string } }
|
||||
|
||||
var testsIssue2 = []readtest{
|
||||
{"[main]\n;\nfoo = bar\n", &confIssue2{struct{ Foo string }{"bar"}}, true},
|
||||
{"[main]\r\n;\r\nfoo = bar\r\n", &confIssue2{struct{ Foo string }{"bar"}}, true},
|
||||
}
|
||||
|
||||
func TestIssue2(t *testing.T) {
|
||||
for i, tt := range testsIssue2 {
|
||||
id := fmt.Sprintf("issue2:%d", i)
|
||||
testRead(t, id, tt)
|
||||
}
|
||||
}
|
181
Godeps/_workspace/src/code.google.com/p/gcfg/read.go
generated
vendored
Normal file
181
Godeps/_workspace/src/code.google.com/p/gcfg/read.go
generated
vendored
Normal file
|
@ -0,0 +1,181 @@
|
|||
package gcfg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
import (
|
||||
"code.google.com/p/gcfg/scanner"
|
||||
"code.google.com/p/gcfg/token"
|
||||
)
|
||||
|
||||
var unescape = map[rune]rune{'\\': '\\', '"': '"', 'n': '\n', 't': '\t'}
|
||||
|
||||
// no error: invalid literals should be caught by scanner
|
||||
func unquote(s string) string {
|
||||
u, q, esc := make([]rune, 0, len(s)), false, false
|
||||
for _, c := range s {
|
||||
if esc {
|
||||
uc, ok := unescape[c]
|
||||
switch {
|
||||
case ok:
|
||||
u = append(u, uc)
|
||||
fallthrough
|
||||
case !q && c == '\n':
|
||||
esc = false
|
||||
continue
|
||||
}
|
||||
panic("invalid escape sequence")
|
||||
}
|
||||
switch c {
|
||||
case '"':
|
||||
q = !q
|
||||
case '\\':
|
||||
esc = true
|
||||
default:
|
||||
u = append(u, c)
|
||||
}
|
||||
}
|
||||
if q {
|
||||
panic("missing end quote")
|
||||
}
|
||||
if esc {
|
||||
panic("invalid escape sequence")
|
||||
}
|
||||
return string(u)
|
||||
}
|
||||
|
||||
func readInto(config interface{}, fset *token.FileSet, file *token.File, src []byte) error {
|
||||
var s scanner.Scanner
|
||||
var errs scanner.ErrorList
|
||||
s.Init(file, src, func(p token.Position, m string) { errs.Add(p, m) }, 0)
|
||||
sect, sectsub := "", ""
|
||||
pos, tok, lit := s.Scan()
|
||||
errfn := func(msg string) error {
|
||||
return fmt.Errorf("%s: %s", fset.Position(pos), msg)
|
||||
}
|
||||
for {
|
||||
if errs.Len() > 0 {
|
||||
return errs.Err()
|
||||
}
|
||||
switch tok {
|
||||
case token.EOF:
|
||||
return nil
|
||||
case token.EOL, token.COMMENT:
|
||||
pos, tok, lit = s.Scan()
|
||||
case token.LBRACK:
|
||||
pos, tok, lit = s.Scan()
|
||||
if errs.Len() > 0 {
|
||||
return errs.Err()
|
||||
}
|
||||
if tok != token.IDENT {
|
||||
return errfn("expected section name")
|
||||
}
|
||||
sect, sectsub = lit, ""
|
||||
pos, tok, lit = s.Scan()
|
||||
if errs.Len() > 0 {
|
||||
return errs.Err()
|
||||
}
|
||||
if tok == token.STRING {
|
||||
sectsub = unquote(lit)
|
||||
if sectsub == "" {
|
||||
return errfn("empty subsection name")
|
||||
}
|
||||
pos, tok, lit = s.Scan()
|
||||
if errs.Len() > 0 {
|
||||
return errs.Err()
|
||||
}
|
||||
}
|
||||
if tok != token.RBRACK {
|
||||
if sectsub == "" {
|
||||
return errfn("expected subsection name or right bracket")
|
||||
}
|
||||
return errfn("expected right bracket")
|
||||
}
|
||||
pos, tok, lit = s.Scan()
|
||||
if tok != token.EOL && tok != token.EOF && tok != token.COMMENT {
|
||||
return errfn("expected EOL, EOF, or comment")
|
||||
}
|
||||
case token.IDENT:
|
||||
if sect == "" {
|
||||
return errfn("expected section header")
|
||||
}
|
||||
n := lit
|
||||
pos, tok, lit = s.Scan()
|
||||
if errs.Len() > 0 {
|
||||
return errs.Err()
|
||||
}
|
||||
blank, v := tok == token.EOF || tok == token.EOL || tok == token.COMMENT, ""
|
||||
if !blank {
|
||||
if tok != token.ASSIGN {
|
||||
return errfn("expected '='")
|
||||
}
|
||||
pos, tok, lit = s.Scan()
|
||||
if errs.Len() > 0 {
|
||||
return errs.Err()
|
||||
}
|
||||
if tok != token.STRING {
|
||||
return errfn("expected value")
|
||||
}
|
||||
v = unquote(lit)
|
||||
pos, tok, lit = s.Scan()
|
||||
if errs.Len() > 0 {
|
||||
return errs.Err()
|
||||
}
|
||||
if tok != token.EOL && tok != token.EOF && tok != token.COMMENT {
|
||||
return errfn("expected EOL, EOF, or comment")
|
||||
}
|
||||
}
|
||||
err := set(config, sect, sectsub, n, blank, v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
if sect == "" {
|
||||
return errfn("expected section header")
|
||||
}
|
||||
return errfn("expected section header or variable declaration")
|
||||
}
|
||||
}
|
||||
panic("never reached")
|
||||
}
|
||||
|
||||
// ReadInto reads gcfg formatted data from reader and sets the values into the
|
||||
// corresponding fields in config.
|
||||
func ReadInto(config interface{}, reader io.Reader) error {
|
||||
src, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fset := token.NewFileSet()
|
||||
file := fset.AddFile("", fset.Base(), len(src))
|
||||
return readInto(config, fset, file, src)
|
||||
}
|
||||
|
||||
// ReadStringInto reads gcfg formatted data from str and sets the values into
|
||||
// the corresponding fields in config.
|
||||
func ReadStringInto(config interface{}, str string) error {
|
||||
r := strings.NewReader(str)
|
||||
return ReadInto(config, r)
|
||||
}
|
||||
|
||||
// ReadFileInto reads gcfg formatted data from the file filename and sets the
|
||||
// values into the corresponding fields in config.
|
||||
func ReadFileInto(config interface{}, filename string) error {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
src, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fset := token.NewFileSet()
|
||||
file := fset.AddFile(filename, fset.Base(), len(src))
|
||||
return readInto(config, fset, file, src)
|
||||
}
|
333
Godeps/_workspace/src/code.google.com/p/gcfg/read_test.go
generated
vendored
Normal file
333
Godeps/_workspace/src/code.google.com/p/gcfg/read_test.go
generated
vendored
Normal file
|
@ -0,0 +1,333 @@
|
|||
package gcfg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
// 64 spaces
|
||||
sp64 = " "
|
||||
// 512 spaces
|
||||
sp512 = sp64 + sp64 + sp64 + sp64 + sp64 + sp64 + sp64 + sp64
|
||||
// 4096 spaces
|
||||
sp4096 = sp512 + sp512 + sp512 + sp512 + sp512 + sp512 + sp512 + sp512
|
||||
)
|
||||
|
||||
type cBasic struct {
|
||||
Section cBasicS1
|
||||
Hyphen_In_Section cBasicS2
|
||||
unexported cBasicS1
|
||||
Exported cBasicS3
|
||||
TagName cBasicS1 `gcfg:"tag-name"`
|
||||
}
|
||||
type cBasicS1 struct {
|
||||
Name string
|
||||
Int int
|
||||
PName *string
|
||||
}
|
||||
type cBasicS2 struct {
|
||||
Hyphen_In_Name string
|
||||
}
|
||||
type cBasicS3 struct {
|
||||
unexported string
|
||||
}
|
||||
|
||||
type nonMulti []string
|
||||
|
||||
type unmarshalable string
|
||||
|
||||
func (u *unmarshalable) UnmarshalText(text []byte) error {
|
||||
s := string(text)
|
||||
if s == "error" {
|
||||
return fmt.Errorf("%s", s)
|
||||
}
|
||||
*u = unmarshalable(s)
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ textUnmarshaler = new(unmarshalable)
|
||||
|
||||
type cUni struct {
|
||||
X甲 cUniS1
|
||||
XSection cUniS2
|
||||
}
|
||||
type cUniS1 struct {
|
||||
X乙 string
|
||||
}
|
||||
type cUniS2 struct {
|
||||
XName string
|
||||
}
|
||||
|
||||
type cMulti struct {
|
||||
M1 cMultiS1
|
||||
M2 cMultiS2
|
||||
M3 cMultiS3
|
||||
}
|
||||
type cMultiS1 struct{ Multi []string }
|
||||
type cMultiS2 struct{ NonMulti nonMulti }
|
||||
type cMultiS3 struct{ MultiInt []int }
|
||||
|
||||
type cSubs struct{ Sub map[string]*cSubsS1 }
|
||||
type cSubsS1 struct{ Name string }
|
||||
|
||||
type cBool struct{ Section cBoolS1 }
|
||||
type cBoolS1 struct{ Bool bool }
|
||||
|
||||
type cTxUnm struct{ Section cTxUnmS1 }
|
||||
type cTxUnmS1 struct{ Name unmarshalable }
|
||||
|
||||
type cNum struct {
|
||||
N1 cNumS1
|
||||
N2 cNumS2
|
||||
N3 cNumS3
|
||||
}
|
||||
type cNumS1 struct {
|
||||
Int int
|
||||
IntDHO int `gcfg:",int=dho"`
|
||||
Big *big.Int
|
||||
}
|
||||
type cNumS2 struct {
|
||||
MultiInt []int
|
||||
MultiBig []*big.Int
|
||||
}
|
||||
type cNumS3 struct{ FileMode os.FileMode }
|
||||
type readtest struct {
|
||||
gcfg string
|
||||
exp interface{}
|
||||
ok bool
|
||||
}
|
||||
|
||||
func newString(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
var readtests = []struct {
|
||||
group string
|
||||
tests []readtest
|
||||
}{{"scanning", []readtest{
|
||||
{"[section]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true},
|
||||
// hyphen in name
|
||||
{"[hyphen-in-section]\nhyphen-in-name=value", &cBasic{Hyphen_In_Section: cBasicS2{Hyphen_In_Name: "value"}}, true},
|
||||
// quoted string value
|
||||
{"[section]\nname=\"\"", &cBasic{Section: cBasicS1{Name: ""}}, true},
|
||||
{"[section]\nname=\" \"", &cBasic{Section: cBasicS1{Name: " "}}, true},
|
||||
{"[section]\nname=\"value\"", &cBasic{Section: cBasicS1{Name: "value"}}, true},
|
||||
{"[section]\nname=\" value \"", &cBasic{Section: cBasicS1{Name: " value "}}, true},
|
||||
{"\n[section]\nname=\"va ; lue\"", &cBasic{Section: cBasicS1{Name: "va ; lue"}}, true},
|
||||
{"[section]\nname=\"val\" \"ue\"", &cBasic{Section: cBasicS1{Name: "val ue"}}, true},
|
||||
{"[section]\nname=\"value", &cBasic{}, false},
|
||||
// escape sequences
|
||||
{"[section]\nname=\"va\\\\lue\"", &cBasic{Section: cBasicS1{Name: "va\\lue"}}, true},
|
||||
{"[section]\nname=\"va\\\"lue\"", &cBasic{Section: cBasicS1{Name: "va\"lue"}}, true},
|
||||
{"[section]\nname=\"va\\nlue\"", &cBasic{Section: cBasicS1{Name: "va\nlue"}}, true},
|
||||
{"[section]\nname=\"va\\tlue\"", &cBasic{Section: cBasicS1{Name: "va\tlue"}}, true},
|
||||
{"\n[section]\nname=\\", &cBasic{}, false},
|
||||
{"\n[section]\nname=\\a", &cBasic{}, false},
|
||||
{"\n[section]\nname=\"val\\a\"", &cBasic{}, false},
|
||||
{"\n[section]\nname=val\\", &cBasic{}, false},
|
||||
{"\n[sub \"A\\\n\"]\nname=value", &cSubs{}, false},
|
||||
{"\n[sub \"A\\\t\"]\nname=value", &cSubs{}, false},
|
||||
// broken line
|
||||
{"[section]\nname=value \\\n value", &cBasic{Section: cBasicS1{Name: "value value"}}, true},
|
||||
{"[section]\nname=\"value \\\n value\"", &cBasic{}, false},
|
||||
}}, {"scanning:whitespace", []readtest{
|
||||
{" \n[section]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true},
|
||||
{" [section]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true},
|
||||
{"\t[section]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true},
|
||||
{"[ section]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true},
|
||||
{"[section ]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true},
|
||||
{"[section]\n name=value", &cBasic{Section: cBasicS1{Name: "value"}}, true},
|
||||
{"[section]\nname =value", &cBasic{Section: cBasicS1{Name: "value"}}, true},
|
||||
{"[section]\nname= value", &cBasic{Section: cBasicS1{Name: "value"}}, true},
|
||||
{"[section]\nname=value ", &cBasic{Section: cBasicS1{Name: "value"}}, true},
|
||||
{"[section]\r\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true},
|
||||
{"[section]\r\nname=value\r\n", &cBasic{Section: cBasicS1{Name: "value"}}, true},
|
||||
{";cmnt\r\n[section]\r\nname=value\r\n", &cBasic{Section: cBasicS1{Name: "value"}}, true},
|
||||
// long lines
|
||||
{sp4096 + "[section]\nname=value\n", &cBasic{Section: cBasicS1{Name: "value"}}, true},
|
||||
{"[" + sp4096 + "section]\nname=value\n", &cBasic{Section: cBasicS1{Name: "value"}}, true},
|
||||
{"[section" + sp4096 + "]\nname=value\n", &cBasic{Section: cBasicS1{Name: "value"}}, true},
|
||||
{"[section]" + sp4096 + "\nname=value\n", &cBasic{Section: cBasicS1{Name: "value"}}, true},
|
||||
{"[section]\n" + sp4096 + "name=value\n", &cBasic{Section: cBasicS1{Name: "value"}}, true},
|
||||
{"[section]\nname" + sp4096 + "=value\n", &cBasic{Section: cBasicS1{Name: "value"}}, true},
|
||||
{"[section]\nname=" + sp4096 + "value\n", &cBasic{Section: cBasicS1{Name: "value"}}, true},
|
||||
{"[section]\nname=value\n" + sp4096, &cBasic{Section: cBasicS1{Name: "value"}}, true},
|
||||
}}, {"scanning:comments", []readtest{
|
||||
{"; cmnt\n[section]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true},
|
||||
{"# cmnt\n[section]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true},
|
||||
{" ; cmnt\n[section]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true},
|
||||
{"\t; cmnt\n[section]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true},
|
||||
{"\n[section]; cmnt\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true},
|
||||
{"\n[section] ; cmnt\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true},
|
||||
{"\n[section]\nname=value; cmnt", &cBasic{Section: cBasicS1{Name: "value"}}, true},
|
||||
{"\n[section]\nname=value ; cmnt", &cBasic{Section: cBasicS1{Name: "value"}}, true},
|
||||
{"\n[section]\nname=\"value\" ; cmnt", &cBasic{Section: cBasicS1{Name: "value"}}, true},
|
||||
{"\n[section]\nname=value ; \"cmnt", &cBasic{Section: cBasicS1{Name: "value"}}, true},
|
||||
{"\n[section]\nname=\"va ; lue\" ; cmnt", &cBasic{Section: cBasicS1{Name: "va ; lue"}}, true},
|
||||
{"\n[section]\nname=; cmnt", &cBasic{Section: cBasicS1{Name: ""}}, true},
|
||||
}}, {"scanning:subsections", []readtest{
|
||||
{"\n[sub \"A\"]\nname=value", &cSubs{map[string]*cSubsS1{"A": &cSubsS1{"value"}}}, true},
|
||||
{"\n[sub \"b\"]\nname=value", &cSubs{map[string]*cSubsS1{"b": &cSubsS1{"value"}}}, true},
|
||||
{"\n[sub \"A\\\\\"]\nname=value", &cSubs{map[string]*cSubsS1{"A\\": &cSubsS1{"value"}}}, true},
|
||||
{"\n[sub \"A\\\"\"]\nname=value", &cSubs{map[string]*cSubsS1{"A\"": &cSubsS1{"value"}}}, true},
|
||||
}}, {"syntax", []readtest{
|
||||
// invalid line
|
||||
{"\n[section]\n=", &cBasic{}, false},
|
||||
// no section
|
||||
{"name=value", &cBasic{}, false},
|
||||
// empty section
|
||||
{"\n[]\nname=value", &cBasic{}, false},
|
||||
// empty subsection
|
||||
{"\n[sub \"\"]\nname=value", &cSubs{}, false},
|
||||
}}, {"setting", []readtest{
|
||||
{"[section]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true},
|
||||
// pointer
|
||||
{"[section]", &cBasic{Section: cBasicS1{PName: nil}}, true},
|
||||
{"[section]\npname=value", &cBasic{Section: cBasicS1{PName: newString("value")}}, true},
|
||||
// section name not matched
|
||||
{"\n[nonexistent]\nname=value", &cBasic{}, false},
|
||||
// subsection name not matched
|
||||
{"\n[section \"nonexistent\"]\nname=value", &cBasic{}, false},
|
||||
// variable name not matched
|
||||
{"\n[section]\nnonexistent=value", &cBasic{}, false},
|
||||
// hyphen in name
|
||||
{"[hyphen-in-section]\nhyphen-in-name=value", &cBasic{Hyphen_In_Section: cBasicS2{Hyphen_In_Name: "value"}}, true},
|
||||
// ignore unexported fields
|
||||
{"[unexported]\nname=value", &cBasic{}, false},
|
||||
{"[exported]\nunexported=value", &cBasic{}, false},
|
||||
// 'X' prefix for non-upper/lower-case letters
|
||||
{"[甲]\n乙=丙", &cUni{X甲: cUniS1{X乙: "丙"}}, true},
|
||||
//{"[section]\nxname=value", &cBasic{XSection: cBasicS4{XName: "value"}}, false},
|
||||
//{"[xsection]\nname=value", &cBasic{XSection: cBasicS4{XName: "value"}}, false},
|
||||
// name specified as struct tag
|
||||
{"[tag-name]\nname=value", &cBasic{TagName: cBasicS1{Name: "value"}}, true},
|
||||
}}, {"multivalue", []readtest{
|
||||
// unnamed slice type: treat as multi-value
|
||||
{"\n[m1]", &cMulti{M1: cMultiS1{}}, true},
|
||||
{"\n[m1]\nmulti=value", &cMulti{M1: cMultiS1{[]string{"value"}}}, true},
|
||||
{"\n[m1]\nmulti=value1\nmulti=value2", &cMulti{M1: cMultiS1{[]string{"value1", "value2"}}}, true},
|
||||
// "blank" empties multi-valued slice -- here same result as above
|
||||
{"\n[m1]\nmulti\nmulti=value1\nmulti=value2", &cMulti{M1: cMultiS1{[]string{"value1", "value2"}}}, true},
|
||||
// named slice type: do not treat as multi-value
|
||||
{"\n[m2]", &cMulti{}, true},
|
||||
{"\n[m2]\nmulti=value", &cMulti{}, false},
|
||||
{"\n[m2]\nmulti=value1\nmulti=value2", &cMulti{}, false},
|
||||
}}, {"type:string", []readtest{
|
||||
{"[section]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true},
|
||||
{"[section]\nname=", &cBasic{Section: cBasicS1{Name: ""}}, true},
|
||||
}}, {"type:bool", []readtest{
|
||||
// explicit values
|
||||
{"[section]\nbool=true", &cBool{cBoolS1{true}}, true},
|
||||
{"[section]\nbool=yes", &cBool{cBoolS1{true}}, true},
|
||||
{"[section]\nbool=on", &cBool{cBoolS1{true}}, true},
|
||||
{"[section]\nbool=1", &cBool{cBoolS1{true}}, true},
|
||||
{"[section]\nbool=tRuE", &cBool{cBoolS1{true}}, true},
|
||||
{"[section]\nbool=false", &cBool{cBoolS1{false}}, true},
|
||||
{"[section]\nbool=no", &cBool{cBoolS1{false}}, true},
|
||||
{"[section]\nbool=off", &cBool{cBoolS1{false}}, true},
|
||||
{"[section]\nbool=0", &cBool{cBoolS1{false}}, true},
|
||||
{"[section]\nbool=NO", &cBool{cBoolS1{false}}, true},
|
||||
// "blank" value handled as true
|
||||
{"[section]\nbool", &cBool{cBoolS1{true}}, true},
|
||||
// bool parse errors
|
||||
{"[section]\nbool=maybe", &cBool{}, false},
|
||||
{"[section]\nbool=t", &cBool{}, false},
|
||||
{"[section]\nbool=truer", &cBool{}, false},
|
||||
{"[section]\nbool=2", &cBool{}, false},
|
||||
{"[section]\nbool=-1", &cBool{}, false},
|
||||
}}, {"type:numeric", []readtest{
|
||||
{"[section]\nint=0", &cBasic{Section: cBasicS1{Int: 0}}, true},
|
||||
{"[section]\nint=1", &cBasic{Section: cBasicS1{Int: 1}}, true},
|
||||
{"[section]\nint=-1", &cBasic{Section: cBasicS1{Int: -1}}, true},
|
||||
{"[section]\nint=0.2", &cBasic{}, false},
|
||||
{"[section]\nint=1e3", &cBasic{}, false},
|
||||
// primitive [u]int(|8|16|32|64) and big.Int is parsed as dec or hex (not octal)
|
||||
{"[n1]\nint=010", &cNum{N1: cNumS1{Int: 10}}, true},
|
||||
{"[n1]\nint=0x10", &cNum{N1: cNumS1{Int: 0x10}}, true},
|
||||
{"[n1]\nbig=1", &cNum{N1: cNumS1{Big: big.NewInt(1)}}, true},
|
||||
{"[n1]\nbig=0x10", &cNum{N1: cNumS1{Big: big.NewInt(0x10)}}, true},
|
||||
{"[n1]\nbig=010", &cNum{N1: cNumS1{Big: big.NewInt(10)}}, true},
|
||||
{"[n2]\nmultiint=010", &cNum{N2: cNumS2{MultiInt: []int{10}}}, true},
|
||||
{"[n2]\nmultibig=010", &cNum{N2: cNumS2{MultiBig: []*big.Int{big.NewInt(10)}}}, true},
|
||||
// set parse mode for int types via struct tag
|
||||
{"[n1]\nintdho=010", &cNum{N1: cNumS1{IntDHO: 010}}, true},
|
||||
// octal allowed for named type
|
||||
{"[n3]\nfilemode=0777", &cNum{N3: cNumS3{FileMode: 0777}}, true},
|
||||
}}, {"type:textUnmarshaler", []readtest{
|
||||
{"[section]\nname=value", &cTxUnm{Section: cTxUnmS1{Name: "value"}}, true},
|
||||
{"[section]\nname=error", &cTxUnm{}, false},
|
||||
}},
|
||||
}
|
||||
|
||||
func TestReadStringInto(t *testing.T) {
|
||||
for _, tg := range readtests {
|
||||
for i, tt := range tg.tests {
|
||||
id := fmt.Sprintf("%s:%d", tg.group, i)
|
||||
testRead(t, id, tt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadStringIntoMultiBlankPreset(t *testing.T) {
|
||||
tt := readtest{"\n[m1]\nmulti\nmulti=value1\nmulti=value2", &cMulti{M1: cMultiS1{[]string{"value1", "value2"}}}, true}
|
||||
cfg := &cMulti{M1: cMultiS1{[]string{"preset1", "preset2"}}}
|
||||
testReadInto(t, "multi:blank", tt, cfg)
|
||||
}
|
||||
|
||||
func testRead(t *testing.T, id string, tt readtest) {
|
||||
// get the type of the expected result
|
||||
restyp := reflect.TypeOf(tt.exp).Elem()
|
||||
// create a new instance to hold the actual result
|
||||
res := reflect.New(restyp).Interface()
|
||||
testReadInto(t, id, tt, res)
|
||||
}
|
||||
|
||||
func testReadInto(t *testing.T, id string, tt readtest, res interface{}) {
|
||||
err := ReadStringInto(res, tt.gcfg)
|
||||
if tt.ok {
|
||||
if err != nil {
|
||||
t.Errorf("%s fail: got error %v, wanted ok", id, err)
|
||||
return
|
||||
} else if !reflect.DeepEqual(res, tt.exp) {
|
||||
t.Errorf("%s fail: got value %#v, wanted value %#v", id, res, tt.exp)
|
||||
return
|
||||
}
|
||||
if !testing.Short() {
|
||||
t.Logf("%s pass: got value %#v", id, res)
|
||||
}
|
||||
} else { // !tt.ok
|
||||
if err == nil {
|
||||
t.Errorf("%s fail: got value %#v, wanted error", id, res)
|
||||
return
|
||||
}
|
||||
if !testing.Short() {
|
||||
t.Logf("%s pass: got error %v", id, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadFileInto(t *testing.T) {
|
||||
res := &struct{ Section struct{ Name string } }{}
|
||||
err := ReadFileInto(res, "testdata/gcfg_test.gcfg")
|
||||
if err != nil {
|
||||
t.Errorf(err.Error())
|
||||
}
|
||||
if "value" != res.Section.Name {
|
||||
t.Errorf("got %q, wanted %q", res.Section.Name, "value")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadFileIntoUnicode(t *testing.T) {
|
||||
res := &struct{ X甲 struct{ X乙 string } }{}
|
||||
err := ReadFileInto(res, "testdata/gcfg_unicode_test.gcfg")
|
||||
if err != nil {
|
||||
t.Errorf(err.Error())
|
||||
}
|
||||
if "丙" != res.X甲.X乙 {
|
||||
t.Errorf("got %q, wanted %q", res.X甲.X乙, "丙")
|
||||
}
|
||||
}
|
121
Godeps/_workspace/src/code.google.com/p/gcfg/scanner/errors.go
generated
vendored
Normal file
121
Godeps/_workspace/src/code.google.com/p/gcfg/scanner/errors.go
generated
vendored
Normal file
|
@ -0,0 +1,121 @@
|
|||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package scanner
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
)
|
||||
|
||||
import (
|
||||
"code.google.com/p/gcfg/token"
|
||||
)
|
||||
|
||||
// In an ErrorList, an error is represented by an *Error.
|
||||
// The position Pos, if valid, points to the beginning of
|
||||
// the offending token, and the error condition is described
|
||||
// by Msg.
|
||||
//
|
||||
type Error struct {
|
||||
Pos token.Position
|
||||
Msg string
|
||||
}
|
||||
|
||||
// Error implements the error interface.
|
||||
func (e Error) Error() string {
|
||||
if e.Pos.Filename != "" || e.Pos.IsValid() {
|
||||
// don't print "<unknown position>"
|
||||
// TODO(gri) reconsider the semantics of Position.IsValid
|
||||
return e.Pos.String() + ": " + e.Msg
|
||||
}
|
||||
return e.Msg
|
||||
}
|
||||
|
||||
// ErrorList is a list of *Errors.
|
||||
// The zero value for an ErrorList is an empty ErrorList ready to use.
|
||||
//
|
||||
type ErrorList []*Error
|
||||
|
||||
// Add adds an Error with given position and error message to an ErrorList.
|
||||
func (p *ErrorList) Add(pos token.Position, msg string) {
|
||||
*p = append(*p, &Error{pos, msg})
|
||||
}
|
||||
|
||||
// Reset resets an ErrorList to no errors.
|
||||
func (p *ErrorList) Reset() { *p = (*p)[0:0] }
|
||||
|
||||
// ErrorList implements the sort Interface.
|
||||
func (p ErrorList) Len() int { return len(p) }
|
||||
func (p ErrorList) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||
|
||||
func (p ErrorList) Less(i, j int) bool {
|
||||
e := &p[i].Pos
|
||||
f := &p[j].Pos
|
||||
if e.Filename < f.Filename {
|
||||
return true
|
||||
}
|
||||
if e.Filename == f.Filename {
|
||||
return e.Offset < f.Offset
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Sort sorts an ErrorList. *Error entries are sorted by position,
|
||||
// other errors are sorted by error message, and before any *Error
|
||||
// entry.
|
||||
//
|
||||
func (p ErrorList) Sort() {
|
||||
sort.Sort(p)
|
||||
}
|
||||
|
||||
// RemoveMultiples sorts an ErrorList and removes all but the first error per line.
|
||||
func (p *ErrorList) RemoveMultiples() {
|
||||
sort.Sort(p)
|
||||
var last token.Position // initial last.Line is != any legal error line
|
||||
i := 0
|
||||
for _, e := range *p {
|
||||
if e.Pos.Filename != last.Filename || e.Pos.Line != last.Line {
|
||||
last = e.Pos
|
||||
(*p)[i] = e
|
||||
i++
|
||||
}
|
||||
}
|
||||
(*p) = (*p)[0:i]
|
||||
}
|
||||
|
||||
// An ErrorList implements the error interface.
|
||||
func (p ErrorList) Error() string {
|
||||
switch len(p) {
|
||||
case 0:
|
||||
return "no errors"
|
||||
case 1:
|
||||
return p[0].Error()
|
||||
}
|
||||
return fmt.Sprintf("%s (and %d more errors)", p[0], len(p)-1)
|
||||
}
|
||||
|
||||
// Err returns an error equivalent to this error list.
|
||||
// If the list is empty, Err returns nil.
|
||||
func (p ErrorList) Err() error {
|
||||
if len(p) == 0 {
|
||||
return nil
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// PrintError is a utility function that prints a list of errors to w,
|
||||
// one error per line, if the err parameter is an ErrorList. Otherwise
|
||||
// it prints the err string.
|
||||
//
|
||||
func PrintError(w io.Writer, err error) {
|
||||
if list, ok := err.(ErrorList); ok {
|
||||
for _, e := range list {
|
||||
fmt.Fprintf(w, "%s\n", e)
|
||||
}
|
||||
} else if err != nil {
|
||||
fmt.Fprintf(w, "%s\n", err)
|
||||
}
|
||||
}
|
46
Godeps/_workspace/src/code.google.com/p/gcfg/scanner/example_test.go
generated
vendored
Normal file
46
Godeps/_workspace/src/code.google.com/p/gcfg/scanner/example_test.go
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package scanner_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
import (
|
||||
"code.google.com/p/gcfg/scanner"
|
||||
"code.google.com/p/gcfg/token"
|
||||
)
|
||||
|
||||
func ExampleScanner_Scan() {
|
||||
// src is the input that we want to tokenize.
|
||||
src := []byte(`[profile "A"]
|
||||
color = blue ; Comment`)
|
||||
|
||||
// Initialize the scanner.
|
||||
var s scanner.Scanner
|
||||
fset := token.NewFileSet() // positions are relative to fset
|
||||
file := fset.AddFile("", fset.Base(), len(src)) // register input "file"
|
||||
s.Init(file, src, nil /* no error handler */, scanner.ScanComments)
|
||||
|
||||
// Repeated calls to Scan yield the token sequence found in the input.
|
||||
for {
|
||||
pos, tok, lit := s.Scan()
|
||||
if tok == token.EOF {
|
||||
break
|
||||
}
|
||||
fmt.Printf("%s\t%q\t%q\n", fset.Position(pos), tok, lit)
|
||||
}
|
||||
|
||||
// output:
|
||||
// 1:1 "[" ""
|
||||
// 1:2 "IDENT" "profile"
|
||||
// 1:10 "STRING" "\"A\""
|
||||
// 1:13 "]" ""
|
||||
// 1:14 "\n" ""
|
||||
// 2:1 "IDENT" "color"
|
||||
// 2:7 "=" ""
|
||||
// 2:9 "STRING" "blue"
|
||||
// 2:14 "COMMENT" "; Comment"
|
||||
}
|
342
Godeps/_workspace/src/code.google.com/p/gcfg/scanner/scanner.go
generated
vendored
Normal file
342
Godeps/_workspace/src/code.google.com/p/gcfg/scanner/scanner.go
generated
vendored
Normal file
|
@ -0,0 +1,342 @@
|
|||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package scanner implements a scanner for gcfg configuration text.
|
||||
// It takes a []byte as source which can then be tokenized
|
||||
// through repeated calls to the Scan method.
|
||||
//
|
||||
// Note that the API for the scanner package may change to accommodate new
|
||||
// features or implementation changes in gcfg.
|
||||
//
|
||||
package scanner
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
import (
|
||||
"code.google.com/p/gcfg/token"
|
||||
)
|
||||
|
||||
// An ErrorHandler may be provided to Scanner.Init. If a syntax error is
|
||||
// encountered and a handler was installed, the handler is called with a
|
||||
// position and an error message. The position points to the beginning of
|
||||
// the offending token.
|
||||
//
|
||||
type ErrorHandler func(pos token.Position, msg string)
|
||||
|
||||
// A Scanner holds the scanner's internal state while processing
|
||||
// a given text. It can be allocated as part of another data
|
||||
// structure but must be initialized via Init before use.
|
||||
//
|
||||
type Scanner struct {
|
||||
// immutable state
|
||||
file *token.File // source file handle
|
||||
dir string // directory portion of file.Name()
|
||||
src []byte // source
|
||||
err ErrorHandler // error reporting; or nil
|
||||
mode Mode // scanning mode
|
||||
|
||||
// scanning state
|
||||
ch rune // current character
|
||||
offset int // character offset
|
||||
rdOffset int // reading offset (position after current character)
|
||||
lineOffset int // current line offset
|
||||
nextVal bool // next token is expected to be a value
|
||||
|
||||
// public state - ok to modify
|
||||
ErrorCount int // number of errors encountered
|
||||
}
|
||||
|
||||
// Read the next Unicode char into s.ch.
|
||||
// s.ch < 0 means end-of-file.
|
||||
//
|
||||
func (s *Scanner) next() {
|
||||
if s.rdOffset < len(s.src) {
|
||||
s.offset = s.rdOffset
|
||||
if s.ch == '\n' {
|
||||
s.lineOffset = s.offset
|
||||
s.file.AddLine(s.offset)
|
||||
}
|
||||
r, w := rune(s.src[s.rdOffset]), 1
|
||||
switch {
|
||||
case r == 0:
|
||||
s.error(s.offset, "illegal character NUL")
|
||||
case r >= 0x80:
|
||||
// not ASCII
|
||||
r, w = utf8.DecodeRune(s.src[s.rdOffset:])
|
||||
if r == utf8.RuneError && w == 1 {
|
||||
s.error(s.offset, "illegal UTF-8 encoding")
|
||||
}
|
||||
}
|
||||
s.rdOffset += w
|
||||
s.ch = r
|
||||
} else {
|
||||
s.offset = len(s.src)
|
||||
if s.ch == '\n' {
|
||||
s.lineOffset = s.offset
|
||||
s.file.AddLine(s.offset)
|
||||
}
|
||||
s.ch = -1 // eof
|
||||
}
|
||||
}
|
||||
|
||||
// A mode value is a set of flags (or 0).
|
||||
// They control scanner behavior.
|
||||
//
|
||||
type Mode uint
|
||||
|
||||
const (
|
||||
ScanComments Mode = 1 << iota // return comments as COMMENT tokens
|
||||
)
|
||||
|
||||
// Init prepares the scanner s to tokenize the text src by setting the
|
||||
// scanner at the beginning of src. The scanner uses the file set file
|
||||
// for position information and it adds line information for each line.
|
||||
// It is ok to re-use the same file when re-scanning the same file as
|
||||
// line information which is already present is ignored. Init causes a
|
||||
// panic if the file size does not match the src size.
|
||||
//
|
||||
// Calls to Scan will invoke the error handler err if they encounter a
|
||||
// syntax error and err is not nil. Also, for each error encountered,
|
||||
// the Scanner field ErrorCount is incremented by one. The mode parameter
|
||||
// determines how comments are handled.
|
||||
//
|
||||
// Note that Init may call err if there is an error in the first character
|
||||
// of the file.
|
||||
//
|
||||
func (s *Scanner) Init(file *token.File, src []byte, err ErrorHandler, mode Mode) {
|
||||
// Explicitly initialize all fields since a scanner may be reused.
|
||||
if file.Size() != len(src) {
|
||||
panic(fmt.Sprintf("file size (%d) does not match src len (%d)", file.Size(), len(src)))
|
||||
}
|
||||
s.file = file
|
||||
s.dir, _ = filepath.Split(file.Name())
|
||||
s.src = src
|
||||
s.err = err
|
||||
s.mode = mode
|
||||
|
||||
s.ch = ' '
|
||||
s.offset = 0
|
||||
s.rdOffset = 0
|
||||
s.lineOffset = 0
|
||||
s.ErrorCount = 0
|
||||
s.nextVal = false
|
||||
|
||||
s.next()
|
||||
}
|
||||
|
||||
func (s *Scanner) error(offs int, msg string) {
|
||||
if s.err != nil {
|
||||
s.err(s.file.Position(s.file.Pos(offs)), msg)
|
||||
}
|
||||
s.ErrorCount++
|
||||
}
|
||||
|
||||
func (s *Scanner) scanComment() string {
|
||||
// initial [;#] already consumed
|
||||
offs := s.offset - 1 // position of initial [;#]
|
||||
|
||||
for s.ch != '\n' && s.ch >= 0 {
|
||||
s.next()
|
||||
}
|
||||
return string(s.src[offs:s.offset])
|
||||
}
|
||||
|
||||
func isLetter(ch rune) bool {
|
||||
return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch >= 0x80 && unicode.IsLetter(ch)
|
||||
}
|
||||
|
||||
func isDigit(ch rune) bool {
|
||||
return '0' <= ch && ch <= '9' || ch >= 0x80 && unicode.IsDigit(ch)
|
||||
}
|
||||
|
||||
func (s *Scanner) scanIdentifier() string {
|
||||
offs := s.offset
|
||||
for isLetter(s.ch) || isDigit(s.ch) || s.ch == '-' {
|
||||
s.next()
|
||||
}
|
||||
return string(s.src[offs:s.offset])
|
||||
}
|
||||
|
||||
func (s *Scanner) scanEscape(val bool) {
|
||||
offs := s.offset
|
||||
ch := s.ch
|
||||
s.next() // always make progress
|
||||
switch ch {
|
||||
case '\\', '"':
|
||||
// ok
|
||||
case 'n', 't':
|
||||
if val {
|
||||
break // ok
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
s.error(offs, "unknown escape sequence")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Scanner) scanString() string {
|
||||
// '"' opening already consumed
|
||||
offs := s.offset - 1
|
||||
|
||||
for s.ch != '"' {
|
||||
ch := s.ch
|
||||
s.next()
|
||||
if ch == '\n' || ch < 0 {
|
||||
s.error(offs, "string not terminated")
|
||||
break
|
||||
}
|
||||
if ch == '\\' {
|
||||
s.scanEscape(false)
|
||||
}
|
||||
}
|
||||
|
||||
s.next()
|
||||
|
||||
return string(s.src[offs:s.offset])
|
||||
}
|
||||
|
||||
func stripCR(b []byte) []byte {
|
||||
c := make([]byte, len(b))
|
||||
i := 0
|
||||
for _, ch := range b {
|
||||
if ch != '\r' {
|
||||
c[i] = ch
|
||||
i++
|
||||
}
|
||||
}
|
||||
return c[:i]
|
||||
}
|
||||
|
||||
func (s *Scanner) scanValString() string {
|
||||
offs := s.offset
|
||||
|
||||
hasCR := false
|
||||
end := offs
|
||||
inQuote := false
|
||||
loop:
|
||||
for inQuote || s.ch >= 0 && s.ch != '\n' && s.ch != ';' && s.ch != '#' {
|
||||
ch := s.ch
|
||||
s.next()
|
||||
switch {
|
||||
case inQuote && ch == '\\':
|
||||
s.scanEscape(true)
|
||||
case !inQuote && ch == '\\':
|
||||
if s.ch == '\r' {
|
||||
hasCR = true
|
||||
s.next()
|
||||
}
|
||||
if s.ch != '\n' {
|
||||
s.error(offs, "unquoted '\\' must be followed by new line")
|
||||
break loop
|
||||
}
|
||||
s.next()
|
||||
case ch == '"':
|
||||
inQuote = !inQuote
|
||||
case ch == '\r':
|
||||
hasCR = true
|
||||
case ch < 0 || inQuote && ch == '\n':
|
||||
s.error(offs, "string not terminated")
|
||||
break loop
|
||||
}
|
||||
if inQuote || !isWhiteSpace(ch) {
|
||||
end = s.offset
|
||||
}
|
||||
}
|
||||
|
||||
lit := s.src[offs:end]
|
||||
if hasCR {
|
||||
lit = stripCR(lit)
|
||||
}
|
||||
|
||||
return string(lit)
|
||||
}
|
||||
|
||||
func isWhiteSpace(ch rune) bool {
|
||||
return ch == ' ' || ch == '\t' || ch == '\r'
|
||||
}
|
||||
|
||||
func (s *Scanner) skipWhitespace() {
|
||||
for isWhiteSpace(s.ch) {
|
||||
s.next()
|
||||
}
|
||||
}
|
||||
|
||||
// Scan scans the next token and returns the token position, the token,
|
||||
// and its literal string if applicable. The source end is indicated by
|
||||
// token.EOF.
|
||||
//
|
||||
// If the returned token is a literal (token.IDENT, token.STRING) or
|
||||
// token.COMMENT, the literal string has the corresponding value.
|
||||
//
|
||||
// If the returned token is token.ILLEGAL, the literal string is the
|
||||
// offending character.
|
||||
//
|
||||
// In all other cases, Scan returns an empty literal string.
|
||||
//
|
||||
// For more tolerant parsing, Scan will return a valid token if
|
||||
// possible even if a syntax error was encountered. Thus, even
|
||||
// if the resulting token sequence contains no illegal tokens,
|
||||
// a client may not assume that no error occurred. Instead it
|
||||
// must check the scanner's ErrorCount or the number of calls
|
||||
// of the error handler, if there was one installed.
|
||||
//
|
||||
// Scan adds line information to the file added to the file
|
||||
// set with Init. Token positions are relative to that file
|
||||
// and thus relative to the file set.
|
||||
//
|
||||
func (s *Scanner) Scan() (pos token.Pos, tok token.Token, lit string) {
|
||||
scanAgain:
|
||||
s.skipWhitespace()
|
||||
|
||||
// current token start
|
||||
pos = s.file.Pos(s.offset)
|
||||
|
||||
// determine token value
|
||||
switch ch := s.ch; {
|
||||
case s.nextVal:
|
||||
lit = s.scanValString()
|
||||
tok = token.STRING
|
||||
s.nextVal = false
|
||||
case isLetter(ch):
|
||||
lit = s.scanIdentifier()
|
||||
tok = token.IDENT
|
||||
default:
|
||||
s.next() // always make progress
|
||||
switch ch {
|
||||
case -1:
|
||||
tok = token.EOF
|
||||
case '\n':
|
||||
tok = token.EOL
|
||||
case '"':
|
||||
tok = token.STRING
|
||||
lit = s.scanString()
|
||||
case '[':
|
||||
tok = token.LBRACK
|
||||
case ']':
|
||||
tok = token.RBRACK
|
||||
case ';', '#':
|
||||
// comment
|
||||
lit = s.scanComment()
|
||||
if s.mode&ScanComments == 0 {
|
||||
// skip comment
|
||||
goto scanAgain
|
||||
}
|
||||
tok = token.COMMENT
|
||||
case '=':
|
||||
tok = token.ASSIGN
|
||||
s.nextVal = true
|
||||
default:
|
||||
s.error(s.file.Offset(pos), fmt.Sprintf("illegal character %#U", ch))
|
||||
tok = token.ILLEGAL
|
||||
lit = string(ch)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
417
Godeps/_workspace/src/code.google.com/p/gcfg/scanner/scanner_test.go
generated
vendored
Normal file
417
Godeps/_workspace/src/code.google.com/p/gcfg/scanner/scanner_test.go
generated
vendored
Normal file
|
@ -0,0 +1,417 @@
|
|||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package scanner
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
import (
|
||||
"code.google.com/p/gcfg/token"
|
||||
)
|
||||
|
||||
var fset = token.NewFileSet()
|
||||
|
||||
const /* class */ (
|
||||
special = iota
|
||||
literal
|
||||
operator
|
||||
)
|
||||
|
||||
func tokenclass(tok token.Token) int {
|
||||
switch {
|
||||
case tok.IsLiteral():
|
||||
return literal
|
||||
case tok.IsOperator():
|
||||
return operator
|
||||
}
|
||||
return special
|
||||
}
|
||||
|
||||
type elt struct {
|
||||
tok token.Token
|
||||
lit string
|
||||
class int
|
||||
pre string
|
||||
suf string
|
||||
}
|
||||
|
||||
var tokens = [...]elt{
|
||||
// Special tokens
|
||||
{token.COMMENT, "; a comment", special, "", "\n"},
|
||||
{token.COMMENT, "# a comment", special, "", "\n"},
|
||||
|
||||
// Operators and delimiters
|
||||
{token.ASSIGN, "=", operator, "", "value"},
|
||||
{token.LBRACK, "[", operator, "", ""},
|
||||
{token.RBRACK, "]", operator, "", ""},
|
||||
{token.EOL, "\n", operator, "", ""},
|
||||
|
||||
// Identifiers
|
||||
{token.IDENT, "foobar", literal, "", ""},
|
||||
{token.IDENT, "a۰۱۸", literal, "", ""},
|
||||
{token.IDENT, "foo६४", literal, "", ""},
|
||||
{token.IDENT, "bar9876", literal, "", ""},
|
||||
{token.IDENT, "foo-bar", literal, "", ""},
|
||||
{token.IDENT, "foo", literal, ";\n", ""},
|
||||
// String literals (subsection names)
|
||||
{token.STRING, `"foobar"`, literal, "", ""},
|
||||
{token.STRING, `"\""`, literal, "", ""},
|
||||
// String literals (values)
|
||||
{token.STRING, `"\n"`, literal, "=", ""},
|
||||
{token.STRING, `"foobar"`, literal, "=", ""},
|
||||
{token.STRING, `"foo\nbar"`, literal, "=", ""},
|
||||
{token.STRING, `"foo\"bar"`, literal, "=", ""},
|
||||
{token.STRING, `"foo\\bar"`, literal, "=", ""},
|
||||
{token.STRING, `"foobar"`, literal, "=", ""},
|
||||
{token.STRING, `"foobar"`, literal, "= ", ""},
|
||||
{token.STRING, `"foobar"`, literal, "=", "\n"},
|
||||
{token.STRING, `"foobar"`, literal, "=", ";"},
|
||||
{token.STRING, `"foobar"`, literal, "=", " ;"},
|
||||
{token.STRING, `"foobar"`, literal, "=", "#"},
|
||||
{token.STRING, `"foobar"`, literal, "=", " #"},
|
||||
{token.STRING, "foobar", literal, "=", ""},
|
||||
{token.STRING, "foobar", literal, "= ", ""},
|
||||
{token.STRING, "foobar", literal, "=", " "},
|
||||
{token.STRING, `"foo" "bar"`, literal, "=", " "},
|
||||
{token.STRING, "foo\\\nbar", literal, "=", ""},
|
||||
{token.STRING, "foo\\\r\nbar", literal, "=", ""},
|
||||
}
|
||||
|
||||
const whitespace = " \t \n\n\n" // to separate tokens
|
||||
|
||||
var source = func() []byte {
|
||||
var src []byte
|
||||
for _, t := range tokens {
|
||||
src = append(src, t.pre...)
|
||||
src = append(src, t.lit...)
|
||||
src = append(src, t.suf...)
|
||||
src = append(src, whitespace...)
|
||||
}
|
||||
return src
|
||||
}()
|
||||
|
||||
func newlineCount(s string) int {
|
||||
n := 0
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] == '\n' {
|
||||
n++
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func checkPos(t *testing.T, lit string, p token.Pos, expected token.Position) {
|
||||
pos := fset.Position(p)
|
||||
if pos.Filename != expected.Filename {
|
||||
t.Errorf("bad filename for %q: got %s, expected %s", lit, pos.Filename, expected.Filename)
|
||||
}
|
||||
if pos.Offset != expected.Offset {
|
||||
t.Errorf("bad position for %q: got %d, expected %d", lit, pos.Offset, expected.Offset)
|
||||
}
|
||||
if pos.Line != expected.Line {
|
||||
t.Errorf("bad line for %q: got %d, expected %d", lit, pos.Line, expected.Line)
|
||||
}
|
||||
if pos.Column != expected.Column {
|
||||
t.Errorf("bad column for %q: got %d, expected %d", lit, pos.Column, expected.Column)
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that calling Scan() provides the correct results.
|
||||
func TestScan(t *testing.T) {
|
||||
// make source
|
||||
src_linecount := newlineCount(string(source))
|
||||
whitespace_linecount := newlineCount(whitespace)
|
||||
|
||||
index := 0
|
||||
|
||||
// error handler
|
||||
eh := func(_ token.Position, msg string) {
|
||||
t.Errorf("%d: error handler called (msg = %s)", index, msg)
|
||||
}
|
||||
|
||||
// verify scan
|
||||
var s Scanner
|
||||
s.Init(fset.AddFile("", fset.Base(), len(source)), source, eh, ScanComments)
|
||||
// epos is the expected position
|
||||
epos := token.Position{
|
||||
Filename: "",
|
||||
Offset: 0,
|
||||
Line: 1,
|
||||
Column: 1,
|
||||
}
|
||||
for {
|
||||
pos, tok, lit := s.Scan()
|
||||
if lit == "" {
|
||||
// no literal value for non-literal tokens
|
||||
lit = tok.String()
|
||||
}
|
||||
e := elt{token.EOF, "", special, "", ""}
|
||||
if index < len(tokens) {
|
||||
e = tokens[index]
|
||||
}
|
||||
if tok == token.EOF {
|
||||
lit = "<EOF>"
|
||||
epos.Line = src_linecount
|
||||
epos.Column = 2
|
||||
}
|
||||
if e.pre != "" && strings.ContainsRune("=;#", rune(e.pre[0])) {
|
||||
epos.Column = 1
|
||||
checkPos(t, lit, pos, epos)
|
||||
var etok token.Token
|
||||
if e.pre[0] == '=' {
|
||||
etok = token.ASSIGN
|
||||
} else {
|
||||
etok = token.COMMENT
|
||||
}
|
||||
if tok != etok {
|
||||
t.Errorf("bad token for %q: got %q, expected %q", lit, tok, etok)
|
||||
}
|
||||
pos, tok, lit = s.Scan()
|
||||
}
|
||||
epos.Offset += len(e.pre)
|
||||
if tok != token.EOF {
|
||||
epos.Column = 1 + len(e.pre)
|
||||
}
|
||||
if e.pre != "" && e.pre[len(e.pre)-1] == '\n' {
|
||||
epos.Offset--
|
||||
epos.Column--
|
||||
checkPos(t, lit, pos, epos)
|
||||
if tok != token.EOL {
|
||||
t.Errorf("bad token for %q: got %q, expected %q", lit, tok, token.EOL)
|
||||
}
|
||||
epos.Line++
|
||||
epos.Offset++
|
||||
epos.Column = 1
|
||||
pos, tok, lit = s.Scan()
|
||||
}
|
||||
checkPos(t, lit, pos, epos)
|
||||
if tok != e.tok {
|
||||
t.Errorf("bad token for %q: got %q, expected %q", lit, tok, e.tok)
|
||||
}
|
||||
if e.tok.IsLiteral() {
|
||||
// no CRs in value string literals
|
||||
elit := e.lit
|
||||
if strings.ContainsRune(e.pre, '=') {
|
||||
elit = string(stripCR([]byte(elit)))
|
||||
epos.Offset += len(e.lit) - len(lit) // correct position
|
||||
}
|
||||
if lit != elit {
|
||||
t.Errorf("bad literal for %q: got %q, expected %q", lit, lit, elit)
|
||||
}
|
||||
}
|
||||
if tokenclass(tok) != e.class {
|
||||
t.Errorf("bad class for %q: got %d, expected %d", lit, tokenclass(tok), e.class)
|
||||
}
|
||||
epos.Offset += len(lit) + len(e.suf) + len(whitespace)
|
||||
epos.Line += newlineCount(lit) + newlineCount(e.suf) + whitespace_linecount
|
||||
index++
|
||||
if tok == token.EOF {
|
||||
break
|
||||
}
|
||||
if e.suf == "value" {
|
||||
pos, tok, lit = s.Scan()
|
||||
if tok != token.STRING {
|
||||
t.Errorf("bad token for %q: got %q, expected %q", lit, tok, token.STRING)
|
||||
}
|
||||
} else if strings.ContainsRune(e.suf, ';') || strings.ContainsRune(e.suf, '#') {
|
||||
pos, tok, lit = s.Scan()
|
||||
if tok != token.COMMENT {
|
||||
t.Errorf("bad token for %q: got %q, expected %q", lit, tok, token.COMMENT)
|
||||
}
|
||||
}
|
||||
// skip EOLs
|
||||
for i := 0; i < whitespace_linecount+newlineCount(e.suf); i++ {
|
||||
pos, tok, lit = s.Scan()
|
||||
if tok != token.EOL {
|
||||
t.Errorf("bad token for %q: got %q, expected %q", lit, tok, token.EOL)
|
||||
}
|
||||
}
|
||||
}
|
||||
if s.ErrorCount != 0 {
|
||||
t.Errorf("found %d errors", s.ErrorCount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestScanValStringEOF(t *testing.T) {
|
||||
var s Scanner
|
||||
src := "= value"
|
||||
f := fset.AddFile("src", fset.Base(), len(src))
|
||||
s.Init(f, []byte(src), nil, 0)
|
||||
s.Scan() // =
|
||||
s.Scan() // value
|
||||
_, tok, _ := s.Scan() // EOF
|
||||
if tok != token.EOF {
|
||||
t.Errorf("bad token: got %s, expected %s", tok, token.EOF)
|
||||
}
|
||||
if s.ErrorCount > 0 {
|
||||
t.Error("scanning error")
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that initializing the same scanner more then once works correctly.
|
||||
func TestInit(t *testing.T) {
|
||||
var s Scanner
|
||||
|
||||
// 1st init
|
||||
src1 := "\nname = value"
|
||||
f1 := fset.AddFile("src1", fset.Base(), len(src1))
|
||||
s.Init(f1, []byte(src1), nil, 0)
|
||||
if f1.Size() != len(src1) {
|
||||
t.Errorf("bad file size: got %d, expected %d", f1.Size(), len(src1))
|
||||
}
|
||||
s.Scan() // \n
|
||||
s.Scan() // name
|
||||
_, tok, _ := s.Scan() // =
|
||||
if tok != token.ASSIGN {
|
||||
t.Errorf("bad token: got %s, expected %s", tok, token.ASSIGN)
|
||||
}
|
||||
|
||||
// 2nd init
|
||||
src2 := "[section]"
|
||||
f2 := fset.AddFile("src2", fset.Base(), len(src2))
|
||||
s.Init(f2, []byte(src2), nil, 0)
|
||||
if f2.Size() != len(src2) {
|
||||
t.Errorf("bad file size: got %d, expected %d", f2.Size(), len(src2))
|
||||
}
|
||||
_, tok, _ = s.Scan() // [
|
||||
if tok != token.LBRACK {
|
||||
t.Errorf("bad token: got %s, expected %s", tok, token.LBRACK)
|
||||
}
|
||||
|
||||
if s.ErrorCount != 0 {
|
||||
t.Errorf("found %d errors", s.ErrorCount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStdErrorHandler(t *testing.T) {
|
||||
const src = "@\n" + // illegal character, cause an error
|
||||
"@ @\n" // two errors on the same line
|
||||
|
||||
var list ErrorList
|
||||
eh := func(pos token.Position, msg string) { list.Add(pos, msg) }
|
||||
|
||||
var s Scanner
|
||||
s.Init(fset.AddFile("File1", fset.Base(), len(src)), []byte(src), eh, 0)
|
||||
for {
|
||||
if _, tok, _ := s.Scan(); tok == token.EOF {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(list) != s.ErrorCount {
|
||||
t.Errorf("found %d errors, expected %d", len(list), s.ErrorCount)
|
||||
}
|
||||
|
||||
if len(list) != 3 {
|
||||
t.Errorf("found %d raw errors, expected 3", len(list))
|
||||
PrintError(os.Stderr, list)
|
||||
}
|
||||
|
||||
list.Sort()
|
||||
if len(list) != 3 {
|
||||
t.Errorf("found %d sorted errors, expected 3", len(list))
|
||||
PrintError(os.Stderr, list)
|
||||
}
|
||||
|
||||
list.RemoveMultiples()
|
||||
if len(list) != 2 {
|
||||
t.Errorf("found %d one-per-line errors, expected 2", len(list))
|
||||
PrintError(os.Stderr, list)
|
||||
}
|
||||
}
|
||||
|
||||
type errorCollector struct {
|
||||
cnt int // number of errors encountered
|
||||
msg string // last error message encountered
|
||||
pos token.Position // last error position encountered
|
||||
}
|
||||
|
||||
func checkError(t *testing.T, src string, tok token.Token, pos int, err string) {
|
||||
var s Scanner
|
||||
var h errorCollector
|
||||
eh := func(pos token.Position, msg string) {
|
||||
h.cnt++
|
||||
h.msg = msg
|
||||
h.pos = pos
|
||||
}
|
||||
s.Init(fset.AddFile("", fset.Base(), len(src)), []byte(src), eh, ScanComments)
|
||||
if src[0] == '=' {
|
||||
_, _, _ = s.Scan()
|
||||
}
|
||||
_, tok0, _ := s.Scan()
|
||||
_, tok1, _ := s.Scan()
|
||||
if tok0 != tok {
|
||||
t.Errorf("%q: got %s, expected %s", src, tok0, tok)
|
||||
}
|
||||
if tok1 != token.EOF {
|
||||
t.Errorf("%q: got %s, expected EOF", src, tok1)
|
||||
}
|
||||
cnt := 0
|
||||
if err != "" {
|
||||
cnt = 1
|
||||
}
|
||||
if h.cnt != cnt {
|
||||
t.Errorf("%q: got cnt %d, expected %d", src, h.cnt, cnt)
|
||||
}
|
||||
if h.msg != err {
|
||||
t.Errorf("%q: got msg %q, expected %q", src, h.msg, err)
|
||||
}
|
||||
if h.pos.Offset != pos {
|
||||
t.Errorf("%q: got offset %d, expected %d", src, h.pos.Offset, pos)
|
||||
}
|
||||
}
|
||||
|
||||
var errors = []struct {
|
||||
src string
|
||||
tok token.Token
|
||||
pos int
|
||||
err string
|
||||
}{
|
||||
{"\a", token.ILLEGAL, 0, "illegal character U+0007"},
|
||||
{"/", token.ILLEGAL, 0, "illegal character U+002F '/'"},
|
||||
{"_", token.ILLEGAL, 0, "illegal character U+005F '_'"},
|
||||
{`…`, token.ILLEGAL, 0, "illegal character U+2026 '…'"},
|
||||
{`""`, token.STRING, 0, ""},
|
||||
{`"`, token.STRING, 0, "string not terminated"},
|
||||
{"\"\n", token.STRING, 0, "string not terminated"},
|
||||
{`="`, token.STRING, 1, "string not terminated"},
|
||||
{"=\"\n", token.STRING, 1, "string not terminated"},
|
||||
{"=\\", token.STRING, 1, "unquoted '\\' must be followed by new line"},
|
||||
{"=\\\r", token.STRING, 1, "unquoted '\\' must be followed by new line"},
|
||||
{`"\z"`, token.STRING, 2, "unknown escape sequence"},
|
||||
{`"\a"`, token.STRING, 2, "unknown escape sequence"},
|
||||
{`"\b"`, token.STRING, 2, "unknown escape sequence"},
|
||||
{`"\f"`, token.STRING, 2, "unknown escape sequence"},
|
||||
{`"\r"`, token.STRING, 2, "unknown escape sequence"},
|
||||
{`"\t"`, token.STRING, 2, "unknown escape sequence"},
|
||||
{`"\v"`, token.STRING, 2, "unknown escape sequence"},
|
||||
{`"\0"`, token.STRING, 2, "unknown escape sequence"},
|
||||
}
|
||||
|
||||
func TestScanErrors(t *testing.T) {
|
||||
for _, e := range errors {
|
||||
checkError(t, e.src, e.tok, e.pos, e.err)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkScan(b *testing.B) {
|
||||
b.StopTimer()
|
||||
fset := token.NewFileSet()
|
||||
file := fset.AddFile("", fset.Base(), len(source))
|
||||
var s Scanner
|
||||
b.StartTimer()
|
||||
for i := b.N - 1; i >= 0; i-- {
|
||||
s.Init(file, source, nil, ScanComments)
|
||||
for {
|
||||
_, tok, _ := s.Scan()
|
||||
if tok == token.EOF {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
281
Godeps/_workspace/src/code.google.com/p/gcfg/set.go
generated
vendored
Normal file
281
Godeps/_workspace/src/code.google.com/p/gcfg/set.go
generated
vendored
Normal file
|
@ -0,0 +1,281 @@
|
|||
package gcfg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"code.google.com/p/gcfg/types"
|
||||
)
|
||||
|
||||
type tag struct {
|
||||
ident string
|
||||
intMode string
|
||||
}
|
||||
|
||||
func newTag(ts string) tag {
|
||||
t := tag{}
|
||||
s := strings.Split(ts, ",")
|
||||
t.ident = s[0]
|
||||
for _, tse := range s[1:] {
|
||||
if strings.HasPrefix(tse, "int=") {
|
||||
t.intMode = tse[len("int="):]
|
||||
}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func fieldFold(v reflect.Value, name string) (reflect.Value, tag) {
|
||||
var n string
|
||||
r0, _ := utf8.DecodeRuneInString(name)
|
||||
if unicode.IsLetter(r0) && !unicode.IsLower(r0) && !unicode.IsUpper(r0) {
|
||||
n = "X"
|
||||
}
|
||||
n += strings.Replace(name, "-", "_", -1)
|
||||
f, ok := v.Type().FieldByNameFunc(func(fieldName string) bool {
|
||||
if !v.FieldByName(fieldName).CanSet() {
|
||||
return false
|
||||
}
|
||||
f, _ := v.Type().FieldByName(fieldName)
|
||||
t := newTag(f.Tag.Get("gcfg"))
|
||||
if t.ident != "" {
|
||||
return strings.EqualFold(t.ident, name)
|
||||
}
|
||||
return strings.EqualFold(n, fieldName)
|
||||
})
|
||||
if !ok {
|
||||
return reflect.Value{}, tag{}
|
||||
}
|
||||
return v.FieldByName(f.Name), newTag(f.Tag.Get("gcfg"))
|
||||
}
|
||||
|
||||
type setter func(destp interface{}, blank bool, val string, t tag) error
|
||||
|
||||
var errUnsupportedType = fmt.Errorf("unsupported type")
|
||||
var errBlankUnsupported = fmt.Errorf("blank value not supported for type")
|
||||
|
||||
var setters = []setter{
|
||||
typeSetter, textUnmarshalerSetter, kindSetter, scanSetter,
|
||||
}
|
||||
|
||||
func textUnmarshalerSetter(d interface{}, blank bool, val string, t tag) error {
|
||||
dtu, ok := d.(textUnmarshaler)
|
||||
if !ok {
|
||||
return errUnsupportedType
|
||||
}
|
||||
if blank {
|
||||
return errBlankUnsupported
|
||||
}
|
||||
return dtu.UnmarshalText([]byte(val))
|
||||
}
|
||||
|
||||
func boolSetter(d interface{}, blank bool, val string, t tag) error {
|
||||
if blank {
|
||||
reflect.ValueOf(d).Elem().Set(reflect.ValueOf(true))
|
||||
return nil
|
||||
}
|
||||
b, err := types.ParseBool(val)
|
||||
if err == nil {
|
||||
reflect.ValueOf(d).Elem().Set(reflect.ValueOf(b))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func intMode(mode string) types.IntMode {
|
||||
var m types.IntMode
|
||||
if strings.ContainsAny(mode, "dD") {
|
||||
m |= types.Dec
|
||||
}
|
||||
if strings.ContainsAny(mode, "hH") {
|
||||
m |= types.Hex
|
||||
}
|
||||
if strings.ContainsAny(mode, "oO") {
|
||||
m |= types.Oct
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
var typeModes = map[reflect.Type]types.IntMode{
|
||||
reflect.TypeOf(int(0)): types.Dec | types.Hex,
|
||||
reflect.TypeOf(int8(0)): types.Dec | types.Hex,
|
||||
reflect.TypeOf(int16(0)): types.Dec | types.Hex,
|
||||
reflect.TypeOf(int32(0)): types.Dec | types.Hex,
|
||||
reflect.TypeOf(int64(0)): types.Dec | types.Hex,
|
||||
reflect.TypeOf(uint(0)): types.Dec | types.Hex,
|
||||
reflect.TypeOf(uint8(0)): types.Dec | types.Hex,
|
||||
reflect.TypeOf(uint16(0)): types.Dec | types.Hex,
|
||||
reflect.TypeOf(uint32(0)): types.Dec | types.Hex,
|
||||
reflect.TypeOf(uint64(0)): types.Dec | types.Hex,
|
||||
// use default mode (allow dec/hex/oct) for uintptr type
|
||||
reflect.TypeOf(big.Int{}): types.Dec | types.Hex,
|
||||
}
|
||||
|
||||
func intModeDefault(t reflect.Type) types.IntMode {
|
||||
m, ok := typeModes[t]
|
||||
if !ok {
|
||||
m = types.Dec | types.Hex | types.Oct
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func intSetter(d interface{}, blank bool, val string, t tag) error {
|
||||
if blank {
|
||||
return errBlankUnsupported
|
||||
}
|
||||
mode := intMode(t.intMode)
|
||||
if mode == 0 {
|
||||
mode = intModeDefault(reflect.TypeOf(d).Elem())
|
||||
}
|
||||
return types.ParseInt(d, val, mode)
|
||||
}
|
||||
|
||||
func stringSetter(d interface{}, blank bool, val string, t tag) error {
|
||||
if blank {
|
||||
return errBlankUnsupported
|
||||
}
|
||||
dsp, ok := d.(*string)
|
||||
if !ok {
|
||||
return errUnsupportedType
|
||||
}
|
||||
*dsp = val
|
||||
return nil
|
||||
}
|
||||
|
||||
var kindSetters = map[reflect.Kind]setter{
|
||||
reflect.String: stringSetter,
|
||||
reflect.Bool: boolSetter,
|
||||
reflect.Int: intSetter,
|
||||
reflect.Int8: intSetter,
|
||||
reflect.Int16: intSetter,
|
||||
reflect.Int32: intSetter,
|
||||
reflect.Int64: intSetter,
|
||||
reflect.Uint: intSetter,
|
||||
reflect.Uint8: intSetter,
|
||||
reflect.Uint16: intSetter,
|
||||
reflect.Uint32: intSetter,
|
||||
reflect.Uint64: intSetter,
|
||||
reflect.Uintptr: intSetter,
|
||||
}
|
||||
|
||||
var typeSetters = map[reflect.Type]setter{
|
||||
reflect.TypeOf(big.Int{}): intSetter,
|
||||
}
|
||||
|
||||
func typeSetter(d interface{}, blank bool, val string, tt tag) error {
|
||||
t := reflect.ValueOf(d).Type().Elem()
|
||||
setter, ok := typeSetters[t]
|
||||
if !ok {
|
||||
return errUnsupportedType
|
||||
}
|
||||
return setter(d, blank, val, tt)
|
||||
}
|
||||
|
||||
func kindSetter(d interface{}, blank bool, val string, tt tag) error {
|
||||
k := reflect.ValueOf(d).Type().Elem().Kind()
|
||||
setter, ok := kindSetters[k]
|
||||
if !ok {
|
||||
return errUnsupportedType
|
||||
}
|
||||
return setter(d, blank, val, tt)
|
||||
}
|
||||
|
||||
func scanSetter(d interface{}, blank bool, val string, tt tag) error {
|
||||
if blank {
|
||||
return errBlankUnsupported
|
||||
}
|
||||
return types.ScanFully(d, val, 'v')
|
||||
}
|
||||
|
||||
func set(cfg interface{}, sect, sub, name string, blank bool, value string) error {
|
||||
vPCfg := reflect.ValueOf(cfg)
|
||||
if vPCfg.Kind() != reflect.Ptr || vPCfg.Elem().Kind() != reflect.Struct {
|
||||
panic(fmt.Errorf("config must be a pointer to a struct"))
|
||||
}
|
||||
vCfg := vPCfg.Elem()
|
||||
vSect, _ := fieldFold(vCfg, sect)
|
||||
if !vSect.IsValid() {
|
||||
return fmt.Errorf("invalid section: section %q", sect)
|
||||
}
|
||||
if vSect.Kind() == reflect.Map {
|
||||
vst := vSect.Type()
|
||||
if vst.Key().Kind() != reflect.String ||
|
||||
vst.Elem().Kind() != reflect.Ptr ||
|
||||
vst.Elem().Elem().Kind() != reflect.Struct {
|
||||
panic(fmt.Errorf("map field for section must have string keys and "+
|
||||
" pointer-to-struct values: section %q", sect))
|
||||
}
|
||||
if vSect.IsNil() {
|
||||
vSect.Set(reflect.MakeMap(vst))
|
||||
}
|
||||
k := reflect.ValueOf(sub)
|
||||
pv := vSect.MapIndex(k)
|
||||
if !pv.IsValid() {
|
||||
vType := vSect.Type().Elem().Elem()
|
||||
pv = reflect.New(vType)
|
||||
vSect.SetMapIndex(k, pv)
|
||||
}
|
||||
vSect = pv.Elem()
|
||||
} else if vSect.Kind() != reflect.Struct {
|
||||
panic(fmt.Errorf("field for section must be a map or a struct: "+
|
||||
"section %q", sect))
|
||||
} else if sub != "" {
|
||||
return fmt.Errorf("invalid subsection: "+
|
||||
"section %q subsection %q", sect, sub)
|
||||
}
|
||||
vVar, t := fieldFold(vSect, name)
|
||||
if !vVar.IsValid() {
|
||||
return fmt.Errorf("invalid variable: "+
|
||||
"section %q subsection %q variable %q", sect, sub, name)
|
||||
}
|
||||
// vVal is either single-valued var, or newly allocated value within multi-valued var
|
||||
var vVal reflect.Value
|
||||
// multi-value if unnamed slice type
|
||||
isMulti := vVar.Type().Name() == "" && vVar.Kind() == reflect.Slice
|
||||
if isMulti && blank {
|
||||
vVar.Set(reflect.Zero(vVar.Type()))
|
||||
return nil
|
||||
}
|
||||
if isMulti {
|
||||
vVal = reflect.New(vVar.Type().Elem()).Elem()
|
||||
} else {
|
||||
vVal = vVar
|
||||
}
|
||||
isDeref := vVal.Type().Name() == "" && vVal.Type().Kind() == reflect.Ptr
|
||||
isNew := isDeref && vVal.IsNil()
|
||||
// vAddr is address of value to set (dereferenced & allocated as needed)
|
||||
var vAddr reflect.Value
|
||||
switch {
|
||||
case isNew:
|
||||
vAddr = reflect.New(vVal.Type().Elem())
|
||||
case isDeref && !isNew:
|
||||
vAddr = vVal
|
||||
default:
|
||||
vAddr = vVal.Addr()
|
||||
}
|
||||
vAddrI := vAddr.Interface()
|
||||
err, ok := error(nil), false
|
||||
for _, s := range setters {
|
||||
err = s(vAddrI, blank, value, t)
|
||||
if err == nil {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
if err != errUnsupportedType {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
// in case all setters returned errUnsupportedType
|
||||
return err
|
||||
}
|
||||
if isNew { // set reference if it was dereferenced and newly allocated
|
||||
vVal.Set(vAddr)
|
||||
}
|
||||
if isMulti { // append if multi-valued
|
||||
vVar.Set(reflect.Append(vVar, vVal))
|
||||
}
|
||||
return nil
|
||||
}
|
3
Godeps/_workspace/src/code.google.com/p/gcfg/testdata/gcfg_test.gcfg
generated
vendored
Normal file
3
Godeps/_workspace/src/code.google.com/p/gcfg/testdata/gcfg_test.gcfg
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
; Comment line
|
||||
[section]
|
||||
name=value # comment
|
3
Godeps/_workspace/src/code.google.com/p/gcfg/testdata/gcfg_unicode_test.gcfg
generated
vendored
Normal file
3
Godeps/_workspace/src/code.google.com/p/gcfg/testdata/gcfg_unicode_test.gcfg
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
; Comment line
|
||||
[甲]
|
||||
乙=丙 # comment
|
435
Godeps/_workspace/src/code.google.com/p/gcfg/token/position.go
generated
vendored
Normal file
435
Godeps/_workspace/src/code.google.com/p/gcfg/token/position.go
generated
vendored
Normal file
|
@ -0,0 +1,435 @@
|
|||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// TODO(gri) consider making this a separate package outside the go directory.
|
||||
|
||||
package token
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Positions
|
||||
|
||||
// Position describes an arbitrary source position
|
||||
// including the file, line, and column location.
|
||||
// A Position is valid if the line number is > 0.
|
||||
//
|
||||
type Position struct {
|
||||
Filename string // filename, if any
|
||||
Offset int // offset, starting at 0
|
||||
Line int // line number, starting at 1
|
||||
Column int // column number, starting at 1 (character count)
|
||||
}
|
||||
|
||||
// IsValid returns true if the position is valid.
|
||||
func (pos *Position) IsValid() bool { return pos.Line > 0 }
|
||||
|
||||
// String returns a string in one of several forms:
|
||||
//
|
||||
// file:line:column valid position with file name
|
||||
// line:column valid position without file name
|
||||
// file invalid position with file name
|
||||
// - invalid position without file name
|
||||
//
|
||||
func (pos Position) String() string {
|
||||
s := pos.Filename
|
||||
if pos.IsValid() {
|
||||
if s != "" {
|
||||
s += ":"
|
||||
}
|
||||
s += fmt.Sprintf("%d:%d", pos.Line, pos.Column)
|
||||
}
|
||||
if s == "" {
|
||||
s = "-"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Pos is a compact encoding of a source position within a file set.
|
||||
// It can be converted into a Position for a more convenient, but much
|
||||
// larger, representation.
|
||||
//
|
||||
// The Pos value for a given file is a number in the range [base, base+size],
|
||||
// where base and size are specified when adding the file to the file set via
|
||||
// AddFile.
|
||||
//
|
||||
// To create the Pos value for a specific source offset, first add
|
||||
// the respective file to the current file set (via FileSet.AddFile)
|
||||
// and then call File.Pos(offset) for that file. Given a Pos value p
|
||||
// for a specific file set fset, the corresponding Position value is
|
||||
// obtained by calling fset.Position(p).
|
||||
//
|
||||
// Pos values can be compared directly with the usual comparison operators:
|
||||
// If two Pos values p and q are in the same file, comparing p and q is
|
||||
// equivalent to comparing the respective source file offsets. If p and q
|
||||
// are in different files, p < q is true if the file implied by p was added
|
||||
// to the respective file set before the file implied by q.
|
||||
//
|
||||
type Pos int
|
||||
|
||||
// The zero value for Pos is NoPos; there is no file and line information
|
||||
// associated with it, and NoPos().IsValid() is false. NoPos is always
|
||||
// smaller than any other Pos value. The corresponding Position value
|
||||
// for NoPos is the zero value for Position.
|
||||
//
|
||||
const NoPos Pos = 0
|
||||
|
||||
// IsValid returns true if the position is valid.
|
||||
func (p Pos) IsValid() bool {
|
||||
return p != NoPos
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// File
|
||||
|
||||
// A File is a handle for a file belonging to a FileSet.
|
||||
// A File has a name, size, and line offset table.
|
||||
//
|
||||
type File struct {
|
||||
set *FileSet
|
||||
name string // file name as provided to AddFile
|
||||
base int // Pos value range for this file is [base...base+size]
|
||||
size int // file size as provided to AddFile
|
||||
|
||||
// lines and infos are protected by set.mutex
|
||||
lines []int
|
||||
infos []lineInfo
|
||||
}
|
||||
|
||||
// Name returns the file name of file f as registered with AddFile.
|
||||
func (f *File) Name() string {
|
||||
return f.name
|
||||
}
|
||||
|
||||
// Base returns the base offset of file f as registered with AddFile.
|
||||
func (f *File) Base() int {
|
||||
return f.base
|
||||
}
|
||||
|
||||
// Size returns the size of file f as registered with AddFile.
|
||||
func (f *File) Size() int {
|
||||
return f.size
|
||||
}
|
||||
|
||||
// LineCount returns the number of lines in file f.
|
||||
func (f *File) LineCount() int {
|
||||
f.set.mutex.RLock()
|
||||
n := len(f.lines)
|
||||
f.set.mutex.RUnlock()
|
||||
return n
|
||||
}
|
||||
|
||||
// AddLine adds the line offset for a new line.
|
||||
// The line offset must be larger than the offset for the previous line
|
||||
// and smaller than the file size; otherwise the line offset is ignored.
|
||||
//
|
||||
func (f *File) AddLine(offset int) {
|
||||
f.set.mutex.Lock()
|
||||
if i := len(f.lines); (i == 0 || f.lines[i-1] < offset) && offset < f.size {
|
||||
f.lines = append(f.lines, offset)
|
||||
}
|
||||
f.set.mutex.Unlock()
|
||||
}
|
||||
|
||||
// SetLines sets the line offsets for a file and returns true if successful.
|
||||
// The line offsets are the offsets of the first character of each line;
|
||||
// for instance for the content "ab\nc\n" the line offsets are {0, 3}.
|
||||
// An empty file has an empty line offset table.
|
||||
// Each line offset must be larger than the offset for the previous line
|
||||
// and smaller than the file size; otherwise SetLines fails and returns
|
||||
// false.
|
||||
//
|
||||
func (f *File) SetLines(lines []int) bool {
|
||||
// verify validity of lines table
|
||||
size := f.size
|
||||
for i, offset := range lines {
|
||||
if i > 0 && offset <= lines[i-1] || size <= offset {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// set lines table
|
||||
f.set.mutex.Lock()
|
||||
f.lines = lines
|
||||
f.set.mutex.Unlock()
|
||||
return true
|
||||
}
|
||||
|
||||
// SetLinesForContent sets the line offsets for the given file content.
|
||||
func (f *File) SetLinesForContent(content []byte) {
|
||||
var lines []int
|
||||
line := 0
|
||||
for offset, b := range content {
|
||||
if line >= 0 {
|
||||
lines = append(lines, line)
|
||||
}
|
||||
line = -1
|
||||
if b == '\n' {
|
||||
line = offset + 1
|
||||
}
|
||||
}
|
||||
|
||||
// set lines table
|
||||
f.set.mutex.Lock()
|
||||
f.lines = lines
|
||||
f.set.mutex.Unlock()
|
||||
}
|
||||
|
||||
// A lineInfo object describes alternative file and line number
|
||||
// information (such as provided via a //line comment in a .go
|
||||
// file) for a given file offset.
|
||||
type lineInfo struct {
|
||||
// fields are exported to make them accessible to gob
|
||||
Offset int
|
||||
Filename string
|
||||
Line int
|
||||
}
|
||||
|
||||
// AddLineInfo adds alternative file and line number information for
|
||||
// a given file offset. The offset must be larger than the offset for
|
||||
// the previously added alternative line info and smaller than the
|
||||
// file size; otherwise the information is ignored.
|
||||
//
|
||||
// AddLineInfo is typically used to register alternative position
|
||||
// information for //line filename:line comments in source files.
|
||||
//
|
||||
func (f *File) AddLineInfo(offset int, filename string, line int) {
|
||||
f.set.mutex.Lock()
|
||||
if i := len(f.infos); i == 0 || f.infos[i-1].Offset < offset && offset < f.size {
|
||||
f.infos = append(f.infos, lineInfo{offset, filename, line})
|
||||
}
|
||||
f.set.mutex.Unlock()
|
||||
}
|
||||
|
||||
// Pos returns the Pos value for the given file offset;
|
||||
// the offset must be <= f.Size().
|
||||
// f.Pos(f.Offset(p)) == p.
|
||||
//
|
||||
func (f *File) Pos(offset int) Pos {
|
||||
if offset > f.size {
|
||||
panic("illegal file offset")
|
||||
}
|
||||
return Pos(f.base + offset)
|
||||
}
|
||||
|
||||
// Offset returns the offset for the given file position p;
|
||||
// p must be a valid Pos value in that file.
|
||||
// f.Offset(f.Pos(offset)) == offset.
|
||||
//
|
||||
func (f *File) Offset(p Pos) int {
|
||||
if int(p) < f.base || int(p) > f.base+f.size {
|
||||
panic("illegal Pos value")
|
||||
}
|
||||
return int(p) - f.base
|
||||
}
|
||||
|
||||
// Line returns the line number for the given file position p;
|
||||
// p must be a Pos value in that file or NoPos.
|
||||
//
|
||||
func (f *File) Line(p Pos) int {
|
||||
// TODO(gri) this can be implemented much more efficiently
|
||||
return f.Position(p).Line
|
||||
}
|
||||
|
||||
func searchLineInfos(a []lineInfo, x int) int {
|
||||
return sort.Search(len(a), func(i int) bool { return a[i].Offset > x }) - 1
|
||||
}
|
||||
|
||||
// info returns the file name, line, and column number for a file offset.
|
||||
func (f *File) info(offset int) (filename string, line, column int) {
|
||||
filename = f.name
|
||||
if i := searchInts(f.lines, offset); i >= 0 {
|
||||
line, column = i+1, offset-f.lines[i]+1
|
||||
}
|
||||
if len(f.infos) > 0 {
|
||||
// almost no files have extra line infos
|
||||
if i := searchLineInfos(f.infos, offset); i >= 0 {
|
||||
alt := &f.infos[i]
|
||||
filename = alt.Filename
|
||||
if i := searchInts(f.lines, alt.Offset); i >= 0 {
|
||||
line += alt.Line - i - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (f *File) position(p Pos) (pos Position) {
|
||||
offset := int(p) - f.base
|
||||
pos.Offset = offset
|
||||
pos.Filename, pos.Line, pos.Column = f.info(offset)
|
||||
return
|
||||
}
|
||||
|
||||
// Position returns the Position value for the given file position p;
|
||||
// p must be a Pos value in that file or NoPos.
|
||||
//
|
||||
func (f *File) Position(p Pos) (pos Position) {
|
||||
if p != NoPos {
|
||||
if int(p) < f.base || int(p) > f.base+f.size {
|
||||
panic("illegal Pos value")
|
||||
}
|
||||
pos = f.position(p)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// FileSet
|
||||
|
||||
// A FileSet represents a set of source files.
|
||||
// Methods of file sets are synchronized; multiple goroutines
|
||||
// may invoke them concurrently.
|
||||
//
|
||||
type FileSet struct {
|
||||
mutex sync.RWMutex // protects the file set
|
||||
base int // base offset for the next file
|
||||
files []*File // list of files in the order added to the set
|
||||
last *File // cache of last file looked up
|
||||
}
|
||||
|
||||
// NewFileSet creates a new file set.
|
||||
func NewFileSet() *FileSet {
|
||||
s := new(FileSet)
|
||||
s.base = 1 // 0 == NoPos
|
||||
return s
|
||||
}
|
||||
|
||||
// Base returns the minimum base offset that must be provided to
|
||||
// AddFile when adding the next file.
|
||||
//
|
||||
func (s *FileSet) Base() int {
|
||||
s.mutex.RLock()
|
||||
b := s.base
|
||||
s.mutex.RUnlock()
|
||||
return b
|
||||
|
||||
}
|
||||
|
||||
// AddFile adds a new file with a given filename, base offset, and file size
|
||||
// to the file set s and returns the file. Multiple files may have the same
|
||||
// name. The base offset must not be smaller than the FileSet's Base(), and
|
||||
// size must not be negative.
|
||||
//
|
||||
// Adding the file will set the file set's Base() value to base + size + 1
|
||||
// as the minimum base value for the next file. The following relationship
|
||||
// exists between a Pos value p for a given file offset offs:
|
||||
//
|
||||
// int(p) = base + offs
|
||||
//
|
||||
// with offs in the range [0, size] and thus p in the range [base, base+size].
|
||||
// For convenience, File.Pos may be used to create file-specific position
|
||||
// values from a file offset.
|
||||
//
|
||||
func (s *FileSet) AddFile(filename string, base, size int) *File {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
if base < s.base || size < 0 {
|
||||
panic("illegal base or size")
|
||||
}
|
||||
// base >= s.base && size >= 0
|
||||
f := &File{s, filename, base, size, []int{0}, nil}
|
||||
base += size + 1 // +1 because EOF also has a position
|
||||
if base < 0 {
|
||||
panic("token.Pos offset overflow (> 2G of source code in file set)")
|
||||
}
|
||||
// add the file to the file set
|
||||
s.base = base
|
||||
s.files = append(s.files, f)
|
||||
s.last = f
|
||||
return f
|
||||
}
|
||||
|
||||
// Iterate calls f for the files in the file set in the order they were added
|
||||
// until f returns false.
|
||||
//
|
||||
func (s *FileSet) Iterate(f func(*File) bool) {
|
||||
for i := 0; ; i++ {
|
||||
var file *File
|
||||
s.mutex.RLock()
|
||||
if i < len(s.files) {
|
||||
file = s.files[i]
|
||||
}
|
||||
s.mutex.RUnlock()
|
||||
if file == nil || !f(file) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func searchFiles(a []*File, x int) int {
|
||||
return sort.Search(len(a), func(i int) bool { return a[i].base > x }) - 1
|
||||
}
|
||||
|
||||
func (s *FileSet) file(p Pos) *File {
|
||||
// common case: p is in last file
|
||||
if f := s.last; f != nil && f.base <= int(p) && int(p) <= f.base+f.size {
|
||||
return f
|
||||
}
|
||||
// p is not in last file - search all files
|
||||
if i := searchFiles(s.files, int(p)); i >= 0 {
|
||||
f := s.files[i]
|
||||
// f.base <= int(p) by definition of searchFiles
|
||||
if int(p) <= f.base+f.size {
|
||||
s.last = f
|
||||
return f
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// File returns the file that contains the position p.
|
||||
// If no such file is found (for instance for p == NoPos),
|
||||
// the result is nil.
|
||||
//
|
||||
func (s *FileSet) File(p Pos) (f *File) {
|
||||
if p != NoPos {
|
||||
s.mutex.RLock()
|
||||
f = s.file(p)
|
||||
s.mutex.RUnlock()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Position converts a Pos in the fileset into a general Position.
|
||||
func (s *FileSet) Position(p Pos) (pos Position) {
|
||||
if p != NoPos {
|
||||
s.mutex.RLock()
|
||||
if f := s.file(p); f != nil {
|
||||
pos = f.position(p)
|
||||
}
|
||||
s.mutex.RUnlock()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Helper functions
|
||||
|
||||
func searchInts(a []int, x int) int {
|
||||
// This function body is a manually inlined version of:
|
||||
//
|
||||
// return sort.Search(len(a), func(i int) bool { return a[i] > x }) - 1
|
||||
//
|
||||
// With better compiler optimizations, this may not be needed in the
|
||||
// future, but at the moment this change improves the go/printer
|
||||
// benchmark performance by ~30%. This has a direct impact on the
|
||||
// speed of gofmt and thus seems worthwhile (2011-04-29).
|
||||
// TODO(gri): Remove this when compilers have caught up.
|
||||
i, j := 0, len(a)
|
||||
for i < j {
|
||||
h := i + (j-i)/2 // avoid overflow when computing h
|
||||
// i ≤ h < j
|
||||
if a[h] <= x {
|
||||
i = h + 1
|
||||
} else {
|
||||
j = h
|
||||
}
|
||||
}
|
||||
return i - 1
|
||||
}
|
181
Godeps/_workspace/src/code.google.com/p/gcfg/token/position_test.go
generated
vendored
Normal file
181
Godeps/_workspace/src/code.google.com/p/gcfg/token/position_test.go
generated
vendored
Normal file
|
@ -0,0 +1,181 @@
|
|||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package token
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func checkPos(t *testing.T, msg string, p, q Position) {
|
||||
if p.Filename != q.Filename {
|
||||
t.Errorf("%s: expected filename = %q; got %q", msg, q.Filename, p.Filename)
|
||||
}
|
||||
if p.Offset != q.Offset {
|
||||
t.Errorf("%s: expected offset = %d; got %d", msg, q.Offset, p.Offset)
|
||||
}
|
||||
if p.Line != q.Line {
|
||||
t.Errorf("%s: expected line = %d; got %d", msg, q.Line, p.Line)
|
||||
}
|
||||
if p.Column != q.Column {
|
||||
t.Errorf("%s: expected column = %d; got %d", msg, q.Column, p.Column)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoPos(t *testing.T) {
|
||||
if NoPos.IsValid() {
|
||||
t.Errorf("NoPos should not be valid")
|
||||
}
|
||||
var fset *FileSet
|
||||
checkPos(t, "nil NoPos", fset.Position(NoPos), Position{})
|
||||
fset = NewFileSet()
|
||||
checkPos(t, "fset NoPos", fset.Position(NoPos), Position{})
|
||||
}
|
||||
|
||||
var tests = []struct {
|
||||
filename string
|
||||
source []byte // may be nil
|
||||
size int
|
||||
lines []int
|
||||
}{
|
||||
{"a", []byte{}, 0, []int{}},
|
||||
{"b", []byte("01234"), 5, []int{0}},
|
||||
{"c", []byte("\n\n\n\n\n\n\n\n\n"), 9, []int{0, 1, 2, 3, 4, 5, 6, 7, 8}},
|
||||
{"d", nil, 100, []int{0, 5, 10, 20, 30, 70, 71, 72, 80, 85, 90, 99}},
|
||||
{"e", nil, 777, []int{0, 80, 100, 120, 130, 180, 267, 455, 500, 567, 620}},
|
||||
{"f", []byte("package p\n\nimport \"fmt\""), 23, []int{0, 10, 11}},
|
||||
{"g", []byte("package p\n\nimport \"fmt\"\n"), 24, []int{0, 10, 11}},
|
||||
{"h", []byte("package p\n\nimport \"fmt\"\n "), 25, []int{0, 10, 11, 24}},
|
||||
}
|
||||
|
||||
func linecol(lines []int, offs int) (int, int) {
|
||||
prevLineOffs := 0
|
||||
for line, lineOffs := range lines {
|
||||
if offs < lineOffs {
|
||||
return line, offs - prevLineOffs + 1
|
||||
}
|
||||
prevLineOffs = lineOffs
|
||||
}
|
||||
return len(lines), offs - prevLineOffs + 1
|
||||
}
|
||||
|
||||
func verifyPositions(t *testing.T, fset *FileSet, f *File, lines []int) {
|
||||
for offs := 0; offs < f.Size(); offs++ {
|
||||
p := f.Pos(offs)
|
||||
offs2 := f.Offset(p)
|
||||
if offs2 != offs {
|
||||
t.Errorf("%s, Offset: expected offset %d; got %d", f.Name(), offs, offs2)
|
||||
}
|
||||
line, col := linecol(lines, offs)
|
||||
msg := fmt.Sprintf("%s (offs = %d, p = %d)", f.Name(), offs, p)
|
||||
checkPos(t, msg, f.Position(f.Pos(offs)), Position{f.Name(), offs, line, col})
|
||||
checkPos(t, msg, fset.Position(p), Position{f.Name(), offs, line, col})
|
||||
}
|
||||
}
|
||||
|
||||
func makeTestSource(size int, lines []int) []byte {
|
||||
src := make([]byte, size)
|
||||
for _, offs := range lines {
|
||||
if offs > 0 {
|
||||
src[offs-1] = '\n'
|
||||
}
|
||||
}
|
||||
return src
|
||||
}
|
||||
|
||||
func TestPositions(t *testing.T) {
|
||||
const delta = 7 // a non-zero base offset increment
|
||||
fset := NewFileSet()
|
||||
for _, test := range tests {
|
||||
// verify consistency of test case
|
||||
if test.source != nil && len(test.source) != test.size {
|
||||
t.Errorf("%s: inconsistent test case: expected file size %d; got %d", test.filename, test.size, len(test.source))
|
||||
}
|
||||
|
||||
// add file and verify name and size
|
||||
f := fset.AddFile(test.filename, fset.Base()+delta, test.size)
|
||||
if f.Name() != test.filename {
|
||||
t.Errorf("expected filename %q; got %q", test.filename, f.Name())
|
||||
}
|
||||
if f.Size() != test.size {
|
||||
t.Errorf("%s: expected file size %d; got %d", f.Name(), test.size, f.Size())
|
||||
}
|
||||
if fset.File(f.Pos(0)) != f {
|
||||
t.Errorf("%s: f.Pos(0) was not found in f", f.Name())
|
||||
}
|
||||
|
||||
// add lines individually and verify all positions
|
||||
for i, offset := range test.lines {
|
||||
f.AddLine(offset)
|
||||
if f.LineCount() != i+1 {
|
||||
t.Errorf("%s, AddLine: expected line count %d; got %d", f.Name(), i+1, f.LineCount())
|
||||
}
|
||||
// adding the same offset again should be ignored
|
||||
f.AddLine(offset)
|
||||
if f.LineCount() != i+1 {
|
||||
t.Errorf("%s, AddLine: expected unchanged line count %d; got %d", f.Name(), i+1, f.LineCount())
|
||||
}
|
||||
verifyPositions(t, fset, f, test.lines[0:i+1])
|
||||
}
|
||||
|
||||
// add lines with SetLines and verify all positions
|
||||
if ok := f.SetLines(test.lines); !ok {
|
||||
t.Errorf("%s: SetLines failed", f.Name())
|
||||
}
|
||||
if f.LineCount() != len(test.lines) {
|
||||
t.Errorf("%s, SetLines: expected line count %d; got %d", f.Name(), len(test.lines), f.LineCount())
|
||||
}
|
||||
verifyPositions(t, fset, f, test.lines)
|
||||
|
||||
// add lines with SetLinesForContent and verify all positions
|
||||
src := test.source
|
||||
if src == nil {
|
||||
// no test source available - create one from scratch
|
||||
src = makeTestSource(test.size, test.lines)
|
||||
}
|
||||
f.SetLinesForContent(src)
|
||||
if f.LineCount() != len(test.lines) {
|
||||
t.Errorf("%s, SetLinesForContent: expected line count %d; got %d", f.Name(), len(test.lines), f.LineCount())
|
||||
}
|
||||
verifyPositions(t, fset, f, test.lines)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLineInfo(t *testing.T) {
|
||||
fset := NewFileSet()
|
||||
f := fset.AddFile("foo", fset.Base(), 500)
|
||||
lines := []int{0, 42, 77, 100, 210, 220, 277, 300, 333, 401}
|
||||
// add lines individually and provide alternative line information
|
||||
for _, offs := range lines {
|
||||
f.AddLine(offs)
|
||||
f.AddLineInfo(offs, "bar", 42)
|
||||
}
|
||||
// verify positions for all offsets
|
||||
for offs := 0; offs <= f.Size(); offs++ {
|
||||
p := f.Pos(offs)
|
||||
_, col := linecol(lines, offs)
|
||||
msg := fmt.Sprintf("%s (offs = %d, p = %d)", f.Name(), offs, p)
|
||||
checkPos(t, msg, f.Position(f.Pos(offs)), Position{"bar", offs, 42, col})
|
||||
checkPos(t, msg, fset.Position(p), Position{"bar", offs, 42, col})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFiles(t *testing.T) {
|
||||
fset := NewFileSet()
|
||||
for i, test := range tests {
|
||||
fset.AddFile(test.filename, fset.Base(), test.size)
|
||||
j := 0
|
||||
fset.Iterate(func(f *File) bool {
|
||||
if f.Name() != tests[j].filename {
|
||||
t.Errorf("expected filename = %s; got %s", tests[j].filename, f.Name())
|
||||
}
|
||||
j++
|
||||
return true
|
||||
})
|
||||
if j != i+1 {
|
||||
t.Errorf("expected %d files; got %d", i+1, j)
|
||||
}
|
||||
}
|
||||
}
|
56
Godeps/_workspace/src/code.google.com/p/gcfg/token/serialize.go
generated
vendored
Normal file
56
Godeps/_workspace/src/code.google.com/p/gcfg/token/serialize.go
generated
vendored
Normal file
|
@ -0,0 +1,56 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package token
|
||||
|
||||
type serializedFile struct {
|
||||
// fields correspond 1:1 to fields with same (lower-case) name in File
|
||||
Name string
|
||||
Base int
|
||||
Size int
|
||||
Lines []int
|
||||
Infos []lineInfo
|
||||
}
|
||||
|
||||
type serializedFileSet struct {
|
||||
Base int
|
||||
Files []serializedFile
|
||||
}
|
||||
|
||||
// Read calls decode to deserialize a file set into s; s must not be nil.
|
||||
func (s *FileSet) Read(decode func(interface{}) error) error {
|
||||
var ss serializedFileSet
|
||||
if err := decode(&ss); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.mutex.Lock()
|
||||
s.base = ss.Base
|
||||
files := make([]*File, len(ss.Files))
|
||||
for i := 0; i < len(ss.Files); i++ {
|
||||
f := &ss.Files[i]
|
||||
files[i] = &File{s, f.Name, f.Base, f.Size, f.Lines, f.Infos}
|
||||
}
|
||||
s.files = files
|
||||
s.last = nil
|
||||
s.mutex.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write calls encode to serialize the file set s.
|
||||
func (s *FileSet) Write(encode func(interface{}) error) error {
|
||||
var ss serializedFileSet
|
||||
|
||||
s.mutex.Lock()
|
||||
ss.Base = s.base
|
||||
files := make([]serializedFile, len(s.files))
|
||||
for i, f := range s.files {
|
||||
files[i] = serializedFile{f.name, f.base, f.size, f.lines, f.infos}
|
||||
}
|
||||
ss.Files = files
|
||||
s.mutex.Unlock()
|
||||
|
||||
return encode(ss)
|
||||
}
|
111
Godeps/_workspace/src/code.google.com/p/gcfg/token/serialize_test.go
generated
vendored
Normal file
111
Godeps/_workspace/src/code.google.com/p/gcfg/token/serialize_test.go
generated
vendored
Normal file
|
@ -0,0 +1,111 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package token
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// equal returns nil if p and q describe the same file set;
|
||||
// otherwise it returns an error describing the discrepancy.
|
||||
func equal(p, q *FileSet) error {
|
||||
if p == q {
|
||||
// avoid deadlock if p == q
|
||||
return nil
|
||||
}
|
||||
|
||||
// not strictly needed for the test
|
||||
p.mutex.Lock()
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
defer p.mutex.Unlock()
|
||||
|
||||
if p.base != q.base {
|
||||
return fmt.Errorf("different bases: %d != %d", p.base, q.base)
|
||||
}
|
||||
|
||||
if len(p.files) != len(q.files) {
|
||||
return fmt.Errorf("different number of files: %d != %d", len(p.files), len(q.files))
|
||||
}
|
||||
|
||||
for i, f := range p.files {
|
||||
g := q.files[i]
|
||||
if f.set != p {
|
||||
return fmt.Errorf("wrong fileset for %q", f.name)
|
||||
}
|
||||
if g.set != q {
|
||||
return fmt.Errorf("wrong fileset for %q", g.name)
|
||||
}
|
||||
if f.name != g.name {
|
||||
return fmt.Errorf("different filenames: %q != %q", f.name, g.name)
|
||||
}
|
||||
if f.base != g.base {
|
||||
return fmt.Errorf("different base for %q: %d != %d", f.name, f.base, g.base)
|
||||
}
|
||||
if f.size != g.size {
|
||||
return fmt.Errorf("different size for %q: %d != %d", f.name, f.size, g.size)
|
||||
}
|
||||
for j, l := range f.lines {
|
||||
m := g.lines[j]
|
||||
if l != m {
|
||||
return fmt.Errorf("different offsets for %q", f.name)
|
||||
}
|
||||
}
|
||||
for j, l := range f.infos {
|
||||
m := g.infos[j]
|
||||
if l.Offset != m.Offset || l.Filename != m.Filename || l.Line != m.Line {
|
||||
return fmt.Errorf("different infos for %q", f.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we don't care about .last - it's just a cache
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkSerialize(t *testing.T, p *FileSet) {
|
||||
var buf bytes.Buffer
|
||||
encode := func(x interface{}) error {
|
||||
return gob.NewEncoder(&buf).Encode(x)
|
||||
}
|
||||
if err := p.Write(encode); err != nil {
|
||||
t.Errorf("writing fileset failed: %s", err)
|
||||
return
|
||||
}
|
||||
q := NewFileSet()
|
||||
decode := func(x interface{}) error {
|
||||
return gob.NewDecoder(&buf).Decode(x)
|
||||
}
|
||||
if err := q.Read(decode); err != nil {
|
||||
t.Errorf("reading fileset failed: %s", err)
|
||||
return
|
||||
}
|
||||
if err := equal(p, q); err != nil {
|
||||
t.Errorf("filesets not identical: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSerialization(t *testing.T) {
|
||||
p := NewFileSet()
|
||||
checkSerialize(t, p)
|
||||
// add some files
|
||||
for i := 0; i < 10; i++ {
|
||||
f := p.AddFile(fmt.Sprintf("file%d", i), p.Base()+i, i*100)
|
||||
checkSerialize(t, p)
|
||||
// add some lines and alternative file infos
|
||||
line := 1000
|
||||
for offs := 0; offs < f.Size(); offs += 40 + i {
|
||||
f.AddLine(offs)
|
||||
if offs%7 == 0 {
|
||||
f.AddLineInfo(offs, fmt.Sprintf("file%d", offs), line)
|
||||
line += 33
|
||||
}
|
||||
}
|
||||
checkSerialize(t, p)
|
||||
}
|
||||
}
|
83
Godeps/_workspace/src/code.google.com/p/gcfg/token/token.go
generated
vendored
Normal file
83
Godeps/_workspace/src/code.google.com/p/gcfg/token/token.go
generated
vendored
Normal file
|
@ -0,0 +1,83 @@
|
|||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package token defines constants representing the lexical tokens of the gcfg
|
||||
// configuration syntax and basic operations on tokens (printing, predicates).
|
||||
//
|
||||
// Note that the API for the token package may change to accommodate new
|
||||
// features or implementation changes in gcfg.
|
||||
//
|
||||
package token
|
||||
|
||||
import "strconv"
|
||||
|
||||
// Token is the set of lexical tokens of the gcfg configuration syntax.
|
||||
type Token int
|
||||
|
||||
// The list of tokens.
|
||||
const (
|
||||
// Special tokens
|
||||
ILLEGAL Token = iota
|
||||
EOF
|
||||
COMMENT
|
||||
|
||||
literal_beg
|
||||
// Identifiers and basic type literals
|
||||
// (these tokens stand for classes of literals)
|
||||
IDENT // section-name, variable-name
|
||||
STRING // "subsection-name", variable value
|
||||
literal_end
|
||||
|
||||
operator_beg
|
||||
// Operators and delimiters
|
||||
ASSIGN // =
|
||||
LBRACK // [
|
||||
RBRACK // ]
|
||||
EOL // \n
|
||||
operator_end
|
||||
)
|
||||
|
||||
var tokens = [...]string{
|
||||
ILLEGAL: "ILLEGAL",
|
||||
|
||||
EOF: "EOF",
|
||||
COMMENT: "COMMENT",
|
||||
|
||||
IDENT: "IDENT",
|
||||
STRING: "STRING",
|
||||
|
||||
ASSIGN: "=",
|
||||
LBRACK: "[",
|
||||
RBRACK: "]",
|
||||
EOL: "\n",
|
||||
}
|
||||
|
||||
// String returns the string corresponding to the token tok.
|
||||
// For operators and delimiters, the string is the actual token character
|
||||
// sequence (e.g., for the token ASSIGN, the string is "="). For all other
|
||||
// tokens the string corresponds to the token constant name (e.g. for the
|
||||
// token IDENT, the string is "IDENT").
|
||||
//
|
||||
func (tok Token) String() string {
|
||||
s := ""
|
||||
if 0 <= tok && tok < Token(len(tokens)) {
|
||||
s = tokens[tok]
|
||||
}
|
||||
if s == "" {
|
||||
s = "token(" + strconv.Itoa(int(tok)) + ")"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Predicates
|
||||
|
||||
// IsLiteral returns true for tokens corresponding to identifiers
|
||||
// and basic type literals; it returns false otherwise.
|
||||
//
|
||||
func (tok Token) IsLiteral() bool { return literal_beg < tok && tok < literal_end }
|
||||
|
||||
// IsOperator returns true for tokens corresponding to operators and
|
||||
// delimiters; it returns false otherwise.
|
||||
//
|
||||
func (tok Token) IsOperator() bool { return operator_beg < tok && tok < operator_end }
|
23
Godeps/_workspace/src/code.google.com/p/gcfg/types/bool.go
generated
vendored
Normal file
23
Godeps/_workspace/src/code.google.com/p/gcfg/types/bool.go
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
package types
|
||||
|
||||
// BoolValues defines the name and value mappings for ParseBool.
|
||||
var BoolValues = map[string]interface{}{
|
||||
"true": true, "yes": true, "on": true, "1": true,
|
||||
"false": false, "no": false, "off": false, "0": false,
|
||||
}
|
||||
|
||||
var boolParser = func() *EnumParser {
|
||||
ep := &EnumParser{}
|
||||
ep.AddVals(BoolValues)
|
||||
return ep
|
||||
}()
|
||||
|
||||
// ParseBool parses bool values according to the definitions in BoolValues.
|
||||
// Parsing is case-insensitive.
|
||||
func ParseBool(s string) (bool, error) {
|
||||
v, err := boolParser.Parse(s)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return v.(bool), nil
|
||||
}
|
4
Godeps/_workspace/src/code.google.com/p/gcfg/types/doc.go
generated
vendored
Normal file
4
Godeps/_workspace/src/code.google.com/p/gcfg/types/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
// Package types defines helpers for type conversions.
|
||||
//
|
||||
// The API for this package is not finalized yet.
|
||||
package types
|
44
Godeps/_workspace/src/code.google.com/p/gcfg/types/enum.go
generated
vendored
Normal file
44
Godeps/_workspace/src/code.google.com/p/gcfg/types/enum.go
generated
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// EnumParser parses "enum" values; i.e. a predefined set of strings to
|
||||
// predefined values.
|
||||
type EnumParser struct {
|
||||
Type string // type name; if not set, use type of first value added
|
||||
CaseMatch bool // if true, matching of strings is case-sensitive
|
||||
// PrefixMatch bool
|
||||
vals map[string]interface{}
|
||||
}
|
||||
|
||||
// AddVals adds strings and values to an EnumParser.
|
||||
func (ep *EnumParser) AddVals(vals map[string]interface{}) {
|
||||
if ep.vals == nil {
|
||||
ep.vals = make(map[string]interface{})
|
||||
}
|
||||
for k, v := range vals {
|
||||
if ep.Type == "" {
|
||||
ep.Type = reflect.TypeOf(v).Name()
|
||||
}
|
||||
if !ep.CaseMatch {
|
||||
k = strings.ToLower(k)
|
||||
}
|
||||
ep.vals[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// Parse parses the string and returns the value or an error.
|
||||
func (ep EnumParser) Parse(s string) (interface{}, error) {
|
||||
if !ep.CaseMatch {
|
||||
s = strings.ToLower(s)
|
||||
}
|
||||
v, ok := ep.vals[s]
|
||||
if !ok {
|
||||
return false, fmt.Errorf("failed to parse %s %#q", ep.Type, s)
|
||||
}
|
||||
return v, nil
|
||||
}
|
29
Godeps/_workspace/src/code.google.com/p/gcfg/types/enum_test.go
generated
vendored
Normal file
29
Godeps/_workspace/src/code.google.com/p/gcfg/types/enum_test.go
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEnumParserBool(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
val string
|
||||
res bool
|
||||
ok bool
|
||||
}{
|
||||
{val: "tRuE", res: true, ok: true},
|
||||
{val: "False", res: false, ok: true},
|
||||
{val: "t", ok: false},
|
||||
} {
|
||||
b, err := ParseBool(tt.val)
|
||||
switch {
|
||||
case tt.ok && err != nil:
|
||||
t.Errorf("%q: got error %v, want %v", tt.val, err, tt.res)
|
||||
case !tt.ok && err == nil:
|
||||
t.Errorf("%q: got %v, want error", tt.val, b)
|
||||
case tt.ok && b != tt.res:
|
||||
t.Errorf("%q: got %v, want %v", tt.val, b, tt.res)
|
||||
default:
|
||||
t.Logf("%q: got %v, %v", tt.val, b, err)
|
||||
}
|
||||
}
|
||||
}
|
86
Godeps/_workspace/src/code.google.com/p/gcfg/types/int.go
generated
vendored
Normal file
86
Godeps/_workspace/src/code.google.com/p/gcfg/types/int.go
generated
vendored
Normal file
|
@ -0,0 +1,86 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// An IntMode is a mode for parsing integer values, representing a set of
|
||||
// accepted bases.
|
||||
type IntMode uint8
|
||||
|
||||
// IntMode values for ParseInt; can be combined using binary or.
|
||||
const (
|
||||
Dec IntMode = 1 << iota
|
||||
Hex
|
||||
Oct
|
||||
)
|
||||
|
||||
// String returns a string representation of IntMode; e.g. `IntMode(Dec|Hex)`.
|
||||
func (m IntMode) String() string {
|
||||
var modes []string
|
||||
if m&Dec != 0 {
|
||||
modes = append(modes, "Dec")
|
||||
}
|
||||
if m&Hex != 0 {
|
||||
modes = append(modes, "Hex")
|
||||
}
|
||||
if m&Oct != 0 {
|
||||
modes = append(modes, "Oct")
|
||||
}
|
||||
return "IntMode(" + strings.Join(modes, "|") + ")"
|
||||
}
|
||||
|
||||
var errIntAmbig = fmt.Errorf("ambiguous integer value; must include '0' prefix")
|
||||
|
||||
func prefix0(val string) bool {
|
||||
return strings.HasPrefix(val, "0") || strings.HasPrefix(val, "-0")
|
||||
}
|
||||
|
||||
func prefix0x(val string) bool {
|
||||
return strings.HasPrefix(val, "0x") || strings.HasPrefix(val, "-0x")
|
||||
}
|
||||
|
||||
// ParseInt parses val using mode into intptr, which must be a pointer to an
|
||||
// integer kind type. Non-decimal value require prefix `0` or `0x` in the cases
|
||||
// when mode permits ambiguity of base; otherwise the prefix can be omitted.
|
||||
func ParseInt(intptr interface{}, val string, mode IntMode) error {
|
||||
val = strings.TrimSpace(val)
|
||||
verb := byte(0)
|
||||
switch mode {
|
||||
case Dec:
|
||||
verb = 'd'
|
||||
case Dec + Hex:
|
||||
if prefix0x(val) {
|
||||
verb = 'v'
|
||||
} else {
|
||||
verb = 'd'
|
||||
}
|
||||
case Dec + Oct:
|
||||
if prefix0(val) && !prefix0x(val) {
|
||||
verb = 'v'
|
||||
} else {
|
||||
verb = 'd'
|
||||
}
|
||||
case Dec + Hex + Oct:
|
||||
verb = 'v'
|
||||
case Hex:
|
||||
if prefix0x(val) {
|
||||
verb = 'v'
|
||||
} else {
|
||||
verb = 'x'
|
||||
}
|
||||
case Oct:
|
||||
verb = 'o'
|
||||
case Hex + Oct:
|
||||
if prefix0(val) {
|
||||
verb = 'v'
|
||||
} else {
|
||||
return errIntAmbig
|
||||
}
|
||||
}
|
||||
if verb == 0 {
|
||||
panic("unsupported mode")
|
||||
}
|
||||
return ScanFully(intptr, val, verb)
|
||||
}
|
67
Godeps/_workspace/src/code.google.com/p/gcfg/types/int_test.go
generated
vendored
Normal file
67
Godeps/_workspace/src/code.google.com/p/gcfg/types/int_test.go
generated
vendored
Normal file
|
@ -0,0 +1,67 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func elem(p interface{}) interface{} {
|
||||
return reflect.ValueOf(p).Elem().Interface()
|
||||
}
|
||||
|
||||
func TestParseInt(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
val string
|
||||
mode IntMode
|
||||
exp interface{}
|
||||
ok bool
|
||||
}{
|
||||
{"0", Dec, int(0), true},
|
||||
{"10", Dec, int(10), true},
|
||||
{"-10", Dec, int(-10), true},
|
||||
{"x", Dec, int(0), false},
|
||||
{"0xa", Hex, int(0xa), true},
|
||||
{"a", Hex, int(0xa), true},
|
||||
{"10", Hex, int(0x10), true},
|
||||
{"-0xa", Hex, int(-0xa), true},
|
||||
{"0x", Hex, int(0x0), true}, // Scanf doesn't require digit behind 0x
|
||||
{"-0x", Hex, int(0x0), true}, // Scanf doesn't require digit behind 0x
|
||||
{"-a", Hex, int(-0xa), true},
|
||||
{"-10", Hex, int(-0x10), true},
|
||||
{"x", Hex, int(0), false},
|
||||
{"10", Oct, int(010), true},
|
||||
{"010", Oct, int(010), true},
|
||||
{"-10", Oct, int(-010), true},
|
||||
{"-010", Oct, int(-010), true},
|
||||
{"10", Dec | Hex, int(10), true},
|
||||
{"010", Dec | Hex, int(10), true},
|
||||
{"0x10", Dec | Hex, int(0x10), true},
|
||||
{"10", Dec | Oct, int(10), true},
|
||||
{"010", Dec | Oct, int(010), true},
|
||||
{"0x10", Dec | Oct, int(0), false},
|
||||
{"10", Hex | Oct, int(0), false}, // need prefix to distinguish Hex/Oct
|
||||
{"010", Hex | Oct, int(010), true},
|
||||
{"0x10", Hex | Oct, int(0x10), true},
|
||||
{"10", Dec | Hex | Oct, int(10), true},
|
||||
{"010", Dec | Hex | Oct, int(010), true},
|
||||
{"0x10", Dec | Hex | Oct, int(0x10), true},
|
||||
} {
|
||||
typ := reflect.TypeOf(tt.exp)
|
||||
res := reflect.New(typ).Interface()
|
||||
err := ParseInt(res, tt.val, tt.mode)
|
||||
switch {
|
||||
case tt.ok && err != nil:
|
||||
t.Errorf("ParseInt(%v, %#v, %v): fail; got error %v, want ok",
|
||||
typ, tt.val, tt.mode, err)
|
||||
case !tt.ok && err == nil:
|
||||
t.Errorf("ParseInt(%v, %#v, %v): fail; got %v, want error",
|
||||
typ, tt.val, tt.mode, elem(res))
|
||||
case tt.ok && !reflect.DeepEqual(elem(res), tt.exp):
|
||||
t.Errorf("ParseInt(%v, %#v, %v): fail; got %v, want %v",
|
||||
typ, tt.val, tt.mode, elem(res), tt.exp)
|
||||
default:
|
||||
t.Logf("ParseInt(%v, %#v, %s): pass; got %v, error %v",
|
||||
typ, tt.val, tt.mode, elem(res), err)
|
||||
}
|
||||
}
|
||||
}
|
23
Godeps/_workspace/src/code.google.com/p/gcfg/types/scan.go
generated
vendored
Normal file
23
Godeps/_workspace/src/code.google.com/p/gcfg/types/scan.go
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// ScanFully uses fmt.Sscanf with verb to fully scan val into ptr.
|
||||
func ScanFully(ptr interface{}, val string, verb byte) error {
|
||||
t := reflect.ValueOf(ptr).Elem().Type()
|
||||
// attempt to read extra bytes to make sure the value is consumed
|
||||
var b []byte
|
||||
n, err := fmt.Sscanf(val, "%"+string(verb)+"%s", ptr, &b)
|
||||
switch {
|
||||
case n < 1 || n == 1 && err != io.EOF:
|
||||
return fmt.Errorf("failed to parse %q as %v: %v", val, t, err)
|
||||
case n > 1:
|
||||
return fmt.Errorf("failed to parse %q as %v: extra characters %q", val, t, string(b))
|
||||
}
|
||||
// n == 1 && err == io.EOF
|
||||
return nil
|
||||
}
|
36
Godeps/_workspace/src/code.google.com/p/gcfg/types/scan_test.go
generated
vendored
Normal file
36
Godeps/_workspace/src/code.google.com/p/gcfg/types/scan_test.go
generated
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestScanFully(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
val string
|
||||
verb byte
|
||||
res interface{}
|
||||
ok bool
|
||||
}{
|
||||
{"a", 'v', int(0), false},
|
||||
{"0x", 'v', int(0), true},
|
||||
{"0x", 'd', int(0), false},
|
||||
} {
|
||||
d := reflect.New(reflect.TypeOf(tt.res)).Interface()
|
||||
err := ScanFully(d, tt.val, tt.verb)
|
||||
switch {
|
||||
case tt.ok && err != nil:
|
||||
t.Errorf("ScanFully(%T, %q, '%c'): want ok, got error %v",
|
||||
d, tt.val, tt.verb, err)
|
||||
case !tt.ok && err == nil:
|
||||
t.Errorf("ScanFully(%T, %q, '%c'): want error, got %v",
|
||||
d, tt.val, tt.verb, elem(d))
|
||||
case tt.ok && err == nil && !reflect.DeepEqual(tt.res, elem(d)):
|
||||
t.Errorf("ScanFully(%T, %q, '%c'): want %v, got %v",
|
||||
d, tt.val, tt.verb, tt.res, elem(d))
|
||||
default:
|
||||
t.Logf("ScanFully(%T, %q, '%c') = %v; *ptr==%v",
|
||||
d, tt.val, tt.verb, err, elem(d))
|
||||
}
|
||||
}
|
||||
}
|
43
Godeps/_workspace/src/github.com/golang/protobuf/proto/Makefile
generated
vendored
Normal file
43
Godeps/_workspace/src/github.com/golang/protobuf/proto/Makefile
generated
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
# Go support for Protocol Buffers - Google's data interchange format
|
||||
#
|
||||
# Copyright 2010 The Go Authors. All rights reserved.
|
||||
# https://github.com/golang/protobuf
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
install:
|
||||
go install
|
||||
|
||||
test: install generate-test-pbs
|
||||
go test
|
||||
|
||||
|
||||
generate-test-pbs:
|
||||
make install
|
||||
make -C testdata
|
||||
protoc --go_out=Mtestdata/test.proto=github.com/golang/protobuf/proto/testdata:. proto3_proto/proto3.proto
|
||||
make
|
2083
Godeps/_workspace/src/github.com/golang/protobuf/proto/all_test.go
generated
vendored
Normal file
2083
Godeps/_workspace/src/github.com/golang/protobuf/proto/all_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
212
Godeps/_workspace/src/github.com/golang/protobuf/proto/clone.go
generated
vendored
Normal file
212
Godeps/_workspace/src/github.com/golang/protobuf/proto/clone.go
generated
vendored
Normal file
|
@ -0,0 +1,212 @@
|
|||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Protocol buffer deep copy and merge.
|
||||
// TODO: MessageSet and RawMessage.
|
||||
|
||||
package proto
|
||||
|
||||
import (
|
||||
"log"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Clone returns a deep copy of a protocol buffer.
|
||||
func Clone(pb Message) Message {
|
||||
in := reflect.ValueOf(pb)
|
||||
if in.IsNil() {
|
||||
return pb
|
||||
}
|
||||
|
||||
out := reflect.New(in.Type().Elem())
|
||||
// out is empty so a merge is a deep copy.
|
||||
mergeStruct(out.Elem(), in.Elem())
|
||||
return out.Interface().(Message)
|
||||
}
|
||||
|
||||
// Merge merges src into dst.
|
||||
// Required and optional fields that are set in src will be set to that value in dst.
|
||||
// Elements of repeated fields will be appended.
|
||||
// Merge panics if src and dst are not the same type, or if dst is nil.
|
||||
func Merge(dst, src Message) {
|
||||
in := reflect.ValueOf(src)
|
||||
out := reflect.ValueOf(dst)
|
||||
if out.IsNil() {
|
||||
panic("proto: nil destination")
|
||||
}
|
||||
if in.Type() != out.Type() {
|
||||
// Explicit test prior to mergeStruct so that mistyped nils will fail
|
||||
panic("proto: type mismatch")
|
||||
}
|
||||
if in.IsNil() {
|
||||
// Merging nil into non-nil is a quiet no-op
|
||||
return
|
||||
}
|
||||
mergeStruct(out.Elem(), in.Elem())
|
||||
}
|
||||
|
||||
func mergeStruct(out, in reflect.Value) {
|
||||
sprop := GetProperties(in.Type())
|
||||
for i := 0; i < in.NumField(); i++ {
|
||||
f := in.Type().Field(i)
|
||||
if strings.HasPrefix(f.Name, "XXX_") {
|
||||
continue
|
||||
}
|
||||
mergeAny(out.Field(i), in.Field(i), false, sprop.Prop[i])
|
||||
}
|
||||
|
||||
if emIn, ok := in.Addr().Interface().(extendableProto); ok {
|
||||
emOut := out.Addr().Interface().(extendableProto)
|
||||
mergeExtension(emOut.ExtensionMap(), emIn.ExtensionMap())
|
||||
}
|
||||
|
||||
uf := in.FieldByName("XXX_unrecognized")
|
||||
if !uf.IsValid() {
|
||||
return
|
||||
}
|
||||
uin := uf.Bytes()
|
||||
if len(uin) > 0 {
|
||||
out.FieldByName("XXX_unrecognized").SetBytes(append([]byte(nil), uin...))
|
||||
}
|
||||
}
|
||||
|
||||
// mergeAny performs a merge between two values of the same type.
|
||||
// viaPtr indicates whether the values were indirected through a pointer (implying proto2).
|
||||
// prop is set if this is a struct field (it may be nil).
|
||||
func mergeAny(out, in reflect.Value, viaPtr bool, prop *Properties) {
|
||||
if in.Type() == protoMessageType {
|
||||
if !in.IsNil() {
|
||||
if out.IsNil() {
|
||||
out.Set(reflect.ValueOf(Clone(in.Interface().(Message))))
|
||||
} else {
|
||||
Merge(out.Interface().(Message), in.Interface().(Message))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
switch in.Kind() {
|
||||
case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64,
|
||||
reflect.String, reflect.Uint32, reflect.Uint64:
|
||||
if !viaPtr && isProto3Zero(in) {
|
||||
return
|
||||
}
|
||||
out.Set(in)
|
||||
case reflect.Map:
|
||||
if in.Len() == 0 {
|
||||
return
|
||||
}
|
||||
if out.IsNil() {
|
||||
out.Set(reflect.MakeMap(in.Type()))
|
||||
}
|
||||
// For maps with value types of *T or []byte we need to deep copy each value.
|
||||
elemKind := in.Type().Elem().Kind()
|
||||
for _, key := range in.MapKeys() {
|
||||
var val reflect.Value
|
||||
switch elemKind {
|
||||
case reflect.Ptr:
|
||||
val = reflect.New(in.Type().Elem().Elem())
|
||||
mergeAny(val, in.MapIndex(key), false, nil)
|
||||
case reflect.Slice:
|
||||
val = in.MapIndex(key)
|
||||
val = reflect.ValueOf(append([]byte{}, val.Bytes()...))
|
||||
default:
|
||||
val = in.MapIndex(key)
|
||||
}
|
||||
out.SetMapIndex(key, val)
|
||||
}
|
||||
case reflect.Ptr:
|
||||
if in.IsNil() {
|
||||
return
|
||||
}
|
||||
if out.IsNil() {
|
||||
out.Set(reflect.New(in.Elem().Type()))
|
||||
}
|
||||
mergeAny(out.Elem(), in.Elem(), true, nil)
|
||||
case reflect.Slice:
|
||||
if in.IsNil() {
|
||||
return
|
||||
}
|
||||
if in.Type().Elem().Kind() == reflect.Uint8 {
|
||||
// []byte is a scalar bytes field, not a repeated field.
|
||||
|
||||
// Edge case: if this is in a proto3 message, a zero length
|
||||
// bytes field is considered the zero value, and should not
|
||||
// be merged.
|
||||
if prop != nil && prop.proto3 && in.Len() == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Make a deep copy.
|
||||
// Append to []byte{} instead of []byte(nil) so that we never end up
|
||||
// with a nil result.
|
||||
out.SetBytes(append([]byte{}, in.Bytes()...))
|
||||
return
|
||||
}
|
||||
n := in.Len()
|
||||
if out.IsNil() {
|
||||
out.Set(reflect.MakeSlice(in.Type(), 0, n))
|
||||
}
|
||||
switch in.Type().Elem().Kind() {
|
||||
case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64,
|
||||
reflect.String, reflect.Uint32, reflect.Uint64:
|
||||
out.Set(reflect.AppendSlice(out, in))
|
||||
default:
|
||||
for i := 0; i < n; i++ {
|
||||
x := reflect.Indirect(reflect.New(in.Type().Elem()))
|
||||
mergeAny(x, in.Index(i), false, nil)
|
||||
out.Set(reflect.Append(out, x))
|
||||
}
|
||||
}
|
||||
case reflect.Struct:
|
||||
mergeStruct(out, in)
|
||||
default:
|
||||
// unknown type, so not a protocol buffer
|
||||
log.Printf("proto: don't know how to copy %v", in)
|
||||
}
|
||||
}
|
||||
|
||||
func mergeExtension(out, in map[int32]Extension) {
|
||||
for extNum, eIn := range in {
|
||||
eOut := Extension{desc: eIn.desc}
|
||||
if eIn.value != nil {
|
||||
v := reflect.New(reflect.TypeOf(eIn.value)).Elem()
|
||||
mergeAny(v, reflect.ValueOf(eIn.value), false, nil)
|
||||
eOut.value = v.Interface()
|
||||
}
|
||||
if eIn.enc != nil {
|
||||
eOut.enc = make([]byte, len(eIn.enc))
|
||||
copy(eOut.enc, eIn.enc)
|
||||
}
|
||||
|
||||
out[extNum] = eOut
|
||||
}
|
||||
}
|
245
Godeps/_workspace/src/github.com/golang/protobuf/proto/clone_test.go
generated
vendored
Normal file
245
Godeps/_workspace/src/github.com/golang/protobuf/proto/clone_test.go
generated
vendored
Normal file
|
@ -0,0 +1,245 @@
|
|||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package proto_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
|
||||
proto3pb "github.com/golang/protobuf/proto/proto3_proto"
|
||||
pb "github.com/golang/protobuf/proto/testdata"
|
||||
)
|
||||
|
||||
var cloneTestMessage = &pb.MyMessage{
|
||||
Count: proto.Int32(42),
|
||||
Name: proto.String("Dave"),
|
||||
Pet: []string{"bunny", "kitty", "horsey"},
|
||||
Inner: &pb.InnerMessage{
|
||||
Host: proto.String("niles"),
|
||||
Port: proto.Int32(9099),
|
||||
Connected: proto.Bool(true),
|
||||
},
|
||||
Others: []*pb.OtherMessage{
|
||||
{
|
||||
Value: []byte("some bytes"),
|
||||
},
|
||||
},
|
||||
Somegroup: &pb.MyMessage_SomeGroup{
|
||||
GroupField: proto.Int32(6),
|
||||
},
|
||||
RepBytes: [][]byte{[]byte("sham"), []byte("wow")},
|
||||
}
|
||||
|
||||
func init() {
|
||||
ext := &pb.Ext{
|
||||
Data: proto.String("extension"),
|
||||
}
|
||||
if err := proto.SetExtension(cloneTestMessage, pb.E_Ext_More, ext); err != nil {
|
||||
panic("SetExtension: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestClone(t *testing.T) {
|
||||
m := proto.Clone(cloneTestMessage).(*pb.MyMessage)
|
||||
if !proto.Equal(m, cloneTestMessage) {
|
||||
t.Errorf("Clone(%v) = %v", cloneTestMessage, m)
|
||||
}
|
||||
|
||||
// Verify it was a deep copy.
|
||||
*m.Inner.Port++
|
||||
if proto.Equal(m, cloneTestMessage) {
|
||||
t.Error("Mutating clone changed the original")
|
||||
}
|
||||
// Byte fields and repeated fields should be copied.
|
||||
if &m.Pet[0] == &cloneTestMessage.Pet[0] {
|
||||
t.Error("Pet: repeated field not copied")
|
||||
}
|
||||
if &m.Others[0] == &cloneTestMessage.Others[0] {
|
||||
t.Error("Others: repeated field not copied")
|
||||
}
|
||||
if &m.Others[0].Value[0] == &cloneTestMessage.Others[0].Value[0] {
|
||||
t.Error("Others[0].Value: bytes field not copied")
|
||||
}
|
||||
if &m.RepBytes[0] == &cloneTestMessage.RepBytes[0] {
|
||||
t.Error("RepBytes: repeated field not copied")
|
||||
}
|
||||
if &m.RepBytes[0][0] == &cloneTestMessage.RepBytes[0][0] {
|
||||
t.Error("RepBytes[0]: bytes field not copied")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCloneNil(t *testing.T) {
|
||||
var m *pb.MyMessage
|
||||
if c := proto.Clone(m); !proto.Equal(m, c) {
|
||||
t.Errorf("Clone(%v) = %v", m, c)
|
||||
}
|
||||
}
|
||||
|
||||
var mergeTests = []struct {
|
||||
src, dst, want proto.Message
|
||||
}{
|
||||
{
|
||||
src: &pb.MyMessage{
|
||||
Count: proto.Int32(42),
|
||||
},
|
||||
dst: &pb.MyMessage{
|
||||
Name: proto.String("Dave"),
|
||||
},
|
||||
want: &pb.MyMessage{
|
||||
Count: proto.Int32(42),
|
||||
Name: proto.String("Dave"),
|
||||
},
|
||||
},
|
||||
{
|
||||
src: &pb.MyMessage{
|
||||
Inner: &pb.InnerMessage{
|
||||
Host: proto.String("hey"),
|
||||
Connected: proto.Bool(true),
|
||||
},
|
||||
Pet: []string{"horsey"},
|
||||
Others: []*pb.OtherMessage{
|
||||
{
|
||||
Value: []byte("some bytes"),
|
||||
},
|
||||
},
|
||||
},
|
||||
dst: &pb.MyMessage{
|
||||
Inner: &pb.InnerMessage{
|
||||
Host: proto.String("niles"),
|
||||
Port: proto.Int32(9099),
|
||||
},
|
||||
Pet: []string{"bunny", "kitty"},
|
||||
Others: []*pb.OtherMessage{
|
||||
{
|
||||
Key: proto.Int64(31415926535),
|
||||
},
|
||||
{
|
||||
// Explicitly test a src=nil field
|
||||
Inner: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &pb.MyMessage{
|
||||
Inner: &pb.InnerMessage{
|
||||
Host: proto.String("hey"),
|
||||
Connected: proto.Bool(true),
|
||||
Port: proto.Int32(9099),
|
||||
},
|
||||
Pet: []string{"bunny", "kitty", "horsey"},
|
||||
Others: []*pb.OtherMessage{
|
||||
{
|
||||
Key: proto.Int64(31415926535),
|
||||
},
|
||||
{},
|
||||
{
|
||||
Value: []byte("some bytes"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
src: &pb.MyMessage{
|
||||
RepBytes: [][]byte{[]byte("wow")},
|
||||
},
|
||||
dst: &pb.MyMessage{
|
||||
Somegroup: &pb.MyMessage_SomeGroup{
|
||||
GroupField: proto.Int32(6),
|
||||
},
|
||||
RepBytes: [][]byte{[]byte("sham")},
|
||||
},
|
||||
want: &pb.MyMessage{
|
||||
Somegroup: &pb.MyMessage_SomeGroup{
|
||||
GroupField: proto.Int32(6),
|
||||
},
|
||||
RepBytes: [][]byte{[]byte("sham"), []byte("wow")},
|
||||
},
|
||||
},
|
||||
// Check that a scalar bytes field replaces rather than appends.
|
||||
{
|
||||
src: &pb.OtherMessage{Value: []byte("foo")},
|
||||
dst: &pb.OtherMessage{Value: []byte("bar")},
|
||||
want: &pb.OtherMessage{Value: []byte("foo")},
|
||||
},
|
||||
{
|
||||
src: &pb.MessageWithMap{
|
||||
NameMapping: map[int32]string{6: "Nigel"},
|
||||
MsgMapping: map[int64]*pb.FloatingPoint{
|
||||
0x4001: &pb.FloatingPoint{F: proto.Float64(2.0)},
|
||||
},
|
||||
ByteMapping: map[bool][]byte{true: []byte("wowsa")},
|
||||
},
|
||||
dst: &pb.MessageWithMap{
|
||||
NameMapping: map[int32]string{
|
||||
6: "Bruce", // should be overwritten
|
||||
7: "Andrew",
|
||||
},
|
||||
},
|
||||
want: &pb.MessageWithMap{
|
||||
NameMapping: map[int32]string{
|
||||
6: "Nigel",
|
||||
7: "Andrew",
|
||||
},
|
||||
MsgMapping: map[int64]*pb.FloatingPoint{
|
||||
0x4001: &pb.FloatingPoint{F: proto.Float64(2.0)},
|
||||
},
|
||||
ByteMapping: map[bool][]byte{true: []byte("wowsa")},
|
||||
},
|
||||
},
|
||||
// proto3 shouldn't merge zero values,
|
||||
// in the same way that proto2 shouldn't merge nils.
|
||||
{
|
||||
src: &proto3pb.Message{
|
||||
Name: "Aaron",
|
||||
Data: []byte(""), // zero value, but not nil
|
||||
},
|
||||
dst: &proto3pb.Message{
|
||||
HeightInCm: 176,
|
||||
Data: []byte("texas!"),
|
||||
},
|
||||
want: &proto3pb.Message{
|
||||
Name: "Aaron",
|
||||
HeightInCm: 176,
|
||||
Data: []byte("texas!"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestMerge(t *testing.T) {
|
||||
for _, m := range mergeTests {
|
||||
got := proto.Clone(m.dst)
|
||||
proto.Merge(got, m.src)
|
||||
if !proto.Equal(got, m.want) {
|
||||
t.Errorf("Merge(%v, %v)\n got %v\nwant %v\n", m.dst, m.src, got, m.want)
|
||||
}
|
||||
}
|
||||
}
|
827
Godeps/_workspace/src/github.com/golang/protobuf/proto/decode.go
generated
vendored
Normal file
827
Godeps/_workspace/src/github.com/golang/protobuf/proto/decode.go
generated
vendored
Normal file
|
@ -0,0 +1,827 @@
|
|||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package proto
|
||||
|
||||
/*
|
||||
* Routines for decoding protocol buffer data to construct in-memory representations.
|
||||
*/
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// errOverflow is returned when an integer is too large to be represented.
|
||||
var errOverflow = errors.New("proto: integer overflow")
|
||||
|
||||
// The fundamental decoders that interpret bytes on the wire.
|
||||
// Those that take integer types all return uint64 and are
|
||||
// therefore of type valueDecoder.
|
||||
|
||||
// DecodeVarint reads a varint-encoded integer from the slice.
|
||||
// It returns the integer and the number of bytes consumed, or
|
||||
// zero if there is not enough.
|
||||
// This is the format for the
|
||||
// int32, int64, uint32, uint64, bool, and enum
|
||||
// protocol buffer types.
|
||||
func DecodeVarint(buf []byte) (x uint64, n int) {
|
||||
// x, n already 0
|
||||
for shift := uint(0); shift < 64; shift += 7 {
|
||||
if n >= len(buf) {
|
||||
return 0, 0
|
||||
}
|
||||
b := uint64(buf[n])
|
||||
n++
|
||||
x |= (b & 0x7F) << shift
|
||||
if (b & 0x80) == 0 {
|
||||
return x, n
|
||||
}
|
||||
}
|
||||
|
||||
// The number is too large to represent in a 64-bit value.
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
// DecodeVarint reads a varint-encoded integer from the Buffer.
|
||||
// This is the format for the
|
||||
// int32, int64, uint32, uint64, bool, and enum
|
||||
// protocol buffer types.
|
||||
func (p *Buffer) DecodeVarint() (x uint64, err error) {
|
||||
// x, err already 0
|
||||
|
||||
i := p.index
|
||||
l := len(p.buf)
|
||||
|
||||
for shift := uint(0); shift < 64; shift += 7 {
|
||||
if i >= l {
|
||||
err = io.ErrUnexpectedEOF
|
||||
return
|
||||
}
|
||||
b := p.buf[i]
|
||||
i++
|
||||
x |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
p.index = i
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// The number is too large to represent in a 64-bit value.
|
||||
err = errOverflow
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeFixed64 reads a 64-bit integer from the Buffer.
|
||||
// This is the format for the
|
||||
// fixed64, sfixed64, and double protocol buffer types.
|
||||
func (p *Buffer) DecodeFixed64() (x uint64, err error) {
|
||||
// x, err already 0
|
||||
i := p.index + 8
|
||||
if i < 0 || i > len(p.buf) {
|
||||
err = io.ErrUnexpectedEOF
|
||||
return
|
||||
}
|
||||
p.index = i
|
||||
|
||||
x = uint64(p.buf[i-8])
|
||||
x |= uint64(p.buf[i-7]) << 8
|
||||
x |= uint64(p.buf[i-6]) << 16
|
||||
x |= uint64(p.buf[i-5]) << 24
|
||||
x |= uint64(p.buf[i-4]) << 32
|
||||
x |= uint64(p.buf[i-3]) << 40
|
||||
x |= uint64(p.buf[i-2]) << 48
|
||||
x |= uint64(p.buf[i-1]) << 56
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeFixed32 reads a 32-bit integer from the Buffer.
|
||||
// This is the format for the
|
||||
// fixed32, sfixed32, and float protocol buffer types.
|
||||
func (p *Buffer) DecodeFixed32() (x uint64, err error) {
|
||||
// x, err already 0
|
||||
i := p.index + 4
|
||||
if i < 0 || i > len(p.buf) {
|
||||
err = io.ErrUnexpectedEOF
|
||||
return
|
||||
}
|
||||
p.index = i
|
||||
|
||||
x = uint64(p.buf[i-4])
|
||||
x |= uint64(p.buf[i-3]) << 8
|
||||
x |= uint64(p.buf[i-2]) << 16
|
||||
x |= uint64(p.buf[i-1]) << 24
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeZigzag64 reads a zigzag-encoded 64-bit integer
|
||||
// from the Buffer.
|
||||
// This is the format used for the sint64 protocol buffer type.
|
||||
func (p *Buffer) DecodeZigzag64() (x uint64, err error) {
|
||||
x, err = p.DecodeVarint()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
x = (x >> 1) ^ uint64((int64(x&1)<<63)>>63)
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeZigzag32 reads a zigzag-encoded 32-bit integer
|
||||
// from the Buffer.
|
||||
// This is the format used for the sint32 protocol buffer type.
|
||||
func (p *Buffer) DecodeZigzag32() (x uint64, err error) {
|
||||
x, err = p.DecodeVarint()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
x = uint64((uint32(x) >> 1) ^ uint32((int32(x&1)<<31)>>31))
|
||||
return
|
||||
}
|
||||
|
||||
// These are not ValueDecoders: they produce an array of bytes or a string.
|
||||
// bytes, embedded messages
|
||||
|
||||
// DecodeRawBytes reads a count-delimited byte buffer from the Buffer.
|
||||
// This is the format used for the bytes protocol buffer
|
||||
// type and for embedded messages.
|
||||
func (p *Buffer) DecodeRawBytes(alloc bool) (buf []byte, err error) {
|
||||
n, err := p.DecodeVarint()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nb := int(n)
|
||||
if nb < 0 {
|
||||
return nil, fmt.Errorf("proto: bad byte length %d", nb)
|
||||
}
|
||||
end := p.index + nb
|
||||
if end < p.index || end > len(p.buf) {
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
if !alloc {
|
||||
// todo: check if can get more uses of alloc=false
|
||||
buf = p.buf[p.index:end]
|
||||
p.index += nb
|
||||
return
|
||||
}
|
||||
|
||||
buf = make([]byte, nb)
|
||||
copy(buf, p.buf[p.index:])
|
||||
p.index += nb
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeStringBytes reads an encoded string from the Buffer.
|
||||
// This is the format used for the proto2 string type.
|
||||
func (p *Buffer) DecodeStringBytes() (s string, err error) {
|
||||
buf, err := p.DecodeRawBytes(false)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return string(buf), nil
|
||||
}
|
||||
|
||||
// Skip the next item in the buffer. Its wire type is decoded and presented as an argument.
|
||||
// If the protocol buffer has extensions, and the field matches, add it as an extension.
|
||||
// Otherwise, if the XXX_unrecognized field exists, append the skipped data there.
|
||||
func (o *Buffer) skipAndSave(t reflect.Type, tag, wire int, base structPointer, unrecField field) error {
|
||||
oi := o.index
|
||||
|
||||
err := o.skip(t, tag, wire)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !unrecField.IsValid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
ptr := structPointer_Bytes(base, unrecField)
|
||||
|
||||
// Add the skipped field to struct field
|
||||
obuf := o.buf
|
||||
|
||||
o.buf = *ptr
|
||||
o.EncodeVarint(uint64(tag<<3 | wire))
|
||||
*ptr = append(o.buf, obuf[oi:o.index]...)
|
||||
|
||||
o.buf = obuf
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Skip the next item in the buffer. Its wire type is decoded and presented as an argument.
|
||||
func (o *Buffer) skip(t reflect.Type, tag, wire int) error {
|
||||
|
||||
var u uint64
|
||||
var err error
|
||||
|
||||
switch wire {
|
||||
case WireVarint:
|
||||
_, err = o.DecodeVarint()
|
||||
case WireFixed64:
|
||||
_, err = o.DecodeFixed64()
|
||||
case WireBytes:
|
||||
_, err = o.DecodeRawBytes(false)
|
||||
case WireFixed32:
|
||||
_, err = o.DecodeFixed32()
|
||||
case WireStartGroup:
|
||||
for {
|
||||
u, err = o.DecodeVarint()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
fwire := int(u & 0x7)
|
||||
if fwire == WireEndGroup {
|
||||
break
|
||||
}
|
||||
ftag := int(u >> 3)
|
||||
err = o.skip(t, ftag, fwire)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
err = fmt.Errorf("proto: can't skip unknown wire type %d for %s", wire, t)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Unmarshaler is the interface representing objects that can
|
||||
// unmarshal themselves. The method should reset the receiver before
|
||||
// decoding starts. The argument points to data that may be
|
||||
// overwritten, so implementations should not keep references to the
|
||||
// buffer.
|
||||
type Unmarshaler interface {
|
||||
Unmarshal([]byte) error
|
||||
}
|
||||
|
||||
// Unmarshal parses the protocol buffer representation in buf and places the
|
||||
// decoded result in pb. If the struct underlying pb does not match
|
||||
// the data in buf, the results can be unpredictable.
|
||||
//
|
||||
// Unmarshal resets pb before starting to unmarshal, so any
|
||||
// existing data in pb is always removed. Use UnmarshalMerge
|
||||
// to preserve and append to existing data.
|
||||
func Unmarshal(buf []byte, pb Message) error {
|
||||
pb.Reset()
|
||||
return UnmarshalMerge(buf, pb)
|
||||
}
|
||||
|
||||
// UnmarshalMerge parses the protocol buffer representation in buf and
|
||||
// writes the decoded result to pb. If the struct underlying pb does not match
|
||||
// the data in buf, the results can be unpredictable.
|
||||
//
|
||||
// UnmarshalMerge merges into existing data in pb.
|
||||
// Most code should use Unmarshal instead.
|
||||
func UnmarshalMerge(buf []byte, pb Message) error {
|
||||
// If the object can unmarshal itself, let it.
|
||||
if u, ok := pb.(Unmarshaler); ok {
|
||||
return u.Unmarshal(buf)
|
||||
}
|
||||
return NewBuffer(buf).Unmarshal(pb)
|
||||
}
|
||||
|
||||
// Unmarshal parses the protocol buffer representation in the
|
||||
// Buffer and places the decoded result in pb. If the struct
|
||||
// underlying pb does not match the data in the buffer, the results can be
|
||||
// unpredictable.
|
||||
func (p *Buffer) Unmarshal(pb Message) error {
|
||||
// If the object can unmarshal itself, let it.
|
||||
if u, ok := pb.(Unmarshaler); ok {
|
||||
err := u.Unmarshal(p.buf[p.index:])
|
||||
p.index = len(p.buf)
|
||||
return err
|
||||
}
|
||||
|
||||
typ, base, err := getbase(pb)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = p.unmarshalType(typ.Elem(), GetProperties(typ.Elem()), false, base)
|
||||
|
||||
if collectStats {
|
||||
stats.Decode++
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// unmarshalType does the work of unmarshaling a structure.
|
||||
func (o *Buffer) unmarshalType(st reflect.Type, prop *StructProperties, is_group bool, base structPointer) error {
|
||||
var state errorState
|
||||
required, reqFields := prop.reqCount, uint64(0)
|
||||
|
||||
var err error
|
||||
for err == nil && o.index < len(o.buf) {
|
||||
oi := o.index
|
||||
var u uint64
|
||||
u, err = o.DecodeVarint()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
wire := int(u & 0x7)
|
||||
if wire == WireEndGroup {
|
||||
if is_group {
|
||||
return nil // input is satisfied
|
||||
}
|
||||
return fmt.Errorf("proto: %s: wiretype end group for non-group", st)
|
||||
}
|
||||
tag := int(u >> 3)
|
||||
if tag <= 0 {
|
||||
return fmt.Errorf("proto: %s: illegal tag %d (wire type %d)", st, tag, wire)
|
||||
}
|
||||
fieldnum, ok := prop.decoderTags.get(tag)
|
||||
if !ok {
|
||||
// Maybe it's an extension?
|
||||
if prop.extendable {
|
||||
if e := structPointer_Interface(base, st).(extendableProto); isExtensionField(e, int32(tag)) {
|
||||
if err = o.skip(st, tag, wire); err == nil {
|
||||
ext := e.ExtensionMap()[int32(tag)] // may be missing
|
||||
ext.enc = append(ext.enc, o.buf[oi:o.index]...)
|
||||
e.ExtensionMap()[int32(tag)] = ext
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
err = o.skipAndSave(st, tag, wire, base, prop.unrecField)
|
||||
continue
|
||||
}
|
||||
p := prop.Prop[fieldnum]
|
||||
|
||||
if p.dec == nil {
|
||||
fmt.Fprintf(os.Stderr, "proto: no protobuf decoder for %s.%s\n", st, st.Field(fieldnum).Name)
|
||||
continue
|
||||
}
|
||||
dec := p.dec
|
||||
if wire != WireStartGroup && wire != p.WireType {
|
||||
if wire == WireBytes && p.packedDec != nil {
|
||||
// a packable field
|
||||
dec = p.packedDec
|
||||
} else {
|
||||
err = fmt.Errorf("proto: bad wiretype for field %s.%s: got wiretype %d, want %d", st, st.Field(fieldnum).Name, wire, p.WireType)
|
||||
continue
|
||||
}
|
||||
}
|
||||
decErr := dec(o, p, base)
|
||||
if decErr != nil && !state.shouldContinue(decErr, p) {
|
||||
err = decErr
|
||||
}
|
||||
if err == nil && p.Required {
|
||||
// Successfully decoded a required field.
|
||||
if tag <= 64 {
|
||||
// use bitmap for fields 1-64 to catch field reuse.
|
||||
var mask uint64 = 1 << uint64(tag-1)
|
||||
if reqFields&mask == 0 {
|
||||
// new required field
|
||||
reqFields |= mask
|
||||
required--
|
||||
}
|
||||
} else {
|
||||
// This is imprecise. It can be fooled by a required field
|
||||
// with a tag > 64 that is encoded twice; that's very rare.
|
||||
// A fully correct implementation would require allocating
|
||||
// a data structure, which we would like to avoid.
|
||||
required--
|
||||
}
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
if is_group {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if state.err != nil {
|
||||
return state.err
|
||||
}
|
||||
if required > 0 {
|
||||
// Not enough information to determine the exact field. If we use extra
|
||||
// CPU, we could determine the field only if the missing required field
|
||||
// has a tag <= 64 and we check reqFields.
|
||||
return &RequiredNotSetError{"{Unknown}"}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Individual type decoders
|
||||
// For each,
|
||||
// u is the decoded value,
|
||||
// v is a pointer to the field (pointer) in the struct
|
||||
|
||||
// Sizes of the pools to allocate inside the Buffer.
|
||||
// The goal is modest amortization and allocation
|
||||
// on at least 16-byte boundaries.
|
||||
const (
|
||||
boolPoolSize = 16
|
||||
uint32PoolSize = 8
|
||||
uint64PoolSize = 4
|
||||
)
|
||||
|
||||
// Decode a bool.
|
||||
func (o *Buffer) dec_bool(p *Properties, base structPointer) error {
|
||||
u, err := p.valDec(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(o.bools) == 0 {
|
||||
o.bools = make([]bool, boolPoolSize)
|
||||
}
|
||||
o.bools[0] = u != 0
|
||||
*structPointer_Bool(base, p.field) = &o.bools[0]
|
||||
o.bools = o.bools[1:]
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *Buffer) dec_proto3_bool(p *Properties, base structPointer) error {
|
||||
u, err := p.valDec(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*structPointer_BoolVal(base, p.field) = u != 0
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode an int32.
|
||||
func (o *Buffer) dec_int32(p *Properties, base structPointer) error {
|
||||
u, err := p.valDec(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
word32_Set(structPointer_Word32(base, p.field), o, uint32(u))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *Buffer) dec_proto3_int32(p *Properties, base structPointer) error {
|
||||
u, err := p.valDec(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
word32Val_Set(structPointer_Word32Val(base, p.field), uint32(u))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode an int64.
|
||||
func (o *Buffer) dec_int64(p *Properties, base structPointer) error {
|
||||
u, err := p.valDec(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
word64_Set(structPointer_Word64(base, p.field), o, u)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *Buffer) dec_proto3_int64(p *Properties, base structPointer) error {
|
||||
u, err := p.valDec(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
word64Val_Set(structPointer_Word64Val(base, p.field), o, u)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode a string.
|
||||
func (o *Buffer) dec_string(p *Properties, base structPointer) error {
|
||||
s, err := o.DecodeStringBytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*structPointer_String(base, p.field) = &s
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *Buffer) dec_proto3_string(p *Properties, base structPointer) error {
|
||||
s, err := o.DecodeStringBytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*structPointer_StringVal(base, p.field) = s
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode a slice of bytes ([]byte).
|
||||
func (o *Buffer) dec_slice_byte(p *Properties, base structPointer) error {
|
||||
b, err := o.DecodeRawBytes(true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*structPointer_Bytes(base, p.field) = b
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode a slice of bools ([]bool).
|
||||
func (o *Buffer) dec_slice_bool(p *Properties, base structPointer) error {
|
||||
u, err := p.valDec(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v := structPointer_BoolSlice(base, p.field)
|
||||
*v = append(*v, u != 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode a slice of bools ([]bool) in packed format.
|
||||
func (o *Buffer) dec_slice_packed_bool(p *Properties, base structPointer) error {
|
||||
v := structPointer_BoolSlice(base, p.field)
|
||||
|
||||
nn, err := o.DecodeVarint()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nb := int(nn) // number of bytes of encoded bools
|
||||
|
||||
y := *v
|
||||
for i := 0; i < nb; i++ {
|
||||
u, err := p.valDec(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
y = append(y, u != 0)
|
||||
}
|
||||
|
||||
*v = y
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode a slice of int32s ([]int32).
|
||||
func (o *Buffer) dec_slice_int32(p *Properties, base structPointer) error {
|
||||
u, err := p.valDec(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
structPointer_Word32Slice(base, p.field).Append(uint32(u))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode a slice of int32s ([]int32) in packed format.
|
||||
func (o *Buffer) dec_slice_packed_int32(p *Properties, base structPointer) error {
|
||||
v := structPointer_Word32Slice(base, p.field)
|
||||
|
||||
nn, err := o.DecodeVarint()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nb := int(nn) // number of bytes of encoded int32s
|
||||
|
||||
fin := o.index + nb
|
||||
if fin < o.index {
|
||||
return errOverflow
|
||||
}
|
||||
for o.index < fin {
|
||||
u, err := p.valDec(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Append(uint32(u))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode a slice of int64s ([]int64).
|
||||
func (o *Buffer) dec_slice_int64(p *Properties, base structPointer) error {
|
||||
u, err := p.valDec(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
structPointer_Word64Slice(base, p.field).Append(u)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode a slice of int64s ([]int64) in packed format.
|
||||
func (o *Buffer) dec_slice_packed_int64(p *Properties, base structPointer) error {
|
||||
v := structPointer_Word64Slice(base, p.field)
|
||||
|
||||
nn, err := o.DecodeVarint()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nb := int(nn) // number of bytes of encoded int64s
|
||||
|
||||
fin := o.index + nb
|
||||
if fin < o.index {
|
||||
return errOverflow
|
||||
}
|
||||
for o.index < fin {
|
||||
u, err := p.valDec(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Append(u)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode a slice of strings ([]string).
|
||||
func (o *Buffer) dec_slice_string(p *Properties, base structPointer) error {
|
||||
s, err := o.DecodeStringBytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v := structPointer_StringSlice(base, p.field)
|
||||
*v = append(*v, s)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode a slice of slice of bytes ([][]byte).
|
||||
func (o *Buffer) dec_slice_slice_byte(p *Properties, base structPointer) error {
|
||||
b, err := o.DecodeRawBytes(true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v := structPointer_BytesSlice(base, p.field)
|
||||
*v = append(*v, b)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode a map field.
|
||||
func (o *Buffer) dec_new_map(p *Properties, base structPointer) error {
|
||||
raw, err := o.DecodeRawBytes(false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oi := o.index // index at the end of this map entry
|
||||
o.index -= len(raw) // move buffer back to start of map entry
|
||||
|
||||
mptr := structPointer_NewAt(base, p.field, p.mtype) // *map[K]V
|
||||
if mptr.Elem().IsNil() {
|
||||
mptr.Elem().Set(reflect.MakeMap(mptr.Type().Elem()))
|
||||
}
|
||||
v := mptr.Elem() // map[K]V
|
||||
|
||||
// Prepare addressable doubly-indirect placeholders for the key and value types.
|
||||
// See enc_new_map for why.
|
||||
keyptr := reflect.New(reflect.PtrTo(p.mtype.Key())).Elem() // addressable *K
|
||||
keybase := toStructPointer(keyptr.Addr()) // **K
|
||||
|
||||
var valbase structPointer
|
||||
var valptr reflect.Value
|
||||
switch p.mtype.Elem().Kind() {
|
||||
case reflect.Slice:
|
||||
// []byte
|
||||
var dummy []byte
|
||||
valptr = reflect.ValueOf(&dummy) // *[]byte
|
||||
valbase = toStructPointer(valptr) // *[]byte
|
||||
case reflect.Ptr:
|
||||
// message; valptr is **Msg; need to allocate the intermediate pointer
|
||||
valptr = reflect.New(reflect.PtrTo(p.mtype.Elem())).Elem() // addressable *V
|
||||
valptr.Set(reflect.New(valptr.Type().Elem()))
|
||||
valbase = toStructPointer(valptr)
|
||||
default:
|
||||
// everything else
|
||||
valptr = reflect.New(reflect.PtrTo(p.mtype.Elem())).Elem() // addressable *V
|
||||
valbase = toStructPointer(valptr.Addr()) // **V
|
||||
}
|
||||
|
||||
// Decode.
|
||||
// This parses a restricted wire format, namely the encoding of a message
|
||||
// with two fields. See enc_new_map for the format.
|
||||
for o.index < oi {
|
||||
// tagcode for key and value properties are always a single byte
|
||||
// because they have tags 1 and 2.
|
||||
tagcode := o.buf[o.index]
|
||||
o.index++
|
||||
switch tagcode {
|
||||
case p.mkeyprop.tagcode[0]:
|
||||
if err := p.mkeyprop.dec(o, p.mkeyprop, keybase); err != nil {
|
||||
return err
|
||||
}
|
||||
case p.mvalprop.tagcode[0]:
|
||||
if err := p.mvalprop.dec(o, p.mvalprop, valbase); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
// TODO: Should we silently skip this instead?
|
||||
return fmt.Errorf("proto: bad map data tag %d", raw[0])
|
||||
}
|
||||
}
|
||||
keyelem, valelem := keyptr.Elem(), valptr.Elem()
|
||||
if !keyelem.IsValid() || !valelem.IsValid() {
|
||||
// We did not decode the key or the value in the map entry.
|
||||
// Either way, it's an invalid map entry.
|
||||
return fmt.Errorf("proto: bad map data: missing key/val")
|
||||
}
|
||||
|
||||
v.SetMapIndex(keyelem, valelem)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode a group.
|
||||
func (o *Buffer) dec_struct_group(p *Properties, base structPointer) error {
|
||||
bas := structPointer_GetStructPointer(base, p.field)
|
||||
if structPointer_IsNil(bas) {
|
||||
// allocate new nested message
|
||||
bas = toStructPointer(reflect.New(p.stype))
|
||||
structPointer_SetStructPointer(base, p.field, bas)
|
||||
}
|
||||
return o.unmarshalType(p.stype, p.sprop, true, bas)
|
||||
}
|
||||
|
||||
// Decode an embedded message.
|
||||
func (o *Buffer) dec_struct_message(p *Properties, base structPointer) (err error) {
|
||||
raw, e := o.DecodeRawBytes(false)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
bas := structPointer_GetStructPointer(base, p.field)
|
||||
if structPointer_IsNil(bas) {
|
||||
// allocate new nested message
|
||||
bas = toStructPointer(reflect.New(p.stype))
|
||||
structPointer_SetStructPointer(base, p.field, bas)
|
||||
}
|
||||
|
||||
// If the object can unmarshal itself, let it.
|
||||
if p.isUnmarshaler {
|
||||
iv := structPointer_Interface(bas, p.stype)
|
||||
return iv.(Unmarshaler).Unmarshal(raw)
|
||||
}
|
||||
|
||||
obuf := o.buf
|
||||
oi := o.index
|
||||
o.buf = raw
|
||||
o.index = 0
|
||||
|
||||
err = o.unmarshalType(p.stype, p.sprop, false, bas)
|
||||
o.buf = obuf
|
||||
o.index = oi
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Decode a slice of embedded messages.
|
||||
func (o *Buffer) dec_slice_struct_message(p *Properties, base structPointer) error {
|
||||
return o.dec_slice_struct(p, false, base)
|
||||
}
|
||||
|
||||
// Decode a slice of embedded groups.
|
||||
func (o *Buffer) dec_slice_struct_group(p *Properties, base structPointer) error {
|
||||
return o.dec_slice_struct(p, true, base)
|
||||
}
|
||||
|
||||
// Decode a slice of structs ([]*struct).
|
||||
func (o *Buffer) dec_slice_struct(p *Properties, is_group bool, base structPointer) error {
|
||||
v := reflect.New(p.stype)
|
||||
bas := toStructPointer(v)
|
||||
structPointer_StructPointerSlice(base, p.field).Append(bas)
|
||||
|
||||
if is_group {
|
||||
err := o.unmarshalType(p.stype, p.sprop, is_group, bas)
|
||||
return err
|
||||
}
|
||||
|
||||
raw, err := o.DecodeRawBytes(false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If the object can unmarshal itself, let it.
|
||||
if p.isUnmarshaler {
|
||||
iv := v.Interface()
|
||||
return iv.(Unmarshaler).Unmarshal(raw)
|
||||
}
|
||||
|
||||
obuf := o.buf
|
||||
oi := o.index
|
||||
o.buf = raw
|
||||
o.index = 0
|
||||
|
||||
err = o.unmarshalType(p.stype, p.sprop, is_group, bas)
|
||||
|
||||
o.buf = obuf
|
||||
o.index = oi
|
||||
|
||||
return err
|
||||
}
|
1293
Godeps/_workspace/src/github.com/golang/protobuf/proto/encode.go
generated
vendored
Normal file
1293
Godeps/_workspace/src/github.com/golang/protobuf/proto/encode.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
256
Godeps/_workspace/src/github.com/golang/protobuf/proto/equal.go
generated
vendored
Normal file
256
Godeps/_workspace/src/github.com/golang/protobuf/proto/equal.go
generated
vendored
Normal file
|
@ -0,0 +1,256 @@
|
|||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Protocol buffer comparison.
|
||||
// TODO: MessageSet.
|
||||
|
||||
package proto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
/*
|
||||
Equal returns true iff protocol buffers a and b are equal.
|
||||
The arguments must both be pointers to protocol buffer structs.
|
||||
|
||||
Equality is defined in this way:
|
||||
- Two messages are equal iff they are the same type,
|
||||
corresponding fields are equal, unknown field sets
|
||||
are equal, and extensions sets are equal.
|
||||
- Two set scalar fields are equal iff their values are equal.
|
||||
If the fields are of a floating-point type, remember that
|
||||
NaN != x for all x, including NaN.
|
||||
- Two repeated fields are equal iff their lengths are the same,
|
||||
and their corresponding elements are equal (a "bytes" field,
|
||||
although represented by []byte, is not a repeated field)
|
||||
- Two unset fields are equal.
|
||||
- Two unknown field sets are equal if their current
|
||||
encoded state is equal.
|
||||
- Two extension sets are equal iff they have corresponding
|
||||
elements that are pairwise equal.
|
||||
- Every other combination of things are not equal.
|
||||
|
||||
The return value is undefined if a and b are not protocol buffers.
|
||||
*/
|
||||
func Equal(a, b Message) bool {
|
||||
if a == nil || b == nil {
|
||||
return a == b
|
||||
}
|
||||
v1, v2 := reflect.ValueOf(a), reflect.ValueOf(b)
|
||||
if v1.Type() != v2.Type() {
|
||||
return false
|
||||
}
|
||||
if v1.Kind() == reflect.Ptr {
|
||||
if v1.IsNil() {
|
||||
return v2.IsNil()
|
||||
}
|
||||
if v2.IsNil() {
|
||||
return false
|
||||
}
|
||||
v1, v2 = v1.Elem(), v2.Elem()
|
||||
}
|
||||
if v1.Kind() != reflect.Struct {
|
||||
return false
|
||||
}
|
||||
return equalStruct(v1, v2)
|
||||
}
|
||||
|
||||
// v1 and v2 are known to have the same type.
|
||||
func equalStruct(v1, v2 reflect.Value) bool {
|
||||
for i := 0; i < v1.NumField(); i++ {
|
||||
f := v1.Type().Field(i)
|
||||
if strings.HasPrefix(f.Name, "XXX_") {
|
||||
continue
|
||||
}
|
||||
f1, f2 := v1.Field(i), v2.Field(i)
|
||||
if f.Type.Kind() == reflect.Ptr {
|
||||
if n1, n2 := f1.IsNil(), f2.IsNil(); n1 && n2 {
|
||||
// both unset
|
||||
continue
|
||||
} else if n1 != n2 {
|
||||
// set/unset mismatch
|
||||
return false
|
||||
}
|
||||
b1, ok := f1.Interface().(raw)
|
||||
if ok {
|
||||
b2 := f2.Interface().(raw)
|
||||
// RawMessage
|
||||
if !bytes.Equal(b1.Bytes(), b2.Bytes()) {
|
||||
return false
|
||||
}
|
||||
continue
|
||||
}
|
||||
f1, f2 = f1.Elem(), f2.Elem()
|
||||
}
|
||||
if !equalAny(f1, f2) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if em1 := v1.FieldByName("XXX_extensions"); em1.IsValid() {
|
||||
em2 := v2.FieldByName("XXX_extensions")
|
||||
if !equalExtensions(v1.Type(), em1.Interface().(map[int32]Extension), em2.Interface().(map[int32]Extension)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
uf := v1.FieldByName("XXX_unrecognized")
|
||||
if !uf.IsValid() {
|
||||
return true
|
||||
}
|
||||
|
||||
u1 := uf.Bytes()
|
||||
u2 := v2.FieldByName("XXX_unrecognized").Bytes()
|
||||
if !bytes.Equal(u1, u2) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// v1 and v2 are known to have the same type.
|
||||
func equalAny(v1, v2 reflect.Value) bool {
|
||||
if v1.Type() == protoMessageType {
|
||||
m1, _ := v1.Interface().(Message)
|
||||
m2, _ := v2.Interface().(Message)
|
||||
return Equal(m1, m2)
|
||||
}
|
||||
switch v1.Kind() {
|
||||
case reflect.Bool:
|
||||
return v1.Bool() == v2.Bool()
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v1.Float() == v2.Float()
|
||||
case reflect.Int32, reflect.Int64:
|
||||
return v1.Int() == v2.Int()
|
||||
case reflect.Map:
|
||||
if v1.Len() != v2.Len() {
|
||||
return false
|
||||
}
|
||||
for _, key := range v1.MapKeys() {
|
||||
val2 := v2.MapIndex(key)
|
||||
if !val2.IsValid() {
|
||||
// This key was not found in the second map.
|
||||
return false
|
||||
}
|
||||
if !equalAny(v1.MapIndex(key), val2) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.Ptr:
|
||||
return equalAny(v1.Elem(), v2.Elem())
|
||||
case reflect.Slice:
|
||||
if v1.Type().Elem().Kind() == reflect.Uint8 {
|
||||
// short circuit: []byte
|
||||
if v1.IsNil() != v2.IsNil() {
|
||||
return false
|
||||
}
|
||||
return bytes.Equal(v1.Interface().([]byte), v2.Interface().([]byte))
|
||||
}
|
||||
|
||||
if v1.Len() != v2.Len() {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < v1.Len(); i++ {
|
||||
if !equalAny(v1.Index(i), v2.Index(i)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.String:
|
||||
return v1.Interface().(string) == v2.Interface().(string)
|
||||
case reflect.Struct:
|
||||
return equalStruct(v1, v2)
|
||||
case reflect.Uint32, reflect.Uint64:
|
||||
return v1.Uint() == v2.Uint()
|
||||
}
|
||||
|
||||
// unknown type, so not a protocol buffer
|
||||
log.Printf("proto: don't know how to compare %v", v1)
|
||||
return false
|
||||
}
|
||||
|
||||
// base is the struct type that the extensions are based on.
|
||||
// em1 and em2 are extension maps.
|
||||
func equalExtensions(base reflect.Type, em1, em2 map[int32]Extension) bool {
|
||||
if len(em1) != len(em2) {
|
||||
return false
|
||||
}
|
||||
|
||||
for extNum, e1 := range em1 {
|
||||
e2, ok := em2[extNum]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
m1, m2 := e1.value, e2.value
|
||||
|
||||
if m1 != nil && m2 != nil {
|
||||
// Both are unencoded.
|
||||
if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2)) {
|
||||
return false
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// At least one is encoded. To do a semantically correct comparison
|
||||
// we need to unmarshal them first.
|
||||
var desc *ExtensionDesc
|
||||
if m := extensionMaps[base]; m != nil {
|
||||
desc = m[extNum]
|
||||
}
|
||||
if desc == nil {
|
||||
log.Printf("proto: don't know how to compare extension %d of %v", extNum, base)
|
||||
continue
|
||||
}
|
||||
var err error
|
||||
if m1 == nil {
|
||||
m1, err = decodeExtension(e1.enc, desc)
|
||||
}
|
||||
if m2 == nil && err == nil {
|
||||
m2, err = decodeExtension(e2.enc, desc)
|
||||
}
|
||||
if err != nil {
|
||||
// The encoded form is invalid.
|
||||
log.Printf("proto: badly encoded extension %d of %v: %v", extNum, base, err)
|
||||
return false
|
||||
}
|
||||
if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
191
Godeps/_workspace/src/github.com/golang/protobuf/proto/equal_test.go
generated
vendored
Normal file
191
Godeps/_workspace/src/github.com/golang/protobuf/proto/equal_test.go
generated
vendored
Normal file
|
@ -0,0 +1,191 @@
|
|||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package proto_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/golang/protobuf/proto"
|
||||
pb "github.com/golang/protobuf/proto/testdata"
|
||||
)
|
||||
|
||||
// Four identical base messages.
|
||||
// The init function adds extensions to some of them.
|
||||
var messageWithoutExtension = &pb.MyMessage{Count: Int32(7)}
|
||||
var messageWithExtension1a = &pb.MyMessage{Count: Int32(7)}
|
||||
var messageWithExtension1b = &pb.MyMessage{Count: Int32(7)}
|
||||
var messageWithExtension2 = &pb.MyMessage{Count: Int32(7)}
|
||||
|
||||
// Two messages with non-message extensions.
|
||||
var messageWithInt32Extension1 = &pb.MyMessage{Count: Int32(8)}
|
||||
var messageWithInt32Extension2 = &pb.MyMessage{Count: Int32(8)}
|
||||
|
||||
func init() {
|
||||
ext1 := &pb.Ext{Data: String("Kirk")}
|
||||
ext2 := &pb.Ext{Data: String("Picard")}
|
||||
|
||||
// messageWithExtension1a has ext1, but never marshals it.
|
||||
if err := SetExtension(messageWithExtension1a, pb.E_Ext_More, ext1); err != nil {
|
||||
panic("SetExtension on 1a failed: " + err.Error())
|
||||
}
|
||||
|
||||
// messageWithExtension1b is the unmarshaled form of messageWithExtension1a.
|
||||
if err := SetExtension(messageWithExtension1b, pb.E_Ext_More, ext1); err != nil {
|
||||
panic("SetExtension on 1b failed: " + err.Error())
|
||||
}
|
||||
buf, err := Marshal(messageWithExtension1b)
|
||||
if err != nil {
|
||||
panic("Marshal of 1b failed: " + err.Error())
|
||||
}
|
||||
messageWithExtension1b.Reset()
|
||||
if err := Unmarshal(buf, messageWithExtension1b); err != nil {
|
||||
panic("Unmarshal of 1b failed: " + err.Error())
|
||||
}
|
||||
|
||||
// messageWithExtension2 has ext2.
|
||||
if err := SetExtension(messageWithExtension2, pb.E_Ext_More, ext2); err != nil {
|
||||
panic("SetExtension on 2 failed: " + err.Error())
|
||||
}
|
||||
|
||||
if err := SetExtension(messageWithInt32Extension1, pb.E_Ext_Number, Int32(23)); err != nil {
|
||||
panic("SetExtension on Int32-1 failed: " + err.Error())
|
||||
}
|
||||
if err := SetExtension(messageWithInt32Extension1, pb.E_Ext_Number, Int32(24)); err != nil {
|
||||
panic("SetExtension on Int32-2 failed: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
var EqualTests = []struct {
|
||||
desc string
|
||||
a, b Message
|
||||
exp bool
|
||||
}{
|
||||
{"different types", &pb.GoEnum{}, &pb.GoTestField{}, false},
|
||||
{"equal empty", &pb.GoEnum{}, &pb.GoEnum{}, true},
|
||||
{"nil vs nil", nil, nil, true},
|
||||
{"typed nil vs typed nil", (*pb.GoEnum)(nil), (*pb.GoEnum)(nil), true},
|
||||
{"typed nil vs empty", (*pb.GoEnum)(nil), &pb.GoEnum{}, false},
|
||||
{"different typed nil", (*pb.GoEnum)(nil), (*pb.GoTestField)(nil), false},
|
||||
|
||||
{"one set field, one unset field", &pb.GoTestField{Label: String("foo")}, &pb.GoTestField{}, false},
|
||||
{"one set field zero, one unset field", &pb.GoTest{Param: Int32(0)}, &pb.GoTest{}, false},
|
||||
{"different set fields", &pb.GoTestField{Label: String("foo")}, &pb.GoTestField{Label: String("bar")}, false},
|
||||
{"equal set", &pb.GoTestField{Label: String("foo")}, &pb.GoTestField{Label: String("foo")}, true},
|
||||
|
||||
{"repeated, one set", &pb.GoTest{F_Int32Repeated: []int32{2, 3}}, &pb.GoTest{}, false},
|
||||
{"repeated, different length", &pb.GoTest{F_Int32Repeated: []int32{2, 3}}, &pb.GoTest{F_Int32Repeated: []int32{2}}, false},
|
||||
{"repeated, different value", &pb.GoTest{F_Int32Repeated: []int32{2}}, &pb.GoTest{F_Int32Repeated: []int32{3}}, false},
|
||||
{"repeated, equal", &pb.GoTest{F_Int32Repeated: []int32{2, 4}}, &pb.GoTest{F_Int32Repeated: []int32{2, 4}}, true},
|
||||
{"repeated, nil equal nil", &pb.GoTest{F_Int32Repeated: nil}, &pb.GoTest{F_Int32Repeated: nil}, true},
|
||||
{"repeated, nil equal empty", &pb.GoTest{F_Int32Repeated: nil}, &pb.GoTest{F_Int32Repeated: []int32{}}, true},
|
||||
{"repeated, empty equal nil", &pb.GoTest{F_Int32Repeated: []int32{}}, &pb.GoTest{F_Int32Repeated: nil}, true},
|
||||
|
||||
{
|
||||
"nested, different",
|
||||
&pb.GoTest{RequiredField: &pb.GoTestField{Label: String("foo")}},
|
||||
&pb.GoTest{RequiredField: &pb.GoTestField{Label: String("bar")}},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"nested, equal",
|
||||
&pb.GoTest{RequiredField: &pb.GoTestField{Label: String("wow")}},
|
||||
&pb.GoTest{RequiredField: &pb.GoTestField{Label: String("wow")}},
|
||||
true,
|
||||
},
|
||||
|
||||
{"bytes", &pb.OtherMessage{Value: []byte("foo")}, &pb.OtherMessage{Value: []byte("foo")}, true},
|
||||
{"bytes, empty", &pb.OtherMessage{Value: []byte{}}, &pb.OtherMessage{Value: []byte{}}, true},
|
||||
{"bytes, empty vs nil", &pb.OtherMessage{Value: []byte{}}, &pb.OtherMessage{Value: nil}, false},
|
||||
{
|
||||
"repeated bytes",
|
||||
&pb.MyMessage{RepBytes: [][]byte{[]byte("sham"), []byte("wow")}},
|
||||
&pb.MyMessage{RepBytes: [][]byte{[]byte("sham"), []byte("wow")}},
|
||||
true,
|
||||
},
|
||||
|
||||
{"extension vs. no extension", messageWithoutExtension, messageWithExtension1a, false},
|
||||
{"extension vs. same extension", messageWithExtension1a, messageWithExtension1b, true},
|
||||
{"extension vs. different extension", messageWithExtension1a, messageWithExtension2, false},
|
||||
|
||||
{"int32 extension vs. itself", messageWithInt32Extension1, messageWithInt32Extension1, true},
|
||||
{"int32 extension vs. a different int32", messageWithInt32Extension1, messageWithInt32Extension2, false},
|
||||
|
||||
{
|
||||
"message with group",
|
||||
&pb.MyMessage{
|
||||
Count: Int32(1),
|
||||
Somegroup: &pb.MyMessage_SomeGroup{
|
||||
GroupField: Int32(5),
|
||||
},
|
||||
},
|
||||
&pb.MyMessage{
|
||||
Count: Int32(1),
|
||||
Somegroup: &pb.MyMessage_SomeGroup{
|
||||
GroupField: Int32(5),
|
||||
},
|
||||
},
|
||||
true,
|
||||
},
|
||||
|
||||
{
|
||||
"map same",
|
||||
&pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}},
|
||||
&pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"map different entry",
|
||||
&pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}},
|
||||
&pb.MessageWithMap{NameMapping: map[int32]string{2: "Rob"}},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"map different key only",
|
||||
&pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}},
|
||||
&pb.MessageWithMap{NameMapping: map[int32]string{2: "Ken"}},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"map different value only",
|
||||
&pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}},
|
||||
&pb.MessageWithMap{NameMapping: map[int32]string{1: "Rob"}},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
func TestEqual(t *testing.T) {
|
||||
for _, tc := range EqualTests {
|
||||
if res := Equal(tc.a, tc.b); res != tc.exp {
|
||||
t.Errorf("%v: Equal(%v, %v) = %v, want %v", tc.desc, tc.a, tc.b, res, tc.exp)
|
||||
}
|
||||
}
|
||||
}
|
400
Godeps/_workspace/src/github.com/golang/protobuf/proto/extensions.go
generated
vendored
Normal file
400
Godeps/_workspace/src/github.com/golang/protobuf/proto/extensions.go
generated
vendored
Normal file
|
@ -0,0 +1,400 @@
|
|||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package proto
|
||||
|
||||
/*
|
||||
* Types and routines for supporting protocol buffer extensions.
|
||||
*/
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// ErrMissingExtension is the error returned by GetExtension if the named extension is not in the message.
|
||||
var ErrMissingExtension = errors.New("proto: missing extension")
|
||||
|
||||
// ExtensionRange represents a range of message extensions for a protocol buffer.
|
||||
// Used in code generated by the protocol compiler.
|
||||
type ExtensionRange struct {
|
||||
Start, End int32 // both inclusive
|
||||
}
|
||||
|
||||
// extendableProto is an interface implemented by any protocol buffer that may be extended.
|
||||
type extendableProto interface {
|
||||
Message
|
||||
ExtensionRangeArray() []ExtensionRange
|
||||
ExtensionMap() map[int32]Extension
|
||||
}
|
||||
|
||||
var extendableProtoType = reflect.TypeOf((*extendableProto)(nil)).Elem()
|
||||
|
||||
// ExtensionDesc represents an extension specification.
|
||||
// Used in generated code from the protocol compiler.
|
||||
type ExtensionDesc struct {
|
||||
ExtendedType Message // nil pointer to the type that is being extended
|
||||
ExtensionType interface{} // nil pointer to the extension type
|
||||
Field int32 // field number
|
||||
Name string // fully-qualified name of extension, for text formatting
|
||||
Tag string // protobuf tag style
|
||||
}
|
||||
|
||||
func (ed *ExtensionDesc) repeated() bool {
|
||||
t := reflect.TypeOf(ed.ExtensionType)
|
||||
return t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8
|
||||
}
|
||||
|
||||
// Extension represents an extension in a message.
|
||||
type Extension struct {
|
||||
// When an extension is stored in a message using SetExtension
|
||||
// only desc and value are set. When the message is marshaled
|
||||
// enc will be set to the encoded form of the message.
|
||||
//
|
||||
// When a message is unmarshaled and contains extensions, each
|
||||
// extension will have only enc set. When such an extension is
|
||||
// accessed using GetExtension (or GetExtensions) desc and value
|
||||
// will be set.
|
||||
desc *ExtensionDesc
|
||||
value interface{}
|
||||
enc []byte
|
||||
}
|
||||
|
||||
// SetRawExtension is for testing only.
|
||||
func SetRawExtension(base extendableProto, id int32, b []byte) {
|
||||
base.ExtensionMap()[id] = Extension{enc: b}
|
||||
}
|
||||
|
||||
// isExtensionField returns true iff the given field number is in an extension range.
|
||||
func isExtensionField(pb extendableProto, field int32) bool {
|
||||
for _, er := range pb.ExtensionRangeArray() {
|
||||
if er.Start <= field && field <= er.End {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// checkExtensionTypes checks that the given extension is valid for pb.
|
||||
func checkExtensionTypes(pb extendableProto, extension *ExtensionDesc) error {
|
||||
// Check the extended type.
|
||||
if a, b := reflect.TypeOf(pb), reflect.TypeOf(extension.ExtendedType); a != b {
|
||||
return errors.New("proto: bad extended type; " + b.String() + " does not extend " + a.String())
|
||||
}
|
||||
// Check the range.
|
||||
if !isExtensionField(pb, extension.Field) {
|
||||
return errors.New("proto: bad extension number; not in declared ranges")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// extPropKey is sufficient to uniquely identify an extension.
|
||||
type extPropKey struct {
|
||||
base reflect.Type
|
||||
field int32
|
||||
}
|
||||
|
||||
var extProp = struct {
|
||||
sync.RWMutex
|
||||
m map[extPropKey]*Properties
|
||||
}{
|
||||
m: make(map[extPropKey]*Properties),
|
||||
}
|
||||
|
||||
func extensionProperties(ed *ExtensionDesc) *Properties {
|
||||
key := extPropKey{base: reflect.TypeOf(ed.ExtendedType), field: ed.Field}
|
||||
|
||||
extProp.RLock()
|
||||
if prop, ok := extProp.m[key]; ok {
|
||||
extProp.RUnlock()
|
||||
return prop
|
||||
}
|
||||
extProp.RUnlock()
|
||||
|
||||
extProp.Lock()
|
||||
defer extProp.Unlock()
|
||||
// Check again.
|
||||
if prop, ok := extProp.m[key]; ok {
|
||||
return prop
|
||||
}
|
||||
|
||||
prop := new(Properties)
|
||||
prop.Init(reflect.TypeOf(ed.ExtensionType), "unknown_name", ed.Tag, nil)
|
||||
extProp.m[key] = prop
|
||||
return prop
|
||||
}
|
||||
|
||||
// encodeExtensionMap encodes any unmarshaled (unencoded) extensions in m.
|
||||
func encodeExtensionMap(m map[int32]Extension) error {
|
||||
for k, e := range m {
|
||||
if e.value == nil || e.desc == nil {
|
||||
// Extension is only in its encoded form.
|
||||
continue
|
||||
}
|
||||
|
||||
// We don't skip extensions that have an encoded form set,
|
||||
// because the extension value may have been mutated after
|
||||
// the last time this function was called.
|
||||
|
||||
et := reflect.TypeOf(e.desc.ExtensionType)
|
||||
props := extensionProperties(e.desc)
|
||||
|
||||
p := NewBuffer(nil)
|
||||
// If e.value has type T, the encoder expects a *struct{ X T }.
|
||||
// Pass a *T with a zero field and hope it all works out.
|
||||
x := reflect.New(et)
|
||||
x.Elem().Set(reflect.ValueOf(e.value))
|
||||
if err := props.enc(p, props, toStructPointer(x)); err != nil {
|
||||
return err
|
||||
}
|
||||
e.enc = p.buf
|
||||
m[k] = e
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func sizeExtensionMap(m map[int32]Extension) (n int) {
|
||||
for _, e := range m {
|
||||
if e.value == nil || e.desc == nil {
|
||||
// Extension is only in its encoded form.
|
||||
n += len(e.enc)
|
||||
continue
|
||||
}
|
||||
|
||||
// We don't skip extensions that have an encoded form set,
|
||||
// because the extension value may have been mutated after
|
||||
// the last time this function was called.
|
||||
|
||||
et := reflect.TypeOf(e.desc.ExtensionType)
|
||||
props := extensionProperties(e.desc)
|
||||
|
||||
// If e.value has type T, the encoder expects a *struct{ X T }.
|
||||
// Pass a *T with a zero field and hope it all works out.
|
||||
x := reflect.New(et)
|
||||
x.Elem().Set(reflect.ValueOf(e.value))
|
||||
n += props.size(props, toStructPointer(x))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// HasExtension returns whether the given extension is present in pb.
|
||||
func HasExtension(pb extendableProto, extension *ExtensionDesc) bool {
|
||||
// TODO: Check types, field numbers, etc.?
|
||||
_, ok := pb.ExtensionMap()[extension.Field]
|
||||
return ok
|
||||
}
|
||||
|
||||
// ClearExtension removes the given extension from pb.
|
||||
func ClearExtension(pb extendableProto, extension *ExtensionDesc) {
|
||||
// TODO: Check types, field numbers, etc.?
|
||||
delete(pb.ExtensionMap(), extension.Field)
|
||||
}
|
||||
|
||||
// GetExtension parses and returns the given extension of pb.
|
||||
// If the extension is not present and has no default value it returns ErrMissingExtension.
|
||||
func GetExtension(pb extendableProto, extension *ExtensionDesc) (interface{}, error) {
|
||||
if err := checkExtensionTypes(pb, extension); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
emap := pb.ExtensionMap()
|
||||
e, ok := emap[extension.Field]
|
||||
if !ok {
|
||||
// defaultExtensionValue returns the default value or
|
||||
// ErrMissingExtension if there is no default.
|
||||
return defaultExtensionValue(extension)
|
||||
}
|
||||
|
||||
if e.value != nil {
|
||||
// Already decoded. Check the descriptor, though.
|
||||
if e.desc != extension {
|
||||
// This shouldn't happen. If it does, it means that
|
||||
// GetExtension was called twice with two different
|
||||
// descriptors with the same field number.
|
||||
return nil, errors.New("proto: descriptor conflict")
|
||||
}
|
||||
return e.value, nil
|
||||
}
|
||||
|
||||
v, err := decodeExtension(e.enc, extension)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Remember the decoded version and drop the encoded version.
|
||||
// That way it is safe to mutate what we return.
|
||||
e.value = v
|
||||
e.desc = extension
|
||||
e.enc = nil
|
||||
emap[extension.Field] = e
|
||||
return e.value, nil
|
||||
}
|
||||
|
||||
// defaultExtensionValue returns the default value for extension.
|
||||
// If no default for an extension is defined ErrMissingExtension is returned.
|
||||
func defaultExtensionValue(extension *ExtensionDesc) (interface{}, error) {
|
||||
t := reflect.TypeOf(extension.ExtensionType)
|
||||
props := extensionProperties(extension)
|
||||
|
||||
sf, _, err := fieldDefault(t, props)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if sf == nil || sf.value == nil {
|
||||
// There is no default value.
|
||||
return nil, ErrMissingExtension
|
||||
}
|
||||
|
||||
if t.Kind() != reflect.Ptr {
|
||||
// We do not need to return a Ptr, we can directly return sf.value.
|
||||
return sf.value, nil
|
||||
}
|
||||
|
||||
// We need to return an interface{} that is a pointer to sf.value.
|
||||
value := reflect.New(t).Elem()
|
||||
value.Set(reflect.New(value.Type().Elem()))
|
||||
if sf.kind == reflect.Int32 {
|
||||
// We may have an int32 or an enum, but the underlying data is int32.
|
||||
// Since we can't set an int32 into a non int32 reflect.value directly
|
||||
// set it as a int32.
|
||||
value.Elem().SetInt(int64(sf.value.(int32)))
|
||||
} else {
|
||||
value.Elem().Set(reflect.ValueOf(sf.value))
|
||||
}
|
||||
return value.Interface(), nil
|
||||
}
|
||||
|
||||
// decodeExtension decodes an extension encoded in b.
|
||||
func decodeExtension(b []byte, extension *ExtensionDesc) (interface{}, error) {
|
||||
o := NewBuffer(b)
|
||||
|
||||
t := reflect.TypeOf(extension.ExtensionType)
|
||||
rep := extension.repeated()
|
||||
|
||||
props := extensionProperties(extension)
|
||||
|
||||
// t is a pointer to a struct, pointer to basic type or a slice.
|
||||
// Allocate a "field" to store the pointer/slice itself; the
|
||||
// pointer/slice will be stored here. We pass
|
||||
// the address of this field to props.dec.
|
||||
// This passes a zero field and a *t and lets props.dec
|
||||
// interpret it as a *struct{ x t }.
|
||||
value := reflect.New(t).Elem()
|
||||
|
||||
for {
|
||||
// Discard wire type and field number varint. It isn't needed.
|
||||
if _, err := o.DecodeVarint(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := props.dec(o, props, toStructPointer(value.Addr())); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !rep || o.index >= len(o.buf) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return value.Interface(), nil
|
||||
}
|
||||
|
||||
// GetExtensions returns a slice of the extensions present in pb that are also listed in es.
|
||||
// The returned slice has the same length as es; missing extensions will appear as nil elements.
|
||||
func GetExtensions(pb Message, es []*ExtensionDesc) (extensions []interface{}, err error) {
|
||||
epb, ok := pb.(extendableProto)
|
||||
if !ok {
|
||||
err = errors.New("proto: not an extendable proto")
|
||||
return
|
||||
}
|
||||
extensions = make([]interface{}, len(es))
|
||||
for i, e := range es {
|
||||
extensions[i], err = GetExtension(epb, e)
|
||||
if err == ErrMissingExtension {
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SetExtension sets the specified extension of pb to the specified value.
|
||||
func SetExtension(pb extendableProto, extension *ExtensionDesc, value interface{}) error {
|
||||
if err := checkExtensionTypes(pb, extension); err != nil {
|
||||
return err
|
||||
}
|
||||
typ := reflect.TypeOf(extension.ExtensionType)
|
||||
if typ != reflect.TypeOf(value) {
|
||||
return errors.New("proto: bad extension value type")
|
||||
}
|
||||
// nil extension values need to be caught early, because the
|
||||
// encoder can't distinguish an ErrNil due to a nil extension
|
||||
// from an ErrNil due to a missing field. Extensions are
|
||||
// always optional, so the encoder would just swallow the error
|
||||
// and drop all the extensions from the encoded message.
|
||||
if reflect.ValueOf(value).IsNil() {
|
||||
return fmt.Errorf("proto: SetExtension called with nil value of type %T", value)
|
||||
}
|
||||
|
||||
pb.ExtensionMap()[extension.Field] = Extension{desc: extension, value: value}
|
||||
return nil
|
||||
}
|
||||
|
||||
// A global registry of extensions.
|
||||
// The generated code will register the generated descriptors by calling RegisterExtension.
|
||||
|
||||
var extensionMaps = make(map[reflect.Type]map[int32]*ExtensionDesc)
|
||||
|
||||
// RegisterExtension is called from the generated code.
|
||||
func RegisterExtension(desc *ExtensionDesc) {
|
||||
st := reflect.TypeOf(desc.ExtendedType).Elem()
|
||||
m := extensionMaps[st]
|
||||
if m == nil {
|
||||
m = make(map[int32]*ExtensionDesc)
|
||||
extensionMaps[st] = m
|
||||
}
|
||||
if _, ok := m[desc.Field]; ok {
|
||||
panic("proto: duplicate extension registered: " + st.String() + " " + strconv.Itoa(int(desc.Field)))
|
||||
}
|
||||
m[desc.Field] = desc
|
||||
}
|
||||
|
||||
// RegisteredExtensions returns a map of the registered extensions of a
|
||||
// protocol buffer struct, indexed by the extension number.
|
||||
// The argument pb should be a nil pointer to the struct type.
|
||||
func RegisteredExtensions(pb Message) map[int32]*ExtensionDesc {
|
||||
return extensionMaps[reflect.TypeOf(pb).Elem()]
|
||||
}
|
292
Godeps/_workspace/src/github.com/golang/protobuf/proto/extensions_test.go
generated
vendored
Normal file
292
Godeps/_workspace/src/github.com/golang/protobuf/proto/extensions_test.go
generated
vendored
Normal file
|
@ -0,0 +1,292 @@
|
|||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package proto_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
pb "github.com/golang/protobuf/proto/testdata"
|
||||
)
|
||||
|
||||
func TestGetExtensionsWithMissingExtensions(t *testing.T) {
|
||||
msg := &pb.MyMessage{}
|
||||
ext1 := &pb.Ext{}
|
||||
if err := proto.SetExtension(msg, pb.E_Ext_More, ext1); err != nil {
|
||||
t.Fatalf("Could not set ext1: %s", ext1)
|
||||
}
|
||||
exts, err := proto.GetExtensions(msg, []*proto.ExtensionDesc{
|
||||
pb.E_Ext_More,
|
||||
pb.E_Ext_Text,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("GetExtensions() failed: %s", err)
|
||||
}
|
||||
if exts[0] != ext1 {
|
||||
t.Errorf("ext1 not in returned extensions: %T %v", exts[0], exts[0])
|
||||
}
|
||||
if exts[1] != nil {
|
||||
t.Errorf("ext2 in returned extensions: %T %v", exts[1], exts[1])
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetExtensionStability(t *testing.T) {
|
||||
check := func(m *pb.MyMessage) bool {
|
||||
ext1, err := proto.GetExtension(m, pb.E_Ext_More)
|
||||
if err != nil {
|
||||
t.Fatalf("GetExtension() failed: %s", err)
|
||||
}
|
||||
ext2, err := proto.GetExtension(m, pb.E_Ext_More)
|
||||
if err != nil {
|
||||
t.Fatalf("GetExtension() failed: %s", err)
|
||||
}
|
||||
return ext1 == ext2
|
||||
}
|
||||
msg := &pb.MyMessage{Count: proto.Int32(4)}
|
||||
ext0 := &pb.Ext{}
|
||||
if err := proto.SetExtension(msg, pb.E_Ext_More, ext0); err != nil {
|
||||
t.Fatalf("Could not set ext1: %s", ext0)
|
||||
}
|
||||
if !check(msg) {
|
||||
t.Errorf("GetExtension() not stable before marshaling")
|
||||
}
|
||||
bb, err := proto.Marshal(msg)
|
||||
if err != nil {
|
||||
t.Fatalf("Marshal() failed: %s", err)
|
||||
}
|
||||
msg1 := &pb.MyMessage{}
|
||||
err = proto.Unmarshal(bb, msg1)
|
||||
if err != nil {
|
||||
t.Fatalf("Unmarshal() failed: %s", err)
|
||||
}
|
||||
if !check(msg1) {
|
||||
t.Errorf("GetExtension() not stable after unmarshaling")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetExtensionDefaults(t *testing.T) {
|
||||
var setFloat64 float64 = 1
|
||||
var setFloat32 float32 = 2
|
||||
var setInt32 int32 = 3
|
||||
var setInt64 int64 = 4
|
||||
var setUint32 uint32 = 5
|
||||
var setUint64 uint64 = 6
|
||||
var setBool = true
|
||||
var setBool2 = false
|
||||
var setString = "Goodnight string"
|
||||
var setBytes = []byte("Goodnight bytes")
|
||||
var setEnum = pb.DefaultsMessage_TWO
|
||||
|
||||
type testcase struct {
|
||||
ext *proto.ExtensionDesc // Extension we are testing.
|
||||
want interface{} // Expected value of extension, or nil (meaning that GetExtension will fail).
|
||||
def interface{} // Expected value of extension after ClearExtension().
|
||||
}
|
||||
tests := []testcase{
|
||||
{pb.E_NoDefaultDouble, setFloat64, nil},
|
||||
{pb.E_NoDefaultFloat, setFloat32, nil},
|
||||
{pb.E_NoDefaultInt32, setInt32, nil},
|
||||
{pb.E_NoDefaultInt64, setInt64, nil},
|
||||
{pb.E_NoDefaultUint32, setUint32, nil},
|
||||
{pb.E_NoDefaultUint64, setUint64, nil},
|
||||
{pb.E_NoDefaultSint32, setInt32, nil},
|
||||
{pb.E_NoDefaultSint64, setInt64, nil},
|
||||
{pb.E_NoDefaultFixed32, setUint32, nil},
|
||||
{pb.E_NoDefaultFixed64, setUint64, nil},
|
||||
{pb.E_NoDefaultSfixed32, setInt32, nil},
|
||||
{pb.E_NoDefaultSfixed64, setInt64, nil},
|
||||
{pb.E_NoDefaultBool, setBool, nil},
|
||||
{pb.E_NoDefaultBool, setBool2, nil},
|
||||
{pb.E_NoDefaultString, setString, nil},
|
||||
{pb.E_NoDefaultBytes, setBytes, nil},
|
||||
{pb.E_NoDefaultEnum, setEnum, nil},
|
||||
{pb.E_DefaultDouble, setFloat64, float64(3.1415)},
|
||||
{pb.E_DefaultFloat, setFloat32, float32(3.14)},
|
||||
{pb.E_DefaultInt32, setInt32, int32(42)},
|
||||
{pb.E_DefaultInt64, setInt64, int64(43)},
|
||||
{pb.E_DefaultUint32, setUint32, uint32(44)},
|
||||
{pb.E_DefaultUint64, setUint64, uint64(45)},
|
||||
{pb.E_DefaultSint32, setInt32, int32(46)},
|
||||
{pb.E_DefaultSint64, setInt64, int64(47)},
|
||||
{pb.E_DefaultFixed32, setUint32, uint32(48)},
|
||||
{pb.E_DefaultFixed64, setUint64, uint64(49)},
|
||||
{pb.E_DefaultSfixed32, setInt32, int32(50)},
|
||||
{pb.E_DefaultSfixed64, setInt64, int64(51)},
|
||||
{pb.E_DefaultBool, setBool, true},
|
||||
{pb.E_DefaultBool, setBool2, true},
|
||||
{pb.E_DefaultString, setString, "Hello, string"},
|
||||
{pb.E_DefaultBytes, setBytes, []byte("Hello, bytes")},
|
||||
{pb.E_DefaultEnum, setEnum, pb.DefaultsMessage_ONE},
|
||||
}
|
||||
|
||||
checkVal := func(test testcase, msg *pb.DefaultsMessage, valWant interface{}) error {
|
||||
val, err := proto.GetExtension(msg, test.ext)
|
||||
if err != nil {
|
||||
if valWant != nil {
|
||||
return fmt.Errorf("GetExtension(): %s", err)
|
||||
}
|
||||
if want := proto.ErrMissingExtension; err != want {
|
||||
return fmt.Errorf("Unexpected error: got %v, want %v", err, want)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// All proto2 extension values are either a pointer to a value or a slice of values.
|
||||
ty := reflect.TypeOf(val)
|
||||
tyWant := reflect.TypeOf(test.ext.ExtensionType)
|
||||
if got, want := ty, tyWant; got != want {
|
||||
return fmt.Errorf("unexpected reflect.TypeOf(): got %v want %v", got, want)
|
||||
}
|
||||
tye := ty.Elem()
|
||||
tyeWant := tyWant.Elem()
|
||||
if got, want := tye, tyeWant; got != want {
|
||||
return fmt.Errorf("unexpected reflect.TypeOf().Elem(): got %v want %v", got, want)
|
||||
}
|
||||
|
||||
// Check the name of the type of the value.
|
||||
// If it is an enum it will be type int32 with the name of the enum.
|
||||
if got, want := tye.Name(), tye.Name(); got != want {
|
||||
return fmt.Errorf("unexpected reflect.TypeOf().Elem().Name(): got %v want %v", got, want)
|
||||
}
|
||||
|
||||
// Check that value is what we expect.
|
||||
// If we have a pointer in val, get the value it points to.
|
||||
valExp := val
|
||||
if ty.Kind() == reflect.Ptr {
|
||||
valExp = reflect.ValueOf(val).Elem().Interface()
|
||||
}
|
||||
if got, want := valExp, valWant; !reflect.DeepEqual(got, want) {
|
||||
return fmt.Errorf("unexpected reflect.DeepEqual(): got %v want %v", got, want)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
setTo := func(test testcase) interface{} {
|
||||
setTo := reflect.ValueOf(test.want)
|
||||
if typ := reflect.TypeOf(test.ext.ExtensionType); typ.Kind() == reflect.Ptr {
|
||||
setTo = reflect.New(typ).Elem()
|
||||
setTo.Set(reflect.New(setTo.Type().Elem()))
|
||||
setTo.Elem().Set(reflect.ValueOf(test.want))
|
||||
}
|
||||
return setTo.Interface()
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
msg := &pb.DefaultsMessage{}
|
||||
name := test.ext.Name
|
||||
|
||||
// Check the initial value.
|
||||
if err := checkVal(test, msg, test.def); err != nil {
|
||||
t.Errorf("%s: %v", name, err)
|
||||
}
|
||||
|
||||
// Set the per-type value and check value.
|
||||
name = fmt.Sprintf("%s (set to %T %v)", name, test.want, test.want)
|
||||
if err := proto.SetExtension(msg, test.ext, setTo(test)); err != nil {
|
||||
t.Errorf("%s: SetExtension(): %v", name, err)
|
||||
continue
|
||||
}
|
||||
if err := checkVal(test, msg, test.want); err != nil {
|
||||
t.Errorf("%s: %v", name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Set and check the value.
|
||||
name += " (cleared)"
|
||||
proto.ClearExtension(msg, test.ext)
|
||||
if err := checkVal(test, msg, test.def); err != nil {
|
||||
t.Errorf("%s: %v", name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtensionsRoundTrip(t *testing.T) {
|
||||
msg := &pb.MyMessage{}
|
||||
ext1 := &pb.Ext{
|
||||
Data: proto.String("hi"),
|
||||
}
|
||||
ext2 := &pb.Ext{
|
||||
Data: proto.String("there"),
|
||||
}
|
||||
exists := proto.HasExtension(msg, pb.E_Ext_More)
|
||||
if exists {
|
||||
t.Error("Extension More present unexpectedly")
|
||||
}
|
||||
if err := proto.SetExtension(msg, pb.E_Ext_More, ext1); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if err := proto.SetExtension(msg, pb.E_Ext_More, ext2); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
e, err := proto.GetExtension(msg, pb.E_Ext_More)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
x, ok := e.(*pb.Ext)
|
||||
if !ok {
|
||||
t.Errorf("e has type %T, expected testdata.Ext", e)
|
||||
} else if *x.Data != "there" {
|
||||
t.Errorf("SetExtension failed to overwrite, got %+v, not 'there'", x)
|
||||
}
|
||||
proto.ClearExtension(msg, pb.E_Ext_More)
|
||||
if _, err = proto.GetExtension(msg, pb.E_Ext_More); err != proto.ErrMissingExtension {
|
||||
t.Errorf("got %v, expected ErrMissingExtension", e)
|
||||
}
|
||||
if _, err := proto.GetExtension(msg, pb.E_X215); err == nil {
|
||||
t.Error("expected bad extension error, got nil")
|
||||
}
|
||||
if err := proto.SetExtension(msg, pb.E_X215, 12); err == nil {
|
||||
t.Error("expected extension err")
|
||||
}
|
||||
if err := proto.SetExtension(msg, pb.E_Ext_More, 12); err == nil {
|
||||
t.Error("expected some sort of type mismatch error, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNilExtension(t *testing.T) {
|
||||
msg := &pb.MyMessage{
|
||||
Count: proto.Int32(1),
|
||||
}
|
||||
if err := proto.SetExtension(msg, pb.E_Ext_Text, proto.String("hello")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := proto.SetExtension(msg, pb.E_Ext_More, (*pb.Ext)(nil)); err == nil {
|
||||
t.Error("expected SetExtension to fail due to a nil extension")
|
||||
} else if want := "proto: SetExtension called with nil value of type *testdata.Ext"; err.Error() != want {
|
||||
t.Errorf("expected error %v, got %v", want, err)
|
||||
}
|
||||
// Note: if the behavior of Marshal is ever changed to ignore nil extensions, update
|
||||
// this test to verify that E_Ext_Text is properly propagated through marshal->unmarshal.
|
||||
}
|
813
Godeps/_workspace/src/github.com/golang/protobuf/proto/lib.go
generated
vendored
Normal file
813
Godeps/_workspace/src/github.com/golang/protobuf/proto/lib.go
generated
vendored
Normal file
|
@ -0,0 +1,813 @@
|
|||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
/*
|
||||
Package proto converts data structures to and from the wire format of
|
||||
protocol buffers. It works in concert with the Go source code generated
|
||||
for .proto files by the protocol compiler.
|
||||
|
||||
A summary of the properties of the protocol buffer interface
|
||||
for a protocol buffer variable v:
|
||||
|
||||
- Names are turned from camel_case to CamelCase for export.
|
||||
- There are no methods on v to set fields; just treat
|
||||
them as structure fields.
|
||||
- There are getters that return a field's value if set,
|
||||
and return the field's default value if unset.
|
||||
The getters work even if the receiver is a nil message.
|
||||
- The zero value for a struct is its correct initialization state.
|
||||
All desired fields must be set before marshaling.
|
||||
- A Reset() method will restore a protobuf struct to its zero state.
|
||||
- Non-repeated fields are pointers to the values; nil means unset.
|
||||
That is, optional or required field int32 f becomes F *int32.
|
||||
- Repeated fields are slices.
|
||||
- Helper functions are available to aid the setting of fields.
|
||||
msg.Foo = proto.String("hello") // set field
|
||||
- Constants are defined to hold the default values of all fields that
|
||||
have them. They have the form Default_StructName_FieldName.
|
||||
Because the getter methods handle defaulted values,
|
||||
direct use of these constants should be rare.
|
||||
- Enums are given type names and maps from names to values.
|
||||
Enum values are prefixed by the enclosing message's name, or by the
|
||||
enum's type name if it is a top-level enum. Enum types have a String
|
||||
method, and a Enum method to assist in message construction.
|
||||
- Nested messages, groups and enums have type names prefixed with the name of
|
||||
the surrounding message type.
|
||||
- Extensions are given descriptor names that start with E_,
|
||||
followed by an underscore-delimited list of the nested messages
|
||||
that contain it (if any) followed by the CamelCased name of the
|
||||
extension field itself. HasExtension, ClearExtension, GetExtension
|
||||
and SetExtension are functions for manipulating extensions.
|
||||
- Marshal and Unmarshal are functions to encode and decode the wire format.
|
||||
|
||||
The simplest way to describe this is to see an example.
|
||||
Given file test.proto, containing
|
||||
|
||||
package example;
|
||||
|
||||
enum FOO { X = 17; }
|
||||
|
||||
message Test {
|
||||
required string label = 1;
|
||||
optional int32 type = 2 [default=77];
|
||||
repeated int64 reps = 3;
|
||||
optional group OptionalGroup = 4 {
|
||||
required string RequiredField = 5;
|
||||
}
|
||||
}
|
||||
|
||||
The resulting file, test.pb.go, is:
|
||||
|
||||
package example
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import math "math"
|
||||
|
||||
type FOO int32
|
||||
const (
|
||||
FOO_X FOO = 17
|
||||
)
|
||||
var FOO_name = map[int32]string{
|
||||
17: "X",
|
||||
}
|
||||
var FOO_value = map[string]int32{
|
||||
"X": 17,
|
||||
}
|
||||
|
||||
func (x FOO) Enum() *FOO {
|
||||
p := new(FOO)
|
||||
*p = x
|
||||
return p
|
||||
}
|
||||
func (x FOO) String() string {
|
||||
return proto.EnumName(FOO_name, int32(x))
|
||||
}
|
||||
func (x *FOO) UnmarshalJSON(data []byte) error {
|
||||
value, err := proto.UnmarshalJSONEnum(FOO_value, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*x = FOO(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
type Test struct {
|
||||
Label *string `protobuf:"bytes,1,req,name=label" json:"label,omitempty"`
|
||||
Type *int32 `protobuf:"varint,2,opt,name=type,def=77" json:"type,omitempty"`
|
||||
Reps []int64 `protobuf:"varint,3,rep,name=reps" json:"reps,omitempty"`
|
||||
Optionalgroup *Test_OptionalGroup `protobuf:"group,4,opt,name=OptionalGroup" json:"optionalgroup,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
func (m *Test) Reset() { *m = Test{} }
|
||||
func (m *Test) String() string { return proto.CompactTextString(m) }
|
||||
func (*Test) ProtoMessage() {}
|
||||
const Default_Test_Type int32 = 77
|
||||
|
||||
func (m *Test) GetLabel() string {
|
||||
if m != nil && m.Label != nil {
|
||||
return *m.Label
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Test) GetType() int32 {
|
||||
if m != nil && m.Type != nil {
|
||||
return *m.Type
|
||||
}
|
||||
return Default_Test_Type
|
||||
}
|
||||
|
||||
func (m *Test) GetOptionalgroup() *Test_OptionalGroup {
|
||||
if m != nil {
|
||||
return m.Optionalgroup
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Test_OptionalGroup struct {
|
||||
RequiredField *string `protobuf:"bytes,5,req" json:"RequiredField,omitempty"`
|
||||
}
|
||||
func (m *Test_OptionalGroup) Reset() { *m = Test_OptionalGroup{} }
|
||||
func (m *Test_OptionalGroup) String() string { return proto.CompactTextString(m) }
|
||||
|
||||
func (m *Test_OptionalGroup) GetRequiredField() string {
|
||||
if m != nil && m.RequiredField != nil {
|
||||
return *m.RequiredField
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterEnum("example.FOO", FOO_name, FOO_value)
|
||||
}
|
||||
|
||||
To create and play with a Test object:
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
pb "./example.pb"
|
||||
)
|
||||
|
||||
func main() {
|
||||
test := &pb.Test{
|
||||
Label: proto.String("hello"),
|
||||
Type: proto.Int32(17),
|
||||
Optionalgroup: &pb.Test_OptionalGroup{
|
||||
RequiredField: proto.String("good bye"),
|
||||
},
|
||||
}
|
||||
data, err := proto.Marshal(test)
|
||||
if err != nil {
|
||||
log.Fatal("marshaling error: ", err)
|
||||
}
|
||||
newTest := &pb.Test{}
|
||||
err = proto.Unmarshal(data, newTest)
|
||||
if err != nil {
|
||||
log.Fatal("unmarshaling error: ", err)
|
||||
}
|
||||
// Now test and newTest contain the same data.
|
||||
if test.GetLabel() != newTest.GetLabel() {
|
||||
log.Fatalf("data mismatch %q != %q", test.GetLabel(), newTest.GetLabel())
|
||||
}
|
||||
// etc.
|
||||
}
|
||||
*/
|
||||
package proto
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Message is implemented by generated protocol buffer messages.
|
||||
type Message interface {
|
||||
Reset()
|
||||
String() string
|
||||
ProtoMessage()
|
||||
}
|
||||
|
||||
// Stats records allocation details about the protocol buffer encoders
|
||||
// and decoders. Useful for tuning the library itself.
|
||||
type Stats struct {
|
||||
Emalloc uint64 // mallocs in encode
|
||||
Dmalloc uint64 // mallocs in decode
|
||||
Encode uint64 // number of encodes
|
||||
Decode uint64 // number of decodes
|
||||
Chit uint64 // number of cache hits
|
||||
Cmiss uint64 // number of cache misses
|
||||
Size uint64 // number of sizes
|
||||
}
|
||||
|
||||
// Set to true to enable stats collection.
|
||||
const collectStats = false
|
||||
|
||||
var stats Stats
|
||||
|
||||
// GetStats returns a copy of the global Stats structure.
|
||||
func GetStats() Stats { return stats }
|
||||
|
||||
// A Buffer is a buffer manager for marshaling and unmarshaling
|
||||
// protocol buffers. It may be reused between invocations to
|
||||
// reduce memory usage. It is not necessary to use a Buffer;
|
||||
// the global functions Marshal and Unmarshal create a
|
||||
// temporary Buffer and are fine for most applications.
|
||||
type Buffer struct {
|
||||
buf []byte // encode/decode byte stream
|
||||
index int // write point
|
||||
|
||||
// pools of basic types to amortize allocation.
|
||||
bools []bool
|
||||
uint32s []uint32
|
||||
uint64s []uint64
|
||||
|
||||
// extra pools, only used with pointer_reflect.go
|
||||
int32s []int32
|
||||
int64s []int64
|
||||
float32s []float32
|
||||
float64s []float64
|
||||
}
|
||||
|
||||
// NewBuffer allocates a new Buffer and initializes its internal data to
|
||||
// the contents of the argument slice.
|
||||
func NewBuffer(e []byte) *Buffer {
|
||||
return &Buffer{buf: e}
|
||||
}
|
||||
|
||||
// Reset resets the Buffer, ready for marshaling a new protocol buffer.
|
||||
func (p *Buffer) Reset() {
|
||||
p.buf = p.buf[0:0] // for reading/writing
|
||||
p.index = 0 // for reading
|
||||
}
|
||||
|
||||
// SetBuf replaces the internal buffer with the slice,
|
||||
// ready for unmarshaling the contents of the slice.
|
||||
func (p *Buffer) SetBuf(s []byte) {
|
||||
p.buf = s
|
||||
p.index = 0
|
||||
}
|
||||
|
||||
// Bytes returns the contents of the Buffer.
|
||||
func (p *Buffer) Bytes() []byte { return p.buf }
|
||||
|
||||
/*
|
||||
* Helper routines for simplifying the creation of optional fields of basic type.
|
||||
*/
|
||||
|
||||
// Bool is a helper routine that allocates a new bool value
|
||||
// to store v and returns a pointer to it.
|
||||
func Bool(v bool) *bool {
|
||||
return &v
|
||||
}
|
||||
|
||||
// Int32 is a helper routine that allocates a new int32 value
|
||||
// to store v and returns a pointer to it.
|
||||
func Int32(v int32) *int32 {
|
||||
return &v
|
||||
}
|
||||
|
||||
// Int is a helper routine that allocates a new int32 value
|
||||
// to store v and returns a pointer to it, but unlike Int32
|
||||
// its argument value is an int.
|
||||
func Int(v int) *int32 {
|
||||
p := new(int32)
|
||||
*p = int32(v)
|
||||
return p
|
||||
}
|
||||
|
||||
// Int64 is a helper routine that allocates a new int64 value
|
||||
// to store v and returns a pointer to it.
|
||||
func Int64(v int64) *int64 {
|
||||
return &v
|
||||
}
|
||||
|
||||
// Float32 is a helper routine that allocates a new float32 value
|
||||
// to store v and returns a pointer to it.
|
||||
func Float32(v float32) *float32 {
|
||||
return &v
|
||||
}
|
||||
|
||||
// Float64 is a helper routine that allocates a new float64 value
|
||||
// to store v and returns a pointer to it.
|
||||
func Float64(v float64) *float64 {
|
||||
return &v
|
||||
}
|
||||
|
||||
// Uint32 is a helper routine that allocates a new uint32 value
|
||||
// to store v and returns a pointer to it.
|
||||
func Uint32(v uint32) *uint32 {
|
||||
return &v
|
||||
}
|
||||
|
||||
// Uint64 is a helper routine that allocates a new uint64 value
|
||||
// to store v and returns a pointer to it.
|
||||
func Uint64(v uint64) *uint64 {
|
||||
return &v
|
||||
}
|
||||
|
||||
// String is a helper routine that allocates a new string value
|
||||
// to store v and returns a pointer to it.
|
||||
func String(v string) *string {
|
||||
return &v
|
||||
}
|
||||
|
||||
// EnumName is a helper function to simplify printing protocol buffer enums
|
||||
// by name. Given an enum map and a value, it returns a useful string.
|
||||
func EnumName(m map[int32]string, v int32) string {
|
||||
s, ok := m[v]
|
||||
if ok {
|
||||
return s
|
||||
}
|
||||
return strconv.Itoa(int(v))
|
||||
}
|
||||
|
||||
// UnmarshalJSONEnum is a helper function to simplify recovering enum int values
|
||||
// from their JSON-encoded representation. Given a map from the enum's symbolic
|
||||
// names to its int values, and a byte buffer containing the JSON-encoded
|
||||
// value, it returns an int32 that can be cast to the enum type by the caller.
|
||||
//
|
||||
// The function can deal with both JSON representations, numeric and symbolic.
|
||||
func UnmarshalJSONEnum(m map[string]int32, data []byte, enumName string) (int32, error) {
|
||||
if data[0] == '"' {
|
||||
// New style: enums are strings.
|
||||
var repr string
|
||||
if err := json.Unmarshal(data, &repr); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
val, ok := m[repr]
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("unrecognized enum %s value %q", enumName, repr)
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
// Old style: enums are ints.
|
||||
var val int32
|
||||
if err := json.Unmarshal(data, &val); err != nil {
|
||||
return 0, fmt.Errorf("cannot unmarshal %#q into enum %s", data, enumName)
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
// DebugPrint dumps the encoded data in b in a debugging format with a header
|
||||
// including the string s. Used in testing but made available for general debugging.
|
||||
func (p *Buffer) DebugPrint(s string, b []byte) {
|
||||
var u uint64
|
||||
|
||||
obuf := p.buf
|
||||
index := p.index
|
||||
p.buf = b
|
||||
p.index = 0
|
||||
depth := 0
|
||||
|
||||
fmt.Printf("\n--- %s ---\n", s)
|
||||
|
||||
out:
|
||||
for {
|
||||
for i := 0; i < depth; i++ {
|
||||
fmt.Print(" ")
|
||||
}
|
||||
|
||||
index := p.index
|
||||
if index == len(p.buf) {
|
||||
break
|
||||
}
|
||||
|
||||
op, err := p.DecodeVarint()
|
||||
if err != nil {
|
||||
fmt.Printf("%3d: fetching op err %v\n", index, err)
|
||||
break out
|
||||
}
|
||||
tag := op >> 3
|
||||
wire := op & 7
|
||||
|
||||
switch wire {
|
||||
default:
|
||||
fmt.Printf("%3d: t=%3d unknown wire=%d\n",
|
||||
index, tag, wire)
|
||||
break out
|
||||
|
||||
case WireBytes:
|
||||
var r []byte
|
||||
|
||||
r, err = p.DecodeRawBytes(false)
|
||||
if err != nil {
|
||||
break out
|
||||
}
|
||||
fmt.Printf("%3d: t=%3d bytes [%d]", index, tag, len(r))
|
||||
if len(r) <= 6 {
|
||||
for i := 0; i < len(r); i++ {
|
||||
fmt.Printf(" %.2x", r[i])
|
||||
}
|
||||
} else {
|
||||
for i := 0; i < 3; i++ {
|
||||
fmt.Printf(" %.2x", r[i])
|
||||
}
|
||||
fmt.Printf(" ..")
|
||||
for i := len(r) - 3; i < len(r); i++ {
|
||||
fmt.Printf(" %.2x", r[i])
|
||||
}
|
||||
}
|
||||
fmt.Printf("\n")
|
||||
|
||||
case WireFixed32:
|
||||
u, err = p.DecodeFixed32()
|
||||
if err != nil {
|
||||
fmt.Printf("%3d: t=%3d fix32 err %v\n", index, tag, err)
|
||||
break out
|
||||
}
|
||||
fmt.Printf("%3d: t=%3d fix32 %d\n", index, tag, u)
|
||||
|
||||
case WireFixed64:
|
||||
u, err = p.DecodeFixed64()
|
||||
if err != nil {
|
||||
fmt.Printf("%3d: t=%3d fix64 err %v\n", index, tag, err)
|
||||
break out
|
||||
}
|
||||
fmt.Printf("%3d: t=%3d fix64 %d\n", index, tag, u)
|
||||
break
|
||||
|
||||
case WireVarint:
|
||||
u, err = p.DecodeVarint()
|
||||
if err != nil {
|
||||
fmt.Printf("%3d: t=%3d varint err %v\n", index, tag, err)
|
||||
break out
|
||||
}
|
||||
fmt.Printf("%3d: t=%3d varint %d\n", index, tag, u)
|
||||
|
||||
case WireStartGroup:
|
||||
if err != nil {
|
||||
fmt.Printf("%3d: t=%3d start err %v\n", index, tag, err)
|
||||
break out
|
||||
}
|
||||
fmt.Printf("%3d: t=%3d start\n", index, tag)
|
||||
depth++
|
||||
|
||||
case WireEndGroup:
|
||||
depth--
|
||||
if err != nil {
|
||||
fmt.Printf("%3d: t=%3d end err %v\n", index, tag, err)
|
||||
break out
|
||||
}
|
||||
fmt.Printf("%3d: t=%3d end\n", index, tag)
|
||||
}
|
||||
}
|
||||
|
||||
if depth != 0 {
|
||||
fmt.Printf("%3d: start-end not balanced %d\n", p.index, depth)
|
||||
}
|
||||
fmt.Printf("\n")
|
||||
|
||||
p.buf = obuf
|
||||
p.index = index
|
||||
}
|
||||
|
||||
// SetDefaults sets unset protocol buffer fields to their default values.
|
||||
// It only modifies fields that are both unset and have defined defaults.
|
||||
// It recursively sets default values in any non-nil sub-messages.
|
||||
func SetDefaults(pb Message) {
|
||||
setDefaults(reflect.ValueOf(pb), true, false)
|
||||
}
|
||||
|
||||
// v is a pointer to a struct.
|
||||
func setDefaults(v reflect.Value, recur, zeros bool) {
|
||||
v = v.Elem()
|
||||
|
||||
defaultMu.RLock()
|
||||
dm, ok := defaults[v.Type()]
|
||||
defaultMu.RUnlock()
|
||||
if !ok {
|
||||
dm = buildDefaultMessage(v.Type())
|
||||
defaultMu.Lock()
|
||||
defaults[v.Type()] = dm
|
||||
defaultMu.Unlock()
|
||||
}
|
||||
|
||||
for _, sf := range dm.scalars {
|
||||
f := v.Field(sf.index)
|
||||
if !f.IsNil() {
|
||||
// field already set
|
||||
continue
|
||||
}
|
||||
dv := sf.value
|
||||
if dv == nil && !zeros {
|
||||
// no explicit default, and don't want to set zeros
|
||||
continue
|
||||
}
|
||||
fptr := f.Addr().Interface() // **T
|
||||
// TODO: Consider batching the allocations we do here.
|
||||
switch sf.kind {
|
||||
case reflect.Bool:
|
||||
b := new(bool)
|
||||
if dv != nil {
|
||||
*b = dv.(bool)
|
||||
}
|
||||
*(fptr.(**bool)) = b
|
||||
case reflect.Float32:
|
||||
f := new(float32)
|
||||
if dv != nil {
|
||||
*f = dv.(float32)
|
||||
}
|
||||
*(fptr.(**float32)) = f
|
||||
case reflect.Float64:
|
||||
f := new(float64)
|
||||
if dv != nil {
|
||||
*f = dv.(float64)
|
||||
}
|
||||
*(fptr.(**float64)) = f
|
||||
case reflect.Int32:
|
||||
// might be an enum
|
||||
if ft := f.Type(); ft != int32PtrType {
|
||||
// enum
|
||||
f.Set(reflect.New(ft.Elem()))
|
||||
if dv != nil {
|
||||
f.Elem().SetInt(int64(dv.(int32)))
|
||||
}
|
||||
} else {
|
||||
// int32 field
|
||||
i := new(int32)
|
||||
if dv != nil {
|
||||
*i = dv.(int32)
|
||||
}
|
||||
*(fptr.(**int32)) = i
|
||||
}
|
||||
case reflect.Int64:
|
||||
i := new(int64)
|
||||
if dv != nil {
|
||||
*i = dv.(int64)
|
||||
}
|
||||
*(fptr.(**int64)) = i
|
||||
case reflect.String:
|
||||
s := new(string)
|
||||
if dv != nil {
|
||||
*s = dv.(string)
|
||||
}
|
||||
*(fptr.(**string)) = s
|
||||
case reflect.Uint8:
|
||||
// exceptional case: []byte
|
||||
var b []byte
|
||||
if dv != nil {
|
||||
db := dv.([]byte)
|
||||
b = make([]byte, len(db))
|
||||
copy(b, db)
|
||||
} else {
|
||||
b = []byte{}
|
||||
}
|
||||
*(fptr.(*[]byte)) = b
|
||||
case reflect.Uint32:
|
||||
u := new(uint32)
|
||||
if dv != nil {
|
||||
*u = dv.(uint32)
|
||||
}
|
||||
*(fptr.(**uint32)) = u
|
||||
case reflect.Uint64:
|
||||
u := new(uint64)
|
||||
if dv != nil {
|
||||
*u = dv.(uint64)
|
||||
}
|
||||
*(fptr.(**uint64)) = u
|
||||
default:
|
||||
log.Printf("proto: can't set default for field %v (sf.kind=%v)", f, sf.kind)
|
||||
}
|
||||
}
|
||||
|
||||
for _, ni := range dm.nested {
|
||||
f := v.Field(ni)
|
||||
// f is *T or []*T or map[T]*T
|
||||
switch f.Kind() {
|
||||
case reflect.Ptr:
|
||||
if f.IsNil() {
|
||||
continue
|
||||
}
|
||||
setDefaults(f, recur, zeros)
|
||||
|
||||
case reflect.Slice:
|
||||
for i := 0; i < f.Len(); i++ {
|
||||
e := f.Index(i)
|
||||
if e.IsNil() {
|
||||
continue
|
||||
}
|
||||
setDefaults(e, recur, zeros)
|
||||
}
|
||||
|
||||
case reflect.Map:
|
||||
for _, k := range f.MapKeys() {
|
||||
e := f.MapIndex(k)
|
||||
if e.IsNil() {
|
||||
continue
|
||||
}
|
||||
setDefaults(e, recur, zeros)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
// defaults maps a protocol buffer struct type to a slice of the fields,
|
||||
// with its scalar fields set to their proto-declared non-zero default values.
|
||||
defaultMu sync.RWMutex
|
||||
defaults = make(map[reflect.Type]defaultMessage)
|
||||
|
||||
int32PtrType = reflect.TypeOf((*int32)(nil))
|
||||
)
|
||||
|
||||
// defaultMessage represents information about the default values of a message.
|
||||
type defaultMessage struct {
|
||||
scalars []scalarField
|
||||
nested []int // struct field index of nested messages
|
||||
}
|
||||
|
||||
type scalarField struct {
|
||||
index int // struct field index
|
||||
kind reflect.Kind // element type (the T in *T or []T)
|
||||
value interface{} // the proto-declared default value, or nil
|
||||
}
|
||||
|
||||
// t is a struct type.
|
||||
func buildDefaultMessage(t reflect.Type) (dm defaultMessage) {
|
||||
sprop := GetProperties(t)
|
||||
for _, prop := range sprop.Prop {
|
||||
fi, ok := sprop.decoderTags.get(prop.Tag)
|
||||
if !ok {
|
||||
// XXX_unrecognized
|
||||
continue
|
||||
}
|
||||
ft := t.Field(fi).Type
|
||||
|
||||
sf, nested, err := fieldDefault(ft, prop)
|
||||
switch {
|
||||
case err != nil:
|
||||
log.Print(err)
|
||||
case nested:
|
||||
dm.nested = append(dm.nested, fi)
|
||||
case sf != nil:
|
||||
sf.index = fi
|
||||
dm.scalars = append(dm.scalars, *sf)
|
||||
}
|
||||
}
|
||||
|
||||
return dm
|
||||
}
|
||||
|
||||
// fieldDefault returns the scalarField for field type ft.
|
||||
// sf will be nil if the field can not have a default.
|
||||
// nestedMessage will be true if this is a nested message.
|
||||
// Note that sf.index is not set on return.
|
||||
func fieldDefault(ft reflect.Type, prop *Properties) (sf *scalarField, nestedMessage bool, err error) {
|
||||
var canHaveDefault bool
|
||||
switch ft.Kind() {
|
||||
case reflect.Ptr:
|
||||
if ft.Elem().Kind() == reflect.Struct {
|
||||
nestedMessage = true
|
||||
} else {
|
||||
canHaveDefault = true // proto2 scalar field
|
||||
}
|
||||
|
||||
case reflect.Slice:
|
||||
switch ft.Elem().Kind() {
|
||||
case reflect.Ptr:
|
||||
nestedMessage = true // repeated message
|
||||
case reflect.Uint8:
|
||||
canHaveDefault = true // bytes field
|
||||
}
|
||||
|
||||
case reflect.Map:
|
||||
if ft.Elem().Kind() == reflect.Ptr {
|
||||
nestedMessage = true // map with message values
|
||||
}
|
||||
}
|
||||
|
||||
if !canHaveDefault {
|
||||
if nestedMessage {
|
||||
return nil, true, nil
|
||||
}
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
// We now know that ft is a pointer or slice.
|
||||
sf = &scalarField{kind: ft.Elem().Kind()}
|
||||
|
||||
// scalar fields without defaults
|
||||
if !prop.HasDefault {
|
||||
return sf, false, nil
|
||||
}
|
||||
|
||||
// a scalar field: either *T or []byte
|
||||
switch ft.Elem().Kind() {
|
||||
case reflect.Bool:
|
||||
x, err := strconv.ParseBool(prop.Default)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("proto: bad default bool %q: %v", prop.Default, err)
|
||||
}
|
||||
sf.value = x
|
||||
case reflect.Float32:
|
||||
x, err := strconv.ParseFloat(prop.Default, 32)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("proto: bad default float32 %q: %v", prop.Default, err)
|
||||
}
|
||||
sf.value = float32(x)
|
||||
case reflect.Float64:
|
||||
x, err := strconv.ParseFloat(prop.Default, 64)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("proto: bad default float64 %q: %v", prop.Default, err)
|
||||
}
|
||||
sf.value = x
|
||||
case reflect.Int32:
|
||||
x, err := strconv.ParseInt(prop.Default, 10, 32)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("proto: bad default int32 %q: %v", prop.Default, err)
|
||||
}
|
||||
sf.value = int32(x)
|
||||
case reflect.Int64:
|
||||
x, err := strconv.ParseInt(prop.Default, 10, 64)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("proto: bad default int64 %q: %v", prop.Default, err)
|
||||
}
|
||||
sf.value = x
|
||||
case reflect.String:
|
||||
sf.value = prop.Default
|
||||
case reflect.Uint8:
|
||||
// []byte (not *uint8)
|
||||
sf.value = []byte(prop.Default)
|
||||
case reflect.Uint32:
|
||||
x, err := strconv.ParseUint(prop.Default, 10, 32)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("proto: bad default uint32 %q: %v", prop.Default, err)
|
||||
}
|
||||
sf.value = uint32(x)
|
||||
case reflect.Uint64:
|
||||
x, err := strconv.ParseUint(prop.Default, 10, 64)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("proto: bad default uint64 %q: %v", prop.Default, err)
|
||||
}
|
||||
sf.value = x
|
||||
default:
|
||||
return nil, false, fmt.Errorf("proto: unhandled def kind %v", ft.Elem().Kind())
|
||||
}
|
||||
|
||||
return sf, false, nil
|
||||
}
|
||||
|
||||
// Map fields may have key types of non-float scalars, strings and enums.
|
||||
// The easiest way to sort them in some deterministic order is to use fmt.
|
||||
// If this turns out to be inefficient we can always consider other options,
|
||||
// such as doing a Schwartzian transform.
|
||||
|
||||
type mapKeys []reflect.Value
|
||||
|
||||
func (s mapKeys) Len() int { return len(s) }
|
||||
func (s mapKeys) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
func (s mapKeys) Less(i, j int) bool {
|
||||
return fmt.Sprint(s[i].Interface()) < fmt.Sprint(s[j].Interface())
|
||||
}
|
||||
|
||||
// isProto3Zero reports whether v is a zero proto3 value.
|
||||
func isProto3Zero(v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
case reflect.Bool:
|
||||
return !v.Bool()
|
||||
case reflect.Int32, reflect.Int64:
|
||||
return v.Int() == 0
|
||||
case reflect.Uint32, reflect.Uint64:
|
||||
return v.Uint() == 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v.Float() == 0
|
||||
case reflect.String:
|
||||
return v.String() == ""
|
||||
}
|
||||
return false
|
||||
}
|
287
Godeps/_workspace/src/github.com/golang/protobuf/proto/message_set.go
generated
vendored
Normal file
287
Godeps/_workspace/src/github.com/golang/protobuf/proto/message_set.go
generated
vendored
Normal file
|
@ -0,0 +1,287 @@
|
|||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package proto
|
||||
|
||||
/*
|
||||
* Support for message sets.
|
||||
*/
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// ErrNoMessageTypeId occurs when a protocol buffer does not have a message type ID.
|
||||
// A message type ID is required for storing a protocol buffer in a message set.
|
||||
var ErrNoMessageTypeId = errors.New("proto does not have a message type ID")
|
||||
|
||||
// The first two types (_MessageSet_Item and MessageSet)
|
||||
// model what the protocol compiler produces for the following protocol message:
|
||||
// message MessageSet {
|
||||
// repeated group Item = 1 {
|
||||
// required int32 type_id = 2;
|
||||
// required string message = 3;
|
||||
// };
|
||||
// }
|
||||
// That is the MessageSet wire format. We can't use a proto to generate these
|
||||
// because that would introduce a circular dependency between it and this package.
|
||||
//
|
||||
// When a proto1 proto has a field that looks like:
|
||||
// optional message<MessageSet> info = 3;
|
||||
// the protocol compiler produces a field in the generated struct that looks like:
|
||||
// Info *_proto_.MessageSet `protobuf:"bytes,3,opt,name=info"`
|
||||
// The package is automatically inserted so there is no need for that proto file to
|
||||
// import this package.
|
||||
|
||||
type _MessageSet_Item struct {
|
||||
TypeId *int32 `protobuf:"varint,2,req,name=type_id"`
|
||||
Message []byte `protobuf:"bytes,3,req,name=message"`
|
||||
}
|
||||
|
||||
type MessageSet struct {
|
||||
Item []*_MessageSet_Item `protobuf:"group,1,rep"`
|
||||
XXX_unrecognized []byte
|
||||
// TODO: caching?
|
||||
}
|
||||
|
||||
// Make sure MessageSet is a Message.
|
||||
var _ Message = (*MessageSet)(nil)
|
||||
|
||||
// messageTypeIder is an interface satisfied by a protocol buffer type
|
||||
// that may be stored in a MessageSet.
|
||||
type messageTypeIder interface {
|
||||
MessageTypeId() int32
|
||||
}
|
||||
|
||||
func (ms *MessageSet) find(pb Message) *_MessageSet_Item {
|
||||
mti, ok := pb.(messageTypeIder)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
id := mti.MessageTypeId()
|
||||
for _, item := range ms.Item {
|
||||
if *item.TypeId == id {
|
||||
return item
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ms *MessageSet) Has(pb Message) bool {
|
||||
if ms.find(pb) != nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (ms *MessageSet) Unmarshal(pb Message) error {
|
||||
if item := ms.find(pb); item != nil {
|
||||
return Unmarshal(item.Message, pb)
|
||||
}
|
||||
if _, ok := pb.(messageTypeIder); !ok {
|
||||
return ErrNoMessageTypeId
|
||||
}
|
||||
return nil // TODO: return error instead?
|
||||
}
|
||||
|
||||
func (ms *MessageSet) Marshal(pb Message) error {
|
||||
msg, err := Marshal(pb)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if item := ms.find(pb); item != nil {
|
||||
// reuse existing item
|
||||
item.Message = msg
|
||||
return nil
|
||||
}
|
||||
|
||||
mti, ok := pb.(messageTypeIder)
|
||||
if !ok {
|
||||
return ErrNoMessageTypeId
|
||||
}
|
||||
|
||||
mtid := mti.MessageTypeId()
|
||||
ms.Item = append(ms.Item, &_MessageSet_Item{
|
||||
TypeId: &mtid,
|
||||
Message: msg,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ms *MessageSet) Reset() { *ms = MessageSet{} }
|
||||
func (ms *MessageSet) String() string { return CompactTextString(ms) }
|
||||
func (*MessageSet) ProtoMessage() {}
|
||||
|
||||
// Support for the message_set_wire_format message option.
|
||||
|
||||
func skipVarint(buf []byte) []byte {
|
||||
i := 0
|
||||
for ; buf[i]&0x80 != 0; i++ {
|
||||
}
|
||||
return buf[i+1:]
|
||||
}
|
||||
|
||||
// MarshalMessageSet encodes the extension map represented by m in the message set wire format.
|
||||
// It is called by generated Marshal methods on protocol buffer messages with the message_set_wire_format option.
|
||||
func MarshalMessageSet(m map[int32]Extension) ([]byte, error) {
|
||||
if err := encodeExtensionMap(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Sort extension IDs to provide a deterministic encoding.
|
||||
// See also enc_map in encode.go.
|
||||
ids := make([]int, 0, len(m))
|
||||
for id := range m {
|
||||
ids = append(ids, int(id))
|
||||
}
|
||||
sort.Ints(ids)
|
||||
|
||||
ms := &MessageSet{Item: make([]*_MessageSet_Item, 0, len(m))}
|
||||
for _, id := range ids {
|
||||
e := m[int32(id)]
|
||||
// Remove the wire type and field number varint, as well as the length varint.
|
||||
msg := skipVarint(skipVarint(e.enc))
|
||||
|
||||
ms.Item = append(ms.Item, &_MessageSet_Item{
|
||||
TypeId: Int32(int32(id)),
|
||||
Message: msg,
|
||||
})
|
||||
}
|
||||
return Marshal(ms)
|
||||
}
|
||||
|
||||
// UnmarshalMessageSet decodes the extension map encoded in buf in the message set wire format.
|
||||
// It is called by generated Unmarshal methods on protocol buffer messages with the message_set_wire_format option.
|
||||
func UnmarshalMessageSet(buf []byte, m map[int32]Extension) error {
|
||||
ms := new(MessageSet)
|
||||
if err := Unmarshal(buf, ms); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, item := range ms.Item {
|
||||
id := *item.TypeId
|
||||
msg := item.Message
|
||||
|
||||
// Restore wire type and field number varint, plus length varint.
|
||||
// Be careful to preserve duplicate items.
|
||||
b := EncodeVarint(uint64(id)<<3 | WireBytes)
|
||||
if ext, ok := m[id]; ok {
|
||||
// Existing data; rip off the tag and length varint
|
||||
// so we join the new data correctly.
|
||||
// We can assume that ext.enc is set because we are unmarshaling.
|
||||
o := ext.enc[len(b):] // skip wire type and field number
|
||||
_, n := DecodeVarint(o) // calculate length of length varint
|
||||
o = o[n:] // skip length varint
|
||||
msg = append(o, msg...) // join old data and new data
|
||||
}
|
||||
b = append(b, EncodeVarint(uint64(len(msg)))...)
|
||||
b = append(b, msg...)
|
||||
|
||||
m[id] = Extension{enc: b}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalMessageSetJSON encodes the extension map represented by m in JSON format.
|
||||
// It is called by generated MarshalJSON methods on protocol buffer messages with the message_set_wire_format option.
|
||||
func MarshalMessageSetJSON(m map[int32]Extension) ([]byte, error) {
|
||||
var b bytes.Buffer
|
||||
b.WriteByte('{')
|
||||
|
||||
// Process the map in key order for deterministic output.
|
||||
ids := make([]int32, 0, len(m))
|
||||
for id := range m {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
sort.Sort(int32Slice(ids)) // int32Slice defined in text.go
|
||||
|
||||
for i, id := range ids {
|
||||
ext := m[id]
|
||||
if i > 0 {
|
||||
b.WriteByte(',')
|
||||
}
|
||||
|
||||
msd, ok := messageSetMap[id]
|
||||
if !ok {
|
||||
// Unknown type; we can't render it, so skip it.
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(&b, `"[%s]":`, msd.name)
|
||||
|
||||
x := ext.value
|
||||
if x == nil {
|
||||
x = reflect.New(msd.t.Elem()).Interface()
|
||||
if err := Unmarshal(ext.enc, x.(Message)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
d, err := json.Marshal(x)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.Write(d)
|
||||
}
|
||||
b.WriteByte('}')
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalMessageSetJSON decodes the extension map encoded in buf in JSON format.
|
||||
// It is called by generated UnmarshalJSON methods on protocol buffer messages with the message_set_wire_format option.
|
||||
func UnmarshalMessageSetJSON(buf []byte, m map[int32]Extension) error {
|
||||
// Common-case fast path.
|
||||
if len(buf) == 0 || bytes.Equal(buf, []byte("{}")) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// This is fairly tricky, and it's not clear that it is needed.
|
||||
return errors.New("TODO: UnmarshalMessageSetJSON not yet implemented")
|
||||
}
|
||||
|
||||
// A global registry of types that can be used in a MessageSet.
|
||||
|
||||
var messageSetMap = make(map[int32]messageSetDesc)
|
||||
|
||||
type messageSetDesc struct {
|
||||
t reflect.Type // pointer to struct
|
||||
name string
|
||||
}
|
||||
|
||||
// RegisterMessageSetType is called from the generated code.
|
||||
func RegisterMessageSetType(m Message, fieldNum int32, name string) {
|
||||
messageSetMap[fieldNum] = messageSetDesc{
|
||||
t: reflect.TypeOf(m),
|
||||
name: name,
|
||||
}
|
||||
}
|
66
Godeps/_workspace/src/github.com/golang/protobuf/proto/message_set_test.go
generated
vendored
Normal file
66
Godeps/_workspace/src/github.com/golang/protobuf/proto/message_set_test.go
generated
vendored
Normal file
|
@ -0,0 +1,66 @@
|
|||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package proto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUnmarshalMessageSetWithDuplicate(t *testing.T) {
|
||||
// Check that a repeated message set entry will be concatenated.
|
||||
in := &MessageSet{
|
||||
Item: []*_MessageSet_Item{
|
||||
{TypeId: Int32(12345), Message: []byte("hoo")},
|
||||
{TypeId: Int32(12345), Message: []byte("hah")},
|
||||
},
|
||||
}
|
||||
b, err := Marshal(in)
|
||||
if err != nil {
|
||||
t.Fatalf("Marshal: %v", err)
|
||||
}
|
||||
t.Logf("Marshaled bytes: %q", b)
|
||||
|
||||
m := make(map[int32]Extension)
|
||||
if err := UnmarshalMessageSet(b, m); err != nil {
|
||||
t.Fatalf("UnmarshalMessageSet: %v", err)
|
||||
}
|
||||
ext, ok := m[12345]
|
||||
if !ok {
|
||||
t.Fatalf("Didn't retrieve extension 12345; map is %v", m)
|
||||
}
|
||||
// Skip wire type/field number and length varints.
|
||||
got := skipVarint(skipVarint(ext.enc))
|
||||
if want := []byte("hoohah"); !bytes.Equal(got, want) {
|
||||
t.Errorf("Combined extension is %q, want %q", got, want)
|
||||
}
|
||||
}
|
479
Godeps/_workspace/src/github.com/golang/protobuf/proto/pointer_reflect.go
generated
vendored
Normal file
479
Godeps/_workspace/src/github.com/golang/protobuf/proto/pointer_reflect.go
generated
vendored
Normal file
|
@ -0,0 +1,479 @@
|
|||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// +build appengine
|
||||
|
||||
// This file contains an implementation of proto field accesses using package reflect.
|
||||
// It is slower than the code in pointer_unsafe.go but it avoids package unsafe and can
|
||||
// be used on App Engine.
|
||||
|
||||
package proto
|
||||
|
||||
import (
|
||||
"math"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// A structPointer is a pointer to a struct.
|
||||
type structPointer struct {
|
||||
v reflect.Value
|
||||
}
|
||||
|
||||
// toStructPointer returns a structPointer equivalent to the given reflect value.
|
||||
// The reflect value must itself be a pointer to a struct.
|
||||
func toStructPointer(v reflect.Value) structPointer {
|
||||
return structPointer{v}
|
||||
}
|
||||
|
||||
// IsNil reports whether p is nil.
|
||||
func structPointer_IsNil(p structPointer) bool {
|
||||
return p.v.IsNil()
|
||||
}
|
||||
|
||||
// Interface returns the struct pointer as an interface value.
|
||||
func structPointer_Interface(p structPointer, _ reflect.Type) interface{} {
|
||||
return p.v.Interface()
|
||||
}
|
||||
|
||||
// A field identifies a field in a struct, accessible from a structPointer.
|
||||
// In this implementation, a field is identified by the sequence of field indices
|
||||
// passed to reflect's FieldByIndex.
|
||||
type field []int
|
||||
|
||||
// toField returns a field equivalent to the given reflect field.
|
||||
func toField(f *reflect.StructField) field {
|
||||
return f.Index
|
||||
}
|
||||
|
||||
// invalidField is an invalid field identifier.
|
||||
var invalidField = field(nil)
|
||||
|
||||
// IsValid reports whether the field identifier is valid.
|
||||
func (f field) IsValid() bool { return f != nil }
|
||||
|
||||
// field returns the given field in the struct as a reflect value.
|
||||
func structPointer_field(p structPointer, f field) reflect.Value {
|
||||
// Special case: an extension map entry with a value of type T
|
||||
// passes a *T to the struct-handling code with a zero field,
|
||||
// expecting that it will be treated as equivalent to *struct{ X T },
|
||||
// which has the same memory layout. We have to handle that case
|
||||
// specially, because reflect will panic if we call FieldByIndex on a
|
||||
// non-struct.
|
||||
if f == nil {
|
||||
return p.v.Elem()
|
||||
}
|
||||
|
||||
return p.v.Elem().FieldByIndex(f)
|
||||
}
|
||||
|
||||
// ifield returns the given field in the struct as an interface value.
|
||||
func structPointer_ifield(p structPointer, f field) interface{} {
|
||||
return structPointer_field(p, f).Addr().Interface()
|
||||
}
|
||||
|
||||
// Bytes returns the address of a []byte field in the struct.
|
||||
func structPointer_Bytes(p structPointer, f field) *[]byte {
|
||||
return structPointer_ifield(p, f).(*[]byte)
|
||||
}
|
||||
|
||||
// BytesSlice returns the address of a [][]byte field in the struct.
|
||||
func structPointer_BytesSlice(p structPointer, f field) *[][]byte {
|
||||
return structPointer_ifield(p, f).(*[][]byte)
|
||||
}
|
||||
|
||||
// Bool returns the address of a *bool field in the struct.
|
||||
func structPointer_Bool(p structPointer, f field) **bool {
|
||||
return structPointer_ifield(p, f).(**bool)
|
||||
}
|
||||
|
||||
// BoolVal returns the address of a bool field in the struct.
|
||||
func structPointer_BoolVal(p structPointer, f field) *bool {
|
||||
return structPointer_ifield(p, f).(*bool)
|
||||
}
|
||||
|
||||
// BoolSlice returns the address of a []bool field in the struct.
|
||||
func structPointer_BoolSlice(p structPointer, f field) *[]bool {
|
||||
return structPointer_ifield(p, f).(*[]bool)
|
||||
}
|
||||
|
||||
// String returns the address of a *string field in the struct.
|
||||
func structPointer_String(p structPointer, f field) **string {
|
||||
return structPointer_ifield(p, f).(**string)
|
||||
}
|
||||
|
||||
// StringVal returns the address of a string field in the struct.
|
||||
func structPointer_StringVal(p structPointer, f field) *string {
|
||||
return structPointer_ifield(p, f).(*string)
|
||||
}
|
||||
|
||||
// StringSlice returns the address of a []string field in the struct.
|
||||
func structPointer_StringSlice(p structPointer, f field) *[]string {
|
||||
return structPointer_ifield(p, f).(*[]string)
|
||||
}
|
||||
|
||||
// ExtMap returns the address of an extension map field in the struct.
|
||||
func structPointer_ExtMap(p structPointer, f field) *map[int32]Extension {
|
||||
return structPointer_ifield(p, f).(*map[int32]Extension)
|
||||
}
|
||||
|
||||
// NewAt returns the reflect.Value for a pointer to a field in the struct.
|
||||
func structPointer_NewAt(p structPointer, f field, typ reflect.Type) reflect.Value {
|
||||
return structPointer_field(p, f).Addr()
|
||||
}
|
||||
|
||||
// SetStructPointer writes a *struct field in the struct.
|
||||
func structPointer_SetStructPointer(p structPointer, f field, q structPointer) {
|
||||
structPointer_field(p, f).Set(q.v)
|
||||
}
|
||||
|
||||
// GetStructPointer reads a *struct field in the struct.
|
||||
func structPointer_GetStructPointer(p structPointer, f field) structPointer {
|
||||
return structPointer{structPointer_field(p, f)}
|
||||
}
|
||||
|
||||
// StructPointerSlice the address of a []*struct field in the struct.
|
||||
func structPointer_StructPointerSlice(p structPointer, f field) structPointerSlice {
|
||||
return structPointerSlice{structPointer_field(p, f)}
|
||||
}
|
||||
|
||||
// A structPointerSlice represents the address of a slice of pointers to structs
|
||||
// (themselves messages or groups). That is, v.Type() is *[]*struct{...}.
|
||||
type structPointerSlice struct {
|
||||
v reflect.Value
|
||||
}
|
||||
|
||||
func (p structPointerSlice) Len() int { return p.v.Len() }
|
||||
func (p structPointerSlice) Index(i int) structPointer { return structPointer{p.v.Index(i)} }
|
||||
func (p structPointerSlice) Append(q structPointer) {
|
||||
p.v.Set(reflect.Append(p.v, q.v))
|
||||
}
|
||||
|
||||
var (
|
||||
int32Type = reflect.TypeOf(int32(0))
|
||||
uint32Type = reflect.TypeOf(uint32(0))
|
||||
float32Type = reflect.TypeOf(float32(0))
|
||||
int64Type = reflect.TypeOf(int64(0))
|
||||
uint64Type = reflect.TypeOf(uint64(0))
|
||||
float64Type = reflect.TypeOf(float64(0))
|
||||
)
|
||||
|
||||
// A word32 represents a field of type *int32, *uint32, *float32, or *enum.
|
||||
// That is, v.Type() is *int32, *uint32, *float32, or *enum and v is assignable.
|
||||
type word32 struct {
|
||||
v reflect.Value
|
||||
}
|
||||
|
||||
// IsNil reports whether p is nil.
|
||||
func word32_IsNil(p word32) bool {
|
||||
return p.v.IsNil()
|
||||
}
|
||||
|
||||
// Set sets p to point at a newly allocated word with bits set to x.
|
||||
func word32_Set(p word32, o *Buffer, x uint32) {
|
||||
t := p.v.Type().Elem()
|
||||
switch t {
|
||||
case int32Type:
|
||||
if len(o.int32s) == 0 {
|
||||
o.int32s = make([]int32, uint32PoolSize)
|
||||
}
|
||||
o.int32s[0] = int32(x)
|
||||
p.v.Set(reflect.ValueOf(&o.int32s[0]))
|
||||
o.int32s = o.int32s[1:]
|
||||
return
|
||||
case uint32Type:
|
||||
if len(o.uint32s) == 0 {
|
||||
o.uint32s = make([]uint32, uint32PoolSize)
|
||||
}
|
||||
o.uint32s[0] = x
|
||||
p.v.Set(reflect.ValueOf(&o.uint32s[0]))
|
||||
o.uint32s = o.uint32s[1:]
|
||||
return
|
||||
case float32Type:
|
||||
if len(o.float32s) == 0 {
|
||||
o.float32s = make([]float32, uint32PoolSize)
|
||||
}
|
||||
o.float32s[0] = math.Float32frombits(x)
|
||||
p.v.Set(reflect.ValueOf(&o.float32s[0]))
|
||||
o.float32s = o.float32s[1:]
|
||||
return
|
||||
}
|
||||
|
||||
// must be enum
|
||||
p.v.Set(reflect.New(t))
|
||||
p.v.Elem().SetInt(int64(int32(x)))
|
||||
}
|
||||
|
||||
// Get gets the bits pointed at by p, as a uint32.
|
||||
func word32_Get(p word32) uint32 {
|
||||
elem := p.v.Elem()
|
||||
switch elem.Kind() {
|
||||
case reflect.Int32:
|
||||
return uint32(elem.Int())
|
||||
case reflect.Uint32:
|
||||
return uint32(elem.Uint())
|
||||
case reflect.Float32:
|
||||
return math.Float32bits(float32(elem.Float()))
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// Word32 returns a reference to a *int32, *uint32, *float32, or *enum field in the struct.
|
||||
func structPointer_Word32(p structPointer, f field) word32 {
|
||||
return word32{structPointer_field(p, f)}
|
||||
}
|
||||
|
||||
// A word32Val represents a field of type int32, uint32, float32, or enum.
|
||||
// That is, v.Type() is int32, uint32, float32, or enum and v is assignable.
|
||||
type word32Val struct {
|
||||
v reflect.Value
|
||||
}
|
||||
|
||||
// Set sets *p to x.
|
||||
func word32Val_Set(p word32Val, x uint32) {
|
||||
switch p.v.Type() {
|
||||
case int32Type:
|
||||
p.v.SetInt(int64(x))
|
||||
return
|
||||
case uint32Type:
|
||||
p.v.SetUint(uint64(x))
|
||||
return
|
||||
case float32Type:
|
||||
p.v.SetFloat(float64(math.Float32frombits(x)))
|
||||
return
|
||||
}
|
||||
|
||||
// must be enum
|
||||
p.v.SetInt(int64(int32(x)))
|
||||
}
|
||||
|
||||
// Get gets the bits pointed at by p, as a uint32.
|
||||
func word32Val_Get(p word32Val) uint32 {
|
||||
elem := p.v
|
||||
switch elem.Kind() {
|
||||
case reflect.Int32:
|
||||
return uint32(elem.Int())
|
||||
case reflect.Uint32:
|
||||
return uint32(elem.Uint())
|
||||
case reflect.Float32:
|
||||
return math.Float32bits(float32(elem.Float()))
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// Word32Val returns a reference to a int32, uint32, float32, or enum field in the struct.
|
||||
func structPointer_Word32Val(p structPointer, f field) word32Val {
|
||||
return word32Val{structPointer_field(p, f)}
|
||||
}
|
||||
|
||||
// A word32Slice is a slice of 32-bit values.
|
||||
// That is, v.Type() is []int32, []uint32, []float32, or []enum.
|
||||
type word32Slice struct {
|
||||
v reflect.Value
|
||||
}
|
||||
|
||||
func (p word32Slice) Append(x uint32) {
|
||||
n, m := p.v.Len(), p.v.Cap()
|
||||
if n < m {
|
||||
p.v.SetLen(n + 1)
|
||||
} else {
|
||||
t := p.v.Type().Elem()
|
||||
p.v.Set(reflect.Append(p.v, reflect.Zero(t)))
|
||||
}
|
||||
elem := p.v.Index(n)
|
||||
switch elem.Kind() {
|
||||
case reflect.Int32:
|
||||
elem.SetInt(int64(int32(x)))
|
||||
case reflect.Uint32:
|
||||
elem.SetUint(uint64(x))
|
||||
case reflect.Float32:
|
||||
elem.SetFloat(float64(math.Float32frombits(x)))
|
||||
}
|
||||
}
|
||||
|
||||
func (p word32Slice) Len() int {
|
||||
return p.v.Len()
|
||||
}
|
||||
|
||||
func (p word32Slice) Index(i int) uint32 {
|
||||
elem := p.v.Index(i)
|
||||
switch elem.Kind() {
|
||||
case reflect.Int32:
|
||||
return uint32(elem.Int())
|
||||
case reflect.Uint32:
|
||||
return uint32(elem.Uint())
|
||||
case reflect.Float32:
|
||||
return math.Float32bits(float32(elem.Float()))
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// Word32Slice returns a reference to a []int32, []uint32, []float32, or []enum field in the struct.
|
||||
func structPointer_Word32Slice(p structPointer, f field) word32Slice {
|
||||
return word32Slice{structPointer_field(p, f)}
|
||||
}
|
||||
|
||||
// word64 is like word32 but for 64-bit values.
|
||||
type word64 struct {
|
||||
v reflect.Value
|
||||
}
|
||||
|
||||
func word64_Set(p word64, o *Buffer, x uint64) {
|
||||
t := p.v.Type().Elem()
|
||||
switch t {
|
||||
case int64Type:
|
||||
if len(o.int64s) == 0 {
|
||||
o.int64s = make([]int64, uint64PoolSize)
|
||||
}
|
||||
o.int64s[0] = int64(x)
|
||||
p.v.Set(reflect.ValueOf(&o.int64s[0]))
|
||||
o.int64s = o.int64s[1:]
|
||||
return
|
||||
case uint64Type:
|
||||
if len(o.uint64s) == 0 {
|
||||
o.uint64s = make([]uint64, uint64PoolSize)
|
||||
}
|
||||
o.uint64s[0] = x
|
||||
p.v.Set(reflect.ValueOf(&o.uint64s[0]))
|
||||
o.uint64s = o.uint64s[1:]
|
||||
return
|
||||
case float64Type:
|
||||
if len(o.float64s) == 0 {
|
||||
o.float64s = make([]float64, uint64PoolSize)
|
||||
}
|
||||
o.float64s[0] = math.Float64frombits(x)
|
||||
p.v.Set(reflect.ValueOf(&o.float64s[0]))
|
||||
o.float64s = o.float64s[1:]
|
||||
return
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
func word64_IsNil(p word64) bool {
|
||||
return p.v.IsNil()
|
||||
}
|
||||
|
||||
func word64_Get(p word64) uint64 {
|
||||
elem := p.v.Elem()
|
||||
switch elem.Kind() {
|
||||
case reflect.Int64:
|
||||
return uint64(elem.Int())
|
||||
case reflect.Uint64:
|
||||
return elem.Uint()
|
||||
case reflect.Float64:
|
||||
return math.Float64bits(elem.Float())
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
func structPointer_Word64(p structPointer, f field) word64 {
|
||||
return word64{structPointer_field(p, f)}
|
||||
}
|
||||
|
||||
// word64Val is like word32Val but for 64-bit values.
|
||||
type word64Val struct {
|
||||
v reflect.Value
|
||||
}
|
||||
|
||||
func word64Val_Set(p word64Val, o *Buffer, x uint64) {
|
||||
switch p.v.Type() {
|
||||
case int64Type:
|
||||
p.v.SetInt(int64(x))
|
||||
return
|
||||
case uint64Type:
|
||||
p.v.SetUint(x)
|
||||
return
|
||||
case float64Type:
|
||||
p.v.SetFloat(math.Float64frombits(x))
|
||||
return
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
func word64Val_Get(p word64Val) uint64 {
|
||||
elem := p.v
|
||||
switch elem.Kind() {
|
||||
case reflect.Int64:
|
||||
return uint64(elem.Int())
|
||||
case reflect.Uint64:
|
||||
return elem.Uint()
|
||||
case reflect.Float64:
|
||||
return math.Float64bits(elem.Float())
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
func structPointer_Word64Val(p structPointer, f field) word64Val {
|
||||
return word64Val{structPointer_field(p, f)}
|
||||
}
|
||||
|
||||
type word64Slice struct {
|
||||
v reflect.Value
|
||||
}
|
||||
|
||||
func (p word64Slice) Append(x uint64) {
|
||||
n, m := p.v.Len(), p.v.Cap()
|
||||
if n < m {
|
||||
p.v.SetLen(n + 1)
|
||||
} else {
|
||||
t := p.v.Type().Elem()
|
||||
p.v.Set(reflect.Append(p.v, reflect.Zero(t)))
|
||||
}
|
||||
elem := p.v.Index(n)
|
||||
switch elem.Kind() {
|
||||
case reflect.Int64:
|
||||
elem.SetInt(int64(int64(x)))
|
||||
case reflect.Uint64:
|
||||
elem.SetUint(uint64(x))
|
||||
case reflect.Float64:
|
||||
elem.SetFloat(float64(math.Float64frombits(x)))
|
||||
}
|
||||
}
|
||||
|
||||
func (p word64Slice) Len() int {
|
||||
return p.v.Len()
|
||||
}
|
||||
|
||||
func (p word64Slice) Index(i int) uint64 {
|
||||
elem := p.v.Index(i)
|
||||
switch elem.Kind() {
|
||||
case reflect.Int64:
|
||||
return uint64(elem.Int())
|
||||
case reflect.Uint64:
|
||||
return uint64(elem.Uint())
|
||||
case reflect.Float64:
|
||||
return math.Float64bits(float64(elem.Float()))
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
func structPointer_Word64Slice(p structPointer, f field) word64Slice {
|
||||
return word64Slice{structPointer_field(p, f)}
|
||||
}
|
266
Godeps/_workspace/src/github.com/golang/protobuf/proto/pointer_unsafe.go
generated
vendored
Normal file
266
Godeps/_workspace/src/github.com/golang/protobuf/proto/pointer_unsafe.go
generated
vendored
Normal file
|
@ -0,0 +1,266 @@
|
|||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// +build !appengine
|
||||
|
||||
// This file contains the implementation of the proto field accesses using package unsafe.
|
||||
|
||||
package proto
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// NOTE: These type_Foo functions would more idiomatically be methods,
|
||||
// but Go does not allow methods on pointer types, and we must preserve
|
||||
// some pointer type for the garbage collector. We use these
|
||||
// funcs with clunky names as our poor approximation to methods.
|
||||
//
|
||||
// An alternative would be
|
||||
// type structPointer struct { p unsafe.Pointer }
|
||||
// but that does not registerize as well.
|
||||
|
||||
// A structPointer is a pointer to a struct.
|
||||
type structPointer unsafe.Pointer
|
||||
|
||||
// toStructPointer returns a structPointer equivalent to the given reflect value.
|
||||
func toStructPointer(v reflect.Value) structPointer {
|
||||
return structPointer(unsafe.Pointer(v.Pointer()))
|
||||
}
|
||||
|
||||
// IsNil reports whether p is nil.
|
||||
func structPointer_IsNil(p structPointer) bool {
|
||||
return p == nil
|
||||
}
|
||||
|
||||
// Interface returns the struct pointer, assumed to have element type t,
|
||||
// as an interface value.
|
||||
func structPointer_Interface(p structPointer, t reflect.Type) interface{} {
|
||||
return reflect.NewAt(t, unsafe.Pointer(p)).Interface()
|
||||
}
|
||||
|
||||
// A field identifies a field in a struct, accessible from a structPointer.
|
||||
// In this implementation, a field is identified by its byte offset from the start of the struct.
|
||||
type field uintptr
|
||||
|
||||
// toField returns a field equivalent to the given reflect field.
|
||||
func toField(f *reflect.StructField) field {
|
||||
return field(f.Offset)
|
||||
}
|
||||
|
||||
// invalidField is an invalid field identifier.
|
||||
const invalidField = ^field(0)
|
||||
|
||||
// IsValid reports whether the field identifier is valid.
|
||||
func (f field) IsValid() bool {
|
||||
return f != ^field(0)
|
||||
}
|
||||
|
||||
// Bytes returns the address of a []byte field in the struct.
|
||||
func structPointer_Bytes(p structPointer, f field) *[]byte {
|
||||
return (*[]byte)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
||||
}
|
||||
|
||||
// BytesSlice returns the address of a [][]byte field in the struct.
|
||||
func structPointer_BytesSlice(p structPointer, f field) *[][]byte {
|
||||
return (*[][]byte)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
||||
}
|
||||
|
||||
// Bool returns the address of a *bool field in the struct.
|
||||
func structPointer_Bool(p structPointer, f field) **bool {
|
||||
return (**bool)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
||||
}
|
||||
|
||||
// BoolVal returns the address of a bool field in the struct.
|
||||
func structPointer_BoolVal(p structPointer, f field) *bool {
|
||||
return (*bool)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
||||
}
|
||||
|
||||
// BoolSlice returns the address of a []bool field in the struct.
|
||||
func structPointer_BoolSlice(p structPointer, f field) *[]bool {
|
||||
return (*[]bool)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
||||
}
|
||||
|
||||
// String returns the address of a *string field in the struct.
|
||||
func structPointer_String(p structPointer, f field) **string {
|
||||
return (**string)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
||||
}
|
||||
|
||||
// StringVal returns the address of a string field in the struct.
|
||||
func structPointer_StringVal(p structPointer, f field) *string {
|
||||
return (*string)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
||||
}
|
||||
|
||||
// StringSlice returns the address of a []string field in the struct.
|
||||
func structPointer_StringSlice(p structPointer, f field) *[]string {
|
||||
return (*[]string)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
||||
}
|
||||
|
||||
// ExtMap returns the address of an extension map field in the struct.
|
||||
func structPointer_ExtMap(p structPointer, f field) *map[int32]Extension {
|
||||
return (*map[int32]Extension)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
||||
}
|
||||
|
||||
// NewAt returns the reflect.Value for a pointer to a field in the struct.
|
||||
func structPointer_NewAt(p structPointer, f field, typ reflect.Type) reflect.Value {
|
||||
return reflect.NewAt(typ, unsafe.Pointer(uintptr(p)+uintptr(f)))
|
||||
}
|
||||
|
||||
// SetStructPointer writes a *struct field in the struct.
|
||||
func structPointer_SetStructPointer(p structPointer, f field, q structPointer) {
|
||||
*(*structPointer)(unsafe.Pointer(uintptr(p) + uintptr(f))) = q
|
||||
}
|
||||
|
||||
// GetStructPointer reads a *struct field in the struct.
|
||||
func structPointer_GetStructPointer(p structPointer, f field) structPointer {
|
||||
return *(*structPointer)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
||||
}
|
||||
|
||||
// StructPointerSlice the address of a []*struct field in the struct.
|
||||
func structPointer_StructPointerSlice(p structPointer, f field) *structPointerSlice {
|
||||
return (*structPointerSlice)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
||||
}
|
||||
|
||||
// A structPointerSlice represents a slice of pointers to structs (themselves submessages or groups).
|
||||
type structPointerSlice []structPointer
|
||||
|
||||
func (v *structPointerSlice) Len() int { return len(*v) }
|
||||
func (v *structPointerSlice) Index(i int) structPointer { return (*v)[i] }
|
||||
func (v *structPointerSlice) Append(p structPointer) { *v = append(*v, p) }
|
||||
|
||||
// A word32 is the address of a "pointer to 32-bit value" field.
|
||||
type word32 **uint32
|
||||
|
||||
// IsNil reports whether *v is nil.
|
||||
func word32_IsNil(p word32) bool {
|
||||
return *p == nil
|
||||
}
|
||||
|
||||
// Set sets *v to point at a newly allocated word set to x.
|
||||
func word32_Set(p word32, o *Buffer, x uint32) {
|
||||
if len(o.uint32s) == 0 {
|
||||
o.uint32s = make([]uint32, uint32PoolSize)
|
||||
}
|
||||
o.uint32s[0] = x
|
||||
*p = &o.uint32s[0]
|
||||
o.uint32s = o.uint32s[1:]
|
||||
}
|
||||
|
||||
// Get gets the value pointed at by *v.
|
||||
func word32_Get(p word32) uint32 {
|
||||
return **p
|
||||
}
|
||||
|
||||
// Word32 returns the address of a *int32, *uint32, *float32, or *enum field in the struct.
|
||||
func structPointer_Word32(p structPointer, f field) word32 {
|
||||
return word32((**uint32)(unsafe.Pointer(uintptr(p) + uintptr(f))))
|
||||
}
|
||||
|
||||
// A word32Val is the address of a 32-bit value field.
|
||||
type word32Val *uint32
|
||||
|
||||
// Set sets *p to x.
|
||||
func word32Val_Set(p word32Val, x uint32) {
|
||||
*p = x
|
||||
}
|
||||
|
||||
// Get gets the value pointed at by p.
|
||||
func word32Val_Get(p word32Val) uint32 {
|
||||
return *p
|
||||
}
|
||||
|
||||
// Word32Val returns the address of a *int32, *uint32, *float32, or *enum field in the struct.
|
||||
func structPointer_Word32Val(p structPointer, f field) word32Val {
|
||||
return word32Val((*uint32)(unsafe.Pointer(uintptr(p) + uintptr(f))))
|
||||
}
|
||||
|
||||
// A word32Slice is a slice of 32-bit values.
|
||||
type word32Slice []uint32
|
||||
|
||||
func (v *word32Slice) Append(x uint32) { *v = append(*v, x) }
|
||||
func (v *word32Slice) Len() int { return len(*v) }
|
||||
func (v *word32Slice) Index(i int) uint32 { return (*v)[i] }
|
||||
|
||||
// Word32Slice returns the address of a []int32, []uint32, []float32, or []enum field in the struct.
|
||||
func structPointer_Word32Slice(p structPointer, f field) *word32Slice {
|
||||
return (*word32Slice)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
||||
}
|
||||
|
||||
// word64 is like word32 but for 64-bit values.
|
||||
type word64 **uint64
|
||||
|
||||
func word64_Set(p word64, o *Buffer, x uint64) {
|
||||
if len(o.uint64s) == 0 {
|
||||
o.uint64s = make([]uint64, uint64PoolSize)
|
||||
}
|
||||
o.uint64s[0] = x
|
||||
*p = &o.uint64s[0]
|
||||
o.uint64s = o.uint64s[1:]
|
||||
}
|
||||
|
||||
func word64_IsNil(p word64) bool {
|
||||
return *p == nil
|
||||
}
|
||||
|
||||
func word64_Get(p word64) uint64 {
|
||||
return **p
|
||||
}
|
||||
|
||||
func structPointer_Word64(p structPointer, f field) word64 {
|
||||
return word64((**uint64)(unsafe.Pointer(uintptr(p) + uintptr(f))))
|
||||
}
|
||||
|
||||
// word64Val is like word32Val but for 64-bit values.
|
||||
type word64Val *uint64
|
||||
|
||||
func word64Val_Set(p word64Val, o *Buffer, x uint64) {
|
||||
*p = x
|
||||
}
|
||||
|
||||
func word64Val_Get(p word64Val) uint64 {
|
||||
return *p
|
||||
}
|
||||
|
||||
func structPointer_Word64Val(p structPointer, f field) word64Val {
|
||||
return word64Val((*uint64)(unsafe.Pointer(uintptr(p) + uintptr(f))))
|
||||
}
|
||||
|
||||
// word64Slice is like word32Slice but for 64-bit values.
|
||||
type word64Slice []uint64
|
||||
|
||||
func (v *word64Slice) Append(x uint64) { *v = append(*v, x) }
|
||||
func (v *word64Slice) Len() int { return len(*v) }
|
||||
func (v *word64Slice) Index(i int) uint64 { return (*v)[i] }
|
||||
|
||||
func structPointer_Word64Slice(p structPointer, f field) *word64Slice {
|
||||
return (*word64Slice)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
||||
}
|
742
Godeps/_workspace/src/github.com/golang/protobuf/proto/properties.go
generated
vendored
Normal file
742
Godeps/_workspace/src/github.com/golang/protobuf/proto/properties.go
generated
vendored
Normal file
|
@ -0,0 +1,742 @@
|
|||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package proto
|
||||
|
||||
/*
|
||||
* Routines for encoding data into the wire format for protocol buffers.
|
||||
*/
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const debug bool = false
|
||||
|
||||
// Constants that identify the encoding of a value on the wire.
|
||||
const (
|
||||
WireVarint = 0
|
||||
WireFixed64 = 1
|
||||
WireBytes = 2
|
||||
WireStartGroup = 3
|
||||
WireEndGroup = 4
|
||||
WireFixed32 = 5
|
||||
)
|
||||
|
||||
const startSize = 10 // initial slice/string sizes
|
||||
|
||||
// Encoders are defined in encode.go
|
||||
// An encoder outputs the full representation of a field, including its
|
||||
// tag and encoder type.
|
||||
type encoder func(p *Buffer, prop *Properties, base structPointer) error
|
||||
|
||||
// A valueEncoder encodes a single integer in a particular encoding.
|
||||
type valueEncoder func(o *Buffer, x uint64) error
|
||||
|
||||
// Sizers are defined in encode.go
|
||||
// A sizer returns the encoded size of a field, including its tag and encoder
|
||||
// type.
|
||||
type sizer func(prop *Properties, base structPointer) int
|
||||
|
||||
// A valueSizer returns the encoded size of a single integer in a particular
|
||||
// encoding.
|
||||
type valueSizer func(x uint64) int
|
||||
|
||||
// Decoders are defined in decode.go
|
||||
// A decoder creates a value from its wire representation.
|
||||
// Unrecognized subelements are saved in unrec.
|
||||
type decoder func(p *Buffer, prop *Properties, base structPointer) error
|
||||
|
||||
// A valueDecoder decodes a single integer in a particular encoding.
|
||||
type valueDecoder func(o *Buffer) (x uint64, err error)
|
||||
|
||||
// tagMap is an optimization over map[int]int for typical protocol buffer
|
||||
// use-cases. Encoded protocol buffers are often in tag order with small tag
|
||||
// numbers.
|
||||
type tagMap struct {
|
||||
fastTags []int
|
||||
slowTags map[int]int
|
||||
}
|
||||
|
||||
// tagMapFastLimit is the upper bound on the tag number that will be stored in
|
||||
// the tagMap slice rather than its map.
|
||||
const tagMapFastLimit = 1024
|
||||
|
||||
func (p *tagMap) get(t int) (int, bool) {
|
||||
if t > 0 && t < tagMapFastLimit {
|
||||
if t >= len(p.fastTags) {
|
||||
return 0, false
|
||||
}
|
||||
fi := p.fastTags[t]
|
||||
return fi, fi >= 0
|
||||
}
|
||||
fi, ok := p.slowTags[t]
|
||||
return fi, ok
|
||||
}
|
||||
|
||||
func (p *tagMap) put(t int, fi int) {
|
||||
if t > 0 && t < tagMapFastLimit {
|
||||
for len(p.fastTags) < t+1 {
|
||||
p.fastTags = append(p.fastTags, -1)
|
||||
}
|
||||
p.fastTags[t] = fi
|
||||
return
|
||||
}
|
||||
if p.slowTags == nil {
|
||||
p.slowTags = make(map[int]int)
|
||||
}
|
||||
p.slowTags[t] = fi
|
||||
}
|
||||
|
||||
// StructProperties represents properties for all the fields of a struct.
|
||||
// decoderTags and decoderOrigNames should only be used by the decoder.
|
||||
type StructProperties struct {
|
||||
Prop []*Properties // properties for each field
|
||||
reqCount int // required count
|
||||
decoderTags tagMap // map from proto tag to struct field number
|
||||
decoderOrigNames map[string]int // map from original name to struct field number
|
||||
order []int // list of struct field numbers in tag order
|
||||
unrecField field // field id of the XXX_unrecognized []byte field
|
||||
extendable bool // is this an extendable proto
|
||||
}
|
||||
|
||||
// Implement the sorting interface so we can sort the fields in tag order, as recommended by the spec.
|
||||
// See encode.go, (*Buffer).enc_struct.
|
||||
|
||||
func (sp *StructProperties) Len() int { return len(sp.order) }
|
||||
func (sp *StructProperties) Less(i, j int) bool {
|
||||
return sp.Prop[sp.order[i]].Tag < sp.Prop[sp.order[j]].Tag
|
||||
}
|
||||
func (sp *StructProperties) Swap(i, j int) { sp.order[i], sp.order[j] = sp.order[j], sp.order[i] }
|
||||
|
||||
// Properties represents the protocol-specific behavior of a single struct field.
|
||||
type Properties struct {
|
||||
Name string // name of the field, for error messages
|
||||
OrigName string // original name before protocol compiler (always set)
|
||||
Wire string
|
||||
WireType int
|
||||
Tag int
|
||||
Required bool
|
||||
Optional bool
|
||||
Repeated bool
|
||||
Packed bool // relevant for repeated primitives only
|
||||
Enum string // set for enum types only
|
||||
proto3 bool // whether this is known to be a proto3 field; set for []byte only
|
||||
|
||||
Default string // default value
|
||||
HasDefault bool // whether an explicit default was provided
|
||||
def_uint64 uint64
|
||||
|
||||
enc encoder
|
||||
valEnc valueEncoder // set for bool and numeric types only
|
||||
field field
|
||||
tagcode []byte // encoding of EncodeVarint((Tag<<3)|WireType)
|
||||
tagbuf [8]byte
|
||||
stype reflect.Type // set for struct types only
|
||||
sprop *StructProperties // set for struct types only
|
||||
isMarshaler bool
|
||||
isUnmarshaler bool
|
||||
|
||||
mtype reflect.Type // set for map types only
|
||||
mkeyprop *Properties // set for map types only
|
||||
mvalprop *Properties // set for map types only
|
||||
|
||||
size sizer
|
||||
valSize valueSizer // set for bool and numeric types only
|
||||
|
||||
dec decoder
|
||||
valDec valueDecoder // set for bool and numeric types only
|
||||
|
||||
// If this is a packable field, this will be the decoder for the packed version of the field.
|
||||
packedDec decoder
|
||||
}
|
||||
|
||||
// String formats the properties in the protobuf struct field tag style.
|
||||
func (p *Properties) String() string {
|
||||
s := p.Wire
|
||||
s = ","
|
||||
s += strconv.Itoa(p.Tag)
|
||||
if p.Required {
|
||||
s += ",req"
|
||||
}
|
||||
if p.Optional {
|
||||
s += ",opt"
|
||||
}
|
||||
if p.Repeated {
|
||||
s += ",rep"
|
||||
}
|
||||
if p.Packed {
|
||||
s += ",packed"
|
||||
}
|
||||
if p.OrigName != p.Name {
|
||||
s += ",name=" + p.OrigName
|
||||
}
|
||||
if p.proto3 {
|
||||
s += ",proto3"
|
||||
}
|
||||
if len(p.Enum) > 0 {
|
||||
s += ",enum=" + p.Enum
|
||||
}
|
||||
if p.HasDefault {
|
||||
s += ",def=" + p.Default
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Parse populates p by parsing a string in the protobuf struct field tag style.
|
||||
func (p *Properties) Parse(s string) {
|
||||
// "bytes,49,opt,name=foo,def=hello!"
|
||||
fields := strings.Split(s, ",") // breaks def=, but handled below.
|
||||
if len(fields) < 2 {
|
||||
fmt.Fprintf(os.Stderr, "proto: tag has too few fields: %q\n", s)
|
||||
return
|
||||
}
|
||||
|
||||
p.Wire = fields[0]
|
||||
switch p.Wire {
|
||||
case "varint":
|
||||
p.WireType = WireVarint
|
||||
p.valEnc = (*Buffer).EncodeVarint
|
||||
p.valDec = (*Buffer).DecodeVarint
|
||||
p.valSize = sizeVarint
|
||||
case "fixed32":
|
||||
p.WireType = WireFixed32
|
||||
p.valEnc = (*Buffer).EncodeFixed32
|
||||
p.valDec = (*Buffer).DecodeFixed32
|
||||
p.valSize = sizeFixed32
|
||||
case "fixed64":
|
||||
p.WireType = WireFixed64
|
||||
p.valEnc = (*Buffer).EncodeFixed64
|
||||
p.valDec = (*Buffer).DecodeFixed64
|
||||
p.valSize = sizeFixed64
|
||||
case "zigzag32":
|
||||
p.WireType = WireVarint
|
||||
p.valEnc = (*Buffer).EncodeZigzag32
|
||||
p.valDec = (*Buffer).DecodeZigzag32
|
||||
p.valSize = sizeZigzag32
|
||||
case "zigzag64":
|
||||
p.WireType = WireVarint
|
||||
p.valEnc = (*Buffer).EncodeZigzag64
|
||||
p.valDec = (*Buffer).DecodeZigzag64
|
||||
p.valSize = sizeZigzag64
|
||||
case "bytes", "group":
|
||||
p.WireType = WireBytes
|
||||
// no numeric converter for non-numeric types
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "proto: tag has unknown wire type: %q\n", s)
|
||||
return
|
||||
}
|
||||
|
||||
var err error
|
||||
p.Tag, err = strconv.Atoi(fields[1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for i := 2; i < len(fields); i++ {
|
||||
f := fields[i]
|
||||
switch {
|
||||
case f == "req":
|
||||
p.Required = true
|
||||
case f == "opt":
|
||||
p.Optional = true
|
||||
case f == "rep":
|
||||
p.Repeated = true
|
||||
case f == "packed":
|
||||
p.Packed = true
|
||||
case strings.HasPrefix(f, "name="):
|
||||
p.OrigName = f[5:]
|
||||
case strings.HasPrefix(f, "enum="):
|
||||
p.Enum = f[5:]
|
||||
case f == "proto3":
|
||||
p.proto3 = true
|
||||
case strings.HasPrefix(f, "def="):
|
||||
p.HasDefault = true
|
||||
p.Default = f[4:] // rest of string
|
||||
if i+1 < len(fields) {
|
||||
// Commas aren't escaped, and def is always last.
|
||||
p.Default += "," + strings.Join(fields[i+1:], ",")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func logNoSliceEnc(t1, t2 reflect.Type) {
|
||||
fmt.Fprintf(os.Stderr, "proto: no slice oenc for %T = []%T\n", t1, t2)
|
||||
}
|
||||
|
||||
var protoMessageType = reflect.TypeOf((*Message)(nil)).Elem()
|
||||
|
||||
// Initialize the fields for encoding and decoding.
|
||||
func (p *Properties) setEncAndDec(typ reflect.Type, f *reflect.StructField, lockGetProp bool) {
|
||||
p.enc = nil
|
||||
p.dec = nil
|
||||
p.size = nil
|
||||
|
||||
switch t1 := typ; t1.Kind() {
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "proto: no coders for %v\n", t1)
|
||||
|
||||
// proto3 scalar types
|
||||
|
||||
case reflect.Bool:
|
||||
p.enc = (*Buffer).enc_proto3_bool
|
||||
p.dec = (*Buffer).dec_proto3_bool
|
||||
p.size = size_proto3_bool
|
||||
case reflect.Int32:
|
||||
p.enc = (*Buffer).enc_proto3_int32
|
||||
p.dec = (*Buffer).dec_proto3_int32
|
||||
p.size = size_proto3_int32
|
||||
case reflect.Uint32:
|
||||
p.enc = (*Buffer).enc_proto3_uint32
|
||||
p.dec = (*Buffer).dec_proto3_int32 // can reuse
|
||||
p.size = size_proto3_uint32
|
||||
case reflect.Int64, reflect.Uint64:
|
||||
p.enc = (*Buffer).enc_proto3_int64
|
||||
p.dec = (*Buffer).dec_proto3_int64
|
||||
p.size = size_proto3_int64
|
||||
case reflect.Float32:
|
||||
p.enc = (*Buffer).enc_proto3_uint32 // can just treat them as bits
|
||||
p.dec = (*Buffer).dec_proto3_int32
|
||||
p.size = size_proto3_uint32
|
||||
case reflect.Float64:
|
||||
p.enc = (*Buffer).enc_proto3_int64 // can just treat them as bits
|
||||
p.dec = (*Buffer).dec_proto3_int64
|
||||
p.size = size_proto3_int64
|
||||
case reflect.String:
|
||||
p.enc = (*Buffer).enc_proto3_string
|
||||
p.dec = (*Buffer).dec_proto3_string
|
||||
p.size = size_proto3_string
|
||||
|
||||
case reflect.Ptr:
|
||||
switch t2 := t1.Elem(); t2.Kind() {
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "proto: no encoder function for %v -> %v\n", t1, t2)
|
||||
break
|
||||
case reflect.Bool:
|
||||
p.enc = (*Buffer).enc_bool
|
||||
p.dec = (*Buffer).dec_bool
|
||||
p.size = size_bool
|
||||
case reflect.Int32:
|
||||
p.enc = (*Buffer).enc_int32
|
||||
p.dec = (*Buffer).dec_int32
|
||||
p.size = size_int32
|
||||
case reflect.Uint32:
|
||||
p.enc = (*Buffer).enc_uint32
|
||||
p.dec = (*Buffer).dec_int32 // can reuse
|
||||
p.size = size_uint32
|
||||
case reflect.Int64, reflect.Uint64:
|
||||
p.enc = (*Buffer).enc_int64
|
||||
p.dec = (*Buffer).dec_int64
|
||||
p.size = size_int64
|
||||
case reflect.Float32:
|
||||
p.enc = (*Buffer).enc_uint32 // can just treat them as bits
|
||||
p.dec = (*Buffer).dec_int32
|
||||
p.size = size_uint32
|
||||
case reflect.Float64:
|
||||
p.enc = (*Buffer).enc_int64 // can just treat them as bits
|
||||
p.dec = (*Buffer).dec_int64
|
||||
p.size = size_int64
|
||||
case reflect.String:
|
||||
p.enc = (*Buffer).enc_string
|
||||
p.dec = (*Buffer).dec_string
|
||||
p.size = size_string
|
||||
case reflect.Struct:
|
||||
p.stype = t1.Elem()
|
||||
p.isMarshaler = isMarshaler(t1)
|
||||
p.isUnmarshaler = isUnmarshaler(t1)
|
||||
if p.Wire == "bytes" {
|
||||
p.enc = (*Buffer).enc_struct_message
|
||||
p.dec = (*Buffer).dec_struct_message
|
||||
p.size = size_struct_message
|
||||
} else {
|
||||
p.enc = (*Buffer).enc_struct_group
|
||||
p.dec = (*Buffer).dec_struct_group
|
||||
p.size = size_struct_group
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.Slice:
|
||||
switch t2 := t1.Elem(); t2.Kind() {
|
||||
default:
|
||||
logNoSliceEnc(t1, t2)
|
||||
break
|
||||
case reflect.Bool:
|
||||
if p.Packed {
|
||||
p.enc = (*Buffer).enc_slice_packed_bool
|
||||
p.size = size_slice_packed_bool
|
||||
} else {
|
||||
p.enc = (*Buffer).enc_slice_bool
|
||||
p.size = size_slice_bool
|
||||
}
|
||||
p.dec = (*Buffer).dec_slice_bool
|
||||
p.packedDec = (*Buffer).dec_slice_packed_bool
|
||||
case reflect.Int32:
|
||||
if p.Packed {
|
||||
p.enc = (*Buffer).enc_slice_packed_int32
|
||||
p.size = size_slice_packed_int32
|
||||
} else {
|
||||
p.enc = (*Buffer).enc_slice_int32
|
||||
p.size = size_slice_int32
|
||||
}
|
||||
p.dec = (*Buffer).dec_slice_int32
|
||||
p.packedDec = (*Buffer).dec_slice_packed_int32
|
||||
case reflect.Uint32:
|
||||
if p.Packed {
|
||||
p.enc = (*Buffer).enc_slice_packed_uint32
|
||||
p.size = size_slice_packed_uint32
|
||||
} else {
|
||||
p.enc = (*Buffer).enc_slice_uint32
|
||||
p.size = size_slice_uint32
|
||||
}
|
||||
p.dec = (*Buffer).dec_slice_int32
|
||||
p.packedDec = (*Buffer).dec_slice_packed_int32
|
||||
case reflect.Int64, reflect.Uint64:
|
||||
if p.Packed {
|
||||
p.enc = (*Buffer).enc_slice_packed_int64
|
||||
p.size = size_slice_packed_int64
|
||||
} else {
|
||||
p.enc = (*Buffer).enc_slice_int64
|
||||
p.size = size_slice_int64
|
||||
}
|
||||
p.dec = (*Buffer).dec_slice_int64
|
||||
p.packedDec = (*Buffer).dec_slice_packed_int64
|
||||
case reflect.Uint8:
|
||||
p.enc = (*Buffer).enc_slice_byte
|
||||
p.dec = (*Buffer).dec_slice_byte
|
||||
p.size = size_slice_byte
|
||||
// This is a []byte, which is either a bytes field,
|
||||
// or the value of a map field. In the latter case,
|
||||
// we always encode an empty []byte, so we should not
|
||||
// use the proto3 enc/size funcs.
|
||||
// f == nil iff this is the key/value of a map field.
|
||||
if p.proto3 && f != nil {
|
||||
p.enc = (*Buffer).enc_proto3_slice_byte
|
||||
p.size = size_proto3_slice_byte
|
||||
}
|
||||
case reflect.Float32, reflect.Float64:
|
||||
switch t2.Bits() {
|
||||
case 32:
|
||||
// can just treat them as bits
|
||||
if p.Packed {
|
||||
p.enc = (*Buffer).enc_slice_packed_uint32
|
||||
p.size = size_slice_packed_uint32
|
||||
} else {
|
||||
p.enc = (*Buffer).enc_slice_uint32
|
||||
p.size = size_slice_uint32
|
||||
}
|
||||
p.dec = (*Buffer).dec_slice_int32
|
||||
p.packedDec = (*Buffer).dec_slice_packed_int32
|
||||
case 64:
|
||||
// can just treat them as bits
|
||||
if p.Packed {
|
||||
p.enc = (*Buffer).enc_slice_packed_int64
|
||||
p.size = size_slice_packed_int64
|
||||
} else {
|
||||
p.enc = (*Buffer).enc_slice_int64
|
||||
p.size = size_slice_int64
|
||||
}
|
||||
p.dec = (*Buffer).dec_slice_int64
|
||||
p.packedDec = (*Buffer).dec_slice_packed_int64
|
||||
default:
|
||||
logNoSliceEnc(t1, t2)
|
||||
break
|
||||
}
|
||||
case reflect.String:
|
||||
p.enc = (*Buffer).enc_slice_string
|
||||
p.dec = (*Buffer).dec_slice_string
|
||||
p.size = size_slice_string
|
||||
case reflect.Ptr:
|
||||
switch t3 := t2.Elem(); t3.Kind() {
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "proto: no ptr oenc for %T -> %T -> %T\n", t1, t2, t3)
|
||||
break
|
||||
case reflect.Struct:
|
||||
p.stype = t2.Elem()
|
||||
p.isMarshaler = isMarshaler(t2)
|
||||
p.isUnmarshaler = isUnmarshaler(t2)
|
||||
if p.Wire == "bytes" {
|
||||
p.enc = (*Buffer).enc_slice_struct_message
|
||||
p.dec = (*Buffer).dec_slice_struct_message
|
||||
p.size = size_slice_struct_message
|
||||
} else {
|
||||
p.enc = (*Buffer).enc_slice_struct_group
|
||||
p.dec = (*Buffer).dec_slice_struct_group
|
||||
p.size = size_slice_struct_group
|
||||
}
|
||||
}
|
||||
case reflect.Slice:
|
||||
switch t2.Elem().Kind() {
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "proto: no slice elem oenc for %T -> %T -> %T\n", t1, t2, t2.Elem())
|
||||
break
|
||||
case reflect.Uint8:
|
||||
p.enc = (*Buffer).enc_slice_slice_byte
|
||||
p.dec = (*Buffer).dec_slice_slice_byte
|
||||
p.size = size_slice_slice_byte
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.Map:
|
||||
p.enc = (*Buffer).enc_new_map
|
||||
p.dec = (*Buffer).dec_new_map
|
||||
p.size = size_new_map
|
||||
|
||||
p.mtype = t1
|
||||
p.mkeyprop = &Properties{}
|
||||
p.mkeyprop.init(reflect.PtrTo(p.mtype.Key()), "Key", f.Tag.Get("protobuf_key"), nil, lockGetProp)
|
||||
p.mvalprop = &Properties{}
|
||||
vtype := p.mtype.Elem()
|
||||
if vtype.Kind() != reflect.Ptr && vtype.Kind() != reflect.Slice {
|
||||
// The value type is not a message (*T) or bytes ([]byte),
|
||||
// so we need encoders for the pointer to this type.
|
||||
vtype = reflect.PtrTo(vtype)
|
||||
}
|
||||
p.mvalprop.init(vtype, "Value", f.Tag.Get("protobuf_val"), nil, lockGetProp)
|
||||
}
|
||||
|
||||
// precalculate tag code
|
||||
wire := p.WireType
|
||||
if p.Packed {
|
||||
wire = WireBytes
|
||||
}
|
||||
x := uint32(p.Tag)<<3 | uint32(wire)
|
||||
i := 0
|
||||
for i = 0; x > 127; i++ {
|
||||
p.tagbuf[i] = 0x80 | uint8(x&0x7F)
|
||||
x >>= 7
|
||||
}
|
||||
p.tagbuf[i] = uint8(x)
|
||||
p.tagcode = p.tagbuf[0 : i+1]
|
||||
|
||||
if p.stype != nil {
|
||||
if lockGetProp {
|
||||
p.sprop = GetProperties(p.stype)
|
||||
} else {
|
||||
p.sprop = getPropertiesLocked(p.stype)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem()
|
||||
unmarshalerType = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
|
||||
)
|
||||
|
||||
// isMarshaler reports whether type t implements Marshaler.
|
||||
func isMarshaler(t reflect.Type) bool {
|
||||
// We're checking for (likely) pointer-receiver methods
|
||||
// so if t is not a pointer, something is very wrong.
|
||||
// The calls above only invoke isMarshaler on pointer types.
|
||||
if t.Kind() != reflect.Ptr {
|
||||
panic("proto: misuse of isMarshaler")
|
||||
}
|
||||
return t.Implements(marshalerType)
|
||||
}
|
||||
|
||||
// isUnmarshaler reports whether type t implements Unmarshaler.
|
||||
func isUnmarshaler(t reflect.Type) bool {
|
||||
// We're checking for (likely) pointer-receiver methods
|
||||
// so if t is not a pointer, something is very wrong.
|
||||
// The calls above only invoke isUnmarshaler on pointer types.
|
||||
if t.Kind() != reflect.Ptr {
|
||||
panic("proto: misuse of isUnmarshaler")
|
||||
}
|
||||
return t.Implements(unmarshalerType)
|
||||
}
|
||||
|
||||
// Init populates the properties from a protocol buffer struct tag.
|
||||
func (p *Properties) Init(typ reflect.Type, name, tag string, f *reflect.StructField) {
|
||||
p.init(typ, name, tag, f, true)
|
||||
}
|
||||
|
||||
func (p *Properties) init(typ reflect.Type, name, tag string, f *reflect.StructField, lockGetProp bool) {
|
||||
// "bytes,49,opt,def=hello!"
|
||||
p.Name = name
|
||||
p.OrigName = name
|
||||
if f != nil {
|
||||
p.field = toField(f)
|
||||
}
|
||||
if tag == "" {
|
||||
return
|
||||
}
|
||||
p.Parse(tag)
|
||||
p.setEncAndDec(typ, f, lockGetProp)
|
||||
}
|
||||
|
||||
var (
|
||||
propertiesMu sync.RWMutex
|
||||
propertiesMap = make(map[reflect.Type]*StructProperties)
|
||||
)
|
||||
|
||||
// GetProperties returns the list of properties for the type represented by t.
|
||||
// t must represent a generated struct type of a protocol message.
|
||||
func GetProperties(t reflect.Type) *StructProperties {
|
||||
if t.Kind() != reflect.Struct {
|
||||
panic("proto: type must have kind struct")
|
||||
}
|
||||
|
||||
// Most calls to GetProperties in a long-running program will be
|
||||
// retrieving details for types we have seen before.
|
||||
propertiesMu.RLock()
|
||||
sprop, ok := propertiesMap[t]
|
||||
propertiesMu.RUnlock()
|
||||
if ok {
|
||||
if collectStats {
|
||||
stats.Chit++
|
||||
}
|
||||
return sprop
|
||||
}
|
||||
|
||||
propertiesMu.Lock()
|
||||
sprop = getPropertiesLocked(t)
|
||||
propertiesMu.Unlock()
|
||||
return sprop
|
||||
}
|
||||
|
||||
// getPropertiesLocked requires that propertiesMu is held.
|
||||
func getPropertiesLocked(t reflect.Type) *StructProperties {
|
||||
if prop, ok := propertiesMap[t]; ok {
|
||||
if collectStats {
|
||||
stats.Chit++
|
||||
}
|
||||
return prop
|
||||
}
|
||||
if collectStats {
|
||||
stats.Cmiss++
|
||||
}
|
||||
|
||||
prop := new(StructProperties)
|
||||
// in case of recursive protos, fill this in now.
|
||||
propertiesMap[t] = prop
|
||||
|
||||
// build properties
|
||||
prop.extendable = reflect.PtrTo(t).Implements(extendableProtoType)
|
||||
prop.unrecField = invalidField
|
||||
prop.Prop = make([]*Properties, t.NumField())
|
||||
prop.order = make([]int, t.NumField())
|
||||
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
f := t.Field(i)
|
||||
p := new(Properties)
|
||||
name := f.Name
|
||||
p.init(f.Type, name, f.Tag.Get("protobuf"), &f, false)
|
||||
|
||||
if f.Name == "XXX_extensions" { // special case
|
||||
p.enc = (*Buffer).enc_map
|
||||
p.dec = nil // not needed
|
||||
p.size = size_map
|
||||
}
|
||||
if f.Name == "XXX_unrecognized" { // special case
|
||||
prop.unrecField = toField(&f)
|
||||
}
|
||||
prop.Prop[i] = p
|
||||
prop.order[i] = i
|
||||
if debug {
|
||||
print(i, " ", f.Name, " ", t.String(), " ")
|
||||
if p.Tag > 0 {
|
||||
print(p.String())
|
||||
}
|
||||
print("\n")
|
||||
}
|
||||
if p.enc == nil && !strings.HasPrefix(f.Name, "XXX_") {
|
||||
fmt.Fprintln(os.Stderr, "proto: no encoder for", f.Name, f.Type.String(), "[GetProperties]")
|
||||
}
|
||||
}
|
||||
|
||||
// Re-order prop.order.
|
||||
sort.Sort(prop)
|
||||
|
||||
// build required counts
|
||||
// build tags
|
||||
reqCount := 0
|
||||
prop.decoderOrigNames = make(map[string]int)
|
||||
for i, p := range prop.Prop {
|
||||
if strings.HasPrefix(p.Name, "XXX_") {
|
||||
// Internal fields should not appear in tags/origNames maps.
|
||||
// They are handled specially when encoding and decoding.
|
||||
continue
|
||||
}
|
||||
if p.Required {
|
||||
reqCount++
|
||||
}
|
||||
prop.decoderTags.put(p.Tag, i)
|
||||
prop.decoderOrigNames[p.OrigName] = i
|
||||
}
|
||||
prop.reqCount = reqCount
|
||||
|
||||
return prop
|
||||
}
|
||||
|
||||
// Return the Properties object for the x[0]'th field of the structure.
|
||||
func propByIndex(t reflect.Type, x []int) *Properties {
|
||||
if len(x) != 1 {
|
||||
fmt.Fprintf(os.Stderr, "proto: field index dimension %d (not 1) for type %s\n", len(x), t)
|
||||
return nil
|
||||
}
|
||||
prop := GetProperties(t)
|
||||
return prop.Prop[x[0]]
|
||||
}
|
||||
|
||||
// Get the address and type of a pointer to a struct from an interface.
|
||||
func getbase(pb Message) (t reflect.Type, b structPointer, err error) {
|
||||
if pb == nil {
|
||||
err = ErrNil
|
||||
return
|
||||
}
|
||||
// get the reflect type of the pointer to the struct.
|
||||
t = reflect.TypeOf(pb)
|
||||
// get the address of the struct.
|
||||
value := reflect.ValueOf(pb)
|
||||
b = toStructPointer(value)
|
||||
return
|
||||
}
|
||||
|
||||
// A global registry of enum types.
|
||||
// The generated code will register the generated maps by calling RegisterEnum.
|
||||
|
||||
var enumValueMaps = make(map[string]map[string]int32)
|
||||
|
||||
// RegisterEnum is called from the generated code to install the enum descriptor
|
||||
// maps into the global table to aid parsing text format protocol buffers.
|
||||
func RegisterEnum(typeName string, unusedNameMap map[int32]string, valueMap map[string]int32) {
|
||||
if _, ok := enumValueMaps[typeName]; ok {
|
||||
panic("proto: duplicate enum registered: " + typeName)
|
||||
}
|
||||
enumValueMaps[typeName] = valueMap
|
||||
}
|
122
Godeps/_workspace/src/github.com/golang/protobuf/proto/proto3_proto/proto3.pb.go
generated
vendored
Normal file
122
Godeps/_workspace/src/github.com/golang/protobuf/proto/proto3_proto/proto3.pb.go
generated
vendored
Normal file
|
@ -0,0 +1,122 @@
|
|||
// Code generated by protoc-gen-go.
|
||||
// source: proto3_proto/proto3.proto
|
||||
// DO NOT EDIT!
|
||||
|
||||
/*
|
||||
Package proto3_proto is a generated protocol buffer package.
|
||||
|
||||
It is generated from these files:
|
||||
proto3_proto/proto3.proto
|
||||
|
||||
It has these top-level messages:
|
||||
Message
|
||||
Nested
|
||||
MessageWithMap
|
||||
*/
|
||||
package proto3_proto
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import testdata "github.com/golang/protobuf/proto/testdata"
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
|
||||
type Message_Humour int32
|
||||
|
||||
const (
|
||||
Message_UNKNOWN Message_Humour = 0
|
||||
Message_PUNS Message_Humour = 1
|
||||
Message_SLAPSTICK Message_Humour = 2
|
||||
Message_BILL_BAILEY Message_Humour = 3
|
||||
)
|
||||
|
||||
var Message_Humour_name = map[int32]string{
|
||||
0: "UNKNOWN",
|
||||
1: "PUNS",
|
||||
2: "SLAPSTICK",
|
||||
3: "BILL_BAILEY",
|
||||
}
|
||||
var Message_Humour_value = map[string]int32{
|
||||
"UNKNOWN": 0,
|
||||
"PUNS": 1,
|
||||
"SLAPSTICK": 2,
|
||||
"BILL_BAILEY": 3,
|
||||
}
|
||||
|
||||
func (x Message_Humour) String() string {
|
||||
return proto.EnumName(Message_Humour_name, int32(x))
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
|
||||
Hilarity Message_Humour `protobuf:"varint,2,opt,name=hilarity,enum=proto3_proto.Message_Humour" json:"hilarity,omitempty"`
|
||||
HeightInCm uint32 `protobuf:"varint,3,opt,name=height_in_cm" json:"height_in_cm,omitempty"`
|
||||
Data []byte `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"`
|
||||
ResultCount int64 `protobuf:"varint,7,opt,name=result_count" json:"result_count,omitempty"`
|
||||
TrueScotsman bool `protobuf:"varint,8,opt,name=true_scotsman" json:"true_scotsman,omitempty"`
|
||||
Score float32 `protobuf:"fixed32,9,opt,name=score" json:"score,omitempty"`
|
||||
Key []uint64 `protobuf:"varint,5,rep,name=key" json:"key,omitempty"`
|
||||
Nested *Nested `protobuf:"bytes,6,opt,name=nested" json:"nested,omitempty"`
|
||||
Terrain map[string]*Nested `protobuf:"bytes,10,rep,name=terrain" json:"terrain,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
|
||||
Proto2Field *testdata.SubDefaults `protobuf:"bytes,11,opt,name=proto2_field" json:"proto2_field,omitempty"`
|
||||
Proto2Value map[string]*testdata.SubDefaults `protobuf:"bytes,13,rep,name=proto2_value" json:"proto2_value,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
|
||||
}
|
||||
|
||||
func (m *Message) Reset() { *m = Message{} }
|
||||
func (m *Message) String() string { return proto.CompactTextString(m) }
|
||||
func (*Message) ProtoMessage() {}
|
||||
|
||||
func (m *Message) GetNested() *Nested {
|
||||
if m != nil {
|
||||
return m.Nested
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Message) GetTerrain() map[string]*Nested {
|
||||
if m != nil {
|
||||
return m.Terrain
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Message) GetProto2Field() *testdata.SubDefaults {
|
||||
if m != nil {
|
||||
return m.Proto2Field
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Message) GetProto2Value() map[string]*testdata.SubDefaults {
|
||||
if m != nil {
|
||||
return m.Proto2Value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Nested struct {
|
||||
Bunny string `protobuf:"bytes,1,opt,name=bunny" json:"bunny,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Nested) Reset() { *m = Nested{} }
|
||||
func (m *Nested) String() string { return proto.CompactTextString(m) }
|
||||
func (*Nested) ProtoMessage() {}
|
||||
|
||||
type MessageWithMap struct {
|
||||
ByteMapping map[bool][]byte `protobuf:"bytes,1,rep,name=byte_mapping" json:"byte_mapping,omitempty" protobuf_key:"varint,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||
}
|
||||
|
||||
func (m *MessageWithMap) Reset() { *m = MessageWithMap{} }
|
||||
func (m *MessageWithMap) String() string { return proto.CompactTextString(m) }
|
||||
func (*MessageWithMap) ProtoMessage() {}
|
||||
|
||||
func (m *MessageWithMap) GetByteMapping() map[bool][]byte {
|
||||
if m != nil {
|
||||
return m.ByteMapping
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterEnum("proto3_proto.Message_Humour", Message_Humour_name, Message_Humour_value)
|
||||
}
|
68
Godeps/_workspace/src/github.com/golang/protobuf/proto/proto3_proto/proto3.proto
generated
vendored
Normal file
68
Godeps/_workspace/src/github.com/golang/protobuf/proto/proto3_proto/proto3.proto
generated
vendored
Normal file
|
@ -0,0 +1,68 @@
|
|||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
import "testdata/test.proto";
|
||||
|
||||
package proto3_proto;
|
||||
|
||||
message Message {
|
||||
enum Humour {
|
||||
UNKNOWN = 0;
|
||||
PUNS = 1;
|
||||
SLAPSTICK = 2;
|
||||
BILL_BAILEY = 3;
|
||||
}
|
||||
|
||||
string name = 1;
|
||||
Humour hilarity = 2;
|
||||
uint32 height_in_cm = 3;
|
||||
bytes data = 4;
|
||||
int64 result_count = 7;
|
||||
bool true_scotsman = 8;
|
||||
float score = 9;
|
||||
|
||||
repeated uint64 key = 5;
|
||||
Nested nested = 6;
|
||||
|
||||
map<string, Nested> terrain = 10;
|
||||
testdata.SubDefaults proto2_field = 11;
|
||||
map<string, testdata.SubDefaults> proto2_value = 13;
|
||||
}
|
||||
|
||||
message Nested {
|
||||
string bunny = 1;
|
||||
}
|
||||
|
||||
message MessageWithMap {
|
||||
map<bool, bytes> byte_mapping = 1;
|
||||
}
|
125
Godeps/_workspace/src/github.com/golang/protobuf/proto/proto3_test.go
generated
vendored
Normal file
125
Godeps/_workspace/src/github.com/golang/protobuf/proto/proto3_test.go
generated
vendored
Normal file
|
@ -0,0 +1,125 @@
|
|||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package proto_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
pb "github.com/golang/protobuf/proto/proto3_proto"
|
||||
tpb "github.com/golang/protobuf/proto/testdata"
|
||||
)
|
||||
|
||||
func TestProto3ZeroValues(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
m proto.Message
|
||||
}{
|
||||
{"zero message", &pb.Message{}},
|
||||
{"empty bytes field", &pb.Message{Data: []byte{}}},
|
||||
}
|
||||
for _, test := range tests {
|
||||
b, err := proto.Marshal(test.m)
|
||||
if err != nil {
|
||||
t.Errorf("%s: proto.Marshal: %v", test.desc, err)
|
||||
continue
|
||||
}
|
||||
if len(b) > 0 {
|
||||
t.Errorf("%s: Encoding is non-empty: %q", test.desc, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundTripProto3(t *testing.T) {
|
||||
m := &pb.Message{
|
||||
Name: "David", // (2 | 1<<3): 0x0a 0x05 "David"
|
||||
Hilarity: pb.Message_PUNS, // (0 | 2<<3): 0x10 0x01
|
||||
HeightInCm: 178, // (0 | 3<<3): 0x18 0xb2 0x01
|
||||
Data: []byte("roboto"), // (2 | 4<<3): 0x20 0x06 "roboto"
|
||||
ResultCount: 47, // (0 | 7<<3): 0x38 0x2f
|
||||
TrueScotsman: true, // (0 | 8<<3): 0x40 0x01
|
||||
Score: 8.1, // (5 | 9<<3): 0x4d <8.1>
|
||||
|
||||
Key: []uint64{1, 0xdeadbeef},
|
||||
Nested: &pb.Nested{
|
||||
Bunny: "Monty",
|
||||
},
|
||||
}
|
||||
t.Logf(" m: %v", m)
|
||||
|
||||
b, err := proto.Marshal(m)
|
||||
if err != nil {
|
||||
t.Fatalf("proto.Marshal: %v", err)
|
||||
}
|
||||
t.Logf(" b: %q", b)
|
||||
|
||||
m2 := new(pb.Message)
|
||||
if err := proto.Unmarshal(b, m2); err != nil {
|
||||
t.Fatalf("proto.Unmarshal: %v", err)
|
||||
}
|
||||
t.Logf("m2: %v", m2)
|
||||
|
||||
if !proto.Equal(m, m2) {
|
||||
t.Errorf("proto.Equal returned false:\n m: %v\nm2: %v", m, m2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProto3SetDefaults(t *testing.T) {
|
||||
in := &pb.Message{
|
||||
Terrain: map[string]*pb.Nested{
|
||||
"meadow": new(pb.Nested),
|
||||
},
|
||||
Proto2Field: new(tpb.SubDefaults),
|
||||
Proto2Value: map[string]*tpb.SubDefaults{
|
||||
"badlands": new(tpb.SubDefaults),
|
||||
},
|
||||
}
|
||||
|
||||
got := proto.Clone(in).(*pb.Message)
|
||||
proto.SetDefaults(got)
|
||||
|
||||
// There are no defaults in proto3. Everything should be the zero value, but
|
||||
// we need to remember to set defaults for nested proto2 messages.
|
||||
want := &pb.Message{
|
||||
Terrain: map[string]*pb.Nested{
|
||||
"meadow": new(pb.Nested),
|
||||
},
|
||||
Proto2Field: &tpb.SubDefaults{N: proto.Int64(7)},
|
||||
Proto2Value: map[string]*tpb.SubDefaults{
|
||||
"badlands": &tpb.SubDefaults{N: proto.Int64(7)},
|
||||
},
|
||||
}
|
||||
|
||||
if !proto.Equal(got, want) {
|
||||
t.Errorf("with in = %v\nproto.SetDefaults(in) =>\ngot %v\nwant %v", in, got, want)
|
||||
}
|
||||
}
|
63
Godeps/_workspace/src/github.com/golang/protobuf/proto/size2_test.go
generated
vendored
Normal file
63
Godeps/_workspace/src/github.com/golang/protobuf/proto/size2_test.go
generated
vendored
Normal file
|
@ -0,0 +1,63 @@
|
|||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package proto
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// This is a separate file and package from size_test.go because that one uses
|
||||
// generated messages and thus may not be in package proto without having a circular
|
||||
// dependency, whereas this file tests unexported details of size.go.
|
||||
|
||||
func TestVarintSize(t *testing.T) {
|
||||
// Check the edge cases carefully.
|
||||
testCases := []struct {
|
||||
n uint64
|
||||
size int
|
||||
}{
|
||||
{0, 1},
|
||||
{1, 1},
|
||||
{127, 1},
|
||||
{128, 2},
|
||||
{16383, 2},
|
||||
{16384, 3},
|
||||
{1<<63 - 1, 9},
|
||||
{1 << 63, 10},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
size := sizeVarint(tc.n)
|
||||
if size != tc.size {
|
||||
t.Errorf("sizeVarint(%d) = %d, want %d", tc.n, size, tc.size)
|
||||
}
|
||||
}
|
||||
}
|
142
Godeps/_workspace/src/github.com/golang/protobuf/proto/size_test.go
generated
vendored
Normal file
142
Godeps/_workspace/src/github.com/golang/protobuf/proto/size_test.go
generated
vendored
Normal file
|
@ -0,0 +1,142 @@
|
|||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package proto_test
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
. "github.com/golang/protobuf/proto"
|
||||
proto3pb "github.com/golang/protobuf/proto/proto3_proto"
|
||||
pb "github.com/golang/protobuf/proto/testdata"
|
||||
)
|
||||
|
||||
var messageWithExtension1 = &pb.MyMessage{Count: Int32(7)}
|
||||
|
||||
// messageWithExtension2 is in equal_test.go.
|
||||
var messageWithExtension3 = &pb.MyMessage{Count: Int32(8)}
|
||||
|
||||
func init() {
|
||||
if err := SetExtension(messageWithExtension1, pb.E_Ext_More, &pb.Ext{Data: String("Abbott")}); err != nil {
|
||||
log.Panicf("SetExtension: %v", err)
|
||||
}
|
||||
if err := SetExtension(messageWithExtension3, pb.E_Ext_More, &pb.Ext{Data: String("Costello")}); err != nil {
|
||||
log.Panicf("SetExtension: %v", err)
|
||||
}
|
||||
|
||||
// Force messageWithExtension3 to have the extension encoded.
|
||||
Marshal(messageWithExtension3)
|
||||
|
||||
}
|
||||
|
||||
var SizeTests = []struct {
|
||||
desc string
|
||||
pb Message
|
||||
}{
|
||||
{"empty", &pb.OtherMessage{}},
|
||||
// Basic types.
|
||||
{"bool", &pb.Defaults{F_Bool: Bool(true)}},
|
||||
{"int32", &pb.Defaults{F_Int32: Int32(12)}},
|
||||
{"negative int32", &pb.Defaults{F_Int32: Int32(-1)}},
|
||||
{"small int64", &pb.Defaults{F_Int64: Int64(1)}},
|
||||
{"big int64", &pb.Defaults{F_Int64: Int64(1 << 20)}},
|
||||
{"negative int64", &pb.Defaults{F_Int64: Int64(-1)}},
|
||||
{"fixed32", &pb.Defaults{F_Fixed32: Uint32(71)}},
|
||||
{"fixed64", &pb.Defaults{F_Fixed64: Uint64(72)}},
|
||||
{"uint32", &pb.Defaults{F_Uint32: Uint32(123)}},
|
||||
{"uint64", &pb.Defaults{F_Uint64: Uint64(124)}},
|
||||
{"float", &pb.Defaults{F_Float: Float32(12.6)}},
|
||||
{"double", &pb.Defaults{F_Double: Float64(13.9)}},
|
||||
{"string", &pb.Defaults{F_String: String("niles")}},
|
||||
{"bytes", &pb.Defaults{F_Bytes: []byte("wowsa")}},
|
||||
{"bytes, empty", &pb.Defaults{F_Bytes: []byte{}}},
|
||||
{"sint32", &pb.Defaults{F_Sint32: Int32(65)}},
|
||||
{"sint64", &pb.Defaults{F_Sint64: Int64(67)}},
|
||||
{"enum", &pb.Defaults{F_Enum: pb.Defaults_BLUE.Enum()}},
|
||||
// Repeated.
|
||||
{"empty repeated bool", &pb.MoreRepeated{Bools: []bool{}}},
|
||||
{"repeated bool", &pb.MoreRepeated{Bools: []bool{false, true, true, false}}},
|
||||
{"packed repeated bool", &pb.MoreRepeated{BoolsPacked: []bool{false, true, true, false, true, true, true}}},
|
||||
{"repeated int32", &pb.MoreRepeated{Ints: []int32{1, 12203, 1729, -1}}},
|
||||
{"repeated int32 packed", &pb.MoreRepeated{IntsPacked: []int32{1, 12203, 1729}}},
|
||||
{"repeated int64 packed", &pb.MoreRepeated{Int64SPacked: []int64{
|
||||
// Need enough large numbers to verify that the header is counting the number of bytes
|
||||
// for the field, not the number of elements.
|
||||
1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62,
|
||||
1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62,
|
||||
}}},
|
||||
{"repeated string", &pb.MoreRepeated{Strings: []string{"r", "ken", "gri"}}},
|
||||
{"repeated fixed", &pb.MoreRepeated{Fixeds: []uint32{1, 2, 3, 4}}},
|
||||
// Nested.
|
||||
{"nested", &pb.OldMessage{Nested: &pb.OldMessage_Nested{Name: String("whatever")}}},
|
||||
{"group", &pb.GroupOld{G: &pb.GroupOld_G{X: Int32(12345)}}},
|
||||
// Other things.
|
||||
{"unrecognized", &pb.MoreRepeated{XXX_unrecognized: []byte{13<<3 | 0, 4}}},
|
||||
{"extension (unencoded)", messageWithExtension1},
|
||||
{"extension (encoded)", messageWithExtension3},
|
||||
// proto3 message
|
||||
{"proto3 empty", &proto3pb.Message{}},
|
||||
{"proto3 bool", &proto3pb.Message{TrueScotsman: true}},
|
||||
{"proto3 int64", &proto3pb.Message{ResultCount: 1}},
|
||||
{"proto3 uint32", &proto3pb.Message{HeightInCm: 123}},
|
||||
{"proto3 float", &proto3pb.Message{Score: 12.6}},
|
||||
{"proto3 string", &proto3pb.Message{Name: "Snezana"}},
|
||||
{"proto3 bytes", &proto3pb.Message{Data: []byte("wowsa")}},
|
||||
{"proto3 bytes, empty", &proto3pb.Message{Data: []byte{}}},
|
||||
{"proto3 enum", &proto3pb.Message{Hilarity: proto3pb.Message_PUNS}},
|
||||
{"proto3 map field with empty bytes", &proto3pb.MessageWithMap{ByteMapping: map[bool][]byte{false: []byte{}}}},
|
||||
|
||||
{"map field", &pb.MessageWithMap{NameMapping: map[int32]string{1: "Rob", 7: "Andrew"}}},
|
||||
{"map field with message", &pb.MessageWithMap{MsgMapping: map[int64]*pb.FloatingPoint{0x7001: &pb.FloatingPoint{F: Float64(2.0)}}}},
|
||||
{"map field with bytes", &pb.MessageWithMap{ByteMapping: map[bool][]byte{true: []byte("this time for sure")}}},
|
||||
{"map field with empty bytes", &pb.MessageWithMap{ByteMapping: map[bool][]byte{true: []byte{}}}},
|
||||
|
||||
{"map field with big entry", &pb.MessageWithMap{NameMapping: map[int32]string{8: strings.Repeat("x", 125)}}},
|
||||
{"map field with big key and val", &pb.MessageWithMap{StrToStr: map[string]string{strings.Repeat("x", 70): strings.Repeat("y", 70)}}},
|
||||
{"map field with big numeric key", &pb.MessageWithMap{NameMapping: map[int32]string{0xf00d: "om nom nom"}}},
|
||||
}
|
||||
|
||||
func TestSize(t *testing.T) {
|
||||
for _, tc := range SizeTests {
|
||||
size := Size(tc.pb)
|
||||
b, err := Marshal(tc.pb)
|
||||
if err != nil {
|
||||
t.Errorf("%v: Marshal failed: %v", tc.desc, err)
|
||||
continue
|
||||
}
|
||||
if size != len(b) {
|
||||
t.Errorf("%v: Size(%v) = %d, want %d", tc.desc, tc.pb, size, len(b))
|
||||
t.Logf("%v: bytes: %#v", tc.desc, b)
|
||||
}
|
||||
}
|
||||
}
|
50
Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/Makefile
generated
vendored
Normal file
50
Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/Makefile
generated
vendored
Normal file
|
@ -0,0 +1,50 @@
|
|||
# Go support for Protocol Buffers - Google's data interchange format
|
||||
#
|
||||
# Copyright 2010 The Go Authors. All rights reserved.
|
||||
# https://github.com/golang/protobuf
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
include ../../Make.protobuf
|
||||
|
||||
all: regenerate
|
||||
|
||||
regenerate:
|
||||
rm -f test.pb.go
|
||||
make test.pb.go
|
||||
|
||||
# The following rules are just aids to development. Not needed for typical testing.
|
||||
|
||||
diff: regenerate
|
||||
git diff test.pb.go
|
||||
|
||||
restore:
|
||||
cp test.pb.go.golden test.pb.go
|
||||
|
||||
preserve:
|
||||
cp test.pb.go test.pb.go.golden
|
86
Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/golden_test.go
generated
vendored
Normal file
86
Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/golden_test.go
generated
vendored
Normal file
|
@ -0,0 +1,86 @@
|
|||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Verify that the compiler output for test.proto is unchanged.
|
||||
|
||||
package testdata
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// sum returns in string form (for easy comparison) the SHA-1 hash of the named file.
|
||||
func sum(t *testing.T, name string) string {
|
||||
data, err := ioutil.ReadFile(name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("sum(%q): length is %d", name, len(data))
|
||||
hash := sha1.New()
|
||||
_, err = hash.Write(data)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return fmt.Sprintf("% x", hash.Sum(nil))
|
||||
}
|
||||
|
||||
func run(t *testing.T, name string, args ...string) {
|
||||
cmd := exec.Command(name, args...)
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGolden(t *testing.T) {
|
||||
// Compute the original checksum.
|
||||
goldenSum := sum(t, "test.pb.go")
|
||||
// Run the proto compiler.
|
||||
run(t, "protoc", "--go_out="+os.TempDir(), "test.proto")
|
||||
newFile := filepath.Join(os.TempDir(), "test.pb.go")
|
||||
defer os.Remove(newFile)
|
||||
// Compute the new checksum.
|
||||
newSum := sum(t, newFile)
|
||||
// Verify
|
||||
if newSum != goldenSum {
|
||||
run(t, "diff", "-u", "test.pb.go", newFile)
|
||||
t.Fatal("Code generated by protoc-gen-go has changed; update test.pb.go")
|
||||
}
|
||||
}
|
2746
Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/test.pb.go
generated
vendored
Normal file
2746
Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/test.pb.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
480
Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/test.proto
generated
vendored
Normal file
480
Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/test.proto
generated
vendored
Normal file
|
@ -0,0 +1,480 @@
|
|||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// A feature-rich test file for the protocol compiler and libraries.
|
||||
|
||||
syntax = "proto2";
|
||||
|
||||
package testdata;
|
||||
|
||||
enum FOO { FOO1 = 1; };
|
||||
|
||||
message GoEnum {
|
||||
required FOO foo = 1;
|
||||
}
|
||||
|
||||
message GoTestField {
|
||||
required string Label = 1;
|
||||
required string Type = 2;
|
||||
}
|
||||
|
||||
message GoTest {
|
||||
// An enum, for completeness.
|
||||
enum KIND {
|
||||
VOID = 0;
|
||||
|
||||
// Basic types
|
||||
BOOL = 1;
|
||||
BYTES = 2;
|
||||
FINGERPRINT = 3;
|
||||
FLOAT = 4;
|
||||
INT = 5;
|
||||
STRING = 6;
|
||||
TIME = 7;
|
||||
|
||||
// Groupings
|
||||
TUPLE = 8;
|
||||
ARRAY = 9;
|
||||
MAP = 10;
|
||||
|
||||
// Table types
|
||||
TABLE = 11;
|
||||
|
||||
// Functions
|
||||
FUNCTION = 12; // last tag
|
||||
};
|
||||
|
||||
// Some typical parameters
|
||||
required KIND Kind = 1;
|
||||
optional string Table = 2;
|
||||
optional int32 Param = 3;
|
||||
|
||||
// Required, repeated and optional foreign fields.
|
||||
required GoTestField RequiredField = 4;
|
||||
repeated GoTestField RepeatedField = 5;
|
||||
optional GoTestField OptionalField = 6;
|
||||
|
||||
// Required fields of all basic types
|
||||
required bool F_Bool_required = 10;
|
||||
required int32 F_Int32_required = 11;
|
||||
required int64 F_Int64_required = 12;
|
||||
required fixed32 F_Fixed32_required = 13;
|
||||
required fixed64 F_Fixed64_required = 14;
|
||||
required uint32 F_Uint32_required = 15;
|
||||
required uint64 F_Uint64_required = 16;
|
||||
required float F_Float_required = 17;
|
||||
required double F_Double_required = 18;
|
||||
required string F_String_required = 19;
|
||||
required bytes F_Bytes_required = 101;
|
||||
required sint32 F_Sint32_required = 102;
|
||||
required sint64 F_Sint64_required = 103;
|
||||
|
||||
// Repeated fields of all basic types
|
||||
repeated bool F_Bool_repeated = 20;
|
||||
repeated int32 F_Int32_repeated = 21;
|
||||
repeated int64 F_Int64_repeated = 22;
|
||||
repeated fixed32 F_Fixed32_repeated = 23;
|
||||
repeated fixed64 F_Fixed64_repeated = 24;
|
||||
repeated uint32 F_Uint32_repeated = 25;
|
||||
repeated uint64 F_Uint64_repeated = 26;
|
||||
repeated float F_Float_repeated = 27;
|
||||
repeated double F_Double_repeated = 28;
|
||||
repeated string F_String_repeated = 29;
|
||||
repeated bytes F_Bytes_repeated = 201;
|
||||
repeated sint32 F_Sint32_repeated = 202;
|
||||
repeated sint64 F_Sint64_repeated = 203;
|
||||
|
||||
// Optional fields of all basic types
|
||||
optional bool F_Bool_optional = 30;
|
||||
optional int32 F_Int32_optional = 31;
|
||||
optional int64 F_Int64_optional = 32;
|
||||
optional fixed32 F_Fixed32_optional = 33;
|
||||
optional fixed64 F_Fixed64_optional = 34;
|
||||
optional uint32 F_Uint32_optional = 35;
|
||||
optional uint64 F_Uint64_optional = 36;
|
||||
optional float F_Float_optional = 37;
|
||||
optional double F_Double_optional = 38;
|
||||
optional string F_String_optional = 39;
|
||||
optional bytes F_Bytes_optional = 301;
|
||||
optional sint32 F_Sint32_optional = 302;
|
||||
optional sint64 F_Sint64_optional = 303;
|
||||
|
||||
// Default-valued fields of all basic types
|
||||
optional bool F_Bool_defaulted = 40 [default=true];
|
||||
optional int32 F_Int32_defaulted = 41 [default=32];
|
||||
optional int64 F_Int64_defaulted = 42 [default=64];
|
||||
optional fixed32 F_Fixed32_defaulted = 43 [default=320];
|
||||
optional fixed64 F_Fixed64_defaulted = 44 [default=640];
|
||||
optional uint32 F_Uint32_defaulted = 45 [default=3200];
|
||||
optional uint64 F_Uint64_defaulted = 46 [default=6400];
|
||||
optional float F_Float_defaulted = 47 [default=314159.];
|
||||
optional double F_Double_defaulted = 48 [default=271828.];
|
||||
optional string F_String_defaulted = 49 [default="hello, \"world!\"\n"];
|
||||
optional bytes F_Bytes_defaulted = 401 [default="Bignose"];
|
||||
optional sint32 F_Sint32_defaulted = 402 [default = -32];
|
||||
optional sint64 F_Sint64_defaulted = 403 [default = -64];
|
||||
|
||||
// Packed repeated fields (no string or bytes).
|
||||
repeated bool F_Bool_repeated_packed = 50 [packed=true];
|
||||
repeated int32 F_Int32_repeated_packed = 51 [packed=true];
|
||||
repeated int64 F_Int64_repeated_packed = 52 [packed=true];
|
||||
repeated fixed32 F_Fixed32_repeated_packed = 53 [packed=true];
|
||||
repeated fixed64 F_Fixed64_repeated_packed = 54 [packed=true];
|
||||
repeated uint32 F_Uint32_repeated_packed = 55 [packed=true];
|
||||
repeated uint64 F_Uint64_repeated_packed = 56 [packed=true];
|
||||
repeated float F_Float_repeated_packed = 57 [packed=true];
|
||||
repeated double F_Double_repeated_packed = 58 [packed=true];
|
||||
repeated sint32 F_Sint32_repeated_packed = 502 [packed=true];
|
||||
repeated sint64 F_Sint64_repeated_packed = 503 [packed=true];
|
||||
|
||||
// Required, repeated, and optional groups.
|
||||
required group RequiredGroup = 70 {
|
||||
required string RequiredField = 71;
|
||||
};
|
||||
|
||||
repeated group RepeatedGroup = 80 {
|
||||
required string RequiredField = 81;
|
||||
};
|
||||
|
||||
optional group OptionalGroup = 90 {
|
||||
required string RequiredField = 91;
|
||||
};
|
||||
}
|
||||
|
||||
// For testing skipping of unrecognized fields.
|
||||
// Numbers are all big, larger than tag numbers in GoTestField,
|
||||
// the message used in the corresponding test.
|
||||
message GoSkipTest {
|
||||
required int32 skip_int32 = 11;
|
||||
required fixed32 skip_fixed32 = 12;
|
||||
required fixed64 skip_fixed64 = 13;
|
||||
required string skip_string = 14;
|
||||
required group SkipGroup = 15 {
|
||||
required int32 group_int32 = 16;
|
||||
required string group_string = 17;
|
||||
}
|
||||
}
|
||||
|
||||
// For testing packed/non-packed decoder switching.
|
||||
// A serialized instance of one should be deserializable as the other.
|
||||
message NonPackedTest {
|
||||
repeated int32 a = 1;
|
||||
}
|
||||
|
||||
message PackedTest {
|
||||
repeated int32 b = 1 [packed=true];
|
||||
}
|
||||
|
||||
message MaxTag {
|
||||
// Maximum possible tag number.
|
||||
optional string last_field = 536870911;
|
||||
}
|
||||
|
||||
message OldMessage {
|
||||
message Nested {
|
||||
optional string name = 1;
|
||||
}
|
||||
optional Nested nested = 1;
|
||||
|
||||
optional int32 num = 2;
|
||||
}
|
||||
|
||||
// NewMessage is wire compatible with OldMessage;
|
||||
// imagine it as a future version.
|
||||
message NewMessage {
|
||||
message Nested {
|
||||
optional string name = 1;
|
||||
optional string food_group = 2;
|
||||
}
|
||||
optional Nested nested = 1;
|
||||
|
||||
// This is an int32 in OldMessage.
|
||||
optional int64 num = 2;
|
||||
}
|
||||
|
||||
// Smaller tests for ASCII formatting.
|
||||
|
||||
message InnerMessage {
|
||||
required string host = 1;
|
||||
optional int32 port = 2 [default=4000];
|
||||
optional bool connected = 3;
|
||||
}
|
||||
|
||||
message OtherMessage {
|
||||
optional int64 key = 1;
|
||||
optional bytes value = 2;
|
||||
optional float weight = 3;
|
||||
optional InnerMessage inner = 4;
|
||||
}
|
||||
|
||||
message MyMessage {
|
||||
required int32 count = 1;
|
||||
optional string name = 2;
|
||||
optional string quote = 3;
|
||||
repeated string pet = 4;
|
||||
optional InnerMessage inner = 5;
|
||||
repeated OtherMessage others = 6;
|
||||
repeated InnerMessage rep_inner = 12;
|
||||
|
||||
enum Color {
|
||||
RED = 0;
|
||||
GREEN = 1;
|
||||
BLUE = 2;
|
||||
};
|
||||
optional Color bikeshed = 7;
|
||||
|
||||
optional group SomeGroup = 8 {
|
||||
optional int32 group_field = 9;
|
||||
}
|
||||
|
||||
// This field becomes [][]byte in the generated code.
|
||||
repeated bytes rep_bytes = 10;
|
||||
|
||||
optional double bigfloat = 11;
|
||||
|
||||
extensions 100 to max;
|
||||
}
|
||||
|
||||
message Ext {
|
||||
extend MyMessage {
|
||||
optional Ext more = 103;
|
||||
optional string text = 104;
|
||||
optional int32 number = 105;
|
||||
}
|
||||
|
||||
optional string data = 1;
|
||||
}
|
||||
|
||||
extend MyMessage {
|
||||
repeated string greeting = 106;
|
||||
}
|
||||
|
||||
message DefaultsMessage {
|
||||
enum DefaultsEnum {
|
||||
ZERO = 0;
|
||||
ONE = 1;
|
||||
TWO = 2;
|
||||
};
|
||||
extensions 100 to max;
|
||||
}
|
||||
|
||||
extend DefaultsMessage {
|
||||
optional double no_default_double = 101;
|
||||
optional float no_default_float = 102;
|
||||
optional int32 no_default_int32 = 103;
|
||||
optional int64 no_default_int64 = 104;
|
||||
optional uint32 no_default_uint32 = 105;
|
||||
optional uint64 no_default_uint64 = 106;
|
||||
optional sint32 no_default_sint32 = 107;
|
||||
optional sint64 no_default_sint64 = 108;
|
||||
optional fixed32 no_default_fixed32 = 109;
|
||||
optional fixed64 no_default_fixed64 = 110;
|
||||
optional sfixed32 no_default_sfixed32 = 111;
|
||||
optional sfixed64 no_default_sfixed64 = 112;
|
||||
optional bool no_default_bool = 113;
|
||||
optional string no_default_string = 114;
|
||||
optional bytes no_default_bytes = 115;
|
||||
optional DefaultsMessage.DefaultsEnum no_default_enum = 116;
|
||||
|
||||
optional double default_double = 201 [default = 3.1415];
|
||||
optional float default_float = 202 [default = 3.14];
|
||||
optional int32 default_int32 = 203 [default = 42];
|
||||
optional int64 default_int64 = 204 [default = 43];
|
||||
optional uint32 default_uint32 = 205 [default = 44];
|
||||
optional uint64 default_uint64 = 206 [default = 45];
|
||||
optional sint32 default_sint32 = 207 [default = 46];
|
||||
optional sint64 default_sint64 = 208 [default = 47];
|
||||
optional fixed32 default_fixed32 = 209 [default = 48];
|
||||
optional fixed64 default_fixed64 = 210 [default = 49];
|
||||
optional sfixed32 default_sfixed32 = 211 [default = 50];
|
||||
optional sfixed64 default_sfixed64 = 212 [default = 51];
|
||||
optional bool default_bool = 213 [default = true];
|
||||
optional string default_string = 214 [default = "Hello, string"];
|
||||
optional bytes default_bytes = 215 [default = "Hello, bytes"];
|
||||
optional DefaultsMessage.DefaultsEnum default_enum = 216 [default = ONE];
|
||||
}
|
||||
|
||||
message MyMessageSet {
|
||||
option message_set_wire_format = true;
|
||||
extensions 100 to max;
|
||||
}
|
||||
|
||||
message Empty {
|
||||
}
|
||||
|
||||
extend MyMessageSet {
|
||||
optional Empty x201 = 201;
|
||||
optional Empty x202 = 202;
|
||||
optional Empty x203 = 203;
|
||||
optional Empty x204 = 204;
|
||||
optional Empty x205 = 205;
|
||||
optional Empty x206 = 206;
|
||||
optional Empty x207 = 207;
|
||||
optional Empty x208 = 208;
|
||||
optional Empty x209 = 209;
|
||||
optional Empty x210 = 210;
|
||||
optional Empty x211 = 211;
|
||||
optional Empty x212 = 212;
|
||||
optional Empty x213 = 213;
|
||||
optional Empty x214 = 214;
|
||||
optional Empty x215 = 215;
|
||||
optional Empty x216 = 216;
|
||||
optional Empty x217 = 217;
|
||||
optional Empty x218 = 218;
|
||||
optional Empty x219 = 219;
|
||||
optional Empty x220 = 220;
|
||||
optional Empty x221 = 221;
|
||||
optional Empty x222 = 222;
|
||||
optional Empty x223 = 223;
|
||||
optional Empty x224 = 224;
|
||||
optional Empty x225 = 225;
|
||||
optional Empty x226 = 226;
|
||||
optional Empty x227 = 227;
|
||||
optional Empty x228 = 228;
|
||||
optional Empty x229 = 229;
|
||||
optional Empty x230 = 230;
|
||||
optional Empty x231 = 231;
|
||||
optional Empty x232 = 232;
|
||||
optional Empty x233 = 233;
|
||||
optional Empty x234 = 234;
|
||||
optional Empty x235 = 235;
|
||||
optional Empty x236 = 236;
|
||||
optional Empty x237 = 237;
|
||||
optional Empty x238 = 238;
|
||||
optional Empty x239 = 239;
|
||||
optional Empty x240 = 240;
|
||||
optional Empty x241 = 241;
|
||||
optional Empty x242 = 242;
|
||||
optional Empty x243 = 243;
|
||||
optional Empty x244 = 244;
|
||||
optional Empty x245 = 245;
|
||||
optional Empty x246 = 246;
|
||||
optional Empty x247 = 247;
|
||||
optional Empty x248 = 248;
|
||||
optional Empty x249 = 249;
|
||||
optional Empty x250 = 250;
|
||||
}
|
||||
|
||||
message MessageList {
|
||||
repeated group Message = 1 {
|
||||
required string name = 2;
|
||||
required int32 count = 3;
|
||||
}
|
||||
}
|
||||
|
||||
message Strings {
|
||||
optional string string_field = 1;
|
||||
optional bytes bytes_field = 2;
|
||||
}
|
||||
|
||||
message Defaults {
|
||||
enum Color {
|
||||
RED = 0;
|
||||
GREEN = 1;
|
||||
BLUE = 2;
|
||||
}
|
||||
|
||||
// Default-valued fields of all basic types.
|
||||
// Same as GoTest, but copied here to make testing easier.
|
||||
optional bool F_Bool = 1 [default=true];
|
||||
optional int32 F_Int32 = 2 [default=32];
|
||||
optional int64 F_Int64 = 3 [default=64];
|
||||
optional fixed32 F_Fixed32 = 4 [default=320];
|
||||
optional fixed64 F_Fixed64 = 5 [default=640];
|
||||
optional uint32 F_Uint32 = 6 [default=3200];
|
||||
optional uint64 F_Uint64 = 7 [default=6400];
|
||||
optional float F_Float = 8 [default=314159.];
|
||||
optional double F_Double = 9 [default=271828.];
|
||||
optional string F_String = 10 [default="hello, \"world!\"\n"];
|
||||
optional bytes F_Bytes = 11 [default="Bignose"];
|
||||
optional sint32 F_Sint32 = 12 [default=-32];
|
||||
optional sint64 F_Sint64 = 13 [default=-64];
|
||||
optional Color F_Enum = 14 [default=GREEN];
|
||||
|
||||
// More fields with crazy defaults.
|
||||
optional float F_Pinf = 15 [default=inf];
|
||||
optional float F_Ninf = 16 [default=-inf];
|
||||
optional float F_Nan = 17 [default=nan];
|
||||
|
||||
// Sub-message.
|
||||
optional SubDefaults sub = 18;
|
||||
|
||||
// Redundant but explicit defaults.
|
||||
optional string str_zero = 19 [default=""];
|
||||
}
|
||||
|
||||
message SubDefaults {
|
||||
optional int64 n = 1 [default=7];
|
||||
}
|
||||
|
||||
message RepeatedEnum {
|
||||
enum Color {
|
||||
RED = 1;
|
||||
}
|
||||
repeated Color color = 1;
|
||||
}
|
||||
|
||||
message MoreRepeated {
|
||||
repeated bool bools = 1;
|
||||
repeated bool bools_packed = 2 [packed=true];
|
||||
repeated int32 ints = 3;
|
||||
repeated int32 ints_packed = 4 [packed=true];
|
||||
repeated int64 int64s_packed = 7 [packed=true];
|
||||
repeated string strings = 5;
|
||||
repeated fixed32 fixeds = 6;
|
||||
}
|
||||
|
||||
// GroupOld and GroupNew have the same wire format.
|
||||
// GroupNew has a new field inside a group.
|
||||
|
||||
message GroupOld {
|
||||
optional group G = 101 {
|
||||
optional int32 x = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message GroupNew {
|
||||
optional group G = 101 {
|
||||
optional int32 x = 2;
|
||||
optional int32 y = 3;
|
||||
}
|
||||
}
|
||||
|
||||
message FloatingPoint {
|
||||
required double f = 1;
|
||||
}
|
||||
|
||||
message MessageWithMap {
|
||||
map<int32, string> name_mapping = 1;
|
||||
map<sint64, FloatingPoint> msg_mapping = 2;
|
||||
map<bool, bytes> byte_mapping = 3;
|
||||
map<string, string> str_to_str = 4;
|
||||
}
|
769
Godeps/_workspace/src/github.com/golang/protobuf/proto/text.go
generated
vendored
Normal file
769
Godeps/_workspace/src/github.com/golang/protobuf/proto/text.go
generated
vendored
Normal file
|
@ -0,0 +1,769 @@
|
|||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package proto
|
||||
|
||||
// Functions for writing the text protocol buffer format.
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"math"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
newline = []byte("\n")
|
||||
spaces = []byte(" ")
|
||||
gtNewline = []byte(">\n")
|
||||
endBraceNewline = []byte("}\n")
|
||||
backslashN = []byte{'\\', 'n'}
|
||||
backslashR = []byte{'\\', 'r'}
|
||||
backslashT = []byte{'\\', 't'}
|
||||
backslashDQ = []byte{'\\', '"'}
|
||||
backslashBS = []byte{'\\', '\\'}
|
||||
posInf = []byte("inf")
|
||||
negInf = []byte("-inf")
|
||||
nan = []byte("nan")
|
||||
)
|
||||
|
||||
type writer interface {
|
||||
io.Writer
|
||||
WriteByte(byte) error
|
||||
}
|
||||
|
||||
// textWriter is an io.Writer that tracks its indentation level.
|
||||
type textWriter struct {
|
||||
ind int
|
||||
complete bool // if the current position is a complete line
|
||||
compact bool // whether to write out as a one-liner
|
||||
w writer
|
||||
}
|
||||
|
||||
func (w *textWriter) WriteString(s string) (n int, err error) {
|
||||
if !strings.Contains(s, "\n") {
|
||||
if !w.compact && w.complete {
|
||||
w.writeIndent()
|
||||
}
|
||||
w.complete = false
|
||||
return io.WriteString(w.w, s)
|
||||
}
|
||||
// WriteString is typically called without newlines, so this
|
||||
// codepath and its copy are rare. We copy to avoid
|
||||
// duplicating all of Write's logic here.
|
||||
return w.Write([]byte(s))
|
||||
}
|
||||
|
||||
func (w *textWriter) Write(p []byte) (n int, err error) {
|
||||
newlines := bytes.Count(p, newline)
|
||||
if newlines == 0 {
|
||||
if !w.compact && w.complete {
|
||||
w.writeIndent()
|
||||
}
|
||||
n, err = w.w.Write(p)
|
||||
w.complete = false
|
||||
return n, err
|
||||
}
|
||||
|
||||
frags := bytes.SplitN(p, newline, newlines+1)
|
||||
if w.compact {
|
||||
for i, frag := range frags {
|
||||
if i > 0 {
|
||||
if err := w.w.WriteByte(' '); err != nil {
|
||||
return n, err
|
||||
}
|
||||
n++
|
||||
}
|
||||
nn, err := w.w.Write(frag)
|
||||
n += nn
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
for i, frag := range frags {
|
||||
if w.complete {
|
||||
w.writeIndent()
|
||||
}
|
||||
nn, err := w.w.Write(frag)
|
||||
n += nn
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
if i+1 < len(frags) {
|
||||
if err := w.w.WriteByte('\n'); err != nil {
|
||||
return n, err
|
||||
}
|
||||
n++
|
||||
}
|
||||
}
|
||||
w.complete = len(frags[len(frags)-1]) == 0
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (w *textWriter) WriteByte(c byte) error {
|
||||
if w.compact && c == '\n' {
|
||||
c = ' '
|
||||
}
|
||||
if !w.compact && w.complete {
|
||||
w.writeIndent()
|
||||
}
|
||||
err := w.w.WriteByte(c)
|
||||
w.complete = c == '\n'
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *textWriter) indent() { w.ind++ }
|
||||
|
||||
func (w *textWriter) unindent() {
|
||||
if w.ind == 0 {
|
||||
log.Printf("proto: textWriter unindented too far")
|
||||
return
|
||||
}
|
||||
w.ind--
|
||||
}
|
||||
|
||||
func writeName(w *textWriter, props *Properties) error {
|
||||
if _, err := w.WriteString(props.OrigName); err != nil {
|
||||
return err
|
||||
}
|
||||
if props.Wire != "group" {
|
||||
return w.WriteByte(':')
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
messageSetType = reflect.TypeOf((*MessageSet)(nil)).Elem()
|
||||
)
|
||||
|
||||
// raw is the interface satisfied by RawMessage.
|
||||
type raw interface {
|
||||
Bytes() []byte
|
||||
}
|
||||
|
||||
func writeStruct(w *textWriter, sv reflect.Value) error {
|
||||
if sv.Type() == messageSetType {
|
||||
return writeMessageSet(w, sv.Addr().Interface().(*MessageSet))
|
||||
}
|
||||
|
||||
st := sv.Type()
|
||||
sprops := GetProperties(st)
|
||||
for i := 0; i < sv.NumField(); i++ {
|
||||
fv := sv.Field(i)
|
||||
props := sprops.Prop[i]
|
||||
name := st.Field(i).Name
|
||||
|
||||
if strings.HasPrefix(name, "XXX_") {
|
||||
// There are two XXX_ fields:
|
||||
// XXX_unrecognized []byte
|
||||
// XXX_extensions map[int32]proto.Extension
|
||||
// The first is handled here;
|
||||
// the second is handled at the bottom of this function.
|
||||
if name == "XXX_unrecognized" && !fv.IsNil() {
|
||||
if err := writeUnknownStruct(w, fv.Interface().([]byte)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if fv.Kind() == reflect.Ptr && fv.IsNil() {
|
||||
// Field not filled in. This could be an optional field or
|
||||
// a required field that wasn't filled in. Either way, there
|
||||
// isn't anything we can show for it.
|
||||
continue
|
||||
}
|
||||
if fv.Kind() == reflect.Slice && fv.IsNil() {
|
||||
// Repeated field that is empty, or a bytes field that is unused.
|
||||
continue
|
||||
}
|
||||
|
||||
if props.Repeated && fv.Kind() == reflect.Slice {
|
||||
// Repeated field.
|
||||
for j := 0; j < fv.Len(); j++ {
|
||||
if err := writeName(w, props); err != nil {
|
||||
return err
|
||||
}
|
||||
if !w.compact {
|
||||
if err := w.WriteByte(' '); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
v := fv.Index(j)
|
||||
if v.Kind() == reflect.Ptr && v.IsNil() {
|
||||
// A nil message in a repeated field is not valid,
|
||||
// but we can handle that more gracefully than panicking.
|
||||
if _, err := w.Write([]byte("<nil>\n")); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err := writeAny(w, v, props); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := w.WriteByte('\n'); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if fv.Kind() == reflect.Map {
|
||||
// Map fields are rendered as a repeated struct with key/value fields.
|
||||
keys := fv.MapKeys() // TODO: should we sort these for deterministic output?
|
||||
sort.Sort(mapKeys(keys))
|
||||
for _, key := range keys {
|
||||
val := fv.MapIndex(key)
|
||||
if err := writeName(w, props); err != nil {
|
||||
return err
|
||||
}
|
||||
if !w.compact {
|
||||
if err := w.WriteByte(' '); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// open struct
|
||||
if err := w.WriteByte('<'); err != nil {
|
||||
return err
|
||||
}
|
||||
if !w.compact {
|
||||
if err := w.WriteByte('\n'); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
w.indent()
|
||||
// key
|
||||
if _, err := w.WriteString("key:"); err != nil {
|
||||
return err
|
||||
}
|
||||
if !w.compact {
|
||||
if err := w.WriteByte(' '); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := writeAny(w, key, props.mkeyprop); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := w.WriteByte('\n'); err != nil {
|
||||
return err
|
||||
}
|
||||
// nil values aren't legal, but we can avoid panicking because of them.
|
||||
if val.Kind() != reflect.Ptr || !val.IsNil() {
|
||||
// value
|
||||
if _, err := w.WriteString("value:"); err != nil {
|
||||
return err
|
||||
}
|
||||
if !w.compact {
|
||||
if err := w.WriteByte(' '); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := writeAny(w, val, props.mvalprop); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := w.WriteByte('\n'); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// close struct
|
||||
w.unindent()
|
||||
if err := w.WriteByte('>'); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := w.WriteByte('\n'); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if props.proto3 && fv.Kind() == reflect.Slice && fv.Len() == 0 {
|
||||
// empty bytes field
|
||||
continue
|
||||
}
|
||||
if fv.Kind() != reflect.Ptr && fv.Kind() != reflect.Slice {
|
||||
// proto3 non-repeated scalar field; skip if zero value
|
||||
if isProto3Zero(fv) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if err := writeName(w, props); err != nil {
|
||||
return err
|
||||
}
|
||||
if !w.compact {
|
||||
if err := w.WriteByte(' '); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if b, ok := fv.Interface().(raw); ok {
|
||||
if err := writeRaw(w, b.Bytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Enums have a String method, so writeAny will work fine.
|
||||
if err := writeAny(w, fv, props); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := w.WriteByte('\n'); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Extensions (the XXX_extensions field).
|
||||
pv := sv.Addr()
|
||||
if pv.Type().Implements(extendableProtoType) {
|
||||
if err := writeExtensions(w, pv); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeRaw writes an uninterpreted raw message.
|
||||
func writeRaw(w *textWriter, b []byte) error {
|
||||
if err := w.WriteByte('<'); err != nil {
|
||||
return err
|
||||
}
|
||||
if !w.compact {
|
||||
if err := w.WriteByte('\n'); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
w.indent()
|
||||
if err := writeUnknownStruct(w, b); err != nil {
|
||||
return err
|
||||
}
|
||||
w.unindent()
|
||||
if err := w.WriteByte('>'); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeAny writes an arbitrary field.
|
||||
func writeAny(w *textWriter, v reflect.Value, props *Properties) error {
|
||||
v = reflect.Indirect(v)
|
||||
|
||||
// Floats have special cases.
|
||||
if v.Kind() == reflect.Float32 || v.Kind() == reflect.Float64 {
|
||||
x := v.Float()
|
||||
var b []byte
|
||||
switch {
|
||||
case math.IsInf(x, 1):
|
||||
b = posInf
|
||||
case math.IsInf(x, -1):
|
||||
b = negInf
|
||||
case math.IsNaN(x):
|
||||
b = nan
|
||||
}
|
||||
if b != nil {
|
||||
_, err := w.Write(b)
|
||||
return err
|
||||
}
|
||||
// Other values are handled below.
|
||||
}
|
||||
|
||||
// We don't attempt to serialise every possible value type; only those
|
||||
// that can occur in protocol buffers.
|
||||
switch v.Kind() {
|
||||
case reflect.Slice:
|
||||
// Should only be a []byte; repeated fields are handled in writeStruct.
|
||||
if err := writeString(w, string(v.Interface().([]byte))); err != nil {
|
||||
return err
|
||||
}
|
||||
case reflect.String:
|
||||
if err := writeString(w, v.String()); err != nil {
|
||||
return err
|
||||
}
|
||||
case reflect.Struct:
|
||||
// Required/optional group/message.
|
||||
var bra, ket byte = '<', '>'
|
||||
if props != nil && props.Wire == "group" {
|
||||
bra, ket = '{', '}'
|
||||
}
|
||||
if err := w.WriteByte(bra); err != nil {
|
||||
return err
|
||||
}
|
||||
if !w.compact {
|
||||
if err := w.WriteByte('\n'); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
w.indent()
|
||||
if tm, ok := v.Interface().(encoding.TextMarshaler); ok {
|
||||
text, err := tm.MarshalText()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = w.Write(text); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err := writeStruct(w, v); err != nil {
|
||||
return err
|
||||
}
|
||||
w.unindent()
|
||||
if err := w.WriteByte(ket); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
_, err := fmt.Fprint(w, v.Interface())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// equivalent to C's isprint.
|
||||
func isprint(c byte) bool {
|
||||
return c >= 0x20 && c < 0x7f
|
||||
}
|
||||
|
||||
// writeString writes a string in the protocol buffer text format.
|
||||
// It is similar to strconv.Quote except we don't use Go escape sequences,
|
||||
// we treat the string as a byte sequence, and we use octal escapes.
|
||||
// These differences are to maintain interoperability with the other
|
||||
// languages' implementations of the text format.
|
||||
func writeString(w *textWriter, s string) error {
|
||||
// use WriteByte here to get any needed indent
|
||||
if err := w.WriteByte('"'); err != nil {
|
||||
return err
|
||||
}
|
||||
// Loop over the bytes, not the runes.
|
||||
for i := 0; i < len(s); i++ {
|
||||
var err error
|
||||
// Divergence from C++: we don't escape apostrophes.
|
||||
// There's no need to escape them, and the C++ parser
|
||||
// copes with a naked apostrophe.
|
||||
switch c := s[i]; c {
|
||||
case '\n':
|
||||
_, err = w.w.Write(backslashN)
|
||||
case '\r':
|
||||
_, err = w.w.Write(backslashR)
|
||||
case '\t':
|
||||
_, err = w.w.Write(backslashT)
|
||||
case '"':
|
||||
_, err = w.w.Write(backslashDQ)
|
||||
case '\\':
|
||||
_, err = w.w.Write(backslashBS)
|
||||
default:
|
||||
if isprint(c) {
|
||||
err = w.w.WriteByte(c)
|
||||
} else {
|
||||
_, err = fmt.Fprintf(w.w, "\\%03o", c)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return w.WriteByte('"')
|
||||
}
|
||||
|
||||
func writeMessageSet(w *textWriter, ms *MessageSet) error {
|
||||
for _, item := range ms.Item {
|
||||
id := *item.TypeId
|
||||
if msd, ok := messageSetMap[id]; ok {
|
||||
// Known message set type.
|
||||
if _, err := fmt.Fprintf(w, "[%s]: <\n", msd.name); err != nil {
|
||||
return err
|
||||
}
|
||||
w.indent()
|
||||
|
||||
pb := reflect.New(msd.t.Elem())
|
||||
if err := Unmarshal(item.Message, pb.Interface().(Message)); err != nil {
|
||||
if _, err := fmt.Fprintf(w, "/* bad message: %v */\n", err); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := writeStruct(w, pb.Elem()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Unknown type.
|
||||
if _, err := fmt.Fprintf(w, "[%d]: <\n", id); err != nil {
|
||||
return err
|
||||
}
|
||||
w.indent()
|
||||
if err := writeUnknownStruct(w, item.Message); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
w.unindent()
|
||||
if _, err := w.Write(gtNewline); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeUnknownStruct(w *textWriter, data []byte) (err error) {
|
||||
if !w.compact {
|
||||
if _, err := fmt.Fprintf(w, "/* %d unknown bytes */\n", len(data)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
b := NewBuffer(data)
|
||||
for b.index < len(b.buf) {
|
||||
x, err := b.DecodeVarint()
|
||||
if err != nil {
|
||||
_, err := fmt.Fprintf(w, "/* %v */\n", err)
|
||||
return err
|
||||
}
|
||||
wire, tag := x&7, x>>3
|
||||
if wire == WireEndGroup {
|
||||
w.unindent()
|
||||
if _, err := w.Write(endBraceNewline); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
if _, err := fmt.Fprint(w, tag); err != nil {
|
||||
return err
|
||||
}
|
||||
if wire != WireStartGroup {
|
||||
if err := w.WriteByte(':'); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !w.compact || wire == WireStartGroup {
|
||||
if err := w.WriteByte(' '); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
switch wire {
|
||||
case WireBytes:
|
||||
buf, e := b.DecodeRawBytes(false)
|
||||
if e == nil {
|
||||
_, err = fmt.Fprintf(w, "%q", buf)
|
||||
} else {
|
||||
_, err = fmt.Fprintf(w, "/* %v */", e)
|
||||
}
|
||||
case WireFixed32:
|
||||
x, err = b.DecodeFixed32()
|
||||
err = writeUnknownInt(w, x, err)
|
||||
case WireFixed64:
|
||||
x, err = b.DecodeFixed64()
|
||||
err = writeUnknownInt(w, x, err)
|
||||
case WireStartGroup:
|
||||
err = w.WriteByte('{')
|
||||
w.indent()
|
||||
case WireVarint:
|
||||
x, err = b.DecodeVarint()
|
||||
err = writeUnknownInt(w, x, err)
|
||||
default:
|
||||
_, err = fmt.Fprintf(w, "/* unknown wire type %d */", wire)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = w.WriteByte('\n'); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeUnknownInt(w *textWriter, x uint64, err error) error {
|
||||
if err == nil {
|
||||
_, err = fmt.Fprint(w, x)
|
||||
} else {
|
||||
_, err = fmt.Fprintf(w, "/* %v */", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
type int32Slice []int32
|
||||
|
||||
func (s int32Slice) Len() int { return len(s) }
|
||||
func (s int32Slice) Less(i, j int) bool { return s[i] < s[j] }
|
||||
func (s int32Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
|
||||
// writeExtensions writes all the extensions in pv.
|
||||
// pv is assumed to be a pointer to a protocol message struct that is extendable.
|
||||
func writeExtensions(w *textWriter, pv reflect.Value) error {
|
||||
emap := extensionMaps[pv.Type().Elem()]
|
||||
ep := pv.Interface().(extendableProto)
|
||||
|
||||
// Order the extensions by ID.
|
||||
// This isn't strictly necessary, but it will give us
|
||||
// canonical output, which will also make testing easier.
|
||||
m := ep.ExtensionMap()
|
||||
ids := make([]int32, 0, len(m))
|
||||
for id := range m {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
sort.Sort(int32Slice(ids))
|
||||
|
||||
for _, extNum := range ids {
|
||||
ext := m[extNum]
|
||||
var desc *ExtensionDesc
|
||||
if emap != nil {
|
||||
desc = emap[extNum]
|
||||
}
|
||||
if desc == nil {
|
||||
// Unknown extension.
|
||||
if err := writeUnknownStruct(w, ext.enc); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
pb, err := GetExtension(ep, desc)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed getting extension: %v", err)
|
||||
}
|
||||
|
||||
// Repeated extensions will appear as a slice.
|
||||
if !desc.repeated() {
|
||||
if err := writeExtension(w, desc.Name, pb); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
v := reflect.ValueOf(pb)
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
if err := writeExtension(w, desc.Name, v.Index(i).Interface()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeExtension(w *textWriter, name string, pb interface{}) error {
|
||||
if _, err := fmt.Fprintf(w, "[%s]:", name); err != nil {
|
||||
return err
|
||||
}
|
||||
if !w.compact {
|
||||
if err := w.WriteByte(' '); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := writeAny(w, reflect.ValueOf(pb), nil); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := w.WriteByte('\n'); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *textWriter) writeIndent() {
|
||||
if !w.complete {
|
||||
return
|
||||
}
|
||||
remain := w.ind * 2
|
||||
for remain > 0 {
|
||||
n := remain
|
||||
if n > len(spaces) {
|
||||
n = len(spaces)
|
||||
}
|
||||
w.w.Write(spaces[:n])
|
||||
remain -= n
|
||||
}
|
||||
w.complete = false
|
||||
}
|
||||
|
||||
func marshalText(w io.Writer, pb Message, compact bool) error {
|
||||
val := reflect.ValueOf(pb)
|
||||
if pb == nil || val.IsNil() {
|
||||
w.Write([]byte("<nil>"))
|
||||
return nil
|
||||
}
|
||||
var bw *bufio.Writer
|
||||
ww, ok := w.(writer)
|
||||
if !ok {
|
||||
bw = bufio.NewWriter(w)
|
||||
ww = bw
|
||||
}
|
||||
aw := &textWriter{
|
||||
w: ww,
|
||||
complete: true,
|
||||
compact: compact,
|
||||
}
|
||||
|
||||
if tm, ok := pb.(encoding.TextMarshaler); ok {
|
||||
text, err := tm.MarshalText()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = aw.Write(text); err != nil {
|
||||
return err
|
||||
}
|
||||
if bw != nil {
|
||||
return bw.Flush()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// Dereference the received pointer so we don't have outer < and >.
|
||||
v := reflect.Indirect(val)
|
||||
if err := writeStruct(aw, v); err != nil {
|
||||
return err
|
||||
}
|
||||
if bw != nil {
|
||||
return bw.Flush()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalText writes a given protocol buffer in text format.
|
||||
// The only errors returned are from w.
|
||||
func MarshalText(w io.Writer, pb Message) error {
|
||||
return marshalText(w, pb, false)
|
||||
}
|
||||
|
||||
// MarshalTextString is the same as MarshalText, but returns the string directly.
|
||||
func MarshalTextString(pb Message) string {
|
||||
var buf bytes.Buffer
|
||||
marshalText(&buf, pb, false)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// CompactText writes a given protocol buffer in compact text format (one line).
|
||||
func CompactText(w io.Writer, pb Message) error { return marshalText(w, pb, true) }
|
||||
|
||||
// CompactTextString is the same as CompactText, but returns the string directly.
|
||||
func CompactTextString(pb Message) string {
|
||||
var buf bytes.Buffer
|
||||
marshalText(&buf, pb, true)
|
||||
return buf.String()
|
||||
}
|
772
Godeps/_workspace/src/github.com/golang/protobuf/proto/text_parser.go
generated
vendored
Normal file
772
Godeps/_workspace/src/github.com/golang/protobuf/proto/text_parser.go
generated
vendored
Normal file
|
@ -0,0 +1,772 @@
|
|||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package proto
|
||||
|
||||
// Functions for parsing the Text protocol buffer format.
|
||||
// TODO: message sets.
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type ParseError struct {
|
||||
Message string
|
||||
Line int // 1-based line number
|
||||
Offset int // 0-based byte offset from start of input
|
||||
}
|
||||
|
||||
func (p *ParseError) Error() string {
|
||||
if p.Line == 1 {
|
||||
// show offset only for first line
|
||||
return fmt.Sprintf("line 1.%d: %v", p.Offset, p.Message)
|
||||
}
|
||||
return fmt.Sprintf("line %d: %v", p.Line, p.Message)
|
||||
}
|
||||
|
||||
type token struct {
|
||||
value string
|
||||
err *ParseError
|
||||
line int // line number
|
||||
offset int // byte number from start of input, not start of line
|
||||
unquoted string // the unquoted version of value, if it was a quoted string
|
||||
}
|
||||
|
||||
func (t *token) String() string {
|
||||
if t.err == nil {
|
||||
return fmt.Sprintf("%q (line=%d, offset=%d)", t.value, t.line, t.offset)
|
||||
}
|
||||
return fmt.Sprintf("parse error: %v", t.err)
|
||||
}
|
||||
|
||||
type textParser struct {
|
||||
s string // remaining input
|
||||
done bool // whether the parsing is finished (success or error)
|
||||
backed bool // whether back() was called
|
||||
offset, line int
|
||||
cur token
|
||||
}
|
||||
|
||||
func newTextParser(s string) *textParser {
|
||||
p := new(textParser)
|
||||
p.s = s
|
||||
p.line = 1
|
||||
p.cur.line = 1
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *textParser) errorf(format string, a ...interface{}) *ParseError {
|
||||
pe := &ParseError{fmt.Sprintf(format, a...), p.cur.line, p.cur.offset}
|
||||
p.cur.err = pe
|
||||
p.done = true
|
||||
return pe
|
||||
}
|
||||
|
||||
// Numbers and identifiers are matched by [-+._A-Za-z0-9]
|
||||
func isIdentOrNumberChar(c byte) bool {
|
||||
switch {
|
||||
case 'A' <= c && c <= 'Z', 'a' <= c && c <= 'z':
|
||||
return true
|
||||
case '0' <= c && c <= '9':
|
||||
return true
|
||||
}
|
||||
switch c {
|
||||
case '-', '+', '.', '_':
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isWhitespace(c byte) bool {
|
||||
switch c {
|
||||
case ' ', '\t', '\n', '\r':
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *textParser) skipWhitespace() {
|
||||
i := 0
|
||||
for i < len(p.s) && (isWhitespace(p.s[i]) || p.s[i] == '#') {
|
||||
if p.s[i] == '#' {
|
||||
// comment; skip to end of line or input
|
||||
for i < len(p.s) && p.s[i] != '\n' {
|
||||
i++
|
||||
}
|
||||
if i == len(p.s) {
|
||||
break
|
||||
}
|
||||
}
|
||||
if p.s[i] == '\n' {
|
||||
p.line++
|
||||
}
|
||||
i++
|
||||
}
|
||||
p.offset += i
|
||||
p.s = p.s[i:len(p.s)]
|
||||
if len(p.s) == 0 {
|
||||
p.done = true
|
||||
}
|
||||
}
|
||||
|
||||
func (p *textParser) advance() {
|
||||
// Skip whitespace
|
||||
p.skipWhitespace()
|
||||
if p.done {
|
||||
return
|
||||
}
|
||||
|
||||
// Start of non-whitespace
|
||||
p.cur.err = nil
|
||||
p.cur.offset, p.cur.line = p.offset, p.line
|
||||
p.cur.unquoted = ""
|
||||
switch p.s[0] {
|
||||
case '<', '>', '{', '}', ':', '[', ']', ';', ',':
|
||||
// Single symbol
|
||||
p.cur.value, p.s = p.s[0:1], p.s[1:len(p.s)]
|
||||
case '"', '\'':
|
||||
// Quoted string
|
||||
i := 1
|
||||
for i < len(p.s) && p.s[i] != p.s[0] && p.s[i] != '\n' {
|
||||
if p.s[i] == '\\' && i+1 < len(p.s) {
|
||||
// skip escaped char
|
||||
i++
|
||||
}
|
||||
i++
|
||||
}
|
||||
if i >= len(p.s) || p.s[i] != p.s[0] {
|
||||
p.errorf("unmatched quote")
|
||||
return
|
||||
}
|
||||
unq, err := unquoteC(p.s[1:i], rune(p.s[0]))
|
||||
if err != nil {
|
||||
p.errorf("invalid quoted string %s: %v", p.s[0:i+1], err)
|
||||
return
|
||||
}
|
||||
p.cur.value, p.s = p.s[0:i+1], p.s[i+1:len(p.s)]
|
||||
p.cur.unquoted = unq
|
||||
default:
|
||||
i := 0
|
||||
for i < len(p.s) && isIdentOrNumberChar(p.s[i]) {
|
||||
i++
|
||||
}
|
||||
if i == 0 {
|
||||
p.errorf("unexpected byte %#x", p.s[0])
|
||||
return
|
||||
}
|
||||
p.cur.value, p.s = p.s[0:i], p.s[i:len(p.s)]
|
||||
}
|
||||
p.offset += len(p.cur.value)
|
||||
}
|
||||
|
||||
var (
|
||||
errBadUTF8 = errors.New("proto: bad UTF-8")
|
||||
errBadHex = errors.New("proto: bad hexadecimal")
|
||||
)
|
||||
|
||||
func unquoteC(s string, quote rune) (string, error) {
|
||||
// This is based on C++'s tokenizer.cc.
|
||||
// Despite its name, this is *not* parsing C syntax.
|
||||
// For instance, "\0" is an invalid quoted string.
|
||||
|
||||
// Avoid allocation in trivial cases.
|
||||
simple := true
|
||||
for _, r := range s {
|
||||
if r == '\\' || r == quote {
|
||||
simple = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if simple {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
buf := make([]byte, 0, 3*len(s)/2)
|
||||
for len(s) > 0 {
|
||||
r, n := utf8.DecodeRuneInString(s)
|
||||
if r == utf8.RuneError && n == 1 {
|
||||
return "", errBadUTF8
|
||||
}
|
||||
s = s[n:]
|
||||
if r != '\\' {
|
||||
if r < utf8.RuneSelf {
|
||||
buf = append(buf, byte(r))
|
||||
} else {
|
||||
buf = append(buf, string(r)...)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
ch, tail, err := unescape(s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buf = append(buf, ch...)
|
||||
s = tail
|
||||
}
|
||||
return string(buf), nil
|
||||
}
|
||||
|
||||
func unescape(s string) (ch string, tail string, err error) {
|
||||
r, n := utf8.DecodeRuneInString(s)
|
||||
if r == utf8.RuneError && n == 1 {
|
||||
return "", "", errBadUTF8
|
||||
}
|
||||
s = s[n:]
|
||||
switch r {
|
||||
case 'a':
|
||||
return "\a", s, nil
|
||||
case 'b':
|
||||
return "\b", s, nil
|
||||
case 'f':
|
||||
return "\f", s, nil
|
||||
case 'n':
|
||||
return "\n", s, nil
|
||||
case 'r':
|
||||
return "\r", s, nil
|
||||
case 't':
|
||||
return "\t", s, nil
|
||||
case 'v':
|
||||
return "\v", s, nil
|
||||
case '?':
|
||||
return "?", s, nil // trigraph workaround
|
||||
case '\'', '"', '\\':
|
||||
return string(r), s, nil
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', 'x', 'X':
|
||||
if len(s) < 2 {
|
||||
return "", "", fmt.Errorf(`\%c requires 2 following digits`, r)
|
||||
}
|
||||
base := 8
|
||||
ss := s[:2]
|
||||
s = s[2:]
|
||||
if r == 'x' || r == 'X' {
|
||||
base = 16
|
||||
} else {
|
||||
ss = string(r) + ss
|
||||
}
|
||||
i, err := strconv.ParseUint(ss, base, 8)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return string([]byte{byte(i)}), s, nil
|
||||
case 'u', 'U':
|
||||
n := 4
|
||||
if r == 'U' {
|
||||
n = 8
|
||||
}
|
||||
if len(s) < n {
|
||||
return "", "", fmt.Errorf(`\%c requires %d digits`, r, n)
|
||||
}
|
||||
|
||||
bs := make([]byte, n/2)
|
||||
for i := 0; i < n; i += 2 {
|
||||
a, ok1 := unhex(s[i])
|
||||
b, ok2 := unhex(s[i+1])
|
||||
if !ok1 || !ok2 {
|
||||
return "", "", errBadHex
|
||||
}
|
||||
bs[i/2] = a<<4 | b
|
||||
}
|
||||
s = s[n:]
|
||||
return string(bs), s, nil
|
||||
}
|
||||
return "", "", fmt.Errorf(`unknown escape \%c`, r)
|
||||
}
|
||||
|
||||
// Adapted from src/pkg/strconv/quote.go.
|
||||
func unhex(b byte) (v byte, ok bool) {
|
||||
switch {
|
||||
case '0' <= b && b <= '9':
|
||||
return b - '0', true
|
||||
case 'a' <= b && b <= 'f':
|
||||
return b - 'a' + 10, true
|
||||
case 'A' <= b && b <= 'F':
|
||||
return b - 'A' + 10, true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// Back off the parser by one token. Can only be done between calls to next().
|
||||
// It makes the next advance() a no-op.
|
||||
func (p *textParser) back() { p.backed = true }
|
||||
|
||||
// Advances the parser and returns the new current token.
|
||||
func (p *textParser) next() *token {
|
||||
if p.backed || p.done {
|
||||
p.backed = false
|
||||
return &p.cur
|
||||
}
|
||||
p.advance()
|
||||
if p.done {
|
||||
p.cur.value = ""
|
||||
} else if len(p.cur.value) > 0 && p.cur.value[0] == '"' {
|
||||
// Look for multiple quoted strings separated by whitespace,
|
||||
// and concatenate them.
|
||||
cat := p.cur
|
||||
for {
|
||||
p.skipWhitespace()
|
||||
if p.done || p.s[0] != '"' {
|
||||
break
|
||||
}
|
||||
p.advance()
|
||||
if p.cur.err != nil {
|
||||
return &p.cur
|
||||
}
|
||||
cat.value += " " + p.cur.value
|
||||
cat.unquoted += p.cur.unquoted
|
||||
}
|
||||
p.done = false // parser may have seen EOF, but we want to return cat
|
||||
p.cur = cat
|
||||
}
|
||||
return &p.cur
|
||||
}
|
||||
|
||||
func (p *textParser) consumeToken(s string) error {
|
||||
tok := p.next()
|
||||
if tok.err != nil {
|
||||
return tok.err
|
||||
}
|
||||
if tok.value != s {
|
||||
p.back()
|
||||
return p.errorf("expected %q, found %q", s, tok.value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Return a RequiredNotSetError indicating which required field was not set.
|
||||
func (p *textParser) missingRequiredFieldError(sv reflect.Value) *RequiredNotSetError {
|
||||
st := sv.Type()
|
||||
sprops := GetProperties(st)
|
||||
for i := 0; i < st.NumField(); i++ {
|
||||
if !isNil(sv.Field(i)) {
|
||||
continue
|
||||
}
|
||||
|
||||
props := sprops.Prop[i]
|
||||
if props.Required {
|
||||
return &RequiredNotSetError{fmt.Sprintf("%v.%v", st, props.OrigName)}
|
||||
}
|
||||
}
|
||||
return &RequiredNotSetError{fmt.Sprintf("%v.<unknown field name>", st)} // should not happen
|
||||
}
|
||||
|
||||
// Returns the index in the struct for the named field, as well as the parsed tag properties.
|
||||
func structFieldByName(st reflect.Type, name string) (int, *Properties, bool) {
|
||||
sprops := GetProperties(st)
|
||||
i, ok := sprops.decoderOrigNames[name]
|
||||
if ok {
|
||||
return i, sprops.Prop[i], true
|
||||
}
|
||||
return -1, nil, false
|
||||
}
|
||||
|
||||
// Consume a ':' from the input stream (if the next token is a colon),
|
||||
// returning an error if a colon is needed but not present.
|
||||
func (p *textParser) checkForColon(props *Properties, typ reflect.Type) *ParseError {
|
||||
tok := p.next()
|
||||
if tok.err != nil {
|
||||
return tok.err
|
||||
}
|
||||
if tok.value != ":" {
|
||||
// Colon is optional when the field is a group or message.
|
||||
needColon := true
|
||||
switch props.Wire {
|
||||
case "group":
|
||||
needColon = false
|
||||
case "bytes":
|
||||
// A "bytes" field is either a message, a string, or a repeated field;
|
||||
// those three become *T, *string and []T respectively, so we can check for
|
||||
// this field being a pointer to a non-string.
|
||||
if typ.Kind() == reflect.Ptr {
|
||||
// *T or *string
|
||||
if typ.Elem().Kind() == reflect.String {
|
||||
break
|
||||
}
|
||||
} else if typ.Kind() == reflect.Slice {
|
||||
// []T or []*T
|
||||
if typ.Elem().Kind() != reflect.Ptr {
|
||||
break
|
||||
}
|
||||
} else if typ.Kind() == reflect.String {
|
||||
// The proto3 exception is for a string field,
|
||||
// which requires a colon.
|
||||
break
|
||||
}
|
||||
needColon = false
|
||||
}
|
||||
if needColon {
|
||||
return p.errorf("expected ':', found %q", tok.value)
|
||||
}
|
||||
p.back()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
|
||||
st := sv.Type()
|
||||
reqCount := GetProperties(st).reqCount
|
||||
var reqFieldErr error
|
||||
fieldSet := make(map[string]bool)
|
||||
// A struct is a sequence of "name: value", terminated by one of
|
||||
// '>' or '}', or the end of the input. A name may also be
|
||||
// "[extension]".
|
||||
for {
|
||||
tok := p.next()
|
||||
if tok.err != nil {
|
||||
return tok.err
|
||||
}
|
||||
if tok.value == terminator {
|
||||
break
|
||||
}
|
||||
if tok.value == "[" {
|
||||
// Looks like an extension.
|
||||
//
|
||||
// TODO: Check whether we need to handle
|
||||
// namespace rooted names (e.g. ".something.Foo").
|
||||
tok = p.next()
|
||||
if tok.err != nil {
|
||||
return tok.err
|
||||
}
|
||||
var desc *ExtensionDesc
|
||||
// This could be faster, but it's functional.
|
||||
// TODO: Do something smarter than a linear scan.
|
||||
for _, d := range RegisteredExtensions(reflect.New(st).Interface().(Message)) {
|
||||
if d.Name == tok.value {
|
||||
desc = d
|
||||
break
|
||||
}
|
||||
}
|
||||
if desc == nil {
|
||||
return p.errorf("unrecognized extension %q", tok.value)
|
||||
}
|
||||
// Check the extension terminator.
|
||||
tok = p.next()
|
||||
if tok.err != nil {
|
||||
return tok.err
|
||||
}
|
||||
if tok.value != "]" {
|
||||
return p.errorf("unrecognized extension terminator %q", tok.value)
|
||||
}
|
||||
|
||||
props := &Properties{}
|
||||
props.Parse(desc.Tag)
|
||||
|
||||
typ := reflect.TypeOf(desc.ExtensionType)
|
||||
if err := p.checkForColon(props, typ); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rep := desc.repeated()
|
||||
|
||||
// Read the extension structure, and set it in
|
||||
// the value we're constructing.
|
||||
var ext reflect.Value
|
||||
if !rep {
|
||||
ext = reflect.New(typ).Elem()
|
||||
} else {
|
||||
ext = reflect.New(typ.Elem()).Elem()
|
||||
}
|
||||
if err := p.readAny(ext, props); err != nil {
|
||||
if _, ok := err.(*RequiredNotSetError); !ok {
|
||||
return err
|
||||
}
|
||||
reqFieldErr = err
|
||||
}
|
||||
ep := sv.Addr().Interface().(extendableProto)
|
||||
if !rep {
|
||||
SetExtension(ep, desc, ext.Interface())
|
||||
} else {
|
||||
old, err := GetExtension(ep, desc)
|
||||
var sl reflect.Value
|
||||
if err == nil {
|
||||
sl = reflect.ValueOf(old) // existing slice
|
||||
} else {
|
||||
sl = reflect.MakeSlice(typ, 0, 1)
|
||||
}
|
||||
sl = reflect.Append(sl, ext)
|
||||
SetExtension(ep, desc, sl.Interface())
|
||||
}
|
||||
} else {
|
||||
// This is a normal, non-extension field.
|
||||
name := tok.value
|
||||
fi, props, ok := structFieldByName(st, name)
|
||||
if !ok {
|
||||
return p.errorf("unknown field name %q in %v", name, st)
|
||||
}
|
||||
|
||||
dst := sv.Field(fi)
|
||||
|
||||
if dst.Kind() == reflect.Map {
|
||||
// Consume any colon.
|
||||
if err := p.checkForColon(props, dst.Type()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Construct the map if it doesn't already exist.
|
||||
if dst.IsNil() {
|
||||
dst.Set(reflect.MakeMap(dst.Type()))
|
||||
}
|
||||
key := reflect.New(dst.Type().Key()).Elem()
|
||||
val := reflect.New(dst.Type().Elem()).Elem()
|
||||
|
||||
// The map entry should be this sequence of tokens:
|
||||
// < key : KEY value : VALUE >
|
||||
// Technically the "key" and "value" could come in any order,
|
||||
// but in practice they won't.
|
||||
|
||||
tok := p.next()
|
||||
var terminator string
|
||||
switch tok.value {
|
||||
case "<":
|
||||
terminator = ">"
|
||||
case "{":
|
||||
terminator = "}"
|
||||
default:
|
||||
return p.errorf("expected '{' or '<', found %q", tok.value)
|
||||
}
|
||||
if err := p.consumeToken("key"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := p.consumeToken(":"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := p.readAny(key, props.mkeyprop); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := p.consumeOptionalSeparator(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := p.consumeToken("value"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := p.checkForColon(props.mvalprop, dst.Type().Elem()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := p.readAny(val, props.mvalprop); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := p.consumeOptionalSeparator(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := p.consumeToken(terminator); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dst.SetMapIndex(key, val)
|
||||
continue
|
||||
}
|
||||
|
||||
// Check that it's not already set if it's not a repeated field.
|
||||
if !props.Repeated && fieldSet[name] {
|
||||
return p.errorf("non-repeated field %q was repeated", name)
|
||||
}
|
||||
|
||||
if err := p.checkForColon(props, st.Field(fi).Type); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Parse into the field.
|
||||
fieldSet[name] = true
|
||||
if err := p.readAny(dst, props); err != nil {
|
||||
if _, ok := err.(*RequiredNotSetError); !ok {
|
||||
return err
|
||||
}
|
||||
reqFieldErr = err
|
||||
} else if props.Required {
|
||||
reqCount--
|
||||
}
|
||||
}
|
||||
|
||||
if err := p.consumeOptionalSeparator(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if reqCount > 0 {
|
||||
return p.missingRequiredFieldError(sv)
|
||||
}
|
||||
return reqFieldErr
|
||||
}
|
||||
|
||||
// consumeOptionalSeparator consumes an optional semicolon or comma.
|
||||
// It is used in readStruct to provide backward compatibility.
|
||||
func (p *textParser) consumeOptionalSeparator() error {
|
||||
tok := p.next()
|
||||
if tok.err != nil {
|
||||
return tok.err
|
||||
}
|
||||
if tok.value != ";" && tok.value != "," {
|
||||
p.back()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *textParser) readAny(v reflect.Value, props *Properties) error {
|
||||
tok := p.next()
|
||||
if tok.err != nil {
|
||||
return tok.err
|
||||
}
|
||||
if tok.value == "" {
|
||||
return p.errorf("unexpected EOF")
|
||||
}
|
||||
|
||||
switch fv := v; fv.Kind() {
|
||||
case reflect.Slice:
|
||||
at := v.Type()
|
||||
if at.Elem().Kind() == reflect.Uint8 {
|
||||
// Special case for []byte
|
||||
if tok.value[0] != '"' && tok.value[0] != '\'' {
|
||||
// Deliberately written out here, as the error after
|
||||
// this switch statement would write "invalid []byte: ...",
|
||||
// which is not as user-friendly.
|
||||
return p.errorf("invalid string: %v", tok.value)
|
||||
}
|
||||
bytes := []byte(tok.unquoted)
|
||||
fv.Set(reflect.ValueOf(bytes))
|
||||
return nil
|
||||
}
|
||||
// Repeated field. May already exist.
|
||||
flen := fv.Len()
|
||||
if flen == fv.Cap() {
|
||||
nav := reflect.MakeSlice(at, flen, 2*flen+1)
|
||||
reflect.Copy(nav, fv)
|
||||
fv.Set(nav)
|
||||
}
|
||||
fv.SetLen(flen + 1)
|
||||
|
||||
// Read one.
|
||||
p.back()
|
||||
return p.readAny(fv.Index(flen), props)
|
||||
case reflect.Bool:
|
||||
// Either "true", "false", 1 or 0.
|
||||
switch tok.value {
|
||||
case "true", "1":
|
||||
fv.SetBool(true)
|
||||
return nil
|
||||
case "false", "0":
|
||||
fv.SetBool(false)
|
||||
return nil
|
||||
}
|
||||
case reflect.Float32, reflect.Float64:
|
||||
v := tok.value
|
||||
// Ignore 'f' for compatibility with output generated by C++, but don't
|
||||
// remove 'f' when the value is "-inf" or "inf".
|
||||
if strings.HasSuffix(v, "f") && tok.value != "-inf" && tok.value != "inf" {
|
||||
v = v[:len(v)-1]
|
||||
}
|
||||
if f, err := strconv.ParseFloat(v, fv.Type().Bits()); err == nil {
|
||||
fv.SetFloat(f)
|
||||
return nil
|
||||
}
|
||||
case reflect.Int32:
|
||||
if x, err := strconv.ParseInt(tok.value, 0, 32); err == nil {
|
||||
fv.SetInt(x)
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(props.Enum) == 0 {
|
||||
break
|
||||
}
|
||||
m, ok := enumValueMaps[props.Enum]
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
x, ok := m[tok.value]
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
fv.SetInt(int64(x))
|
||||
return nil
|
||||
case reflect.Int64:
|
||||
if x, err := strconv.ParseInt(tok.value, 0, 64); err == nil {
|
||||
fv.SetInt(x)
|
||||
return nil
|
||||
}
|
||||
|
||||
case reflect.Ptr:
|
||||
// A basic field (indirected through pointer), or a repeated message/group
|
||||
p.back()
|
||||
fv.Set(reflect.New(fv.Type().Elem()))
|
||||
return p.readAny(fv.Elem(), props)
|
||||
case reflect.String:
|
||||
if tok.value[0] == '"' || tok.value[0] == '\'' {
|
||||
fv.SetString(tok.unquoted)
|
||||
return nil
|
||||
}
|
||||
case reflect.Struct:
|
||||
var terminator string
|
||||
switch tok.value {
|
||||
case "{":
|
||||
terminator = "}"
|
||||
case "<":
|
||||
terminator = ">"
|
||||
default:
|
||||
return p.errorf("expected '{' or '<', found %q", tok.value)
|
||||
}
|
||||
// TODO: Handle nested messages which implement encoding.TextUnmarshaler.
|
||||
return p.readStruct(fv, terminator)
|
||||
case reflect.Uint32:
|
||||
if x, err := strconv.ParseUint(tok.value, 0, 32); err == nil {
|
||||
fv.SetUint(uint64(x))
|
||||
return nil
|
||||
}
|
||||
case reflect.Uint64:
|
||||
if x, err := strconv.ParseUint(tok.value, 0, 64); err == nil {
|
||||
fv.SetUint(x)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return p.errorf("invalid %v: %v", v.Type(), tok.value)
|
||||
}
|
||||
|
||||
// UnmarshalText reads a protocol buffer in Text format. UnmarshalText resets pb
|
||||
// before starting to unmarshal, so any existing data in pb is always removed.
|
||||
// If a required field is not set and no other error occurs,
|
||||
// UnmarshalText returns *RequiredNotSetError.
|
||||
func UnmarshalText(s string, pb Message) error {
|
||||
if um, ok := pb.(encoding.TextUnmarshaler); ok {
|
||||
err := um.UnmarshalText([]byte(s))
|
||||
return err
|
||||
}
|
||||
pb.Reset()
|
||||
v := reflect.ValueOf(pb)
|
||||
if pe := newTextParser(s).readStruct(v.Elem(), ""); pe != nil {
|
||||
return pe
|
||||
}
|
||||
return nil
|
||||
}
|
511
Godeps/_workspace/src/github.com/golang/protobuf/proto/text_parser_test.go
generated
vendored
Normal file
511
Godeps/_workspace/src/github.com/golang/protobuf/proto/text_parser_test.go
generated
vendored
Normal file
|
@ -0,0 +1,511 @@
|
|||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package proto_test
|
||||
|
||||
import (
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
. "github.com/golang/protobuf/proto"
|
||||
proto3pb "github.com/golang/protobuf/proto/proto3_proto"
|
||||
. "github.com/golang/protobuf/proto/testdata"
|
||||
)
|
||||
|
||||
type UnmarshalTextTest struct {
|
||||
in string
|
||||
err string // if "", no error expected
|
||||
out *MyMessage
|
||||
}
|
||||
|
||||
func buildExtStructTest(text string) UnmarshalTextTest {
|
||||
msg := &MyMessage{
|
||||
Count: Int32(42),
|
||||
}
|
||||
SetExtension(msg, E_Ext_More, &Ext{
|
||||
Data: String("Hello, world!"),
|
||||
})
|
||||
return UnmarshalTextTest{in: text, out: msg}
|
||||
}
|
||||
|
||||
func buildExtDataTest(text string) UnmarshalTextTest {
|
||||
msg := &MyMessage{
|
||||
Count: Int32(42),
|
||||
}
|
||||
SetExtension(msg, E_Ext_Text, String("Hello, world!"))
|
||||
SetExtension(msg, E_Ext_Number, Int32(1729))
|
||||
return UnmarshalTextTest{in: text, out: msg}
|
||||
}
|
||||
|
||||
func buildExtRepStringTest(text string) UnmarshalTextTest {
|
||||
msg := &MyMessage{
|
||||
Count: Int32(42),
|
||||
}
|
||||
if err := SetExtension(msg, E_Greeting, []string{"bula", "hola"}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return UnmarshalTextTest{in: text, out: msg}
|
||||
}
|
||||
|
||||
var unMarshalTextTests = []UnmarshalTextTest{
|
||||
// Basic
|
||||
{
|
||||
in: " count:42\n name:\"Dave\" ",
|
||||
out: &MyMessage{
|
||||
Count: Int32(42),
|
||||
Name: String("Dave"),
|
||||
},
|
||||
},
|
||||
|
||||
// Empty quoted string
|
||||
{
|
||||
in: `count:42 name:""`,
|
||||
out: &MyMessage{
|
||||
Count: Int32(42),
|
||||
Name: String(""),
|
||||
},
|
||||
},
|
||||
|
||||
// Quoted string concatenation
|
||||
{
|
||||
in: `count:42 name: "My name is "` + "\n" + `"elsewhere"`,
|
||||
out: &MyMessage{
|
||||
Count: Int32(42),
|
||||
Name: String("My name is elsewhere"),
|
||||
},
|
||||
},
|
||||
|
||||
// Quoted string with escaped apostrophe
|
||||
{
|
||||
in: `count:42 name: "HOLIDAY - New Year\'s Day"`,
|
||||
out: &MyMessage{
|
||||
Count: Int32(42),
|
||||
Name: String("HOLIDAY - New Year's Day"),
|
||||
},
|
||||
},
|
||||
|
||||
// Quoted string with single quote
|
||||
{
|
||||
in: `count:42 name: 'Roger "The Ramster" Ramjet'`,
|
||||
out: &MyMessage{
|
||||
Count: Int32(42),
|
||||
Name: String(`Roger "The Ramster" Ramjet`),
|
||||
},
|
||||
},
|
||||
|
||||
// Quoted string with all the accepted special characters from the C++ test
|
||||
{
|
||||
in: `count:42 name: ` + "\"\\\"A string with \\' characters \\n and \\r newlines and \\t tabs and \\001 slashes \\\\ and multiple spaces\"",
|
||||
out: &MyMessage{
|
||||
Count: Int32(42),
|
||||
Name: String("\"A string with ' characters \n and \r newlines and \t tabs and \001 slashes \\ and multiple spaces"),
|
||||
},
|
||||
},
|
||||
|
||||
// Quoted string with quoted backslash
|
||||
{
|
||||
in: `count:42 name: "\\'xyz"`,
|
||||
out: &MyMessage{
|
||||
Count: Int32(42),
|
||||
Name: String(`\'xyz`),
|
||||
},
|
||||
},
|
||||
|
||||
// Quoted string with UTF-8 bytes.
|
||||
{
|
||||
in: "count:42 name: '\303\277\302\201\xAB'",
|
||||
out: &MyMessage{
|
||||
Count: Int32(42),
|
||||
Name: String("\303\277\302\201\xAB"),
|
||||
},
|
||||
},
|
||||
|
||||
// Bad quoted string
|
||||
{
|
||||
in: `inner: < host: "\0" >` + "\n",
|
||||
err: `line 1.15: invalid quoted string "\0": \0 requires 2 following digits`,
|
||||
},
|
||||
|
||||
// Number too large for int64
|
||||
{
|
||||
in: "count: 1 others { key: 123456789012345678901 }",
|
||||
err: "line 1.23: invalid int64: 123456789012345678901",
|
||||
},
|
||||
|
||||
// Number too large for int32
|
||||
{
|
||||
in: "count: 1234567890123",
|
||||
err: "line 1.7: invalid int32: 1234567890123",
|
||||
},
|
||||
|
||||
// Number in hexadecimal
|
||||
{
|
||||
in: "count: 0x2beef",
|
||||
out: &MyMessage{
|
||||
Count: Int32(0x2beef),
|
||||
},
|
||||
},
|
||||
|
||||
// Number in octal
|
||||
{
|
||||
in: "count: 024601",
|
||||
out: &MyMessage{
|
||||
Count: Int32(024601),
|
||||
},
|
||||
},
|
||||
|
||||
// Floating point number with "f" suffix
|
||||
{
|
||||
in: "count: 4 others:< weight: 17.0f >",
|
||||
out: &MyMessage{
|
||||
Count: Int32(4),
|
||||
Others: []*OtherMessage{
|
||||
{
|
||||
Weight: Float32(17),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// Floating point positive infinity
|
||||
{
|
||||
in: "count: 4 bigfloat: inf",
|
||||
out: &MyMessage{
|
||||
Count: Int32(4),
|
||||
Bigfloat: Float64(math.Inf(1)),
|
||||
},
|
||||
},
|
||||
|
||||
// Floating point negative infinity
|
||||
{
|
||||
in: "count: 4 bigfloat: -inf",
|
||||
out: &MyMessage{
|
||||
Count: Int32(4),
|
||||
Bigfloat: Float64(math.Inf(-1)),
|
||||
},
|
||||
},
|
||||
|
||||
// Number too large for float32
|
||||
{
|
||||
in: "others:< weight: 12345678901234567890123456789012345678901234567890 >",
|
||||
err: "line 1.17: invalid float32: 12345678901234567890123456789012345678901234567890",
|
||||
},
|
||||
|
||||
// Number posing as a quoted string
|
||||
{
|
||||
in: `inner: < host: 12 >` + "\n",
|
||||
err: `line 1.15: invalid string: 12`,
|
||||
},
|
||||
|
||||
// Quoted string posing as int32
|
||||
{
|
||||
in: `count: "12"`,
|
||||
err: `line 1.7: invalid int32: "12"`,
|
||||
},
|
||||
|
||||
// Quoted string posing a float32
|
||||
{
|
||||
in: `others:< weight: "17.4" >`,
|
||||
err: `line 1.17: invalid float32: "17.4"`,
|
||||
},
|
||||
|
||||
// Enum
|
||||
{
|
||||
in: `count:42 bikeshed: BLUE`,
|
||||
out: &MyMessage{
|
||||
Count: Int32(42),
|
||||
Bikeshed: MyMessage_BLUE.Enum(),
|
||||
},
|
||||
},
|
||||
|
||||
// Repeated field
|
||||
{
|
||||
in: `count:42 pet: "horsey" pet:"bunny"`,
|
||||
out: &MyMessage{
|
||||
Count: Int32(42),
|
||||
Pet: []string{"horsey", "bunny"},
|
||||
},
|
||||
},
|
||||
|
||||
// Repeated message with/without colon and <>/{}
|
||||
{
|
||||
in: `count:42 others:{} others{} others:<> others:{}`,
|
||||
out: &MyMessage{
|
||||
Count: Int32(42),
|
||||
Others: []*OtherMessage{
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// Missing colon for inner message
|
||||
{
|
||||
in: `count:42 inner < host: "cauchy.syd" >`,
|
||||
out: &MyMessage{
|
||||
Count: Int32(42),
|
||||
Inner: &InnerMessage{
|
||||
Host: String("cauchy.syd"),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// Missing colon for string field
|
||||
{
|
||||
in: `name "Dave"`,
|
||||
err: `line 1.5: expected ':', found "\"Dave\""`,
|
||||
},
|
||||
|
||||
// Missing colon for int32 field
|
||||
{
|
||||
in: `count 42`,
|
||||
err: `line 1.6: expected ':', found "42"`,
|
||||
},
|
||||
|
||||
// Missing required field
|
||||
{
|
||||
in: `name: "Pawel"`,
|
||||
err: `proto: required field "testdata.MyMessage.count" not set`,
|
||||
out: &MyMessage{
|
||||
Name: String("Pawel"),
|
||||
},
|
||||
},
|
||||
|
||||
// Repeated non-repeated field
|
||||
{
|
||||
in: `name: "Rob" name: "Russ"`,
|
||||
err: `line 1.12: non-repeated field "name" was repeated`,
|
||||
},
|
||||
|
||||
// Group
|
||||
{
|
||||
in: `count: 17 SomeGroup { group_field: 12 }`,
|
||||
out: &MyMessage{
|
||||
Count: Int32(17),
|
||||
Somegroup: &MyMessage_SomeGroup{
|
||||
GroupField: Int32(12),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// Semicolon between fields
|
||||
{
|
||||
in: `count:3;name:"Calvin"`,
|
||||
out: &MyMessage{
|
||||
Count: Int32(3),
|
||||
Name: String("Calvin"),
|
||||
},
|
||||
},
|
||||
// Comma between fields
|
||||
{
|
||||
in: `count:4,name:"Ezekiel"`,
|
||||
out: &MyMessage{
|
||||
Count: Int32(4),
|
||||
Name: String("Ezekiel"),
|
||||
},
|
||||
},
|
||||
|
||||
// Extension
|
||||
buildExtStructTest(`count: 42 [testdata.Ext.more]:<data:"Hello, world!" >`),
|
||||
buildExtStructTest(`count: 42 [testdata.Ext.more] {data:"Hello, world!"}`),
|
||||
buildExtDataTest(`count: 42 [testdata.Ext.text]:"Hello, world!" [testdata.Ext.number]:1729`),
|
||||
buildExtRepStringTest(`count: 42 [testdata.greeting]:"bula" [testdata.greeting]:"hola"`),
|
||||
|
||||
// Big all-in-one
|
||||
{
|
||||
in: "count:42 # Meaning\n" +
|
||||
`name:"Dave" ` +
|
||||
`quote:"\"I didn't want to go.\"" ` +
|
||||
`pet:"bunny" ` +
|
||||
`pet:"kitty" ` +
|
||||
`pet:"horsey" ` +
|
||||
`inner:<` +
|
||||
` host:"footrest.syd" ` +
|
||||
` port:7001 ` +
|
||||
` connected:true ` +
|
||||
`> ` +
|
||||
`others:<` +
|
||||
` key:3735928559 ` +
|
||||
` value:"\x01A\a\f" ` +
|
||||
`> ` +
|
||||
`others:<` +
|
||||
" weight:58.9 # Atomic weight of Co\n" +
|
||||
` inner:<` +
|
||||
` host:"lesha.mtv" ` +
|
||||
` port:8002 ` +
|
||||
` >` +
|
||||
`>`,
|
||||
out: &MyMessage{
|
||||
Count: Int32(42),
|
||||
Name: String("Dave"),
|
||||
Quote: String(`"I didn't want to go."`),
|
||||
Pet: []string{"bunny", "kitty", "horsey"},
|
||||
Inner: &InnerMessage{
|
||||
Host: String("footrest.syd"),
|
||||
Port: Int32(7001),
|
||||
Connected: Bool(true),
|
||||
},
|
||||
Others: []*OtherMessage{
|
||||
{
|
||||
Key: Int64(3735928559),
|
||||
Value: []byte{0x1, 'A', '\a', '\f'},
|
||||
},
|
||||
{
|
||||
Weight: Float32(58.9),
|
||||
Inner: &InnerMessage{
|
||||
Host: String("lesha.mtv"),
|
||||
Port: Int32(8002),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestUnmarshalText(t *testing.T) {
|
||||
for i, test := range unMarshalTextTests {
|
||||
pb := new(MyMessage)
|
||||
err := UnmarshalText(test.in, pb)
|
||||
if test.err == "" {
|
||||
// We don't expect failure.
|
||||
if err != nil {
|
||||
t.Errorf("Test %d: Unexpected error: %v", i, err)
|
||||
} else if !reflect.DeepEqual(pb, test.out) {
|
||||
t.Errorf("Test %d: Incorrect populated \nHave: %v\nWant: %v",
|
||||
i, pb, test.out)
|
||||
}
|
||||
} else {
|
||||
// We do expect failure.
|
||||
if err == nil {
|
||||
t.Errorf("Test %d: Didn't get expected error: %v", i, test.err)
|
||||
} else if err.Error() != test.err {
|
||||
t.Errorf("Test %d: Incorrect error.\nHave: %v\nWant: %v",
|
||||
i, err.Error(), test.err)
|
||||
} else if _, ok := err.(*RequiredNotSetError); ok && test.out != nil && !reflect.DeepEqual(pb, test.out) {
|
||||
t.Errorf("Test %d: Incorrect populated \nHave: %v\nWant: %v",
|
||||
i, pb, test.out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalTextCustomMessage(t *testing.T) {
|
||||
msg := &textMessage{}
|
||||
if err := UnmarshalText("custom", msg); err != nil {
|
||||
t.Errorf("Unexpected error from custom unmarshal: %v", err)
|
||||
}
|
||||
if UnmarshalText("not custom", msg) == nil {
|
||||
t.Errorf("Didn't get expected error from custom unmarshal")
|
||||
}
|
||||
}
|
||||
|
||||
// Regression test; this caused a panic.
|
||||
func TestRepeatedEnum(t *testing.T) {
|
||||
pb := new(RepeatedEnum)
|
||||
if err := UnmarshalText("color: RED", pb); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
exp := &RepeatedEnum{
|
||||
Color: []RepeatedEnum_Color{RepeatedEnum_RED},
|
||||
}
|
||||
if !Equal(pb, exp) {
|
||||
t.Errorf("Incorrect populated \nHave: %v\nWant: %v", pb, exp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProto3TextParsing(t *testing.T) {
|
||||
m := new(proto3pb.Message)
|
||||
const in = `name: "Wallace" true_scotsman: true`
|
||||
want := &proto3pb.Message{
|
||||
Name: "Wallace",
|
||||
TrueScotsman: true,
|
||||
}
|
||||
if err := UnmarshalText(in, m); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !Equal(m, want) {
|
||||
t.Errorf("\n got %v\nwant %v", m, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapParsing(t *testing.T) {
|
||||
m := new(MessageWithMap)
|
||||
const in = `name_mapping:<key:1234 value:"Feist"> name_mapping:<key:1 value:"Beatles">` +
|
||||
`msg_mapping:<key:-4, value:<f: 2.0>,>` + // separating commas are okay
|
||||
`msg_mapping<key:-2 value<f: 4.0>>` + // no colon after "value"
|
||||
`byte_mapping:<key:true value:"so be it">`
|
||||
want := &MessageWithMap{
|
||||
NameMapping: map[int32]string{
|
||||
1: "Beatles",
|
||||
1234: "Feist",
|
||||
},
|
||||
MsgMapping: map[int64]*FloatingPoint{
|
||||
-4: {F: Float64(2.0)},
|
||||
-2: {F: Float64(4.0)},
|
||||
},
|
||||
ByteMapping: map[bool][]byte{
|
||||
true: []byte("so be it"),
|
||||
},
|
||||
}
|
||||
if err := UnmarshalText(in, m); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !Equal(m, want) {
|
||||
t.Errorf("\n got %v\nwant %v", m, want)
|
||||
}
|
||||
}
|
||||
|
||||
var benchInput string
|
||||
|
||||
func init() {
|
||||
benchInput = "count: 4\n"
|
||||
for i := 0; i < 1000; i++ {
|
||||
benchInput += "pet: \"fido\"\n"
|
||||
}
|
||||
|
||||
// Check it is valid input.
|
||||
pb := new(MyMessage)
|
||||
err := UnmarshalText(benchInput, pb)
|
||||
if err != nil {
|
||||
panic("Bad benchmark input: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnmarshalText(b *testing.B) {
|
||||
pb := new(MyMessage)
|
||||
for i := 0; i < b.N; i++ {
|
||||
UnmarshalText(benchInput, pb)
|
||||
}
|
||||
b.SetBytes(int64(len(benchInput)))
|
||||
}
|
441
Godeps/_workspace/src/github.com/golang/protobuf/proto/text_test.go
generated
vendored
Normal file
441
Godeps/_workspace/src/github.com/golang/protobuf/proto/text_test.go
generated
vendored
Normal file
|
@ -0,0 +1,441 @@
|
|||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package proto_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
|
||||
proto3pb "github.com/golang/protobuf/proto/proto3_proto"
|
||||
pb "github.com/golang/protobuf/proto/testdata"
|
||||
)
|
||||
|
||||
// textMessage implements the methods that allow it to marshal and unmarshal
|
||||
// itself as text.
|
||||
type textMessage struct {
|
||||
}
|
||||
|
||||
func (*textMessage) MarshalText() ([]byte, error) {
|
||||
return []byte("custom"), nil
|
||||
}
|
||||
|
||||
func (*textMessage) UnmarshalText(bytes []byte) error {
|
||||
if string(bytes) != "custom" {
|
||||
return errors.New("expected 'custom'")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*textMessage) Reset() {}
|
||||
func (*textMessage) String() string { return "" }
|
||||
func (*textMessage) ProtoMessage() {}
|
||||
|
||||
func newTestMessage() *pb.MyMessage {
|
||||
msg := &pb.MyMessage{
|
||||
Count: proto.Int32(42),
|
||||
Name: proto.String("Dave"),
|
||||
Quote: proto.String(`"I didn't want to go."`),
|
||||
Pet: []string{"bunny", "kitty", "horsey"},
|
||||
Inner: &pb.InnerMessage{
|
||||
Host: proto.String("footrest.syd"),
|
||||
Port: proto.Int32(7001),
|
||||
Connected: proto.Bool(true),
|
||||
},
|
||||
Others: []*pb.OtherMessage{
|
||||
{
|
||||
Key: proto.Int64(0xdeadbeef),
|
||||
Value: []byte{1, 65, 7, 12},
|
||||
},
|
||||
{
|
||||
Weight: proto.Float32(6.022),
|
||||
Inner: &pb.InnerMessage{
|
||||
Host: proto.String("lesha.mtv"),
|
||||
Port: proto.Int32(8002),
|
||||
},
|
||||
},
|
||||
},
|
||||
Bikeshed: pb.MyMessage_BLUE.Enum(),
|
||||
Somegroup: &pb.MyMessage_SomeGroup{
|
||||
GroupField: proto.Int32(8),
|
||||
},
|
||||
// One normally wouldn't do this.
|
||||
// This is an undeclared tag 13, as a varint (wire type 0) with value 4.
|
||||
XXX_unrecognized: []byte{13<<3 | 0, 4},
|
||||
}
|
||||
ext := &pb.Ext{
|
||||
Data: proto.String("Big gobs for big rats"),
|
||||
}
|
||||
if err := proto.SetExtension(msg, pb.E_Ext_More, ext); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
greetings := []string{"adg", "easy", "cow"}
|
||||
if err := proto.SetExtension(msg, pb.E_Greeting, greetings); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Add an unknown extension. We marshal a pb.Ext, and fake the ID.
|
||||
b, err := proto.Marshal(&pb.Ext{Data: proto.String("3G skiing")})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
b = append(proto.EncodeVarint(201<<3|proto.WireBytes), b...)
|
||||
proto.SetRawExtension(msg, 201, b)
|
||||
|
||||
// Extensions can be plain fields, too, so let's test that.
|
||||
b = append(proto.EncodeVarint(202<<3|proto.WireVarint), 19)
|
||||
proto.SetRawExtension(msg, 202, b)
|
||||
|
||||
return msg
|
||||
}
|
||||
|
||||
const text = `count: 42
|
||||
name: "Dave"
|
||||
quote: "\"I didn't want to go.\""
|
||||
pet: "bunny"
|
||||
pet: "kitty"
|
||||
pet: "horsey"
|
||||
inner: <
|
||||
host: "footrest.syd"
|
||||
port: 7001
|
||||
connected: true
|
||||
>
|
||||
others: <
|
||||
key: 3735928559
|
||||
value: "\001A\007\014"
|
||||
>
|
||||
others: <
|
||||
weight: 6.022
|
||||
inner: <
|
||||
host: "lesha.mtv"
|
||||
port: 8002
|
||||
>
|
||||
>
|
||||
bikeshed: BLUE
|
||||
SomeGroup {
|
||||
group_field: 8
|
||||
}
|
||||
/* 2 unknown bytes */
|
||||
13: 4
|
||||
[testdata.Ext.more]: <
|
||||
data: "Big gobs for big rats"
|
||||
>
|
||||
[testdata.greeting]: "adg"
|
||||
[testdata.greeting]: "easy"
|
||||
[testdata.greeting]: "cow"
|
||||
/* 13 unknown bytes */
|
||||
201: "\t3G skiing"
|
||||
/* 3 unknown bytes */
|
||||
202: 19
|
||||
`
|
||||
|
||||
func TestMarshalText(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
if err := proto.MarshalText(buf, newTestMessage()); err != nil {
|
||||
t.Fatalf("proto.MarshalText: %v", err)
|
||||
}
|
||||
s := buf.String()
|
||||
if s != text {
|
||||
t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", s, text)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalTextCustomMessage(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
if err := proto.MarshalText(buf, &textMessage{}); err != nil {
|
||||
t.Fatalf("proto.MarshalText: %v", err)
|
||||
}
|
||||
s := buf.String()
|
||||
if s != "custom" {
|
||||
t.Errorf("Got %q, expected %q", s, "custom")
|
||||
}
|
||||
}
|
||||
func TestMarshalTextNil(t *testing.T) {
|
||||
want := "<nil>"
|
||||
tests := []proto.Message{nil, (*pb.MyMessage)(nil)}
|
||||
for i, test := range tests {
|
||||
buf := new(bytes.Buffer)
|
||||
if err := proto.MarshalText(buf, test); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := buf.String(); got != want {
|
||||
t.Errorf("%d: got %q want %q", i, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalTextUnknownEnum(t *testing.T) {
|
||||
// The Color enum only specifies values 0-2.
|
||||
m := &pb.MyMessage{Bikeshed: pb.MyMessage_Color(3).Enum()}
|
||||
got := m.String()
|
||||
const want = `bikeshed:3 `
|
||||
if got != want {
|
||||
t.Errorf("\n got %q\nwant %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMarshalTextBuffered(b *testing.B) {
|
||||
buf := new(bytes.Buffer)
|
||||
m := newTestMessage()
|
||||
for i := 0; i < b.N; i++ {
|
||||
buf.Reset()
|
||||
proto.MarshalText(buf, m)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMarshalTextUnbuffered(b *testing.B) {
|
||||
w := ioutil.Discard
|
||||
m := newTestMessage()
|
||||
for i := 0; i < b.N; i++ {
|
||||
proto.MarshalText(w, m)
|
||||
}
|
||||
}
|
||||
|
||||
func compact(src string) string {
|
||||
// s/[ \n]+/ /g; s/ $//;
|
||||
dst := make([]byte, len(src))
|
||||
space, comment := false, false
|
||||
j := 0
|
||||
for i := 0; i < len(src); i++ {
|
||||
if strings.HasPrefix(src[i:], "/*") {
|
||||
comment = true
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if comment && strings.HasPrefix(src[i:], "*/") {
|
||||
comment = false
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if comment {
|
||||
continue
|
||||
}
|
||||
c := src[i]
|
||||
if c == ' ' || c == '\n' {
|
||||
space = true
|
||||
continue
|
||||
}
|
||||
if j > 0 && (dst[j-1] == ':' || dst[j-1] == '<' || dst[j-1] == '{') {
|
||||
space = false
|
||||
}
|
||||
if c == '{' {
|
||||
space = false
|
||||
}
|
||||
if space {
|
||||
dst[j] = ' '
|
||||
j++
|
||||
space = false
|
||||
}
|
||||
dst[j] = c
|
||||
j++
|
||||
}
|
||||
if space {
|
||||
dst[j] = ' '
|
||||
j++
|
||||
}
|
||||
return string(dst[0:j])
|
||||
}
|
||||
|
||||
var compactText = compact(text)
|
||||
|
||||
func TestCompactText(t *testing.T) {
|
||||
s := proto.CompactTextString(newTestMessage())
|
||||
if s != compactText {
|
||||
t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v\n===\n", s, compactText)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringEscaping(t *testing.T) {
|
||||
testCases := []struct {
|
||||
in *pb.Strings
|
||||
out string
|
||||
}{
|
||||
{
|
||||
// Test data from C++ test (TextFormatTest.StringEscape).
|
||||
// Single divergence: we don't escape apostrophes.
|
||||
&pb.Strings{StringField: proto.String("\"A string with ' characters \n and \r newlines and \t tabs and \001 slashes \\ and multiple spaces")},
|
||||
"string_field: \"\\\"A string with ' characters \\n and \\r newlines and \\t tabs and \\001 slashes \\\\ and multiple spaces\"\n",
|
||||
},
|
||||
{
|
||||
// Test data from the same C++ test.
|
||||
&pb.Strings{StringField: proto.String("\350\260\267\346\255\214")},
|
||||
"string_field: \"\\350\\260\\267\\346\\255\\214\"\n",
|
||||
},
|
||||
{
|
||||
// Some UTF-8.
|
||||
&pb.Strings{StringField: proto.String("\x00\x01\xff\x81")},
|
||||
`string_field: "\000\001\377\201"` + "\n",
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
var buf bytes.Buffer
|
||||
if err := proto.MarshalText(&buf, tc.in); err != nil {
|
||||
t.Errorf("proto.MarsalText: %v", err)
|
||||
continue
|
||||
}
|
||||
s := buf.String()
|
||||
if s != tc.out {
|
||||
t.Errorf("#%d: Got:\n%s\nExpected:\n%s\n", i, s, tc.out)
|
||||
continue
|
||||
}
|
||||
|
||||
// Check round-trip.
|
||||
pb := new(pb.Strings)
|
||||
if err := proto.UnmarshalText(s, pb); err != nil {
|
||||
t.Errorf("#%d: UnmarshalText: %v", i, err)
|
||||
continue
|
||||
}
|
||||
if !proto.Equal(pb, tc.in) {
|
||||
t.Errorf("#%d: Round-trip failed:\nstart: %v\n end: %v", i, tc.in, pb)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A limitedWriter accepts some output before it fails.
|
||||
// This is a proxy for something like a nearly-full or imminently-failing disk,
|
||||
// or a network connection that is about to die.
|
||||
type limitedWriter struct {
|
||||
b bytes.Buffer
|
||||
limit int
|
||||
}
|
||||
|
||||
var outOfSpace = errors.New("proto: insufficient space")
|
||||
|
||||
func (w *limitedWriter) Write(p []byte) (n int, err error) {
|
||||
var avail = w.limit - w.b.Len()
|
||||
if avail <= 0 {
|
||||
return 0, outOfSpace
|
||||
}
|
||||
if len(p) <= avail {
|
||||
return w.b.Write(p)
|
||||
}
|
||||
n, _ = w.b.Write(p[:avail])
|
||||
return n, outOfSpace
|
||||
}
|
||||
|
||||
func TestMarshalTextFailing(t *testing.T) {
|
||||
// Try lots of different sizes to exercise more error code-paths.
|
||||
for lim := 0; lim < len(text); lim++ {
|
||||
buf := new(limitedWriter)
|
||||
buf.limit = lim
|
||||
err := proto.MarshalText(buf, newTestMessage())
|
||||
// We expect a certain error, but also some partial results in the buffer.
|
||||
if err != outOfSpace {
|
||||
t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", err, outOfSpace)
|
||||
}
|
||||
s := buf.b.String()
|
||||
x := text[:buf.limit]
|
||||
if s != x {
|
||||
t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", s, x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFloats(t *testing.T) {
|
||||
tests := []struct {
|
||||
f float64
|
||||
want string
|
||||
}{
|
||||
{0, "0"},
|
||||
{4.7, "4.7"},
|
||||
{math.Inf(1), "inf"},
|
||||
{math.Inf(-1), "-inf"},
|
||||
{math.NaN(), "nan"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
msg := &pb.FloatingPoint{F: &test.f}
|
||||
got := strings.TrimSpace(msg.String())
|
||||
want := `f:` + test.want
|
||||
if got != want {
|
||||
t.Errorf("f=%f: got %q, want %q", test.f, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepeatedNilText(t *testing.T) {
|
||||
m := &pb.MessageList{
|
||||
Message: []*pb.MessageList_Message{
|
||||
nil,
|
||||
&pb.MessageList_Message{
|
||||
Name: proto.String("Horse"),
|
||||
},
|
||||
nil,
|
||||
},
|
||||
}
|
||||
want := `Message <nil>
|
||||
Message {
|
||||
name: "Horse"
|
||||
}
|
||||
Message <nil>
|
||||
`
|
||||
if s := proto.MarshalTextString(m); s != want {
|
||||
t.Errorf(" got: %s\nwant: %s", s, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProto3Text(t *testing.T) {
|
||||
tests := []struct {
|
||||
m proto.Message
|
||||
want string
|
||||
}{
|
||||
// zero message
|
||||
{&proto3pb.Message{}, ``},
|
||||
// zero message except for an empty byte slice
|
||||
{&proto3pb.Message{Data: []byte{}}, ``},
|
||||
// trivial case
|
||||
{&proto3pb.Message{Name: "Rob", HeightInCm: 175}, `name:"Rob" height_in_cm:175`},
|
||||
// empty map
|
||||
{&pb.MessageWithMap{}, ``},
|
||||
// non-empty map; current map format is the same as a repeated struct
|
||||
{
|
||||
&pb.MessageWithMap{NameMapping: map[int32]string{1234: "Feist"}},
|
||||
`name_mapping:<key:1234 value:"Feist" >`,
|
||||
},
|
||||
// map with nil value; not well-defined, but we shouldn't crash
|
||||
{
|
||||
&pb.MessageWithMap{MsgMapping: map[int64]*pb.FloatingPoint{7: nil}},
|
||||
`msg_mapping:<key:7 >`,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
got := strings.TrimSpace(test.m.String())
|
||||
if got != test.want {
|
||||
t.Errorf("\n got %s\nwant %s", got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
1
Godeps/_workspace/src/github.com/jmoiron/jsonq/.gitignore
generated
vendored
Normal file
1
Godeps/_workspace/src/github.com/jmoiron/jsonq/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
*.sw[op]
|
23
Godeps/_workspace/src/github.com/jmoiron/jsonq/LICENSE
generated
vendored
Normal file
23
Godeps/_workspace/src/github.com/jmoiron/jsonq/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
Copyright (c) 2012, Jason Moiron
|
||||
|
||||
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.
|
||||
|
83
Godeps/_workspace/src/github.com/jmoiron/jsonq/README.md
generated
vendored
Normal file
83
Godeps/_workspace/src/github.com/jmoiron/jsonq/README.md
generated
vendored
Normal file
|
@ -0,0 +1,83 @@
|
|||
# jsonq
|
||||
|
||||
[![Build Status](https://drone.io/github.com/jmoiron/jsonq/status.png)](https://drone.io/github.com/jmoiron/jsonq/latest) [![Godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/jmoiron/jsonq) [![license](http://img.shields.io/badge/license-MIT-red.svg?style=flat)](https://raw.githubusercontent.com/jmoiron/jsonq/master/LICENSE)
|
||||
|
||||
|
||||
Simplify your golang json usage by extracting fields or items from arrays and objects with a simple, hierarchical query. [API Documentation](http://godoc.org/github.com/jmoiron/jsonq) on godoc.org.
|
||||
|
||||
This package is meant to make working with complex feeds a bit more easy. If you have simple feeds you want to model with struct types, check out [jflect](http://github.com/str1ngs/jflect), which will create struct definitions given a json document.
|
||||
|
||||
# installing
|
||||
|
||||
```
|
||||
go get github.com/jmoiron/jsonq
|
||||
```
|
||||
|
||||
# usage
|
||||
|
||||
Given some json data like:
|
||||
|
||||
```javascript
|
||||
{
|
||||
"foo": 1,
|
||||
"bar": 2,
|
||||
"test": "Hello, world!",
|
||||
"baz": 123.1,
|
||||
"array": [
|
||||
{"foo": 1},
|
||||
{"bar": 2},
|
||||
{"baz": 3}
|
||||
],
|
||||
"subobj": {
|
||||
"foo": 1,
|
||||
"subarray": [1,2,3],
|
||||
"subsubobj": {
|
||||
"bar": 2,
|
||||
"baz": 3,
|
||||
"array": ["hello", "world"]
|
||||
}
|
||||
},
|
||||
"bool": true
|
||||
}
|
||||
```
|
||||
|
||||
Decode it into a `map[string]interface{}`:
|
||||
|
||||
```go
|
||||
import (
|
||||
"strings"
|
||||
"encoding/json"
|
||||
"github.com/jmoiron/jsonq"
|
||||
)
|
||||
|
||||
data := map[string]interface{}{}
|
||||
dec := json.NewDecoder(strings.NewReader(jsonstring))
|
||||
dec.Decode(&data)
|
||||
jq := jsonq.NewQuery(data)
|
||||
```
|
||||
|
||||
From here, you can query along different keys and indexes:
|
||||
|
||||
```go
|
||||
// data["foo"] -> 1
|
||||
jq.Int("foo")
|
||||
|
||||
// data["subobj"]["subarray"][1] -> 2
|
||||
jq.Int("subobj", "subarray", "1")
|
||||
|
||||
// data["subobj"]["subarray"]["array"][0] -> "hello"
|
||||
jq.String("subobj", "subsubobj", "array", "0")
|
||||
|
||||
// data["subobj"] -> map[string]interface{}{"subobj": ...}
|
||||
obj, err := jq.Object("subobj")
|
||||
```
|
||||
|
||||
Missing keys, out of bounds indexes, and type failures will return errors.
|
||||
For simplicity, integer keys (ie, {"0": "zero"}) are inaccessible
|
||||
by `jsonq` as integer strings are assumed to be array indexes.
|
||||
|
||||
The `Int` and `Float` methods will attempt to parse numbers from string
|
||||
values to ease the use of many real world feeds which deliver numbers as strings.
|
||||
|
||||
Suggestions/comments please tweet [@jmoiron](http://twitter.com/jmoiron)
|
||||
|
13
Godeps/_workspace/src/github.com/jmoiron/jsonq/autotest.sh
generated
vendored
Normal file
13
Godeps/_workspace/src/github.com/jmoiron/jsonq/autotest.sh
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
#!/bin/bash
|
||||
|
||||
cur=`pwd`
|
||||
|
||||
inotifywait -mqr --timefmt '%d/%m/%y %H:%M' --format '%T %w %f' \
|
||||
-e modify ./ | while read date time dir file; do
|
||||
ext="${file##*.}"
|
||||
if [[ "$ext" = "go" ]]; then
|
||||
echo "$file changed @ $time $date, rebuilding..."
|
||||
go test
|
||||
fi
|
||||
done
|
||||
|
68
Godeps/_workspace/src/github.com/jmoiron/jsonq/doc.go
generated
vendored
Normal file
68
Godeps/_workspace/src/github.com/jmoiron/jsonq/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
Simplify your golang json usage with a simple hierarchical query.
|
||||
|
||||
Installing
|
||||
|
||||
go get github.com/jmoiron/jsonq
|
||||
|
||||
Examples
|
||||
|
||||
Given some json data like:
|
||||
|
||||
{
|
||||
"foo": 1,
|
||||
"bar": 2,
|
||||
"test": "Hello, world!",
|
||||
"baz": 123.1,
|
||||
"array": [
|
||||
{"foo": 1},
|
||||
{"bar": 2},
|
||||
{"baz": 3}
|
||||
],
|
||||
"subobj": {
|
||||
"foo": 1,
|
||||
"subarray": [1,2,3],
|
||||
"subsubobj": {
|
||||
"bar": 2,
|
||||
"baz": 3,
|
||||
"array": ["hello", "world"]
|
||||
}
|
||||
},
|
||||
"bool": true
|
||||
}
|
||||
|
||||
Decode it into a map[string]interrface{}:
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"encoding/json"
|
||||
"github.com/jmoiron/jsonq"
|
||||
)
|
||||
|
||||
data := map[string]interface{}{}
|
||||
dec := json.NewDecoder(strings.NewReader(jsonstring))
|
||||
dec.Decode(&data)
|
||||
jq := jsonq.NewQuery(data)
|
||||
|
||||
From here, you can query along different keys and indexes:
|
||||
|
||||
// data["foo"] -> 1
|
||||
jq.Int("foo")
|
||||
|
||||
// data["subobj"]["subarray"][1] -> 2
|
||||
jq.Int("subobj", "subarray", "1")
|
||||
|
||||
// data["subobj"]["subarray"]["array"][0] -> "hello"
|
||||
jq.String("subobj", "subsubobj", "array", "0")
|
||||
|
||||
// data["subobj"] -> map[string]interface{}{"subobj": ...}
|
||||
obj, err := jq.Object("subobj")
|
||||
|
||||
Notes
|
||||
|
||||
Missing keys, out of bounds indexes, and type failures will return errors.
|
||||
For simplicity, integer keys (ie, {"0": "zero"}) are inaccessible by `jsonq`
|
||||
as integer strings are assumed to be array indexes.
|
||||
|
||||
*/
|
||||
package jsonq
|
311
Godeps/_workspace/src/github.com/jmoiron/jsonq/jsonq.go
generated
vendored
Normal file
311
Godeps/_workspace/src/github.com/jmoiron/jsonq/jsonq.go
generated
vendored
Normal file
|
@ -0,0 +1,311 @@
|
|||
package jsonq
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type JsonQuery struct {
|
||||
blob map[string]interface{}
|
||||
}
|
||||
|
||||
/*
|
||||
The following methods are identical to the routines that were originally embedded in the realted query methods.
|
||||
They are seperated out here to keep the code as dry as possible.
|
||||
*/
|
||||
|
||||
//stringFromInterface converts an interface{} to a string and returns an error if types don't match.
|
||||
func stringFromInterface(val interface{}) (string, error) {
|
||||
switch val.(type) {
|
||||
case string:
|
||||
return val.(string), nil
|
||||
}
|
||||
return "", fmt.Errorf("Expected string value for String, got \"%v\"\n", val)
|
||||
}
|
||||
|
||||
//boolFromInterface converts an interface{} to a bool and returns an error if types don't match.
|
||||
func boolFromInterface(val interface{}) (bool, error) {
|
||||
switch val.(type) {
|
||||
case bool:
|
||||
return val.(bool), nil
|
||||
}
|
||||
return false, fmt.Errorf("Expected boolean value for Bool, got \"%v\"\n", val)
|
||||
}
|
||||
|
||||
//floatFromInterface converts an interface{} to a float64 and returns an error if types don't match.
|
||||
func floatFromInterface(val interface{}) (float64, error) {
|
||||
switch val.(type) {
|
||||
case float64:
|
||||
return val.(float64), nil
|
||||
case int:
|
||||
return float64(val.(int)), nil
|
||||
case string:
|
||||
fval, err := strconv.ParseFloat(val.(string), 64)
|
||||
if err == nil {
|
||||
return fval, nil
|
||||
}
|
||||
}
|
||||
return 0.0, fmt.Errorf("Expected numeric value for Float, got \"%v\"\n", val)
|
||||
}
|
||||
|
||||
//intFromInterface converts an interface{} to an int and returns an error if types don't match.
|
||||
func intFromInterface(val interface{}) (int, error) {
|
||||
switch val.(type) {
|
||||
case float64:
|
||||
return int(val.(float64)), nil
|
||||
case string:
|
||||
ival, err := strconv.ParseFloat(val.(string), 64)
|
||||
if err == nil {
|
||||
return int(ival), nil
|
||||
}
|
||||
case int:
|
||||
return val.(int), nil
|
||||
}
|
||||
return 0, fmt.Errorf("Expected numeric value for Int, got \"%v\"\n", val)
|
||||
}
|
||||
|
||||
//objectFromInterface converts an interface{} to a map[string]interface{} and returns an error if types don't match.
|
||||
func objectFromInterface(val interface{}) (map[string]interface{}, error) {
|
||||
switch val.(type) {
|
||||
case map[string]interface{}:
|
||||
return val.(map[string]interface{}), nil
|
||||
}
|
||||
return map[string]interface{}{}, fmt.Errorf("Expected json object for Object, got \"%v\"\n", val)
|
||||
}
|
||||
|
||||
//arrayFromInterface converts an interface{} to an []interface{} and returns an error if types don't match.
|
||||
func arrayFromInterface(val interface{}) ([]interface{}, error) {
|
||||
switch val.(type) {
|
||||
case []interface{}:
|
||||
return val.([]interface{}), nil
|
||||
}
|
||||
return []interface{}{}, fmt.Errorf("Expected json array for Array, got \"%v\"\n", val)
|
||||
}
|
||||
|
||||
// Create a new JsonQuery obj from a json-decoded interface{}
|
||||
func NewQuery(data interface{}) *JsonQuery {
|
||||
j := new(JsonQuery)
|
||||
j.blob = data.(map[string]interface{})
|
||||
return j
|
||||
}
|
||||
|
||||
// Extract a Bool from some json
|
||||
func (j *JsonQuery) Bool(s ...string) (bool, error) {
|
||||
val, err := rquery(j.blob, s...)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return boolFromInterface(val)
|
||||
}
|
||||
|
||||
// Extract a float from some json
|
||||
func (j *JsonQuery) Float(s ...string) (float64, error) {
|
||||
val, err := rquery(j.blob, s...)
|
||||
if err != nil {
|
||||
return 0.0, err
|
||||
}
|
||||
return floatFromInterface(val)
|
||||
}
|
||||
|
||||
// Extract an int from some json
|
||||
func (j *JsonQuery) Int(s ...string) (int, error) {
|
||||
val, err := rquery(j.blob, s...)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return intFromInterface(val)
|
||||
}
|
||||
|
||||
// Extract a string from some json
|
||||
func (j *JsonQuery) String(s ...string) (string, error) {
|
||||
val, err := rquery(j.blob, s...)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return stringFromInterface(val)
|
||||
}
|
||||
|
||||
// Extract an object from some json
|
||||
func (j *JsonQuery) Object(s ...string) (map[string]interface{}, error) {
|
||||
val, err := rquery(j.blob, s...)
|
||||
if err != nil {
|
||||
return map[string]interface{}{}, err
|
||||
}
|
||||
return objectFromInterface(val)
|
||||
}
|
||||
|
||||
// Extract an array from some json
|
||||
func (j *JsonQuery) Array(s ...string) ([]interface{}, error) {
|
||||
val, err := rquery(j.blob, s...)
|
||||
if err != nil {
|
||||
return []interface{}{}, err
|
||||
}
|
||||
return arrayFromInterface(val)
|
||||
}
|
||||
|
||||
// Extract interface from some json
|
||||
func (j *JsonQuery) Interface(s ...string) (interface{}, error) {
|
||||
val, err := rquery(j.blob, s...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
/*
|
||||
Extract typed slices.
|
||||
*/
|
||||
|
||||
//ArrayOfStrings extracts an array of strings from some json
|
||||
func (j *JsonQuery) ArrayOfStrings(s ...string) ([]string, error) {
|
||||
array, err := j.Array(s...)
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
toReturn := make([]string, len(array))
|
||||
for index, val := range array {
|
||||
toReturn[index], err = stringFromInterface(val)
|
||||
if err != nil {
|
||||
return toReturn, err
|
||||
}
|
||||
}
|
||||
return toReturn, nil
|
||||
}
|
||||
|
||||
//ArrayOfInts extracts an array of ints from some json
|
||||
func (j *JsonQuery) ArrayOfInts(s ...string) ([]int, error) {
|
||||
array, err := j.Array(s...)
|
||||
if err != nil {
|
||||
return []int{}, err
|
||||
}
|
||||
toReturn := make([]int, len(array))
|
||||
for index, val := range array {
|
||||
toReturn[index], err = intFromInterface(val)
|
||||
if err != nil {
|
||||
return toReturn, err
|
||||
}
|
||||
}
|
||||
return toReturn, nil
|
||||
}
|
||||
|
||||
//ArrayOfFloats extracts an array of float64s from some json
|
||||
func (j *JsonQuery) ArrayOfFloats(s ...string) ([]float64, error) {
|
||||
array, err := j.Array(s...)
|
||||
if err != nil {
|
||||
return []float64{}, err
|
||||
}
|
||||
toReturn := make([]float64, len(array))
|
||||
for index, val := range array {
|
||||
toReturn[index], err = floatFromInterface(val)
|
||||
if err != nil {
|
||||
return toReturn, err
|
||||
}
|
||||
}
|
||||
return toReturn, nil
|
||||
}
|
||||
|
||||
//ArrayOfBools extracts an array of bools from some json
|
||||
func (j *JsonQuery) ArrayOfBools(s ...string) ([]bool, error) {
|
||||
array, err := j.Array(s...)
|
||||
if err != nil {
|
||||
return []bool{}, err
|
||||
}
|
||||
toReturn := make([]bool, len(array))
|
||||
for index, val := range array {
|
||||
toReturn[index], err = boolFromInterface(val)
|
||||
if err != nil {
|
||||
return toReturn, err
|
||||
}
|
||||
}
|
||||
return toReturn, nil
|
||||
}
|
||||
|
||||
//ArrayOfObjects extracts an array of map[string]interface{} (objects) from some json
|
||||
func (j *JsonQuery) ArrayOfObjects(s ...string) ([]map[string]interface{}, error) {
|
||||
array, err := j.Array(s...)
|
||||
if err != nil {
|
||||
return []map[string]interface{}{}, err
|
||||
}
|
||||
toReturn := make([]map[string]interface{}, len(array))
|
||||
for index, val := range array {
|
||||
toReturn[index], err = objectFromInterface(val)
|
||||
if err != nil {
|
||||
return toReturn, err
|
||||
}
|
||||
}
|
||||
return toReturn, nil
|
||||
}
|
||||
|
||||
//ArrayOfArrays extracts an array of []interface{} (arrays) from some json
|
||||
func (j *JsonQuery) ArrayOfArrays(s ...string) ([][]interface{}, error) {
|
||||
array, err := j.Array(s...)
|
||||
if err != nil {
|
||||
return [][]interface{}{}, err
|
||||
}
|
||||
toReturn := make([][]interface{}, len(array))
|
||||
for index, val := range array {
|
||||
toReturn[index], err = arrayFromInterface(val)
|
||||
if err != nil {
|
||||
return toReturn, err
|
||||
}
|
||||
}
|
||||
return toReturn, nil
|
||||
}
|
||||
|
||||
//Matrix2D is an alias for ArrayOfArrays
|
||||
func (j *JsonQuery) Matrix2D(s ...string) ([][]interface{}, error) {
|
||||
return j.ArrayOfArrays(s...)
|
||||
}
|
||||
|
||||
// Recursively query a decoded json blob
|
||||
func rquery(blob interface{}, s ...string) (interface{}, error) {
|
||||
var (
|
||||
val interface{}
|
||||
err error
|
||||
)
|
||||
val = blob
|
||||
for _, q := range s {
|
||||
val, err = query(val, q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
switch val.(type) {
|
||||
case nil:
|
||||
return nil, fmt.Errorf("Nil value found at %s\n", s[len(s)-1])
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
// Query a json blob for a single field or index. If query is a string, then
|
||||
// the blob is treated as a json object (map[string]interface{}). If query is
|
||||
// an integer, the blob is treated as a json array ([]interface{}). Any kind
|
||||
// of key or index error will result in a nil return value with an error set.
|
||||
func query(blob interface{}, query string) (interface{}, error) {
|
||||
index, err := strconv.Atoi(query)
|
||||
// if it's an integer, then we treat the current interface as an array
|
||||
if err == nil {
|
||||
switch blob.(type) {
|
||||
case []interface{}:
|
||||
default:
|
||||
return nil, fmt.Errorf("Array index on non-array %v\n", blob)
|
||||
}
|
||||
if len(blob.([]interface{})) > index {
|
||||
return blob.([]interface{})[index], nil
|
||||
}
|
||||
return nil, fmt.Errorf("Array index %d on array %v out of bounds\n", index, blob)
|
||||
}
|
||||
|
||||
// blob is likely an object, but verify first
|
||||
switch blob.(type) {
|
||||
case map[string]interface{}:
|
||||
default:
|
||||
return nil, fmt.Errorf("Object lookup \"%s\" on non-object %v\n", query, blob)
|
||||
}
|
||||
|
||||
val, ok := blob.(map[string]interface{})[query]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Object %v does not contain field %s\n", blob, query)
|
||||
}
|
||||
return val, nil
|
||||
}
|
186
Godeps/_workspace/src/github.com/jmoiron/jsonq/jsonq_test.go
generated
vendored
Normal file
186
Godeps/_workspace/src/github.com/jmoiron/jsonq/jsonq_test.go
generated
vendored
Normal file
|
@ -0,0 +1,186 @@
|
|||
package jsonq
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const TestData = `{
|
||||
"foo": 1,
|
||||
"bar": 2,
|
||||
"test": "Hello, world!",
|
||||
"baz": 123.1,
|
||||
"numstring": "42",
|
||||
"floatstring": "42.1",
|
||||
"array": [
|
||||
{"foo": 1},
|
||||
{"bar": 2},
|
||||
{"baz": 3}
|
||||
],
|
||||
"subobj": {
|
||||
"foo": 1,
|
||||
"subarray": [1,2,3],
|
||||
"subsubobj": {
|
||||
"bar": 2,
|
||||
"baz": 3,
|
||||
"array": ["hello", "world"]
|
||||
}
|
||||
},
|
||||
"collections": {
|
||||
"bools": [false, true, false],
|
||||
"strings": ["hello", "strings"],
|
||||
"numbers": [1,2,3,4],
|
||||
"arrays": [[1.0,2.0],[2.0,3.0],[4.0,3.0]],
|
||||
"objects": [
|
||||
{"obj1": 1},
|
||||
{"obj2": 2}
|
||||
]
|
||||
},
|
||||
"bool": true
|
||||
}`
|
||||
|
||||
func tErr(t *testing.T, err error) {
|
||||
if err != nil {
|
||||
t.Errorf("Error: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestQuery(t *testing.T) {
|
||||
data := map[string]interface{}{}
|
||||
dec := json.NewDecoder(strings.NewReader(TestData))
|
||||
err := dec.Decode(&data)
|
||||
tErr(t, err)
|
||||
q := NewQuery(data)
|
||||
|
||||
ival, err := q.Int("foo")
|
||||
if ival != 1 {
|
||||
t.Errorf("Expecting 1, got %v\n", ival)
|
||||
}
|
||||
tErr(t, err)
|
||||
ival, err = q.Int("bar")
|
||||
if ival != 2 {
|
||||
t.Errorf("Expecting 2, got %v\n", ival)
|
||||
}
|
||||
tErr(t, err)
|
||||
|
||||
ival, err = q.Int("subobj", "foo")
|
||||
if ival != 1 {
|
||||
t.Errorf("Expecting 1, got %v\n", ival)
|
||||
}
|
||||
tErr(t, err)
|
||||
|
||||
// test that strings can get int-ed
|
||||
ival, err = q.Int("numstring")
|
||||
if ival != 42 {
|
||||
t.Errorf("Expecting 42, got %v\n", ival)
|
||||
}
|
||||
tErr(t, err)
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
ival, err := q.Int("subobj", "subarray", fmt.Sprintf("%d", i))
|
||||
if ival != i+1 {
|
||||
t.Errorf("Expecting %d, got %v\n", i+1, ival)
|
||||
}
|
||||
tErr(t, err)
|
||||
}
|
||||
|
||||
fval, err := q.Float("baz")
|
||||
if fval != 123.1 {
|
||||
t.Errorf("Expecting 123.1, got %f\n", fval)
|
||||
}
|
||||
tErr(t, err)
|
||||
|
||||
// test that strings can get float-ed
|
||||
fval, err = q.Float("floatstring")
|
||||
if fval != 42.1 {
|
||||
t.Errorf("Expecting 42.1, got %v\n", fval)
|
||||
}
|
||||
tErr(t, err)
|
||||
|
||||
sval, err := q.String("test")
|
||||
if sval != "Hello, world!" {
|
||||
t.Errorf("Expecting \"Hello, World!\", got \"%v\"\n", sval)
|
||||
}
|
||||
|
||||
sval, err = q.String("subobj", "subsubobj", "array", "0")
|
||||
if sval != "hello" {
|
||||
t.Errorf("Expecting \"hello\", got \"%s\"\n", sval)
|
||||
}
|
||||
tErr(t, err)
|
||||
|
||||
bval, err := q.Bool("bool")
|
||||
if !bval {
|
||||
t.Errorf("Expecting true, got %v\n", bval)
|
||||
}
|
||||
tErr(t, err)
|
||||
|
||||
obj, err := q.Object("subobj", "subsubobj")
|
||||
tErr(t, err)
|
||||
q2 := NewQuery(obj)
|
||||
sval, err = q2.String("array", "1")
|
||||
if sval != "world" {
|
||||
t.Errorf("Expecting \"world\", got \"%s\"\n", sval)
|
||||
}
|
||||
tErr(t, err)
|
||||
|
||||
aobj, err := q.Array("subobj", "subarray")
|
||||
tErr(t, err)
|
||||
if aobj[0].(float64) != 1 {
|
||||
t.Errorf("Expecting 1, got %v\n", aobj[0])
|
||||
}
|
||||
|
||||
iobj, err := q.Interface("numstring")
|
||||
tErr(t, err)
|
||||
if _, ok := iobj.(string); !ok {
|
||||
t.Errorf("Expecting type string got: %s", iobj)
|
||||
}
|
||||
|
||||
/*
|
||||
Test Extraction of typed slices
|
||||
*/
|
||||
|
||||
//test array of strings
|
||||
astrings, err := q.ArrayOfStrings("collections", "strings")
|
||||
tErr(t, err)
|
||||
if astrings[0] != "hello" {
|
||||
t.Errorf("Expecting hello, got %v\n", astrings[0])
|
||||
}
|
||||
|
||||
//test array of ints
|
||||
aints, err := q.ArrayOfInts("collections", "numbers")
|
||||
tErr(t, err)
|
||||
if aints[0] != 1 {
|
||||
t.Errorf("Expecting 1, got %v\n", aints[0])
|
||||
}
|
||||
|
||||
//test array of floats
|
||||
afloats, err := q.ArrayOfFloats("collections", "numbers")
|
||||
tErr(t, err)
|
||||
if afloats[0] != 1.0 {
|
||||
t.Errorf("Expecting 1.0, got %v\n", afloats[0])
|
||||
}
|
||||
|
||||
//test array of bools
|
||||
abools, err := q.ArrayOfBools("collections", "bools")
|
||||
tErr(t, err)
|
||||
if abools[0] {
|
||||
t.Errorf("Expecting true, got %v\n", abools[0])
|
||||
}
|
||||
|
||||
//test array of arrays
|
||||
aa, err := q.ArrayOfArrays("collections", "arrays")
|
||||
tErr(t, err)
|
||||
if aa[0][0].(float64) != 1 {
|
||||
t.Errorf("Expecting 1, got %v\n", aa[0][0])
|
||||
}
|
||||
|
||||
//test array of objs
|
||||
aobjs, err := q.ArrayOfObjects("collections", "objects")
|
||||
tErr(t, err)
|
||||
if aobjs[0]["obj1"].(float64) != 1 {
|
||||
t.Errorf("Expecting 1, got %v\n", aobjs[0]["obj1"])
|
||||
}
|
||||
|
||||
}
|
19
Godeps/_workspace/src/github.com/layeh/gopus/LICENSE
generated
vendored
Normal file
19
Godeps/_workspace/src/github.com/layeh/gopus/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2014 Tim Cooper
|
||||
|
||||
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.
|
20
Godeps/_workspace/src/github.com/layeh/gopus/README.md
generated
vendored
Normal file
20
Godeps/_workspace/src/github.com/layeh/gopus/README.md
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
# gopus
|
||||
|
||||
gopus is a Go binding for the [Opus](http://www.opus-codec.org/) audio codec.
|
||||
|
||||
## Documentation
|
||||
|
||||
- [API Reference](https://godoc.org/github.com/layeh/gopus)
|
||||
|
||||
## Requirements
|
||||
|
||||
- cgo
|
||||
- [opus](http://www.opus-codec.org/)
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
## Author
|
||||
|
||||
Tim Cooper (<tim.cooper@layeh.com>)
|
71
Godeps/_workspace/src/github.com/layeh/gopus/decoder.go
generated
vendored
Normal file
71
Godeps/_workspace/src/github.com/layeh/gopus/decoder.go
generated
vendored
Normal file
|
@ -0,0 +1,71 @@
|
|||
package gopus
|
||||
|
||||
// #cgo !nopkgconfig pkg-config: opus
|
||||
// #include <opus.h>
|
||||
//
|
||||
// void gopus_decoder_resetstate(OpusDecoder *decoder) {
|
||||
// opus_decoder_ctl(decoder, OPUS_RESET_STATE);
|
||||
// }
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type Decoder struct {
|
||||
data []byte
|
||||
cDecoder *C.struct_OpusDecoder
|
||||
channels int
|
||||
}
|
||||
|
||||
func NewDecoder(sampleRate, channels int) (*Decoder, error) {
|
||||
decoder := &Decoder{}
|
||||
decoder.data = make([]byte, int(C.opus_decoder_get_size(C.int(channels))))
|
||||
decoder.cDecoder = (*C.struct_OpusDecoder)(unsafe.Pointer(&decoder.data[0]))
|
||||
|
||||
ret := C.opus_decoder_init(decoder.cDecoder, C.opus_int32(sampleRate), C.int(channels))
|
||||
if err := getErr(ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decoder.channels = channels
|
||||
|
||||
return decoder, nil
|
||||
}
|
||||
|
||||
func (d *Decoder) Decode(data []byte, frameSize int, fec bool) ([]int16, error) {
|
||||
dataPtr := (*C.uchar)(unsafe.Pointer(&data[0]))
|
||||
dataLen := C.opus_int32(len(data))
|
||||
|
||||
output := make([]int16, d.channels * frameSize)
|
||||
outputPtr := (*C.opus_int16)(unsafe.Pointer(&output[0]))
|
||||
|
||||
var cFec C.int
|
||||
if fec {
|
||||
cFec = 1
|
||||
} else {
|
||||
cFec = 0
|
||||
}
|
||||
|
||||
cRet := C.opus_decode(d.cDecoder, dataPtr, dataLen, outputPtr, C.int(frameSize), cFec)
|
||||
ret := int(cRet)
|
||||
|
||||
if ret < 0 {
|
||||
return nil, getErr(cRet)
|
||||
}
|
||||
return output[:ret], nil
|
||||
}
|
||||
|
||||
func (d *Decoder) ResetState() {
|
||||
C.gopus_decoder_resetstate(d.cDecoder)
|
||||
}
|
||||
|
||||
func CountFrames(data []byte) (int, error) {
|
||||
dataPtr := (*C.uchar)(unsafe.Pointer(&data[0]))
|
||||
cLen := C.opus_int32(len(data))
|
||||
|
||||
cRet := C.opus_packet_get_nb_frames(dataPtr, cLen)
|
||||
if err := getErr(cRet); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(cRet), nil
|
||||
}
|
119
Godeps/_workspace/src/github.com/layeh/gopus/encoder.go
generated
vendored
Normal file
119
Godeps/_workspace/src/github.com/layeh/gopus/encoder.go
generated
vendored
Normal file
|
@ -0,0 +1,119 @@
|
|||
package gopus
|
||||
|
||||
// #cgo !nopkgconfig pkg-config: opus
|
||||
// #include <opus.h>
|
||||
//
|
||||
// enum {
|
||||
// gopus_application_voip = OPUS_APPLICATION_VOIP,
|
||||
// gopus_application_audio = OPUS_APPLICATION_AUDIO,
|
||||
// gopus_restricted_lowdelay = OPUS_APPLICATION_RESTRICTED_LOWDELAY,
|
||||
// gopus_bitrate_max = OPUS_BITRATE_MAX,
|
||||
// };
|
||||
//
|
||||
//
|
||||
// void gopus_setvbr(OpusEncoder *encoder, int vbr) {
|
||||
// opus_encoder_ctl(encoder, OPUS_SET_VBR(vbr));
|
||||
// }
|
||||
//
|
||||
// void gopus_setbitrate(OpusEncoder *encoder, int bitrate) {
|
||||
// opus_encoder_ctl(encoder, OPUS_SET_BITRATE(bitrate));
|
||||
// }
|
||||
//
|
||||
// opus_int32 gopus_bitrate(OpusEncoder *encoder) {
|
||||
// opus_int32 bitrate;
|
||||
// opus_encoder_ctl(encoder, OPUS_GET_BITRATE(&bitrate));
|
||||
// return bitrate;
|
||||
// }
|
||||
//
|
||||
// void gopus_setapplication(OpusEncoder *encoder, int application) {
|
||||
// opus_encoder_ctl(encoder, OPUS_SET_APPLICATION(application));
|
||||
// }
|
||||
//
|
||||
// opus_int32 gopus_application(OpusEncoder *encoder) {
|
||||
// opus_int32 application;
|
||||
// opus_encoder_ctl(encoder, OPUS_GET_APPLICATION(&application));
|
||||
// return application;
|
||||
// }
|
||||
//
|
||||
// void gopus_encoder_resetstate(OpusEncoder *encoder) {
|
||||
// opus_encoder_ctl(encoder, OPUS_RESET_STATE);
|
||||
// }
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type Application int
|
||||
|
||||
const (
|
||||
Voip Application = C.gopus_application_voip
|
||||
Audio Application = C.gopus_application_audio
|
||||
RestrictedLowDelay Application = C.gopus_restricted_lowdelay
|
||||
)
|
||||
|
||||
const (
|
||||
BitrateMaximum = C.gopus_bitrate_max
|
||||
)
|
||||
|
||||
type Encoder struct {
|
||||
data []byte
|
||||
cEncoder *C.struct_OpusEncoder
|
||||
}
|
||||
|
||||
func NewEncoder(sampleRate, channels int, application Application) (*Encoder, error) {
|
||||
encoder := &Encoder{}
|
||||
encoder.data = make([]byte, int(C.opus_encoder_get_size(C.int(channels))))
|
||||
encoder.cEncoder = (*C.struct_OpusEncoder)(unsafe.Pointer(&encoder.data[0]))
|
||||
|
||||
ret := C.opus_encoder_init(encoder.cEncoder, C.opus_int32(sampleRate), C.int(channels), C.int(application))
|
||||
if err := getErr(ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return encoder, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) Encode(pcm []int16, frameSize, maxDataBytes int) ([]byte, error) {
|
||||
pcmPtr := (*C.opus_int16)(unsafe.Pointer(&pcm[0]))
|
||||
|
||||
data := make([]byte, maxDataBytes)
|
||||
dataPtr := (*C.uchar)(unsafe.Pointer(&data[0]))
|
||||
|
||||
encodedC := C.opus_encode(e.cEncoder, pcmPtr, C.int(frameSize), dataPtr, C.opus_int32(len(data)))
|
||||
encoded := int(encodedC)
|
||||
|
||||
if encoded < 0 {
|
||||
return nil, getErr(C.int(encodedC))
|
||||
}
|
||||
return data[0:encoded], nil
|
||||
}
|
||||
|
||||
func (e *Encoder) SetVbr(vbr bool) {
|
||||
var cVbr C.int
|
||||
if vbr {
|
||||
cVbr = 1
|
||||
} else {
|
||||
cVbr = 0
|
||||
}
|
||||
C.gopus_setvbr(e.cEncoder, cVbr)
|
||||
}
|
||||
|
||||
func (e *Encoder) SetBitrate(bitrate int) {
|
||||
C.gopus_setbitrate(e.cEncoder, C.int(bitrate))
|
||||
}
|
||||
|
||||
func (e *Encoder) Bitrate() int {
|
||||
return int(C.gopus_bitrate(e.cEncoder))
|
||||
}
|
||||
|
||||
func (e *Encoder) SetApplication(application Application) {
|
||||
C.gopus_setapplication(e.cEncoder, C.int(application))
|
||||
}
|
||||
|
||||
func (e *Encoder) Application() Application {
|
||||
return Application(C.gopus_application(e.cEncoder))
|
||||
}
|
||||
|
||||
func (e *Encoder) ResetState() {
|
||||
C.gopus_encoder_resetstate(e.cEncoder)
|
||||
}
|
53
Godeps/_workspace/src/github.com/layeh/gopus/gopus.go
generated
vendored
Normal file
53
Godeps/_workspace/src/github.com/layeh/gopus/gopus.go
generated
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
package gopus
|
||||
|
||||
// #include <opus.h>
|
||||
//
|
||||
// enum {
|
||||
// gopus_ok = OPUS_OK,
|
||||
// gopus_bad_arg = OPUS_BAD_ARG,
|
||||
// gopus_small_buffer = OPUS_BUFFER_TOO_SMALL,
|
||||
// gopus_internal = OPUS_INTERNAL_ERROR,
|
||||
// gopus_invalid_packet = OPUS_INVALID_PACKET,
|
||||
// gopus_unimplemented = OPUS_UNIMPLEMENTED,
|
||||
// gopus_invalid_state = OPUS_INVALID_STATE,
|
||||
// gopus_alloc_fail = OPUS_ALLOC_FAIL,
|
||||
// };
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrBadArgument = errors.New("bad argument")
|
||||
ErrSmallBuffer = errors.New("buffer is too small")
|
||||
ErrInternal = errors.New("internal error")
|
||||
ErrInvalidPacket = errors.New("invalid packet")
|
||||
ErrUnimplemented = errors.New("unimplemented")
|
||||
ErrInvalidState = errors.New("invalid state")
|
||||
ErrAllocFail = errors.New("allocation failed")
|
||||
ErrUnknown = errors.New("unknown error")
|
||||
)
|
||||
|
||||
func getErr(code C.int) error {
|
||||
switch code {
|
||||
case C.gopus_ok:
|
||||
return nil
|
||||
case C.gopus_bad_arg:
|
||||
return ErrBadArgument
|
||||
case C.gopus_small_buffer:
|
||||
return ErrSmallBuffer
|
||||
case C.gopus_internal:
|
||||
return ErrInternal
|
||||
case C.gopus_invalid_packet:
|
||||
return ErrInvalidPacket
|
||||
case C.gopus_unimplemented:
|
||||
return ErrUnimplemented
|
||||
case C.gopus_invalid_state:
|
||||
return ErrInvalidState
|
||||
case C.gopus_alloc_fail:
|
||||
return ErrAllocFail
|
||||
default:
|
||||
return ErrUnknown
|
||||
}
|
||||
}
|
37
Godeps/_workspace/src/github.com/layeh/gumble/gumble/MumbleProto/LICENSE
generated
vendored
Normal file
37
Godeps/_workspace/src/github.com/layeh/gumble/gumble/MumbleProto/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
Copyright (C) 2005-2013, Thorvald Natvig <thorvald@natvig.com>
|
||||
Copyright (C) 2007, Stefan Gehn <mETz AT gehn DOT net>
|
||||
Copyright (C) 2007, Sebastian Schlingmann <mit_service@users.sourceforge.net>
|
||||
Copyright (C) 2008-2013, Mikkel Krautz <mikkel@krautz.dk>
|
||||
Copyright (C) 2008, Andreas Messer <andi@bupfen.de>
|
||||
Copyright (C) 2007, Trenton Schulz
|
||||
Copyright (C) 2008-2013, Stefan Hacker <dd0t@users.sourceforge.net>
|
||||
Copyright (C) 2008-2011, Snares <snares@users.sourceforge.net>
|
||||
Copyright (C) 2009-2013, Benjamin Jemlich <pcgod@users.sourceforge.net>
|
||||
Copyright (C) 2009-2013, Kissaki <kissaki@gmx.de>
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
- Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
- Neither the name of the Mumble Developers nor the names of its
|
||||
contributors may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
`AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
2092
Godeps/_workspace/src/github.com/layeh/gumble/gumble/MumbleProto/Mumble.pb.go
generated
vendored
Normal file
2092
Godeps/_workspace/src/github.com/layeh/gumble/gumble/MumbleProto/Mumble.pb.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
544
Godeps/_workspace/src/github.com/layeh/gumble/gumble/MumbleProto/Mumble.proto
generated
vendored
Normal file
544
Godeps/_workspace/src/github.com/layeh/gumble/gumble/MumbleProto/Mumble.proto
generated
vendored
Normal file
|
@ -0,0 +1,544 @@
|
|||
package MumbleProto;
|
||||
|
||||
option optimize_for = SPEED;
|
||||
|
||||
message Version {
|
||||
// 2-byte Major, 1-byte Minor and 1-byte Patch version number.
|
||||
optional uint32 version = 1;
|
||||
// Client release name.
|
||||
optional string release = 2;
|
||||
// Client OS name.
|
||||
optional string os = 3;
|
||||
// Client OS version.
|
||||
optional string os_version = 4;
|
||||
}
|
||||
|
||||
// Not used. Not even for tunneling UDP through TCP.
|
||||
message UDPTunnel {
|
||||
// Not used.
|
||||
required bytes packet = 1;
|
||||
}
|
||||
|
||||
// Used by the client to send the authentication credentials to the server.
|
||||
message Authenticate {
|
||||
// UTF-8 encoded username.
|
||||
optional string username = 1;
|
||||
// Server or user password.
|
||||
optional string password = 2;
|
||||
// Additional access tokens for server ACL groups.
|
||||
repeated string tokens = 3;
|
||||
// A list of CELT bitstream version constants supported by the client.
|
||||
repeated int32 celt_versions = 4;
|
||||
optional bool opus = 5 [default = false];
|
||||
}
|
||||
|
||||
// Sent by the client to notify the server that the client is still alive.
|
||||
// Server must reply to the packet with the same timestamp and its own
|
||||
// good/late/lost/resync numbers. None of the fields is strictly required.
|
||||
message Ping {
|
||||
// Client timestamp. Server should not attempt to decode.
|
||||
optional uint64 timestamp = 1;
|
||||
// The amount of good packets received.
|
||||
optional uint32 good = 2;
|
||||
// The amount of late packets received.
|
||||
optional uint32 late = 3;
|
||||
// The amount of packets never received.
|
||||
optional uint32 lost = 4;
|
||||
// The amount of nonce resyncs.
|
||||
optional uint32 resync = 5;
|
||||
// The total amount of UDP packets received.
|
||||
optional uint32 udp_packets = 6;
|
||||
// The total amount of TCP packets received.
|
||||
optional uint32 tcp_packets = 7;
|
||||
// UDP ping average.
|
||||
optional float udp_ping_avg = 8;
|
||||
// UDP ping variance.
|
||||
optional float udp_ping_var = 9;
|
||||
// TCP ping average.
|
||||
optional float tcp_ping_avg = 10;
|
||||
// TCP ping variance.
|
||||
optional float tcp_ping_var = 11;
|
||||
}
|
||||
|
||||
// Sent by the server when it rejects the user connection.
|
||||
message Reject {
|
||||
enum RejectType {
|
||||
// TODO ??
|
||||
None = 0;
|
||||
// The client attempted to connect with an incompatible version.
|
||||
WrongVersion = 1;
|
||||
// The user name supplied by the client was invalid.
|
||||
InvalidUsername = 2;
|
||||
// The client attempted to authenticate as a user with a password but it
|
||||
// was wrong.
|
||||
WrongUserPW = 3;
|
||||
// The client attempted to connect to a passworded server but the password
|
||||
// was wrong.
|
||||
WrongServerPW = 4;
|
||||
// Supplied username is already in use.
|
||||
UsernameInUse = 5;
|
||||
// Server is currently full and cannot accept more users.
|
||||
ServerFull = 6;
|
||||
// The user did not provide a certificate but one is required.
|
||||
NoCertificate = 7;
|
||||
AuthenticatorFail = 8;
|
||||
}
|
||||
// Rejection type.
|
||||
optional RejectType type = 1;
|
||||
// Human readable rejection reason.
|
||||
optional string reason = 2;
|
||||
}
|
||||
|
||||
// ServerSync message is sent by the server when it has authenticated the user
|
||||
// and finished synchronizing the server state.
|
||||
message ServerSync {
|
||||
// The session of the current user.
|
||||
optional uint32 session = 1;
|
||||
// Maximum bandwidth that the user should use.
|
||||
optional uint32 max_bandwidth = 2;
|
||||
// Server welcome text.
|
||||
optional string welcome_text = 3;
|
||||
// Current user permissions TODO: Confirm??
|
||||
optional uint64 permissions = 4;
|
||||
}
|
||||
|
||||
// Sent by the client when it wants a channel removed. Sent by the server when
|
||||
// a channel has been removed and clients should be notified.
|
||||
message ChannelRemove {
|
||||
required uint32 channel_id = 1;
|
||||
}
|
||||
|
||||
// Used to communicate channel properties between the client and the server.
|
||||
// Sent by the server during the login process or when channel properties are
|
||||
// updated. Client may use this message to update said channel properties.
|
||||
message ChannelState {
|
||||
// Unique ID for the channel within the server.
|
||||
optional uint32 channel_id = 1;
|
||||
// channel_id of the parent channel.
|
||||
optional uint32 parent = 2;
|
||||
// UTF-8 encoded channel name.
|
||||
optional string name = 3;
|
||||
// A collection of channel id values of the linked channels. Absent during
|
||||
// the first channel listing.
|
||||
repeated uint32 links = 4;
|
||||
// UTF-8 encoded channel description. Only if the description is less than
|
||||
// 128 bytes
|
||||
optional string description = 5;
|
||||
// A collection of channel_id values that should be added to links.
|
||||
repeated uint32 links_add = 6;
|
||||
// A collection of channel_id values that should be removed from links.
|
||||
repeated uint32 links_remove = 7;
|
||||
// True if the channel is temporary.
|
||||
optional bool temporary = 8 [default = false];
|
||||
// Position weight to tweak the channel position in the channel list.
|
||||
optional int32 position = 9 [default = 0];
|
||||
// SHA1 hash of the description if the description is 128 bytes or more.
|
||||
optional bytes description_hash = 10;
|
||||
}
|
||||
|
||||
// Used to communicate user leaving or being kicked. May be sent by the client
|
||||
// when it attempts to kick a user. Sent by the server when it informs the
|
||||
// clients that a user is not present anymore.
|
||||
message UserRemove {
|
||||
// The user who is being kicked, identified by their session, not present
|
||||
// when no one is being kicked.
|
||||
required uint32 session = 1;
|
||||
// The user who initiated the removal. Either the user who performs the kick
|
||||
// or the user who is currently leaving.
|
||||
optional uint32 actor = 2;
|
||||
// Reason for the kick, stored as the ban reason if the user is banned.
|
||||
optional string reason = 3;
|
||||
// True if the kick should result in a ban.
|
||||
optional bool ban = 4;
|
||||
}
|
||||
|
||||
// Sent by the server when it communicates new and changed users to client.
|
||||
// First seen during login procedure. May be sent by the client when it wishes
|
||||
// to alter its state.
|
||||
message UserState {
|
||||
// Unique user session ID of the user whose state this is, may change on
|
||||
// reconnect.
|
||||
optional uint32 session = 1;
|
||||
// The session of the user who is updating this user.
|
||||
optional uint32 actor = 2;
|
||||
// User name, UTF-8 encoded.
|
||||
optional string name = 3;
|
||||
// Registered user ID if the user is registered.
|
||||
optional uint32 user_id = 4;
|
||||
// Channel on which the user is.
|
||||
optional uint32 channel_id = 5;
|
||||
// True if the user is muted by admin.
|
||||
optional bool mute = 6;
|
||||
// True if the user is deafened by admin.
|
||||
optional bool deaf = 7;
|
||||
// True if the user has been suppressed from talking by a reason other than
|
||||
// being muted.
|
||||
optional bool suppress = 8;
|
||||
// True if the user has muted self.
|
||||
optional bool self_mute = 9;
|
||||
// True if the user has deafened self.
|
||||
optional bool self_deaf = 10;
|
||||
// User image if it is less than 128 bytes.
|
||||
optional bytes texture = 11;
|
||||
// TODO ??
|
||||
optional bytes plugin_context = 12;
|
||||
// TODO ??
|
||||
optional string plugin_identity = 13;
|
||||
// User comment if it is less than 128 bytes.
|
||||
optional string comment = 14;
|
||||
// The hash of the user certificate.
|
||||
optional string hash = 15;
|
||||
// SHA1 hash of the user comment if it 128 bytes or more.
|
||||
optional bytes comment_hash = 16;
|
||||
// SHA1 hash of the user picture if it 128 bytes or more.
|
||||
optional bytes texture_hash = 17;
|
||||
// True if the user is a priority speaker.
|
||||
optional bool priority_speaker = 18;
|
||||
// True if the user is currently recording.
|
||||
optional bool recording = 19;
|
||||
}
|
||||
|
||||
// Relays information on the bans. The client may send the BanList message to
|
||||
// either modify the list of bans or query them from the server. The server
|
||||
// sends this list only after a client queries for it.
|
||||
message BanList {
|
||||
message BanEntry {
|
||||
// Banned IP address.
|
||||
required bytes address = 1;
|
||||
// The length of the subnet mask for the ban.
|
||||
required uint32 mask = 2;
|
||||
// User name for identification purposes (does not affect the ban).
|
||||
optional string name = 3;
|
||||
// TODO ??
|
||||
optional string hash = 4;
|
||||
// Reason for the ban (does not affect the ban).
|
||||
optional string reason = 5;
|
||||
// Ban start time.
|
||||
optional string start = 6;
|
||||
// Ban duration in seconds.
|
||||
optional uint32 duration = 7;
|
||||
}
|
||||
// List of ban entries currently in place.
|
||||
repeated BanEntry bans = 1;
|
||||
// True if the server should return the list, false if it should replace old
|
||||
// ban list with the one provided.
|
||||
optional bool query = 2 [default = false];
|
||||
}
|
||||
|
||||
// Used to send and broadcast text messages.
|
||||
message TextMessage {
|
||||
// The message sender, identified by its session.
|
||||
optional uint32 actor = 1;
|
||||
// Target users for the message, identified by their session.
|
||||
repeated uint32 session = 2;
|
||||
// The channels to which the message is sent, identified by their
|
||||
// channel_ids.
|
||||
repeated uint32 channel_id = 3;
|
||||
// The root channels when sending message recursively to several channels,
|
||||
// identified by their channel_ids.
|
||||
repeated uint32 tree_id = 4;
|
||||
// The UTF-8 encoded message. May be HTML if the server allows.
|
||||
required string message = 5;
|
||||
}
|
||||
|
||||
message PermissionDenied {
|
||||
enum DenyType {
|
||||
// Operation denied for other reason, see reason field.
|
||||
Text = 0;
|
||||
// Permissions were denied.
|
||||
Permission = 1;
|
||||
// Cannot modify SuperUser.
|
||||
SuperUser = 2;
|
||||
// Invalid channel name.
|
||||
ChannelName = 3;
|
||||
// Text message too long.
|
||||
TextTooLong = 4;
|
||||
// The flux capacitor was spelled wrong.
|
||||
H9K = 5;
|
||||
// Operation not permitted in temporary channel.
|
||||
TemporaryChannel = 6;
|
||||
// Operation requires certificate.
|
||||
MissingCertificate = 7;
|
||||
// Invalid username.
|
||||
UserName = 8;
|
||||
// Channel is full.
|
||||
ChannelFull = 9;
|
||||
NestingLimit = 10;
|
||||
}
|
||||
// The denied permission when type is Permission.
|
||||
optional uint32 permission = 1;
|
||||
// channel_id for the channel where the permission was denied when type is
|
||||
// Permission.
|
||||
optional uint32 channel_id = 2;
|
||||
// The user who was denied permissions, identified by session.
|
||||
optional uint32 session = 3;
|
||||
// Textual reason for the denial.
|
||||
optional string reason = 4;
|
||||
// Type of the denial.
|
||||
optional DenyType type = 5;
|
||||
// The name that is invalid when type is UserName.
|
||||
optional string name = 6;
|
||||
}
|
||||
|
||||
message ACL {
|
||||
message ChanGroup {
|
||||
// Name of the channel group, UTF-8 encoded.
|
||||
required string name = 1;
|
||||
// True if the group has been inherited from the parent (Read only).
|
||||
optional bool inherited = 2 [default = true];
|
||||
// True if the group members are inherited.
|
||||
optional bool inherit = 3 [default = true];
|
||||
// True if the group can be inherited by sub channels.
|
||||
optional bool inheritable = 4 [default = true];
|
||||
// Users explicitly included in this group, identified by user_id.
|
||||
repeated uint32 add = 5;
|
||||
// Users explicitly removed from this group in this channel if the group
|
||||
// has been inherited, identified by user_id.
|
||||
repeated uint32 remove = 6;
|
||||
// Users inherited, identified by user_id.
|
||||
repeated uint32 inherited_members = 7;
|
||||
}
|
||||
message ChanACL {
|
||||
// True if this ACL applies to the current channel.
|
||||
optional bool apply_here = 1 [default = true];
|
||||
// True if this ACL applies to the sub channels.
|
||||
optional bool apply_subs = 2 [default = true];
|
||||
// True if the ACL has been inherited from the parent.
|
||||
optional bool inherited = 3 [default = true];
|
||||
// ID of the user that is affected by this ACL.
|
||||
optional uint32 user_id = 4;
|
||||
// ID of the group that is affected by this ACL.
|
||||
optional string group = 5;
|
||||
// Bit flag field of the permissions granted by this ACL.
|
||||
optional uint32 grant = 6;
|
||||
// Bit flag field of the permissions denied by this ACL.
|
||||
optional uint32 deny = 7;
|
||||
}
|
||||
// Channel ID of the channel this message affects.
|
||||
required uint32 channel_id = 1;
|
||||
// True if the channel inherits its parent's ACLs.
|
||||
optional bool inherit_acls = 2 [default = true];
|
||||
// User group specifications.
|
||||
repeated ChanGroup groups = 3;
|
||||
// ACL specifications.
|
||||
repeated ChanACL acls = 4;
|
||||
// True if the message is a query for ACLs instead of setting them.
|
||||
optional bool query = 5 [default = false];
|
||||
}
|
||||
|
||||
// Client may use this message to refresh its registered user information. The
|
||||
// client should fill the IDs or Names of the users it wants to refresh. The
|
||||
// server fills the missing parts and sends the message back.
|
||||
message QueryUsers {
|
||||
// user_ids.
|
||||
repeated uint32 ids = 1;
|
||||
// User names in the same order as ids.
|
||||
repeated string names = 2;
|
||||
}
|
||||
|
||||
// Used to initialize and resync the UDP encryption. Either side may request a
|
||||
// resync by sending the message without any values filled. The resync is
|
||||
// performed by sending the message with only the client or server nonce
|
||||
// filled.
|
||||
message CryptSetup {
|
||||
// Encryption key.
|
||||
optional bytes key = 1;
|
||||
// Client nonce.
|
||||
optional bytes client_nonce = 2;
|
||||
// Server nonce.
|
||||
optional bytes server_nonce = 3;
|
||||
}
|
||||
|
||||
message ContextActionModify {
|
||||
enum Context {
|
||||
// Action is applicable to the server.
|
||||
Server = 0x01;
|
||||
// Action can target a Channel.
|
||||
Channel = 0x02;
|
||||
// Action can target a User.
|
||||
User = 0x04;
|
||||
}
|
||||
enum Operation {
|
||||
Add = 0;
|
||||
Remove = 1;
|
||||
}
|
||||
// The action name.
|
||||
required string action = 1;
|
||||
// The display name of the action.
|
||||
optional string text = 2;
|
||||
// Context bit flags defining where the action should be displayed.
|
||||
optional uint32 context = 3;
|
||||
optional Operation operation = 4;
|
||||
}
|
||||
|
||||
// Sent by the client when it wants to initiate a Context action.
|
||||
message ContextAction {
|
||||
// The target User for the action, identified by session.
|
||||
optional uint32 session = 1;
|
||||
// The target Channel for the action, identified by channel_id.
|
||||
optional uint32 channel_id = 2;
|
||||
// The action that should be executed.
|
||||
required string action = 3;
|
||||
}
|
||||
|
||||
// Lists the registered users.
|
||||
message UserList {
|
||||
message User {
|
||||
// Registered user ID.
|
||||
required uint32 user_id = 1;
|
||||
// Registered user name.
|
||||
optional string name = 2;
|
||||
optional string last_seen = 3;
|
||||
optional uint32 last_channel = 4;
|
||||
}
|
||||
// A list of registered users.
|
||||
repeated User users = 1;
|
||||
}
|
||||
|
||||
// Sent by the client when it wants to register or clear whisper targets.
|
||||
//
|
||||
// Note: The first available target ID is 1 as 0 is reserved for normal
|
||||
// talking. Maximum target ID is 30.
|
||||
message VoiceTarget {
|
||||
message Target {
|
||||
// Users that are included as targets.
|
||||
repeated uint32 session = 1;
|
||||
// Channels that are included as targets.
|
||||
optional uint32 channel_id = 2;
|
||||
// TODO ??
|
||||
optional string group = 3;
|
||||
// True if the voice should follow links from the specified channel.
|
||||
optional bool links = 4 [default = false];
|
||||
// True if the voice should also be sent to children of the specific
|
||||
// channel.
|
||||
optional bool children = 5 [default = false];
|
||||
}
|
||||
// Voice target ID.
|
||||
optional uint32 id = 1;
|
||||
// The receivers that this voice target includes.
|
||||
repeated Target targets = 2;
|
||||
}
|
||||
|
||||
// Sent by the client when it wants permissions for a certain channel. Sent by
|
||||
// the server when it replies to the query or wants the user to resync all
|
||||
// channel permissions.
|
||||
message PermissionQuery {
|
||||
// channel_id of the channel for which the permissions are queried.
|
||||
optional uint32 channel_id = 1;
|
||||
// Channel permissions.
|
||||
optional uint32 permissions = 2;
|
||||
// True if the client should drop its current permission information for all
|
||||
// channels.
|
||||
optional bool flush = 3 [default = false];
|
||||
}
|
||||
|
||||
// Sent by the server to notify the users of the version of the CELT codec they
|
||||
// should use. This may change during the connection when new users join.
|
||||
message CodecVersion {
|
||||
// The version of the CELT Alpha codec.
|
||||
required int32 alpha = 1;
|
||||
// The version of the CELT Beta codec.
|
||||
required int32 beta = 2;
|
||||
// True if the user should prefer Alpha over Beta.
|
||||
required bool prefer_alpha = 3 [default = true];
|
||||
optional bool opus = 4 [default = false];
|
||||
}
|
||||
|
||||
// Used to communicate user stats between the server and clients.
|
||||
message UserStats {
|
||||
message Stats {
|
||||
// The amount of good packets received.
|
||||
optional uint32 good = 1;
|
||||
// The amount of late packets received.
|
||||
optional uint32 late = 2;
|
||||
// The amount of packets never received.
|
||||
optional uint32 lost = 3;
|
||||
// The amount of nonce resyncs.
|
||||
optional uint32 resync = 4;
|
||||
}
|
||||
|
||||
// User whose stats these are.
|
||||
optional uint32 session = 1;
|
||||
// True if the message contains only mutable stats (packets, ping).
|
||||
optional bool stats_only = 2 [default = false];
|
||||
// Full user certificate chain of the user certificate in DER format.
|
||||
repeated bytes certificates = 3;
|
||||
// Packet statistics for packets received from the client.
|
||||
optional Stats from_client = 4;
|
||||
// Packet statistics for packets sent by the server.
|
||||
optional Stats from_server = 5;
|
||||
|
||||
// Amount of UDP packets sent.
|
||||
optional uint32 udp_packets = 6;
|
||||
// Amount of TCP packets sent.
|
||||
optional uint32 tcp_packets = 7;
|
||||
// UDP ping average.
|
||||
optional float udp_ping_avg = 8;
|
||||
// UDP ping variance.
|
||||
optional float udp_ping_var = 9;
|
||||
// TCP ping average.
|
||||
optional float tcp_ping_avg = 10;
|
||||
// TCP ping variance.
|
||||
optional float tcp_ping_var = 11;
|
||||
|
||||
// Client version.
|
||||
optional Version version = 12;
|
||||
// A list of CELT bitstream version constants supported by the client of this
|
||||
// user.
|
||||
repeated int32 celt_versions = 13;
|
||||
// Client IP address.
|
||||
optional bytes address = 14;
|
||||
// Bandwith used by this client.
|
||||
optional uint32 bandwidth = 15;
|
||||
// Connection duration.
|
||||
optional uint32 onlinesecs = 16;
|
||||
// Duration since last activity.
|
||||
optional uint32 idlesecs = 17;
|
||||
// True if the user has a strong certificate.
|
||||
optional bool strong_certificate = 18 [default = false];
|
||||
optional bool opus = 19 [default = false];
|
||||
}
|
||||
|
||||
// Used by the client to request binary data from the server. By default large
|
||||
// comments or textures are not sent within standard messages but instead the
|
||||
// hash is. If the client does not recognize the hash it may request the
|
||||
// resource when it needs it. The client does so by sending a RequestBlob
|
||||
// message with the correct fields filled with the user sessions or channel_ids
|
||||
// it wants to receive. The server replies to this by sending a new
|
||||
// UserState/ChannelState message with the resources filled even if they would
|
||||
// normally be transmitted as hashes.
|
||||
message RequestBlob {
|
||||
// sessions of the requested UserState textures.
|
||||
repeated uint32 session_texture = 1;
|
||||
// sessions of the requested UserState comments.
|
||||
repeated uint32 session_comment = 2;
|
||||
// channel_ids of the requested ChannelState descriptions.
|
||||
repeated uint32 channel_description = 3;
|
||||
}
|
||||
|
||||
// Sent by the server when it informs the clients on server configuration
|
||||
// details.
|
||||
message ServerConfig {
|
||||
// The maximum bandwidth the clients should use.
|
||||
optional uint32 max_bandwidth = 1;
|
||||
// Server welcome text.
|
||||
optional string welcome_text = 2;
|
||||
// True if the server allows HTML.
|
||||
optional bool allow_html = 3;
|
||||
// Maximum text message length.
|
||||
optional uint32 message_length = 4;
|
||||
// Maximum image message length.
|
||||
optional uint32 image_message_length = 5;
|
||||
}
|
||||
|
||||
// Sent by the server to inform the clients of suggested client configuration
|
||||
// specified by the server administrator.
|
||||
message SuggestConfig {
|
||||
// Suggested client version.
|
||||
optional uint32 version = 1;
|
||||
// True if the administrator suggests positional audio to be used on this
|
||||
// server.
|
||||
optional bool positional = 2;
|
||||
// True if the administrator suggests push to talk to be used on this server.
|
||||
optional bool push_to_talk = 3;
|
||||
}
|
2
Godeps/_workspace/src/github.com/layeh/gumble/gumble/MumbleProto/generate.go
generated
vendored
Normal file
2
Godeps/_workspace/src/github.com/layeh/gumble/gumble/MumbleProto/generate.go
generated
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
//go:generate protoc --go_out=. Mumble.proto
|
||||
package MumbleProto
|
58
Godeps/_workspace/src/github.com/layeh/gumble/gumble/acl.go
generated
vendored
Normal file
58
Godeps/_workspace/src/github.com/layeh/gumble/gumble/acl.go
generated
vendored
Normal file
|
@ -0,0 +1,58 @@
|
|||
package gumble
|
||||
|
||||
// ACL contains a list of ACLGroups and ACLRules linked to a channel.
|
||||
type ACL struct {
|
||||
// The channel to which the ACL belongs.
|
||||
Channel *Channel
|
||||
// The ACL's groups.
|
||||
Groups []*ACLGroup
|
||||
// The ACL's rules.
|
||||
Rules []*ACLRule
|
||||
// Does the ACL inherits the parent channel's ACL?s
|
||||
Inherits bool
|
||||
}
|
||||
|
||||
// ACLUser is a registered user who is part of or can be part of an ACL group
|
||||
// or rule.
|
||||
type ACLUser struct {
|
||||
// The user ID of the user.
|
||||
UserID uint32
|
||||
// The name of the user.
|
||||
Name string
|
||||
}
|
||||
|
||||
// ACLGroup is a named group of registered users which can be used in an
|
||||
// ACLRule.
|
||||
type ACLGroup struct {
|
||||
// The ACL group name.
|
||||
Name string
|
||||
// Is the group inherited from the parent channel's ACL?
|
||||
Inherited bool
|
||||
// Are group members are inherited from the parent channel's ACL?
|
||||
InheritUsers bool
|
||||
// Can the group be inherited by child channels?
|
||||
Inheritable bool
|
||||
usersAdd, usersRemove, usersInherited map[uint32]*ACLUser
|
||||
}
|
||||
|
||||
// ACLRule is a set of granted and denied permissions given to an ACLUser or
|
||||
// ACLGroup.
|
||||
type ACLRule struct {
|
||||
// Does the rule apply to the channel in which the rule is defined?
|
||||
AppliesCurrent bool
|
||||
// Does the rule apply to the children of the channel in which the rule is
|
||||
// defined?
|
||||
AppliesChildren bool
|
||||
// Is the rule inherited from the parent channel's ACL?
|
||||
Inherited bool
|
||||
|
||||
// The permissions granted by the rule.
|
||||
Granted Permission
|
||||
// The permissions denied by the rule.
|
||||
Denied Permission
|
||||
|
||||
// The ACL user the rule applies to. Can be nil.
|
||||
User *ACLUser
|
||||
// The ACL group the rule applies to. Can be nil.
|
||||
Group *ACLGroup
|
||||
}
|
85
Godeps/_workspace/src/github.com/layeh/gumble/gumble/audio.go
generated
vendored
Normal file
85
Godeps/_workspace/src/github.com/layeh/gumble/gumble/audio.go
generated
vendored
Normal file
|
@ -0,0 +1,85 @@
|
|||
package gumble
|
||||
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// AudioSampleRate is the audio sample rate (in hertz) for incoming and
|
||||
// outgoing audio.
|
||||
AudioSampleRate = 48000
|
||||
|
||||
// AudioDefaultInterval is the default interval that audio packets are sent
|
||||
// at.
|
||||
AudioDefaultInterval = 10 * time.Millisecond
|
||||
|
||||
// AudioDefaultFrameSize is the number of audio frames that should be sent in
|
||||
// a 10ms window.
|
||||
AudioDefaultFrameSize = AudioSampleRate / 100
|
||||
|
||||
// AudioMaximumFrameSize is the maximum audio frame size from another user
|
||||
// that will be processed.
|
||||
AudioMaximumFrameSize = AudioDefaultFrameSize * 10
|
||||
|
||||
// AudioDefaultDataBytes is the default number of bytes that an audio frame
|
||||
// can use.
|
||||
AudioDefaultDataBytes = 40
|
||||
)
|
||||
|
||||
// AudioListener is the interface that must be implemented by types wishing to
|
||||
// receive incoming audio data from the server.
|
||||
type AudioListener interface {
|
||||
OnAudioPacket(e *AudioPacketEvent)
|
||||
}
|
||||
|
||||
// AudioPacketEvent is event that is passed to AudioListener.OnAudioPacket.
|
||||
type AudioPacketEvent struct {
|
||||
Client *Client
|
||||
AudioPacket AudioPacket
|
||||
}
|
||||
|
||||
// AudioBuffer is a slice of PCM samples.
|
||||
type AudioBuffer []int16
|
||||
|
||||
// PositionalAudioBuffer is an AudioBuffer that has a position in 3D space
|
||||
// associated with it.
|
||||
type PositionalAudioBuffer struct {
|
||||
X, Y, Z float32
|
||||
AudioBuffer
|
||||
}
|
||||
|
||||
func (pab PositionalAudioBuffer) writeMessage(client *Client) error {
|
||||
return writeAudioTo(client, pab.AudioBuffer, &pab)
|
||||
}
|
||||
|
||||
// AudioPacket contains incoming audio data and information.
|
||||
type AudioPacket struct {
|
||||
Sender *User
|
||||
Target int
|
||||
Sequence int
|
||||
PositionalAudioBuffer
|
||||
}
|
||||
|
||||
func (ab AudioBuffer) writeMessage(client *Client) error {
|
||||
return writeAudioTo(client, ab, nil)
|
||||
}
|
||||
|
||||
func writeAudioTo(client *Client, ab AudioBuffer, pab *PositionalAudioBuffer) error {
|
||||
dataBytes := client.Config.GetAudioDataBytes()
|
||||
opus, err := client.AudioEncoder.Encode(ab, len(ab), dataBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var targetID int
|
||||
if target := client.VoiceTarget; target != nil {
|
||||
targetID = int(target.ID)
|
||||
}
|
||||
seq := client.audioSequence
|
||||
client.audioSequence = (client.audioSequence + 1) % math.MaxInt32
|
||||
if pab == nil {
|
||||
return client.Conn.WriteAudio(4, targetID, seq, opus, nil, nil, nil)
|
||||
}
|
||||
return client.Conn.WriteAudio(4, targetID, seq, opus, &pab.X, &pab.Y, &pab.Z)
|
||||
}
|
47
Godeps/_workspace/src/github.com/layeh/gumble/gumble/audiomultiplexer.go
generated
vendored
Normal file
47
Godeps/_workspace/src/github.com/layeh/gumble/gumble/audiomultiplexer.go
generated
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
package gumble
|
||||
|
||||
type audioEventMultiplexerItem struct {
|
||||
mux *audioEventMultiplexer
|
||||
prev, next *audioEventMultiplexerItem
|
||||
listener AudioListener
|
||||
}
|
||||
|
||||
func (emi *audioEventMultiplexerItem) Detach() {
|
||||
if emi.prev == nil {
|
||||
emi.mux.head = emi.next
|
||||
} else {
|
||||
emi.prev.next = emi.next
|
||||
}
|
||||
if emi.next == nil {
|
||||
emi.mux.tail = emi.prev
|
||||
} else {
|
||||
emi.next.prev = emi.prev
|
||||
}
|
||||
}
|
||||
|
||||
type audioEventMultiplexer struct {
|
||||
head, tail *audioEventMultiplexerItem
|
||||
}
|
||||
|
||||
func (aem *audioEventMultiplexer) Attach(listener AudioListener) Detacher {
|
||||
item := &audioEventMultiplexerItem{
|
||||
mux: aem,
|
||||
prev: aem.tail,
|
||||
listener: listener,
|
||||
}
|
||||
if aem.head == nil {
|
||||
aem.head = item
|
||||
}
|
||||
if aem.tail == nil {
|
||||
aem.tail = item
|
||||
} else {
|
||||
aem.tail.next = item
|
||||
}
|
||||
return item
|
||||
}
|
||||
|
||||
func (aem audioEventMultiplexer) OnAudioPacket(event *AudioPacketEvent) {
|
||||
for item := aem.head; item != nil; item = item.next {
|
||||
item.listener.OnAudioPacket(event)
|
||||
}
|
||||
}
|
102
Godeps/_workspace/src/github.com/layeh/gumble/gumble/bans.go
generated
vendored
Normal file
102
Godeps/_workspace/src/github.com/layeh/gumble/gumble/bans.go
generated
vendored
Normal file
|
@ -0,0 +1,102 @@
|
|||
package gumble
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/layeh/gumble/gumble/MumbleProto"
|
||||
)
|
||||
|
||||
// BanList is a list of server ban entries.
|
||||
//
|
||||
// Whenever a ban is changed, it does not come into effect until the ban list
|
||||
// is sent back to the server.
|
||||
type BanList []*Ban
|
||||
|
||||
// Add creates a new ban list entry with the given parameters.
|
||||
func (bl *BanList) Add(address net.IP, mask net.IPMask, reason string, duration time.Duration) *Ban {
|
||||
ban := &Ban{
|
||||
Address: address,
|
||||
Mask: mask,
|
||||
Reason: reason,
|
||||
Duration: duration,
|
||||
}
|
||||
*bl = append(*bl, ban)
|
||||
return ban
|
||||
}
|
||||
|
||||
// Ban represents an entry in the server ban list.
|
||||
//
|
||||
// This type should not be initialized manually. Instead, create new ban
|
||||
// entries using BanList.Add().
|
||||
type Ban struct {
|
||||
// The banned IP address.
|
||||
Address net.IP
|
||||
// The IP mask that the ban applies to.
|
||||
Mask net.IPMask
|
||||
// The name of the banned user.
|
||||
Name string
|
||||
// The certificate hash of the banned user.
|
||||
Hash string
|
||||
// The reason for the ban.
|
||||
Reason string
|
||||
// The start time from which the ban applies.
|
||||
Start time.Time
|
||||
// How long the ban is for.
|
||||
Duration time.Duration
|
||||
|
||||
unban bool
|
||||
}
|
||||
|
||||
// SetAddress sets the banned IP address.
|
||||
func (b *Ban) SetAddress(address net.IP) {
|
||||
b.Address = address
|
||||
}
|
||||
|
||||
// SetMask sets the IP mask that the ban applies to.
|
||||
func (b *Ban) SetMask(mask net.IPMask) {
|
||||
b.Mask = mask
|
||||
}
|
||||
|
||||
// SetReason changes the reason for the ban.
|
||||
func (b *Ban) SetReason(reason string) {
|
||||
b.Reason = reason
|
||||
}
|
||||
|
||||
// SetDuration changes the duration of the ban.
|
||||
func (b *Ban) SetDuration(duration time.Duration) {
|
||||
b.Duration = duration
|
||||
}
|
||||
|
||||
// Unban will unban the user from the server.
|
||||
func (b *Ban) Unban() {
|
||||
b.unban = true
|
||||
}
|
||||
|
||||
// Ban will ban the user from the server. This is only useful if Unban() was
|
||||
// called on the ban entry.
|
||||
func (b *Ban) Ban() {
|
||||
b.unban = false
|
||||
}
|
||||
|
||||
func (bl BanList) writeMessage(client *Client) error {
|
||||
packet := MumbleProto.BanList{
|
||||
Query: proto.Bool(false),
|
||||
}
|
||||
|
||||
for _, ban := range bl {
|
||||
if !ban.unban {
|
||||
maskSize, _ := ban.Mask.Size()
|
||||
packet.Bans = append(packet.Bans, &MumbleProto.BanList_BanEntry{
|
||||
Address: ban.Address,
|
||||
Mask: proto.Uint32(uint32(maskSize)),
|
||||
Reason: &ban.Reason,
|
||||
Duration: proto.Uint32(uint32(ban.Duration / time.Second)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
proto := protoMessage{&packet}
|
||||
return proto.writeMessage(client)
|
||||
}
|
147
Godeps/_workspace/src/github.com/layeh/gumble/gumble/channel.go
generated
vendored
Normal file
147
Godeps/_workspace/src/github.com/layeh/gumble/gumble/channel.go
generated
vendored
Normal file
|
@ -0,0 +1,147 @@
|
|||
package gumble
|
||||
|
||||
import (
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/layeh/gumble/gumble/MumbleProto"
|
||||
)
|
||||
|
||||
// Channel represents a channel in the server's channel tree.
|
||||
type Channel struct {
|
||||
// The channel's unique ID.
|
||||
ID uint32
|
||||
// The channel's name.
|
||||
Name string
|
||||
// The channel's parent. Contains nil if the channel is the root channel.
|
||||
Parent *Channel
|
||||
// The channels directly underneath the channel.
|
||||
Children Channels
|
||||
// The users currently in the channel.
|
||||
Users Users
|
||||
// The channel's description. Contains the empty string if the channel does
|
||||
// not have a description, or if it needs to be requested.
|
||||
Description string
|
||||
// The channel's description hash. Contains nil if Channel.Description has
|
||||
// been populated.
|
||||
DescriptionHash []byte
|
||||
// The position at which the channel should be displayed in an ordered list.
|
||||
Position int32
|
||||
// Is the channel temporary?
|
||||
Temporary bool
|
||||
|
||||
client *Client
|
||||
}
|
||||
|
||||
// IsRoot returns true if the channel is the server's root channel.
|
||||
func (c *Channel) IsRoot() bool {
|
||||
return c.ID == 0
|
||||
}
|
||||
|
||||
// Add will add a sub-channel to the given channel.
|
||||
func (c *Channel) Add(name string, temporary bool) {
|
||||
packet := MumbleProto.ChannelState{
|
||||
Parent: &c.ID,
|
||||
Name: &name,
|
||||
Temporary: &temporary,
|
||||
}
|
||||
c.client.Send(protoMessage{&packet})
|
||||
}
|
||||
|
||||
// Remove will remove the given channel and all sub-channels from the server's
|
||||
// channel tree.
|
||||
func (c *Channel) Remove() {
|
||||
packet := MumbleProto.ChannelRemove{
|
||||
ChannelId: &c.ID,
|
||||
}
|
||||
c.client.Send(protoMessage{&packet})
|
||||
}
|
||||
|
||||
// SetName will set the name of the channel. This will have no effect if the
|
||||
// channel is the server's root channel.
|
||||
func (c *Channel) SetName(name string) {
|
||||
packet := MumbleProto.ChannelState{
|
||||
ChannelId: &c.ID,
|
||||
Name: &name,
|
||||
}
|
||||
c.client.Send(protoMessage{&packet})
|
||||
}
|
||||
|
||||
// SetDescription will set the description of the channel.
|
||||
func (c *Channel) SetDescription(description string) {
|
||||
packet := MumbleProto.ChannelState{
|
||||
ChannelId: &c.ID,
|
||||
Description: &description,
|
||||
}
|
||||
c.client.Send(protoMessage{&packet})
|
||||
}
|
||||
|
||||
// Find returns a channel whose path (by channel name) from the current channel
|
||||
// is equal to the arguments passed.
|
||||
//
|
||||
// For example, given the following server channel tree:
|
||||
// Root
|
||||
// Child 1
|
||||
// Child 2
|
||||
// Child 2.1
|
||||
// Child 2.2
|
||||
// Child 2.2.1
|
||||
// Child 3
|
||||
// To get the "Child 2.2.1" channel:
|
||||
// root.Find("Child 2", "Child 2.2", "Child 2.2.1")
|
||||
func (c *Channel) Find(names ...string) *Channel {
|
||||
if len(names) == 0 {
|
||||
return c
|
||||
}
|
||||
for _, child := range c.Children {
|
||||
if child.Name == names[0] {
|
||||
return child.Find(names[1:]...)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Request requests channel information that has not yet been sent to the
|
||||
// client. The supported request types are: RequestACL, RequestDescription,
|
||||
// RequestPermission.
|
||||
//
|
||||
// Note: the server will not reply to a RequestPermission request if the client
|
||||
// has up-to-date permission information.
|
||||
func (c *Channel) Request(request Request) {
|
||||
if (request & RequestDescription) != 0 {
|
||||
packet := MumbleProto.RequestBlob{
|
||||
ChannelDescription: []uint32{c.ID},
|
||||
}
|
||||
c.client.Send(protoMessage{&packet})
|
||||
}
|
||||
if (request & RequestACL) != 0 {
|
||||
packet := MumbleProto.ACL{
|
||||
ChannelId: &c.ID,
|
||||
Query: proto.Bool(true),
|
||||
}
|
||||
c.client.Send(protoMessage{&packet})
|
||||
}
|
||||
if (request & RequestPermission) != 0 {
|
||||
packet := MumbleProto.PermissionQuery{
|
||||
ChannelId: &c.ID,
|
||||
}
|
||||
c.client.Send(protoMessage{&packet})
|
||||
}
|
||||
}
|
||||
|
||||
// Send will send a text message to the channel.
|
||||
func (c *Channel) Send(message string, recursive bool) {
|
||||
textMessage := TextMessage{
|
||||
Message: message,
|
||||
}
|
||||
if recursive {
|
||||
textMessage.Trees = []*Channel{c}
|
||||
} else {
|
||||
textMessage.Channels = []*Channel{c}
|
||||
}
|
||||
c.client.Send(&textMessage)
|
||||
}
|
||||
|
||||
// Permission returns the permissions the user has in the channel, or nil if
|
||||
// the permissions are unknown.
|
||||
func (c *Channel) Permission() *Permission {
|
||||
return c.client.permissions[c.ID]
|
||||
}
|
32
Godeps/_workspace/src/github.com/layeh/gumble/gumble/channels.go
generated
vendored
Normal file
32
Godeps/_workspace/src/github.com/layeh/gumble/gumble/channels.go
generated
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
package gumble
|
||||
|
||||
// Channels is a map of server channels.
|
||||
//
|
||||
// When accessed through Client.Channels, it contains all channels on the
|
||||
// server. When accessed through a specific channel
|
||||
// (e.g. client.Channels[0].Children), it contains only the children of the
|
||||
// channel.
|
||||
type Channels map[uint32]*Channel
|
||||
|
||||
// create adds a new channel with the given id to the collection. If a channel
|
||||
// with the given id already exists, it is overwritten.
|
||||
func (c Channels) create(id uint32) *Channel {
|
||||
channel := &Channel{
|
||||
ID: id,
|
||||
Children: Channels{},
|
||||
Users: Users{},
|
||||
}
|
||||
c[id] = channel
|
||||
return channel
|
||||
}
|
||||
|
||||
// Find returns a channel whose path (by channel name) from the server root
|
||||
// channel is equal to the arguments passed. If the root channel does not
|
||||
// exist, nil is returned.
|
||||
func (c Channels) Find(names ...string) *Channel {
|
||||
root := c[0]
|
||||
if names == nil || root == nil {
|
||||
return root
|
||||
}
|
||||
return root.Find(names...)
|
||||
}
|
230
Godeps/_workspace/src/github.com/layeh/gumble/gumble/client.go
generated
vendored
Normal file
230
Godeps/_workspace/src/github.com/layeh/gumble/gumble/client.go
generated
vendored
Normal file
|
@ -0,0 +1,230 @@
|
|||
package gumble
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/layeh/gopus"
|
||||
"github.com/layeh/gumble/gumble/MumbleProto"
|
||||
)
|
||||
|
||||
// State is the current state of the client's connection to the server.
|
||||
type State int
|
||||
|
||||
const (
|
||||
// StateDisconnected means the client is not connected to a server.
|
||||
StateDisconnected State = iota
|
||||
|
||||
// StateConnected means the client is connected to a server, but has yet to
|
||||
// receive the initial server state.
|
||||
StateConnected
|
||||
|
||||
// StateSynced means the client is connected to a server and has been sent
|
||||
// the server state.
|
||||
StateSynced
|
||||
)
|
||||
|
||||
// ClientVersion is the protocol version that Client implements.
|
||||
const ClientVersion = 1<<16 | 2<<8 | 4
|
||||
|
||||
// Client is the type used to create a connection to a server.
|
||||
type Client struct {
|
||||
// The current state of the client.
|
||||
State State
|
||||
// The User associated with the client (nil if the client has not yet been
|
||||
// synced).
|
||||
Self *User
|
||||
// The client's configuration.
|
||||
Config *Config
|
||||
// The underlying Conn to the server.
|
||||
*Conn
|
||||
|
||||
listeners eventMultiplexer
|
||||
audioListeners audioEventMultiplexer
|
||||
|
||||
// The users currently connected to the server.
|
||||
Users Users
|
||||
// The connected server's channels.
|
||||
Channels Channels
|
||||
permissions map[uint32]*Permission
|
||||
tmpACL *ACL
|
||||
|
||||
pingStats pingStats
|
||||
|
||||
// A collection containing the server's context actions.
|
||||
ContextActions ContextActions
|
||||
|
||||
// The audio encoder used when sending audio to the server.
|
||||
AudioEncoder *gopus.Encoder
|
||||
audioSequence int
|
||||
// To whom transmitted audio will be sent. The VoiceTarget must have already
|
||||
// been sent to the server for targeting to work correctly. Setting to nil
|
||||
// will disable voice targeting (i.e. switch back to regular speaking).
|
||||
VoiceTarget *VoiceTarget
|
||||
|
||||
end chan bool
|
||||
disconnectEvent DisconnectEvent
|
||||
}
|
||||
|
||||
// NewClient creates a new gumble client. Returns nil if config is nil.
|
||||
func NewClient(config *Config) *Client {
|
||||
if config == nil {
|
||||
return nil
|
||||
}
|
||||
client := &Client{
|
||||
Config: config,
|
||||
end: make(chan bool),
|
||||
}
|
||||
return client
|
||||
}
|
||||
|
||||
// Connect connects to the server.
|
||||
func (c *Client) Connect() error {
|
||||
if c.State != StateDisconnected {
|
||||
return errors.New("client is already connected")
|
||||
}
|
||||
encoder, err := gopus.NewEncoder(AudioSampleRate, 1, gopus.Voip)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
encoder.SetBitrate(gopus.BitrateMaximum)
|
||||
c.audioSequence = 0
|
||||
c.VoiceTarget = nil
|
||||
|
||||
tlsConn, err := tls.DialWithDialer(&c.Config.Dialer, "tcp", c.Config.Address, &c.Config.TLSConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if verify := c.Config.TLSVerify; verify != nil {
|
||||
state := tlsConn.ConnectionState()
|
||||
if err := verify(&state); err != nil {
|
||||
tlsConn.Close()
|
||||
return err
|
||||
}
|
||||
}
|
||||
c.Conn = NewConn(tlsConn)
|
||||
|
||||
c.AudioEncoder = encoder
|
||||
c.Users = Users{}
|
||||
c.Channels = Channels{}
|
||||
c.permissions = make(map[uint32]*Permission)
|
||||
c.ContextActions = ContextActions{}
|
||||
c.State = StateConnected
|
||||
|
||||
// Channels and goroutines
|
||||
go c.readRoutine()
|
||||
go c.pingRoutine()
|
||||
|
||||
// Initial packets
|
||||
versionPacket := MumbleProto.Version{
|
||||
Version: proto.Uint32(ClientVersion),
|
||||
Release: proto.String("gumble"),
|
||||
Os: proto.String(runtime.GOOS),
|
||||
OsVersion: proto.String(runtime.GOARCH),
|
||||
}
|
||||
authenticationPacket := MumbleProto.Authenticate{
|
||||
Username: &c.Config.Username,
|
||||
Password: &c.Config.Password,
|
||||
Opus: proto.Bool(true),
|
||||
Tokens: c.Config.Tokens,
|
||||
}
|
||||
c.Send(protoMessage{&versionPacket})
|
||||
c.Send(protoMessage{&authenticationPacket})
|
||||
return nil
|
||||
}
|
||||
|
||||
// Attach adds an event listener that will receive incoming connection events.
|
||||
func (c *Client) Attach(listener EventListener) Detacher {
|
||||
return c.listeners.Attach(listener)
|
||||
}
|
||||
|
||||
// AttachAudio adds an audio event listener that will receive incoming audio
|
||||
// packets.
|
||||
func (c *Client) AttachAudio(listener AudioListener) Detacher {
|
||||
return c.audioListeners.Attach(listener)
|
||||
}
|
||||
|
||||
// pingRoutine sends ping packets to the server at regular intervals.
|
||||
func (c *Client) pingRoutine() {
|
||||
ticker := time.NewTicker(time.Second * 10)
|
||||
defer ticker.Stop()
|
||||
|
||||
pingPacket := MumbleProto.Ping{
|
||||
Timestamp: proto.Uint64(0),
|
||||
TcpPackets: &c.pingStats.TCPPackets,
|
||||
}
|
||||
pingProto := protoMessage{&pingPacket}
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-c.end:
|
||||
return
|
||||
case time := <-ticker.C:
|
||||
*pingPacket.Timestamp = uint64(time.Unix())
|
||||
c.Send(pingProto)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// readRoutine reads protocol buffer messages from the server.
|
||||
func (c *Client) readRoutine() {
|
||||
c.disconnectEvent = DisconnectEvent{
|
||||
Client: c,
|
||||
Type: DisconnectError,
|
||||
}
|
||||
|
||||
for {
|
||||
pType, data, err := c.Conn.ReadPacket()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
if handle, ok := handlers[pType]; ok {
|
||||
handle(c, data)
|
||||
}
|
||||
}
|
||||
|
||||
c.end <- true
|
||||
c.Conn = nil
|
||||
c.State = StateDisconnected
|
||||
c.tmpACL = nil
|
||||
c.pingStats = pingStats{}
|
||||
c.listeners.OnDisconnect(&c.disconnectEvent)
|
||||
}
|
||||
|
||||
// Request requests that specific server information be sent to the client. The
|
||||
// supported request types are: RequestUserList, and RequestBanList.
|
||||
func (c *Client) Request(request Request) {
|
||||
if (request & RequestUserList) != 0 {
|
||||
packet := MumbleProto.UserList{}
|
||||
proto := protoMessage{&packet}
|
||||
c.Send(proto)
|
||||
}
|
||||
if (request & RequestBanList) != 0 {
|
||||
packet := MumbleProto.BanList{
|
||||
Query: proto.Bool(true),
|
||||
}
|
||||
proto := protoMessage{&packet}
|
||||
c.Send(proto)
|
||||
}
|
||||
}
|
||||
|
||||
// Disconnect disconnects the client from the server.
|
||||
func (c *Client) Disconnect() error {
|
||||
if c.State == StateDisconnected {
|
||||
return errors.New("client is already disconnected")
|
||||
}
|
||||
c.disconnectEvent.Type = DisconnectUser
|
||||
c.Conn.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Send will send a message to the server.
|
||||
func (c *Client) Send(message Message) error {
|
||||
if err := message.writeMessage(c); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
72
Godeps/_workspace/src/github.com/layeh/gumble/gumble/config.go
generated
vendored
Normal file
72
Godeps/_workspace/src/github.com/layeh/gumble/gumble/config.go
generated
vendored
Normal file
|
@ -0,0 +1,72 @@
|
|||
package gumble
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/layeh/gumble/gumble/MumbleProto"
|
||||
)
|
||||
|
||||
// Config holds the configuration data used by Client.
|
||||
type Config struct {
|
||||
// User name used when authenticating with the server.
|
||||
Username string
|
||||
// Password used when authenticating with the server. A password is not
|
||||
// usually required to connect to a server.
|
||||
Password string
|
||||
// Server address, including port (e.g. localhost:64738).
|
||||
Address string
|
||||
Tokens AccessTokens
|
||||
|
||||
// AudioInterval is the interval at which audio packets are sent. Valid
|
||||
// values are 10ms, 20ms, 40ms, and 60ms.
|
||||
AudioInterval time.Duration
|
||||
|
||||
// AudioDataBytes is the number of bytes that an audio frame can use.
|
||||
AudioDataBytes int
|
||||
|
||||
TLSConfig tls.Config
|
||||
// If non-nil, this function will be called after the connection to the
|
||||
// server has been made. If it returns nil, the connection will stay alive,
|
||||
// otherwise, it will be closed and Client.Connect will return the returned
|
||||
// error.
|
||||
TLSVerify func(state *tls.ConnectionState) error
|
||||
Dialer net.Dialer
|
||||
}
|
||||
|
||||
// GetAudioInterval returns c.AudioInterval if it is valid, else returns
|
||||
// AudioDefaultInterval.
|
||||
func (c *Config) GetAudioInterval() time.Duration {
|
||||
if c.AudioInterval <= 0 {
|
||||
return AudioDefaultInterval
|
||||
}
|
||||
return c.AudioInterval
|
||||
}
|
||||
|
||||
// GetAudioDataBytes returns c.AudioDataBytes if it is valid, else returns
|
||||
// AudioDefaultDataBytes.
|
||||
func (c *Config) GetAudioDataBytes() int {
|
||||
if c.AudioDataBytes <= 0 {
|
||||
return AudioDefaultDataBytes
|
||||
}
|
||||
return c.AudioDataBytes
|
||||
}
|
||||
|
||||
// GetAudioFrameSize returns the appropriate audio frame size, based off of the
|
||||
// audio interval.
|
||||
func (c *Config) GetAudioFrameSize() int {
|
||||
return int(c.GetAudioInterval()/AudioDefaultInterval) * AudioDefaultFrameSize
|
||||
}
|
||||
|
||||
// AccessTokens are additional passwords that can be provided to the server to
|
||||
// gain access to restricted channels.
|
||||
type AccessTokens []string
|
||||
|
||||
func (at AccessTokens) writeMessage(client *Client) error {
|
||||
packet := MumbleProto.Authenticate{
|
||||
Tokens: at,
|
||||
}
|
||||
proto := protoMessage{&packet}
|
||||
return proto.writeMessage(client)
|
||||
}
|
197
Godeps/_workspace/src/github.com/layeh/gumble/gumble/conn.go
generated
vendored
Normal file
197
Godeps/_workspace/src/github.com/layeh/gumble/gumble/conn.go
generated
vendored
Normal file
|
@ -0,0 +1,197 @@
|
|||
package gumble
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/layeh/gumble/gumble/MumbleProto"
|
||||
"github.com/layeh/gumble/gumble/varint"
|
||||
)
|
||||
|
||||
// Conn represents a connection to a Mumble client/server.
|
||||
type Conn struct {
|
||||
sync.Mutex
|
||||
net.Conn
|
||||
|
||||
MaximumPacketBytes int
|
||||
Timeout time.Duration
|
||||
|
||||
buffer []byte
|
||||
}
|
||||
|
||||
// NewConn creates a new Conn with the given net.Conn.
|
||||
func NewConn(conn net.Conn) *Conn {
|
||||
return &Conn{
|
||||
Conn: conn,
|
||||
Timeout: time.Second * 20,
|
||||
MaximumPacketBytes: 1024 * 1024 * 10,
|
||||
}
|
||||
}
|
||||
|
||||
// ReadPacket reads a packet from the server. Returns the packet type, the
|
||||
// packet data, and nil on success.
|
||||
//
|
||||
// This function should only be called by a single go routine.
|
||||
func (c *Conn) ReadPacket() (uint16, []byte, error) {
|
||||
var pType uint16
|
||||
var pLength uint32
|
||||
|
||||
c.Conn.SetReadDeadline(time.Now().Add(c.Timeout))
|
||||
if err := binary.Read(c.Conn, binary.BigEndian, &pType); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
if err := binary.Read(c.Conn, binary.BigEndian, &pLength); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
pLengthInt := int(pLength)
|
||||
if pLengthInt > c.MaximumPacketBytes {
|
||||
return 0, nil, errors.New("packet larger than maximum allowed size")
|
||||
}
|
||||
if pLengthInt > cap(c.buffer) {
|
||||
c.buffer = make([]byte, pLengthInt)
|
||||
}
|
||||
if _, err := io.ReadFull(c.Conn, c.buffer[:pLengthInt]); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return pType, c.buffer[:pLengthInt], nil
|
||||
}
|
||||
|
||||
// WriteAudio writes an audio packet to the connection.
|
||||
func (c *Conn) WriteAudio(format, target, sequence int, data []byte, X, Y, Z *float32) error {
|
||||
var header bytes.Buffer
|
||||
formatTarget := byte(format)<<5 | byte(target)
|
||||
if err := header.WriteByte(formatTarget); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := varint.WriteTo(&header, int64(sequence)); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := varint.WriteTo(&header, int64(len(data))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var positionalLength int
|
||||
if X != nil {
|
||||
positionalLength = 3 * 4
|
||||
}
|
||||
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
if err := c.writeHeader(1, uint32(header.Len()+len(data)+positionalLength)); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := header.WriteTo(c.Conn); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := c.Conn.Write(data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if positionalLength > 0 {
|
||||
if err := binary.Write(c.Conn, binary.LittleEndian, *X); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := binary.Write(c.Conn, binary.LittleEndian, *Y); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := binary.Write(c.Conn, binary.LittleEndian, *Z); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WritePacket writes a data packet of the given type to the connection.
|
||||
func (c *Conn) WritePacket(ptype uint16, data []byte) error {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
if err := c.writeHeader(uint16(ptype), uint32(len(data))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := c.Conn.Write(data); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) writeHeader(pType uint16, pLength uint32) error {
|
||||
if err := binary.Write(c.Conn, binary.BigEndian, pType); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := binary.Write(c.Conn, binary.BigEndian, pLength); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteProto writes a protocol buffer message to the connection.
|
||||
func (c *Conn) WriteProto(message proto.Message) error {
|
||||
var protoType uint16
|
||||
switch message.(type) {
|
||||
case *MumbleProto.Version:
|
||||
protoType = 0
|
||||
case *MumbleProto.Authenticate:
|
||||
protoType = 2
|
||||
case *MumbleProto.Ping:
|
||||
protoType = 3
|
||||
case *MumbleProto.Reject:
|
||||
protoType = 4
|
||||
case *MumbleProto.ServerSync:
|
||||
protoType = 5
|
||||
case *MumbleProto.ChannelRemove:
|
||||
protoType = 6
|
||||
case *MumbleProto.ChannelState:
|
||||
protoType = 7
|
||||
case *MumbleProto.UserRemove:
|
||||
protoType = 8
|
||||
case *MumbleProto.UserState:
|
||||
protoType = 9
|
||||
case *MumbleProto.BanList:
|
||||
protoType = 10
|
||||
case *MumbleProto.TextMessage:
|
||||
protoType = 11
|
||||
case *MumbleProto.PermissionDenied:
|
||||
protoType = 12
|
||||
case *MumbleProto.ACL:
|
||||
protoType = 13
|
||||
case *MumbleProto.QueryUsers:
|
||||
protoType = 14
|
||||
case *MumbleProto.CryptSetup:
|
||||
protoType = 15
|
||||
case *MumbleProto.ContextActionModify:
|
||||
protoType = 16
|
||||
case *MumbleProto.ContextAction:
|
||||
protoType = 17
|
||||
case *MumbleProto.UserList:
|
||||
protoType = 18
|
||||
case *MumbleProto.VoiceTarget:
|
||||
protoType = 19
|
||||
case *MumbleProto.PermissionQuery:
|
||||
protoType = 20
|
||||
case *MumbleProto.CodecVersion:
|
||||
protoType = 21
|
||||
case *MumbleProto.UserStats:
|
||||
protoType = 22
|
||||
case *MumbleProto.RequestBlob:
|
||||
protoType = 23
|
||||
case *MumbleProto.ServerConfig:
|
||||
protoType = 24
|
||||
case *MumbleProto.SuggestConfig:
|
||||
protoType = 25
|
||||
default:
|
||||
return errors.New("unknown message type")
|
||||
}
|
||||
data, err := proto.Marshal(message)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.WritePacket(protoType, data)
|
||||
}
|
57
Godeps/_workspace/src/github.com/layeh/gumble/gumble/contextaction.go
generated
vendored
Normal file
57
Godeps/_workspace/src/github.com/layeh/gumble/gumble/contextaction.go
generated
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
package gumble
|
||||
|
||||
import (
|
||||
"github.com/layeh/gumble/gumble/MumbleProto"
|
||||
)
|
||||
|
||||
// ContextActionType is a bitmask of contexts where a ContextAction can be
|
||||
// triggered.
|
||||
type ContextActionType int
|
||||
|
||||
// Supported ContextAction contexts.
|
||||
const (
|
||||
ContextActionServer ContextActionType = ContextActionType(MumbleProto.ContextActionModify_Server)
|
||||
ContextActionChannel ContextActionType = ContextActionType(MumbleProto.ContextActionModify_Channel)
|
||||
ContextActionUser ContextActionType = ContextActionType(MumbleProto.ContextActionModify_User)
|
||||
)
|
||||
|
||||
// ContextAction is an triggerable item that has been added by a server-side
|
||||
// plugin.
|
||||
type ContextAction struct {
|
||||
client *Client
|
||||
|
||||
// The context action type.
|
||||
Type ContextActionType
|
||||
// The name of the context action.
|
||||
Name string
|
||||
// The user-friendly description of the context action.
|
||||
Label string
|
||||
}
|
||||
|
||||
// Trigger will trigger the context action in the context of the server.
|
||||
func (ca *ContextAction) Trigger() {
|
||||
packet := MumbleProto.ContextAction{
|
||||
Action: &ca.Name,
|
||||
}
|
||||
ca.client.Send(protoMessage{&packet})
|
||||
}
|
||||
|
||||
// TriggerUser will trigger the context action in the context of the given
|
||||
// user.
|
||||
func (ca *ContextAction) TriggerUser(user *User) {
|
||||
packet := MumbleProto.ContextAction{
|
||||
Session: &user.Session,
|
||||
Action: &ca.Name,
|
||||
}
|
||||
ca.client.Send(protoMessage{&packet})
|
||||
}
|
||||
|
||||
// TriggerChannel will trigger the context action in the context of the given
|
||||
// channel.
|
||||
func (ca *ContextAction) TriggerChannel(channel *Channel) {
|
||||
packet := MumbleProto.ContextAction{
|
||||
ChannelId: &channel.ID,
|
||||
Action: &ca.Name,
|
||||
}
|
||||
ca.client.Send(protoMessage{&packet})
|
||||
}
|
12
Godeps/_workspace/src/github.com/layeh/gumble/gumble/contextactions.go
generated
vendored
Normal file
12
Godeps/_workspace/src/github.com/layeh/gumble/gumble/contextactions.go
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
package gumble
|
||||
|
||||
// ContextActions is a map of ContextActions.
|
||||
type ContextActions map[string]*ContextAction
|
||||
|
||||
func (ca ContextActions) create(action string) *ContextAction {
|
||||
contextAction := &ContextAction{
|
||||
Name: action,
|
||||
}
|
||||
ca[action] = contextAction
|
||||
return contextAction
|
||||
}
|
39
Godeps/_workspace/src/github.com/layeh/gumble/gumble/doc.go
generated
vendored
Normal file
39
Godeps/_workspace/src/github.com/layeh/gumble/gumble/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
// Package gumble is a client for the Mumble voice chat software.
|
||||
//
|
||||
// Getting started
|
||||
//
|
||||
//1. Create a new Config to hold your connection settings:
|
||||
//
|
||||
// config := gumble.Config{
|
||||
// Username: "gumble-test",
|
||||
// Address: "example.com:64738",
|
||||
// }
|
||||
//
|
||||
//2. Create a new Client:
|
||||
//
|
||||
// client := gumble.NewClient(&config)
|
||||
//
|
||||
//3. Implement EventListener (or use gumbleutil.Listener) and attach it to the client:
|
||||
//
|
||||
// client.Attach(gumbleutil.Listener{
|
||||
// TextMessage: func(e *gumble.TextMessageEvent) {
|
||||
// fmt.Printf("Received text message: %s\n", e.Message)
|
||||
// },
|
||||
// })
|
||||
//
|
||||
//4. Connect to the server:
|
||||
//
|
||||
// if err := client.Connect(); err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
//
|
||||
// Audio codecs
|
||||
//
|
||||
// Currently, only the Opus codec (https://www.opus-codec.org/) is supported
|
||||
// for transmitting and receiving audio.
|
||||
//
|
||||
// To ensure that gumble clients can always transmit and receive audio to and
|
||||
// from your server, add the following line to your murmur configuration file:
|
||||
//
|
||||
// opusthreshold=0
|
||||
package gumble
|
202
Godeps/_workspace/src/github.com/layeh/gumble/gumble/event.go
generated
vendored
Normal file
202
Godeps/_workspace/src/github.com/layeh/gumble/gumble/event.go
generated
vendored
Normal file
|
@ -0,0 +1,202 @@
|
|||
package gumble
|
||||
|
||||
import (
|
||||
"github.com/layeh/gumble/gumble/MumbleProto"
|
||||
)
|
||||
|
||||
// EventListener is the interface that must be implemented by a type if it
|
||||
// wishes to be notified of Client events.
|
||||
type EventListener interface {
|
||||
OnConnect(e *ConnectEvent)
|
||||
OnDisconnect(e *DisconnectEvent)
|
||||
OnTextMessage(e *TextMessageEvent)
|
||||
OnUserChange(e *UserChangeEvent)
|
||||
OnChannelChange(e *ChannelChangeEvent)
|
||||
OnPermissionDenied(e *PermissionDeniedEvent)
|
||||
OnUserList(e *UserListEvent)
|
||||
OnACL(e *ACLEvent)
|
||||
OnBanList(e *BanListEvent)
|
||||
OnContextActionChange(e *ContextActionChangeEvent)
|
||||
}
|
||||
|
||||
// ConnectEvent is the event that is passed to EventListener.OnConnect.
|
||||
type ConnectEvent struct {
|
||||
Client *Client
|
||||
WelcomeMessage string
|
||||
MaximumBitrate int
|
||||
}
|
||||
|
||||
// DisconnectType specifies why a Client disconnected from a server.
|
||||
type DisconnectType int
|
||||
|
||||
// Client disconnect reasons.
|
||||
const (
|
||||
DisconnectError DisconnectType = 0xFE - iota
|
||||
DisconnectKicked
|
||||
DisconnectBanned
|
||||
DisconnectUser
|
||||
|
||||
DisconnectOther DisconnectType = DisconnectType(MumbleProto.Reject_None)
|
||||
DisconnectVersion DisconnectType = DisconnectType(MumbleProto.Reject_WrongVersion)
|
||||
DisconnectUserName DisconnectType = DisconnectType(MumbleProto.Reject_InvalidUsername)
|
||||
DisconnectUserCredentials DisconnectType = DisconnectType(MumbleProto.Reject_WrongUserPW)
|
||||
DisconnectServerPassword DisconnectType = DisconnectType(MumbleProto.Reject_WrongServerPW)
|
||||
DisconnectUsernameInUse DisconnectType = DisconnectType(MumbleProto.Reject_UsernameInUse)
|
||||
DisconnectServerFull DisconnectType = DisconnectType(MumbleProto.Reject_ServerFull)
|
||||
DisconnectNoCertificate DisconnectType = DisconnectType(MumbleProto.Reject_NoCertificate)
|
||||
DisconnectAuthenticatorFail DisconnectType = DisconnectType(MumbleProto.Reject_AuthenticatorFail)
|
||||
)
|
||||
|
||||
// Has returns true if the DisconnectType has changeType part of its bitmask.
|
||||
func (dt DisconnectType) Has(changeType DisconnectType) bool {
|
||||
return (dt & changeType) != 0
|
||||
}
|
||||
|
||||
// DisconnectEvent is the event that is passed to EventListener.OnDisconnect.
|
||||
type DisconnectEvent struct {
|
||||
Client *Client
|
||||
Type DisconnectType
|
||||
|
||||
String string
|
||||
}
|
||||
|
||||
// TextMessageEvent is the event that is passed to EventListener.OnTextMessage.
|
||||
type TextMessageEvent struct {
|
||||
Client *Client
|
||||
TextMessage
|
||||
}
|
||||
|
||||
// UserChangeType is a bitmask of items that changed for a user.
|
||||
type UserChangeType int
|
||||
|
||||
// User change items.
|
||||
const (
|
||||
UserChangeConnected UserChangeType = 1 << iota
|
||||
UserChangeDisconnected
|
||||
UserChangeKicked
|
||||
UserChangeBanned
|
||||
UserChangeRegistered
|
||||
UserChangeUnregistered
|
||||
UserChangeName
|
||||
UserChangeChannel
|
||||
UserChangeComment
|
||||
UserChangeAudio
|
||||
UserChangeTexture
|
||||
UserChangePrioritySpeaker
|
||||
UserChangeRecording
|
||||
UserChangeStats
|
||||
)
|
||||
|
||||
// Has returns true if the UserChangeType has changeType part of its bitmask.
|
||||
func (uct UserChangeType) Has(changeType UserChangeType) bool {
|
||||
return (uct & changeType) != 0
|
||||
}
|
||||
|
||||
// UserChangeEvent is the event that is passed to EventListener.OnUserChange.
|
||||
type UserChangeEvent struct {
|
||||
Client *Client
|
||||
Type UserChangeType
|
||||
User *User
|
||||
Actor *User
|
||||
|
||||
String string
|
||||
}
|
||||
|
||||
// ChannelChangeType is a bitmask of items that changed for a channel.
|
||||
type ChannelChangeType int
|
||||
|
||||
// Channel change items.
|
||||
const (
|
||||
ChannelChangeCreated ChannelChangeType = 1 << iota
|
||||
ChannelChangeRemoved
|
||||
ChannelChangeMoved
|
||||
ChannelChangeName
|
||||
ChannelChangeDescription
|
||||
ChannelChangePosition
|
||||
ChannelChangePermission
|
||||
)
|
||||
|
||||
// Has returns true if the ChannelChangeType has changeType part of its
|
||||
// bitmask.
|
||||
func (cct ChannelChangeType) Has(changeType ChannelChangeType) bool {
|
||||
return (cct & changeType) != 0
|
||||
}
|
||||
|
||||
// ChannelChangeEvent is the event that is passed to
|
||||
// EventListener.OnChannelChange.
|
||||
type ChannelChangeEvent struct {
|
||||
Client *Client
|
||||
Type ChannelChangeType
|
||||
Channel *Channel
|
||||
}
|
||||
|
||||
// PermissionDeniedType specifies why a Client was denied permission to perform
|
||||
// a particular action.
|
||||
type PermissionDeniedType int
|
||||
|
||||
// Permission denied types.
|
||||
const (
|
||||
PermissionDeniedOther PermissionDeniedType = PermissionDeniedType(MumbleProto.PermissionDenied_Text)
|
||||
PermissionDeniedPermission PermissionDeniedType = PermissionDeniedType(MumbleProto.PermissionDenied_Permission)
|
||||
PermissionDeniedSuperUser PermissionDeniedType = PermissionDeniedType(MumbleProto.PermissionDenied_SuperUser)
|
||||
PermissionDeniedInvalidChannelName PermissionDeniedType = PermissionDeniedType(MumbleProto.PermissionDenied_ChannelName)
|
||||
PermissionDeniedTextTooLong PermissionDeniedType = PermissionDeniedType(MumbleProto.PermissionDenied_TextTooLong)
|
||||
PermissionDeniedTemporaryChannel PermissionDeniedType = PermissionDeniedType(MumbleProto.PermissionDenied_TemporaryChannel)
|
||||
PermissionDeniedMissingCertificate PermissionDeniedType = PermissionDeniedType(MumbleProto.PermissionDenied_MissingCertificate)
|
||||
PermissionDeniedInvalidUserName PermissionDeniedType = PermissionDeniedType(MumbleProto.PermissionDenied_UserName)
|
||||
PermissionDeniedChannelFull PermissionDeniedType = PermissionDeniedType(MumbleProto.PermissionDenied_ChannelFull)
|
||||
PermissionDeniedNestingLimit PermissionDeniedType = PermissionDeniedType(MumbleProto.PermissionDenied_NestingLimit)
|
||||
)
|
||||
|
||||
// Has returns true if the PermissionDeniedType has changeType part of its
|
||||
// bitmask.
|
||||
func (pdt PermissionDeniedType) Has(changeType PermissionDeniedType) bool {
|
||||
return (pdt & changeType) != 0
|
||||
}
|
||||
|
||||
// PermissionDeniedEvent is the event that is passed to
|
||||
// EventListener.OnPermissionDenied.
|
||||
type PermissionDeniedEvent struct {
|
||||
Client *Client
|
||||
Type PermissionDeniedType
|
||||
Channel *Channel
|
||||
User *User
|
||||
|
||||
Permission Permission
|
||||
String string
|
||||
}
|
||||
|
||||
// UserListEvent is the event that is passed to EventListener.OnUserList.
|
||||
type UserListEvent struct {
|
||||
Client *Client
|
||||
UserList RegisteredUsers
|
||||
}
|
||||
|
||||
// ACLEvent is the event that is passed to EventListener.OnACL.
|
||||
type ACLEvent struct {
|
||||
Client *Client
|
||||
ACL *ACL
|
||||
}
|
||||
|
||||
// BanListEvent is the event that is passed to EventListener.OnBanList.
|
||||
type BanListEvent struct {
|
||||
Client *Client
|
||||
BanList BanList
|
||||
}
|
||||
|
||||
// ContextActionChangeType specifies how a ContextAction changed.
|
||||
type ContextActionChangeType int
|
||||
|
||||
// ContextAction change types.
|
||||
const (
|
||||
ContextActionAdd ContextActionChangeType = ContextActionChangeType(MumbleProto.ContextActionModify_Add)
|
||||
ContextActionRemove ContextActionChangeType = ContextActionChangeType(MumbleProto.ContextActionModify_Remove)
|
||||
)
|
||||
|
||||
// ContextActionChangeEvent is the event that is passed to
|
||||
// EventListener.OnContextActionChange.
|
||||
type ContextActionChangeEvent struct {
|
||||
Client *Client
|
||||
Type ContextActionChangeType
|
||||
ContextAction *ContextAction
|
||||
}
|
106
Godeps/_workspace/src/github.com/layeh/gumble/gumble/eventmultiplexer.go
generated
vendored
Normal file
106
Godeps/_workspace/src/github.com/layeh/gumble/gumble/eventmultiplexer.go
generated
vendored
Normal file
|
@ -0,0 +1,106 @@
|
|||
package gumble
|
||||
|
||||
// Detacher is an interface that event listeners implement. After the Detach
|
||||
// method is called, the listener will no longer receive events.
|
||||
type Detacher interface {
|
||||
Detach()
|
||||
}
|
||||
|
||||
type eventMultiplexerItem struct {
|
||||
mux *eventMultiplexer
|
||||
prev, next *eventMultiplexerItem
|
||||
listener EventListener
|
||||
}
|
||||
|
||||
func (emi *eventMultiplexerItem) Detach() {
|
||||
if emi.prev == nil {
|
||||
emi.mux.head = emi.next
|
||||
} else {
|
||||
emi.prev.next = emi.next
|
||||
}
|
||||
if emi.next == nil {
|
||||
emi.mux.tail = emi.prev
|
||||
} else {
|
||||
emi.next.prev = emi.prev
|
||||
}
|
||||
}
|
||||
|
||||
type eventMultiplexer struct {
|
||||
head, tail *eventMultiplexerItem
|
||||
}
|
||||
|
||||
func (em *eventMultiplexer) Attach(listener EventListener) Detacher {
|
||||
item := &eventMultiplexerItem{
|
||||
mux: em,
|
||||
prev: em.tail,
|
||||
listener: listener,
|
||||
}
|
||||
if em.head == nil {
|
||||
em.head = item
|
||||
}
|
||||
if em.tail != nil {
|
||||
em.tail.next = item
|
||||
}
|
||||
em.tail = item
|
||||
return item
|
||||
}
|
||||
|
||||
func (em eventMultiplexer) OnConnect(event *ConnectEvent) {
|
||||
for item := em.head; item != nil; item = item.next {
|
||||
item.listener.OnConnect(event)
|
||||
}
|
||||
}
|
||||
|
||||
func (em eventMultiplexer) OnDisconnect(event *DisconnectEvent) {
|
||||
for item := em.head; item != nil; item = item.next {
|
||||
item.listener.OnDisconnect(event)
|
||||
}
|
||||
}
|
||||
|
||||
func (em eventMultiplexer) OnTextMessage(event *TextMessageEvent) {
|
||||
for item := em.head; item != nil; item = item.next {
|
||||
item.listener.OnTextMessage(event)
|
||||
}
|
||||
}
|
||||
|
||||
func (em eventMultiplexer) OnUserChange(event *UserChangeEvent) {
|
||||
for item := em.head; item != nil; item = item.next {
|
||||
item.listener.OnUserChange(event)
|
||||
}
|
||||
}
|
||||
|
||||
func (em eventMultiplexer) OnChannelChange(event *ChannelChangeEvent) {
|
||||
for item := em.head; item != nil; item = item.next {
|
||||
item.listener.OnChannelChange(event)
|
||||
}
|
||||
}
|
||||
|
||||
func (em eventMultiplexer) OnPermissionDenied(event *PermissionDeniedEvent) {
|
||||
for item := em.head; item != nil; item = item.next {
|
||||
item.listener.OnPermissionDenied(event)
|
||||
}
|
||||
}
|
||||
|
||||
func (em eventMultiplexer) OnUserList(event *UserListEvent) {
|
||||
for item := em.head; item != nil; item = item.next {
|
||||
item.listener.OnUserList(event)
|
||||
}
|
||||
}
|
||||
|
||||
func (em eventMultiplexer) OnACL(event *ACLEvent) {
|
||||
for item := em.head; item != nil; item = item.next {
|
||||
item.listener.OnACL(event)
|
||||
}
|
||||
}
|
||||
|
||||
func (em eventMultiplexer) OnBanList(event *BanListEvent) {
|
||||
for item := em.head; item != nil; item = item.next {
|
||||
item.listener.OnBanList(event)
|
||||
}
|
||||
}
|
||||
|
||||
func (em eventMultiplexer) OnContextActionChange(event *ContextActionChangeEvent) {
|
||||
for item := em.head; item != nil; item = item.next {
|
||||
item.listener.OnContextActionChange(event)
|
||||
}
|
||||
}
|
962
Godeps/_workspace/src/github.com/layeh/gumble/gumble/handlers.go
generated
vendored
Normal file
962
Godeps/_workspace/src/github.com/layeh/gumble/gumble/handlers.go
generated
vendored
Normal file
|
@ -0,0 +1,962 @@
|
|||
package gumble
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/x509"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"math"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/layeh/gopus"
|
||||
"github.com/layeh/gumble/gumble/MumbleProto"
|
||||
"github.com/layeh/gumble/gumble/varint"
|
||||
)
|
||||
|
||||
type handlerFunc func(*Client, []byte) error
|
||||
|
||||
var (
|
||||
errUnimplementedHandler = errors.New("the handler has not been implemented")
|
||||
errIncompleteProtobuf = errors.New("protobuf message is missing a required field")
|
||||
errInvalidProtobuf = errors.New("protobuf message has an invalid field")
|
||||
errUnsupportedAudio = errors.New("unsupported audio codec")
|
||||
)
|
||||
|
||||
var handlers = map[uint16]handlerFunc{
|
||||
0: (*Client).handleVersion,
|
||||
1: (*Client).handleUdpTunnel,
|
||||
2: (*Client).handleAuthenticate,
|
||||
3: (*Client).handlePing,
|
||||
4: (*Client).handleReject,
|
||||
5: (*Client).handleServerSync,
|
||||
6: (*Client).handleChannelRemove,
|
||||
7: (*Client).handleChannelState,
|
||||
8: (*Client).handleUserRemove,
|
||||
9: (*Client).handleUserState,
|
||||
10: (*Client).handleBanList,
|
||||
11: (*Client).handleTextMessage,
|
||||
12: (*Client).handlePermissionDenied,
|
||||
13: (*Client).handleACL,
|
||||
14: (*Client).handleQueryUsers,
|
||||
15: (*Client).handleCryptSetup,
|
||||
16: (*Client).handleContextActionModify,
|
||||
17: (*Client).handleContextAction,
|
||||
18: (*Client).handleUserList,
|
||||
19: (*Client).handleVoiceTarget,
|
||||
20: (*Client).handlePermissionQuery,
|
||||
21: (*Client).handleCodecVersion,
|
||||
22: (*Client).handleUserStats,
|
||||
23: (*Client).handleRequestBlob,
|
||||
24: (*Client).handleServerConfig,
|
||||
25: (*Client).handleSuggestConfig,
|
||||
}
|
||||
|
||||
func parseVersion(packet *MumbleProto.Version) Version {
|
||||
var version Version
|
||||
if packet.Version != nil {
|
||||
version.Version = *packet.Version
|
||||
}
|
||||
if packet.Release != nil {
|
||||
version.Release = *packet.Release
|
||||
}
|
||||
if packet.Os != nil {
|
||||
version.OS = *packet.Os
|
||||
}
|
||||
if packet.OsVersion != nil {
|
||||
version.OSVersion = *packet.OsVersion
|
||||
}
|
||||
return version
|
||||
}
|
||||
|
||||
func (c *Client) handleVersion(buffer []byte) error {
|
||||
var packet MumbleProto.Version
|
||||
if err := proto.Unmarshal(buffer, &packet); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) handleUdpTunnel(buffer []byte) error {
|
||||
reader := bytes.NewReader(buffer)
|
||||
var bytesRead int64
|
||||
|
||||
var audioType byte
|
||||
var audioTarget byte
|
||||
var user *User
|
||||
var audioLength int
|
||||
|
||||
// Header byte
|
||||
typeTarget, err := varint.ReadByte(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
audioType = (typeTarget >> 5) & 0x7
|
||||
audioTarget = typeTarget & 0x1F
|
||||
// Opus only
|
||||
if audioType != 4 {
|
||||
return errUnsupportedAudio
|
||||
}
|
||||
bytesRead++
|
||||
|
||||
// Session
|
||||
session, n, err := varint.ReadFrom(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
user = c.Users[uint32(session)]
|
||||
if user == nil {
|
||||
return errInvalidProtobuf
|
||||
}
|
||||
bytesRead += n
|
||||
|
||||
// Sequence
|
||||
sequence, n, err := varint.ReadFrom(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bytesRead += n
|
||||
|
||||
// Length
|
||||
length, n, err := varint.ReadFrom(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Opus audio packets set the 13th bit in the size field as the terminator.
|
||||
audioLength = int(length) &^ 0x2000
|
||||
if audioLength > reader.Len() {
|
||||
return errInvalidProtobuf
|
||||
}
|
||||
audioLength64 := int64(audioLength)
|
||||
bytesRead += n
|
||||
|
||||
opus := buffer[bytesRead : bytesRead+audioLength64]
|
||||
pcm, err := user.decoder.Decode(opus, AudioMaximumFrameSize, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
event := AudioPacketEvent{
|
||||
Client: c,
|
||||
}
|
||||
event.AudioPacket.Sender = user
|
||||
event.AudioPacket.Target = int(audioTarget)
|
||||
event.AudioPacket.Sequence = int(sequence)
|
||||
event.AudioPacket.PositionalAudioBuffer.AudioBuffer = pcm
|
||||
|
||||
reader.Seek(audioLength64, 1)
|
||||
binary.Read(reader, binary.LittleEndian, &event.AudioPacket.PositionalAudioBuffer.X)
|
||||
binary.Read(reader, binary.LittleEndian, &event.AudioPacket.PositionalAudioBuffer.Y)
|
||||
binary.Read(reader, binary.LittleEndian, &event.AudioPacket.PositionalAudioBuffer.Z)
|
||||
|
||||
c.audioListeners.OnAudioPacket(&event)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) handleAuthenticate(buffer []byte) error {
|
||||
return errUnimplementedHandler
|
||||
}
|
||||
|
||||
func (c *Client) handlePing(buffer []byte) error {
|
||||
var packet MumbleProto.Ping
|
||||
if err := proto.Unmarshal(buffer, &packet); err != nil {
|
||||
return err
|
||||
}
|
||||
c.pingStats.TCPPackets++
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) handleReject(buffer []byte) error {
|
||||
var packet MumbleProto.Reject
|
||||
if err := proto.Unmarshal(buffer, &packet); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if packet.Type != nil {
|
||||
c.disconnectEvent.Type = DisconnectType(*packet.Type)
|
||||
}
|
||||
if packet.Reason != nil {
|
||||
c.disconnectEvent.String = *packet.Reason
|
||||
}
|
||||
c.Conn.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) handleServerSync(buffer []byte) error {
|
||||
var packet MumbleProto.ServerSync
|
||||
if err := proto.Unmarshal(buffer, &packet); err != nil {
|
||||
return err
|
||||
}
|
||||
event := ConnectEvent{
|
||||
Client: c,
|
||||
}
|
||||
|
||||
if packet.Session != nil {
|
||||
c.Self = c.Users[*packet.Session]
|
||||
}
|
||||
if packet.WelcomeText != nil {
|
||||
event.WelcomeMessage = *packet.WelcomeText
|
||||
}
|
||||
if packet.MaxBandwidth != nil {
|
||||
event.MaximumBitrate = int(*packet.MaxBandwidth)
|
||||
}
|
||||
c.State = StateSynced
|
||||
|
||||
c.listeners.OnConnect(&event)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) handleChannelRemove(buffer []byte) error {
|
||||
var packet MumbleProto.ChannelRemove
|
||||
if err := proto.Unmarshal(buffer, &packet); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if packet.ChannelId == nil {
|
||||
return errIncompleteProtobuf
|
||||
}
|
||||
var channel *Channel
|
||||
{
|
||||
channelID := *packet.ChannelId
|
||||
channel = c.Channels[channelID]
|
||||
if channel == nil {
|
||||
return errInvalidProtobuf
|
||||
}
|
||||
delete(c.Channels, channelID)
|
||||
delete(c.permissions, channelID)
|
||||
if parent := channel.Parent; parent != nil {
|
||||
delete(parent.Children, channel.ID)
|
||||
}
|
||||
}
|
||||
|
||||
if c.State == StateSynced {
|
||||
event := ChannelChangeEvent{
|
||||
Client: c,
|
||||
Type: ChannelChangeRemoved,
|
||||
Channel: channel,
|
||||
}
|
||||
c.listeners.OnChannelChange(&event)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) handleChannelState(buffer []byte) error {
|
||||
var packet MumbleProto.ChannelState
|
||||
if err := proto.Unmarshal(buffer, &packet); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if packet.ChannelId == nil {
|
||||
return errIncompleteProtobuf
|
||||
}
|
||||
event := ChannelChangeEvent{
|
||||
Client: c,
|
||||
}
|
||||
channelID := *packet.ChannelId
|
||||
channel := c.Channels[channelID]
|
||||
if channel == nil {
|
||||
channel = c.Channels.create(channelID)
|
||||
channel.client = c
|
||||
|
||||
event.Type |= ChannelChangeCreated
|
||||
}
|
||||
event.Channel = channel
|
||||
if packet.Parent != nil {
|
||||
if channel.Parent != nil {
|
||||
delete(channel.Parent.Children, channelID)
|
||||
}
|
||||
newParent := c.Channels[*packet.Parent]
|
||||
if newParent != channel.Parent {
|
||||
event.Type |= ChannelChangeMoved
|
||||
}
|
||||
channel.Parent = newParent
|
||||
if channel.Parent != nil {
|
||||
channel.Parent.Children[channel.ID] = channel
|
||||
}
|
||||
}
|
||||
if packet.Name != nil {
|
||||
if *packet.Name != channel.Name {
|
||||
event.Type |= ChannelChangeName
|
||||
}
|
||||
channel.Name = *packet.Name
|
||||
}
|
||||
if packet.Description != nil {
|
||||
if *packet.Description != channel.Description {
|
||||
event.Type |= ChannelChangeDescription
|
||||
}
|
||||
channel.Description = *packet.Description
|
||||
channel.DescriptionHash = nil
|
||||
}
|
||||
if packet.Temporary != nil {
|
||||
channel.Temporary = *packet.Temporary
|
||||
}
|
||||
if packet.Position != nil {
|
||||
if *packet.Position != channel.Position {
|
||||
event.Type |= ChannelChangePosition
|
||||
}
|
||||
channel.Position = *packet.Position
|
||||
}
|
||||
if packet.DescriptionHash != nil {
|
||||
event.Type |= ChannelChangeDescription
|
||||
channel.DescriptionHash = packet.DescriptionHash
|
||||
channel.Description = ""
|
||||
}
|
||||
|
||||
if c.State == StateSynced {
|
||||
c.listeners.OnChannelChange(&event)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) handleUserRemove(buffer []byte) error {
|
||||
var packet MumbleProto.UserRemove
|
||||
if err := proto.Unmarshal(buffer, &packet); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if packet.Session == nil {
|
||||
return errIncompleteProtobuf
|
||||
}
|
||||
event := UserChangeEvent{
|
||||
Client: c,
|
||||
Type: UserChangeDisconnected,
|
||||
}
|
||||
{
|
||||
session := *packet.Session
|
||||
event.User = c.Users[session]
|
||||
if event.User == nil {
|
||||
return errInvalidProtobuf
|
||||
}
|
||||
if event.User.Channel != nil {
|
||||
delete(event.User.Channel.Users, session)
|
||||
}
|
||||
delete(c.Users, session)
|
||||
}
|
||||
if packet.Actor != nil {
|
||||
event.Actor = c.Users[*packet.Actor]
|
||||
if event.Actor == nil {
|
||||
return errInvalidProtobuf
|
||||
}
|
||||
event.Type |= UserChangeKicked
|
||||
}
|
||||
if packet.Reason != nil {
|
||||
event.String = *packet.Reason
|
||||
}
|
||||
if packet.Ban != nil && *packet.Ban {
|
||||
event.Type |= UserChangeBanned
|
||||
}
|
||||
if event.User == c.Self {
|
||||
if packet.Ban != nil && *packet.Ban {
|
||||
c.disconnectEvent.Type = DisconnectBanned
|
||||
} else {
|
||||
c.disconnectEvent.Type = DisconnectKicked
|
||||
}
|
||||
}
|
||||
|
||||
if c.State == StateSynced {
|
||||
c.listeners.OnUserChange(&event)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) handleUserState(buffer []byte) error {
|
||||
var packet MumbleProto.UserState
|
||||
if err := proto.Unmarshal(buffer, &packet); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if packet.Session == nil {
|
||||
return errIncompleteProtobuf
|
||||
}
|
||||
event := UserChangeEvent{
|
||||
Client: c,
|
||||
}
|
||||
var user, actor *User
|
||||
{
|
||||
session := *packet.Session
|
||||
user = c.Users[session]
|
||||
if user == nil {
|
||||
user = c.Users.create(session)
|
||||
user.Channel = c.Channels[0]
|
||||
user.client = c
|
||||
|
||||
event.Type |= UserChangeConnected
|
||||
|
||||
decoder, _ := gopus.NewDecoder(AudioSampleRate, 1)
|
||||
user.decoder = decoder
|
||||
|
||||
if user.Channel == nil {
|
||||
return errInvalidProtobuf
|
||||
}
|
||||
event.Type |= UserChangeChannel
|
||||
user.Channel.Users[session] = user
|
||||
}
|
||||
}
|
||||
event.User = user
|
||||
if packet.Actor != nil {
|
||||
actor = c.Users[*packet.Actor]
|
||||
if actor == nil {
|
||||
return errInvalidProtobuf
|
||||
}
|
||||
event.Actor = actor
|
||||
}
|
||||
if packet.Name != nil {
|
||||
if *packet.Name != user.Name {
|
||||
event.Type |= UserChangeName
|
||||
}
|
||||
user.Name = *packet.Name
|
||||
}
|
||||
if packet.UserId != nil {
|
||||
if *packet.UserId != user.UserID && !event.Type.Has(UserChangeConnected) {
|
||||
if *packet.UserId != math.MaxUint32 {
|
||||
event.Type |= UserChangeRegistered
|
||||
user.UserID = *packet.UserId
|
||||
} else {
|
||||
event.Type |= UserChangeUnregistered
|
||||
user.UserID = 0
|
||||
}
|
||||
} else {
|
||||
user.UserID = *packet.UserId
|
||||
}
|
||||
}
|
||||
if packet.ChannelId != nil {
|
||||
if user.Channel != nil {
|
||||
delete(user.Channel.Users, user.Session)
|
||||
}
|
||||
newChannel := c.Channels[*packet.ChannelId]
|
||||
if newChannel == nil {
|
||||
return errInvalidProtobuf
|
||||
}
|
||||
if newChannel != user.Channel {
|
||||
event.Type |= UserChangeChannel
|
||||
user.Channel = newChannel
|
||||
}
|
||||
user.Channel.Users[user.Session] = user
|
||||
}
|
||||
if packet.Mute != nil {
|
||||
if *packet.Mute != user.Muted {
|
||||
event.Type |= UserChangeAudio
|
||||
}
|
||||
user.Muted = *packet.Mute
|
||||
}
|
||||
if packet.Deaf != nil {
|
||||
if *packet.Deaf != user.Deafened {
|
||||
event.Type |= UserChangeAudio
|
||||
}
|
||||
user.Deafened = *packet.Deaf
|
||||
}
|
||||
if packet.Suppress != nil {
|
||||
if *packet.Suppress != user.Suppressed {
|
||||
event.Type |= UserChangeAudio
|
||||
}
|
||||
user.Suppressed = *packet.Suppress
|
||||
}
|
||||
if packet.SelfMute != nil {
|
||||
if *packet.SelfMute != user.SelfMuted {
|
||||
event.Type |= UserChangeAudio
|
||||
}
|
||||
user.SelfMuted = *packet.SelfMute
|
||||
}
|
||||
if packet.SelfDeaf != nil {
|
||||
if *packet.SelfDeaf != user.SelfDeafened {
|
||||
event.Type |= UserChangeAudio
|
||||
}
|
||||
user.SelfDeafened = *packet.SelfDeaf
|
||||
}
|
||||
if packet.Texture != nil {
|
||||
event.Type |= UserChangeTexture
|
||||
user.Texture = packet.Texture
|
||||
user.TextureHash = nil
|
||||
}
|
||||
if packet.Comment != nil {
|
||||
if *packet.Comment != user.Comment {
|
||||
event.Type |= UserChangeComment
|
||||
}
|
||||
user.Comment = *packet.Comment
|
||||
user.CommentHash = nil
|
||||
}
|
||||
if packet.Hash != nil {
|
||||
user.Hash = *packet.Hash
|
||||
}
|
||||
if packet.CommentHash != nil {
|
||||
event.Type |= UserChangeComment
|
||||
user.CommentHash = packet.CommentHash
|
||||
user.Comment = ""
|
||||
}
|
||||
if packet.TextureHash != nil {
|
||||
event.Type |= UserChangeTexture
|
||||
user.TextureHash = packet.TextureHash
|
||||
user.Texture = nil
|
||||
}
|
||||
if packet.PrioritySpeaker != nil {
|
||||
if *packet.PrioritySpeaker != user.PrioritySpeaker {
|
||||
event.Type |= UserChangePrioritySpeaker
|
||||
}
|
||||
user.PrioritySpeaker = *packet.PrioritySpeaker
|
||||
}
|
||||
if packet.Recording != nil {
|
||||
if *packet.Recording != user.Recording {
|
||||
event.Type |= UserChangeRecording
|
||||
}
|
||||
user.Recording = *packet.Recording
|
||||
}
|
||||
|
||||
if c.State == StateSynced {
|
||||
c.listeners.OnUserChange(&event)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) handleBanList(buffer []byte) error {
|
||||
var packet MumbleProto.BanList
|
||||
if err := proto.Unmarshal(buffer, &packet); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
event := BanListEvent{
|
||||
Client: c,
|
||||
BanList: make(BanList, 0, len(packet.Bans)),
|
||||
}
|
||||
|
||||
for _, banPacket := range packet.Bans {
|
||||
ban := &Ban{
|
||||
Address: net.IP(banPacket.Address),
|
||||
}
|
||||
if banPacket.Mask != nil {
|
||||
size := net.IPv4len * 8
|
||||
if len(ban.Address) == net.IPv6len {
|
||||
size = net.IPv6len * 8
|
||||
}
|
||||
ban.Mask = net.CIDRMask(int(*banPacket.Mask), size)
|
||||
}
|
||||
if banPacket.Name != nil {
|
||||
ban.Name = *banPacket.Name
|
||||
}
|
||||
if banPacket.Hash != nil {
|
||||
ban.Hash = *banPacket.Hash
|
||||
}
|
||||
if banPacket.Reason != nil {
|
||||
ban.Reason = *banPacket.Reason
|
||||
}
|
||||
if banPacket.Start != nil {
|
||||
ban.Start, _ = time.Parse(time.RFC3339, *banPacket.Start)
|
||||
}
|
||||
if banPacket.Duration != nil {
|
||||
ban.Duration = time.Duration(*banPacket.Duration) * time.Second
|
||||
}
|
||||
event.BanList = append(event.BanList, ban)
|
||||
}
|
||||
|
||||
c.listeners.OnBanList(&event)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) handleTextMessage(buffer []byte) error {
|
||||
var packet MumbleProto.TextMessage
|
||||
if err := proto.Unmarshal(buffer, &packet); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
event := TextMessageEvent{
|
||||
Client: c,
|
||||
}
|
||||
if packet.Actor != nil {
|
||||
event.Sender = c.Users[*packet.Actor]
|
||||
}
|
||||
if packet.Session != nil {
|
||||
event.Users = make([]*User, 0, len(packet.Session))
|
||||
for _, session := range packet.Session {
|
||||
if user := c.Users[session]; user != nil {
|
||||
event.Users = append(event.Users, user)
|
||||
}
|
||||
}
|
||||
}
|
||||
if packet.ChannelId != nil {
|
||||
event.Channels = make([]*Channel, 0, len(packet.ChannelId))
|
||||
for _, id := range packet.ChannelId {
|
||||
if channel := c.Channels[id]; channel != nil {
|
||||
event.Channels = append(event.Channels, channel)
|
||||
}
|
||||
}
|
||||
}
|
||||
if packet.TreeId != nil {
|
||||
event.Trees = make([]*Channel, 0, len(packet.TreeId))
|
||||
for _, id := range packet.TreeId {
|
||||
if channel := c.Channels[id]; channel != nil {
|
||||
event.Trees = append(event.Trees, channel)
|
||||
}
|
||||
}
|
||||
}
|
||||
if packet.Message != nil {
|
||||
event.Message = *packet.Message
|
||||
}
|
||||
|
||||
c.listeners.OnTextMessage(&event)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) handlePermissionDenied(buffer []byte) error {
|
||||
var packet MumbleProto.PermissionDenied
|
||||
if err := proto.Unmarshal(buffer, &packet); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if packet.Type == nil || *packet.Type == MumbleProto.PermissionDenied_H9K {
|
||||
return errInvalidProtobuf
|
||||
}
|
||||
|
||||
event := PermissionDeniedEvent{
|
||||
Client: c,
|
||||
Type: PermissionDeniedType(*packet.Type),
|
||||
}
|
||||
if packet.Reason != nil {
|
||||
event.String = *packet.Reason
|
||||
}
|
||||
if packet.Name != nil {
|
||||
event.String = *packet.Name
|
||||
}
|
||||
if packet.Session != nil {
|
||||
event.User = c.Users[*packet.Session]
|
||||
if event.User == nil {
|
||||
return errInvalidProtobuf
|
||||
}
|
||||
}
|
||||
if packet.ChannelId != nil {
|
||||
event.Channel = c.Channels[*packet.ChannelId]
|
||||
if event.Channel == nil {
|
||||
return errInvalidProtobuf
|
||||
}
|
||||
}
|
||||
if packet.Permission != nil {
|
||||
event.Permission = Permission(*packet.Permission)
|
||||
}
|
||||
|
||||
c.listeners.OnPermissionDenied(&event)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) handleACL(buffer []byte) error {
|
||||
var packet MumbleProto.ACL
|
||||
if err := proto.Unmarshal(buffer, &packet); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
acl := &ACL{
|
||||
Inherits: packet.GetInheritAcls(),
|
||||
}
|
||||
if packet.ChannelId == nil {
|
||||
return errInvalidProtobuf
|
||||
}
|
||||
acl.Channel = c.Channels[*packet.ChannelId]
|
||||
if acl.Channel == nil {
|
||||
return errInvalidProtobuf
|
||||
}
|
||||
|
||||
if packet.Groups != nil {
|
||||
acl.Groups = make([]*ACLGroup, 0, len(packet.Groups))
|
||||
for _, group := range packet.Groups {
|
||||
aclGroup := &ACLGroup{
|
||||
Name: *group.Name,
|
||||
Inherited: group.GetInherited(),
|
||||
InheritUsers: group.GetInherit(),
|
||||
Inheritable: group.GetInheritable(),
|
||||
}
|
||||
if group.Add != nil {
|
||||
aclGroup.usersAdd = make(map[uint32]*ACLUser)
|
||||
for _, userID := range group.Add {
|
||||
aclGroup.usersAdd[userID] = &ACLUser{
|
||||
UserID: userID,
|
||||
}
|
||||
}
|
||||
}
|
||||
if group.Remove != nil {
|
||||
aclGroup.usersRemove = make(map[uint32]*ACLUser)
|
||||
for _, userID := range group.Remove {
|
||||
aclGroup.usersRemove[userID] = &ACLUser{
|
||||
UserID: userID,
|
||||
}
|
||||
}
|
||||
}
|
||||
if group.InheritedMembers != nil {
|
||||
aclGroup.usersInherited = make(map[uint32]*ACLUser)
|
||||
for _, userID := range group.InheritedMembers {
|
||||
aclGroup.usersInherited[userID] = &ACLUser{
|
||||
UserID: userID,
|
||||
}
|
||||
}
|
||||
}
|
||||
acl.Groups = append(acl.Groups, aclGroup)
|
||||
}
|
||||
}
|
||||
if packet.Acls != nil {
|
||||
acl.Rules = make([]*ACLRule, 0, len(packet.Acls))
|
||||
for _, rule := range packet.Acls {
|
||||
aclRule := &ACLRule{
|
||||
AppliesCurrent: rule.GetApplyHere(),
|
||||
AppliesChildren: rule.GetApplySubs(),
|
||||
Inherited: rule.GetInherited(),
|
||||
Granted: Permission(rule.GetGrant()),
|
||||
Denied: Permission(rule.GetDeny()),
|
||||
}
|
||||
if rule.UserId != nil {
|
||||
aclRule.User = &ACLUser{
|
||||
UserID: *rule.UserId,
|
||||
}
|
||||
} else if rule.Group != nil {
|
||||
var group *ACLGroup
|
||||
for _, g := range acl.Groups {
|
||||
if g.Name == *rule.Group {
|
||||
group = g
|
||||
break
|
||||
}
|
||||
}
|
||||
if group == nil {
|
||||
group = &ACLGroup{
|
||||
Name: *rule.Group,
|
||||
}
|
||||
}
|
||||
aclRule.Group = group
|
||||
}
|
||||
acl.Rules = append(acl.Rules, aclRule)
|
||||
}
|
||||
}
|
||||
c.tmpACL = acl
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) handleQueryUsers(buffer []byte) error {
|
||||
var packet MumbleProto.QueryUsers
|
||||
if err := proto.Unmarshal(buffer, &packet); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
acl := c.tmpACL
|
||||
if acl == nil {
|
||||
return errIncompleteProtobuf
|
||||
}
|
||||
c.tmpACL = nil
|
||||
|
||||
userMap := make(map[uint32]string)
|
||||
for i := 0; i < len(packet.Ids) && i < len(packet.Names); i++ {
|
||||
userMap[packet.Ids[i]] = packet.Names[i]
|
||||
}
|
||||
|
||||
for _, group := range acl.Groups {
|
||||
for _, user := range group.usersAdd {
|
||||
user.Name = userMap[user.UserID]
|
||||
}
|
||||
for _, user := range group.usersRemove {
|
||||
user.Name = userMap[user.UserID]
|
||||
}
|
||||
for _, user := range group.usersInherited {
|
||||
user.Name = userMap[user.UserID]
|
||||
}
|
||||
}
|
||||
for _, rule := range acl.Rules {
|
||||
if rule.User != nil {
|
||||
rule.User.Name = userMap[rule.User.UserID]
|
||||
}
|
||||
}
|
||||
|
||||
event := ACLEvent{
|
||||
Client: c,
|
||||
ACL: acl,
|
||||
}
|
||||
c.listeners.OnACL(&event)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) handleCryptSetup(buffer []byte) error {
|
||||
return errUnimplementedHandler
|
||||
}
|
||||
|
||||
func (c *Client) handleContextActionModify(buffer []byte) error {
|
||||
var packet MumbleProto.ContextActionModify
|
||||
if err := proto.Unmarshal(buffer, &packet); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if packet.Action == nil || packet.Operation == nil {
|
||||
return errInvalidProtobuf
|
||||
}
|
||||
|
||||
event := ContextActionChangeEvent{
|
||||
Client: c,
|
||||
}
|
||||
|
||||
switch *packet.Operation {
|
||||
case MumbleProto.ContextActionModify_Add:
|
||||
if ca := c.ContextActions[*packet.Action]; ca != nil {
|
||||
return nil
|
||||
}
|
||||
event.Type = ContextActionAdd
|
||||
contextAction := c.ContextActions.create(*packet.Action)
|
||||
if packet.Text != nil {
|
||||
contextAction.Label = *packet.Text
|
||||
}
|
||||
if packet.Context != nil {
|
||||
contextAction.Type = ContextActionType(*packet.Context)
|
||||
}
|
||||
event.ContextAction = contextAction
|
||||
case MumbleProto.ContextActionModify_Remove:
|
||||
contextAction := c.ContextActions[*packet.Action]
|
||||
if contextAction == nil {
|
||||
return nil
|
||||
}
|
||||
event.Type = ContextActionRemove
|
||||
delete(c.ContextActions, *packet.Action)
|
||||
event.ContextAction = contextAction
|
||||
default:
|
||||
return errInvalidProtobuf
|
||||
}
|
||||
|
||||
c.listeners.OnContextActionChange(&event)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) handleContextAction(buffer []byte) error {
|
||||
return errUnimplementedHandler
|
||||
}
|
||||
|
||||
func (c *Client) handleUserList(buffer []byte) error {
|
||||
var packet MumbleProto.UserList
|
||||
if err := proto.Unmarshal(buffer, &packet); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
event := UserListEvent{
|
||||
Client: c,
|
||||
UserList: make(RegisteredUsers, 0, len(packet.Users)),
|
||||
}
|
||||
|
||||
for _, user := range packet.Users {
|
||||
registeredUser := &RegisteredUser{
|
||||
UserID: *user.UserId,
|
||||
}
|
||||
if user.Name != nil {
|
||||
registeredUser.Name = *user.Name
|
||||
}
|
||||
event.UserList = append(event.UserList, registeredUser)
|
||||
}
|
||||
|
||||
c.listeners.OnUserList(&event)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) handleVoiceTarget(buffer []byte) error {
|
||||
return errUnimplementedHandler
|
||||
}
|
||||
|
||||
func (c *Client) handlePermissionQuery(buffer []byte) error {
|
||||
var packet MumbleProto.PermissionQuery
|
||||
if err := proto.Unmarshal(buffer, &packet); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if packet.Flush != nil && *packet.Flush {
|
||||
oldPermissions := c.permissions
|
||||
c.permissions = make(map[uint32]*Permission)
|
||||
for channelID := range oldPermissions {
|
||||
channel := c.Channels[channelID]
|
||||
event := ChannelChangeEvent{
|
||||
Client: c,
|
||||
Type: ChannelChangePermission,
|
||||
Channel: channel,
|
||||
}
|
||||
c.listeners.OnChannelChange(&event)
|
||||
}
|
||||
}
|
||||
if packet.ChannelId != nil {
|
||||
channel := c.Channels[*packet.ChannelId]
|
||||
if packet.Permissions != nil {
|
||||
p := Permission(*packet.Permissions)
|
||||
c.permissions[channel.ID] = &p
|
||||
event := ChannelChangeEvent{
|
||||
Client: c,
|
||||
Type: ChannelChangePermission,
|
||||
Channel: channel,
|
||||
}
|
||||
c.listeners.OnChannelChange(&event)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) handleCodecVersion(buffer []byte) error {
|
||||
return errUnimplementedHandler
|
||||
}
|
||||
|
||||
func (c *Client) handleUserStats(buffer []byte) error {
|
||||
var packet MumbleProto.UserStats
|
||||
if err := proto.Unmarshal(buffer, &packet); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if packet.Session == nil {
|
||||
return errIncompleteProtobuf
|
||||
}
|
||||
user := c.Users[*packet.Session]
|
||||
if user == nil {
|
||||
return errInvalidProtobuf
|
||||
}
|
||||
|
||||
if user.Stats == nil {
|
||||
user.Stats = &UserStats{}
|
||||
}
|
||||
*user.Stats = UserStats{
|
||||
User: user,
|
||||
}
|
||||
stats := user.Stats
|
||||
|
||||
if packet.Version != nil {
|
||||
stats.Version = parseVersion(packet.Version)
|
||||
}
|
||||
if packet.Onlinesecs != nil {
|
||||
stats.Connected = time.Now().Add(time.Duration(*packet.Onlinesecs) * -time.Second)
|
||||
}
|
||||
if packet.Idlesecs != nil {
|
||||
stats.Idle = time.Duration(*packet.Idlesecs) * time.Second
|
||||
}
|
||||
if packet.Bandwidth != nil {
|
||||
stats.Bandwidth = int(*packet.Bandwidth)
|
||||
}
|
||||
if packet.Address != nil {
|
||||
stats.IP = net.IP(packet.Address)
|
||||
}
|
||||
if packet.Certificates != nil {
|
||||
stats.Certificates = make([]*x509.Certificate, 0, len(packet.Certificates))
|
||||
for _, data := range packet.Certificates {
|
||||
if data != nil {
|
||||
if cert, err := x509.ParseCertificate(data); err == nil {
|
||||
stats.Certificates = append(stats.Certificates, cert)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stats.StrongCertificate = packet.GetStrongCertificate()
|
||||
stats.CELTVersions = packet.GetCeltVersions()
|
||||
if packet.Opus != nil {
|
||||
stats.Opus = *packet.Opus
|
||||
}
|
||||
|
||||
event := UserChangeEvent{
|
||||
Client: c,
|
||||
Type: UserChangeStats,
|
||||
User: user,
|
||||
}
|
||||
|
||||
c.listeners.OnUserChange(&event)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) handleRequestBlob(buffer []byte) error {
|
||||
return errUnimplementedHandler
|
||||
}
|
||||
|
||||
func (c *Client) handleServerConfig(buffer []byte) error {
|
||||
return errUnimplementedHandler
|
||||
}
|
||||
|
||||
func (c *Client) handleSuggestConfig(buffer []byte) error {
|
||||
return errUnimplementedHandler
|
||||
}
|
8
Godeps/_workspace/src/github.com/layeh/gumble/gumble/message.go
generated
vendored
Normal file
8
Godeps/_workspace/src/github.com/layeh/gumble/gumble/message.go
generated
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
package gumble
|
||||
|
||||
// Message is data that be encoded and sent to the server. The following
|
||||
// types implement this interface: AudioBuffer, AccessTokens, BanList,
|
||||
// RegisteredUsers, TextMessage, and VoiceTarget.
|
||||
type Message interface {
|
||||
writeMessage(client *Client) error
|
||||
}
|
27
Godeps/_workspace/src/github.com/layeh/gumble/gumble/permission.go
generated
vendored
Normal file
27
Godeps/_workspace/src/github.com/layeh/gumble/gumble/permission.go
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
package gumble
|
||||
|
||||
// Permission is a bitmask of permissions given to a certain user.
|
||||
type Permission int
|
||||
|
||||
// Permissions that can be applied in any channel.
|
||||
const (
|
||||
PermissionWrite Permission = 1 << iota
|
||||
PermissionTraverse
|
||||
PermissionEnter
|
||||
PermissionSpeak
|
||||
PermissionMuteDeafen
|
||||
PermissionMove
|
||||
PermissionMakeChannel
|
||||
PermissionLinkChannel
|
||||
PermissionWhisper
|
||||
PermissionTextMessage
|
||||
PermissionMakeTemporaryChannel
|
||||
)
|
||||
|
||||
// Permissions that can only be applied in the root channel.
|
||||
const (
|
||||
PermissionKick Permission = 0x10000 << iota
|
||||
PermissionBan
|
||||
PermissionRegister
|
||||
PermissionRegisterSelf
|
||||
)
|
73
Godeps/_workspace/src/github.com/layeh/gumble/gumble/ping.go
generated
vendored
Normal file
73
Godeps/_workspace/src/github.com/layeh/gumble/gumble/ping.go
generated
vendored
Normal file
|
@ -0,0 +1,73 @@
|
|||
package gumble
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
// PingResponse contains information about a server that responded to a UDP
|
||||
// ping packet.
|
||||
type PingResponse struct {
|
||||
// The address of the pinged server.
|
||||
Address *net.UDPAddr
|
||||
// The round-trip time from the client to the server.
|
||||
Ping time.Duration
|
||||
// The server's version. Only the Version field and SemanticVersion method of
|
||||
// the value will be valid.
|
||||
Version Version
|
||||
// The number users currently connected to the server.
|
||||
ConnectedUsers int
|
||||
// The maximum number of users that can connect to the server.
|
||||
MaximumUsers int
|
||||
// The maximum audio bitrate per user for the server.
|
||||
MaximumBitrate int
|
||||
}
|
||||
|
||||
// Ping sends a UDP ping packet to the given server. Returns a PingResponse and
|
||||
// nil on success. The function will return nil and an error if a valid
|
||||
// response is not received after the given timeout.
|
||||
func Ping(address string, timeout time.Duration) (*PingResponse, error) {
|
||||
addr, err := net.ResolveUDPAddr("udp", address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn, err := net.DialUDP("udp", nil, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var packet [12]byte
|
||||
if _, err := rand.Read(packet[4:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
start := time.Now()
|
||||
if _, err := conn.Write(packet[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn.SetReadDeadline(time.Now().Add(timeout))
|
||||
for {
|
||||
var incoming [24]byte
|
||||
if _, err := io.ReadFull(conn, incoming[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !bytes.Equal(incoming[4:12], packet[4:]) {
|
||||
continue
|
||||
}
|
||||
|
||||
return &PingResponse{
|
||||
Address: addr,
|
||||
Ping: time.Since(start),
|
||||
Version: Version{
|
||||
Version: binary.BigEndian.Uint32(incoming[0:]),
|
||||
},
|
||||
ConnectedUsers: int(binary.BigEndian.Uint32(incoming[12:])),
|
||||
MaximumUsers: int(binary.BigEndian.Uint32(incoming[16:])),
|
||||
MaximumBitrate: int(binary.BigEndian.Uint32(incoming[20:])),
|
||||
}, nil
|
||||
}
|
||||
}
|
5
Godeps/_workspace/src/github.com/layeh/gumble/gumble/pingstats.go
generated
vendored
Normal file
5
Godeps/_workspace/src/github.com/layeh/gumble/gumble/pingstats.go
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
package gumble
|
||||
|
||||
type pingStats struct {
|
||||
TCPPackets uint32
|
||||
}
|
16
Godeps/_workspace/src/github.com/layeh/gumble/gumble/protomessage.go
generated
vendored
Normal file
16
Godeps/_workspace/src/github.com/layeh/gumble/gumble/protomessage.go
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
package gumble
|
||||
|
||||
import (
|
||||
"github.com/golang/protobuf/proto"
|
||||
)
|
||||
|
||||
type protoMessage struct {
|
||||
proto.Message
|
||||
}
|
||||
|
||||
func (pm protoMessage) writeMessage(client *Client) error {
|
||||
if err := client.Conn.WriteProto(pm.Message); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
18
Godeps/_workspace/src/github.com/layeh/gumble/gumble/request.go
generated
vendored
Normal file
18
Godeps/_workspace/src/github.com/layeh/gumble/gumble/request.go
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
package gumble
|
||||
|
||||
// Request is a mask of items that the client can ask the server to send.
|
||||
type Request int
|
||||
|
||||
// Items that can be requested from the server. See the documentation for
|
||||
// Channel.Request, Client.Request, and User.Request to see which request types
|
||||
// each one supports.
|
||||
const (
|
||||
RequestDescription Request = 1 << iota
|
||||
RequestComment
|
||||
RequestTexture
|
||||
RequestStats
|
||||
RequestUserList
|
||||
RequestACL
|
||||
RequestBanList
|
||||
RequestPermission
|
||||
)
|
50
Godeps/_workspace/src/github.com/layeh/gumble/gumble/textmessage.go
generated
vendored
Normal file
50
Godeps/_workspace/src/github.com/layeh/gumble/gumble/textmessage.go
generated
vendored
Normal file
|
@ -0,0 +1,50 @@
|
|||
package gumble
|
||||
|
||||
import (
|
||||
"github.com/layeh/gumble/gumble/MumbleProto"
|
||||
)
|
||||
|
||||
// TextMessage is a chat message that can be received from and sent to the
|
||||
// server.
|
||||
type TextMessage struct {
|
||||
// User who sent the message (can be nil).
|
||||
Sender *User
|
||||
|
||||
// Users that receive the message.
|
||||
Users []*User
|
||||
|
||||
// Channels that receive the message.
|
||||
Channels []*Channel
|
||||
|
||||
// Channels that receive the message and send it recursively to sub-channels.
|
||||
Trees []*Channel
|
||||
|
||||
// Chat message.
|
||||
Message string
|
||||
}
|
||||
|
||||
func (tm *TextMessage) writeMessage(client *Client) error {
|
||||
packet := MumbleProto.TextMessage{
|
||||
Message: &tm.Message,
|
||||
}
|
||||
if tm.Users != nil {
|
||||
packet.Session = make([]uint32, len(tm.Users))
|
||||
for i, user := range tm.Users {
|
||||
packet.Session[i] = user.Session
|
||||
}
|
||||
}
|
||||
if tm.Channels != nil {
|
||||
packet.ChannelId = make([]uint32, len(tm.Channels))
|
||||
for i, channel := range tm.Channels {
|
||||
packet.ChannelId[i] = channel.ID
|
||||
}
|
||||
}
|
||||
if tm.Trees != nil {
|
||||
packet.TreeId = make([]uint32, len(tm.Trees))
|
||||
for i, channel := range tm.Trees {
|
||||
packet.TreeId[i] = channel.ID
|
||||
}
|
||||
}
|
||||
proto := protoMessage{&packet}
|
||||
return proto.writeMessage(client)
|
||||
}
|
231
Godeps/_workspace/src/github.com/layeh/gumble/gumble/user.go
generated
vendored
Normal file
231
Godeps/_workspace/src/github.com/layeh/gumble/gumble/user.go
generated
vendored
Normal file
|
@ -0,0 +1,231 @@
|
|||
package gumble
|
||||
|
||||
import (
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/layeh/gopus"
|
||||
"github.com/layeh/gumble/gumble/MumbleProto"
|
||||
)
|
||||
|
||||
// User represents a user that is currently connected to the server.
|
||||
type User struct {
|
||||
// The user's unique session ID.
|
||||
Session uint32
|
||||
// The user's ID. Contains an invalid value if the user is not registered.
|
||||
UserID uint32
|
||||
// The user's name.
|
||||
Name string
|
||||
// The channel that the user is currently in.
|
||||
Channel *Channel
|
||||
|
||||
// Has the user has been muted?
|
||||
Muted bool
|
||||
// Has the user been deafened?
|
||||
Deafened bool
|
||||
// Has the user been suppressed?
|
||||
Suppressed bool
|
||||
// Has the user been muted by him/herself?
|
||||
SelfMuted bool
|
||||
// Has the user been deafened by him/herself?
|
||||
SelfDeafened bool
|
||||
// Is the user a priority speaker in the channel?
|
||||
PrioritySpeaker bool
|
||||
// Is the user recording audio?
|
||||
Recording bool
|
||||
|
||||
// The user's comment. Contains the empty string if the user does not have a
|
||||
// comment, or if the comment needs to be requested.
|
||||
Comment string
|
||||
// The user's comment hash. Contains nil if User.Comment has been populated.
|
||||
CommentHash []byte
|
||||
// The hash of the user's certificate (can be empty).
|
||||
Hash string
|
||||
// The user's texture (avatar). Contains nil if the user does not have a
|
||||
// texture, or if the texture needs to be requested.
|
||||
Texture []byte
|
||||
// The user's texture hash. Contains nil if User.Texture has been populated.
|
||||
TextureHash []byte
|
||||
|
||||
// The user's stats. Containts nil if the stats have not yet been requested.
|
||||
Stats *UserStats
|
||||
|
||||
client *Client
|
||||
decoder *gopus.Decoder
|
||||
}
|
||||
|
||||
// SetTexture sets the user's texture.
|
||||
func (u *User) SetTexture(texture []byte) {
|
||||
packet := MumbleProto.UserState{
|
||||
Session: &u.Session,
|
||||
Texture: texture,
|
||||
}
|
||||
u.client.Send(protoMessage{&packet})
|
||||
}
|
||||
|
||||
// SetPrioritySpeaker sets if the user is a priority speaker in the channel.
|
||||
func (u *User) SetPrioritySpeaker(prioritySpeaker bool) {
|
||||
packet := MumbleProto.UserState{
|
||||
Session: &u.Session,
|
||||
PrioritySpeaker: &prioritySpeaker,
|
||||
}
|
||||
u.client.Send(protoMessage{&packet})
|
||||
}
|
||||
|
||||
// SetRecording sets if the user is recording audio.
|
||||
func (u *User) SetRecording(recording bool) {
|
||||
packet := MumbleProto.UserState{
|
||||
Session: &u.Session,
|
||||
Recording: &recording,
|
||||
}
|
||||
u.client.Send(protoMessage{&packet})
|
||||
}
|
||||
|
||||
// IsRegistered returns true if the user's certificate has been registered with
|
||||
// the server. A registered user will have a valid user ID.
|
||||
func (u *User) IsRegistered() bool {
|
||||
return u.UserID > 0
|
||||
}
|
||||
|
||||
// Register will register the user with the server. If the client has
|
||||
// permission to do so, the user will shortly be given a UserID.
|
||||
func (u *User) Register() {
|
||||
packet := MumbleProto.UserState{
|
||||
Session: &u.Session,
|
||||
UserId: proto.Uint32(0),
|
||||
}
|
||||
u.client.Send(protoMessage{&packet})
|
||||
}
|
||||
|
||||
// SetComment will set the user's comment to the given string. The user's
|
||||
// comment will be erased if the comment is set to the empty string.
|
||||
func (u *User) SetComment(comment string) {
|
||||
packet := MumbleProto.UserState{
|
||||
Session: &u.Session,
|
||||
Comment: &comment,
|
||||
}
|
||||
u.client.Send(protoMessage{&packet})
|
||||
}
|
||||
|
||||
// Move will move the user to the given channel.
|
||||
func (u *User) Move(channel *Channel) {
|
||||
packet := MumbleProto.UserState{
|
||||
Session: &u.Session,
|
||||
ChannelId: &channel.ID,
|
||||
}
|
||||
u.client.Send(protoMessage{&packet})
|
||||
}
|
||||
|
||||
// Kick will kick the user from the server.
|
||||
func (u *User) Kick(reason string) {
|
||||
packet := MumbleProto.UserRemove{
|
||||
Session: &u.Session,
|
||||
Reason: &reason,
|
||||
}
|
||||
u.client.Send(protoMessage{&packet})
|
||||
}
|
||||
|
||||
// Ban will ban the user from the server.
|
||||
func (u *User) Ban(reason string) {
|
||||
packet := MumbleProto.UserRemove{
|
||||
Session: &u.Session,
|
||||
Reason: &reason,
|
||||
Ban: proto.Bool(true),
|
||||
}
|
||||
u.client.Send(protoMessage{&packet})
|
||||
}
|
||||
|
||||
// SetMuted sets whether the user can transmit audio or not.
|
||||
func (u *User) SetMuted(muted bool) {
|
||||
packet := MumbleProto.UserState{
|
||||
Session: &u.Session,
|
||||
Mute: &muted,
|
||||
}
|
||||
u.client.Send(protoMessage{&packet})
|
||||
}
|
||||
|
||||
// SetSuppressed sets whether the user is suppressed by the server or not.
|
||||
func (u *User) SetSuppressed(supressed bool) {
|
||||
packet := MumbleProto.UserState{
|
||||
Session: &u.Session,
|
||||
Suppress: &supressed,
|
||||
}
|
||||
u.client.Send(protoMessage{&packet})
|
||||
}
|
||||
|
||||
// SetDeafened sets whether the user can receive audio or not.
|
||||
func (u *User) SetDeafened(muted bool) {
|
||||
packet := MumbleProto.UserState{
|
||||
Session: &u.Session,
|
||||
Deaf: &muted,
|
||||
}
|
||||
u.client.Send(protoMessage{&packet})
|
||||
}
|
||||
|
||||
// SetSelfMuted sets whether the user can transmit audio or not.
|
||||
//
|
||||
// This method should only be called on Client.Self().
|
||||
func (u *User) SetSelfMuted(muted bool) {
|
||||
packet := MumbleProto.UserState{
|
||||
Session: &u.Session,
|
||||
SelfMute: &muted,
|
||||
}
|
||||
u.client.Send(protoMessage{&packet})
|
||||
}
|
||||
|
||||
// SetSelfDeafened sets whether the user can receive audio or not.
|
||||
//
|
||||
// This method should only be called on Client.Self().
|
||||
func (u *User) SetSelfDeafened(muted bool) {
|
||||
packet := MumbleProto.UserState{
|
||||
Session: &u.Session,
|
||||
SelfDeaf: &muted,
|
||||
}
|
||||
u.client.Send(protoMessage{&packet})
|
||||
}
|
||||
|
||||
// Request requests user information that has not yet been sent to the client.
|
||||
// The supported request types are: RequestStats, RequestTexture, and
|
||||
// RequestComment.
|
||||
func (u *User) Request(request Request) {
|
||||
if (request & RequestStats) != 0 {
|
||||
packet := MumbleProto.UserStats{
|
||||
Session: &u.Session,
|
||||
}
|
||||
u.client.Send(protoMessage{&packet})
|
||||
}
|
||||
|
||||
packet := MumbleProto.RequestBlob{}
|
||||
if (request & RequestTexture) != 0 {
|
||||
packet.SessionTexture = []uint32{u.Session}
|
||||
}
|
||||
if (request & RequestComment) != 0 {
|
||||
packet.SessionComment = []uint32{u.Session}
|
||||
}
|
||||
if packet.SessionTexture != nil || packet.SessionComment != nil {
|
||||
u.client.Send(protoMessage{&packet})
|
||||
}
|
||||
}
|
||||
|
||||
// Send will send a text message to the user.
|
||||
func (u *User) Send(message string) {
|
||||
textMessage := TextMessage{
|
||||
Users: []*User{u},
|
||||
Message: message,
|
||||
}
|
||||
u.client.Send(&textMessage)
|
||||
}
|
||||
|
||||
// SetPlugin sets the user's plugin data.
|
||||
//
|
||||
// Plugins are currently only used for positional audio. Clients will receive
|
||||
// positional audio information from other users if their plugin context is the
|
||||
// same. The official Mumble client sets the context to:
|
||||
//
|
||||
// PluginShortName + "\x00" + AdditionalContextInformation
|
||||
func (u *User) SetPlugin(context []byte, identity string) {
|
||||
packet := MumbleProto.UserState{
|
||||
Session: &u.Session,
|
||||
PluginContext: context,
|
||||
PluginIdentity: &identity,
|
||||
}
|
||||
u.client.Send(protoMessage{&packet})
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue