65.9K
CodeProject 正在变化。 阅读更多。
Home

bits.hpp: 一个包含位和字操作实用工具的头文件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.53/5 (4投票s)

2021年5月18日

MIT

3分钟阅读

viewsIcon

5035

downloadIcon

121

轻松地在任意长度的内存中移动位,以编程方式声明整数大小,进行字节序转换等等

引言

我编写这个东西,一部分是为了我自己,一部分是为了你,因为它的一些部分很难写,而且所有这些部分都证明对我来说是有用的 - 足够让我想要对这段代码做一个半永久性的记录,同时与你分享,亲爱的读者。

它的范围并不完全“干净”,因为它是在我需要功能时演变和增长的。 它处理了一系列有用的东西,但它们并不总是彼此相关,比如位移与 uintx<X>intx<X> 模板。 基本范围是它处理位操作和机器固有字操作,涵盖上述内容。

编译器兼容性

这已经在 GCC 和 Clang 上使用 -std=C++14 或更高版本进行了测试。 它可能无法在 Microsoft 的编译器上工作,我也没有为此付出努力。 这样做并非易事,不是因为我使用了非标准功能或不常见的头文件,而是因为 Microsoft 的原因。

使用这个烂摊子

首先,#define HTCW_LITTLE_ENDIANHTCW_BIG_ENDIAN。 这默认为小端序。 它指示目标机器的字节顺序。

接下来,#define HTCW_MAX_WORD X,其中 X 是位数。 它默认为 64,应该是机器字的大小。 这指示机器可以处理的最大字大小,不一定是它可以本地处理的最大字大小。 例如,如果以 ATmega2560 8 位处理器为目标,则应使用 32,因为它最多可以处理 32 位的整数,即使它只能使用最多 16 位的本机字操作指令。

接下来,包含 *bits.hpp*,之后你需要使用 bits 命名空间。

就功能而言,首先我们有 endianness()endian_mode,它们指示目标平台的字节顺序。

接下来,我们有 swap() 函数,它只是交换字节顺序。 其中一种方法修改数据内联并交换任意数量的字节。

接下来,我们有 from_be()from_le(),它们分别从大端序转换为目标机器的字节顺序,或者从小端序转换为目标机器的字节顺序。

接下来,有三种辅助方法,您应该不需要它们。 它们计算最接近的字大小,并且还包含用于为任意数量的字节创建复合字结构的算法。

接下来,有一些帮助器可以帮助使用模板 intx<>uintx<>realx<>。 这些模板生成一个本机字整数或实数类型,它是指定位数的最小值。 intx<6> 将解析为 int8_t,而 uintx<9> 将解析为 uint16_t。 请注意,还有 int_maxuint_maxreal_max,它们为您提供最大可用大小的整数或实数。

现在我们有 signedx<T>unsignedx<T>。 这些返回传入的整数类型的有符号或无符号版本。

现在我们有 mask<> 模板,它为指定数量的位创建一个掩码,该掩码与字的左侧或字的右侧对齐,并且包括每个的非掩码。

下一组函数用于位操作。 它们设置和清除位或向左和向右移动位,即使在非字节边界上也可以这样做,这意味着例如,您可以将一个 3 位块从内存中较大的块的第 7 位开始向左或向右移动。

它们通常采用偏移量和位数。 这表示您的移位或设置操作的“窗口”。

请注意,这些函数的灵活性可能会使它们比您自己进行移位等操作慢,但它们可以处理整个数组。

这是完整的代码,以防您喜欢复制和粘贴而不是下载

