lets say we have a binary protocol, with fields network ordered (big endian).
struct msg1
{
int32 a;
int16 b;
uint32 c
}
if instead of copying the network buffer to my msg1 and then use the "networkToHost" functions to read msg1
I rearrange / reverse msg1 to
struct msg1
{
uint32 c
int16 b;
int32 a;
}
and simply do a reverse copy from the network buffer to create msg1. In that case, there is no need for networkToHost functions. this idiomatic approach doesn't work in big endian machines but for me this is not a problem. Apart from that, is there any other drawback that I miss?
thanks
P.S. for the above we enforce strict alignment(#pragma pack(1) etc)
Apart from that, is there any other drawback that I miss?
I'm afraid you've misunderstood the nature of endian conversion problems. "Big endian" doesn't mean your fields are laid out in reverse, so that a
struct msg1_bigendian
{
int32 a;
int16 b;
uint32 c
}
on a big endian architecture is equivalent to a
struct msg1_littleendian
{
uint32 c;
int16 b;
int32 a;
}
on a little endian architecture. Rather, it means that the byte-order within each field is reversed. Let's assume:
a = 0x1000000a;
b = 0xb;
c = 0xc;
On a big-endian architecture, this will be laid out as:
10 00 00 0a
00 0b
00 00 00 0c
The high-order (most significant) byte comes first.
On a little-endian machine, this will be laid out as:
0a 00 00 10
0b 00
0c 00 00 00
The lowest order byte comes first, the highest order last.
Serialize them and overlay the serialized form of the messages on top of each other, and you will discover the incompatibility:
10 00 00 0a 00 0b 00 00 00 0c (big endian)
0a 00 00 10 0b 00 0c 00 00 00 (little endian)
int32 a int16 b int32 c
Note that this isn't simply a case of the fields running in reverse. You proposal would result in a little endian machine mistaking the big endian representation as:
a = 0xc000000;
b = 0xb00;
c = 0xa000010;
Certainly not what was transmitted!
You really do have to convert every individual field to network byte order and back again, for every field transmitted.
UPDATE:
Ok, I understand what you are trying to do now. You want to define the struct in reverse, then memcpy from the end of the byte string to the beginning (reverse copy) and reverse the byte order that way. In which case I would say, yes, this is a hack, and yes, it makes your code un-portable, and yes, it isn't worth it. Converting between byte orders is not, in fact, a very expensive operation and it is far easier to deal with than reversing the layout of every structure.
Are you sure this is required? More than likely, your network traffic is going to be your bottleneck, rather than CPU speed.
Agree with #ribond -
This has great potential to be very confusing to developers, since they'll have to work to keep these to semantically identical structures separate.
Given that network latency is on the order of 10,000,000x slower than it would take the CPU to process it, I'd just keep them the same.
Depending on how your compiler packs the bytes inside a struct, the 16-bit number in the middle might not end up in the right place. It might be stored in a 32-bit field and when you reverse the bytes it will "vanish".
Seriously, tricks like this may seem cute when you write them but in the long term they simply aren't worth it.
edit
You added the "pack 1" information so the bug goes away but the thing about "cute tricks" still stands - not worth it. Write a function to reverse 32-bit and 16-bit numbers.
inline void reverse(int16 &n)
{
...
}
inline void reverse(int32 &n)
{
...
}
Unless you can demonstrate that there is a significant performance penalty, you should use the same code to transfer data onto and off the network regardless of the endian-ness of the machine. As an optimization, for the platforms where the network order is the same as the hardware byte order, you can use tricks, but remember about alignment requirements and the like.
In the example, many machines (especially, as it happens, big-endian ones) will require a 2-byte pad between the end of the int16 member and the next int32 member. So, although you can read into a 10-byte buffer, you cannot treat that buffer as an image of the structure - which will be 12 bytes on most platforms.
As you say, this is not portable to big-endian machines. That is an absolute dealbreaker if you ever expect your code to be used outside of the x86 world. Do the rest of us a favor and just use the ntoh/hton routines or you'll probably find yourself featured on thedailywtf someady.
Please do the programmers that come after you a favor and write explicit conversions to and from a sequence of bytes in some buffer. Trickery with structures will lead you straight into endianness and alignment hell (been there).
Related
Note: I am trying to write my own function that performs this conversion
I understand that a char is 1 byte, while a wchar_t is 2 bytes.
So this is how a conversion would happen:
1) Input a text
Hello, world
2) Get the bytes of the string
48 65 6c 6c 6f 2c 20 77 6f 72 6c 64 21
3) Allocate memory twice the number of bytes
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
4) Fill a byte with the ANSI value, skipping one byte at a time
48 00 65 00 6c 00 6c 00 6f 00 2c 00 20 00 77 00 6f 00 72 00 6c 00 64 00 21 00
I have a couple of questions about this process:
1) Can I simply cast an ANSI string to UNICODE and have it replicate the exact process above, or will it simply fill the first half of the bytes with the ANSI bytes and leave the rest to 0?
char a[] = { "Hello, world!" };
wchar_t* b = reinterpret_cast<wchar_t*>(a);
2) Looking at the MultiByteToWideChar function, I see a CodePage argument and I wonder what it is. Isn't the conversion all the same (as I understand it and wrote it out above)? I thought the ASCII character codes were all the same everywhere, but this argument seems to say otherwise if I am understanding correctly from the fact it has values for Mac and Windows there.
I thought the ASCII character codes were all the same everywhere, but this argument seems to say otherwise if I am understanding correctly from the fact it has values for Mac and Windows there.
The ASCII codes are, yes, but the high bit of an "Extended ASCII" string (spoiler: there's no such thing) maps to any of a large number of codepages, all different encodings intended for use mostly in different geographic locales. The approach you've taken is fine for the simple, plain ASCII case, but it doesn't work in general, and MultiByteToWideChar knows this. It will re-encode properly from whatever codepage you're using, to what Windows confusingly calls "Unicode" (not "UNICODE"), which is actually more specifically the "UTF-16" encoding.
Can I simply cast an ANSI string to UNICODE and have it replicate the exact process above, or will it simply fill the first half of the bytes with the ANSI bytes and leave the rest to 0?
No. A cast does not reencode things or change values. There you are just saying "I promise that a is a bunch of wchar_ts, even though it has type char* (it doesn't, it has array type, but close enough for today).
That code actually has undefined behaviour, if you use b, because you've broken aliasing rules (you can examine a T through a char*, but you can't treat a char[] as some T that you never created). But, if it didn't, you'd find that your "string" were now half the length, and more than likely an invalid UTF-16 sequence that would not render correctly anywhere.
So if I wanted to support UTF-32, I would have to create my own wrapper for strings since wchar_t is only 2 bytes long and I need 4 bytes, and also I would not be able to print it with printf for example, correct?
Technically, sort of yes (though you'd use a library like libicu rather than rolling your own).
But, in reality, you don't want to use UTF-32. Working with the Windows API you're stuck with UTF-16, but other than that we generally prefer UTF-8 over char, which is nice and portable and flexible and good and nice. (You will again want a library for this though.)
It'd then be up to you as to where you perform the relevant conversions, and/or whether you have a switch that flips from UTF-8 to UTF-16 depending on the platform (like Windows's old UNICODE macro) or just run UTF-8 everywhere until you hit a Windows API boundary.
Or, if all your input is ASCII as you imply, then you don't really need to do anything other than what you are already: either keep your ASCII throughout the program but convert it to UTF-16 when using the Windows API, or use UTF-16 (and wchar_ts throughout your whole program and have no conversions. Make sure to use wide-char versions of your favourite functions, though (like wprintf) if you go down that route.
What you are attempting to do will only work for ASCII character codes in the range of 0..127. Those characters have the same numeric values in Unicode, and thus can be copied as-is between char and wchar_t strings.
And no, you can't just reinterpret_cast the memory address of the char data to wchar_t*, you need to allocate a new wchar_t array and copy the values, eg:
char a[] = { "Hello, world!" };
wchar_t* b = new wchar_t[sizeof(a) * sizeof(wchar_t)];
for(size_t i = 0; i < sizeof(a); ++i) {
b[i] = static_cast<wchar_t>(a[i]);
}
...
delete[] b;
This type of copying would be better handled using std::string and std::wstring iterator-based constructors instead, eg:
std::string a = "Hello, world!";
std::wstring b(a.begin(), a.end());
...
However, beyond the ASCII range, you need to convert the data between char and wchar_t via charset/codepage lookups. Different charsets/codepages encode Unicode characters in different ways. MultiByteToWideChar() (and WideCharToMultiByte()) handle those conversion for you, using the codepage that you tell it to use. There are also many 3rd party libraries that can also handle these conversions, such as ICONV, ICU, etc. To an extent, even C++'s own std::wstring_convert and std::wbuffer_convert can, too (though they are deprecated in C++17 onwards).
For example, let's look at codepoint U+20AC EURO SIGN (€):
in a wchar_t string, it takes up a single wchar_t whose numeric value is 0x20AC.
in a UTF-8 encoded char string, it takes up 3 chars whose numeric values are 0xE2 0x82 0xAC.
in a Windows-1252 encoded char string, it takes up a single char whose numeric value is 0x80.
in a Latin-1 (ISO-8859-1) encoded char string, the Euro sign doesn't even have a numeric value assigned!
So, a simple value copy will not suffice for non-ASCII characters.
I'm using UBSAN and am getting the following error. Note that I'm compiling with clang 6.0.1 with -fsanitize=undefined. I've read a number of background questions on SO and still can't solve my particular issue. Here are the background questions for reference:
What is the recommended way to align memory in C++11
Misaligned address using virtual inheritance
runtime error: member call on misaligned address 0x000001f67230 for type 'const A *', which requires 64 byte alignment
0x000001f67230: note: pointer points here
00 00 00 00 c0 72 f6 01 00 00 00 00 08 00 00 00 00 02 00 00 40 02 00 00 00 00 00 00 00 00 00 00
Here are some things to note about class C:
the object of type C is created using new (C* o = new C();)
type C has a member of type A that has 64 byte alignment. I verified this using alignof.
C is declared using class alignas(64) C -- but that doesn't solve my problem
My current hypothesis is that I need to use the C++11 equivalent of the C++17 std::aligned_alloc to create the object using aligned storage. But, I'm not sure how to best do this or if it will actually solve my problem. I would prefer to solve the problem once in the definition of class C as opposed to every time I create a C, if possible. What is the recommended approach to solve this issue to remove the UBSAN error?
If your class already has a member that requires 64 Byte alignment, then the class will already also have 64 Byte alignment out of necessity. So adding an explicit alignas(64) is not really gonna change anything.
The basic problem here is that allocation functions (in C++11) are only required to return memory aligned to fundamental alignment. C++11 left it implementation-defined whether over-aligned types are supported by new or not [expr.new]/1. C++17 introduced new-extended alignment and additional allocation functions to deal with that (if and which new-extended alignments are supported, however, is still implementation-defined).
If you can switch to a compiler that supports C++17, chances are that your code will just work. Otherwise you will probably have to either use some implementation-specific function to allocate aligned memory or just roll your own solution, e.g., based on std::align and placement new (which would work in C++11 too)…
Can someone please help me figure out why the "next" pointer in my linked list is dereferencing to the wrong memory address in code on 32-bit platform, but works fine on 64-bit platform? My program is built as a universal binary on Xcode 7.3 and written in C++.
I've got a linked list, and dereferencing the "next" pointer in the debugger shows the correct memory, but dereferencing it in code reads the memory that is 4-bytes beyond where it should read. I will try to explain..
The objects on the list are 4144 bytes each, the last 4-bytes are a 32-bit pointer to the "next" item on the list. Looking at the "next" pointer in memory (0xBFFD63AC), we see that it is 4 zeros (NULL), this is correct. BUT notice that memory at 0xBFFD63B0 is a 0x01. This is the byte beyond the "next" pointer. When I ask the debugger to print the next variable, it prints the proper value (NULL):
(lldb) print l_pObject->Next
(Object__t *) $0 = 0x00000000
(lldb) memory read &(l_pObject->Next)
0xbffd63ac: 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 ................
However, if I execute the code that dereferences the "next" pointer, it actually reads the data from 0xBFFD63B0 instead of 0xBFFD63AC:
l_pObject = l_pObject->Next;
(lldb) print l_pObject
(Object_t *) $3 = 0x00000001
(lldb) memory read &(l_pObject)
0xbffd2504: 01 00 00 00 80 53 fd bf 00 00 00 00 6c 82 2e
I am positive it is reading the 0x01 from 0xBFFD63B0. The debugger seems to know that "next" indicates the memory at 0xBFFD63AC, but for some reason dereferencing "next" in code actually reads from 0xBFFD63B0, but I'm not sure how to figure out why. I've tried adding __attribute__((packed)) to the struct, but that made no difference. It's difficult to determine where things are going wrong because the debugger is telling me something different from what is really happening. Any tips on how to proceed from here would be very greatly appreciated!
EDITED TO ADD MORE INFO:
In the least there is a debugger error here! I ask the debugger to print the sizeof the struct and it gives me 4144, but in code I use the C funciton sizeof() and that gives me 4148! So there is definitely padding happening in the struct, but the debugger and apparently this section of code are blind to it. This is the root of my problem.
Debugger:
(lldb) print sizeof(*l_pObject)
(unsigned long) $0 = 4144
Code:
unsigned int iSizeOf = sizeof(*l_pObject); /* iSizeOf will equal 4148! */
Something funny is happening...
Without seeing the code it is impossible to tell. In general though, pointers tend to be 8-bytes wide on 64-bit systems and 4-bites wide on 32-bit systems. Obviously you observed that it's jumping 4 bytes beyond where it should be, which is a strong indicator. (0xBFFD63B0 - 0xBFFD63AC) == 4.
OK I found the problem, I should have realized it right away. The code that was crashing is in a library. Both the library and the calling application share definitions for variable types. Due to a missing preprocessor flag on the application side, one of those types was being allocated 4-bytes smaller on the application side than in the library code. So when the application created a linked list of objects and passed a reference to the library, each object on that list was 4-bytes smaller than the library was expecting them to be. What made it less obvious was the debugger was showing me addresses and offsets based on what was allocated in the application, so at first glance everything appeared to be sized correctly. I decided to not trust the debugger and wrote my own code to check addresses and offsets, and that's when it became obvious what was happening. So anyway, long story short, be sure your application and library allocate types to be the same size and then things will work much better. :)
I'm basically new to C++, aside from attempting to learn the language over 10 years ago and giving up, as I didn't really have a project to motivate me... Anyways, I'm just stating that I'm pretty much a n00b to C++ to let you guys/gals know my current knowledge level. That said, I am fairly proficient with Python and PHP. And since both of those languages are loosely typed, I am not that familiar with the impact type casting in C++ has on executable size, if any.
I am writing an Arduino program to take some data from a couple of ultra-sonic distance sensors and apply the data to a servo control algorithm. No problems with that, but I am now trying to optimize my code, as I'm getting close to the Arduino Micro's limit of 28,672 bytes. My first thought was to change my data types wherever possible to things like short int's and char's, expect it to have either no effect, or to slightly reduce my executable size. What I found is that the executable actually increased in size, after these changes, by a few hundred bytes.
Could someone with more C++ knowledge than I kindly help me understand the reason for this, and why I should, or shouldn't, even bother trying to choose the smallest possible data types for my variables? Obviously the results dictate what I should do here, but I really like to understand the 'why' behind things, and after some Googling, I still came up unsure.
Also, if it's not too much to ask; does anyone have some tips, or a link to some info on optimizing C++ code for limited-memory micro-controllers such as the Arduino?
You ask many things, but this can be answered with an example:
What I found is that the executable actually increased in size, after these changes, by a few hundred bytes.
... help me understand the reason for this ...
In general, you cannot predict whether a smaller data type is better or worse, which the small bit of code below will demonstrate.
To see what is going on, you have to look at the assembly code produced by the compiler. The AVR tool chain has a component that will produce such a listing, typically an .LSS file. I don't think Arduino supports this. The assembly listings below are via Eclipse which drives the extended listing by default.
Here is a little section of an LED blink program that can be used to demonstrate your confusion. It has a brightness value that it sets to the LED in the loop:
boolean fadein = true;
int bright = 0; // we will change this data type int <-> int8_t
void loop() {
// adjust brightness based on current direction
if(fadein) {
bright += 1;
}
else {
bright -= 1;
}
// apply current light level
analogWrite(13,bright);
To demonstrate, the bright variable is changed between 1 byte and 2 byte int's and we compare the assembly listing:
Compare The Increment Line
Here is the listing for just the increment line with two data types:
// int bright - increment line - must load and store 2 bytes
// 18 bytes of code
bright += 1;
18a: 80 91 02 01 lds r24, 0x0102
18e: 90 91 03 01 lds r25, 0x0103
192: 01 96 adiw r24, 0x01 ; 1
194: 90 93 03 01 sts 0x0103, r25
198: 80 93 02 01 sts 0x0102, r24
The first column is the code space address, the second column the actual code bytes, and the last column is the assembly human readable form. LDS is load from memory, ADIW is the add, and STS is storing back to memory
Here is the smaller data type, with the expected result:
// int8_t bright - increment line - only load and store 1 byte
// 10 bytes of code
bright += 1;
18a: 80 91 02 01 lds r24, 0x0102
18e: 8f 5f subi r24, 0xFF ; 255
190: 80 93 02 01 sts 0x0102, r24
Note the weirdness of SUBI 255 instead of adding 1 -- that is compiler devs tricks.
So there you go, the smaller data type produces smaller code as you expected. You were correct! Oh wait, you already stated you where not correct...
Compare the function call
But what about function calls? The analogWrite() method expects an int, so the compiler will be forced to create a conversion if needed
// int - needs no type conversion, can directly load value
// from addresses 0x0102 and 0x0103 and call
// 16 bytes code
// apply current light level
analogWrite(13,bright);
1b0: 20 91 02 01 lds r18, 0x0102
1b4: 30 91 03 01 lds r19, 0x0103
1b8: 8d e0 ldi r24, 0x0D ; 13
1ba: b9 01 movw r22, r18
1bc: 0e 94 87 02 call 0x50e ; 0x50e <analogWrite>
LDI is loading the constant, MOVW is moving variable in preparation for call.
// int8_t - needs a type conversion before call
// 20 bytes code
// apply current light level
analogWrite(13,bright);
1a0: 80 91 02 01 lds r24, 0x0102
1a4: 28 2f mov r18, r24
1a6: 33 27 eor r19, r19
1a8: 27 fd sbrc r18, 7
1aa: 30 95 com r19
1ac: 8d e0 ldi r24, 0x0D ; 13
1ae: b9 01 movw r22, r18
1b0: 0e 94 76 02 call 0x4ec ; 0x4ec <analogWrite>
No need to understand the assembly for the type conversion to see the effect. The smaller data type has produced more code.
So what does it mean? The smaller data type both reduces code size and increase code size. Unless you can compile code in your head, you cannot figure this out by inspection, you have to just try it.
First, take a look to how to optimize your Arduino memory usage and optimizing Arduino memory use. also, take a look to saving RAM space.
Generally, you have to distinguish between code size and data size. Optimizing data size is likely to increase your code size (and also slow things down), because the compiler needs to put more instructions into the code to convert forth and back between the various possible data sizes.
So, as a rule of thumb: Use the default data size (e.g. "int") for any value, that appears in the data at most a few times. On the other hand, if you have large arrays, setting the optimum data size (e.g. "short", if the value is guaranteed to be in the range -32768 .. 32767) can greatly reduce the memory footprint of your app at runtime.
In your case, where you don't have much data, focus more on optimizing code size: Reduce the number of libraries used, and avoid wrappers etc. pp.
One of the biggest memory consumers are floating point numbers (both in RAM and FLASH). Ram because the types are larger than integers and Flash because the Arduino does not have a floating point unit. Thus all floating point operations will result in a larger executable.
Also take care that using libraries may link lots of not really needed stuff that consumes significant amounts of your memory.
Having said that: without any more details on your code it is pretty hard to determine why you have such a large memory footprint.
I am currently facing a problem of which I have no idea how to avoid it..
I try to process data which can be either in big endian or little endian. This is not really a problem because it always starts with a header so I can check which endian mode I have to use but during the decoding of the values there are some operations which I dont know how to implement for big endian data.
The code runs on a nVidia Tegra (Cortex-A9 based on ARMv7 architecture) which is little endian (or runs in little endian mode) but sometimes I get big endian data.
Most operations on the data are not really a problem but I dont know how to get the addition right..
Example: D5 1B EE 96 | 96 EE 1B D5
+ AC 84 F4 D5 | + D5 F4 84 AC
= 1 81 A0 E3 6B | = 1 6C E2 A0 81
As you can see, most bytes are already correct in the result but some are not. They differ by +1 or -1 from the expected result because the addition is always made from right to left (little endian machine) and so we take the carry (if any) to the left.
In the case of the big endian addition on this little endian machine I would have to add from left to right and take the carry (if any) to the right.
My question now is, whether there is a possibility (maybe using special instructions for the processor?) to get the right result? Maybe ther are further operations I can make on the result to get rid of these +1/-1 differences which are "cheaper" than to revert both operands and also the result?
Best Regards,
Tobias
The most logical way to do this is to simply convert the numbers to the correct endianness, then perform the calculation, then (if needed) convert back again.
You could of course use a loop to do the byte-by-byte backwards caclulation and handle the carry - but it's more complicated, and I'm pretty certain that it won't be faster either, because there are more conditionals and processors are pretty good at "byteswapping".
You should be able to use the ntohl and htons networking functions to convert the numbers.
Something like this:
int add_big_endian(int a, int b)
{
x = ntohl(a);
y = ntohl(b);
z = x + y;
return htonl(z);
}
You have two options: you can write two sets of code, one for each endianness, and try to keep track of what's going on where, or you can use a single internal representation and convert incoming and outgoing values appropriately. The latter is much simpler.