// Things that maybe should go in Zig standard library at some point
const std = @import("std");
const bun = @import("root").bun;
pub fn Key(comptime Map: type) type {
return FieldType(Map.KV, "key").?;
}
pub fn Value(comptime Map: type) type {
return FieldType(Map.KV, "value").?;
}
pub fn fromEntries(
comptime Map: type,
allocator: std.mem.Allocator,
comptime EntryType: type,
entries: EntryType,
) !Map {
var map: Map = undefined;
if (@hasField(Map, "allocator")) {
map = Map.init(allocator);
} else {
map = Map{};
}
if (comptime std.meta.trait.isIndexable(EntryType)) {
if (comptime !needsAllocator(Map.ensureUnusedCapacity)) {
try map.ensureUnusedCapacity(entries.len);
} else {
try map.ensureUnusedCapacity(allocator, entries.len);
}
comptime var i: usize = 0;
inline while (i < std.meta.fields(EntryType).len) : (i += 1) {
map.putAssumeCapacity(entries[i].@"0", entries[i].@"1");
}
return map;
} else if (comptime std.meta.trait.isContainer(EntryType) and @hasDecl(EntryType, "count")) {
if (comptime !needsAllocator(Map.ensureUnusedCapacity)) {
try map.ensureUnusedCapacity(entries.count());
} else {
try map.ensureUnusedCapacity(allocator, entries.count());
}
if (comptime @hasDecl(EntryType, "iterator")) {
var iter = entries.iterator();
while (iter.next()) |entry| {
map.putAssumeCapacity(entry.@"0", entry.@"1");
}
return map;
}
} else if (comptime std.meta.trait.isContainer(EntryType) and std.meta.fields(EntryType).len > 0) {
if (comptime !needsAllocator(Map.ensureUnusedCapacity)) {
try map.ensureUnusedCapacity(std.meta.fields(EntryType).len);
} else {
try map.ensureUnusedCapacity(allocator, std.meta.fields(EntryType).len);
}
inline for (comptime std.meta.fieldNames(@TypeOf(EntryType))) |entry| {
map.putAssumeCapacity(entry.@"0", entry.@"1");
}
return map;
} else if (comptime std.meta.trait.isConstPtr(EntryType) and std.meta.fields(std.meta.Child(EntryType)).len > 0) {
if (comptime !needsAllocator(Map.ensureUnusedCapacity)) {
try map.ensureUnusedCapacity(std.meta.fields(std.meta.Child(EntryType)).len);
} else {
try map.ensureUnusedCapacity(allocator, std.meta.fields(std.meta.Child(EntryType)).len);
}
comptime var i: usize = 0;
inline while (i < std.meta.fields(std.meta.Child(EntryType)).len) : (i += 1) {
map.putAssumeCapacity(entries.*[i].@"0", entries.*[i].@"1");
}
return map;
}
@compileError("Cannot construct Map from entries of type " ++ @typeName(EntryType));
}
pub fn fromMapLike(
comptime Map: type,
allocator: std.mem.Allocator,
entries: anytype,
) !Map {
var map: Map = undefined;
if (comptime @hasField(Map, "allocator")) {
map = Map.init(allocator);
} else {
map = Map{};
}
try map.ensureUnusedCapacity(entries.count());
var iter = entries.iterator();
while (iter.next()) |entry| {
map.putAssumeCapacityNoClobber(entry.key_ptr.*, entry.value_ptr.*);
}
return map;
}
pub fn FieldType(comptime Map: type, comptime name: []const u8) ?type {
const i = std.meta.fieldIndex(Map, name) orelse return null;
const field = std.meta.fields(Map)[i];
return field.field_type;
}
pub fn Of(comptime ArrayLike: type) type {
if (std.meta.trait.isSlice(ArrayLike)) {
return std.meta.Child(ArrayLike);
}
if (comptime @hasDecl(ArrayLike, "Elem")) {
return ArrayLike.Elem;
}
if (comptime @hasField(ArrayLike, "items")) {
return std.meta.Child(FieldType(ArrayLike, "items").?);
}
if (comptime @hasField(ArrayLike, "ptr")) {
return std.meta.Child(FieldType(ArrayLike, "ptr").?);
}
@compileError("Cannot infer type within " ++ @typeName(ArrayLike));
}
pub inline fn from(
comptime Array: type,
allocator: std.mem.Allocator,
default: anytype,
) !Array {
const DefaultType = @TypeOf(default);
if (comptime std.meta.trait.isSlice(DefaultType)) {
return fromSlice(Array, allocator, DefaultType, default);
}
if (comptime std.meta.trait.isContainer(DefaultType)) {
if (comptime std.meta.trait.isContainer(Array) and @hasDecl(DefaultType, "put")) {
return fromMapLike(Array, allocator, default);
}
if (comptime @hasField(DefaultType, "items")) {
if (Of(FieldType(DefaultType, "items").?) == Of(Array)) {
return fromSlice(Array, allocator, @TypeOf(default.items), default.items);
}
}
}
if (comptime std.meta.trait.isContainer(Array) and @hasDecl(Array, "put")) {
if (comptime std.meta.trait.isConstPtr(DefaultType) and std.meta.fields(std.meta.Child(DefaultType)).len > 0) {
return fromEntries(Array, allocator, @TypeOf(default.*), default.*);
}
return fromEntries(Array, allocator, DefaultType, default);
}
if (comptime @typeInfo(DefaultType) == .Struct) {
return fromSlice(Array, allocator, DefaultType, default);
}
if (comptime @typeInfo(DefaultType) == .Array) {
return fromSlice(Array, allocator, []const Of(Array), @as([]const Of(Array), &default));
}
return fromSlice(Array, allocator, []const Of(Array), @as([]const Of(Array), default));
}
pub fn concat(
comptime T: type,
dest: []T,
src: []const []const T,
) void {
var remain = dest;
for (src) |group| {
bun.copy(T, remain[0..group.len], group);
remain = remain[group.len..];
}
}
pub fn fromSlice(
comptime Array: type,
allocator: std.mem.Allocator,
comptime DefaultType: type,
default: DefaultType,
) !Array {
var map: Array = undefined;
if (comptime std.meta.trait.isSlice(Array)) {} else if (comptime @hasField(Array, "allocator")) {
map = Array.init(allocator);
} else {
map = Array{};
}
// is it a MultiArrayList?
if (comptime !std.meta.trait.isSlice(Array) and @hasField(Array, "bytes")) {
try map.ensureUnusedCapacity(allocator, default.len);
for (default) |elem| {
map.appendAssumeCapacity(elem);
}
return map;
} else {
var slice: []Of(Array) = undefined;
if (comptime !std.meta.trait.isSlice(Array)) {
// is it an ArrayList with an allocator?
if (comptime !needsAllocator(Array.ensureUnusedCapacity)) {
try map.ensureUnusedCapacity(default.len);
// is it an ArrayList without an allocator?
} else {
try map.ensureUnusedCapacity(allocator, default.len);
}
if (comptime @hasField(Array, "items")) {
map.items.len = default.len;
slice = map.items;
} else if (comptime @hasField(Array, "len")) {
map.len = @as(u32, @intCast(default.len));
slice = map.slice();
} else {
@compileError("Cannot set length of " ++ @typeName(Array));
}
} else if (comptime std.meta.trait.isSlice(Array)) {
slice = try allocator.alloc(Of(Array), default.len);
} else if (comptime @hasField(map, "ptr")) {
slice = try allocator.alloc(Of(Array), default.len);
map = .{
.ptr = slice.ptr,
.len = @as(u32, @truncate(default.len)),
.cap = @as(u32, @truncate(default.len)),
};
}
if (comptime std.meta.trait.isIndexable(DefaultType) and (std.meta.trait.isSlice(DefaultType) or std.meta.trait.is(.Array)(DefaultType))) {
var in = std.mem.sliceAsBytes(default);
var out = std.mem.sliceAsBytes(slice);
@memcpy(out[0..in.len], in);
} else {
@compileError("Needs a more specific type to copy from");
}
if (comptime std.meta.trait.isSlice(Array)) {
return @as(Array, slice);
}
return map;
}
}
/// Say you need to allocate a bunch of tiny arrays
/// You could just do separate allocations for each, but that is slow
/// With std.ArrayList, pointers invalidate on resize and that means it will crash.
/// So a better idea is to batch up your allocations into one larger allocation
/// and then just make all the arrays point to different parts of the larger allocation
pub fn Batcher(comptime Type: type) type {
return struct {
head: []Type,
pub fn init(allocator: std.mem.Allocator, count: usize) !@This() {
var all = try allocator.alloc(Type, count);
return @This(){ .head = all };
}
pub inline fn done(this: *@This()) void {
std.debug.assert(this.head.len == 0);
}
pub inline fn eat(this: *@This(), value: Type) *Type {
return @as(*Type, @ptrCast(&this.head.eat1(value).ptr));
}
pub inline fn eat1(this: *@This(), value: Type) []Type {
var prev = this.head[0..1];
prev[0] = value;
this.head = this.head[1..];
return prev;
}
pub inline fn next(this: *@This(), values: anytype) []Type {
this.head[0..values.len].* = values;
var prev = this.head[0..values.len];
this.head = this.head[values.len..];
return prev;
}
};
}
test "fromEntries" {
const values = try from(std.AutoHashMap(u32, u32), std.heap.page_allocator, .{
.{ 123, 456 },
.{ 789, 101112 },
});
const mapToMap = try from(std.AutoHashMap(u32, u32), std.heap.page_allocator, values);
try std.testing.expectEqual(values.get(123).?, 456);
try std.testing.expectEqual(values.get(789).?, 101112);
try std.testing.expectEqual(mapToMap.get(123).?, 456);
try std.testing.expectEqual(mapToMap.get(789).?, 101112);
}
test "from" {
const values = try from(
[]const u32,
std.heap.page_allocator,
&.{ 1, 2, 3, 4, 5, 6 },
);
try std.testing.expectEqualSlices(u32, &.{ 1, 2, 3, 4, 5, 6 }, values);
}
test "from arraylist" {
const values = try from(
std.ArrayList(u32),
std.heap.page_allocator,
&.{ 1, 2, 3, 4, 5, 6 },
);
try std.testing.expectEqualSlices(u32, &.{ 1, 2, 3, 4, 5, 6 }, values.items);
const cloned = try from(
std.ArrayListUnmanaged(u32),
std.heap.page_allocator,
values,
);
try std.testing.expectEqualSlices(u32, &.{ 1, 2, 3, 4, 5, 6 }, cloned.items);
}
test "from arraylist with struct" {
const Entry = std.meta.Tuple(&.{ u32, u32 });
const values = try from(
std.ArrayList(Entry),
std.heap.page_allocator,
&.{ Entry{ 123, 456 }, Entry{ 123, 456 }, Entry{ 123, 456 }, Entry{ 123, 456 } },
);
try std.testing.expectEqualSlices(Entry, &[_]Entry{ .{ 123, 456 }, .{ 123, 456 }, .{ 123, 456 }, .{ 123, 456 } }, values.items);
}
fn needsAllocator(comptime Fn: anytype) bool {
return std.meta.fields(std.meta.ArgsTuple(@TypeOf(Fn))).len > 2;
}
nfig-refresh
Unnamed repository; edit this file 'description' to name the repository. | |
Age | Commit message (Collapse) | Author | Files | Lines |
|
|
|
|
|
|
|
|
|
|
|
|