Enable switch map processing at runtime

This commit is contained in:
Masaaki Goshima 2020-09-16 18:15:47 +09:00
parent 898d58b8b8
commit aaea586778
8 changed files with 962 additions and 1120 deletions

View File

@ -238,13 +238,6 @@ func (t opType) fieldToStringTagField() opType {
{"RootMapKey", "RootMapKeyIndent", "MapKey"}, {"RootMapKey", "RootMapKeyIndent", "MapKey"},
{"MapValue", "MapValueIndent", "MapValue"}, {"MapValue", "MapValueIndent", "MapValue"},
{"MapEnd", "MapEndIndent", "Op"}, {"MapEnd", "MapEndIndent", "Op"},
{"SortedMapHead", "SortedMapHeadIndent", "MapHead"},
{"SortedMapHeadLoad", "SortedMapHeadLoadIndent", "MapHead"},
{"SortedRootMapHead", "SortedRootMapHeadIndent", "MapHead"},
{"SortedMapKey", "SortedMapKeyIndent", "MapKey"},
{"SortedRootMapKey", "SortedRootMapKeyIndent", "MapKey"},
{"SortedMapValue", "SortedMapValueIndent", "MapValue"},
{"SortedMapEnd", "SortedMapEndIndent", "MapEnd"},
{"StructFieldHead", "StructFieldHeadIndent", "StructField"}, {"StructFieldHead", "StructFieldHeadIndent", "StructField"},
{"StructFieldHeadOmitEmpty", "StructFieldHeadOmitEmptyIndent", "StructField"}, {"StructFieldHeadOmitEmpty", "StructFieldHeadOmitEmptyIndent", "StructField"},
{"StructFieldHeadStringTag", "StructFieldHeadStringTagIndent", "StructField"}, {"StructFieldHeadStringTag", "StructFieldHeadStringTagIndent", "StructField"},

View File

@ -18,6 +18,7 @@ type Encoder struct {
buf []byte buf []byte
enabledIndent bool enabledIndent bool
enabledHTMLEscape bool enabledHTMLEscape bool
unorderedMap bool
prefix []byte prefix []byte
indentStr []byte indentStr []byte
structTypeToCompiledCode map[uintptr]*compiledCode structTypeToCompiledCode map[uintptr]*compiledCode
@ -88,6 +89,16 @@ func NewEncoder(w io.Writer) *Encoder {
// //
// See the documentation for Marshal for details about the conversion of Go values to JSON. // See the documentation for Marshal for details about the conversion of Go values to JSON.
func (e *Encoder) Encode(v interface{}) error { func (e *Encoder) Encode(v interface{}) error {
return e.EncodeWithOption(v)
}
// EncodeWithOption call Encode with EncodeOption.
func (e *Encoder) EncodeWithOption(v interface{}, opts ...EncodeOption) error {
for _, opt := range opts {
if err := opt(e); err != nil {
return err
}
}
if err := e.encode(v); err != nil { if err := e.encode(v); err != nil {
return err return err
} }
@ -126,6 +137,7 @@ func (e *Encoder) reset() {
e.buf = e.buf[:0] e.buf = e.buf[:0]
e.enabledHTMLEscape = true e.enabledHTMLEscape = true
e.enabledIndent = false e.enabledIndent = false
e.unorderedMap = false
} }
func (e *Encoder) encodeForMarshal(v interface{}) ([]byte, error) { func (e *Encoder) encodeForMarshal(v interface{}) ([]byte, error) {

View File

@ -452,13 +452,13 @@ func (e *Encoder) typeToHeaderType(op opType) opType {
return opStructFieldHeadString return opStructFieldHeadString
case opBool: case opBool:
return opStructFieldHeadBool return opStructFieldHeadBool
case opMapHead, opSortedMapHead: case opMapHead:
return opStructFieldHeadMap return opStructFieldHeadMap
case opMapHeadLoad, opSortedMapHeadLoad: case opMapHeadLoad:
return opStructFieldHeadMapLoad return opStructFieldHeadMapLoad
case opMapHeadIndent, opSortedMapHeadIndent: case opMapHeadIndent:
return opStructFieldHeadMapIndent return opStructFieldHeadMapIndent
case opMapHeadLoadIndent, opSortedMapHeadLoadIndent: case opMapHeadLoadIndent:
return opStructFieldHeadMapLoadIndent return opStructFieldHeadMapLoadIndent
case opArrayHead: case opArrayHead:
return opStructFieldHeadArray return opStructFieldHeadArray
@ -510,13 +510,13 @@ func (e *Encoder) typeToFieldType(op opType) opType {
return opStructFieldString return opStructFieldString
case opBool: case opBool:
return opStructFieldBool return opStructFieldBool
case opMapHead, opSortedMapHead: case opMapHead:
return opStructFieldMap return opStructFieldMap
case opMapHeadLoad, opSortedMapHeadLoad: case opMapHeadLoad:
return opStructFieldMapLoad return opStructFieldMapLoad
case opMapHeadIndent, opSortedMapHeadIndent: case opMapHeadIndent:
return opStructFieldMapIndent return opStructFieldMapIndent
case opMapHeadLoadIndent, opSortedMapHeadLoadIndent: case opMapHeadLoadIndent:
return opStructFieldMapLoadIndent return opStructFieldMapLoadIndent
case opArrayHead: case opArrayHead:
return opStructFieldArray return opStructFieldArray

View File

@ -389,9 +389,9 @@ func newArrayElemCode(ctx *encodeCompileContext, head *opcode, length int, size
func newMapHeaderCode(ctx *encodeCompileContext, withLoad bool) *opcode { func newMapHeaderCode(ctx *encodeCompileContext, withLoad bool) *opcode {
var op opType var op opType
if withLoad { if withLoad {
op = opSortedMapHeadLoad op = opMapHeadLoad
} else { } else {
op = opSortedMapHead op = opMapHead
} }
idx := opcodeOffset(ctx.ptrIndex) idx := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex() ctx.incPtrIndex()
@ -414,7 +414,7 @@ func newMapHeaderCode(ctx *encodeCompileContext, withLoad bool) *opcode {
func newMapKeyCode(ctx *encodeCompileContext, head *opcode) *opcode { func newMapKeyCode(ctx *encodeCompileContext, head *opcode) *opcode {
return &opcode{ return &opcode{
op: opSortedMapKey, op: opMapKey,
displayIdx: ctx.opcodeIndex, displayIdx: ctx.opcodeIndex,
idx: opcodeOffset(ctx.ptrIndex), idx: opcodeOffset(ctx.ptrIndex),
elemIdx: head.elemIdx, elemIdx: head.elemIdx,
@ -426,7 +426,7 @@ func newMapKeyCode(ctx *encodeCompileContext, head *opcode) *opcode {
func newMapValueCode(ctx *encodeCompileContext, head *opcode) *opcode { func newMapValueCode(ctx *encodeCompileContext, head *opcode) *opcode {
return &opcode{ return &opcode{
op: opSortedMapValue, op: opMapValue,
displayIdx: ctx.opcodeIndex, displayIdx: ctx.opcodeIndex,
idx: opcodeOffset(ctx.ptrIndex), idx: opcodeOffset(ctx.ptrIndex),
elemIdx: head.elemIdx, elemIdx: head.elemIdx,
@ -443,7 +443,7 @@ func newMapEndCode(ctx *encodeCompileContext, head *opcode) *opcode {
ctx.incPtrIndex() ctx.incPtrIndex()
idx := opcodeOffset(ctx.ptrIndex) idx := opcodeOffset(ctx.ptrIndex)
return &opcode{ return &opcode{
op: opSortedMapEnd, op: opMapEnd,
displayIdx: ctx.opcodeIndex, displayIdx: ctx.opcodeIndex,
idx: idx, idx: idx,
length: head.length, length: head.length,

File diff suppressed because it is too large Load Diff

View File

@ -377,14 +377,18 @@ func Test_Marshal(t *testing.T) {
}) })
t.Run("map", func(t *testing.T) { t.Run("map", func(t *testing.T) {
t.Run("map[string]int", func(t *testing.T) { t.Run("map[string]int", func(t *testing.T) {
bytes, err := json.Marshal(map[string]int{ v := map[string]int{
"a": 1, "a": 1,
"b": 2, "b": 2,
"c": 3, "c": 3,
"d": 4, "d": 4,
}) }
bytes, err := json.Marshal(v)
assertErr(t, err) assertErr(t, err)
assertEq(t, "map", `{"a":1,"b":2,"c":3,"d":4}`, string(bytes)) assertEq(t, "map", `{"a":1,"b":2,"c":3,"d":4}`, string(bytes))
b, err := json.MarshalWithOption(v, json.UnorderedMap())
assertErr(t, err)
assertEq(t, "unordered map", len(`{"a":1,"b":2,"c":3,"d":4}`), len(string(b)))
}) })
t.Run("map[string]interface{}", func(t *testing.T) { t.Run("map[string]interface{}", func(t *testing.T) {
type T struct { type T struct {

View File

@ -426,7 +426,7 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, code *opcode) error {
e.encodeBytes([]byte{']', '\n'}) e.encodeBytes([]byte{']', '\n'})
code = code.end.next code = code.end.next
} }
case opSortedMapHead: case opMapHead:
ptr := load(ctxptr, code.idx) ptr := load(ctxptr, code.idx)
if ptr == 0 { if ptr == 0 {
e.encodeNull() e.encodeNull()
@ -440,11 +440,13 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, code *opcode) error {
store(ctxptr, code.elemIdx, 0) store(ctxptr, code.elemIdx, 0)
store(ctxptr, code.length, uintptr(mlen)) store(ctxptr, code.length, uintptr(mlen))
store(ctxptr, code.mapIter, uintptr(iter)) store(ctxptr, code.mapIter, uintptr(iter))
pos := make([]int, 0, mlen) if !e.unorderedMap {
pos = append(pos, len(e.buf)) pos := make([]int, 0, mlen)
posPtr := unsafe.Pointer(&pos) pos = append(pos, len(e.buf))
ctx.keepRefs = append(ctx.keepRefs, posPtr) posPtr := unsafe.Pointer(&pos)
store(ctxptr, code.end.mapPos, uintptr(posPtr)) ctx.keepRefs = append(ctx.keepRefs, posPtr)
store(ctxptr, code.end.mapPos, uintptr(posPtr))
}
key := mapiterkey(iter) key := mapiterkey(iter)
store(ctxptr, code.next.idx, uintptr(key)) store(ctxptr, code.next.idx, uintptr(key))
code = code.next code = code.next
@ -453,7 +455,7 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, code *opcode) error {
code = code.end.next code = code.end.next
} }
} }
case opSortedMapHeadLoad: case opMapHeadLoad:
ptr := load(ctxptr, code.idx) ptr := load(ctxptr, code.idx)
if ptr == 0 { if ptr == 0 {
e.encodeNull() e.encodeNull()
@ -471,41 +473,62 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, code *opcode) error {
store(ctxptr, code.mapIter, uintptr(iter)) store(ctxptr, code.mapIter, uintptr(iter))
key := mapiterkey(iter) key := mapiterkey(iter)
store(ctxptr, code.next.idx, uintptr(key)) store(ctxptr, code.next.idx, uintptr(key))
pos := make([]int, 0, mlen) if !e.unorderedMap {
pos = append(pos, len(e.buf)) pos := make([]int, 0, mlen)
posPtr := unsafe.Pointer(&pos) pos = append(pos, len(e.buf))
ctx.keepRefs = append(ctx.keepRefs, posPtr) posPtr := unsafe.Pointer(&pos)
store(ctxptr, code.end.mapPos, uintptr(posPtr)) ctx.keepRefs = append(ctx.keepRefs, posPtr)
store(ctxptr, code.end.mapPos, uintptr(posPtr))
}
code = code.next code = code.next
} else { } else {
e.encodeByte('}') e.encodeByte('}')
code = code.end.next code = code.end.next
} }
} }
case opSortedMapKey: case opMapKey:
idx := load(ctxptr, code.elemIdx) idx := load(ctxptr, code.elemIdx)
length := load(ctxptr, code.length) length := load(ctxptr, code.length)
posPtr := (*[]int)(unsafe.Pointer(load(ctxptr, code.end.mapPos)))
*posPtr = append(*posPtr, len(e.buf))
idx++ idx++
if idx < length { if e.unorderedMap {
iter := unsafe.Pointer(load(ctxptr, code.mapIter)) if idx < length {
store(ctxptr, code.elemIdx, idx) e.encodeByte(',')
key := mapiterkey(iter) iter := unsafe.Pointer(load(ctxptr, code.mapIter))
store(ctxptr, code.next.idx, uintptr(key)) store(ctxptr, code.elemIdx, idx)
code = code.next key := mapiterkey(iter)
store(ctxptr, code.next.idx, uintptr(key))
code = code.next
} else {
e.encodeByte('}')
code = code.end.next
}
} else { } else {
code = code.end posPtr := (*[]int)(unsafe.Pointer(load(ctxptr, code.end.mapPos)))
*posPtr = append(*posPtr, len(e.buf))
if idx < length {
iter := unsafe.Pointer(load(ctxptr, code.mapIter))
store(ctxptr, code.elemIdx, idx)
key := mapiterkey(iter)
store(ctxptr, code.next.idx, uintptr(key))
code = code.next
} else {
code = code.end
}
}
case opMapValue:
if e.unorderedMap {
e.encodeByte(':')
} else {
posPtr := (*[]int)(unsafe.Pointer(load(ctxptr, code.end.mapPos)))
*posPtr = append(*posPtr, len(e.buf))
} }
case opSortedMapValue:
posPtr := (*[]int)(unsafe.Pointer(load(ctxptr, code.end.mapPos)))
*posPtr = append(*posPtr, len(e.buf))
iter := unsafe.Pointer(load(ctxptr, code.mapIter)) iter := unsafe.Pointer(load(ctxptr, code.mapIter))
value := mapitervalue(iter) value := mapitervalue(iter)
store(ctxptr, code.next.idx, uintptr(value)) store(ctxptr, code.next.idx, uintptr(value))
mapiternext(iter) mapiternext(iter)
code = code.next code = code.next
case opSortedMapEnd: case opMapEnd:
// this operation only used by sorted map.
length := int(load(ctxptr, code.length)) length := int(load(ctxptr, code.length))
type mapKV struct { type mapKV struct {
key string key string
@ -545,204 +568,6 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, code *opcode) error {
e.buf = e.buf[:pos[0]] e.buf = e.buf[:pos[0]]
e.buf = append(e.buf, buf...) e.buf = append(e.buf, buf...)
code = code.next code = code.next
case opMapHead:
ptr := load(ctxptr, code.idx)
if ptr == 0 {
e.encodeNull()
code = code.end.next
} else {
e.encodeByte('{')
mlen := maplen(unsafe.Pointer(ptr))
if mlen > 0 {
iter := mapiterinit(code.typ, unsafe.Pointer(ptr))
ctx.keepRefs = append(ctx.keepRefs, iter)
store(ctxptr, code.elemIdx, 0)
store(ctxptr, code.length, uintptr(mlen))
store(ctxptr, code.mapIter, uintptr(iter))
key := mapiterkey(iter)
store(ctxptr, code.next.idx, uintptr(key))
code = code.next
} else {
e.encodeByte('}')
code = code.end.next
}
}
case opMapHeadLoad:
ptr := load(ctxptr, code.idx)
if ptr == 0 {
e.encodeNull()
code = code.end.next
} else {
// load pointer
ptr = uintptr(*(*unsafe.Pointer)(unsafe.Pointer(ptr)))
e.encodeByte('{')
mlen := maplen(unsafe.Pointer(ptr))
if mlen > 0 {
iter := mapiterinit(code.typ, unsafe.Pointer(ptr))
ctx.keepRefs = append(ctx.keepRefs, iter)
store(ctxptr, code.elemIdx, 0)
store(ctxptr, code.length, uintptr(mlen))
store(ctxptr, code.mapIter, uintptr(iter))
key := mapiterkey(iter)
store(ctxptr, code.next.idx, uintptr(key))
code = code.next
} else {
e.encodeByte('}')
code = code.end.next
}
}
case opMapKey:
idx := load(ctxptr, code.elemIdx)
length := load(ctxptr, code.length)
idx++
if idx < length {
e.encodeByte(',')
iter := unsafe.Pointer(load(ctxptr, code.mapIter))
store(ctxptr, code.elemIdx, idx)
key := mapiterkey(iter)
store(ctxptr, code.next.idx, uintptr(key))
code = code.next
} else {
e.encodeByte('}')
code = code.end.next
}
case opMapValue:
e.encodeByte(':')
iter := unsafe.Pointer(load(ctxptr, code.mapIter))
value := mapitervalue(iter)
store(ctxptr, code.next.idx, uintptr(value))
mapiternext(iter)
code = code.next
case opSortedMapHeadIndent:
ptr := load(ctxptr, code.idx)
if ptr == 0 {
e.encodeNull()
code = code.end.next
} else {
mlen := maplen(unsafe.Pointer(ptr))
if mlen > 0 {
e.encodeBytes([]byte{'{', '\n'})
iter := mapiterinit(code.typ, unsafe.Pointer(ptr))
ctx.keepRefs = append(ctx.keepRefs, iter)
store(ctxptr, code.elemIdx, 0)
store(ctxptr, code.length, uintptr(mlen))
store(ctxptr, code.mapIter, uintptr(iter))
pos := make([]int, 0, mlen)
pos = append(pos, len(e.buf))
posPtr := unsafe.Pointer(&pos)
ctx.keepRefs = append(ctx.keepRefs, posPtr)
store(ctxptr, code.end.mapPos, uintptr(posPtr))
key := mapiterkey(iter)
store(ctxptr, code.next.idx, uintptr(key))
code = code.next
} else {
e.encodeIndent(code.indent)
e.encodeBytes([]byte{'{', '}'})
code = code.end.next
}
}
case opSortedMapHeadLoadIndent:
ptr := load(ctxptr, code.idx)
if ptr == 0 {
e.encodeNull()
code = code.end.next
} else {
// load pointer
ptr = uintptr(*(*unsafe.Pointer)(unsafe.Pointer(ptr)))
mlen := maplen(unsafe.Pointer(ptr))
if mlen > 0 {
e.encodeBytes([]byte{'{', '\n'})
iter := mapiterinit(code.typ, unsafe.Pointer(ptr))
ctx.keepRefs = append(ctx.keepRefs, iter)
store(ctxptr, code.elemIdx, 0)
store(ctxptr, code.length, uintptr(mlen))
store(ctxptr, code.mapIter, uintptr(iter))
key := mapiterkey(iter)
store(ctxptr, code.next.idx, uintptr(key))
pos := make([]int, 0, mlen)
pos = append(pos, len(e.buf))
posPtr := unsafe.Pointer(&pos)
ctx.keepRefs = append(ctx.keepRefs, posPtr)
store(ctxptr, code.end.mapPos, uintptr(posPtr))
code = code.next
} else {
e.encodeIndent(code.indent)
e.encodeBytes([]byte{'{', '}'})
code = code.end.next
}
}
case opSortedMapKeyIndent:
idx := load(ctxptr, code.elemIdx)
length := load(ctxptr, code.length)
posPtr := (*[]int)(unsafe.Pointer(load(ctxptr, code.end.mapPos)))
*posPtr = append(*posPtr, len(e.buf))
idx++
if idx < length {
iter := unsafe.Pointer(load(ctxptr, code.mapIter))
store(ctxptr, code.elemIdx, idx)
key := mapiterkey(iter)
store(ctxptr, code.next.idx, uintptr(key))
code = code.next
} else {
code = code.end
}
case opSortedMapValueIndent:
posPtr := (*[]int)(unsafe.Pointer(load(ctxptr, code.end.mapPos)))
*posPtr = append(*posPtr, len(e.buf))
iter := unsafe.Pointer(load(ctxptr, code.mapIter))
value := mapitervalue(iter)
store(ctxptr, code.next.idx, uintptr(value))
mapiternext(iter)
code = code.next
case opSortedMapEndIndent:
length := int(load(ctxptr, code.length))
type mapKV struct {
key string
value string
}
kvs := make([]mapKV, 0, length)
pos := *(*[]int)(unsafe.Pointer(load(ctxptr, code.mapPos)))
for i := 0; i < length; i++ {
startKey := pos[i*2]
startValue := pos[i*2+1]
var endValue int
if i+1 < length {
endValue = pos[i*2+2]
} else {
endValue = len(e.buf)
}
kvs = append(kvs, mapKV{
key: string(e.buf[startKey:startValue]),
value: string(e.buf[startValue:endValue]),
})
}
fmt.Println("kvs = ", kvs)
sort.Slice(kvs, func(i, j int) bool {
return kvs[i].key < kvs[j].key
})
buf := e.buf[pos[0]:]
buf = buf[:0]
for idx, kv := range kvs {
if idx != 0 {
buf = append(buf, []byte{',', '\n'}...)
}
buf = append(buf, e.prefix...)
buf = append(buf, bytes.Repeat(e.indentStr, code.indent+1)...)
buf = append(buf, []byte(kv.key)...)
buf = append(buf, []byte{':', ' '}...)
buf = append(buf, []byte(kv.value)...)
}
buf = append(buf, '\n')
buf = append(buf, e.prefix...)
buf = append(buf, bytes.Repeat(e.indentStr, code.indent)...)
buf = append(buf, '}')
e.buf = e.buf[:pos[0]]
e.buf = append(e.buf, buf...)
code = code.next
case opMapHeadIndent: case opMapHeadIndent:
ptr := load(ctxptr, code.idx) ptr := load(ctxptr, code.idx)
if ptr == 0 { if ptr == 0 {
@ -758,10 +583,20 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, code *opcode) error {
store(ctxptr, code.elemIdx, 0) store(ctxptr, code.elemIdx, 0)
store(ctxptr, code.length, uintptr(mlen)) store(ctxptr, code.length, uintptr(mlen))
store(ctxptr, code.mapIter, uintptr(iter)) store(ctxptr, code.mapIter, uintptr(iter))
if !e.unorderedMap {
pos := make([]int, 0, mlen)
pos = append(pos, len(e.buf))
posPtr := unsafe.Pointer(&pos)
ctx.keepRefs = append(ctx.keepRefs, posPtr)
store(ctxptr, code.end.mapPos, uintptr(posPtr))
} else {
e.encodeIndent(code.next.indent)
}
key := mapiterkey(iter) key := mapiterkey(iter)
store(ctxptr, code.next.idx, uintptr(key)) store(ctxptr, code.next.idx, uintptr(key))
code = code.next code = code.next
e.encodeIndent(code.indent)
} else { } else {
e.encodeIndent(code.indent) e.encodeIndent(code.indent)
e.encodeBytes([]byte{'{', '}'}) e.encodeBytes([]byte{'{', '}'})
@ -787,8 +622,18 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, code *opcode) error {
store(ctxptr, code.mapIter, uintptr(iter)) store(ctxptr, code.mapIter, uintptr(iter))
key := mapiterkey(iter) key := mapiterkey(iter)
store(ctxptr, code.next.idx, uintptr(key)) store(ctxptr, code.next.idx, uintptr(key))
if !e.unorderedMap {
pos := make([]int, 0, mlen)
pos = append(pos, len(e.buf))
posPtr := unsafe.Pointer(&pos)
ctx.keepRefs = append(ctx.keepRefs, posPtr)
store(ctxptr, code.end.mapPos, uintptr(posPtr))
} else {
e.encodeIndent(code.next.indent)
}
code = code.next code = code.next
e.encodeIndent(code.indent)
} else { } else {
e.encodeIndent(code.indent) e.encodeIndent(code.indent)
e.encodeBytes([]byte{'{', '}'}) e.encodeBytes([]byte{'{', '}'})
@ -824,19 +669,33 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, code *opcode) error {
idx := load(ctxptr, code.elemIdx) idx := load(ctxptr, code.elemIdx)
length := load(ctxptr, code.length) length := load(ctxptr, code.length)
idx++ idx++
if idx < length { if e.unorderedMap {
e.encodeBytes([]byte{',', '\n'}) if idx < length {
e.encodeIndent(code.indent) e.encodeBytes([]byte{',', '\n'})
store(ctxptr, code.elemIdx, idx) e.encodeIndent(code.indent)
iter := unsafe.Pointer(load(ctxptr, code.mapIter)) store(ctxptr, code.elemIdx, idx)
key := mapiterkey(iter) iter := unsafe.Pointer(load(ctxptr, code.mapIter))
store(ctxptr, code.next.idx, uintptr(key)) key := mapiterkey(iter)
code = code.next store(ctxptr, code.next.idx, uintptr(key))
code = code.next
} else {
e.encodeByte('\n')
e.encodeIndent(code.indent - 1)
e.encodeByte('}')
code = code.end.next
}
} else { } else {
e.encodeByte('\n') posPtr := (*[]int)(unsafe.Pointer(load(ctxptr, code.end.mapPos)))
e.encodeIndent(code.indent - 1) *posPtr = append(*posPtr, len(e.buf))
e.encodeByte('}') if idx < length {
code = code.end.next iter := unsafe.Pointer(load(ctxptr, code.mapIter))
store(ctxptr, code.elemIdx, idx)
key := mapiterkey(iter)
store(ctxptr, code.next.idx, uintptr(key))
code = code.next
} else {
code = code.end
}
} }
case opRootMapKeyIndent: case opRootMapKeyIndent:
idx := load(ctxptr, code.elemIdx) idx := load(ctxptr, code.elemIdx)
@ -857,12 +716,64 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, code *opcode) error {
code = code.end.next code = code.end.next
} }
case opMapValueIndent: case opMapValueIndent:
e.encodeBytes([]byte{':', ' '}) if e.unorderedMap {
e.encodeBytes([]byte{':', ' '})
} else {
posPtr := (*[]int)(unsafe.Pointer(load(ctxptr, code.end.mapPos)))
*posPtr = append(*posPtr, len(e.buf))
}
iter := unsafe.Pointer(load(ctxptr, code.mapIter)) iter := unsafe.Pointer(load(ctxptr, code.mapIter))
value := mapitervalue(iter) value := mapitervalue(iter)
store(ctxptr, code.next.idx, uintptr(value)) store(ctxptr, code.next.idx, uintptr(value))
mapiternext(iter) mapiternext(iter)
code = code.next code = code.next
case opMapEndIndent:
// this operation only used by sorted map
length := int(load(ctxptr, code.length))
type mapKV struct {
key string
value string
}
kvs := make([]mapKV, 0, length)
pos := *(*[]int)(unsafe.Pointer(load(ctxptr, code.mapPos)))
for i := 0; i < length; i++ {
startKey := pos[i*2]
startValue := pos[i*2+1]
var endValue int
if i+1 < length {
endValue = pos[i*2+2]
} else {
endValue = len(e.buf)
}
kvs = append(kvs, mapKV{
key: string(e.buf[startKey:startValue]),
value: string(e.buf[startValue:endValue]),
})
}
sort.Slice(kvs, func(i, j int) bool {
return kvs[i].key < kvs[j].key
})
buf := e.buf[pos[0]:]
buf = buf[:0]
for idx, kv := range kvs {
if idx != 0 {
buf = append(buf, []byte{',', '\n'}...)
}
buf = append(buf, e.prefix...)
buf = append(buf, bytes.Repeat(e.indentStr, code.indent+1)...)
buf = append(buf, []byte(kv.key)...)
buf = append(buf, []byte{':', ' '}...)
buf = append(buf, []byte(kv.value)...)
}
buf = append(buf, '\n')
buf = append(buf, e.prefix...)
buf = append(buf, bytes.Repeat(e.indentStr, code.indent)...)
buf = append(buf, '}')
e.buf = e.buf[:pos[0]]
e.buf = append(e.buf, buf...)
code = code.next
case opStructFieldRecursive: case opStructFieldRecursive:
ptr := load(ctxptr, code.idx) ptr := load(ctxptr, code.idx)
if ptr != 0 { if ptr != 0 {

20
json.go
View File

@ -154,8 +154,18 @@ type Unmarshaler interface {
// an infinite recursion. // an infinite recursion.
// //
func Marshal(v interface{}) ([]byte, error) { func Marshal(v interface{}) ([]byte, error) {
return MarshalWithOption(v)
}
// MarshalWithOption returns the JSON encoding of v with EncodeOption.
func MarshalWithOption(v interface{}, opts ...EncodeOption) ([]byte, error) {
var b *bytes.Buffer var b *bytes.Buffer
enc := NewEncoder(b) enc := NewEncoder(b)
for _, opt := range opts {
if err := opt(enc); err != nil {
return nil, err
}
}
bytes, err := enc.encodeForMarshal(v) bytes, err := enc.encodeForMarshal(v)
if err != nil { if err != nil {
enc.release() enc.release()
@ -169,8 +179,18 @@ func Marshal(v interface{}) ([]byte, error) {
// Each JSON element in the output will begin on a new line beginning with prefix // Each JSON element in the output will begin on a new line beginning with prefix
// followed by one or more copies of indent according to the indentation nesting. // followed by one or more copies of indent according to the indentation nesting.
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) { func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
return MarshalIndentWithOption(v, prefix, indent)
}
// MarshalIndentWithOption is like Marshal but applies Indent to format the output with EncodeOption.
func MarshalIndentWithOption(v interface{}, prefix, indent string, opts ...EncodeOption) ([]byte, error) {
var b *bytes.Buffer var b *bytes.Buffer
enc := NewEncoder(b) enc := NewEncoder(b)
for _, opt := range opts {
if err := opt(enc); err != nil {
return nil, err
}
}
enc.SetIndent(prefix, indent) enc.SetIndent(prefix, indent)
bytes, err := enc.encodeForMarshal(v) bytes, err := enc.encodeForMarshal(v)
if err != nil { if err != nil {