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 }