#ifndef HTCW_BITS_HPP
#define HTCW_BITS_HPP
#define PACKED __attribute__((__packed__))
#define PACK // TODO: Add Microsoft pack pragmas here
#define RESTORE_PACK
#ifndef HTCW_MAX_WORD
#define HTCW_MAX_WORD 64
#endif
#include <stddef.h>
#include <stdint.h>
#include <string.h>
namespace bits {
    enum struct endian_mode {
        none = 0,
        big_endian = 1,
        little_endian = 2
    };
    // true if the platform is little endian
    constexpr static endian_mode endianness()
    {
#ifdef HTCW_BIG_ENDIAN
        return endian_mode::big_endian;
#elif defined(HTCW_LITTLE_ENDIAN) || defined(ESP_PLATFORM)
        return endian_mode::little_endian;
#else
        return endian_mode::none;
#endif
        
    }
    // swaps byte order
    constexpr static inline uint16_t swap(uint16_t value)
    {
        return (value >> 8) | (value << 8);
    }
    // swaps byte order
    constexpr static inline uint32_t swap(uint32_t value)
    {
        uint32_t tmp = ((value << 8) & 0xFF00FF00) | ((value >> 8) & 0xFF00FF);
        return (tmp << 16) | (tmp >> 16);
    }
#if HTCW_MAX_WORD >= 64
    // swaps byte order
    constexpr static inline uint64_t swap(uint64_t value) {
        value = (value & 0x00000000FFFFFFFF) << 32 | (value & 0xFFFFFFFF00000000) >> 32;
        value = (value & 0x0000FFFF0000FFFF) << 16 | (value & 0xFFFF0000FFFF0000) >> 16;
        value = (value & 0x00FF00FF00FF00FF) << 8  | (value & 0xFF00FF00FF00FF00) >> 8;
        return value;
    }
#endif

    // swaps byte order (no-op to resolve ambiguous overload)
    constexpr static inline uint8_t swap(uint8_t value) {
        return value;
    }
    template<size_t SizeBytes>
    constexpr static inline void swap_inline(void* data) {
        switch(SizeBytes) {
            case 0:
            case 1:
                return;
            case 2:
                *((uint16_t*)data)=swap(*(uint16_t*)data);
                return;
            case 4:
                *((uint32_t*)data)=swap(*(uint32_t*)data);
                return;
#if HTCW_MAX_WORD >=64            
            case 8:
                *((uint64_t*)data)=swap(*(uint64_t*)data);
                return;
#endif
        }
        for (size_t low = 0, high = SizeBytes - 1; low < high; low++, high--) {
            size_t tmp = ((uint8_t*)data)[low];
            ((uint8_t*)data)[low] = ((uint8_t*)data)[high];
            ((uint8_t*)data)[high] = tmp;
        }
    }
    constexpr static inline uint16_t from_le(uint16_t value) {
        if(endianness()==endian_mode::big_endian)
            return swap(value);
        return value;
    }
    constexpr static inline uint32_t from_le(uint32_t value) {
        if(endianness()==endian_mode::big_endian)
            return swap(value);
        return value;
    }
#if HTCW_MAX_WORD >=64
    constexpr static inline uint64_t from_le(uint64_t value) {
        if(endianness()==endian_mode::big_endian)
            return swap(value);
        return value;
    }
#endif
    constexpr static inline uint8_t from_le(uint8_t value) {
        return value;
    }

    constexpr static inline uint16_t from_be(uint16_t value) {
        if(endianness()==endian_mode::little_endian)
            return swap(value);
        return value;
    }
    constexpr static inline uint32_t from_be(uint32_t value) {
        if(endianness()==endian_mode::little_endian)
            return swap(value);
        return value;
    }
#if HTCW_MAX_WORD >=64
    constexpr static inline uint64_t from_be(uint64_t value) {
        if(endianness()==endian_mode::little_endian)
            return swap(value);
        return value;
    }
#endif
    constexpr static inline uint8_t from_be(uint8_t value) {
        return value;
    }
    constexpr size_t get_word_size(size_t size) {
#if HTCW_MAX_WORD >= 64
        if(size>64) return 0; 
        if(size>32) return 64;
#else 
        if(size>32) return 0;
#endif
        
        if(size>16) return 32;
        if(size>8) return 16;
        return 8;
    }

