Closed
Description
When using reusable encoders/decoders, decoding fails when using the reused decode()
function in the custom decoding function.
The following example works if USE_REUSABLE_CODING
is set to false
, meaning the whole logic uses the global encode
/decode
function exported by msgpack, but when set to true
, encode
and decode
are reusable instances of Encode
/Decore
correspondibly. It fails with RangeError: Extra 18 of 21 byte(s) found at buffer[3]
.
Does reusable encoders/decoders not work with custom codecs?
Example:
import {
Encoder,
Decoder,
ExtensionCodec,
encode as _encode,
decode as _decode,
} from "@msgpack/msgpack"
const USE_REUSABLE_CODING = true;
export class MsgPackContext
{
readonly context = this;
readonly encode: ( value: unknown ) => Uint8Array;
readonly decode: ( buffer: BufferSource | ArrayLike< number > ) => unknown;
readonly extensionCodec = new ExtensionCodec< MsgPackContext >( );
constructor( )
{
const encoder = new Encoder( this.extensionCodec, this );
const decoder = new Decoder( this.extensionCodec, this );
this.encode = encoder.encode.bind( encoder );
this.decode = decoder.decode.bind( decoder );
registerCodecs( this );
}
}
const MSGPACK_EXT_TYPE_BIGINT = 0;
const MSGPACK_EXT_TYPE_MAP = 1;
export function registerCodecs( context: MsgPackContext )
{
const { extensionCodec, encode, decode } = context;
extensionCodec.register( {
type: MSGPACK_EXT_TYPE_BIGINT,
encode: value =>
( typeof value === 'bigint' || value instanceof BigInt )
?
USE_REUSABLE_CODING
? encode( value.toString( ) )
: _encode( value.toString( ), context )
: null,
decode: data =>
USE_REUSABLE_CODING
? BigInt( decode( data ) as string )
: BigInt( _decode( data, context ) as string ),
} );
extensionCodec.register( {
type: MSGPACK_EXT_TYPE_MAP,
encode: value =>
value instanceof Map
?
USE_REUSABLE_CODING
? encode( [ ...value.entries( ) ] )
: _encode( [ ...value.entries( ) ], context )
: null,
decode: data =>
USE_REUSABLE_CODING
? new Map( decode( data ) as Array<any> )
: new Map( _decode( data, context ) as Array<any> ),
} );
}
const context = new MsgPackContext( );
if ( USE_REUSABLE_CODING )
{
const buf = context.encode( { m: new Map( [ [ 'big', BigInt( 42 ) ] ] ) } );
const data = context.decode( buf );
console.log( data );
}
else
{
const buf = _encode( { m: new Map( [ [ 'big', BigInt( 42 ) ] ] ) }, context );
const data = _decode( buf, context );
console.log( data );
}
Just to be clear, the following is the same code without global encode
/decode
usage (USE_REUSABLE_CODING
being false
). This code should work, and could be a regression unit test
import {
Encoder,
Decoder,
ExtensionCodec,
} from "@msgpack/msgpack"
export class MsgPackContext
{
readonly encode: ( value: unknown ) => Uint8Array;
readonly decode: ( buffer: BufferSource | ArrayLike< number > ) => unknown;
readonly extensionCodec = new ExtensionCodec< MsgPackContext >( );
constructor( )
{
const encoder = new Encoder( this.extensionCodec, this );
const decoder = new Decoder( this.extensionCodec, this );
this.encode = encoder.encode.bind( encoder );
this.decode = decoder.decode.bind( decoder );
registerCodecs( this );
}
}
const MSGPACK_EXT_TYPE_BIGINT = 0;
const MSGPACK_EXT_TYPE_MAP = 1;
export function registerCodecs( context: MsgPackContext )
{
const { extensionCodec, encode, decode } = context;
extensionCodec.register( {
type: MSGPACK_EXT_TYPE_BIGINT,
encode: value =>
( typeof value === 'bigint' || value instanceof BigInt )
? encode( value.toString( ) )
: null,
decode: data =>
BigInt( decode( data ) as string ),
} );
extensionCodec.register( {
type: MSGPACK_EXT_TYPE_MAP,
encode: value =>
value instanceof Map
? encode( [ ...value.entries( ) ] )
: null,
decode: data =>
new Map( decode( data ) as Array<any> ),
} );
}
const context = new MsgPackContext( );
const buf = context.encode( { m: new Map( [ [ 'big', BigInt( 42 ) ] ] ) } );
const data = context.decode( buf );
console.log( data );
Metadata
Metadata
Assignees
Labels
No labels