Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

io.WriteSeeker and io.ReadSeeker from []byte or file

Tags:

file-io

go

buffer

I have a method called "DoSomething". DoSomething will take binary source data perform an operation on it, and write out binary data. DoSomething needs to be generic enough to handle either a []byte array or a file handle for both the source and destination. To accomplish this, I have attempted to declare the method like this:

func DoSomething(source *io.ReadSeeker, destination *io.WriteSeeker)

I have implemented the ReadSeeker and WriteSeeker for working with buffers, using my own custom, required methods (if there is a way to automatically accomplish this, I'd love to hear about it as well). Unfortunately, I can't seem to figure out how to create either an io.ReadSeeker or io.WriteSeeker from a file handle. I'm fairly sure there must be some pre-cooked way of handling this without having to manually implement them. Is this possible?

like image 360
shellster Avatar asked Dec 16 '13 01:12

shellster


2 Answers

For anyone else who needs to accomplish something like this, here's what I ended up with. It isn't complete, but it is close enough for what I needed:

package filebuffer

import (
    "bytes"
    "errors"
)

type FileBuffer struct {
    Buffer bytes.Buffer
    Index  int64
}

func NewFileBuffer() FileBuffer {
    return FileBuffer{}
}

func (fbuffer *FileBuffer) Bytes() []byte {
    return fbuffer.Buffer.Bytes()
}

func (fbuffer *FileBuffer) Read(p []byte) (int, error) {
    n, err := bytes.NewBuffer(fbuffer.Buffer.Bytes()[fbuffer.Index:]).Read(p)

    if err == nil {
        if fbuffer.Index+int64(len(p)) < int64(fbuffer.Buffer.Len()) {
            fbuffer.Index += int64(len(p))
        } else {
            fbuffer.Index = int64(fbuffer.Buffer.Len())
        }
    }

    return n, err
}

func (fbuffer *FileBuffer) Write(p []byte) (int, error) {
    n, err := fbuffer.Buffer.Write(p)

    if err == nil {
        fbuffer.Index = int64(fbuffer.Buffer.Len())
    }

    return n, err
}

func (fbuffer *FileBuffer) Seek(offset int64, whence int) (int64, error) {
    var err error
    var Index int64 = 0

    switch whence {
    case 0:
        if offset >= int64(fbuffer.Buffer.Len()) || offset < 0 {
            err = errors.New("Invalid Offset.")
        } else {
            fbuffer.Index = offset
            Index = offset
        }
    default:
        err = errors.New("Unsupported Seek Method.")
    }

    return Index, err
}

You then use it like this:

destination := filebuffer.NewFileBuffer()

source, err := os.Open(pathString)
if err != nil {
    return nil, err
}
defer source.Close()

if _, err := encrypter.Decrypt(source, &destination, password); err != nil {
    return nil, err
}
like image 155
shellster Avatar answered Oct 06 '22 01:10

shellster


A file already implements both of those. You can do something like this:

package main

import (
   "fmt"
   "io"
   "os"
)

func main() {
   f, err := os.Open("test.txt")
   if err != nil {
     fmt.Println(err)
   }
   defer f.Close()
   f2, err := os.Create("test2.txt")
   if err != nil {
      fmt.Println(err)
   }
   defer f2.Close()
   DoSomething(f, f2) 
}

func DoSomething(source io.ReadSeeker, destination io.WriteSeeker) {
   io.Copy(destination, source)
}

Also, you don't need to pass pointers to interfaces, which makes it easier to deal with them.

like image 34
Eve Freeman Avatar answered Oct 05 '22 23:10

Eve Freeman