    constexpr size_t get_word_count(size_t size) {
    size_t result = 0;
        while(size>0) {
            size_t ws = get_word_size(size);
            if(ws==0)
            break;
            size-=ws;
            ++result;
        }
        return result;
    }
    constexpr size_t get_left_word(size_t size,size_t index) {
        size_t i = 0;
        while(size>0 && i<=index) {
            size_t ws = get_word_size(size);
            if(ws==0)
            return 0;
            if(index==i)
                return ws;
            size-=ws;
            ++i;
        }
        return 0;
    }
    constexpr size_t get_right_word(size_t size,size_t index) {
        size_t c = get_word_count(size);
        return get_left_word(size,c-index-1);
        size_t i = 0;
        while(size>0 && i<=index) {
            size_t ws = get_word_size(size);
            if(ws==0)
            return 0;
            if(index==i)
                return ws;
            size-=ws;
            ++i;
        }
        return 0;
    }
    template <size_t BitWidth> class int_helper {};
    template <> struct int_helper<8> { using type = int8_t; };
    template <> struct int_helper<16> { using type = int16_t; };
    template <> struct int_helper<32> { using type = int32_t; };
#if HTCW_MAX_WORD >=64
    template <> struct int_helper<64> { using type = int64_t; };
    template<size_t Width> using intx = typename int_helper<Width>::type;
    using int_max = typename int_helper<HTCW_MAX_WORD>::type;
#endif
    template <size_t BitWidth> class uint_helper {};
    template <> struct uint_helper<8> { using type = uint8_t; };
    template <> struct uint_helper<16> { using type = uint16_t; };
    template <> struct uint_helper<32> { using type = uint32_t; };
#if HTCW_MAX_WORD >=64
    template <> struct uint_helper<64> { using type = uint64_t; };
#endif
    template<size_t Width> using uintx = typename uint_helper<Width>::type;
    using uint_max = uint_helper<HTCW_MAX_WORD>::type;
    template <size_t BitWidth> class real_helper {};
    template <> struct real_helper<32> { using type = float; };
#if HTCW_MAX_WORD >=64
    template <> struct real_helper<64> { using type = double; };
#endif
    template<size_t Width> using realx = typename real_helper<Width>::type;
    using real_max = typename real_helper<HTCW_MAX_WORD>::type;
    template <typename T> struct signed_helper {};
    template <> struct signed_helper<float> { using type = float; };
    template <> struct signed_helper<double> { using type = double; };
    template <> struct signed_helper<int8_t> { using type = int8_t; };
    template <> struct signed_helper<uint8_t> { using type = int8_t; };
    template <> struct signed_helper<int16_t> { using type = int16_t; };
    template <> struct signed_helper<uint16_t> { using type = int16_t; };
    template <> struct signed_helper<int32_t> { using type = int32_t; };
    template <> struct signed_helper<uint32_t> { using type = int32_t; };
#if HTCW_MAX_WORD >=64
    template <> struct signed_helper<int64_t> { using type = int64_t; };
    template <> struct signed_helper<uint64_t> { using type = int64_t; };
#endif
    template<typename T> using signedx = typename signed_helper<T>::type;

    template <typename T> struct unsigned_helper {};
    template <> struct unsigned_helper<float> { using type = float; };
    template <> struct unsigned_helper<double> { using type = double; };
    template <> struct unsigned_helper<int8_t> { using type = uint8_t; };
    template <> struct unsigned_helper<uint8_t> { using type = uint8_t; };
    template <> struct unsigned_helper<int16_t> { using type = uint16_t; };
    template <> struct unsigned_helper<uint16_t> { using type = uint16_t; };
    template <> struct unsigned_helper<int32_t> { using type = uint32_t; };
    template <> struct unsigned_helper<uint32_t> { using type = uint32_t; };
#if HTCW_MAX_WORD >=64
    template <> struct unsigned_helper<int64_t> { using type = uint64_t; };
    template <> struct unsigned_helper<uint64_t> { using type = uint64_t; };
#endif
    template<typename T> using unsignedx = typename unsigned_helper<T>::type;
    
