mirror of
https://github.com/ziglang/zig.git
synced 2026-02-21 16:54:52 +00:00
Merge remote-tracking branch 'origin/flate' into wrangle-writer-buffering
This commit is contained in:
commit
08efe0d598
@ -3215,7 +3215,7 @@ fn createFoo(param: i32) !Foo {
|
||||
to increase their development pace.
|
||||
</p>
|
||||
<p>
|
||||
Error Return Traces are enabled by default in {#link|Debug#} and {#link|ReleaseSafe#} builds and disabled by default in {#link|ReleaseFast#} and {#link|ReleaseSmall#} builds.
|
||||
Error Return Traces are enabled by default in {#link|Debug#} builds and disabled by default in {#link|ReleaseFast#}, {#link|ReleaseSafe#} and {#link|ReleaseSmall#} builds.
|
||||
</p>
|
||||
<p>
|
||||
There are a few ways to activate this error return tracing feature:
|
||||
@ -4319,7 +4319,7 @@ comptime {
|
||||
</p>
|
||||
<p>
|
||||
{#syntax#}T{#endsyntax#} must be a pointer, a {#syntax#}bool{#endsyntax#}, a float,
|
||||
an integer or an enum.
|
||||
an integer, an enum, or a packed struct.
|
||||
</p>
|
||||
<p>{#syntax#}AtomicOrder{#endsyntax#} can be found with {#syntax#}@import("std").builtin.AtomicOrder{#endsyntax#}.</p>
|
||||
{#see_also|@atomicStore|@atomicRmw||@cmpxchgWeak|@cmpxchgStrong#}
|
||||
@ -4333,7 +4333,7 @@ comptime {
|
||||
</p>
|
||||
<p>
|
||||
{#syntax#}T{#endsyntax#} must be a pointer, a {#syntax#}bool{#endsyntax#}, a float,
|
||||
an integer or an enum.
|
||||
an integer, an enum, or a packed struct.
|
||||
</p>
|
||||
<p>{#syntax#}AtomicOrder{#endsyntax#} can be found with {#syntax#}@import("std").builtin.AtomicOrder{#endsyntax#}.</p>
|
||||
<p>{#syntax#}AtomicRmwOp{#endsyntax#} can be found with {#syntax#}@import("std").builtin.AtomicRmwOp{#endsyntax#}.</p>
|
||||
@ -4347,7 +4347,7 @@ comptime {
|
||||
</p>
|
||||
<p>
|
||||
{#syntax#}T{#endsyntax#} must be a pointer, a {#syntax#}bool{#endsyntax#}, a float,
|
||||
an integer or an enum.
|
||||
an integer, an enum, or a packed struct.
|
||||
</p>
|
||||
<p>{#syntax#}AtomicOrder{#endsyntax#} can be found with {#syntax#}@import("std").builtin.AtomicOrder{#endsyntax#}.</p>
|
||||
{#see_also|@atomicLoad|@atomicRmw|@cmpxchgWeak|@cmpxchgStrong#}
|
||||
@ -4576,8 +4576,8 @@ comptime {
|
||||
more efficiently in machine instructions.
|
||||
</p>
|
||||
<p>
|
||||
{#syntax#}T{#endsyntax#} must be a pointer, a {#syntax#}bool{#endsyntax#}, a float,
|
||||
an integer or an enum.
|
||||
{#syntax#}T{#endsyntax#} must be a pointer, a {#syntax#}bool{#endsyntax#},
|
||||
an integer, an enum, or a packed struct.
|
||||
</p>
|
||||
<p>{#syntax#}@typeInfo(@TypeOf(ptr)).pointer.alignment{#endsyntax#} must be {#syntax#}>= @sizeOf(T).{#endsyntax#}</p>
|
||||
<p>{#syntax#}AtomicOrder{#endsyntax#} can be found with {#syntax#}@import("std").builtin.AtomicOrder{#endsyntax#}.</p>
|
||||
@ -4608,8 +4608,8 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
|
||||
However if you need a stronger guarantee, use {#link|@cmpxchgStrong#}.
|
||||
</p>
|
||||
<p>
|
||||
{#syntax#}T{#endsyntax#} must be a pointer, a {#syntax#}bool{#endsyntax#}, a float,
|
||||
an integer or an enum.
|
||||
{#syntax#}T{#endsyntax#} must be a pointer, a {#syntax#}bool{#endsyntax#},
|
||||
an integer, an enum, or a packed struct.
|
||||
</p>
|
||||
<p>{#syntax#}@typeInfo(@TypeOf(ptr)).pointer.alignment{#endsyntax#} must be {#syntax#}>= @sizeOf(T).{#endsyntax#}</p>
|
||||
<p>{#syntax#}AtomicOrder{#endsyntax#} can be found with {#syntax#}@import("std").builtin.AtomicOrder{#endsyntax#}.</p>
|
||||
@ -4840,7 +4840,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
|
||||
<p>
|
||||
This builtin can be called from a {#link|comptime#} block to conditionally export symbols.
|
||||
When <code>ptr</code> points to a function with the C calling convention and
|
||||
{#syntax#}options.linkage{#endsyntax#} is {#syntax#}.Strong{#endsyntax#}, this is equivalent to
|
||||
{#syntax#}options.linkage{#endsyntax#} is {#syntax#}.strong{#endsyntax#}, this is equivalent to
|
||||
the {#syntax#}export{#endsyntax#} keyword used on a function:
|
||||
</p>
|
||||
{#code|export_builtin.zig#}
|
||||
@ -4952,34 +4952,25 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
|
||||
{#header_close#}
|
||||
|
||||
{#header_open|@import#}
|
||||
<pre>{#syntax#}@import(comptime path: []const u8) type{#endsyntax#}</pre>
|
||||
<p>
|
||||
This function finds a zig file corresponding to {#syntax#}path{#endsyntax#} and adds it to the build,
|
||||
if it is not already added.
|
||||
</p>
|
||||
<p>
|
||||
Zig source files are implicitly structs, with a name equal to the file's basename with the extension
|
||||
truncated. {#syntax#}@import{#endsyntax#} returns the struct type corresponding to the file.
|
||||
</p>
|
||||
<p>
|
||||
Declarations which have the {#syntax#}pub{#endsyntax#} keyword may be referenced from a different
|
||||
source file than the one they are declared in.
|
||||
</p>
|
||||
<p>
|
||||
{#syntax#}path{#endsyntax#} can be a relative path or it can be the name of a package.
|
||||
If it is a relative path, it is relative to the file that contains the {#syntax#}@import{#endsyntax#}
|
||||
function call.
|
||||
</p>
|
||||
<p>
|
||||
The following packages are always available:
|
||||
</p>
|
||||
<pre>{#syntax#}@import(comptime target: []const u8) anytype{#endsyntax#}</pre>
|
||||
<p>Imports the file at {#syntax#}target{#endsyntax#}, adding it to the compilation if it is not already
|
||||
added. {#syntax#}target{#endsyntax#} is either a relative path to another file from the file containing
|
||||
the {#syntax#}@import{#endsyntax#} call, or it is the name of a {#link|module|Compilation Model#}, with
|
||||
the import referring to the root source file of that module. Either way, the file path must end in
|
||||
either <code>.zig</code> (for a Zig source file) or <code>.zon</code> (for a ZON data file).</p>
|
||||
<p>If {#syntax#}target{#endsyntax#} refers to a Zig source file, then {#syntax#}@import{#endsyntax#} returns
|
||||
that file's {#link|corresponding struct type|Source File Structs#}, essentially as if the builtin call was
|
||||
replaced by {#syntax#}struct { FILE_CONTENTS }{#endsyntax#}. The return type is {#syntax#}type{#endsyntax#}.</p>
|
||||
<p>If {#syntax#}target{#endsyntax#} refers to a ZON file, then {#syntax#}@import{#endsyntax#} returns the value
|
||||
of the literal in the file. If there is an inferred {#link|result type|Result Types#}, then the return type
|
||||
is that type, and the ZON literal is interpreted as that type ({#link|Result Types#} are propagated through
|
||||
the ZON expression). Otherwise, the return type is the type of the equivalent Zig expression, essentially as
|
||||
if the builtin call was replaced by the ZON file contents.</p>
|
||||
<p>The following modules are always available for import:</p>
|
||||
<ul>
|
||||
<li>{#syntax#}@import("std"){#endsyntax#} - Zig Standard Library</li>
|
||||
<li>{#syntax#}@import("builtin"){#endsyntax#} - Target-specific information
|
||||
The command <code>zig build-exe --show-builtin</code> outputs the source to stdout for reference.
|
||||
</li>
|
||||
<li>{#syntax#}@import("root"){#endsyntax#} - Root source file
|
||||
This is usually <code>src/main.zig</code> but depends on what file is built.
|
||||
<li>{#syntax#}@import("builtin"){#endsyntax#} - Target-specific information. The command <code>zig build-exe --show-builtin</code> outputs the source to stdout for reference.</li>
|
||||
<li>{#syntax#}@import("root"){#endsyntax#} - Alias for the root module. In typical project structures, this means it refers back to <code>src/main.zig</code>.
|
||||
</li>
|
||||
</ul>
|
||||
{#see_also|Compile Variables|@embedFile#}
|
||||
@ -5179,7 +5170,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
|
||||
<pre>{#syntax#}@mod(numerator: T, denominator: T) T{#endsyntax#}</pre>
|
||||
<p>
|
||||
Modulus division. For unsigned integers this is the same as
|
||||
{#syntax#}numerator % denominator{#endsyntax#}. Caller guarantees {#syntax#}denominator > 0{#endsyntax#}, otherwise the
|
||||
{#syntax#}numerator % denominator{#endsyntax#}. Caller guarantees {#syntax#}denominator != 0{#endsyntax#}, otherwise the
|
||||
operation will result in a {#link|Remainder Division by Zero#} when runtime safety checks are enabled.
|
||||
</p>
|
||||
<ul>
|
||||
@ -5284,7 +5275,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
|
||||
<pre>{#syntax#}@rem(numerator: T, denominator: T) T{#endsyntax#}</pre>
|
||||
<p>
|
||||
Remainder division. For unsigned integers this is the same as
|
||||
{#syntax#}numerator % denominator{#endsyntax#}. Caller guarantees {#syntax#}denominator > 0{#endsyntax#}, otherwise the
|
||||
{#syntax#}numerator % denominator{#endsyntax#}. Caller guarantees {#syntax#}denominator != 0{#endsyntax#}, otherwise the
|
||||
operation will result in a {#link|Remainder Division by Zero#} when runtime safety checks are enabled.
|
||||
</p>
|
||||
<ul>
|
||||
|
||||
@ -4,8 +4,10 @@ pub fn build(b: *std.Build) void {
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "example",
|
||||
.root_source_file = b.path("example.zig"),
|
||||
.optimize = optimize,
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("example.zig"),
|
||||
.optimize = optimize,
|
||||
}),
|
||||
});
|
||||
b.default_step.dependOn(&exe.step);
|
||||
}
|
||||
|
||||
@ -4,15 +4,19 @@ pub fn build(b: *std.Build) void {
|
||||
const lib = b.addLibrary(.{
|
||||
.linkage = .dynamic,
|
||||
.name = "mathtest",
|
||||
.root_source_file = b.path("mathtest.zig"),
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("mathtest.zig"),
|
||||
}),
|
||||
.version = .{ .major = 1, .minor = 0, .patch = 0 },
|
||||
});
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "test",
|
||||
.root_module = b.createModule(.{
|
||||
.link_libc = true,
|
||||
}),
|
||||
});
|
||||
exe.addCSourceFile(.{ .file = b.path("test.c"), .flags = &.{"-std=c99"} });
|
||||
exe.linkLibrary(lib);
|
||||
exe.linkSystemLibrary("c");
|
||||
exe.root_module.addCSourceFile(.{ .file = b.path("test.c"), .flags = &.{"-std=c99"} });
|
||||
exe.root_module.linkLibrary(lib);
|
||||
|
||||
b.default_step.dependOn(&exe.step);
|
||||
|
||||
|
||||
@ -3,15 +3,19 @@ const std = @import("std");
|
||||
pub fn build(b: *std.Build) void {
|
||||
const obj = b.addObject(.{
|
||||
.name = "base64",
|
||||
.root_source_file = b.path("base64.zig"),
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("base64.zig"),
|
||||
}),
|
||||
});
|
||||
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "test",
|
||||
.root_module = b.createModule(.{
|
||||
.link_libc = true,
|
||||
}),
|
||||
});
|
||||
exe.addCSourceFile(.{ .file = b.path("test.c"), .flags = &.{"-std=c99"} });
|
||||
exe.addObject(obj);
|
||||
exe.linkSystemLibrary("c");
|
||||
exe.root_module.addCSourceFile(.{ .file = b.path("test.c"), .flags = &.{"-std=c99"} });
|
||||
exe.root_module.addObject(obj);
|
||||
b.installArtifact(exe);
|
||||
}
|
||||
|
||||
|
||||
@ -67,4 +67,11 @@ test "*T to *[1]T" {
|
||||
try expect(z[0] == 1234);
|
||||
}
|
||||
|
||||
// Sentinel-terminated slices can be coerced into sentinel-terminated pointers
|
||||
test "[:x]T to [*:x]T" {
|
||||
const buf: [:0]const u8 = "hello";
|
||||
const buf2: [*:0]const u8 = buf;
|
||||
try expect(buf2[4] == 'o');
|
||||
}
|
||||
|
||||
// test
|
||||
|
||||
@ -696,8 +696,11 @@ fn runStepNames(
|
||||
.failures, .none => true,
|
||||
else => false,
|
||||
};
|
||||
if (failure_count == 0 and failures_only) {
|
||||
return run.cleanExit();
|
||||
if (failure_count == 0) {
|
||||
std.Progress.setStatus(.success);
|
||||
if (failures_only) return run.cleanExit();
|
||||
} else {
|
||||
std.Progress.setStatus(.failure);
|
||||
}
|
||||
|
||||
const ttyconf = run.ttyconf;
|
||||
@ -1149,6 +1152,7 @@ fn workerMakeOneStep(
|
||||
} else |err| switch (err) {
|
||||
error.MakeFailed => {
|
||||
@atomicStore(Step.State, &s.state, .failure, .seq_cst);
|
||||
std.Progress.setStatus(.failure_working);
|
||||
break :handle_result;
|
||||
},
|
||||
error.MakeSkipped => @atomicStore(Step.State, &s.state, .skipped, .seq_cst),
|
||||
|
||||
@ -240,7 +240,7 @@ comptime {
|
||||
_ = @import("compiler_rt/udivmodti4.zig");
|
||||
|
||||
// extra
|
||||
if (builtin.zig_backend != .stage2_aarch64) _ = @import("compiler_rt/os_version_check.zig");
|
||||
_ = @import("compiler_rt/os_version_check.zig");
|
||||
_ = @import("compiler_rt/emutls.zig");
|
||||
_ = @import("compiler_rt/arm.zig");
|
||||
_ = @import("compiler_rt/aulldiv.zig");
|
||||
|
||||
@ -409,104 +409,179 @@ fn createChildOnly(
|
||||
return child;
|
||||
}
|
||||
|
||||
fn userInputOptionsFromArgs(allocator: Allocator, args: anytype) UserInputOptionsMap {
|
||||
var user_input_options = UserInputOptionsMap.init(allocator);
|
||||
fn userInputOptionsFromArgs(arena: Allocator, args: anytype) UserInputOptionsMap {
|
||||
var map = UserInputOptionsMap.init(arena);
|
||||
inline for (@typeInfo(@TypeOf(args)).@"struct".fields) |field| {
|
||||
const v = @field(args, field.name);
|
||||
const T = @TypeOf(v);
|
||||
switch (T) {
|
||||
Target.Query => {
|
||||
user_input_options.put(field.name, .{
|
||||
.name = field.name,
|
||||
.value = .{ .scalar = v.zigTriple(allocator) catch @panic("OOM") },
|
||||
.used = false,
|
||||
}) catch @panic("OOM");
|
||||
user_input_options.put("cpu", .{
|
||||
.name = "cpu",
|
||||
.value = .{ .scalar = v.serializeCpuAlloc(allocator) catch @panic("OOM") },
|
||||
.used = false,
|
||||
}) catch @panic("OOM");
|
||||
},
|
||||
ResolvedTarget => {
|
||||
user_input_options.put(field.name, .{
|
||||
.name = field.name,
|
||||
.value = .{ .scalar = v.query.zigTriple(allocator) catch @panic("OOM") },
|
||||
.used = false,
|
||||
}) catch @panic("OOM");
|
||||
user_input_options.put("cpu", .{
|
||||
.name = "cpu",
|
||||
.value = .{ .scalar = v.query.serializeCpuAlloc(allocator) catch @panic("OOM") },
|
||||
.used = false,
|
||||
}) catch @panic("OOM");
|
||||
},
|
||||
LazyPath => {
|
||||
user_input_options.put(field.name, .{
|
||||
.name = field.name,
|
||||
.value = .{ .lazy_path = v.dupeInner(allocator) },
|
||||
.used = false,
|
||||
}) catch @panic("OOM");
|
||||
},
|
||||
[]const LazyPath => {
|
||||
var list = ArrayList(LazyPath).initCapacity(allocator, v.len) catch @panic("OOM");
|
||||
for (v) |lp| list.appendAssumeCapacity(lp.dupeInner(allocator));
|
||||
user_input_options.put(field.name, .{
|
||||
.name = field.name,
|
||||
.value = .{ .lazy_path_list = list },
|
||||
.used = false,
|
||||
}) catch @panic("OOM");
|
||||
},
|
||||
[]const u8 => {
|
||||
user_input_options.put(field.name, .{
|
||||
.name = field.name,
|
||||
.value = .{ .scalar = v },
|
||||
.used = false,
|
||||
}) catch @panic("OOM");
|
||||
},
|
||||
[]const []const u8 => {
|
||||
var list = ArrayList([]const u8).initCapacity(allocator, v.len) catch @panic("OOM");
|
||||
list.appendSliceAssumeCapacity(v);
|
||||
|
||||
user_input_options.put(field.name, .{
|
||||
.name = field.name,
|
||||
.value = .{ .list = list },
|
||||
.used = false,
|
||||
}) catch @panic("OOM");
|
||||
},
|
||||
else => switch (@typeInfo(T)) {
|
||||
.bool => {
|
||||
user_input_options.put(field.name, .{
|
||||
.name = field.name,
|
||||
.value = .{ .scalar = if (v) "true" else "false" },
|
||||
.used = false,
|
||||
}) catch @panic("OOM");
|
||||
},
|
||||
.@"enum", .enum_literal => {
|
||||
user_input_options.put(field.name, .{
|
||||
.name = field.name,
|
||||
.value = .{ .scalar = @tagName(v) },
|
||||
.used = false,
|
||||
}) catch @panic("OOM");
|
||||
},
|
||||
.comptime_int, .int => {
|
||||
user_input_options.put(field.name, .{
|
||||
.name = field.name,
|
||||
.value = .{ .scalar = std.fmt.allocPrint(allocator, "{d}", .{v}) catch @panic("OOM") },
|
||||
.used = false,
|
||||
}) catch @panic("OOM");
|
||||
},
|
||||
.comptime_float, .float => {
|
||||
user_input_options.put(field.name, .{
|
||||
.name = field.name,
|
||||
.value = .{ .scalar = std.fmt.allocPrint(allocator, "{e}", .{v}) catch @panic("OOM") },
|
||||
.used = false,
|
||||
}) catch @panic("OOM");
|
||||
},
|
||||
else => @compileError("option '" ++ field.name ++ "' has unsupported type: " ++ @typeName(T)),
|
||||
},
|
||||
}
|
||||
if (field.type == @Type(.null)) continue;
|
||||
addUserInputOptionFromArg(arena, &map, field, field.type, @field(args, field.name));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
return user_input_options;
|
||||
fn addUserInputOptionFromArg(
|
||||
arena: Allocator,
|
||||
map: *UserInputOptionsMap,
|
||||
field: std.builtin.Type.StructField,
|
||||
comptime T: type,
|
||||
/// If null, the value won't be added, but `T` will still be type-checked.
|
||||
maybe_value: ?T,
|
||||
) void {
|
||||
switch (T) {
|
||||
Target.Query => return if (maybe_value) |v| {
|
||||
map.put(field.name, .{
|
||||
.name = field.name,
|
||||
.value = .{ .scalar = v.zigTriple(arena) catch @panic("OOM") },
|
||||
.used = false,
|
||||
}) catch @panic("OOM");
|
||||
map.put("cpu", .{
|
||||
.name = "cpu",
|
||||
.value = .{ .scalar = v.serializeCpuAlloc(arena) catch @panic("OOM") },
|
||||
.used = false,
|
||||
}) catch @panic("OOM");
|
||||
},
|
||||
ResolvedTarget => return if (maybe_value) |v| {
|
||||
map.put(field.name, .{
|
||||
.name = field.name,
|
||||
.value = .{ .scalar = v.query.zigTriple(arena) catch @panic("OOM") },
|
||||
.used = false,
|
||||
}) catch @panic("OOM");
|
||||
map.put("cpu", .{
|
||||
.name = "cpu",
|
||||
.value = .{ .scalar = v.query.serializeCpuAlloc(arena) catch @panic("OOM") },
|
||||
.used = false,
|
||||
}) catch @panic("OOM");
|
||||
},
|
||||
std.zig.BuildId => return if (maybe_value) |v| {
|
||||
map.put(field.name, .{
|
||||
.name = field.name,
|
||||
.value = .{ .scalar = std.fmt.allocPrint(arena, "{f}", .{v}) catch @panic("OOM") },
|
||||
.used = false,
|
||||
}) catch @panic("OOM");
|
||||
},
|
||||
LazyPath => return if (maybe_value) |v| {
|
||||
map.put(field.name, .{
|
||||
.name = field.name,
|
||||
.value = .{ .lazy_path = v.dupeInner(arena) },
|
||||
.used = false,
|
||||
}) catch @panic("OOM");
|
||||
},
|
||||
[]const LazyPath => return if (maybe_value) |v| {
|
||||
var list = ArrayList(LazyPath).initCapacity(arena, v.len) catch @panic("OOM");
|
||||
for (v) |lp| list.appendAssumeCapacity(lp.dupeInner(arena));
|
||||
map.put(field.name, .{
|
||||
.name = field.name,
|
||||
.value = .{ .lazy_path_list = list },
|
||||
.used = false,
|
||||
}) catch @panic("OOM");
|
||||
},
|
||||
[]const u8 => return if (maybe_value) |v| {
|
||||
map.put(field.name, .{
|
||||
.name = field.name,
|
||||
.value = .{ .scalar = arena.dupe(u8, v) catch @panic("OOM") },
|
||||
.used = false,
|
||||
}) catch @panic("OOM");
|
||||
},
|
||||
[]const []const u8 => return if (maybe_value) |v| {
|
||||
var list = ArrayList([]const u8).initCapacity(arena, v.len) catch @panic("OOM");
|
||||
for (v) |s| list.appendAssumeCapacity(arena.dupe(u8, s) catch @panic("OOM"));
|
||||
map.put(field.name, .{
|
||||
.name = field.name,
|
||||
.value = .{ .list = list },
|
||||
.used = false,
|
||||
}) catch @panic("OOM");
|
||||
},
|
||||
else => switch (@typeInfo(T)) {
|
||||
.bool => return if (maybe_value) |v| {
|
||||
map.put(field.name, .{
|
||||
.name = field.name,
|
||||
.value = .{ .scalar = if (v) "true" else "false" },
|
||||
.used = false,
|
||||
}) catch @panic("OOM");
|
||||
},
|
||||
.@"enum", .enum_literal => return if (maybe_value) |v| {
|
||||
map.put(field.name, .{
|
||||
.name = field.name,
|
||||
.value = .{ .scalar = @tagName(v) },
|
||||
.used = false,
|
||||
}) catch @panic("OOM");
|
||||
},
|
||||
.comptime_int, .int => return if (maybe_value) |v| {
|
||||
map.put(field.name, .{
|
||||
.name = field.name,
|
||||
.value = .{ .scalar = std.fmt.allocPrint(arena, "{d}", .{v}) catch @panic("OOM") },
|
||||
.used = false,
|
||||
}) catch @panic("OOM");
|
||||
},
|
||||
.comptime_float, .float => return if (maybe_value) |v| {
|
||||
map.put(field.name, .{
|
||||
.name = field.name,
|
||||
.value = .{ .scalar = std.fmt.allocPrint(arena, "{x}", .{v}) catch @panic("OOM") },
|
||||
.used = false,
|
||||
}) catch @panic("OOM");
|
||||
},
|
||||
.pointer => |ptr_info| switch (ptr_info.size) {
|
||||
.one => switch (@typeInfo(ptr_info.child)) {
|
||||
.array => |array_info| {
|
||||
comptime var slice_info = ptr_info;
|
||||
slice_info.size = .slice;
|
||||
slice_info.is_const = true;
|
||||
slice_info.child = array_info.child;
|
||||
slice_info.sentinel_ptr = null;
|
||||
addUserInputOptionFromArg(
|
||||
arena,
|
||||
map,
|
||||
field,
|
||||
@Type(.{ .pointer = slice_info }),
|
||||
maybe_value orelse null,
|
||||
);
|
||||
return;
|
||||
},
|
||||
else => {},
|
||||
},
|
||||
.slice => switch (@typeInfo(ptr_info.child)) {
|
||||
.@"enum" => return if (maybe_value) |v| {
|
||||
var list = ArrayList([]const u8).initCapacity(arena, v.len) catch @panic("OOM");
|
||||
for (v) |tag| list.appendAssumeCapacity(@tagName(tag));
|
||||
map.put(field.name, .{
|
||||
.name = field.name,
|
||||
.value = .{ .list = list },
|
||||
.used = false,
|
||||
}) catch @panic("OOM");
|
||||
},
|
||||
else => {
|
||||
comptime var slice_info = ptr_info;
|
||||
slice_info.is_const = true;
|
||||
slice_info.sentinel_ptr = null;
|
||||
addUserInputOptionFromArg(
|
||||
arena,
|
||||
map,
|
||||
field,
|
||||
@Type(.{ .pointer = slice_info }),
|
||||
maybe_value orelse null,
|
||||
);
|
||||
return;
|
||||
},
|
||||
},
|
||||
else => {},
|
||||
},
|
||||
.null => unreachable,
|
||||
.optional => |info| switch (@typeInfo(info.child)) {
|
||||
.optional => {},
|
||||
else => {
|
||||
addUserInputOptionFromArg(
|
||||
arena,
|
||||
map,
|
||||
field,
|
||||
info.child,
|
||||
maybe_value orelse null,
|
||||
);
|
||||
return;
|
||||
},
|
||||
},
|
||||
else => {},
|
||||
},
|
||||
}
|
||||
@compileError("option '" ++ field.name ++ "' has unsupported type: " ++ @typeName(field.type));
|
||||
}
|
||||
|
||||
const OrderedUserValue = union(enum) {
|
||||
|
||||
@ -681,10 +681,14 @@ pub fn producesImplib(compile: *Compile) bool {
|
||||
return compile.isDll();
|
||||
}
|
||||
|
||||
/// Deprecated; use `compile.root_module.link_libc = true` instead.
|
||||
/// To be removed after 0.15.0 is tagged.
|
||||
pub fn linkLibC(compile: *Compile) void {
|
||||
compile.root_module.link_libc = true;
|
||||
}
|
||||
|
||||
/// Deprecated; use `compile.root_module.link_libcpp = true` instead.
|
||||
/// To be removed after 0.15.0 is tagged.
|
||||
pub fn linkLibCpp(compile: *Compile) void {
|
||||
compile.root_module.link_libcpp = true;
|
||||
}
|
||||
@ -802,10 +806,14 @@ fn runPkgConfig(compile: *Compile, lib_name: []const u8) !PkgConfigResult {
|
||||
};
|
||||
}
|
||||
|
||||
/// Deprecated; use `compile.root_module.linkSystemLibrary(name, .{})` instead.
|
||||
/// To be removed after 0.15.0 is tagged.
|
||||
pub fn linkSystemLibrary(compile: *Compile, name: []const u8) void {
|
||||
return compile.root_module.linkSystemLibrary(name, .{});
|
||||
}
|
||||
|
||||
/// Deprecated; use `compile.root_module.linkSystemLibrary(name, options)` instead.
|
||||
/// To be removed after 0.15.0 is tagged.
|
||||
pub fn linkSystemLibrary2(
|
||||
compile: *Compile,
|
||||
name: []const u8,
|
||||
@ -814,22 +822,26 @@ pub fn linkSystemLibrary2(
|
||||
return compile.root_module.linkSystemLibrary(name, options);
|
||||
}
|
||||
|
||||
/// Deprecated; use `c.root_module.linkFramework(name, .{})` instead.
|
||||
/// To be removed after 0.15.0 is tagged.
|
||||
pub fn linkFramework(c: *Compile, name: []const u8) void {
|
||||
c.root_module.linkFramework(name, .{});
|
||||
}
|
||||
|
||||
/// Handy when you have many C/C++ source files and want them all to have the same flags.
|
||||
/// Deprecated; use `compile.root_module.addCSourceFiles(options)` instead.
|
||||
/// To be removed after 0.15.0 is tagged.
|
||||
pub fn addCSourceFiles(compile: *Compile, options: Module.AddCSourceFilesOptions) void {
|
||||
compile.root_module.addCSourceFiles(options);
|
||||
}
|
||||
|
||||
/// Deprecated; use `compile.root_module.addCSourceFile(source)` instead.
|
||||
/// To be removed after 0.15.0 is tagged.
|
||||
pub fn addCSourceFile(compile: *Compile, source: Module.CSourceFile) void {
|
||||
compile.root_module.addCSourceFile(source);
|
||||
}
|
||||
|
||||
/// Resource files must have the extension `.rc`.
|
||||
/// Can be called regardless of target. The .rc file will be ignored
|
||||
/// if the target object format does not support embedded resources.
|
||||
/// Deprecated; use `compile.root_module.addWin32ResourceFile(source)` instead.
|
||||
/// To be removed after 0.15.0 is tagged.
|
||||
pub fn addWin32ResourceFile(compile: *Compile, source: Module.RcSourceFile) void {
|
||||
compile.root_module.addWin32ResourceFile(source);
|
||||
}
|
||||
@ -915,54 +927,80 @@ pub fn getEmittedLlvmBc(compile: *Compile) LazyPath {
|
||||
return compile.getEmittedFileGeneric(&compile.generated_llvm_bc);
|
||||
}
|
||||
|
||||
/// Deprecated; use `compile.root_module.addAssemblyFile(source)` instead.
|
||||
/// To be removed after 0.15.0 is tagged.
|
||||
pub fn addAssemblyFile(compile: *Compile, source: LazyPath) void {
|
||||
compile.root_module.addAssemblyFile(source);
|
||||
}
|
||||
|
||||
/// Deprecated; use `compile.root_module.addObjectFile(source)` instead.
|
||||
/// To be removed after 0.15.0 is tagged.
|
||||
pub fn addObjectFile(compile: *Compile, source: LazyPath) void {
|
||||
compile.root_module.addObjectFile(source);
|
||||
}
|
||||
|
||||
/// Deprecated; use `compile.root_module.addObject(object)` instead.
|
||||
/// To be removed after 0.15.0 is tagged.
|
||||
pub fn addObject(compile: *Compile, object: *Compile) void {
|
||||
compile.root_module.addObject(object);
|
||||
}
|
||||
|
||||
/// Deprecated; use `compile.root_module.linkLibrary(library)` instead.
|
||||
/// To be removed after 0.15.0 is tagged.
|
||||
pub fn linkLibrary(compile: *Compile, library: *Compile) void {
|
||||
compile.root_module.linkLibrary(library);
|
||||
}
|
||||
|
||||
/// Deprecated; use `compile.root_module.addAfterIncludePath(lazy_path)` instead.
|
||||
/// To be removed after 0.15.0 is tagged.
|
||||
pub fn addAfterIncludePath(compile: *Compile, lazy_path: LazyPath) void {
|
||||
compile.root_module.addAfterIncludePath(lazy_path);
|
||||
}
|
||||
|
||||
/// Deprecated; use `compile.root_module.addSystemIncludePath(lazy_path)` instead.
|
||||
/// To be removed after 0.15.0 is tagged.
|
||||
pub fn addSystemIncludePath(compile: *Compile, lazy_path: LazyPath) void {
|
||||
compile.root_module.addSystemIncludePath(lazy_path);
|
||||
}
|
||||
|
||||
/// Deprecated; use `compile.root_module.addIncludePath(lazy_path)` instead.
|
||||
/// To be removed after 0.15.0 is tagged.
|
||||
pub fn addIncludePath(compile: *Compile, lazy_path: LazyPath) void {
|
||||
compile.root_module.addIncludePath(lazy_path);
|
||||
}
|
||||
|
||||
/// Deprecated; use `compile.root_module.addConfigHeader(config_header)` instead.
|
||||
/// To be removed after 0.15.0 is tagged.
|
||||
pub fn addConfigHeader(compile: *Compile, config_header: *Step.ConfigHeader) void {
|
||||
compile.root_module.addConfigHeader(config_header);
|
||||
}
|
||||
|
||||
/// Deprecated; use `compile.root_module.addEmbedPath(lazy_path)` instead.
|
||||
/// To be removed after 0.15.0 is tagged.
|
||||
pub fn addEmbedPath(compile: *Compile, lazy_path: LazyPath) void {
|
||||
compile.root_module.addEmbedPath(lazy_path);
|
||||
}
|
||||
|
||||
/// Deprecated; use `compile.root_module.addLibraryPath(directory_path)` instead.
|
||||
/// To be removed after 0.15.0 is tagged.
|
||||
pub fn addLibraryPath(compile: *Compile, directory_path: LazyPath) void {
|
||||
compile.root_module.addLibraryPath(directory_path);
|
||||
}
|
||||
|
||||
/// Deprecated; use `compile.root_module.addRPath(directory_path)` instead.
|
||||
/// To be removed after 0.15.0 is tagged.
|
||||
pub fn addRPath(compile: *Compile, directory_path: LazyPath) void {
|
||||
compile.root_module.addRPath(directory_path);
|
||||
}
|
||||
|
||||
/// Deprecated; use `compile.root_module.addSystemFrameworkPath(directory_path)` instead.
|
||||
/// To be removed after 0.15.0 is tagged.
|
||||
pub fn addSystemFrameworkPath(compile: *Compile, directory_path: LazyPath) void {
|
||||
compile.root_module.addSystemFrameworkPath(directory_path);
|
||||
}
|
||||
|
||||
/// Deprecated; use `compile.root_module.addFrameworkPath(directory_path)` instead.
|
||||
/// To be removed after 0.15.0 is tagged.
|
||||
pub fn addFrameworkPath(compile: *Compile, directory_path: LazyPath) void {
|
||||
compile.root_module.addFrameworkPath(directory_path);
|
||||
}
|
||||
|
||||
@ -1391,6 +1391,16 @@ fn runCommand(
|
||||
}
|
||||
},
|
||||
else => {
|
||||
// On failure, print stderr if captured.
|
||||
const bad_exit = switch (result.term) {
|
||||
.Exited => |code| code != 0,
|
||||
.Signal, .Stopped, .Unknown => true,
|
||||
};
|
||||
|
||||
if (bad_exit) if (result.stdio.stderr) |err| {
|
||||
try step.addError("stderr:\n{s}", .{err});
|
||||
};
|
||||
|
||||
try step.handleChildProcessTerm(result.term, cwd, final_argv);
|
||||
},
|
||||
}
|
||||
|
||||
@ -354,7 +354,7 @@ pub fn Poller(comptime StreamEnum: type) type {
|
||||
const unused = r.buffer[r.end..];
|
||||
if (unused.len >= min_len) return unused;
|
||||
}
|
||||
if (r.seek > 0) r.rebase();
|
||||
if (r.seek > 0) r.rebase(r.buffer.len) catch unreachable;
|
||||
{
|
||||
var list: std.ArrayListUnmanaged(u8) = .{
|
||||
.items = r.buffer[0..r.end],
|
||||
@ -544,7 +544,6 @@ pub fn PollFiles(comptime StreamEnum: type) type {
|
||||
|
||||
test {
|
||||
_ = Reader;
|
||||
_ = Reader.Limited;
|
||||
_ = Writer;
|
||||
_ = tty;
|
||||
_ = @import("Io/test.zig");
|
||||
|
||||
@ -397,6 +397,7 @@ pub const Adapter = struct {
|
||||
a.err = err;
|
||||
return error.ReadFailed;
|
||||
};
|
||||
if (n == 0) return error.EndOfStream;
|
||||
w.advance(n);
|
||||
return n;
|
||||
}
|
||||
|
||||
@ -43,8 +43,8 @@ pub const VTable = struct {
|
||||
///
|
||||
/// In addition to, or instead of writing to `w`, the implementation may
|
||||
/// choose to store data in `buffer`, modifying `seek` and `end`
|
||||
/// accordingly. Stream implementations are encouraged to take advantage of
|
||||
/// this if simplifies the logic.
|
||||
/// accordingly. Implementations are encouraged to take advantage of
|
||||
/// this if it simplifies the logic.
|
||||
stream: *const fn (r: *Reader, w: *Writer, limit: Limit) StreamError!usize,
|
||||
|
||||
/// Consumes bytes from the internally tracked stream position without
|
||||
@ -67,6 +67,37 @@ pub const VTable = struct {
|
||||
///
|
||||
/// This function is only called when `buffer` is empty.
|
||||
discard: *const fn (r: *Reader, limit: Limit) Error!usize = defaultDiscard,
|
||||
|
||||
/// Returns number of bytes written to `data`.
|
||||
///
|
||||
/// `data` may not have nonzero length.
|
||||
///
|
||||
/// `data` may not contain an alias to `Reader.buffer`.
|
||||
///
|
||||
/// `data` is mutable because the implementation may to temporarily modify
|
||||
/// the fields in order to handle partial reads. Implementations must
|
||||
/// restore the original value before returning.
|
||||
///
|
||||
/// Implementations may ignore `data`, writing directly to `Reader.buffer`,
|
||||
/// modifying `seek` and `end` accordingly, and returning 0 from this
|
||||
/// function. Implementations are encouraged to take advantage of this if
|
||||
/// it simplifies the logic.
|
||||
///
|
||||
/// The default implementation calls `stream` with either `data[0]` or
|
||||
/// `Reader.buffer`, whichever is bigger.
|
||||
readVec: *const fn (r: *Reader, data: [][]u8) Error!usize = defaultReadVec,
|
||||
|
||||
/// Ensures `capacity` more data can be buffered without rebasing.
|
||||
///
|
||||
/// Asserts `capacity` is within buffer capacity, or that the stream ends
|
||||
/// within `capacity` bytes.
|
||||
///
|
||||
/// Only called when `capacity` cannot fit into the unused capacity of
|
||||
/// `buffer`.
|
||||
///
|
||||
/// The default implementation moves buffered data to the start of
|
||||
/// `buffer`, setting `seek` to zero, and cannot fail.
|
||||
rebase: *const fn (r: *Reader, capacity: usize) RebaseError!void = defaultRebase,
|
||||
};
|
||||
|
||||
pub const StreamError = error{
|
||||
@ -97,6 +128,10 @@ pub const ShortError = error{
|
||||
ReadFailed,
|
||||
};
|
||||
|
||||
pub const RebaseError = error{
|
||||
EndOfStream,
|
||||
};
|
||||
|
||||
pub const failing: Reader = .{
|
||||
.vtable = &.{
|
||||
.stream = failingStream,
|
||||
@ -122,6 +157,8 @@ pub fn fixed(buffer: []const u8) Reader {
|
||||
.vtable = &.{
|
||||
.stream = endingStream,
|
||||
.discard = endingDiscard,
|
||||
.readVec = endingReadVec,
|
||||
.rebase = endingRebase,
|
||||
},
|
||||
// This cast is safe because all potential writes to it will instead
|
||||
// return `error.EndOfStream`.
|
||||
@ -153,18 +190,18 @@ pub fn discard(r: *Reader, limit: Limit) Error!usize {
|
||||
}
|
||||
break :l .limited(n - buffered_len);
|
||||
} else .unlimited;
|
||||
r.seek = 0;
|
||||
r.end = 0;
|
||||
r.seek = r.end;
|
||||
const n = try r.vtable.discard(r, remaining);
|
||||
assert(n <= @intFromEnum(remaining));
|
||||
return buffered_len + n;
|
||||
}
|
||||
|
||||
pub fn defaultDiscard(r: *Reader, limit: Limit) Error!usize {
|
||||
assert(r.seek == 0);
|
||||
assert(r.end == 0);
|
||||
var dw: Writer.Discarding = .init(r.buffer);
|
||||
const n = r.stream(&dw.writer, limit) catch |err| switch (err) {
|
||||
assert(r.seek == r.end);
|
||||
r.seek = 0;
|
||||
r.end = 0;
|
||||
var d: Writer.Discarding = .init(r.buffer);
|
||||
const n = r.stream(&d.writer, limit) catch |err| switch (err) {
|
||||
error.WriteFailed => unreachable,
|
||||
error.ReadFailed => return error.ReadFailed,
|
||||
error.EndOfStream => return error.EndOfStream,
|
||||
@ -229,8 +266,7 @@ pub fn streamRemaining(r: *Reader, w: *Writer) StreamRemainingError!usize {
|
||||
/// number of bytes discarded.
|
||||
pub fn discardRemaining(r: *Reader) ShortError!usize {
|
||||
var offset: usize = r.end - r.seek;
|
||||
r.seek = 0;
|
||||
r.end = 0;
|
||||
r.seek = r.end;
|
||||
while (true) {
|
||||
offset += r.vtable.discard(r, .unlimited) catch |err| switch (err) {
|
||||
error.EndOfStream => return offset,
|
||||
@ -277,7 +313,8 @@ pub fn appendRemaining(
|
||||
list: *std.ArrayListAlignedUnmanaged(u8, alignment),
|
||||
limit: Limit,
|
||||
) LimitedAllocError!void {
|
||||
if (limit != .unlimited) assert(r.buffer.len != 0); // Needed to detect limit exceeded without losing data.
|
||||
if (limit == .unlimited) return appendRemainingUnlimited(r, gpa, alignment, list, 1);
|
||||
assert(r.buffer.len != 0); // Needed to detect limit exceeded without losing data.
|
||||
const buffer_contents = r.buffer[r.seek..r.end];
|
||||
const copy_len = limit.minInt(buffer_contents.len);
|
||||
try list.appendSlice(gpa, r.buffer[0..copy_len]);
|
||||
@ -286,32 +323,67 @@ pub fn appendRemaining(
|
||||
r.seek = 0;
|
||||
r.end = 0;
|
||||
var remaining = @intFromEnum(limit) - copy_len;
|
||||
// From here, we leave `buffer` empty, appending directly to `list`.
|
||||
var writer: Writer = .{
|
||||
.buffer = undefined,
|
||||
.end = undefined,
|
||||
.vtable = &.{ .drain = Writer.fixedDrain },
|
||||
};
|
||||
while (true) {
|
||||
try list.ensureUnusedCapacity(gpa, 1);
|
||||
try list.ensureUnusedCapacity(gpa, 2);
|
||||
const cap = list.unusedCapacitySlice();
|
||||
const dest = cap[0..@min(cap.len, remaining)];
|
||||
if (remaining - dest.len == 0) {
|
||||
// Additionally provides `buffer` to detect end.
|
||||
const new_remaining = readVecInner(r, &.{}, dest, remaining) catch |err| switch (err) {
|
||||
error.EndOfStream => {
|
||||
if (r.bufferedLen() != 0) return error.StreamTooLong;
|
||||
return;
|
||||
},
|
||||
error.ReadFailed => return error.ReadFailed,
|
||||
};
|
||||
list.items.len += remaining - new_remaining;
|
||||
remaining = new_remaining;
|
||||
} else {
|
||||
// Leave `buffer` empty, appending directly to `list`.
|
||||
var dest_w: Writer = .fixed(dest);
|
||||
const n = r.vtable.stream(r, &dest_w, .limited(dest.len)) catch |err| switch (err) {
|
||||
error.WriteFailed => unreachable, // Prevented by the limit.
|
||||
error.EndOfStream => return,
|
||||
error.ReadFailed => return error.ReadFailed,
|
||||
};
|
||||
list.items.len += n;
|
||||
remaining -= n;
|
||||
const dest = cap[0..@min(cap.len, remaining + 1)];
|
||||
writer.buffer = list.allocatedSlice();
|
||||
writer.end = list.items.len;
|
||||
const n = r.vtable.stream(r, &writer, .limited(dest.len)) catch |err| switch (err) {
|
||||
error.WriteFailed => unreachable, // Prevented by the limit.
|
||||
error.EndOfStream => return,
|
||||
error.ReadFailed => return error.ReadFailed,
|
||||
};
|
||||
list.items.len += n;
|
||||
if (n > remaining) {
|
||||
// Move the byte to `Reader.buffer` so it is not lost.
|
||||
assert(n - remaining == 1);
|
||||
assert(r.end == 0);
|
||||
r.buffer[0] = list.items[list.items.len - 1];
|
||||
list.items.len -= 1;
|
||||
r.end = 1;
|
||||
return;
|
||||
}
|
||||
remaining -= n;
|
||||
}
|
||||
}
|
||||
|
||||
pub const UnlimitedAllocError = Allocator.Error || ShortError;
|
||||
|
||||
pub fn appendRemainingUnlimited(
|
||||
r: *Reader,
|
||||
gpa: Allocator,
|
||||
comptime alignment: ?std.mem.Alignment,
|
||||
list: *std.ArrayListAlignedUnmanaged(u8, alignment),
|
||||
bump: usize,
|
||||
) UnlimitedAllocError!void {
|
||||
const buffer_contents = r.buffer[r.seek..r.end];
|
||||
try list.ensureUnusedCapacity(gpa, buffer_contents.len + bump);
|
||||
list.appendSliceAssumeCapacity(buffer_contents);
|
||||
r.seek = 0;
|
||||
r.end = 0;
|
||||
// From here, we leave `buffer` empty, appending directly to `list`.
|
||||
var writer: Writer = .{
|
||||
.buffer = undefined,
|
||||
.end = undefined,
|
||||
.vtable = &.{ .drain = Writer.fixedDrain },
|
||||
};
|
||||
while (true) {
|
||||
try list.ensureUnusedCapacity(gpa, bump);
|
||||
writer.buffer = list.allocatedSlice();
|
||||
writer.end = list.items.len;
|
||||
const n = r.vtable.stream(r, &writer, .limited(list.unusedCapacitySlice().len)) catch |err| switch (err) {
|
||||
error.WriteFailed => unreachable, // Prevented by the limit.
|
||||
error.EndOfStream => return,
|
||||
error.ReadFailed => return error.ReadFailed,
|
||||
};
|
||||
list.items.len += n;
|
||||
}
|
||||
}
|
||||
|
||||
@ -323,95 +395,50 @@ pub fn appendRemaining(
|
||||
///
|
||||
/// The reader's internal logical seek position moves forward in accordance
|
||||
/// with the number of bytes returned from this function.
|
||||
pub fn readVec(r: *Reader, data: []const []u8) Error!usize {
|
||||
return readVecLimit(r, data, .unlimited);
|
||||
}
|
||||
|
||||
/// Equivalent to `readVec` but reads at most `limit` bytes.
|
||||
///
|
||||
/// This ultimately will lower to a call to `stream`, but it must ensure
|
||||
/// that the buffer used has at least as much capacity, in case that function
|
||||
/// depends on a minimum buffer capacity. It also ensures that if the `stream`
|
||||
/// implementation calls `Writer.writableVector`, it will get this data slice
|
||||
/// along with the buffer at the end.
|
||||
pub fn readVecLimit(r: *Reader, data: []const []u8, limit: Limit) Error!usize {
|
||||
comptime assert(@intFromEnum(Limit.unlimited) == std.math.maxInt(usize));
|
||||
var remaining = @intFromEnum(limit);
|
||||
pub fn readVec(r: *Reader, data: [][]u8) Error!usize {
|
||||
var seek = r.seek;
|
||||
for (data, 0..) |buf, i| {
|
||||
const buffer_contents = r.buffer[r.seek..r.end];
|
||||
const copy_len = @min(buffer_contents.len, buf.len, remaining);
|
||||
@memcpy(buf[0..copy_len], buffer_contents[0..copy_len]);
|
||||
r.seek += copy_len;
|
||||
remaining -= copy_len;
|
||||
if (remaining == 0) break;
|
||||
const contents = r.buffer[seek..r.end];
|
||||
const copy_len = @min(contents.len, buf.len);
|
||||
@memcpy(buf[0..copy_len], contents[0..copy_len]);
|
||||
seek += copy_len;
|
||||
if (buf.len - copy_len == 0) continue;
|
||||
|
||||
// All of `buffer` has been copied to `data`. We now set up a structure
|
||||
// that enables the `Writer.writableVector` API, while also ensuring
|
||||
// API that directly operates on the `Writable.buffer` has its minimum
|
||||
// buffer capacity requirements met.
|
||||
r.seek = 0;
|
||||
r.end = 0;
|
||||
remaining = try readVecInner(r, data[i + 1 ..], buf[copy_len..], remaining);
|
||||
break;
|
||||
// All of `buffer` has been copied to `data`.
|
||||
const n = seek - r.seek;
|
||||
r.seek = seek;
|
||||
data[i] = buf[copy_len..];
|
||||
defer data[i] = buf;
|
||||
return n + (r.vtable.readVec(r, data[i..]) catch |err| switch (err) {
|
||||
error.EndOfStream => if (n == 0) return error.EndOfStream else 0,
|
||||
error.ReadFailed => return error.ReadFailed,
|
||||
});
|
||||
}
|
||||
return @intFromEnum(limit) - remaining;
|
||||
const n = seek - r.seek;
|
||||
r.seek = seek;
|
||||
return n;
|
||||
}
|
||||
|
||||
fn readVecInner(r: *Reader, middle: []const []u8, first: []u8, remaining: usize) Error!usize {
|
||||
var wrapper: Writer.VectorWrapper = .{
|
||||
.it = .{
|
||||
.first = first,
|
||||
.middle = middle,
|
||||
.last = r.buffer,
|
||||
},
|
||||
.writer = .{
|
||||
.buffer = if (first.len >= r.buffer.len) first else r.buffer,
|
||||
.vtable = Writer.VectorWrapper.vtable,
|
||||
},
|
||||
/// Writes to `Reader.buffer` or `data`, whichever has larger capacity.
|
||||
pub fn defaultReadVec(r: *Reader, data: [][]u8) Error!usize {
|
||||
assert(r.seek == r.end);
|
||||
r.seek = 0;
|
||||
r.end = 0;
|
||||
const first = data[0];
|
||||
const direct = first.len >= r.buffer.len;
|
||||
var writer: Writer = .{
|
||||
.buffer = if (direct) first else r.buffer,
|
||||
.end = 0,
|
||||
.vtable = &.{ .drain = Writer.fixedDrain },
|
||||
};
|
||||
// If the limit may pass beyond user buffer into Reader buffer, use
|
||||
// unlimited, allowing the Reader buffer to fill.
|
||||
const limit: Limit = l: {
|
||||
var n: usize = first.len;
|
||||
for (middle) |m| n += m.len;
|
||||
break :l if (remaining >= n) .unlimited else .limited(remaining);
|
||||
};
|
||||
var n = r.vtable.stream(r, &wrapper.writer, limit) catch |err| switch (err) {
|
||||
error.WriteFailed => {
|
||||
assert(!wrapper.used);
|
||||
if (wrapper.writer.buffer.ptr == first.ptr) {
|
||||
return remaining - wrapper.writer.end;
|
||||
} else {
|
||||
assert(wrapper.writer.end <= r.buffer.len);
|
||||
r.end = wrapper.writer.end;
|
||||
return remaining;
|
||||
}
|
||||
},
|
||||
const limit: Limit = .limited(writer.buffer.len - writer.end);
|
||||
const n = r.vtable.stream(r, &writer, limit) catch |err| switch (err) {
|
||||
error.WriteFailed => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
if (!wrapper.used) {
|
||||
if (wrapper.writer.buffer.ptr == first.ptr) {
|
||||
return remaining - n;
|
||||
} else {
|
||||
assert(n <= r.buffer.len);
|
||||
r.end = n;
|
||||
return remaining;
|
||||
}
|
||||
}
|
||||
if (n < first.len) return remaining - n;
|
||||
var result = remaining - first.len;
|
||||
n -= first.len;
|
||||
for (middle) |mid| {
|
||||
if (n < mid.len) {
|
||||
return result - n;
|
||||
}
|
||||
result -= mid.len;
|
||||
n -= mid.len;
|
||||
}
|
||||
assert(n <= r.buffer.len);
|
||||
r.end = n;
|
||||
return result;
|
||||
if (direct) return n;
|
||||
r.end += n;
|
||||
return 0;
|
||||
}
|
||||
|
||||
pub fn buffered(r: *Reader) []u8 {
|
||||
@ -422,8 +449,8 @@ pub fn bufferedLen(r: *const Reader) usize {
|
||||
return r.end - r.seek;
|
||||
}
|
||||
|
||||
pub fn hashed(r: *Reader, hasher: anytype) Hashed(@TypeOf(hasher)) {
|
||||
return .{ .in = r, .hasher = hasher };
|
||||
pub fn hashed(r: *Reader, hasher: anytype, buffer: []u8) Hashed(@TypeOf(hasher)) {
|
||||
return .init(r, hasher, buffer);
|
||||
}
|
||||
|
||||
pub fn readVecAll(r: *Reader, data: [][]u8) Error!void {
|
||||
@ -498,8 +525,7 @@ pub fn toss(r: *Reader, n: usize) void {
|
||||
|
||||
/// Equivalent to `toss(r.bufferedLen())`.
|
||||
pub fn tossBuffered(r: *Reader) void {
|
||||
r.seek = 0;
|
||||
r.end = 0;
|
||||
r.seek = r.end;
|
||||
}
|
||||
|
||||
/// Equivalent to `peek` followed by `toss`.
|
||||
@ -586,8 +612,7 @@ pub fn discardShort(r: *Reader, n: usize) ShortError!usize {
|
||||
return n;
|
||||
}
|
||||
var remaining = n - (r.end - r.seek);
|
||||
r.end = 0;
|
||||
r.seek = 0;
|
||||
r.seek = r.end;
|
||||
while (true) {
|
||||
const discard_len = r.vtable.discard(r, .limited(remaining)) catch |err| switch (err) {
|
||||
error.EndOfStream => return n - remaining,
|
||||
@ -625,29 +650,24 @@ pub fn readSliceAll(r: *Reader, buffer: []u8) Error!void {
|
||||
/// See also:
|
||||
/// * `readSliceAll`
|
||||
pub fn readSliceShort(r: *Reader, buffer: []u8) ShortError!usize {
|
||||
var i: usize = 0;
|
||||
const contents = r.buffer[r.seek..r.end];
|
||||
const copy_len = @min(buffer.len, contents.len);
|
||||
@memcpy(buffer[0..copy_len], contents[0..copy_len]);
|
||||
r.seek += copy_len;
|
||||
if (buffer.len - copy_len == 0) {
|
||||
@branchHint(.likely);
|
||||
return buffer.len;
|
||||
}
|
||||
var i: usize = copy_len;
|
||||
var data: [1][]u8 = undefined;
|
||||
while (true) {
|
||||
const buffer_contents = r.buffer[r.seek..r.end];
|
||||
const dest = buffer[i..];
|
||||
const copy_len = @min(dest.len, buffer_contents.len);
|
||||
@memcpy(dest[0..copy_len], buffer_contents[0..copy_len]);
|
||||
if (dest.len - copy_len == 0) {
|
||||
@branchHint(.likely);
|
||||
r.seek += copy_len;
|
||||
return buffer.len;
|
||||
}
|
||||
i += copy_len;
|
||||
r.end = 0;
|
||||
r.seek = 0;
|
||||
const remaining = buffer[i..];
|
||||
const new_remaining_len = readVecInner(r, &.{}, remaining, remaining.len) catch |err| switch (err) {
|
||||
data[0] = buffer[i..];
|
||||
i += readVec(r, &data) catch |err| switch (err) {
|
||||
error.EndOfStream => return i,
|
||||
error.ReadFailed => return error.ReadFailed,
|
||||
};
|
||||
if (new_remaining_len == 0) return buffer.len;
|
||||
i += remaining.len - new_remaining_len;
|
||||
if (buffer.len - i == 0) return buffer.len;
|
||||
}
|
||||
return buffer.len;
|
||||
}
|
||||
|
||||
/// Fill `buffer` with the next `buffer.len` bytes from the stream, advancing
|
||||
@ -780,11 +800,8 @@ pub fn peekDelimiterInclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 {
|
||||
@branchHint(.likely);
|
||||
return buffer[seek .. end + 1];
|
||||
}
|
||||
if (r.vtable.stream == &endingStream) {
|
||||
// Protect the `@constCast` of `fixed`.
|
||||
return error.EndOfStream;
|
||||
}
|
||||
r.rebase();
|
||||
// TODO take a parameter for max search length rather than relying on buffer capacity
|
||||
try rebase(r, r.buffer.len);
|
||||
while (r.buffer.len - r.end != 0) {
|
||||
const end_cap = r.buffer[r.end..];
|
||||
var writer: Writer = .fixed(end_cap);
|
||||
@ -1041,20 +1058,7 @@ pub fn fill(r: *Reader, n: usize) Error!void {
|
||||
/// Missing this optimization can result in wall-clock time for the most affected benchmarks
|
||||
/// increasing by a factor of 5 or more.
|
||||
fn fillUnbuffered(r: *Reader, n: usize) Error!void {
|
||||
if (r.seek + n <= r.buffer.len) while (true) {
|
||||
const end_cap = r.buffer[r.end..];
|
||||
var writer: Writer = .fixed(end_cap);
|
||||
r.end += r.vtable.stream(r, &writer, .limited(end_cap.len)) catch |err| switch (err) {
|
||||
error.WriteFailed => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
if (r.seek + n <= r.end) return;
|
||||
};
|
||||
if (r.vtable.stream == &endingStream) {
|
||||
// Protect the `@constCast` of `fixed`.
|
||||
return error.EndOfStream;
|
||||
}
|
||||
rebaseCapacity(r, n);
|
||||
try rebase(r, n);
|
||||
var writer: Writer = .{
|
||||
.buffer = r.buffer,
|
||||
.vtable = &.{ .drain = Writer.fixedDrain },
|
||||
@ -1074,7 +1078,7 @@ fn fillUnbuffered(r: *Reader, n: usize) Error!void {
|
||||
///
|
||||
/// Asserts buffer capacity is at least 1.
|
||||
pub fn fillMore(r: *Reader) Error!void {
|
||||
rebaseCapacity(r, 1);
|
||||
try rebase(r, 1);
|
||||
var writer: Writer = .{
|
||||
.buffer = r.buffer,
|
||||
.end = r.end,
|
||||
@ -1251,7 +1255,7 @@ pub fn takeLeb128(r: *Reader, comptime Result: type) TakeLeb128Error!Result {
|
||||
|
||||
pub fn expandTotalCapacity(r: *Reader, allocator: Allocator, n: usize) Allocator.Error!void {
|
||||
if (n <= r.buffer.len) return;
|
||||
if (r.seek > 0) rebase(r);
|
||||
if (r.seek > 0) rebase(r, r.buffer.len);
|
||||
var list: ArrayList(u8) = .{
|
||||
.items = r.buffer[0..r.end],
|
||||
.capacity = r.buffer.len,
|
||||
@ -1297,37 +1301,20 @@ fn takeMultipleOf7Leb128(r: *Reader, comptime Result: type) TakeLeb128Error!Resu
|
||||
}
|
||||
}
|
||||
|
||||
/// Left-aligns data such that `r.seek` becomes zero.
|
||||
///
|
||||
/// If `r.seek` is not already zero then `buffer` is mutated, making it illegal
|
||||
/// to call this function with a const-casted `buffer`, such as in the case of
|
||||
/// `fixed`. This issue can be avoided:
|
||||
/// * in implementations, by attempting a read before a rebase, in which
|
||||
/// case the read will return `error.EndOfStream`, preventing the rebase.
|
||||
/// * in usage, by copying into a mutable buffer before initializing `fixed`.
|
||||
pub fn rebase(r: *Reader) void {
|
||||
if (r.seek == 0) return;
|
||||
/// Ensures `capacity` more data can be buffered without rebasing.
|
||||
pub fn rebase(r: *Reader, capacity: usize) RebaseError!void {
|
||||
if (r.end + capacity <= r.buffer.len) return;
|
||||
return r.vtable.rebase(r, capacity);
|
||||
}
|
||||
|
||||
pub fn defaultRebase(r: *Reader, capacity: usize) RebaseError!void {
|
||||
if (r.end <= r.buffer.len - capacity) return;
|
||||
const data = r.buffer[r.seek..r.end];
|
||||
@memmove(r.buffer[0..data.len], data);
|
||||
r.seek = 0;
|
||||
r.end = data.len;
|
||||
}
|
||||
|
||||
/// Ensures `capacity` more data can be buffered without rebasing, by rebasing
|
||||
/// if necessary.
|
||||
///
|
||||
/// Asserts `capacity` is within the buffer capacity.
|
||||
///
|
||||
/// If the rebase occurs then `buffer` is mutated, making it illegal to call
|
||||
/// this function with a const-casted `buffer`, such as in the case of `fixed`.
|
||||
/// This issue can be avoided:
|
||||
/// * in implementations, by attempting a read before a rebase, in which
|
||||
/// case the read will return `error.EndOfStream`, preventing the rebase.
|
||||
/// * in usage, by copying into a mutable buffer before initializing `fixed`.
|
||||
pub fn rebaseCapacity(r: *Reader, capacity: usize) void {
|
||||
if (r.end > r.buffer.len - capacity) rebase(r);
|
||||
}
|
||||
|
||||
/// Advances the stream and decreases the size of the storage buffer by `n`,
|
||||
/// returning the range of bytes no longer accessible by `r`.
|
||||
///
|
||||
@ -1648,19 +1635,6 @@ test readVec {
|
||||
try testing.expectEqualStrings(std.ascii.letters[26..], bufs[1]);
|
||||
}
|
||||
|
||||
test readVecLimit {
|
||||
var r: Reader = .fixed(std.ascii.letters);
|
||||
var flat_buffer: [52]u8 = undefined;
|
||||
var bufs: [2][]u8 = .{
|
||||
flat_buffer[0..26],
|
||||
flat_buffer[26..],
|
||||
};
|
||||
// Short reads are possible with this function but not with fixed.
|
||||
try testing.expectEqual(50, try r.readVecLimit(&bufs, .limited(50)));
|
||||
try testing.expectEqualStrings(std.ascii.letters[0..26], bufs[0]);
|
||||
try testing.expectEqualStrings(std.ascii.letters[26..50], bufs[1][0..24]);
|
||||
}
|
||||
|
||||
test "expected error.EndOfStream" {
|
||||
// Unit test inspired by https://github.com/ziglang/zig/issues/17733
|
||||
var buffer: [3]u8 = undefined;
|
||||
@ -1670,6 +1644,17 @@ test "expected error.EndOfStream" {
|
||||
try std.testing.expectError(error.EndOfStream, r.take(3));
|
||||
}
|
||||
|
||||
test "readVec at end" {
|
||||
var reader_buffer: [8]u8 = "abcd1234".*;
|
||||
var reader: testing.Reader = .init(&reader_buffer, &.{});
|
||||
reader.interface.end = reader_buffer.len;
|
||||
|
||||
var out: [16]u8 = undefined;
|
||||
var vecs: [1][]u8 = .{&out};
|
||||
try testing.expectEqual(8, try reader.interface.readVec(&vecs));
|
||||
try testing.expectEqualStrings("abcd1234", vecs[0][0..8]);
|
||||
}
|
||||
|
||||
fn endingStream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize {
|
||||
_ = r;
|
||||
_ = w;
|
||||
@ -1677,12 +1662,24 @@ fn endingStream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize {
|
||||
return error.EndOfStream;
|
||||
}
|
||||
|
||||
fn endingReadVec(r: *Reader, data: [][]u8) Error!usize {
|
||||
_ = r;
|
||||
_ = data;
|
||||
return error.EndOfStream;
|
||||
}
|
||||
|
||||
fn endingDiscard(r: *Reader, limit: Limit) Error!usize {
|
||||
_ = r;
|
||||
_ = limit;
|
||||
return error.EndOfStream;
|
||||
}
|
||||
|
||||
fn endingRebase(r: *Reader, capacity: usize) RebaseError!void {
|
||||
_ = r;
|
||||
_ = capacity;
|
||||
return error.EndOfStream;
|
||||
}
|
||||
|
||||
fn failingStream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize {
|
||||
_ = r;
|
||||
_ = w;
|
||||
@ -1696,6 +1693,15 @@ fn failingDiscard(r: *Reader, limit: Limit) Error!usize {
|
||||
return error.ReadFailed;
|
||||
}
|
||||
|
||||
pub fn adaptToOldInterface(r: *Reader) std.Io.AnyReader {
|
||||
return .{ .context = r, .readFn = derpRead };
|
||||
}
|
||||
|
||||
fn derpRead(context: *const anyopaque, buffer: []u8) anyerror!usize {
|
||||
const r: *Reader = @constCast(@alignCast(@ptrCast(context)));
|
||||
return r.readSliceShort(buffer);
|
||||
}
|
||||
|
||||
test "readAlloc when the backing reader provides one byte at a time" {
|
||||
const str = "This is a test";
|
||||
var tiny_buffer: [1]u8 = undefined;
|
||||
@ -1759,15 +1765,16 @@ pub fn Hashed(comptime Hasher: type) type {
|
||||
return struct {
|
||||
in: *Reader,
|
||||
hasher: Hasher,
|
||||
interface: Reader,
|
||||
reader: Reader,
|
||||
|
||||
pub fn init(in: *Reader, hasher: Hasher, buffer: []u8) @This() {
|
||||
return .{
|
||||
.in = in,
|
||||
.hasher = hasher,
|
||||
.interface = .{
|
||||
.reader = .{
|
||||
.vtable = &.{
|
||||
.read = @This().read,
|
||||
.stream = @This().stream,
|
||||
.readVec = @This().readVec,
|
||||
.discard = @This().discard,
|
||||
},
|
||||
.buffer = buffer,
|
||||
@ -1777,33 +1784,97 @@ pub fn Hashed(comptime Hasher: type) type {
|
||||
};
|
||||
}
|
||||
|
||||
fn read(r: *Reader, w: *Writer, limit: Limit) StreamError!usize {
|
||||
const this: *@This() = @alignCast(@fieldParentPtr("interface", r));
|
||||
const data = w.writableVector(limit);
|
||||
fn stream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize {
|
||||
const this: *@This() = @alignCast(@fieldParentPtr("reader", r));
|
||||
const data = limit.slice(try w.writableSliceGreedy(1));
|
||||
var vec: [1][]u8 = .{data};
|
||||
const n = try this.in.readVec(&vec);
|
||||
this.hasher.update(data[0..n]);
|
||||
w.advance(n);
|
||||
return n;
|
||||
}
|
||||
|
||||
fn readVec(r: *Reader, data: [][]u8) Error!usize {
|
||||
const this: *@This() = @alignCast(@fieldParentPtr("reader", r));
|
||||
const n = try this.in.readVec(data);
|
||||
const result = w.advanceVector(n);
|
||||
var remaining: usize = n;
|
||||
for (data) |slice| {
|
||||
if (remaining < slice.len) {
|
||||
this.hasher.update(slice[0..remaining]);
|
||||
return result;
|
||||
return n;
|
||||
} else {
|
||||
remaining -= slice.len;
|
||||
this.hasher.update(slice);
|
||||
}
|
||||
}
|
||||
assert(remaining == 0);
|
||||
return result;
|
||||
return n;
|
||||
}
|
||||
|
||||
fn discard(r: *Reader, limit: Limit) Error!usize {
|
||||
const this: *@This() = @alignCast(@fieldParentPtr("interface", r));
|
||||
var w = this.hasher.writer(&.{});
|
||||
const n = this.in.stream(&w, limit) catch |err| switch (err) {
|
||||
error.WriteFailed => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
return n;
|
||||
const this: *@This() = @alignCast(@fieldParentPtr("reader", r));
|
||||
const peeked = limit.slice(try this.in.peekGreedy(1));
|
||||
this.hasher.update(peeked);
|
||||
this.in.toss(peeked.len);
|
||||
return peeked.len;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn writableVectorPosix(r: *Reader, buffer: []std.posix.iovec, data: []const []u8) Error!struct { usize, usize } {
|
||||
var i: usize = 0;
|
||||
var n: usize = 0;
|
||||
for (data) |buf| {
|
||||
if (buffer.len - i == 0) return .{ i, n };
|
||||
if (buf.len != 0) {
|
||||
buffer[i] = .{ .base = buf.ptr, .len = buf.len };
|
||||
i += 1;
|
||||
n += buf.len;
|
||||
}
|
||||
}
|
||||
assert(r.seek == r.end);
|
||||
const buf = r.buffer;
|
||||
if (buf.len != 0) {
|
||||
buffer[i] = .{ .base = buf.ptr, .len = buf.len };
|
||||
i += 1;
|
||||
}
|
||||
return .{ i, n };
|
||||
}
|
||||
|
||||
pub fn writableVectorWsa(
|
||||
r: *Reader,
|
||||
buffer: []std.os.windows.ws2_32.WSABUF,
|
||||
data: []const []u8,
|
||||
) Error!struct { usize, usize } {
|
||||
var i: usize = 0;
|
||||
var n: usize = 0;
|
||||
for (data) |buf| {
|
||||
if (buffer.len - i == 0) return .{ i, n };
|
||||
if (buf.len == 0) continue;
|
||||
if (std.math.cast(u32, buf.len)) |len| {
|
||||
buffer[i] = .{ .buf = buf.ptr, .len = len };
|
||||
i += 1;
|
||||
n += len;
|
||||
continue;
|
||||
}
|
||||
buffer[i] = .{ .buf = buf.ptr, .len = std.math.maxInt(u32) };
|
||||
i += 1;
|
||||
n += std.math.maxInt(u32);
|
||||
return .{ i, n };
|
||||
}
|
||||
assert(r.seek == r.end);
|
||||
const buf = r.buffer;
|
||||
if (buf.len != 0) {
|
||||
if (std.math.cast(u32, buf.len)) |len| {
|
||||
buffer[i] = .{ .buf = buf.ptr, .len = len };
|
||||
} else {
|
||||
buffer[i] = .{ .buf = buf.ptr, .len = std.math.maxInt(u32) };
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
return .{ i, n };
|
||||
}
|
||||
|
||||
test {
|
||||
_ = Limited;
|
||||
}
|
||||
|
||||
@ -342,97 +342,6 @@ pub fn writableSlicePreserve(w: *Writer, preserve_len: usize, len: usize) Error!
|
||||
return big_slice[0..len];
|
||||
}
|
||||
|
||||
pub const WritableVectorIterator = struct {
|
||||
first: []u8,
|
||||
middle: []const []u8 = &.{},
|
||||
last: []u8 = &.{},
|
||||
index: usize = 0,
|
||||
|
||||
pub fn next(it: *WritableVectorIterator) ?[]u8 {
|
||||
while (true) {
|
||||
const i = it.index;
|
||||
it.index += 1;
|
||||
if (i == 0) {
|
||||
if (it.first.len == 0) continue;
|
||||
return it.first;
|
||||
}
|
||||
const middle_index = i - 1;
|
||||
if (middle_index < it.middle.len) {
|
||||
const middle = it.middle[middle_index];
|
||||
if (middle.len == 0) continue;
|
||||
return middle;
|
||||
}
|
||||
if (middle_index == it.middle.len) {
|
||||
if (it.last.len == 0) continue;
|
||||
return it.last;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const VectorWrapper = struct {
|
||||
writer: Writer,
|
||||
it: WritableVectorIterator,
|
||||
/// Tracks whether the "writable vector" API was used.
|
||||
used: bool = false,
|
||||
pub const vtable: *const VTable = &unique_vtable_allocation;
|
||||
/// This is intended to be constant but it must be a unique address for
|
||||
/// `@fieldParentPtr` to work.
|
||||
var unique_vtable_allocation: VTable = .{ .drain = fixedDrain };
|
||||
};
|
||||
|
||||
pub fn writableVectorIterator(w: *Writer) Error!WritableVectorIterator {
|
||||
if (w.vtable == VectorWrapper.vtable) {
|
||||
const wrapper: *VectorWrapper = @fieldParentPtr("writer", w);
|
||||
wrapper.used = true;
|
||||
return wrapper.it;
|
||||
}
|
||||
return .{ .first = try writableSliceGreedy(w, 1) };
|
||||
}
|
||||
|
||||
pub fn writableVectorPosix(w: *Writer, buffer: []std.posix.iovec, limit: Limit) Error![]std.posix.iovec {
|
||||
var it = try writableVectorIterator(w);
|
||||
var i: usize = 0;
|
||||
var remaining = limit;
|
||||
while (it.next()) |full_buffer| {
|
||||
if (!remaining.nonzero()) break;
|
||||
if (buffer.len - i == 0) break;
|
||||
const buf = remaining.slice(full_buffer);
|
||||
if (buf.len == 0) continue;
|
||||
buffer[i] = .{ .base = buf.ptr, .len = buf.len };
|
||||
i += 1;
|
||||
remaining = remaining.subtract(buf.len).?;
|
||||
}
|
||||
return buffer[0..i];
|
||||
}
|
||||
|
||||
pub fn writableVectorWsa(
|
||||
w: *Writer,
|
||||
buffer: []std.os.windows.ws2_32.WSABUF,
|
||||
limit: Limit,
|
||||
) Error![]std.os.windows.ws2_32.WSABUF {
|
||||
var it = try writableVectorIterator(w);
|
||||
var i: usize = 0;
|
||||
var remaining = limit;
|
||||
while (it.next()) |full_buffer| {
|
||||
if (!remaining.nonzero()) break;
|
||||
if (buffer.len - i == 0) break;
|
||||
const buf = remaining.slice(full_buffer);
|
||||
if (buf.len == 0) continue;
|
||||
if (std.math.cast(u32, buf.len)) |len| {
|
||||
buffer[i] = .{ .buf = buf.ptr, .len = len };
|
||||
i += 1;
|
||||
remaining = remaining.subtract(len).?;
|
||||
continue;
|
||||
}
|
||||
buffer[i] = .{ .buf = buf.ptr, .len = std.math.maxInt(u32) };
|
||||
i += 1;
|
||||
break;
|
||||
}
|
||||
return buffer[0..i];
|
||||
}
|
||||
|
||||
pub fn ensureUnusedCapacity(w: *Writer, n: usize) Error!void {
|
||||
_ = try writableSliceGreedy(w, n);
|
||||
}
|
||||
@ -451,13 +360,6 @@ pub fn advance(w: *Writer, n: usize) void {
|
||||
w.end = new_end;
|
||||
}
|
||||
|
||||
/// After calling `writableVector`, this function tracks how many bytes were
|
||||
/// written to it.
|
||||
pub fn advanceVector(w: *Writer, n: usize) usize {
|
||||
if (w.vtable != VectorWrapper.vtable) advance(w, n);
|
||||
return n;
|
||||
}
|
||||
|
||||
/// The `data` parameter is mutable because this function needs to mutate the
|
||||
/// fields in order to handle partial writes from `VTable.writeSplat`.
|
||||
pub fn writeVecAll(w: *Writer, data: [][]const u8) Error!void {
|
||||
@ -1614,17 +1516,23 @@ pub fn printFloatHexOptions(w: *Writer, value: anytype, options: std.fmt.Number)
|
||||
}
|
||||
|
||||
pub fn printFloatHex(w: *Writer, value: anytype, case: std.fmt.Case, opt_precision: ?usize) Error!void {
|
||||
if (std.math.signbit(value)) try w.writeByte('-');
|
||||
if (std.math.isNan(value)) return w.writeAll(switch (case) {
|
||||
const v = switch (@TypeOf(value)) {
|
||||
// comptime_float internally is a f128; this preserves precision.
|
||||
comptime_float => @as(f128, value),
|
||||
else => value,
|
||||
};
|
||||
|
||||
if (std.math.signbit(v)) try w.writeByte('-');
|
||||
if (std.math.isNan(v)) return w.writeAll(switch (case) {
|
||||
.lower => "nan",
|
||||
.upper => "NAN",
|
||||
});
|
||||
if (std.math.isInf(value)) return w.writeAll(switch (case) {
|
||||
if (std.math.isInf(v)) return w.writeAll(switch (case) {
|
||||
.lower => "inf",
|
||||
.upper => "INF",
|
||||
});
|
||||
|
||||
const T = @TypeOf(value);
|
||||
const T = @TypeOf(v);
|
||||
const TU = std.meta.Int(.unsigned, @bitSizeOf(T));
|
||||
|
||||
const mantissa_bits = std.math.floatMantissaBits(T);
|
||||
@ -1634,7 +1542,7 @@ pub fn printFloatHex(w: *Writer, value: anytype, case: std.fmt.Case, opt_precisi
|
||||
const exponent_mask = (1 << exponent_bits) - 1;
|
||||
const exponent_bias = (1 << (exponent_bits - 1)) - 1;
|
||||
|
||||
const as_bits: TU = @bitCast(value);
|
||||
const as_bits: TU = @bitCast(v);
|
||||
var mantissa = as_bits & mantissa_mask;
|
||||
var exponent: i32 = @as(u16, @truncate((as_bits >> mantissa_bits) & exponent_mask));
|
||||
|
||||
@ -2361,7 +2269,7 @@ pub fn fixedDrain(w: *Writer, data: []const []const u8, splat: usize) Error!usiz
|
||||
const pattern = data[data.len - 1];
|
||||
const dest = w.buffer[w.end..];
|
||||
switch (pattern.len) {
|
||||
0 => return w.end,
|
||||
0 => return 0,
|
||||
1 => {
|
||||
assert(splat >= dest.len);
|
||||
@memset(dest, pattern[0]);
|
||||
@ -2381,6 +2289,13 @@ pub fn fixedDrain(w: *Writer, data: []const []const u8, splat: usize) Error!usiz
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unreachableDrain(w: *Writer, data: []const []const u8, splat: usize) Error!usize {
|
||||
_ = w;
|
||||
_ = data;
|
||||
_ = splat;
|
||||
unreachable;
|
||||
}
|
||||
|
||||
/// Provides a `Writer` implementation based on calling `Hasher.update`, sending
|
||||
/// all data also to an underlying `Writer`.
|
||||
///
|
||||
@ -2391,6 +2306,8 @@ pub fn fixedDrain(w: *Writer, data: []const []const u8, splat: usize) Error!usiz
|
||||
/// generic. A better solution will involve creating a writer for each hash
|
||||
/// function, where the splat buffer can be tailored to the hash implementation
|
||||
/// details.
|
||||
///
|
||||
/// Contrast with `Hashing` which terminates the stream pipeline.
|
||||
pub fn Hashed(comptime Hasher: type) type {
|
||||
return struct {
|
||||
out: *Writer,
|
||||
@ -2463,6 +2380,52 @@ pub fn Hashed(comptime Hasher: type) type {
|
||||
};
|
||||
}
|
||||
|
||||
/// Provides a `Writer` implementation based on calling `Hasher.update`,
|
||||
/// discarding all data.
|
||||
///
|
||||
/// This implementation makes suboptimal buffering decisions due to being
|
||||
/// generic. A better solution will involve creating a writer for each hash
|
||||
/// function, where the splat buffer can be tailored to the hash implementation
|
||||
/// details.
|
||||
///
|
||||
/// The total number of bytes written is stored in `hasher`.
|
||||
///
|
||||
/// Contrast with `Hashed` which also passes the data to an underlying stream.
|
||||
pub fn Hashing(comptime Hasher: type) type {
|
||||
return struct {
|
||||
hasher: Hasher,
|
||||
writer: Writer,
|
||||
|
||||
pub fn init(buffer: []u8) @This() {
|
||||
return .initHasher(.init(.{}), buffer);
|
||||
}
|
||||
|
||||
pub fn initHasher(hasher: Hasher, buffer: []u8) @This() {
|
||||
return .{
|
||||
.hasher = hasher,
|
||||
.writer = .{
|
||||
.buffer = buffer,
|
||||
.vtable = &.{ .drain = @This().drain },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
fn drain(w: *Writer, data: []const []const u8, splat: usize) Error!usize {
|
||||
const this: *@This() = @alignCast(@fieldParentPtr("writer", w));
|
||||
const hasher = &this.hasher;
|
||||
hasher.update(w.buffered());
|
||||
w.end = 0;
|
||||
var n: usize = 0;
|
||||
for (data[0 .. data.len - 1]) |slice| {
|
||||
hasher.update(slice);
|
||||
n += slice.len;
|
||||
}
|
||||
for (0..splat) |_| hasher.update(data[data.len - 1]);
|
||||
return n + splat * data[data.len - 1].len;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Maintains `Writer` state such that it writes to the unused capacity of an
|
||||
/// array list, filling it up completely before making a call through the
|
||||
/// vtable, causing a resize. Consequently, the same, optimized, non-generic
|
||||
|
||||
@ -57,51 +57,6 @@ test "write a file, read it, then delete it" {
|
||||
try tmp.dir.deleteFile(tmp_file_name);
|
||||
}
|
||||
|
||||
test "BitStreams with File Stream" {
|
||||
var tmp = tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
|
||||
const tmp_file_name = "temp_test_file.txt";
|
||||
{
|
||||
var file = try tmp.dir.createFile(tmp_file_name, .{});
|
||||
defer file.close();
|
||||
|
||||
var bit_stream = io.bitWriter(native_endian, file.deprecatedWriter());
|
||||
|
||||
try bit_stream.writeBits(@as(u2, 1), 1);
|
||||
try bit_stream.writeBits(@as(u5, 2), 2);
|
||||
try bit_stream.writeBits(@as(u128, 3), 3);
|
||||
try bit_stream.writeBits(@as(u8, 4), 4);
|
||||
try bit_stream.writeBits(@as(u9, 5), 5);
|
||||
try bit_stream.writeBits(@as(u1, 1), 1);
|
||||
try bit_stream.flushBits();
|
||||
}
|
||||
{
|
||||
var file = try tmp.dir.openFile(tmp_file_name, .{});
|
||||
defer file.close();
|
||||
|
||||
var bit_stream = io.bitReader(native_endian, file.deprecatedReader());
|
||||
|
||||
var out_bits: u16 = undefined;
|
||||
|
||||
try expect(1 == try bit_stream.readBits(u2, 1, &out_bits));
|
||||
try expect(out_bits == 1);
|
||||
try expect(2 == try bit_stream.readBits(u5, 2, &out_bits));
|
||||
try expect(out_bits == 2);
|
||||
try expect(3 == try bit_stream.readBits(u128, 3, &out_bits));
|
||||
try expect(out_bits == 3);
|
||||
try expect(4 == try bit_stream.readBits(u8, 4, &out_bits));
|
||||
try expect(out_bits == 4);
|
||||
try expect(5 == try bit_stream.readBits(u9, 5, &out_bits));
|
||||
try expect(out_bits == 5);
|
||||
try expect(1 == try bit_stream.readBits(u1, 1, &out_bits));
|
||||
try expect(out_bits == 1);
|
||||
|
||||
try expectError(error.EndOfStream, bit_stream.readBitsNoEof(u1, 1));
|
||||
}
|
||||
try tmp.dir.deleteFile(tmp_file_name);
|
||||
}
|
||||
|
||||
test "File seek ops" {
|
||||
var tmp = tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
|
||||
@ -25,6 +25,7 @@ redraw_event: std.Thread.ResetEvent,
|
||||
/// Accessed atomically.
|
||||
done: bool,
|
||||
need_clear: bool,
|
||||
status: Status,
|
||||
|
||||
refresh_rate_ns: u64,
|
||||
initial_delay_ns: u64,
|
||||
@ -47,6 +48,22 @@ node_freelist: Freelist,
|
||||
/// value may at times temporarily exceed the node count.
|
||||
node_end_index: u32,
|
||||
|
||||
pub const Status = enum {
|
||||
/// Indicates the application is progressing towards completion of a task.
|
||||
/// Unless the application is interactive, this is the only status the
|
||||
/// program will ever have!
|
||||
working,
|
||||
/// The application has completed an operation, and is now waiting for user
|
||||
/// input rather than calling exit(0).
|
||||
success,
|
||||
/// The application encountered an error, and is now waiting for user input
|
||||
/// rather than calling exit(1).
|
||||
failure,
|
||||
/// The application encountered at least one error, but is still working on
|
||||
/// more tasks.
|
||||
failure_working,
|
||||
};
|
||||
|
||||
const Freelist = packed struct(u32) {
|
||||
head: Node.OptionalIndex,
|
||||
/// Whenever `node_freelist` is added to, this generation is incremented
|
||||
@ -383,6 +400,7 @@ var global_progress: Progress = .{
|
||||
.draw_buffer = undefined,
|
||||
.done = false,
|
||||
.need_clear = false,
|
||||
.status = .working,
|
||||
|
||||
.node_parents = &node_parents_buffer,
|
||||
.node_storage = &node_storage_buffer,
|
||||
@ -498,6 +516,11 @@ pub fn start(options: Options) Node {
|
||||
return root_node;
|
||||
}
|
||||
|
||||
pub fn setStatus(new_status: Status) void {
|
||||
if (noop_impl) return;
|
||||
@atomicStore(Status, &global_progress.status, new_status, .monotonic);
|
||||
}
|
||||
|
||||
/// Returns whether a resize is needed to learn the terminal size.
|
||||
fn wait(timeout_ns: u64) bool {
|
||||
const resize_flag = if (global_progress.redraw_event.timedWait(timeout_ns)) |_|
|
||||
@ -678,6 +701,14 @@ const save = "\x1b7";
|
||||
const restore = "\x1b8";
|
||||
const finish_sync = "\x1b[?2026l";
|
||||
|
||||
const progress_remove = "\x1b]9;4;0\x07";
|
||||
const @"progress_normal {d}" = "\x1b]9;4;1;{d}\x07";
|
||||
const @"progress_error {d}" = "\x1b]9;4;2;{d}\x07";
|
||||
const progress_pulsing = "\x1b]9;4;3\x07";
|
||||
const progress_pulsing_error = "\x1b]9;4;2\x07";
|
||||
const progress_normal_100 = "\x1b]9;4;1;100\x07";
|
||||
const progress_error_100 = "\x1b]9;4;2;100\x07";
|
||||
|
||||
const TreeSymbol = enum {
|
||||
/// ├─
|
||||
tee,
|
||||
@ -760,7 +791,7 @@ fn clearWrittenWithEscapeCodes() anyerror!void {
|
||||
if (noop_impl or !global_progress.need_clear) return;
|
||||
|
||||
global_progress.need_clear = false;
|
||||
try write(clear);
|
||||
try write(clear ++ progress_remove);
|
||||
}
|
||||
|
||||
/// U+25BA or ►
|
||||
@ -1203,6 +1234,43 @@ fn computeRedraw(serialized_buffer: *Serialized.Buffer) struct { []u8, usize } {
|
||||
i, const nl_n = computeNode(buf, i, 0, serialized, children, root_node_index);
|
||||
|
||||
if (global_progress.terminal_mode == .ansi_escape_codes) {
|
||||
{
|
||||
// Set progress state https://conemu.github.io/en/AnsiEscapeCodes.html#ConEmu_specific_OSC
|
||||
const root_storage = &serialized.storage[0];
|
||||
const storage = if (root_storage.name[0] != 0 or children[0].child == .none) root_storage else &serialized.storage[@intFromEnum(children[0].child)];
|
||||
const estimated_total = storage.estimated_total_count;
|
||||
const completed_items = storage.completed_count;
|
||||
const status = @atomicLoad(Status, &global_progress.status, .monotonic);
|
||||
switch (status) {
|
||||
.working => {
|
||||
if (estimated_total == 0) {
|
||||
buf[i..][0..progress_pulsing.len].* = progress_pulsing.*;
|
||||
i += progress_pulsing.len;
|
||||
} else {
|
||||
const percent = completed_items * 100 / estimated_total;
|
||||
i += (std.fmt.bufPrint(buf[i..], @"progress_normal {d}", .{percent}) catch &.{}).len;
|
||||
}
|
||||
},
|
||||
.success => {
|
||||
buf[i..][0..progress_remove.len].* = progress_remove.*;
|
||||
i += progress_remove.len;
|
||||
},
|
||||
.failure => {
|
||||
buf[i..][0..progress_error_100.len].* = progress_error_100.*;
|
||||
i += progress_error_100.len;
|
||||
},
|
||||
.failure_working => {
|
||||
if (estimated_total == 0) {
|
||||
buf[i..][0..progress_pulsing_error.len].* = progress_pulsing_error.*;
|
||||
i += progress_pulsing_error.len;
|
||||
} else {
|
||||
const percent = completed_items * 100 / estimated_total;
|
||||
i += (std.fmt.bufPrint(buf[i..], @"progress_error {d}", .{percent}) catch &.{}).len;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if (nl_n > 0) {
|
||||
buf[i] = '\r';
|
||||
i += 1;
|
||||
@ -1480,6 +1548,7 @@ const have_sigwinch = switch (builtin.os.tag) {
|
||||
.visionos,
|
||||
.dragonfly,
|
||||
.freebsd,
|
||||
.serenity,
|
||||
=> true,
|
||||
|
||||
else => false,
|
||||
|
||||
@ -897,8 +897,8 @@ pub const VaList = switch (builtin.cpu.arch) {
|
||||
.windows => *u8,
|
||||
.ios, .macos, .tvos, .watchos, .visionos => *u8,
|
||||
else => switch (builtin.zig_backend) {
|
||||
.stage2_aarch64 => VaListAarch64,
|
||||
else => @compileError("disabled due to miscompilations"),
|
||||
else => VaListAarch64,
|
||||
.stage2_llvm => @compileError("disabled due to miscompilations"),
|
||||
},
|
||||
},
|
||||
.arm, .armeb, .thumb, .thumbeb => switch (builtin.os.tag) {
|
||||
@ -923,7 +923,10 @@ pub const VaList = switch (builtin.cpu.arch) {
|
||||
.wasm32, .wasm64 => *anyopaque,
|
||||
.x86 => *u8,
|
||||
.x86_64 => switch (builtin.os.tag) {
|
||||
.windows => @compileError("disabled due to miscompilations"), // *u8,
|
||||
.windows => switch (builtin.zig_backend) {
|
||||
else => *u8,
|
||||
.stage2_llvm => @compileError("disabled due to miscompilations"),
|
||||
},
|
||||
else => VaListX86_64,
|
||||
},
|
||||
.xtensa => VaListXtensa,
|
||||
|
||||
@ -204,6 +204,29 @@ pub const passwd = switch (native_os) {
|
||||
shell: ?[*:0]const u8, // default shell
|
||||
expire: time_t, // account expiration
|
||||
},
|
||||
.dragonfly, .freebsd => extern struct {
|
||||
name: ?[*:0]const u8, // user name
|
||||
passwd: ?[*:0]const u8, // encrypted password
|
||||
uid: uid_t, // user uid
|
||||
gid: gid_t, // user gid
|
||||
change: time_t, // password change time
|
||||
class: ?[*:0]const u8, // user access class
|
||||
gecos: ?[*:0]const u8, // Honeywell login info
|
||||
dir: ?[*:0]const u8, // home directory
|
||||
shell: ?[*:0]const u8, // default shell
|
||||
expire: time_t, // account expiration
|
||||
fields: c_int, // internal
|
||||
},
|
||||
else => void,
|
||||
};
|
||||
|
||||
pub const group = switch (native_os) {
|
||||
.linux, .freebsd, .openbsd, .dragonfly, .netbsd, .macos => extern struct {
|
||||
name: ?[*:0]const u8,
|
||||
passwd: ?[*:0]const u8,
|
||||
gid: gid_t,
|
||||
mem: [*:null]?[*:0]const u8,
|
||||
},
|
||||
else => void,
|
||||
};
|
||||
|
||||
@ -3291,8 +3314,8 @@ pub const T = switch (native_os) {
|
||||
.macos, .ios, .tvos, .watchos, .visionos => struct {
|
||||
pub const IOCGWINSZ = ior(0x40000000, 't', 104, @sizeOf(winsize));
|
||||
|
||||
fn ior(inout: u32, group: usize, num: usize, len: usize) usize {
|
||||
return (inout | ((len & IOCPARM_MASK) << 16) | ((group) << 8) | (num));
|
||||
fn ior(inout: u32, group_arg: usize, num: usize, len: usize) usize {
|
||||
return (inout | ((len & IOCPARM_MASK) << 16) | ((group_arg) << 8) | (num));
|
||||
}
|
||||
},
|
||||
.freebsd => struct {
|
||||
@ -4121,7 +4144,7 @@ pub const msghdr_const = switch (native_os) {
|
||||
.serenity => extern struct {
|
||||
name: ?*const anyopaque,
|
||||
namelen: socklen_t,
|
||||
iov: [*]const iovec,
|
||||
iov: [*]const iovec_const,
|
||||
iovlen: c_uint,
|
||||
control: ?*const anyopaque,
|
||||
controllen: socklen_t,
|
||||
@ -8604,7 +8627,7 @@ pub const O = switch (native_os) {
|
||||
},
|
||||
// https://github.com/SerenityOS/serenity/blob/2808b0376406a40e31293bb3bcb9170374e90506/Kernel/API/POSIX/fcntl.h#L28-L43
|
||||
.serenity => packed struct(c_int) {
|
||||
ACCMODE: std.posix.ACCMODE = .RDONLY,
|
||||
ACCMODE: std.posix.ACCMODE = .NONE,
|
||||
EXEC: bool = false,
|
||||
CREAT: bool = false,
|
||||
EXCL: bool = false,
|
||||
@ -8760,10 +8783,10 @@ pub const MAP = switch (native_os) {
|
||||
},
|
||||
// https://github.com/SerenityOS/serenity/blob/6d59d4d3d9e76e39112842ec487840828f1c9bfe/Kernel/API/POSIX/sys/mman.h#L16-L26
|
||||
.serenity => packed struct(c_int) {
|
||||
FILE: bool = false,
|
||||
SHARED: bool = false,
|
||||
PRIVATE: bool = false,
|
||||
_3: u2 = 0,
|
||||
TYPE: enum(u4) {
|
||||
SHARED = 0x01,
|
||||
PRIVATE = 0x02,
|
||||
},
|
||||
FIXED: bool = false,
|
||||
ANONYMOUS: bool = false,
|
||||
STACK: bool = false,
|
||||
@ -8771,7 +8794,7 @@ pub const MAP = switch (native_os) {
|
||||
RANDOMIZED: bool = false,
|
||||
PURGEABLE: bool = false,
|
||||
FIXED_NOREPLACE: bool = false,
|
||||
_: std.meta.Int(.unsigned, @bitSizeOf(c_int) - 12) = 0,
|
||||
_: std.meta.Int(.unsigned, @bitSizeOf(c_int) - 11) = 0,
|
||||
},
|
||||
else => void,
|
||||
};
|
||||
@ -10261,9 +10284,20 @@ pub const fstatat = switch (native_os) {
|
||||
},
|
||||
else => private.fstatat,
|
||||
};
|
||||
|
||||
pub extern "c" fn getpwent() ?*passwd;
|
||||
pub extern "c" fn endpwent() void;
|
||||
pub extern "c" fn setpwent() void;
|
||||
pub extern "c" fn getpwnam(name: [*:0]const u8) ?*passwd;
|
||||
pub extern "c" fn getpwnam_r(name: [*:0]const u8, pwd: *passwd, buf: [*]u8, buflen: usize, result: *?*passwd) c_int;
|
||||
pub extern "c" fn getpwuid(uid: uid_t) ?*passwd;
|
||||
pub extern "c" fn getpwuid_r(uid: uid_t, pwd: *passwd, buf: [*]u8, buflen: usize, result: *?*passwd) c_int;
|
||||
pub extern "c" fn getgrent() ?*group;
|
||||
pub extern "c" fn setgrent() void;
|
||||
pub extern "c" fn endgrent() void;
|
||||
pub extern "c" fn getgrnam(name: [*:0]const u8) ?*passwd;
|
||||
pub extern "c" fn getgrnam_r(name: [*:0]const u8, grp: *group, buf: [*]u8, buflen: usize, result: *?*group) c_int;
|
||||
pub extern "c" fn getgrgid(gid: gid_t) ?*group;
|
||||
pub extern "c" fn getgrgid_r(gid: gid_t, grp: *group, buf: [*]u8, buflen: usize, result: *?*group) c_int;
|
||||
pub extern "c" fn getrlimit64(resource: rlimit_resource, rlim: *rlimit) c_int;
|
||||
pub extern "c" fn lseek64(fd: fd_t, offset: i64, whence: c_int) i64;
|
||||
pub extern "c" fn mmap64(addr: ?*align(page_size) anyopaque, len: usize, prot: c_uint, flags: c_uint, fd: fd_t, offset: i64) *anyopaque;
|
||||
@ -10992,11 +11026,7 @@ pub const bcrypt = openbsd.bcrypt;
|
||||
pub const bcrypt_checkpass = openbsd.bcrypt_checkpass;
|
||||
pub const bcrypt_gensalt = openbsd.bcrypt_gensalt;
|
||||
pub const bcrypt_newhash = openbsd.bcrypt_newhash;
|
||||
pub const endpwent = openbsd.endpwent;
|
||||
pub const getpwent = openbsd.getpwent;
|
||||
pub const getpwnam_r = openbsd.getpwnam_r;
|
||||
pub const getpwnam_shadow = openbsd.getpwnam_shadow;
|
||||
pub const getpwuid_r = openbsd.getpwuid_r;
|
||||
pub const getpwuid_shadow = openbsd.getpwuid_shadow;
|
||||
pub const getthrid = openbsd.getthrid;
|
||||
pub const login_cap_t = openbsd.login_cap_t;
|
||||
@ -11013,7 +11043,6 @@ pub const pthread_spinlock_t = openbsd.pthread_spinlock_t;
|
||||
pub const pw_dup = openbsd.pw_dup;
|
||||
pub const setclasscontext = openbsd.setclasscontext;
|
||||
pub const setpassent = openbsd.setpassent;
|
||||
pub const setpwent = openbsd.setpwent;
|
||||
pub const setusercontext = openbsd.setusercontext;
|
||||
pub const uid_from_user = openbsd.uid_from_user;
|
||||
pub const unveil = openbsd.unveil;
|
||||
|
||||
@ -81,11 +81,6 @@ pub extern "c" fn auth_checknologin(lc: *login_cap_t) void;
|
||||
|
||||
pub extern "c" fn getpwuid_shadow(uid: uid_t) ?*passwd;
|
||||
pub extern "c" fn getpwnam_shadow(name: [*:0]const u8) ?*passwd;
|
||||
pub extern "c" fn getpwnam_r(name: [*:0]const u8, pw: *passwd, buf: [*]u8, buflen: usize, pwretp: *?*passwd) c_int;
|
||||
pub extern "c" fn getpwuid_r(uid: uid_t, pw: *passwd, buf: [*]u8, buflen: usize, pwretp: *?*passwd) c_int;
|
||||
pub extern "c" fn getpwent() ?*passwd;
|
||||
pub extern "c" fn setpwent() void;
|
||||
pub extern "c" fn endpwent() void;
|
||||
pub extern "c" fn setpassent(stayopen: c_int) c_int;
|
||||
pub extern "c" fn uid_from_user(name: [*:0]const u8, uid: *uid_t) c_int;
|
||||
pub extern "c" fn user_from_uid(uid: uid_t, noname: c_int) ?[*:0]const u8;
|
||||
|
||||
@ -1,7 +1,23 @@
|
||||
const builtin = @import("builtin");
|
||||
const std = @import("../std.zig");
|
||||
const testing = std.testing;
|
||||
const Writer = std.io.Writer;
|
||||
|
||||
/// When decompressing, the output buffer is used as the history window, so
|
||||
/// less than this may result in failure to decompress streams that were
|
||||
/// compressed with a larger window.
|
||||
pub const max_window_len = history_len * 2;
|
||||
|
||||
pub const history_len = 32768;
|
||||
|
||||
/// Deflate is a lossless data compression file format that uses a combination
|
||||
/// of LZ77 and Huffman coding.
|
||||
pub const Compress = @import("flate/Compress.zig");
|
||||
|
||||
/// Inflate is the decoding process that consumes a Deflate bitstream and
|
||||
/// produces the original full-size data.
|
||||
pub const Decompress = @import("flate/Decompress.zig");
|
||||
|
||||
/// Compression without Lempel-Ziv match searching. Faster compression, less
|
||||
/// memory requirements but bigger compressed sizes.
|
||||
pub const HuffmanEncoder = @import("flate/HuffmanEncoder.zig");
|
||||
|
||||
/// Container of the deflate bit stream body. Container adds header before
|
||||
/// deflate bit stream and footer after. It can bi gzip, zlib or raw (no header,
|
||||
@ -13,7 +29,6 @@ const Writer = std.io.Writer;
|
||||
/// Gzip format is defined in rfc 1952. Header has 10+ bytes and footer 4 bytes
|
||||
/// crc32 checksum and 4 bytes of uncompressed data length.
|
||||
///
|
||||
///
|
||||
/// rfc 1950: https://datatracker.ietf.org/doc/html/rfc1950#page-4
|
||||
/// rfc 1952: https://datatracker.ietf.org/doc/html/rfc1952#page-5
|
||||
pub const Container = enum {
|
||||
@ -77,14 +92,14 @@ pub const Container = enum {
|
||||
raw: void,
|
||||
gzip: struct {
|
||||
crc: std.hash.Crc32 = .init(),
|
||||
count: usize = 0,
|
||||
count: u32 = 0,
|
||||
},
|
||||
zlib: std.hash.Adler32,
|
||||
|
||||
pub fn init(containter: Container) Hasher {
|
||||
return switch (containter) {
|
||||
.gzip => .{ .gzip = .{} },
|
||||
.zlib => .{ .zlib = .init() },
|
||||
.zlib => .{ .zlib = .{} },
|
||||
.raw => .raw,
|
||||
};
|
||||
}
|
||||
@ -98,7 +113,7 @@ pub const Container = enum {
|
||||
.raw => {},
|
||||
.gzip => |*gzip| {
|
||||
gzip.update(buf);
|
||||
gzip.count += buf.len;
|
||||
gzip.count +%= buf.len;
|
||||
},
|
||||
.zlib => |*zlib| {
|
||||
zlib.update(buf);
|
||||
@ -107,7 +122,7 @@ pub const Container = enum {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn writeFooter(hasher: *Hasher, writer: *Writer) Writer.Error!void {
|
||||
pub fn writeFooter(hasher: *Hasher, writer: *std.Io.Writer) std.Io.Writer.Error!void {
|
||||
var bits: [4]u8 = undefined;
|
||||
switch (hasher.*) {
|
||||
.gzip => |*gzip| {
|
||||
@ -133,502 +148,33 @@ pub const Container = enum {
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/// When decompressing, the output buffer is used as the history window, so
|
||||
/// less than this may result in failure to decompress streams that were
|
||||
/// compressed with a larger window.
|
||||
pub const max_window_len = 1 << 16;
|
||||
pub const Metadata = union(Container) {
|
||||
raw: void,
|
||||
gzip: struct {
|
||||
crc: u32 = 0,
|
||||
count: u32 = 0,
|
||||
},
|
||||
zlib: struct {
|
||||
adler: u32 = 0,
|
||||
},
|
||||
|
||||
/// Deflate is a lossless data compression file format that uses a combination
|
||||
/// of LZ77 and Huffman coding.
|
||||
pub const Compress = @import("flate/Compress.zig");
|
||||
pub fn init(containter: Container) Metadata {
|
||||
return switch (containter) {
|
||||
.gzip => .{ .gzip = .{} },
|
||||
.zlib => .{ .zlib = .{} },
|
||||
.raw => .raw,
|
||||
};
|
||||
}
|
||||
|
||||
/// Inflate is the decoding process that takes a Deflate bitstream for
|
||||
/// decompression and correctly produces the original full-size data or file.
|
||||
pub const Decompress = @import("flate/Decompress.zig");
|
||||
|
||||
/// Huffman only compression. Without Lempel-Ziv match searching. Faster
|
||||
/// compression, less memory requirements but bigger compressed sizes.
|
||||
pub const huffman = struct {
|
||||
// The odd order in which the codegen code sizes are written.
|
||||
pub const codegen_order = [_]u32{ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
|
||||
// The number of codegen codes.
|
||||
pub const codegen_code_count = 19;
|
||||
|
||||
// The largest distance code.
|
||||
pub const distance_code_count = 30;
|
||||
|
||||
// Maximum number of literals.
|
||||
pub const max_num_lit = 286;
|
||||
|
||||
// Max number of frequencies used for a Huffman Code
|
||||
// Possible lengths are codegen_code_count (19), distance_code_count (30) and max_num_lit (286).
|
||||
// The largest of these is max_num_lit.
|
||||
pub const max_num_frequencies = max_num_lit;
|
||||
|
||||
// Biggest block size for uncompressed block.
|
||||
pub const max_store_block_size = 65535;
|
||||
// The special code used to mark the end of a block.
|
||||
pub const end_block_marker = 256;
|
||||
pub fn container(m: Metadata) Container {
|
||||
return m;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
test {
|
||||
_ = HuffmanEncoder;
|
||||
_ = Compress;
|
||||
_ = Decompress;
|
||||
}
|
||||
|
||||
test "compress/decompress" {
|
||||
const print = std.debug.print;
|
||||
var cmp_buf: [64 * 1024]u8 = undefined; // compressed data buffer
|
||||
var dcm_buf: [64 * 1024]u8 = undefined; // decompressed data buffer
|
||||
|
||||
const levels = [_]Compress.Level{ .level_4, .level_5, .level_6, .level_7, .level_8, .level_9 };
|
||||
const cases = [_]struct {
|
||||
data: []const u8, // uncompressed content
|
||||
// compressed data sizes per level 4-9
|
||||
gzip_sizes: [levels.len]usize = [_]usize{0} ** levels.len,
|
||||
huffman_only_size: usize = 0,
|
||||
store_size: usize = 0,
|
||||
}{
|
||||
.{
|
||||
.data = @embedFile("flate/testdata/rfc1951.txt"),
|
||||
.gzip_sizes = [_]usize{ 11513, 11217, 11139, 11126, 11122, 11119 },
|
||||
.huffman_only_size = 20287,
|
||||
.store_size = 36967,
|
||||
},
|
||||
.{
|
||||
.data = @embedFile("flate/testdata/fuzz/roundtrip1.input"),
|
||||
.gzip_sizes = [_]usize{ 373, 370, 370, 370, 370, 370 },
|
||||
.huffman_only_size = 393,
|
||||
.store_size = 393,
|
||||
},
|
||||
.{
|
||||
.data = @embedFile("flate/testdata/fuzz/roundtrip2.input"),
|
||||
.gzip_sizes = [_]usize{ 373, 373, 373, 373, 373, 373 },
|
||||
.huffman_only_size = 394,
|
||||
.store_size = 394,
|
||||
},
|
||||
.{
|
||||
.data = @embedFile("flate/testdata/fuzz/deflate-stream.expect"),
|
||||
.gzip_sizes = [_]usize{ 351, 347, 347, 347, 347, 347 },
|
||||
.huffman_only_size = 498,
|
||||
.store_size = 747,
|
||||
},
|
||||
};
|
||||
|
||||
for (cases, 0..) |case, case_no| { // for each case
|
||||
const data = case.data;
|
||||
|
||||
for (levels, 0..) |level, i| { // for each compression level
|
||||
|
||||
inline for (Container.list) |container| { // for each wrapping
|
||||
var compressed_size: usize = if (case.gzip_sizes[i] > 0)
|
||||
case.gzip_sizes[i] - Container.gzip.size() + container.size()
|
||||
else
|
||||
0;
|
||||
|
||||
// compress original stream to compressed stream
|
||||
{
|
||||
var original: std.io.Reader = .fixed(data);
|
||||
var compressed: Writer = .fixed(&cmp_buf);
|
||||
var compress: Compress = .init(&original, &.{}, .{ .container = .raw, .level = level });
|
||||
const n = try compress.reader.streamRemaining(&compressed);
|
||||
if (compressed_size == 0) {
|
||||
if (container == .gzip)
|
||||
print("case {d} gzip level {} compressed size: {d}\n", .{ case_no, level, compressed.pos });
|
||||
compressed_size = compressed.end;
|
||||
}
|
||||
try testing.expectEqual(compressed_size, n);
|
||||
try testing.expectEqual(compressed_size, compressed.end);
|
||||
}
|
||||
// decompress compressed stream to decompressed stream
|
||||
{
|
||||
var compressed: std.io.Reader = .fixed(cmp_buf[0..compressed_size]);
|
||||
var decompressed: Writer = .fixed(&dcm_buf);
|
||||
try Decompress.pump(container, &compressed, &decompressed);
|
||||
try testing.expectEqualSlices(u8, data, decompressed.getWritten());
|
||||
}
|
||||
|
||||
// compressor writer interface
|
||||
{
|
||||
var compressed: Writer = .fixed(&cmp_buf);
|
||||
var cmp = try Compress.init(container, &compressed, .{ .level = level });
|
||||
var cmp_wrt = cmp.writer();
|
||||
try cmp_wrt.writeAll(data);
|
||||
try cmp.finish();
|
||||
|
||||
try testing.expectEqual(compressed_size, compressed.pos);
|
||||
}
|
||||
// decompressor reader interface
|
||||
{
|
||||
var compressed: std.io.Reader = .fixed(cmp_buf[0..compressed_size]);
|
||||
var dcm = Decompress.pump(container, &compressed);
|
||||
var dcm_rdr = dcm.reader();
|
||||
const n = try dcm_rdr.readAll(&dcm_buf);
|
||||
try testing.expectEqual(data.len, n);
|
||||
try testing.expectEqualSlices(u8, data, dcm_buf[0..n]);
|
||||
}
|
||||
}
|
||||
}
|
||||
// huffman only compression
|
||||
{
|
||||
inline for (Container.list) |container| { // for each wrapping
|
||||
var compressed_size: usize = if (case.huffman_only_size > 0)
|
||||
case.huffman_only_size - Container.gzip.size() + container.size()
|
||||
else
|
||||
0;
|
||||
|
||||
// compress original stream to compressed stream
|
||||
{
|
||||
var original: std.io.Reader = .fixed(data);
|
||||
var compressed: Writer = .fixed(&cmp_buf);
|
||||
var cmp = try Compress.Huffman.init(container, &compressed);
|
||||
try cmp.compress(original.reader());
|
||||
try cmp.finish();
|
||||
if (compressed_size == 0) {
|
||||
if (container == .gzip)
|
||||
print("case {d} huffman only compressed size: {d}\n", .{ case_no, compressed.pos });
|
||||
compressed_size = compressed.pos;
|
||||
}
|
||||
try testing.expectEqual(compressed_size, compressed.pos);
|
||||
}
|
||||
// decompress compressed stream to decompressed stream
|
||||
{
|
||||
var compressed: std.io.Reader = .fixed(cmp_buf[0..compressed_size]);
|
||||
var decompressed: Writer = .fixed(&dcm_buf);
|
||||
try Decompress.pump(container, &compressed, &decompressed);
|
||||
try testing.expectEqualSlices(u8, data, decompressed.getWritten());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// store only
|
||||
{
|
||||
inline for (Container.list) |container| { // for each wrapping
|
||||
var compressed_size: usize = if (case.store_size > 0)
|
||||
case.store_size - Container.gzip.size() + container.size()
|
||||
else
|
||||
0;
|
||||
|
||||
// compress original stream to compressed stream
|
||||
{
|
||||
var original: std.io.Reader = .fixed(data);
|
||||
var compressed: Writer = .fixed(&cmp_buf);
|
||||
var cmp = try Compress.SimpleCompressor(.store, container).init(&compressed);
|
||||
try cmp.compress(original.reader());
|
||||
try cmp.finish();
|
||||
if (compressed_size == 0) {
|
||||
if (container == .gzip)
|
||||
print("case {d} store only compressed size: {d}\n", .{ case_no, compressed.pos });
|
||||
compressed_size = compressed.pos;
|
||||
}
|
||||
|
||||
try testing.expectEqual(compressed_size, compressed.pos);
|
||||
}
|
||||
// decompress compressed stream to decompressed stream
|
||||
{
|
||||
var compressed: std.io.Reader = .fixed(cmp_buf[0..compressed_size]);
|
||||
var decompressed: Writer = .fixed(&dcm_buf);
|
||||
try Decompress.pump(container, &compressed, &decompressed);
|
||||
try testing.expectEqualSlices(u8, data, decompressed.getWritten());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn testDecompress(comptime container: Container, compressed: []const u8, expected_plain: []const u8) !void {
|
||||
var in: std.io.Reader = .fixed(compressed);
|
||||
var out: std.io.Writer.Allocating = .init(testing.allocator);
|
||||
defer out.deinit();
|
||||
|
||||
try Decompress.pump(container, &in, &out.interface);
|
||||
try testing.expectEqualSlices(u8, expected_plain, out.items);
|
||||
}
|
||||
|
||||
test "don't read past deflate stream's end" {
|
||||
try testDecompress(.zlib, &[_]u8{
|
||||
0x08, 0xd7, 0x63, 0xf8, 0xcf, 0xc0, 0xc0, 0x00, 0xc1, 0xff,
|
||||
0xff, 0x43, 0x30, 0x03, 0x03, 0xc3, 0xff, 0xff, 0xff, 0x01,
|
||||
0x83, 0x95, 0x0b, 0xf5,
|
||||
}, &[_]u8{
|
||||
0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff,
|
||||
0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00,
|
||||
0x00, 0x00, 0xff, 0xff, 0xff,
|
||||
});
|
||||
}
|
||||
|
||||
test "zlib header" {
|
||||
// Truncated header
|
||||
try testing.expectError(
|
||||
error.EndOfStream,
|
||||
testDecompress(.zlib, &[_]u8{0x78}, ""),
|
||||
);
|
||||
// Wrong CM
|
||||
try testing.expectError(
|
||||
error.BadZlibHeader,
|
||||
testDecompress(.zlib, &[_]u8{ 0x79, 0x94 }, ""),
|
||||
);
|
||||
// Wrong CINFO
|
||||
try testing.expectError(
|
||||
error.BadZlibHeader,
|
||||
testDecompress(.zlib, &[_]u8{ 0x88, 0x98 }, ""),
|
||||
);
|
||||
// Wrong checksum
|
||||
try testing.expectError(
|
||||
error.WrongZlibChecksum,
|
||||
testDecompress(.zlib, &[_]u8{ 0x78, 0xda, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 }, ""),
|
||||
);
|
||||
// Truncated checksum
|
||||
try testing.expectError(
|
||||
error.EndOfStream,
|
||||
testDecompress(.zlib, &[_]u8{ 0x78, 0xda, 0x03, 0x00, 0x00 }, ""),
|
||||
);
|
||||
}
|
||||
|
||||
test "gzip header" {
|
||||
// Truncated header
|
||||
try testing.expectError(
|
||||
error.EndOfStream,
|
||||
testDecompress(.gzip, &[_]u8{ 0x1f, 0x8B }, undefined),
|
||||
);
|
||||
// Wrong CM
|
||||
try testing.expectError(
|
||||
error.BadGzipHeader,
|
||||
testDecompress(.gzip, &[_]u8{
|
||||
0x1f, 0x8b, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x03,
|
||||
}, undefined),
|
||||
);
|
||||
|
||||
// Wrong checksum
|
||||
try testing.expectError(
|
||||
error.WrongGzipChecksum,
|
||||
testDecompress(.gzip, &[_]u8{
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
}, undefined),
|
||||
);
|
||||
// Truncated checksum
|
||||
try testing.expectError(
|
||||
error.EndOfStream,
|
||||
testDecompress(.gzip, &[_]u8{
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00,
|
||||
}, undefined),
|
||||
);
|
||||
// Wrong initial size
|
||||
try testing.expectError(
|
||||
error.WrongGzipSize,
|
||||
testDecompress(.gzip, &[_]u8{
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01,
|
||||
}, undefined),
|
||||
);
|
||||
// Truncated initial size field
|
||||
try testing.expectError(
|
||||
error.EndOfStream,
|
||||
testDecompress(.gzip, &[_]u8{
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00,
|
||||
}, undefined),
|
||||
);
|
||||
|
||||
try testDecompress(.gzip, &[_]u8{
|
||||
// GZIP header
|
||||
0x1f, 0x8b, 0x08, 0x12, 0x00, 0x09, 0x6e, 0x88, 0x00, 0xff, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x00,
|
||||
// header.FHCRC (should cover entire header)
|
||||
0x99, 0xd6,
|
||||
// GZIP data
|
||||
0x01, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
}, "");
|
||||
}
|
||||
|
||||
test "public interface" {
|
||||
const plain_data_buf = [_]u8{ 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', 0x0a };
|
||||
|
||||
// deflate final stored block, header + plain (stored) data
|
||||
const deflate_block = [_]u8{
|
||||
0b0000_0001, 0b0000_1100, 0x00, 0b1111_0011, 0xff, // deflate fixed buffer header len, nlen
|
||||
} ++ plain_data_buf;
|
||||
|
||||
const plain_data: []const u8 = &plain_data_buf;
|
||||
const gzip_data: []const u8 = &deflate_block;
|
||||
|
||||
//// gzip header/footer + deflate block
|
||||
//const gzip_data =
|
||||
// [_]u8{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03 } ++ // gzip header (10 bytes)
|
||||
// deflate_block ++
|
||||
// [_]u8{ 0xd5, 0xe0, 0x39, 0xb7, 0x0c, 0x00, 0x00, 0x00 }; // gzip footer checksum (4 byte), size (4 bytes)
|
||||
|
||||
//// zlib header/footer + deflate block
|
||||
//const zlib_data = [_]u8{ 0x78, 0b10_0_11100 } ++ // zlib header (2 bytes)}
|
||||
// deflate_block ++
|
||||
// [_]u8{ 0x1c, 0xf2, 0x04, 0x47 }; // zlib footer: checksum
|
||||
|
||||
// TODO
|
||||
//const gzip = @import("gzip.zig");
|
||||
//const zlib = @import("zlib.zig");
|
||||
|
||||
var buffer1: [64]u8 = undefined;
|
||||
var buffer2: [64]u8 = undefined;
|
||||
|
||||
// TODO These used to be functions, need to migrate the tests
|
||||
const decompress = void;
|
||||
const compress = void;
|
||||
const store = void;
|
||||
|
||||
// decompress
|
||||
{
|
||||
var plain: Writer = .fixed(&buffer2);
|
||||
|
||||
var in: std.io.Reader = .fixed(gzip_data);
|
||||
try decompress(&in, &plain);
|
||||
try testing.expectEqualSlices(u8, plain_data, plain.getWritten());
|
||||
}
|
||||
|
||||
// compress/decompress
|
||||
{
|
||||
var plain: Writer = .fixed(&buffer2);
|
||||
var compressed: Writer = .fixed(&buffer1);
|
||||
|
||||
var in: std.io.Reader = .fixed(plain_data);
|
||||
try compress(&in, &compressed, .{});
|
||||
|
||||
var compressed_br: std.io.Reader = .fixed(&buffer1);
|
||||
try decompress(&compressed_br, &plain);
|
||||
try testing.expectEqualSlices(u8, plain_data, plain.getWritten());
|
||||
}
|
||||
|
||||
// compressor/decompressor
|
||||
{
|
||||
var plain: Writer = .fixed(&buffer2);
|
||||
var compressed: Writer = .fixed(&buffer1);
|
||||
|
||||
var in: std.io.Reader = .fixed(plain_data);
|
||||
var cmp = try Compress(&compressed, .{});
|
||||
try cmp.compress(&in);
|
||||
try cmp.finish();
|
||||
|
||||
var compressed_br: std.io.Reader = .fixed(&buffer1);
|
||||
var dcp = Decompress(&compressed_br);
|
||||
try dcp.decompress(&plain);
|
||||
try testing.expectEqualSlices(u8, plain_data, plain.getWritten());
|
||||
}
|
||||
|
||||
// huffman
|
||||
{
|
||||
// huffman compress/decompress
|
||||
{
|
||||
var plain: Writer = .fixed(&buffer2);
|
||||
var compressed: Writer = .fixed(&buffer1);
|
||||
|
||||
var in: std.io.Reader = .fixed(plain_data);
|
||||
try huffman.compress(&in, &compressed);
|
||||
|
||||
var compressed_br: std.io.Reader = .fixed(&buffer1);
|
||||
try decompress(&compressed_br, &plain);
|
||||
try testing.expectEqualSlices(u8, plain_data, plain.getWritten());
|
||||
}
|
||||
|
||||
// huffman compressor/decompressor
|
||||
{
|
||||
var plain: Writer = .fixed(&buffer2);
|
||||
var compressed: Writer = .fixed(&buffer1);
|
||||
|
||||
var in: std.io.Reader = .fixed(plain_data);
|
||||
var cmp = try huffman.Compressor(&compressed);
|
||||
try cmp.compress(&in);
|
||||
try cmp.finish();
|
||||
|
||||
var compressed_br: std.io.Reader = .fixed(&buffer1);
|
||||
try decompress(&compressed_br, &plain);
|
||||
try testing.expectEqualSlices(u8, plain_data, plain.getWritten());
|
||||
}
|
||||
}
|
||||
|
||||
// store
|
||||
{
|
||||
// store compress/decompress
|
||||
{
|
||||
var plain: Writer = .fixed(&buffer2);
|
||||
var compressed: Writer = .fixed(&buffer1);
|
||||
|
||||
var in: std.io.Reader = .fixed(plain_data);
|
||||
try store.compress(&in, &compressed);
|
||||
|
||||
var compressed_br: std.io.Reader = .fixed(&buffer1);
|
||||
try decompress(&compressed_br, &plain);
|
||||
try testing.expectEqualSlices(u8, plain_data, plain.getWritten());
|
||||
}
|
||||
|
||||
// store compressor/decompressor
|
||||
{
|
||||
var plain: Writer = .fixed(&buffer2);
|
||||
var compressed: Writer = .fixed(&buffer1);
|
||||
|
||||
var in: std.io.Reader = .fixed(plain_data);
|
||||
var cmp = try store.compressor(&compressed);
|
||||
try cmp.compress(&in);
|
||||
try cmp.finish();
|
||||
|
||||
var compressed_br: std.io.Reader = .fixed(&buffer1);
|
||||
try decompress(&compressed_br, &plain);
|
||||
try testing.expectEqualSlices(u8, plain_data, plain.getWritten());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const match = struct {
|
||||
pub const base_length = 3; // smallest match length per the RFC section 3.2.5
|
||||
pub const min_length = 4; // min length used in this algorithm
|
||||
pub const max_length = 258;
|
||||
|
||||
pub const min_distance = 1;
|
||||
pub const max_distance = 32768;
|
||||
};
|
||||
|
||||
pub const history_len = match.max_distance;
|
||||
|
||||
pub const lookup = struct {
|
||||
pub const bits = 15;
|
||||
pub const len = 1 << bits;
|
||||
pub const shift = 32 - bits;
|
||||
};
|
||||
|
||||
test "zlib should not overshoot" {
|
||||
// Compressed zlib data with extra 4 bytes at the end.
|
||||
const data = [_]u8{
|
||||
0x78, 0x9c, 0x73, 0xce, 0x2f, 0xa8, 0x2c, 0xca, 0x4c, 0xcf, 0x28, 0x51, 0x08, 0xcf, 0xcc, 0xc9,
|
||||
0x49, 0xcd, 0x55, 0x28, 0x4b, 0xcc, 0x53, 0x08, 0x4e, 0xce, 0x48, 0xcc, 0xcc, 0xd6, 0x51, 0x08,
|
||||
0xce, 0xcc, 0x4b, 0x4f, 0x2c, 0xc8, 0x2f, 0x4a, 0x55, 0x30, 0xb4, 0xb4, 0x34, 0xd5, 0xb5, 0x34,
|
||||
0x03, 0x00, 0x8b, 0x61, 0x0f, 0xa4, 0x52, 0x5a, 0x94, 0x12,
|
||||
};
|
||||
|
||||
var stream: std.io.Reader = .fixed(&data);
|
||||
const reader = stream.reader();
|
||||
|
||||
var dcp = Decompress.init(reader);
|
||||
var out: [128]u8 = undefined;
|
||||
|
||||
// Decompress
|
||||
var n = try dcp.reader().readAll(out[0..]);
|
||||
|
||||
// Expected decompressed data
|
||||
try std.testing.expectEqual(46, n);
|
||||
try std.testing.expectEqualStrings("Copyright Willem van Schaik, Singapore 1995-96", out[0..n]);
|
||||
|
||||
// Decompressor don't overshoot underlying reader.
|
||||
// It is leaving it at the end of compressed data chunk.
|
||||
try std.testing.expectEqual(data.len - 4, stream.getPos());
|
||||
try std.testing.expectEqual(0, dcp.unreadBytes());
|
||||
|
||||
// 4 bytes after compressed chunk are available in reader.
|
||||
n = try reader.readAll(out[0..]);
|
||||
try std.testing.expectEqual(n, 4);
|
||||
try std.testing.expectEqualSlices(u8, data[data.len - 4 .. data.len], out[0..n]);
|
||||
}
|
||||
|
||||
@ -8,34 +8,54 @@ const Writer = std.io.Writer;
|
||||
const BlockWriter = @This();
|
||||
const flate = @import("../flate.zig");
|
||||
const Compress = flate.Compress;
|
||||
const huffman = flate.huffman;
|
||||
const HuffmanEncoder = flate.HuffmanEncoder;
|
||||
const Token = @import("Token.zig");
|
||||
|
||||
const codegen_order = huffman.codegen_order;
|
||||
const codegen_order = HuffmanEncoder.codegen_order;
|
||||
const end_code_mark = 255;
|
||||
|
||||
output: *Writer,
|
||||
|
||||
codegen_freq: [huffman.codegen_code_count]u16 = undefined,
|
||||
literal_freq: [huffman.max_num_lit]u16 = undefined,
|
||||
distance_freq: [huffman.distance_code_count]u16 = undefined,
|
||||
codegen: [huffman.max_num_lit + huffman.distance_code_count + 1]u8 = undefined,
|
||||
literal_encoding: Compress.LiteralEncoder = .{},
|
||||
distance_encoding: Compress.DistanceEncoder = .{},
|
||||
codegen_encoding: Compress.CodegenEncoder = .{},
|
||||
fixed_literal_encoding: Compress.LiteralEncoder,
|
||||
fixed_distance_encoding: Compress.DistanceEncoder,
|
||||
huff_distance: Compress.DistanceEncoder,
|
||||
codegen_freq: [HuffmanEncoder.codegen_code_count]u16,
|
||||
literal_freq: [HuffmanEncoder.max_num_lit]u16,
|
||||
distance_freq: [HuffmanEncoder.distance_code_count]u16,
|
||||
codegen: [HuffmanEncoder.max_num_lit + HuffmanEncoder.distance_code_count + 1]u8,
|
||||
literal_encoding: HuffmanEncoder,
|
||||
distance_encoding: HuffmanEncoder,
|
||||
codegen_encoding: HuffmanEncoder,
|
||||
fixed_literal_encoding: HuffmanEncoder,
|
||||
fixed_distance_encoding: HuffmanEncoder,
|
||||
huff_distance: HuffmanEncoder,
|
||||
|
||||
fixed_literal_codes: [HuffmanEncoder.max_num_frequencies]HuffmanEncoder.Code,
|
||||
fixed_distance_codes: [HuffmanEncoder.distance_code_count]HuffmanEncoder.Code,
|
||||
distance_codes: [HuffmanEncoder.distance_code_count]HuffmanEncoder.Code,
|
||||
|
||||
pub fn init(output: *Writer) BlockWriter {
|
||||
return .{
|
||||
.output = output,
|
||||
.fixed_literal_encoding = Compress.fixedLiteralEncoder(),
|
||||
.fixed_distance_encoding = Compress.fixedDistanceEncoder(),
|
||||
.huff_distance = Compress.huffmanDistanceEncoder(),
|
||||
.codegen_freq = undefined,
|
||||
.literal_freq = undefined,
|
||||
.distance_freq = undefined,
|
||||
.codegen = undefined,
|
||||
.literal_encoding = undefined,
|
||||
.distance_encoding = undefined,
|
||||
.codegen_encoding = undefined,
|
||||
.fixed_literal_encoding = undefined,
|
||||
.fixed_distance_encoding = undefined,
|
||||
.huff_distance = undefined,
|
||||
.fixed_literal_codes = undefined,
|
||||
.fixed_distance_codes = undefined,
|
||||
.distance_codes = undefined,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn initBuffers(bw: *BlockWriter) void {
|
||||
bw.fixed_literal_encoding = .fixedLiteralEncoder(&bw.fixed_literal_codes);
|
||||
bw.fixed_distance_encoding = .fixedDistanceEncoder(&bw.fixed_distance_codes);
|
||||
bw.huff_distance = .huffmanDistanceEncoder(&bw.distance_codes);
|
||||
}
|
||||
|
||||
/// Flush intrenal bit buffer to the writer.
|
||||
/// Should be called only when bit stream is at byte boundary.
|
||||
///
|
||||
@ -46,27 +66,23 @@ pub fn flush(self: *BlockWriter) Writer.Error!void {
|
||||
try self.bit_writer.flush();
|
||||
}
|
||||
|
||||
pub fn setWriter(self: *BlockWriter, new_writer: *Writer) void {
|
||||
self.bit_writer.setWriter(new_writer);
|
||||
}
|
||||
|
||||
fn writeCode(self: *BlockWriter, c: Compress.HuffCode) Writer.Error!void {
|
||||
try self.bit_writer.writeBits(c.code, c.len);
|
||||
}
|
||||
|
||||
// RFC 1951 3.2.7 specifies a special run-length encoding for specifying
|
||||
// the literal and distance lengths arrays (which are concatenated into a single
|
||||
// array). This method generates that run-length encoding.
|
||||
//
|
||||
// The result is written into the codegen array, and the frequencies
|
||||
// of each code is written into the codegen_freq array.
|
||||
// Codes 0-15 are single byte codes. Codes 16-18 are followed by additional
|
||||
// information. Code bad_code is an end marker
|
||||
//
|
||||
// num_literals: The number of literals in literal_encoding
|
||||
// num_distances: The number of distances in distance_encoding
|
||||
// lit_enc: The literal encoder to use
|
||||
// dist_enc: The distance encoder to use
|
||||
/// RFC 1951 3.2.7 specifies a special run-length encoding for specifying
|
||||
/// the literal and distance lengths arrays (which are concatenated into a single
|
||||
/// array). This method generates that run-length encoding.
|
||||
///
|
||||
/// The result is written into the codegen array, and the frequencies
|
||||
/// of each code is written into the codegen_freq array.
|
||||
/// Codes 0-15 are single byte codes. Codes 16-18 are followed by additional
|
||||
/// information. Code bad_code is an end marker
|
||||
///
|
||||
/// num_literals: The number of literals in literal_encoding
|
||||
/// num_distances: The number of distances in distance_encoding
|
||||
/// lit_enc: The literal encoder to use
|
||||
/// dist_enc: The distance encoder to use
|
||||
fn generateCodegen(
|
||||
self: *BlockWriter,
|
||||
num_literals: u32,
|
||||
@ -167,7 +183,7 @@ const DynamicSize = struct {
|
||||
num_codegens: u32,
|
||||
};
|
||||
|
||||
// dynamicSize returns the size of dynamically encoded data in bits.
|
||||
/// dynamicSize returns the size of dynamically encoded data in bits.
|
||||
fn dynamicSize(
|
||||
self: *BlockWriter,
|
||||
lit_enc: *Compress.LiteralEncoder, // literal encoder
|
||||
@ -194,7 +210,7 @@ fn dynamicSize(
|
||||
};
|
||||
}
|
||||
|
||||
// fixedSize returns the size of dynamically encoded data in bits.
|
||||
/// fixedSize returns the size of dynamically encoded data in bits.
|
||||
fn fixedSize(self: *BlockWriter, extra_bits: u32) u32 {
|
||||
return 3 +
|
||||
self.fixed_literal_encoding.bitLength(&self.literal_freq) +
|
||||
@ -207,25 +223,25 @@ const StoredSize = struct {
|
||||
storable: bool,
|
||||
};
|
||||
|
||||
// storedSizeFits calculates the stored size, including header.
|
||||
// The function returns the size in bits and whether the block
|
||||
// fits inside a single block.
|
||||
/// storedSizeFits calculates the stored size, including header.
|
||||
/// The function returns the size in bits and whether the block
|
||||
/// fits inside a single block.
|
||||
fn storedSizeFits(in: ?[]const u8) StoredSize {
|
||||
if (in == null) {
|
||||
return .{ .size = 0, .storable = false };
|
||||
}
|
||||
if (in.?.len <= huffman.max_store_block_size) {
|
||||
if (in.?.len <= HuffmanEncoder.max_store_block_size) {
|
||||
return .{ .size = @as(u32, @intCast((in.?.len + 5) * 8)), .storable = true };
|
||||
}
|
||||
return .{ .size = 0, .storable = false };
|
||||
}
|
||||
|
||||
// Write the header of a dynamic Huffman block to the output stream.
|
||||
//
|
||||
// num_literals: The number of literals specified in codegen
|
||||
// num_distances: The number of distances specified in codegen
|
||||
// num_codegens: The number of codegens used in codegen
|
||||
// eof: Is it the end-of-file? (end of stream)
|
||||
/// Write the header of a dynamic Huffman block to the output stream.
|
||||
///
|
||||
/// num_literals: The number of literals specified in codegen
|
||||
/// num_distances: The number of distances specified in codegen
|
||||
/// num_codegens: The number of codegens used in codegen
|
||||
/// eof: Is it the end-of-file? (end of stream)
|
||||
fn dynamicHeader(
|
||||
self: *BlockWriter,
|
||||
num_literals: u32,
|
||||
@ -291,11 +307,11 @@ fn fixedHeader(self: *BlockWriter, eof: bool) Writer.Error!void {
|
||||
try self.bit_writer.writeBits(value, 3);
|
||||
}
|
||||
|
||||
// Write a block of tokens with the smallest encoding. Will choose block type.
|
||||
// The original input can be supplied, and if the huffman encoded data
|
||||
// is larger than the original bytes, the data will be written as a
|
||||
// stored block.
|
||||
// If the input is null, the tokens will always be Huffman encoded.
|
||||
/// Write a block of tokens with the smallest encoding. Will choose block type.
|
||||
/// The original input can be supplied, and if the huffman encoded data
|
||||
/// is larger than the original bytes, the data will be written as a
|
||||
/// stored block.
|
||||
/// If the input is null, the tokens will always be Huffman encoded.
|
||||
pub fn write(self: *BlockWriter, tokens: []const Token, eof: bool, input: ?[]const u8) Writer.Error!void {
|
||||
const lit_and_dist = self.indexTokens(tokens);
|
||||
const num_literals = lit_and_dist.num_literals;
|
||||
@ -379,11 +395,11 @@ pub fn storedBlock(self: *BlockWriter, input: []const u8, eof: bool) Writer.Erro
|
||||
try self.bit_writer.writeBytes(input);
|
||||
}
|
||||
|
||||
// writeBlockDynamic encodes a block using a dynamic Huffman table.
|
||||
// This should be used if the symbols used have a disproportionate
|
||||
// histogram distribution.
|
||||
// If input is supplied and the compression savings are below 1/16th of the
|
||||
// input size the block is stored.
|
||||
/// writeBlockDynamic encodes a block using a dynamic Huffman table.
|
||||
/// This should be used if the symbols used have a disproportionate
|
||||
/// histogram distribution.
|
||||
/// If input is supplied and the compression savings are below 1/16th of the
|
||||
/// input size the block is stored.
|
||||
fn dynamicBlock(
|
||||
self: *BlockWriter,
|
||||
tokens: []const Token,
|
||||
@ -429,10 +445,10 @@ const TotalIndexedTokens = struct {
|
||||
num_distances: u32,
|
||||
};
|
||||
|
||||
// Indexes a slice of tokens followed by an end_block_marker, and updates
|
||||
// literal_freq and distance_freq, and generates literal_encoding
|
||||
// and distance_encoding.
|
||||
// The number of literal and distance tokens is returned.
|
||||
/// Indexes a slice of tokens followed by an end_block_marker, and updates
|
||||
/// literal_freq and distance_freq, and generates literal_encoding
|
||||
/// and distance_encoding.
|
||||
/// The number of literal and distance tokens is returned.
|
||||
fn indexTokens(self: *BlockWriter, tokens: []const Token) TotalIndexedTokens {
|
||||
var num_literals: u32 = 0;
|
||||
var num_distances: u32 = 0;
|
||||
@ -453,7 +469,7 @@ fn indexTokens(self: *BlockWriter, tokens: []const Token) TotalIndexedTokens {
|
||||
self.distance_freq[t.distanceCode()] += 1;
|
||||
}
|
||||
// add end_block_marker token at the end
|
||||
self.literal_freq[huffman.end_block_marker] += 1;
|
||||
self.literal_freq[HuffmanEncoder.end_block_marker] += 1;
|
||||
|
||||
// get the number of literals
|
||||
num_literals = @as(u32, @intCast(self.literal_freq.len));
|
||||
@ -479,8 +495,8 @@ fn indexTokens(self: *BlockWriter, tokens: []const Token) TotalIndexedTokens {
|
||||
};
|
||||
}
|
||||
|
||||
// Writes a slice of tokens to the output followed by and end_block_marker.
|
||||
// codes for literal and distance encoding must be supplied.
|
||||
/// Writes a slice of tokens to the output followed by and end_block_marker.
|
||||
/// codes for literal and distance encoding must be supplied.
|
||||
fn writeTokens(
|
||||
self: *BlockWriter,
|
||||
tokens: []const Token,
|
||||
@ -508,18 +524,18 @@ fn writeTokens(
|
||||
}
|
||||
}
|
||||
// add end_block_marker at the end
|
||||
try self.writeCode(le_codes[huffman.end_block_marker]);
|
||||
try self.writeCode(le_codes[HuffmanEncoder.end_block_marker]);
|
||||
}
|
||||
|
||||
// Encodes a block of bytes as either Huffman encoded literals or uncompressed bytes
|
||||
// if the results only gains very little from compression.
|
||||
/// Encodes a block of bytes as either Huffman encoded literals or uncompressed bytes
|
||||
/// if the results only gains very little from compression.
|
||||
pub fn huffmanBlock(self: *BlockWriter, input: []const u8, eof: bool) Writer.Error!void {
|
||||
// Add everything as literals
|
||||
histogram(input, &self.literal_freq);
|
||||
|
||||
self.literal_freq[huffman.end_block_marker] = 1;
|
||||
self.literal_freq[HuffmanEncoder.end_block_marker] = 1;
|
||||
|
||||
const num_literals = huffman.end_block_marker + 1;
|
||||
const num_literals = HuffmanEncoder.end_block_marker + 1;
|
||||
self.distance_freq[0] = 1;
|
||||
const num_distances = 1;
|
||||
|
||||
@ -560,10 +576,9 @@ pub fn huffmanBlock(self: *BlockWriter, input: []const u8, eof: bool) Writer.Err
|
||||
const c = encoding[t];
|
||||
try self.bit_writer.writeBits(c.code, c.len);
|
||||
}
|
||||
try self.writeCode(encoding[huffman.end_block_marker]);
|
||||
try self.writeCode(encoding[HuffmanEncoder.end_block_marker]);
|
||||
}
|
||||
|
||||
// histogram accumulates a histogram of b in h.
|
||||
fn histogram(b: []const u8, h: *[286]u16) void {
|
||||
// Clear histogram
|
||||
for (h, 0..) |_, i| {
|
||||
@ -575,122 +590,3 @@ fn histogram(b: []const u8, h: *[286]u16) void {
|
||||
lh[t] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// tests
|
||||
const expect = std.testing.expect;
|
||||
const fmt = std.fmt;
|
||||
const testing = std.testing;
|
||||
const ArrayList = std.ArrayList;
|
||||
|
||||
const TestCase = @import("testdata/block_writer.zig").TestCase;
|
||||
const testCases = @import("testdata/block_writer.zig").testCases;
|
||||
|
||||
// tests if the writeBlock encoding has changed.
|
||||
test "write" {
|
||||
inline for (0..testCases.len) |i| {
|
||||
try testBlock(testCases[i], .write_block);
|
||||
}
|
||||
}
|
||||
|
||||
// tests if the writeBlockDynamic encoding has changed.
|
||||
test "dynamicBlock" {
|
||||
inline for (0..testCases.len) |i| {
|
||||
try testBlock(testCases[i], .write_dyn_block);
|
||||
}
|
||||
}
|
||||
|
||||
test "huffmanBlock" {
|
||||
inline for (0..testCases.len) |i| {
|
||||
try testBlock(testCases[i], .write_huffman_block);
|
||||
}
|
||||
try testBlock(.{
|
||||
.tokens = &[_]Token{},
|
||||
.input = "huffman-rand-max.input",
|
||||
.want = "huffman-rand-max.{s}.expect",
|
||||
}, .write_huffman_block);
|
||||
}
|
||||
|
||||
const TestFn = enum {
|
||||
write_block,
|
||||
write_dyn_block, // write dynamic block
|
||||
write_huffman_block,
|
||||
|
||||
fn to_s(self: TestFn) []const u8 {
|
||||
return switch (self) {
|
||||
.write_block => "wb",
|
||||
.write_dyn_block => "dyn",
|
||||
.write_huffman_block => "huff",
|
||||
};
|
||||
}
|
||||
|
||||
fn write(
|
||||
comptime self: TestFn,
|
||||
bw: anytype,
|
||||
tok: []const Token,
|
||||
input: ?[]const u8,
|
||||
final: bool,
|
||||
) !void {
|
||||
switch (self) {
|
||||
.write_block => try bw.write(tok, final, input),
|
||||
.write_dyn_block => try bw.dynamicBlock(tok, final, input),
|
||||
.write_huffman_block => try bw.huffmanBlock(input.?, final),
|
||||
}
|
||||
try bw.flush();
|
||||
}
|
||||
};
|
||||
|
||||
// testBlock tests a block against its references
|
||||
//
|
||||
// size
|
||||
// 64K [file-name].input - input non compressed file
|
||||
// 8.1K [file-name].golden -
|
||||
// 78 [file-name].dyn.expect - output with writeBlockDynamic
|
||||
// 78 [file-name].wb.expect - output with writeBlock
|
||||
// 8.1K [file-name].huff.expect - output with writeBlockHuff
|
||||
// 78 [file-name].dyn.expect-noinput - output with writeBlockDynamic when input is null
|
||||
// 78 [file-name].wb.expect-noinput - output with writeBlock when input is null
|
||||
//
|
||||
// wb - writeBlock
|
||||
// dyn - writeBlockDynamic
|
||||
// huff - writeBlockHuff
|
||||
//
|
||||
fn testBlock(comptime tc: TestCase, comptime tfn: TestFn) !void {
|
||||
if (tc.input.len != 0 and tc.want.len != 0) {
|
||||
const want_name = comptime fmt.comptimePrint(tc.want, .{tfn.to_s()});
|
||||
const input = @embedFile("testdata/block_writer/" ++ tc.input);
|
||||
const want = @embedFile("testdata/block_writer/" ++ want_name);
|
||||
try testWriteBlock(tfn, input, want, tc.tokens);
|
||||
}
|
||||
|
||||
if (tfn == .write_huffman_block) {
|
||||
return;
|
||||
}
|
||||
|
||||
const want_name_no_input = comptime fmt.comptimePrint(tc.want_no_input, .{tfn.to_s()});
|
||||
const want = @embedFile("testdata/block_writer/" ++ want_name_no_input);
|
||||
try testWriteBlock(tfn, null, want, tc.tokens);
|
||||
}
|
||||
|
||||
// Uses writer function `tfn` to write `tokens`, tests that we got `want` as output.
|
||||
fn testWriteBlock(comptime tfn: TestFn, input: ?[]const u8, want: []const u8, tokens: []const Token) !void {
|
||||
var buf = ArrayList(u8).init(testing.allocator);
|
||||
var bw: BlockWriter = .init(buf.writer());
|
||||
try tfn.write(&bw, tokens, input, false);
|
||||
var got = buf.items;
|
||||
try testing.expectEqualSlices(u8, want, got); // expect writeBlock to yield expected result
|
||||
try expect(got[0] & 0b0000_0001 == 0); // bfinal is not set
|
||||
//
|
||||
// Test if the writer produces the same output after reset.
|
||||
buf.deinit();
|
||||
buf = ArrayList(u8).init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
bw.setWriter(buf.writer());
|
||||
|
||||
try tfn.write(&bw, tokens, input, true);
|
||||
try bw.flush();
|
||||
got = buf.items;
|
||||
|
||||
try expect(got[0] & 1 == 1); // bfinal is set
|
||||
buf.items[0] &= 0b1111_1110; // remove bfinal bit, so we can run test slices
|
||||
try testing.expectEqualSlices(u8, want, got); // expect writeBlock to yield expected result
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
463
lib/std/compress/flate/HuffmanEncoder.zig
Normal file
463
lib/std/compress/flate/HuffmanEncoder.zig
Normal file
@ -0,0 +1,463 @@
|
||||
const HuffmanEncoder = @This();
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const testing = std.testing;
|
||||
|
||||
codes: []Code,
|
||||
// Reusable buffer with the longest possible frequency table.
|
||||
freq_cache: [max_num_frequencies + 1]LiteralNode,
|
||||
bit_count: [17]u32,
|
||||
lns: []LiteralNode, // sorted by literal, stored to avoid repeated allocation in generate
|
||||
lfs: []LiteralNode, // sorted by frequency, stored to avoid repeated allocation in generate
|
||||
|
||||
pub const LiteralNode = struct {
|
||||
literal: u16,
|
||||
freq: u16,
|
||||
|
||||
pub fn max() LiteralNode {
|
||||
return .{
|
||||
.literal = std.math.maxInt(u16),
|
||||
.freq = std.math.maxInt(u16),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const Code = struct {
|
||||
code: u16 = 0,
|
||||
len: u16 = 0,
|
||||
};
|
||||
|
||||
/// The odd order in which the codegen code sizes are written.
|
||||
pub const codegen_order = [_]u32{ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
|
||||
/// The number of codegen codes.
|
||||
pub const codegen_code_count = 19;
|
||||
|
||||
/// The largest distance code.
|
||||
pub const distance_code_count = 30;
|
||||
|
||||
/// Maximum number of literals.
|
||||
pub const max_num_lit = 286;
|
||||
|
||||
/// Max number of frequencies used for a Huffman Code
|
||||
/// Possible lengths are codegen_code_count (19), distance_code_count (30) and max_num_lit (286).
|
||||
/// The largest of these is max_num_lit.
|
||||
pub const max_num_frequencies = max_num_lit;
|
||||
|
||||
/// Biggest block size for uncompressed block.
|
||||
pub const max_store_block_size = 65535;
|
||||
/// The special code used to mark the end of a block.
|
||||
pub const end_block_marker = 256;
|
||||
|
||||
/// Update this Huffman Code object to be the minimum code for the specified frequency count.
|
||||
///
|
||||
/// freq An array of frequencies, in which frequency[i] gives the frequency of literal i.
|
||||
/// max_bits The maximum number of bits to use for any literal.
|
||||
pub fn generate(self: *HuffmanEncoder, freq: []u16, max_bits: u32) void {
|
||||
var list = self.freq_cache[0 .. freq.len + 1];
|
||||
// Number of non-zero literals
|
||||
var count: u32 = 0;
|
||||
// Set list to be the set of all non-zero literals and their frequencies
|
||||
for (freq, 0..) |f, i| {
|
||||
if (f != 0) {
|
||||
list[count] = LiteralNode{ .literal = @as(u16, @intCast(i)), .freq = f };
|
||||
count += 1;
|
||||
} else {
|
||||
list[count] = LiteralNode{ .literal = 0x00, .freq = 0 };
|
||||
self.codes[i].len = 0;
|
||||
}
|
||||
}
|
||||
list[freq.len] = LiteralNode{ .literal = 0x00, .freq = 0 };
|
||||
|
||||
list = list[0..count];
|
||||
if (count <= 2) {
|
||||
// Handle the small cases here, because they are awkward for the general case code. With
|
||||
// two or fewer literals, everything has bit length 1.
|
||||
for (list, 0..) |node, i| {
|
||||
// "list" is in order of increasing literal value.
|
||||
self.codes[node.literal] = .{
|
||||
.code = @intCast(i),
|
||||
.len = 1,
|
||||
};
|
||||
}
|
||||
return;
|
||||
}
|
||||
self.lfs = list;
|
||||
std.mem.sort(LiteralNode, self.lfs, {}, byFreq);
|
||||
|
||||
// Get the number of literals for each bit count
|
||||
const bit_count = self.bitCounts(list, max_bits);
|
||||
// And do the assignment
|
||||
self.assignEncodingAndSize(bit_count, list);
|
||||
}
|
||||
|
||||
pub fn bitLength(self: *HuffmanEncoder, freq: []u16) u32 {
|
||||
var total: u32 = 0;
|
||||
for (freq, 0..) |f, i| {
|
||||
if (f != 0) {
|
||||
total += @as(u32, @intCast(f)) * @as(u32, @intCast(self.codes[i].len));
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
/// Return the number of literals assigned to each bit size in the Huffman encoding
|
||||
///
|
||||
/// This method is only called when list.len >= 3
|
||||
/// The cases of 0, 1, and 2 literals are handled by special case code.
|
||||
///
|
||||
/// list: An array of the literals with non-zero frequencies
|
||||
/// and their associated frequencies. The array is in order of increasing
|
||||
/// frequency, and has as its last element a special element with frequency
|
||||
/// `math.maxInt(i32)`
|
||||
///
|
||||
/// max_bits: The maximum number of bits that should be used to encode any literal.
|
||||
/// Must be less than 16.
|
||||
///
|
||||
/// Returns an integer array in which array[i] indicates the number of literals
|
||||
/// that should be encoded in i bits.
|
||||
fn bitCounts(self: *HuffmanEncoder, list: []LiteralNode, max_bits_to_use: usize) []u32 {
|
||||
var max_bits = max_bits_to_use;
|
||||
const n = list.len;
|
||||
const max_bits_limit = 16;
|
||||
|
||||
assert(max_bits < max_bits_limit);
|
||||
|
||||
// The tree can't have greater depth than n - 1, no matter what. This
|
||||
// saves a little bit of work in some small cases
|
||||
max_bits = @min(max_bits, n - 1);
|
||||
|
||||
// Create information about each of the levels.
|
||||
// A bogus "Level 0" whose sole purpose is so that
|
||||
// level1.prev.needed == 0. This makes level1.next_pair_freq
|
||||
// be a legitimate value that never gets chosen.
|
||||
var levels: [max_bits_limit]LevelInfo = std.mem.zeroes([max_bits_limit]LevelInfo);
|
||||
// leaf_counts[i] counts the number of literals at the left
|
||||
// of ancestors of the rightmost node at level i.
|
||||
// leaf_counts[i][j] is the number of literals at the left
|
||||
// of the level j ancestor.
|
||||
var leaf_counts: [max_bits_limit][max_bits_limit]u32 = @splat(@splat(0));
|
||||
|
||||
{
|
||||
var level = @as(u32, 1);
|
||||
while (level <= max_bits) : (level += 1) {
|
||||
// For every level, the first two items are the first two characters.
|
||||
// We initialize the levels as if we had already figured this out.
|
||||
levels[level] = LevelInfo{
|
||||
.level = level,
|
||||
.last_freq = list[1].freq,
|
||||
.next_char_freq = list[2].freq,
|
||||
.next_pair_freq = list[0].freq + list[1].freq,
|
||||
.needed = 0,
|
||||
};
|
||||
leaf_counts[level][level] = 2;
|
||||
if (level == 1) {
|
||||
levels[level].next_pair_freq = std.math.maxInt(i32);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We need a total of 2*n - 2 items at top level and have already generated 2.
|
||||
levels[max_bits].needed = 2 * @as(u32, @intCast(n)) - 4;
|
||||
|
||||
{
|
||||
var level = max_bits;
|
||||
while (true) {
|
||||
var l = &levels[level];
|
||||
if (l.next_pair_freq == std.math.maxInt(i32) and l.next_char_freq == std.math.maxInt(i32)) {
|
||||
// We've run out of both leaves and pairs.
|
||||
// End all calculations for this level.
|
||||
// To make sure we never come back to this level or any lower level,
|
||||
// set next_pair_freq impossibly large.
|
||||
l.needed = 0;
|
||||
levels[level + 1].next_pair_freq = std.math.maxInt(i32);
|
||||
level += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
const prev_freq = l.last_freq;
|
||||
if (l.next_char_freq < l.next_pair_freq) {
|
||||
// The next item on this row is a leaf node.
|
||||
const next = leaf_counts[level][level] + 1;
|
||||
l.last_freq = l.next_char_freq;
|
||||
// Lower leaf_counts are the same of the previous node.
|
||||
leaf_counts[level][level] = next;
|
||||
if (next >= list.len) {
|
||||
l.next_char_freq = LiteralNode.max().freq;
|
||||
} else {
|
||||
l.next_char_freq = list[next].freq;
|
||||
}
|
||||
} else {
|
||||
// The next item on this row is a pair from the previous row.
|
||||
// next_pair_freq isn't valid until we generate two
|
||||
// more values in the level below
|
||||
l.last_freq = l.next_pair_freq;
|
||||
// Take leaf counts from the lower level, except counts[level] remains the same.
|
||||
@memcpy(leaf_counts[level][0..level], leaf_counts[level - 1][0..level]);
|
||||
levels[l.level - 1].needed = 2;
|
||||
}
|
||||
|
||||
l.needed -= 1;
|
||||
if (l.needed == 0) {
|
||||
// We've done everything we need to do for this level.
|
||||
// Continue calculating one level up. Fill in next_pair_freq
|
||||
// of that level with the sum of the two nodes we've just calculated on
|
||||
// this level.
|
||||
if (l.level == max_bits) {
|
||||
// All done!
|
||||
break;
|
||||
}
|
||||
levels[l.level + 1].next_pair_freq = prev_freq + l.last_freq;
|
||||
level += 1;
|
||||
} else {
|
||||
// If we stole from below, move down temporarily to replenish it.
|
||||
while (levels[level - 1].needed > 0) {
|
||||
level -= 1;
|
||||
if (level == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Somethings is wrong if at the end, the top level is null or hasn't used
|
||||
// all of the leaves.
|
||||
assert(leaf_counts[max_bits][max_bits] == n);
|
||||
|
||||
var bit_count = self.bit_count[0 .. max_bits + 1];
|
||||
var bits: u32 = 1;
|
||||
const counts = &leaf_counts[max_bits];
|
||||
{
|
||||
var level = max_bits;
|
||||
while (level > 0) : (level -= 1) {
|
||||
// counts[level] gives the number of literals requiring at least "bits"
|
||||
// bits to encode.
|
||||
bit_count[bits] = counts[level] - counts[level - 1];
|
||||
bits += 1;
|
||||
if (level == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return bit_count;
|
||||
}
|
||||
|
||||
/// Look at the leaves and assign them a bit count and an encoding as specified
|
||||
/// in RFC 1951 3.2.2
|
||||
fn assignEncodingAndSize(self: *HuffmanEncoder, bit_count: []u32, list_arg: []LiteralNode) void {
|
||||
var code = @as(u16, 0);
|
||||
var list = list_arg;
|
||||
|
||||
for (bit_count, 0..) |bits, n| {
|
||||
code <<= 1;
|
||||
if (n == 0 or bits == 0) {
|
||||
continue;
|
||||
}
|
||||
// The literals list[list.len-bits] .. list[list.len-bits]
|
||||
// are encoded using "bits" bits, and get the values
|
||||
// code, code + 1, .... The code values are
|
||||
// assigned in literal order (not frequency order).
|
||||
const chunk = list[list.len - @as(u32, @intCast(bits)) ..];
|
||||
|
||||
self.lns = chunk;
|
||||
std.mem.sort(LiteralNode, self.lns, {}, byLiteral);
|
||||
|
||||
for (chunk) |node| {
|
||||
self.codes[node.literal] = .{
|
||||
.code = bitReverse(u16, code, @as(u5, @intCast(n))),
|
||||
.len = @as(u16, @intCast(n)),
|
||||
};
|
||||
code += 1;
|
||||
}
|
||||
list = list[0 .. list.len - @as(u32, @intCast(bits))];
|
||||
}
|
||||
}
|
||||
|
||||
fn byFreq(context: void, a: LiteralNode, b: LiteralNode) bool {
|
||||
_ = context;
|
||||
if (a.freq == b.freq) {
|
||||
return a.literal < b.literal;
|
||||
}
|
||||
return a.freq < b.freq;
|
||||
}
|
||||
|
||||
/// Describes the state of the constructed tree for a given depth.
|
||||
const LevelInfo = struct {
|
||||
/// Our level. for better printing
|
||||
level: u32,
|
||||
/// The frequency of the last node at this level
|
||||
last_freq: u32,
|
||||
/// The frequency of the next character to add to this level
|
||||
next_char_freq: u32,
|
||||
/// The frequency of the next pair (from level below) to add to this level.
|
||||
/// Only valid if the "needed" value of the next lower level is 0.
|
||||
next_pair_freq: u32,
|
||||
/// The number of chains remaining to generate for this level before moving
|
||||
/// up to the next level
|
||||
needed: u32,
|
||||
};
|
||||
|
||||
fn byLiteral(context: void, a: LiteralNode, b: LiteralNode) bool {
|
||||
_ = context;
|
||||
return a.literal < b.literal;
|
||||
}
|
||||
|
||||
/// Reverse bit-by-bit a N-bit code.
|
||||
fn bitReverse(comptime T: type, value: T, n: usize) T {
|
||||
const r = @bitReverse(value);
|
||||
return r >> @as(std.math.Log2Int(T), @intCast(@typeInfo(T).int.bits - n));
|
||||
}
|
||||
|
||||
test bitReverse {
|
||||
const ReverseBitsTest = struct {
|
||||
in: u16,
|
||||
bit_count: u5,
|
||||
out: u16,
|
||||
};
|
||||
|
||||
const reverse_bits_tests = [_]ReverseBitsTest{
|
||||
.{ .in = 1, .bit_count = 1, .out = 1 },
|
||||
.{ .in = 1, .bit_count = 2, .out = 2 },
|
||||
.{ .in = 1, .bit_count = 3, .out = 4 },
|
||||
.{ .in = 1, .bit_count = 4, .out = 8 },
|
||||
.{ .in = 1, .bit_count = 5, .out = 16 },
|
||||
.{ .in = 17, .bit_count = 5, .out = 17 },
|
||||
.{ .in = 257, .bit_count = 9, .out = 257 },
|
||||
.{ .in = 29, .bit_count = 5, .out = 23 },
|
||||
};
|
||||
|
||||
for (reverse_bits_tests) |h| {
|
||||
const v = bitReverse(u16, h.in, h.bit_count);
|
||||
try std.testing.expectEqual(h.out, v);
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates a HuffmanCode corresponding to the fixed literal table
|
||||
pub fn fixedLiteralEncoder(codes: *[max_num_frequencies]Code) HuffmanEncoder {
|
||||
var h: HuffmanEncoder = undefined;
|
||||
h.codes = codes;
|
||||
var ch: u16 = 0;
|
||||
|
||||
while (ch < max_num_frequencies) : (ch += 1) {
|
||||
var bits: u16 = undefined;
|
||||
var size: u16 = undefined;
|
||||
switch (ch) {
|
||||
0...143 => {
|
||||
// size 8, 000110000 .. 10111111
|
||||
bits = ch + 48;
|
||||
size = 8;
|
||||
},
|
||||
144...255 => {
|
||||
// size 9, 110010000 .. 111111111
|
||||
bits = ch + 400 - 144;
|
||||
size = 9;
|
||||
},
|
||||
256...279 => {
|
||||
// size 7, 0000000 .. 0010111
|
||||
bits = ch - 256;
|
||||
size = 7;
|
||||
},
|
||||
else => {
|
||||
// size 8, 11000000 .. 11000111
|
||||
bits = ch + 192 - 280;
|
||||
size = 8;
|
||||
},
|
||||
}
|
||||
h.codes[ch] = .{ .code = bitReverse(u16, bits, @as(u5, @intCast(size))), .len = size };
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
pub fn fixedDistanceEncoder(codes: *[distance_code_count]Code) HuffmanEncoder {
|
||||
var h: HuffmanEncoder = undefined;
|
||||
h.codes = codes;
|
||||
for (h.codes, 0..) |_, ch| {
|
||||
h.codes[ch] = .{ .code = bitReverse(u16, @as(u16, @intCast(ch)), 5), .len = 5 };
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
pub fn huffmanDistanceEncoder(codes: *[distance_code_count]Code) HuffmanEncoder {
|
||||
var distance_freq: [distance_code_count]u16 = @splat(0);
|
||||
distance_freq[0] = 1;
|
||||
// huff_distance is a static distance encoder used for huffman only encoding.
|
||||
// It can be reused since we will not be encoding distance values.
|
||||
var h: HuffmanEncoder = .{};
|
||||
h.codes = codes;
|
||||
h.generate(distance_freq[0..], 15);
|
||||
return h;
|
||||
}
|
||||
|
||||
test "generate a Huffman code for the fixed literal table specific to Deflate" {
|
||||
var codes: [max_num_frequencies]Code = undefined;
|
||||
const enc: HuffmanEncoder = .fixedLiteralEncoder(&codes);
|
||||
for (enc.codes) |c| {
|
||||
switch (c.len) {
|
||||
7 => {
|
||||
const v = @bitReverse(@as(u7, @intCast(c.code)));
|
||||
try testing.expect(v <= 0b0010111);
|
||||
},
|
||||
8 => {
|
||||
const v = @bitReverse(@as(u8, @intCast(c.code)));
|
||||
try testing.expect((v >= 0b000110000 and v <= 0b10111111) or
|
||||
(v >= 0b11000000 and v <= 11000111));
|
||||
},
|
||||
9 => {
|
||||
const v = @bitReverse(@as(u9, @intCast(c.code)));
|
||||
try testing.expect(v >= 0b110010000 and v <= 0b111111111);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test "generate a Huffman code for the 30 possible relative distances (LZ77 distances) of Deflate" {
|
||||
var codes: [distance_code_count]Code = undefined;
|
||||
const enc = fixedDistanceEncoder(&codes);
|
||||
for (enc.codes) |c| {
|
||||
const v = @bitReverse(@as(u5, @intCast(c.code)));
|
||||
try testing.expect(v <= 29);
|
||||
try testing.expect(c.len == 5);
|
||||
}
|
||||
}
|
||||
|
||||
pub const fixed_codes = [_]u8{
|
||||
0b00001100, 0b10001100, 0b01001100, 0b11001100, 0b00101100, 0b10101100, 0b01101100, 0b11101100,
|
||||
0b00011100, 0b10011100, 0b01011100, 0b11011100, 0b00111100, 0b10111100, 0b01111100, 0b11111100,
|
||||
0b00000010, 0b10000010, 0b01000010, 0b11000010, 0b00100010, 0b10100010, 0b01100010, 0b11100010,
|
||||
0b00010010, 0b10010010, 0b01010010, 0b11010010, 0b00110010, 0b10110010, 0b01110010, 0b11110010,
|
||||
0b00001010, 0b10001010, 0b01001010, 0b11001010, 0b00101010, 0b10101010, 0b01101010, 0b11101010,
|
||||
0b00011010, 0b10011010, 0b01011010, 0b11011010, 0b00111010, 0b10111010, 0b01111010, 0b11111010,
|
||||
0b00000110, 0b10000110, 0b01000110, 0b11000110, 0b00100110, 0b10100110, 0b01100110, 0b11100110,
|
||||
0b00010110, 0b10010110, 0b01010110, 0b11010110, 0b00110110, 0b10110110, 0b01110110, 0b11110110,
|
||||
0b00001110, 0b10001110, 0b01001110, 0b11001110, 0b00101110, 0b10101110, 0b01101110, 0b11101110,
|
||||
0b00011110, 0b10011110, 0b01011110, 0b11011110, 0b00111110, 0b10111110, 0b01111110, 0b11111110,
|
||||
0b00000001, 0b10000001, 0b01000001, 0b11000001, 0b00100001, 0b10100001, 0b01100001, 0b11100001,
|
||||
0b00010001, 0b10010001, 0b01010001, 0b11010001, 0b00110001, 0b10110001, 0b01110001, 0b11110001,
|
||||
0b00001001, 0b10001001, 0b01001001, 0b11001001, 0b00101001, 0b10101001, 0b01101001, 0b11101001,
|
||||
0b00011001, 0b10011001, 0b01011001, 0b11011001, 0b00111001, 0b10111001, 0b01111001, 0b11111001,
|
||||
0b00000101, 0b10000101, 0b01000101, 0b11000101, 0b00100101, 0b10100101, 0b01100101, 0b11100101,
|
||||
0b00010101, 0b10010101, 0b01010101, 0b11010101, 0b00110101, 0b10110101, 0b01110101, 0b11110101,
|
||||
0b00001101, 0b10001101, 0b01001101, 0b11001101, 0b00101101, 0b10101101, 0b01101101, 0b11101101,
|
||||
0b00011101, 0b10011101, 0b01011101, 0b11011101, 0b00111101, 0b10111101, 0b01111101, 0b11111101,
|
||||
0b00010011, 0b00100110, 0b01001110, 0b10011010, 0b00111100, 0b01100101, 0b11101010, 0b10110100,
|
||||
0b11101001, 0b00110011, 0b01100110, 0b11001110, 0b10011010, 0b00111101, 0b01100111, 0b11101110,
|
||||
0b10111100, 0b11111001, 0b00001011, 0b00010110, 0b00101110, 0b01011010, 0b10111100, 0b01100100,
|
||||
0b11101001, 0b10110010, 0b11100101, 0b00101011, 0b01010110, 0b10101110, 0b01011010, 0b10111101,
|
||||
0b01100110, 0b11101101, 0b10111010, 0b11110101, 0b00011011, 0b00110110, 0b01101110, 0b11011010,
|
||||
0b10111100, 0b01100101, 0b11101011, 0b10110110, 0b11101101, 0b00111011, 0b01110110, 0b11101110,
|
||||
0b11011010, 0b10111101, 0b01100111, 0b11101111, 0b10111110, 0b11111101, 0b00000111, 0b00001110,
|
||||
0b00011110, 0b00111010, 0b01111100, 0b11100100, 0b11101000, 0b10110001, 0b11100011, 0b00100111,
|
||||
0b01001110, 0b10011110, 0b00111010, 0b01111101, 0b11100110, 0b11101100, 0b10111001, 0b11110011,
|
||||
0b00010111, 0b00101110, 0b01011110, 0b10111010, 0b01111100, 0b11100101, 0b11101010, 0b10110101,
|
||||
0b11101011, 0b00110111, 0b01101110, 0b11011110, 0b10111010, 0b01111101, 0b11100111, 0b11101110,
|
||||
0b10111101, 0b11111011, 0b00001111, 0b00011110, 0b00111110, 0b01111010, 0b11111100, 0b11100100,
|
||||
0b11101001, 0b10110011, 0b11100111, 0b00101111, 0b01011110, 0b10111110, 0b01111010, 0b11111101,
|
||||
0b11100110, 0b11101101, 0b10111011, 0b11110111, 0b00011111, 0b00111110, 0b01111110, 0b11111010,
|
||||
0b11111100, 0b11100101, 0b11101011, 0b10110111, 0b11101111, 0b00111111, 0b01111110, 0b11111110,
|
||||
0b11111010, 0b11111101, 0b11100111, 0b11101111, 0b10111111, 0b11111111, 0b00000000, 0b00100000,
|
||||
0b00001000, 0b00001100, 0b10000001, 0b11000010, 0b11100000, 0b00001000, 0b00100100, 0b00001010,
|
||||
0b10001101, 0b11000001, 0b11100010, 0b11110000, 0b00000100, 0b00100010, 0b10001001, 0b01001100,
|
||||
0b10100001, 0b11010010, 0b11101000, 0b00000011, 0b10000011, 0b01000011, 0b11000011, 0b00100011,
|
||||
0b10100011,
|
||||
};
|
||||
@ -6,14 +6,19 @@ const std = @import("std");
|
||||
const testing = std.testing;
|
||||
const expect = testing.expect;
|
||||
const flate = @import("../flate.zig");
|
||||
const Token = @import("Token.zig");
|
||||
|
||||
const Lookup = @This();
|
||||
|
||||
const prime4 = 0x9E3779B1; // 4 bytes prime number 2654435761
|
||||
const chain_len = 2 * flate.history_len;
|
||||
|
||||
pub const bits = 15;
|
||||
pub const len = 1 << bits;
|
||||
pub const shift = 32 - bits;
|
||||
|
||||
// Maps hash => first position
|
||||
head: [flate.lookup.len]u16 = [_]u16{0} ** flate.lookup.len,
|
||||
head: [len]u16 = [_]u16{0} ** len,
|
||||
// Maps position => previous positions for the same hash value
|
||||
chain: [chain_len]u16 = [_]u16{0} ** (chain_len),
|
||||
|
||||
@ -52,8 +57,8 @@ pub fn slide(self: *Lookup, n: u16) void {
|
||||
|
||||
// Add `len` 4 bytes hashes from `data` into lookup.
|
||||
// Position of the first byte is `pos`.
|
||||
pub fn bulkAdd(self: *Lookup, data: []const u8, len: u16, pos: u16) void {
|
||||
if (len == 0 or data.len < flate.match.min_length) {
|
||||
pub fn bulkAdd(self: *Lookup, data: []const u8, length: u16, pos: u16) void {
|
||||
if (length == 0 or data.len < Token.min_length) {
|
||||
return;
|
||||
}
|
||||
var hb =
|
||||
@ -64,7 +69,7 @@ pub fn bulkAdd(self: *Lookup, data: []const u8, len: u16, pos: u16) void {
|
||||
_ = self.set(hashu(hb), pos);
|
||||
|
||||
var i = pos;
|
||||
for (4..@min(len + 3, data.len)) |j| {
|
||||
for (4..@min(length + 3, data.len)) |j| {
|
||||
hb = (hb << 8) | @as(u32, data[j]);
|
||||
i += 1;
|
||||
_ = self.set(hashu(hb), i);
|
||||
@ -80,7 +85,7 @@ fn hash(b: *const [4]u8) u32 {
|
||||
}
|
||||
|
||||
fn hashu(v: u32) u32 {
|
||||
return @intCast((v *% prime4) >> flate.lookup.shift);
|
||||
return @intCast((v *% prime4) >> shift);
|
||||
}
|
||||
|
||||
test add {
|
||||
|
||||
@ -6,7 +6,6 @@ const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const print = std.debug.print;
|
||||
const expect = std.testing.expect;
|
||||
const match = std.compress.flate.match;
|
||||
|
||||
const Token = @This();
|
||||
|
||||
@ -21,16 +20,23 @@ dist: u15 = 0,
|
||||
len_lit: u8 = 0,
|
||||
kind: Kind = .literal,
|
||||
|
||||
pub const base_length = 3; // smallest match length per the RFC section 3.2.5
|
||||
pub const min_length = 4; // min length used in this algorithm
|
||||
pub const max_length = 258;
|
||||
|
||||
pub const min_distance = 1;
|
||||
pub const max_distance = std.compress.flate.history_len;
|
||||
|
||||
pub fn literal(t: Token) u8 {
|
||||
return t.len_lit;
|
||||
}
|
||||
|
||||
pub fn distance(t: Token) u16 {
|
||||
return @as(u16, t.dist) + match.min_distance;
|
||||
return @as(u16, t.dist) + min_distance;
|
||||
}
|
||||
|
||||
pub fn length(t: Token) u16 {
|
||||
return @as(u16, t.len_lit) + match.base_length;
|
||||
return @as(u16, t.len_lit) + base_length;
|
||||
}
|
||||
|
||||
pub fn initLiteral(lit: u8) Token {
|
||||
@ -40,12 +46,12 @@ pub fn initLiteral(lit: u8) Token {
|
||||
// distance range 1 - 32768, stored in dist as 0 - 32767 (u15)
|
||||
// length range 3 - 258, stored in len_lit as 0 - 255 (u8)
|
||||
pub fn initMatch(dist: u16, len: u16) Token {
|
||||
assert(len >= match.min_length and len <= match.max_length);
|
||||
assert(dist >= match.min_distance and dist <= match.max_distance);
|
||||
assert(len >= min_length and len <= max_length);
|
||||
assert(dist >= min_distance and dist <= max_distance);
|
||||
return .{
|
||||
.kind = .match,
|
||||
.dist = @intCast(dist - match.min_distance),
|
||||
.len_lit = @intCast(len - match.base_length),
|
||||
.dist = @intCast(dist - min_distance),
|
||||
.len_lit = @intCast(len - base_length),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
606
lib/std/compress/flate/testdata/block_writer.zig
vendored
606
lib/std/compress/flate/testdata/block_writer.zig
vendored
@ -1,606 +0,0 @@
|
||||
const Token = @import("../Token.zig");
|
||||
|
||||
pub const TestCase = struct {
|
||||
tokens: []const Token,
|
||||
input: []const u8 = "", // File name of input data matching the tokens.
|
||||
want: []const u8 = "", // File name of data with the expected output with input available.
|
||||
want_no_input: []const u8 = "", // File name of the expected output when no input is available.
|
||||
};
|
||||
|
||||
pub const testCases = blk: {
|
||||
@setEvalBranchQuota(4096 * 2);
|
||||
|
||||
const L = Token.initLiteral;
|
||||
const M = Token.initMatch;
|
||||
const ml = M(1, 258); // Maximum length token. Used to reduce the size of writeBlockTests
|
||||
|
||||
break :blk &[_]TestCase{
|
||||
TestCase{
|
||||
.input = "huffman-null-max.input",
|
||||
.want = "huffman-null-max.{s}.expect",
|
||||
.want_no_input = "huffman-null-max.{s}.expect-noinput",
|
||||
.tokens = &[_]Token{
|
||||
L(0x0), ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, L(0x0), L(0x0),
|
||||
},
|
||||
},
|
||||
TestCase{
|
||||
.input = "huffman-pi.input",
|
||||
.want = "huffman-pi.{s}.expect",
|
||||
.want_no_input = "huffman-pi.{s}.expect-noinput",
|
||||
.tokens = &[_]Token{
|
||||
L('3'), L('.'), L('1'), L('4'), L('1'), L('5'), L('9'), L('2'),
|
||||
L('6'), L('5'), L('3'), L('5'), L('8'), L('9'), L('7'), L('9'),
|
||||
L('3'), L('2'), L('3'), L('8'), L('4'), L('6'), L('2'), L('6'),
|
||||
L('4'), L('3'), L('3'), L('8'), L('3'), L('2'), L('7'), L('9'),
|
||||
L('5'), L('0'), L('2'), L('8'), L('8'), L('4'), L('1'), L('9'),
|
||||
L('7'), L('1'), L('6'), L('9'), L('3'), L('9'), L('9'), L('3'),
|
||||
L('7'), L('5'), L('1'), L('0'), L('5'), L('8'), L('2'), L('0'),
|
||||
L('9'), L('7'), L('4'), L('9'), L('4'), L('4'), L('5'), L('9'),
|
||||
L('2'), L('3'), L('0'), L('7'), L('8'), L('1'), L('6'), L('4'),
|
||||
L('0'), L('6'), L('2'), L('8'), L('6'), L('2'), L('0'), L('8'),
|
||||
L('9'), L('9'), L('8'), L('6'), L('2'), L('8'), L('0'), L('3'),
|
||||
L('4'), L('8'), L('2'), L('5'), L('3'), L('4'), L('2'), L('1'),
|
||||
L('1'), L('7'), L('0'), L('6'), L('7'), L('9'), L('8'), L('2'),
|
||||
L('1'), L('4'), L('8'), L('0'), L('8'), L('6'), L('5'), L('1'),
|
||||
L('3'), L('2'), L('8'), L('2'), L('3'), L('0'), L('6'), L('6'),
|
||||
L('4'), L('7'), L('0'), L('9'), L('3'), L('8'), L('4'), L('4'),
|
||||
L('6'), L('0'), L('9'), L('5'), L('5'), L('0'), L('5'), L('8'),
|
||||
L('2'), L('2'), L('3'), L('1'), L('7'), L('2'), L('5'), L('3'),
|
||||
L('5'), L('9'), L('4'), L('0'), L('8'), L('1'), L('2'), L('8'),
|
||||
L('4'), L('8'), L('1'), L('1'), L('1'), L('7'), L('4'), M(127, 4),
|
||||
L('4'), L('1'), L('0'), L('2'), L('7'), L('0'), L('1'), L('9'),
|
||||
L('3'), L('8'), L('5'), L('2'), L('1'), L('1'), L('0'), L('5'),
|
||||
L('5'), L('5'), L('9'), L('6'), L('4'), L('4'), L('6'), L('2'),
|
||||
L('2'), L('9'), L('4'), L('8'), L('9'), L('5'), L('4'), L('9'),
|
||||
L('3'), L('0'), L('3'), L('8'), L('1'), M(19, 4), L('2'), L('8'),
|
||||
L('8'), L('1'), L('0'), L('9'), L('7'), L('5'), L('6'), L('6'),
|
||||
L('5'), L('9'), L('3'), L('3'), L('4'), L('4'), L('6'), M(72, 4),
|
||||
L('7'), L('5'), L('6'), L('4'), L('8'), L('2'), L('3'), L('3'),
|
||||
L('7'), L('8'), L('6'), L('7'), L('8'), L('3'), L('1'), L('6'),
|
||||
L('5'), L('2'), L('7'), L('1'), L('2'), L('0'), L('1'), L('9'),
|
||||
L('0'), L('9'), L('1'), L('4'), M(27, 4), L('5'), L('6'), L('6'),
|
||||
L('9'), L('2'), L('3'), L('4'), L('6'), M(179, 4), L('6'), L('1'),
|
||||
L('0'), L('4'), L('5'), L('4'), L('3'), L('2'), L('6'), M(51, 4),
|
||||
L('1'), L('3'), L('3'), L('9'), L('3'), L('6'), L('0'), L('7'),
|
||||
L('2'), L('6'), L('0'), L('2'), L('4'), L('9'), L('1'), L('4'),
|
||||
L('1'), L('2'), L('7'), L('3'), L('7'), L('2'), L('4'), L('5'),
|
||||
L('8'), L('7'), L('0'), L('0'), L('6'), L('6'), L('0'), L('6'),
|
||||
L('3'), L('1'), L('5'), L('5'), L('8'), L('8'), L('1'), L('7'),
|
||||
L('4'), L('8'), L('8'), L('1'), L('5'), L('2'), L('0'), L('9'),
|
||||
L('2'), L('0'), L('9'), L('6'), L('2'), L('8'), L('2'), L('9'),
|
||||
L('2'), L('5'), L('4'), L('0'), L('9'), L('1'), L('7'), L('1'),
|
||||
L('5'), L('3'), L('6'), L('4'), L('3'), L('6'), L('7'), L('8'),
|
||||
L('9'), L('2'), L('5'), L('9'), L('0'), L('3'), L('6'), L('0'),
|
||||
L('0'), L('1'), L('1'), L('3'), L('3'), L('0'), L('5'), L('3'),
|
||||
L('0'), L('5'), L('4'), L('8'), L('8'), L('2'), L('0'), L('4'),
|
||||
L('6'), L('6'), L('5'), L('2'), L('1'), L('3'), L('8'), L('4'),
|
||||
L('1'), L('4'), L('6'), L('9'), L('5'), L('1'), L('9'), L('4'),
|
||||
L('1'), L('5'), L('1'), L('1'), L('6'), L('0'), L('9'), L('4'),
|
||||
L('3'), L('3'), L('0'), L('5'), L('7'), L('2'), L('7'), L('0'),
|
||||
L('3'), L('6'), L('5'), L('7'), L('5'), L('9'), L('5'), L('9'),
|
||||
L('1'), L('9'), L('5'), L('3'), L('0'), L('9'), L('2'), L('1'),
|
||||
L('8'), L('6'), L('1'), L('1'), L('7'), M(234, 4), L('3'), L('2'),
|
||||
M(10, 4), L('9'), L('3'), L('1'), L('0'), L('5'), L('1'), L('1'),
|
||||
L('8'), L('5'), L('4'), L('8'), L('0'), L('7'), M(271, 4), L('3'),
|
||||
L('7'), L('9'), L('9'), L('6'), L('2'), L('7'), L('4'), L('9'),
|
||||
L('5'), L('6'), L('7'), L('3'), L('5'), L('1'), L('8'), L('8'),
|
||||
L('5'), L('7'), L('5'), L('2'), L('7'), L('2'), L('4'), L('8'),
|
||||
L('9'), L('1'), L('2'), L('2'), L('7'), L('9'), L('3'), L('8'),
|
||||
L('1'), L('8'), L('3'), L('0'), L('1'), L('1'), L('9'), L('4'),
|
||||
L('9'), L('1'), L('2'), L('9'), L('8'), L('3'), L('3'), L('6'),
|
||||
L('7'), L('3'), L('3'), L('6'), L('2'), L('4'), L('4'), L('0'),
|
||||
L('6'), L('5'), L('6'), L('6'), L('4'), L('3'), L('0'), L('8'),
|
||||
L('6'), L('0'), L('2'), L('1'), L('3'), L('9'), L('4'), L('9'),
|
||||
L('4'), L('6'), L('3'), L('9'), L('5'), L('2'), L('2'), L('4'),
|
||||
L('7'), L('3'), L('7'), L('1'), L('9'), L('0'), L('7'), L('0'),
|
||||
L('2'), L('1'), L('7'), L('9'), L('8'), M(154, 5), L('7'), L('0'),
|
||||
L('2'), L('7'), L('7'), L('0'), L('5'), L('3'), L('9'), L('2'),
|
||||
L('1'), L('7'), L('1'), L('7'), L('6'), L('2'), L('9'), L('3'),
|
||||
L('1'), L('7'), L('6'), L('7'), L('5'), M(563, 5), L('7'), L('4'),
|
||||
L('8'), L('1'), M(7, 4), L('6'), L('6'), L('9'), L('4'), L('0'),
|
||||
M(488, 4), L('0'), L('0'), L('0'), L('5'), L('6'), L('8'), L('1'),
|
||||
L('2'), L('7'), L('1'), L('4'), L('5'), L('2'), L('6'), L('3'),
|
||||
L('5'), L('6'), L('0'), L('8'), L('2'), L('7'), L('7'), L('8'),
|
||||
L('5'), L('7'), L('7'), L('1'), L('3'), L('4'), L('2'), L('7'),
|
||||
L('5'), L('7'), L('7'), L('8'), L('9'), L('6'), M(298, 4), L('3'),
|
||||
L('6'), L('3'), L('7'), L('1'), L('7'), L('8'), L('7'), L('2'),
|
||||
L('1'), L('4'), L('6'), L('8'), L('4'), L('4'), L('0'), L('9'),
|
||||
L('0'), L('1'), L('2'), L('2'), L('4'), L('9'), L('5'), L('3'),
|
||||
L('4'), L('3'), L('0'), L('1'), L('4'), L('6'), L('5'), L('4'),
|
||||
L('9'), L('5'), L('8'), L('5'), L('3'), L('7'), L('1'), L('0'),
|
||||
L('5'), L('0'), L('7'), L('9'), M(203, 4), L('6'), M(340, 4), L('8'),
|
||||
L('9'), L('2'), L('3'), L('5'), L('4'), M(458, 4), L('9'), L('5'),
|
||||
L('6'), L('1'), L('1'), L('2'), L('1'), L('2'), L('9'), L('0'),
|
||||
L('2'), L('1'), L('9'), L('6'), L('0'), L('8'), L('6'), L('4'),
|
||||
L('0'), L('3'), L('4'), L('4'), L('1'), L('8'), L('1'), L('5'),
|
||||
L('9'), L('8'), L('1'), L('3'), L('6'), L('2'), L('9'), L('7'),
|
||||
L('7'), L('4'), M(117, 4), L('0'), L('9'), L('9'), L('6'), L('0'),
|
||||
L('5'), L('1'), L('8'), L('7'), L('0'), L('7'), L('2'), L('1'),
|
||||
L('1'), L('3'), L('4'), L('9'), M(1, 5), L('8'), L('3'), L('7'),
|
||||
L('2'), L('9'), L('7'), L('8'), L('0'), L('4'), L('9'), L('9'),
|
||||
M(731, 4), L('9'), L('7'), L('3'), L('1'), L('7'), L('3'), L('2'),
|
||||
L('8'), M(395, 4), L('6'), L('3'), L('1'), L('8'), L('5'), M(770, 4),
|
||||
M(745, 4), L('4'), L('5'), L('5'), L('3'), L('4'), L('6'), L('9'),
|
||||
L('0'), L('8'), L('3'), L('0'), L('2'), L('6'), L('4'), L('2'),
|
||||
L('5'), L('2'), L('2'), L('3'), L('0'), M(740, 4), M(616, 4), L('8'),
|
||||
L('5'), L('0'), L('3'), L('5'), L('2'), L('6'), L('1'), L('9'),
|
||||
L('3'), L('1'), L('1'), M(531, 4), L('1'), L('0'), L('1'), L('0'),
|
||||
L('0'), L('0'), L('3'), L('1'), L('3'), L('7'), L('8'), L('3'),
|
||||
L('8'), L('7'), L('5'), L('2'), L('8'), L('8'), L('6'), L('5'),
|
||||
L('8'), L('7'), L('5'), L('3'), L('3'), L('2'), L('0'), L('8'),
|
||||
L('3'), L('8'), L('1'), L('4'), L('2'), L('0'), L('6'), M(321, 4),
|
||||
M(300, 4), L('1'), L('4'), L('7'), L('3'), L('0'), L('3'), L('5'),
|
||||
L('9'), M(815, 5), L('9'), L('0'), L('4'), L('2'), L('8'), L('7'),
|
||||
L('5'), L('5'), L('4'), L('6'), L('8'), L('7'), L('3'), L('1'),
|
||||
L('1'), L('5'), L('9'), L('5'), M(854, 4), L('3'), L('8'), L('8'),
|
||||
L('2'), L('3'), L('5'), L('3'), L('7'), L('8'), L('7'), L('5'),
|
||||
M(896, 5), L('9'), M(315, 4), L('1'), M(329, 4), L('8'), L('0'), L('5'),
|
||||
L('3'), M(395, 4), L('2'), L('2'), L('6'), L('8'), L('0'), L('6'),
|
||||
L('6'), L('1'), L('3'), L('0'), L('0'), L('1'), L('9'), L('2'),
|
||||
L('7'), L('8'), L('7'), L('6'), L('6'), L('1'), L('1'), L('1'),
|
||||
L('9'), L('5'), L('9'), M(568, 4), L('6'), M(293, 5), L('8'), L('9'),
|
||||
L('3'), L('8'), L('0'), L('9'), L('5'), L('2'), L('5'), L('7'),
|
||||
L('2'), L('0'), L('1'), L('0'), L('6'), L('5'), L('4'), L('8'),
|
||||
L('5'), L('8'), L('6'), L('3'), L('2'), L('7'), M(155, 4), L('9'),
|
||||
L('3'), L('6'), L('1'), L('5'), L('3'), M(545, 4), M(349, 5), L('2'),
|
||||
L('3'), L('0'), L('3'), L('0'), L('1'), L('9'), L('5'), L('2'),
|
||||
L('0'), L('3'), L('5'), L('3'), L('0'), L('1'), L('8'), L('5'),
|
||||
L('2'), M(370, 4), M(118, 4), L('3'), L('6'), L('2'), L('2'), L('5'),
|
||||
L('9'), L('9'), L('4'), L('1'), L('3'), M(597, 4), L('4'), L('9'),
|
||||
L('7'), L('2'), L('1'), L('7'), M(223, 4), L('3'), L('4'), L('7'),
|
||||
L('9'), L('1'), L('3'), L('1'), L('5'), L('1'), L('5'), L('5'),
|
||||
L('7'), L('4'), L('8'), L('5'), L('7'), L('2'), L('4'), L('2'),
|
||||
L('4'), L('5'), L('4'), L('1'), L('5'), L('0'), L('6'), L('9'),
|
||||
M(320, 4), L('8'), L('2'), L('9'), L('5'), L('3'), L('3'), L('1'),
|
||||
L('1'), L('6'), L('8'), L('6'), L('1'), L('7'), L('2'), L('7'),
|
||||
L('8'), M(824, 4), L('9'), L('0'), L('7'), L('5'), L('0'), L('9'),
|
||||
M(270, 4), L('7'), L('5'), L('4'), L('6'), L('3'), L('7'), L('4'),
|
||||
L('6'), L('4'), L('9'), L('3'), L('9'), L('3'), L('1'), L('9'),
|
||||
L('2'), L('5'), L('5'), L('0'), L('6'), L('0'), L('4'), L('0'),
|
||||
L('0'), L('9'), M(620, 4), L('1'), L('6'), L('7'), L('1'), L('1'),
|
||||
L('3'), L('9'), L('0'), L('0'), L('9'), L('8'), M(822, 4), L('4'),
|
||||
L('0'), L('1'), L('2'), L('8'), L('5'), L('8'), L('3'), L('6'),
|
||||
L('1'), L('6'), L('0'), L('3'), L('5'), L('6'), L('3'), L('7'),
|
||||
L('0'), L('7'), L('6'), L('6'), L('0'), L('1'), L('0'), L('4'),
|
||||
M(371, 4), L('8'), L('1'), L('9'), L('4'), L('2'), L('9'), M(1055, 5),
|
||||
M(240, 4), M(652, 4), L('7'), L('8'), L('3'), L('7'), L('4'), M(1193, 4),
|
||||
L('8'), L('2'), L('5'), L('5'), L('3'), L('7'), M(522, 5), L('2'),
|
||||
L('6'), L('8'), M(47, 4), L('4'), L('0'), L('4'), L('7'), M(466, 4),
|
||||
L('4'), M(1206, 4), M(910, 4), L('8'), L('4'), M(937, 4), L('6'), M(800, 6),
|
||||
L('3'), L('3'), L('1'), L('3'), L('6'), L('7'), L('7'), L('0'),
|
||||
L('2'), L('8'), L('9'), L('8'), L('9'), L('1'), L('5'), L('2'),
|
||||
M(99, 4), L('5'), L('2'), L('1'), L('6'), L('2'), L('0'), L('5'),
|
||||
L('6'), L('9'), L('6'), M(1042, 4), L('0'), L('5'), L('8'), M(1144, 4),
|
||||
L('5'), M(1177, 4), L('5'), L('1'), L('1'), M(522, 4), L('8'), L('2'),
|
||||
L('4'), L('3'), L('0'), L('0'), L('3'), L('5'), L('5'), L('8'),
|
||||
L('7'), L('6'), L('4'), L('0'), L('2'), L('4'), L('7'), L('4'),
|
||||
L('9'), L('6'), L('4'), L('7'), L('3'), L('2'), L('6'), L('3'),
|
||||
M(1087, 4), L('9'), L('9'), L('2'), M(1100, 4), L('4'), L('2'), L('6'),
|
||||
L('9'), M(710, 6), L('7'), M(471, 4), L('4'), M(1342, 4), M(1054, 4), L('9'),
|
||||
L('3'), L('4'), L('1'), L('7'), M(430, 4), L('1'), L('2'), M(43, 4),
|
||||
L('4'), M(415, 4), L('1'), L('5'), L('0'), L('3'), L('0'), L('2'),
|
||||
L('8'), L('6'), L('1'), L('8'), L('2'), L('9'), L('7'), L('4'),
|
||||
L('5'), L('5'), L('5'), L('7'), L('0'), L('6'), L('7'), L('4'),
|
||||
M(310, 4), L('5'), L('0'), L('5'), L('4'), L('9'), L('4'), L('5'),
|
||||
L('8'), M(454, 4), L('9'), M(82, 4), L('5'), L('6'), M(493, 4), L('7'),
|
||||
L('2'), L('1'), L('0'), L('7'), L('9'), M(346, 4), L('3'), L('0'),
|
||||
M(267, 4), L('3'), L('2'), L('1'), L('1'), L('6'), L('5'), L('3'),
|
||||
L('4'), L('4'), L('9'), L('8'), L('7'), L('2'), L('0'), L('2'),
|
||||
L('7'), M(284, 4), L('0'), L('2'), L('3'), L('6'), L('4'), M(559, 4),
|
||||
L('5'), L('4'), L('9'), L('9'), L('1'), L('1'), L('9'), L('8'),
|
||||
M(1049, 4), L('4'), M(284, 4), L('5'), L('3'), L('5'), L('6'), L('6'),
|
||||
L('3'), L('6'), L('9'), M(1105, 4), L('2'), L('6'), L('5'), M(741, 4),
|
||||
L('7'), L('8'), L('6'), L('2'), L('5'), L('5'), L('1'), M(987, 4),
|
||||
L('1'), L('7'), L('5'), L('7'), L('4'), L('6'), L('7'), L('2'),
|
||||
L('8'), L('9'), L('0'), L('9'), L('7'), L('7'), L('7'), L('7'),
|
||||
M(1108, 5), L('0'), L('0'), L('0'), M(1534, 4), L('7'), L('0'), M(1248, 4),
|
||||
L('6'), M(1002, 4), L('4'), L('9'), L('1'), M(1055, 4), M(664, 4), L('2'),
|
||||
L('1'), L('4'), L('7'), L('7'), L('2'), L('3'), L('5'), L('0'),
|
||||
L('1'), L('4'), L('1'), L('4'), M(1604, 4), L('3'), L('5'), L('6'),
|
||||
M(1200, 4), L('1'), L('6'), L('1'), L('3'), L('6'), L('1'), L('1'),
|
||||
L('5'), L('7'), L('3'), L('5'), L('2'), L('5'), M(1285, 4), L('3'),
|
||||
L('4'), M(92, 4), L('1'), L('8'), M(1148, 4), L('8'), L('4'), M(1512, 4),
|
||||
L('3'), L('3'), L('2'), L('3'), L('9'), L('0'), L('7'), L('3'),
|
||||
L('9'), L('4'), L('1'), L('4'), L('3'), L('3'), L('3'), L('4'),
|
||||
L('5'), L('4'), L('7'), L('7'), L('6'), L('2'), L('4'), M(579, 4),
|
||||
L('2'), L('5'), L('1'), L('8'), L('9'), L('8'), L('3'), L('5'),
|
||||
L('6'), L('9'), L('4'), L('8'), L('5'), L('5'), L('6'), L('2'),
|
||||
L('0'), L('9'), L('9'), L('2'), L('1'), L('9'), L('2'), L('2'),
|
||||
L('2'), L('1'), L('8'), L('4'), L('2'), L('7'), M(575, 4), L('2'),
|
||||
M(187, 4), L('6'), L('8'), L('8'), L('7'), L('6'), L('7'), L('1'),
|
||||
L('7'), L('9'), L('0'), M(86, 4), L('0'), M(263, 5), L('6'), L('6'),
|
||||
M(1000, 4), L('8'), L('8'), L('6'), L('2'), L('7'), L('2'), M(1757, 4),
|
||||
L('1'), L('7'), L('8'), L('6'), L('0'), L('8'), L('5'), L('7'),
|
||||
M(116, 4), L('3'), M(765, 5), L('7'), L('9'), L('7'), L('6'), L('6'),
|
||||
L('8'), L('1'), M(702, 4), L('0'), L('0'), L('9'), L('5'), L('3'),
|
||||
L('8'), L('8'), M(1593, 4), L('3'), M(1702, 4), L('0'), L('6'), L('8'),
|
||||
L('0'), L('0'), L('6'), L('4'), L('2'), L('2'), L('5'), L('1'),
|
||||
L('2'), L('5'), L('2'), M(1404, 4), L('7'), L('3'), L('9'), L('2'),
|
||||
M(664, 4), M(1141, 4), L('4'), M(1716, 5), L('8'), L('6'), L('2'), L('6'),
|
||||
L('9'), L('4'), L('5'), M(486, 4), L('4'), L('1'), L('9'), L('6'),
|
||||
L('5'), L('2'), L('8'), L('5'), L('0'), M(154, 4), M(925, 4), L('1'),
|
||||
L('8'), L('6'), L('3'), M(447, 4), L('4'), M(341, 5), L('2'), L('0'),
|
||||
L('3'), L('9'), M(1420, 4), L('4'), L('5'), M(701, 4), L('2'), L('3'),
|
||||
L('7'), M(1069, 4), L('6'), M(1297, 4), L('5'), L('6'), M(1593, 4), L('7'),
|
||||
L('1'), L('9'), L('1'), L('7'), L('2'), L('8'), M(370, 4), L('7'),
|
||||
L('6'), L('4'), L('6'), L('5'), L('7'), L('5'), L('7'), L('3'),
|
||||
L('9'), M(258, 4), L('3'), L('8'), L('9'), M(1865, 4), L('8'), L('3'),
|
||||
L('2'), L('6'), L('4'), L('5'), L('9'), L('9'), L('5'), L('8'),
|
||||
M(1704, 4), L('0'), L('4'), L('7'), L('8'), M(479, 4), M(809, 4), L('9'),
|
||||
M(46, 4), L('6'), L('4'), L('0'), L('7'), L('8'), L('9'), L('5'),
|
||||
L('1'), M(143, 4), L('6'), L('8'), L('3'), M(304, 4), L('2'), L('5'),
|
||||
L('9'), L('5'), L('7'), L('0'), M(1129, 4), L('8'), L('2'), L('2'),
|
||||
M(713, 4), L('2'), M(1564, 4), L('4'), L('0'), L('7'), L('7'), L('2'),
|
||||
L('6'), L('7'), L('1'), L('9'), L('4'), L('7'), L('8'), M(794, 4),
|
||||
L('8'), L('2'), L('6'), L('0'), L('1'), L('4'), L('7'), L('6'),
|
||||
L('9'), L('9'), L('0'), L('9'), M(1257, 4), L('0'), L('1'), L('3'),
|
||||
L('6'), L('3'), L('9'), L('4'), L('4'), L('3'), M(640, 4), L('3'),
|
||||
L('0'), M(262, 4), L('2'), L('0'), L('3'), L('4'), L('9'), L('6'),
|
||||
L('2'), L('5'), L('2'), L('4'), L('5'), L('1'), L('7'), M(950, 4),
|
||||
L('9'), L('6'), L('5'), L('1'), L('4'), L('3'), L('1'), L('4'),
|
||||
L('2'), L('9'), L('8'), L('0'), L('9'), L('1'), L('9'), L('0'),
|
||||
L('6'), L('5'), L('9'), L('2'), M(643, 4), L('7'), L('2'), L('2'),
|
||||
L('1'), L('6'), L('9'), L('6'), L('4'), L('6'), M(1050, 4), M(123, 4),
|
||||
L('5'), M(1295, 4), L('4'), M(1382, 5), L('8'), M(1370, 4), L('9'), L('7'),
|
||||
M(1404, 4), L('5'), L('4'), M(1182, 4), M(575, 4), L('7'), M(1627, 4), L('8'),
|
||||
L('4'), L('6'), L('8'), L('1'), L('3'), M(141, 4), L('6'), L('8'),
|
||||
L('3'), L('8'), L('6'), L('8'), L('9'), L('4'), L('2'), L('7'),
|
||||
L('7'), L('4'), L('1'), L('5'), L('5'), L('9'), L('9'), L('1'),
|
||||
L('8'), L('5'), M(91, 4), L('2'), L('4'), L('5'), L('9'), L('5'),
|
||||
L('3'), L('9'), L('5'), L('9'), L('4'), L('3'), L('1'), M(1464, 4),
|
||||
L('7'), M(19, 4), L('6'), L('8'), L('0'), L('8'), L('4'), L('5'),
|
||||
M(744, 4), L('7'), L('3'), M(2079, 4), L('9'), L('5'), L('8'), L('4'),
|
||||
L('8'), L('6'), L('5'), L('3'), L('8'), M(1769, 4), L('6'), L('2'),
|
||||
M(243, 4), L('6'), L('0'), L('9'), M(1207, 4), L('6'), L('0'), L('8'),
|
||||
L('0'), L('5'), L('1'), L('2'), L('4'), L('3'), L('8'), L('8'),
|
||||
L('4'), M(315, 4), M(12, 4), L('4'), L('1'), L('3'), M(784, 4), L('7'),
|
||||
L('6'), L('2'), L('7'), L('8'), M(834, 4), L('7'), L('1'), L('5'),
|
||||
M(1436, 4), L('3'), L('5'), L('9'), L('9'), L('7'), L('7'), L('0'),
|
||||
L('0'), L('1'), L('2'), L('9'), M(1139, 4), L('8'), L('9'), L('4'),
|
||||
L('4'), L('1'), M(632, 4), L('6'), L('8'), L('5'), L('5'), M(96, 4),
|
||||
L('4'), L('0'), L('6'), L('3'), M(2279, 4), L('2'), L('0'), L('7'),
|
||||
L('2'), L('2'), M(345, 4), M(516, 5), L('4'), L('8'), L('1'), L('5'),
|
||||
L('8'), M(518, 4), M(511, 4), M(635, 4), M(665, 4), L('3'), L('9'), L('4'),
|
||||
L('5'), L('2'), L('2'), L('6'), L('7'), M(1175, 6), L('8'), M(1419, 4),
|
||||
L('2'), L('1'), M(747, 4), L('2'), M(904, 4), L('5'), L('4'), L('6'),
|
||||
L('6'), L('6'), M(1308, 4), L('2'), L('3'), L('9'), L('8'), L('6'),
|
||||
L('4'), L('5'), L('6'), M(1221, 4), L('1'), L('6'), L('3'), L('5'),
|
||||
M(596, 5), M(2066, 4), L('7'), M(2222, 4), L('9'), L('8'), M(1119, 4), L('9'),
|
||||
L('3'), L('6'), L('3'), L('4'), M(1884, 4), L('7'), L('4'), L('3'),
|
||||
L('2'), L('4'), M(1148, 4), L('1'), L('5'), L('0'), L('7'), L('6'),
|
||||
M(1212, 4), L('7'), L('9'), L('4'), L('5'), L('1'), L('0'), L('9'),
|
||||
M(63, 4), L('0'), L('9'), L('4'), L('0'), M(1703, 4), L('8'), L('8'),
|
||||
L('7'), L('9'), L('7'), L('1'), L('0'), L('8'), L('9'), L('3'),
|
||||
M(2289, 4), L('6'), L('9'), L('1'), L('3'), L('6'), L('8'), L('6'),
|
||||
L('7'), L('2'), M(604, 4), M(511, 4), L('5'), M(1344, 4), M(1129, 4), M(2050, 4),
|
||||
L('1'), L('7'), L('9'), L('2'), L('8'), L('6'), L('8'), M(2253, 4),
|
||||
L('8'), L('7'), L('4'), L('7'), M(1951, 5), L('8'), L('2'), L('4'),
|
||||
M(2427, 4), L('8'), M(604, 4), L('7'), L('1'), L('4'), L('9'), L('0'),
|
||||
L('9'), L('6'), L('7'), L('5'), L('9'), L('8'), M(1776, 4), L('3'),
|
||||
L('6'), L('5'), M(309, 4), L('8'), L('1'), M(93, 4), M(1862, 4), M(2359, 4),
|
||||
L('6'), L('8'), L('2'), L('9'), M(1407, 4), L('8'), L('7'), L('2'),
|
||||
L('2'), L('6'), L('5'), L('8'), L('8'), L('0'), M(1554, 4), L('5'),
|
||||
M(586, 4), L('4'), L('2'), L('7'), L('0'), L('4'), L('7'), L('7'),
|
||||
L('5'), L('5'), M(2079, 4), L('3'), L('7'), L('9'), L('6'), L('4'),
|
||||
L('1'), L('4'), L('5'), L('1'), L('5'), L('2'), M(1534, 4), L('2'),
|
||||
L('3'), L('4'), L('3'), L('6'), L('4'), L('5'), L('4'), M(1503, 4),
|
||||
L('4'), L('4'), L('4'), L('7'), L('9'), L('5'), M(61, 4), M(1316, 4),
|
||||
M(2279, 5), L('4'), L('1'), M(1323, 4), L('3'), M(773, 4), L('5'), L('2'),
|
||||
L('3'), L('1'), M(2114, 5), L('1'), L('6'), L('6'), L('1'), M(2227, 4),
|
||||
L('5'), L('9'), L('6'), L('9'), L('5'), L('3'), L('6'), L('2'),
|
||||
L('3'), L('1'), L('4'), M(1536, 4), L('2'), L('4'), L('8'), L('4'),
|
||||
L('9'), L('3'), L('7'), L('1'), L('8'), L('7'), L('1'), L('1'),
|
||||
L('0'), L('1'), L('4'), L('5'), L('7'), L('6'), L('5'), L('4'),
|
||||
M(1890, 4), L('0'), L('2'), L('7'), L('9'), L('9'), L('3'), L('4'),
|
||||
L('4'), L('0'), L('3'), L('7'), L('4'), L('2'), L('0'), L('0'),
|
||||
L('7'), M(2368, 4), L('7'), L('8'), L('5'), L('3'), L('9'), L('0'),
|
||||
L('6'), L('2'), L('1'), L('9'), M(666, 5), M(838, 4), L('8'), L('4'),
|
||||
L('7'), M(979, 5), L('8'), L('3'), L('3'), L('2'), L('1'), L('4'),
|
||||
L('4'), L('5'), L('7'), L('1'), M(645, 4), M(1911, 4), L('4'), L('3'),
|
||||
L('5'), L('0'), M(2345, 4), M(1129, 4), L('5'), L('3'), L('1'), L('9'),
|
||||
L('1'), L('0'), L('4'), L('8'), L('4'), L('8'), L('1'), L('0'),
|
||||
L('0'), L('5'), L('3'), L('7'), L('0'), L('6'), M(2237, 4), M(1438, 5),
|
||||
M(1922, 5), L('1'), M(1370, 4), L('7'), M(796, 4), L('5'), M(2029, 4), M(1037, 4),
|
||||
L('6'), L('3'), M(2013, 5), L('4'), M(2418, 4), M(847, 5), M(1014, 5), L('8'),
|
||||
M(1326, 5), M(2184, 5), L('9'), M(392, 4), L('9'), L('1'), M(2255, 4), L('8'),
|
||||
L('1'), L('4'), L('6'), L('7'), L('5'), L('1'), M(1580, 4), L('1'),
|
||||
L('2'), L('3'), L('9'), M(426, 6), L('9'), L('0'), L('7'), L('1'),
|
||||
L('8'), L('6'), L('4'), L('9'), L('4'), L('2'), L('3'), L('1'),
|
||||
L('9'), L('6'), L('1'), L('5'), L('6'), M(493, 4), M(1725, 4), L('9'),
|
||||
L('5'), M(2343, 4), M(1130, 4), M(284, 4), L('6'), L('0'), L('3'), L('8'),
|
||||
M(2598, 4), M(368, 4), M(901, 4), L('6'), L('2'), M(1115, 4), L('5'), M(2125, 4),
|
||||
L('6'), L('3'), L('8'), L('9'), L('3'), L('7'), L('7'), L('8'),
|
||||
L('7'), M(2246, 4), M(249, 4), L('9'), L('7'), L('9'), L('2'), L('0'),
|
||||
L('7'), L('7'), L('3'), M(1496, 4), L('2'), L('1'), L('8'), L('2'),
|
||||
L('5'), L('6'), M(2016, 4), L('6'), L('6'), M(1751, 4), L('4'), L('2'),
|
||||
M(1663, 5), L('6'), M(1767, 4), L('4'), L('4'), M(37, 4), L('5'), L('4'),
|
||||
L('9'), L('2'), L('0'), L('2'), L('6'), L('0'), L('5'), M(2740, 4),
|
||||
M(997, 5), L('2'), L('0'), L('1'), L('4'), L('9'), M(1235, 4), L('8'),
|
||||
L('5'), L('0'), L('7'), L('3'), M(1434, 4), L('6'), L('6'), L('6'),
|
||||
L('0'), M(405, 4), L('2'), L('4'), L('3'), L('4'), L('0'), M(136, 4),
|
||||
L('0'), M(1900, 4), L('8'), L('6'), L('3'), M(2391, 4), M(2021, 4), M(1068, 4),
|
||||
M(373, 4), L('5'), L('7'), L('9'), L('6'), L('2'), L('6'), L('8'),
|
||||
L('5'), L('6'), M(321, 4), L('5'), L('0'), L('8'), M(1316, 4), L('5'),
|
||||
L('8'), L('7'), L('9'), L('6'), L('9'), L('9'), M(1810, 4), L('5'),
|
||||
L('7'), L('4'), M(2585, 4), L('8'), L('4'), L('0'), M(2228, 4), L('1'),
|
||||
L('4'), L('5'), L('9'), L('1'), M(1933, 4), L('7'), L('0'), M(565, 4),
|
||||
L('0'), L('1'), M(3048, 4), L('1'), L('2'), M(3189, 4), L('0'), M(964, 4),
|
||||
L('3'), L('9'), M(2859, 4), M(275, 4), L('7'), L('1'), L('5'), M(945, 4),
|
||||
L('4'), L('2'), L('0'), M(3059, 5), L('9'), M(3011, 4), L('0'), L('7'),
|
||||
M(834, 4), M(1942, 4), M(2736, 4), M(3171, 4), L('2'), L('1'), M(2401, 4), L('2'),
|
||||
L('5'), L('1'), M(1404, 4), M(2373, 4), L('9'), L('2'), M(435, 4), L('8'),
|
||||
L('2'), L('6'), M(2919, 4), L('2'), M(633, 4), L('3'), L('2'), L('1'),
|
||||
L('5'), L('7'), L('9'), L('1'), L('9'), L('8'), L('4'), L('1'),
|
||||
L('4'), M(2172, 5), L('9'), L('1'), L('6'), L('4'), M(1769, 5), L('9'),
|
||||
M(2905, 5), M(2268, 4), L('7'), L('2'), L('2'), M(802, 4), L('5'), M(2213, 4),
|
||||
M(322, 4), L('9'), L('1'), L('0'), M(189, 4), M(3164, 4), L('5'), L('2'),
|
||||
L('8'), L('0'), L('1'), L('7'), M(562, 4), L('7'), L('1'), L('2'),
|
||||
M(2325, 4), L('8'), L('3'), L('2'), M(884, 4), L('1'), M(1418, 4), L('0'),
|
||||
L('9'), L('3'), L('5'), L('3'), L('9'), L('6'), L('5'), L('7'),
|
||||
M(1612, 4), L('1'), L('0'), L('8'), L('3'), M(106, 4), L('5'), L('1'),
|
||||
M(1915, 4), M(3419, 4), L('1'), L('4'), L('4'), L('4'), L('2'), L('1'),
|
||||
L('0'), L('0'), M(515, 4), L('0'), L('3'), M(413, 4), L('1'), L('1'),
|
||||
L('0'), L('3'), M(3202, 4), M(10, 4), M(39, 4), M(1539, 6), L('5'), L('1'),
|
||||
L('6'), M(1498, 4), M(2180, 5), M(2347, 4), L('5'), M(3139, 5), L('8'), L('5'),
|
||||
L('1'), L('7'), L('1'), L('4'), L('3'), L('7'), M(1542, 4), M(110, 4),
|
||||
L('1'), L('5'), L('5'), L('6'), L('5'), L('0'), L('8'), L('8'),
|
||||
M(954, 4), L('9'), L('8'), L('9'), L('8'), L('5'), L('9'), L('9'),
|
||||
L('8'), L('2'), L('3'), L('8'), M(464, 4), M(2491, 4), L('3'), M(365, 4),
|
||||
M(1087, 4), M(2500, 4), L('8'), M(3590, 5), L('3'), L('2'), M(264, 4), L('5'),
|
||||
M(774, 4), L('3'), M(459, 4), L('9'), M(1052, 4), L('9'), L('8'), M(2174, 4),
|
||||
L('4'), M(3257, 4), L('7'), M(1612, 4), L('0'), L('7'), M(230, 4), L('4'),
|
||||
L('8'), L('1'), L('4'), L('1'), M(1338, 4), L('8'), L('5'), L('9'),
|
||||
L('4'), L('6'), L('1'), M(3018, 4), L('8'), L('0'),
|
||||
},
|
||||
},
|
||||
TestCase{
|
||||
.input = "huffman-rand-1k.input",
|
||||
.want = "huffman-rand-1k.{s}.expect",
|
||||
.want_no_input = "huffman-rand-1k.{s}.expect-noinput",
|
||||
.tokens = &[_]Token{
|
||||
L(0xf8), L(0x8b), L(0x96), L(0x76), L(0x48), L(0xd), L(0x85), L(0x94), L(0x25), L(0x80), L(0xaf), L(0xc2), L(0xfe), L(0x8d),
|
||||
L(0xe8), L(0x20), L(0xeb), L(0x17), L(0x86), L(0xc9), L(0xb7), L(0xc5), L(0xde), L(0x6), L(0xea), L(0x7d), L(0x18), L(0x8b),
|
||||
L(0xe7), L(0x3e), L(0x7), L(0xda), L(0xdf), L(0xff), L(0x6c), L(0x73), L(0xde), L(0xcc), L(0xe7), L(0x6d), L(0x8d), L(0x4),
|
||||
L(0x19), L(0x49), L(0x7f), L(0x47), L(0x1f), L(0x48), L(0x15), L(0xb0), L(0xe8), L(0x9e), L(0xf2), L(0x31), L(0x59), L(0xde),
|
||||
L(0x34), L(0xb4), L(0x5b), L(0xe5), L(0xe0), L(0x9), L(0x11), L(0x30), L(0xc2), L(0x88), L(0x5b), L(0x7c), L(0x5d), L(0x14),
|
||||
L(0x13), L(0x6f), L(0x23), L(0xa9), L(0xd), L(0xbc), L(0x2d), L(0x23), L(0xbe), L(0xd9), L(0xed), L(0x75), L(0x4), L(0x6c),
|
||||
L(0x99), L(0xdf), L(0xfd), L(0x70), L(0x66), L(0xe6), L(0xee), L(0xd9), L(0xb1), L(0x9e), L(0x6e), L(0x83), L(0x59), L(0xd5),
|
||||
L(0xd4), L(0x80), L(0x59), L(0x98), L(0x77), L(0x89), L(0x43), L(0x38), L(0xc9), L(0xaf), L(0x30), L(0x32), L(0x9a), L(0x20),
|
||||
L(0x1b), L(0x46), L(0x3d), L(0x67), L(0x6e), L(0xd7), L(0x72), L(0x9e), L(0x4e), L(0x21), L(0x4f), L(0xc6), L(0xe0), L(0xd4),
|
||||
L(0x7b), L(0x4), L(0x8d), L(0xa5), L(0x3), L(0xf6), L(0x5), L(0x9b), L(0x6b), L(0xdc), L(0x2a), L(0x93), L(0x77), L(0x28),
|
||||
L(0xfd), L(0xb4), L(0x62), L(0xda), L(0x20), L(0xe7), L(0x1f), L(0xab), L(0x6b), L(0x51), L(0x43), L(0x39), L(0x2f), L(0xa0),
|
||||
L(0x92), L(0x1), L(0x6c), L(0x75), L(0x3e), L(0xf4), L(0x35), L(0xfd), L(0x43), L(0x2e), L(0xf7), L(0xa4), L(0x75), L(0xda),
|
||||
L(0xea), L(0x9b), L(0xa), L(0x64), L(0xb), L(0xe0), L(0x23), L(0x29), L(0xbd), L(0xf7), L(0xe7), L(0x83), L(0x3c), L(0xfb),
|
||||
L(0xdf), L(0xb3), L(0xae), L(0x4f), L(0xa4), L(0x47), L(0x55), L(0x99), L(0xde), L(0x2f), L(0x96), L(0x6e), L(0x1c), L(0x43),
|
||||
L(0x4c), L(0x87), L(0xe2), L(0x7c), L(0xd9), L(0x5f), L(0x4c), L(0x7c), L(0xe8), L(0x90), L(0x3), L(0xdb), L(0x30), L(0x95),
|
||||
L(0xd6), L(0x22), L(0xc), L(0x47), L(0xb8), L(0x4d), L(0x6b), L(0xbd), L(0x24), L(0x11), L(0xab), L(0x2c), L(0xd7), L(0xbe),
|
||||
L(0x6e), L(0x7a), L(0xd6), L(0x8), L(0xa3), L(0x98), L(0xd8), L(0xdd), L(0x15), L(0x6a), L(0xfa), L(0x93), L(0x30), L(0x1),
|
||||
L(0x25), L(0x1d), L(0xa2), L(0x74), L(0x86), L(0x4b), L(0x6a), L(0x95), L(0xe8), L(0xe1), L(0x4e), L(0xe), L(0x76), L(0xb9),
|
||||
L(0x49), L(0xa9), L(0x5f), L(0xa0), L(0xa6), L(0x63), L(0x3c), L(0x7e), L(0x7e), L(0x20), L(0x13), L(0x4f), L(0xbb), L(0x66),
|
||||
L(0x92), L(0xb8), L(0x2e), L(0xa4), L(0xfa), L(0x48), L(0xcb), L(0xae), L(0xb9), L(0x3c), L(0xaf), L(0xd3), L(0x1f), L(0xe1),
|
||||
L(0xd5), L(0x8d), L(0x42), L(0x6d), L(0xf0), L(0xfc), L(0x8c), L(0xc), L(0x0), L(0xde), L(0x40), L(0xab), L(0x8b), L(0x47),
|
||||
L(0x97), L(0x4e), L(0xa8), L(0xcf), L(0x8e), L(0xdb), L(0xa6), L(0x8b), L(0x20), L(0x9), L(0x84), L(0x7a), L(0x66), L(0xe5),
|
||||
L(0x98), L(0x29), L(0x2), L(0x95), L(0xe6), L(0x38), L(0x32), L(0x60), L(0x3), L(0xe3), L(0x9a), L(0x1e), L(0x54), L(0xe8),
|
||||
L(0x63), L(0x80), L(0x48), L(0x9c), L(0xe7), L(0x63), L(0x33), L(0x6e), L(0xa0), L(0x65), L(0x83), L(0xfa), L(0xc6), L(0xba),
|
||||
L(0x7a), L(0x43), L(0x71), L(0x5), L(0xf5), L(0x68), L(0x69), L(0x85), L(0x9c), L(0xba), L(0x45), L(0xcd), L(0x6b), L(0xb),
|
||||
L(0x19), L(0xd1), L(0xbb), L(0x7f), L(0x70), L(0x85), L(0x92), L(0xd1), L(0xb4), L(0x64), L(0x82), L(0xb1), L(0xe4), L(0x62),
|
||||
L(0xc5), L(0x3c), L(0x46), L(0x1f), L(0x92), L(0x31), L(0x1c), L(0x4e), L(0x41), L(0x77), L(0xf7), L(0xe7), L(0x87), L(0xa2),
|
||||
L(0xf), L(0x6e), L(0xe8), L(0x92), L(0x3), L(0x6b), L(0xa), L(0xe7), L(0xa9), L(0x3b), L(0x11), L(0xda), L(0x66), L(0x8a),
|
||||
L(0x29), L(0xda), L(0x79), L(0xe1), L(0x64), L(0x8d), L(0xe3), L(0x54), L(0xd4), L(0xf5), L(0xef), L(0x64), L(0x87), L(0x3b),
|
||||
L(0xf4), L(0xc2), L(0xf4), L(0x71), L(0x13), L(0xa9), L(0xe9), L(0xe0), L(0xa2), L(0x6), L(0x14), L(0xab), L(0x5d), L(0xa7),
|
||||
L(0x96), L(0x0), L(0xd6), L(0xc3), L(0xcc), L(0x57), L(0xed), L(0x39), L(0x6a), L(0x25), L(0xcd), L(0x76), L(0xea), L(0xba),
|
||||
L(0x3a), L(0xf2), L(0xa1), L(0x95), L(0x5d), L(0xe5), L(0x71), L(0xcf), L(0x9c), L(0x62), L(0x9e), L(0x6a), L(0xfa), L(0xd5),
|
||||
L(0x31), L(0xd1), L(0xa8), L(0x66), L(0x30), L(0x33), L(0xaa), L(0x51), L(0x17), L(0x13), L(0x82), L(0x99), L(0xc8), L(0x14),
|
||||
L(0x60), L(0x9f), L(0x4d), L(0x32), L(0x6d), L(0xda), L(0x19), L(0x26), L(0x21), L(0xdc), L(0x7e), L(0x2e), L(0x25), L(0x67),
|
||||
L(0x72), L(0xca), L(0xf), L(0x92), L(0xcd), L(0xf6), L(0xd6), L(0xcb), L(0x97), L(0x8a), L(0x33), L(0x58), L(0x73), L(0x70),
|
||||
L(0x91), L(0x1d), L(0xbf), L(0x28), L(0x23), L(0xa3), L(0xc), L(0xf1), L(0x83), L(0xc3), L(0xc8), L(0x56), L(0x77), L(0x68),
|
||||
L(0xe3), L(0x82), L(0xba), L(0xb9), L(0x57), L(0x56), L(0x57), L(0x9c), L(0xc3), L(0xd6), L(0x14), L(0x5), L(0x3c), L(0xb1),
|
||||
L(0xaf), L(0x93), L(0xc8), L(0x8a), L(0x57), L(0x7f), L(0x53), L(0xfa), L(0x2f), L(0xaa), L(0x6e), L(0x66), L(0x83), L(0xfa),
|
||||
L(0x33), L(0xd1), L(0x21), L(0xab), L(0x1b), L(0x71), L(0xb4), L(0x7c), L(0xda), L(0xfd), L(0xfb), L(0x7f), L(0x20), L(0xab),
|
||||
L(0x5e), L(0xd5), L(0xca), L(0xfd), L(0xdd), L(0xe0), L(0xee), L(0xda), L(0xba), L(0xa8), L(0x27), L(0x99), L(0x97), L(0x69),
|
||||
L(0xc1), L(0x3c), L(0x82), L(0x8c), L(0xa), L(0x5c), L(0x2d), L(0x5b), L(0x88), L(0x3e), L(0x34), L(0x35), L(0x86), L(0x37),
|
||||
L(0x46), L(0x79), L(0xe1), L(0xaa), L(0x19), L(0xfb), L(0xaa), L(0xde), L(0x15), L(0x9), L(0xd), L(0x1a), L(0x57), L(0xff),
|
||||
L(0xb5), L(0xf), L(0xf3), L(0x2b), L(0x5a), L(0x6a), L(0x4d), L(0x19), L(0x77), L(0x71), L(0x45), L(0xdf), L(0x4f), L(0xb3),
|
||||
L(0xec), L(0xf1), L(0xeb), L(0x18), L(0x53), L(0x3e), L(0x3b), L(0x47), L(0x8), L(0x9a), L(0x73), L(0xa0), L(0x5c), L(0x8c),
|
||||
L(0x5f), L(0xeb), L(0xf), L(0x3a), L(0xc2), L(0x43), L(0x67), L(0xb4), L(0x66), L(0x67), L(0x80), L(0x58), L(0xe), L(0xc1),
|
||||
L(0xec), L(0x40), L(0xd4), L(0x22), L(0x94), L(0xca), L(0xf9), L(0xe8), L(0x92), L(0xe4), L(0x69), L(0x38), L(0xbe), L(0x67),
|
||||
L(0x64), L(0xca), L(0x50), L(0xc7), L(0x6), L(0x67), L(0x42), L(0x6e), L(0xa3), L(0xf0), L(0xb7), L(0x6c), L(0xf2), L(0xe8),
|
||||
L(0x5f), L(0xb1), L(0xaf), L(0xe7), L(0xdb), L(0xbb), L(0x77), L(0xb5), L(0xf8), L(0xcb), L(0x8), L(0xc4), L(0x75), L(0x7e),
|
||||
L(0xc0), L(0xf9), L(0x1c), L(0x7f), L(0x3c), L(0x89), L(0x2f), L(0xd2), L(0x58), L(0x3a), L(0xe2), L(0xf8), L(0x91), L(0xb6),
|
||||
L(0x7b), L(0x24), L(0x27), L(0xe9), L(0xae), L(0x84), L(0x8b), L(0xde), L(0x74), L(0xac), L(0xfd), L(0xd9), L(0xb7), L(0x69),
|
||||
L(0x2a), L(0xec), L(0x32), L(0x6f), L(0xf0), L(0x92), L(0x84), L(0xf1), L(0x40), L(0xc), L(0x8a), L(0xbc), L(0x39), L(0x6e),
|
||||
L(0x2e), L(0x73), L(0xd4), L(0x6e), L(0x8a), L(0x74), L(0x2a), L(0xdc), L(0x60), L(0x1f), L(0xa3), L(0x7), L(0xde), L(0x75),
|
||||
L(0x8b), L(0x74), L(0xc8), L(0xfe), L(0x63), L(0x75), L(0xf6), L(0x3d), L(0x63), L(0xac), L(0x33), L(0x89), L(0xc3), L(0xf0),
|
||||
L(0xf8), L(0x2d), L(0x6b), L(0xb4), L(0x9e), L(0x74), L(0x8b), L(0x5c), L(0x33), L(0xb4), L(0xca), L(0xa8), L(0xe4), L(0x99),
|
||||
L(0xb6), L(0x90), L(0xa1), L(0xef), L(0xf), L(0xd3), L(0x61), L(0xb2), L(0xc6), L(0x1a), L(0x94), L(0x7c), L(0x44), L(0x55),
|
||||
L(0xf4), L(0x45), L(0xff), L(0x9e), L(0xa5), L(0x5a), L(0xc6), L(0xa0), L(0xe8), L(0x2a), L(0xc1), L(0x8d), L(0x6f), L(0x34),
|
||||
L(0x11), L(0xb9), L(0xbe), L(0x4e), L(0xd9), L(0x87), L(0x97), L(0x73), L(0xcf), L(0x3d), L(0x23), L(0xae), L(0xd5), L(0x1a),
|
||||
L(0x5e), L(0xae), L(0x5d), L(0x6a), L(0x3), L(0xf9), L(0x22), L(0xd), L(0x10), L(0xd9), L(0x47), L(0x69), L(0x15), L(0x3f),
|
||||
L(0xee), L(0x52), L(0xa3), L(0x8), L(0xd2), L(0x3c), L(0x51), L(0xf4), L(0xf8), L(0x9d), L(0xe4), L(0x98), L(0x89), L(0xc8),
|
||||
L(0x67), L(0x39), L(0xd5), L(0x5e), L(0x35), L(0x78), L(0x27), L(0xe8), L(0x3c), L(0x80), L(0xae), L(0x79), L(0x71), L(0xd2),
|
||||
L(0x93), L(0xf4), L(0xaa), L(0x51), L(0x12), L(0x1c), L(0x4b), L(0x1b), L(0xe5), L(0x6e), L(0x15), L(0x6f), L(0xe4), L(0xbb),
|
||||
L(0x51), L(0x9b), L(0x45), L(0x9f), L(0xf9), L(0xc4), L(0x8c), L(0x2a), L(0xfb), L(0x1a), L(0xdf), L(0x55), L(0xd3), L(0x48),
|
||||
L(0x93), L(0x27), L(0x1), L(0x26), L(0xc2), L(0x6b), L(0x55), L(0x6d), L(0xa2), L(0xfb), L(0x84), L(0x8b), L(0xc9), L(0x9e),
|
||||
L(0x28), L(0xc2), L(0xef), L(0x1a), L(0x24), L(0xec), L(0x9b), L(0xae), L(0xbd), L(0x60), L(0xe9), L(0x15), L(0x35), L(0xee),
|
||||
L(0x42), L(0xa4), L(0x33), L(0x5b), L(0xfa), L(0xf), L(0xb6), L(0xf7), L(0x1), L(0xa6), L(0x2), L(0x4c), L(0xca), L(0x90),
|
||||
L(0x58), L(0x3a), L(0x96), L(0x41), L(0xe7), L(0xcb), L(0x9), L(0x8c), L(0xdb), L(0x85), L(0x4d), L(0xa8), L(0x89), L(0xf3),
|
||||
L(0xb5), L(0x8e), L(0xfd), L(0x75), L(0x5b), L(0x4f), L(0xed), L(0xde), L(0x3f), L(0xeb), L(0x38), L(0xa3), L(0xbe), L(0xb0),
|
||||
L(0x73), L(0xfc), L(0xb8), L(0x54), L(0xf7), L(0x4c), L(0x30), L(0x67), L(0x2e), L(0x38), L(0xa2), L(0x54), L(0x18), L(0xba),
|
||||
L(0x8), L(0xbf), L(0xf2), L(0x39), L(0xd5), L(0xfe), L(0xa5), L(0x41), L(0xc6), L(0x66), L(0x66), L(0xba), L(0x81), L(0xef),
|
||||
L(0x67), L(0xe4), L(0xe6), L(0x3c), L(0xc), L(0xca), L(0xa4), L(0xa), L(0x79), L(0xb3), L(0x57), L(0x8b), L(0x8a), L(0x75),
|
||||
L(0x98), L(0x18), L(0x42), L(0x2f), L(0x29), L(0xa3), L(0x82), L(0xef), L(0x9f), L(0x86), L(0x6), L(0x23), L(0xe1), L(0x75),
|
||||
L(0xfa), L(0x8), L(0xb1), L(0xde), L(0x17), L(0x4a),
|
||||
},
|
||||
},
|
||||
TestCase{
|
||||
.input = "huffman-rand-limit.input",
|
||||
.want = "huffman-rand-limit.{s}.expect",
|
||||
.want_no_input = "huffman-rand-limit.{s}.expect-noinput",
|
||||
.tokens = &[_]Token{
|
||||
L(0x61), M(1, 74), L(0xa), L(0xf8), L(0x8b), L(0x96), L(0x76), L(0x48), L(0xa), L(0x85), L(0x94), L(0x25), L(0x80),
|
||||
L(0xaf), L(0xc2), L(0xfe), L(0x8d), L(0xe8), L(0x20), L(0xeb), L(0x17), L(0x86), L(0xc9), L(0xb7), L(0xc5), L(0xde),
|
||||
L(0x6), L(0xea), L(0x7d), L(0x18), L(0x8b), L(0xe7), L(0x3e), L(0x7), L(0xda), L(0xdf), L(0xff), L(0x6c), L(0x73),
|
||||
L(0xde), L(0xcc), L(0xe7), L(0x6d), L(0x8d), L(0x4), L(0x19), L(0x49), L(0x7f), L(0x47), L(0x1f), L(0x48), L(0x15),
|
||||
L(0xb0), L(0xe8), L(0x9e), L(0xf2), L(0x31), L(0x59), L(0xde), L(0x34), L(0xb4), L(0x5b), L(0xe5), L(0xe0), L(0x9),
|
||||
L(0x11), L(0x30), L(0xc2), L(0x88), L(0x5b), L(0x7c), L(0x5d), L(0x14), L(0x13), L(0x6f), L(0x23), L(0xa9), L(0xa),
|
||||
L(0xbc), L(0x2d), L(0x23), L(0xbe), L(0xd9), L(0xed), L(0x75), L(0x4), L(0x6c), L(0x99), L(0xdf), L(0xfd), L(0x70),
|
||||
L(0x66), L(0xe6), L(0xee), L(0xd9), L(0xb1), L(0x9e), L(0x6e), L(0x83), L(0x59), L(0xd5), L(0xd4), L(0x80), L(0x59),
|
||||
L(0x98), L(0x77), L(0x89), L(0x43), L(0x38), L(0xc9), L(0xaf), L(0x30), L(0x32), L(0x9a), L(0x20), L(0x1b), L(0x46),
|
||||
L(0x3d), L(0x67), L(0x6e), L(0xd7), L(0x72), L(0x9e), L(0x4e), L(0x21), L(0x4f), L(0xc6), L(0xe0), L(0xd4), L(0x7b),
|
||||
L(0x4), L(0x8d), L(0xa5), L(0x3), L(0xf6), L(0x5), L(0x9b), L(0x6b), L(0xdc), L(0x2a), L(0x93), L(0x77), L(0x28),
|
||||
L(0xfd), L(0xb4), L(0x62), L(0xda), L(0x20), L(0xe7), L(0x1f), L(0xab), L(0x6b), L(0x51), L(0x43), L(0x39), L(0x2f),
|
||||
L(0xa0), L(0x92), L(0x1), L(0x6c), L(0x75), L(0x3e), L(0xf4), L(0x35), L(0xfd), L(0x43), L(0x2e), L(0xf7), L(0xa4),
|
||||
L(0x75), L(0xda), L(0xea), L(0x9b), L(0xa),
|
||||
},
|
||||
},
|
||||
TestCase{
|
||||
.input = "huffman-shifts.input",
|
||||
.want = "huffman-shifts.{s}.expect",
|
||||
.want_no_input = "huffman-shifts.{s}.expect-noinput",
|
||||
.tokens = &[_]Token{
|
||||
L('1'), L('0'), M(2, 258), M(2, 258), M(2, 258), M(2, 258), M(2, 258), M(2, 258),
|
||||
M(2, 258), M(2, 258), M(2, 258), M(2, 258), M(2, 258), M(2, 258), M(2, 258), M(2, 258),
|
||||
M(2, 258), M(2, 76), L(0xd), L(0xa), L('2'), L('3'), M(2, 258), M(2, 258),
|
||||
M(2, 258), M(2, 258), M(2, 258), M(2, 258), M(2, 258), M(2, 258), M(2, 258), M(2, 256),
|
||||
},
|
||||
},
|
||||
TestCase{
|
||||
.input = "huffman-text-shift.input",
|
||||
.want = "huffman-text-shift.{s}.expect",
|
||||
.want_no_input = "huffman-text-shift.{s}.expect-noinput",
|
||||
.tokens = &[_]Token{
|
||||
L('/'), L('/'), L('C'), L('o'), L('p'), L('y'), L('r'), L('i'),
|
||||
L('g'), L('h'), L('t'), L('2'), L('0'), L('0'), L('9'), L('T'),
|
||||
L('h'), L('G'), L('o'), L('A'), L('u'), L('t'), L('h'), L('o'),
|
||||
L('r'), L('.'), L('A'), L('l'), L('l'), M(23, 5), L('r'), L('r'),
|
||||
L('v'), L('d'), L('.'), L(0xd), L(0xa), L('/'), L('/'), L('U'),
|
||||
L('o'), L('f'), L('t'), L('h'), L('i'), L('o'), L('u'), L('r'),
|
||||
L('c'), L('c'), L('o'), L('d'), L('i'), L('g'), L('o'), L('v'),
|
||||
L('r'), L('n'), L('d'), L('b'), L('y'), L('B'), L('S'), L('D'),
|
||||
L('-'), L('t'), L('y'), L('l'), M(33, 4), L('l'), L('i'), L('c'),
|
||||
L('n'), L('t'), L('h'), L('t'), L('c'), L('n'), L('b'), L('f'),
|
||||
L('o'), L('u'), L('n'), L('d'), L('i'), L('n'), L('t'), L('h'),
|
||||
L('L'), L('I'), L('C'), L('E'), L('N'), L('S'), L('E'), L('f'),
|
||||
L('i'), L('l'), L('.'), L(0xd), L(0xa), L(0xd), L(0xa), L('p'),
|
||||
L('c'), L('k'), L('g'), L('m'), L('i'), L('n'), M(11, 4), L('i'),
|
||||
L('m'), L('p'), L('o'), L('r'), L('t'), L('"'), L('o'), L('"'),
|
||||
M(13, 4), L('f'), L('u'), L('n'), L('c'), L('m'), L('i'), L('n'),
|
||||
L('('), L(')'), L('{'), L(0xd), L(0xa), L(0x9), L('v'), L('r'),
|
||||
L('b'), L('='), L('m'), L('k'), L('('), L('['), L(']'), L('b'),
|
||||
L('y'), L('t'), L(','), L('6'), L('5'), L('5'), L('3'), L('5'),
|
||||
L(')'), L(0xd), L(0xa), L(0x9), L('f'), L(','), L('_'), L(':'),
|
||||
L('='), L('o'), L('.'), L('C'), L('r'), L('t'), L('('), L('"'),
|
||||
L('h'), L('u'), L('f'), L('f'), L('m'), L('n'), L('-'), L('n'),
|
||||
L('u'), L('l'), L('l'), L('-'), L('m'), L('x'), L('.'), L('i'),
|
||||
L('n'), L('"'), M(34, 5), L('.'), L('W'), L('r'), L('i'), L('t'),
|
||||
L('('), L('b'), L(')'), L(0xd), L(0xa), L('}'), L(0xd), L(0xa),
|
||||
L('A'), L('B'), L('C'), L('D'), L('E'), L('F'), L('G'), L('H'),
|
||||
L('I'), L('J'), L('K'), L('L'), L('M'), L('N'), L('O'), L('P'),
|
||||
L('Q'), L('R'), L('S'), L('T'), L('U'), L('V'), L('X'), L('x'),
|
||||
L('y'), L('z'), L('!'), L('"'), L('#'), L(0xc2), L(0xa4), L('%'),
|
||||
L('&'), L('/'), L('?'), L('"'),
|
||||
},
|
||||
},
|
||||
TestCase{
|
||||
.input = "huffman-text.input",
|
||||
.want = "huffman-text.{s}.expect",
|
||||
.want_no_input = "huffman-text.{s}.expect-noinput",
|
||||
.tokens = &[_]Token{
|
||||
L('/'), L('/'), L(' '), L('z'), L('i'), L('g'), L(' '), L('v'),
|
||||
L('0'), L('.'), L('1'), L('0'), L('.'), L('0'), L(0xa), L('/'),
|
||||
L('/'), L(' '), L('c'), L('r'), L('e'), L('a'), L('t'), L('e'),
|
||||
L(' '), L('a'), L(' '), L('f'), L('i'), L('l'), L('e'), M(5, 4),
|
||||
L('l'), L('e'), L('d'), L(' '), L('w'), L('i'), L('t'), L('h'),
|
||||
L(' '), L('0'), L('x'), L('0'), L('0'), L(0xa), L('c'), L('o'),
|
||||
L('n'), L('s'), L('t'), L(' '), L('s'), L('t'), L('d'), L(' '),
|
||||
L('='), L(' '), L('@'), L('i'), L('m'), L('p'), L('o'), L('r'),
|
||||
L('t'), L('('), L('"'), L('s'), L('t'), L('d'), L('"'), L(')'),
|
||||
L(';'), L(0xa), L(0xa), L('p'), L('u'), L('b'), L(' '), L('f'),
|
||||
L('n'), L(' '), L('m'), L('a'), L('i'), L('n'), L('('), L(')'),
|
||||
L(' '), L('!'), L('v'), L('o'), L('i'), L('d'), L(' '), L('{'),
|
||||
L(0xa), L(' '), L(' '), L(' '), L(' '), L('v'), L('a'), L('r'),
|
||||
L(' '), L('b'), L(' '), L('='), L(' '), L('['), L('1'), L(']'),
|
||||
L('u'), L('8'), L('{'), L('0'), L('}'), L(' '), L('*'), L('*'),
|
||||
L(' '), L('6'), L('5'), L('5'), L('3'), L('5'), L(';'), M(31, 5),
|
||||
M(86, 6), L('f'), L(' '), L('='), L(' '), L('t'), L('r'), L('y'),
|
||||
M(94, 4), L('.'), L('f'), L('s'), L('.'), L('c'), L('w'), L('d'),
|
||||
L('('), L(')'), L('.'), M(144, 6), L('F'), L('i'), L('l'), L('e'),
|
||||
L('('), M(43, 5), M(1, 4), L('"'), L('h'), L('u'), L('f'), L('f'),
|
||||
L('m'), L('a'), L('n'), L('-'), L('n'), L('u'), L('l'), L('l'),
|
||||
L('-'), L('m'), L('a'), L('x'), L('.'), L('i'), L('n'), L('"'),
|
||||
L(','), M(31, 9), L('.'), L('{'), L(' '), L('.'), L('r'), L('e'),
|
||||
L('a'), L('d'), M(79, 5), L('u'), L('e'), L(' '), L('}'), M(27, 6),
|
||||
L(')'), M(108, 6), L('d'), L('e'), L('f'), L('e'), L('r'), L(' '),
|
||||
L('f'), L('.'), L('c'), L('l'), L('o'), L('s'), L('e'), L('('),
|
||||
M(183, 4), M(22, 4), L('_'), M(124, 7), L('f'), L('.'), L('w'), L('r'),
|
||||
L('i'), L('t'), L('e'), L('A'), L('l'), L('l'), L('('), L('b'),
|
||||
L('['), L('0'), L('.'), L('.'), L(']'), L(')'), L(';'), L(0xa),
|
||||
L('}'), L(0xa),
|
||||
},
|
||||
},
|
||||
TestCase{
|
||||
.input = "huffman-zero.input",
|
||||
.want = "huffman-zero.{s}.expect",
|
||||
.want_no_input = "huffman-zero.{s}.expect-noinput",
|
||||
.tokens = &[_]Token{ L(0x30), ml, M(1, 49) },
|
||||
},
|
||||
TestCase{
|
||||
.input = "",
|
||||
.want = "",
|
||||
.want_no_input = "null-long-match.{s}.expect-noinput",
|
||||
.tokens = &[_]Token{
|
||||
L(0x0), ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml, ml,
|
||||
ml, ml, ml, M(1, 8),
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1 +0,0 @@
|
||||
3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930381964428810975665933446128475648233786783165271201909145648566923460348610454326648213393607260249141273724587006606315588174881520920962829254091715364367892590360011330530548820466521384146951941511609433057270365759591953092186117381932611793105118548074462379962749567351885752724891227938183011949129833673362440656643086021394946395224737190702179860943702770539217176293176752384674818467669405132000568127145263560827785771342757789609173637178721468440901224953430146549585371050792279689258923542019956112129021960864034418159813629774771309960518707211349999998372978049951059731732816096318595024459455346908302642522308253344685035261931188171010003137838752886587533208381420617177669147303598253490428755468731159562863882353787593751957781857780532171226806613001927876611195909216420198938095257201065485863278865936153381827968230301952035301852968995773622599413891249721775283479131515574857242454150695950829533116861727855889075098381754637464939319255060400927701671139009848824012858361603563707660104710181942955596198946767837449448255379774726847104047534646208046684259069491293313677028989152104752162056966024058038150193511253382430035587640247496473263914199272604269922796782354781636009341721641219924586315030286182974555706749838505494588586926995690927210797509302955321165344987202755960236480665499119881834797753566369807426542527862551818417574672890977772793800081647060016145249192173217214772350141441973568548161361157352552133475741849468438523323907394143334547762416862518983569485562099219222184272550254256887671790494601653466804988627232791786085784383827967976681454100953883786360950680064225125205117392984896084128488626945604241965285022210661186306744278622039194945047123713786960956364371917287467764657573962413890865832645995813390478027590099465764078951269468398352595709825822620522489407726719478268482601476990902640136394437455305068203496252451749399651431429809190659250937221696461515709858387410597885959772975498930161753928468138268683868942774155991855925245953959431049972524680845987273644695848653836736222626099124608051243884390451244136549762780797715691435997700129616089441694868555848406353422072225828488648158456028506016842739452267467678895252138522549954666727823986456596116354886230577456498035593634568174324112515076069479451096596094025228879710893145669136867228748940560101503308617928680920874760917824938589009714909675985261365549781893129784821682998948722658804857564014270477555132379641451523746234364542858444795265867821051141354735739523113427166102135969536231442952484937187110145765403590279934403742007310578539062198387447808478489683321445713868751943506430218453191048481005370614680674919278191197939952061419663428754440643745123718192179998391015919561814675142691239748940907186494231961567945208095146550225231603881930142093762137855956638937787083039069792077346722182562599661501421503068038447734549202605414665925201497442850732518666002132434088190710486331734649651453905796268561005508106658796998163574736384052571459102897064140110971206280439039759515677157700420337869936007230558763176359421873125147120532928191826186125867321579198414848829164470609575270695722091756711672291098169091528017350671274858322287183520935396572512108357915136988209144421006751033467110314126711136990865851639831501970165151168517143765761835155650884909989859982387345528331635507647918535893226185489632132933089857064204675259070915481416549859461637180
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,4 +0,0 @@
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
ř‹–vH
|
||||
…”%€ŻÂţŤč ë†É·ĹŢę}‹ç>Úß˙lsŢĚçmŤIGH°čžň1YŢ4´[ĺŕ 0Â<30>[|]o#©
|
||||
Ľ-#ľŮíul™ßýpfćîٱžn<C5BE>YŐÔ€Y<E282AC>w‰C8ÉŻ02š F=gn×ržN!OĆŕÔ{ŤĄö›kÜ*“w(ý´bÚ ç«kQC9/ ’lu>ô5ýC.÷¤uÚę›
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,2 +0,0 @@
|
||||
101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010
|
||||
232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,14 +0,0 @@
|
||||
//Copyright2009ThGoAuthor.Allrightrrvd.
|
||||
//UofthiourccodigovrndbyBSD-tyl
|
||||
//licnthtcnbfoundinthLICENSEfil.
|
||||
|
||||
pckgmin
|
||||
|
||||
import"o"
|
||||
|
||||
funcmin(){
|
||||
vrb=mk([]byt,65535)
|
||||
f,_:=o.Crt("huffmn-null-mx.in")
|
||||
f.Writ(b)
|
||||
}
|
||||
ABCDEFGHIJKLMNOPQRSTUVXxyz!"#¤%&/?"
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,14 +0,0 @@
|
||||
// zig v0.10.0
|
||||
// create a file filled with 0x00
|
||||
const std = @import("std");
|
||||
|
||||
pub fn main() !void {
|
||||
var b = [1]u8{0} ** 65535;
|
||||
const f = try std.fs.cwd().createFile(
|
||||
"huffman-null-max.in",
|
||||
.{ .read = true },
|
||||
);
|
||||
defer f.close();
|
||||
|
||||
_ = try f.writeAll(b[0..]);
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1 +0,0 @@
|
||||
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -12,17 +12,11 @@ pub const Check = enum(u4) {
|
||||
};
|
||||
|
||||
fn readStreamFlags(reader: anytype, check: *Check) !void {
|
||||
var bit_reader = std.io.bitReader(.little, reader);
|
||||
|
||||
const reserved1 = try bit_reader.readBitsNoEof(u8, 8);
|
||||
if (reserved1 != 0)
|
||||
return error.CorruptInput;
|
||||
|
||||
check.* = @as(Check, @enumFromInt(try bit_reader.readBitsNoEof(u4, 4)));
|
||||
|
||||
const reserved2 = try bit_reader.readBitsNoEof(u4, 4);
|
||||
if (reserved2 != 0)
|
||||
return error.CorruptInput;
|
||||
const reserved1 = try reader.readByte();
|
||||
if (reserved1 != 0) return error.CorruptInput;
|
||||
const byte = try reader.readByte();
|
||||
if ((byte >> 4) != 0) return error.CorruptInput;
|
||||
check.* = @enumFromInt(@as(u4, @truncate(byte)));
|
||||
}
|
||||
|
||||
pub fn decompress(allocator: Allocator, reader: anytype) !Decompress(@TypeOf(reader)) {
|
||||
|
||||
@ -31,7 +31,12 @@ pub const Options = struct {
|
||||
/// Verifying checksums is not implemented yet and will cause a panic if
|
||||
/// you set this to true.
|
||||
verify_checksum: bool = false,
|
||||
/// Affects the minimum capacity of the provided buffer.
|
||||
|
||||
/// The output buffer is asserted to have capacity for `window_len` plus
|
||||
/// `zstd.block_size_max`.
|
||||
///
|
||||
/// If `window_len` is too small, then some streams will fail to decompress
|
||||
/// with `error.OutputBufferUndersize`.
|
||||
window_len: u32 = zstd.default_window_len,
|
||||
};
|
||||
|
||||
@ -69,8 +74,10 @@ pub const Error = error{
|
||||
WindowSizeUnknown,
|
||||
};
|
||||
|
||||
/// If buffer that is written to is not big enough, some streams will fail with
|
||||
/// `error.OutputBufferUndersize`. A safe value is `zstd.default_window_len * 2`.
|
||||
/// When connecting `reader` to a `Writer`, `buffer` should be empty, and
|
||||
/// `Writer.buffer` capacity has requirements based on `Options.window_len`.
|
||||
///
|
||||
/// Otherwise, `buffer` has those requirements.
|
||||
pub fn init(input: *Reader, buffer: []u8, options: Options) Decompress {
|
||||
return .{
|
||||
.input = input,
|
||||
@ -78,7 +85,12 @@ pub fn init(input: *Reader, buffer: []u8, options: Options) Decompress {
|
||||
.verify_checksum = options.verify_checksum,
|
||||
.window_len = options.window_len,
|
||||
.reader = .{
|
||||
.vtable = &.{ .stream = stream },
|
||||
.vtable = &.{
|
||||
.stream = stream,
|
||||
.rebase = rebase,
|
||||
.discard = discard,
|
||||
.readVec = readVec,
|
||||
},
|
||||
.buffer = buffer,
|
||||
.seek = 0,
|
||||
.end = 0,
|
||||
@ -86,6 +98,60 @@ pub fn init(input: *Reader, buffer: []u8, options: Options) Decompress {
|
||||
};
|
||||
}
|
||||
|
||||
fn rebase(r: *Reader, capacity: usize) Reader.RebaseError!void {
|
||||
const d: *Decompress = @alignCast(@fieldParentPtr("reader", r));
|
||||
assert(capacity <= r.buffer.len - d.window_len);
|
||||
assert(r.end + capacity > r.buffer.len);
|
||||
const discard_n = r.end - d.window_len;
|
||||
const keep = r.buffer[discard_n..r.end];
|
||||
@memmove(r.buffer[0..keep.len], keep);
|
||||
r.end = keep.len;
|
||||
r.seek -= discard_n;
|
||||
}
|
||||
|
||||
/// This could be improved so that when an amount is discarded that includes an
|
||||
/// entire frame, skip decoding that frame.
|
||||
fn discard(r: *Reader, limit: std.Io.Limit) Reader.Error!usize {
|
||||
const d: *Decompress = @alignCast(@fieldParentPtr("reader", r));
|
||||
r.rebase(d.window_len) catch unreachable;
|
||||
var writer: Writer = .{
|
||||
.vtable = &.{
|
||||
.drain = std.Io.Writer.Discarding.drain,
|
||||
.sendFile = std.Io.Writer.Discarding.sendFile,
|
||||
},
|
||||
.buffer = r.buffer,
|
||||
.end = r.end,
|
||||
};
|
||||
defer {
|
||||
r.end = writer.end;
|
||||
r.seek = r.end;
|
||||
}
|
||||
const n = r.stream(&writer, limit) catch |err| switch (err) {
|
||||
error.WriteFailed => unreachable,
|
||||
error.ReadFailed => return error.ReadFailed,
|
||||
error.EndOfStream => return error.EndOfStream,
|
||||
};
|
||||
assert(n <= @intFromEnum(limit));
|
||||
return n;
|
||||
}
|
||||
|
||||
fn readVec(r: *Reader, data: [][]u8) Reader.Error!usize {
|
||||
_ = data;
|
||||
const d: *Decompress = @alignCast(@fieldParentPtr("reader", r));
|
||||
assert(r.seek == r.end);
|
||||
r.rebase(d.window_len) catch unreachable;
|
||||
var writer: Writer = .{
|
||||
.buffer = r.buffer,
|
||||
.end = r.end,
|
||||
.vtable = &.{ .drain = Writer.fixedDrain },
|
||||
};
|
||||
r.end += r.vtable.stream(r, &writer, .limited(writer.buffer.len - writer.end)) catch |err| switch (err) {
|
||||
error.WriteFailed => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn stream(r: *Reader, w: *Writer, limit: Limit) Reader.StreamError!usize {
|
||||
const d: *Decompress = @alignCast(@fieldParentPtr("reader", r));
|
||||
const in = d.input;
|
||||
|
||||
@ -2019,10 +2019,14 @@ pub fn compactUnwindToDwarfRegNumber(unwind_reg_number: u3) !u8 {
|
||||
/// This function is to make it handy to comment out the return and make it
|
||||
/// into a crash when working on this file.
|
||||
pub fn bad() error{InvalidDebugInfo} {
|
||||
if (debug_debug_mode) @panic("bad dwarf");
|
||||
invalidDebugInfoDetected();
|
||||
return error.InvalidDebugInfo;
|
||||
}
|
||||
|
||||
fn invalidDebugInfoDetected() void {
|
||||
if (debug_debug_mode) @panic("bad dwarf");
|
||||
}
|
||||
|
||||
fn missing() error{MissingDebugInfo} {
|
||||
if (debug_debug_mode) @panic("missing dwarf");
|
||||
return error.MissingDebugInfo;
|
||||
@ -2235,23 +2239,23 @@ pub const ElfModule = struct {
|
||||
|
||||
const section_bytes = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
|
||||
sections[section_index.?] = if ((shdr.sh_flags & elf.SHF_COMPRESSED) > 0) blk: {
|
||||
var section_reader: std.io.Reader = .fixed(section_bytes);
|
||||
const chdr = section_reader.takeStruct(elf.Chdr) catch continue;
|
||||
var section_reader: std.Io.Reader = .fixed(section_bytes);
|
||||
const chdr = section_reader.takeStruct(elf.Chdr, endian) catch continue;
|
||||
if (chdr.ch_type != .ZLIB) continue;
|
||||
const ch_size = chdr.ch_size;
|
||||
|
||||
var zlib_stream: std.compress.flate.Decompress = .init(§ion_reader, .zlib, &.{});
|
||||
|
||||
const decompressed_section = zlib_stream.reader.allocRemaining(gpa, .limited(ch_size)) catch
|
||||
var decompress: std.compress.flate.Decompress = .init(§ion_reader, .zlib, &.{});
|
||||
var decompressed_section: std.ArrayListUnmanaged(u8) = .empty;
|
||||
defer decompressed_section.deinit(gpa);
|
||||
decompress.reader.appendRemainingUnlimited(gpa, null, &decompressed_section, std.compress.flate.history_len) catch {
|
||||
invalidDebugInfoDetected();
|
||||
continue;
|
||||
if (decompressed_section.len != ch_size) {
|
||||
gpa.free(decompressed_section);
|
||||
};
|
||||
if (chdr.ch_size != decompressed_section.items.len) {
|
||||
invalidDebugInfoDetected();
|
||||
continue;
|
||||
}
|
||||
errdefer gpa.free(decompressed_section);
|
||||
|
||||
break :blk .{
|
||||
.data = decompressed_section,
|
||||
.data = try decompressed_section.toOwnedSlice(gpa),
|
||||
.virtual_address = shdr.sh_addr,
|
||||
.owned = true,
|
||||
};
|
||||
|
||||
@ -1317,9 +1317,9 @@ test "EnumSet non-exhaustive" {
|
||||
}
|
||||
|
||||
pub fn EnumIndexer(comptime E: type) type {
|
||||
// Assumes that the enum fields are sorted in ascending order (optimistic).
|
||||
// Unsorted enums may require the user to manually increase the quota.
|
||||
@setEvalBranchQuota(3 * @typeInfo(E).@"enum".fields.len + eval_branch_quota_cushion);
|
||||
// n log n for `std.mem.sortUnstable` call below.
|
||||
const fields_len = @typeInfo(E).@"enum".fields.len;
|
||||
@setEvalBranchQuota(3 * fields_len * std.math.log2(@max(fields_len, 1)) + eval_branch_quota_cushion);
|
||||
|
||||
if (!@typeInfo(E).@"enum".is_exhaustive) {
|
||||
const BackingInt = @typeInfo(E).@"enum".tag_type;
|
||||
@ -1354,10 +1354,6 @@ pub fn EnumIndexer(comptime E: type) type {
|
||||
};
|
||||
}
|
||||
|
||||
const const_fields = @typeInfo(E).@"enum".fields;
|
||||
var fields = const_fields[0..const_fields.len].*;
|
||||
const fields_len = fields.len;
|
||||
|
||||
if (fields_len == 0) {
|
||||
return struct {
|
||||
pub const Key = E;
|
||||
@ -1373,22 +1369,17 @@ pub fn EnumIndexer(comptime E: type) type {
|
||||
};
|
||||
}
|
||||
|
||||
var fields: [fields_len]EnumField = @typeInfo(E).@"enum".fields[0..].*;
|
||||
|
||||
std.mem.sortUnstable(EnumField, &fields, {}, struct {
|
||||
fn lessThan(ctx: void, lhs: EnumField, rhs: EnumField) bool {
|
||||
ctx;
|
||||
return lhs.value < rhs.value;
|
||||
}
|
||||
}.lessThan);
|
||||
|
||||
const min = fields[0].value;
|
||||
const max = fields[fields.len - 1].value;
|
||||
|
||||
const SortContext = struct {
|
||||
fields: []EnumField,
|
||||
|
||||
pub fn lessThan(comptime ctx: @This(), comptime a: usize, comptime b: usize) bool {
|
||||
return ctx.fields[a].value < ctx.fields[b].value;
|
||||
}
|
||||
|
||||
pub fn swap(comptime ctx: @This(), comptime a: usize, comptime b: usize) void {
|
||||
return std.mem.swap(EnumField, &ctx.fields[a], &ctx.fields[b]);
|
||||
}
|
||||
};
|
||||
std.sort.insertionContext(0, fields_len, SortContext{ .fields = &fields });
|
||||
|
||||
const max = fields[fields_len - 1].value;
|
||||
if (max - min == fields.len - 1) {
|
||||
return struct {
|
||||
pub const Key = E;
|
||||
@ -1538,6 +1529,29 @@ test "EnumIndexer empty" {
|
||||
try testing.expectEqual(0, Indexer.count);
|
||||
}
|
||||
|
||||
test "EnumIndexer large dense unsorted" {
|
||||
@setEvalBranchQuota(500_000); // many `comptimePrint`s
|
||||
// Make an enum with 500 fields with values in *descending* order.
|
||||
const E = @Type(.{ .@"enum" = .{
|
||||
.tag_type = u32,
|
||||
.fields = comptime fields: {
|
||||
var fields: [500]EnumField = undefined;
|
||||
for (&fields, 0..) |*f, i| f.* = .{
|
||||
.name = std.fmt.comptimePrint("f{d}", .{i}),
|
||||
.value = 500 - i,
|
||||
};
|
||||
break :fields &fields;
|
||||
},
|
||||
.decls = &.{},
|
||||
.is_exhaustive = true,
|
||||
} });
|
||||
const Indexer = EnumIndexer(E);
|
||||
try testing.expectEqual(E.f0, Indexer.keyForIndex(499));
|
||||
try testing.expectEqual(E.f499, Indexer.keyForIndex(0));
|
||||
try testing.expectEqual(499, Indexer.indexOf(.f0));
|
||||
try testing.expectEqual(0, Indexer.indexOf(.f499));
|
||||
}
|
||||
|
||||
test values {
|
||||
const E = enum {
|
||||
X,
|
||||
|
||||
@ -1101,6 +1101,8 @@ test "float.libc.sanity" {
|
||||
}
|
||||
|
||||
test "union" {
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
|
||||
|
||||
const TU = union(enum) {
|
||||
float: f32,
|
||||
int: u32,
|
||||
|
||||
@ -912,7 +912,7 @@ pub fn pwritev(self: File, iovecs: []posix.iovec_const, offset: u64) PWriteError
|
||||
/// * Whether reading should be done via fd-to-fd syscalls (e.g. `sendfile`)
|
||||
/// versus plain variants (e.g. `read`).
|
||||
///
|
||||
/// Fulfills the `std.io.Reader` interface.
|
||||
/// Fulfills the `std.Io.Reader` interface.
|
||||
pub const Reader = struct {
|
||||
file: File,
|
||||
err: ?ReadError = null,
|
||||
@ -923,7 +923,7 @@ pub const Reader = struct {
|
||||
size: ?u64 = null,
|
||||
size_err: ?GetEndPosError = null,
|
||||
seek_err: ?Reader.SeekError = null,
|
||||
interface: std.io.Reader,
|
||||
interface: std.Io.Reader,
|
||||
|
||||
pub const SeekError = File.SeekError || error{
|
||||
/// Seeking fell back to reading, and reached the end before the requested seek position.
|
||||
@ -960,11 +960,12 @@ pub const Reader = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub fn initInterface(buffer: []u8) std.io.Reader {
|
||||
pub fn initInterface(buffer: []u8) std.Io.Reader {
|
||||
return .{
|
||||
.vtable = &.{
|
||||
.stream = Reader.stream,
|
||||
.discard = Reader.discard,
|
||||
.readVec = Reader.readVec,
|
||||
},
|
||||
.buffer = buffer,
|
||||
.seek = 0,
|
||||
@ -1077,7 +1078,7 @@ pub const Reader = struct {
|
||||
/// vectors through the underlying read calls as possible.
|
||||
const max_buffers_len = 16;
|
||||
|
||||
fn stream(io_reader: *std.io.Reader, w: *std.io.Writer, limit: std.io.Limit) std.io.Reader.StreamError!usize {
|
||||
fn stream(io_reader: *std.Io.Reader, w: *std.Io.Writer, limit: std.Io.Limit) std.Io.Reader.StreamError!usize {
|
||||
const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader));
|
||||
switch (r.mode) {
|
||||
.positional, .streaming => return w.sendFile(r, limit) catch |write_err| switch (write_err) {
|
||||
@ -1088,16 +1089,33 @@ pub const Reader = struct {
|
||||
else => |e| return e,
|
||||
},
|
||||
.positional_reading => {
|
||||
const dest = limit.slice(try w.writableSliceGreedy(1));
|
||||
const n = try readPositional(r, dest);
|
||||
w.advance(n);
|
||||
return n;
|
||||
},
|
||||
.streaming_reading => {
|
||||
const dest = limit.slice(try w.writableSliceGreedy(1));
|
||||
const n = try readStreaming(r, dest);
|
||||
w.advance(n);
|
||||
return n;
|
||||
},
|
||||
.failure => return error.ReadFailed,
|
||||
}
|
||||
}
|
||||
|
||||
fn readVec(io_reader: *std.Io.Reader, data: [][]u8) std.Io.Reader.Error!usize {
|
||||
const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader));
|
||||
switch (r.mode) {
|
||||
.positional, .positional_reading => {
|
||||
if (is_windows) {
|
||||
// Unfortunately, `ReadFileScatter` cannot be used since it
|
||||
// requires page alignment.
|
||||
const dest = limit.slice(try w.writableSliceGreedy(1));
|
||||
const n = try readPositional(r, dest);
|
||||
w.advance(n);
|
||||
return n;
|
||||
return readPositional(r, data[0]);
|
||||
}
|
||||
var iovecs_buffer: [max_buffers_len]posix.iovec = undefined;
|
||||
const dest = try w.writableVectorPosix(&iovecs_buffer, limit);
|
||||
const dest_n, const data_size = try io_reader.writableVectorPosix(&iovecs_buffer, data);
|
||||
const dest = iovecs_buffer[0..dest_n];
|
||||
assert(dest[0].len > 0);
|
||||
const n = posix.preadv(r.file.handle, dest, r.pos) catch |err| switch (err) {
|
||||
error.Unseekable => {
|
||||
@ -1122,19 +1140,22 @@ pub const Reader = struct {
|
||||
return error.EndOfStream;
|
||||
}
|
||||
r.pos += n;
|
||||
return w.advanceVector(n);
|
||||
if (n > data_size) {
|
||||
io_reader.seek = 0;
|
||||
io_reader.end = n - data_size;
|
||||
return data_size;
|
||||
}
|
||||
return n;
|
||||
},
|
||||
.streaming_reading => {
|
||||
.streaming, .streaming_reading => {
|
||||
if (is_windows) {
|
||||
// Unfortunately, `ReadFileScatter` cannot be used since it
|
||||
// requires page alignment.
|
||||
const dest = limit.slice(try w.writableSliceGreedy(1));
|
||||
const n = try readStreaming(r, dest);
|
||||
w.advance(n);
|
||||
return n;
|
||||
return readStreaming(r, data[0]);
|
||||
}
|
||||
var iovecs_buffer: [max_buffers_len]posix.iovec = undefined;
|
||||
const dest = try w.writableVectorPosix(&iovecs_buffer, limit);
|
||||
const dest_n, const data_size = try io_reader.writableVectorPosix(&iovecs_buffer, data);
|
||||
const dest = iovecs_buffer[0..dest_n];
|
||||
assert(dest[0].len > 0);
|
||||
const n = posix.readv(r.file.handle, dest) catch |err| {
|
||||
r.err = err;
|
||||
@ -1145,13 +1166,18 @@ pub const Reader = struct {
|
||||
return error.EndOfStream;
|
||||
}
|
||||
r.pos += n;
|
||||
return w.advanceVector(n);
|
||||
if (n > data_size) {
|
||||
io_reader.seek = 0;
|
||||
io_reader.end = n - data_size;
|
||||
return data_size;
|
||||
}
|
||||
return n;
|
||||
},
|
||||
.failure => return error.ReadFailed,
|
||||
}
|
||||
}
|
||||
|
||||
fn discard(io_reader: *std.io.Reader, limit: std.io.Limit) std.io.Reader.Error!usize {
|
||||
fn discard(io_reader: *std.Io.Reader, limit: std.Io.Limit) std.Io.Reader.Error!usize {
|
||||
const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader));
|
||||
const file = r.file;
|
||||
const pos = r.pos;
|
||||
@ -1230,7 +1256,7 @@ pub const Reader = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn readPositional(r: *Reader, dest: []u8) std.io.Reader.Error!usize {
|
||||
pub fn readPositional(r: *Reader, dest: []u8) std.Io.Reader.Error!usize {
|
||||
const n = r.file.pread(dest, r.pos) catch |err| switch (err) {
|
||||
error.Unseekable => {
|
||||
r.mode = r.mode.toStreaming();
|
||||
@ -1257,7 +1283,7 @@ pub const Reader = struct {
|
||||
return n;
|
||||
}
|
||||
|
||||
pub fn readStreaming(r: *Reader, dest: []u8) std.io.Reader.Error!usize {
|
||||
pub fn readStreaming(r: *Reader, dest: []u8) std.Io.Reader.Error!usize {
|
||||
const n = r.file.read(dest) catch |err| {
|
||||
r.err = err;
|
||||
return error.ReadFailed;
|
||||
@ -1270,7 +1296,7 @@ pub const Reader = struct {
|
||||
return n;
|
||||
}
|
||||
|
||||
pub fn read(r: *Reader, dest: []u8) std.io.Reader.Error!usize {
|
||||
pub fn read(r: *Reader, dest: []u8) std.Io.Reader.Error!usize {
|
||||
switch (r.mode) {
|
||||
.positional, .positional_reading => return readPositional(r, dest),
|
||||
.streaming, .streaming_reading => return readStreaming(r, dest),
|
||||
@ -1296,7 +1322,7 @@ pub const Writer = struct {
|
||||
copy_file_range_err: ?CopyFileRangeError = null,
|
||||
fcopyfile_err: ?FcopyfileError = null,
|
||||
seek_err: ?SeekError = null,
|
||||
interface: std.io.Writer,
|
||||
interface: std.Io.Writer,
|
||||
|
||||
pub const Mode = Reader.Mode;
|
||||
|
||||
@ -1333,13 +1359,13 @@ pub const Writer = struct {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn initInterface(buffer: []u8) std.io.Writer {
|
||||
pub fn initInterface(buffer: []u8) std.Io.Writer {
|
||||
return .{
|
||||
.vtable = &.{
|
||||
.drain = drain,
|
||||
.sendFile = switch (builtin.zig_backend) {
|
||||
else => sendFile,
|
||||
.stage2_aarch64 => std.io.Writer.unimplementedSendFile,
|
||||
.stage2_aarch64 => std.Io.Writer.unimplementedSendFile,
|
||||
},
|
||||
},
|
||||
.buffer = buffer,
|
||||
@ -1357,7 +1383,7 @@ pub const Writer = struct {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn drain(io_w: *std.io.Writer, data: []const []const u8, splat: usize) std.io.Writer.Error!usize {
|
||||
pub fn drain(io_w: *std.Io.Writer, data: []const []const u8, splat: usize) std.Io.Writer.Error!usize {
|
||||
const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w));
|
||||
const handle = w.file.handle;
|
||||
const buffered = io_w.buffered();
|
||||
@ -1507,10 +1533,10 @@ pub const Writer = struct {
|
||||
}
|
||||
|
||||
pub fn sendFile(
|
||||
io_w: *std.io.Writer,
|
||||
io_w: *std.Io.Writer,
|
||||
file_reader: *Reader,
|
||||
limit: std.io.Limit,
|
||||
) std.io.Writer.FileError!usize {
|
||||
limit: std.Io.Limit,
|
||||
) std.Io.Writer.FileError!usize {
|
||||
const reader_buffered = file_reader.interface.buffered();
|
||||
if (reader_buffered.len >= @intFromEnum(limit))
|
||||
return sendFileBuffered(io_w, file_reader, reader_buffered);
|
||||
@ -1772,10 +1798,10 @@ pub const Writer = struct {
|
||||
}
|
||||
|
||||
fn sendFileBuffered(
|
||||
io_w: *std.io.Writer,
|
||||
io_w: *std.Io.Writer,
|
||||
file_reader: *Reader,
|
||||
reader_buffered: []const u8,
|
||||
) std.io.Writer.FileError!usize {
|
||||
) std.Io.Writer.FileError!usize {
|
||||
const n = try drain(io_w, &.{reader_buffered}, 1);
|
||||
file_reader.seekTo(file_reader.pos + n) catch return error.ReadFailed;
|
||||
return n;
|
||||
@ -1798,7 +1824,7 @@ pub const Writer = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub const EndError = SetEndPosError || std.io.Writer.Error;
|
||||
pub const EndError = SetEndPosError || std.Io.Writer.Error;
|
||||
|
||||
/// Flushes any buffered data and sets the end position of the file.
|
||||
///
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
const adler = @import("hash/adler.zig");
|
||||
pub const Adler32 = adler.Adler32;
|
||||
pub const Adler32 = @import("hash/Adler32.zig");
|
||||
|
||||
const auto_hash = @import("hash/auto_hash.zig");
|
||||
pub const autoHash = auto_hash.autoHash;
|
||||
@ -115,7 +114,7 @@ test int {
|
||||
}
|
||||
|
||||
test {
|
||||
_ = adler;
|
||||
_ = Adler32;
|
||||
_ = auto_hash;
|
||||
_ = crc;
|
||||
_ = fnv;
|
||||
|
||||
117
lib/std/hash/Adler32.zig
Normal file
117
lib/std/hash/Adler32.zig
Normal file
@ -0,0 +1,117 @@
|
||||
//! https://tools.ietf.org/html/rfc1950#section-9
|
||||
//! https://github.com/madler/zlib/blob/master/adler32.c
|
||||
|
||||
const Adler32 = @This();
|
||||
const std = @import("std");
|
||||
const testing = std.testing;
|
||||
|
||||
adler: u32 = 1,
|
||||
|
||||
pub fn permute(state: u32, input: []const u8) u32 {
|
||||
const base = 65521;
|
||||
const nmax = 5552;
|
||||
|
||||
var s1 = state & 0xffff;
|
||||
var s2 = (state >> 16) & 0xffff;
|
||||
|
||||
if (input.len == 1) {
|
||||
s1 +%= input[0];
|
||||
if (s1 >= base) {
|
||||
s1 -= base;
|
||||
}
|
||||
s2 +%= s1;
|
||||
if (s2 >= base) {
|
||||
s2 -= base;
|
||||
}
|
||||
} else if (input.len < 16) {
|
||||
for (input) |b| {
|
||||
s1 +%= b;
|
||||
s2 +%= s1;
|
||||
}
|
||||
if (s1 >= base) {
|
||||
s1 -= base;
|
||||
}
|
||||
|
||||
s2 %= base;
|
||||
} else {
|
||||
const n = nmax / 16; // note: 16 | nmax
|
||||
|
||||
var i: usize = 0;
|
||||
|
||||
while (i + nmax <= input.len) {
|
||||
var rounds: usize = 0;
|
||||
while (rounds < n) : (rounds += 1) {
|
||||
comptime var j: usize = 0;
|
||||
inline while (j < 16) : (j += 1) {
|
||||
s1 +%= input[i + j];
|
||||
s2 +%= s1;
|
||||
}
|
||||
i += 16;
|
||||
}
|
||||
|
||||
s1 %= base;
|
||||
s2 %= base;
|
||||
}
|
||||
|
||||
if (i < input.len) {
|
||||
while (i + 16 <= input.len) : (i += 16) {
|
||||
comptime var j: usize = 0;
|
||||
inline while (j < 16) : (j += 1) {
|
||||
s1 +%= input[i + j];
|
||||
s2 +%= s1;
|
||||
}
|
||||
}
|
||||
while (i < input.len) : (i += 1) {
|
||||
s1 +%= input[i];
|
||||
s2 +%= s1;
|
||||
}
|
||||
|
||||
s1 %= base;
|
||||
s2 %= base;
|
||||
}
|
||||
}
|
||||
|
||||
return s1 | (s2 << 16);
|
||||
}
|
||||
|
||||
pub fn update(a: *Adler32, input: []const u8) void {
|
||||
a.adler = permute(a.adler, input);
|
||||
}
|
||||
|
||||
pub fn hash(input: []const u8) u32 {
|
||||
return permute(1, input);
|
||||
}
|
||||
|
||||
test "sanity" {
|
||||
try testing.expectEqual(@as(u32, 0x620062), hash("a"));
|
||||
try testing.expectEqual(@as(u32, 0xbc002ed), hash("example"));
|
||||
}
|
||||
|
||||
test "long" {
|
||||
const long1 = [_]u8{1} ** 1024;
|
||||
try testing.expectEqual(@as(u32, 0x06780401), hash(long1[0..]));
|
||||
|
||||
const long2 = [_]u8{1} ** 1025;
|
||||
try testing.expectEqual(@as(u32, 0x0a7a0402), hash(long2[0..]));
|
||||
}
|
||||
|
||||
test "very long" {
|
||||
const long = [_]u8{1} ** 5553;
|
||||
try testing.expectEqual(@as(u32, 0x707f15b2), hash(long[0..]));
|
||||
}
|
||||
|
||||
test "very long with variation" {
|
||||
const long = comptime blk: {
|
||||
@setEvalBranchQuota(7000);
|
||||
var result: [6000]u8 = undefined;
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < result.len) : (i += 1) {
|
||||
result[i] = @as(u8, @truncate(i));
|
||||
}
|
||||
|
||||
break :blk result;
|
||||
};
|
||||
|
||||
try testing.expectEqual(@as(u32, 0x5af38d6e), hash(long[0..]));
|
||||
}
|
||||
@ -1,134 +0,0 @@
|
||||
// Adler32 checksum.
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc1950#section-9
|
||||
// https://github.com/madler/zlib/blob/master/adler32.c
|
||||
|
||||
const std = @import("std");
|
||||
const testing = std.testing;
|
||||
|
||||
pub const Adler32 = struct {
|
||||
const base = 65521;
|
||||
const nmax = 5552;
|
||||
|
||||
adler: u32,
|
||||
|
||||
pub fn init() Adler32 {
|
||||
return Adler32{ .adler = 1 };
|
||||
}
|
||||
|
||||
// This fast variant is taken from zlib. It reduces the required modulos and unrolls longer
|
||||
// buffer inputs and should be much quicker.
|
||||
pub fn update(self: *Adler32, input: []const u8) void {
|
||||
var s1 = self.adler & 0xffff;
|
||||
var s2 = (self.adler >> 16) & 0xffff;
|
||||
|
||||
if (input.len == 1) {
|
||||
s1 +%= input[0];
|
||||
if (s1 >= base) {
|
||||
s1 -= base;
|
||||
}
|
||||
s2 +%= s1;
|
||||
if (s2 >= base) {
|
||||
s2 -= base;
|
||||
}
|
||||
} else if (input.len < 16) {
|
||||
for (input) |b| {
|
||||
s1 +%= b;
|
||||
s2 +%= s1;
|
||||
}
|
||||
if (s1 >= base) {
|
||||
s1 -= base;
|
||||
}
|
||||
|
||||
s2 %= base;
|
||||
} else {
|
||||
const n = nmax / 16; // note: 16 | nmax
|
||||
|
||||
var i: usize = 0;
|
||||
|
||||
while (i + nmax <= input.len) {
|
||||
var rounds: usize = 0;
|
||||
while (rounds < n) : (rounds += 1) {
|
||||
comptime var j: usize = 0;
|
||||
inline while (j < 16) : (j += 1) {
|
||||
s1 +%= input[i + j];
|
||||
s2 +%= s1;
|
||||
}
|
||||
i += 16;
|
||||
}
|
||||
|
||||
s1 %= base;
|
||||
s2 %= base;
|
||||
}
|
||||
|
||||
if (i < input.len) {
|
||||
while (i + 16 <= input.len) : (i += 16) {
|
||||
comptime var j: usize = 0;
|
||||
inline while (j < 16) : (j += 1) {
|
||||
s1 +%= input[i + j];
|
||||
s2 +%= s1;
|
||||
}
|
||||
}
|
||||
while (i < input.len) : (i += 1) {
|
||||
s1 +%= input[i];
|
||||
s2 +%= s1;
|
||||
}
|
||||
|
||||
s1 %= base;
|
||||
s2 %= base;
|
||||
}
|
||||
}
|
||||
|
||||
self.adler = s1 | (s2 << 16);
|
||||
}
|
||||
|
||||
pub fn final(self: *Adler32) u32 {
|
||||
return self.adler;
|
||||
}
|
||||
|
||||
pub fn hash(input: []const u8) u32 {
|
||||
var c = Adler32.init();
|
||||
c.update(input);
|
||||
return c.final();
|
||||
}
|
||||
};
|
||||
|
||||
test "adler32 sanity" {
|
||||
try testing.expectEqual(@as(u32, 0x620062), Adler32.hash("a"));
|
||||
try testing.expectEqual(@as(u32, 0xbc002ed), Adler32.hash("example"));
|
||||
}
|
||||
|
||||
test "adler32 long" {
|
||||
const long1 = [_]u8{1} ** 1024;
|
||||
try testing.expectEqual(@as(u32, 0x06780401), Adler32.hash(long1[0..]));
|
||||
|
||||
const long2 = [_]u8{1} ** 1025;
|
||||
try testing.expectEqual(@as(u32, 0x0a7a0402), Adler32.hash(long2[0..]));
|
||||
}
|
||||
|
||||
test "adler32 very long" {
|
||||
const long = [_]u8{1} ** 5553;
|
||||
try testing.expectEqual(@as(u32, 0x707f15b2), Adler32.hash(long[0..]));
|
||||
}
|
||||
|
||||
test "adler32 very long with variation" {
|
||||
const long = comptime blk: {
|
||||
@setEvalBranchQuota(7000);
|
||||
var result: [6000]u8 = undefined;
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < result.len) : (i += 1) {
|
||||
result[i] = @as(u8, @truncate(i));
|
||||
}
|
||||
|
||||
break :blk result;
|
||||
};
|
||||
|
||||
try testing.expectEqual(@as(u32, 0x5af38d6e), std.hash.Adler32.hash(long[0..]));
|
||||
}
|
||||
|
||||
const verify = @import("verify.zig");
|
||||
|
||||
test "adler32 iterative" {
|
||||
try verify.iterativeApi(Adler32);
|
||||
}
|
||||
@ -45,7 +45,7 @@ pub fn smhasher(comptime hash_fn: anytype) u32 {
|
||||
|
||||
pub fn iterativeApi(comptime Hash: anytype) !void {
|
||||
// Sum(1..32) = 528
|
||||
var buf: [528]u8 = [_]u8{0} ** 528;
|
||||
var buf: [528]u8 = @splat(0);
|
||||
var len: usize = 0;
|
||||
const seed = 0;
|
||||
|
||||
|
||||
@ -146,7 +146,7 @@ const CAllocator = struct {
|
||||
else {};
|
||||
|
||||
pub const supports_posix_memalign = switch (builtin.os.tag) {
|
||||
.dragonfly, .netbsd, .freebsd, .solaris, .openbsd, .linux, .macos, .ios, .tvos, .watchos, .visionos => true,
|
||||
.dragonfly, .netbsd, .freebsd, .solaris, .openbsd, .linux, .macos, .ios, .tvos, .watchos, .visionos, .serenity => true,
|
||||
else => false,
|
||||
};
|
||||
|
||||
|
||||
@ -1358,11 +1358,15 @@ pub fn lossyCast(comptime T: type, value: anytype) T {
|
||||
}
|
||||
},
|
||||
.float, .comptime_float => {
|
||||
// In extreme cases, we probably need a language enhancement to be able to
|
||||
// specify a rounding mode here to prevent `@intFromFloat` panics.
|
||||
const max: @TypeOf(value) = @floatFromInt(maxInt(T));
|
||||
const min: @TypeOf(value) = @floatFromInt(minInt(T));
|
||||
if (isNan(value)) {
|
||||
return 0;
|
||||
} else if (value >= maxInt(T)) {
|
||||
} else if (value >= max) {
|
||||
return maxInt(T);
|
||||
} else if (value <= minInt(T)) {
|
||||
} else if (value <= min) {
|
||||
return minInt(T);
|
||||
} else {
|
||||
return @intFromFloat(value);
|
||||
@ -1379,7 +1383,7 @@ test lossyCast {
|
||||
try testing.expect(lossyCast(i16, 70000.0) == @as(i16, 32767));
|
||||
try testing.expect(lossyCast(u32, @as(i16, -255)) == @as(u32, 0));
|
||||
try testing.expect(lossyCast(i9, @as(u32, 200)) == @as(i9, 200));
|
||||
try testing.expect(lossyCast(u32, @as(f32, maxInt(u32))) == maxInt(u32));
|
||||
try testing.expect(lossyCast(u32, @as(f32, @floatFromInt(maxInt(u32)))) == maxInt(u32));
|
||||
try testing.expect(lossyCast(u32, nan(f32)) == 0);
|
||||
}
|
||||
|
||||
|
||||
@ -189,19 +189,19 @@ fn series(comptime T: type, abs: T) T {
|
||||
2.5066282746310002701649081771338373386264310793408,
|
||||
};
|
||||
const denominator = [_]T{
|
||||
0,
|
||||
39916800,
|
||||
120543840,
|
||||
150917976,
|
||||
105258076,
|
||||
45995730,
|
||||
13339535,
|
||||
2637558,
|
||||
357423,
|
||||
32670,
|
||||
1925,
|
||||
66,
|
||||
1,
|
||||
0.0,
|
||||
39916800.0,
|
||||
120543840.0,
|
||||
150917976.0,
|
||||
105258076.0,
|
||||
45995730.0,
|
||||
13339535.0,
|
||||
2637558.0,
|
||||
357423.0,
|
||||
32670.0,
|
||||
1925.0,
|
||||
66.0,
|
||||
1.0,
|
||||
};
|
||||
var num: T = 0;
|
||||
var den: T = 0;
|
||||
@ -244,9 +244,9 @@ const expectApproxEqRel = std.testing.expectApproxEqRel;
|
||||
test gamma {
|
||||
inline for (&.{ f32, f64 }) |T| {
|
||||
const eps = @sqrt(std.math.floatEps(T));
|
||||
try expectApproxEqRel(@as(T, 120), gamma(T, 6), eps);
|
||||
try expectApproxEqRel(@as(T, 362880), gamma(T, 10), eps);
|
||||
try expectApproxEqRel(@as(T, 6402373705728000), gamma(T, 19), eps);
|
||||
try expectApproxEqRel(@as(T, 120.0), gamma(T, 6), eps);
|
||||
try expectApproxEqRel(@as(T, 362880.0), gamma(T, 10), eps);
|
||||
try expectApproxEqRel(@as(T, 6402373705728000.0), gamma(T, 19), eps);
|
||||
|
||||
try expectApproxEqRel(@as(T, 332.7590766955334570), gamma(T, 0.003), eps);
|
||||
try expectApproxEqRel(@as(T, 1.377260301981044573), gamma(T, 0.654), eps);
|
||||
|
||||
@ -74,7 +74,7 @@ fn ModfTests(comptime T: type) type {
|
||||
r = modf(@as(T, 43874.3));
|
||||
try expectEqual(43874.0, r.ipart);
|
||||
// account for precision error
|
||||
const expected_b: T = 43874.3 - @as(T, 43874);
|
||||
const expected_b: T = 43874.3 - @as(T, 43874.0);
|
||||
try expectApproxEqAbs(expected_b, r.fpart, epsilon);
|
||||
|
||||
r = modf(@as(T, 1234.340780));
|
||||
|
||||
@ -192,8 +192,8 @@ fn isOddInteger(x: f64) bool {
|
||||
}
|
||||
|
||||
test isOddInteger {
|
||||
try expect(isOddInteger(math.maxInt(i64) * 2) == false);
|
||||
try expect(isOddInteger(math.maxInt(i64) * 2 + 1) == false);
|
||||
try expect(isOddInteger(@floatFromInt(math.maxInt(i64) * 2)) == false);
|
||||
try expect(isOddInteger(@floatFromInt(math.maxInt(i64) * 2 + 1)) == false);
|
||||
try expect(isOddInteger(1 << 53) == false);
|
||||
try expect(isOddInteger(12.0) == false);
|
||||
try expect(isOddInteger(15.0) == true);
|
||||
|
||||
@ -7,7 +7,7 @@ const net = @This();
|
||||
const mem = std.mem;
|
||||
const posix = std.posix;
|
||||
const fs = std.fs;
|
||||
const io = std.io;
|
||||
const Io = std.Io;
|
||||
const native_endian = builtin.target.cpu.arch.endian();
|
||||
const native_os = builtin.os.tag;
|
||||
const windows = std.os.windows;
|
||||
@ -165,7 +165,7 @@ pub const Address = extern union {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format(self: Address, w: *std.io.Writer) std.io.Writer.Error!void {
|
||||
pub fn format(self: Address, w: *Io.Writer) Io.Writer.Error!void {
|
||||
switch (self.any.family) {
|
||||
posix.AF.INET => try self.in.format(w),
|
||||
posix.AF.INET6 => try self.in6.format(w),
|
||||
@ -342,7 +342,7 @@ pub const Ip4Address = extern struct {
|
||||
self.sa.port = mem.nativeToBig(u16, port);
|
||||
}
|
||||
|
||||
pub fn format(self: Ip4Address, w: *std.io.Writer) std.io.Writer.Error!void {
|
||||
pub fn format(self: Ip4Address, w: *Io.Writer) Io.Writer.Error!void {
|
||||
const bytes: *const [4]u8 = @ptrCast(&self.sa.addr);
|
||||
try w.print("{d}.{d}.{d}.{d}:{d}", .{ bytes[0], bytes[1], bytes[2], bytes[3], self.getPort() });
|
||||
}
|
||||
@ -633,7 +633,7 @@ pub const Ip6Address = extern struct {
|
||||
self.sa.port = mem.nativeToBig(u16, port);
|
||||
}
|
||||
|
||||
pub fn format(self: Ip6Address, w: *std.io.Writer) std.io.Writer.Error!void {
|
||||
pub fn format(self: Ip6Address, w: *Io.Writer) Io.Writer.Error!void {
|
||||
const port = mem.bigToNative(u16, self.sa.port);
|
||||
if (mem.eql(u8, self.sa.addr[0..12], &[_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff })) {
|
||||
try w.print("[::ffff:{d}.{d}.{d}.{d}]:{d}", .{
|
||||
@ -1348,7 +1348,7 @@ fn parseHosts(
|
||||
name: []const u8,
|
||||
family: posix.sa_family_t,
|
||||
port: u16,
|
||||
br: *io.Reader,
|
||||
br: *Io.Reader,
|
||||
) error{ OutOfMemory, ReadFailed }!void {
|
||||
while (true) {
|
||||
const line = br.takeDelimiterExclusive('\n') catch |err| switch (err) {
|
||||
@ -1402,7 +1402,7 @@ test parseHosts {
|
||||
// TODO parsing addresses should not have OS dependencies
|
||||
return error.SkipZigTest;
|
||||
}
|
||||
var reader: std.io.Reader = .fixed(
|
||||
var reader: Io.Reader = .fixed(
|
||||
\\127.0.0.1 localhost
|
||||
\\::1 localhost
|
||||
\\127.0.0.2 abcd
|
||||
@ -1583,7 +1583,7 @@ const ResolvConf = struct {
|
||||
const Directive = enum { options, nameserver, domain, search };
|
||||
const Option = enum { ndots, attempts, timeout };
|
||||
|
||||
fn parse(rc: *ResolvConf, reader: *io.Reader) !void {
|
||||
fn parse(rc: *ResolvConf, reader: *Io.Reader) !void {
|
||||
const gpa = rc.gpa;
|
||||
while (reader.takeSentinel('\n')) |line_with_comment| {
|
||||
const line = line: {
|
||||
@ -1894,7 +1894,7 @@ pub const Stream = struct {
|
||||
pub const Reader = switch (native_os) {
|
||||
.windows => struct {
|
||||
/// Use `interface` for portable code.
|
||||
interface_state: io.Reader,
|
||||
interface_state: Io.Reader,
|
||||
/// Use `getStream` for portable code.
|
||||
net_stream: Stream,
|
||||
/// Use `getError` for portable code.
|
||||
@ -1910,14 +1910,17 @@ pub const Stream = struct {
|
||||
return r.error_state;
|
||||
}
|
||||
|
||||
pub fn interface(r: *Reader) *io.Reader {
|
||||
pub fn interface(r: *Reader) *Io.Reader {
|
||||
return &r.interface_state;
|
||||
}
|
||||
|
||||
pub fn init(net_stream: Stream, buffer: []u8) Reader {
|
||||
return .{
|
||||
.interface_state = .{
|
||||
.vtable = &.{ .stream = stream },
|
||||
.vtable = &.{
|
||||
.stream = stream,
|
||||
.readVec = readVec,
|
||||
},
|
||||
.buffer = buffer,
|
||||
.seek = 0,
|
||||
.end = 0,
|
||||
@ -1927,16 +1930,30 @@ pub const Stream = struct {
|
||||
};
|
||||
}
|
||||
|
||||
fn stream(io_r: *io.Reader, io_w: *io.Writer, limit: io.Limit) io.Reader.StreamError!usize {
|
||||
fn stream(io_r: *Io.Reader, io_w: *Io.Writer, limit: Io.Limit) Io.Reader.StreamError!usize {
|
||||
const dest = limit.slice(try io_w.writableSliceGreedy(1));
|
||||
var bufs: [1][]u8 = .{dest};
|
||||
const n = try readVec(io_r, &bufs);
|
||||
io_w.advance(n);
|
||||
return n;
|
||||
}
|
||||
|
||||
fn readVec(io_r: *std.Io.Reader, data: [][]u8) Io.Reader.Error!usize {
|
||||
const r: *Reader = @alignCast(@fieldParentPtr("interface_state", io_r));
|
||||
var iovecs: [max_buffers_len]windows.ws2_32.WSABUF = undefined;
|
||||
const bufs = try io_w.writableVectorWsa(&iovecs, limit);
|
||||
const bufs_n, const data_size = try io_r.writableVectorWsa(&iovecs, data);
|
||||
const bufs = iovecs[0..bufs_n];
|
||||
assert(bufs[0].len != 0);
|
||||
const n = streamBufs(r, bufs) catch |err| {
|
||||
r.error_state = err;
|
||||
return error.ReadFailed;
|
||||
};
|
||||
if (n == 0) return error.EndOfStream;
|
||||
if (n > data_size) {
|
||||
io_r.seek = 0;
|
||||
io_r.end = n - data_size;
|
||||
return data_size;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
@ -1968,7 +1985,7 @@ pub const Stream = struct {
|
||||
|
||||
pub const Error = ReadError;
|
||||
|
||||
pub fn interface(r: *Reader) *io.Reader {
|
||||
pub fn interface(r: *Reader) *Io.Reader {
|
||||
return &r.file_reader.interface;
|
||||
}
|
||||
|
||||
@ -1996,7 +2013,7 @@ pub const Stream = struct {
|
||||
pub const Writer = switch (native_os) {
|
||||
.windows => struct {
|
||||
/// This field is present on all systems.
|
||||
interface: io.Writer,
|
||||
interface: Io.Writer,
|
||||
/// Use `getStream` for cross-platform support.
|
||||
stream: Stream,
|
||||
/// This field is present on all systems.
|
||||
@ -2034,7 +2051,7 @@ pub const Stream = struct {
|
||||
}
|
||||
}
|
||||
|
||||
fn drain(io_w: *io.Writer, data: []const []const u8, splat: usize) io.Writer.Error!usize {
|
||||
fn drain(io_w: *Io.Writer, data: []const []const u8, splat: usize) Io.Writer.Error!usize {
|
||||
const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w));
|
||||
const buffered = io_w.buffered();
|
||||
comptime assert(native_os == .windows);
|
||||
@ -2106,7 +2123,7 @@ pub const Stream = struct {
|
||||
},
|
||||
else => struct {
|
||||
/// This field is present on all systems.
|
||||
interface: io.Writer,
|
||||
interface: Io.Writer,
|
||||
|
||||
err: ?Error = null,
|
||||
file_writer: File.Writer,
|
||||
@ -2138,7 +2155,7 @@ pub const Stream = struct {
|
||||
i.* += 1;
|
||||
}
|
||||
|
||||
fn drain(io_w: *io.Writer, data: []const []const u8, splat: usize) io.Writer.Error!usize {
|
||||
fn drain(io_w: *Io.Writer, data: []const []const u8, splat: usize) Io.Writer.Error!usize {
|
||||
const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w));
|
||||
const buffered = io_w.buffered();
|
||||
var iovecs: [max_buffers_len]posix.iovec_const = undefined;
|
||||
@ -2190,7 +2207,7 @@ pub const Stream = struct {
|
||||
});
|
||||
}
|
||||
|
||||
fn sendFile(io_w: *io.Writer, file_reader: *File.Reader, limit: io.Limit) io.Writer.FileError!usize {
|
||||
fn sendFile(io_w: *Io.Writer, file_reader: *File.Reader, limit: Io.Limit) Io.Writer.FileError!usize {
|
||||
const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w));
|
||||
const n = try w.file_writer.interface.sendFileHeader(io_w.buffered(), file_reader, limit);
|
||||
return io_w.consume(n);
|
||||
|
||||
@ -204,6 +204,7 @@ pub const ACCMODE = switch (native_os) {
|
||||
// implements this suggestion.
|
||||
// https://github.com/SerenityOS/serenity/blob/4adc51fdf6af7d50679c48b39362e062f5a3b2cb/Kernel/API/POSIX/fcntl.h#L28-L30
|
||||
.serenity => enum(u2) {
|
||||
NONE = 0,
|
||||
RDONLY = 1,
|
||||
WRONLY = 2,
|
||||
RDWR = 3,
|
||||
@ -1128,8 +1129,9 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
|
||||
/// * Windows
|
||||
/// On these systems, the read races with concurrent writes to the same file descriptor.
|
||||
pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) PReadError!usize {
|
||||
// NOTE: serenity does not have preadv but it *does* have pwritev.
|
||||
const have_pread_but_not_preadv = switch (native_os) {
|
||||
.windows, .macos, .ios, .watchos, .tvos, .visionos, .haiku => true,
|
||||
.windows, .macos, .ios, .watchos, .tvos, .visionos, .haiku, .serenity => true,
|
||||
else => false,
|
||||
};
|
||||
if (have_pread_but_not_preadv) {
|
||||
|
||||
@ -626,7 +626,6 @@ pub inline fn callMain() u8 {
|
||||
|
||||
const result = root.main() catch |err| {
|
||||
switch (builtin.zig_backend) {
|
||||
.stage2_aarch64,
|
||||
.stage2_powerpc,
|
||||
.stage2_riscv64,
|
||||
=> {
|
||||
@ -700,6 +699,7 @@ fn maybeIgnoreSigpipe() void {
|
||||
.visionos,
|
||||
.dragonfly,
|
||||
.freebsd,
|
||||
.serenity,
|
||||
=> true,
|
||||
|
||||
else => false,
|
||||
|
||||
@ -321,6 +321,27 @@ pub const BuildId = union(enum) {
|
||||
try std.testing.expectError(error.InvalidCharacter, parse("0xfoobbb"));
|
||||
try std.testing.expectError(error.InvalidBuildIdStyle, parse("yaddaxxx"));
|
||||
}
|
||||
|
||||
pub fn format(id: BuildId, writer: *std.io.Writer) std.io.Writer.Error!void {
|
||||
switch (id) {
|
||||
.none, .fast, .uuid, .sha1, .md5 => {
|
||||
try writer.writeAll(@tagName(id));
|
||||
},
|
||||
.hexstring => |hs| {
|
||||
try writer.print("0x{x}", .{hs.toSlice()});
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
test format {
|
||||
try std.testing.expectFmt("none", "{f}", .{@as(BuildId, .none)});
|
||||
try std.testing.expectFmt("fast", "{f}", .{@as(BuildId, .fast)});
|
||||
try std.testing.expectFmt("uuid", "{f}", .{@as(BuildId, .uuid)});
|
||||
try std.testing.expectFmt("sha1", "{f}", .{@as(BuildId, .sha1)});
|
||||
try std.testing.expectFmt("md5", "{f}", .{@as(BuildId, .md5)});
|
||||
try std.testing.expectFmt("0x", "{f}", .{BuildId.initHexString("")});
|
||||
try std.testing.expectFmt("0x1234cdef", "{f}", .{BuildId.initHexString("\x12\x34\xcd\xef")});
|
||||
}
|
||||
};
|
||||
|
||||
pub const LtoMode = enum { none, full, thin };
|
||||
@ -364,23 +385,23 @@ pub fn serializeCpuAlloc(ally: Allocator, cpu: std.Target.Cpu) Allocator.Error![
|
||||
/// Return a Formatter for a Zig identifier, escaping it with `@""` syntax if needed.
|
||||
///
|
||||
/// See also `fmtIdFlags`.
|
||||
pub fn fmtId(bytes: []const u8) std.fmt.Formatter(FormatId, FormatId.render) {
|
||||
return .{ .data = .{ .bytes = bytes, .flags = .{} } };
|
||||
pub fn fmtId(bytes: []const u8) FormatId {
|
||||
return .{ .bytes = bytes, .flags = .{} };
|
||||
}
|
||||
|
||||
/// Return a Formatter for a Zig identifier, escaping it with `@""` syntax if needed.
|
||||
///
|
||||
/// See also `fmtId`.
|
||||
pub fn fmtIdFlags(bytes: []const u8, flags: FormatId.Flags) std.fmt.Formatter(FormatId, FormatId.render) {
|
||||
return .{ .data = .{ .bytes = bytes, .flags = flags } };
|
||||
pub fn fmtIdFlags(bytes: []const u8, flags: FormatId.Flags) FormatId {
|
||||
return .{ .bytes = bytes, .flags = flags };
|
||||
}
|
||||
|
||||
pub fn fmtIdPU(bytes: []const u8) std.fmt.Formatter(FormatId, FormatId.render) {
|
||||
return .{ .data = .{ .bytes = bytes, .flags = .{ .allow_primitive = true, .allow_underscore = true } } };
|
||||
pub fn fmtIdPU(bytes: []const u8) FormatId {
|
||||
return .{ .bytes = bytes, .flags = .{ .allow_primitive = true, .allow_underscore = true } };
|
||||
}
|
||||
|
||||
pub fn fmtIdP(bytes: []const u8) std.fmt.Formatter(FormatId, FormatId.render) {
|
||||
return .{ .data = .{ .bytes = bytes, .flags = .{ .allow_primitive = true } } };
|
||||
pub fn fmtIdP(bytes: []const u8) FormatId {
|
||||
return .{ .bytes = bytes, .flags = .{ .allow_primitive = true } };
|
||||
}
|
||||
|
||||
test fmtId {
|
||||
@ -426,7 +447,7 @@ pub const FormatId = struct {
|
||||
};
|
||||
|
||||
/// Print the string as a Zig identifier, escaping it with `@""` syntax if needed.
|
||||
fn render(ctx: FormatId, writer: *Writer) Writer.Error!void {
|
||||
pub fn format(ctx: FormatId, writer: *Writer) Writer.Error!void {
|
||||
const bytes = ctx.bytes;
|
||||
if (isValidId(bytes) and
|
||||
(ctx.flags.allow_primitive or !std.zig.isPrimitive(bytes)) and
|
||||
|
||||
@ -3361,7 +3361,7 @@ pub const Node = struct {
|
||||
/// The `main_token` might be a ** token, which is shared with a
|
||||
/// parent/child pointer type and may require special handling.
|
||||
ptr_type_sentinel,
|
||||
/// The `data` field is a `.opt_node_and_node`:
|
||||
/// The `data` field is a `.extra_and_node`:
|
||||
/// 1. a `ExtraIndex` to `PtrType`.
|
||||
/// 2. a `Node.Index` to the element type expression.
|
||||
///
|
||||
@ -3370,7 +3370,7 @@ pub const Node = struct {
|
||||
/// The `main_token` might be a ** token, which is shared with a
|
||||
/// parent/child pointer type and may require special handling.
|
||||
ptr_type,
|
||||
/// The `data` field is a `.opt_node_and_node`:
|
||||
/// The `data` field is a `.extra_and_node`:
|
||||
/// 1. a `ExtraIndex` to `PtrTypeBitRange`.
|
||||
/// 2. a `Node.Index` to the element type expression.
|
||||
///
|
||||
|
||||
@ -7,8 +7,9 @@ const builtin = @import("builtin");
|
||||
const std = @import("std");
|
||||
const File = std.fs.File;
|
||||
const is_le = builtin.target.cpu.arch.endian() == .little;
|
||||
const Writer = std.io.Writer;
|
||||
const Reader = std.io.Reader;
|
||||
const Writer = std.Io.Writer;
|
||||
const Reader = std.Io.Reader;
|
||||
const flate = std.compress.flate;
|
||||
|
||||
pub const CompressionMethod = enum(u16) {
|
||||
store = 0,
|
||||
@ -117,6 +118,7 @@ pub const EndRecord = extern struct {
|
||||
pub const FindFileError = File.GetEndPosError || File.SeekError || File.ReadError || error{
|
||||
ZipNoEndRecord,
|
||||
EndOfStream,
|
||||
ReadFailed,
|
||||
};
|
||||
|
||||
pub fn findFile(fr: *File.Reader) FindFileError!EndRecord {
|
||||
@ -137,8 +139,7 @@ pub const EndRecord = extern struct {
|
||||
|
||||
try fr.seekTo(end_pos - @as(u64, new_loaded_len));
|
||||
const read_buf: []u8 = buf[buf.len - new_loaded_len ..][0..read_len];
|
||||
var br = fr.interface().unbuffered();
|
||||
br.readSlice(read_buf) catch |err| switch (err) {
|
||||
fr.interface.readSliceAll(read_buf) catch |err| switch (err) {
|
||||
error.ReadFailed => return fr.err.?,
|
||||
error.EndOfStream => return error.EndOfStream,
|
||||
};
|
||||
@ -164,7 +165,7 @@ pub const EndRecord = extern struct {
|
||||
pub const Decompress = struct {
|
||||
interface: Reader,
|
||||
state: union {
|
||||
inflate: std.compress.flate.Decompress,
|
||||
inflate: flate.Decompress,
|
||||
store: *Reader,
|
||||
},
|
||||
|
||||
@ -201,7 +202,7 @@ pub const Decompress = struct {
|
||||
|
||||
fn streamDeflate(r: *Reader, w: *Writer, limit: std.io.Limit) Reader.StreamError!usize {
|
||||
const d: *Decompress = @fieldParentPtr("interface", r);
|
||||
return std.compress.flate.Decompress.read(&d.inflate, w, limit);
|
||||
return flate.Decompress.read(&d.inflate, w, limit);
|
||||
}
|
||||
};
|
||||
|
||||
@ -305,7 +306,7 @@ pub const Iterator = struct {
|
||||
if (locator_end_offset > stream_len)
|
||||
return error.ZipTruncated;
|
||||
try input.seekTo(stream_len - locator_end_offset);
|
||||
const locator = input.interface.takeStructEndian(EndLocator64, .little) catch |err| switch (err) {
|
||||
const locator = input.interface.takeStruct(EndLocator64, .little) catch |err| switch (err) {
|
||||
error.ReadFailed => return input.err.?,
|
||||
error.EndOfStream => return error.EndOfStream,
|
||||
};
|
||||
@ -318,7 +319,7 @@ pub const Iterator = struct {
|
||||
|
||||
try input.seekTo(locator.record_file_offset);
|
||||
|
||||
const record64 = input.interface.takeStructEndian(EndRecord64, .little) catch |err| switch (err) {
|
||||
const record64 = input.interface.takeStruct(EndRecord64, .little) catch |err| switch (err) {
|
||||
error.ReadFailed => return input.err.?,
|
||||
error.EndOfStream => return error.EndOfStream,
|
||||
};
|
||||
@ -374,7 +375,7 @@ pub const Iterator = struct {
|
||||
const header_zip_offset = self.cd_zip_offset + self.cd_record_offset;
|
||||
const input = self.input;
|
||||
try input.seekTo(header_zip_offset);
|
||||
const header = input.interface.takeStructEndian(CentralDirectoryFileHeader, .little) catch |err| switch (err) {
|
||||
const header = input.interface.takeStruct(CentralDirectoryFileHeader, .little) catch |err| switch (err) {
|
||||
error.ReadFailed => return input.err.?,
|
||||
error.EndOfStream => return error.EndOfStream,
|
||||
};
|
||||
@ -405,7 +406,7 @@ pub const Iterator = struct {
|
||||
const extra = extra_buf[0..header.extra_len];
|
||||
|
||||
try input.seekTo(header_zip_offset + @sizeOf(CentralDirectoryFileHeader) + header.filename_len);
|
||||
input.interface.readSlice(extra) catch |err| switch (err) {
|
||||
input.interface.readSliceAll(extra) catch |err| switch (err) {
|
||||
error.ReadFailed => return input.err.?,
|
||||
error.EndOfStream => return error.EndOfStream,
|
||||
};
|
||||
@ -460,7 +461,7 @@ pub const Iterator = struct {
|
||||
options: ExtractOptions,
|
||||
filename_buf: []u8,
|
||||
dest: std.fs.Dir,
|
||||
) !u32 {
|
||||
) !void {
|
||||
if (filename_buf.len < self.filename_len)
|
||||
return error.ZipInsufficientBuffer;
|
||||
switch (self.compression_method) {
|
||||
@ -470,13 +471,13 @@ pub const Iterator = struct {
|
||||
const filename = filename_buf[0..self.filename_len];
|
||||
{
|
||||
try stream.seekTo(self.header_zip_offset + @sizeOf(CentralDirectoryFileHeader));
|
||||
try stream.interface.readSlice(filename);
|
||||
try stream.interface.readSliceAll(filename);
|
||||
}
|
||||
|
||||
const local_data_header_offset: u64 = local_data_header_offset: {
|
||||
const local_header = blk: {
|
||||
try stream.seekTo(self.file_offset);
|
||||
break :blk try stream.interface.takeStructEndian(LocalFileHeader, .little);
|
||||
break :blk try stream.interface.takeStruct(LocalFileHeader, .little);
|
||||
};
|
||||
if (!std.mem.eql(u8, &local_header.signature, &local_file_header_sig))
|
||||
return error.ZipBadFileOffset;
|
||||
@ -502,7 +503,7 @@ pub const Iterator = struct {
|
||||
|
||||
{
|
||||
try stream.seekTo(self.file_offset + @sizeOf(LocalFileHeader) + local_header.filename_len);
|
||||
try stream.interface.readSlice(extra);
|
||||
try stream.interface.readSliceAll(extra);
|
||||
}
|
||||
|
||||
var extra_offset: usize = 0;
|
||||
@ -550,7 +551,7 @@ pub const Iterator = struct {
|
||||
if (self.uncompressed_size != 0)
|
||||
return error.ZipBadDirectorySize;
|
||||
try dest.makePath(filename[0 .. filename.len - 1]);
|
||||
return std.hash.Crc32.hash(&.{});
|
||||
return;
|
||||
}
|
||||
|
||||
const out_file = blk: {
|
||||
@ -564,31 +565,36 @@ pub const Iterator = struct {
|
||||
break :blk try dest.createFile(filename, .{ .exclusive = true });
|
||||
};
|
||||
defer out_file.close();
|
||||
var file_writer = out_file.writer();
|
||||
var file_bw = file_writer.writer(&.{});
|
||||
var out_file_buffer: [1024]u8 = undefined;
|
||||
var file_writer = out_file.writer(&out_file_buffer);
|
||||
const local_data_file_offset: u64 =
|
||||
@as(u64, self.file_offset) +
|
||||
@as(u64, @sizeOf(LocalFileHeader)) +
|
||||
local_data_header_offset;
|
||||
try stream.seekTo(local_data_file_offset);
|
||||
var limited_file_reader = stream.interface.limited(.limited(self.compressed_size));
|
||||
var file_read_buffer: [1000]u8 = undefined;
|
||||
var decompress_read_buffer: [1000]u8 = undefined;
|
||||
var limited_br = limited_file_reader.reader().buffered(&file_read_buffer);
|
||||
var decompress: Decompress = undefined;
|
||||
var decompress_br = decompress.readable(&limited_br, self.compression_method, &decompress_read_buffer);
|
||||
const start_out = file_bw.count;
|
||||
var hash_writer = file_bw.hashed(std.hash.Crc32.init());
|
||||
var hash_bw = hash_writer.writer(&.{});
|
||||
decompress_br.readAll(&hash_bw, .limited(self.uncompressed_size)) catch |err| switch (err) {
|
||||
error.ReadFailed => return stream.err.?,
|
||||
error.WriteFailed => return file_writer.err.?,
|
||||
error.EndOfStream => return error.ZipDecompressTruncated,
|
||||
};
|
||||
if (limited_file_reader.remaining.nonzero()) return error.ZipDecompressTruncated;
|
||||
const written = file_bw.count - start_out;
|
||||
if (written != self.uncompressed_size) return error.ZipUncompressSizeMismatch;
|
||||
return hash_writer.hasher.final();
|
||||
|
||||
// TODO limit based on self.compressed_size
|
||||
|
||||
switch (self.compression_method) {
|
||||
.store => {
|
||||
stream.interface.streamExact64(&file_writer.interface, self.uncompressed_size) catch |err| switch (err) {
|
||||
error.ReadFailed => return stream.err.?,
|
||||
error.WriteFailed => return file_writer.err.?,
|
||||
error.EndOfStream => return error.ZipDecompressTruncated,
|
||||
};
|
||||
},
|
||||
.deflate => {
|
||||
var flate_buffer: [flate.max_window_len]u8 = undefined;
|
||||
var decompress: flate.Decompress = .init(&stream.interface, .raw, &flate_buffer);
|
||||
decompress.reader.streamExact64(&file_writer.interface, self.uncompressed_size) catch |err| switch (err) {
|
||||
error.ReadFailed => return stream.err.?,
|
||||
error.WriteFailed => return file_writer.err orelse decompress.err.?,
|
||||
error.EndOfStream => return error.ZipDecompressTruncated,
|
||||
};
|
||||
},
|
||||
else => return error.UnsupportedCompressionMethod,
|
||||
}
|
||||
try file_writer.end();
|
||||
}
|
||||
};
|
||||
};
|
||||
@ -636,25 +642,21 @@ pub const ExtractOptions = struct {
|
||||
/// Allow filenames within the zip to use backslashes. Back slashes are normalized
|
||||
/// to forward slashes before forwarding them to platform APIs.
|
||||
allow_backslashes: bool = false,
|
||||
|
||||
diagnostics: ?*Diagnostics = null,
|
||||
verify_checksums: bool = false,
|
||||
};
|
||||
|
||||
/// Extract the zipped files to the given `dest` directory.
|
||||
pub fn extract(dest: std.fs.Dir, fr: *File.Reader, options: ExtractOptions) !void {
|
||||
if (options.verify_checksums) @panic("TODO unimplemented");
|
||||
|
||||
var iter = try Iterator.init(fr);
|
||||
|
||||
var filename_buf: [std.fs.max_path_bytes]u8 = undefined;
|
||||
while (try iter.next()) |entry| {
|
||||
const crc32 = try entry.extract(fr, options, &filename_buf, dest);
|
||||
if (crc32 != entry.crc32)
|
||||
return error.ZipCrcMismatch;
|
||||
try entry.extract(fr, options, &filename_buf, dest);
|
||||
if (options.diagnostics) |d| {
|
||||
try d.nextFilename(filename_buf[0..entry.filename_len]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test {
|
||||
_ = @import("zip/test.zig");
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user