github.com/ipld/go-ipld-prime@v0.21.0/schema/gen/go/genpartsList.go (about)

     1  package gengo
     2  
     3  import (
     4  	"io"
     5  )
     6  
     7  // FIXME docs: these methods all say "-oid" but I think that was overoptimistic and not actually that applicable, really.
     8  //  AssignNode?  Okay, that one's fine.
     9  //  The rest?  They're all *very* emphatic about knowing either:
    10  //   - that na.w.x is a slice; or,
    11  //   - that there's only one 'va' (one type; and that it's reused).
    12  //   The reuse level for those two traits is pretty minimal.
    13  
    14  func emitNodeAssemblerMethodBeginList_listoid(w io.Writer, adjCfg *AdjunctCfg, data interface{}) {
    15  	// This method contains a branch to support MaybeUsesPtr because new memory may need to be allocated.
    16  	//  This allocation only happens if the 'w' ptr is nil, which means we're being used on a Maybe;
    17  	//  otherwise, the 'w' ptr should already be set, and we fill that memory location without allocating, as usual.
    18  	//
    19  	// There's surprisingly little variation for IsRepr on this one:
    20  	//   - the child types we *store* are the same either way, so that doesn't vary;
    21  	//   - the only thing that we return that's different is... ourself.
    22  	//
    23  	// DRY: even further, to an extracted function in the final output?  Maybe.
    24  	//  This could be plausible, iff... the top half of the struct (na.m, na.w) was independently addressable.  (na.va has a varying concrete type and blocks extractions.)
    25  	//  Would also want to examine if that makes desirable trades in gsloc/asmsize/speed/debuggability.
    26  	//  Only seems to apply to case of list-repr-list, so unclear if worth the effort.
    27  	doTemplate(`
    28  		func (na *_{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Assembler) BeginList(sizeHint int64) (datamodel.ListAssembler, error) {
    29  			switch *na.m {
    30  			case schema.Maybe_Value, schema.Maybe_Null:
    31  				panic("invalid state: cannot assign into assembler that's already finished")
    32  			case midvalue:
    33  				panic("invalid state: it makes no sense to 'begin' twice on the same assembler!")
    34  			}
    35  			*na.m = midvalue
    36  			if sizeHint < 0 {
    37  				sizeHint = 0
    38  			}
    39  			{{- if .Type | MaybeUsesPtr }}
    40  			if na.w == nil {
    41  				na.w = &_{{ .Type | TypeSymbol }}{}
    42  			}
    43  			{{- end}}
    44  			if sizeHint > 0 {
    45  				na.w.x = make([]_{{ .Type.ValueType | TypeSymbol }}{{if .Type.ValueIsNullable }}__Maybe{{end}}, 0, sizeHint)
    46  			}
    47  			return na, nil
    48  		}
    49  	`, w, adjCfg, data)
    50  }
    51  
    52  func emitNodeAssemblerMethodAssignNode_listoid(w io.Writer, adjCfg *AdjunctCfg, data interface{}) {
    53  	// AssignNode goes through three phases:
    54  	// 1. is it null?  Jump over to AssignNull (which may or may not reject it).
    55  	// 2. is it our own type?  Handle specially -- we might be able to do efficient things.
    56  	// 3. is it the right kind to morph into us?  Do so.
    57  	//
    58  	// We do not set m=midvalue in phase 3 -- it shouldn't matter unless you're trying to pull off concurrent access, which is wrong and unsafe regardless.
    59  	//
    60  	// This works easily for both type-level and representational nodes because
    61  	//  any divergences that have to do with the child value are nicely hidden behind `AssembleValue`.
    62  	doTemplate(`
    63  		func (na *_{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Assembler) AssignNode(v datamodel.Node) error {
    64  			if v.IsNull() {
    65  				return na.AssignNull()
    66  			}
    67  			if v2, ok := v.(*_{{ .Type | TypeSymbol }}); ok {
    68  				switch *na.m {
    69  				case schema.Maybe_Value, schema.Maybe_Null:
    70  					panic("invalid state: cannot assign into assembler that's already finished")
    71  				case midvalue:
    72  					panic("invalid state: cannot assign null into an assembler that's already begun working on recursive structures!")
    73  				}
    74  				{{- if .Type | MaybeUsesPtr }}
    75  				if na.w == nil {
    76  					na.w = v2
    77  					*na.m = schema.Maybe_Value
    78  					return nil
    79  				}
    80  				{{- end}}
    81  				*na.w = *v2
    82  				*na.m = schema.Maybe_Value
    83  				return nil
    84  			}
    85  			if v.Kind() != datamodel.Kind_List {
    86  				return datamodel.ErrWrongKind{TypeName: "{{ .PkgName }}.{{ .Type.Name }}{{ if .IsRepr }}.Repr{{end}}", MethodName: "AssignNode", AppropriateKind: datamodel.KindSet_JustList, ActualKind: v.Kind()}
    87  			}
    88  			itr := v.ListIterator()
    89  			for !itr.Done() {
    90  				_, v, err := itr.Next()
    91  				if err != nil {
    92  					return err
    93  				}
    94  				if err := na.AssembleValue().AssignNode(v); err != nil {
    95  					return err
    96  				}
    97  			}
    98  			return na.Finish()
    99  		}
   100  	`, w, adjCfg, data)
   101  }
   102  
   103  func emitNodeAssemblerHelper_listoid_tidyHelper(w io.Writer, adjCfg *AdjunctCfg, data interface{}) {
   104  	// This function attempts to clean up the state machine to acknolwedge child value assembly finish.
   105  	//  If the child was finished and we just collected it, return true and update state to laState_initial.
   106  	//  Otherwise, if it wasn't done, return false;
   107  	//   and the caller is almost certain to emit an error momentarily.
   108  	// The function will only be called when the current state is laState_midValue.
   109  	//  (In general, the idea is that if the user is doing things correctly,
   110  	//   this function will only be called when the child is in fact finished.)
   111  	// If 'cm' is used, we reset it to its initial condition of Maybe_Absent here.
   112  	//  At the same time, we nil the 'w' pointer for the child assembler; otherwise its own state machine would probably let it modify 'w' again!
   113  	//
   114  	// DRY(nope): Can this be extracted to be a shared function between repr and type level nodes?
   115  	//  It is textually identical, so... yeah, that'd be nice.  But...
   116  	//  Nope.  It touches `la.va` directly.
   117  	//   Attempting to extract that or hide it behind an interface would create virtual function calls in a very tight spot, and we don't want the execution time cost.
   118  	doTemplate(`
   119  		func (la *_{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Assembler) valueFinishTidy() bool {
   120  			{{- if .Type.ValueIsNullable }}
   121  			row := &la.w.x[len(la.w.x)-1]
   122  			switch row.m {
   123  			case schema.Maybe_Value:
   124  				{{- if (MaybeUsesPtr .Type.ValueType) }}
   125  				row.v = la.va.w
   126  				{{- end}}
   127  				la.va.w = nil
   128  				fallthrough
   129  			case schema.Maybe_Null:
   130  				la.state = laState_initial
   131  				la.va.reset()
   132  				return true
   133  			{{- else}}
   134  			switch la.cm {
   135  			case schema.Maybe_Value:
   136  				la.va.w = nil
   137  				la.cm = schema.Maybe_Absent
   138  				la.state = laState_initial
   139  				la.va.reset()
   140  				return true
   141  			{{- end}}
   142  			default:
   143  				return false
   144  			}
   145  		}
   146  	`, w, adjCfg, data)
   147  }
   148  
   149  func emitNodeAssemblerHelper_listoid_listAssemblerMethods(w io.Writer, adjCfg *AdjunctCfg, data interface{}) {
   150  	// DRY: Might want to split this up a bit further so it can be used by more kinds.
   151  	//  Some parts of this could be reused by struct-repr-tuple, potentially, but would require being able to insert some more checks relating to length.
   152  	//   This would also require excluding *all* 'va' references; those are radicaly different for structs, in that there's not even one (singular) of them.
   153  	//
   154  	// DRY(nope): Can this be extracted to a shared function in the output?
   155  	//  Same story as the tidy helper -- it touches `la.va` concretely in several places, and that blocks extraction.
   156  	doTemplate(`
   157  		func (la *_{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Assembler) AssembleValue() datamodel.NodeAssembler {
   158  			switch la.state {
   159  			case laState_initial:
   160  				// carry on
   161  			case laState_midValue:
   162  				if !la.valueFinishTidy() {
   163  					panic("invalid state: AssembleValue cannot be called when still in the middle of assembling the previous value")
   164  				} // if tidy success: carry on
   165  			case laState_finished:
   166  				panic("invalid state: AssembleValue cannot be called on an assembler that's already finished")
   167  			}
   168  			la.w.x = append(la.w.x, _{{ .Type.ValueType | TypeSymbol }}{{if .Type.ValueIsNullable }}__Maybe{{end}}{})
   169  			la.state = laState_midValue
   170  			row := &la.w.x[len(la.w.x)-1]
   171  			{{- if .Type.ValueIsNullable }}
   172  			{{- if not (MaybeUsesPtr .Type.ValueType) }}
   173  			la.va.w = &row.v
   174  			{{- end}}
   175  			la.va.m = &row.m
   176  			row.m = allowNull
   177  			{{- else}}
   178  			la.va.w = row
   179  			la.va.m = &la.cm
   180  			{{- end}}
   181  			return &la.va
   182  		}
   183  	`, w, adjCfg, data)
   184  	doTemplate(`
   185  		func (la *_{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Assembler) Finish() error {
   186  			switch la.state {
   187  			case laState_initial:
   188  				// carry on
   189  			case laState_midValue:
   190  				if !la.valueFinishTidy() {
   191  					panic("invalid state: Finish cannot be called when in the middle of assembling a value")
   192  				} // if tidy success: carry on
   193  			case laState_finished:
   194  				panic("invalid state: Finish cannot be called on an assembler that's already finished")
   195  			}
   196  			la.state = laState_finished
   197  			*la.m = schema.Maybe_Value
   198  			return nil
   199  		}
   200  	`, w, adjCfg, data)
   201  	doTemplate(`
   202  		func (la *_{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Assembler) ValuePrototype(_ int64) datamodel.NodePrototype {
   203  			return _{{ .Type.ValueType | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Prototype{}
   204  		}
   205  	`, w, adjCfg, data)
   206  }