mirror of
https://github.com/oven-sh/bun
synced 2026-02-16 22:01:47 +00:00
Compare commits
3 Commits
claude/upg
...
jarred/shr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d7e104bb1 | ||
|
|
b39a3726ea | ||
|
|
d4dc50e238 |
@@ -1310,12 +1310,14 @@ pub const JSValue = enum(i64) {
|
||||
cmd,
|
||||
|
||||
pub fn has(property: []const u8) bool {
|
||||
return bun.ComptimeEnumMap(BuiltinName).has(property);
|
||||
return map.has(property);
|
||||
}
|
||||
|
||||
pub fn get(property: []const u8) ?BuiltinName {
|
||||
return bun.ComptimeEnumMap(BuiltinName).get(property);
|
||||
return map.get(property);
|
||||
}
|
||||
|
||||
pub const map = bun.ComptimeEnumMap(BuiltinName);
|
||||
};
|
||||
|
||||
pub fn fastGetOrElse(this: JSValue, global: *JSGlobalObject, builtin_name: BuiltinName, alternate: ?JSC.JSValue) ?JSValue {
|
||||
@@ -1526,7 +1528,7 @@ pub const JSValue = enum(i64) {
|
||||
|
||||
// TODO: replace calls to this function with `getOptional`
|
||||
pub fn getOwnTruthyComptime(this: JSValue, global: *JSGlobalObject, comptime property: []const u8) ?JSValue {
|
||||
if (comptime bun.ComptimeEnumMap(BuiltinName).has(property)) {
|
||||
if (comptime BuiltinName.map.has(property)) {
|
||||
return fastGetOwn(this, global, @field(BuiltinName, property));
|
||||
}
|
||||
|
||||
|
||||
24
src/bun.zig
24
src/bun.zig
@@ -183,8 +183,6 @@ pub const Progress = @import("./Progress.zig");
|
||||
/// Modified version of Zig's ComptimeStringMap
|
||||
pub const comptime_string_map = @import("./comptime_string_map.zig");
|
||||
pub const ComptimeStringMap = comptime_string_map.ComptimeStringMap;
|
||||
pub const ComptimeStringMap16 = comptime_string_map.ComptimeStringMap16;
|
||||
pub const ComptimeStringMapWithKeyType = comptime_string_map.ComptimeStringMapWithKeyType;
|
||||
|
||||
pub const glob = @import("./glob.zig");
|
||||
pub const patch = @import("./patch.zig");
|
||||
@@ -1235,14 +1233,24 @@ pub fn enumMap(comptime T: type, comptime args: anytype) (fn (T) [:0]const u8) {
|
||||
return Map.get;
|
||||
}
|
||||
|
||||
pub fn ComptimeEnumMap(comptime T: type) type {
|
||||
var entries: [std.enums.values(T).len]struct { [:0]const u8, T } = undefined;
|
||||
for (std.enums.values(T), &entries) |value, *entry| {
|
||||
entry.* = .{ .@"0" = @tagName(value), .@"1" = value };
|
||||
}
|
||||
return ComptimeStringMap(T, entries);
|
||||
fn ComptimeEnumValueMapList(comptime T: type) *const [std.enums.values(T).len]struct { []const u8, T } {
|
||||
const Memoized = struct {
|
||||
pub const result_buffer = brk: {
|
||||
var entries: [std.enums.values(T).len]struct { []const u8, T } = undefined;
|
||||
for (std.enums.values(T), &entries) |value, *entry| {
|
||||
entry.* = .{ .@"0" = @tagName(value), .@"1" = value };
|
||||
}
|
||||
break :brk entries;
|
||||
};
|
||||
|
||||
pub const result = &result_buffer;
|
||||
};
|
||||
return Memoized.result;
|
||||
}
|
||||
|
||||
pub fn ComptimeEnumMap(comptime T: type) type {
|
||||
return ComptimeStringMap(T, comptime ComptimeEnumValueMapList(T));
|
||||
}
|
||||
/// Write 0's for every byte in Type
|
||||
/// Ignores default struct values.
|
||||
pub fn zero(comptime Type: type) Type {
|
||||
|
||||
@@ -12,78 +12,160 @@ const strings = @import("./string_immutable.zig");
|
||||
/// where `.@"0"` is the `[]const u8` key and `.@"1"` is the associated value of type `V`.
|
||||
/// TODO: https://github.com/ziglang/zig/issues/4335
|
||||
pub fn ComptimeStringMapWithKeyType(comptime KeyType: type, comptime V: type, comptime kvs_list: anytype) type {
|
||||
const KV = struct {
|
||||
key: []const KeyType,
|
||||
value: V,
|
||||
};
|
||||
|
||||
const precomputed = comptime blk: {
|
||||
@setEvalBranchQuota(99999);
|
||||
|
||||
var sorted_kvs: [kvs_list.len]KV = undefined;
|
||||
var sorted_keys: [kvs_list.len][]const u8 = undefined;
|
||||
var sorted_values: if (V != void) [kvs_list.len]V else void = undefined;
|
||||
|
||||
for (kvs_list, 0..) |kv, i| {
|
||||
sorted_keys[i] = kv.@"0";
|
||||
if (V != void) {
|
||||
sorted_values[i] = kv.@"1";
|
||||
}
|
||||
}
|
||||
const lenAsc = (struct {
|
||||
fn lenAsc(context: void, a: KV, b: KV) bool {
|
||||
fn lenAsc(context: void, a: []const u8, b: []const u8) bool {
|
||||
_ = context;
|
||||
if (a.key.len != b.key.len) {
|
||||
return a.key.len < b.key.len;
|
||||
if (a.len != b.len) {
|
||||
return a.len < b.len;
|
||||
}
|
||||
// https://stackoverflow.com/questions/11227809/why-is-processing-a-sorted-array-faster-than-processing-an-unsorted-array
|
||||
@setEvalBranchQuota(999999);
|
||||
return std.mem.order(KeyType, a.key, b.key) == .lt;
|
||||
return std.mem.order(KeyType, a, b) == .lt;
|
||||
}
|
||||
}).lenAsc;
|
||||
if (KeyType == u8) {
|
||||
for (kvs_list, 0..) |kv, i| {
|
||||
if (V != void) {
|
||||
sorted_kvs[i] = .{ .key = kv.@"0", .value = kv.@"1" };
|
||||
} else {
|
||||
sorted_kvs[i] = .{ .key = kv.@"0", .value = {} };
|
||||
const Context = struct {
|
||||
keys: [][]const u8,
|
||||
values: if (V != void) []V else void,
|
||||
|
||||
pub fn lessThan(ctx: @This(), a: usize, b: usize) bool {
|
||||
return lenAsc({}, ctx.keys[a], ctx.keys[b]);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn swap(ctx: @This(), a: usize, b: usize) void {
|
||||
std.mem.swap([]const u8, &ctx.keys[a], &ctx.keys[b]);
|
||||
if (V != void) {
|
||||
std.mem.swap(V, &ctx.values[a], &ctx.values[b]);
|
||||
}
|
||||
}
|
||||
};
|
||||
std.sort.pdqContext(0, sorted_keys.len, Context{ .keys = &sorted_keys, .values = if (V != void) &sorted_values else undefined });
|
||||
} else {
|
||||
@compileError("Not implemented for this key type");
|
||||
}
|
||||
std.sort.pdq(KV, &sorted_kvs, {}, lenAsc);
|
||||
const min_len = sorted_kvs[0].key.len;
|
||||
const max_len = sorted_kvs[sorted_kvs.len - 1].key.len;
|
||||
const min_len = sorted_keys[0].len;
|
||||
const max_len = sorted_keys[sorted_keys.len - 1].len;
|
||||
|
||||
var len_indexes: [max_len + 1]usize = undefined;
|
||||
var len: usize = 0;
|
||||
var i: usize = 0;
|
||||
|
||||
var max_len_index: usize = 0;
|
||||
|
||||
while (len <= max_len) : (len += 1) {
|
||||
@setEvalBranchQuota(99999);
|
||||
|
||||
// find the first keyword len == len
|
||||
while (len > sorted_kvs[i].key.len) {
|
||||
while (len > sorted_keys[i].len) {
|
||||
i += 1;
|
||||
}
|
||||
len_indexes[len] = i;
|
||||
max_len_index = @max(max_len_index, i);
|
||||
}
|
||||
|
||||
switch (max_len_index) {
|
||||
0...std.math.maxInt(u8) => {
|
||||
break :blk .{
|
||||
.min_len = min_len,
|
||||
.max_len = max_len,
|
||||
.sorted_keys = brk: {
|
||||
var b: [sorted_keys.len][]const u8 = undefined;
|
||||
@memcpy(b[0..sorted_keys.len], sorted_keys[0..sorted_keys.len]);
|
||||
break :brk b;
|
||||
},
|
||||
.sorted_values = if (V != void) sorted_values,
|
||||
.len_indexes = brk: {
|
||||
var b: [len_indexes.len]u8 = undefined;
|
||||
for (len_indexes, &b) |v, *ptr| {
|
||||
ptr.* = @intCast(v);
|
||||
}
|
||||
break :brk b;
|
||||
},
|
||||
};
|
||||
},
|
||||
std.math.maxInt(u8) + 1...std.math.maxInt(u16) => {
|
||||
break :blk .{
|
||||
.min_len = min_len,
|
||||
.max_len = max_len,
|
||||
.sorted_keys = brk: {
|
||||
var b: [sorted_keys.len][]const u8 = undefined;
|
||||
@memcpy(b[0..sorted_keys.len], sorted_keys[0..sorted_keys.len]);
|
||||
break :brk b;
|
||||
},
|
||||
.sorted_values = if (V != void) sorted_values,
|
||||
.len_indexes = brk: {
|
||||
var b: [len_indexes.len]u16 = undefined;
|
||||
for (len_indexes, &b) |v, *ptr| {
|
||||
ptr.* = @intCast(v);
|
||||
}
|
||||
break :brk b;
|
||||
},
|
||||
};
|
||||
},
|
||||
std.math.maxInt(u16) + 1...std.math.maxInt(u32) => {
|
||||
break :blk .{
|
||||
.min_len = min_len,
|
||||
.max_len = max_len,
|
||||
.sorted_keys = brk: {
|
||||
var b: [sorted_keys.len][]const u8 = undefined;
|
||||
@memcpy(b[0..sorted_keys.len], sorted_keys[0..sorted_keys.len]);
|
||||
break :brk b;
|
||||
},
|
||||
.sorted_values = if (V != void) sorted_values,
|
||||
.len_indexes = brk: {
|
||||
var b: [len_indexes.len]u32 = undefined;
|
||||
for (len_indexes, &b) |v, *ptr| {
|
||||
ptr.* = @intCast(v);
|
||||
}
|
||||
break :brk b;
|
||||
},
|
||||
};
|
||||
},
|
||||
std.math.maxInt(u32) + 1...std.math.maxInt(u64) => {
|
||||
break :blk .{
|
||||
.min_len = min_len,
|
||||
.max_len = max_len,
|
||||
.sorted_keys = brk: {
|
||||
var b: [sorted_keys.len][]const u8 = undefined;
|
||||
@memcpy(b[0..sorted_keys.len], sorted_keys[0..sorted_keys.len]);
|
||||
break :brk b;
|
||||
},
|
||||
.sorted_values = if (V != void) sorted_values,
|
||||
.len_indexes = len_indexes,
|
||||
};
|
||||
},
|
||||
}
|
||||
break :blk .{
|
||||
.min_len = min_len,
|
||||
.max_len = max_len,
|
||||
.sorted_kvs = sorted_kvs,
|
||||
.len_indexes = len_indexes,
|
||||
};
|
||||
};
|
||||
|
||||
return struct {
|
||||
const len_indexes = precomputed.len_indexes;
|
||||
pub const kvs = precomputed.sorted_kvs;
|
||||
|
||||
const keys_list: []const []const KeyType = blk: {
|
||||
var k: [kvs.len][]const KeyType = undefined;
|
||||
for (kvs, 0..) |kv, i| {
|
||||
k[i] = kv.key;
|
||||
}
|
||||
const final = k;
|
||||
break :blk &final;
|
||||
};
|
||||
const keys_list = precomputed.sorted_keys;
|
||||
const values_slice_list = if (V != void) precomputed.sorted_values else undefined;
|
||||
const values_list = if (V != void) &values_slice_list else undefined;
|
||||
|
||||
pub const Value = V;
|
||||
|
||||
pub fn keys() []const []const KeyType {
|
||||
return keys_list;
|
||||
const keys_list_ptr = struct {
|
||||
pub const keys_list_slice_to_avoid_keeping_keys_list_in_text_section_unnecessarily = &keys_list;
|
||||
};
|
||||
return keys_list_ptr.keys_list_slice_to_avoid_keeping_keys_list_in_text_section_unnecessarily;
|
||||
}
|
||||
|
||||
pub fn values() []const V {
|
||||
return values_list;
|
||||
}
|
||||
|
||||
pub fn has(str: []const KeyType) bool {
|
||||
@@ -95,7 +177,7 @@ pub fn ComptimeStringMapWithKeyType(comptime KeyType: type, comptime V: type, co
|
||||
var i = len_indexes[len];
|
||||
@setEvalBranchQuota(99999);
|
||||
|
||||
while (i < kvs.len and kvs[i].key.len == len) : (i += 1) {}
|
||||
while (i < keys_list.len and keys_list[i].len == len) : (i += 1) {}
|
||||
|
||||
break :brk i;
|
||||
};
|
||||
@@ -103,8 +185,11 @@ pub fn ComptimeStringMapWithKeyType(comptime KeyType: type, comptime V: type, co
|
||||
// This benchmarked faster for both small and large lists of strings than using a big switch statement
|
||||
// But only so long as the keys are a sorted list.
|
||||
inline for (len_indexes[len]..end) |i| {
|
||||
if (strings.eqlComptimeCheckLenWithType(KeyType, str, kvs[i].key, false)) {
|
||||
return kvs[i].value;
|
||||
if (strings.eqlComptimeCheckLenWithType(KeyType, str, comptime keys_list[i], false)) {
|
||||
if (comptime V == void) {
|
||||
return {};
|
||||
}
|
||||
return values_slice_list[i];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +201,9 @@ pub fn ComptimeStringMapWithKeyType(comptime KeyType: type, comptime V: type, co
|
||||
var i = len_indexes[len];
|
||||
@setEvalBranchQuota(99999);
|
||||
|
||||
while (i < kvs.len and kvs[i].key.len == len) : (i += 1) {}
|
||||
while (i < keys_list.len and
|
||||
keys_list[i].len == len) : (i += 1)
|
||||
{}
|
||||
|
||||
break :brk i;
|
||||
};
|
||||
@@ -124,8 +211,12 @@ pub fn ComptimeStringMapWithKeyType(comptime KeyType: type, comptime V: type, co
|
||||
// This benchmarked faster for both small and large lists of strings than using a big switch statement
|
||||
// But only so long as the keys are a sorted list.
|
||||
inline for (len_indexes[len]..end) |i| {
|
||||
if (eqls(str, kvs[i].key)) {
|
||||
return kvs[i].value;
|
||||
if (eqls(str, comptime keys_list[i])) {
|
||||
if (comptime V == void) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return values_slice_list[i];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +228,7 @@ pub fn ComptimeStringMapWithKeyType(comptime KeyType: type, comptime V: type, co
|
||||
var i = len_indexes[len];
|
||||
@setEvalBranchQuota(99999);
|
||||
|
||||
while (i < kvs.len and kvs[i].key.len == len) : (i += 1) {}
|
||||
while (i < keys_list.len and keys_list[i].len == len) : (i += 1) {}
|
||||
|
||||
break :brk i;
|
||||
};
|
||||
@@ -145,7 +236,11 @@ pub fn ComptimeStringMapWithKeyType(comptime KeyType: type, comptime V: type, co
|
||||
const start = comptime len_indexes[len];
|
||||
const range = comptime keys()[start..end];
|
||||
if (eqls(str, range)) |k| {
|
||||
return kvs[start + k].value;
|
||||
if (comptime V == void) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return values_slice_list[start + k];
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -177,7 +272,7 @@ pub fn ComptimeStringMapWithKeyType(comptime KeyType: type, comptime V: type, co
|
||||
var i = len_indexes[len];
|
||||
@setEvalBranchQuota(99999);
|
||||
|
||||
while (i < kvs.len and kvs[i].key.len == len) : (i += 1) {}
|
||||
while (i < keys_list.len and keys_list[i].len == len) : (i += 1) {}
|
||||
|
||||
break :brk i;
|
||||
};
|
||||
@@ -185,7 +280,7 @@ pub fn ComptimeStringMapWithKeyType(comptime KeyType: type, comptime V: type, co
|
||||
// This benchmarked faster for both small and large lists of strings than using a big switch statement
|
||||
// But only so long as the keys are a sorted list.
|
||||
inline for (len_indexes[len]..end) |i| {
|
||||
if (strings.eqlComptimeCheckLenWithType(KeyType, str, kvs[i].key, false)) {
|
||||
if (strings.eqlComptimeCheckLenWithType(KeyType, str, comptime keys_list[i], false)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@@ -324,10 +419,6 @@ pub fn ComptimeStringMap(comptime V: type, comptime kvs_list: anytype) type {
|
||||
return ComptimeStringMapWithKeyType(u8, V, kvs_list);
|
||||
}
|
||||
|
||||
pub fn ComptimeStringMap16(comptime V: type, comptime kvs_list: anytype) type {
|
||||
return ComptimeStringMapWithKeyType(u16, V, kvs_list);
|
||||
}
|
||||
|
||||
const TestEnum = enum {
|
||||
A,
|
||||
B,
|
||||
|
||||
@@ -157,6 +157,30 @@ pub const Browsers = struct {
|
||||
safari: ?u32 = null,
|
||||
samsung: ?u32 = null,
|
||||
|
||||
const Browser = enum {
|
||||
chrome,
|
||||
edge,
|
||||
firefox,
|
||||
ie,
|
||||
ios_saf,
|
||||
opera,
|
||||
safari,
|
||||
no_mapping,
|
||||
};
|
||||
// Hoisting this reduces the number of instantiations
|
||||
const Map = bun.ComptimeStringMap(Browser, .{
|
||||
.{ "chrome", Browser.chrome },
|
||||
.{ "edge", Browser.edge },
|
||||
.{ "firefox", Browser.firefox },
|
||||
.{ "hermes", Browser.no_mapping },
|
||||
.{ "ie", Browser.ie },
|
||||
.{ "ios", Browser.ios_saf },
|
||||
.{ "node", Browser.no_mapping },
|
||||
.{ "opera", Browser.opera },
|
||||
.{ "rhino", Browser.no_mapping },
|
||||
.{ "safari", Browser.safari },
|
||||
});
|
||||
|
||||
pub const browserDefault = convertFromString(&.{
|
||||
"es2020", // support import.meta.url
|
||||
"edge88",
|
||||
@@ -245,28 +269,6 @@ pub const Browsers = struct {
|
||||
};
|
||||
|
||||
if (maybe_idx) |idx| {
|
||||
const Browser = enum {
|
||||
chrome,
|
||||
edge,
|
||||
firefox,
|
||||
ie,
|
||||
ios_saf,
|
||||
opera,
|
||||
safari,
|
||||
no_mapping,
|
||||
};
|
||||
const Map = bun.ComptimeStringMap(Browser, .{
|
||||
.{ "chrome", Browser.chrome },
|
||||
.{ "edge", Browser.edge },
|
||||
.{ "firefox", Browser.firefox },
|
||||
.{ "hermes", Browser.no_mapping },
|
||||
.{ "ie", Browser.ie },
|
||||
.{ "ios", Browser.ios_saf },
|
||||
.{ "node", Browser.no_mapping },
|
||||
.{ "opera", Browser.opera },
|
||||
.{ "rhino", Browser.no_mapping },
|
||||
.{ "safari", Browser.safari },
|
||||
});
|
||||
const browser = Map.get(entry[0..idx]);
|
||||
if (browser == null or browser.? == .no_mapping) continue; // No mapping available
|
||||
|
||||
|
||||
@@ -294,9 +294,9 @@ pub fn jsonStringify(this: *const Lockfile, w: anytype) !void {
|
||||
try w.beginArray();
|
||||
defer w.endArray() catch {};
|
||||
|
||||
for (Npm.Architecture.NameMap.kvs) |kv| {
|
||||
if (pkg.meta.arch.has(kv.value)) {
|
||||
try w.write(kv.key);
|
||||
for (Npm.Architecture.NameMap.keys(), Npm.Architecture.NameMap.values()) |key, value| {
|
||||
if (pkg.meta.arch.has(value)) {
|
||||
try w.write(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -306,9 +306,9 @@ pub fn jsonStringify(this: *const Lockfile, w: anytype) !void {
|
||||
try w.beginArray();
|
||||
defer w.endArray() catch {};
|
||||
|
||||
for (Npm.OperatingSystem.NameMap.kvs) |kv| {
|
||||
if (pkg.meta.os.has(kv.value)) {
|
||||
try w.write(kv.key);
|
||||
for (Npm.OperatingSystem.NameMap.keys(), Npm.OperatingSystem.NameMap.values()) |key, value| {
|
||||
if (pkg.meta.os.has(value)) {
|
||||
try w.write(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -627,15 +627,14 @@ pub fn Negatable(comptime T: type) type {
|
||||
return;
|
||||
}
|
||||
|
||||
const kvs = T.NameMap.kvs;
|
||||
var removed: u8 = 0;
|
||||
for (kvs) |kv| {
|
||||
if (!field.has(kv.value)) {
|
||||
for (T.NameMap.values()) |value| {
|
||||
if (!field.has(value)) {
|
||||
removed += 1;
|
||||
}
|
||||
}
|
||||
const included = kvs.len - removed;
|
||||
const print_included = removed > kvs.len - removed;
|
||||
const included = T.NameMap.keys().len - removed;
|
||||
const print_included = removed > T.NameMap.keys().len - removed;
|
||||
|
||||
const one = (print_included and included == 1) or (!print_included and removed == 1);
|
||||
|
||||
@@ -643,18 +642,18 @@ pub fn Negatable(comptime T: type) type {
|
||||
try writer.writeAll("[ ");
|
||||
}
|
||||
|
||||
for (kvs) |kv| {
|
||||
const has = field.has(kv.value);
|
||||
for (T.NameMap.keys(), T.NameMap.values()) |key, value| {
|
||||
const has = field.has(value);
|
||||
if (has and print_included) {
|
||||
try writer.print(
|
||||
\\"{s}"
|
||||
, .{kv.key});
|
||||
, .{key});
|
||||
if (one) return;
|
||||
try writer.writeAll(", ");
|
||||
} else if (!has and !print_included) {
|
||||
try writer.print(
|
||||
\\"!{s}"
|
||||
, .{kv.key});
|
||||
, .{key});
|
||||
if (one) return;
|
||||
try writer.writeAll(", ");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user