Did you ever saw a char and thought: “Damn, 1 byte for a single char is pretty darn inefficient”? No? Well I did. So what I decided to do instead is to pack 5 chars, convert each char to a 2 digit integer and then concat those 5 2 digit ints together into one big unsigned int and boom, I saved 5 chars using only 4 instead of 5 bytes. The reason this works is, because one unsigned int is a ten digit long number and so I can save one char using 2 digits. In theory you could save 32 different chars using this technique (the first two digits of an unsigned int are 42 and if you dont want to account for a possible 0 in the beginning you end up with 32 chars). If you would decide to use all 10 digits you could save exactly 3 chars. Why should anyone do that? Idk. Is it way to much work to be useful? Yes. Was it funny? Yes.
Anyone whos interested in the code: Heres how I did it in C: https://pastebin.com/hDeHijX6
Yes I know, the code is probably bad, but I do not care. It was just a funny useless idea I had.
After all… Why not?
Why shouldn’t I ignore the 100+ cultures whose character set couldn’t fit into this encoding?
Not 100% relevant but it was in my collection and I thought it was close enough to be funny. :D
ŚĆŻRŹĘĄMŚ
So did ASCII
Yes, which is why we’ve broadly accepted that ASCII isn’t sufficient any more.
They left one bit for the other cultures use.
Åååååå!
unsigned int turn_char_to_int(char pChar) { switch(pChar) { case 'a': return 10; case 'b': return 11; case 'c': return 12; case 'd': return 13; case 'e': return 14; case 'f': return 15; case 'g': return 16; case 'h': return 17; case 'i': return 18; case 'j': return 19; case 'k': return 20; case 'l': return 21; case 'm': return 22; case 'n': return 23; case 'o': return 24; case 'p': return 25; case 'q': return 26; case 'r': return 27; case 's': return 28; case 't': return 29; case 'u': return 30; case 'v': return 31; case 'w': return 32; case 'x': return 33; case 'y': return 34; case 'z': return 35; case ' ': return 36; case '.': return 37; } }
Are you a monster or just stupid?
Just stupid
If you couldn’t write
if(pChar >= 'a' && pChar <= 'z') return pChar - ('a' - 10);
I suppose you typed this “all the size of a lookup table with none of the speed” abomination manually too.
switch case structures are very efficient in c and c++. They work similarly like an offset into memory. Compute the offset once (any expression in the ‘case’ lines), then jump. Using primitives directly, like here with chars, is directly the offset. Contrary to if-else branches, where each case must be evaluated first and the CPU has basically no-op cycles in the pipeline until the result of the branch is known. If it fails, it proceeds with the next one, waits again etc… (Some CPU architectures might have stuff like speculative branch execution, which can speed this up.)
However, code-style wise this is really not elegant and something like your proposal or similar would be much better.
Oh, I didn’t know that they were a LUT of jump addresses. Stil, a LUT of values would be more space-efficient and likely faster. Also, what if the values are big and sparse, e.g.
switch (banknoteValue) { case 5000: check_uv(); check_holograph(); case 2000: check_stripe(); case 1000: check_watermark(); }
…does the compiler make it into an if-else-like machine code instead?
Yes, I did type it out manually (not really, I just copy pasted it and changed the according values)
CPU still pulls a 32kb block from RAM…
Lol, using RAM like last century. We have enough L3 cache for a full linux desktop in cache. Git gud and don’t miss it (/s).
(As an aside, now I want to see a version of puppylinux running entirely in L3 cache)
I decided to take a look and my current CPU has the same L1 as my high school computer had total RAM. And the L3 is the same as the total for the machine I built in college. It should be possible to run a great desktop environment entirely in L3.
Look at this guy with their fancy RAM caches.
Cache man, its a fun thing.
32k32 (derp, 32 not 32k) is a common cache line size. Some compilers realise that your data might be hit often and aligns it to a cache line start to make its access fast and easy. So yes, it might allocate more memory than it should need, but then its to align the data to something like a cache line.
There is also a hardware reasons that might also be the case. I know the wii’s main processor communicates with the co processor over memory locations that should be 32k aligned because of access speed, not only because of cache. Sometimes, more is less :')Hell, might even be a cause of instruction speed that loading and handling 32k of data might be faster than a single byte :').
Then there is also the minimum heap allocation size that might factor in. Though a 32k minimum memory block seems… Excessive xD
Cache Man, I would watch that movie.
Cache lines are 64 bytes though? Pages are 4k.
Ye derp, im used to 32, not 32k lol.
Oh god, please don’t. Just use utf8mb4 like a normal human being, and let the encoding issues finally die out (when microsoft kills code pages). If space is of consideration, just use compression, like gz or something.
Bro reinventing sixbits
Did not knew that this existed, but yeah its kinda like that. Except that I only allow 5 characters.
Given you are only a-z with space and dot; you could do 5bit and pack 6 characters per 32bit word with space to spare
25 bits.
Have you heard of Proquints?
https://www.ietf.org/archive/id/draft-rayner-proquint-03.html
Funny how they have a typo in test vectors:
0x0000 -> babab
0xFFFF -> zvzuz
0x1234 -> damuh
0xF00D -> zabat
0xBEEF -> ruroz
This is hilarious. I’m not sure how often anyone would actually need to verbalize arbitrary binary data, but I do see an advantage over base64 since the English letter names are so often phonetically similar.
The FAA/ICAO use a similar system to name aerial navigation and fixed GPS waypoints. It addresses the challenge of communicating identifiers of unique nodes in a vast network using VHF communications.
I did something like this once, in the course of a project whose purpose I don’t remember. Realising that 8-bit ASCII was wasted on the constrained alphabet of some kind of identifiers, I packed them into 6 bits, 1⅓ to a byte. I recall naming the code to do this “ShortASC”, pronounced “short-ass”
The AD&D “Gold Box” games from SSI Inc. stored game text in 6-bit encoding. The first one of these I played was “Champions of Krynn” and the PC release came on 4 360k 5.25 dsdd floppy disks. They actually needed the packing in those days, and couldn’t afford to spent cpu cycles or ram on built in compression.
I remember opening up the game data files in a file viewer (maybe pc-tools?) and being confounded by the lack of text in the files.
I’m not sure if this is the right setting for technical discussion, but as a relative elder of computing I’d like to answer the question in the image earnestly. There’s a few factors squeezing the practicality out of this for almost all applications: processor architectures (like all of them these days) make operating on packed characters take more operations than 8 bit characters so there’s a speed tradeoff (especially considering cache and pipelining). Computers these days are built to handle extremely memory demanding video and 3d workloads and memory usage of text data is basically a blip in comparison. When it comes to actual storage and not in-memory representation, compression algorithms typically perform better than just packing each character into fewer bits. You’d need to be in a pretty specific niche for this technique to come in handy again, for better or for worse
I liked the technical discussion so thank you. Keep it up, I got into this career because there was always so much to learn.
This is 100% true. I never plan on actually using this. It might be useful for working on microcontrollers like an ESP32, but apart from that the trade of for more computational power is not worth the memory savings.
Having seen many of Kaze’s videos on N64 development, I’ve learned that the N64 has like 4x the processing power it needs compared to its memory. In hardware cases like that the trade-off of computational power and memory memory savings gets you some nice performance gains.
There is still fun to be had! Just… Different fun!
In database land lookup tables are pretty common. Prefix tries and the like are super common in search land. I’ve seen GCD, offset, delta-of-delta, and some funky bitwise floating point compression used. Sometimes just to save dist space. But usually to save working set space or IO or S3 cache space.
And squeezing the most out of modern CPUs is its own art. Compilers are glorious. And modern CPUs are magic lightning rocks. But you can learn to sing to them just right to make them all happy.
Not useless – you have a future in tiny, embedded systems.
I do this kind of thing everyday as a firmware engineer :)
What do u write the firmware for?
You would have done well with this kind of thinking in the mid-80s when you needed to fit code and data into maybe 16k!
As long as you were happy to rewrite it in Z80 or 6502.
Another alternative is arithmetic encoding. For instance, if you only needed to store A-Z and space, you code those as 0-26, then multiply each char by 1, 27, 27^2, 26^3 etc, the add them.
To unpack them, divide by 27 repeatedly, the remainder each time is each character. It’s simply covering numbers to base-27.
It wouldn’t make much difference from using 5 bits per char for a short run, though, but could be efficient for longer strings, or if encoding a smaller set of characters.
It sucks for me because my brain innately enjoys this kind of thinking
Play the Zachtronics programming games (Exapunks, Shenzen I/O, TIS-100) if you haven’t yet.
Real kings store 32 booleans as a single int
That’s where
std::vector<bool>
or bitfields come in handy!An enum and a logical “or” operator.
👑
Interesting idea but type conversion and parsing is much more slower than wasting 1 byte. Nowadays memory is “free” and the main issue is the execution speed.
Fuck it. *uses
ulong
to store a boolean*So, python?
I know. This whole thing was never meant to be very useful, and more like a proof of concept
Alignment wastes much more anyways
now use the intchar to store prefixes for a smart string , or pack them to make a simd optimized b tree with 40 string prefixes per node instead of 32
That’s bootleg gzip.
Back in the day those tricks were common. Some PDP-11 OS’s supported a “Radix-50” encoding (50 octal = 40 decimal) that packed 3 characters into a 16 bit word (40 codepoints=26 letters, 10 digits, and a few punctuation). So you could have a 6.3 filename in 3 words.
Oh god that switch statement. Here, let me come up with something better:
if (pChar >= 'a' && pChar <= 'z') { return pChar - 'a' + 10; } else if (pChar == ' ') { return 36; } else if (pChar == '.'){ return 37; } return 0;
First rule of code review, do not sound judgemental.
Ah, thats cool. Did not knew you could that… Thanks.
The original switch statement is great as it is. Easy to understand at a quick glance.
Yes, it’s easy to understand, but this ifelse is much safer because it “handles” uppercase and digits by just returning 0. If you’d call OP’s function with something like ‘A’, you’ll get nonsense data because it doesn’t have a default. (I think it will return whatever value currently resides in eax)
Can easily be dealt with by adding a default case. You also figured out this undefined behavior because you easily understood the switch statement.
Fair.
A single line comment would make it as easy to understand, and much more flexible if you wanted to add handling upper case letters or digits. Or even remap the values to a more standard 6 bit encoding.