Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 42 additions & 17 deletions pkg/generators/golang/builders_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,11 @@ func (g *BuildersGenerator) generateStructBuilderFile(typ *concepts.Type) error
Packages(g.packages).
Package(pkgName).
File(fileName).
Function("fieldIndex", g.types.FieldIndex).
Function("bitMask", g.types.BitMask).
Function("fieldSetType", g.types.FieldSetType).
Function("bitmapType", g.types.BitmapType).
Function("fieldSetSize", g.types.FieldSetSize).
Function("builderCtor", g.builderCtor).
Function("builderName", g.builderName).
Function("fieldName", g.fieldName).
Expand Down Expand Up @@ -213,11 +216,11 @@ func (g *BuildersGenerator) generateStructBuilderSource(typ *concepts.Type) {
{{ $builderCtor := builderCtor .Type }}
{{ $objectName := objectName .Type }}

// {{ $builderName }} contains the data and logic needed to build '{{ .Type.Name }}' objects.

//
{{ lineComment .Type.Doc }}
type {{ $builderName }} struct {
bitmap_ {{ bitmapType .Type }}
fieldSet_ {{ fieldSetType .Type }}
{{ if .Type.IsClass }}
id string
href string
Expand All @@ -236,45 +239,61 @@ func (g *BuildersGenerator) generateStructBuilderSource(typ *concepts.Type) {

// {{ $builderCtor }} creates a new builder of '{{ .Type.Name }}' objects.
func {{ $builderCtor }}() *{{ $builderName }} {
return &{{ $builderName }}{}
return &{{ $builderName }}{
fieldSet_: make([]bool, {{ fieldSetSize .Type }}),
}
}

{{ if .Type.IsClass }}
// Link sets the flag that indicates if this is a link.
func (b *{{ $builderName }}) Link(value bool) *{{ $builderName }} {
b.bitmap_ |= 1
b.fieldSet_[0] = true
return b
}

// ID sets the identifier of the object.
func (b *{{ $builderName }}) ID(value string) *{{ $builderName }} {
b.id = value
b.bitmap_ |= 2
b.fieldSet_[1] = true
return b
}

// HREF sets the link to the object.
func (b *{{ $builderName }}) HREF(value string) *{{ $builderName }} {
b.href = value
b.bitmap_ |= 4
b.fieldSet_[2] = true
return b
}
{{ end }}

// Empty returns true if the builder is empty, i.e. no attribute has a value.
func (b *{{ $builderName }}) Empty() bool {
if b == nil || len(b.fieldSet_) == 0 {
return true
}
{{ if .Type.IsClass }}
return b == nil || b.bitmap_&^1 == 0
// Check all fields except the link flag (index 0)
for i := 1; i < len(b.fieldSet_); i++ {
if b.fieldSet_[i] {
return false
}
}
return true
{{ else }}
return b == nil || b.bitmap_ == 0
for _, set := range b.fieldSet_ {
if set {
return false
}
}
return true
{{ end }}
}

{{ range .Type.Attributes }}
{{ $fieldName := fieldName . }}
{{ $setterName := setterName . }}
{{ $setterType := setterType . }}
{{ $fieldMask := bitMask . }}
{{ $fieldIndex := fieldIndex . }}
{{ $selectorType := selectorType . }}

{{ if .Type.IsList }}
Expand All @@ -284,7 +303,7 @@ func (g *BuildersGenerator) generateStructBuilderSource(typ *concepts.Type) {
{{ if .Link }}
func (b *{{ $builderName }}) {{ $setterName }}(value {{ $setterType }}) *{{ $builderName }} {
b.{{ $fieldName }} = value
b.bitmap_ |= {{ $fieldMask }}
b.fieldSet_[{{ $fieldIndex }}] = true
return b
}
{{ else }}
Expand All @@ -293,15 +312,15 @@ func (g *BuildersGenerator) generateStructBuilderSource(typ *concepts.Type) {
func (b *{{ $builderName }}) {{ $setterName }}(values ...{{ $elementType }}) *{{ $builderName }} {
b.{{ $fieldName }} = make([]{{ $elementType }}, len(values))
copy(b.{{ $fieldName }}, values)
b.bitmap_ |= {{ $fieldMask }}
b.fieldSet_[{{ $fieldIndex }}] = true
return b
}
{{ else }}
{{ $elementBuilderName := builderName .Type.Element }}
func (b *{{ $builderName }}) {{ $setterName }}(values ...*{{ selectorType . }}{{ $elementBuilderName }}) *{{ $builderName }} {
b.{{ $fieldName }} = make([]*{{ selectorType . }}{{ $elementBuilderName }}, len(values))
copy(b.{{ $fieldName }}, values)
b.bitmap_ |= {{ $fieldMask }}
b.fieldSet_[{{ $fieldIndex }}] = true
return b
}
{{ end }}
Expand All @@ -313,12 +332,12 @@ func (g *BuildersGenerator) generateStructBuilderSource(typ *concepts.Type) {
func (b *{{ $builderName }}) {{ $setterName }}(value {{ $setterType }}) *{{ $builderName }} {
b.{{ $fieldName }} = value
{{ if .Type.IsScalar }}
b.bitmap_ |= {{ $fieldMask }}
b.fieldSet_[{{ $fieldIndex }}] = true
{{ else }}
if value != nil {
b.bitmap_ |= {{ $fieldMask }}
b.fieldSet_[{{ $fieldIndex }}] = true
} else {
b.bitmap_ &^= {{ $fieldMask }}
b.fieldSet_[{{ $fieldIndex }}] = false
}
{{ end }}
return b
Expand All @@ -331,7 +350,10 @@ func (g *BuildersGenerator) generateStructBuilderSource(typ *concepts.Type) {
if object == nil {
return b
}
b.bitmap_ = object.bitmap_
if len(object.fieldSet_) > 0 {
b.fieldSet_ = make([]bool, len(object.fieldSet_))
copy(b.fieldSet_, object.fieldSet_)
}
{{ if .Type.IsClass }}
b.id = object.id
b.href = object.href
Expand Down Expand Up @@ -394,7 +416,10 @@ func (g *BuildersGenerator) generateStructBuilderSource(typ *concepts.Type) {
object.id = b.id
object.href = b.href
{{ end }}
object.bitmap_ = b.bitmap_
if len(b.fieldSet_) > 0 {
object.fieldSet_ = make([]bool, len(b.fieldSet_))
copy(object.fieldSet_, b.fieldSet_)
}
{{ range .Type.Attributes }}
{{ $fieldName := fieldName . }}
{{ $fieldType := fieldType . }}
Expand Down
35 changes: 21 additions & 14 deletions pkg/generators/golang/json_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,7 @@ func (g *JSONSupportGenerator) generateVersionMetadataSource(version *concepts.V

func writeMetadata(object *Metadata, stream *jsoniter.Stream) {
stream.WriteObjectStart()
if object.bitmap_&1 != 0 {
if len(object.fieldSet_) > 0 && object.fieldSet_[0] {
stream.WriteObjectField("server_version")
stream.WriteString(object.serverVersion)
}
Expand Down Expand Up @@ -510,7 +510,10 @@ func (g *JSONSupportGenerator) generateVersionMetadataSource(version *concepts.V
switch field {
case "server_version":
object.serverVersion = iterator.ReadString()
object.bitmap_ |= 1
if len(object.fieldSet_) <= 0 {
object.fieldSet_ = make([]bool, 1)
}
object.fieldSet_[0] = true
default:
iterator.ReadAny()
}
Expand Down Expand Up @@ -540,7 +543,9 @@ func (g *JSONSupportGenerator) generateStructTypeSupport(typ *concepts.Type,
Packages(g.packages).
Package(pkgName).
File(fileName).
Function("fieldIndex", g.types.FieldIndex).
Function("bitMask", g.types.BitMask).
Function("fieldSetSize", g.types.FieldSetSize).
Function("enumName", g.types.EnumName).
Function("fieldName", g.fieldName).
Function("fieldTag", g.binding.AttributeName).
Expand Down Expand Up @@ -601,21 +606,21 @@ func (g *JSONSupportGenerator) generateStructTypeSource(typ *concepts.Type) {
stream.WriteObjectStart()
{{ if .Type.IsClass }}
stream.WriteObjectField("kind")
if object.bitmap_&1 != 0 {
if len(object.fieldSet_) > 0 && object.fieldSet_[0] {
stream.WriteString({{ $structName }}LinkKind)
} else {
stream.WriteString({{ $structName }}Kind)
}
count++
if object.bitmap_&2 != 0 {
if len(object.fieldSet_) > 1 && object.fieldSet_[1] {
if count > 0 {
stream.WriteMore()
}
stream.WriteObjectField("id")
stream.WriteString(object.id)
count++
}
if object.bitmap_&4 != 0 {
if len(object.fieldSet_) > 2 && object.fieldSet_[2] {
if count > 0 {
stream.WriteMore()
}
Expand All @@ -632,11 +637,11 @@ func (g *JSONSupportGenerator) generateStructTypeSource(typ *concepts.Type) {
{{ range $i, $v := .Type.Attributes }}
{{ $fieldName := fieldName $v }}
{{ $fieldTag := fieldTag $v }}
{{ $fieldMask := bitMask $v }}
{{ $fieldIndex := fieldIndex $v }}
{{ if .Type.IsScalar }}
present_ = object.bitmap_&{{ $fieldMask }} != 0
present_ = len(object.fieldSet_) > {{ $fieldIndex }} && object.fieldSet_[{{ $fieldIndex }}]
{{ else }}
present_ = object.bitmap_&{{ $fieldMask }} != 0 && object.{{ $fieldName }} != nil
present_ = len(object.fieldSet_) > {{ $fieldIndex }} && object.fieldSet_[{{ $fieldIndex }}] && object.{{ $fieldName }} != nil
{{ end }}
if present_ {
if count > 0 {
Expand Down Expand Up @@ -667,7 +672,9 @@ func (g *JSONSupportGenerator) generateStructTypeSource(typ *concepts.Type) {

// {{ $readTypeFunc }} reads a value of the '{{ .Type.Name }}' type from the given iterator.
func {{ $readTypeFunc }}(iterator *jsoniter.Iterator) *{{ $structName }} {
object := &{{ $structName }}{}
object := &{{ $structName }}{
fieldSet_: make([]bool, {{ fieldSetSize .Type }}),
}
for {
field := iterator.ReadObject()
if field == "" {
Expand All @@ -678,23 +685,23 @@ func (g *JSONSupportGenerator) generateStructTypeSource(typ *concepts.Type) {
case "kind":
value := iterator.ReadString()
if value == {{ $structName }}LinkKind {
object.bitmap_ |= 1
object.fieldSet_[0] = true
}
case "id":
object.id = iterator.ReadString()
object.bitmap_ |= 2
object.fieldSet_[1] = true
case "href":
object.href = iterator.ReadString()
object.bitmap_ |= 4
object.fieldSet_[2] = true
{{ end }}
{{ range .Type.Attributes }}
{{ $fieldName := fieldName . }}
{{ $fieldTag := fieldTag . }}
{{ $fieldMask := bitMask . }}
{{ $fieldIndex := fieldIndex . }}
case "{{ $fieldTag }}":
{{ generateReadValue "value" .Type .Link .LinkOwner }}
object.{{ $fieldName }} = value
object.bitmap_ |= {{ $fieldMask }}
object.fieldSet_[{{ $fieldIndex }}] = true
{{ end }}
default:
iterator.ReadAny()
Expand Down
67 changes: 27 additions & 40 deletions pkg/generators/golang/types_calculator.go
Original file line number Diff line number Diff line change
Expand Up @@ -467,8 +467,8 @@ func (c *TypesCalculator) Package(typ *concepts.Type) (imprt, selector string) {
}
}

// BitMask calculates the bit mask used to check presence of the given attribute.
func (c *TypesCalculator) BitMask(attribute *concepts.Attribute) string {
// FieldIndex calculates the index used to track presence of the given attribute in the boolean slice.
func (c *TypesCalculator) FieldIndex(attribute *concepts.Attribute) string {
// Calculate the index taking into account that the the builtin `link`, `id` and `href`
// fields of class types:
var index int
Expand All @@ -483,64 +483,51 @@ func (c *TypesCalculator) BitMask(attribute *concepts.Attribute) string {
index++
}

// Calculate the mask:
mask := 1 << index
return fmt.Sprintf("%d", mask)
return fmt.Sprintf("%d", index)
}

// BitmapType calculates the reference for the type that will be used to implement the attribute
// presence bitset for the given struct type.
func (c *TypesCalculator) BitmapType(typ *concepts.Type) *TypeReference {
// BitMask is kept for backward compatibility but now just returns the field index
func (c *TypesCalculator) BitMask(attribute *concepts.Attribute) string {
return c.FieldIndex(attribute)
}

// FieldSetType calculates the reference for the type that will be used to implement the attribute
// presence tracking for the given struct type.
func (c *TypesCalculator) FieldSetType(typ *concepts.Type) *TypeReference {
ref := &TypeReference{}

// Check that the given type is a struct:
if !typ.IsStruct() {
c.reporter.Errorf(
"Don't know how to make package name for type '%s' of kind '%s'",
"Don't know how to make field set type for type '%s' of kind '%s'",
typ.Name(), typ.Kind(),
)
return ref
}

// Check that there are no more than the maximum number of attributes that can fit in the
// bitmap:
max := 64
if typ.IsClass() {
max -= 3
}
count := len(typ.Attributes())
if count > max {
c.reporter.Errorf(
"Struct type '%s' has more %d attributes, but at most %d attributes "+
"are supported",
typ, count, max,
)
return ref
}

// Select the smaller usigned integer tha thas room for all the attributes:
size := c.bitmapSize(typ)
name := fmt.Sprintf("uint%d", size)
ref.name = name
ref.text = name
// Always use boolean slice - no field limits!
ref.name = "[]bool"
ref.text = "[]bool"
return ref
}

// bitmapSize calculates the number of bits used for the presence bitmap of the given type, rounded
// up to a multiple of eight.
func (c *TypesCalculator) bitmapSize(typ *concepts.Type) int {
// For classes the first bit is reserved to indicate if the object is a link, and the second
// and third bits are used for the built-in `id` and `href` attributes:
// BitmapType is kept for backward compatibility but now returns boolean slice
func (c *TypesCalculator) BitmapType(typ *concepts.Type) *TypeReference {
return c.FieldSetType(typ)
}

// FieldSetSize calculates the number of fields that need tracking for the given type
func (c *TypesCalculator) FieldSetSize(typ *concepts.Type) int {
count := len(typ.Attributes())
if typ.IsClass() {
count += 3
}
return count
}

// Use a normal unsigned integer if possible, or a long one if more than 32 bits are needed:
if count <= 32 {
return 32
}
return 64
// BitmapSize is kept for backward compatibility
func (c *TypesCalculator) BitmapSize(typ *concepts.Type) int {
return c.FieldSetSize(typ)
}

// TypeReference represents a reference to a Go type.
Expand Down
Loading