    template<size_t Width>
    struct mask {
        using int_type = uintx<get_word_size(Width)>;
    private:
        constexpr static const int_type int_mask = ~int_type(0);
    public:
        constexpr static const int_type left = int_mask<<((sizeof(int_type)*8-Width)&int_mask);
        constexpr static const int_type right = int_mask>>(sizeof(int_type)*8-Width);
        constexpr static const int_type not_left = ~left;
        constexpr static const int_type not_right = ~right;
    };

    constexpr inline static void set_bits
        (void* bits,size_t offset_bits,size_t size_bits,bool value) {
        const size_t offset_bytes = offset_bits / 8;
        const size_t offset = offset_bits % 8;
        const size_t total_size_bytes = (offset_bits+size_bits+7)/8;
        const size_t overhang = (offset_bits+size_bits) % 8;
        uint8_t* pbegin = ((uint8_t*)bits)+offset_bytes;
        uint8_t* pend = ((uint8_t*)bits)+total_size_bytes;
        uint8_t* plast = pend-(pbegin!=pend);

        const uint8_t maskL = 0!=offset?
                                    (uint8_t)((uint8_t(0xFF>>offset))):
                                    uint8_t(0xff);
        const uint8_t maskR = 0!=overhang?
                                (uint8_t)~((uint8_t(0xFF>>overhang))):
                                uint8_t(0xFF);
        if(value) {
            if(pbegin==plast) {
                const uint8_t mask = maskL & maskR;
                *pbegin|=mask;
                return;
            }
            *pbegin|=maskL;
            *plast|=maskR;
            if(pbegin+1<plast) {
                const size_t len = (plast)-(pbegin+1);
                if(0!=len && len<=total_size_bytes)
                    memset(pbegin+1,0xFF,len);
            }
        } else {
            if(pbegin==plast) {
                const uint8_t mask = maskL & maskR;
                *pbegin&=~mask;
                return;
            }
            *pbegin&=~maskL;
            *plast&=~maskR;
            
            if(pbegin+1<plast) {
                const size_t len = (plast)-(pbegin+1);
                if(0!=len && len<=total_size_bytes)
                    memset(pbegin+1,0,len);
            }        
        }
    }

