aboutsummaryrefslogtreecommitdiff
path: root/src/storage/image-manager/root.zig
blob: 7fa16079f35b167a2cf892e1579b7a5513c1c718 (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
const std = @import("std");

const memora = @import("memora");
const Storage = memora.Storage;
pub const Image = @import("image.zig");

const Self = @This();

pub const empty: Self = .{};

const Timestamp = struct {
    index: usize,
    node: std.DoublyLinkedList.Node,

    pub fn next(self: *Timestamp) ?*Timestamp {
        return @fieldParentPtr("node", self.node.next orelse return null);
    }

    pub fn prev(self: *Timestamp) ?*Timestamp {
        return @fieldParentPtr("node", self.node.prev orelse return null);
    }
};

items: std.ArrayList(Image) = .empty,
rw_lock: std.Thread.RwLock = .{},
timestamp_order: std.DoublyLinkedList = .{},

pub fn init(
    self: *Self,
    storage: *Storage,
) !void {
    var dir = storage.dir.openDir("image", .{ .iterate = true }) catch blk: {
        try storage.dir.makeDir("image");
        break :blk try storage.dir.openDir("image", .{ .iterate = true });
    };
    defer dir.close();

    var iterator = dir.iterate();

    errdefer self.items.deinit(storage.allocator);

    while (try iterator.next()) |entry| {
        if (entry.kind == .file and std.mem.endsWith(u8, entry.name, ".jpg")) {
            try self.add(
                storage.allocator,
                try .init(storage, entry.name[0..entry.name.len - 4])
            );
        }
    }
}

pub fn add(self: *Self, allocator: std.mem.Allocator, image: Image) !void {
    self.rw_lock.lock();
    defer self.rw_lock.unlock();

    try self.items.append(
        allocator,
        image,
    );

    var current = self.timestamp_order.first;

    if (current == null) {
        const timestamp = try allocator.create(Timestamp);
        timestamp.index = self.items.items.len - 1;
        self.timestamp_order.append(&timestamp.node);
        return;
    }

    while (current) |c| {
        const index = @as(*Timestamp, @fieldParentPtr("node", c)).index;
        if (image.timestamp orelse 0 > self.items.items[index].timestamp orelse 0) {
            const timestamp = try allocator.create(Timestamp);
            timestamp.index = self.items.items.len - 1;
            self.timestamp_order.insertBefore(c, &timestamp.node);
            break;
        }

        current = c.next;
    }
}

pub fn save(
    self: *Self,
    storage: *Storage,
    reader: *std.Io.Reader,
    size: usize,
) !void {
    return self.add(storage.allocator, try Image.new(storage, reader, size));
}

pub fn list(self: *Self) memora.locked.Shared([]Image) {
    self.rw_lock.lockShared();
    return .{
        .value = self.items.items,
        .rw_lock = &self.rw_lock,
    };
}

pub fn first_by_timestamp(self: *Self) memora.locked.Shared(?*Timestamp) {
    self.rw_lock.lockShared();
    return .{
        .value = if (self.timestamp_order.first) |first| @fieldParentPtr("node", first)
        else null,
        .rw_lock = &self.rw_lock,
    };
}