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
/**
 * Start scanning the disk to build the dircache.
 * Either transparent or non-transparent build method is used.
 */
int dircache_build(int last_size)
{
    if (dircache_initialized || thread_enabled)
        return -3;

    logf("Building directory cache");
#ifdef HAVE_EEPROM_SETTINGS
    remove_dircache_file();
#endif

    /* Background build, dircache has been previously allocated */
    if (dircache_size > 0)
    {
        thread_enabled = true;
        dircache_initializing = true;
        queue_post(&dircache_queue, DIRCACHE_BUILD, 0);
        return 2;
    }
    if (last_size > DIRCACHE_RESERVE && last_size < DIRCACHE_LIMIT )
    {
        allocated_size = last_size + DIRCACHE_RESERVE;
        dircache_root = buffer_alloc(allocated_size);
        d_names_start =
        d_names_end = ((char*)dircache_root)+allocated_size-1;
        dircache_size = 0;
        thread_enabled = true;
        generate_dot_d_names();

        /* Start a transparent rebuild. */
        queue_post(&dircache_queue, DIRCACHE_BUILD, 0);
        return 3;
    }

    /* We'll use the entire audiobuf to allocate the dircache
     * struct dircache_entrys are allocated from the beginning
     * and their corresponding d_name from the end
     * after generation the buffer will be compacted with DIRCACHE_RESERVE
     * free bytes inbetween */
    audiobuf = (char*)(((intptr_t)audiobuf & ~0x03) + 0x04);
    dircache_root = (struct dircache_entry*)audiobuf;
    d_names_start = d_names_end = audiobufend - 1;
    dircache_size = 0;
    generate_dot_d_names();

    /* Start a non-transparent rebuild. */
    int res = dircache_do_rebuild();
    if (ret < 0)
        return res:

    /* now compact the dircache buffer */
    char* dst;
    ptrdiff_t offset = d_names_start - dst, size_to_move;
    if (offset <= 0) /* something went wrong */
        return -1;

    /* memmove d_names down, there's a possibility of overlap */
    dst = ((char*)&dircache_root[entry_count] + DIRCACHE_RESERVE)
    size_to_move = dircache_size - entry_count*sizeof(struct dircache_entry);
    memmove(dst, d_names_start, size_to_move);
    
    /* fix up pointers to the d_names */
    for(unsigned i = 0; i < entry_count; i++)
        dircache_root[i].d_name -= offset;

    d_names_end -= offset;
    /* equivalent to dircache_size + DIRCACHE_RESERVE */
    allocated_size = (d_names_end - dircache_root);
    reserve_used = 0;
    audiobuf += allocated_size;

    return res;
}