/// <summary>Generates a method similar to this: /// <code> /// void Write(IO.BitStream s, TEnum v, int bitCount) /// { /// s.Write((TStreamType)v, bitCount); /// } /// </code> /// </summary> /// <returns>The generated method.</returns> /// <param name="args"></param> /// <param name="writeMethodInfo"></param> /// <param name="bitSwapMethod"></param> /// <remarks> /// If <see cref="args.UnderlyingType"/> is the same as <typeparamref name="TStreamType"/>, no conversion code is generated /// </remarks> static Action <IO.BitStream, TEnum, int> GenerateWriteMethod(MethodGenerationArgs args, MethodInfo writeMethodInfo, MethodInfo bitSwapMethod) { ////////////////////////////////////////////////////////////////////////// // Define the generated method's parameters var param_s = Expr.Parameter(kBitStreamType, "s"); // BitStream s var param_v = Expr.Parameter(args.EnumType, "v"); // TEnum v var param_bc = Expr.Parameter(typeof(int), "bitCount"); // int bitCount ////////////////////////////////////////////////////////////////////////// // Define the member access var param_v_member = Expr.PropertyOrField(param_v, EnumUtils.kMemberName); // i.e., 'v.value__' var write_param = args.UnderlyingTypeNeedsConversion ? // If the underlying type is different from the type we're writing, Expr.Convert(param_v_member, args.StreamType) : // we need to cast the Write param from UnderlyingType to TStreamType (Expr)param_v_member; if (args.Options.UseNoneSentinelEncoding) { write_param = Expr.Increment(write_param); } #region options.BitSwap if (args.Options.BitSwap) { // i.e., Bits.BitSwap( value, bitCount-1 ); var start_bit_index = Expr.Decrement(param_bc); Expr swap_call = Expr.Call(null, bitSwapMethod, write_param, start_bit_index); // i.e., bitCount-1 ? Bits.BitSwap( value, bitCount-1 ) : value ; if (args.Options.BitSwapGuardAgainstOneBit) { var start_bit_index_is_not_zero = Expr.NotEqual(start_bit_index, Expr.Constant(0, typeof(int))); swap_call = Expr.Condition(start_bit_index_is_not_zero, swap_call, write_param); } write_param = swap_call; } #endregion ////////////////////////////////////////////////////////////////////////// // Define the Write call // i.e., 's.Write(v.value__, bitCount)' or 's.Write((TStreamType)v.value__, bitCount)' var call_write = Expr.Call(param_s, writeMethodInfo, write_param, param_bc); ////////////////////////////////////////////////////////////////////////// // Generate a method based on the expression tree we've built var lambda = Expr.Lambda <Action <IO.BitStream, TEnum, int> >(call_write, param_s, param_v, param_bc); return(lambda.Compile()); }
/// <summary>Generates a method similar to this: /// <code> /// void Read(IO.BitStream s, out TEnum v, int bitCount) /// { /// v = (UnderlyingType)s.Read[TStreamType](bitCount); /// } /// </code> /// </summary> /// <param name="args"></param> /// <param name="readMethodInfo"></param> /// <param name="bitSwapMethod"></param> /// <returns>The generated method.</returns> /// <remarks> /// If <see cref="args.UnderlyingType"/> is the same as <typeparamref name="TStreamType"/>, no conversion code is generated /// </remarks> static ReadDelegate GenerateReadMethod(MethodGenerationArgs args, MethodInfo readMethodInfo, MethodInfo bitSwapMethod) { // Get a "ref type" of the enum we're dealing with so we can define the enum value as an 'out' parameter var enum_ref = args.EnumType.MakeByRefType(); ////////////////////////////////////////////////////////////////////////// // Define the generated method's parameters var param_s = Expr.Parameter(kBitStreamType, "s"); // BitStream s var param_v = Expr.Parameter(enum_ref, "v"); // ref TEnum v var param_bc = Expr.Parameter(typeof(int), "bitCount"); // int bitCount ////////////////////////////////////////////////////////////////////////// // Define the Read call Expr call_read; if (args.StreamTypeIsSigned) { call_read = Expr.Call(param_s, readMethodInfo, param_bc, Expr.Constant(args.Options.SignExtend)); } else { call_read = Expr.Call(param_s, readMethodInfo, param_bc); // i.e., 's.Read<Type>(bitCount)' } if (args.Options.UseNoneSentinelEncoding) { call_read = Expr.Decrement(call_read); } #region options.BitSwap if (args.Options.BitSwap) { // i.e., Bits.BitSwap( Read(), bitCount-1 ); var start_bit_index = Expr.Decrement(param_bc); Expr swap_call = Expr.Call(null, bitSwapMethod, call_read, start_bit_index); // i.e., bitCount-1 ? Bits.BitSwap( Read(), bitCount-1 ) : Read() ; if (args.Options.BitSwapGuardAgainstOneBit) { var start_bit_index_is_not_zero = Expr.NotEqual(start_bit_index, Expr.Constant(0, typeof(int))); swap_call = Expr.Condition(start_bit_index_is_not_zero, swap_call, call_read); } call_read = swap_call; } #endregion var read_result = args.UnderlyingTypeNeedsConversion ? // If the underlying type is different from the type we're reading, Expr.Convert(call_read, args.UnderlyingType) : // we need to cast the Read result from TStreamType to UnderlyingType (Expr)call_read; ////////////////////////////////////////////////////////////////////////// // Define the member assignment var param_v_member = Expr.PropertyOrField(param_v, EnumUtils.kMemberName); // i.e., 'v.value__' // i.e., 'v.value__ = s.Read<Type>()' or 'v.value__ = (UnderlyingType)s.Read<Type>()' var assign = Expr.Assign(param_v_member, read_result); ////////////////////////////////////////////////////////////////////////// // Generate a method based on the expression tree we've built var lambda = Expr.Lambda <ReadDelegate>(assign, param_s, param_v, param_bc); return(lambda.Compile()); }