github.com/zerosnake0/jzon@v0.0.9-0.20230801092939-1b135cb83f7f/val_encoder_native_struct.go (about) 1 package jzon 2 3 import ( 4 "reflect" 5 "unsafe" 6 ) 7 8 type structEncoderBuilder struct { 9 encoder *structEncoder 10 fields structFields 11 } 12 13 func (encCfg *EncoderConfig) newStructEncoder(typ reflect.Type) *structEncoderBuilder { 14 fields := describeStruct(typ, encCfg.tag, encCfg.onlyTaggedField) 15 if len(fields) == 0 { 16 return nil 17 } 18 return &structEncoderBuilder{ 19 encoder: &structEncoder{}, 20 fields: fields, 21 } 22 } 23 24 // encoder field info 25 type encoderFieldInfo struct { 26 offsets []uintptr 27 rawField []byte 28 safeRawField []byte 29 quoted bool 30 omitEmpty bool 31 encoder ValEncoder 32 } 33 34 type encoderFields struct { 35 list []encoderFieldInfo 36 } 37 38 func (ef *encoderFields) init(size int) { 39 ef.list = make([]encoderFieldInfo, 0, size) 40 } 41 42 func (ef *encoderFields) add(f *field, enc ValEncoder) { 43 safeRawField := encodeString(nil, f.name, htmlSafeSet[:]) 44 rawField := encodeString(nil, f.name, safeSet[:]) 45 offsets := make([]uintptr, len(f.offsets)) 46 for i := range f.offsets { 47 offsets[i] = f.offsets[i].val 48 } 49 ef.list = append(ef.list, encoderFieldInfo{ 50 offsets: offsets, 51 rawField: rawField, 52 safeRawField: safeRawField, 53 quoted: f.quoted, 54 omitEmpty: f.omitEmpty, 55 encoder: enc, 56 }) 57 } 58 59 // struct encoder 60 type structEncoder struct { 61 fields encoderFields 62 } 63 64 func (enc *structEncoder) IsEmpty(ptr unsafe.Pointer) bool { 65 return false 66 } 67 68 func (enc *structEncoder) Encode(ptr unsafe.Pointer, s *Streamer, opts *EncOpts) { 69 if s.Error != nil { 70 return 71 } 72 if ptr == nil { 73 s.Null() 74 return 75 } 76 s.ObjectStart() 77 OuterLoop: 78 for i := range enc.fields.list { 79 fi := &enc.fields.list[i] 80 curPtr := add(ptr, fi.offsets[0], "struct field") 81 // ptr != nil => curPtr != nil 82 for _, offset := range fi.offsets[1:] { 83 curPtr = *(*unsafe.Pointer)(curPtr) 84 if curPtr == nil { 85 // the embedded pointer field is nil 86 continue OuterLoop 87 } 88 curPtr = add(curPtr, offset, "struct field") 89 } 90 if fi.omitEmpty { 91 if fi.encoder.IsEmpty(curPtr) { 92 continue 93 } 94 } 95 if s.escapeHTML { 96 s.RawField(fi.safeRawField) 97 } else { 98 s.RawField(fi.rawField) 99 } 100 opt := EncOpts{ 101 Quoted: fi.quoted, 102 } 103 fi.encoder.Encode(curPtr, s, opt.noescape()) 104 if s.Error != nil { 105 return 106 } 107 } 108 s.ObjectEnd() 109 } 110 111 // no fields to encoder 112 type emptyStructEncoder struct{} 113 114 func (*emptyStructEncoder) IsEmpty(ptr unsafe.Pointer) bool { 115 return true 116 } 117 118 func (*emptyStructEncoder) Encode(ptr unsafe.Pointer, s *Streamer, opts *EncOpts) { 119 if ptr == nil { 120 s.Null() 121 return 122 } 123 s.RawString("{}") 124 }