Not a direct answer to your question (your code looks like a reasonable implementation modulo any bugs I did not notice) but: Aren't you over engineering things? If the source is a PDF file, why not also pass the size to the function instead of doing all this work to get it? At some point you have to open the file, right? And checking the size of a file is trivial (it is just a stat call) and then you do not need to do that.
On Fri, Mar 31, 2023 at 4:30 PM Jochen Voss <jochen.v...@gmail.com> wrote: > Dear all, > > I am trying to get the size of an io.ReaderAt, i.e. the offset after > which no more data can be read. I have some working (?) code ( > https://go.dev/play/p/wTouYbaJ7RG , also reproduced below), but I am not > sure whether what I do is correct and the best way to do this. Some > questions: > > - Does the standard library provide a way to get the size of an > io,ReaderAt? > - Is my code correct? > - Is there a better way to do this? > - If a type provides not only a ReadAt() method, but also a Size() > method, would it be save to assume that Size() returns how many bytes are > accessible via ReadAt()? This seems to work for bytes.Reader and > strings.Reader, but there may be types out there where Size() does > something different? > - If ReadAt(p, x) returns io.EOF for an offset x, is it then > guaranteed that then ReadAt(p, y) also returns io.EOF for all y > x? > Or could there be different error messages, or "files with holes", or > whatnot? > - Which types in the standard library provide ReadAt methods? I know > of os.File, strings.Reader, and bytes.Reader. Any others? > > For context: this is for reading the cross reference table of PDF files, > which have to be located by following some convoluted route starting *from > the end* of the PDF file. > > Many thanks, > Jochen > > func getSize(r io.ReaderAt) (int64, error) { > if f, ok := r.(*os.File); ok { > fi, err := f.Stat() > if err != nil { > return 0, err > } > return fi.Size(), nil > } > if b, ok := r.(*bytes.Reader); ok { > return int64(b.Size()), nil > } > if s, ok := r.(*strings.Reader); ok { > return int64(s.Size()), nil > } > > buf := make([]byte, 1024) > n, err := r.ReadAt(buf, 0) > if err == io.EOF { > return int64(n), nil > } else if err != nil { > return 0, err > } > > lowerBound := int64(n) // all bytes before lowerBound are known to be > present > var upperBound int64 // at least one byte before upperBound is known to > be missing > for { > test := 2 * lowerBound > _, err := r.ReadAt(buf[:1], test-1) > if err == io.EOF { > upperBound = test > break > } else if err != nil { > return 0, err > } > lowerBound = test > } > > for lowerBound+1 < upperBound { > test := (lowerBound + upperBound + 1) / 2 > _, err := r.ReadAt(buf[:1], test-1) > if err == io.EOF { > upperBound = test > } else if err != nil { > return 0, err > } else { > lowerBound = test > } > } > return lowerBound, nil > } > > -- > You received this message because you are subscribed to the Google Groups > "golang-nuts" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to golang-nuts+unsubscr...@googlegroups.com. > To view this discussion on the web visit > https://groups.google.com/d/msgid/golang-nuts/aa604ac0-3542-4a9e-adef-a9eaecfd8170n%40googlegroups.com > <https://groups.google.com/d/msgid/golang-nuts/aa604ac0-3542-4a9e-adef-a9eaecfd8170n%40googlegroups.com?utm_medium=email&utm_source=footer> > . > -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAEd86Tw3FQxjr5PpBfKT3eJKS1RTa-CJZyieKxj%2BSVz7Qh_hbg%40mail.gmail.com.