    template<size_t OffsetBits,size_t SizeBits,bool Value> 
        constexpr inline static void set_bits(void* bits) {
        if(0==SizeBits)
            return;
        const size_t offset_bytes = OffsetBits / 8;
        const size_t offset = OffsetBits % 8;
        const size_t total_size_bytes = (OffsetBits+SizeBits+7)/8;
        const size_t overhang = (OffsetBits+SizeBits) % 8;
        uint8_t* pbegin = ((uint8_t*)bits)+offset_bytes;
        uint8_t* pend = ((uint8_t*)bits)+total_size_bytes;
        uint8_t* plast = pend-(pbegin!=pend);

        const uint8_t maskL = 0!=offset?
                                    (uint8_t)((uint8_t(0xFF>>offset))):
                                    uint8_t(0xff);
        const uint8_t maskR = 0!=overhang?
                                (uint8_t)~((uint8_t(0xFF>>overhang))):
                                uint8_t(0xFF);
        if(Value) {
            if(pbegin==plast) {
                const uint8_t mask = maskL & maskR;
                *pbegin|=mask;
                return;
            }
            *pbegin|=maskL;
            *plast|=maskR;
            if(pbegin+1<plast) {
                const size_t len = (plast)-(pbegin+1);
                if(0!=len && len<=total_size_bytes)
                    memset(pbegin+1,0xFF,len);
            }
        } else {
            if(pbegin==plast) {
                const uint8_t mask = maskL & maskR;
                *pbegin&=~mask;
                return;
            }
            *pbegin&=~maskL;
            *plast&=~maskR;
            
            if(pbegin+1<plast) {
                const size_t len = (plast)-(pbegin+1);
                if(0!=len && len<=total_size_bytes)
                    memset(pbegin+1,0,len);
            }        
        }
    }
constexpr inline static void set_bits
    (size_t offset_bits,size_t size_bits,void* dst,const void* src) {
    const size_t offset_bytes = offset_bits / 8;
    const size_t offset = offset_bits % 8;
    const size_t total_size_bytes = (offset_bits+size_bits+7)/8;
    const size_t overhang = (offset_bits+size_bits) % 8;
    uint8_t* pbegin = ((uint8_t*)dst)+offset_bytes;
    uint8_t* psbegin = ((uint8_t*)src)+offset_bytes;
    uint8_t* pend = ((uint8_t*)dst)+total_size_bytes;
    uint8_t* plast = pend-(pbegin!=pend);

    const uint8_t maskL = 0!=offset?
                                (uint8_t)((uint8_t(0xFF>>offset))):
                                uint8_t(0xff);
    const uint8_t maskR = 0!=overhang?
                            (uint8_t)~((uint8_t(0xFF>>overhang))):
                            uint8_t(0xFF);
    if(pbegin==plast) {
        uint8_t v = *psbegin;
        const uint8_t mask = maskL & maskR;
        v&=mask;
        *pbegin&=~mask;
        *pbegin|=v;
        return;
    }
    *pbegin&=~maskL;
    *pbegin|=((*psbegin)&maskL);
    *plast&=~maskR;
    *plast|=((*(psbegin+total_size_bytes-1))&maskR);
    if(pbegin+1<plast) {
        const size_t len = plast-(pbegin+1);
        if(0!=len&&len<=total_size_bytes) 
            memcpy(pbegin+1,psbegin+1,len);
    }
}
    template<size_t OffsetBits,size_t SizeBits> 
        constexpr inline static void set_bits(void* dst,const void* src) {
        const size_t offset_bytes = OffsetBits / 8;
        const size_t offset = OffsetBits % 8;
        const size_t total_size_bytes = (OffsetBits+SizeBits+7)/8;
        const size_t overhang = (OffsetBits+SizeBits) % 8;
        uint8_t* pbegin = ((uint8_t*)dst)+offset_bytes;
        uint8_t* psbegin = ((uint8_t*)src)+offset_bytes;
        uint8_t* pend = ((uint8_t*)dst)+total_size_bytes;
        uint8_t* plast = pend-(pbegin!=pend);
        uint8_t* pslast =psbegin+total_size_bytes-1;
        const uint8_t maskL = 0!=offset?
                                    (uint8_t)((uint8_t(0xFF>>offset))):
                                    uint8_t(0xff);
        const uint8_t maskR = 0!=overhang?
                                (uint8_t)~((uint8_t(0xFF>>overhang))):
                                uint8_t(0xFF);
        if(pbegin==plast) {
            uint8_t v = *psbegin;
            const uint8_t mask = maskL & maskR;
            v&=mask;
            *pbegin=(*pbegin&uint8_t(~mask))|v;
            return;
        }
        *pbegin=(*pbegin&~maskL)|((*psbegin)&maskL);
        
        *plast=(*plast&~maskR)|((*pslast)&maskR);
        if(pbegin+1<plast) {
            const size_t len = plast-(pbegin+1);
            if(0!=len&&len<=total_size_bytes) 
                memcpy(pbegin+1,psbegin+1,len);
        }
    }
    
