is it possible to do memcpy in bits instead of bytes?
Asked Answered
A

4

7

I would like to know is it possible to do memcpy by bits instead of bytes?

I am writing a C code for Ethernet frame with VLAN tagging, in which I need to fill different values for VLAN header attributes (PCP-3bits,DEI-1bit,VID-12bits).

How can I do memcpy to those bits, or any other possibility to fill values to those attributes in bits.

Thanks in advance !

Aluino answered 26/6, 2013 at 12:55 Comment(3)
The most important question is whether you expect the bits to shift in relation to 8-bit boundaries as part of the copy; and do you expect that bits could overlap?Monohydroxy
memcpy copies n bytes from source to destination.You can't use memcpy for bits.Culmination
I need this functionality for the same reason, please provide an answer as to how you solved this.Burnisher
P
8

No. Bits are not addressable (meaning that it is not possible to read them and only them directly from memory. They have no address. Only bytes have addresses).

You need to read the byte or word that contains the bits you are interested in and do the masking yourself.

Persons answered 26/6, 2013 at 12:59 Comment(0)
W
2

I have done the 802.1Q VLAN tagging of frames for a client where they had only 802.3 Ethernet frames but wanted to migrate to 802.1Q as there was a new VLAN aware switch installed.

Firstly, you cannot copy bits. We had copied the tag in bytes using memcpy.

Illustration (refer Wikipedia for descriptions of fields):-

VLAN Tag = 4 Bytes; consisting of TPID(2 Bytes) and TCI(2 Bytes).

TPID is simple and is always 0x8100 indicating a VLAN tagged frame.

TCI consists of PCP-3bits, DEI-1bit, VID-12bits. Breakdown the TCI to nibbles i.e. 4-bits. By default, the nibble(PCP+DEI) = 0x0 assuming priority is disabled and DEI = 0. The remaining 3-nibbles(12bits) are for the VLAN-ID itself. Say, you want to tag a frame for VLAN-ID = 123. In hex this will be = 0x07B.

Group the nibbles together and there you have your 2 byte TCI field which can now be seen as 0x007B.

Then you can do the below. (code is not compiled)

unsigned short int vlanTPID, vlanTCI;
unsigned char     *dest, *src;

// Set the VLAN Tag
vlanTPID = 0x8100;
vlanTCI  = 0x007B;

// Pointer to the TPID position of ethernet frame
dest = &vlanTagPosition;
src = &vlanTPID;
memcpy(dest, src, sizeof(vlanTPID));

// Increment dest pointer by 2 bytes to insert TCI in the ethernet frame
dest += 2;
src = &vlanTCI;
memcpy(dest, src, sizeof(vlanTCI));
Wyatt answered 10/11, 2013 at 7:26 Comment(2)
That is the easy part where the PCP and DEI are 0, now lets assume a vlanID of 4011 and a PCP 7 with DEI 0, I'd like to see that (because I'm really struggling with this!).Burnisher
| 1 1 1 0 | 1 1 1 1 | 1 0 1 0 | 1 0 1 1 | - As I said, you need to imagine the TCI field as 16-bits or 4-nibbles. The first nibble is PCP+DEI = 7 and the remaining 3-nibbles are the VLAN-ID = 4011. Once converting the binary bits to hex your TCI field will be 0xEFAB.Wyatt
S
0

If you need to fill fields, you can use C bit-fields with a struct, like this:

struct box_props {
    unsigned first  : 1;
    unsigned second : 3;
    unsigned : 4;
};

Where 1, for instance, means that the field is 1bit long. The last (unnamed) field means: 4bit padding.

Define struct, memcpy to it and read fields as if they where unsigned. Same for writing.

NOTE: always pad to integer byte, or memcpy could have unwanted effects.

Stigmatize answered 26/6, 2013 at 12:59 Comment(3)
Note that this is entirely implementation defined. See this answer for details, but basically it's the order and packing that's not portable.Grimsley
Thank you... I'll try with structure method.Aluino
But keep in mind what other people said and the SO answer linked by @Grimsley (I guess I should update my C-fu): this is not portable and weird bugs might arise. Actually, Pascal provided you with the canonical answer.Stigmatize
D
0

Just implementation to copy any arbitrary bitset. The last implementation is a part of the tacklelib library:
https://github.com/andry81/tacklelib
https://github.com/andry81/tacklelib/tree/HEAD/include/tacklelib/utility/memory.hpp

Pros:

  • Copies by 64-bit words.
  • Dynamically use memcpy function when it is possible to deduce to it.

