@@ -32,7 +32,10 @@ import {
32
32
file_google_protobuf_field_mask ,
33
33
file_google_protobuf_source_context ,
34
34
file_google_protobuf_struct ,
35
- file_google_protobuf_timestamp , file_google_protobuf_type , file_google_protobuf_wrappers ,
35
+ file_google_protobuf_timestamp ,
36
+ file_google_protobuf_type ,
37
+ file_google_protobuf_wrappers ,
38
+ FileDescriptorProto ,
36
39
FileDescriptorProtoSchema
37
40
} from "@bufbuild/protobuf/wkt" ;
38
41
import { BufferWrapper , MAX_VARINT_LEN_64 } from "./buffer-wrapper" ;
@@ -100,6 +103,7 @@ export type ProtobufSerializerConfig = SerializerConfig & {
100
103
*/
101
104
export class ProtobufSerializer extends Serializer implements ProtobufSerde {
102
105
registry : MutableRegistry
106
+ fileRegistry : FileRegistry
103
107
schemaToDescCache : LRUCache < string , DescFile >
104
108
descToSchemaCache : LRUCache < string , SchemaInfo >
105
109
@@ -113,6 +117,7 @@ export class ProtobufSerializer extends Serializer implements ProtobufSerde {
113
117
constructor ( client : Client , serdeType : SerdeType , conf : ProtobufSerializerConfig , ruleRegistry ?: RuleRegistry ) {
114
118
super ( client , serdeType , conf , ruleRegistry )
115
119
this . registry = conf . registry ?? createMutableRegistry ( )
120
+ this . fileRegistry = createFileRegistry ( )
116
121
this . schemaToDescCache = new LRUCache < string , DescFile > ( { max : this . config ( ) . cacheCapacity ?? 1000 } )
117
122
this . descToSchemaCache = new LRUCache < string , SchemaInfo > ( { max : this . config ( ) . cacheCapacity ?? 1000 } )
118
123
this . fieldTransformer = async ( ctx : RuleContext , fieldTransform : FieldTransform , msg : any ) => {
@@ -281,15 +286,41 @@ export class ProtobufSerializer extends Serializer implements ProtobufSerde {
281
286
}
282
287
283
288
async fieldTransform ( ctx : RuleContext , fieldTransform : FieldTransform , msg : any ) : Promise < any > {
289
+ const fileDesc = await this . toFileDesc ( this . client , ctx . target )
284
290
const typeName = msg . $typeName
285
- if ( typeName == null ) {
286
- throw new SerializationError ( 'message type name is empty' )
291
+ const messageDesc = this . toMessageDescFromName ( fileDesc , typeName )
292
+ return await transform ( ctx , messageDesc , msg , fieldTransform )
293
+ }
294
+
295
+ async toFileDesc ( client : Client , info : SchemaInfo ) : Promise < DescFile > {
296
+ const value = this . schemaToDescCache . get ( stringify ( info . schema ) )
297
+ if ( value != null ) {
298
+ return value
287
299
}
288
- const messageDesc = this . registry . getMessage ( typeName )
289
- if ( messageDesc == null ) {
290
- throw new SerializationError ( 'message descriptor not in registry ' )
300
+ const fileDesc = await this . parseFileDesc ( client , info )
301
+ if ( fileDesc == null ) {
302
+ throw new SerializationError ( 'file descriptor not found ' )
291
303
}
292
- return await transform ( ctx , messageDesc , msg , fieldTransform )
304
+ this . schemaToDescCache . set ( stringify ( info . schema ) , fileDesc )
305
+ return fileDesc
306
+ }
307
+
308
+ async parseFileDesc ( client : Client , info : SchemaInfo ) : Promise < DescFile | undefined > {
309
+ const deps = new Map < string , string > ( )
310
+ await this . resolveReferences ( client , info , deps , 'serialized' )
311
+ const fileDesc = fromBinary ( FileDescriptorProtoSchema , Buffer . from ( info . schema , 'base64' ) )
312
+ const fileRegistry = newFileRegistry ( fileDesc , deps )
313
+ this . fileRegistry = createFileRegistry ( this . fileRegistry , fileRegistry )
314
+ return this . fileRegistry . getFile ( fileDesc . name )
315
+ }
316
+
317
+ toMessageDescFromName ( fd : DescFile , msgName : string ) : DescMessage {
318
+ for ( let i = 0 ; i < fd . messages . length ; i ++ ) {
319
+ if ( fd . messages [ i ] . typeName === msgName ) {
320
+ return fd . messages [ i ]
321
+ }
322
+ }
323
+ throw new SerializationError ( 'message descriptor not found' )
293
324
}
294
325
}
295
326
@@ -302,7 +333,7 @@ export type ProtobufDeserializerConfig = DeserializerConfig
302
333
* ProtobufDeserializer is a deserializer for Protobuf messages.
303
334
*/
304
335
export class ProtobufDeserializer extends Deserializer implements ProtobufSerde {
305
- registry : FileRegistry
336
+ fileRegistry : FileRegistry
306
337
schemaToDescCache : LRUCache < string , DescFile >
307
338
308
339
/**
@@ -314,7 +345,7 @@ export class ProtobufDeserializer extends Deserializer implements ProtobufSerde
314
345
*/
315
346
constructor ( client : Client , serdeType : SerdeType , conf : ProtobufDeserializerConfig , ruleRegistry ?: RuleRegistry ) {
316
347
super ( client , serdeType , conf , ruleRegistry )
317
- this . registry = createFileRegistry ( )
348
+ this . fileRegistry = createFileRegistry ( )
318
349
this . schemaToDescCache = new LRUCache < string , DescFile > ( { max : this . config ( ) . cacheCapacity ?? 1000 } )
319
350
this . fieldTransformer = async ( ctx : RuleContext , fieldTransform : FieldTransform , msg : any ) => {
320
351
return await this . fieldTransform ( ctx , fieldTransform , msg )
@@ -340,7 +371,7 @@ export class ProtobufDeserializer extends Deserializer implements ProtobufSerde
340
371
const info = await this . getSchema ( topic , payload , 'serialized' )
341
372
const fd = await this . toFileDesc ( this . client , info )
342
373
const [ bytesRead , msgIndexes ] = this . readMessageIndexes ( payload . subarray ( 5 ) )
343
- const messageDesc = this . toMessageDesc ( fd , msgIndexes )
374
+ const messageDesc = this . toMessageDescFromIndexes ( fd , msgIndexes )
344
375
345
376
const subject = this . subjectName ( topic , info )
346
377
const readerMeta = await this . getReaderSchema ( subject , 'serialized' )
@@ -361,14 +392,9 @@ export class ProtobufDeserializer extends Deserializer implements ProtobufSerde
361
392
}
362
393
363
394
async fieldTransform ( ctx : RuleContext , fieldTransform : FieldTransform , msg : any ) : Promise < any > {
395
+ const fileDesc = await this . toFileDesc ( this . client , ctx . target )
364
396
const typeName = msg . $typeName
365
- if ( typeName == null ) {
366
- throw new SerializationError ( 'message type name is empty' )
367
- }
368
- const messageDesc = this . registry . getMessage ( typeName )
369
- if ( messageDesc == null ) {
370
- throw new SerializationError ( 'message descriptor not in registry' )
371
- }
397
+ const messageDesc = this . toMessageDescFromName ( fileDesc , typeName )
372
398
return await transform ( ctx , messageDesc , msg , fieldTransform )
373
399
}
374
400
@@ -389,26 +415,18 @@ export class ProtobufDeserializer extends Deserializer implements ProtobufSerde
389
415
const deps = new Map < string , string > ( )
390
416
await this . resolveReferences ( client , info , deps , 'serialized' )
391
417
const fileDesc = fromBinary ( FileDescriptorProtoSchema , Buffer . from ( info . schema , 'base64' ) )
392
- const resolve = ( depName : string ) => {
393
- if ( isBuiltin ( depName ) ) {
394
- const dep = builtinDeps . get ( depName )
395
- if ( dep == null ) {
396
- throw new SerializationError ( `dependency ${ depName } not found` )
397
- }
398
- return dep
399
- } else {
400
- const dep = deps . get ( depName )
401
- if ( dep == null ) {
402
- throw new SerializationError ( `dependency ${ depName } not found` )
403
- }
404
- const fileDesc = fromBinary ( FileDescriptorProtoSchema , Buffer . from ( dep , 'base64' ) )
405
- fileDesc . name = depName
406
- return fileDesc
418
+ const fileRegistry = newFileRegistry ( fileDesc , deps )
419
+ this . fileRegistry = createFileRegistry ( this . fileRegistry , fileRegistry )
420
+ return this . fileRegistry . getFile ( fileDesc . name )
421
+ }
422
+
423
+ toMessageDescFromName ( fd : DescFile , msgName : string ) : DescMessage {
424
+ for ( let i = 0 ; i < fd . messages . length ; i ++ ) {
425
+ if ( fd . messages [ i ] . typeName === msgName ) {
426
+ return fd . messages [ i ]
407
427
}
408
428
}
409
- const fileRegistry = createFileRegistry ( fileDesc , resolve )
410
- this . registry = createFileRegistry ( this . registry , fileRegistry )
411
- return this . registry . getFile ( fileDesc . name )
429
+ throw new SerializationError ( 'message descriptor not found' )
412
430
}
413
431
414
432
readMessageIndexes ( payload : Buffer ) : [ number , number [ ] ] {
@@ -421,7 +439,7 @@ export class ProtobufDeserializer extends Deserializer implements ProtobufSerde
421
439
return [ bw . pos , msgIndexes ]
422
440
}
423
441
424
- toMessageDesc ( fd : DescFile , msgIndexes : number [ ] ) : DescMessage {
442
+ toMessageDescFromIndexes ( fd : DescFile , msgIndexes : number [ ] ) : DescMessage {
425
443
let index = msgIndexes [ 0 ]
426
444
if ( msgIndexes . length === 1 ) {
427
445
return fd . messages [ index ]
@@ -438,6 +456,27 @@ export class ProtobufDeserializer extends Deserializer implements ProtobufSerde
438
456
}
439
457
}
440
458
459
+ function newFileRegistry ( fileDesc : FileDescriptorProto , deps : Map < string , string > ) : FileRegistry {
460
+ const resolve = ( depName : string ) => {
461
+ if ( isBuiltin ( depName ) ) {
462
+ const dep = builtinDeps . get ( depName )
463
+ if ( dep == null ) {
464
+ throw new SerializationError ( `dependency ${ depName } not found` )
465
+ }
466
+ return dep
467
+ } else {
468
+ const dep = deps . get ( depName )
469
+ if ( dep == null ) {
470
+ throw new SerializationError ( `dependency ${ depName } not found` )
471
+ }
472
+ const fileDesc = fromBinary ( FileDescriptorProtoSchema , Buffer . from ( dep , 'base64' ) )
473
+ fileDesc . name = depName
474
+ return fileDesc
475
+ }
476
+ }
477
+ return createFileRegistry ( fileDesc , resolve )
478
+ }
479
+
441
480
async function transform ( ctx : RuleContext , descriptor : DescMessage , msg : any , fieldTransform : FieldTransform ) : Promise < any > {
442
481
if ( msg == null || descriptor == null ) {
443
482
return msg
@@ -470,7 +509,7 @@ async function transform(ctx: RuleContext, descriptor: DescMessage, msg: any, fi
470
509
}
471
510
472
511
async function transformField ( ctx : RuleContext , fd : DescField , desc : DescMessage ,
473
- msg : any , fieldTransform : FieldTransform ) {
512
+ msg : any , fieldTransform : FieldTransform ) {
474
513
try {
475
514
ctx . enterField (
476
515
msg ,
0 commit comments