diff --git a/coverage_test.go b/coverage_test.go new file mode 100644 index 0000000..43f194e --- /dev/null +++ b/coverage_test.go @@ -0,0 +1,143 @@ +package json + +import ( + "bytes" + "strings" + "testing" +) + +func intptr(v int) *int { + return &v +} + +func int8ptr(v int8) *int8 { + return &v +} + +func TestCoverage(t *testing.T) { + tests := []struct { + name string + expected string + data interface{} + }{ + { + name: "IntHead", + expected: `{"a":1}`, + data: struct { + A int `json:"a"` + }{A: 1}, + }, + { + name: "IntPtrHead", + expected: `{"a":1}`, + data: struct { + A *int `json:"a"` + }{A: intptr(1)}, + }, + /* + { + name: "IntPtrNilHead", + expected: `{"a":null}`, + data: struct { + A *int `json:"a"` + }{A: nil}, + }, + */ + { + name: "PtrIntHead", + expected: `{"a":1}`, + data: &struct { + A int `json:"a"` + }{A: 1}, + }, + { + name: "PtrIntPtrHead", + expected: `{"a":1}`, + data: &struct { + A *int `json:"a"` + }{A: intptr(1)}, + }, + { + name: "IntField", + expected: `{"a":1,"b":2}`, + data: struct { + A int `json:"a"` + B int `json:"b"` + }{A: 1, B: 2}, + }, + { + name: "IntPtrField", + expected: `{"a":1,"b":2}`, + data: struct { + A *int `json:"a"` + B *int `json:"b"` + }{A: intptr(1), B: intptr(2)}, + }, + { + name: "Int8Head", + expected: `{"a":1}`, + data: struct { + A int8 `json:"a"` + }{A: 1}, + }, + { + name: "Int8PtrHead", + expected: `{"a":1}`, + data: struct { + A *int8 `json:"a"` + }{A: int8ptr(1)}, + }, + /* + { + name: "Int8PtrNilHead", + expected: `{"a":null}`, + data: struct { + A *int8 `json:"a"` + }{A: nil}, + }, + */ + { + name: "PtrInt8Head", + expected: `{"a":1}`, + data: &struct { + A int8 `json:"a"` + }{A: 1}, + }, + { + name: "PtrInt8PtrHead", + expected: `{"a":1}`, + data: &struct { + A *int8 `json:"a"` + }{A: int8ptr(1)}, + }, + { + name: "Int8Field", + expected: `{"a":1,"b":2}`, + data: struct { + A int8 `json:"a"` + B int8 `json:"b"` + }{A: 1, B: 2}, + }, + { + name: "Int8PtrField", + expected: `{"a":1,"b":2}`, + data: struct { + A *int8 `json:"a"` + B *int8 `json:"b"` + }{A: int8ptr(1), B: int8ptr(2)}, + }, + } + for _, test := range tests { + for _, htmlEscape := range []bool{true, false} { + var buf bytes.Buffer + enc := NewEncoder(&buf) + enc.SetEscapeHTML(htmlEscape) + if err := enc.Encode(test.data); err != nil { + t.Fatalf("%s(htmlEscape:%T): %s: %s", test.name, htmlEscape, test.expected, err) + } + if strings.TrimRight(buf.String(), "\n") != test.expected { + t.Fatalf("%s(htmlEscape:%T): expected %q but got %q", test.name, htmlEscape, test.expected, buf.String()) + } + } + } +} diff --git a/encode_compile.go b/encode_compile.go index cb19ac4..0721cec 100644 --- a/encode_compile.go +++ b/encode_compile.go @@ -1102,7 +1102,8 @@ func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode, anonymousFields[k] = append(anonymousFields[k], v...) } } - if fieldNum == 1 && valueCode.op == opPtr { + + if fieldNum == 1 && valueCode.op == opPtr && !isPtr { // if field number is one and primitive pointer type, // it should encode as **not** pointer . switch valueCode.next.op { diff --git a/encode_vm.go b/encode_vm.go index 4089717..391b826 100644 --- a/encode_vm.go +++ b/encode_vm.go @@ -1290,10 +1290,21 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, b []byte, code *opcode) ([]byte b = encodeComma(b) code = code.next } + case opStructFieldPtrHeadIntPtr: + p := load(ctxptr, code.idx) + if p == 0 { + b = encodeNull(b) + b = encodeComma(b) + code = code.end.next + break + } + store(ctxptr, code.idx, e.ptrToPtr(p)) + fallthrough case opStructFieldHeadIntPtr: p := load(ctxptr, code.idx) if p == 0 { b = encodeNull(b) + code = code.end.next } else { b = append(b, '{') b = append(b, code.key...) @@ -1306,10 +1317,21 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, b []byte, code *opcode) ([]byte } b = encodeComma(b) code = code.next + case opStructEscapedFieldPtrHeadIntPtr: + p := load(ctxptr, code.idx) + if p == 0 { + b = encodeNull(b) + b = encodeComma(b) + code = code.end.next + break + } + store(ctxptr, code.idx, e.ptrToPtr(p)) + fallthrough case opStructEscapedFieldHeadIntPtr: p := load(ctxptr, code.idx) if p == 0 { b = encodeNull(b) + code = code.end.next } else { b = append(b, '{') b = append(b, code.escapedKey...) @@ -1319,13 +1341,14 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, b []byte, code *opcode) ([]byte } else { b = appendInt(b, int64(e.ptrToInt(p+code.offset))) } + code = code.next } b = encodeComma(b) - code = code.next case opStructFieldHeadIntNPtr: p := load(ctxptr, code.idx) if p == 0 { b = encodeNull(b) + code = code.end.next } else { b = append(b, '{') b = append(b, code.key...) @@ -1444,6 +1467,60 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, b []byte, code *opcode) ([]byte b = encodeComma(b) code = code.next } + case opStructFieldPtrHeadInt8Ptr: + p := load(ctxptr, code.idx) + if p == 0 { + b = encodeNull(b) + b = encodeComma(b) + code = code.end.next + break + } + store(ctxptr, code.idx, e.ptrToPtr(p)) + fallthrough + case opStructFieldHeadInt8Ptr: + p := load(ctxptr, code.idx) + if p == 0 { + b = encodeNull(b) + code = code.end.next + } else { + b = append(b, '{') + b = append(b, code.key...) + p = e.ptrToPtr(p) + if p == 0 { + b = encodeNull(b) + } else { + b = appendInt(b, int64(e.ptrToInt8(p+code.offset))) + } + code = code.next + } + b = encodeComma(b) + case opStructEscapedFieldPtrHeadInt8Ptr: + p := load(ctxptr, code.idx) + if p == 0 { + b = encodeNull(b) + b = encodeComma(b) + code = code.end.next + break + } + store(ctxptr, code.idx, e.ptrToPtr(p)) + fallthrough + case opStructEscapedFieldHeadInt8Ptr: + p := load(ctxptr, code.idx) + if p == 0 { + b = encodeNull(b) + code = code.end.next + } else { + b = append(b, '{') + b = append(b, code.escapedKey...) + p = e.ptrToPtr(p) + if p == 0 { + b = encodeNull(b) + } else { + b = appendInt(b, int64(e.ptrToInt8(p+code.offset))) + } + code = code.next + } + b = encodeComma(b) case opStructFieldPtrAnonymousHeadInt8: store(ctxptr, code.idx, e.ptrToPtr(load(ctxptr, code.idx))) fallthrough