/
payload.zig
133 lines (110 loc) · 4.54 KB
/
payload.zig
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
const std = @import("std");
const beam = @import("beam.zig");
const e = @import("erl_nif.zig");
pub fn Payload(comptime function: anytype) type {
const T = if (@TypeOf(function) == type) function else @TypeOf(function);
const args = switch (@typeInfo(T)) {
.Fn => |f| f.args,
else => @compileError("Payload is only available for a function"),
};
const SF = std.builtin.Type.StructField;
var fields: []const SF = &[_]SF{};
const decls = [0]std.builtin.Type.Declaration{};
for (args) |arg, index| {
const new_field = [1]SF{.{
.name = std.fmt.comptimePrint("{}", .{index}),
.field_type = arg.arg_type.?,
.default_value = null,
.is_comptime = false,
.alignment = 4,
}};
fields = fields ++ &new_field;
}
const result_type_info: std.builtin.Type = .{ .Struct = .{
.layout = .Auto,
.fields = fields,
.decls = &decls,
.is_tuple = true,
} };
return @Type(result_type_info);
}
// checks to see if the function has env as its first term
fn is_env_first(fun: anytype) bool {
const T: type = if (@TypeOf(fun) == type) fun else @TypeOf(fun);
return switch (@typeInfo(T)) {
.Fn => |f| (f.args.len > 0) and (f.args[0].arg_type == beam.env),
.Struct => |s| (s.fields.len > 0) and (s.fields[0].field_type == beam.env),
else => @compileError("is_env_first is only available for a functions and Payloads"),
};
}
// gets the arity of a function f
fn arity(fun: anytype) u8 {
return switch (@typeInfo(@TypeOf(fun))) {
.Fn => |f| if (comptime is_env_first(fun)) f.args.len - 1 else f.args.len,
.Struct => |s| if (comptime is_env_first(fun)) s.fields.len - 1 else s.fields.len,
else => @compileError("arity is only available for a function"),
};
}
// builds up a payload tuple for a function. the error_index parameter is an in-out parameter that stores the
// index of which parameter failed to be stuffed into the payload tuple. Only access it in the case that
// build function fails.
pub fn build(fun: anytype, env: beam.env, argc: c_int, args: [*c]const e.ErlNifTerm, error_index: *u8, build_opts: anytype) !Payload(fun) {
if (comptime is_env_first(fun)) {
return build_with_env(fun, env, argc, args, error_index, build_opts);
} else {
return build_no_env(fun, env, argc, args, error_index, build_opts);
}
}
fn build_with_env(fun: anytype, env: beam.env, argc: c_int, args: [*c]const e.ErlNifTerm, error_index: *u8, build_opts: anytype) !Payload(fun) {
// assertions
const arity_ = comptime arity(fun);
if (argc != arity_) {
@panic("nif function called with wrong arity");
}
var payload: Payload(fun) = undefined;
payload[0] = env;
comptime var arg_index = 0;
inline while (arg_index < arity_) : (arg_index += 1) {
const payload_index = arg_index + 1;
error_index.* = arg_index;
payload[payload_index] = try beam.get(@TypeOf(payload[payload_index]), env, .{ .v = args[arg_index] }, build_opts[arg_index]);
}
return payload;
}
fn build_no_env(fun: anytype, env: beam.env, argc: c_int, args: [*c]const e.ErlNifTerm, error_index: *u8, build_opts: anytype) !Payload(fun) {
// assertions
const arity_ = comptime arity(fun);
if (argc != arity_) {
@panic("nif function called with wrong arity");
}
if (build_opts.len != arity_) {
@compileError("mismatched arity and build opts");
}
var result: Payload(fun) = undefined;
comptime var index = 0;
inline while (index < arity_) : (index += 1) {
error_index.* = index;
result[index] = try beam.get(@TypeOf(result[index]), env, .{ .v = args[index] }, build_opts[index]);
}
return result;
}
pub fn cleanup(payload: anytype, should_clean: anytype) void {
if (@TypeOf(should_clean) == @TypeOf(null)) return;
if (comptime is_env_first(@TypeOf(payload))) {
cleanup_with_env(payload, should_clean);
} else {
cleanup_no_env(payload, should_clean);
}
}
fn cleanup_with_env(payload: anytype, should_clean: anytype) void {
inline for (should_clean) |item_clean_opts, index| {
if (@TypeOf(item_clean_opts) == @TypeOf(null)) continue;
beam.cleanup(payload[index + 1], item_clean_opts);
}
}
fn cleanup_no_env(payload: anytype, should_clean: anytype) void {
inline for (should_clean) |item_clean_opts, index| {
if (@TypeOf(item_clean_opts) == @TypeOf(null)) continue;
beam.cleanup(payload[index], item_clean_opts);
}
}