WTF Bites



  • @Zecc said in WTF Bites:

    @Gribnit said in WTF Bites:

    Ah, familiarity with a highly repetitive children's program. Congratulations, I bow to you.

    More like familiarity with an oft-cited meme.

    Do you know how many times this meme has been used by nerds everywhere?

    No, but enough times that Wikipedia thinks it's notable enough to have its own Wikipedia page.



  • @Gribnit said in WTF Bites:

    You've multiplied it... by ten? What do you call this novel operation?

    Mathematics


  • ♿ (Parody)

    @Gąska said in WTF Bites:

    @Zerosquare well, yes, but as a package manager/repository, what are you going to do? Ban users who upload too much like some kind of American ISP?

    Yeah, except this user is Amazon, so it would be slitting their own throats to ban Amazon from the repository.


  • :belt_onion:


  • Considered Harmful

    @Zecc said in WTF Bites:

    @Gribnit said in WTF Bites:

    Ah, familiarity with a highly repetitive children's program. Congratulations, I bow to you.

    More like familiarity with an oft-cited meme.

    Do you know how many times this meme has been used by nerds everywhere?

    Jesus fuck, you want to use it again so soon? Yes. I heard DragonBall morons talk in the lab I was an aide at. They weren't very bright. Still aren't. People can use a computer for all sorts of stupid shit.

    Weirdly, as I take higher and higher paying positions, hadn't heard it in years. Maybe snuffling in garbage has drawbacks?


  • Discourse touched me in a no-no place

    @Gąska said in WTF Bites:

    It seems like two libraries used by the project have a common dependency but the latest releases have incompatible versions. The package manager tries to find a combination of releases of each of the conflicting libraries such that the two aren't in conflict anymore.

    They could also do the traditional approach: throw it back in the user's face and let them fix it by pinning versions.



  • @cvi said in WTF Bites:

    Fuck the guy who rode in on his signed horse and decreed that the message ID should be a singed 32-bit integer. Nobody's going to do arithmetic on the bloody message IDs - why the hell would make them signed?

    There are a couple of places in our code where an ID is made signed so that we can use -1 for "no ID set" (nullptr for ints, in a way) (we can't use 0 for that for... :raisins:).

    I'm not saying it's a good design, but just that the developer might have had something in mind when they chose to make it signed rather than unsigned.



  • @remi said in WTF Bites:

    There are a couple of places in our code where an ID is made signed so that we can use -1 for "no ID set"

    After a lot of deliberation I concluded that implicitly converting -1 to whatever unsigned type is the right thing to do and turned off the warning.

    Except it's a bit more problem now that implicit conversions were prohibited in initializer lists. Thinking about it now, you could create a

    struct no_id_t {
        template<typename T>
        constexpr std::enable_if<std::is_unsigned<T>, T> operator T() const {
            return std::numeric_limits<T>::max();
        }
    };
    static const no_id_t no_id;
    

    (off the top of my head, not tested for anything). std::nullptr_t works basically the same way, except they made nullptr a keyword and you are stuck with a const for your own analogs.



  • @Bulb said in WTF Bites:

    @remi said in WTF Bites:

    There are a couple of places in our code where an ID is made signed so that we can use -1 for "no ID set"

    After a lot of deliberation I concluded that implicitly converting -1 to whatever unsigned type is the right thing to do and turned off the warning.

    Yes, we also do that in some cases, but not always, in part because it triggers various warnings (and rightly so!), unless you pepper the code with casts, which is equally ugly. It's not a huge issue, but the problem is that when writing code it's hard to know if there will, or not, be an implicit conversion with or without a warning, which means you have to wait for what the compiler tells you or resort to various cargo-cults for syntaxes in different contexts, and frankly, that's an awful code smell.

    Except it's a bit more problem now that implicit conversions were prohibited in initializer lists.

    I guess you still can do an explicit cast there? Not that it's nice, but if it's just in a couple of places that might be the easiest way...

    Thinking about it now, you could create a [no_id_t]

    In a sense, if you get bothered-enough by this thing to write a specific class to handle it, you might want to reconsider the whole design in the first place. At least, that's what I would do. Of course, real life being real life, I might still end up with this as a stop-gap measure.

    All in all, I'm OK for having signed IDs in internal classes where the ID shouldn't really be exposed much, if it makes life easier, but I probably would hesitate a lot more to do it in more public classes, unless there is a very good reason for it (and a more complete and polished design to handle it). Obviously the definition of "more public" and "internal classes" and "not exposed much" are very vague (insert pirates' meme about rules and guidelines).

    Which of course is not what's in our code base (there is at least one widely used signed ID that I can think of right now), but that's real life for you... :mlp_shrug:



  • @remi said in WTF Bites:

    Yes, we also do that in some cases, but not always, in part because it triggers various warnings (and rightly so!), unless you pepper the code with casts, which is equally ugly.

    No, wrongly so!. Any warnings that it produces should be turned off with extreme prejudice. Because

    @remi said in WTF Bites:

    unless you pepper the code with casts, which is equally ugly.

    You should not do that, because that, unlike the original code, is actually error-prone. Because -1 converts to the target type with all bits set reliably, but you can easily break that property with an incorrect cast and at that point won't get any warning at all.

    @remi said in WTF Bites:

    I guess you still can do an explicit cast there? Not that it's nice, but if it's just in a couple of places that might be the easiest way...

    Again, the problem is that the explicit cast is worse here than the implicit one. The reason is that implicit cast does size-extension first and signed-to-unsigned second, so you reliably end up with the appropriate maximal value for all types. But once you start adding casts, you can easily make the mistake of writing (in effect, with the two types spread far apart, of course) with uint64_t x = (unsigned)-1 and that's different… and no warning at all.

    Also note that the standard library does use -1 cast to size_t rather extensively.


  • BINNED

    All this unsigned bullshit was a mistake, change my mind.

    @Bulb said in WTF Bites:

    Also note that the standard library does use -1 cast to size_t rather extensively.

    Just shows what a horrible kludge it is. If you want signed numbers so you can signal a "not an id", use signed numbers.

    I hate all the bullshit casts that spread everywhere once you have some unsigned numbers.



  • @topspin said in WTF Bites:

    Just shows what a horrible kludge it is. If you want signed numbers so you can signal a "not an id", use signed numbers.

    FWIW, I think casting -1 to an unsigned to get a value with all bits set is damn ugly, regardless of the prevalence of doing so. I tend to prefer something like ~0u or ~size_t(0) instead. I can also get behind using std::numeric_limits or some custom stuff like by @Bulb above.

    All this unsigned bullshit was a mistake, change my mind.
    ...
    I hate all the bullshit casts that spread everywhere once you have some unsigned numbers.

    I have that in reverse. A good portion of the casts I write are because some clown decided to use signed integers for a count/size or for indices (none of which can be negative).

    The other problem is that the signed integer code tends to end up using int or long, both of which are terrible choices by default (one being 32 bits unconditionally on common platforms, and the other being 32 bits on Windows). Few enough use something like ptrdiff_t, and ssize_t isn't standard (IIRC).


  • Discourse touched me in a no-no place

    @cvi said in WTF Bites:

    ssize_t isn't standard (IIRC).

    It's defined by POSIX.



  • @dkf said in WTF Bites:

    @cvi said in WTF Bites:

    ssize_t isn't standard (IIRC).

    It's defined by POSIX.

    Yeah, true. Should have specified that it isn't in the C/C++ standard (library) as far as I know. Consequence is that under Windows it largely isn't available (Microsoft's POSIX stuff don't use it - they prefer using int or long directly, and using __int64 in some cases directly). I think the common workaround is to define it yourself (from e.g. SSIZE_T).


  • BINNED

    @cvi said in WTF Bites:

    I have that in reverse. A good portion of the casts I write are because some clown decided to use signed integers for a count/size or for indices (none of which can be negative).

    The only use case for unsigned is if you explicitly operate in a modulus ring. That’s a tiny fraction compared to all the times you operate with normal integers.
    That something “can’t be negative” is IMO not a good reason to use unsigned because it infects everything further down the line which may or may not be negative. size_t being unsigned also was a mistake.



  • @cvi said in WTF Bites:

    @topspin said in WTF Bites:

    Just shows what a horrible kludge it is. If you want signed numbers so you can signal a "not an id", use signed numbers.

    FWIW, I think casting -1 to an unsigned to get a value with all bits set is damn ugly, regardless of the prevalence of doing so.

    TBF there is a constant npos in all the classes that use it.

    I tend to prefer something like ~0u

    Watch out, the u is a footgun! See, size_t is, depending on platform, either unsigned long or unsigned long long, and on many platforms, unsigned is shorter than that. In those cases ~0 will, implicitly but with a warning, convert the -1 first to -1 of the sizeof(size_t) size, and then convert to unsigned using the mod 2sizeof logic, reliably reaching the intended result.

    But ~0u is already unsigned, so it will just extend to sizeof(size_t), padding with zeroes from the left. OOPS!



  • @topspin said in WTF Bites:

    The only use case for unsigned is if you explicitly operate in a modulus ring.

    And when you are indexing. Unsigned indices are easier, because you only have to test them for being in range one way. Except for the need to have a way to write the max value for explicit no-such-index cases.

    @topspin said in WTF Bites:

    size_t being unsigned also was a mistake.

    No, because of indexing.



  • @Bulb Oh, yeah. Should have clarified -- ~0u is for unsigned and ~size_t(0) is for size_t. (I will admit that I do get a bit sloppy when it comes to uint32_t and will end up using ~0u there occasionally.)


  • BINNED

    @Bulb said in WTF Bites:

    @topspin said in WTF Bites:

    The only use case for unsigned is if you explicitly operate in a modulus ring.

    And when you are indexing. Unsigned indices are easier, because you only have to test them for being in range one way. Except for the need to have a way to write the max value for explicit no-such-index cases.

    @topspin said in WTF Bites:

    size_t being unsigned also was a mistake.

    No, because of indexing.

    And all the discussion above about casting and foot guns is caused by it.



  • @topspin said in WTF Bites:

    The only use case for unsigned is if you explicitly operate in a modulus ring.

    To be fair, C/C++ made a mistake with tying the "overflow behaviour" to signedness. Those are entirely different things. (Rust makes them a separate thing, except it seemingly does so in a quite painful way.)

    operate with normal integers.

    Except we never do that. All of the types have limited ranges, so they're never "normal integers". Same with floats. Those aren't real numbers either. Pretending otherwise might be OK for a bit, but will bring pain eventually.



  • @cvi said in WTF Bites:

    To be fair, C/C++ made a mistake with tying the "overflow behaviour" to signedness.

    It's part of the greater mistake of creating a bunch of implementation-defined types with fairly lose constraint and thinking it'll make things portable. It turned out to only make them more error-prone, which is why eventually stdint.h was added.


  • Discourse touched me in a no-no place

    @cvi said in WTF Bites:

    All of the types have limited ranges, so they're never "normal integers".

    Well maybe in your language they do! (Some languages don't have the limitation, but it greatly increases the complexity of the implementation.)


  • BINNED

    @cvi said in WTF Bites:

    @topspin said in WTF Bites:

    operate with normal integers.

    Except we never do that. All of the types have limited ranges, so they're never "normal integers". Same with floats. Those aren't real numbers either. Pretending otherwise might be OK for a bit, but will bring pain eventually.

    Semantically, they are. Overflow is a bug, except when you're working in a modulo field.



  • @dkf said in WTF Bites:

    Well maybe in your language they do! (Some languages don't have the limitation, but it greatly increases the complexity of the implementation.)

    I believe they do in all languages. A potentially 2^64 byte long integer type is still a type with a limited range, and chances are that most "arbitrary" length integer implementations won't go beyond that. (Most won't even get close to that, especially the ones that store their length internally with a signed int64.) :pendant:



  • Anyway, at the time being I am dabbling into some Java.

    I was thrown over a project and asked to add something to it. It has a core part and some customer-specific additions, and the customer I am doing this for didn't get a new release in some four years. So I pull the master branch and try to test it. There is a repo with some example data, so I copy them over to my workspace and try to run it. After some updating to the test process I get the generic build to run, but the customer specific one throws me a

    435  [main] ERROR com.company.project.Bflmpsvz  - Conversion failed with unexpected Exception 
    java.lang.NullPointerException
            at org.apache.xmlbeans.impl.schema.SchemaTypeImpl.isAssignableFrom(SchemaTypeImpl.java:2274)
            at org.apache.xmlbeans.impl.validator.Validator.beginEvent(Validator.java:466)
            at org.apache.xmlbeans.impl.validator.Validator.nextEvent(Validator.java:241)
            at org.apache.xmlbeans.impl.store.Validate.emitEvent(Validate.java:172)
            at org.apache.xmlbeans.impl.store.Validate.process(Validate.java:79)
            at org.apache.xmlbeans.impl.store.Validate.<init>(Validate.java:39)
            at org.apache.xmlbeans.impl.store.Xobj.validate(Xobj.java:1878)
            at org.apache.xmlbeans.impl.values.XmlObjectBase.validate(XmlObjectBase.java:386)
            at com.company.project.BflmpsvzConverter.validateXml(BflmpsvzConverter.java:1515)
            …
    

    Of course BflmpsvzConverter#validateXml just passes down a XML object (that isn't null, that would throw up earlier) and some fairly trivial options object that it constructs. Oh, and this code is in the common part, so it has no good excuse for causing a crash in the customer-specific build only.

    So I do a 🦆🦆🐐 search and come across this:

    Bug was created, bug was fixed four years later, no word of explanation, no description when it happens or whether there is any workaround, no nothing. So is it the cause? And yes, the project builds against xmlbeans 2.6.0.



  • @topspin said in WTF Bites:

    Overflow is a bug

    Yes, exactly. This is what constrains you to a fixed range, which is a subset of the set of integers. We don't get normal integers, because our types only represent a subset of that. So, instead of doing integer arithmetic freely, you have to be careful to not ever get into a place where you might overflow. (The alternative is doing modular integer arithmetic "freely", which to me just seems like the simpler option by default.)

    A lot of people end up pretending that the non-modular integer types aren't constrained in range. This is especially problematic in cases where 32-bit integer types are being used in a 64-bit environment.

    (This gets back to what @Bulb said above. The constraints that C/C++ gives for its "default" types are not very good, which is why we end up with the default "int" being 32 bits instead of 64. "int" being the first integer type that we typically show people as well as being the shortest one to type makes for a very bad combo.)



  • @cvi said in WTF Bites:

    end up with the default "int" being 32 bits instead of 64

    I still remember the default "int" being 16 bits, and 64 bits being a pipenight dream.


  • Discourse touched me in a no-no place

    @cvi said in WTF Bites:

    An 264 byte long integer type is still a type with a limited range

    You'll run out of storage before the measuring of the size of the representation becomes a problem.


  • BINNED

    @cvi said in WTF Bites:

    So, instead of doing integer arithmetic freely, you have to be careful to not ever get into a place where you might overflow. (The alternative is doing modular integer arithmetic "freely", which to me just seems like the simpler option by default.)

    If you're semantically not dealing with modular arithmetic, then wrapping around in an unsigned type may be defined instead of being undefined, but it's still a bug. You're just masking it.


  • Discourse touched me in a no-no place

    @topspin If you're allowing sizes to be negative, then tell me how to go about allocating an array that can contain -78 elements. And what it means too.


  • BINNED

    @dkf said in WTF Bites:

    @topspin If you're allowing sizes to be negative, then tell me how to go about allocating an array that can contain -78 elements. And what it means too.

    What does it mean to allocate size_t(-78) elements?
    The former would just cause an error, because you can trivially check that, the latter would OOM crash, and yet you don't encode that constraint into the type system.

    Different question: how do you figure out how many elements an array of size x is larger than an array of size y? Well, you compute the difference, but then you have to cast stuff again because computing that modulo 2^64 doesn't make sense (and neither do comparisons, strictly speaking).

    Sizes are something you can do arithmetic with. Thus sizes should use a type that supports arithmetic.


  • Considered Harmful

    @hungrier said in WTF Bites:

    100 or 200 times. Maybe even more

    ITYM up to 200 times or more

    @error_bot xkcd advertising math


  • 🔀


  • Considered Harmful

    @Bulb said in WTF Bites:

    xmlbeans

    Those were dark times.


  • Notification Spam Recipient

    @remi said in WTF Bites:

    Which of course is not what's in our code base (there is at least one widely used signed ID that I can think of right now), but that's real life for you... :mlp_shrug:

    Just be glad your ID is not actually a 255 character string holding a string representation of a guid...


  • Considered Harmful

    @Tsaukpaetra said in WTF Bites:

    255 character string holding a string representation of a guid

    NATO phonetics?



  • @Gribnit said in WTF Bites:

    @Tsaukpaetra said in WTF Bites:

    255 character string holding a string representation of a guid

    NATO phonetics?

    Maybe if it was all C's or F's


  • Considered Harmful

    @hungrier said in WTF Bites:

    @Gribnit said in WTF Bites:

    @Tsaukpaetra said in WTF Bites:

    255 character string holding a string representation of a guid

    NATO phonetics?

    Maybe if it was all C's or F's

    Well it can't be and be a valid GUID but, you'd want to allow for max, yeah.

    Bitwise, CSV, maybe?




  • Considered Harmful

    @TimeBandit At least it was all asterisks.


  • BINNED

    @Gribnit said in WTF Bites:

    @TimeBandit At least it was all asterisks.

    What I heard was hunter2.



  • @topspin said in WTF Bites:

    What I heard was *******.

    That's what @Gribnit just said, no?


  • Considered Harmful

    @cvi said in WTF Bites:

    @topspin said in WTF Bites:

    What I heard was *******.

    That's what @Gribnit just said, no?

    Such a sad case (notes clipboard)


  • Considered Harmful

    @dkf said in WTF Bites:

    @cvi said in WTF Bites:

    An 264 byte long integer type is still a type with a limited range

    You'll run out of storage before the measuring of the size of the representation becomes a problem.

    Compared to the range of Proper Integers™, your limited-by-memory int isn't really any bigger than other people's limited-by-CPU-registers int :pendant: :tro-pop:


  • Considered Harmful

    @topspin said in WTF Bites:

    @dkf said in WTF Bites:

    @topspin If you're allowing sizes to be negative, then tell me how to go about allocating an array that can contain -78 elements. And what it means too.

    What does it mean to allocate size_t(-78) elements?
    The former would just cause an error, because you can trivially check that, the latter would OOM crash, and yet you don't encode that constraint into the type system.

    It only crashes if you ignore the error that would result from the former, too, and that can happen for any size allocation.



  • Yesterday the build server got finally fixed, so I scheduled the two builds I needed. The first passed. The second failed with

    [ERROR] Failed to execute goal com.company.maven:robust-jarsigner:1.4.0-SNAPSHOT:sign (PROJECT Security certificate) on project com.company.project.bflmpsvz.whatever.xmlbeans: Failed executing 'cmd.exe /X /C "D:\Apps\jdk1.8.0_172\jre\..\bin\jarsigner.exe -storetype Windows-ROOT -sigalg SHA1withRSA -digestalg SHA1 -strict -J-Dhttp.proxyHost=the.build.server.company.com -J-Dhttp.proxyPort=3128 -sigfile PROJECT -tsa http://tsa.starfieldtech.com D:\Jenkins\workspace\nch_feature_whatever\customer\Component\Bflmpsvz-XMLBeans-Whatever\target\com.company.project.bflmpsvz.whatever.xmlbeans-2.0.0-SNAPSHOT.jar "PROJECT Core Code Signing""' - exitcode 1 -> [Help 1]
    

    Ok, but why? Well, lets go a bit higher in the log (this is after the final summary). That message is not repeated there, but we have:

    [INFO] --- robust-jarsigner:1.4.0-SNAPSHOT:sign (PROJECT Security certificate) @ com.company.project.bflmpsvz.whatever.xmlbeans ---
    [INFO] Using timestamping server http://sha256timestamp.ws.symantec.com/sha256/timestamp
    [INFO] jarsigner: unable to sign jar: java.io.IOException: Server returned HTTP response code: 503 for URL: http://sha256timestamp.ws.symantec.com/sha256/timestamp
    [ERROR] Picked up JAVA_TOOL_OPTIONS: -Dmaven.ext.class.path="D:\Jenkins\workspace\nch_feature_whatever@tmp\withMaven8f93d76e\pipeline-maven-spy.jar" -Dorg.jenkinsci.plugins.pipeline.maven.reportsFolder="D:\Jenkins\workspace\nch_feature_whatever@tmp\withMaven8f93d76e" 
    [INFO] Using timestamping server http://tsa.starfieldtech.com
    [INFO] jarsigner: unable to sign jar: java.io.FileNotFoundException: http://tsa.starfieldtech.com
    [ERROR] Picked up JAVA_TOOL_OPTIONS: -Dmaven.ext.class.path="D:\Jenkins\workspace\nch_feature_whatever@tmp\withMaven8f93d76e\pipeline-maven-spy.jar" -Dorg.jenkinsci.plugins.pipeline.maven.reportsFolder="D:\Jenkins\workspace\nch_feature_whatever@tmp\withMaven8f93d76e" 
    

    :wtf:1: Notice the severity. The error message (unable to sign jar: java.io…) is INFO, followed by something on ERROR level that is actually just a note, maybe a warning, because the options clearly don't break anything.
    :wtf:2: The error at the end is not repeated from the build log, it is a different one, which makes it harder to go back and look up the context.
    :wtf:3 It wasn't the first jar to be signed. Two or three succeeded before this.


  • Notification Spam Recipient

    @Bulb said in WTF Bites:

    :wtf:3 It wasn't the first jar to be signed. Two or three succeeded before this.

    503... Possibly there was a dead server in the load balancer rotation?


  • BINNED

    @LaoC said in WTF Bites:

    @topspin said in WTF Bites:

    @dkf said in WTF Bites:

    @topspin If you're allowing sizes to be negative, then tell me how to go about allocating an array that can contain -78 elements. And what it means too.

    What does it mean to allocate size_t(-78) elements?
    The former would just cause an error, because you can trivially check that, the latter would OOM crash, and yet you don't encode that constraint into the type system.

    It only crashes if you ignore the error that would result from the former, too, and that can happen for any size allocation.

    But you don't actually get an error. You only get an error if Linux decides to be nice to you, otherwise it tells you "sure, have all the memory you want. I'll kill you if you touch it though."

    Anyways, it doesn’t matter. Checking for negative size is trivial.



  • @Tsaukpaetra said in WTF Bites:

    @Bulb said in WTF Bites:

    :wtf:3 It wasn't the first jar to be signed. Two or three succeeded before this.

    503... Possibly there was a dead server in the load balancer rotation?

    Who nose? It's somewhere out there on the wild internet. http://sha256timestamp.ws.symantec.com/sha256/timestamp.

    The backup (http://tsa.starfieldtech.com) also does not exist anymore apparently.



  • @topspin said in WTF Bites:

    Anyways, it doesn’t matter. Checking for negative size is trivial.

    Checking one side (unsigned) is less work than checking both sides (signed) and since range checking is a very common operation, it does actually make a difference.


Log in to reply