srctree

Andrew Kelley parent fa77b47d 5e684d6c
add a play queue message to the protocol

client/main.zig added: 144, removed: 13, total 131
@@ -67,12 +67,23 @@ export fn message_begin(len: usize) [*]u8 {
 
export fn message_end() void {
const msg_bytes = message_buffer.items;
const header: protocol.Header = @bitCast(msg_bytes[0..@sizeOf(protocol.Header)].*);
 
const files_start = @sizeOf(protocol.Header);
const Tag = protocol.ToClientTag;
switch (msg_bytes[0]) {
@intFromEnum(Tag.full_library) => return fullLibraryMessage(msg_bytes) catch @panic("OOM"),
@intFromEnum(Tag.play_queue) => return playQueueMessage(msg_bytes) catch @panic("OOM"),
else => unreachable,
}
}
 
fn fullLibraryMessage(msg_bytes: []u8) error{OutOfMemory}!void {
const Header = protocol.FullLibraryHeader;
const header: Header = @bitCast(msg_bytes[0..@sizeOf(Header)].*);
 
const files_start = @sizeOf(Header);
const files_end = files_start + header.files_len * @sizeOf(Db.File);
const directories_start = files_end;
const directories_end = directories_start + header.directories_len * @sizeOf(Db.Path);
const directories_end = directories_start + header.flags.directories_len * @sizeOf(Db.Path);
const albums_start = directories_end;
const albums_end = albums_start + header.albums_len * @sizeOf(Db.Album);
const string_bytes = msg_bytes[albums_end..][0..header.string_bytes_len];
@@ -80,7 +91,41 @@ export fn message_end() void {
const files: []const Db.File = @alignCast(std.mem.bytesAsSlice(Db.File, msg_bytes[files_start..files_end]));
const directories: []const Db.Path = @alignCast(std.mem.bytesAsSlice(Db.Path, msg_bytes[directories_start..directories_end]));
const albums: []const Db.Album = @alignCast(std.mem.bytesAsSlice(Db.Album, msg_bytes[albums_start..albums_end]));
updateDb(files, directories, albums, string_bytes) catch @panic("OOM");
try updateDb(files, directories, albums, string_bytes);
}
 
fn playQueueMessage(msg_bytes: []u8) error{OutOfMemory}!void {
const Header = protocol.PlayQueueHeader;
const header: Header = @bitCast(msg_bytes[0..@sizeOf(Header)].*);
 
const removed_start = @sizeOf(Header);
const removed_end = removed_start + header.removed_items_len * @sizeOf(Db.QueueItem.Id);
const added_start = removed_end;
const added_end = added_start + header.added_items_len * @sizeOf(Db.QueueItem);
 
const removed: []const Db.QueueItem.Id =
@alignCast(std.mem.bytesAsSlice(Db.QueueItem.Id, msg_bytes[removed_start..removed_end]));
const added: []const Db.QueueItem =
@alignCast(std.mem.bytesAsSlice(Db.QueueItem, msg_bytes[added_start..added_end]));
 
for (removed) |id| {
_ = db.queue_items.swapRemove(.{
.id = id,
.flags = undefined,
.file = undefined,
});
}
 
switch (added.len) {
0 => {},
1 => try db.queue_items.put(gpa, added[0], {}),
else => {
const old_len = db.queue_items.entries.len;
try db.queue_items.entries.resize(gpa, old_len + added.len);
@memcpy(db.queue_items.entries.items(.key)[old_len..], added);
try db.queue_items.reIndex(gpa);
},
}
}
 
fn updateDb(
 
server/main.zig added: 144, removed: 13, total 131
@@ -247,10 +247,12 @@ pub const Server = struct {
const directories = std.mem.sliceAsBytes(s.db.directories.keys());
const albums = std.mem.sliceAsBytes(s.db.albums.keys());
const string_bytes = s.db.string_bytes.items;
const Header = protocol.Header;
const Header = protocol.FullLibraryHeader;
const header: Header = .{
.files_len = @intCast(s.db.files.entries.len),
.directories_len = @intCast(s.db.directories.entries.len),
.flags = .{
.directories_len = @intCast(s.db.directories.entries.len),
},
.albums_len = @intCast(s.db.albums.entries.len),
.string_bytes_len = @intCast(s.db.string_bytes.items.len),
};
 
shared/Db.zig added: 144, removed: 13, total 131
@@ -7,6 +7,7 @@ files: std.ArrayHashMapUnmanaged(File, void, File.Hasher, false),
directories: std.AutoArrayHashMapUnmanaged(Path, void),
albums: std.AutoArrayHashMapUnmanaged(Album, void),
string_bytes: std.ArrayListUnmanaged(u8),
queue_items: std.ArrayHashMapUnmanaged(QueueItem, void, QueueItem.Hasher, false),
 
/// Used for finding the index inside `string_bytes`.
string_table: std.HashMapUnmanaged(
@@ -22,6 +23,7 @@ pub const empty: Db = .{
.string_bytes = .{},
.string_table = .{},
.albums = .{},
.queue_items = .{},
};
 
/// Points inside `string_bytes`.
@@ -149,7 +151,7 @@ pub const File = extern struct {
composer: OptionalString,
performer: OptionalString,
 
pub const Index = enum(u16) {
pub const Index = enum(u32) {
_,
};
 
@@ -167,6 +169,38 @@ pub const File = extern struct {
};
};
 
pub const QueueItem = extern struct {
id: Id,
flags: Flags,
file: Db.File.Index,
 
pub const Flags = packed struct(u32) {
is_random: bool,
sort_key: SortKey,
};
 
pub const Id = enum(u32) {
_,
};
 
pub const Hasher = struct {
pub fn hash(h: Hasher, a: QueueItem) u32 {
_ = h;
return std.hash.uint32(@intFromEnum(a.id));
}
 
pub fn eql(h: Hasher, a: QueueItem, b: QueueItem, b_index: usize) bool {
_ = h;
_ = b_index;
return a.id == b.id;
}
};
};
 
pub const SortKey = enum(u31) {
_,
};
 
pub fn deinit(db: *Db, gpa: Allocator) void {
db.files.deinit(gpa);
db.directories.deinit(gpa);
 
shared/protocol.zig added: 144, removed: 13, total 131
@@ -1,10 +1,60 @@
const Db = @import("Db.zig");
 
pub const Header = extern struct {
pub const ToClientTag = enum(u8) {
full_library,
play_queue,
};
 
/// A message that gives the initial state. Following messages will be diffs
/// from this data.
///
/// Trailing:
/// * file for each files_len
/// * directory for each directories_len
/// * album for each albums_len
/// * string_byte for each string_bytes_len
pub const FullLibraryHeader = extern struct {
flags: Flags,
files_len: u32,
directories_len: u32,
albums_len: u32,
string_bytes_len: u32,
 
pub const Flags = packed struct(u32) {
tag: ToClientTag = .full_library,
directories_len: u24,
};
};
 
/// Trailing:
/// * QueueItem.Id for each removed_items_len
/// * QueueItem for each added_items_len
/// * Seek if flags.seek
pub const PlayQueueHeader = extern struct {
flags: Flags,
removed_items_len: u16,
added_items_len: u16,
 
pub const Flags = packed struct(u16) {
tag: ToClientTag = .play_queue,
/// `false` when paused.
playing: bool,
auto_dj: bool,
repeat: Repeat,
seek: bool,
_: u3 = 0,
};
 
pub const Repeat = enum(u2) { off, all, one };
 
pub const Seek = extern struct {
/// The queue item that playback began from.
/// This value only changes when seeking; it does not advance due to playback.
started_item: PlayQueueHeader.Id,
/// How far into the future from the beginning of `current_item` the seek
/// position is. This may be several positions in the queue later than
/// `current_item`.
seek_offset: Db.Duration,
};
};
 
pub const Command = enum(u8) {