@@ -3,22 +3,24 @@ import {JsExpression} from '@jsonjoy.com/codegen/lib/util/JsExpression';
33import { normalizeAccessor } from '@jsonjoy.com/codegen/lib/util/normalizeAccessor' ;
44import { MaxEncodingOverhead , maxEncodingCapacity } from '@jsonjoy.com/util/lib/json-size' ;
55import { BoolType , ConType , NumType , KeyOptType } from '../../type' ;
6- import type { ArrType , MapType , KeyType , ObjType , OrType , RefType , Type } from '../../type' ;
6+ import type { AnyType , ArrType , BinType , MapType , KeyType , ObjType , OrType , RefType , StrType , Type } from '../../type' ;
77import { DiscriminatorCodegen } from '../discriminator' ;
88import { lazyKeyedFactory } from '../util' ;
9+ import { AbstractCodegen } from '../AbstractCodege' ;
10+ import type { SchemaPath } from '../types' ;
911
1012export type CompiledCapacityEstimator = ( value : unknown ) => number ;
1113
1214class IncrementSizeStep {
1315 constructor ( public readonly inc : number ) { }
1416}
1517
16- export class CapacityEstimatorCodegen {
18+ export class CapacityEstimatorCodegen extends AbstractCodegen < CompiledCapacityEstimator > {
1719 public static readonly get = lazyKeyedFactory ( ( type : Type , name ?: string ) => {
1820 const codegen = new CapacityEstimatorCodegen ( type , name ) ;
1921 const r = codegen . codegen . options . args [ 0 ] ;
2022 const expression = new JsExpression ( ( ) => r ) ;
21- codegen . onNode ( expression , type ) ;
23+ codegen . onNode ( [ ] , expression , type ) ;
2224 return codegen . compile ( ) ;
2325 } ) ;
2426
@@ -28,6 +30,7 @@ export class CapacityEstimatorCodegen {
2830 public readonly type : Type ,
2931 name ?: string ,
3032 ) {
33+ super ( ) ;
3134 this . codegen = new Codegen ( {
3235 name : 'approxSize' + ( name ? '_' + name : '' ) ,
3336 args : [ 'r0' ] ,
@@ -48,94 +51,118 @@ export class CapacityEstimatorCodegen {
4851 this . codegen . linkDependency ( maxEncodingCapacity , 'maxEncodingCapacity' ) ;
4952 }
5053
51- public inc ( inc : number ) : void {
52- this . codegen . step ( new IncrementSizeStep ( inc ) ) ;
54+ private inc ( amount : number ) : void {
55+ this . codegen . js ( /* js */ `size += ${ amount } ;` ) ;
5356 }
5457
55- public compile ( ) : CompiledCapacityEstimator {
56- return this . codegen . compile ( ) ;
58+ protected onAny ( path : SchemaPath , r : JsExpression , type : AnyType ) : void {
59+ const codegen = this . codegen ;
60+ const rv = codegen . var ( r . use ( ) ) ;
61+ codegen . js ( /* js */ `size += maxEncodingCapacity(${ rv } );` ) ;
5762 }
5863
59- protected genAny ( value : JsExpression ) : void {
60- const codegen = this . codegen ;
61- const r = codegen . var ( value . use ( ) ) ;
62- codegen . js ( /* js */ `size += maxEncodingCapacity(${ r } );` ) ;
64+ protected onCon ( path : SchemaPath , r : JsExpression , type : ConType ) : void {
65+ this . inc ( maxEncodingCapacity ( type . literal ( ) ) ) ;
6366 }
6467
65- protected genArr ( value : JsExpression , type : ArrType < any , any , any > ) : void {
68+ protected onBool ( path : SchemaPath , r : JsExpression , type : BoolType ) : void {
69+ this . inc ( MaxEncodingOverhead . Boolean ) ;
70+ }
71+
72+ protected onNum ( path : SchemaPath , r : JsExpression , type : NumType ) : void {
73+ this . inc ( MaxEncodingOverhead . Number ) ;
74+ }
75+
76+ protected onStr ( path : SchemaPath , r : JsExpression , type : StrType ) : void {
77+ this . inc ( MaxEncodingOverhead . String ) ;
78+ this . codegen . js ( /* js */ `size += ${ MaxEncodingOverhead . StringLengthMultiplier } * ${ r . use ( ) } .length;` ) ;
79+ }
80+
81+ protected onBin ( path : SchemaPath , r : JsExpression , type : BinType ) : void {
82+ this . inc ( MaxEncodingOverhead . Binary ) ;
83+ this . codegen . js ( /* js */ `size += ${ MaxEncodingOverhead . BinaryLengthMultiplier } * ${ r . use ( ) } .length;` ) ;
84+ }
85+
86+ protected onArr ( path : SchemaPath , r : JsExpression , type : ArrType ) : void {
6687 const codegen = this . codegen ;
6788 this . inc ( MaxEncodingOverhead . Array ) ;
68- const rLen = codegen . var ( /* js */ `${ value . use ( ) } .length` ) ;
89+ const rLen = codegen . var ( /* js */ `${ r . use ( ) } .length` ) ;
6990 codegen . js ( /* js */ `size += ${ MaxEncodingOverhead . ArrayElement } * ${ rLen } ` ) ;
70- const itemType = type . _type as Type | undefined ;
71- const headType = type . _head as Type [ ] | undefined ;
72- const tailType = type . _tail as Type [ ] | undefined ;
73- const headLength = headType ? headType . length : 0 ;
74- const tailLength = tailType ? tailType . length : 0 ;
75- if ( itemType ) {
76- const isConstantSizeType = type instanceof ConType || type instanceof BoolType || type instanceof NumType ;
91+ const { _head = [ ] , _type, _tail = [ ] } = type ;
92+ const headLength = _head . length ;
93+ const tailLength = _tail . length ;
94+ // const tupleLength = headLength + tailLength;
95+ // if (tupleLength) {
96+ // codegen.if(/* js */ `${rLen} < ${tupleLength}`, () => {
97+ // codegen.js(/* js */ `throw new Error('INV_LEN');`);
98+ // });
99+ // }
100+ if ( _type ) {
101+ const isConstantSizeType = _type instanceof ConType || _type instanceof BoolType || _type instanceof NumType ;
77102 if ( isConstantSizeType ) {
78103 const elementSize =
79- type instanceof ConType
80- ? maxEncodingCapacity ( type . literal ( ) )
81- : type instanceof BoolType
104+ _type instanceof ConType
105+ ? maxEncodingCapacity ( _type . literal ( ) )
106+ : _type instanceof BoolType
82107 ? MaxEncodingOverhead . Boolean
83108 : MaxEncodingOverhead . Number ;
84109 codegen . js ( /* js */ `size += ${ rLen } * ${ elementSize } ;` ) ;
85110 } else {
86- const r = codegen . var ( value . use ( ) ) ;
111+ const rv = codegen . var ( r . use ( ) ) ;
87112 const ri = codegen . getRegister ( ) ;
88113 codegen . js ( /* js */ `for(var ${ ri } = ${ headLength } ; ${ ri } < ${ rLen } - ${ tailLength } ; ${ ri } ++) {` ) ;
89- this . onNode ( new JsExpression ( ( ) => /* js */ `${ r } [${ ri } ]` ) , itemType ) ;
114+ this . onNode ( [ ... path , { r : ri } ] , new JsExpression ( ( ) => /* js */ `${ rv } [${ ri } ]` ) , _type ) ;
90115 codegen . js ( /* js */ `}` ) ;
91116 }
92117 }
93118 if ( headLength > 0 ) {
94- const r = codegen . var ( value . use ( ) ) ;
95- for ( let i = 0 ; i < headLength ; i ++ ) this . onNode ( new JsExpression ( ( ) => /* js */ `${ r } [${ i } ]` ) , headType ! [ i ] ) ;
119+ const rr = codegen . var ( r . use ( ) ) ;
120+ for ( let i = 0 ; i < headLength ; i ++ ) this . onNode ( [ ... path , i ] , new JsExpression ( ( ) => /* js */ `${ rr } [${ i } ]` ) , _head ! [ i ] ) ;
96121 }
97122 if ( tailLength > 0 ) {
98- const r = codegen . var ( value . use ( ) ) ;
123+ const rr = codegen . var ( r . use ( ) ) ;
99124 for ( let i = 0 ; i < tailLength ; i ++ )
100- this . onNode ( new JsExpression ( ( ) => /* js */ `${ r } [${ rLen } - ${ i + 1 } ]` ) , tailType ! [ i ] ) ;
125+ this . onNode ( [ ... path , { r : ` ${ rLen } - ${ tailLength - i } ` } ] , new JsExpression ( ( ) => /* js */ `${ rr } [${ rLen } - ${ i + 1 } ]` ) , _tail ! [ i ] ) ;
101126 }
102127 }
103128
104- protected genObj ( value : JsExpression , type : Type ) : void {
129+ protected onObj ( path : SchemaPath , r : JsExpression , type : ObjType ) : void {
105130 const codegen = this . codegen ;
106- const r = codegen . var ( value . use ( ) ) ;
107- const objectType = type as ObjType ;
108- const encodeUnknownFields = ! ! objectType . schema . encodeUnknownKeys ;
131+ const rv = codegen . var ( r . use ( ) ) ;
132+ const encodeUnknownFields = ! ! type . schema . encodeUnknownKeys ;
109133 if ( encodeUnknownFields ) {
110- codegen . js ( /* js */ `size += maxEncodingCapacity(${ r } );` ) ;
134+ codegen . js ( /* js */ `size += maxEncodingCapacity(${ rv } );` ) ;
111135 return ;
112136 }
113137 this . inc ( MaxEncodingOverhead . Object ) ;
114- const fields = objectType . keys ;
115- for ( const f of fields ) {
116- const field = f as KeyType < any , any > ;
138+ const fields = type . keys ;
139+ for ( const field of fields ) {
117140 const accessor = normalizeAccessor ( field . key ) ;
118- const fieldExpression = new JsExpression ( ( ) => `${ r } ${ accessor } ` ) ;
141+ const fieldExpression = new JsExpression ( ( ) => `${ rv } ${ accessor } ` ) ;
119142 const isOptional = field instanceof KeyOptType ;
120143 if ( isOptional ) {
121- codegen . if ( /* js */ `${ JSON . stringify ( field . key ) } in ${ r } ` , ( ) => {
144+ codegen . if ( /* js */ `${ JSON . stringify ( field . key ) } in ${ rv } ` , ( ) => {
122145 this . inc ( MaxEncodingOverhead . ObjectElement ) ;
123146 this . inc ( maxEncodingCapacity ( field . key ) ) ;
124- this . onNode ( fieldExpression , field . val ) ;
147+ this . onNode ( [ ... path , field . key ] , fieldExpression , field . val ) ;
125148 } ) ;
126149 } else {
127150 this . inc ( MaxEncodingOverhead . ObjectElement ) ;
128151 this . inc ( maxEncodingCapacity ( field . key ) ) ;
129- this . onNode ( fieldExpression , field . val ) ;
152+ this . onNode ( [ ... path , field . key ] , fieldExpression , field . val ) ;
130153 }
131154 }
132155 }
133156
134- protected genMap ( value : JsExpression , type : MapType < any > ) : void {
157+ protected onKey ( path : SchemaPath , r : JsExpression , type : KeyType < any , any > ) : void {
158+ this . onNode ( [ ...path , type . key ] , r , type . val ) ;
159+ }
160+
161+ protected onMap ( path : SchemaPath , r : JsExpression , type : MapType ) : void {
135162 const codegen = this . codegen ;
136163 this . inc ( MaxEncodingOverhead . Object ) ;
137- const r = codegen . var ( value . use ( ) ) ;
138- const rKeys = codegen . var ( /* js */ `Object.keys(${ r } )` ) ;
164+ const rv = codegen . var ( r . use ( ) ) ;
165+ const rKeys = codegen . var ( /* js */ `Object.keys(${ rv } )` ) ;
139166 const rKey = codegen . var ( ) ;
140167 const rLen = codegen . var ( /* js */ `${ rKeys } .length` ) ;
141168 codegen . js ( /* js */ `size += ${ MaxEncodingOverhead . ObjectElement } * ${ rLen } ` ) ;
@@ -146,81 +173,31 @@ export class CapacityEstimatorCodegen {
146173 codegen . js (
147174 /* js */ `size += ${ MaxEncodingOverhead . String } + ${ MaxEncodingOverhead . StringLengthMultiplier } * ${ rKey } .length;` ,
148175 ) ;
149- this . onNode ( new JsExpression ( ( ) => /* js */ `${ r } [${ rKey } ]` ) , valueType ) ;
176+ this . onNode ( [ ... path , { r : rKey } ] , new JsExpression ( ( ) => /* js */ `${ rv } [${ rKey } ]` ) , valueType ) ;
150177 codegen . js ( /* js */ `}` ) ;
151178 }
152179
153- protected genRef ( value : JsExpression , type : RefType < any > ) : void {
154- const system = type . system ;
155- if ( ! system ) throw new Error ( 'NO_SYSTEM' ) ;
156- const estimator = CapacityEstimatorCodegen . get ( system . resolve ( type . ref ( ) ) . type ) ;
180+ protected onRef ( path : SchemaPath , r : JsExpression , type : RefType ) : void {
181+ const system = type . getSystem ( ) ;
182+ const alias = system . resolve ( type . ref ( ) ) ;
183+ const estimator = CapacityEstimatorCodegen . get ( alias . type ) ;
157184 const d = this . codegen . linkDependency ( estimator ) ;
158- this . codegen . js ( /* js */ `size += ${ d } (${ value . use ( ) } );` ) ;
185+ this . codegen . js ( /* js */ `size += ${ d } (${ r . use ( ) } );` ) ;
159186 }
160187
161- protected genOr ( value : JsExpression , type : OrType < any > ) : void {
188+ protected onOr ( path : SchemaPath , r : JsExpression , type : OrType ) : void {
162189 const codegen = this . codegen ;
163190 const discriminator = DiscriminatorCodegen . get ( type ) ;
164191 const d = codegen . linkDependency ( discriminator ) ;
165192 const types = type . types ;
166193 codegen . switch (
167- /* js */ `${ d } (${ value . use ( ) } )` ,
194+ /* js */ `${ d } (${ r . use ( ) } )` ,
168195 types . map ( ( childType : Type , index : number ) => [
169196 index ,
170197 ( ) => {
171- this . onNode ( value , childType ) ;
198+ this . onNode ( path , r , childType ) ;
172199 } ,
173200 ] ) ,
174201 ) ;
175202 }
176-
177- protected genKey ( r : JsExpression , type : KeyType < any , any > ) : void {
178- this . onNode ( r , type . val ) ;
179- }
180-
181- protected onNode ( value : JsExpression , type : Type ) : void {
182- const kind = type . kind ( ) ;
183- switch ( kind ) {
184- case 'any' :
185- this . genAny ( value ) ;
186- break ;
187- case 'bool' :
188- this . inc ( MaxEncodingOverhead . Boolean ) ;
189- break ;
190- case 'num' :
191- this . inc ( MaxEncodingOverhead . Number ) ;
192- break ;
193- case 'str' :
194- this . inc ( MaxEncodingOverhead . String ) ;
195- this . codegen . js ( /* js */ `size += ${ MaxEncodingOverhead . StringLengthMultiplier } * ${ value . use ( ) } .length;` ) ;
196- break ;
197- case 'bin' :
198- this . inc ( MaxEncodingOverhead . Binary ) ;
199- this . codegen . js ( /* js */ `size += ${ MaxEncodingOverhead . BinaryLengthMultiplier } * ${ value . use ( ) } .length;` ) ;
200- break ;
201- case 'con' :
202- this . inc ( maxEncodingCapacity ( ( type as ConType ) . literal ( ) ) ) ;
203- break ;
204- case 'arr' :
205- this . genArr ( value , type as ArrType < any , any , any > ) ;
206- break ;
207- case 'obj' :
208- this . genObj ( value , type ) ;
209- break ;
210- case 'key' :
211- this . genKey ( value , type as KeyType < any , any > ) ;
212- break ;
213- case 'map' :
214- this . genMap ( value , type as MapType < any > ) ;
215- break ;
216- case 'ref' :
217- this . genRef ( value , type as RefType < any > ) ;
218- break ;
219- case 'or' :
220- this . genOr ( value , type as OrType < any > ) ;
221- break ;
222- default :
223- throw new Error ( `"${ kind } " type capacity estimation not implemented` ) ;
224- }
225- }
226203}
0 commit comments