Skip to content

Instantly share code, notes, and snippets.

@ityonemo
Created September 4, 2022 15:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ityonemo/db1351a305bf1cd103059d29a8de2622 to your computer and use it in GitHub Desktop.
Save ityonemo/db1351a305bf1cd103059d29a8de2622 to your computer and use it in GitHub Desktop.
make for zigler 0.9.1
const beam = @import("beam.zig");
const e = @import("erl_nif.zig");
const std = @import("std");
pub fn make(env: beam.env, value: anytype) beam.term {
const T = @TypeOf(value);
if (T == beam.env) return value;
switch (@typeInfo(T)) {
.Array => return make_array(env, value),
.Pointer => return make_pointer(env, value),
.Int => return make_int(env, value),
.ComptimeInt => return make_comptime_int(env, value),
.Struct => return make_struct(env, value),
.EnumLiteral => return make_enum_literal(env, value),
.Enum => return make_enum(env, value),
.ErrorSet => return make_error(env, value),
.Null => return make_nil(env),
.Float => return make_float(env, value),
else => {
@panic("unknown type encountered");
},
}
}
// TODO: put this in the main namespace.
fn make_nil(env: beam.env) beam.term {
return e.enif_make_atom(env, "nil");
}
fn make_array(env: beam.env, value: anytype) beam.term {
const array = @typeInfo(@TypeOf(value)).Array;
// u8 arrays (sentinel terminated or otherwise) are treated as
// strings.
if (array.child == u8) {
var result: e.ErlNifTerm = undefined;
const buf = e.enif_make_new_binary(env, array.len, &result);
std.mem.copy(u8, buf[0..array.len], value[0..]);
return result;
} else {
@compileError("other arrays not implemented yet");
}
}
fn make_pointer(env: beam.env, value: anytype) beam.term {
const pointer = @typeInfo(@TypeOf(value)).Pointer;
switch (pointer.size) {
.One => {
// pointers are only allowed to be decoded if they are string literals.
const child_info = @typeInfo(pointer.child);
switch (child_info) {
.Array => if (child_info.Array.child == u8) {
return make_array(env, value.*);
} else {
@compileError("this type is unsupported.");
},
else => @compileError("this type is unsupported"),
}
},
.Many => @compileError("not implemented yet"),
.Slice => @compileError("not implemented yet"),
.C => @compileError("not implemented yet"),
}
}
fn make_int(env: beam.env, value: anytype) beam.term {
const int = @typeInfo(@TypeOf(value)).Int;
switch (int.signedness) {
.signed => switch (int.bits) {
0 => return make(env, 0),
1...32 => return e.enif_make_int(env, @intCast(i32, value)),
33...64 => return e.enif_make_int64(env, @intCast(i64, value)),
else => {},
},
.unsigned => switch (int.bits) {
0 => return make(env, 0),
1...32 => return e.enif_make_uint(env, @intCast(u32, value)),
33...64 => return e.enif_make_uint64(env, @intCast(u64, value)),
else => {
const Bigger = std.meta.Int(.unsigned, comptime try std.math.ceilPowerOfTwo(u16, int.bits));
const buf_size = @sizeOf(Bigger);
var result: e.ErlNifTerm = undefined;
var intermediate = @intCast(Bigger, value);
var buf = e.enif_make_new_binary(env, buf_size, &result);
// transfer content.
std.mem.copy(u8, buf.?[0..buf_size], @ptrCast([*]u8, &intermediate)[0..buf_size]);
return result;
},
},
}
unreachable;
}
fn make_comptime_int(env: beam.env, value: anytype) beam.term {
if (value < std.math.minInt(i64)) {
@compileError("directly making a value less than i64 is not supported");
}
if (value < std.math.minInt(i32)) {
return make_int(env, @as(i64, value));
}
if (value < 0) {
return make_int(env, @as(i32, value));
}
if (value <= std.math.maxInt(u32)) {
return make_int(env, @as(u32, value));
}
if (value <= std.math.maxInt(u64)) {
return make_int(env, @as(u64, value));
}
@compileError("directly making a value greater than u64 is not supported");
}
const EMPTY_TUPLE_LIST = [_]beam.term{};
fn make_struct(env: beam.env, value: anytype) beam.term {
const struct_info = @typeInfo(@TypeOf(value)).Struct;
_ = env;
if (struct_info.is_tuple) {
if (value.len > 16_777_215) {
@compileError("The tuple size is too large for the erlang virtual machine");
}
var tuple_list: [value.len]e.ErlNifTerm = undefined;
comptime var index = 0;
inline while (index < value.len) : (index += 1) {
const tuple_term = value[index];
if (@TypeOf(tuple_term) == beam.term) {
tuple_list[index] = tuple_term;
} else {
tuple_list[index] = make(env, tuple_term);
}
}
return e.enif_make_tuple_from_array(env, &tuple_list, value.len);
} else {
const fields = struct_info.fields;
var result: e.ErlNifTerm = undefined;
var keys: [fields.len]e.ErlNifTerm = undefined;
var vals: [fields.len]e.ErlNifTerm = undefined;
inline for (fields) |field, index| {
if (field.name.len > 255) {
@compileError("the length of the struct field name is too large for the erlang virtual machine");
}
keys[index] = e.enif_make_atom_len(env, field.name.ptr, field.name.len);
vals[index] = make(env, @field(value, field.name));
}
_ = e.enif_make_map_from_arrays(env, &keys, &vals, fields.len, &result);
return result;
}
}
fn make_enum_literal(env: beam.env, value: anytype) beam.term {
const tag_name = @tagName(value);
if (tag_name.len > 255) {
@compileError("the length of this enum literal is too large for the erlang virtual machine");
}
return e.enif_make_atom_len(env, tag_name, tag_name.len);
}
fn make_enum(env: beam.env, value: anytype) beam.term {
const tag_name = @tagName(value);
return e.enif_make_atom_len(env, tag_name, tag_name.len);
}
fn make_error(env: beam.env, value: anytype) beam.term {
const tag_name = @errorName(value);
const len: u32 = @truncate(u32, tag_name.len);
return e.enif_make_atom_len(env, tag_name, len);
}
fn make_float(env: beam.env, value: anytype) beam.term {
const floatval = @floatCast(f64, value);
if (std.math.isNan(value)) return make_enum(env, .NaN);
if (std.math.isPositiveInf(value)) return make_enum(env, .infinity);
if (std.math.isNegativeInf(value)) return make_enum(env, .neg_infinity);
return e.enif_make_double(env, floatval);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment