MadBlog
Wednesday 29 November 2006

pearls

Refactoring code is (sometimes) a real bit of fun. I've seen a lot of:

 if (!foo) {
     return NULL;
 } else {
     return foo;
 }

or quite a lot of :

 (<boolean expression>) ? true : false

but today I found one I've never seen before:

 foo ? -foo : 0

On C and strings APIs

C default str* functions have very poor semantics, and a whole lot of consistency problems. That's a fact, a well known one.

For those who still don't know them, and only to cite a few:

  • strlen returns a size_t but *printf functions returns int's;
  • str*{cpy,cat} returns a pointer to the destination, which is useless;
  • strncat can returns things that are not strings (because not NUL-terminated);
  • ...

A good string API MUST treat any string as a buffer + its allocated size (not len, I said size, meaning including the terminating NUL). Moreover, it should return informations that avoid the stupid problem of strcat that has to compute the destination length again and again. There is one function of the usual C string API that has it right, if you forget about the int return type problem: snprintf[1]. Why ? because:

  1. it prevents most buffer overflows because you always pass the buffer size;
  2. unlike strncpy the resulting buffer contains a true NUL-terminated string;
  3. it returns the size the string could have had if there was enough space in the buffer, so that a correct reallocation can work with reduced programatical costs;
  4. if you support negative buffer sizes, you can chain as many of those calls, and be safe !

and if you want a proof that it makes code more concise, just go read that patch. EDIT: Here m_strcpy is a function that respects the 4 previous points: m_strcpy(dest, dlen, src) copies src into dest like its brother strcat, but in a buffer of size dlen, and returns in fact, src length, à la snprintf. And if you do m_strcpy(dest, -1, src) it does not breaks, so that I can use it without needing to check if my virtual result length overflowed or not. With those functions it's just obvious how to build new ones with exactly the same semantics.

For those who wonder, yes I've forked mutt the hard way, and am trying to deobfuscate its code (I was shocked to see how bad it was given how I like mutt as a MUA). And here what you see is that this string API semantics is very easy to use, is safe, and extendable. For example, in that patch, rfc822_write_address and rfc822_write_address_single that did had the bad property to truncate the result without any way for the caller to know that now use the usual snprintf semantics for no pain at all.

with those API no need to care about the ending '\0', or about off-by-ones, you always speak of buffer sizes, and when your current virtual write position in the buffer is pos, you just need to call pos += your_strXXX(buf + pos, buflen - pos, ...) and you'll be safe. If you want to know if a realloc is needed, you just have to verify if pos >= buflen, the same as what you have to do for snprintf.

I really wonder why such API are not common in the C world, I see way to much code using the usual C string functions that just are a PITA.

Notes

[1] and not printf like I said in the first place...

Thursday 23 November 2006

Firefox 2.0, two time worse than the previous ?

When I started firefox 2.0 for the first time, well, my reaction was "woah, it's amazingly fast", meaning that it took it really less than 10seconds to launch, like its predecessor. But my joy ended way too soon.

After many releases, firefox is still completely unable to be a decent application in many trivial points, that separately are completely insignificant, but put together make me freak out:

  • When I launch [edit->preferences] that dumb browser launches its configuration window on THE OTHER SCREEN, yeah I run a dual head setup (who doesn't nowadays ?) and that stupid browser opens the window on the screen when I don't have the focus. My WM is obviously set up to launch applications to the screen that has the mouse in it, every single other application I tested do the same, only firefox manages to start that screen elsewhere, unbelieveable.
  • After years, firefox is still completely unable to have decent configuration of key bindings, so that a decent Unix-like configuration could be set up. For example, in every Unix application I use:
    • ^K/^U kills the current edit like to the end/the start. in firefox it goes to the google search/shows the source. Note that "show the source" when you have a newly opened tab, that you are in the location bar, with the empty page underneath, showing the source is completely stupid anyway.
    • ^Q does not quits, it just does nothing, to quit firefox you need to ^W the last opened tab. Okay, I got used to that, except that I use mouse gestures on my desktop, I use down, then left to quit an application, and down, then right to just close a children window of an application. Guess what, I always get confused with firefox, whereas it works great with ANY other application I use (daily or not).
  • When I click e.g. on get new extensions, whereas my browser is configured to open new pages in a new tab, it always insists to fork a brand new window. gosh, how stupid is that ?
  • After years, it's still unable to respect the usual unix X DPI settings, and insists to think my monitor is under 96DPI, whereas for some personnal reasons I've forced it to be 75DPI. Result is, fonts in web pages are incredibly small and unreadable. In firefox 1.5 there was an obscure way to force it to use the system default (WTF is that not the default setting ?!) now it's gone, and I just can't find the setting in the crazy about:config page.
  • In the same vein, when I open a new tab, it resets the font adjustment I did to my own displeasure, instead of opening a new tab with the same Text size. Hey guys, if I zoomed in once already, do you think opening the tab with liliputian fonts again is a great idea ?
  • After years when you do back and that you just posted a form, you still go back to an empty form. Konqueror returns to the form like you submitted it, which allow you to fix a typo in a form and avoid to fill it from scratch again for crappy sites that forgot what you typed when just one of the fields was wrong.
  • Konqueror has a very nice way to deal with accesskeys: when you hit Control it shows tooltips near links that do have accesskeys, to let you know which are available (and you just need to type the letter to activate it), and moreover it automatically assigns accesskeys (trying to use letters from the link if available) to links in their apparition order in the page, so that even non accessible pages, can be browsed without touching the mouse. Instead of that, firefox still use the braindead alt+accesskey logic that totally conflicts with the menu et al shortcuts. People, god, what are you thinking ? do you sometimes look the good ideas that float around ?

But you know what's the worse ? I've had a SINGLE firefox instance to type that blog post. a top revealed me that this instance ended up with:

  • 268m of virtual memory ;
  • 191m of mapped pages ;
  • 76m of "RES" memory ;
  • 14m of code segment ;
  • 134m of data;
  • 22m of shared memory.

Guys, what are you writing ? I remember days when a browser could run in less than 16megabytes of RAM.

Oh yeah, I do use firefox a lot for web developpement, it has a lot of very nice and full featured extensions (like the web developper one). But please, with the years, none of the itchy and frustrating problems of firefox have been fixed, making it completely unsuited for my daily use. Firefox is completely poorly integrated in the standard Unix® environment, and its memory consumption is a real shame[1]. Well, at least firefox is now vista-ready I presume.

EDIT: just for the sake of it: children, close your firefox when you go to sleep, else it makes your computer explode. I've let a firefox run for more than 10minutes now, at least it's what my kernel pretends, and I dare to belive it. firefox now is the most greedy application on my system, it eats no less than 10.8% of my 1Go of RAM. with that incredible result:

3  PID %MEM  VIRT SWAP  RES CODE DATA  SHR nFLT nDRT COMMAND
 12524 10.8  309m 209m 108m  14m 180m  23m  287    0 firefox-bin

So my recomendation of the day: children, don't do this at home!!!

Notes

[1] Just for the record, since the moment where I copied the top output, the firefox instance now uses: 269, 191, 78, 14, 135 and 22m for the same numbers, meaning that it managed to leak ­­- yes leak how can typing 200 characters use so many memory ? ­- more than 2Meg of RAM, damn the time it too me to write that footnote it leaked 3 more Megs.