@@ -29,6 +29,7 @@ DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
2929namespace Doxense . Collections . Tuples . Encoding
3030{
3131 using System ;
32+ using System . Collections . Concurrent ;
3233 using System . Collections . Generic ;
3334 using System . Globalization ;
3435 using System . Linq ;
@@ -76,8 +77,7 @@ private static Delegate GetSerializerFor([NotNull] Type type)
7677 }
7778
7879 // look for well-known types that have their own (non-generic) TuplePackers.SerializeTo(...) method
79- var typeArgs = new [ ] { typeof ( TupleWriter ) . MakeByRefType ( ) , type } ;
80- var method = typeof ( TuplePackers ) . GetMethod ( nameof ( SerializeTo ) , BindingFlags . Static | BindingFlags . Public , binder : null , types : typeArgs , modifiers : null ) ;
80+ var method = typeof ( TuplePackers ) . GetMethod ( nameof ( SerializeTo ) , BindingFlags . Static | BindingFlags . Public , binder : null , types : new [ ] { typeof ( TupleWriter ) . MakeByRefType ( ) , type } , modifiers : null ) ;
8181 if ( method != null )
8282 { // we have a direct serializer
8383 return method . CreateDelegate ( typeof ( Encoder < > ) . MakeGenericType ( type ) ) ;
@@ -97,7 +97,17 @@ private static Delegate GetSerializerFor([NotNull] Type type)
9797 // maybe it is a tuple ?
9898 if ( typeof ( ITuple ) . IsAssignableFrom ( type ) )
9999 {
100- // If so, try to use the corresponding TuplePackers.SerializeTupleTo(...) method
100+ if ( type == typeof ( STuple ) || ( type . Name . StartsWith ( nameof ( STuple ) + "`" , StringComparison . Ordinal ) && type . Namespace == typeof ( STuple ) . Namespace ) )
101+ { // well-known STuple<T...> struct
102+ var typeArgs = type . GetGenericArguments ( ) ;
103+ method = FindSTupleSerializerMethod ( typeArgs ) ;
104+ if ( method != null )
105+ {
106+ return method . MakeGenericMethod ( typeArgs ) . CreateDelegate ( typeof ( Encoder < > ) . MakeGenericType ( type ) ) ;
107+ }
108+ }
109+
110+ // will use the default ITuple implementation
101111 method = typeof ( TuplePackers ) . GetMethod ( nameof ( SerializeTupleTo ) , BindingFlags . Static | BindingFlags . Public ) ;
102112 if ( method != null )
103113 {
@@ -116,9 +126,10 @@ private static Delegate GetSerializerFor([NotNull] Type type)
116126 }
117127 }
118128
119- if ( ( type . Name == nameof ( System . ValueTuple ) || type . Name . StartsWith ( nameof ( System . ValueTuple ) + "`" , StringComparison . Ordinal ) ) && type . Namespace == "System" )
129+ // ValueTuple<T..>
130+ if ( type == typeof ( ValueTuple ) || ( type . Name . StartsWith ( nameof ( System . ValueTuple ) + "`" , StringComparison . Ordinal ) && type . Namespace == "System" ) )
120131 {
121- typeArgs = type . GetGenericArguments ( ) ;
132+ var typeArgs = type . GetGenericArguments ( ) ;
122133 method = FindValueTupleSerializerMethod ( typeArgs ) ;
123134 if ( method != null )
124135 {
@@ -132,6 +143,15 @@ private static Delegate GetSerializerFor([NotNull] Type type)
132143 return null ;
133144 }
134145
146+ private static MethodInfo FindSTupleSerializerMethod ( Type [ ] args )
147+ {
148+ //note: we want to find the correct SerializeSTuple<...>(ref TupleWriter, (...,), but this cannot be done with Type.GetMethod(...) directly
149+ // => we have to scan for all methods with the correct name, and the same number of Type Arguments than the ValueTuple.
150+ return typeof ( TuplePackers )
151+ . GetMethods ( BindingFlags . Static | BindingFlags . Public )
152+ . SingleOrDefault ( m => m . Name == nameof ( SerializeSTupleTo ) && m . GetGenericArguments ( ) . Length == args . Length ) ;
153+ }
154+
135155 private static MethodInfo FindValueTupleSerializerMethod ( Type [ ] args )
136156 {
137157 //note: we want to find the correct SerializeValueTuple<...>(ref TupleWriter, (...,), but this cannot be done with Type.GetMethod(...) directly
@@ -220,149 +240,65 @@ public static void SerializeObjectTo(ref TupleWriter writer, object value)
220240 TupleParser . WriteNil ( ref writer ) ;
221241 return ;
222242 }
243+ GetBoxedEncoder ( value . GetType ( ) ) ( ref writer , value ) ;
244+ }
223245
224- switch ( Type . GetTypeCode ( value . GetType ( ) ) )
246+ private static Encoder < object > GetBoxedEncoder ( Type type )
247+ {
248+ if ( ! BoxedEncoders . TryGetValue ( type , out var encoder ) )
225249 {
226- case TypeCode . Empty :
227- case TypeCode . Object :
228- {
229- if ( value is byte [ ] bytes )
230- {
231- SerializeTo ( ref writer , bytes ) ;
232- return ;
233- }
234-
235- if ( value is Slice slice )
236- {
237- SerializeTo ( ref writer , slice ) ;
238- return ;
239- }
240-
241- if ( value is Guid g )
242- {
243- SerializeTo ( ref writer , g ) ;
244- return ;
245- }
250+ encoder = CreateBoxedEncoder ( type ) ;
251+ BoxedEncoders . TryAdd ( type , encoder ) ;
252+ }
253+ return encoder ;
254+ }
246255
247- if ( value is Uuid128 u128 )
248- {
249- SerializeTo ( ref writer , u128 ) ;
250- return ;
251- }
256+ private static ConcurrentDictionary < Type , Encoder < object > > BoxedEncoders { get ; } = GetDefaultBoxedEncoders ( ) ;
252257
253- if ( value is Uuid64 u64 )
254- {
255- SerializeTo ( ref writer , u64 ) ;
256- return ;
257- }
258-
259- if ( value is TimeSpan ts )
260- {
261- SerializeTo ( ref writer , ts ) ;
262- return ;
263- }
258+ private static ConcurrentDictionary < Type , Encoder < object > > GetDefaultBoxedEncoders ( )
259+ {
260+ var encoders = new ConcurrentDictionary < Type , Encoder < object > >
261+ {
262+ [ typeof ( bool ) ] = ( ref TupleWriter writer , object value ) => SerializeTo ( ref writer , ( bool ) value ) ,
263+ [ typeof ( char ) ] = ( ref TupleWriter writer , object value ) => SerializeTo ( ref writer , ( char ) value ) ,
264+ [ typeof ( string ) ] = ( ref TupleWriter writer , object value ) => SerializeTo ( ref writer , ( string ) value ) ,
265+ [ typeof ( sbyte ) ] = ( ref TupleWriter writer , object value ) => SerializeTo ( ref writer , ( sbyte ) value ) ,
266+ [ typeof ( short ) ] = ( ref TupleWriter writer , object value ) => SerializeTo ( ref writer , ( short ) value ) ,
267+ [ typeof ( int ) ] = ( ref TupleWriter writer , object value ) => SerializeTo ( ref writer , ( int ) value ) ,
268+ [ typeof ( long ) ] = ( ref TupleWriter writer , object value ) => SerializeTo ( ref writer , ( long ) value ) ,
269+ [ typeof ( byte ) ] = ( ref TupleWriter writer , object value ) => SerializeTo ( ref writer , ( byte ) value ) ,
270+ [ typeof ( ushort ) ] = ( ref TupleWriter writer , object value ) => SerializeTo ( ref writer , ( ushort ) value ) ,
271+ [ typeof ( uint ) ] = ( ref TupleWriter writer , object value ) => SerializeTo ( ref writer , ( uint ) value ) ,
272+ [ typeof ( ulong ) ] = ( ref TupleWriter writer , object value ) => SerializeTo ( ref writer , ( ulong ) value ) ,
273+ [ typeof ( float ) ] = ( ref TupleWriter writer , object value ) => SerializeTo ( ref writer , ( float ) value ) ,
274+ [ typeof ( double ) ] = ( ref TupleWriter writer , object value ) => SerializeTo ( ref writer , ( double ) value ) ,
275+ [ typeof ( decimal ) ] = ( ref TupleWriter writer , object value ) => SerializeTo ( ref writer , ( decimal ) value ) ,
276+ [ typeof ( Slice ) ] = ( ref TupleWriter writer , object value ) => SerializeTo ( ref writer , ( Slice ) value ) ,
277+ [ typeof ( byte [ ] ) ] = ( ref TupleWriter writer , object value ) => SerializeTo ( ref writer , ( byte [ ] ) value ) ,
278+ [ typeof ( Guid ) ] = ( ref TupleWriter writer , object value ) => SerializeTo ( ref writer , ( Guid ) value ) ,
279+ [ typeof ( Uuid128 ) ] = ( ref TupleWriter writer , object value ) => SerializeTo ( ref writer , ( Uuid128 ) value ) ,
280+ [ typeof ( Uuid64 ) ] = ( ref TupleWriter writer , object value ) => SerializeTo ( ref writer , ( Uuid64 ) value ) ,
281+ [ typeof ( TimeSpan ) ] = ( ref TupleWriter writer , object value ) => SerializeTo ( ref writer , ( TimeSpan ) value ) ,
282+ [ typeof ( DateTime ) ] = ( ref TupleWriter writer , object value ) => SerializeTo ( ref writer , ( DateTime ) value ) ,
283+ [ typeof ( DateTimeOffset ) ] = ( ref TupleWriter writer , object value ) => SerializeTo ( ref writer , ( DateTimeOffset ) value ) ,
284+ [ typeof ( ITuple ) ] = ( ref TupleWriter writer , object value ) => SerializeTupleTo ( ref writer , ( ITuple ) value ) ,
285+ [ typeof ( ITupleFormattable ) ] = ( ref TupleWriter writer , object value ) => SerializeTupleTo ( ref writer , ( ITuple ) value ) ,
286+ [ typeof ( DBNull ) ] = ( ref TupleWriter writer , object value ) => TupleParser . WriteNil ( ref writer )
287+ } ;
264288
265- break ;
266- }
267- case TypeCode . DBNull :
268- { // same as null
269- TupleParser . WriteNil ( ref writer ) ;
270- return ;
271- }
272- case TypeCode . Boolean :
273- {
274- SerializeTo ( ref writer , ( bool ) value ) ;
275- return ;
276- }
277- case TypeCode . Char :
278- {
279- // should be treated as a string with only one char
280- SerializeTo ( ref writer , ( char ) value ) ;
281- return ;
282- }
283- case TypeCode . SByte :
284- {
285- SerializeTo ( ref writer , ( sbyte ) value ) ;
286- return ;
287- }
288- case TypeCode . Byte :
289- {
290- SerializeTo ( ref writer , ( byte ) value ) ;
291- return ;
292- }
293- case TypeCode . Int16 :
294- {
295- SerializeTo ( ref writer , ( short ) value ) ;
296- return ;
297- }
298- case TypeCode . UInt16 :
299- {
300- SerializeTo ( ref writer , ( ushort ) value ) ;
301- return ;
302- }
303- case TypeCode . Int32 :
304- {
305- SerializeTo ( ref writer , ( int ) value ) ;
306- return ;
307- }
308- case TypeCode . UInt32 :
309- {
310- SerializeTo ( ref writer , ( uint ) value ) ;
311- return ;
312- }
313- case TypeCode . Int64 :
314- {
315- SerializeTo ( ref writer , ( long ) value ) ;
316- return ;
317- }
318- case TypeCode . UInt64 :
319- {
320- SerializeTo ( ref writer , ( ulong ) value ) ;
321- return ;
322- }
323- case TypeCode . String :
324- {
325- SerializeTo ( ref writer , value as string ) ;
326- return ;
327- }
328- case TypeCode . DateTime :
329- {
330- SerializeTo ( ref writer , ( DateTime ) value ) ;
331- return ;
332- }
333- case TypeCode . Double :
334- {
335- SerializeTo ( ref writer , ( double ) value ) ;
336- return ;
337- }
338- case TypeCode . Single :
339- {
340- SerializeTo ( ref writer , ( float ) value ) ;
341- return ;
342- }
343- case TypeCode . Decimal :
344- {
345- SerializeTo ( ref writer , ( decimal ) value ) ;
346- return ;
347- }
348- }
289+ return encoders ;
290+ }
349291
350- if ( value is ITuple tuple )
351- {
352- SerializeTupleTo ( ref writer , tuple ) ;
353- return ;
354- }
292+ private static Encoder < object > CreateBoxedEncoder ( Type type )
293+ {
294+ var m = typeof ( TuplePacker < > ) . MakeGenericType ( type ) . GetMethod ( nameof ( TuplePacker < int > . SerializeBoxedTo ) ) ;
295+ Contract . Assert ( m != null ) ;
355296
356- if ( value is ITupleFormattable fmt )
357- {
358- tuple = fmt . ToTuple ( ) ;
359- if ( tuple == null ) throw new InvalidOperationException ( $ "An instance of type '{ value . GetType ( ) . Name } ' returned a null Tuple while serialiazing") ;
360- SerializeTupleTo ( ref writer , tuple ) ;
361- return ;
362- }
297+ var writer = Expression . Parameter ( typeof ( TupleWriter ) . MakeByRefType ( ) , "writer" ) ;
298+ var value = Expression . Parameter ( typeof ( object ) , "value" ) ;
363299
364- // Not Supported ?
365- throw new NotSupportedException ( $ "Doesn't know how to serialize objects of type ' { value . GetType ( ) . Name } ' into Tuple Encoding format" ) ;
300+ var body = Expression . Call ( m , writer , value ) ;
301+ return Expression . Lambda < Encoder < object > > ( body , writer , value ) . Compile ( ) ;
366302 }
367303
368304 /// <summary>Writes a slice as a byte[] array</summary>
@@ -570,6 +506,74 @@ public static void SerializeTupleTo<TTuple>(ref TupleWriter writer, TTuple tuple
570506 TupleParser . EndTuple ( ref writer ) ;
571507 }
572508
509+ public static void SerializeSTupleTo < T1 > ( ref TupleWriter writer , STuple < T1 > tuple )
510+ {
511+ TupleParser . BeginTuple ( ref writer ) ;
512+ SerializeTo ( ref writer , tuple . Item1 ) ;
513+ TupleParser . EndTuple ( ref writer ) ;
514+ }
515+
516+ public static void SerializeSTupleTo < T1 , T2 > ( ref TupleWriter writer , STuple < T1 , T2 > tuple )
517+ {
518+ TupleParser . BeginTuple ( ref writer ) ;
519+ SerializeTo ( ref writer , tuple . Item1 ) ;
520+ SerializeTo ( ref writer , tuple . Item2 ) ;
521+ TupleParser . EndTuple ( ref writer ) ;
522+ }
523+
524+ public static void SerializeSTupleTo < T1 , T2 , T3 > ( ref TupleWriter writer , STuple < T1 , T2 , T3 > tuple )
525+ {
526+ TupleParser . BeginTuple ( ref writer ) ;
527+ SerializeTo ( ref writer , tuple . Item1 ) ;
528+ SerializeTo ( ref writer , tuple . Item2 ) ;
529+ SerializeTo ( ref writer , tuple . Item3 ) ;
530+ TupleParser . EndTuple ( ref writer ) ;
531+ }
532+
533+ public static void SerializeSTupleTo < T1 , T2 , T3 , T4 > ( ref TupleWriter writer , STuple < T1 , T2 , T3 , T4 > tuple )
534+ {
535+ TupleParser . BeginTuple ( ref writer ) ;
536+ SerializeTo ( ref writer , tuple . Item1 ) ;
537+ SerializeTo ( ref writer , tuple . Item2 ) ;
538+ SerializeTo ( ref writer , tuple . Item3 ) ;
539+ SerializeTo ( ref writer , tuple . Item4 ) ;
540+ TupleParser . EndTuple ( ref writer ) ;
541+ }
542+
543+ public static void SerializeSTupleTo < T1 , T2 , T3 , T4 , T5 > ( ref TupleWriter writer , STuple < T1 , T2 , T3 , T4 , T5 > tuple )
544+ {
545+ TupleParser . BeginTuple ( ref writer ) ;
546+ SerializeTo ( ref writer , tuple . Item1 ) ;
547+ SerializeTo ( ref writer , tuple . Item2 ) ;
548+ SerializeTo ( ref writer , tuple . Item3 ) ;
549+ SerializeTo ( ref writer , tuple . Item4 ) ;
550+ SerializeTo ( ref writer , tuple . Item5 ) ;
551+ TupleParser . EndTuple ( ref writer ) ;
552+ }
553+
554+ public static void SerializeSTupleTo < T1 , T2 , T3 , T4 , T5 , T6 > ( ref TupleWriter writer , STuple < T1 , T2 , T3 , T4 , T5 , T6 > tuple )
555+ {
556+ TupleParser . BeginTuple ( ref writer ) ;
557+ SerializeTo ( ref writer , tuple . Item1 ) ;
558+ SerializeTo ( ref writer , tuple . Item2 ) ;
559+ SerializeTo ( ref writer , tuple . Item3 ) ;
560+ SerializeTo ( ref writer , tuple . Item4 ) ;
561+ SerializeTo ( ref writer , tuple . Item5 ) ;
562+ SerializeTo ( ref writer , tuple . Item6 ) ;
563+ TupleParser . EndTuple ( ref writer ) ;
564+ }
565+
566+ public static void SerializeTupleFormattableTo < TFormattable > ( ref TupleWriter writer , TFormattable formattable )
567+ where TFormattable : ITupleFormattable
568+ {
569+ var tuple = formattable . ToTuple ( ) ;
570+ if ( tuple == null ) throw new InvalidOperationException ( $ "An instance of type '{ formattable . GetType ( ) . Name } ' returned a null Tuple while serialiazing") ;
571+
572+ TupleParser . BeginTuple ( ref writer ) ;
573+ TupleEncoder . WriteTo ( ref writer , tuple ) ;
574+ TupleParser . EndTuple ( ref writer ) ;
575+ }
576+
573577 /// <summary>Serialize an embedded tuple formattable</summary>
574578 public static void SerializeFormattableTo ( ref TupleWriter writer , ITupleFormattable formattable )
575579 {
0 commit comments