@@ -32,8 +32,11 @@ type Encoder struct {
32
32
isFlowStyle bool
33
33
isJSONStyle bool
34
34
useJSONMarshaler bool
35
+ enableSmartAnchor bool
36
+ aliasRefToName map [uintptr ]string
37
+ anchorRefToName map [uintptr ]string
38
+ anchorNameMap map [string ]struct {}
35
39
anchorCallback func (* ast.AnchorNode , interface {}) error
36
- anchorPtrToNameMap map [uintptr ]string
37
40
customMarshalerMap map [reflect.Type ]func (interface {}) ([]byte , error )
38
41
useLiteralStyleIfMultiline bool
39
42
commentMap map [* Path ][]* Comment
@@ -53,12 +56,14 @@ func NewEncoder(w io.Writer, opts ...EncodeOption) *Encoder {
53
56
return & Encoder {
54
57
writer : w ,
55
58
opts : opts ,
56
- anchorPtrToNameMap : map [uintptr ]string {},
57
59
customMarshalerMap : map [reflect.Type ]func (interface {}) ([]byte , error ){},
58
60
line : 1 ,
59
61
column : 1 ,
60
62
offset : 0 ,
61
63
indentNum : DefaultIndentSpaces ,
64
+ anchorRefToName : make (map [uintptr ]string ),
65
+ anchorNameMap : make (map [string ]struct {}),
66
+ aliasRefToName : make (map [uintptr ]string ),
62
67
}
63
68
}
64
69
@@ -110,6 +115,13 @@ func (e *Encoder) EncodeToNodeContext(ctx context.Context, v interface{}) (ast.N
110
115
return nil , err
111
116
}
112
117
}
118
+ if e .enableSmartAnchor {
119
+ // during the first encoding, store all mappings between alias addresses and their names.
120
+ if _ , err := e .encodeValue (ctx , reflect .ValueOf (v ), 1 ); err != nil {
121
+ return nil , err
122
+ }
123
+ e .clearSmartAnchorRef ()
124
+ }
113
125
node , err := e .encodeValue (ctx , reflect .ValueOf (v ), 1 )
114
126
if err != nil {
115
127
return nil , err
@@ -445,12 +457,8 @@ func (e *Encoder) encodeValue(ctx context.Context, v reflect.Value, column int)
445
457
case reflect .Float64 :
446
458
return e .encodeFloat (v .Float (), 64 ), nil
447
459
case reflect .Ptr :
448
- anchorName := e .anchorPtrToNameMap [v .Pointer ()]
449
- if anchorName != "" {
450
- aliasName := anchorName
451
- alias := ast .Alias (token .New ("*" , "*" , e .pos (column )))
452
- alias .Value = ast .String (token .New (aliasName , aliasName , e .pos (column )))
453
- return alias , nil
460
+ if value := e .encodePtrAnchor (v , column ); value != nil {
461
+ return value , nil
454
462
}
455
463
return e .encodeValue (ctx , v .Elem (), column )
456
464
case reflect .Interface :
@@ -463,6 +471,9 @@ func (e *Encoder) encodeValue(ctx context.Context, v reflect.Value, column int)
463
471
if mapSlice , ok := v .Interface ().(MapSlice ); ok {
464
472
return e .encodeMapSlice (ctx , mapSlice , column )
465
473
}
474
+ if value := e .encodePtrAnchor (v , column ); value != nil {
475
+ return value , nil
476
+ }
466
477
return e .encodeSlice (ctx , v )
467
478
case reflect .Array :
468
479
return e .encodeArray (ctx , v )
@@ -477,12 +488,27 @@ func (e *Encoder) encodeValue(ctx context.Context, v reflect.Value, column int)
477
488
}
478
489
return e .encodeStruct (ctx , v , column )
479
490
case reflect .Map :
491
+ if value := e .encodePtrAnchor (v , column ); value != nil {
492
+ return value , nil
493
+ }
480
494
return e .encodeMap (ctx , v , column ), nil
481
495
default :
482
496
return nil , fmt .Errorf ("unknown value type %s" , v .Type ().String ())
483
497
}
484
498
}
485
499
500
+ func (e * Encoder ) encodePtrAnchor (v reflect.Value , column int ) ast.Node {
501
+ anchorName , exists := e .getAnchor (v .Pointer ())
502
+ if ! exists {
503
+ return nil
504
+ }
505
+ aliasName := anchorName
506
+ alias := ast .Alias (token .New ("*" , "*" , e .pos (column )))
507
+ alias .Value = ast .String (token .New (aliasName , aliasName , e .pos (column )))
508
+ e .setSmartAlias (aliasName , v .Pointer ())
509
+ return alias
510
+ }
511
+
486
512
func (e * Encoder ) pos (column int ) * token.Position {
487
513
return & token.Position {
488
514
Line : e .line ,
@@ -676,11 +702,23 @@ func (e *Encoder) encodeMap(ctx context.Context, value reflect.Value, column int
676
702
if e .isTagAndMapNode (value ) {
677
703
value .AddColumn (e .indentNum )
678
704
}
705
+ keyText := fmt .Sprint (key )
706
+ vRef := e .toPointer (v )
707
+
708
+ // during the second encoding, an anchor is assigned if it is found to be used by an alias.
709
+ if aliasName , exists := e .getSmartAlias (vRef ); exists {
710
+ anchorName := aliasName
711
+ anchorNode := ast .Anchor (token .New ("&" , "&" , e .pos (column )))
712
+ anchorNode .Name = ast .String (token .New (anchorName , anchorName , e .pos (column )))
713
+ anchorNode .Value = value
714
+ value = anchorNode
715
+ }
679
716
node .Values = append (node .Values , ast .MappingValue (
680
717
nil ,
681
- e .encodeString (fmt . Sprint ( key ) , column ),
718
+ e .encodeString (keyText , column ),
682
719
value ,
683
720
))
721
+ e .setSmartAnchor (vRef , keyText )
684
722
}
685
723
return node
686
724
}
@@ -761,7 +799,7 @@ func (e *Encoder) encodeAnchor(anchorName string, value ast.Node, fieldValue ref
761
799
}
762
800
}
763
801
if fieldValue .Kind () == reflect .Ptr {
764
- e .anchorPtrToNameMap [ fieldValue .Pointer ()] = anchorName
802
+ e .setAnchor ( fieldValue .Pointer (), anchorName )
765
803
}
766
804
return anchorNode , nil
767
805
}
@@ -876,9 +914,87 @@ func (e *Encoder) encodeStruct(ctx context.Context, value reflect.Value, column
876
914
}
877
915
}
878
916
if inlineAnchorValue .Kind () == reflect .Ptr {
879
- e .anchorPtrToNameMap [ inlineAnchorValue .Pointer ()] = anchorName
917
+ e .setAnchor ( inlineAnchorValue .Pointer (), anchorName )
880
918
}
881
919
return anchorNode , nil
882
920
}
883
921
return node , nil
884
922
}
923
+
924
+ func (e * Encoder ) toPointer (v reflect.Value ) uintptr {
925
+ if e .isInvalidValue (v ) {
926
+ return 0
927
+ }
928
+
929
+ switch v .Type ().Kind () {
930
+ case reflect .Ptr :
931
+ return v .Pointer ()
932
+ case reflect .Interface :
933
+ return e .toPointer (v .Elem ())
934
+ case reflect .Slice :
935
+ return v .Pointer ()
936
+ case reflect .Map :
937
+ return v .Pointer ()
938
+ }
939
+ return 0
940
+ }
941
+
942
+ func (e * Encoder ) clearSmartAnchorRef () {
943
+ if ! e .enableSmartAnchor {
944
+ return
945
+ }
946
+ e .anchorRefToName = make (map [uintptr ]string )
947
+ e .anchorNameMap = make (map [string ]struct {})
948
+ }
949
+
950
+ func (e * Encoder ) setSmartAnchor (ptr uintptr , name string ) {
951
+ if ! e .enableSmartAnchor {
952
+ return
953
+ }
954
+ e .setAnchor (ptr , e .generateAnchorName (name ))
955
+ }
956
+
957
+ func (e * Encoder ) setAnchor (ptr uintptr , name string ) {
958
+ if ptr == 0 {
959
+ return
960
+ }
961
+ if name == "" {
962
+ return
963
+ }
964
+ e .anchorRefToName [ptr ] = name
965
+ e .anchorNameMap [name ] = struct {}{}
966
+ }
967
+
968
+ func (e * Encoder ) generateAnchorName (base string ) string {
969
+ if _ , exists := e .anchorNameMap [base ]; ! exists {
970
+ return base
971
+ }
972
+ for i := 1 ; i < 100 ; i ++ {
973
+ name := base + strconv .Itoa (i )
974
+ if _ , exists := e .anchorNameMap [name ]; exists {
975
+ continue
976
+ }
977
+ return name
978
+ }
979
+ return ""
980
+ }
981
+
982
+ func (e * Encoder ) getAnchor (ref uintptr ) (string , bool ) {
983
+ anchorName , exists := e .anchorRefToName [ref ]
984
+ return anchorName , exists
985
+ }
986
+
987
+ func (e * Encoder ) setSmartAlias (name string , ref uintptr ) {
988
+ if ! e .enableSmartAnchor {
989
+ return
990
+ }
991
+ e .aliasRefToName [ref ] = name
992
+ }
993
+
994
+ func (e * Encoder ) getSmartAlias (ref uintptr ) (string , bool ) {
995
+ if ! e .enableSmartAnchor {
996
+ return "" , false
997
+ }
998
+ aliasName , exists := e .aliasRefToName [ref ]
999
+ return aliasName , exists
1000
+ }
0 commit comments