    template<size_t OffsetBits,size_t SizeBits, size_t Shift> 
        constexpr inline static void shift_left(void* bits) {
        if(nullptr==bits || 0==SizeBits || 0==Shift) {
            return;
        }
        // special case if we shift all the bits out
        if(Shift>=SizeBits) {
            set_bits<OffsetBits,SizeBits,false>(bits);
            return;
        }
        uint8_t* pbegin = ((uint8_t*)bits)+(OffsetBits/8);
        const size_t offset = OffsetBits % 8;
        const size_t shift_bytes = Shift / 8;
        const size_t shift_bits = Shift % 8;
        const size_t overhang = (SizeBits+OffsetBits) % 8;
        // preserves left prior to offset
        const uint8_t left_mask = ((uint8_t)uint8_t(0xFF<<(8-offset)));
        // preserves right after overhang
        const uint8_t right_mask = 0!=overhang?uint8_t(0xFF>>overhang):0;
        uint8_t* pend = pbegin+(size_t)((OffsetBits+SizeBits+7)/8);
        uint8_t* plast = pend-1;
        uint8_t* psrc = pbegin+shift_bytes;
        uint8_t* pdst = pbegin;
        if(pbegin+1==pend) {
            // special case for a shift all within one byte
            uint8_t save_mask = left_mask|right_mask;
            uint8_t tmp = *pbegin;
            *pbegin = uint8_t(uint8_t(tmp<<shift_bits)&~save_mask)|
                    uint8_t(tmp&save_mask);
            return;
        }
        // preserve the ends so we can
        // fix them up later
        uint8_t left = *pbegin;
        uint8_t right = *(pend-1);
        
        while(pdst!=pend) {
            uint8_t src = psrc<pend?*psrc:0;
            uint8_t src2 = (psrc+1)<pend?*(psrc+1):0;
            *pdst = (src<<shift_bits)|(src2>>(8-shift_bits));
            ++psrc;
            ++pdst;
        }
        
        *pbegin=(left&left_mask)|uint8_t(*pbegin&~left_mask);
        --pend;
        *plast=uint8_t(right&right_mask)|uint8_t(*plast&uint8_t(~right_mask));
    };

constexpr static void shift_left
    (void* bits,size_t offset_bits,size_t size_bits, size_t shift) {
    if(nullptr==bits || 0==size_bits || 0==shift) {
        return;
    }
    // special case if we shift all the bits out
    if(shift>=size_bits) {
        set_bits(bits,offset_bits,size_bits,false);
        return;
    }
    uint8_t* pbegin = ((uint8_t*)bits)+(offset_bits/8);
    const size_t offset = offset_bits % 8;
    const size_t shift_bytes = shift / 8;
    const size_t shift_bits = shift % 8;
    const size_t overhang = (size_bits+offset_bits) % 8;
    // preserves left prior to offset
    const uint8_t left_mask = ((uint8_t)uint8_t(0xFF<<(8-offset)));
    // preserves right after overhang
    const uint8_t right_mask = 0!=overhang?uint8_t(0xFF>>overhang):0;
    uint8_t* pend = pbegin+(size_t)((offset_bits+size_bits+7)/8);
    uint8_t* plast = pend-1;
    uint8_t* psrc = pbegin+shift_bytes;
    uint8_t* pdst = pbegin;
    if(pbegin+1==pend) {
        // special case for a shift all within one byte
        uint8_t save_mask = left_mask|right_mask;
        uint8_t tmp = *pbegin;
        *pbegin = uint8_t(uint8_t(tmp<<shift_bits)&~save_mask)|
                uint8_t(tmp&save_mask);
        return;
    }
    // preserve the ends so we can
    // fix them up later
    uint8_t left = *pbegin;
    uint8_t right = *(pend-1);
    
    while(pdst!=pend) {
        uint8_t src = psrc<pend?*psrc:0;
        uint8_t src2 = (psrc+1)<pend?*(psrc+1):0;
        *pdst = (src<<shift_bits)|(src2>>(8-shift_bits));
        ++psrc;
        ++pdst;
    }
    
    *pbegin=(left&left_mask)|uint8_t(*pbegin&~left_mask);
    --pend;
    *plast=uint8_t(right&right_mask)|uint8_t(*plast&uint8_t(~right_mask));
};

