feat: add fromByte and Status
This commit is contained in:
1 parent
1c123cb114
commit
06617d9292
1 file changed
+158
@@ -0,0 +1,158 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const encode = @import("encode.zig");
|
||||||
|
const dbmod = @import("db.zig");
|
||||||
|
const rowmod = @import("row.zig");
|
||||||
|
const authmod = @import("auth.zig");
|
||||||
|
const ttlmod = @import("ttl.zig");
|
||||||
|
const collmod = @import("collections.zig");
|
||||||
|
const aggregatemod = @import("aggregate.zig");
|
||||||
|
const windowmod = @import("window.zig");
|
||||||
|
const querymod = @import("query.zig");
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
|
/// Guard against a corrupt or hostile length prefix causing an unbounded
|
||||||
|
/// allocation before a single byte of the frame body has been validated.
|
||||||
|
pub const maxFrameBytes: u32 = 64 * 1024 * 1024;
|
||||||
|
|
||||||
|
pub const Op = enum(u8) {
|
||||||
|
ping = 0,
|
||||||
|
get = 1,
|
||||||
|
set = 2,
|
||||||
|
del = 3,
|
||||||
|
scan = 4,
|
||||||
|
prefix = 5,
|
||||||
|
stats = 6,
|
||||||
|
flush = 7,
|
||||||
|
compact = 8,
|
||||||
|
batch = 9,
|
||||||
|
table_select = 10,
|
||||||
|
auth = 11,
|
||||||
|
user_admin = 12,
|
||||||
|
auth_token = 13,
|
||||||
|
metrics = 14,
|
||||||
|
ttl_op = 15,
|
||||||
|
coll_ttl_op = 16,
|
||||||
|
coll_op = 17,
|
||||||
|
txn_op = 18,
|
||||||
|
table_join = 19,
|
||||||
|
table_aggregate = 20,
|
||||||
|
table_explain = 21,
|
||||||
|
table_update = 22,
|
||||||
|
table_delete = 23,
|
||||||
|
table_export = 24,
|
||||||
|
table_import = 25,
|
||||||
|
table_window = 26,
|
||||||
|
subscribe = 27,
|
||||||
|
publish = 28,
|
||||||
|
unsubscribe = 29,
|
||||||
|
|
||||||
|
pub fn fromByte(b: u8) !Op {
|
||||||
|
return std.meta.intToEnum(Op, b) catch error.UnknownOp;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Status = enum(u8) {
|
||||||
|
ok = 0,
|
||||||
|
not_found = 1,
|
||||||
|
bad_request = 2,
|
||||||
|
io_error = 3,
|
||||||
|
conflict = 4,
|
||||||
|
internal = 5,
|
||||||
|
rate_limited = 6,
|
||||||
|
unauthorized = 7,
|
||||||
|
forbidden = 8,
|
||||||
|
|
||||||
|
pub fn fromByte(b: u8) !Status {
|
||||||
|
return std.meta.intToEnum(Status, b) catch error.UnknownStatus;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const ProtoError = error{
|
||||||
|
UnknownOp,
|
||||||
|
UnknownStatus,
|
||||||
|
FrameTooLarge,
|
||||||
|
Malformed,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A frame is the unit exchanged over the wire in both directions: a
|
||||||
|
/// single tag byte (an `Op` on the request side, a `Status` on the
|
||||||
|
/// response side) followed by a little-endian u32 payload length and the
|
||||||
|
/// payload itself.
|
||||||
|
pub const Frame = struct {
|
||||||
|
tag: u8,
|
||||||
|
payload: []u8,
|
||||||
|
allocator: Allocator,
|
||||||
|
|
||||||
|
pub fn deinit(self: *Frame) void {
|
||||||
|
self.allocator.free(self.payload);
|
||||||
|
self.* = undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn writeFrame(writer: anytype, tag: u8, payload: []const u8) !void {
|
||||||
|
if (payload.len > maxFrameBytes) return error.FrameTooLarge;
|
||||||
|
var header: [5]u8 = undefined;
|
||||||
|
header[0] = tag;
|
||||||
|
std.mem.writeInt(u32, header[1..5], @intCast(payload.len), encode.endian);
|
||||||
|
try writer.writeAll(&header);
|
||||||
|
if (payload.len > 0) try writer.writeAll(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn readFrame(allocator: Allocator, reader: anytype) !Frame {
|
||||||
|
var header: [5]u8 = undefined;
|
||||||
|
try reader.readNoEof(&header);
|
||||||
|
const tag = header[0];
|
||||||
|
const len = std.mem.readInt(u32, header[1..5], encode.endian);
|
||||||
|
if (len > maxFrameBytes) return error.FrameTooLarge;
|
||||||
|
const payload = try allocator.alloc(u8, len);
|
||||||
|
errdefer allocator.free(payload);
|
||||||
|
if (len > 0) try reader.readNoEof(payload);
|
||||||
|
return .{ .tag = tag, .payload = payload, .allocator = allocator };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bounds-checked cursor over a decoded payload. Every read returns
|
||||||
|
/// `error.Malformed` instead of panicking when the peer sent a short or
|
||||||
|
/// truncated buffer, since payload bytes come from an untrusted socket.
|
||||||
|
pub const Cursor = struct {
|
||||||
|
data: []const u8,
|
||||||
|
pos: usize = 0,
|
||||||
|
|
||||||
|
pub fn takeByte(self: *Cursor) !u8 {
|
||||||
|
if (self.pos + 1 > self.data.len) return error.Malformed;
|
||||||
|
const v = self.data[self.pos];
|
||||||
|
self.pos += 1;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn takeU32(self: *Cursor) !u32 {
|
||||||
|
if (self.pos + 4 > self.data.len) return error.Malformed;
|
||||||
|
const v = encode.readU32(self.data, self.pos);
|
||||||
|
self.pos += 4;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn takeU64(self: *Cursor) !u64 {
|
||||||
|
if (self.pos + 8 > self.data.len) return error.Malformed;
|
||||||
|
const v = encode.readU64(self.data, self.pos);
|
||||||
|
self.pos += 8;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn takeBytes(self: *Cursor, n: usize) ![]const u8 {
|
||||||
|
if (self.pos + n > self.data.len) return error.Malformed;
|
||||||
|
const v = self.data[self.pos .. self.pos + n];
|
||||||
|
self.pos += n;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn takeLenPrefixed(self: *Cursor) ![]const u8 {
|
||||||
|
const n = try self.takeU32();
|
||||||
|
return self.takeBytes(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn atEnd(self: *Cursor) bool {
|
||||||
|
return self.pos == self.data.len;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Reference in new issue
Block a user