aboutsummaryrefslogtreecommitdiff
path: root/src/base/io/seek.c
blob: 4fa1affe6c56540b27c73725b85c248332f2599b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include "internal.h"

int
io·seek(io·Header *io, intptr offset, int whence, intptr *pos)
{
    intptr n,d,cap;

    switch(io->state){
    default:
        fmt·fprint(2, "seek: unknown state %d\n", io->state);
        return io·BufEof;
    case io·BufEnd:
        io->state = io·BufRdr;
        io->ilen = 0;
        io->g = io->e;
        /* fallthrough */
    case io·BufRdr:
        n = offset;
        if(whence == sys·SeekCur){
            n += io·offset(io);
            whence = sys·SeekSet;
        }

        /* can we seek inside our buffer */
        if(whence == sys·SeekSet){
            d = n - io·offset(io);
            cap = io->e - io->g;
            if(-cap <= d && d <= cap){
                io->ilen += d;
                if(d >= 0){
                    if(io->ilen <= 0){
                        *pos = n;
                        return 0;
                    }
                }else{
                    if(io->e - io->g >= -io->ilen){
                        *pos = n;
                        return 0;
                    }
                }
            }
        }

        /* nope, call the kernel to do it for us */
        sys·seek(io->fd, offset, whence, &n);
        io->ilen = 0;
        io->g = io->e;
        break;

    case io·BufWtr:
        io·flush(io);
        sys·seek(io->fd, offset, whence, &n);
        break;
    }
    io->pos = *pos = n;
    return 0;
}