    template<size_t OffsetBits,size_t SizeBits, size_t Shift> 
        constexpr inline static void shift_right(void* bits) {
        if(nullptr==bits || 0==SizeBits || 0==Shift) {
            return;
        }
        // special case if we shift all the bits out
        if(Shift>=SizeBits) {
            set_bits<OffsetBits,SizeBits,false>(bits);
            return;
        }
        uint8_t* pbegin = ((uint8_t*)bits)+(OffsetBits/8);
        const size_t offset = OffsetBits % 8;
        const size_t shift_bytes = Shift / 8;
        const size_t shift_bits = Shift % 8;
        const size_t overhang = (SizeBits+OffsetBits) % 8;
        // preserves left prior to offset
        const uint8_t left_mask = ((uint8_t)uint8_t(0xFF<<(8-offset)));
        // preserves right after overhang
        const uint8_t right_mask = 0!=overhang?uint8_t(0xFF>>overhang):0;
        uint8_t* pend = pbegin+(size_t)((OffsetBits+SizeBits+7)/8)-(OffsetBits/8);
        uint8_t* plast = pend-1;
        uint8_t* psrc = (pend-1)-shift_bytes;
        uint8_t* pdst = pend-1;
        if(pbegin+1==pend) {
            // special case for a shift all within one byte
            uint8_t save_mask = left_mask|right_mask;
            uint8_t tmp = *pbegin;
            *pbegin = uint8_t(uint8_t(tmp>>shift_bits)&~save_mask)|
                    uint8_t(tmp&save_mask);
            return;
        }
        // preserve the ends so we can
        // fix them up later
        uint8_t left = *pbegin;
        uint8_t right = *psrc;
        
        while(pdst>=pbegin) {
            uint8_t src = psrc>=pbegin?*psrc:0;
            uint8_t src2 = (psrc-1)>=pbegin?*(psrc-1):0;
            *pdst = (src>>shift_bits)|(src2<<(8-shift_bits));
            --psrc;
            --pdst;
        }
        
        *pbegin=uint8_t(left&left_mask)|uint8_t(*pbegin&~left_mask);
        *plast=uint8_t(right&right_mask)|uint8_t(*plast&uint8_t(~right_mask));
    };
    constexpr static void shift_right
        (void* bits,size_t offset_bits,size_t size_bits, size_t shift) {
        if(nullptr==bits || 0==size_bits || 0==shift) {
            return;
        }
        // special case if we shift all the bits out
        if(shift>=size_bits) {
            set_bits(bits,offset_bits,size_bits,false);
            return;
        }
        uint8_t* pbegin = ((uint8_t*)bits)+(offset_bits/8);
        const size_t offset = offset_bits % 8;
        const size_t shift_bytes = shift / 8;
        const size_t shift_bits = shift % 8;
        const size_t overhang = (size_bits+offset_bits) % 8;
        // preserves left prior to offset
        const uint8_t left_mask = ((uint8_t)uint8_t(0xFF<<(8-offset)));
        // preserves right after overhang
        const uint8_t right_mask = 0!=overhang?uint8_t(0xFF>>overhang):0;
        uint8_t* pend = pbegin+(size_t)((offset_bits+size_bits+7)/8)-(offset_bits/8);
        uint8_t* plast = pend-1;
        uint8_t* psrc = (pend-1)-shift_bytes;
        uint8_t* pdst = pend-1;
        if(pbegin+1==pend) {
            // special case for a shift all within one byte
            uint8_t save_mask = left_mask|right_mask;
            uint8_t tmp = *pbegin;
            *pbegin = uint8_t(uint8_t(tmp>>shift_bits)&~save_mask)|
                    uint8_t(tmp&save_mask);
            return;
        }
        // preserve the ends so we can
        // fix them up later
        uint8_t left = *pbegin;
        uint8_t right = *psrc;
        
        while(pdst>=pbegin) {
            uint8_t src = psrc>=pbegin?*psrc:0;
            uint8_t src2 = (psrc-1)>=pbegin?*(psrc-1):0;
            *pdst = (src>>shift_bits)|(src2<<(8-shift_bits));
            --psrc;
            --pdst;
        }
        
        *pbegin=uint8_t(left&left_mask)|uint8_t(*pbegin&~left_mask);
        *plast=uint8_t(right&right_mask)|uint8_t(*plast&uint8_t(~right_mask));
    };
}
#endif

历史

  • 2021 年 5 月 18 日 - 首次提交
© . All rights reserved.