Skip to content

Commit eb17109

Browse files
authored
Merge pull request #235 from tzvatot/slice-bool
Use boolean slice to indicate field set state instead of bitmap to solve uint64 limitation
2 parents 51610f5 + 10c737a commit eb17109

File tree

4 files changed

+135
-86
lines changed

4 files changed

+135
-86
lines changed

pkg/generators/golang/builders_generator.go

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,11 @@ func (g *BuildersGenerator) generateStructBuilderFile(typ *concepts.Type) error
183183
Packages(g.packages).
184184
Package(pkgName).
185185
File(fileName).
186+
Function("fieldIndex", g.types.FieldIndex).
186187
Function("bitMask", g.types.BitMask).
188+
Function("fieldSetType", g.types.FieldSetType).
187189
Function("bitmapType", g.types.BitmapType).
190+
Function("fieldSetSize", g.types.FieldSetSize).
188191
Function("builderCtor", g.builderCtor).
189192
Function("builderName", g.builderName).
190193
Function("fieldName", g.fieldName).
@@ -213,11 +216,11 @@ func (g *BuildersGenerator) generateStructBuilderSource(typ *concepts.Type) {
213216
{{ $builderCtor := builderCtor .Type }}
214217
{{ $objectName := objectName .Type }}
215218
216-
// {{ $builderName }} contains the data and logic needed to build '{{ .Type.Name }}' objects.
219+
217220
//
218221
{{ lineComment .Type.Doc }}
219222
type {{ $builderName }} struct {
220-
bitmap_ {{ bitmapType .Type }}
223+
fieldSet_ {{ fieldSetType .Type }}
221224
{{ if .Type.IsClass }}
222225
id string
223226
href string
@@ -236,45 +239,61 @@ func (g *BuildersGenerator) generateStructBuilderSource(typ *concepts.Type) {
236239
237240
// {{ $builderCtor }} creates a new builder of '{{ .Type.Name }}' objects.
238241
func {{ $builderCtor }}() *{{ $builderName }} {
239-
return &{{ $builderName }}{}
242+
return &{{ $builderName }}{
243+
fieldSet_: make([]bool, {{ fieldSetSize .Type }}),
244+
}
240245
}
241246
242247
{{ if .Type.IsClass }}
243248
// Link sets the flag that indicates if this is a link.
244249
func (b *{{ $builderName }}) Link(value bool) *{{ $builderName }} {
245-
b.bitmap_ |= 1
250+
b.fieldSet_[0] = true
246251
return b
247252
}
248253
249254
// ID sets the identifier of the object.
250255
func (b *{{ $builderName }}) ID(value string) *{{ $builderName }} {
251256
b.id = value
252-
b.bitmap_ |= 2
257+
b.fieldSet_[1] = true
253258
return b
254259
}
255260
256261
// HREF sets the link to the object.
257262
func (b *{{ $builderName }}) HREF(value string) *{{ $builderName }} {
258263
b.href = value
259-
b.bitmap_ |= 4
264+
b.fieldSet_[2] = true
260265
return b
261266
}
262267
{{ end }}
263268
264269
// Empty returns true if the builder is empty, i.e. no attribute has a value.
265270
func (b *{{ $builderName }}) Empty() bool {
271+
if b == nil || len(b.fieldSet_) == 0 {
272+
return true
273+
}
266274
{{ if .Type.IsClass }}
267-
return b == nil || b.bitmap_&^1 == 0
275+
// Check all fields except the link flag (index 0)
276+
for i := 1; i < len(b.fieldSet_); i++ {
277+
if b.fieldSet_[i] {
278+
return false
279+
}
280+
}
281+
return true
268282
{{ else }}
269-
return b == nil || b.bitmap_ == 0
283+
for _, set := range b.fieldSet_ {
284+
if set {
285+
return false
286+
}
287+
}
288+
return true
270289
{{ end }}
271290
}
272291
273292
{{ range .Type.Attributes }}
274293
{{ $fieldName := fieldName . }}
275294
{{ $setterName := setterName . }}
276295
{{ $setterType := setterType . }}
277-
{{ $fieldMask := bitMask . }}
296+
{{ $fieldIndex := fieldIndex . }}
278297
{{ $selectorType := selectorType . }}
279298
280299
{{ if .Type.IsList }}
@@ -284,7 +303,7 @@ func (g *BuildersGenerator) generateStructBuilderSource(typ *concepts.Type) {
284303
{{ if .Link }}
285304
func (b *{{ $builderName }}) {{ $setterName }}(value {{ $setterType }}) *{{ $builderName }} {
286305
b.{{ $fieldName }} = value
287-
b.bitmap_ |= {{ $fieldMask }}
306+
b.fieldSet_[{{ $fieldIndex }}] = true
288307
return b
289308
}
290309
{{ else }}
@@ -293,15 +312,15 @@ func (g *BuildersGenerator) generateStructBuilderSource(typ *concepts.Type) {
293312
func (b *{{ $builderName }}) {{ $setterName }}(values ...{{ $elementType }}) *{{ $builderName }} {
294313
b.{{ $fieldName }} = make([]{{ $elementType }}, len(values))
295314
copy(b.{{ $fieldName }}, values)
296-
b.bitmap_ |= {{ $fieldMask }}
315+
b.fieldSet_[{{ $fieldIndex }}] = true
297316
return b
298317
}
299318
{{ else }}
300319
{{ $elementBuilderName := builderName .Type.Element }}
301320
func (b *{{ $builderName }}) {{ $setterName }}(values ...*{{ selectorType . }}{{ $elementBuilderName }}) *{{ $builderName }} {
302321
b.{{ $fieldName }} = make([]*{{ selectorType . }}{{ $elementBuilderName }}, len(values))
303322
copy(b.{{ $fieldName }}, values)
304-
b.bitmap_ |= {{ $fieldMask }}
323+
b.fieldSet_[{{ $fieldIndex }}] = true
305324
return b
306325
}
307326
{{ end }}
@@ -313,12 +332,12 @@ func (g *BuildersGenerator) generateStructBuilderSource(typ *concepts.Type) {
313332
func (b *{{ $builderName }}) {{ $setterName }}(value {{ $setterType }}) *{{ $builderName }} {
314333
b.{{ $fieldName }} = value
315334
{{ if .Type.IsScalar }}
316-
b.bitmap_ |= {{ $fieldMask }}
335+
b.fieldSet_[{{ $fieldIndex }}] = true
317336
{{ else }}
318337
if value != nil {
319-
b.bitmap_ |= {{ $fieldMask }}
338+
b.fieldSet_[{{ $fieldIndex }}] = true
320339
} else {
321-
b.bitmap_ &^= {{ $fieldMask }}
340+
b.fieldSet_[{{ $fieldIndex }}] = false
322341
}
323342
{{ end }}
324343
return b
@@ -331,7 +350,10 @@ func (g *BuildersGenerator) generateStructBuilderSource(typ *concepts.Type) {
331350
if object == nil {
332351
return b
333352
}
334-
b.bitmap_ = object.bitmap_
353+
if len(object.fieldSet_) > 0 {
354+
b.fieldSet_ = make([]bool, len(object.fieldSet_))
355+
copy(b.fieldSet_, object.fieldSet_)
356+
}
335357
{{ if .Type.IsClass }}
336358
b.id = object.id
337359
b.href = object.href
@@ -394,7 +416,10 @@ func (g *BuildersGenerator) generateStructBuilderSource(typ *concepts.Type) {
394416
object.id = b.id
395417
object.href = b.href
396418
{{ end }}
397-
object.bitmap_ = b.bitmap_
419+
if len(b.fieldSet_) > 0 {
420+
object.fieldSet_ = make([]bool, len(b.fieldSet_))
421+
copy(object.fieldSet_, b.fieldSet_)
422+
}
398423
{{ range .Type.Attributes }}
399424
{{ $fieldName := fieldName . }}
400425
{{ $fieldType := fieldType . }}

pkg/generators/golang/json_generator.go

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ func (g *JSONSupportGenerator) generateVersionMetadataSource(version *concepts.V
481481
482482
func writeMetadata(object *Metadata, stream *jsoniter.Stream) {
483483
stream.WriteObjectStart()
484-
if object.bitmap_&1 != 0 {
484+
if len(object.fieldSet_) > 0 && object.fieldSet_[0] {
485485
stream.WriteObjectField("server_version")
486486
stream.WriteString(object.serverVersion)
487487
}
@@ -510,7 +510,10 @@ func (g *JSONSupportGenerator) generateVersionMetadataSource(version *concepts.V
510510
switch field {
511511
case "server_version":
512512
object.serverVersion = iterator.ReadString()
513-
object.bitmap_ |= 1
513+
if len(object.fieldSet_) <= 0 {
514+
object.fieldSet_ = make([]bool, 1)
515+
}
516+
object.fieldSet_[0] = true
514517
default:
515518
iterator.ReadAny()
516519
}
@@ -540,7 +543,9 @@ func (g *JSONSupportGenerator) generateStructTypeSupport(typ *concepts.Type,
540543
Packages(g.packages).
541544
Package(pkgName).
542545
File(fileName).
546+
Function("fieldIndex", g.types.FieldIndex).
543547
Function("bitMask", g.types.BitMask).
548+
Function("fieldSetSize", g.types.FieldSetSize).
544549
Function("enumName", g.types.EnumName).
545550
Function("fieldName", g.fieldName).
546551
Function("fieldTag", g.binding.AttributeName).
@@ -601,21 +606,21 @@ func (g *JSONSupportGenerator) generateStructTypeSource(typ *concepts.Type) {
601606
stream.WriteObjectStart()
602607
{{ if .Type.IsClass }}
603608
stream.WriteObjectField("kind")
604-
if object.bitmap_&1 != 0 {
609+
if len(object.fieldSet_) > 0 && object.fieldSet_[0] {
605610
stream.WriteString({{ $structName }}LinkKind)
606611
} else {
607612
stream.WriteString({{ $structName }}Kind)
608613
}
609614
count++
610-
if object.bitmap_&2 != 0 {
615+
if len(object.fieldSet_) > 1 && object.fieldSet_[1] {
611616
if count > 0 {
612617
stream.WriteMore()
613618
}
614619
stream.WriteObjectField("id")
615620
stream.WriteString(object.id)
616621
count++
617622
}
618-
if object.bitmap_&4 != 0 {
623+
if len(object.fieldSet_) > 2 && object.fieldSet_[2] {
619624
if count > 0 {
620625
stream.WriteMore()
621626
}
@@ -632,11 +637,11 @@ func (g *JSONSupportGenerator) generateStructTypeSource(typ *concepts.Type) {
632637
{{ range $i, $v := .Type.Attributes }}
633638
{{ $fieldName := fieldName $v }}
634639
{{ $fieldTag := fieldTag $v }}
635-
{{ $fieldMask := bitMask $v }}
640+
{{ $fieldIndex := fieldIndex $v }}
636641
{{ if .Type.IsScalar }}
637-
present_ = object.bitmap_&{{ $fieldMask }} != 0
642+
present_ = len(object.fieldSet_) > {{ $fieldIndex }} && object.fieldSet_[{{ $fieldIndex }}]
638643
{{ else }}
639-
present_ = object.bitmap_&{{ $fieldMask }} != 0 && object.{{ $fieldName }} != nil
644+
present_ = len(object.fieldSet_) > {{ $fieldIndex }} && object.fieldSet_[{{ $fieldIndex }}] && object.{{ $fieldName }} != nil
640645
{{ end }}
641646
if present_ {
642647
if count > 0 {
@@ -667,7 +672,9 @@ func (g *JSONSupportGenerator) generateStructTypeSource(typ *concepts.Type) {
667672
668673
// {{ $readTypeFunc }} reads a value of the '{{ .Type.Name }}' type from the given iterator.
669674
func {{ $readTypeFunc }}(iterator *jsoniter.Iterator) *{{ $structName }} {
670-
object := &{{ $structName }}{}
675+
object := &{{ $structName }}{
676+
fieldSet_: make([]bool, {{ fieldSetSize .Type }}),
677+
}
671678
for {
672679
field := iterator.ReadObject()
673680
if field == "" {
@@ -678,23 +685,23 @@ func (g *JSONSupportGenerator) generateStructTypeSource(typ *concepts.Type) {
678685
case "kind":
679686
value := iterator.ReadString()
680687
if value == {{ $structName }}LinkKind {
681-
object.bitmap_ |= 1
688+
object.fieldSet_[0] = true
682689
}
683690
case "id":
684691
object.id = iterator.ReadString()
685-
object.bitmap_ |= 2
692+
object.fieldSet_[1] = true
686693
case "href":
687694
object.href = iterator.ReadString()
688-
object.bitmap_ |= 4
695+
object.fieldSet_[2] = true
689696
{{ end }}
690697
{{ range .Type.Attributes }}
691698
{{ $fieldName := fieldName . }}
692699
{{ $fieldTag := fieldTag . }}
693-
{{ $fieldMask := bitMask . }}
700+
{{ $fieldIndex := fieldIndex . }}
694701
case "{{ $fieldTag }}":
695702
{{ generateReadValue "value" .Type .Link .LinkOwner }}
696703
object.{{ $fieldName }} = value
697-
object.bitmap_ |= {{ $fieldMask }}
704+
object.fieldSet_[{{ $fieldIndex }}] = true
698705
{{ end }}
699706
default:
700707
iterator.ReadAny()

pkg/generators/golang/types_calculator.go

Lines changed: 27 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -467,8 +467,8 @@ func (c *TypesCalculator) Package(typ *concepts.Type) (imprt, selector string) {
467467
}
468468
}
469469

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

486-
// Calculate the mask:
487-
mask := 1 << index
488-
return fmt.Sprintf("%d", mask)
486+
return fmt.Sprintf("%d", index)
489487
}
490488

491-
// BitmapType calculates the reference for the type that will be used to implement the attribute
492-
// presence bitset for the given struct type.
493-
func (c *TypesCalculator) BitmapType(typ *concepts.Type) *TypeReference {
489+
// BitMask is kept for backward compatibility but now just returns the field index
490+
func (c *TypesCalculator) BitMask(attribute *concepts.Attribute) string {
491+
return c.FieldIndex(attribute)
492+
}
493+
494+
// FieldSetType calculates the reference for the type that will be used to implement the attribute
495+
// presence tracking for the given struct type.
496+
func (c *TypesCalculator) FieldSetType(typ *concepts.Type) *TypeReference {
494497
ref := &TypeReference{}
495498

496499
// Check that the given type is a struct:
497500
if !typ.IsStruct() {
498501
c.reporter.Errorf(
499-
"Don't know how to make package name for type '%s' of kind '%s'",
502+
"Don't know how to make field set type for type '%s' of kind '%s'",
500503
typ.Name(), typ.Kind(),
501504
)
502505
return ref
503506
}
504507

505-
// Check that there are no more than the maximum number of attributes that can fit in the
506-
// bitmap:
507-
max := 64
508-
if typ.IsClass() {
509-
max -= 3
510-
}
511-
count := len(typ.Attributes())
512-
if count > max {
513-
c.reporter.Errorf(
514-
"Struct type '%s' has more %d attributes, but at most %d attributes "+
515-
"are supported",
516-
typ, count, max,
517-
)
518-
return ref
519-
}
520-
521-
// Select the smaller usigned integer tha thas room for all the attributes:
522-
size := c.bitmapSize(typ)
523-
name := fmt.Sprintf("uint%d", size)
524-
ref.name = name
525-
ref.text = name
508+
// Always use boolean slice - no field limits!
509+
ref.name = "[]bool"
510+
ref.text = "[]bool"
526511
return ref
527512
}
528513

529-
// bitmapSize calculates the number of bits used for the presence bitmap of the given type, rounded
530-
// up to a multiple of eight.
531-
func (c *TypesCalculator) bitmapSize(typ *concepts.Type) int {
532-
// For classes the first bit is reserved to indicate if the object is a link, and the second
533-
// and third bits are used for the built-in `id` and `href` attributes:
514+
// BitmapType is kept for backward compatibility but now returns boolean slice
515+
func (c *TypesCalculator) BitmapType(typ *concepts.Type) *TypeReference {
516+
return c.FieldSetType(typ)
517+
}
518+
519+
// FieldSetSize calculates the number of fields that need tracking for the given type
520+
func (c *TypesCalculator) FieldSetSize(typ *concepts.Type) int {
534521
count := len(typ.Attributes())
535522
if typ.IsClass() {
536523
count += 3
537524
}
525+
return count
526+
}
538527

539-
// Use a normal unsigned integer if possible, or a long one if more than 32 bits are needed:
540-
if count <= 32 {
541-
return 32
542-
}
543-
return 64
528+
// BitmapSize is kept for backward compatibility
529+
func (c *TypesCalculator) BitmapSize(typ *concepts.Type) int {
530+
return c.FieldSetSize(typ)
544531
}
545532

546533
// TypeReference represents a reference to a Go type.

0 commit comments

Comments
 (0)