WTF Bites
-
@Zerosquare said in WTF Bites:
An even better solution is not to reinvent the wheel, and use the installation features every modern OS has.
Hey!
Didn't you know? Windows has a package manager now.
Oh wait. Never mind.
-
Shit. I badmouthed Tizen and now he threatens to punch me. He already loaded fist instead of a file:
-
-
@Zerosquare said in WTF Bites:
An even better solution is not to reinvent the wheel, and use the installation features every modern OS has.
Oddly enough, there's a Windows Store version of this application. Can't get much more Modern than that as far as installing goes.
-
@HardwareGeek said in WTF Bites:
@Zerosquare said in WTF Bites:
"not to reinvent the wheel"Now I know you're not a real programmer.
Node.js programmer, probably.
-
@sebastian-galczynski said in WTF Bites:
I badmouthed Tizen and now he threatens to punch me.
That's par for the course for it.
-
@Bulb
Yep, I read the three canonical Tizen threads ;)BTW, we finally got rid of the threatening pop-up. It started appearing when someone activated the 'local developer mode' (which didn't work). After returning to remote mode, it looked like the device rebooted, but the error reappeared every time the app was started. Only after physically pulling the plug it stopped appearing.
-
@hungrier No, I have no interest in that category.
So either he's never posted outside the garage, or NodeBB is too stupid to filter before paginating.
One of those is funnier, and the other is more likely.
I'm going with the first one.
-
@hungrier No, I have no interest in that category.
So either he's never posted outside the garage, or NodeBB is too stupid to filter before paginating.
One of those is funnier, and the other is more likely.
I'm going with the first one.
Guess what it does with collapsing notifications?
-
@sebastian-galczynski said in WTF Bites:
Only after physically pulling the plug it stopped appearing.
Rule Number 1: Always turn it off and
back onleave it off.
-
@sebastian-galczynski said in WTF Bites:
After returning to remote mode, it looked like the device rebooted, but the error reappeared every time the app was started. Only after physically pulling the plug it stopped appearing.
Oh, the story of a reboot that wasn't.
-
@Applied-Mediocrity said in WTF Bites:
I've never actually written C code (outside of college).
It's very simple.
Except for everything to do with pointers, which is incredibly subtle.
-
And undefined behavior hidden behind seemingly trivial stuff.
Fortunately, pointers and undefined behavior are rarely encountered in C programming
-
@HardwareGeek said in WTF Bites:
@Zerosquare said in WTF Bites:
not to reinvent the wheel
Now I know you're not a real programmer.
Just because I say "that's how it should be done" doesn't mean I actually do it
@boomzilla said in WTF Bites:
Node.js programmer, probably.
Hey! This is not the Garage!
-
Except for everything to do with pointers, which is incredibly subtle.
Oh, come on, you just cast everything to void*, and go home.
-
@TimeBandit said in WTF Bites:
I think it did it earlier.
I think it did 15 years ago
Yes. Office/Word 2003 works perfectly fine.
-
@sebastian-galczynski said in WTF Bites:
Except for everything to do with pointers, which is incredibly subtle.
Oh, come on, you just cast everything to void*, and go home.
I'd ask just how much of our codebase did you write… except our idiots prefer to cast everything to
uint16_t*
. Or, for a different (and slightly later) set of idiots,uint32_t*
. Because actually declaring and documenting the types that you're working with is hard, so let'sgo shoppingbake our own structure accesses with array accesses with hardcoded constants.
-
@sebastian-galczynski said in WTF Bites:
Except for everything to do with pointers, which is incredibly subtle.
Oh, come on, you just cast everything to void*, and go home.
I'd ask just how much of our codebase did you write… except our idiots prefer to cast everything to
uint16_t*
.That violates strict aliasing, doesn't it?
-
@Gąska If it wasn't a uint16_t from the beginning, then yes. Unfortunately quite common, and there's a fair amount of developers and libraries that advocate
-fno-strict-aliasing
and equivalents as a consequence.I have the impression that more people are starting to avoid that kind of code, but it'll be around for a while.
-
@sebastian-galczynski said in WTF Bites:
Except for everything to do with pointers, which is incredibly subtle.
Oh, come on, you just cast everything to void*, and go home.
I'd ask just how much of our codebase did you write… except our idiots prefer to cast everything to
uint16_t*
.That violates strict aliasing, doesn't it?
Not necessarily more so than
void*
, it depends on what you do with it. In other words, probably yes, but probably thevoid*
code would too. You can't do much with it except storing / casting back, andmemcpy
ing, which would be the correct thing to do. But I'd assume that after the cast to void usually comes another cast to something else that breaks aliasing anyway. (Not an unfair assumption given that the link goes to some enlightened programmers)
-
That violates strict aliasing, doesn't it?
The code in question is packing values into a buffer that's going to be sent out on the network (with a defined format) and the idiots who defined the protocol made it that in some configurations (not all, but actually the ones that come up most often in practice) you end up having to stuff 32-bit integers at half-word boundaries. And the real data structure describing this is very difficult to describe in C because most of the fields within it are not at fixed offsets. It's really miserable to work with.
I'm generally happy with manually putting
restrict
in where required. I know what's really going on after all. If only there was a good way to declare a type as being a "pointer to a 32-bit unsigned integer with half-word alignment” (so requiring the use of twostrh
instructions to read the value instead of a singlestr
), but there doesn't seem to be a way to do this with GCC (some versions of the ARM process. You have to do something ugly like this:void set_value(uint32_t *storage, uint32_t value) { if ((uint32_t) storage & 2) { uint16_t *ptr = (uint16_t *) storage; ptr[0] = value & 0xFFFF; ptr[1] = value >> 16; } else { // We are aligned after all, and should use the more efficient option storage[0] = value; } }
The result is quite efficient, but it's one heck of a lot more than a simple array write. Did I mention that I hate the jerkwads who decided to save a few bytes by squashing everything down very hard?
-
@dkf I feel much the same way about grognards ( types) who complain that "back in the day, we fit everything into <space> because we were so efficient...programmers these days." Sure, there are idiots who insist on shoving the whole ui down the pipe as javascript or who shove full resolution images just to reduce them in CSS, but at the other extreme, byte-shaving protocols like this are really painful to deal with and cause bugs everywhere.
-
@Benjamin-Hall When you're working well within the capabilities of the system, you can afford things like sending everything as JSON and so on. When you're pushing some small bit of hardware hard, you've got to be a lot more efficient. (That also applies when you're dealing with a big bit of hardware made out of lots of small bits of hardware; the overall system is gigantic, but the individual parts are tiny and you have to be very careful.)
-
@Benjamin-Hall When you're working well within the capabilities of the system, you can afford things like sending everything as JSON and so on. When you're pushing some small bit of hardware hard, you've got to be a lot more efficient. (That also applies when you're dealing with a big bit of hardware made out of lots of small bits of hardware; the overall system is gigantic, but the individual parts are tiny and you have to be very careful.)
True enough. Embedded is different. But isn't there better and worse ways of doing that?
-
@Benjamin-Hall said in WTF Bites:
But isn't there better and worse ways of doing that?
All the better ways involve not over-packing things or playing smartass games with glomming things together in ways that don't fit well with the C model.
-
@dkf if it saves one
lifebyte...
-
Every byte is sacred!
-
@sebastian-galczynski said in WTF Bites:
Except for everything to do with pointers, which is incredibly subtle.
Oh, come on, you just cast everything to void*, and go home.
I'd ask just how much of our codebase did you write… except our idiots prefer to cast everything to
uint16_t*
.That violates strict aliasing, doesn't it?
Not necessarily more so than
void*
IIRC
void
andchar
are explicitly exempt from the strict aliasing rule.
-
@Gąska You can't actually dereference a void* pointer, so there's no need to make it an exception. But, yes, char types and std::byte are exempt from strict aliasing.
-
@sebastian-galczynski said in WTF Bites:
Except for everything to do with pointers, which is incredibly subtle.
Oh, come on, you just cast everything to void*, and go home.
I'd ask just how much of our codebase did you write… except our idiots prefer to cast everything to
uint16_t*
.That violates strict aliasing, doesn't it?
Not necessarily more so than
void*
IIRC
void
andchar
are explicitly exempt from the strict aliasing rule.Yes. But as I said, you can’t really do much with it, so there’s a fair chance that they (the enlightenment morons) would break the aliasing rules afterwards anyway by casting it to some other wrong type.
-
@sebastian-galczynski said in WTF Bites:
Except for everything to do with pointers, which is incredibly subtle.
Oh, come on, you just cast everything to void*, and go home.
I'd ask just how much of our codebase did you write… except our idiots prefer to cast everything to
uint16_t*
. Or, for a different (and slightly later) set of idiots,uint32_t*
. Because actually declaring and documenting the types that you're working with is hard, so let'sgo shoppingbake our own structure accesses with array accesses with hardcoded constants.That sounds like my world. Because when you're dealing with hardware, everything is a 32-bit value, even when it's composed of 7 1-bit fields, a couple of 6-bit fields, a 9-bit field, and a few bits that aren't actually connected to anything. I've seen this done with
struct
s of bits,union
ed withuint32_t
s, but it's rare; usually it's just a bunch of#define
s for masking and shifting the values into the right bits.
-
@HardwareGeek said in WTF Bites:
Because when you're dealing with hardware, everything is a 32-bit value, even when it's composed of 7 1-bit fields, a couple of 6-bit fields, a 9-bit field, and a few bits
that aren't actually connected to anythingwhose only description is "Reserved.", with strong implications that just looking at them the wrong way could be sufficient to cause the hardware to self-combust.
-
Did I mention that I hate the jerkwads who decided to save a few bytes by squashing everything down very hard?
In my current project, I'm dealing with a thing that produces 24-bit data. In a previous version, the data was 16-bits, and could be stored one 16-bit value in a 32-bit word, or two 16-bit values packed in a single 32-bit word. There is a spec that defines all the possible ways you could pack 4 24-bit values into 3 32-bit words, but fortunately somebody sane said no. There is one and only one format we are going to support; each 24-bit value goes into the low 24 bits of a 32-bit word. Period.
-
@HardwareGeek You haven't truly lived until you've debugged a program that uses (variable length) pointer compression. Grab the lower 17 bits from that word, smash them together with 12 from the following, and run them through a function or two, and -presto- there's a (real) pointer.
-
@cvi I've definitely worked with hardware in which a pointer is too big to fit in a single word, many times. I know I've also worked with hardware in which a pointer is split into multiple fields in several words in not entirely sensible ways. The original design had, say, a 16-bit address in one register; a later version added features, including increasing the memory size from 64k to 1M, but the additional bits didn't fit in that register, so they, along with some control bits, got put in another register. Later, the memory size was increased again to, say, 8M, but those 3 additional address bits didn't get put with the others because there were already control bits there, so they got put in yet another register. But I don't remember the context in which I worked with something like that.
-
@HardwareGeek You haven't truly lived until you've debugged a program that uses (variable length) pointer compression. Grab the lower 17 bits from that word, smash them together with 12 from the following, and run them through a function or two, and -presto- there's a (real) pointer.
This one weird trick lets you compress a full pointer into 17 bits. Garbage Collectors hate him.
-
@HardwareGeek I'm having flashbacks to x86 segment addressing.
-
That violates strict aliasing, doesn't it?
Not necessarily more so than
void*
, it depends on what you do with it.There is no way to dereference a
void *
, so as long as you cast it back to compatible type for dereferencing, it is not violating strict aliasing. Given they cast specifically touint16_t *
, I suppose they are actually dereferencing it too.The code in question is packing values into a buffer that's going to be sent out on the network (with a defined format) and the idiots who defined the protocol made it that in some configurations (not all, but actually the ones that come up most often in practice) you end up having to stuff 32-bit integers at half-word boundaries.
The officially sanctioned approach is to use an
uint8_t
buffer, which is exempt from strict aliasing by virtue of actually being achar
variant (unsigned char
).void set_value(uint32_t *storage, uint32_t value) { if ((uint32_t) storage & 2) { uint16_t *ptr = (uint16_t *) storage; ptr[0] = value & 0xFFFF; ptr[1] = value >> 16; } else { // We are aligned after all, and should use the more efficient option storage[0] = value; } }
I don't see any advantage over using
uint8_t *storage
(exempt from strict aliasing) and possibly usingmemcpy
instead of shifts and masks (I'd usememcpy
if it's native-endian and shifts if it is fixed endian—as written the words are native-endian, but the order of the two words is fixed-endian, which just risks creating a mixed-endian garbage. Ok, almost everybody finally agreed on little but anyway).@Benjamin-Hall said in WTF Bites:
at the other extreme, byte-shaving protocols like this are really painful to deal with and cause bugs everywhere.
Well, 60 b/s downlinks are well and alive. Or such that are 1 Mb/s, but with due to number of messages that have to get through and the inability to use any proper access method (range about 1 mls, omnidirectional) are barely usable with the squeezed messages.
Anyway, there is a couple of generators like protobuf that are fairly tightly packed while preventing bugs by generating the bit-fiddling from easily maintainable declaration.
@HardwareGeek said in WTF Bites:
That sounds like my world. Because when you're dealing with hardware, everything is a 32-bit value, even when it's composed of 7 1-bit fields, a couple of 6-bit fields, a 9-bit field, and a few bits that aren't actually connected to anything. I've seen this done with
struct
s of bits,union
ed withuint32_t
s, but it's rare; usually it's just a bunch of#define
s for masking and shifting the values into the right bits.Strangely enough unions are not exempt from strict aliasing rules, so shifting and masking constants are actually the correct approach – as long as you either use only one base type (i.e. no switching between 16 and 32 bits) or only mix it with char (8-bit types).
-
I don't see any advantage over using
uint8_t *storage
(exempt from strict aliasing)It takes a more instructions if you do since when using
uint8_t*
there's no alignment assumed at all and each byte is written on its own. The code I posted instead uses half-word writes, which makes it approximately twice as fast. (It turns out thatmemcpy()
is suboptimal in this case, as that just switches to doing everything bytewise in this particular case, but only after a series of runtime checks. The code formemcpy()
is surprisingly long.)The annoying thing is that I can't tell the compiler the information that I know about the pointer. If I try to do that, it just ignores it and writes assembly out that will cause the target machine to keel over with a bus error. Yes, what I'm writing to memory is an ugly format. I can't help that without rewriting a lot more code elsewhere and doing a lot of testing (including in-depth performance analysis across a range of scenarios, so probably several months work given the way other bits and pieces interact).
risks creating a mixed-endian garbage
Not a problem. Target platform has defined endianness (it's a single revision of a single chip design; it really is a known target) and the network protocol itself has defined endianness. Even better, they're the same. (Only our boot protocol is pants-on-head retarded that way, and that's only because it was written by an idiot and burned into a real ROM.)
-
Strangely enough unions are not exempt from strict aliasing rules
I seem to recall unions are defined so weakly that
#define union struct
is legal.
-
Apparently these video cards are so old that they're both older than each other
-
Strangely enough unions are not exempt from strict aliasing rules
I seem to recall unions are defined so weakly that
#define union struct
is legal.Not quite. From page 103 of the C99 spec, these are the paragraphs that distinguish a
struct
and aunion
:-
Within a structure object, the non-bit-field members and the units in which bit-fields reside have addresses that increase in the order in which they are declared. A pointer to a structure object, suitably converted, points to its initial member (or if that member is a bit-field, then to the unit in which it resides), and vice versa. There may be unnamed padding within a structure object, but not at its beginning.
-
The size of a union is sufficient to contain the largest of its members. The value of at most one of the members can be stored in a union object at any time. A pointer to a union object, suitably converted, points to each of its members (or if a member is a bitfield, then to the unit in which it resides), and vice versa.
Note that for a
struct
we have pointer interconvertability between the struct and the initial member, and for aunion
we've got it for all members. (It doesn't make actually dereferencing those pointers necessarily legal.)
-
-
It doesn't make actually dereferencing those pointers necessarily legal.
I believe it makes them legal if and only if dereferencing the corresponding member is.
-
it was written by an idiot and burned into a real ROM
Letting idiots do that is not recommended.
-
@BernieTheBernie If he insists, the more c#y way would have been
public TimeSpan (long ticks)
as
private TimeSpan m_Timeout = new TimeSpan(8 * TimeSpan.TicksPerSecond);
-
Apparently these video cards are so old that they're both older than each other
Eh, 270X was in most aspects a 7870 rebrand (now with Boost!).
-
-
@MrL Not my fault, that was in the original.
-
If only there was a good way to declare a type as being a "pointer to a 32-bit unsigned integer with half-word alignment”
Surely TRWTF with that is that "storage[0] = value" doesn't do what you expect just because storage isn't aligned. If that's not a valid uint32* then it shouldn't be possible to use that value in the first place. (What does it do, by the way?)
-
The result is quite efficient
I dunno, I could probably make this more efficient.