2019年3月2日土曜日

[C#]ビットアサインを表現する

久しぶりの更新です。

最近はマイコンのファームウェアを書いててC言語ばかりでC#も久々になってしまった。。

C言語よりC#のが便利!って思う所が沢山ある。
といってもC言語にはポインタがあって、こいつを利用しまくるとデータと型を組み合わせたりできてとても効率的なプログラムが書けるのは確かなんだけど、、
保守性を意識したり、文字列回りとかを意識するとやっぱ高級言語な方が便利だよね!って思う。

さて、通信プログラム界隈では受け取ったデータをビットフィールドで表現してアレコレやりたい!っていうのは叶うのんだけど、C#だとどうしても非効率なコードを書かないといけないのです。
もちろん誰かが作ったライブラリを使う手もあるのですが、コード生成系で出力されたコードはメンテナンスしたくない、、、みたいな好き嫌いがでてしまいますw

ということで自分で実装しました。

データ長はuint型(32bit)で固定してます。
long型にすれば64bit、
自在に操りたいならbyte型の配列にしてarray操作回りを書けばなんとか凌げるかな。


ーーー




using System;
using System.Windows.Forms;

namespace BitAssignCheck
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button8_Click(object sender, EventArgs e)
        {
            uint d = 0;
            int offset = 1;
            var v = d.GetInt(0, 3);
            for (var i = -7; i < 8; i++)
            {
                d = d.SetInt(i, offset, 3);
                var z = d.GetInt(offset, 3);
                if (z != i)
                {
                    Console.WriteLine(z);
                }
            }

        }
    }

    public static class BitAssign
    {
        public static uint GetMask(int offset, int count)
        {
            var cnt = offset + count;
            var lo = (uint)((1 << offset) - 1);
            var hi = (32 == cnt) ? 0xffffffff : (uint)((1 << cnt) - 1);
            var result = hi - lo;
            return result;
        }

        static readonly uint[] mask = new uint[]
        {
            0x00000001,
            0x00000003,
            0x00000007,
            0x0000000f,
            0x0000001f,
            0x0000003f,
            0x0000007f,
            0x000000ff,
            0x000001ff,
            0x000003ff,
            0x000007ff,
            0x00000fff,
            0x00001fff,
            0x00003fff,
            0x00007fff,
            0x0000ffff,
            0x0001ffff,
            0x0003ffff,
            0x0007ffff,
            0x000fffff,
            0x001fffff,
            0x003fffff,
            0x007fffff,
            0x00ffffff,
            0x01ffffff,
            0x03ffffff,
            0x07ffffff,
            0x0fffffff,
            0x1fffffff,
            0x3fffffff,
            0x7fffffff,
            0xffffffff,
        };

        static readonly uint[] bits = new uint[]
        {
            0x00000001,
            0x00000002,
            0x00000004,
            0x00000008,
            0x00000010,
            0x00000020,
            0x00000040,
            0x00000080,
            0x00000100,
            0x00000200,
            0x00000400,
            0x00000800,
            0x00001000,
            0x00002000,
            0x00004000,
            0x00008000,
            0x00010000,
            0x00020000,
            0x00040000,
            0x00080000,
            0x00100000,
            0x00200000,
            0x00400000,
            0x00800000,
            0x01000000,
            0x02000000,
            0x04000000,
            0x08000000,
            0x10000000,
            0x20000000,
            0x40000000,
            0x80000000,
        };

        public static bool GetBool(this uint self, int offset)
        {
            var b = bits[offset];
            var result = (0 != (self & b));
            return result;
        }

        public static int GetInt(this uint self, int offset, int count)
        {
            var value = self >> offset;
            var b = bits[count];
            var m = mask[count];
            var sign = value & b;
            var data = value & m;
            var result = (0 == sign) ? (int)data : -((int)(data ^ m) + 1);
            return result;
        }

        public static uint GetUInt(this uint self, int offset, int count)
        {
            var value = self >> offset;
            var m = mask[count];
            var data = value & m;
            return data;
        }

        public static uint SetBool(this uint self, bool value, int offset)
        {
            var b = bits[offset];
            var m = 0xffffffff ^ b;
            var result = (value) ? (self | b) : (self & m);
            return result;
        }

        public static uint SetInt(this uint self, int value, int offset, int count)
        {
            return self.SetUInt((uint)value, offset, count);
        }

        public static uint SetUInt(this uint self, uint value, int offset, int count)
        {
            var mv = mask[count];
            var data1 = (value & mv) << offset;

            var ms = 0xffffffff ^ (mv << offset);
            var data0 = (self & ms);
            var result = data0 | data1;
            return result;
        }
    }
}

もしテーブル使いたくなければ、算出する関数を使えばおk。
public static uint GetMask(int offset, int count)
{
    var cnt = offset + count;
    var lo = (uint)((1 << offset) - 1);
    var hi = (32 == cnt) ? 0xffffffff : (uint)((1 << cnt) - 1);
    var result = hi - lo;
    return result;
}

public static uint GetMask(int bits)
{
    var cnt = bits + 1;
    var result = (32 == cnt) ? 0xffffffff : (uint)((1 << cnt) - 1);
    return result;
}

public static uint GetBits(int bits)
{
    return 1 << bits;
}

0 件のコメント:

Androider