Neat way to go byte by byte

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Post Reply
Dreadstew
Posts: 23
Joined: Wed Apr 01, 2015 5:18 pm

Neat way to go byte by byte

Post by Dreadstew »

I learned this method doing some assembly tinkering and I think it's neat so I'll share a snippet of it. It's useful for easily pushing things like ints byte by byte onto a vector, or things like that.

Code: Select all

 
unsigned int size = str.size();
unsigned char* sref = (unsigned char*)&size;
unsigned int offset = (unsigned int)sref + 4;
for (; (unsigned int)sref < offset; ++sref)
{
     packet.push_back(*sref);
}
 
What do you think?
CuteAlien
Admin
Posts: 9643
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Neat way to go byte by byte

Post by CuteAlien »

Should probably be size_t instead of unsigned int. And instead of +4 better use sizeof(size). And you can make offset an unsigned char* then you should be able to avoid 2 casts.

Alternative - reserve size in the array and use memcpy (can't tell you exact way to do that as it depends if you use std::vector or irr::array here which do that slightly different). And be aware when this packet is for the network and you have computers with processors with different endian systems involved then it won't work without fixing endian order on one side.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
SLC
Posts: 21
Joined: Mon Jan 06, 2014 9:41 pm

Re: Neat way to go byte by byte

Post by SLC »

This is quite a known and very basic thing. In fact, there are quite a few "issues" with that code. Mainly assuming pointers don't know math (in cases where u/intptr_t is not available). Not to mention that on a modern compiler it would get quite loud with aliasing warnings. And a narrow (but possible) chance to fail on 64 bit systems.

You could turn that into a generic function that accepts any contiguous stl container not just std::string.

Code: Select all

#include <array>
#include <vector>
#include <string>
#include <algorithm>
#include <string_view>
 
template <class T> void x(std::vector<uint8_t> & packet, const T & stlc) {
    using U = decltype(stlc.size());
    U n = stlc.size();
    uint8_t *p = reinterpret_cast<uint8_t*>(&n);
    uint8_t *o = p+sizeof(U);
    for (; p < o; ++p) {
        packet.push_back(*p);
    }
}
 
void y(std::vector<uint8_t> & packet) {
    x(packet, std::string("0123456789abcdefghijk"));
}
But if you really want to make it challenging, you should try to make the generic function as a single line of code and with nothing but STL.

Code: Select all

template <class T> void x(std::vector<uint8_t> & packet, const T & stlc) {
    std::copy_n(std::basic_string_view<uint8_t>(reinterpret_cast<uint8_t*>(std::array<decltype(stlc.size()),1>{stlc.size()}.data()), sizeof(decltype(stlc.size()))).begin(), sizeof(decltype(stlc.size())), std::back_inserter(packet));
}
It is quite fun to go out of your way just to see if it can be done. Even if not practical. Although, GCC8 with O3 seems to compile down to the same assembly in either case. And member typedefs (value_type, size_type) were not used on pupose.

Probably outside the scope of this topic but fun nonetheless.
Post Reply