It is clear to me that on the top level, I want to work with strings of "1"s and "0"s, and to use Perl's strengths on them - regexes, string-processing and such. What wasn't clear to me is how I was going to get this nice representation from a rudimentary file.
A short intermission: I'm not a great fan of OOP, in the sense that it's not a panacea, but rather a YADM (Yet Another Design Methodology). That's because I think it's too hyped. I don't believe thinking of everything as objects. I believe in "the right tool for the job". Where OO seems fit, it can be a great and clean solution. Where it doesn't fit, enforcing it on a design will only make it messy and unnatural.
So I started coding. A function would open the file, unpack() it to "00111100...."s and happily use it. But I quickly understood that this reading and unpacking should be abstracted away in another function. I also understood that what I really need is a "stream" abstraction - just ask for N bits, get them and work on them, ask for the next M bits, et cetera. But this clearly starts to separate into two tasks: opening the file, giving away bits. So I tried two subs with shared static data. Frankly, Perl sucks static-variable-wise. They're not directly supported, and must be hacked-over with my() in a BEGIN block. It didn't feel right.
This brought me to consider a package. Vars static to packages are supported nicely, and Perl is very good with packages, so why not... So, I happily tucked the code into a package, and just called BitStream::create($filename) and BitStream::get_bits(128).
Hey, but I might need more than one stream simultaneously... ahh... objects. Why not treat BitStream as an object, which stores all the info it needs in a %self hash, and does what I need ? In fact, this was very simple. I must admit that Perl implements OO nicely, at least when simple things are done. So...
use BitStream; ... ... my $stream = BitStream::new("bigbinfile"); ... my $bitstr = BitStream::get_bits(128);
---------------- UPDATE ----------------
The code posted above is incorrect (copy paste from an old file). It should be -
use BitStream; ... ... my $stream = BitStream::new("bigbinfile"); ... my $bitstr = $stream->get_bits(128);
get_bits() now nicely handles incomplete reads, returning just as much as there was left in the stream.
So, it now looks like this:
-------------
get_bits() | |
script <================ | BitStream | <== file
| |
-------------
Simple, works, clean, b-e-a-u-t-i-f-u-l (forgive my geeky self).
But the best part is ahead:
If you're read about my binary woes, you know there's also a memory problem. Files can get very big (100s of Megs), so a clever implementation must be thought of. But, the caller of get_bits() doesn't care ! He gets his bits, whatever BitStream does under the hood.
As a matter of fact, there are 3 implementations I consider now:
At the moment, the first solution is employed, as it's the simplest. But whatever solution is chosen, the caller just calls get_bits() !
I will add features to BitStream on a need-basis (being the user of yourself rocks). Features like seek()ing and tell()ing a stream, rewind(), reading backwards, etc.
But the moral if the story, to relate to the article title, is: use the best tool for the job, employ techniques on a need-basis. Then, you'll have the most elegant solution for each problem possibly with different design methodologies, but what does it matter, as long as it "feels" clean and robust.
|
|---|