Skip to content

Commit

Permalink
Stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
zackradisic committed Sep 25, 2024
1 parent 837e29c commit 7318fd8
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 26 deletions.
199 changes: 178 additions & 21 deletions src/css/css_parser.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5649,8 +5649,7 @@ pub const serializer = struct {
}

pub fn serializeDimension(value: f32, unit: []const u8, comptime W: type, dest: *Printer(W)) PrintErr!void {
// const int_value: ?i32 = if (@rem(value, 1) == 0.0) @as(i32, @intFromFloat(value)) else null;
const int_value: ?i32 = if (1.0 - @trunc(value) == 0.0) @intFromFloat(value) else null;
const int_value: ?i32 = if (fract(value) == 0.0) @intFromFloat(value) else null;
const token = Token{ .dimension = .{
.num = .{
.has_sign = value < 0.0,
Expand Down Expand Up @@ -5720,39 +5719,46 @@ pub const serializer = struct {
return writer.writeAll(value[chunk_start..]);
}

// pub fn writeNumeric(value: f32, int_value: ?i32, has_sign: bool, writer: anytype) !void {
// // `value >= 0` is true for negative 0.
// if (has_sign and !std.math.signbit(value)) {
// try writer.writeAll("+");
// }

// if (value == 0.0 and signfns.isSignNegative(value)) {
// // Negative zero. Work around #20596.
// try writer.writeAll("-0");
// if (int_value == null and @mod(value, 1) == 0) {
// try writer.writeAll(".0");
// }
// } else {
// var buf: [124]u8 = undefined;
// const bytes = bun.fmt.FormatDouble.dtoa(&buf, @floatCast(value));
// try writer.writeAll(bytes);
// }
// }

pub fn writeNumeric(value: f32, int_value: ?i32, has_sign: bool, writer: anytype) !void {
// `value >= 0` is true for negative 0.
if (has_sign and !std.math.signbit(value)) {
try writer.writeAll("+");
}

const notation: struct { decimal_point: bool, scientific: bool } = if (value == 0.0 and std.math.signbit(value)) notation: {
const notation: Notation = if (value == 0.0 and std.math.signbit(value)) notation: {
// Negative zero. Work around #20596.
try writer.writeAll("-0");
break :notation .{
break :notation Notation{
.decimal_point = false,
.scientific = false,
};
} else notation: {
// TODO: calculate the actual number of chars here
var buf: [64]u8 = undefined;
// PERF/TODO: Compare this to Rust dtoa-short crate
// const floats = std.fmt.formatFloat(buf[0..], value, .{
// .mode = .scientific,
// .precision = 6,
// }) catch unreachable;
const floats = std.fmt.formatFloat(buf[0..], value, .{
.mode = .decimal,
}) catch unreachable;
try writer.writeAll(floats);
// TODO: this is not correct, might need to copy impl from dtoa_short here
break :notation .{
.decimal_point = true,
.scientific = false,
};
var buf: [129]u8 = undefined;
const str, const notation = dtoa_short(&buf, value, 6);
try writer.writeAll(str);
break :notation notation;
};

if (int_value == null and @mod(value, 1) == 0) {
if (int_value == null and fract(value) == 0) {
if (!notation.decimal_point and !notation.scientific) {
try writer.writeAll(".0");
}
Expand Down Expand Up @@ -6119,3 +6125,154 @@ pub fn deepDeinit(comptime V: type, allocator: Allocator, list: *ArrayList(V)) v

list.deinit(allocator);
}

const Notation = struct {
decimal_point: bool,
scientific: bool,

pub fn integer() Notation {
return .{
.decimal_point = false,
.scientific = false,
};
}
};

pub fn dtoa_short(buf: *[129]u8, value: f32, comptime precision: u8) struct { []u8, Notation } {
buf[0] = '0';
const buf_len = bun.fmt.FormatDouble.dtoa(@ptrCast(buf[1..].ptr), @floatCast(value)).len;
return restrict_prec(buf[0 .. buf_len + 1], precision);
}

fn restrict_prec(buf: []u8, comptime prec: u8) struct { []u8, Notation } {
const len: u8 = @intCast(buf.len);

// Put a leading zero to capture any carry.
// Caller must prepare an empty byte for us;
bun.debugAssert(buf[0] == '0');
buf[0] = '0';
// Remove the sign for now. We will put it back at the end.
const sign = switch (buf[1]) {
'+', '-' => brk: {
const s = buf[1];
buf[1] = '0';
break :brk s;
},
else => null,
};

// Locate dot, exponent, and the first significant digit.
var _pos_dot: ?u8 = null;
var pos_exp: ?u8 = null;
var _prec_start: ?u8 = null;
for (1..len) |i| {
if (buf[i] == '.') {
bun.debugAssert(_pos_dot == null);
_pos_dot = @intCast(i);
} else if (buf[i] == 'e') {
pos_exp = @intCast(i);
// We don't change exponent part, so stop here.
break;
} else if (_prec_start == null and buf[i] != '0') {
bun.debugAssert(buf[i] >= '1' and buf[i] <= '9');
_prec_start = @intCast(i);
}
}

const prec_start = if (_prec_start) |i|
i
else
// If there is no non-zero digit at all, it is just zero.
return .{
buf[0..1],
Notation.integer(),
};

// Coefficient part ends at 'e' or the length.
const coeff_end = pos_exp orelse len;
// Decimal dot is effectively at the end of coefficient part if no
// dot presents before that.
const pos_dot = _pos_dot orelse coeff_end;
// Find the end position of the number within the given precision.
const prec_end: u8 = brk: {
const end = prec_start + prec;
break :brk if (pos_dot > prec_start and pos_dot <= end) end + 1 else end;
};
var new_coeff_end = coeff_end;
if (prec_end < coeff_end) {
// Round to the given precision.
const next_char = buf[prec_end];
new_coeff_end = prec_end;
if (next_char >= '5') {
var i = prec_end;
while (i != 0) {
i -= 1;
if (buf[i] == '.') {
continue;
}
if (buf[i] != '9') {
buf[i] += 1;
new_coeff_end = i + 1;
break;
}
buf[i] = '0';
}
}
}
if (new_coeff_end < pos_dot) {
// If the precision isn't enough to reach the dot, set all digits
// in-between to zero and keep the number until the dot.
for (new_coeff_end..pos_dot) |i| {
buf[i] = '0';
}
new_coeff_end = pos_dot;
} else {
// Strip any trailing zeros.
var i = new_coeff_end;
while (i != 0) {
i -= 1;
if (buf[i] != '0') {
if (buf[i] == '.') {
new_coeff_end = i;
}
break;
}
new_coeff_end = i;
}
}
// Move exponent part if necessary.
const real_end = if (pos_exp) |posexp| brk: {
const exp_len = len - posexp;
if (new_coeff_end != posexp) {
for (0..exp_len) |i| {
buf[new_coeff_end + i] = buf[posexp + 1];
}
}
break :brk new_coeff_end + exp_len;
} else new_coeff_end;
// Add back the sign and strip the leading zero.
const result = if (sign) |sgn| brk: {
if (buf[1] == '0' and buf[2] != '.') {
buf[1] = sgn;
break :brk buf[1..real_end];
}
bun.debugAssert(buf[0] == '0');
buf[0] = sgn;
break :brk buf[0..real_end];
} else brk: {
if (buf[0] == '0' and buf[1] != '.') {
break :brk buf[1..real_end];
}
break :brk buf[0..real_end];
};
// Generate the notation info.
const notation = Notation{
.decimal_point = pos_dot < new_coeff_end,
.scientific = pos_exp != null,
};
return .{ result, notation };
}

pub inline fn fract(val: f32) f32 {
return 1.0 - @trunc(val);
}
2 changes: 1 addition & 1 deletion src/css/values/angle.zig
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ pub const Angle = union(Tag) {

// We print 5 digits of precision by default.
// Switch to degrees if there are an even number of them.
if (std.math.round(deg * 100000.0) - (deg - @trunc(deg)) == 0) {
if (css.fract(std.math.round(deg * 100000.0)) == 0) {
break :brk .{ val, "deg" };
} else {
break :brk .{ val, "rad" };
Expand Down
8 changes: 4 additions & 4 deletions src/css/values/color.zig
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ pub const CssColor = union(enum) {
if (hex == expandHex(compact)) {
try dest.writeFmt("#{x:0>4}", .{compact});
} else {
try dest.writeFmt("#{x:0>8}", .{compact});
try dest.writeFmt("#{x:0>8}", .{hex});
}
}
return;
Expand All @@ -184,7 +184,7 @@ pub const CssColor = union(enum) {
dest,
),
.lch => |*lch| writeComponents(
"lab",
"lch",
lch.l,
lch.c,
lch.h,
Expand All @@ -193,7 +193,7 @@ pub const CssColor = union(enum) {
dest,
),
.oklab => |*oklab| writeComponents(
"lab",
"oklab",
oklab.l,
oklab.a,
oklab.b,
Expand All @@ -202,7 +202,7 @@ pub const CssColor = union(enum) {
dest,
),
.oklch => |*oklch| writeComponents(
"lab",
"oklch",
oklch.l,
oklch.c,
oklch.h,
Expand Down

0 comments on commit 7318fd8

Please sign in to comment.