Where is bytes.NewReaderAt?

I have a nice source of []byte slices (see last post), and now I’d like to do something with them that resembles a filesystem. I was planning on just using a map from name to file contents, which would work ok. But then I remembered seeing the archive/zip package, and I thought how much cooler it would be to just make my prototype filesystem, zip it up, put the zip file into my Go program (see last post) and then access the filesystem via the archive/zip package later.

zip.Decode doesn’t want an io.Reader like lots of Go libraries. Instead it wants an io.ReaderAt. This makes sense, because a Zip file has a table of contents, allowing you to efficiently jump around the archive and pull out just one file. So then I went looking for how to turn a []byte into an io.ReaderAt. I looked all through the bytes package and came up short.

So I did it myself. Here’s how, in case you come across the same kind of problem:

type seekable []byte

func NewSeekable(buf []byte) seekable {
  return buf
}

func (r seekable)ReadAt(p []byte, off int64) (n int, err os.Error) {
  o := int(off)
  copy(p, r[o:o+len(p)])
  return len(p), nil
}

Now you can do something like this:

  b := fs.FileMap["/tmp/test.zip"]
  n, err := zip.NewReader(NewSeekable(b), int64(len(b)))

Warning: The implementation of ReadAt probably has some bugs around the edge cases. I wrote it very fast while just trying to see if this approach would work. There’s also a mismatch of integer sizes that is not handled well by this code. It could be you could reason that you’d never see a problem, but the way it is currently written (by me!) does not pass my “smell test”.

Leave a Reply