github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/protobuf/plugin/deepcopy/deepcopy.go (about)

     1  package deepcopy
     2  
     3  import (
     4  	"github.com/docker/swarmkit/protobuf/plugin"
     5  	"github.com/gogo/protobuf/gogoproto"
     6  	"github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
     7  	"github.com/gogo/protobuf/protoc-gen-gogo/generator"
     8  )
     9  
    10  type deepCopyGen struct {
    11  	*generator.Generator
    12  	generator.PluginImports
    13  	copyPkg generator.Single
    14  }
    15  
    16  func init() {
    17  	generator.RegisterPlugin(new(deepCopyGen))
    18  }
    19  
    20  func (d *deepCopyGen) Name() string {
    21  	return "deepcopy"
    22  }
    23  
    24  func (d *deepCopyGen) Init(g *generator.Generator) {
    25  	d.Generator = g
    26  }
    27  
    28  func (d *deepCopyGen) genCopyFunc(dst, src string) {
    29  	d.P(d.copyPkg.Use(), ".Copy(", dst, ", ", src, ")")
    30  }
    31  
    32  func (d *deepCopyGen) genCopyBytes(dst, src string) {
    33  	d.P("if ", src, " != nil {")
    34  	d.In()
    35  	// allocate dst object
    36  	d.P(dst, " = make([]byte, len(", src, "))")
    37  	// copy bytes from src to dst
    38  	d.P("copy(", dst, ", ", src, ")")
    39  	d.Out()
    40  	d.P("}")
    41  }
    42  
    43  func (d *deepCopyGen) genMsgDeepCopy(m *generator.Descriptor) {
    44  	ccTypeName := generator.CamelCaseSlice(m.TypeName())
    45  
    46  	// Generate backwards compatible, type-safe Copy() function.
    47  	d.P("func (m *", ccTypeName, ") Copy() *", ccTypeName, "{")
    48  	d.In()
    49  	d.P("if m == nil {")
    50  	d.In()
    51  	d.P("return nil")
    52  	d.Out()
    53  	d.P("}")
    54  	d.P("o := &", ccTypeName, "{}")
    55  	d.P("o.CopyFrom(m)")
    56  	d.P("return o")
    57  	d.Out()
    58  	d.P("}")
    59  	d.P()
    60  
    61  	if len(m.Field) == 0 {
    62  		d.P("func (m *", ccTypeName, ") CopyFrom(src interface{})", " {}")
    63  		return
    64  	}
    65  
    66  	d.P("func (m *", ccTypeName, ") CopyFrom(src interface{})", " {")
    67  	d.P()
    68  
    69  	d.P("o := src.(*", ccTypeName, ")")
    70  
    71  	// shallow copy handles all scalars
    72  	d.P("*m = *o")
    73  
    74  	oneofByIndex := [][]*descriptor.FieldDescriptorProto{}
    75  	for _, f := range m.Field {
    76  		fName := generator.CamelCase(*f.Name)
    77  		if gogoproto.IsCustomName(f) {
    78  			fName = gogoproto.GetCustomName(f)
    79  		}
    80  
    81  		// Handle oneof type, we defer them to a loop below
    82  		if f.OneofIndex != nil {
    83  			if len(oneofByIndex) <= int(*f.OneofIndex) {
    84  				oneofByIndex = append(oneofByIndex, []*descriptor.FieldDescriptorProto{})
    85  			}
    86  
    87  			oneofByIndex[*f.OneofIndex] = append(oneofByIndex[*f.OneofIndex], f)
    88  			continue
    89  		}
    90  
    91  		// Handle all kinds of message type
    92  		if f.IsMessage() {
    93  			// Handle map type
    94  			if d.genMap(m, f) {
    95  				continue
    96  			}
    97  
    98  			// Handle any message which is not repeated or part of oneof
    99  			if !f.IsRepeated() && f.OneofIndex == nil {
   100  				if !gogoproto.IsNullable(f) {
   101  					d.genCopyFunc("&m."+fName, "&o."+fName)
   102  				} else {
   103  					d.P("if o.", fName, " != nil {")
   104  					d.In()
   105  					// allocate dst object
   106  					d.P("m.", fName, " = &", d.TypeName(d.ObjectNamed(f.GetTypeName())), "{}")
   107  					// copy into the allocated struct
   108  					d.genCopyFunc("m."+fName, "o."+fName)
   109  
   110  					d.Out()
   111  					d.P("}")
   112  				}
   113  				continue
   114  			}
   115  		}
   116  
   117  		// Handle repeated field
   118  		if f.IsRepeated() {
   119  			d.genRepeated(m, f)
   120  			continue
   121  		}
   122  
   123  		// Handle bytes
   124  		if f.IsBytes() {
   125  			d.genCopyBytes("m."+fName, "o."+fName)
   126  			continue
   127  		}
   128  
   129  		// skip: field was a scalar handled by shallow copy!
   130  	}
   131  
   132  	for i, oo := range m.GetOneofDecl() {
   133  		d.genOneOf(m, oo, oneofByIndex[i])
   134  	}
   135  
   136  	d.P("}")
   137  	d.P()
   138  }
   139  
   140  func (d *deepCopyGen) genMap(m *generator.Descriptor, f *descriptor.FieldDescriptorProto) bool {
   141  	fName := generator.CamelCase(*f.Name)
   142  	if gogoproto.IsCustomName(f) {
   143  		fName = gogoproto.GetCustomName(f)
   144  	}
   145  
   146  	dv := d.ObjectNamed(f.GetTypeName())
   147  	desc, ok := dv.(*generator.Descriptor)
   148  	if !ok || !desc.GetOptions().GetMapEntry() {
   149  		return false
   150  	}
   151  
   152  	mt := d.GoMapType(desc, f)
   153  	typename := mt.GoType
   154  
   155  	d.P("if o.", fName, " != nil {")
   156  	d.In()
   157  	d.P("m.", fName, " = make(", typename, ", ", "len(o.", fName, "))")
   158  	d.P("for k, v := range o.", fName, " {")
   159  	d.In()
   160  	if mt.ValueField.IsMessage() {
   161  		if !gogoproto.IsNullable(f) {
   162  			d.P("n := ", d.TypeName(d.ObjectNamed(mt.ValueField.GetTypeName())), "{}")
   163  			d.genCopyFunc("&n", "&v")
   164  			d.P("m.", fName, "[k] = ", "n")
   165  		} else {
   166  			d.P("m.", fName, "[k] = &", d.TypeName(d.ObjectNamed(mt.ValueField.GetTypeName())), "{}")
   167  			d.genCopyFunc("m."+fName+"[k]", "v")
   168  		}
   169  	} else if mt.ValueField.IsBytes() {
   170  		d.P("m.", fName, "[k] = o.", fName, "[k]")
   171  		d.genCopyBytes("m."+fName+"[k]", "o."+fName+"[k]")
   172  	} else {
   173  		d.P("m.", fName, "[k] = v")
   174  	}
   175  	d.Out()
   176  	d.P("}")
   177  	d.Out()
   178  	d.P("}")
   179  	d.P()
   180  
   181  	return true
   182  }
   183  
   184  func (d *deepCopyGen) genRepeated(m *generator.Descriptor, f *descriptor.FieldDescriptorProto) {
   185  	fName := generator.CamelCase(*f.Name)
   186  	if gogoproto.IsCustomName(f) {
   187  		fName = gogoproto.GetCustomName(f)
   188  	}
   189  
   190  	typename, _ := d.GoType(m, f)
   191  
   192  	d.P("if o.", fName, " != nil {")
   193  	d.In()
   194  	d.P("m.", fName, " = make(", typename, ", len(o.", fName, "))")
   195  	if f.IsMessage() {
   196  		// TODO(stevvooe): Handle custom type here?
   197  		goType := d.TypeName(d.ObjectNamed(f.GetTypeName())) // elides [] or *
   198  
   199  		d.P("for i := range m.", fName, " {")
   200  		d.In()
   201  		if !gogoproto.IsNullable(f) {
   202  			d.genCopyFunc("&m."+fName+"[i]", "&o."+fName+"[i]")
   203  		} else {
   204  			d.P("m.", fName, "[i] = &", goType, "{}")
   205  			d.genCopyFunc("m."+fName+"[i]", "o."+fName+"[i]")
   206  		}
   207  		d.Out()
   208  		d.P("}")
   209  	} else if f.IsBytes() {
   210  		d.P("for i := range m.", fName, " {")
   211  		d.In()
   212  		d.genCopyBytes("m."+fName+"[i]", "o."+fName+"[i]")
   213  		d.Out()
   214  		d.P("}")
   215  	} else {
   216  		d.P("copy(m.", fName, ", ", "o.", fName, ")")
   217  	}
   218  	d.Out()
   219  	d.P("}")
   220  	d.P()
   221  }
   222  
   223  func (d *deepCopyGen) genOneOf(m *generator.Descriptor, oneof *descriptor.OneofDescriptorProto, fields []*descriptor.FieldDescriptorProto) {
   224  	oneOfName := generator.CamelCase(oneof.GetName())
   225  
   226  	d.P("if o.", oneOfName, " != nil {")
   227  	d.In()
   228  	d.P("switch o.", oneOfName, ".(type) {")
   229  
   230  	for _, f := range fields {
   231  		ccTypeName := generator.CamelCaseSlice(m.TypeName())
   232  		fName := generator.CamelCase(*f.Name)
   233  		if gogoproto.IsCustomName(f) {
   234  			fName = gogoproto.GetCustomName(f)
   235  		}
   236  
   237  		tName := ccTypeName + "_" + fName
   238  		d.P("case *", tName, ":")
   239  		d.In()
   240  		d.P("v := ", tName, " {")
   241  		d.In()
   242  
   243  		var rhs string
   244  		if f.IsMessage() {
   245  			goType := d.TypeName(d.ObjectNamed(f.GetTypeName())) // elides [] or *
   246  			rhs = "&" + goType + "{}"
   247  		} else if f.IsBytes() {
   248  			rhs = "make([]byte, len(o.Get" + fName + "()))"
   249  		} else {
   250  			rhs = "o.Get" + fName + "()"
   251  		}
   252  		d.P(fName, ": ", rhs, ",")
   253  		d.Out()
   254  		d.P("}")
   255  
   256  		if f.IsMessage() {
   257  			d.genCopyFunc("v."+fName, "o.Get"+fName+"()")
   258  		} else if f.IsBytes() {
   259  			d.genCopyBytes("v."+fName, "o.Get"+fName+"()")
   260  		}
   261  
   262  		d.P("m.", oneOfName, " = &v")
   263  		d.Out()
   264  	}
   265  
   266  	d.Out()
   267  	d.P("}")
   268  	d.Out()
   269  	d.P("}")
   270  	d.P()
   271  }
   272  
   273  func (d *deepCopyGen) Generate(file *generator.FileDescriptor) {
   274  	d.PluginImports = generator.NewPluginImports(d.Generator)
   275  
   276  	// TODO(stevvooe): Ideally, this could be taken as a parameter to the
   277  	// deepcopy plugin to control the package import, but this is good enough,
   278  	// for now.
   279  	d.copyPkg = d.NewImport("github.com/docker/swarmkit/api/deepcopy")
   280  
   281  	d.P()
   282  	for _, m := range file.Messages() {
   283  		if m.DescriptorProto.GetOptions().GetMapEntry() {
   284  			continue
   285  		}
   286  
   287  		if !plugin.DeepcopyEnabled(m.Options) {
   288  			continue
   289  		}
   290  
   291  		d.genMsgDeepCopy(m)
   292  	}
   293  	d.P()
   294  }