Skip to main content
1 of 2
Edward
  • 66.6k
  • 4
  • 118
  • 282

I see a number of things that could help you improve your program.

Fix your formatting

Crowding multiple statements on a single line makes your program harder to read and understand. So instead of this:

if ((*off) > msg.size()) return 0;
int32_t result; int size = sizeof (result);

Write this:

if ((*off) > msg.size()) {
    return 0;
}
int32_t result; 
int size = sizeof (result);

Use the required #includes

The code uses std::vector which means that it should #include <vector>. It was not difficult to infer, but it helps reviewers if the code is complete. For this code, it appears that you need these:

#include <vector>
#include <cstring>
#include <cstdint>

Use C++-style includes

Instead of including string.h you should instead use #include <cstring>. The difference is in namespaces as you can read about in this question. I infer that the code used that because the call to memcpy was not std::memcpy.

Prefer passing a reference to a raw pointer

The writeInt32() function takes a pointer to a vector, but what if the pointer is nullptr? Clearly the function will fail, and probably crash the program if that happens. Better, then, would be to pass a reference instead, which says that it really MUST be a std::vector<std::uint8_t> and nothing else.

Avoid portability problems

The endianness of computers is not fixed by the C++ language standard, so your memory copy or memory casting is not going to work the same way on all machines. This portability problem can be solved by doing one of two things: either rely only on guarantees that are actually made by the standard or use conversion routines to put everything in the same order. This often comes up in network communications, so some platforms have htonl() and friends to convert from "host order" which might be big-endian or little-endian, to network order which is always big-endian. Note, however, that this has its own portability issues because those functions are not part of the C++ standard. So the other way to do it is to do it yourself.

static void writeInt32(std::vector<std::uint8_t>& msg, std::int32_t value)
{
    for (auto i = sizeof(value); i; --i, value >>= 8) {
        msg.push_back(value & 0xff);
    }
}

This is shorter and also portable. In this version, i is declared to be auto but declaring it to be of type std::size_t, as you had it, is another way to do it and even works if you're not using a C++11 compiler.

Be careful with signed and unsigned

In the readInt32 routine, the code compares an int to a size_t, but size_t is unsigned and int is signed. In this case, your off variable should probably be declared as being of std::size_t * type.

Don't fail silently

In the readInt32 function, a returned value of 0 might mean that a value of zero was actually read or it might mean that the offset pointer was off the end of the message. Unfortunately with the current code there's no way to differentiate these cases. I'd suggest throwing an exception instead.

Be careful with memcpy

The code currently catches the case in which the offset is beyond the end of the vector, but what if it's pointing instead to the last byte of the vector? In this case, memcpy will copy that last byte and whatever happens to be in memory beyond that. This is not likely to be a useful value. In this case, I'd recommend not using memcpy at all and rewriting the code to be portable as mentioned above.

Reconsider the interface

Right now, the code consists of two functions which deal with writing/reading a std::int32_t value to/from a message. If your code frequently does things with such messages, it might be better to create a Message class and have member functions that read and write particular types of values, such as your std::int32_t values used here. I'd also recommend that raw pointers such as off not be part of the interface at all.

Edward
  • 66.6k
  • 4
  • 118
  • 282