Merge branch 'master' into win-path

This commit is contained in:
Vasily Romanov
2018-07-24 01:17:34 +03:00
committed by GitHub
19 changed files with 336 additions and 88 deletions
+1
View File
@@ -2,3 +2,4 @@
*_easyjson.go
*.iml
.idea
*.swp
+8 -3
View File
@@ -6,7 +6,7 @@ all: test
.root/src/$(PKG):
mkdir -p $@
for i in $$PWD/* ; do ln -s $$i $@/`basename $$i` ; done
for i in $$PWD/* ; do ln -s $$i $@/`basename $$i` ; done
root: .root/src/$(PKG)
@@ -23,9 +23,11 @@ generate: root build
.root/src/$(PKG)/tests/data.go \
.root/src/$(PKG)/tests/omitempty.go \
.root/src/$(PKG)/tests/nothing.go \
.root/src/$(PKG)/tests/named_type.go
.root/src/$(PKG)/tests/named_type.go \
.root/src/$(PKG)/tests/custom_map_key_type.go \
.root/src/$(PKG)/tests/embedded_type.go
.root/bin/easyjson -all .root/src/$(PKG)/tests/data.go
.root/bin/easyjson -all .root/src/$(PKG)/tests/data.go
.root/bin/easyjson -all .root/src/$(PKG)/tests/nothing.go
.root/bin/easyjson -all .root/src/$(PKG)/tests/errors.go
.root/bin/easyjson -snake_case .root/src/$(PKG)/tests/snake.go
@@ -33,6 +35,9 @@ generate: root build
.root/bin/easyjson -build_tags=use_easyjson .root/src/$(PKG)/benchmark/data.go
.root/bin/easyjson .root/src/$(PKG)/tests/nested_easy.go
.root/bin/easyjson .root/src/$(PKG)/tests/named_type.go
.root/bin/easyjson .root/src/$(PKG)/tests/custom_map_key_type.go
.root/bin/easyjson .root/src/$(PKG)/tests/embedded_type.go
.root/bin/easyjson -disallow_unknown_fields .root/src/$(PKG)/tests/disallow_unknown.go
test: generate root
go test \
+2
View File
@@ -53,6 +53,8 @@ Usage of easyjson:
use lowerCamelCase instead of CamelCase by default
-stubs
only generate stubs for marshaler/unmarshaler funcs
-disallow_unknown_fields
return error if some unknown field in json appeared
```
Using `-all` will generate marshalers/unmarshalers for all Go structs in the
+8 -4
View File
@@ -22,10 +22,11 @@ type Generator struct {
PkgPath, PkgName string
Types []string
NoStdMarshalers bool
SnakeCase bool
LowerCamelCase bool
OmitEmpty bool
NoStdMarshalers bool
SnakeCase bool
LowerCamelCase bool
OmitEmpty bool
DisallowUnknownFields bool
OutName string
BuildTags string
@@ -120,6 +121,9 @@ func (g *Generator) writeMain() (path string, err error) {
if g.NoStdMarshalers {
fmt.Fprintln(f, " g.NoStdMarshalers()")
}
if g.DisallowUnknownFields {
fmt.Fprintln(f, " g.DisallowUnknownFields()")
}
sort.Strings(g.Types)
for _, v := range g.Types {
+14 -12
View File
@@ -27,6 +27,7 @@ var stubs = flag.Bool("stubs", false, "only generate stubs for marshaler/unmarsh
var noformat = flag.Bool("noformat", false, "do not run 'gofmt -w' on output file")
var specifiedName = flag.String("output_filename", "", "specify the filename of the output")
var processPkg = flag.Bool("pkg", false, "process the whole package instead of just the given file")
var disallowUnknownFields = flag.Bool("disallow_unknown_fields", false, "return error if any unknown field in json appeared")
func generate(fname string) (err error) {
fInfo, err := os.Stat(fname)
@@ -60,18 +61,19 @@ func generate(fname string) (err error) {
}
g := bootstrap.Generator{
BuildTags: trimmedBuildTags,
PkgPath: p.PkgPath,
PkgName: p.PkgName,
Types: p.StructNames,
SnakeCase: *snakeCase,
LowerCamelCase: *lowerCamelCase,
NoStdMarshalers: *noStdMarshalers,
OmitEmpty: *omitEmpty,
LeaveTemps: *leaveTemps,
OutName: outName,
StubsOnly: *stubs,
NoFormat: *noformat,
BuildTags: trimmedBuildTags,
PkgPath: p.PkgPath,
PkgName: p.PkgName,
Types: p.StructNames,
SnakeCase: *snakeCase,
LowerCamelCase: *lowerCamelCase,
NoStdMarshalers: *noStdMarshalers,
DisallowUnknownFields: *disallowUnknownFields,
OmitEmpty: *omitEmpty,
LeaveTemps: *leaveTemps,
OutName: outName,
StubsOnly: *stubs,
NoFormat: *noformat,
}
if err := g.Run(); err != nil {
+36 -10
View File
@@ -48,10 +48,12 @@ var primitiveStringDecoders = map[reflect.Kind]string{
reflect.Uint32: "in.Uint32Str()",
reflect.Uint64: "in.Uint64Str()",
reflect.Uintptr: "in.UintptrStr()",
reflect.Float32: "in.Float32Str()",
reflect.Float64: "in.Float64Str()",
}
var customDecoders = map[string]string{
"json.Number": "in.JsonNumber()",
"json.Number": "in.JsonNumber()",
}
// genTypeDecoder generates decoding code for the type t, but uses unmarshaler interface if implemented by t.
@@ -84,11 +86,19 @@ func (g *Generator) genTypeDecoder(t reflect.Type, out string, tags fieldTags, i
return err
}
// returns true of the type t implements one of the custom unmarshaler interfaces
func hasCustomUnmarshaler(t reflect.Type) bool {
t = reflect.PtrTo(t)
return t.Implements(reflect.TypeOf((*easyjson.Unmarshaler)(nil)).Elem()) ||
t.Implements(reflect.TypeOf((*json.Unmarshaler)(nil)).Elem()) ||
t.Implements(reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem())
}
// genTypeDecoderNoCheck generates decoding code for the type t.
func (g *Generator) genTypeDecoderNoCheck(t reflect.Type, out string, tags fieldTags, indent int) error {
ws := strings.Repeat(" ", indent)
// Check whether type is primitive, needs to be done after interface check.
if dec := customDecoders[t.String()]; dec != "" {
if dec := customDecoders[t.String()]; dec != "" {
fmt.Fprintln(g.out, ws+out+" = "+dec)
return nil
} else if dec := primitiveStringDecoders[t.Kind()]; dec != "" && tags.asString {
@@ -104,7 +114,7 @@ func (g *Generator) genTypeDecoderNoCheck(t reflect.Type, out string, tags field
tmpVar := g.uniqueVarName()
elem := t.Elem()
if elem.Kind() == reflect.Uint8 {
if elem.Kind() == reflect.Uint8 && elem.Name() == "uint8" {
fmt.Fprintln(g.out, ws+"if in.IsNull() {")
fmt.Fprintln(g.out, ws+" in.Skip()")
fmt.Fprintln(g.out, ws+" "+out+" = nil")
@@ -151,7 +161,7 @@ func (g *Generator) genTypeDecoderNoCheck(t reflect.Type, out string, tags field
iterVar := g.uniqueVarName()
elem := t.Elem()
if elem.Kind() == reflect.Uint8 {
if elem.Kind() == reflect.Uint8 && elem.Name() == "uint8" {
fmt.Fprintln(g.out, ws+"if in.IsNull() {")
fmt.Fprintln(g.out, ws+" in.Skip()")
fmt.Fprintln(g.out, ws+"} else {")
@@ -170,7 +180,7 @@ func (g *Generator) genTypeDecoderNoCheck(t reflect.Type, out string, tags field
fmt.Fprintln(g.out, ws+" for !in.IsDelim(']') {")
fmt.Fprintln(g.out, ws+" if "+iterVar+" < "+fmt.Sprint(length)+" {")
if err := g.genTypeDecoder(elem, out+"["+iterVar+"]", tags, indent+3); err != nil {
if err := g.genTypeDecoder(elem, "("+out+")["+iterVar+"]", tags, indent+3); err != nil {
return err
}
@@ -208,9 +218,9 @@ func (g *Generator) genTypeDecoderNoCheck(t reflect.Type, out string, tags field
case reflect.Map:
key := t.Key()
keyDec, ok := primitiveStringDecoders[key.Kind()]
if !ok {
return fmt.Errorf("map type %v not supported: only string and integer keys are allowed", key)
}
if !ok && !hasCustomUnmarshaler(key) {
return fmt.Errorf("map type %v not supported: only string and integer keys and types implementing json.Unmarshaler are allowed", key)
} // else assume the caller knows what they are doing and that the custom unmarshaler performs the translation from string or integer keys to the key type
elem := t.Elem()
tmpVar := g.uniqueVarName()
@@ -225,7 +235,15 @@ func (g *Generator) genTypeDecoderNoCheck(t reflect.Type, out string, tags field
fmt.Fprintln(g.out, ws+" }")
fmt.Fprintln(g.out, ws+" for !in.IsDelim('}') {")
fmt.Fprintln(g.out, ws+" key := "+g.getType(key)+"("+keyDec+")")
if keyDec != "" {
fmt.Fprintln(g.out, ws+" key := "+g.getType(key)+"("+keyDec+")")
} else {
fmt.Fprintln(g.out, ws+" var key "+g.getType(key))
if err := g.genTypeDecoder(key, "key", tags, indent+2); err != nil {
return err
}
}
fmt.Fprintln(g.out, ws+" in.WantColon()")
fmt.Fprintln(g.out, ws+" var "+tmpVar+" "+g.getType(elem))
@@ -443,7 +461,15 @@ func (g *Generator) genStructDecoder(t reflect.Type) error {
}
fmt.Fprintln(g.out, " default:")
fmt.Fprintln(g.out, " in.SkipRecursive()")
if g.disallowUnknownFields {
fmt.Fprintln(g.out, ` in.AddError(&jlexer.LexerError{
Offset: in.GetPos(),
Reason: "unknown field",
Data: key,
})`)
} else {
fmt.Fprintln(g.out, " in.SkipRecursive()")
}
fmt.Fprintln(g.out, " }")
fmt.Fprintln(g.out, " in.WantComma()")
fmt.Fprintln(g.out, " }")
+67 -37
View File
@@ -45,6 +45,8 @@ var primitiveStringEncoders = map[reflect.Kind]string{
reflect.Uint32: "out.Uint32Str(uint32(%v))",
reflect.Uint64: "out.Uint64Str(uint64(%v))",
reflect.Uintptr: "out.UintptrStr(uintptr(%v))",
reflect.Float32: "out.Float32Str(float32(%v))",
reflect.Float64: "out.Float64Str(float64(%v))",
}
// fieldTags contains parsed version of json struct field tags.
@@ -83,7 +85,7 @@ func parseFieldTags(f reflect.StructField) fieldTags {
}
// genTypeEncoder generates code that encodes in of type t into the writer, but uses marshaler interface if implemented by t.
func (g *Generator) genTypeEncoder(t reflect.Type, in string, tags fieldTags, indent int) error {
func (g *Generator) genTypeEncoder(t reflect.Type, in string, tags fieldTags, indent int, assumeNonEmpty bool) error {
ws := strings.Repeat(" ", indent)
marshalerIface := reflect.TypeOf((*easyjson.Marshaler)(nil)).Elem()
@@ -104,12 +106,20 @@ func (g *Generator) genTypeEncoder(t reflect.Type, in string, tags fieldTags, in
return nil
}
err := g.genTypeEncoderNoCheck(t, in, tags, indent)
err := g.genTypeEncoderNoCheck(t, in, tags, indent, assumeNonEmpty)
return err
}
// returns true of the type t implements one of the custom marshaler interfaces
func hasCustomMarshaler(t reflect.Type) bool {
t = reflect.PtrTo(t)
return t.Implements(reflect.TypeOf((*easyjson.Marshaler)(nil)).Elem()) ||
t.Implements(reflect.TypeOf((*json.Marshaler)(nil)).Elem()) ||
t.Implements(reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem())
}
// genTypeEncoderNoCheck generates code that encodes in of type t into the writer.
func (g *Generator) genTypeEncoderNoCheck(t reflect.Type, in string, tags fieldTags, indent int) error {
func (g *Generator) genTypeEncoderNoCheck(t reflect.Type, in string, tags fieldTags, indent int, assumeNonEmpty bool) error {
ws := strings.Repeat(" ", indent)
// Check whether type is primitive, needs to be done after interface check.
@@ -127,19 +137,23 @@ func (g *Generator) genTypeEncoderNoCheck(t reflect.Type, in string, tags fieldT
iVar := g.uniqueVarName()
vVar := g.uniqueVarName()
if t.Elem().Kind() == reflect.Uint8 {
if t.Elem().Kind() == reflect.Uint8 && elem.Name() == "uint8" {
fmt.Fprintln(g.out, ws+"out.Base64Bytes("+in+")")
} else {
fmt.Fprintln(g.out, ws+"if "+in+" == nil && (out.Flags & jwriter.NilSliceAsEmpty) == 0 {")
fmt.Fprintln(g.out, ws+` out.RawString("null")`)
fmt.Fprintln(g.out, ws+"} else {")
if !assumeNonEmpty {
fmt.Fprintln(g.out, ws+"if "+in+" == nil && (out.Flags & jwriter.NilSliceAsEmpty) == 0 {")
fmt.Fprintln(g.out, ws+` out.RawString("null")`)
fmt.Fprintln(g.out, ws+"} else {")
} else {
fmt.Fprintln(g.out, ws+"{")
}
fmt.Fprintln(g.out, ws+" out.RawByte('[')")
fmt.Fprintln(g.out, ws+" for "+iVar+", "+vVar+" := range "+in+" {")
fmt.Fprintln(g.out, ws+" if "+iVar+" > 0 {")
fmt.Fprintln(g.out, ws+" out.RawByte(',')")
fmt.Fprintln(g.out, ws+" }")
if err := g.genTypeEncoder(elem, vVar, tags, indent+2); err != nil {
if err := g.genTypeEncoder(elem, vVar, tags, indent+2, false); err != nil {
return err
}
@@ -152,7 +166,7 @@ func (g *Generator) genTypeEncoderNoCheck(t reflect.Type, in string, tags fieldT
elem := t.Elem()
iVar := g.uniqueVarName()
if t.Elem().Kind() == reflect.Uint8 {
if t.Elem().Kind() == reflect.Uint8 && elem.Name() == "uint8" {
fmt.Fprintln(g.out, ws+"out.Base64Bytes("+in+"[:])")
} else {
fmt.Fprintln(g.out, ws+"out.RawByte('[')")
@@ -161,7 +175,7 @@ func (g *Generator) genTypeEncoderNoCheck(t reflect.Type, in string, tags fieldT
fmt.Fprintln(g.out, ws+" out.RawByte(',')")
fmt.Fprintln(g.out, ws+" }")
if err := g.genTypeEncoder(elem, in+"["+iVar+"]", tags, indent+1); err != nil {
if err := g.genTypeEncoder(elem, "("+in+")["+iVar+"]", tags, indent+1, false); err != nil {
return err
}
@@ -176,36 +190,50 @@ func (g *Generator) genTypeEncoderNoCheck(t reflect.Type, in string, tags fieldT
fmt.Fprintln(g.out, ws+enc+"(out, "+in+")")
case reflect.Ptr:
fmt.Fprintln(g.out, ws+"if "+in+" == nil {")
fmt.Fprintln(g.out, ws+` out.RawString("null")`)
fmt.Fprintln(g.out, ws+"} else {")
if !assumeNonEmpty {
fmt.Fprintln(g.out, ws+"if "+in+" == nil {")
fmt.Fprintln(g.out, ws+` out.RawString("null")`)
fmt.Fprintln(g.out, ws+"} else {")
}
if err := g.genTypeEncoder(t.Elem(), "*"+in, tags, indent+1); err != nil {
if err := g.genTypeEncoder(t.Elem(), "*"+in, tags, indent+1, false); err != nil {
return err
}
fmt.Fprintln(g.out, ws+"}")
if !assumeNonEmpty {
fmt.Fprintln(g.out, ws+"}")
}
case reflect.Map:
key := t.Key()
keyEnc, ok := primitiveStringEncoders[key.Kind()]
if !ok {
return fmt.Errorf("map key type %v not supported: only string and integer keys are allowed", key)
}
if !ok && !hasCustomMarshaler(key) {
return fmt.Errorf("map key type %v not supported: only string and integer keys and types implementing Marshaler interfaces are allowed", key)
} // else assume the caller knows what they are doing and that the custom marshaler performs the translation from the key type to a string or integer
tmpVar := g.uniqueVarName()
fmt.Fprintln(g.out, ws+"if "+in+" == nil && (out.Flags & jwriter.NilMapAsEmpty) == 0 {")
fmt.Fprintln(g.out, ws+" out.RawString(`null`)")
fmt.Fprintln(g.out, ws+"} else {")
if !assumeNonEmpty {
fmt.Fprintln(g.out, ws+"if "+in+" == nil && (out.Flags & jwriter.NilMapAsEmpty) == 0 {")
fmt.Fprintln(g.out, ws+" out.RawString(`null`)")
fmt.Fprintln(g.out, ws+"} else {")
} else {
fmt.Fprintln(g.out, ws+"{")
}
fmt.Fprintln(g.out, ws+" out.RawByte('{')")
fmt.Fprintln(g.out, ws+" "+tmpVar+"First := true")
fmt.Fprintln(g.out, ws+" for "+tmpVar+"Name, "+tmpVar+"Value := range "+in+" {")
fmt.Fprintln(g.out, ws+" if !"+tmpVar+"First { out.RawByte(',') }")
fmt.Fprintln(g.out, ws+" "+tmpVar+"First = false")
fmt.Fprintln(g.out, ws+" "+fmt.Sprintf(keyEnc, tmpVar+"Name"))
fmt.Fprintln(g.out, ws+" if "+tmpVar+"First { "+tmpVar+"First = false } else { out.RawByte(',') }")
if keyEnc != "" {
fmt.Fprintln(g.out, ws+" "+fmt.Sprintf(keyEnc, tmpVar+"Name"))
} else {
if err := g.genTypeEncoder(key, tmpVar+"Name", tags, indent+2, false); err != nil {
return err
}
}
fmt.Fprintln(g.out, ws+" out.RawByte(':')")
if err := g.genTypeEncoder(t.Elem(), tmpVar+"Value", tags, indent+2); err != nil {
if err := g.genTypeEncoder(t.Elem(), tmpVar+"Value", tags, indent+2, false); err != nil {
return err
}
@@ -265,19 +293,21 @@ func (g *Generator) genStructFieldEncoder(t reflect.Type, f reflect.StructField)
if tags.omit {
return nil
}
if !tags.omitEmpty && !g.omitEmpty || tags.noOmitEmpty {
fmt.Fprintln(g.out, " if !first { out.RawByte(',') }")
fmt.Fprintln(g.out, " first = false")
fmt.Fprintf(g.out, " out.RawString(%q)\n", strconv.Quote(jsonName)+":")
return g.genTypeEncoder(f.Type, "in."+f.Name, tags, 1)
noOmitEmpty := (!tags.omitEmpty && !g.omitEmpty) || tags.noOmitEmpty
if noOmitEmpty {
fmt.Fprintln(g.out, " {")
} else {
fmt.Fprintln(g.out, " if", g.notEmptyCheck(f.Type, "in."+f.Name), "{")
}
fmt.Fprintf(g.out, " const prefix string = %q\n", ","+strconv.Quote(jsonName)+":")
fmt.Fprintln(g.out, " if first {")
fmt.Fprintln(g.out, " first = false")
fmt.Fprintln(g.out, " out.RawString(prefix[1:])")
fmt.Fprintln(g.out, " } else {")
fmt.Fprintln(g.out, " out.RawString(prefix)")
fmt.Fprintln(g.out, " }")
fmt.Fprintln(g.out, " if", g.notEmptyCheck(f.Type, "in."+f.Name), "{")
fmt.Fprintln(g.out, " if !first { out.RawByte(',') }")
fmt.Fprintln(g.out, " first = false")
fmt.Fprintf(g.out, " out.RawString(%q)\n", strconv.Quote(jsonName)+":")
if err := g.genTypeEncoder(f.Type, "in."+f.Name, tags, 2); err != nil {
if err := g.genTypeEncoder(f.Type, "in."+f.Name, tags, 2, !noOmitEmpty); err != nil {
return err
}
fmt.Fprintln(g.out, " }")
@@ -304,7 +334,7 @@ func (g *Generator) genSliceArrayMapEncoder(t reflect.Type) error {
typ := g.getType(t)
fmt.Fprintln(g.out, "func "+fname+"(out *jwriter.Writer, in "+typ+") {")
err := g.genTypeEncoderNoCheck(t, "in", fieldTags{}, 1)
err := g.genTypeEncoderNoCheck(t, "in", fieldTags{}, 1, false)
if err != nil {
return err
}
+14 -4
View File
@@ -33,9 +33,10 @@ type Generator struct {
varCounter int
noStdMarshalers bool
omitEmpty bool
fieldNamer FieldNamer
noStdMarshalers bool
omitEmpty bool
disallowUnknownFields bool
fieldNamer FieldNamer
// package path to local alias map for tracking imports
imports map[string]string
@@ -110,6 +111,11 @@ func (g *Generator) NoStdMarshalers() {
g.noStdMarshalers = true
}
// DisallowUnknownFields instructs not to skip unknown fields in json and return error.
func (g *Generator) DisallowUnknownFields() {
g.disallowUnknownFields = true
}
// OmitEmpty triggers `json=",omitempty"` behaviour by default.
func (g *Generator) OmitEmpty() {
g.omitEmpty = true
@@ -284,7 +290,11 @@ func (g *Generator) getType(t reflect.Type) string {
lines := make([]string, 0, nf)
for i := 0; i < nf; i++ {
f := t.Field(i)
line := f.Name + " " + g.getType(f.Type)
var line string
if !f.Anonymous {
line = f.Name + " "
} // else the field is anonymous (an embedded type)
line += g.getType(f.Type)
t := f.Tag
if t != "" {
line += " " + escapeTag(t)
+39 -4
View File
@@ -649,7 +649,7 @@ func (r *Lexer) Bytes() []byte {
return nil
}
ret := make([]byte, base64.StdEncoding.DecodedLen(len(r.token.byteValue)))
len, err := base64.StdEncoding.Decode(ret, r.token.byteValue)
n, err := base64.StdEncoding.Decode(ret, r.token.byteValue)
if err != nil {
r.fatalError = &LexerError{
Reason: err.Error(),
@@ -658,7 +658,7 @@ func (r *Lexer) Bytes() []byte {
}
r.consume()
return ret[:len]
return ret[:n]
}
// Bool reads a true or false boolean keyword.
@@ -997,6 +997,22 @@ func (r *Lexer) Float32() float32 {
return float32(n)
}
func (r *Lexer) Float32Str() float32 {
s, b := r.unsafeString()
if !r.Ok() {
return 0
}
n, err := strconv.ParseFloat(s, 32)
if err != nil {
r.addNonfatalError(&LexerError{
Offset: r.start,
Reason: err.Error(),
Data: string(b),
})
}
return float32(n)
}
func (r *Lexer) Float64() float64 {
s := r.number()
if !r.Ok() {
@@ -1014,6 +1030,22 @@ func (r *Lexer) Float64() float64 {
return n
}
func (r *Lexer) Float64Str() float64 {
s, b := r.unsafeString()
if !r.Ok() {
return 0
}
n, err := strconv.ParseFloat(s, 64)
if err != nil {
r.addNonfatalError(&LexerError{
Offset: r.start,
Reason: err.Error(),
Data: string(b),
})
}
return n
}
func (r *Lexer) Error() error {
return r.fatalError
}
@@ -1056,7 +1088,7 @@ func (r *Lexer) JsonNumber() json.Number {
}
if !r.Ok() {
r.errInvalidToken("json.Number")
return json.Number("0")
return json.Number("")
}
switch r.token.kind {
@@ -1064,9 +1096,12 @@ func (r *Lexer) JsonNumber() json.Number {
return json.Number(r.String())
case tokenNumber:
return json.Number(r.Raw())
case tokenNull:
r.Null()
return json.Number("")
default:
r.errSyntax()
return json.Number("0")
return json.Number("")
}
}
+12 -9
View File
@@ -25,9 +25,9 @@ func TestString(t *testing.T) {
{toParse: `"test"junk`, want: "test"},
{toParse: `5`, wantError: true}, // not a string
{toParse: `"\x"`, wantError: true}, // invalid escape
{toParse: `"\ud800"`, want: ""}, // invalid utf-8 char; return replacement char
{toParse: `5`, wantError: true}, // not a string
{toParse: `"\x"`, wantError: true}, // invalid escape
{toParse: `"\ud800"`, want: ""}, // invalid utf-8 char; return replacement char
} {
l := Lexer{Data: []byte(test.toParse)}
@@ -269,16 +269,19 @@ func TestJsonNumber(t *testing.T) {
{toParse: `"0.12"`, want: json.Number("0.12"), wantValue: 0.12},
{toParse: `"25E-4"`, want: json.Number("25E-4"), wantValue: 25E-4},
{toParse: `"a""`, wantValueError: true},
{toParse: `"foo"`, want: json.Number("foo"), wantValueError: true},
{toParse: `null`, want: json.Number(""), wantValueError: true},
{toParse: `[1]`, wantLexerError: true},
{toParse: `{}`, wantLexerError: true},
{toParse: `a`, wantLexerError: true},
{toParse: `"a""`, want: json.Number("a"), wantValueError: true},
{toParse: `[1]`, want: json.Number(""), wantLexerError: true, wantValueError: true},
{toParse: `{}`, want: json.Number(""), wantLexerError: true, wantValueError: true},
{toParse: `a`, want: json.Number(""), wantLexerError: true, wantValueError: true},
} {
l := Lexer{Data: []byte(test.toParse)}
got := l.JsonNumber()
if got != test.want && !test.wantLexerError && !test.wantValueError {
if got != test.want {
t.Errorf("[%d, %q] JsonNumber() = %v; want %v", i, test.toParse, got, test.want)
}
@@ -303,7 +306,7 @@ func TestJsonNumber(t *testing.T) {
}
if valueErr != nil && !test.wantValueError {
t.Errorf("[%d, %q] JsonNumber() value error: %v", i, test.toParse, err)
t.Errorf("[%d, %q] JsonNumber() value error: %v", i, test.toParse, valueErr)
} else if valueErr == nil && test.wantValueError {
t.Errorf("[%d, %q] JsonNumber() ok; want value error", i, test.toParse)
}
+15 -2
View File
@@ -240,11 +240,25 @@ func (w *Writer) Float32(n float32) {
w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, float64(n), 'g', -1, 32)
}
func (w *Writer) Float32Str(n float32) {
w.Buffer.EnsureSpace(20)
w.Buffer.Buf = append(w.Buffer.Buf, '"')
w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, float64(n), 'g', -1, 32)
w.Buffer.Buf = append(w.Buffer.Buf, '"')
}
func (w *Writer) Float64(n float64) {
w.Buffer.EnsureSpace(20)
w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, n, 'g', -1, 64)
}
func (w *Writer) Float64Str(n float64) {
w.Buffer.EnsureSpace(20)
w.Buffer.Buf = append(w.Buffer.Buf, '"')
w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, float64(n), 'g', -1, 64)
w.Buffer.Buf = append(w.Buffer.Buf, '"')
}
func (w *Writer) Bool(v bool) {
w.Buffer.EnsureSpace(5)
if v {
@@ -340,12 +354,11 @@ func (w *Writer) base64(in []byte) {
return
}
w.Buffer.EnsureSpace(((len(in) - 1) / 3 + 1) * 4)
w.Buffer.EnsureSpace(((len(in)-1)/3 + 1) * 4)
si := 0
n := (len(in) / 3) * 3
for si < n {
// Convert 3x 8bit source bytes into 4 bytes
val := uint(in[si+0])<<16 | uint(in[si+1])<<8 | uint(in[si+2])
+1 -1
View File
@@ -93,5 +93,5 @@ func (p *Parser) Parse(fname string, isDir bool) error {
func getDefaultGoPath() (string, error) {
output, err := exec.Command("go", "env", "GOPATH").Output()
return string(output), err
return strings.TrimSpace(string(output)), err
}
+1 -1
View File
@@ -27,7 +27,7 @@ func getPkgPath(fname string, isDir bool) (string, error) {
}
}
for _, p := range strings.Split(os.Getenv("GOPATH"), ":") {
for _, p := range strings.Split(gopath, ":") {
prefix := path.Join(p, "src") + "/"
if rel := strings.TrimPrefix(fname, prefix); rel != fname {
if !isDir {
+1 -1
View File
@@ -33,7 +33,7 @@ func getPkgPath(fname string, isDir bool) (string, error) {
}
}
for _, p := range strings.Split(os.Getenv("GOPATH"), ";") {
for _, p := range strings.Split(gopath, ";") {
prefix := path.Join(normalizePath(p), "src") + "/"
if rel := strings.TrimPrefix(fname, prefix); rel != fname {
if !isDir {
+13
View File
@@ -38,6 +38,8 @@ var testCases = []struct {
{&IntsValue, IntsString},
{&mapStringStringValue, mapStringStringString},
{&namedTypeValue, namedTypeValueString},
{&customMapKeyTypeValue, customMapKeyTypeValueString},
{&embeddedTypeValue, embeddedTypeValueString},
{&mapMyIntStringValue, mapMyIntStringValueString},
{&mapIntStringValue, mapIntStringValueString},
{&mapInt32StringValue, mapInt32StringValueString},
@@ -47,6 +49,9 @@ var testCases = []struct {
{&mapUint64StringValue, mapUint64StringValueString},
{&mapUintptrStringValue, mapUintptrStringValueString},
{&intKeyedMapStructValue, intKeyedMapStructValueString},
{&intArrayStructValue, intArrayStructValueString},
{&myUInt8SliceValue, myUInt8SliceString},
{&myUInt8ArrayValue, myUInt8ArrayString},
}
func TestMarshal(t *testing.T) {
@@ -229,3 +234,11 @@ func TestUnmarshalStructWithEmbeddedPtrStruct(t *testing.T) {
t.Errorf("easyjson.Unmarshal() = %#v; want %#v", s, structWithInterfaceValueFilled)
}
}
func TestDisallowUnknown(t *testing.T) {
var d DisallowUnknown
err := easyjson.Unmarshal([]byte(disallowUnknownString), &d)
if err == nil {
t.Error("want error, got nil")
}
}
+29
View File
@@ -0,0 +1,29 @@
package tests
import fmt "fmt"
//easyjson:json
type CustomMapKeyType struct {
Map map[customKeyType]int
}
type customKeyType [2]byte
func (k customKeyType) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf(`"%02x"`, k)), nil
}
func (k *customKeyType) UnmarshalJSON(b []byte) error {
_, err := fmt.Sscanf(string(b), `"%02x%02x"`, &k[0], &k[1])
return err
}
var customMapKeyTypeValue CustomMapKeyType
func init() {
customMapKeyTypeValue.Map = map[customKeyType]int{
customKeyType{0x01, 0x02}: 3,
}
}
var customMapKeyTypeValueString = `{"Map":{"0102":3}}`
+43
View File
@@ -41,6 +41,9 @@ type PrimitiveTypes struct {
Float32 float32
Float64 float64
Float32String float32 `json:",string"`
Float64String float64 `json:",string"`
Ptr *string
PtrNil *string
}
@@ -77,6 +80,9 @@ var primitiveTypesValue = PrimitiveTypes{
Float32: 1.5,
Float64: math.MaxFloat64,
Float32String: 1.5,
Float64String: math.MaxFloat64,
Ptr: &str,
}
@@ -110,6 +116,9 @@ var primitiveTypesString = "{" +
`"Float32":` + fmt.Sprint(1.5) + `,` +
`"Float64":` + fmt.Sprint(math.MaxFloat64) + `,` +
`"Float32String":"` + fmt.Sprint(1.5) + `",` +
`"Float64String":"` + fmt.Sprint(math.MaxFloat64) + `",` +
`"Ptr":"bla",` +
`"PtrNil":null` +
@@ -757,3 +766,37 @@ var intKeyedMapStructValueString = `{` +
`"foo":{"42":"life"},` +
`"bar":{"32":{"354634382":"life"}}` +
`}`
type IntArray [2]int
//easyjson:json
type IntArrayStruct struct {
Pointer *IntArray `json:"pointer"`
Value IntArray `json:"value"`
}
var intArrayStructValue = IntArrayStruct{
Pointer: &IntArray{1, 2},
Value: IntArray{1, 2},
}
var intArrayStructValueString = `{` +
`"pointer":[1,2],` +
`"value":[1,2]` +
`}`
type MyUInt8 uint8
//easyjson:json
type MyUInt8Slice []MyUInt8
var myUInt8SliceValue = MyUInt8Slice{1, 2, 3, 4, 5}
var myUInt8SliceString = `[1,2,3,4,5]`
//easyjson:json
type MyUInt8Array [2]MyUInt8
var myUInt8ArrayValue = MyUInt8Array{1, 2}
var myUInt8ArrayString = `[1,2]`
+8
View File
@@ -0,0 +1,8 @@
package tests
//easyjson:json
type DisallowUnknown struct {
FieldOne string `json:"field_one"`
}
var disallowUnknownString = `{"field_one": "one", "field_two": "two"}`
+24
View File
@@ -0,0 +1,24 @@
package tests
//easyjson:json
type EmbeddedType struct {
EmbeddedInnerType
Inner struct {
EmbeddedInnerType
}
Field2 int
}
type EmbeddedInnerType struct {
Field1 int
}
var embeddedTypeValue EmbeddedType
func init() {
embeddedTypeValue.Field1 = 1
embeddedTypeValue.Field2 = 2
embeddedTypeValue.Inner.Field1 = 3
}
var embeddedTypeValueString = `{"Inner":{"Field1":3},"Field2":2,"Field1":1}`