Cons:

  • The both buffers must be padded for 7 bytes at the end and so 7 bytes in the output buffer after the end offset basically can be overwritten. If these bytes is not a padded buffer end, then they can be restored by saving this part separately into 64-bit word plus mask and copy back after the function call.
  • Buffers must not overlap.
// Bitwise memory copy.
// Both buffers must be padded to 7 bytes remainder to be able to read/write the last 8-bit block as 64-bit block.
// Buffers must not overlap.
//
inline void memcpy_bitwise64(uint8_t * to_padded_int64_buf, uint64_t to_first_bit_offset, uint8_t * from_padded_int64_buf, uint64_t from_first_bit_offset, uint64_t bit_size)
{
    assert(bit_size);

    uint64_t bit_offset = 0;

    uint32_t from_byte_offset = uint32_t(from_first_bit_offset / 8);
    uint32_t to_byte_offset = uint32_t(to_first_bit_offset / 8);

    uint32_t remainder_from_bit_offset = uint32_t(from_first_bit_offset % 8);
    uint32_t remainder_to_bit_offset = uint32_t(to_first_bit_offset % 8);

    while (bit_offset < bit_size) {
        if (remainder_to_bit_offset >= remainder_from_bit_offset && (remainder_to_bit_offset || remainder_from_bit_offset)) {
            const uint64_t from_bit_block = *(uint64_t *)&from_padded_int64_buf[from_byte_offset];
            uint64_t & to_bit_block = *(uint64_t *)&to_padded_int64_buf[to_byte_offset];

            const uint32_t to_first_bit_delta_offset = remainder_to_bit_offset - remainder_from_bit_offset;
            const uint64_t to_bit_block_inversed_mask = uint64_t(~0) << remainder_to_bit_offset;

            to_bit_block = ((from_bit_block << to_first_bit_delta_offset) & to_bit_block_inversed_mask) | (to_bit_block & ~to_bit_block_inversed_mask);

            const uint32_t bit_size_copied = 64 - remainder_to_bit_offset;

            bit_offset += bit_size_copied;

            from_first_bit_offset += bit_size_copied;
            to_first_bit_offset += bit_size_copied;

            if (remainder_to_bit_offset != remainder_from_bit_offset) {
                from_byte_offset += 7;
                to_byte_offset += 8;

                remainder_from_bit_offset = 8 - to_first_bit_delta_offset;
                remainder_to_bit_offset = 0;
            }
            else {
                from_byte_offset += 8;
                to_byte_offset += 8;

                remainder_from_bit_offset = 0;
                remainder_to_bit_offset = 0;
            }
        }
        else if (remainder_to_bit_offset < remainder_from_bit_offset) {
            const uint64_t from_bit_block = *(uint64_t *)&from_padded_int64_buf[from_byte_offset];
            uint64_t & to_bit_block = *(uint64_t *)&to_padded_int64_buf[to_byte_offset];

            const uint32_t to_first_bit_delta_offset = remainder_from_bit_offset - remainder_to_bit_offset;
            const uint64_t to_bit_block_inversed_mask = uint64_t(~0) << remainder_to_bit_offset;

            to_bit_block = ((from_bit_block >> to_first_bit_delta_offset) & to_bit_block_inversed_mask) | (to_bit_block & ~to_bit_block_inversed_mask);

            const uint32_t bit_size_copied = 64 - remainder_from_bit_offset;

            bit_offset += bit_size_copied;

            from_first_bit_offset += bit_size_copied;
            to_first_bit_offset += bit_size_copied;

            from_byte_offset += 8;
            to_byte_offset += 7;

            remainder_from_bit_offset = 0;
            remainder_to_bit_offset = (8 - to_first_bit_delta_offset);
        }
        // optimization
        else {
            const uint64_t bit_size_remain = bit_size - bit_offset;
            const uint32_t byte_size_remain = uint32_t(bit_size_remain / 8);

            if (byte_size_remain + 1 > 8) {
                memcpy(to_padded_int64_buf + to_byte_offset, from_padded_int64_buf + from_byte_offset, byte_size_remain + 1);
            }
            // optimization
            else {
                *(uint64_t *)&to_padded_int64_buf[to_byte_offset] = *(uint64_t *)&from_padded_int64_buf[from_byte_offset];
            }

            break;
        }

        assert(from_byte_offset == uint32_t(from_first_bit_offset / 8));
        assert(remainder_from_bit_offset == uint32_t(from_first_bit_offset % 8));

        assert(to_byte_offset == uint32_t(to_first_bit_offset / 8));
        assert(remainder_to_bit_offset == uint32_t(to_first_bit_offset % 8));
    }
}
Deckard answered 4/3, 2022 at 6:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.