Power Trip |
It was a hot, cloudless summer day outside the headquarters of SmallTown SoftCorp. That didnt matter much to Neil though, as he basked in the frosty air conditioning of the companys modest, self-owned building. During the buildingsconstruction, Neil oversaw the installation of everything from the demarc to the HVAC system. This made him feel like he had a hand in the arctic clime of the office.
Those nice cool thoughts were torched when the power in the whole office suddenly went out. A chorus of profanity could be heard as all of the developers who hadnt saved their work in a while lost everything. People began to stumble out of the windowless meeting rooms that had become blackened employee traps. What the hell is going on?! Jack, SmallTown SoftCorps owner, bellowed from his dim office. Jack slowly felt his way to Neils desk. Neil! You know how this building is wired better than anyone! Get the power back on NOW! Jack commanded, sweat beginning to form on his brow without the air conditioner combating the heat.
Neil had to bite back an I told you so! During the construction, Neil stressed the importance of a backup diesel generator, but Jack shot it down as an unnecessary luxury. Thankfully, he was allowed to purchase UPSs to keep their mission-critical servers alive. Neil got out his cell and phoned the utility company. The automated voice was happy to tell him there were no outages in the area. He deduced the problem must be isolated to their building.
Neil grabbed a flashlight and headed to the pitch-black bowels of the building. He flipped the main breaker on and everything in the office sprung back to life. Easy enough, he said to himself. But the real problem would be figuring out why this happened. He looked around, all the wiring seemed to be intact. There were no signs of a cooked mouse that bit down on the wrong cord. He decided to check with everyone around the office to see if they noticed anything strange.
He got several stock I was working hard and everything went black! responses. Jack said he was on an important call with a customer, but a check of his internet logs would probably show he was wasting time on Twitter again. As Neil approached Mike, one of their framework developers, he got visibly flustered. Hey Mike. Did you notice anything happening before the power went out?
What? Me? No! Of course not! Why would a lowly programmer as myself know anything about a power outage? Mike replied with a nervous chuckle. It sure is cold in here though! My cubicle feels like an igloo.
Neil looked up and noticed Mikes desk was directly under an air conditioning vent. Ouch, looks like you got the cold spot! Try to think warm thoughts.
Neil went back to Jacks office to report the bad news that he didnt have a root cause for the outage. Jack used his electrical knowledge (or lack thereof) to determine the air conditioning must be sucking too much juice on a hot day. Turn the AC down immediately, Neil! We cant have this happen again. Neil obliged by setting it a few degrees higher and went back to his normal business.
Later that afternoon, Neil was working on documentation when the office again went black. OH COME ON! a frustrated programmer shouted from across the office. NEIL! I SAID THIS CANT HAPPEN AGAIN! Jack roared out like a lion in the darkness.
Im on it, Jack! Neil replied, frustrated. Luckily he still had the flashlight on his desk and the power was back on in a jiffy. He knew telling Jack he didnt have an explanation wouldnt fly this time, so he would not rest until he found the culprit. Of course, there was no obvious cause. Neil searched the building top to bottom, and couldnt figure out the cause. He swore to figure it out the following day.
The next day was the hottest of the day of the year, and Neil dreaded the load the HVAC system was putting on the electrical system- and sure enough, the power went out again. Jack shouted his anger, and Neil scurried off to the basement to fix the problem. With the lights on, as he came back up to the top of the stairs, Neil spotted Mike trying to sneak out the side door with a large object.
What you got there, Mike? Neil asked with suspicion.
Oh, um, nothing, Mike stammered, as he turned towards Neil with a large space heater in his hands. Its just that it gets so damn cold by my desk, I decided to bring this heater in and, its probably just a coincidence, but when I do this& Mike bent down to plug the heater in a nearby outlet.
Mike, NO!!! Neil couldnt stop him fast enough as the heater fired up for a split second before the eerie quiet of a power outage returned. You fool! You can go explain to Jack why the power keeps going out while I go back to the stupid basement.
Метки: Feature Articles |
CodeSOD: Taking Exception |
Like many enterprise organizations, Martins workplace decided that they needed to build a collection of .NET assemblies which would be used in every application they built, and would provide important facilities like error handling.
And of course, not only would every application need to use these libraries, every application needed to make use of every component in them, otherwise why have the libraries at all? This meant that every Exception thrown by the application needed to inherit from the BaseException class:
public class BaseException : System.ApplicationException
{
public BaseException(object oSource, int nCode, string sMessage, System.Exception oInnerException, bool bLog) : base(sMessage, oInnerException)
{
if (oSource != null)
base.Source = oSource.ToString();
Code = nCode;
if (bLog)
{
// trace listeners should already initialized, this
// is called to be prudent
Utilities.InitTraceListeners();
// log it
try
{
Foo.DAL.Utility.ExceptionHandling.BaseException.Track("", message);
}
catch(Exception ex)
{
//Handle db logging error
SendEmail(ConfigurationManager.AppSettings["AdminEmail"], "",
ConfigurationManager.AppSettings["ErrorAlertEmail"],
"Database Error Logging Error", ex.Message + Environment.NewLine + message, "");
}
}
}
}
As Martin explains:
I found this gem at the heart of our grand, all-encompassing, be-all, end-all, work-permanently-in-progress, Godot of a One System To Rule Them All, which is supposed to be a set of assemblies that can be included in every application our department produces. Someone had the avant-garde idea of putting database-logging code, config-file-reading code and another try/catch inside the constructor for a custom exception. Every exception in every application is supposed to inherit from this.
Why handle an exception when you can build an exception object that handles itself?
Of course, reading from the ConfigurationManager may throw an exception for a number of reasons, which is never caught, meaning that throwing an exception can, in some cases, throw an exception.
Метки: CodeSOD |
Sharked |
Andrew M. worked at a small company in Kansas City called EtherTrode. With one facility and about 20 employees, they designed and built custom Ethernet hardware and drivers to fill niche roles where the common integrated chipsets werent good enough. Their hardware worked quite well, which attracted the attention of a multinational conglomerate called Initech. Initech puchased EtherTrode, rather than develop their own Ethernet devices.
Like most others at EtherTrode, Andrew was a jack-of-all-trades. On any given day, he might be doing anything from customer support, to application development, to general office IT tasks, and even once a bit of soldering. It was the general office IT tasks that got Initechs attention: he was selected to work with Initech on integrating their networks.
The first phase went more-or-less smoothly. Initech shipped Andrew a pre-configured Cisco router, and instructions on how to connect it. After a weekend of integration, EtherTrodes network now had a permanent VPN tunnel to Initechs headquarters in Detroit.
Phase One was boring. Yes, there were minor screwups and a few WTFs due to some miscommunication and unnecessary outsourcing, but thats a variation on the same story thats been told a million times before. Phase Two, however, was the Grand Slam over the foul line, lightning in the whiskey warehouse, the Cheese Shop without cheese, and a painful urination upon a subways third rail…
… Active Directory Integration.
Once the networks were connected, Initech wanted to terminate EtherTrodes Active Directory domain and move all of their accounts and systems into Initechs domain. Contractors working for Initech prepared a new domain server and shipped it to Andrew. He received the system, racked it the next weekend, and powered it on. Everything seemed to work, and local systems joined to Initechs domain without issues. Come Monday morning, the office was ready to resume business.
Things turned sour around lunchtime on Monday. All morning, employees had complained about poor network performance. Their emails failed to send, and new messages failed to arrive. Tiny Subversion check-ins would fail more often than not, and even internal web pages took minutes to load- if they loaded at all. Worse still, Initechs security officers had installed a new electronic lock system on EtherTrodes building, which was connected to the LAN. By lunchtime, the locks refused to unlock, and Andrew had to physically unplug the controller unit in the server room to disable the lock so employees could actually enter and exit.
The core staff of EtherTrode had an impromptu meeting shortly after lunch to discuss the issues. Lyle, another engineer, arrived late. But I know what the problem is, he explained.
I noticed the lights on our core switch were blinking like a strobe light in a tornado, so I fired up Wireshark. A few of the engineers nodded, and Andrew wondered why he hadnt thought of that. EtherTrode made Ethernet equipment, and Wireshark was an invaluable tool for examining the behavior of their devices and networks. It was the best way to make sure their hardware complied with Ethernet and TCP/IP standards, and it was really good at not interfering with their network while doing its monitoring. In short, Lyle explained, theres a device at 192.168.16.245 thats issuing tens of thousands of broadcasts per second. The switches are dropping frames, and the IP stack on everyones workstation is working overtime processing irrelevant packets.
254, you say? Andrew said. He frowned. Thats the IP address of our new domain server…
Can we shut it down? asked Ryan, their boss. Rollback to how our network was last week?
Andrew shook his head. Ive already joined everything to the new domain. If we shut down the controller, no one will be able to log in or access network resources… not that they can now.
Lyle spoke up again. The traffic looks pretty fishy. I think the servers got some sort of malware on it. Can we log in and see what its doing? Run an antivirus or kill the process?
Andrew shook his head again. Initech has it locked down tight. I can do some basic domain admin tasks, but I cant actually log into the server.
Alright, Ryan said, visibly irritated, Lyle, give me your Wireshark capture. Im going to go down to the coffee shop for their wi-fi, and Im going to forward it to Initech and raise some hell.
In the meantime, Andrew and Lyle went back to the switch. Since the broadcast was on a single port, they blocked that port at the switch. The server kept shouting its head off, but now the switch dropped the packets. Doing anything that required talking to the domain server- like logging in- was painfully slow and failed half the time, but the rest of the network worked fine. That bought them some time while they waited for a better solution from Initech.
The next day, Andrew came in, grabbed a mug of coffee, and picked up the top priority support ticket. This one was a customer support issue. They had uploaded their own Wireshark capture to an FTP server. Andrew downloaded it so that he could see why their EtherTrode equipment was misbehaving. When he went to open the capture, Wireshark refused to start- or more accurately, it was no longer installed on his system.
Thats strange, Andrew thought to himself. Maybe a Windows Update messed up the registry settings, or something, so Andrew redownloaded Wireshark and ran the installer.
Or tried to. The installer refused to run. This publisher has been blocked by your administrator, explained the error message. As if on cue, his email client dinged and he was a new memo from the Initech IT department:
TO: All Initech Employees SUBJECT: Re: Network Breach at KC Facility
Our new Kansas City facility had an unexpected network attack yesterday. Weve analyzed the data and determined that a freeware application called Wireshark is to blame. This application caused severe issues with the facilitys security system and cannot be trusted. To prevent future attacks, we have globally blocked Wireshark via Group Policy and our antivirus suite. Wireshark is just malware and this decision will not affect day-to-day operations at any Initech facility.
Andrew dropped his forehead right into his desks surface, and all around the office he heard a chorus of thumps as his fellow engineers did the same. Initech refused to flex on banning Wireshark, and refused to admit that anything could be wrong with their domain controller.
Метки: Feature Articles |
CodeSOD: Filter Overflow |
Onboarding was complete, and Denise finally had her PC. Time for her first assignment!
"It's really pretty straightforward," her new boss had said, with a resigned look in his eye that betrayed it wasn't actually straightforward. "Your predecessor almost had this new CMS complete, we just need filtering on the frontend search."
It wasn't glamorous, but it was probably a good way to get her feet wet in a new organization. The requirement was to fetch a list of image IDs that corresponded with a business ID so that users only saw the images for their group. Simple, straightforward, obvious. She had it done inside of a few days and sent to the users for UAT.
And that's when the change requests started.
"Can you make it filter by state? Like, hidden images, public images, that sort of thing?" Sure, easy enough.
"What about filtering by upload date? Like if I gave you a date range?" Um, sure, but it'll add to the time...
Even after the filter was in production, the list of requests kept rolling in, bypassing the helpdesk and going right to her email inbox. "We're getting a lot of spam images, can we filter so only verified users' uploads show up?"
Denise's boss told her it'd be over faster if she just gave in and made the change. Management would ultimately decide to do it after wasting her time with meeting after meeting to discuss priority anyway.
"Can we put videos in the CMS?"
"Can we filter by tags?"
"I need to filter images that are actually images."
"Can we sort on the tags?”
How do you sort on tags? Denise wondered. Apparently, by their business IDs. Sure. Whatever.
"I'm still getting images that shouldn't be showing up. Can we filter for only images that show up?"
"There's too many images, can I limit it to ten per ID?"
Finally, she ended up with this giant Ruby monstrosity:
filter(ext_id: ext_ids, type_id: type_id)
.filter_state(params['state'] || 'public')
.filter_created_at(params['start_date'], params['end_date'])
.default_filters(params)
.left_outer_join(:image_tags, image_relations_id: :id)
.select {
columns.map { |x| "image_relations__#{x}".intern } +
[
COLLECT(:name).cast(:'VARCHAR2_NT').as(:tags),
MIN(
[
[{ image_tags__name: 'profile' }, 1],
[{ image_tags__name: 'cover' }, 2],
[{ primary: 1 }, 3]
].case(4)
).as(:sort_priority)
]
}
.where {
(
{ recurring: 0 } &
(start_date < time) &
(end_date > time)
) |
(
{ recurring: 1 } &
(
(
(to_char(end_date, 'MMDD') >= to_char(start_date, 'MMDD')) &
(to_char(start_date, 'MMDD') <= time.strftime('%m%d')) &
(to_char(end_date, 'MMDD') >= time.strftime('%m%d'))
) |
(
(to_char(end_date, 'MMDD') < to_char(start_date, 'MMDD')) &
(
(to_char(start_date, 'MMDD') <= time.strftime('%m%d')) |
(to_char(end_date, 'MMDD') >= time.strftime('%m%d'))
)
)
)
)
}
.group(columns.map { |x| "image_relations__#{x}".intern })
.from_self
.select {
columns +
[
:tags,
ROW_NUMBER(
:over,
partition: :ext_id,
order: [:sort_priority, *column]
) { }.as(:partition_row_number)
]
}
.from_self
.select { columns + [:tags] }
.where {
(partition_row_number > offset) &
(partition_row_number <= offset + limit)
}
.all
(Note: there are start_date and end_date parameters to filter on, but also, start_date and end_date columns in the table for deducing if the image is valid or not.)
Finally, enough was enough. Seeing a lateral position open up in another team, Denise went for it. Now, at least, it'd be her successor's problem to cater to the scope avalanche.
Метки: CodeSOD |
Error'd: See Something? Say Something! |
Patrick wrote, "I was at Penn Station in NYC, and, well, I just had to say something!"
"Got a stuck drawer? Stock photo avalanche to the rescue," writes Ben S.
"The reason behind why the subway/tube/metro/U-Bahn is always late in Berlin is finally explained!" wrote Juan J.
Jared S. writes, "Did Yahoo copy Google's approach to testing?"
"At the end of the installation wizard, something went wrong," Marko T. wrote, "Luckily this message makes it clear what that reason was."
"When you log out of the OpenDNS site, it'll hope that you have a good morning (but not really)," Wews M writes.
"The sign in London Kings Cross tube station said 'Stand Back' followed by this," Andrew wrote, "I'm not sure what it means, but it looks serious!"
"Photo came in as part of a bug report about a wrong name appearing in a winery touch screen operator selection list," Brian writes, "Instead, I think that we should first re-evaluate our choice of window titles."
Метки: Error'd |
The Depths of Insanity |
George G. came to the Pierce & Pierce office in good spirits and with high hopes. After finally gathering the courage to run away from his previous job, which had involved maintaining a million-line, 15-year-old mess of a codebase, he'd spent the last month interviewing with nearly every tech company in his area. Here he'd found his Promised Land: a modern-looking, professional company with a suite of cutting-edge technologies and, most importantly, a new and interesting project to which George would be assigned.
Or at least that was what Pierce & Pierce had promised at the interview.
"Well, you see, there's been a reorg." George had barely sat down at his desk for the first time when his manager brought him the news. "All new projects are on ice. We're gonna be putting you on our flagship product instead. Don't worry, we'll have something real cutting-edge for you soon!"
Flagship product? That could only mean one thing: legacy code. Another aging codebase which, thanks to the company's "innovative performance-boosting management methods," clocked in at five million lines of uncommented, undocumented code. As much as George wanted to get up and leave at such news, he decided to bite the bullet and give Pierce & Pierce a chance ... for now.
He received his first assignment almost immediately. The application failed to read a particular line from a config file, and the task was to investigate and fix the issue.
Well, that should be simple, he thought. I just need to find the place where the config file gets opened, then there should be some loop that reads it and looks for that line. Easy-peasy.
He submitted a one-hour time estimate, giving himself some leeway, then got down to hunting the bug.
Unfortunately, things weren't so simple. What was supposed to be a quick, introductory trip through the codebase turned out to be a strenuous excavation through layer upon layer of abstract factory builders, inline lambdas spinning off separate threads, callbacks to callbacks, and other examples of architecture astronautics—all to simply read the configuration file.
Four days later, George managed to find something resembling an error logger. He set a breakpoint and ran the application, hoping to work his way through the call stack to where the issue lay.
Sure enough, the breakpoint was hit almost immediately. But even before George started reading the stack trace, he knew it wouldn’t be of much use. The list of classes looked something like this:
ErrorReporter
ErrorInterface
ErrorJuggler
ErrorMux
ErrorRecorder
ErrorCatcher
ErrorFinagler
ExceptionModerator
ExceptionFilter
ExceptionAdvertiser
ExceptFondler
ExceptIncoming
ExceptPeeker
ErrorPoster
ErrorLayout
ErrorGenerator
ParseDistributor
ParseThis
ParseThat
//snip 19 lines
ReadInitFile
Well, at least I know where the log is, he thought. Maybe there's something interesting in there?
[2015-08-05 12:12:33] Parse error.
[2015-08-05 12:08:45] Parse error.
[2015-08-05 11:59:27] Parse error.
...
Not a chance. Apparently, somewhere deep in the error handling code, someone got lazy and dropped all the potentially useful error information.
That was the last straw. George was already mentally drafting his resignation letter, but to his shock, his manager beat him to it the very next week.
"We're going to have to let you go," he said. "We like you, but you're just not getting enough done."
And so, after one week with zero bugs squashed, George returned to job-hunting. The wasted time annoyed him, but he was blessedly free from supporting that gigantic codebase.
Метки: Feature Articles |
Bring Your Own Code: The End of the Lucky Deuce |
What feels like forever ago, we introduced the Lucky Deuce casino contest. This is a series of challenges, brought to you by our pals over at Infragistics, where we call on you to help us build a scoundrels casino.
Last week, we introduced our final challenge: take one of our casino games, add a betting system, and then build a strategy that lets you keep playing (by distributing the load across multiple accounts).
Once again, we had a nice pile of great submissions, and there are two who are going to get to take home a nice TDWTF hoodie for their troubles. As always, check out the full projects on GitHub.
For all of our winners (from this week or any previous)- or anyone who entered using Infragistics controls- expect an email in the next week or two to follow up about how best to get you prizes.
First, lets talk about Brunos solution. For bonus points, he decided that his system should be able to detect a users gender by their name:
public String getSex() {
return (username.matches(".*[aeiou]+$")) ? "female" : "male";
}
Now, that has nothing to do with the requirements, but I always get a chuckle out of how poorly these sorts of regex-based gender detectors work. How did Bruno implement the requirements?
Like an onion, there are layers. First, he introduces a subtle bug in the User.equals method:
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final User other = (User) obj;
if (!Validator.validateTrue(this.profile = other.profile)) {
return false;
}
if (Validator.isNull(this.username) ? !Validator.isNull(other.username) : !Validator.validateEquals(this.username, other.username)) {
return false;
}
return this.username.equals(other.username);
}
Note on the validateTrue
line, hes misusing the assignment operator- something Java tries to prevent. This means that, when comparing two User accounts, they end up using the same profile.
That profile property is important- theres one profile called CHEATER
which is applied to anyone thats caught cheating. With a method included that allows two players to send money to each other, youre basically set up to do a comps hustle, but with a twist- the CHEATER
tag is only set at login. If two players login, they can play the comps hustle until one of them gets caught and kicked out- if the uncaught player sends money to the caught player, they also get marked as a cheater- but since the flag is only checked at login, they can sit there and keep playing. And, if they want to keep making money, Bruno included a catcha[sic] on login that uses the same random number generator as the games being played- which means that by checking the captchas you can deduce what was used to seed the RNG:
for (int i = 1; i < 14; i++) {
source.add("catcha/" + i + ".jpg");
}
for (int j = 13; j > 1; j--) {
catcha.add(source.get(random % j));
source.remove(random % j);
}
Its not exactly our requirements, but its a clever way to leak state about your RNG, and since a clever cheater can avoid getting kicked out of this casino, it does mean you can play indefinitely.
Our other winner this week is also the only player who has entered every week, and already has collected a nice pile of swag: Jonathan is back, with yet another great iteration of the same RNG with a solid strategy for confounding the casino.
Using our biased roulette wheel from the first week, Jonathan decided to build four classes of account- Winners, Losers, High Rollers and the Cage.
Winners use their knowledge of the wheel to keep earning money, Losers still make smart bets, but still lose to the house edge. The high rollers are the risky players- they make big bets, theyre volatile players, but because of their knowledge of the wheels behavior, they may have seemingly big losses, but on average, never actually lose that much. The Cage is just a player that holds the money generated by the winners to hand off to the losers, if needed.
Its a little long, but heres the complete implementation of Jonathans strategy for all of our different players:
void operator() () {
// If increment is positive, we're a "winner" and use inside bets.
// If increment is negative, we're a "loser" and use outside bets.
// If increment is zero, we're a "high roller":
// use inside bets until RNG state is known and float is adequate,
// then use "full complete" bets for maximum returns.
balance = FREE_BALANCE;
compsEarned = 0;
uncertainty = 64;
std::unique_lock lock(cageMutex, std::defer_lock);
// simulated connection to Lucky Deuce casino
char* buffer = (char*) malloc(257);
FILE* server = popen("yes \"\" | ./roulette", "r");
assert(server);
fgets(buffer, 256, server); // initial prompt
// rigged RNG
RoulettePlayerHistory wheel;
// proper RNG
FILE* rngSrc = fopen("/dev/random", "rb");
printf("%s: joining the table.\n", name);
while(1) {
while(balance < bet) {
// replenish float from the cage
uint32_t wanted = bet;
if(FREE_BALANCE > wanted)
wanted = FREE_BALANCE;
if(!increment && bet * 40 > wanted)
wanted = bet * 40;
wanted -= balance;
lock.lock();
if(cageBank > wanted) {
printf("%s: taking $%u from the cage.\n", name, wanted);
balance += wanted;
target += wanted;
cageBank -= wanted;
} else {
printf("%s: taking $%u from the cage.\n", name, cageBank);
balance += cageBank;
target += cageBank;
cageBank = 0;
cageQueue.wait(lock);
}
lock.unlock();
}
if(increment > 0 && balance > target && target > FREE_BALANCE) {
uint32_t surplus = target - FREE_BALANCE;
lock.lock();
printf("%s: donating $%u to the cage.\n", name, surplus);
cageBank += surplus;
balance -= surplus;
target -= surplus;
lock.unlock();
cageQueue.notify_all();
}
if(increment > 0 || (-increment) > target)
target += increment;
uint32_t threshold = bet;
uint32_t effectiveUncertainty = uncertainty;
if(balance > target)
effectiveUncertainty += balance - target;
if(increment < 0)
effectiveUncertainty = 16;
uint32_t stats[38] = {0};
wheel.Predict(effectiveUncertainty, stats);
// choose a bet to place
if(increment > 0) {
uint32_t betIndex = cr.BestOutsideBet(stats);
printf("%s: betting $%u on %s.\n", name, bet, cr.betTypes[betIndex].name);
cr.PlaceBet(betIndex, bet);
balance -= bet;
} else if(increment < 0) {
uint32_t betIndex = cr.BestInsideBet(stats);
printf("%s: betting $%u on %s.\n", name, bet, cr.betTypes[betIndex].name);
cr.PlaceBet(betIndex, bet);
balance -= bet;
} else {
if(!uncertainty && balance > 40*bet) {
uint8_t betNumber = cr.BestFullCompleteBet(stats);
printf("%s: betting %u to the *MAX*!\n", name, betNumber);
balance -= cr.PlaceFullCompleteBet(betNumber, bet);
} else {
uint32_t betIndex = cr.BestInsideBet(stats);
printf("%s: betting $%u on %s.\n", name, bet, cr.betTypes[betIndex].name);
cr.PlaceBet(betIndex, bet);
balance -= bet;
}
}
again:
// spin the wheel
usleep(SPIN_TIME);
fgets(buffer, 256, server);
uint8_t spin = 0;
if(!strcmp(buffer, "00\n")) {
spin = 37;
} else {
char* e;
spin = strtoul(buffer, &e, 10);
if(spin > 36 || !e || *e != '\n' || !*buffer) {
printf("%s: Mit"a vittu?!? '%s'\n", name, buffer);
goto again;
}
}
uint32_t payout = cr.PayBets(spin);
if(payout)
printf("%s: Spun %u, won $%u! :D\n", name, spin, payout);
else
printf("%s: Spun %u, didn't win. :(\n", name, spin);
balance += payout;
wheel.Update(spin);
uncertainty = (uncertainty ? uncertainty-1 : 0);
}
};
Jonathans solution does involve two different programs, so youll need to compile roulette.cpp
to provide the wheel, and roulette-hustle.cpp
to simulate players. Run roulette-hustle
, and it worries about launching roulette
.
Congratulations to all of our winners and participants in every week of the contest. Hopefully everyone had as much fun reading through the entries as I did.
Its six months later. You havent heard anything from Paula since your rushed-hack job behind the coffee shop dumpster, but you havent gone looking, either. With the profits you got flipping comps, and dipping into rigged gambling games, you were able to clean up your reputation.
No more run-down hotels for you, no more fly-by-night coding gigs, no more running from the FBI. Youre staying in the Hyatt Regency right on the Embarcado in San Francisco. You check your reflection in the mirror on your way out into the hallway- youre dressed to the nines in the most professional fashion imaginable, because youre in town to speak at a security conference, of all things.
Thats just one of the perks of giving up your life of crime and turning into a consultant. Honestly, its even more of a con than the stuff you did for the Lucky Deuce: you give bad advice that will either be ignored or horribly misimplemented, and charge thousands of dollars an hour for it. Every time your conscience starts to nag at you, you think back to what it was like staring down the barrel of Paulas gun. Besides, with the rates you charge, only big companies who can soak the costs hire you anyway.
You step inside the elevator, beside another guest who looks like a gorilla wearing a cheap suit. You press the button for the atrium, and turn around to look out the glass-walled elevator as the atrium the size of an airplane hanger rushes up at you.
The gorilla pushes the emergency stop. He smiles at you like hes about to eat you for lunch. You realize something must be very wrong, and your mind latches onto the fact that you cant be in that much danger- everyone in the hotel can see what happens in this elevator.
My employer used to own the Lucky Deuce, he says. His accent is cultured- English is obviously not his native tongue, but the sharp edges of his accent have been rubbed down so much that youd never be able to tell where in the world he came from. The clipped and clear diction contrasts with everything else about his appearance. My employer is displeased by the disruptions in its productivity- and would like to discuss your future.
The ape hands you a thumb drive, then releases the elevator, getting off at the next floor. Thats when you realize… this might not be over…
Thanks to Infragistics for making this possible.
A worldwide leader in user experience, Infragistics helps developers build amazing applications. More than a million developers trust Infragistics for enterprise-ready user interface toolsets that deliver high-performance applications for Web, Windows and mobile applications. Their Indigo Studio is a design tool for rapid, interactive prototyping.
Метки: Bring Your Own Code |
'Tis the Season |
Deep in the wooded vales of red state America, December is hallowed not just for hunting presents, but also hunting deer. Lo, the season opened on a Friday. Clayton’s consulting firm declared it Camo Day in celebration.
Employees festooned themselves and their office in their brown-and-green finest. Some posted deer horns and small taxidermic animals in prominent locations. While this particular company stopped short of installing a shotgun in the kitchen, just in case a bear happened along, it was still the most redneck of wonderlands.
Clayton could even swear he smelled hunting musk as he moved through the floor, trying to get back to his desk after an exhausting code review. And everywhere he looked, camouflage-print duct tape lingered like traces of scat: patching a hole in the carpet, propping up the back of a swivel chair, cradling a leaky ceiling tile …
Both tape and musk led to his manager Buck’s office.
“How you doin’, Clayton!” His voice boomed out from the office like a blunderbuss’ payload, halting Clayton in his tracks. “How’d that code review go?”
Clayton peeked in to find Buck applying a piece of camo duct tape to a patch of ruined drywall behind his desk chair. He ignored the scene to reply, “Fine. John has some changes to make, but nothing major.”
“Good, good.” Buck rubbed a fist over the newly applied tape. “Don’t mind me, I’m just taking the initiative to fix a few things around here.”
Clayton debated whether to say anything. In the end, he couldn’t help himself. “Uh, all that stuff you’re fixing probably needs more than duct tape.”
This prompted Buck’s ringing laugh. “If you can’t fix it with duct tape, you’re not using enough!” Finished with the wall, Buck turned, yanked his laptop out of its docking station, and began wrapping the camo-colored tape around it.
Clayton really knew better, but again couldn’t help himself. “What are you doing?”
“Decorating my laptop!” Buck replied.
“But … you’re covering the vents,” Clayton managed around his shock. “It’s gonna overheat.”
“It’s winter and it’s cold out! This thing’ll be fine.”
“Win—you’re taking it outside?” Clayton faltered.
“Out to my hunting blind! I’m cutting out early to get a jump on the season.” Buck reached into his pocket. Out came an obnoxiously sized lock-back knife that he used to slice a gap into the tape layer, allowing him to open up the laptop.
“Why bother working remotely?” Clayton asked. “Just call it a week.”
“The usual BS quarterly meeting is this afternoon—of course.” Buck rolled his eyes. “Gotta join the WebEx and at least pretend to pay attention.”
The WebEx would handle both video and audio for the meeting. Clayton imagined the deer would take a dim view to budget projections, but Buck’s laptop did have a mute button.
“Happy hunting, champ! I’ll have my cell phone in case you need anything.” Buck packed his laptop, then gathered his coat and a cooler that was almost certainly full of beer.
“Uh, OK.”
In the end, Clayton couldn’t complain about a manager-free afternoon. He returned to his desk, dug into his work, and all was well.
A few hours later, his desk phone rang. Buck’s cell.
Clayton internalized a curse and picked up the phone. “Hello?”
There was nothing on the other end at first aside from scuffling, and a string of very not-internalized curses from Buck.
“Boss?” Clayton prompted.
“The damn thing’s a brick!” Buck cried.
“What is?” Clayton asked.
“It just shut down on me!”
“Your laptop?”
“And the damn meeting’s still on!” More scuffling noises from Buck’s end. “Hopefully everyone’s too busy snoozing to notice I fell off the WebEx. Hang on, I’ll be in the office soon!”
Clayton didn’t bother stifling his groan, but managed to hang up before it escaped.
Twenty minutes later, Buck stampeded into his cube, cooler and laptop in tow. He dropped the camouflaged computer onto Clayton’s desk, shaking out his hand. “Sumbitch shut down on me out of nowhere!”
Clayton felt the waves of hot fury radiating off the laptop when he stuck his hand near it. “It overheated, like I said. You blocked the fans that keep air circulating through there,” he explained. “We gotta get this tape off.”
“Nah! If it’s hot, we just gotta cool it down, right?” Buck opened up his cooler and pulled out a half-melted bag of ice. He then turned and dropped said bag directly onto the laptop.
Clayton’s jaw fell. Should he bother to say anything? No, it never helped.
“While it’s cooling off, I’ll need your computer to log back into the WebEx,” Buck said.
Clayton suppressed his instinctive panic. “You have to leave it here. No ice or camo. I’m done for the day, and I’m not touching any more work until Monday! Deal?”
“Deal.”
With a collecting breath, Clayton logged out, then stood to gather his belongings. “There you go.”
Buck clapped him on the shoulder. “Happy hunting, champ!”
Метки: Feature Articles |
Classic WTF: The Non-Deleting Delete |
It's a holiday in the US today, which means we're taking a 3-day weekend to dig back through the archives and find a classic WTF. One of my favorite features- one that we run far too rarely- are the true confessions. Sometimes, we are TRWTF, and let's applaud Matthew Schaad's story about his misuse of database triggers. - Remy
It started out as an average day for a developer like me. At 11:30AM, I was just getting into the office and fixing my second cup of coffee for the day. Being in the habit of coding till 3:00AM nightly, I was averaging about three to four cups a day. As I sat down at my desk to tackle one the several projects I had been assigned, I got a frantic call from the Director of IT, Jeremy.
“Matt!” the telephone receiver shouted.
“Yes?” I responded coolly, as I was used to hearing about fires that needed to be put out yesterday.
“The reservation system is not allowing users to delete their reservations! Martin booked a conference room every Thursday for several years out, and now he can't delete it. Can you take a look at it?”
“Sure, no problem,” I replied before hanging up the phone and going back to sipping my coffee.
Jeremy was referring to our company reservation system. Similar to Outlook, we use it to manage shared resources, such as conference rooms, company cars and IT equipment. It isn't the most reliable system, and we constantly get complaints about it. Most of the time, though, the problems seem to arise between the chair and keyboard.
So having gotten my marching orders, I opened up the database, wrote a quick DELETE statement, ran it, and saw that it deleted all the records for Martin’s recurring appointment:
(1137 row(s) affected) Query executed successfully.
Man, that is one long-standing appointment, I thought to myself, but problem solved! I called Jeremy to tell him it was fixed.
I got a call later from Jeremy telling me that the problem was very much not solved. The event was still appearing in the system. Whoops. How embarrassing!
What? I could have sworn I took care of that, I wondered as I sipped my fourth cup of coffee. I went back into the database, this time making doubly sure that I was on the correct server, and wrote the DELETE statement again. Not wanting to embarrass myself this time, I ran a SELECT against the reservation table just to make sure I wasn’t declaring success prematurely again. I was shocked to find that the query still returned over 1100 records! I blinked.
I ran the DELETE again. The server reported deleting the 1100 records successfully.
I ran the SELECT again. 1100 records returned.
I stared slack-jawed at the screen. I had never seen this sort of problem before.
I ran the DELETE again. This time, I looked closer at the message:
(1134 row(s) affected)
“Well, some of the rows are getting deleted,” I mused.
I ran the DELETE statement twice more.
(1133 row(s) affected) (1132 row(s) affected)
“There’s one fewer record every time,” I said to myself. “They’re getting deleted one at a time!”
What in the world could cause this sort of behavior?, I thought myself as I rubbed my chin. The more I pondered the problem, the more I realized there was only one answer: a trigger. So I opened up trigger list on the reservation table, and sure enough, there were three triggers.
sendEmailAfterDelete sendEmailAfterInsert sendEmailAfterUpdate
Guessing that sendEmailAfterDelete must be the culprit, I opened up its definition and saw that it started like this.
CREATE TRIGGER [dbo].[sendEmailAfterDelete] ON [dbo].[reservation] INSTEAD OF DELETE
The author of that trigger had created it as an INSTEAD OF
trigger, rather than an AFTER
trigger, as its name implied. That meant that only the trigger code would run, and not the delete statement.
INSTEAD OF DELETE? I thought to myself. Well, that could definitely do it.
I examined the body of the trigger for logical errors, but I didn’t find any. It was a straight-forward trigger: SELECT
the first deleted record from the SELECT
pseudo-table, send an email to the appointment owner, then actually DELETE the record. The INSTEAD OF
declaration was a simple mistake that anyone – even me – could make.
“Waitasec,” I said aloud, “we’re sending email messages from a trigger in the database!?!”
It was one of the worse anti-patterns I had in our database, but now was not the time to fix it. Instead, I did a simple ALTER TRIGGER
to have the code loop over every row instead of just applying to the first one. I then ran the DELETE for Martin’s appointment one more time:
(1131 row(s) affected)
I ran the SELECT for Martin’s appointment one more time:
0 rows
Success! I called Jeremy, and told him the good news. But my quest was not complete: our database committing a WTF of epic proportions and sending emails in a fairly-hidden delete trigger. I had to find the idiot who thought this was a good idea, school him on the most basic principles of software design, and make him fix it.
But then all of a sudden, I had a flashback to work I had done on the system a few years ago. I remembered that I was the idiot that had written the trigger to send emails when reservations got deleted. I wish I could remember my mindset at the time, but I suspect it just seemed like a neat trick.
I stayed late that day fixing the email notification and, as I tested the new code, I wondered what ever happened to Martin’s inbox.
http://thedailywtf.com/articles/classic-wtf-the-non-deleting-delete
Метки: Feature Articles |
Error'd: Crotia? Slovania? DO NOT USE! |
"Canada? Yes! At least they got that one right!" Steve M. writes.
"Photoshop...what exactly would you say that it is that you DO here?" writes Scott.
"Oh crap, now I've seen it. Now what?" wrote Quentin G.
Wiebe H. wrote, "Apparently, working for Google is stressful enough that even the servers have started drinking."
Nate writes, "Wait. I need to log in to log out? Ok, I guess it sort of makes sense."
"Looks like there's something else to do beside figuring out why the app crashed," Eric wrote.
"Saw this on an ASCII art website," David G. writes, "What should be never happened, be happened anyway."
Hans-Peter wrote, "There is no other store than 'The Store'. Bow to the might that is The Store! ...probably"
Метки: Error'd |
The Wunderkind |
Software needs to run quickly. Whether it's to get a response to a shopper so they don't get bored and click on to the next site, or performing calculations on some data that is urgently needed downstream. Efficiency is important!
To that end, most developers attempt to write code that runs quickly. Sometimes, the code needs to run more quickly than conventional means will allow. In those cases, smart developers will figure out how to game the system to get the computer/network/disks/etc. to get things done more quickly than the usual methodologies permit. For example, you might try to cut network overhead by stuffing multiple small requests into one buffer to take advantage of the leftover space created by network packet sizes.
Of course, if you're going to do something sophisticated, it's common courtesy to document what you're doing, and why, so the next person who has to maintain your code will know how to proceed.
Unfortunately, not all developers are smart, or considerate.
Ralph worked on a mature Java/C++ project that needed to replace an even more decrepit mature reporting system. After several half-hearted starts, his company bought a competitor that had a pretty decent reporting system that was written and supported by The Wunderkind.
The Wunderkind convinced management that the reasons that he was so successful were that:
Management decreed that a new team, headed by The Wunderkind, would be formed and tasked with creating a brand new, ultra-modern, full of bells and whistles reporting system.
While they went off into the bright new future, Ralph was content to continue slogging away in other areas using the prehistoric technologies of Java and C++...
Fast forward 2 years, and the reporting project had blown several deadlines. It been scaled back to a shadow of what it was supposed to be. Almost all of the feedback from product management or their UI designers had been deemed technically impossible to implement.
Apparently, management had been asleep at the switch...
Finally, with yet another deadline looming, The Wunderkind resigned, and Ralph (who had not been involved in this project in any way since its inception) was called in to figure out exactly how much trouble they were in.
He got a cursory introduction to the "architecture" in a series of meetings, figured out the hot-spots that required immediate attention, and got to work.
One of the first things that jumped out at him was that there was no DBA working on the team. Apparently, The Wunderkind had done all of the SQL work at his previous company, so there was no need for a DBA. Besides, DBAs weren't "agile" enough for a team that was moving so quickly...
As Ralph spelunked into the bowels of the system, one of the first things that jumped out at him was a strange looking routine that converted a list of ID objects into a comma-separated string. There are no comments ANYWHERE in this code (apparently comments are not "agile"), but this functionality seemed kind of odd given the circumstances. When he saw this long list of IDs being passed as a parameter to String.Format() (the C# equivalent of sprintf) and passed into the DB layer, the klaxons in his mind really started blaring:
StringBuilder idBuilder = new StringBuilder(); foreach (object id in idList) { idBuilder.Append(id.ToString() + ","); if (idBuilder.Length >7500) { command = String.Format(queryBase, new object[] { TableName, workSetId, idBuilder.ToString(), userId } ); [snip-- execute the string as a SQL command] idBuilder = new StringBuilder(); } } command = String.Format(queryBase, new object[]{ TableName, workSetId, idBuilder.ToString(), userId } ); [snip-- execute the string as a SQL command]
Fearing the worst, he chased down the definition of queryBase to find this:
private const string queryBase= @" DECLARE @ids varchar(8000) SET @ids = '{2}' DECLARE @commapos int CREATE TABLE #t( i int ) WHILE( 1 = 1 ) BEGIN SET @commapos = CHARINDEX( ',', @ids ) IF @commapos = 0 BREAK INSERT INTO #t (i) VALUES(LEFT( @ids, @commapos - 1 )) SET @ids = STUFF( @ids, 1, @commapos, null ) END insert into {0} select {1},i,{3}, getDate() from #t drop table #t
Let's see if we can figure this out. The Wunderkind turned a list of ids into a comma separated string, in chunks of about 7500 characters. Then he parses each of those strings into a temp table of ids. Then inserts the temp table into the target database table.
Don't get me wrong, I've turned thousands of database calls, each to insert a single row, into a single structure of arrays of records (each representing a single row), passed the whole thing to the database server and let it grind through it in a single call. This eliminated thousands of round trips to the database and substantially sped things up. Of course, it also drastically complicated both the Java and stored procedure code and I had to document it to-the-extreme to allow others to support it. But, I only did things in a round-about way when it was absolutely necessary. And there were documents - with pictures - explaining what was going on.
When Ralph showed this code to one of their DBAs to make sure it was doing what he though it was doing, the DBAs' only comment was Please tell me this wasn't found in our product...
Метки: Feature Articles |
Bring Your Own Code: Getting Comped |
Today brings us the fifth and final entry about the Lucky Deuce. This is a series of challenges, brought to you by our pals over at Infragistics, where we call on you to help us build a scoundrels casino. Read to the end, because this week's challenge has a bigger prize- some TDWTF-emblazoned hoodies for the best entries.
Last week was your first shot at a straight solution, and the entries really showed it. Pretty much everybody got straight into the problem.
Robert for example, implemented his solution as a straight-forward WinForms application in C#. What gets him his credit is… interesting extension to a simple die class he wrote which helps him count die combinations later:
class DieEnumerator : Die{
public DieEnumerator(int faces, Random randSource) : base(faces, randSource) {
_current = 1;
_overflow = false;
}
public int current { get { return _current; } }
private int _current;
public bool overflow { get { return _overflow; } }
private bool _overflow;
public void Next() {
_overflow = false;
++_current;
if (_current == faces + 1) {
_current = 1;
_overflow = true;
}
}
}
Theres nothing crazy in there, and it does the job, which gets Robert the It Does the Job award.
Jonathan is back again this week, with his Bruce Said So random number generator, and this time he gives us a lovely function for handling overflows with cryptic variable names and a goto
:
static void MultiplyReduceRatio(uint64_t aa, uint64_t ab, uint64_t ba, uint64_t bb, uint64_t& ca, uint64_t& cb)
{
/* try to keep as much ratio precision as possible, by avoiding floating-point */
uint64_t ad, bd, cd;
repeat:
ad = gcd(aa,ab);
bd = gcd(ba,bb);
aa /= ad;
ab /= ad;
ba /= bd;
bb /= bd;
ad = gcd(aa,bb);
bd = gcd(ba,ab);
aa /= ad;
bb /= ad;
ba /= bd;
ab /= bd;
if(Log2(aa) + Log2(ba) > 64 || Log2(ab) + Log2(bb) > 64) {
/* not enough integer precision */
if(aa > ba)
aa /= 2;
else
ba /= 2;
if(ab > bb)
ab /= 2;
else
bb /= 2;
goto repeat;
}
ca = aa * ba;
cb = ab * bb;
/* final reduction */
cd = gcd(ca,cb);
ca /= cd;
cb /= cd;
}
I dont know what it does, but it works well enough for a Going Gone award. Jonathan capped his solution at 31 dice, and raises the point: Hence games up to 4d250, 14d20, 24d6, 27d5 or 31d4 can be constructed. I think 31 dice in play is quite enough for most people, especially given the caltrap-like properties of d4s.
This weeks winner does a great job choosing a platform thats simple to use, extensible, and integrates with Microsofts vision of the future: PowerShell. Scott implemented his solution as a Cmdlet which means you can chain it together with a larger, PowerShell driven casino solution that no one will be able to maintain.
# The logic for when the player gets to roll again is a bit complicated in this
# simulation, so I broke the test into several lines to manage it. PowerShell,
# however, cannot handle line breaks between some (but not all) of its tokens.
# The single backtick tells the parser to ignore the next character, allowing me
# to embed line breaks without interfering with the farce of a parser.
…} while `
( `
( `
( `
(-not $Test) `
-and `
$Point -eq $null `
) `
-or `
$WholeTurn `
) `
-or `
( `
$Test `
-and `
$TestNumber -lt ($Maximum - $Minimum + 1) `
) `
)
Clarity itself.
Youve got your code checked in, but the roach motel youre staying in gives you the bums rush before Paula makes contact again. You know you can rebuild your bankroll with the hacks already in place, but Paula made it sound like she had a big score.
Youre on the street with barely any cash, and no real direction, so you camp out next to the dumpster behind some trendy coffee shop promising hand-roasted beans and artisinal almond milk creamers. It doesnt matter what theyre serving- the dumpster smells like any other, but you can get on their wi-fi and nobodys paying you any attention back there.
You log into the Lucky Deuces admin console. You havent got a lot of permissions, but you notice two things. First, the Lucky Deuce has launched a new marketing campaign, and the second is that since the campaign launched a huge number of new accounts signed up- and a few of them came from your current IP. Paula must have sneaked a script onto your laptop that ran when you connected to wi-fi.
On a whim, you log into the Lucky Deuce storefront using one of those- password Br1llant
- and theres a message waiting in your players inbox from Paula: Get ready, kid. This is it.
Then you put it together. The Lucky Deuces new marketing campaign is a digital version of the traditional casinos comps. If you sit at a table, playing a game, eventually a waiter will wander by and give you a free drink, or a snack- anything to loosen you up and keep you at the table, pumping money into the House Edge- the casinos advantage over the players. The Lucky Deuce cant buy you drinks over the Internet, but it can do the next best thing: throw a few dollars into a gift-card in the Pennsylvania state-controlled liquor monopoly. In a state where you cant even buy beer in grocery stores, those cards are worth their weight in gold.
You and Paula are going to do a comps hustle. These can get really complicated in a regular casino- a wary Pit Boss will spot a lazy hustler, but the Lucky Deuce isnt that wary.
You have control of as many user accounts as you like. Choose one of the betting games from the previous weeks, and write a program that coordinates two or more players so that their wins and losses offset each other (the simplest example would be having one player bet on Red and the other on Black in roulette), so that the total loss across all players you control is minimal, and thus you can play as long as possible. The longer you play, the more comps youll earn.
You dont have to use code from your previous entry, but if you have already put an entry in, it will obviously make your job easier. You can take advantage of your cheats as part of your strategy, but remember, the goal isnt to win big at the table, but to take them to the cleaners on comps.
Alex has written up a more detailed explanation of the comps hustle if you need more help.
The prizes are gonna be a little bigger than a mug or some stickers- were going offer up a few TDWTF-emblazoned hoodies for the best entries. You wont just be the first programmer on your floor to have one- youll be the only programmer you know that has one.
To enter, send an email to byoc15@worsethanfailure.com with a link or attachment of your code. In the body of the email, explain how your cheat works and what we need to do to run your code. You can use any language you like, but we have to be able to run it with minimal setup.
You dont need to build a GUI, but if you do, and you do it using tools from Infragistics, we'll send you a free license (one per entrant, supplies limited). Consider this your Infragistics bonus.
Assume we have access to stock Windows, Linux and OSX instances, if we need to run your software locally. You could target MUMPS running on a mainframe, but we can't run it, and you probably won't win. You must get your submission in before 11:59PM Eastern Time, Sunday the 6th of September to be eligible for judging. We'll announce the winners next Wednesday.
The overall winner will be chosen by how interesting and fun we think their solution and cheat is.
Thanks to Infragistics for making this possible.
A worldwide leader in user experience, Infragistics helps developers build amazing applications. More than a million developers trust Infragistics for enterprise-ready user interface toolsets that deliver high-performance applications for Web, Windows and mobile applications. Their Indigo Studio is a design tool for rapid, interactive prototyping.
Метки: Bring Your Own Code |
CodeSOD: Byte me |
The great thing about Android is the low barrier to entry: thanks to open-source tooling, emulators, and the decision to build on a language often taught in schools, just about anyone can write a simple little app.
The worst thing about Android is the low barrier to entry. Just about anyone can write a simple little app, whether they know what they're doing or not. The following code snippet is intended to take bytes from an InputStream (a common paradigm for web in Java) and save them into a file.
public void download(InputStream in, String fileName) {
Vector vector = new Vector<>();
byte[] tmpByteArray = new byte[1024];
while (true) {
int r = in.read(tmpByteArray, 0, 1024);
if (r == -1) {
break;
}
for (int i = 0; i < r; i++) {
vector.add(Byte.valueOf(tmpByteArray[i]));
}
}
byte[] byteArray = new byte[vector.size()];
for (int i = 0; i < vector.size(); i++) {
byteArray[i] = ((Byte) vector.elementAt(i)).byteValue();
}
File fout = new File(this.outFileDir.getAbsolutePath(), fileName);
FileOutputStream fos = new FileOutputStream(fout);
BufferedOutputStream bos = new BufferedOutputStream(fos);
bos.write(byteArray);
bos.close();
fos.close();
in.close();
}
Our Anonymous submitter writes:
At first glance it doesn't look that bad, but only until you start to estimate the memory usage of this nice little code snippet.
For those not familiar with Java and its internal representation of objects a little hint: Every Byte is a Java Object and every Java Object has a memory overhead of several bytes because of memory alignment. In detail the overhead for every object is 8 bytes for a 32-bit JVM for the Android Dalvik VM.
The code copies every received byte into a Byte object and then copies it again into a byte array before writing everything into the file-system! Hence this tiny little code snippet requires about 9-10 times the file-size in memory for downloading and saving the file.
Incidentally, the last time this author had to write code for this purpose, it looked something like this:
BufferedReader inputReader = new BufferedReader(new InputStreamReader(in));
File destination = new File(fileName);
FileWriter output = new FileWriter(destination)
String line;
while ((line = inputReader.readLine()) != null) {
output.write(line + "\n");
}
output.close();
in.close();
Метки: CodeSOD |
Enlisted |
After a 6-year enlistment with the United States Air Force, followed by a 4-year degree in Computer Science (paid for by Uncle Sam), Tony S. joined with a small company that specialized in criminal background checks. No more unpaid overtime! hed thought to himself upon joining the civilian world for the first time since high school. No more screaming officers! No more sleepless nights from trying to meet deadlines!
Tony quickly noticed his new employer had efficiency problems. They tracked everything the company did in an Excel/VBA monstrosity that had been cobbled together. Wanting to show initiative, Tony suggested that he could build a database-driven website to replace it. His boss agreed, and they drew out a three-phase plan to implement and deploy his idea.
After a month of design and development, Phase 1 was well underway. Tony had written the basic data-layer for the application. It didnt have many features, but could easily show some of the companys in-progress projects hed manually imported from the toxic hellstew of spreadsheets. Pleased with his progress, he scheduled a meeting with his boss and the data leads to demo it.
During the demo, his boss cut him off. Who authorized this project? she interrupted.
Tony paused. Did she not remember helping him plan it all? I… um… uh… you did.
I did no such thing. How long have you been working on this?
About four weeks now, he replied, acutely aware of the burning sensation on his cheeks as his boss grilled him in front of everyone. You helped me pl…
We cant have our employees spending this much time on unapproved projects! she yelled.
The meeting got worse from there. In the end, his boss relented, but declared that Phase 2 and Phase 3 would never happen. In addition, she gave him a list of features that had to be delivered by the Phase 1 deadline- a list that conveniently included everything from Phases 2 and 3.
Tony tried to continue on the project, but now that his boss was involved, requirements constantly changed. One day, he was asked to add in a full-fledged help-desk ticket tracker, and the next day he was asked to rip out the third-party helpdesk library hed integrated because it was a security risk. His boss demanded that it be implemented from scratch, and refused to budge on the deadline.
Thats not enough time! Tony complained.
It will be, she said, fixing him with a stare so cold it could freeze Hell. Or you wont be working here anymore. With a wave of her hand, she dismissed him.
Tony soon found himself working 1516 hours a day, 68 days a week, trying to meet the deadline and keep his job. He worked hard, but the hours took their toll. Life left his eyes and he slowly decayed into a zombie which smelled strongly of coffee.
Two days before the deadline, Tony completed the last of the requirements. The application was buggier than a roach motel, but most of the issues could be worked around and fixed later. He headed home, though he didnt remember it, and passed out. Hed only had about 3 hours of sleep in the past few days. Simple concepts, like sleep, time, and food, and sleep, and lists were now only vague memories he couldnt quite grasp anymore.
He woke to the buzzing of his cellphone. The clock read 1PM, and the caller ID was for his boss.
Why arent you at work today? Tony, this is unacceptable! I cant have lazy employees skipping out on work! I am going to have to discipline you, and this is going in your file. Get to the office RIGHT NOW!!!
Enough was enough. Tony didnt rush straight to the office. He spent the next ten minutes typing up his resignation letter. Then he went to work. As soon as he entered his bosss office, she launched into a new round of screaming anger. You are the worst employee Ive ever had! You invent make-work projects, you come in late. Look how late it is! Dont think I didnt notice you leaving early and taking long lunches! Theres only one response for this inexcusable pattern of unprofessional behavior- I have to fire you! Stay here for the rest of the day and get your stuff sorted out, and Ill arrange for HR to…
Tony mentally phased out at that point. He dropped his resignation letter on her desk, turned around, and walked out. He paid no attention to the shouts that grew louder as his boss became more inflamed from this apparent insubordination. As he left the building, he considered re-enlisting. Maybe scrubbing toilets with a toothbrush at oh-dark-thirty while getting screamed at by a drunk Second Lieutenant with an ego problem, after a double-shift of guard duty, wasnt so bad after all.
Метки: Feature Articles |
Error'd: Political Errors |
"I agree, Google. When it comes to news coverage of politically sensitive topics, many errors have occurred," writes Scott.
"Jay F. wrote, "Not quite what I'd call 'free'..."
"Does this mean I'm the first? Or that my ID doesn't exist? Or did they just insult me and call me a zero?" writes Michael.
Josef V. wrote, "You like EventArgs? I've got EventArgs for days and days."
"To be honest, I don't want either Chuck or Wizards of the Coast sending me emails, so this works out just fine," Andrew P. wrote.
"I gave up on waiting in the queue to chat with a Microsoft representative after noticing the change in my 'progress'," Michael P. wrote.
"Wow, they sure have some strange weather up in Toronto," writes Geoff.
"Great. Not only did Word crash, but now I'm seeing double!" Vance wrote.
Метки: Error'd |
No Changes Please |
A new codebase at a new job is a lot like a new relationship: everythings great until you really get to know each other. Just ask Bradley, who joined Javatechsoft Industries a few months ago. He was brought on to lend a hand with an overdue project. The pay was good, the job came with life insurance, and he had plenty of experience with Enterprise Java. It seemed like the perfect fit.
Specs came in, Bradley shipped code out, and their honeymoon was smooth sailing. The bad things crept up slowly, poking their heads out of the code in funny little ways that didnt seem like a big deal, they were kind of cute, until…
273 warnings
Whats this? Bradley had just compiled a module he hadnt seen before. There were a lot of warnings, sure, but they were all pretty straightforward:
WARNING: Import xxx is never used.
WARNING: Import yyy is never used.
WARNING: Type SomeType is a raw type. References to generic type SomeType should be parameterized
It looked like code written in a hurry. Bradley was more than happy to clean it up. He started by removing the unused imports, and committed his changes. He had left the module far behind by the time the project lead, Bill, caught up to him on instant message.
BILL: brad, hey, we need to revert your changes ok? plz dont make changes other than whats in specs
It was pedantic and poorly spelled, but Bradley understood where Bill was coming from. Still, he hoped that cleaning up the sloppy code was on the roadmap.
BRADLEY: Sorry about that. Whens this code due to be fixed?
BILL: we need to run tests on all affected modules b4 we touch any code
BRADLEY: Even when were just removing unused imports?
BILL: yup
BRADLEY: Okay, but I can run the unit tests for that module…
BILL: there are none. all code to be tested live!1!
No wonder they hadnt cleaned anything up. Bradley kept delivering the new functionality, but he itched to leave every codebase a little better than he found it. Finding himself with some extra time, he decided to get them off the ground with more unit tests. The first step was to mock out the data access layer. He found all beans inherited from an abstract base class, like so:
class MyAbstractBean {
private JDBCTemplate jdbcTemplate;
}
That was a problem. They were using the database adapter object by explicitly naming its type, instead of using its interface, JDBCOperations
. With the interface, he could have mocked out DB access in his tests. He fired up the IM to ask Bill about this.
BILL: whats jdbc operatoins?
BRADLEY: Its the interface that JDBCTemplate implements. By switching the declaration I can automate testing for this code
BILL: we dont have time for new stuff rite now
BRADLEY: Its not new. Its been in Java since 2.5…
BILL: anyway, we cant change the base class then we have to test everything in the code! just stick to the specs! PLZ!!!1!
Javatechsoft Industries had backed themselves into the classic testing trap: it was too risky to make changes, because they had no automated tests, but the only way to get automated test was to make changes. It was an anti-pattern Bradley had seen many times before, and he had to admit, he hadnt been hired to drag them kicking and screaming out of the hole theyd dug. He put his head down, and focused on writing the best code he could while sticking to the specs. If he did that, hed be safe… he thought.
His latest assignment was simple: implement a SOAP call to the service FooService, following these steps:
The functions for each step already existed, so implementing this should have been a cakewalk. He pulled up the Javatechsoft Industries class:
class MySOAPHelper {
Connection openConnection(...params ...) throws Exception { ... }
void sendMessage(...params ...) throws Exception { ... }
String retrieveMessage(...params...) throws Exception { ... }
void closeConnection(...params ...) throws Exception { ... }
}
WHAT? Bradley asked his monitor. The methods all threw Exception
, with no customized type. He could still trap the errors and map them to the integer error codes requested, but it made for a lot more work. This time, he figured improving the codebase was in line with the spec:
Connection openConnection(...params ...) throws SOAPOpenConnectionException { ... }
void sendMessage(...params ...) throws SOAPSendMessageException { ... }
String retrieveMessage(...params...) throws SOAPRetrieveMessageException { ... }
void closeConnection(...params ...) throws SOAPCloseConnectionException { ... }
With those custom Exception
types, the rest of the assignment was easy. So easy, Bradley took an extra-long lunch. When he came back:
BILL: why did you change all these functoins?!? they affect 23% of the code! we cant change them! EVER!
BRADLEY: I could either write broken, fragile code, or they can throw typed exceptions. I could write a separate wrapper class?
BILL: NO. then u have 2 different ways of doing things. code should be CONSISTENT.
Only the instant messengers lack of emoji support saved Bradleys job.
Метки: Feature Articles |
Bring Your Own Code: A Fever on a Crappy Day |
It feels like forever ago, we introduced the Lucky Deuce casino contest. This is a series of challenges, brought to you by our pals over at Infragistics, where we call on you to help us build a scoundrels casino. We are nearing the end of this little BYOC contest- this week is our last "all original" round, and next week, we'll introduce one final challenge that leverages code you may have already written for this contest.
Last week, you had a tricky little problem: you needed to write some code that looked like it was going to cheat, but really would get the cheater caught.
Before we get to the Honorable Mentions, lets once again tip our hat to Alexander, who once again decided to enter using APL. What can I say, Im a sucker for any language that requires a specialized keyboard to write.
cards<-{
Метки: Bring Your Own Code |
CodeSOD: Foxy Checksum |
Pavel D inherited some… well call it software… that helps run warehouse operations for a boiler/heating manufacturer. That software was a Visual FoxPro database.
Now, this application needs to read barcodes off of products in the warehouse. Since the laser-scanners can sometimes mis-read those barcodes, the database uses a custom check-sum algorithm.
FUNCTION GetCheckSum
LPARAMETERS lcSerNum
LOCAL lnCalcSum, lnI, lcCheckSum
m.lnCalcSum=0
FOR lnI=1 TO LEN( m.lcSerNum )
DO CASE
CASE SUBSTR(m.lcSerNum, m.lnI,1)= ' '
m.lnCalcSum= m.lnCalcSum+0*m.lnI
CASE SUBSTR(m.lcSerNum, m.lnI,1)= '!'
m.lnCalcSum= m.lnCalcSum+1*m.lnI
CASE SUBSTR(m.lcSerNum, m.lnI,1)= '"'
m.lnCalcSum= m.lnCalcSum+2*m.lnI
CASE SUBSTR(m.lcSerNum, m.lnI,1)= '#'
m.lnCalcSum= m.lnCalcSum+3*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='$'
m.lnCalcSum=m.lnCalcSum+4*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='%'
m.lnCalcSum=m.lnCalcSum+5*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='&'
m.lnCalcSum=m.lnCalcSum+6*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)="'"
m.lnCalcSum=m.lnCalcSum+7*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='('
m.lnCalcSum=m.lnCalcSum+8*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)=')'
m.lnCalcSum=m.lnCalcSum+9*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='*'
m.lnCalcSum=m.lnCalcSum+10*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='+'
m.lnCalcSum=m.lnCalcSum+11*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)=','
m.lnCalcSum=m.lnCalcSum+12*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='-'
m.lnCalcSum=m.lnCalcSum+13*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='.'
m.lnCalcSum=m.lnCalcSum+14*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='/'
m.lnCalcSum=m.lnCalcSum+15*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='0'
m.lnCalcSum=m.lnCalcSum+16*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='1'
m.lnCalcSum=m.lnCalcSum+17*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='2'
m.lnCalcSum=m.lnCalcSum+18*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='3'
m.lnCalcSum=m.lnCalcSum+19*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='4'
m.lnCalcSum=m.lnCalcSum+20*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='5'
m.lnCalcSum=m.lnCalcSum+21*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='6'
m.lnCalcSum=m.lnCalcSum+22*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='7'
m.lnCalcSum=m.lnCalcSum+23*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='8'
m.lnCalcSum=m.lnCalcSum+24*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='9'
m.lnCalcSum=m.lnCalcSum+25*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)=':'
m.lnCalcSum=m.lnCalcSum+26*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)=';'
m.lnCalcSum=m.lnCalcSum+27*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='<'
m.lnCalcSum=m.lnCalcSum+28*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='='
m.lnCalcSum=m.lnCalcSum+29*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='>'
m.lnCalcSum=m.lnCalcSum+30*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='?'
m.lnCalcSum=m.lnCalcSum+31*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='@'
m.lnCalcSum=m.lnCalcSum+32*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='A'
m.lnCalcSum=m.lnCalcSum+33*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='B'
m.lnCalcSum=m.lnCalcSum+34*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='C'
m.lnCalcSum=m.lnCalcSum+35*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='D'
m.lnCalcSum=m.lnCalcSum+36*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='E'
m.lnCalcSum=m.lnCalcSum+37*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='F'
m.lnCalcSum=m.lnCalcSum+38*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='G'
m.lnCalcSum=m.lnCalcSum+39*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='H'
m.lnCalcSum=m.lnCalcSum+40*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='I'
m.lnCalcSum=m.lnCalcSum+41*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='J'
m.lnCalcSum=m.lnCalcSum+42*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='K'
m.lnCalcSum=m.lnCalcSum+43*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='L'
m.lnCalcSum=m.lnCalcSum+44*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='M'
m.lnCalcSum=m.lnCalcSum+45*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='N'
m.lnCalcSum=m.lnCalcSum+46*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='O'
m.lnCalcSum=m.lnCalcSum+47*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='P'
m.lnCalcSum=m.lnCalcSum+48*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='Q'
m.lnCalcSum=m.lnCalcSum+49*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='R'
m.lnCalcSum=m.lnCalcSum+50*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='S'
m.lnCalcSum=m.lnCalcSum+51*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='T'
m.lnCalcSum=m.lnCalcSum+52*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='U'
m.lnCalcSum=m.lnCalcSum+53*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='V'
m.lnCalcSum=m.lnCalcSum+54*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='W'
m.lnCalcSum=m.lnCalcSum+55*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='X'
m.lnCalcSum=m.lnCalcSum+56*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='Y'
m.lnCalcSum=m.lnCalcSum+57*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='Z'
m.lnCalcSum=m.lnCalcSum+58*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='['
m.lnCalcSum=m.lnCalcSum+59*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='\'
m.lnCalcSum=m.lnCalcSum+60*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)=']'
m.lnCalcSum=m.lnCalcSum+61*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='^'
m.lnCalcSum=m.lnCalcSum+62*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='_'
m.lnCalcSum=m.lnCalcSum+63*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='`'
m.lnCalcSum=m.lnCalcSum+64*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='a'
m.lnCalcSum=m.lnCalcSum+65*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='b'
m.lnCalcSum=m.lnCalcSum+66*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='c'
m.lnCalcSum=m.lnCalcSum+67*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='d'
m.lnCalcSum=m.lnCalcSum+68*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='e'
m.lnCalcSum=m.lnCalcSum+69*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='f'
m.lnCalcSum=m.lnCalcSum+70*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='g'
m.lnCalcSum=m.lnCalcSum+71*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='h'
m.lnCalcSum=m.lnCalcSum+72*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='i'
m.lnCalcSum=m.lnCalcSum+73*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='j'
m.lnCalcSum=m.lnCalcSum+74*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='k'
m.lnCalcSum=m.lnCalcSum+75*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='l'
m.lnCalcSum=m.lnCalcSum+76*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='m'
m.lnCalcSum=m.lnCalcSum+77*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='n'
m.lnCalcSum=m.lnCalcSum+78*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='o'
m.lnCalcSum=m.lnCalcSum+79*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='p'
m.lnCalcSum=m.lnCalcSum+80*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='q'
m.lnCalcSum=m.lnCalcSum+81*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='r'
m.lnCalcSum=m.lnCalcSum+82*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='s'
m.lnCalcSum=m.lnCalcSum+83*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='t'
m.lnCalcSum=m.lnCalcSum+84*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='u'
m.lnCalcSum=m.lnCalcSum+85*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='v'
m.lnCalcSum=m.lnCalcSum+86*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='w'
m.lnCalcSum=m.lnCalcSum+87*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='x'
m.lnCalcSum=m.lnCalcSum+88*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='y'
m.lnCalcSum=m.lnCalcSum+89*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='z'
m.lnCalcSum=m.lnCalcSum+90*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='{'
m.lnCalcSum=m.lnCalcSum+91*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='|'
m.lnCalcSum=m.lnCalcSum+92*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='}'
m.lnCalcSum=m.lnCalcSum+93*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='~'
m.lnCalcSum=m.lnCalcSum+94*m.lnI
ENDCASE
ENDFOR
m.lcCheckSum = RIGHT(STR(m.lnCalcSum,7,0),1)
RETURN m.lcCheckSum
ENDFUNC
Now, the obvious issue here is that the entire CASE statement could be replaced with a call to ASC, which returns the ASCII code for a given character , but as Pavel notes, the value of a character at position which equals a multiple of 10 is ignored.
Pavel was tasked with reimplementing this in Python, and after a little thought, recreated the function in a much more concise fashion:
def validate(sn):
checksum = 0
for i, x in enumerate(sn[:-1]):
checksum += (i + 1) * (ord(x) - 32)
return str(checksum % 10) == sn[-1]
Метки: CodeSOD |
The Old Ways |
Greg never thought hed meet a real-life mentat.
Were so happy to have you aboard, said Jordan, the CEO of IniTech. She showed Greg to the back end of the office, to a closed door marked with just one word: Frank. Jordan, not bothering to knock, opening the door.
Greg was overwhelmed with the stench of burned coffee and old-man smell. The office was unadorned and dark, the blinds drawn, illuminated by the blue light coming from an aging CRT screen. He saw a wrinkled scalp behind a tall, black office chair.
Im busy, Frank said.
Jordan cleared her throat. This is your new programming partner.
Im Greg. Its nice to meet you Greg offered his hand, but a wrinkled appendage slapped it away.
Get yourself a chair. I know where everything is. You just show me you can type.
Greg shot Jordan a glance as they left Franks office.
Hes been with us 22 years, she said. He knows everything about our code. But his typings not what it used to be. Just do what he says. With some luck hell be retiring in a few months.
Greg pulled a spare office chair into Franks den. He could see Franks face in profile now, resembling the mummy of Rameses II. Frank slid his keyboard to Greg. Open C:\project.make
in Vim, Frank said, and go to line 22.
Greg thought it was odd that a makefile would right under C:\
, but he did so. He moved the cursor to line 22.
Increment $VERSION
to 8.3.3
.
Greg noticed that Frank had his eyes shut, but humored him. In fact, line 22 did declare a $VERSION
constant, and Greg changed it to 8.3.3
.
Youll be suitable, Frank said, crossing his arms. Youll do your work from the SMB server. Dont make any changes without my authorization first.
Back at his desk, Greg found the SMB server where Frank kept all of his code. Or rather, the SMB mapped all of the files on Franks hard drive. Curious, Greg searched for .pas
, .make
, and other source files, wondering why Frank would keep his principle makefile under C:\
.
There were 440 source files, about 200 megabytes, spread out all over the directory strucure. C:\Windows\System32
, C:\Users\Shared\Project
, C:\Program Files\
& Franks entire computer was the de facto source repository.
Greg knew if he ever had to make an on-the-fly change to the source, it would take hours just tracking down the right file on SMB. Surely they had a repository he could check changes into. Greg took a deep breath and re-entered Franks den.
Frank, do we have any of this in a repo somewhere? I dont want to SMB onto your computer every time we make a change. What if we have to patch something overnight?
What?! Frank rose from his office chair, unsteady on his disused legs. There will be no code changes without my direct supervision! Its worked just fine for 22 years. Is that understood?
Greg endured this for several months. Frank would harbor no suggestions of version control or repos. Everything, Frank said, was in his head. As long as no one changed the source without his permission, he would know where everything was.
Despite his frustrations, it greatly impressed Greg. Especially when Frank had memorized loop variables such as these:
for RecursiveWaypointCompressionThreadModuleIndexVerifierPropertyHandleIndex := 1 to 99 do ...
Less amusing was Franks insistence on using HEX constants for any encoded string. You cant trust any string encoding, Frank said. It even extended to embedded web pages in their embedded manual:
const
ThirdWebPage : array of byte = [ $2d, $20, ... 660k OF HEX CONSTS..... ];
JQuery33WebPage : array of byte = [ $2d, $20, ... 3,660k OF HEX CONSTS..... ];
But Greg wondered. What would happen if he slipped in just a little change? How long would it take before Frank found out?
One night, he came into the office and logged into Franks SMB server. He opened a file and found an innocuous for-loop block. He replaced the twenty-something variable name with i
, saved a backup on his own machine, and went home.
Greg arrived in the office late that morning, stuck in traffic, and was met by Jordan at the door. Keep this quiet, but Frank just passed away.
Was it last night?
Brain aneurysm in his sleep.
Frank probably died before he had a chance to see Gregs unauthorized change. Greg would never know if Frank actually had the entire codebase memorized. Sometimes Greg would memorize a line or two, or find himself looking up mnemonic tricks to remember long sequences of characters. But it wasnt like Frank rubbed off on him. Not really.
Метки: Feature Articles |
Error'd: Nil, null, nihilism |
"Figures. A suggestion devoid of meaning," writes Blake R..
"I guess since I am an Avis First member I am eligible for free upgrades?" wrote Ryan, "Or maybe they are just reminding me that you never really get the class of vehicle you reserve."
Gordon writes, "Always bound, maybe, but I sure can't find those keys."
Ali B. writes, "What is this? Some kind of code custody dispute?"
"I went onto Argos' website but, unfortunately, they didn't have anything that I was looking for," Luke G. wrote.
"According to Equifax, I have been building my credit history for a lonnnnnnnng time," Michael P. wrote.
"Worst of all, if my name wasn't 'good enough,' I don't even know how long it should be," Quentin G. "I better not pick 'other' else the consequences might be a bit harsh!"
"Sorry, Avast, I have to disagree. In fact, I'm pretty sure that I have plenty of time," German E. writes.
Метки: Error'd |