summaryrefslogtreecommitdiff
path: root/src/cache/handle.c
blob: 74db306a1eac2db600a9c0a58423a2777983c6c0 (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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#define AL_LOG_SECTION "cache_handle"
//#define AL_LOG_ENABLE_TRACE
#include <al/log.h>

#include "../codec/codec.h"

#include "handle.h"
#include "entry.h"
#include "threaded_waits.h"

static off_t wait_for_size(struct cch_handle *handle, struct cch_handler *handler)
{
    handle->wait.end = -1;
    if (!handler->wait_for_range(handler, &handle->wait)) {
        log_trace("Wait for size failed.");
        return -1;
    }
    struct cch_backing *backing = handle->entry->backing;
    return backing->get_size_if_known(backing);
}

s32 cch_handle_read(struct cch_handle *handle, u8 *buf, s32 count)
{
    struct cch_handler *handler = handle->entry->handler;
    struct cch_backing *backing = handle->entry->backing;
    // filesize < 0: Size is yet to be evaluated and we can wait on it.
    // filesize = 0: Size is explicitly unknown.
    off_t filesize = backing->get_size_if_known(backing);
    if (filesize < 0) filesize = wait_for_size(handle, handler);
    if (filesize > 0) {
        if (handle->pointer >= filesize) {
            return CAMU_ERR_EOF;
        } else if (handle->pointer + count >= filesize) {
            count = filesize - handle->pointer;
        }
    }
    al_assert(count >= 0);
    handle->wait.start = handle->pointer;
    handle->wait.end = handle->pointer + count;
    if (!handler->wait_for_range(handler, &handle->wait)) {
        log_trace("Wait for range (%zd-%zd) failed.", handle->wait.start, handle->wait.end);
        return CAMU_ERR_EOF;
    }
    size_t available = (size_t)count;
    if (backing->mode == CACHE_BACKING_MAPPED) {
        u8 *ptr = backing->get_ptr(backing, handle->pointer, &available);
        if (ptr && available > 0) {
            al_memcpy(buf, ptr, available);
        }
        backing->unlock(backing);
    } else if (backing->mode == CACHE_BACKING_READ) {
        backing->read(backing, buf, handle->pointer, &available);
    }
    handle->pointer += available;
    log_trace("read(%u), pointer: %zd.", count, handle->pointer);
    return (available > 0) ? (s32)available : CAMU_ERR_EOF;
}

s32 cch_handle_write(struct cch_handle *handle, const u8 *buf, s32 count)
{
    (void)handle;
    (void)buf;
    (void)count;
    return -1;
}

off_t cch_handle_seek(struct cch_handle *handle, off_t offset, s32 whence)
{
    struct cch_handler *handler = handle->entry->handler;
    struct cch_backing *backing = handle->entry->backing;
    off_t filesize = backing->get_size_if_known(backing);
    if (filesize < 0) filesize = wait_for_size(handle, handler);
    if (filesize <= 0) return -1;
    if (whence == CAMU_SEEK_SIZE) {
        log_trace("SEEK_SIZE(), pointer: %zd, filesize: %zd.", handle->pointer, filesize);
        return filesize;
    }
    switch (whence) {
    case SEEK_SET:
        log_trace("SEEK_SET(%zd, %d), pointer: %zd.", offset, whence, handle->pointer);
        if (offset > filesize || offset < 0) {
            return -1;
        }
        handle->pointer = offset;
        break;
    case SEEK_CUR:
        log_trace("SEEK_CUR(%zd, %d), pointer: %zd.", offset, whence, handle->pointer);
        if (handle->pointer + offset > filesize || handle->pointer + offset < 0) {
            return -1;
        }
        handle->pointer += offset;
        break;
    case SEEK_END:
        log_trace("SEEK_END(%zd, %d), pointer: %zd.", offset, whence, handle->pointer);
        handle->pointer = filesize + offset;
        break;
    default:
        return -1;
    }
    return handle->pointer;
}

bool cch_handle_can_seek(struct cch_handle *handle)
{
    struct cch_handler *handler = handle->entry->handler;
    return handler->can_seek(handler);
}

void cch_handle_disable(struct cch_handle *handle)
{
    cch_threaded_wait_disable(&handle->wait);
}

void cch_handle_enable(struct cch_handle *handle)
{
    cch_threaded_wait_enable(&handle->wait);
}