fun with nsILocalFile

Heh, well my first post went out with the date my blog was set up, and not the date I submitted the post, because I edited the dummy post that was initially set up. So this is just a quick post to describe some of the fun we had replacing nsIFileSpec with nsIFile/nsILocalFile

The single biggest gotcha with nsIFile is that if you open an output stream on a file, write to it, and close the stream, the file still thinks its file size is 0, on Windows, because nsLocalFileWin.cpp caches the file size and has no api for invalidating the cache. The workaround is to Clone the nsIFile, or create instance a new nsILocalFile, and InitWithFile(theOldFile), and use the cloned/new object. You can also call Exists(), which invalidates the cache, at least on Windows (not sure about Linux).

There’s no way to create an input+output stream on a file, using the gecko classes. Surprisingly, there’s apparently no code in Firefox that needs to both read and write to the same file, except for the fastload cache, which has its own way of doing that. So I created an nsMsgFileStream class that implements nsIOutputStream, nsIInputStream, and nsISeekableStream. There are two methods in nsMsgUtils.h for this class:

NS_MSG_BASE nsresult MsgGetFileStream(nsILocalFile *file, nsIOutputStream **fileStream);

NS_MSG_BASE nsresult MsgReopenFileStream(nsILocalFile *file, nsIInputStream *fileStream);

Once you have a stream like this, you can QI it to any particular interface. It’s just a wrapper around a PRFileDesc *, with no buffering or anything fancy. We mostly use this object to deal with local mail folders, e.g., reading and writing the x-mozilla-status lines, or updating the x-mozilla-keywords header.

It doesn’t implement nsILineInputStream, because it’s almost as easy to use NS_ReadLine. The one drawback of NS_ReadLine is that it throws off the position of the stream, because it reads ahead. You can use the line buffer to calculate the real offset into the stream, e.g, http://lxr.mozilla.org/mozilla/source/mailnews/local/src/nsLocalMailFolder.cpp#3998

If I made nsMsgFileStream implement nsILineInputStream, then it could fix Tell and Seek to adjust for the NS_ReadLine buffering, if needed.

- David

3 Responses to “fun with nsILocalFile”

  1. Josh Aas says:

    I hate those stat caches on nsIFile. I filed bug 307815 on killing those because they are flat out wrong. However, we now depend on them for perf so I doubt they’ll go away soon.

  2. Alex Vincent (WeirdAl) says:

    regarding file i/o – that’s one of the reasons I’ve been using jslib’s File libraries so much. It provides a nice API.

  3. Mossop says:

    Serious props on posting this today. I just now discovered testcases for some new code failing on Linux just because of these caches.

Leave a Reply