Merge pull request #4710 from ziglang/io-stream-iface
rework I/O stream abstractions
This commit is contained in:
commit
895f67cc6d
@ -40,12 +40,9 @@ pub fn main() !void {
|
||||
var out_file = try fs.cwd().createFile(out_file_name, .{});
|
||||
defer out_file.close();
|
||||
|
||||
var file_in_stream = in_file.inStream();
|
||||
const input_file_bytes = try in_file.inStream().readAllAlloc(allocator, max_doc_file_size);
|
||||
|
||||
const input_file_bytes = try file_in_stream.stream.readAllAlloc(allocator, max_doc_file_size);
|
||||
|
||||
var file_out_stream = out_file.outStream();
|
||||
var buffered_out_stream = io.BufferedOutStream(fs.File.WriteError).init(&file_out_stream.stream);
|
||||
var buffered_out_stream = io.bufferedOutStream(out_file.outStream());
|
||||
|
||||
var tokenizer = Tokenizer.init(in_file_name, input_file_bytes);
|
||||
var toc = try genToc(allocator, &tokenizer);
|
||||
@ -53,7 +50,7 @@ pub fn main() !void {
|
||||
try fs.cwd().makePath(tmp_dir_name);
|
||||
defer fs.deleteTree(tmp_dir_name) catch {};
|
||||
|
||||
try genHtml(allocator, &tokenizer, &toc, &buffered_out_stream.stream, zig_exe);
|
||||
try genHtml(allocator, &tokenizer, &toc, buffered_out_stream.outStream(), zig_exe);
|
||||
try buffered_out_stream.flush();
|
||||
}
|
||||
|
||||
@ -327,8 +324,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {
|
||||
var toc_buf = try std.Buffer.initSize(allocator, 0);
|
||||
defer toc_buf.deinit();
|
||||
|
||||
var toc_buf_adapter = io.BufferOutStream.init(&toc_buf);
|
||||
var toc = &toc_buf_adapter.stream;
|
||||
var toc = toc_buf.outStream();
|
||||
|
||||
var nodes = std.ArrayList(Node).init(allocator);
|
||||
defer nodes.deinit();
|
||||
@ -342,7 +338,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {
|
||||
if (header_stack_size != 0) {
|
||||
return parseError(tokenizer, token, "unbalanced headers", .{});
|
||||
}
|
||||
try toc.write(" </ul>\n");
|
||||
try toc.writeAll(" </ul>\n");
|
||||
break;
|
||||
},
|
||||
Token.Id.Content => {
|
||||
@ -407,7 +403,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {
|
||||
if (last_columns) |n| {
|
||||
try toc.print("<ul style=\"columns: {}\">\n", .{n});
|
||||
} else {
|
||||
try toc.write("<ul>\n");
|
||||
try toc.writeAll("<ul>\n");
|
||||
}
|
||||
} else {
|
||||
last_action = Action.Open;
|
||||
@ -424,9 +420,9 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {
|
||||
|
||||
if (last_action == Action.Close) {
|
||||
try toc.writeByteNTimes(' ', 8 + header_stack_size * 4);
|
||||
try toc.write("</ul></li>\n");
|
||||
try toc.writeAll("</ul></li>\n");
|
||||
} else {
|
||||
try toc.write("</li>\n");
|
||||
try toc.writeAll("</li>\n");
|
||||
last_action = Action.Close;
|
||||
}
|
||||
} else if (mem.eql(u8, tag_name, "see_also")) {
|
||||
@ -614,8 +610,7 @@ fn urlize(allocator: *mem.Allocator, input: []const u8) ![]u8 {
|
||||
var buf = try std.Buffer.initSize(allocator, 0);
|
||||
defer buf.deinit();
|
||||
|
||||
var buf_adapter = io.BufferOutStream.init(&buf);
|
||||
var out = &buf_adapter.stream;
|
||||
const out = buf.outStream();
|
||||
for (input) |c| {
|
||||
switch (c) {
|
||||
'a'...'z', 'A'...'Z', '_', '-', '0'...'9' => {
|
||||
@ -634,8 +629,7 @@ fn escapeHtml(allocator: *mem.Allocator, input: []const u8) ![]u8 {
|
||||
var buf = try std.Buffer.initSize(allocator, 0);
|
||||
defer buf.deinit();
|
||||
|
||||
var buf_adapter = io.BufferOutStream.init(&buf);
|
||||
var out = &buf_adapter.stream;
|
||||
const out = buf.outStream();
|
||||
try writeEscaped(out, input);
|
||||
return buf.toOwnedSlice();
|
||||
}
|
||||
@ -643,10 +637,10 @@ fn escapeHtml(allocator: *mem.Allocator, input: []const u8) ![]u8 {
|
||||
fn writeEscaped(out: var, input: []const u8) !void {
|
||||
for (input) |c| {
|
||||
try switch (c) {
|
||||
'&' => out.write("&"),
|
||||
'<' => out.write("<"),
|
||||
'>' => out.write(">"),
|
||||
'"' => out.write("""),
|
||||
'&' => out.writeAll("&"),
|
||||
'<' => out.writeAll("<"),
|
||||
'>' => out.writeAll(">"),
|
||||
'"' => out.writeAll("""),
|
||||
else => out.writeByte(c),
|
||||
};
|
||||
}
|
||||
@ -681,8 +675,7 @@ fn termColor(allocator: *mem.Allocator, input: []const u8) ![]u8 {
|
||||
var buf = try std.Buffer.initSize(allocator, 0);
|
||||
defer buf.deinit();
|
||||
|
||||
var buf_adapter = io.BufferOutStream.init(&buf);
|
||||
var out = &buf_adapter.stream;
|
||||
var out = buf.outStream();
|
||||
var number_start_index: usize = undefined;
|
||||
var first_number: usize = undefined;
|
||||
var second_number: usize = undefined;
|
||||
@ -743,7 +736,7 @@ fn termColor(allocator: *mem.Allocator, input: []const u8) ![]u8 {
|
||||
'm' => {
|
||||
state = TermState.Start;
|
||||
while (open_span_count != 0) : (open_span_count -= 1) {
|
||||
try out.write("</span>");
|
||||
try out.writeAll("</span>");
|
||||
}
|
||||
if (first_number != 0 or second_number != 0) {
|
||||
try out.print("<span class=\"t{}_{}\">", .{ first_number, second_number });
|
||||
@ -774,7 +767,7 @@ fn isType(name: []const u8) bool {
|
||||
|
||||
fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Token, raw_src: []const u8) !void {
|
||||
const src = mem.trim(u8, raw_src, " \n");
|
||||
try out.write("<code class=\"zig\">");
|
||||
try out.writeAll("<code class=\"zig\">");
|
||||
var tokenizer = std.zig.Tokenizer.init(src);
|
||||
var index: usize = 0;
|
||||
var next_tok_is_fn = false;
|
||||
@ -835,15 +828,15 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok
|
||||
.Keyword_allowzero,
|
||||
.Keyword_while,
|
||||
=> {
|
||||
try out.write("<span class=\"tok-kw\">");
|
||||
try out.writeAll("<span class=\"tok-kw\">");
|
||||
try writeEscaped(out, src[token.start..token.end]);
|
||||
try out.write("</span>");
|
||||
try out.writeAll("</span>");
|
||||
},
|
||||
|
||||
.Keyword_fn => {
|
||||
try out.write("<span class=\"tok-kw\">");
|
||||
try out.writeAll("<span class=\"tok-kw\">");
|
||||
try writeEscaped(out, src[token.start..token.end]);
|
||||
try out.write("</span>");
|
||||
try out.writeAll("</span>");
|
||||
next_tok_is_fn = true;
|
||||
},
|
||||
|
||||
@ -852,24 +845,24 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok
|
||||
.Keyword_true,
|
||||
.Keyword_false,
|
||||
=> {
|
||||
try out.write("<span class=\"tok-null\">");
|
||||
try out.writeAll("<span class=\"tok-null\">");
|
||||
try writeEscaped(out, src[token.start..token.end]);
|
||||
try out.write("</span>");
|
||||
try out.writeAll("</span>");
|
||||
},
|
||||
|
||||
.StringLiteral,
|
||||
.MultilineStringLiteralLine,
|
||||
.CharLiteral,
|
||||
=> {
|
||||
try out.write("<span class=\"tok-str\">");
|
||||
try out.writeAll("<span class=\"tok-str\">");
|
||||
try writeEscaped(out, src[token.start..token.end]);
|
||||
try out.write("</span>");
|
||||
try out.writeAll("</span>");
|
||||
},
|
||||
|
||||
.Builtin => {
|
||||
try out.write("<span class=\"tok-builtin\">");
|
||||
try out.writeAll("<span class=\"tok-builtin\">");
|
||||
try writeEscaped(out, src[token.start..token.end]);
|
||||
try out.write("</span>");
|
||||
try out.writeAll("</span>");
|
||||
},
|
||||
|
||||
.LineComment,
|
||||
@ -877,16 +870,16 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok
|
||||
.ContainerDocComment,
|
||||
.ShebangLine,
|
||||
=> {
|
||||
try out.write("<span class=\"tok-comment\">");
|
||||
try out.writeAll("<span class=\"tok-comment\">");
|
||||
try writeEscaped(out, src[token.start..token.end]);
|
||||
try out.write("</span>");
|
||||
try out.writeAll("</span>");
|
||||
},
|
||||
|
||||
.Identifier => {
|
||||
if (prev_tok_was_fn) {
|
||||
try out.write("<span class=\"tok-fn\">");
|
||||
try out.writeAll("<span class=\"tok-fn\">");
|
||||
try writeEscaped(out, src[token.start..token.end]);
|
||||
try out.write("</span>");
|
||||
try out.writeAll("</span>");
|
||||
} else {
|
||||
const is_int = blk: {
|
||||
if (src[token.start] != 'i' and src[token.start] != 'u')
|
||||
@ -901,9 +894,9 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok
|
||||
break :blk true;
|
||||
};
|
||||
if (is_int or isType(src[token.start..token.end])) {
|
||||
try out.write("<span class=\"tok-type\">");
|
||||
try out.writeAll("<span class=\"tok-type\">");
|
||||
try writeEscaped(out, src[token.start..token.end]);
|
||||
try out.write("</span>");
|
||||
try out.writeAll("</span>");
|
||||
} else {
|
||||
try writeEscaped(out, src[token.start..token.end]);
|
||||
}
|
||||
@ -913,9 +906,9 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok
|
||||
.IntegerLiteral,
|
||||
.FloatLiteral,
|
||||
=> {
|
||||
try out.write("<span class=\"tok-number\">");
|
||||
try out.writeAll("<span class=\"tok-number\">");
|
||||
try writeEscaped(out, src[token.start..token.end]);
|
||||
try out.write("</span>");
|
||||
try out.writeAll("</span>");
|
||||
},
|
||||
|
||||
.Bang,
|
||||
@ -983,7 +976,7 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok
|
||||
}
|
||||
index = token.end;
|
||||
}
|
||||
try out.write("</code>");
|
||||
try out.writeAll("</code>");
|
||||
}
|
||||
|
||||
fn tokenizeAndPrint(docgen_tokenizer: *Tokenizer, out: var, source_token: Token) !void {
|
||||
@ -1002,7 +995,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
|
||||
for (toc.nodes) |node| {
|
||||
switch (node) {
|
||||
.Content => |data| {
|
||||
try out.write(data);
|
||||
try out.writeAll(data);
|
||||
},
|
||||
.Link => |info| {
|
||||
if (!toc.urls.contains(info.url)) {
|
||||
@ -1011,12 +1004,12 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
|
||||
try out.print("<a href=\"#{}\">{}</a>", .{ info.url, info.name });
|
||||
},
|
||||
.Nav => {
|
||||
try out.write(toc.toc);
|
||||
try out.writeAll(toc.toc);
|
||||
},
|
||||
.Builtin => |tok| {
|
||||
try out.write("<pre>");
|
||||
try out.writeAll("<pre>");
|
||||
try tokenizeAndPrintRaw(tokenizer, out, tok, builtin_code);
|
||||
try out.write("</pre>");
|
||||
try out.writeAll("</pre>");
|
||||
},
|
||||
.HeaderOpen => |info| {
|
||||
try out.print(
|
||||
@ -1025,7 +1018,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
|
||||
);
|
||||
},
|
||||
.SeeAlso => |items| {
|
||||
try out.write("<p>See also:</p><ul>\n");
|
||||
try out.writeAll("<p>See also:</p><ul>\n");
|
||||
for (items) |item| {
|
||||
const url = try urlize(allocator, item.name);
|
||||
if (!toc.urls.contains(url)) {
|
||||
@ -1033,7 +1026,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
|
||||
}
|
||||
try out.print("<li><a href=\"#{}\">{}</a></li>\n", .{ url, item.name });
|
||||
}
|
||||
try out.write("</ul>\n");
|
||||
try out.writeAll("</ul>\n");
|
||||
},
|
||||
.Syntax => |content_tok| {
|
||||
try tokenizeAndPrint(tokenizer, out, content_tok);
|
||||
@ -1047,9 +1040,9 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
|
||||
if (!code.is_inline) {
|
||||
try out.print("<p class=\"file\">{}.zig</p>", .{code.name});
|
||||
}
|
||||
try out.write("<pre>");
|
||||
try out.writeAll("<pre>");
|
||||
try tokenizeAndPrint(tokenizer, out, code.source_token);
|
||||
try out.write("</pre>");
|
||||
try out.writeAll("</pre>");
|
||||
const name_plus_ext = try std.fmt.allocPrint(allocator, "{}.zig", .{code.name});
|
||||
const tmp_source_file_name = try fs.path.join(
|
||||
allocator,
|
||||
|
@ -230,7 +230,7 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub fn main() !void {
|
||||
const stdout = &std.io.getStdOut().outStream().stream;
|
||||
const stdout = std.io.getStdOut().outStream();
|
||||
try stdout.print("Hello, {}!\n", .{"world"});
|
||||
}
|
||||
{#code_end#}
|
||||
|
@ -104,21 +104,17 @@ pub fn Queue(comptime T: type) type {
|
||||
}
|
||||
|
||||
pub fn dump(self: *Self) void {
|
||||
var stderr_file = std.io.getStdErr() catch return;
|
||||
const stderr = &stderr_file.outStream().stream;
|
||||
const Error = @typeInfo(@TypeOf(stderr)).Pointer.child.Error;
|
||||
|
||||
self.dumpToStream(Error, stderr) catch return;
|
||||
self.dumpToStream(std.io.getStdErr().outStream()) catch return;
|
||||
}
|
||||
|
||||
pub fn dumpToStream(self: *Self, comptime Error: type, stream: *std.io.OutStream(Error)) Error!void {
|
||||
pub fn dumpToStream(self: *Self, stream: var) !void {
|
||||
const S = struct {
|
||||
fn dumpRecursive(
|
||||
s: *std.io.OutStream(Error),
|
||||
s: var,
|
||||
optional_node: ?*Node,
|
||||
indent: usize,
|
||||
comptime depth: comptime_int,
|
||||
) Error!void {
|
||||
) !void {
|
||||
try s.writeByteNTimes(' ', indent);
|
||||
if (optional_node) |node| {
|
||||
try s.print("0x{x}={}\n", .{ @ptrToInt(node), node.data });
|
||||
@ -326,17 +322,16 @@ test "std.atomic.Queue single-threaded" {
|
||||
|
||||
test "std.atomic.Queue dump" {
|
||||
const mem = std.mem;
|
||||
const SliceOutStream = std.io.SliceOutStream;
|
||||
var buffer: [1024]u8 = undefined;
|
||||
var expected_buffer: [1024]u8 = undefined;
|
||||
var sos = SliceOutStream.init(buffer[0..]);
|
||||
var fbs = std.io.fixedBufferStream(&buffer);
|
||||
|
||||
var queue = Queue(i32).init();
|
||||
|
||||
// Test empty stream
|
||||
sos.reset();
|
||||
try queue.dumpToStream(SliceOutStream.Error, &sos.stream);
|
||||
expect(mem.eql(u8, buffer[0..sos.pos],
|
||||
fbs.reset();
|
||||
try queue.dumpToStream(fbs.outStream());
|
||||
expect(mem.eql(u8, buffer[0..fbs.pos],
|
||||
\\head: (null)
|
||||
\\tail: (null)
|
||||
\\
|
||||
@ -350,8 +345,8 @@ test "std.atomic.Queue dump" {
|
||||
};
|
||||
queue.put(&node_0);
|
||||
|
||||
sos.reset();
|
||||
try queue.dumpToStream(SliceOutStream.Error, &sos.stream);
|
||||
fbs.reset();
|
||||
try queue.dumpToStream(fbs.outStream());
|
||||
|
||||
var expected = try std.fmt.bufPrint(expected_buffer[0..],
|
||||
\\head: 0x{x}=1
|
||||
@ -360,7 +355,7 @@ test "std.atomic.Queue dump" {
|
||||
\\ (null)
|
||||
\\
|
||||
, .{ @ptrToInt(queue.head), @ptrToInt(queue.tail) });
|
||||
expect(mem.eql(u8, buffer[0..sos.pos], expected));
|
||||
expect(mem.eql(u8, buffer[0..fbs.pos], expected));
|
||||
|
||||
// Test a stream with two elements
|
||||
var node_1 = Queue(i32).Node{
|
||||
@ -370,8 +365,8 @@ test "std.atomic.Queue dump" {
|
||||
};
|
||||
queue.put(&node_1);
|
||||
|
||||
sos.reset();
|
||||
try queue.dumpToStream(SliceOutStream.Error, &sos.stream);
|
||||
fbs.reset();
|
||||
try queue.dumpToStream(fbs.outStream());
|
||||
|
||||
expected = try std.fmt.bufPrint(expected_buffer[0..],
|
||||
\\head: 0x{x}=1
|
||||
@ -381,5 +376,5 @@ test "std.atomic.Queue dump" {
|
||||
\\ (null)
|
||||
\\
|
||||
, .{ @ptrToInt(queue.head), @ptrToInt(queue.head.?.next), @ptrToInt(queue.tail) });
|
||||
expect(mem.eql(u8, buffer[0..sos.pos], expected));
|
||||
expect(mem.eql(u8, buffer[0..fbs.pos], expected));
|
||||
}
|
||||
|
@ -157,6 +157,17 @@ pub const Buffer = struct {
|
||||
pub fn print(self: *Buffer, comptime fmt: []const u8, args: var) !void {
|
||||
return std.fmt.format(self, error{OutOfMemory}, Buffer.append, fmt, args);
|
||||
}
|
||||
|
||||
pub fn outStream(self: *Buffer) std.io.OutStream(*Buffer, error{OutOfMemory}, appendWrite) {
|
||||
return .{ .context = self };
|
||||
}
|
||||
|
||||
/// Same as `append` except it returns the number of bytes written, which is always the same
|
||||
/// as `m.len`. The purpose of this function existing is to match `std.io.OutStream` API.
|
||||
pub fn appendWrite(self: *Buffer, m: []const u8) !usize {
|
||||
try self.append(m);
|
||||
return m.len;
|
||||
}
|
||||
};
|
||||
|
||||
test "simple Buffer" {
|
||||
@ -208,3 +219,15 @@ test "Buffer.print" {
|
||||
try buf.print("Hello {} the {}", .{ 2, "world" });
|
||||
testing.expect(buf.eql("Hello 2 the world"));
|
||||
}
|
||||
|
||||
test "Buffer.outStream" {
|
||||
var buffer = try Buffer.initSize(testing.allocator, 0);
|
||||
defer buffer.deinit();
|
||||
const buf_stream = buffer.outStream();
|
||||
|
||||
const x: i32 = 42;
|
||||
const y: i32 = 1234;
|
||||
try buf_stream.print("x: {}\ny: {}\n", .{ x, y });
|
||||
|
||||
testing.expect(mem.eql(u8, buffer.toSlice(), "x: 42\ny: 1234\n"));
|
||||
}
|
||||
|
@ -926,8 +926,7 @@ pub const Builder = struct {
|
||||
|
||||
try child.spawn();
|
||||
|
||||
var stdout_file_in_stream = child.stdout.?.inStream();
|
||||
const stdout = try stdout_file_in_stream.stream.readAllAlloc(self.allocator, max_output_size);
|
||||
const stdout = try child.stdout.?.inStream().readAllAlloc(self.allocator, max_output_size);
|
||||
errdefer self.allocator.free(stdout);
|
||||
|
||||
const term = try child.wait();
|
||||
|
@ -14,11 +14,6 @@ const io = std.io;
|
||||
const sort = std.sort;
|
||||
const warn = std.debug.warn;
|
||||
|
||||
const BinOutStream = io.OutStream(anyerror);
|
||||
const BinSeekStream = io.SeekableStream(anyerror, anyerror);
|
||||
const ElfSeekStream = io.SeekableStream(anyerror, anyerror);
|
||||
const ElfInStream = io.InStream(anyerror);
|
||||
|
||||
const BinaryElfSection = struct {
|
||||
elfOffset: u64,
|
||||
binaryOffset: u64,
|
||||
@ -41,22 +36,19 @@ const BinaryElfOutput = struct {
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn init(allocator: *Allocator) Self {
|
||||
return Self{
|
||||
.segments = ArrayList(*BinaryElfSegment).init(allocator),
|
||||
.sections = ArrayList(*BinaryElfSection).init(allocator),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
self.sections.deinit();
|
||||
self.segments.deinit();
|
||||
}
|
||||
|
||||
pub fn parseElf(self: *Self, elfFile: elf.Elf) !void {
|
||||
const allocator = self.segments.allocator;
|
||||
pub fn parse(allocator: *Allocator, elf_file: File) !Self {
|
||||
var self: Self = .{
|
||||
.segments = ArrayList(*BinaryElfSegment).init(allocator),
|
||||
.sections = ArrayList(*BinaryElfSection).init(allocator),
|
||||
};
|
||||
const elf_hdrs = try std.elf.readAllHeaders(allocator, elf_file);
|
||||
|
||||
for (elfFile.section_headers) |section, i| {
|
||||
for (elf_hdrs.section_headers) |section, i| {
|
||||
if (sectionValidForOutput(section)) {
|
||||
const newSection = try allocator.create(BinaryElfSection);
|
||||
|
||||
@ -69,19 +61,19 @@ const BinaryElfOutput = struct {
|
||||
}
|
||||
}
|
||||
|
||||
for (elfFile.program_headers) |programHeader, i| {
|
||||
if (programHeader.p_type == elf.PT_LOAD) {
|
||||
for (elf_hdrs.program_headers) |phdr, i| {
|
||||
if (phdr.p_type == elf.PT_LOAD) {
|
||||
const newSegment = try allocator.create(BinaryElfSegment);
|
||||
|
||||
newSegment.physicalAddress = if (programHeader.p_paddr != 0) programHeader.p_paddr else programHeader.p_vaddr;
|
||||
newSegment.virtualAddress = programHeader.p_vaddr;
|
||||
newSegment.fileSize = @intCast(usize, programHeader.p_filesz);
|
||||
newSegment.elfOffset = programHeader.p_offset;
|
||||
newSegment.physicalAddress = if (phdr.p_paddr != 0) phdr.p_paddr else phdr.p_vaddr;
|
||||
newSegment.virtualAddress = phdr.p_vaddr;
|
||||
newSegment.fileSize = @intCast(usize, phdr.p_filesz);
|
||||
newSegment.elfOffset = phdr.p_offset;
|
||||
newSegment.binaryOffset = 0;
|
||||
newSegment.firstSection = null;
|
||||
|
||||
for (self.sections.toSlice()) |section| {
|
||||
if (sectionWithinSegment(section, programHeader)) {
|
||||
if (sectionWithinSegment(section, phdr)) {
|
||||
if (section.segment) |sectionSegment| {
|
||||
if (sectionSegment.elfOffset > newSegment.elfOffset) {
|
||||
section.segment = newSegment;
|
||||
@ -126,14 +118,17 @@ const BinaryElfOutput = struct {
|
||||
}
|
||||
|
||||
sort.sort(*BinaryElfSection, self.sections.toSlice(), sectionSortCompare);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
fn sectionWithinSegment(section: *BinaryElfSection, segment: elf.ProgramHeader) bool {
|
||||
fn sectionWithinSegment(section: *BinaryElfSection, segment: elf.Elf64_Phdr) bool {
|
||||
return segment.p_offset <= section.elfOffset and (segment.p_offset + segment.p_filesz) >= (section.elfOffset + section.fileSize);
|
||||
}
|
||||
|
||||
fn sectionValidForOutput(section: elf.SectionHeader) bool {
|
||||
return section.sh_size > 0 and section.sh_type != elf.SHT_NOBITS and ((section.sh_flags & elf.SHF_ALLOC) == elf.SHF_ALLOC);
|
||||
fn sectionValidForOutput(shdr: var) bool {
|
||||
return shdr.sh_size > 0 and shdr.sh_type != elf.SHT_NOBITS and
|
||||
((shdr.sh_flags & elf.SHF_ALLOC) == elf.SHF_ALLOC);
|
||||
}
|
||||
|
||||
fn segmentSortCompare(left: *BinaryElfSegment, right: *BinaryElfSegment) bool {
|
||||
@ -151,60 +146,27 @@ const BinaryElfOutput = struct {
|
||||
}
|
||||
};
|
||||
|
||||
const WriteContext = struct {
|
||||
inStream: *ElfInStream,
|
||||
inSeekStream: *ElfSeekStream,
|
||||
outStream: *BinOutStream,
|
||||
outSeekStream: *BinSeekStream,
|
||||
};
|
||||
fn writeBinaryElfSection(elf_file: File, out_file: File, section: *BinaryElfSection) !void {
|
||||
try out_file.seekTo(section.binaryOffset);
|
||||
|
||||
fn writeBinaryElfSection(allocator: *Allocator, context: WriteContext, section: *BinaryElfSection) !void {
|
||||
var readBuffer = try allocator.alloc(u8, section.fileSize);
|
||||
defer allocator.free(readBuffer);
|
||||
|
||||
try context.inSeekStream.seekTo(section.elfOffset);
|
||||
_ = try context.inStream.read(readBuffer);
|
||||
|
||||
try context.outSeekStream.seekTo(section.binaryOffset);
|
||||
try context.outStream.write(readBuffer);
|
||||
try out_file.writeFileAll(elf_file, .{
|
||||
.in_offset = section.elfOffset,
|
||||
.in_len = section.fileSize,
|
||||
});
|
||||
}
|
||||
|
||||
fn emit_raw(allocator: *Allocator, elf_path: []const u8, raw_path: []const u8) !void {
|
||||
var arenaAlloc = ArenaAllocator.init(allocator);
|
||||
errdefer arenaAlloc.deinit();
|
||||
var arena_allocator = &arenaAlloc.allocator;
|
||||
fn emitRaw(allocator: *Allocator, elf_path: []const u8, raw_path: []const u8) !void {
|
||||
var elf_file = try fs.cwd().openFile(elf_path, .{});
|
||||
defer elf_file.close();
|
||||
|
||||
const currentDir = fs.cwd();
|
||||
var out_file = try fs.cwd().createFile(raw_path, .{});
|
||||
defer out_file.close();
|
||||
|
||||
var file = try currentDir.openFile(elf_path, File.OpenFlags{});
|
||||
defer file.close();
|
||||
var binary_elf_output = try BinaryElfOutput.parse(allocator, elf_file);
|
||||
defer binary_elf_output.deinit();
|
||||
|
||||
var fileInStream = file.inStream();
|
||||
var fileSeekStream = file.seekableStream();
|
||||
|
||||
var elfFile = try elf.Elf.openStream(allocator, @ptrCast(*ElfSeekStream, &fileSeekStream.stream), @ptrCast(*ElfInStream, &fileInStream.stream));
|
||||
defer elfFile.close();
|
||||
|
||||
var outFile = try currentDir.createFile(raw_path, File.CreateFlags{});
|
||||
defer outFile.close();
|
||||
|
||||
var outFileOutStream = outFile.outStream();
|
||||
var outFileSeekStream = outFile.seekableStream();
|
||||
|
||||
const writeContext = WriteContext{
|
||||
.inStream = @ptrCast(*ElfInStream, &fileInStream.stream),
|
||||
.inSeekStream = @ptrCast(*ElfSeekStream, &fileSeekStream.stream),
|
||||
.outStream = @ptrCast(*BinOutStream, &outFileOutStream.stream),
|
||||
.outSeekStream = @ptrCast(*BinSeekStream, &outFileSeekStream.stream),
|
||||
};
|
||||
|
||||
var binaryElfOutput = BinaryElfOutput.init(arena_allocator);
|
||||
defer binaryElfOutput.deinit();
|
||||
|
||||
try binaryElfOutput.parseElf(elfFile);
|
||||
|
||||
for (binaryElfOutput.sections.toSlice()) |section| {
|
||||
try writeBinaryElfSection(allocator, writeContext, section);
|
||||
for (binary_elf_output.sections.toSlice()) |section| {
|
||||
try writeBinaryElfSection(elf_file, out_file, section);
|
||||
}
|
||||
}
|
||||
|
||||
@ -250,6 +212,6 @@ pub const InstallRawStep = struct {
|
||||
const full_dest_path = builder.getInstallPath(self.dest_dir, self.dest_filename);
|
||||
|
||||
fs.cwd().makePath(builder.getInstallPath(self.dest_dir, "")) catch unreachable;
|
||||
try emit_raw(builder.allocator, full_src_path, full_dest_path);
|
||||
try emitRaw(builder.allocator, full_src_path, full_dest_path);
|
||||
}
|
||||
};
|
||||
|
@ -175,8 +175,7 @@ pub const RunStep = struct {
|
||||
|
||||
switch (self.stdout_action) {
|
||||
.expect_exact, .expect_matches => {
|
||||
var stdout_file_in_stream = child.stdout.?.inStream();
|
||||
stdout = stdout_file_in_stream.stream.readAllAlloc(self.builder.allocator, max_stdout_size) catch unreachable;
|
||||
stdout = child.stdout.?.inStream().readAllAlloc(self.builder.allocator, max_stdout_size) catch unreachable;
|
||||
},
|
||||
.inherit, .ignore => {},
|
||||
}
|
||||
@ -186,8 +185,7 @@ pub const RunStep = struct {
|
||||
|
||||
switch (self.stderr_action) {
|
||||
.expect_exact, .expect_matches => {
|
||||
var stderr_file_in_stream = child.stderr.?.inStream();
|
||||
stderr = stderr_file_in_stream.stream.readAllAlloc(self.builder.allocator, max_stdout_size) catch unreachable;
|
||||
stderr = child.stderr.?.inStream().readAllAlloc(self.builder.allocator, max_stdout_size) catch unreachable;
|
||||
},
|
||||
.inherit, .ignore => {},
|
||||
}
|
||||
|
@ -217,13 +217,13 @@ pub const ChildProcess = struct {
|
||||
|
||||
try child.spawn();
|
||||
|
||||
var stdout_file_in_stream = child.stdout.?.inStream();
|
||||
var stderr_file_in_stream = child.stderr.?.inStream();
|
||||
const stdout_in = child.stdout.?.inStream();
|
||||
const stderr_in = child.stderr.?.inStream();
|
||||
|
||||
// TODO need to poll to read these streams to prevent a deadlock (or rely on evented I/O).
|
||||
const stdout = try stdout_file_in_stream.stream.readAllAlloc(args.allocator, args.max_output_bytes);
|
||||
const stdout = try stdout_in.readAllAlloc(args.allocator, args.max_output_bytes);
|
||||
errdefer args.allocator.free(stdout);
|
||||
const stderr = try stderr_file_in_stream.stream.readAllAlloc(args.allocator, args.max_output_bytes);
|
||||
const stderr = try stderr_in.readAllAlloc(args.allocator, args.max_output_bytes);
|
||||
errdefer args.allocator.free(stderr);
|
||||
|
||||
return ExecResult{
|
||||
@ -780,7 +780,7 @@ fn windowsCreateCommandLine(allocator: *mem.Allocator, argv: []const []const u8)
|
||||
var buf = try Buffer.initSize(allocator, 0);
|
||||
defer buf.deinit();
|
||||
|
||||
var buf_stream = &io.BufferOutStream.init(&buf).stream;
|
||||
var buf_stream = buf.outStream();
|
||||
|
||||
for (argv) |arg, arg_i| {
|
||||
if (arg_i != 0) try buf.appendByte(' ');
|
||||
@ -857,8 +857,7 @@ fn writeIntFd(fd: i32, value: ErrInt) !void {
|
||||
.io_mode = .blocking,
|
||||
.async_block_allowed = File.async_block_allowed_yes,
|
||||
};
|
||||
const stream = &file.outStream().stream;
|
||||
stream.writeIntNative(u64, @intCast(u64, value)) catch return error.SystemResources;
|
||||
file.outStream().writeIntNative(u64, @intCast(u64, value)) catch return error.SystemResources;
|
||||
}
|
||||
|
||||
fn readIntFd(fd: i32) !ErrInt {
|
||||
@ -867,8 +866,7 @@ fn readIntFd(fd: i32) !ErrInt {
|
||||
.io_mode = .blocking,
|
||||
.async_block_allowed = File.async_block_allowed_yes,
|
||||
};
|
||||
const stream = &file.inStream().stream;
|
||||
return @intCast(ErrInt, stream.readIntNative(u64) catch return error.SystemResources);
|
||||
return @intCast(ErrInt, file.inStream().readIntNative(u64) catch return error.SystemResources);
|
||||
}
|
||||
|
||||
/// Caller must free result.
|
||||
|
@ -56,8 +56,7 @@ pub const Coff = struct {
|
||||
pub fn loadHeader(self: *Coff) !void {
|
||||
const pe_pointer_offset = 0x3C;
|
||||
|
||||
var file_stream = self.in_file.inStream();
|
||||
const in = &file_stream.stream;
|
||||
const in = self.in_file.inStream();
|
||||
|
||||
var magic: [2]u8 = undefined;
|
||||
try in.readNoEof(magic[0..]);
|
||||
@ -89,11 +88,11 @@ pub const Coff = struct {
|
||||
else => return error.InvalidMachine,
|
||||
}
|
||||
|
||||
try self.loadOptionalHeader(&file_stream);
|
||||
try self.loadOptionalHeader();
|
||||
}
|
||||
|
||||
fn loadOptionalHeader(self: *Coff, file_stream: *File.InStream) !void {
|
||||
const in = &file_stream.stream;
|
||||
fn loadOptionalHeader(self: *Coff) !void {
|
||||
const in = self.in_file.inStream();
|
||||
self.pe_header.magic = try in.readIntLittle(u16);
|
||||
// For now we're only interested in finding the reference to the .pdb,
|
||||
// so we'll skip most of this header, which size is different in 32
|
||||
@ -136,8 +135,7 @@ pub const Coff = struct {
|
||||
const debug_dir = &self.pe_header.data_directory[DEBUG_DIRECTORY];
|
||||
const file_offset = debug_dir.virtual_address - header.virtual_address + header.pointer_to_raw_data;
|
||||
|
||||
var file_stream = self.in_file.inStream();
|
||||
const in = &file_stream.stream;
|
||||
const in = self.in_file.inStream();
|
||||
try self.in_file.seekTo(file_offset);
|
||||
|
||||
// Find the correct DebugDirectoryEntry, and where its data is stored.
|
||||
@ -188,8 +186,7 @@ pub const Coff = struct {
|
||||
|
||||
try self.sections.ensureCapacity(self.coff_header.number_of_sections);
|
||||
|
||||
var file_stream = self.in_file.inStream();
|
||||
const in = &file_stream.stream;
|
||||
const in = self.in_file.inStream();
|
||||
|
||||
var name: [8]u8 = undefined;
|
||||
|
||||
|
@ -55,7 +55,7 @@ pub const LineInfo = struct {
|
||||
var stderr_file: File = undefined;
|
||||
var stderr_file_out_stream: File.OutStream = undefined;
|
||||
|
||||
var stderr_stream: ?*io.OutStream(File.WriteError) = null;
|
||||
var stderr_stream: ?*File.OutStream = null;
|
||||
var stderr_mutex = std.Mutex.init();
|
||||
|
||||
pub fn warn(comptime fmt: []const u8, args: var) void {
|
||||
@ -65,13 +65,13 @@ pub fn warn(comptime fmt: []const u8, args: var) void {
|
||||
noasync stderr.print(fmt, args) catch return;
|
||||
}
|
||||
|
||||
pub fn getStderrStream() *io.OutStream(File.WriteError) {
|
||||
pub fn getStderrStream() *File.OutStream {
|
||||
if (stderr_stream) |st| {
|
||||
return st;
|
||||
} else {
|
||||
stderr_file = io.getStdErr();
|
||||
stderr_file_out_stream = stderr_file.outStream();
|
||||
const st = &stderr_file_out_stream.stream;
|
||||
const st = &stderr_file_out_stream;
|
||||
stderr_stream = st;
|
||||
return st;
|
||||
}
|
||||
@ -408,15 +408,15 @@ pub const TTY = struct {
|
||||
windows_api,
|
||||
|
||||
fn setColor(conf: Config, out_stream: var, color: Color) void {
|
||||
switch (conf) {
|
||||
noasync switch (conf) {
|
||||
.no_color => return,
|
||||
.escape_codes => switch (color) {
|
||||
.Red => noasync out_stream.write(RED) catch return,
|
||||
.Green => noasync out_stream.write(GREEN) catch return,
|
||||
.Cyan => noasync out_stream.write(CYAN) catch return,
|
||||
.White, .Bold => noasync out_stream.write(WHITE) catch return,
|
||||
.Dim => noasync out_stream.write(DIM) catch return,
|
||||
.Reset => noasync out_stream.write(RESET) catch return,
|
||||
.Red => out_stream.writeAll(RED) catch return,
|
||||
.Green => out_stream.writeAll(GREEN) catch return,
|
||||
.Cyan => out_stream.writeAll(CYAN) catch return,
|
||||
.White, .Bold => out_stream.writeAll(WHITE) catch return,
|
||||
.Dim => out_stream.writeAll(DIM) catch return,
|
||||
.Reset => out_stream.writeAll(RESET) catch return,
|
||||
},
|
||||
.windows_api => if (builtin.os.tag == .windows) {
|
||||
const S = struct {
|
||||
@ -455,7 +455,7 @@ pub const TTY = struct {
|
||||
} else {
|
||||
unreachable;
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
@ -475,15 +475,15 @@ fn populateModule(di: *ModuleDebugInfo, mod: *Module) !void {
|
||||
|
||||
const modi = di.pdb.getStreamById(mod.mod_info.ModuleSymStream) orelse return error.MissingDebugInfo;
|
||||
|
||||
const signature = try modi.stream.readIntLittle(u32);
|
||||
const signature = try modi.inStream().readIntLittle(u32);
|
||||
if (signature != 4)
|
||||
return error.InvalidDebugInfo;
|
||||
|
||||
mod.symbols = try allocator.alloc(u8, mod.mod_info.SymByteSize - 4);
|
||||
try modi.stream.readNoEof(mod.symbols);
|
||||
try modi.inStream().readNoEof(mod.symbols);
|
||||
|
||||
mod.subsect_info = try allocator.alloc(u8, mod.mod_info.C13ByteSize);
|
||||
try modi.stream.readNoEof(mod.subsect_info);
|
||||
try modi.inStream().readNoEof(mod.subsect_info);
|
||||
|
||||
var sect_offset: usize = 0;
|
||||
var skip_len: usize = undefined;
|
||||
@ -565,38 +565,40 @@ fn printLineInfo(
|
||||
tty_config: TTY.Config,
|
||||
comptime printLineFromFile: var,
|
||||
) !void {
|
||||
tty_config.setColor(out_stream, .White);
|
||||
noasync {
|
||||
tty_config.setColor(out_stream, .White);
|
||||
|
||||
if (line_info) |*li| {
|
||||
try noasync out_stream.print("{}:{}:{}", .{ li.file_name, li.line, li.column });
|
||||
} else {
|
||||
try noasync out_stream.write("???:?:?");
|
||||
}
|
||||
if (line_info) |*li| {
|
||||
try out_stream.print("{}:{}:{}", .{ li.file_name, li.line, li.column });
|
||||
} else {
|
||||
try out_stream.writeAll("???:?:?");
|
||||
}
|
||||
|
||||
tty_config.setColor(out_stream, .Reset);
|
||||
try noasync out_stream.write(": ");
|
||||
tty_config.setColor(out_stream, .Dim);
|
||||
try noasync out_stream.print("0x{x} in {} ({})", .{ address, symbol_name, compile_unit_name });
|
||||
tty_config.setColor(out_stream, .Reset);
|
||||
try noasync out_stream.write("\n");
|
||||
tty_config.setColor(out_stream, .Reset);
|
||||
try out_stream.writeAll(": ");
|
||||
tty_config.setColor(out_stream, .Dim);
|
||||
try out_stream.print("0x{x} in {} ({})", .{ address, symbol_name, compile_unit_name });
|
||||
tty_config.setColor(out_stream, .Reset);
|
||||
try out_stream.writeAll("\n");
|
||||
|
||||
// Show the matching source code line if possible
|
||||
if (line_info) |li| {
|
||||
if (noasync printLineFromFile(out_stream, li)) {
|
||||
if (li.column > 0) {
|
||||
// The caret already takes one char
|
||||
const space_needed = @intCast(usize, li.column - 1);
|
||||
// Show the matching source code line if possible
|
||||
if (line_info) |li| {
|
||||
if (printLineFromFile(out_stream, li)) {
|
||||
if (li.column > 0) {
|
||||
// The caret already takes one char
|
||||
const space_needed = @intCast(usize, li.column - 1);
|
||||
|
||||
try noasync out_stream.writeByteNTimes(' ', space_needed);
|
||||
tty_config.setColor(out_stream, .Green);
|
||||
try noasync out_stream.write("^");
|
||||
tty_config.setColor(out_stream, .Reset);
|
||||
try out_stream.writeByteNTimes(' ', space_needed);
|
||||
tty_config.setColor(out_stream, .Green);
|
||||
try out_stream.writeAll("^");
|
||||
tty_config.setColor(out_stream, .Reset);
|
||||
}
|
||||
try out_stream.writeAll("\n");
|
||||
} else |err| switch (err) {
|
||||
error.EndOfFile, error.FileNotFound => {},
|
||||
error.BadPathName => {},
|
||||
else => return err,
|
||||
}
|
||||
try noasync out_stream.write("\n");
|
||||
} else |err| switch (err) {
|
||||
error.EndOfFile, error.FileNotFound => {},
|
||||
error.BadPathName => {},
|
||||
else => return err,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -609,21 +611,21 @@ pub const OpenSelfDebugInfoError = error{
|
||||
};
|
||||
|
||||
/// TODO resources https://github.com/ziglang/zig/issues/4353
|
||||
/// TODO once https://github.com/ziglang/zig/issues/3157 is fully implemented,
|
||||
/// make this `noasync fn` and remove the individual noasync calls.
|
||||
pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo {
|
||||
if (builtin.strip_debug_info)
|
||||
return error.MissingDebugInfo;
|
||||
if (@hasDecl(root, "os") and @hasDecl(root.os, "debug") and @hasDecl(root.os.debug, "openSelfDebugInfo")) {
|
||||
return noasync root.os.debug.openSelfDebugInfo(allocator);
|
||||
}
|
||||
switch (builtin.os.tag) {
|
||||
.linux,
|
||||
.freebsd,
|
||||
.macosx,
|
||||
.windows,
|
||||
=> return DebugInfo.init(allocator),
|
||||
else => @compileError("openSelfDebugInfo unsupported for this platform"),
|
||||
noasync {
|
||||
if (builtin.strip_debug_info)
|
||||
return error.MissingDebugInfo;
|
||||
if (@hasDecl(root, "os") and @hasDecl(root.os, "debug") and @hasDecl(root.os.debug, "openSelfDebugInfo")) {
|
||||
return root.os.debug.openSelfDebugInfo(allocator);
|
||||
}
|
||||
switch (builtin.os.tag) {
|
||||
.linux,
|
||||
.freebsd,
|
||||
.macosx,
|
||||
.windows,
|
||||
=> return DebugInfo.init(allocator),
|
||||
else => @compileError("openSelfDebugInfo unsupported for this platform"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -654,11 +656,11 @@ fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !
|
||||
try di.pdb.openFile(di.coff, path);
|
||||
|
||||
var pdb_stream = di.pdb.getStream(pdb.StreamType.Pdb) orelse return error.InvalidDebugInfo;
|
||||
const version = try pdb_stream.stream.readIntLittle(u32);
|
||||
const signature = try pdb_stream.stream.readIntLittle(u32);
|
||||
const age = try pdb_stream.stream.readIntLittle(u32);
|
||||
const version = try pdb_stream.inStream().readIntLittle(u32);
|
||||
const signature = try pdb_stream.inStream().readIntLittle(u32);
|
||||
const age = try pdb_stream.inStream().readIntLittle(u32);
|
||||
var guid: [16]u8 = undefined;
|
||||
try pdb_stream.stream.readNoEof(&guid);
|
||||
try pdb_stream.inStream().readNoEof(&guid);
|
||||
if (version != 20000404) // VC70, only value observed by LLVM team
|
||||
return error.UnknownPDBVersion;
|
||||
if (!mem.eql(u8, &di.coff.guid, &guid) or di.coff.age != age)
|
||||
@ -666,9 +668,9 @@ fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !
|
||||
// We validated the executable and pdb match.
|
||||
|
||||
const string_table_index = str_tab_index: {
|
||||
const name_bytes_len = try pdb_stream.stream.readIntLittle(u32);
|
||||
const name_bytes_len = try pdb_stream.inStream().readIntLittle(u32);
|
||||
const name_bytes = try allocator.alloc(u8, name_bytes_len);
|
||||
try pdb_stream.stream.readNoEof(name_bytes);
|
||||
try pdb_stream.inStream().readNoEof(name_bytes);
|
||||
|
||||
const HashTableHeader = packed struct {
|
||||
Size: u32,
|
||||
@ -678,17 +680,17 @@ fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !
|
||||
return cap * 2 / 3 + 1;
|
||||
}
|
||||
};
|
||||
const hash_tbl_hdr = try pdb_stream.stream.readStruct(HashTableHeader);
|
||||
const hash_tbl_hdr = try pdb_stream.inStream().readStruct(HashTableHeader);
|
||||
if (hash_tbl_hdr.Capacity == 0)
|
||||
return error.InvalidDebugInfo;
|
||||
|
||||
if (hash_tbl_hdr.Size > HashTableHeader.maxLoad(hash_tbl_hdr.Capacity))
|
||||
return error.InvalidDebugInfo;
|
||||
|
||||
const present = try readSparseBitVector(&pdb_stream.stream, allocator);
|
||||
const present = try readSparseBitVector(&pdb_stream.inStream(), allocator);
|
||||
if (present.len != hash_tbl_hdr.Size)
|
||||
return error.InvalidDebugInfo;
|
||||
const deleted = try readSparseBitVector(&pdb_stream.stream, allocator);
|
||||
const deleted = try readSparseBitVector(&pdb_stream.inStream(), allocator);
|
||||
|
||||
const Bucket = struct {
|
||||
first: u32,
|
||||
@ -696,8 +698,8 @@ fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !
|
||||
};
|
||||
const bucket_list = try allocator.alloc(Bucket, present.len);
|
||||
for (present) |_| {
|
||||
const name_offset = try pdb_stream.stream.readIntLittle(u32);
|
||||
const name_index = try pdb_stream.stream.readIntLittle(u32);
|
||||
const name_offset = try pdb_stream.inStream().readIntLittle(u32);
|
||||
const name_index = try pdb_stream.inStream().readIntLittle(u32);
|
||||
const name = mem.toSlice(u8, @ptrCast([*:0]u8, name_bytes.ptr + name_offset));
|
||||
if (mem.eql(u8, name, "/names")) {
|
||||
break :str_tab_index name_index;
|
||||
@ -712,7 +714,7 @@ fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !
|
||||
const dbi = di.pdb.dbi;
|
||||
|
||||
// Dbi Header
|
||||
const dbi_stream_header = try dbi.stream.readStruct(pdb.DbiStreamHeader);
|
||||
const dbi_stream_header = try dbi.inStream().readStruct(pdb.DbiStreamHeader);
|
||||
if (dbi_stream_header.VersionHeader != 19990903) // V70, only value observed by LLVM team
|
||||
return error.UnknownPDBVersion;
|
||||
if (dbi_stream_header.Age != age)
|
||||
@ -726,7 +728,7 @@ fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !
|
||||
// Module Info Substream
|
||||
var mod_info_offset: usize = 0;
|
||||
while (mod_info_offset != mod_info_size) {
|
||||
const mod_info = try dbi.stream.readStruct(pdb.ModInfo);
|
||||
const mod_info = try dbi.inStream().readStruct(pdb.ModInfo);
|
||||
var this_record_len: usize = @sizeOf(pdb.ModInfo);
|
||||
|
||||
const module_name = try dbi.readNullTermString(allocator);
|
||||
@ -764,14 +766,14 @@ fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !
|
||||
var sect_contribs = ArrayList(pdb.SectionContribEntry).init(allocator);
|
||||
var sect_cont_offset: usize = 0;
|
||||
if (section_contrib_size != 0) {
|
||||
const ver = @intToEnum(pdb.SectionContrSubstreamVersion, try dbi.stream.readIntLittle(u32));
|
||||
const ver = @intToEnum(pdb.SectionContrSubstreamVersion, try dbi.inStream().readIntLittle(u32));
|
||||
if (ver != pdb.SectionContrSubstreamVersion.Ver60)
|
||||
return error.InvalidDebugInfo;
|
||||
sect_cont_offset += @sizeOf(u32);
|
||||
}
|
||||
while (sect_cont_offset != section_contrib_size) {
|
||||
const entry = try sect_contribs.addOne();
|
||||
entry.* = try dbi.stream.readStruct(pdb.SectionContribEntry);
|
||||
entry.* = try dbi.inStream().readStruct(pdb.SectionContribEntry);
|
||||
sect_cont_offset += @sizeOf(pdb.SectionContribEntry);
|
||||
|
||||
if (sect_cont_offset > section_contrib_size)
|
||||
@ -808,45 +810,71 @@ fn chopSlice(ptr: []const u8, offset: u64, size: u64) ![]const u8 {
|
||||
|
||||
/// TODO resources https://github.com/ziglang/zig/issues/4353
|
||||
pub fn openElfDebugInfo(allocator: *mem.Allocator, elf_file_path: []const u8) !ModuleDebugInfo {
|
||||
const mapped_mem = try mapWholeFile(elf_file_path);
|
||||
noasync {
|
||||
const mapped_mem = try mapWholeFile(elf_file_path);
|
||||
const hdr = @ptrCast(*const elf.Ehdr, &mapped_mem[0]);
|
||||
if (!mem.eql(u8, hdr.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic;
|
||||
if (hdr.e_ident[elf.EI_VERSION] != 1) return error.InvalidElfVersion;
|
||||
|
||||
var seekable_stream = io.SliceSeekableInStream.init(mapped_mem);
|
||||
var efile = try noasync elf.Elf.openStream(
|
||||
allocator,
|
||||
@ptrCast(*DW.DwarfSeekableStream, &seekable_stream.seekable_stream),
|
||||
@ptrCast(*DW.DwarfInStream, &seekable_stream.stream),
|
||||
);
|
||||
defer noasync efile.close();
|
||||
const endian: builtin.Endian = switch (hdr.e_ident[elf.EI_DATA]) {
|
||||
elf.ELFDATA2LSB => .Little,
|
||||
elf.ELFDATA2MSB => .Big,
|
||||
else => return error.InvalidElfEndian,
|
||||
};
|
||||
assert(endian == std.builtin.endian); // this is our own debug info
|
||||
|
||||
const debug_info = (try noasync efile.findSection(".debug_info")) orelse
|
||||
return error.MissingDebugInfo;
|
||||
const debug_abbrev = (try noasync efile.findSection(".debug_abbrev")) orelse
|
||||
return error.MissingDebugInfo;
|
||||
const debug_str = (try noasync efile.findSection(".debug_str")) orelse
|
||||
return error.MissingDebugInfo;
|
||||
const debug_line = (try noasync efile.findSection(".debug_line")) orelse
|
||||
return error.MissingDebugInfo;
|
||||
const opt_debug_ranges = try noasync efile.findSection(".debug_ranges");
|
||||
const shoff = hdr.e_shoff;
|
||||
const str_section_off = shoff + @as(u64, hdr.e_shentsize) * @as(u64, hdr.e_shstrndx);
|
||||
const str_shdr = @ptrCast(
|
||||
*const elf.Shdr,
|
||||
@alignCast(@alignOf(elf.Shdr), &mapped_mem[try math.cast(usize, str_section_off)]),
|
||||
);
|
||||
const header_strings = mapped_mem[str_shdr.sh_offset .. str_shdr.sh_offset + str_shdr.sh_size];
|
||||
const shdrs = @ptrCast(
|
||||
[*]const elf.Shdr,
|
||||
@alignCast(@alignOf(elf.Shdr), &mapped_mem[shoff]),
|
||||
)[0..hdr.e_shnum];
|
||||
|
||||
var di = DW.DwarfInfo{
|
||||
.endian = efile.endian,
|
||||
.debug_info = try chopSlice(mapped_mem, debug_info.sh_offset, debug_info.sh_size),
|
||||
.debug_abbrev = try chopSlice(mapped_mem, debug_abbrev.sh_offset, debug_abbrev.sh_size),
|
||||
.debug_str = try chopSlice(mapped_mem, debug_str.sh_offset, debug_str.sh_size),
|
||||
.debug_line = try chopSlice(mapped_mem, debug_line.sh_offset, debug_line.sh_size),
|
||||
.debug_ranges = if (opt_debug_ranges) |debug_ranges|
|
||||
try chopSlice(mapped_mem, debug_ranges.sh_offset, debug_ranges.sh_size)
|
||||
else
|
||||
null,
|
||||
};
|
||||
var opt_debug_info: ?[]const u8 = null;
|
||||
var opt_debug_abbrev: ?[]const u8 = null;
|
||||
var opt_debug_str: ?[]const u8 = null;
|
||||
var opt_debug_line: ?[]const u8 = null;
|
||||
var opt_debug_ranges: ?[]const u8 = null;
|
||||
|
||||
try noasync DW.openDwarfDebugInfo(&di, allocator);
|
||||
for (shdrs) |*shdr| {
|
||||
if (shdr.sh_type == elf.SHT_NULL) continue;
|
||||
|
||||
return ModuleDebugInfo{
|
||||
.base_address = undefined,
|
||||
.dwarf = di,
|
||||
.mapped_memory = mapped_mem,
|
||||
};
|
||||
const name = std.mem.span(@ptrCast([*:0]const u8, header_strings[shdr.sh_name..].ptr));
|
||||
if (mem.eql(u8, name, ".debug_info")) {
|
||||
opt_debug_info = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
|
||||
} else if (mem.eql(u8, name, ".debug_abbrev")) {
|
||||
opt_debug_abbrev = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
|
||||
} else if (mem.eql(u8, name, ".debug_str")) {
|
||||
opt_debug_str = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
|
||||
} else if (mem.eql(u8, name, ".debug_line")) {
|
||||
opt_debug_line = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
|
||||
} else if (mem.eql(u8, name, ".debug_ranges")) {
|
||||
opt_debug_ranges = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
|
||||
}
|
||||
}
|
||||
|
||||
var di = DW.DwarfInfo{
|
||||
.endian = endian,
|
||||
.debug_info = opt_debug_info orelse return error.MissingDebugInfo,
|
||||
.debug_abbrev = opt_debug_abbrev orelse return error.MissingDebugInfo,
|
||||
.debug_str = opt_debug_str orelse return error.MissingDebugInfo,
|
||||
.debug_line = opt_debug_line orelse return error.MissingDebugInfo,
|
||||
.debug_ranges = opt_debug_ranges,
|
||||
};
|
||||
|
||||
try DW.openDwarfDebugInfo(&di, allocator);
|
||||
|
||||
return ModuleDebugInfo{
|
||||
.base_address = undefined,
|
||||
.dwarf = di,
|
||||
.mapped_memory = mapped_mem,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO resources https://github.com/ziglang/zig/issues/4353
|
||||
@ -936,7 +964,9 @@ fn openMachODebugInfo(allocator: *mem.Allocator, macho_file_path: []const u8) !M
|
||||
}
|
||||
|
||||
fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void {
|
||||
var f = try fs.cwd().openFile(line_info.file_name, .{});
|
||||
// Need this to always block even in async I/O mode, because this could potentially
|
||||
// be called from e.g. the event loop code crashing.
|
||||
var f = try fs.cwd().openFile(line_info.file_name, .{ .always_blocking = true });
|
||||
defer f.close();
|
||||
// TODO fstat and make sure that the file has the correct size
|
||||
|
||||
@ -982,22 +1012,24 @@ const MachoSymbol = struct {
|
||||
}
|
||||
};
|
||||
|
||||
fn mapWholeFile(path: []const u8) ![]const u8 {
|
||||
const file = try noasync fs.openFileAbsolute(path, .{ .always_blocking = true });
|
||||
defer noasync file.close();
|
||||
fn mapWholeFile(path: []const u8) ![]align(mem.page_size) const u8 {
|
||||
noasync {
|
||||
const file = try fs.openFileAbsolute(path, .{ .always_blocking = true });
|
||||
defer file.close();
|
||||
|
||||
const file_len = try math.cast(usize, try file.getEndPos());
|
||||
const mapped_mem = try os.mmap(
|
||||
null,
|
||||
file_len,
|
||||
os.PROT_READ,
|
||||
os.MAP_SHARED,
|
||||
file.handle,
|
||||
0,
|
||||
);
|
||||
errdefer os.munmap(mapped_mem);
|
||||
const file_len = try math.cast(usize, try file.getEndPos());
|
||||
const mapped_mem = try os.mmap(
|
||||
null,
|
||||
file_len,
|
||||
os.PROT_READ,
|
||||
os.MAP_SHARED,
|
||||
file.handle,
|
||||
0,
|
||||
);
|
||||
errdefer os.munmap(mapped_mem);
|
||||
|
||||
return mapped_mem;
|
||||
return mapped_mem;
|
||||
}
|
||||
}
|
||||
|
||||
pub const DebugInfo = struct {
|
||||
|
@ -121,18 +121,18 @@ pub fn readILEB128Mem(comptime T: type, ptr: *[*]const u8) !T {
|
||||
}
|
||||
|
||||
fn test_read_stream_ileb128(comptime T: type, encoded: []const u8) !T {
|
||||
var in_stream = std.io.SliceInStream.init(encoded);
|
||||
return try readILEB128(T, &in_stream.stream);
|
||||
var in_stream = std.io.fixedBufferStream(encoded);
|
||||
return try readILEB128(T, in_stream.inStream());
|
||||
}
|
||||
|
||||
fn test_read_stream_uleb128(comptime T: type, encoded: []const u8) !T {
|
||||
var in_stream = std.io.SliceInStream.init(encoded);
|
||||
return try readULEB128(T, &in_stream.stream);
|
||||
var in_stream = std.io.fixedBufferStream(encoded);
|
||||
return try readULEB128(T, in_stream.inStream());
|
||||
}
|
||||
|
||||
fn test_read_ileb128(comptime T: type, encoded: []const u8) !T {
|
||||
var in_stream = std.io.SliceInStream.init(encoded);
|
||||
const v1 = readILEB128(T, &in_stream.stream);
|
||||
var in_stream = std.io.fixedBufferStream(encoded);
|
||||
const v1 = readILEB128(T, in_stream.inStream());
|
||||
var in_ptr = encoded.ptr;
|
||||
const v2 = readILEB128Mem(T, &in_ptr);
|
||||
testing.expectEqual(v1, v2);
|
||||
@ -140,8 +140,8 @@ fn test_read_ileb128(comptime T: type, encoded: []const u8) !T {
|
||||
}
|
||||
|
||||
fn test_read_uleb128(comptime T: type, encoded: []const u8) !T {
|
||||
var in_stream = std.io.SliceInStream.init(encoded);
|
||||
const v1 = readULEB128(T, &in_stream.stream);
|
||||
var in_stream = std.io.fixedBufferStream(encoded);
|
||||
const v1 = readULEB128(T, in_stream.inStream());
|
||||
var in_ptr = encoded.ptr;
|
||||
const v2 = readULEB128Mem(T, &in_ptr);
|
||||
testing.expectEqual(v1, v2);
|
||||
@ -149,22 +149,22 @@ fn test_read_uleb128(comptime T: type, encoded: []const u8) !T {
|
||||
}
|
||||
|
||||
fn test_read_ileb128_seq(comptime T: type, comptime N: usize, encoded: []const u8) void {
|
||||
var in_stream = std.io.SliceInStream.init(encoded);
|
||||
var in_stream = std.io.fixedBufferStream(encoded);
|
||||
var in_ptr = encoded.ptr;
|
||||
var i: usize = 0;
|
||||
while (i < N) : (i += 1) {
|
||||
const v1 = readILEB128(T, &in_stream.stream);
|
||||
const v1 = readILEB128(T, in_stream.inStream());
|
||||
const v2 = readILEB128Mem(T, &in_ptr);
|
||||
testing.expectEqual(v1, v2);
|
||||
}
|
||||
}
|
||||
|
||||
fn test_read_uleb128_seq(comptime T: type, comptime N: usize, encoded: []const u8) void {
|
||||
var in_stream = std.io.SliceInStream.init(encoded);
|
||||
var in_stream = std.io.fixedBufferStream(encoded);
|
||||
var in_ptr = encoded.ptr;
|
||||
var i: usize = 0;
|
||||
while (i < N) : (i += 1) {
|
||||
const v1 = readULEB128(T, &in_stream.stream);
|
||||
const v1 = readULEB128(T, in_stream.inStream());
|
||||
const v2 = readULEB128Mem(T, &in_ptr);
|
||||
testing.expectEqual(v1, v2);
|
||||
}
|
||||
|
@ -11,9 +11,6 @@ const ArrayList = std.ArrayList;
|
||||
|
||||
usingnamespace @import("dwarf_bits.zig");
|
||||
|
||||
pub const DwarfSeekableStream = io.SeekableStream(anyerror, anyerror);
|
||||
pub const DwarfInStream = io.InStream(anyerror);
|
||||
|
||||
const PcRange = struct {
|
||||
start: u64,
|
||||
end: u64,
|
||||
@ -239,7 +236,7 @@ const LineNumberProgram = struct {
|
||||
}
|
||||
};
|
||||
|
||||
fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 {
|
||||
fn readInitialLength(in_stream: var, is_64: *bool) !u64 {
|
||||
const first_32_bits = try in_stream.readIntLittle(u32);
|
||||
is_64.* = (first_32_bits == 0xffffffff);
|
||||
if (is_64.*) {
|
||||
@ -414,40 +411,42 @@ pub const DwarfInfo = struct {
|
||||
}
|
||||
|
||||
fn scanAllFunctions(di: *DwarfInfo) !void {
|
||||
var s = io.SliceSeekableInStream.init(di.debug_info);
|
||||
var stream = io.fixedBufferStream(di.debug_info);
|
||||
const in = &stream.inStream();
|
||||
const seekable = &stream.seekableStream();
|
||||
var this_unit_offset: u64 = 0;
|
||||
|
||||
while (this_unit_offset < try s.seekable_stream.getEndPos()) {
|
||||
s.seekable_stream.seekTo(this_unit_offset) catch |err| switch (err) {
|
||||
while (this_unit_offset < try seekable.getEndPos()) {
|
||||
seekable.seekTo(this_unit_offset) catch |err| switch (err) {
|
||||
error.EndOfStream => unreachable,
|
||||
else => return err,
|
||||
};
|
||||
|
||||
var is_64: bool = undefined;
|
||||
const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64);
|
||||
const unit_length = try readInitialLength(in, &is_64);
|
||||
if (unit_length == 0) return;
|
||||
const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
|
||||
|
||||
const version = try s.stream.readInt(u16, di.endian);
|
||||
const version = try in.readInt(u16, di.endian);
|
||||
if (version < 2 or version > 5) return error.InvalidDebugInfo;
|
||||
|
||||
const debug_abbrev_offset = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian);
|
||||
const debug_abbrev_offset = if (is_64) try in.readInt(u64, di.endian) else try in.readInt(u32, di.endian);
|
||||
|
||||
const address_size = try s.stream.readByte();
|
||||
const address_size = try in.readByte();
|
||||
if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
|
||||
|
||||
const compile_unit_pos = try s.seekable_stream.getPos();
|
||||
const compile_unit_pos = try seekable.getPos();
|
||||
const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset);
|
||||
|
||||
try s.seekable_stream.seekTo(compile_unit_pos);
|
||||
try seekable.seekTo(compile_unit_pos);
|
||||
|
||||
const next_unit_pos = this_unit_offset + next_offset;
|
||||
|
||||
while ((try s.seekable_stream.getPos()) < next_unit_pos) {
|
||||
const die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse continue;
|
||||
while ((try seekable.getPos()) < next_unit_pos) {
|
||||
const die_obj = (try di.parseDie(in, abbrev_table, is_64)) orelse continue;
|
||||
defer die_obj.attrs.deinit();
|
||||
|
||||
const after_die_offset = try s.seekable_stream.getPos();
|
||||
const after_die_offset = try seekable.getPos();
|
||||
|
||||
switch (die_obj.tag_id) {
|
||||
TAG_subprogram, TAG_inlined_subroutine, TAG_subroutine, TAG_entry_point => {
|
||||
@ -463,14 +462,14 @@ pub const DwarfInfo = struct {
|
||||
// Follow the DIE it points to and repeat
|
||||
const ref_offset = try this_die_obj.getAttrRef(AT_abstract_origin);
|
||||
if (ref_offset > next_offset) return error.InvalidDebugInfo;
|
||||
try s.seekable_stream.seekTo(this_unit_offset + ref_offset);
|
||||
this_die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
|
||||
try seekable.seekTo(this_unit_offset + ref_offset);
|
||||
this_die_obj = (try di.parseDie(in, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
|
||||
} else if (this_die_obj.getAttr(AT_specification)) |ref| {
|
||||
// Follow the DIE it points to and repeat
|
||||
const ref_offset = try this_die_obj.getAttrRef(AT_specification);
|
||||
if (ref_offset > next_offset) return error.InvalidDebugInfo;
|
||||
try s.seekable_stream.seekTo(this_unit_offset + ref_offset);
|
||||
this_die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
|
||||
try seekable.seekTo(this_unit_offset + ref_offset);
|
||||
this_die_obj = (try di.parseDie(in, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
|
||||
} else {
|
||||
break :x null;
|
||||
}
|
||||
@ -511,7 +510,7 @@ pub const DwarfInfo = struct {
|
||||
else => {},
|
||||
}
|
||||
|
||||
try s.seekable_stream.seekTo(after_die_offset);
|
||||
try seekable.seekTo(after_die_offset);
|
||||
}
|
||||
|
||||
this_unit_offset += next_offset;
|
||||
@ -519,35 +518,37 @@ pub const DwarfInfo = struct {
|
||||
}
|
||||
|
||||
fn scanAllCompileUnits(di: *DwarfInfo) !void {
|
||||
var s = io.SliceSeekableInStream.init(di.debug_info);
|
||||
var stream = io.fixedBufferStream(di.debug_info);
|
||||
const in = &stream.inStream();
|
||||
const seekable = &stream.seekableStream();
|
||||
var this_unit_offset: u64 = 0;
|
||||
|
||||
while (this_unit_offset < try s.seekable_stream.getEndPos()) {
|
||||
s.seekable_stream.seekTo(this_unit_offset) catch |err| switch (err) {
|
||||
while (this_unit_offset < try seekable.getEndPos()) {
|
||||
seekable.seekTo(this_unit_offset) catch |err| switch (err) {
|
||||
error.EndOfStream => unreachable,
|
||||
else => return err,
|
||||
};
|
||||
|
||||
var is_64: bool = undefined;
|
||||
const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64);
|
||||
const unit_length = try readInitialLength(in, &is_64);
|
||||
if (unit_length == 0) return;
|
||||
const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
|
||||
|
||||
const version = try s.stream.readInt(u16, di.endian);
|
||||
const version = try in.readInt(u16, di.endian);
|
||||
if (version < 2 or version > 5) return error.InvalidDebugInfo;
|
||||
|
||||
const debug_abbrev_offset = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian);
|
||||
const debug_abbrev_offset = if (is_64) try in.readInt(u64, di.endian) else try in.readInt(u32, di.endian);
|
||||
|
||||
const address_size = try s.stream.readByte();
|
||||
const address_size = try in.readByte();
|
||||
if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
|
||||
|
||||
const compile_unit_pos = try s.seekable_stream.getPos();
|
||||
const compile_unit_pos = try seekable.getPos();
|
||||
const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset);
|
||||
|
||||
try s.seekable_stream.seekTo(compile_unit_pos);
|
||||
try seekable.seekTo(compile_unit_pos);
|
||||
|
||||
const compile_unit_die = try di.allocator().create(Die);
|
||||
compile_unit_die.* = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
|
||||
compile_unit_die.* = (try di.parseDie(in, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
|
||||
|
||||
if (compile_unit_die.tag_id != TAG_compile_unit) return error.InvalidDebugInfo;
|
||||
|
||||
@ -593,7 +594,9 @@ pub const DwarfInfo = struct {
|
||||
}
|
||||
if (di.debug_ranges) |debug_ranges| {
|
||||
if (compile_unit.die.getAttrSecOffset(AT_ranges)) |ranges_offset| {
|
||||
var s = io.SliceSeekableInStream.init(debug_ranges);
|
||||
var stream = io.fixedBufferStream(debug_ranges);
|
||||
const in = &stream.inStream();
|
||||
const seekable = &stream.seekableStream();
|
||||
|
||||
// All the addresses in the list are relative to the value
|
||||
// specified by DW_AT_low_pc or to some other value encoded
|
||||
@ -604,11 +607,11 @@ pub const DwarfInfo = struct {
|
||||
else => return err,
|
||||
};
|
||||
|
||||
try s.seekable_stream.seekTo(ranges_offset);
|
||||
try seekable.seekTo(ranges_offset);
|
||||
|
||||
while (true) {
|
||||
const begin_addr = try s.stream.readIntLittle(usize);
|
||||
const end_addr = try s.stream.readIntLittle(usize);
|
||||
const begin_addr = try in.readIntLittle(usize);
|
||||
const end_addr = try in.readIntLittle(usize);
|
||||
if (begin_addr == 0 and end_addr == 0) {
|
||||
break;
|
||||
}
|
||||
@ -646,25 +649,27 @@ pub const DwarfInfo = struct {
|
||||
}
|
||||
|
||||
fn parseAbbrevTable(di: *DwarfInfo, offset: u64) !AbbrevTable {
|
||||
var s = io.SliceSeekableInStream.init(di.debug_abbrev);
|
||||
var stream = io.fixedBufferStream(di.debug_abbrev);
|
||||
const in = &stream.inStream();
|
||||
const seekable = &stream.seekableStream();
|
||||
|
||||
try s.seekable_stream.seekTo(offset);
|
||||
try seekable.seekTo(offset);
|
||||
var result = AbbrevTable.init(di.allocator());
|
||||
errdefer result.deinit();
|
||||
while (true) {
|
||||
const abbrev_code = try leb.readULEB128(u64, &s.stream);
|
||||
const abbrev_code = try leb.readULEB128(u64, in);
|
||||
if (abbrev_code == 0) return result;
|
||||
try result.append(AbbrevTableEntry{
|
||||
.abbrev_code = abbrev_code,
|
||||
.tag_id = try leb.readULEB128(u64, &s.stream),
|
||||
.has_children = (try s.stream.readByte()) == CHILDREN_yes,
|
||||
.tag_id = try leb.readULEB128(u64, in),
|
||||
.has_children = (try in.readByte()) == CHILDREN_yes,
|
||||
.attrs = ArrayList(AbbrevAttr).init(di.allocator()),
|
||||
});
|
||||
const attrs = &result.items[result.len - 1].attrs;
|
||||
|
||||
while (true) {
|
||||
const attr_id = try leb.readULEB128(u64, &s.stream);
|
||||
const form_id = try leb.readULEB128(u64, &s.stream);
|
||||
const attr_id = try leb.readULEB128(u64, in);
|
||||
const form_id = try leb.readULEB128(u64, in);
|
||||
if (attr_id == 0 and form_id == 0) break;
|
||||
try attrs.append(AbbrevAttr{
|
||||
.attr_id = attr_id,
|
||||
@ -695,42 +700,44 @@ pub const DwarfInfo = struct {
|
||||
}
|
||||
|
||||
fn getLineNumberInfo(di: *DwarfInfo, compile_unit: CompileUnit, target_address: usize) !debug.LineInfo {
|
||||
var s = io.SliceSeekableInStream.init(di.debug_line);
|
||||
var stream = io.fixedBufferStream(di.debug_line);
|
||||
const in = &stream.inStream();
|
||||
const seekable = &stream.seekableStream();
|
||||
|
||||
const compile_unit_cwd = try compile_unit.die.getAttrString(di, AT_comp_dir);
|
||||
const line_info_offset = try compile_unit.die.getAttrSecOffset(AT_stmt_list);
|
||||
|
||||
try s.seekable_stream.seekTo(line_info_offset);
|
||||
try seekable.seekTo(line_info_offset);
|
||||
|
||||
var is_64: bool = undefined;
|
||||
const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64);
|
||||
const unit_length = try readInitialLength(in, &is_64);
|
||||
if (unit_length == 0) {
|
||||
return error.MissingDebugInfo;
|
||||
}
|
||||
const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
|
||||
|
||||
const version = try s.stream.readInt(u16, di.endian);
|
||||
const version = try in.readInt(u16, di.endian);
|
||||
// TODO support 3 and 5
|
||||
if (version != 2 and version != 4) return error.InvalidDebugInfo;
|
||||
|
||||
const prologue_length = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian);
|
||||
const prog_start_offset = (try s.seekable_stream.getPos()) + prologue_length;
|
||||
const prologue_length = if (is_64) try in.readInt(u64, di.endian) else try in.readInt(u32, di.endian);
|
||||
const prog_start_offset = (try seekable.getPos()) + prologue_length;
|
||||
|
||||
const minimum_instruction_length = try s.stream.readByte();
|
||||
const minimum_instruction_length = try in.readByte();
|
||||
if (minimum_instruction_length == 0) return error.InvalidDebugInfo;
|
||||
|
||||
if (version >= 4) {
|
||||
// maximum_operations_per_instruction
|
||||
_ = try s.stream.readByte();
|
||||
_ = try in.readByte();
|
||||
}
|
||||
|
||||
const default_is_stmt = (try s.stream.readByte()) != 0;
|
||||
const line_base = try s.stream.readByteSigned();
|
||||
const default_is_stmt = (try in.readByte()) != 0;
|
||||
const line_base = try in.readByteSigned();
|
||||
|
||||
const line_range = try s.stream.readByte();
|
||||
const line_range = try in.readByte();
|
||||
if (line_range == 0) return error.InvalidDebugInfo;
|
||||
|
||||
const opcode_base = try s.stream.readByte();
|
||||
const opcode_base = try in.readByte();
|
||||
|
||||
const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1);
|
||||
defer di.allocator().free(standard_opcode_lengths);
|
||||
@ -738,14 +745,14 @@ pub const DwarfInfo = struct {
|
||||
{
|
||||
var i: usize = 0;
|
||||
while (i < opcode_base - 1) : (i += 1) {
|
||||
standard_opcode_lengths[i] = try s.stream.readByte();
|
||||
standard_opcode_lengths[i] = try in.readByte();
|
||||
}
|
||||
}
|
||||
|
||||
var include_directories = ArrayList([]const u8).init(di.allocator());
|
||||
try include_directories.append(compile_unit_cwd);
|
||||
while (true) {
|
||||
const dir = try s.stream.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize));
|
||||
const dir = try in.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize));
|
||||
if (dir.len == 0) break;
|
||||
try include_directories.append(dir);
|
||||
}
|
||||
@ -754,11 +761,11 @@ pub const DwarfInfo = struct {
|
||||
var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address);
|
||||
|
||||
while (true) {
|
||||
const file_name = try s.stream.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize));
|
||||
const file_name = try in.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize));
|
||||
if (file_name.len == 0) break;
|
||||
const dir_index = try leb.readULEB128(usize, &s.stream);
|
||||
const mtime = try leb.readULEB128(usize, &s.stream);
|
||||
const len_bytes = try leb.readULEB128(usize, &s.stream);
|
||||
const dir_index = try leb.readULEB128(usize, in);
|
||||
const mtime = try leb.readULEB128(usize, in);
|
||||
const len_bytes = try leb.readULEB128(usize, in);
|
||||
try file_entries.append(FileEntry{
|
||||
.file_name = file_name,
|
||||
.dir_index = dir_index,
|
||||
@ -767,17 +774,17 @@ pub const DwarfInfo = struct {
|
||||
});
|
||||
}
|
||||
|
||||
try s.seekable_stream.seekTo(prog_start_offset);
|
||||
try seekable.seekTo(prog_start_offset);
|
||||
|
||||
const next_unit_pos = line_info_offset + next_offset;
|
||||
|
||||
while ((try s.seekable_stream.getPos()) < next_unit_pos) {
|
||||
const opcode = try s.stream.readByte();
|
||||
while ((try seekable.getPos()) < next_unit_pos) {
|
||||
const opcode = try in.readByte();
|
||||
|
||||
if (opcode == LNS_extended_op) {
|
||||
const op_size = try leb.readULEB128(u64, &s.stream);
|
||||
const op_size = try leb.readULEB128(u64, in);
|
||||
if (op_size < 1) return error.InvalidDebugInfo;
|
||||
var sub_op = try s.stream.readByte();
|
||||
var sub_op = try in.readByte();
|
||||
switch (sub_op) {
|
||||
LNE_end_sequence => {
|
||||
prog.end_sequence = true;
|
||||
@ -785,14 +792,14 @@ pub const DwarfInfo = struct {
|
||||
prog.reset();
|
||||
},
|
||||
LNE_set_address => {
|
||||
const addr = try s.stream.readInt(usize, di.endian);
|
||||
const addr = try in.readInt(usize, di.endian);
|
||||
prog.address = addr;
|
||||
},
|
||||
LNE_define_file => {
|
||||
const file_name = try s.stream.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize));
|
||||
const dir_index = try leb.readULEB128(usize, &s.stream);
|
||||
const mtime = try leb.readULEB128(usize, &s.stream);
|
||||
const len_bytes = try leb.readULEB128(usize, &s.stream);
|
||||
const file_name = try in.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize));
|
||||
const dir_index = try leb.readULEB128(usize, in);
|
||||
const mtime = try leb.readULEB128(usize, in);
|
||||
const len_bytes = try leb.readULEB128(usize, in);
|
||||
try file_entries.append(FileEntry{
|
||||
.file_name = file_name,
|
||||
.dir_index = dir_index,
|
||||
@ -802,7 +809,7 @@ pub const DwarfInfo = struct {
|
||||
},
|
||||
else => {
|
||||
const fwd_amt = math.cast(isize, op_size - 1) catch return error.InvalidDebugInfo;
|
||||
try s.seekable_stream.seekBy(fwd_amt);
|
||||
try seekable.seekBy(fwd_amt);
|
||||
},
|
||||
}
|
||||
} else if (opcode >= opcode_base) {
|
||||
@ -821,19 +828,19 @@ pub const DwarfInfo = struct {
|
||||
prog.basic_block = false;
|
||||
},
|
||||
LNS_advance_pc => {
|
||||
const arg = try leb.readULEB128(usize, &s.stream);
|
||||
const arg = try leb.readULEB128(usize, in);
|
||||
prog.address += arg * minimum_instruction_length;
|
||||
},
|
||||
LNS_advance_line => {
|
||||
const arg = try leb.readILEB128(i64, &s.stream);
|
||||
const arg = try leb.readILEB128(i64, in);
|
||||
prog.line += arg;
|
||||
},
|
||||
LNS_set_file => {
|
||||
const arg = try leb.readULEB128(usize, &s.stream);
|
||||
const arg = try leb.readULEB128(usize, in);
|
||||
prog.file = arg;
|
||||
},
|
||||
LNS_set_column => {
|
||||
const arg = try leb.readULEB128(u64, &s.stream);
|
||||
const arg = try leb.readULEB128(u64, in);
|
||||
prog.column = arg;
|
||||
},
|
||||
LNS_negate_stmt => {
|
||||
@ -847,14 +854,14 @@ pub const DwarfInfo = struct {
|
||||
prog.address += inc_addr;
|
||||
},
|
||||
LNS_fixed_advance_pc => {
|
||||
const arg = try s.stream.readInt(u16, di.endian);
|
||||
const arg = try in.readInt(u16, di.endian);
|
||||
prog.address += arg;
|
||||
},
|
||||
LNS_set_prologue_end => {},
|
||||
else => {
|
||||
if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo;
|
||||
const len_bytes = standard_opcode_lengths[opcode - 1];
|
||||
try s.seekable_stream.seekBy(len_bytes);
|
||||
try seekable.seekBy(len_bytes);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
436
lib/std/elf.zig
436
lib/std/elf.zig
@ -1,5 +1,5 @@
|
||||
const builtin = @import("builtin");
|
||||
const std = @import("std.zig");
|
||||
const builtin = std.builtin;
|
||||
const io = std.io;
|
||||
const os = std.os;
|
||||
const math = std.math;
|
||||
@ -330,219 +330,233 @@ pub const ET = extern enum(u16) {
|
||||
pub const HIPROC = 0xffff;
|
||||
};
|
||||
|
||||
pub const SectionHeader = Elf64_Shdr;
|
||||
pub const ProgramHeader = Elf64_Phdr;
|
||||
|
||||
pub const Elf = struct {
|
||||
seekable_stream: *io.SeekableStream(anyerror, anyerror),
|
||||
in_stream: *io.InStream(anyerror),
|
||||
is_64: bool,
|
||||
/// All integers are native endian.
|
||||
const Header = struct {
|
||||
endian: builtin.Endian,
|
||||
file_type: ET,
|
||||
arch: EM,
|
||||
entry_addr: u64,
|
||||
program_header_offset: u64,
|
||||
section_header_offset: u64,
|
||||
string_section_index: usize,
|
||||
string_section: *SectionHeader,
|
||||
section_headers: []SectionHeader,
|
||||
program_headers: []ProgramHeader,
|
||||
allocator: *mem.Allocator,
|
||||
|
||||
pub fn openStream(
|
||||
allocator: *mem.Allocator,
|
||||
seekable_stream: *io.SeekableStream(anyerror, anyerror),
|
||||
in: *io.InStream(anyerror),
|
||||
) !Elf {
|
||||
var elf: Elf = undefined;
|
||||
elf.allocator = allocator;
|
||||
elf.seekable_stream = seekable_stream;
|
||||
elf.in_stream = in;
|
||||
|
||||
var magic: [4]u8 = undefined;
|
||||
try in.readNoEof(magic[0..]);
|
||||
if (!mem.eql(u8, &magic, "\x7fELF")) return error.InvalidFormat;
|
||||
|
||||
elf.is_64 = switch (try in.readByte()) {
|
||||
1 => false,
|
||||
2 => true,
|
||||
else => return error.InvalidFormat,
|
||||
};
|
||||
|
||||
elf.endian = switch (try in.readByte()) {
|
||||
1 => .Little,
|
||||
2 => .Big,
|
||||
else => return error.InvalidFormat,
|
||||
};
|
||||
|
||||
const version_byte = try in.readByte();
|
||||
if (version_byte != 1) return error.InvalidFormat;
|
||||
|
||||
// skip over padding
|
||||
try seekable_stream.seekBy(9);
|
||||
|
||||
elf.file_type = try in.readEnum(ET, elf.endian);
|
||||
elf.arch = try in.readEnum(EM, elf.endian);
|
||||
|
||||
const elf_version = try in.readInt(u32, elf.endian);
|
||||
if (elf_version != 1) return error.InvalidFormat;
|
||||
|
||||
if (elf.is_64) {
|
||||
elf.entry_addr = try in.readInt(u64, elf.endian);
|
||||
elf.program_header_offset = try in.readInt(u64, elf.endian);
|
||||
elf.section_header_offset = try in.readInt(u64, elf.endian);
|
||||
} else {
|
||||
elf.entry_addr = @as(u64, try in.readInt(u32, elf.endian));
|
||||
elf.program_header_offset = @as(u64, try in.readInt(u32, elf.endian));
|
||||
elf.section_header_offset = @as(u64, try in.readInt(u32, elf.endian));
|
||||
}
|
||||
|
||||
// skip over flags
|
||||
try seekable_stream.seekBy(4);
|
||||
|
||||
const header_size = try in.readInt(u16, elf.endian);
|
||||
if ((elf.is_64 and header_size != @sizeOf(Elf64_Ehdr)) or (!elf.is_64 and header_size != @sizeOf(Elf32_Ehdr))) {
|
||||
return error.InvalidFormat;
|
||||
}
|
||||
|
||||
const ph_entry_size = try in.readInt(u16, elf.endian);
|
||||
const ph_entry_count = try in.readInt(u16, elf.endian);
|
||||
|
||||
if ((elf.is_64 and ph_entry_size != @sizeOf(Elf64_Phdr)) or (!elf.is_64 and ph_entry_size != @sizeOf(Elf32_Phdr))) {
|
||||
return error.InvalidFormat;
|
||||
}
|
||||
|
||||
const sh_entry_size = try in.readInt(u16, elf.endian);
|
||||
const sh_entry_count = try in.readInt(u16, elf.endian);
|
||||
|
||||
if ((elf.is_64 and sh_entry_size != @sizeOf(Elf64_Shdr)) or (!elf.is_64 and sh_entry_size != @sizeOf(Elf32_Shdr))) {
|
||||
return error.InvalidFormat;
|
||||
}
|
||||
|
||||
elf.string_section_index = @as(usize, try in.readInt(u16, elf.endian));
|
||||
|
||||
if (elf.string_section_index >= sh_entry_count) return error.InvalidFormat;
|
||||
|
||||
const sh_byte_count = @as(u64, sh_entry_size) * @as(u64, sh_entry_count);
|
||||
const end_sh = try math.add(u64, elf.section_header_offset, sh_byte_count);
|
||||
const ph_byte_count = @as(u64, ph_entry_size) * @as(u64, ph_entry_count);
|
||||
const end_ph = try math.add(u64, elf.program_header_offset, ph_byte_count);
|
||||
|
||||
const stream_end = try seekable_stream.getEndPos();
|
||||
if (stream_end < end_sh or stream_end < end_ph) {
|
||||
return error.InvalidFormat;
|
||||
}
|
||||
|
||||
try seekable_stream.seekTo(elf.program_header_offset);
|
||||
|
||||
elf.program_headers = try elf.allocator.alloc(ProgramHeader, ph_entry_count);
|
||||
errdefer elf.allocator.free(elf.program_headers);
|
||||
|
||||
if (elf.is_64) {
|
||||
for (elf.program_headers) |*elf_program| {
|
||||
elf_program.p_type = try in.readInt(Elf64_Word, elf.endian);
|
||||
elf_program.p_flags = try in.readInt(Elf64_Word, elf.endian);
|
||||
elf_program.p_offset = try in.readInt(Elf64_Off, elf.endian);
|
||||
elf_program.p_vaddr = try in.readInt(Elf64_Addr, elf.endian);
|
||||
elf_program.p_paddr = try in.readInt(Elf64_Addr, elf.endian);
|
||||
elf_program.p_filesz = try in.readInt(Elf64_Xword, elf.endian);
|
||||
elf_program.p_memsz = try in.readInt(Elf64_Xword, elf.endian);
|
||||
elf_program.p_align = try in.readInt(Elf64_Xword, elf.endian);
|
||||
}
|
||||
} else {
|
||||
for (elf.program_headers) |*elf_program| {
|
||||
elf_program.p_type = @as(Elf64_Word, try in.readInt(Elf32_Word, elf.endian));
|
||||
elf_program.p_offset = @as(Elf64_Off, try in.readInt(Elf32_Off, elf.endian));
|
||||
elf_program.p_vaddr = @as(Elf64_Addr, try in.readInt(Elf32_Addr, elf.endian));
|
||||
elf_program.p_paddr = @as(Elf64_Addr, try in.readInt(Elf32_Addr, elf.endian));
|
||||
elf_program.p_filesz = @as(Elf64_Word, try in.readInt(Elf32_Word, elf.endian));
|
||||
elf_program.p_memsz = @as(Elf64_Word, try in.readInt(Elf32_Word, elf.endian));
|
||||
elf_program.p_flags = @as(Elf64_Word, try in.readInt(Elf32_Word, elf.endian));
|
||||
elf_program.p_align = @as(Elf64_Word, try in.readInt(Elf32_Word, elf.endian));
|
||||
}
|
||||
}
|
||||
|
||||
try seekable_stream.seekTo(elf.section_header_offset);
|
||||
|
||||
elf.section_headers = try elf.allocator.alloc(SectionHeader, sh_entry_count);
|
||||
errdefer elf.allocator.free(elf.section_headers);
|
||||
|
||||
if (elf.is_64) {
|
||||
for (elf.section_headers) |*elf_section| {
|
||||
elf_section.sh_name = try in.readInt(u32, elf.endian);
|
||||
elf_section.sh_type = try in.readInt(u32, elf.endian);
|
||||
elf_section.sh_flags = try in.readInt(u64, elf.endian);
|
||||
elf_section.sh_addr = try in.readInt(u64, elf.endian);
|
||||
elf_section.sh_offset = try in.readInt(u64, elf.endian);
|
||||
elf_section.sh_size = try in.readInt(u64, elf.endian);
|
||||
elf_section.sh_link = try in.readInt(u32, elf.endian);
|
||||
elf_section.sh_info = try in.readInt(u32, elf.endian);
|
||||
elf_section.sh_addralign = try in.readInt(u64, elf.endian);
|
||||
elf_section.sh_entsize = try in.readInt(u64, elf.endian);
|
||||
}
|
||||
} else {
|
||||
for (elf.section_headers) |*elf_section| {
|
||||
// TODO (multiple occurrences) allow implicit cast from %u32 -> %u64 ?
|
||||
elf_section.sh_name = try in.readInt(u32, elf.endian);
|
||||
elf_section.sh_type = try in.readInt(u32, elf.endian);
|
||||
elf_section.sh_flags = @as(u64, try in.readInt(u32, elf.endian));
|
||||
elf_section.sh_addr = @as(u64, try in.readInt(u32, elf.endian));
|
||||
elf_section.sh_offset = @as(u64, try in.readInt(u32, elf.endian));
|
||||
elf_section.sh_size = @as(u64, try in.readInt(u32, elf.endian));
|
||||
elf_section.sh_link = try in.readInt(u32, elf.endian);
|
||||
elf_section.sh_info = try in.readInt(u32, elf.endian);
|
||||
elf_section.sh_addralign = @as(u64, try in.readInt(u32, elf.endian));
|
||||
elf_section.sh_entsize = @as(u64, try in.readInt(u32, elf.endian));
|
||||
}
|
||||
}
|
||||
|
||||
for (elf.section_headers) |*elf_section| {
|
||||
if (elf_section.sh_type != SHT_NOBITS) {
|
||||
const file_end_offset = try math.add(u64, elf_section.sh_offset, elf_section.sh_size);
|
||||
if (stream_end < file_end_offset) return error.InvalidFormat;
|
||||
}
|
||||
}
|
||||
|
||||
elf.string_section = &elf.section_headers[elf.string_section_index];
|
||||
if (elf.string_section.sh_type != SHT_STRTAB) {
|
||||
// not a string table
|
||||
return error.InvalidFormat;
|
||||
}
|
||||
|
||||
return elf;
|
||||
}
|
||||
|
||||
pub fn close(elf: *Elf) void {
|
||||
elf.allocator.free(elf.section_headers);
|
||||
elf.allocator.free(elf.program_headers);
|
||||
}
|
||||
|
||||
pub fn findSection(elf: *Elf, name: []const u8) !?*SectionHeader {
|
||||
section_loop: for (elf.section_headers) |*elf_section| {
|
||||
if (elf_section.sh_type == SHT_NULL) continue;
|
||||
|
||||
const name_offset = elf.string_section.sh_offset + elf_section.sh_name;
|
||||
try elf.seekable_stream.seekTo(name_offset);
|
||||
|
||||
for (name) |expected_c| {
|
||||
const target_c = try elf.in_stream.readByte();
|
||||
if (target_c == 0 or expected_c != target_c) continue :section_loop;
|
||||
}
|
||||
|
||||
{
|
||||
const null_byte = try elf.in_stream.readByte();
|
||||
if (null_byte == 0) return elf_section;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn seekToSection(elf: *Elf, elf_section: *SectionHeader) !void {
|
||||
try elf.seekable_stream.seekTo(elf_section.sh_offset);
|
||||
}
|
||||
is_64: bool,
|
||||
entry: u64,
|
||||
phoff: u64,
|
||||
shoff: u64,
|
||||
phentsize: u16,
|
||||
phnum: u16,
|
||||
shentsize: u16,
|
||||
shnum: u16,
|
||||
shstrndx: u16,
|
||||
};
|
||||
|
||||
pub fn readHeader(file: File) !Header {
|
||||
var hdr_buf: [@sizeOf(Elf64_Ehdr)]u8 align(@alignOf(Elf64_Ehdr)) = undefined;
|
||||
try preadNoEof(file, &hdr_buf, 0);
|
||||
const hdr32 = @ptrCast(*Elf32_Ehdr, &hdr_buf);
|
||||
const hdr64 = @ptrCast(*Elf64_Ehdr, &hdr_buf);
|
||||
if (!mem.eql(u8, hdr32.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic;
|
||||
if (hdr32.e_ident[EI_VERSION] != 1) return error.InvalidElfVersion;
|
||||
|
||||
const endian: std.builtin.Endian = switch (hdr32.e_ident[EI_DATA]) {
|
||||
ELFDATA2LSB => .Little,
|
||||
ELFDATA2MSB => .Big,
|
||||
else => return error.InvalidElfEndian,
|
||||
};
|
||||
const need_bswap = endian != std.builtin.endian;
|
||||
|
||||
const is_64 = switch (hdr32.e_ident[EI_CLASS]) {
|
||||
ELFCLASS32 => false,
|
||||
ELFCLASS64 => true,
|
||||
else => return error.InvalidElfClass,
|
||||
};
|
||||
|
||||
return @as(Header, .{
|
||||
.endian = endian,
|
||||
.is_64 = is_64,
|
||||
.entry = int(is_64, need_bswap, hdr32.e_entry, hdr64.e_entry),
|
||||
.phoff = int(is_64, need_bswap, hdr32.e_phoff, hdr64.e_phoff),
|
||||
.shoff = int(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff),
|
||||
.phentsize = int(is_64, need_bswap, hdr32.e_phentsize, hdr64.e_phentsize),
|
||||
.phnum = int(is_64, need_bswap, hdr32.e_phnum, hdr64.e_phnum),
|
||||
.shentsize = int(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize),
|
||||
.shnum = int(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum),
|
||||
.shstrndx = int(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx),
|
||||
});
|
||||
}
|
||||
|
||||
/// All integers are native endian.
|
||||
pub const AllHeaders = struct {
|
||||
header: Header,
|
||||
section_headers: []Elf64_Shdr,
|
||||
program_headers: []Elf64_Phdr,
|
||||
allocator: *mem.Allocator,
|
||||
};
|
||||
|
||||
pub fn readAllHeaders(allocator: *mem.Allocator, file: File) !AllHeaders {
|
||||
var hdrs: AllHeaders = .{
|
||||
.allocator = allocator,
|
||||
.header = try readHeader(file),
|
||||
.section_headers = undefined,
|
||||
.program_headers = undefined,
|
||||
};
|
||||
const is_64 = hdrs.header.is_64;
|
||||
const need_bswap = hdrs.header.endian != std.builtin.endian;
|
||||
|
||||
hdrs.section_headers = try allocator.alloc(Elf64_Shdr, hdrs.header.shnum);
|
||||
errdefer allocator.free(hdrs.section_headers);
|
||||
|
||||
hdrs.program_headers = try allocator.alloc(Elf64_Phdr, hdrs.header.phnum);
|
||||
errdefer allocator.free(hdrs.program_headers);
|
||||
|
||||
// If the ELF file is 64-bit and same-endianness, then all we have to do is
|
||||
// yeet the bytes into memory.
|
||||
// If only the endianness is different, they can be simply byte swapped.
|
||||
if (is_64) {
|
||||
const shdr_buf = std.mem.sliceAsBytes(hdrs.section_headers);
|
||||
const phdr_buf = std.mem.sliceAsBytes(hdrs.program_headers);
|
||||
try preadNoEof(file, shdr_buf, hdrs.header.shoff);
|
||||
try preadNoEof(file, phdr_buf, hdrs.header.phoff);
|
||||
|
||||
if (need_bswap) {
|
||||
for (hdrs.section_headers) |*shdr| {
|
||||
shdr.* = .{
|
||||
.sh_name = @byteSwap(@TypeOf(shdr.sh_name), shdr.sh_name),
|
||||
.sh_type = @byteSwap(@TypeOf(shdr.sh_type), shdr.sh_type),
|
||||
.sh_flags = @byteSwap(@TypeOf(shdr.sh_flags), shdr.sh_flags),
|
||||
.sh_addr = @byteSwap(@TypeOf(shdr.sh_addr), shdr.sh_addr),
|
||||
.sh_offset = @byteSwap(@TypeOf(shdr.sh_offset), shdr.sh_offset),
|
||||
.sh_size = @byteSwap(@TypeOf(shdr.sh_size), shdr.sh_size),
|
||||
.sh_link = @byteSwap(@TypeOf(shdr.sh_link), shdr.sh_link),
|
||||
.sh_info = @byteSwap(@TypeOf(shdr.sh_info), shdr.sh_info),
|
||||
.sh_addralign = @byteSwap(@TypeOf(shdr.sh_addralign), shdr.sh_addralign),
|
||||
.sh_entsize = @byteSwap(@TypeOf(shdr.sh_entsize), shdr.sh_entsize),
|
||||
};
|
||||
}
|
||||
for (hdrs.program_headers) |*phdr| {
|
||||
phdr.* = .{
|
||||
.p_type = @byteSwap(@TypeOf(phdr.p_type), phdr.p_type),
|
||||
.p_offset = @byteSwap(@TypeOf(phdr.p_offset), phdr.p_offset),
|
||||
.p_vaddr = @byteSwap(@TypeOf(phdr.p_vaddr), phdr.p_vaddr),
|
||||
.p_paddr = @byteSwap(@TypeOf(phdr.p_paddr), phdr.p_paddr),
|
||||
.p_filesz = @byteSwap(@TypeOf(phdr.p_filesz), phdr.p_filesz),
|
||||
.p_memsz = @byteSwap(@TypeOf(phdr.p_memsz), phdr.p_memsz),
|
||||
.p_flags = @byteSwap(@TypeOf(phdr.p_flags), phdr.p_flags),
|
||||
.p_align = @byteSwap(@TypeOf(phdr.p_align), phdr.p_align),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return hdrs;
|
||||
}
|
||||
|
||||
const shdrs_32 = try allocator.alloc(Elf32_Shdr, hdrs.header.shnum);
|
||||
defer allocator.free(shdrs_32);
|
||||
|
||||
const phdrs_32 = try allocator.alloc(Elf32_Phdr, hdrs.header.phnum);
|
||||
defer allocator.free(phdrs_32);
|
||||
|
||||
const shdr_buf = std.mem.sliceAsBytes(shdrs_32);
|
||||
const phdr_buf = std.mem.sliceAsBytes(phdrs_32);
|
||||
try preadNoEof(file, shdr_buf, hdrs.header.shoff);
|
||||
try preadNoEof(file, phdr_buf, hdrs.header.phoff);
|
||||
|
||||
if (need_bswap) {
|
||||
for (hdrs.section_headers) |*shdr, i| {
|
||||
const o = shdrs_32[i];
|
||||
shdr.* = .{
|
||||
.sh_name = @byteSwap(@TypeOf(o.sh_name), o.sh_name),
|
||||
.sh_type = @byteSwap(@TypeOf(o.sh_type), o.sh_type),
|
||||
.sh_flags = @byteSwap(@TypeOf(o.sh_flags), o.sh_flags),
|
||||
.sh_addr = @byteSwap(@TypeOf(o.sh_addr), o.sh_addr),
|
||||
.sh_offset = @byteSwap(@TypeOf(o.sh_offset), o.sh_offset),
|
||||
.sh_size = @byteSwap(@TypeOf(o.sh_size), o.sh_size),
|
||||
.sh_link = @byteSwap(@TypeOf(o.sh_link), o.sh_link),
|
||||
.sh_info = @byteSwap(@TypeOf(o.sh_info), o.sh_info),
|
||||
.sh_addralign = @byteSwap(@TypeOf(o.sh_addralign), o.sh_addralign),
|
||||
.sh_entsize = @byteSwap(@TypeOf(o.sh_entsize), o.sh_entsize),
|
||||
};
|
||||
}
|
||||
for (hdrs.program_headers) |*phdr, i| {
|
||||
const o = phdrs_32[i];
|
||||
phdr.* = .{
|
||||
.p_type = @byteSwap(@TypeOf(o.p_type), o.p_type),
|
||||
.p_offset = @byteSwap(@TypeOf(o.p_offset), o.p_offset),
|
||||
.p_vaddr = @byteSwap(@TypeOf(o.p_vaddr), o.p_vaddr),
|
||||
.p_paddr = @byteSwap(@TypeOf(o.p_paddr), o.p_paddr),
|
||||
.p_filesz = @byteSwap(@TypeOf(o.p_filesz), o.p_filesz),
|
||||
.p_memsz = @byteSwap(@TypeOf(o.p_memsz), o.p_memsz),
|
||||
.p_flags = @byteSwap(@TypeOf(o.p_flags), o.p_flags),
|
||||
.p_align = @byteSwap(@TypeOf(o.p_align), o.p_align),
|
||||
};
|
||||
}
|
||||
} else {
|
||||
for (hdrs.section_headers) |*shdr, i| {
|
||||
const o = shdrs_32[i];
|
||||
shdr.* = .{
|
||||
.sh_name = o.sh_name,
|
||||
.sh_type = o.sh_type,
|
||||
.sh_flags = o.sh_flags,
|
||||
.sh_addr = o.sh_addr,
|
||||
.sh_offset = o.sh_offset,
|
||||
.sh_size = o.sh_size,
|
||||
.sh_link = o.sh_link,
|
||||
.sh_info = o.sh_info,
|
||||
.sh_addralign = o.sh_addralign,
|
||||
.sh_entsize = o.sh_entsize,
|
||||
};
|
||||
}
|
||||
for (hdrs.program_headers) |*phdr, i| {
|
||||
const o = phdrs_32[i];
|
||||
phdr.* = .{
|
||||
.p_type = o.p_type,
|
||||
.p_offset = o.p_offset,
|
||||
.p_vaddr = o.p_vaddr,
|
||||
.p_paddr = o.p_paddr,
|
||||
.p_filesz = o.p_filesz,
|
||||
.p_memsz = o.p_memsz,
|
||||
.p_flags = o.p_flags,
|
||||
.p_align = o.p_align,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return hdrs;
|
||||
}
|
||||
|
||||
pub fn int(is_64: bool, need_bswap: bool, int_32: var, int_64: var) @TypeOf(int_64) {
|
||||
if (is_64) {
|
||||
if (need_bswap) {
|
||||
return @byteSwap(@TypeOf(int_64), int_64);
|
||||
} else {
|
||||
return int_64;
|
||||
}
|
||||
} else {
|
||||
return int32(need_bswap, int_32, @TypeOf(int_64));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn int32(need_bswap: bool, int_32: var, comptime Int64: var) Int64 {
|
||||
if (need_bswap) {
|
||||
return @byteSwap(@TypeOf(int_32), int_32);
|
||||
} else {
|
||||
return int_32;
|
||||
}
|
||||
}
|
||||
|
||||
fn preadNoEof(file: std.fs.File, buf: []u8, offset: u64) !void {
|
||||
var i: u64 = 0;
|
||||
while (i < buf.len) {
|
||||
const len = file.pread(buf[i .. buf.len - i], offset + i) catch |err| switch (err) {
|
||||
error.SystemResources => return error.SystemResources,
|
||||
error.IsDir => return error.UnableToReadElfFile,
|
||||
error.OperationAborted => return error.UnableToReadElfFile,
|
||||
error.BrokenPipe => return error.UnableToReadElfFile,
|
||||
error.Unseekable => return error.UnableToReadElfFile,
|
||||
error.ConnectionResetByPeer => return error.UnableToReadElfFile,
|
||||
error.InputOutput => return error.FileSystem,
|
||||
error.Unexpected => return error.Unexpected,
|
||||
error.WouldBlock => return error.Unexpected,
|
||||
};
|
||||
if (len == 0) return error.UnexpectedEndOfFile;
|
||||
i += len;
|
||||
}
|
||||
}
|
||||
|
||||
pub const EI_NIDENT = 16;
|
||||
|
||||
pub const EI_CLASS = 4;
|
||||
|
@ -120,9 +120,11 @@ test "std.event.Group" {
|
||||
// https://github.com/ziglang/zig/issues/1908
|
||||
if (builtin.single_threaded) return error.SkipZigTest;
|
||||
|
||||
// TODO provide a way to run tests in evented I/O mode
|
||||
if (!std.io.is_async) return error.SkipZigTest;
|
||||
|
||||
// TODO this file has bit-rotted. repair it
|
||||
if (true) return error.SkipZigTest;
|
||||
|
||||
const handle = async testGroup(std.heap.page_allocator);
|
||||
}
|
||||
|
||||
|
@ -125,6 +125,9 @@ test "std.event.Lock" {
|
||||
// TODO https://github.com/ziglang/zig/issues/3251
|
||||
if (builtin.os.tag == .freebsd) return error.SkipZigTest;
|
||||
|
||||
// TODO this file has bit-rotted. repair it
|
||||
if (true) return error.SkipZigTest;
|
||||
|
||||
var lock = Lock.init();
|
||||
defer lock.deinit();
|
||||
|
||||
|
@ -96,6 +96,7 @@ pub fn updateFile(source_path: []const u8, dest_path: []const u8) !PrevStatus {
|
||||
/// atime, and mode of the source file so that the next call to `updateFile` will not need a copy.
|
||||
/// Returns the previous status of the file before updating.
|
||||
/// If any of the directories do not exist for dest_path, they are created.
|
||||
/// TODO rework this to integrate with Dir
|
||||
pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?File.Mode) !PrevStatus {
|
||||
const my_cwd = cwd();
|
||||
|
||||
@ -141,29 +142,25 @@ pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?Fil
|
||||
/// there is a possibility of power loss or application termination leaving temporary files present
|
||||
/// in the same directory as dest_path.
|
||||
/// Destination file will have the same mode as the source file.
|
||||
/// TODO rework this to integrate with Dir
|
||||
pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void {
|
||||
var in_file = try cwd().openFile(source_path, .{});
|
||||
defer in_file.close();
|
||||
|
||||
const mode = try in_file.mode();
|
||||
const in_stream = &in_file.inStream().stream;
|
||||
const stat = try in_file.stat();
|
||||
|
||||
var atomic_file = try AtomicFile.init(dest_path, mode);
|
||||
var atomic_file = try AtomicFile.init(dest_path, stat.mode);
|
||||
defer atomic_file.deinit();
|
||||
|
||||
var buf: [mem.page_size]u8 = undefined;
|
||||
while (true) {
|
||||
const amt = try in_stream.readFull(buf[0..]);
|
||||
try atomic_file.file.write(buf[0..amt]);
|
||||
if (amt != buf.len) {
|
||||
return atomic_file.finish();
|
||||
}
|
||||
}
|
||||
try atomic_file.file.writeFileAll(in_file, .{ .in_len = stat.size });
|
||||
return atomic_file.finish();
|
||||
}
|
||||
|
||||
/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is
|
||||
/// merged and readily available,
|
||||
/// Guaranteed to be atomic.
|
||||
/// On Linux, until https://patchwork.kernel.org/patch/9636735/ is merged and readily available,
|
||||
/// there is a possibility of power loss or application termination leaving temporary files present
|
||||
/// in the same directory as dest_path.
|
||||
/// TODO rework this to integrate with Dir
|
||||
pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void {
|
||||
var in_file = try cwd().openFile(source_path, .{});
|
||||
defer in_file.close();
|
||||
@ -171,14 +168,8 @@ pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.M
|
||||
var atomic_file = try AtomicFile.init(dest_path, mode);
|
||||
defer atomic_file.deinit();
|
||||
|
||||
var buf: [mem.page_size * 6]u8 = undefined;
|
||||
while (true) {
|
||||
const amt = try in_file.read(buf[0..]);
|
||||
try atomic_file.file.write(buf[0..amt]);
|
||||
if (amt != buf.len) {
|
||||
return atomic_file.finish();
|
||||
}
|
||||
}
|
||||
try atomic_file.file.writeFileAll(in_file, .{});
|
||||
return atomic_file.finish();
|
||||
}
|
||||
|
||||
/// TODO update this API to avoid a getrandom syscall for every operation. It
|
||||
@ -1150,7 +1141,7 @@ pub const Dir = struct {
|
||||
const buf = try allocator.alignedAlloc(u8, A, size);
|
||||
errdefer allocator.free(buf);
|
||||
|
||||
try file.inStream().stream.readNoEof(buf);
|
||||
try file.inStream().readNoEof(buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
@ -71,7 +71,7 @@ pub const File = struct {
|
||||
if (need_async_thread and self.io_mode == .blocking and !self.async_block_allowed) {
|
||||
std.event.Loop.instance.?.close(self.handle);
|
||||
} else {
|
||||
return os.close(self.handle);
|
||||
os.close(self.handle);
|
||||
}
|
||||
}
|
||||
|
||||
@ -250,11 +250,16 @@ pub const File = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn readAll(self: File, buffer: []u8) ReadError!void {
|
||||
/// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it
|
||||
/// means the file reached the end. Reaching the end of a file is not an error condition.
|
||||
pub fn readAll(self: File, buffer: []u8) ReadError!usize {
|
||||
var index: usize = 0;
|
||||
while (index < buffer.len) {
|
||||
index += try self.read(buffer[index..]);
|
||||
while (index != buffer.len) {
|
||||
const amt = try self.read(buffer[index..]);
|
||||
if (amt == 0) break;
|
||||
index += amt;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize {
|
||||
@ -265,11 +270,16 @@ pub const File = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn preadAll(self: File, buffer: []u8, offset: u64) PReadError!void {
|
||||
/// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it
|
||||
/// means the file reached the end. Reaching the end of a file is not an error condition.
|
||||
pub fn preadAll(self: File, buffer: []u8, offset: u64) PReadError!usize {
|
||||
var index: usize = 0;
|
||||
while (index < buffer.len) {
|
||||
index += try self.pread(buffer[index..], offset + index);
|
||||
while (index != buffer.len) {
|
||||
const amt = try self.pread(buffer[index..], offset + index);
|
||||
if (amt == 0) break;
|
||||
index += amt;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
pub fn readv(self: File, iovecs: []const os.iovec) ReadError!usize {
|
||||
@ -280,19 +290,27 @@ pub const File = struct {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of bytes read. If the number read is smaller than the total bytes
|
||||
/// from all the buffers, it means the file reached the end. Reaching the end of a file
|
||||
/// is not an error condition.
|
||||
/// The `iovecs` parameter is mutable because this function needs to mutate the fields in
|
||||
/// order to handle partial reads from the underlying OS layer.
|
||||
pub fn readvAll(self: File, iovecs: []os.iovec) ReadError!void {
|
||||
pub fn readvAll(self: File, iovecs: []os.iovec) ReadError!usize {
|
||||
if (iovecs.len == 0) return;
|
||||
|
||||
var i: usize = 0;
|
||||
var off: usize = 0;
|
||||
while (true) {
|
||||
var amt = try self.readv(iovecs[i..]);
|
||||
var eof = amt == 0;
|
||||
off += amt;
|
||||
while (amt >= iovecs[i].iov_len) {
|
||||
amt -= iovecs[i].iov_len;
|
||||
i += 1;
|
||||
if (i >= iovecs.len) return;
|
||||
if (i >= iovecs.len) return off;
|
||||
eof = false;
|
||||
}
|
||||
if (eof) return off;
|
||||
iovecs[i].iov_base += amt;
|
||||
iovecs[i].iov_len -= amt;
|
||||
}
|
||||
@ -306,6 +324,9 @@ pub const File = struct {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of bytes read. If the number read is smaller than the total bytes
|
||||
/// from all the buffers, it means the file reached the end. Reaching the end of a file
|
||||
/// is not an error condition.
|
||||
/// The `iovecs` parameter is mutable because this function needs to mutate the fields in
|
||||
/// order to handle partial reads from the underlying OS layer.
|
||||
pub fn preadvAll(self: File, iovecs: []const os.iovec, offset: u64) PReadError!void {
|
||||
@ -315,12 +336,15 @@ pub const File = struct {
|
||||
var off: usize = 0;
|
||||
while (true) {
|
||||
var amt = try self.preadv(iovecs[i..], offset + off);
|
||||
var eof = amt == 0;
|
||||
off += amt;
|
||||
while (amt >= iovecs[i].iov_len) {
|
||||
amt -= iovecs[i].iov_len;
|
||||
i += 1;
|
||||
if (i >= iovecs.len) return;
|
||||
if (i >= iovecs.len) return off;
|
||||
eof = false;
|
||||
}
|
||||
if (eof) return off;
|
||||
iovecs[i].iov_base += amt;
|
||||
iovecs[i].iov_len -= amt;
|
||||
}
|
||||
@ -496,85 +520,29 @@ pub const File = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inStream(file: File) InStream {
|
||||
return InStream{
|
||||
.file = file,
|
||||
.stream = InStream.Stream{ .readFn = InStream.readFn },
|
||||
};
|
||||
pub const InStream = io.InStream(File, ReadError, read);
|
||||
|
||||
pub fn inStream(file: File) io.InStream(File, ReadError, read) {
|
||||
return .{ .context = file };
|
||||
}
|
||||
|
||||
pub const OutStream = io.OutStream(File, WriteError, write);
|
||||
|
||||
pub fn outStream(file: File) OutStream {
|
||||
return OutStream{
|
||||
.file = file,
|
||||
.stream = OutStream.Stream{ .writeFn = OutStream.writeFn },
|
||||
};
|
||||
return .{ .context = file };
|
||||
}
|
||||
|
||||
pub const SeekableStream = io.SeekableStream(
|
||||
File,
|
||||
SeekError,
|
||||
GetPosError,
|
||||
seekTo,
|
||||
seekBy,
|
||||
getPos,
|
||||
getEndPos,
|
||||
);
|
||||
|
||||
pub fn seekableStream(file: File) SeekableStream {
|
||||
return SeekableStream{
|
||||
.file = file,
|
||||
.stream = SeekableStream.Stream{
|
||||
.seekToFn = SeekableStream.seekToFn,
|
||||
.seekByFn = SeekableStream.seekByFn,
|
||||
.getPosFn = SeekableStream.getPosFn,
|
||||
.getEndPosFn = SeekableStream.getEndPosFn,
|
||||
},
|
||||
};
|
||||
return .{ .context = file };
|
||||
}
|
||||
|
||||
/// Implementation of io.InStream trait for File
|
||||
pub const InStream = struct {
|
||||
file: File,
|
||||
stream: Stream,
|
||||
|
||||
pub const Error = ReadError;
|
||||
pub const Stream = io.InStream(Error);
|
||||
|
||||
fn readFn(in_stream: *Stream, buffer: []u8) Error!usize {
|
||||
const self = @fieldParentPtr(InStream, "stream", in_stream);
|
||||
return self.file.read(buffer);
|
||||
}
|
||||
};
|
||||
|
||||
/// Implementation of io.OutStream trait for File
|
||||
pub const OutStream = struct {
|
||||
file: File,
|
||||
stream: Stream,
|
||||
|
||||
pub const Error = WriteError;
|
||||
pub const Stream = io.OutStream(Error);
|
||||
|
||||
fn writeFn(out_stream: *Stream, bytes: []const u8) Error!usize {
|
||||
const self = @fieldParentPtr(OutStream, "stream", out_stream);
|
||||
return self.file.write(bytes);
|
||||
}
|
||||
};
|
||||
|
||||
/// Implementation of io.SeekableStream trait for File
|
||||
pub const SeekableStream = struct {
|
||||
file: File,
|
||||
stream: Stream,
|
||||
|
||||
pub const Stream = io.SeekableStream(SeekError, GetPosError);
|
||||
|
||||
pub fn seekToFn(seekable_stream: *Stream, pos: u64) SeekError!void {
|
||||
const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream);
|
||||
return self.file.seekTo(pos);
|
||||
}
|
||||
|
||||
pub fn seekByFn(seekable_stream: *Stream, amt: i64) SeekError!void {
|
||||
const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream);
|
||||
return self.file.seekBy(amt);
|
||||
}
|
||||
|
||||
pub fn getEndPosFn(seekable_stream: *Stream) GetPosError!u64 {
|
||||
const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream);
|
||||
return self.file.getEndPos();
|
||||
}
|
||||
|
||||
pub fn getPosFn(seekable_stream: *Stream) GetPosError!u64 {
|
||||
const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream);
|
||||
return self.file.getPos();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -10,6 +10,7 @@ const c = std.c;
|
||||
const maxInt = std.math.maxInt;
|
||||
|
||||
pub const LoggingAllocator = @import("heap/logging_allocator.zig").LoggingAllocator;
|
||||
pub const loggingAllocator = @import("heap/logging_allocator.zig").loggingAllocator;
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
|
||||
|
@ -1,63 +1,69 @@
|
||||
const std = @import("../std.zig");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const AnyErrorOutStream = std.io.OutStream(anyerror);
|
||||
|
||||
/// This allocator is used in front of another allocator and logs to the provided stream
|
||||
/// on every call to the allocator. Stream errors are ignored.
|
||||
/// If https://github.com/ziglang/zig/issues/2586 is implemented, this API can be improved.
|
||||
pub const LoggingAllocator = struct {
|
||||
allocator: Allocator,
|
||||
pub fn LoggingAllocator(comptime OutStreamType: type) type {
|
||||
return struct {
|
||||
allocator: Allocator,
|
||||
parent_allocator: *Allocator,
|
||||
out_stream: OutStreamType,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn init(parent_allocator: *Allocator, out_stream: OutStreamType) Self {
|
||||
return Self{
|
||||
.allocator = Allocator{
|
||||
.reallocFn = realloc,
|
||||
.shrinkFn = shrink,
|
||||
},
|
||||
.parent_allocator = parent_allocator,
|
||||
.out_stream = out_stream,
|
||||
};
|
||||
}
|
||||
|
||||
fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
|
||||
const self = @fieldParentPtr(Self, "allocator", allocator);
|
||||
if (old_mem.len == 0) {
|
||||
self.out_stream.print("allocation of {} ", .{new_size}) catch {};
|
||||
} else {
|
||||
self.out_stream.print("resize from {} to {} ", .{ old_mem.len, new_size }) catch {};
|
||||
}
|
||||
const result = self.parent_allocator.reallocFn(self.parent_allocator, old_mem, old_align, new_size, new_align);
|
||||
if (result) |buff| {
|
||||
self.out_stream.print("success!\n", .{}) catch {};
|
||||
} else |err| {
|
||||
self.out_stream.print("failure!\n", .{}) catch {};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
|
||||
const self = @fieldParentPtr(Self, "allocator", allocator);
|
||||
const result = self.parent_allocator.shrinkFn(self.parent_allocator, old_mem, old_align, new_size, new_align);
|
||||
if (new_size == 0) {
|
||||
self.out_stream.print("free of {} bytes success!\n", .{old_mem.len}) catch {};
|
||||
} else {
|
||||
self.out_stream.print("shrink from {} bytes to {} bytes success!\n", .{ old_mem.len, new_size }) catch {};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn loggingAllocator(
|
||||
parent_allocator: *Allocator,
|
||||
out_stream: *AnyErrorOutStream,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn init(parent_allocator: *Allocator, out_stream: *AnyErrorOutStream) Self {
|
||||
return Self{
|
||||
.allocator = Allocator{
|
||||
.reallocFn = realloc,
|
||||
.shrinkFn = shrink,
|
||||
},
|
||||
.parent_allocator = parent_allocator,
|
||||
.out_stream = out_stream,
|
||||
};
|
||||
}
|
||||
|
||||
fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
|
||||
const self = @fieldParentPtr(Self, "allocator", allocator);
|
||||
if (old_mem.len == 0) {
|
||||
self.out_stream.print("allocation of {} ", .{new_size}) catch {};
|
||||
} else {
|
||||
self.out_stream.print("resize from {} to {} ", .{ old_mem.len, new_size }) catch {};
|
||||
}
|
||||
const result = self.parent_allocator.reallocFn(self.parent_allocator, old_mem, old_align, new_size, new_align);
|
||||
if (result) |buff| {
|
||||
self.out_stream.print("success!\n", .{}) catch {};
|
||||
} else |err| {
|
||||
self.out_stream.print("failure!\n", .{}) catch {};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
|
||||
const self = @fieldParentPtr(Self, "allocator", allocator);
|
||||
const result = self.parent_allocator.shrinkFn(self.parent_allocator, old_mem, old_align, new_size, new_align);
|
||||
if (new_size == 0) {
|
||||
self.out_stream.print("free of {} bytes success!\n", .{old_mem.len}) catch {};
|
||||
} else {
|
||||
self.out_stream.print("shrink from {} bytes to {} bytes success!\n", .{ old_mem.len, new_size }) catch {};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
out_stream: var,
|
||||
) LoggingAllocator(@TypeOf(out_stream)) {
|
||||
return LoggingAllocator(@TypeOf(out_stream)).init(parent_allocator, out_stream);
|
||||
}
|
||||
|
||||
test "LoggingAllocator" {
|
||||
var buf: [255]u8 = undefined;
|
||||
var slice_stream = std.io.SliceOutStream.init(buf[0..]);
|
||||
const stream = &slice_stream.stream;
|
||||
var fbs = std.io.fixedBufferStream(&buf);
|
||||
|
||||
const allocator = &LoggingAllocator.init(std.testing.allocator, @ptrCast(*AnyErrorOutStream, stream)).allocator;
|
||||
const allocator = &loggingAllocator(std.testing.allocator, fbs.outStream()).allocator;
|
||||
|
||||
const ptr = try allocator.alloc(u8, 10);
|
||||
allocator.free(ptr);
|
||||
@ -66,5 +72,5 @@ test "LoggingAllocator" {
|
||||
\\allocation of 10 success!
|
||||
\\free of 10 bytes success!
|
||||
\\
|
||||
, slice_stream.getWritten());
|
||||
, fbs.getWritten());
|
||||
}
|
||||
|
1079
lib/std/io.zig
1079
lib/std/io.zig
File diff suppressed because it is too large
Load Diff
243
lib/std/io/bit_in_stream.zig
Normal file
243
lib/std/io/bit_in_stream.zig
Normal file
@ -0,0 +1,243 @@
|
||||
const std = @import("../std.zig");
|
||||
const builtin = std.builtin;
|
||||
const io = std.io;
|
||||
const assert = std.debug.assert;
|
||||
const testing = std.testing;
|
||||
const trait = std.meta.trait;
|
||||
const meta = std.meta;
|
||||
const math = std.math;
|
||||
|
||||
/// Creates a stream which allows for reading bit fields from another stream
|
||||
pub fn BitInStream(endian: builtin.Endian, comptime InStreamType: type) type {
|
||||
return struct {
|
||||
in_stream: InStreamType,
|
||||
bit_buffer: u7,
|
||||
bit_count: u3,
|
||||
|
||||
pub const Error = InStreamType.Error;
|
||||
pub const InStream = io.InStream(*Self, Error, read);
|
||||
|
||||
const Self = @This();
|
||||
const u8_bit_count = comptime meta.bitCount(u8);
|
||||
const u7_bit_count = comptime meta.bitCount(u7);
|
||||
const u4_bit_count = comptime meta.bitCount(u4);
|
||||
|
||||
pub fn init(in_stream: InStreamType) Self {
|
||||
return Self{
|
||||
.in_stream = in_stream,
|
||||
.bit_buffer = 0,
|
||||
.bit_count = 0,
|
||||
};
|
||||
}
|
||||
|
||||
/// Reads `bits` bits from the stream and returns a specified unsigned int type
|
||||
/// containing them in the least significant end, returning an error if the
|
||||
/// specified number of bits could not be read.
|
||||
pub fn readBitsNoEof(self: *Self, comptime U: type, bits: usize) !U {
|
||||
var n: usize = undefined;
|
||||
const result = try self.readBits(U, bits, &n);
|
||||
if (n < bits) return error.EndOfStream;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Reads `bits` bits from the stream and returns a specified unsigned int type
|
||||
/// containing them in the least significant end. The number of bits successfully
|
||||
/// read is placed in `out_bits`, as reaching the end of the stream is not an error.
|
||||
pub fn readBits(self: *Self, comptime U: type, bits: usize, out_bits: *usize) Error!U {
|
||||
comptime assert(trait.isUnsignedInt(U));
|
||||
|
||||
//by extending the buffer to a minimum of u8 we can cover a number of edge cases
|
||||
// related to shifting and casting.
|
||||
const u_bit_count = comptime meta.bitCount(U);
|
||||
const buf_bit_count = bc: {
|
||||
assert(u_bit_count >= bits);
|
||||
break :bc if (u_bit_count <= u8_bit_count) u8_bit_count else u_bit_count;
|
||||
};
|
||||
const Buf = std.meta.IntType(false, buf_bit_count);
|
||||
const BufShift = math.Log2Int(Buf);
|
||||
|
||||
out_bits.* = @as(usize, 0);
|
||||
if (U == u0 or bits == 0) return 0;
|
||||
var out_buffer = @as(Buf, 0);
|
||||
|
||||
if (self.bit_count > 0) {
|
||||
const n = if (self.bit_count >= bits) @intCast(u3, bits) else self.bit_count;
|
||||
const shift = u7_bit_count - n;
|
||||
switch (endian) {
|
||||
.Big => {
|
||||
out_buffer = @as(Buf, self.bit_buffer >> shift);
|
||||
if (n >= u7_bit_count)
|
||||
self.bit_buffer = 0
|
||||
else
|
||||
self.bit_buffer <<= n;
|
||||
},
|
||||
.Little => {
|
||||
const value = (self.bit_buffer << shift) >> shift;
|
||||
out_buffer = @as(Buf, value);
|
||||
if (n >= u7_bit_count)
|
||||
self.bit_buffer = 0
|
||||
else
|
||||
self.bit_buffer >>= n;
|
||||
},
|
||||
}
|
||||
self.bit_count -= n;
|
||||
out_bits.* = n;
|
||||
}
|
||||
//at this point we know bit_buffer is empty
|
||||
|
||||
//copy bytes until we have enough bits, then leave the rest in bit_buffer
|
||||
while (out_bits.* < bits) {
|
||||
const n = bits - out_bits.*;
|
||||
const next_byte = self.in_stream.readByte() catch |err| {
|
||||
if (err == error.EndOfStream) {
|
||||
return @intCast(U, out_buffer);
|
||||
}
|
||||
//@BUG: See #1810. Not sure if the bug is that I have to do this for some
|
||||
// streams, or that I don't for streams with emtpy errorsets.
|
||||
return @errSetCast(Error, err);
|
||||
};
|
||||
|
||||
switch (endian) {
|
||||
.Big => {
|
||||
if (n >= u8_bit_count) {
|
||||
out_buffer <<= @intCast(u3, u8_bit_count - 1);
|
||||
out_buffer <<= 1;
|
||||
out_buffer |= @as(Buf, next_byte);
|
||||
out_bits.* += u8_bit_count;
|
||||
continue;
|
||||
}
|
||||
|
||||
const shift = @intCast(u3, u8_bit_count - n);
|
||||
out_buffer <<= @intCast(BufShift, n);
|
||||
out_buffer |= @as(Buf, next_byte >> shift);
|
||||
out_bits.* += n;
|
||||
self.bit_buffer = @truncate(u7, next_byte << @intCast(u3, n - 1));
|
||||
self.bit_count = shift;
|
||||
},
|
||||
.Little => {
|
||||
if (n >= u8_bit_count) {
|
||||
out_buffer |= @as(Buf, next_byte) << @intCast(BufShift, out_bits.*);
|
||||
out_bits.* += u8_bit_count;
|
||||
continue;
|
||||
}
|
||||
|
||||
const shift = @intCast(u3, u8_bit_count - n);
|
||||
const value = (next_byte << shift) >> shift;
|
||||
out_buffer |= @as(Buf, value) << @intCast(BufShift, out_bits.*);
|
||||
out_bits.* += n;
|
||||
self.bit_buffer = @truncate(u7, next_byte >> @intCast(u3, n));
|
||||
self.bit_count = shift;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return @intCast(U, out_buffer);
|
||||
}
|
||||
|
||||
pub fn alignToByte(self: *Self) void {
|
||||
self.bit_buffer = 0;
|
||||
self.bit_count = 0;
|
||||
}
|
||||
|
||||
pub fn read(self: *Self, buffer: []u8) Error!usize {
|
||||
var out_bits: usize = undefined;
|
||||
var out_bits_total = @as(usize, 0);
|
||||
//@NOTE: I'm not sure this is a good idea, maybe alignToByte should be forced
|
||||
if (self.bit_count > 0) {
|
||||
for (buffer) |*b, i| {
|
||||
b.* = try self.readBits(u8, u8_bit_count, &out_bits);
|
||||
out_bits_total += out_bits;
|
||||
}
|
||||
const incomplete_byte = @boolToInt(out_bits_total % u8_bit_count > 0);
|
||||
return (out_bits_total / u8_bit_count) + incomplete_byte;
|
||||
}
|
||||
|
||||
return self.in_stream.read(buffer);
|
||||
}
|
||||
|
||||
pub fn inStream(self: *Self) InStream {
|
||||
return .{ .context = self };
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn bitInStream(
|
||||
comptime endian: builtin.Endian,
|
||||
underlying_stream: var,
|
||||
) BitInStream(endian, @TypeOf(underlying_stream)) {
|
||||
return BitInStream(endian, @TypeOf(underlying_stream)).init(underlying_stream);
|
||||
}
|
||||
|
||||
test "api coverage" {
|
||||
const mem_be = [_]u8{ 0b11001101, 0b00001011 };
|
||||
const mem_le = [_]u8{ 0b00011101, 0b10010101 };
|
||||
|
||||
var mem_in_be = io.fixedBufferStream(&mem_be);
|
||||
var bit_stream_be = bitInStream(.Big, mem_in_be.inStream());
|
||||
|
||||
var out_bits: usize = undefined;
|
||||
|
||||
const expect = testing.expect;
|
||||
const expectError = testing.expectError;
|
||||
|
||||
expect(1 == try bit_stream_be.readBits(u2, 1, &out_bits));
|
||||
expect(out_bits == 1);
|
||||
expect(2 == try bit_stream_be.readBits(u5, 2, &out_bits));
|
||||
expect(out_bits == 2);
|
||||
expect(3 == try bit_stream_be.readBits(u128, 3, &out_bits));
|
||||
expect(out_bits == 3);
|
||||
expect(4 == try bit_stream_be.readBits(u8, 4, &out_bits));
|
||||
expect(out_bits == 4);
|
||||
expect(5 == try bit_stream_be.readBits(u9, 5, &out_bits));
|
||||
expect(out_bits == 5);
|
||||
expect(1 == try bit_stream_be.readBits(u1, 1, &out_bits));
|
||||
expect(out_bits == 1);
|
||||
|
||||
mem_in_be.pos = 0;
|
||||
bit_stream_be.bit_count = 0;
|
||||
expect(0b110011010000101 == try bit_stream_be.readBits(u15, 15, &out_bits));
|
||||
expect(out_bits == 15);
|
||||
|
||||
mem_in_be.pos = 0;
|
||||
bit_stream_be.bit_count = 0;
|
||||
expect(0b1100110100001011 == try bit_stream_be.readBits(u16, 16, &out_bits));
|
||||
expect(out_bits == 16);
|
||||
|
||||
_ = try bit_stream_be.readBits(u0, 0, &out_bits);
|
||||
|
||||
expect(0 == try bit_stream_be.readBits(u1, 1, &out_bits));
|
||||
expect(out_bits == 0);
|
||||
expectError(error.EndOfStream, bit_stream_be.readBitsNoEof(u1, 1));
|
||||
|
||||
var mem_in_le = io.fixedBufferStream(&mem_le);
|
||||
var bit_stream_le = bitInStream(.Little, mem_in_le.inStream());
|
||||
|
||||
expect(1 == try bit_stream_le.readBits(u2, 1, &out_bits));
|
||||
expect(out_bits == 1);
|
||||
expect(2 == try bit_stream_le.readBits(u5, 2, &out_bits));
|
||||
expect(out_bits == 2);
|
||||
expect(3 == try bit_stream_le.readBits(u128, 3, &out_bits));
|
||||
expect(out_bits == 3);
|
||||
expect(4 == try bit_stream_le.readBits(u8, 4, &out_bits));
|
||||
expect(out_bits == 4);
|
||||
expect(5 == try bit_stream_le.readBits(u9, 5, &out_bits));
|
||||
expect(out_bits == 5);
|
||||
expect(1 == try bit_stream_le.readBits(u1, 1, &out_bits));
|
||||
expect(out_bits == 1);
|
||||
|
||||
mem_in_le.pos = 0;
|
||||
bit_stream_le.bit_count = 0;
|
||||
expect(0b001010100011101 == try bit_stream_le.readBits(u15, 15, &out_bits));
|
||||
expect(out_bits == 15);
|
||||
|
||||
mem_in_le.pos = 0;
|
||||
bit_stream_le.bit_count = 0;
|
||||
expect(0b1001010100011101 == try bit_stream_le.readBits(u16, 16, &out_bits));
|
||||
expect(out_bits == 16);
|
||||
|
||||
_ = try bit_stream_le.readBits(u0, 0, &out_bits);
|
||||
|
||||
expect(0 == try bit_stream_le.readBits(u1, 1, &out_bits));
|
||||
expect(out_bits == 0);
|
||||
expectError(error.EndOfStream, bit_stream_le.readBitsNoEof(u1, 1));
|
||||
}
|
197
lib/std/io/bit_out_stream.zig
Normal file
197
lib/std/io/bit_out_stream.zig
Normal file
@ -0,0 +1,197 @@
|
||||
const std = @import("../std.zig");
|
||||
const builtin = std.builtin;
|
||||
const io = std.io;
|
||||
const testing = std.testing;
|
||||
const assert = std.debug.assert;
|
||||
const trait = std.meta.trait;
|
||||
const meta = std.meta;
|
||||
const math = std.math;
|
||||
|
||||
/// Creates a stream which allows for writing bit fields to another stream
|
||||
pub fn BitOutStream(endian: builtin.Endian, comptime OutStreamType: type) type {
|
||||
return struct {
|
||||
out_stream: OutStreamType,
|
||||
bit_buffer: u8,
|
||||
bit_count: u4,
|
||||
|
||||
pub const Error = OutStreamType.Error;
|
||||
pub const OutStream = io.OutStream(*Self, Error, write);
|
||||
|
||||
const Self = @This();
|
||||
const u8_bit_count = comptime meta.bitCount(u8);
|
||||
const u4_bit_count = comptime meta.bitCount(u4);
|
||||
|
||||
pub fn init(out_stream: OutStreamType) Self {
|
||||
return Self{
|
||||
.out_stream = out_stream,
|
||||
.bit_buffer = 0,
|
||||
.bit_count = 0,
|
||||
};
|
||||
}
|
||||
|
||||
/// Write the specified number of bits to the stream from the least significant bits of
|
||||
/// the specified unsigned int value. Bits will only be written to the stream when there
|
||||
/// are enough to fill a byte.
|
||||
pub fn writeBits(self: *Self, value: var, bits: usize) Error!void {
|
||||
if (bits == 0) return;
|
||||
|
||||
const U = @TypeOf(value);
|
||||
comptime assert(trait.isUnsignedInt(U));
|
||||
|
||||
//by extending the buffer to a minimum of u8 we can cover a number of edge cases
|
||||
// related to shifting and casting.
|
||||
const u_bit_count = comptime meta.bitCount(U);
|
||||
const buf_bit_count = bc: {
|
||||
assert(u_bit_count >= bits);
|
||||
break :bc if (u_bit_count <= u8_bit_count) u8_bit_count else u_bit_count;
|
||||
};
|
||||
const Buf = std.meta.IntType(false, buf_bit_count);
|
||||
const BufShift = math.Log2Int(Buf);
|
||||
|
||||
const buf_value = @intCast(Buf, value);
|
||||
|
||||
const high_byte_shift = @intCast(BufShift, buf_bit_count - u8_bit_count);
|
||||
var in_buffer = switch (endian) {
|
||||
.Big => buf_value << @intCast(BufShift, buf_bit_count - bits),
|
||||
.Little => buf_value,
|
||||
};
|
||||
var in_bits = bits;
|
||||
|
||||
if (self.bit_count > 0) {
|
||||
const bits_remaining = u8_bit_count - self.bit_count;
|
||||
const n = @intCast(u3, if (bits_remaining > bits) bits else bits_remaining);
|
||||
switch (endian) {
|
||||
.Big => {
|
||||
const shift = @intCast(BufShift, high_byte_shift + self.bit_count);
|
||||
const v = @intCast(u8, in_buffer >> shift);
|
||||
self.bit_buffer |= v;
|
||||
in_buffer <<= n;
|
||||
},
|
||||
.Little => {
|
||||
const v = @truncate(u8, in_buffer) << @intCast(u3, self.bit_count);
|
||||
self.bit_buffer |= v;
|
||||
in_buffer >>= n;
|
||||
},
|
||||
}
|
||||
self.bit_count += n;
|
||||
in_bits -= n;
|
||||
|
||||
//if we didn't fill the buffer, it's because bits < bits_remaining;
|
||||
if (self.bit_count != u8_bit_count) return;
|
||||
try self.out_stream.writeByte(self.bit_buffer);
|
||||
self.bit_buffer = 0;
|
||||
self.bit_count = 0;
|
||||
}
|
||||
//at this point we know bit_buffer is empty
|
||||
|
||||
//copy bytes until we can't fill one anymore, then leave the rest in bit_buffer
|
||||
while (in_bits >= u8_bit_count) {
|
||||
switch (endian) {
|
||||
.Big => {
|
||||
const v = @intCast(u8, in_buffer >> high_byte_shift);
|
||||
try self.out_stream.writeByte(v);
|
||||
in_buffer <<= @intCast(u3, u8_bit_count - 1);
|
||||
in_buffer <<= 1;
|
||||
},
|
||||
.Little => {
|
||||
const v = @truncate(u8, in_buffer);
|
||||
try self.out_stream.writeByte(v);
|
||||
in_buffer >>= @intCast(u3, u8_bit_count - 1);
|
||||
in_buffer >>= 1;
|
||||
},
|
||||
}
|
||||
in_bits -= u8_bit_count;
|
||||
}
|
||||
|
||||
if (in_bits > 0) {
|
||||
self.bit_count = @intCast(u4, in_bits);
|
||||
self.bit_buffer = switch (endian) {
|
||||
.Big => @truncate(u8, in_buffer >> high_byte_shift),
|
||||
.Little => @truncate(u8, in_buffer),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Flush any remaining bits to the stream.
|
||||
pub fn flushBits(self: *Self) Error!void {
|
||||
if (self.bit_count == 0) return;
|
||||
try self.out_stream.writeByte(self.bit_buffer);
|
||||
self.bit_buffer = 0;
|
||||
self.bit_count = 0;
|
||||
}
|
||||
|
||||
pub fn write(self: *Self, buffer: []const u8) Error!usize {
|
||||
// TODO: I'm not sure this is a good idea, maybe flushBits should be forced
|
||||
if (self.bit_count > 0) {
|
||||
for (buffer) |b, i|
|
||||
try self.writeBits(b, u8_bit_count);
|
||||
return buffer.len;
|
||||
}
|
||||
|
||||
return self.out_stream.write(buffer);
|
||||
}
|
||||
|
||||
pub fn outStream(self: *Self) OutStream {
|
||||
return .{ .context = self };
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn bitOutStream(
|
||||
comptime endian: builtin.Endian,
|
||||
underlying_stream: var,
|
||||
) BitOutStream(endian, @TypeOf(underlying_stream)) {
|
||||
return BitOutStream(endian, @TypeOf(underlying_stream)).init(underlying_stream);
|
||||
}
|
||||
|
||||
test "api coverage" {
|
||||
var mem_be = [_]u8{0} ** 2;
|
||||
var mem_le = [_]u8{0} ** 2;
|
||||
|
||||
var mem_out_be = io.fixedBufferStream(&mem_be);
|
||||
var bit_stream_be = bitOutStream(.Big, mem_out_be.outStream());
|
||||
|
||||
try bit_stream_be.writeBits(@as(u2, 1), 1);
|
||||
try bit_stream_be.writeBits(@as(u5, 2), 2);
|
||||
try bit_stream_be.writeBits(@as(u128, 3), 3);
|
||||
try bit_stream_be.writeBits(@as(u8, 4), 4);
|
||||
try bit_stream_be.writeBits(@as(u9, 5), 5);
|
||||
try bit_stream_be.writeBits(@as(u1, 1), 1);
|
||||
|
||||
testing.expect(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001011);
|
||||
|
||||
mem_out_be.pos = 0;
|
||||
|
||||
try bit_stream_be.writeBits(@as(u15, 0b110011010000101), 15);
|
||||
try bit_stream_be.flushBits();
|
||||
testing.expect(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001010);
|
||||
|
||||
mem_out_be.pos = 0;
|
||||
try bit_stream_be.writeBits(@as(u32, 0b110011010000101), 16);
|
||||
testing.expect(mem_be[0] == 0b01100110 and mem_be[1] == 0b10000101);
|
||||
|
||||
try bit_stream_be.writeBits(@as(u0, 0), 0);
|
||||
|
||||
var mem_out_le = io.fixedBufferStream(&mem_le);
|
||||
var bit_stream_le = bitOutStream(.Little, mem_out_le.outStream());
|
||||
|
||||
try bit_stream_le.writeBits(@as(u2, 1), 1);
|
||||
try bit_stream_le.writeBits(@as(u5, 2), 2);
|
||||
try bit_stream_le.writeBits(@as(u128, 3), 3);
|
||||
try bit_stream_le.writeBits(@as(u8, 4), 4);
|
||||
try bit_stream_le.writeBits(@as(u9, 5), 5);
|
||||
try bit_stream_le.writeBits(@as(u1, 1), 1);
|
||||
|
||||
testing.expect(mem_le[0] == 0b00011101 and mem_le[1] == 0b10010101);
|
||||
|
||||
mem_out_le.pos = 0;
|
||||
try bit_stream_le.writeBits(@as(u15, 0b110011010000101), 15);
|
||||
try bit_stream_le.flushBits();
|
||||
testing.expect(mem_le[0] == 0b10000101 and mem_le[1] == 0b01100110);
|
||||
|
||||
mem_out_le.pos = 0;
|
||||
try bit_stream_le.writeBits(@as(u32, 0b1100110100001011), 16);
|
||||
testing.expect(mem_le[0] == 0b00001011 and mem_le[1] == 0b11001101);
|
||||
|
||||
try bit_stream_le.writeBits(@as(u0, 0), 0);
|
||||
}
|
50
lib/std/io/buffered_atomic_file.zig
Normal file
50
lib/std/io/buffered_atomic_file.zig
Normal file
@ -0,0 +1,50 @@
|
||||
const std = @import("../std.zig");
|
||||
const mem = std.mem;
|
||||
const fs = std.fs;
|
||||
const File = std.fs.File;
|
||||
|
||||
pub const BufferedAtomicFile = struct {
|
||||
atomic_file: fs.AtomicFile,
|
||||
file_stream: File.OutStream,
|
||||
buffered_stream: BufferedOutStream,
|
||||
allocator: *mem.Allocator,
|
||||
|
||||
pub const buffer_size = 4096;
|
||||
pub const BufferedOutStream = std.io.BufferedOutStream(buffer_size, File.OutStream);
|
||||
pub const OutStream = std.io.OutStream(*BufferedOutStream, BufferedOutStream.Error, BufferedOutStream.write);
|
||||
|
||||
/// TODO when https://github.com/ziglang/zig/issues/2761 is solved
|
||||
/// this API will not need an allocator
|
||||
pub fn create(allocator: *mem.Allocator, dest_path: []const u8) !*BufferedAtomicFile {
|
||||
var self = try allocator.create(BufferedAtomicFile);
|
||||
self.* = BufferedAtomicFile{
|
||||
.atomic_file = undefined,
|
||||
.file_stream = undefined,
|
||||
.buffered_stream = undefined,
|
||||
.allocator = allocator,
|
||||
};
|
||||
errdefer allocator.destroy(self);
|
||||
|
||||
self.atomic_file = try fs.AtomicFile.init(dest_path, File.default_mode);
|
||||
errdefer self.atomic_file.deinit();
|
||||
|
||||
self.file_stream = self.atomic_file.file.outStream();
|
||||
self.buffered_stream = .{ .unbuffered_out_stream = self.file_stream };
|
||||
return self;
|
||||
}
|
||||
|
||||
/// always call destroy, even after successful finish()
|
||||
pub fn destroy(self: *BufferedAtomicFile) void {
|
||||
self.atomic_file.deinit();
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
|
||||
pub fn finish(self: *BufferedAtomicFile) !void {
|
||||
try self.buffered_stream.flush();
|
||||
try self.atomic_file.finish();
|
||||
}
|
||||
|
||||
pub fn stream(self: *BufferedAtomicFile) OutStream {
|
||||
return .{ .context = &self.buffered_stream };
|
||||
}
|
||||
};
|
86
lib/std/io/buffered_in_stream.zig
Normal file
86
lib/std/io/buffered_in_stream.zig
Normal file
@ -0,0 +1,86 @@
|
||||
const std = @import("../std.zig");
|
||||
const io = std.io;
|
||||
const assert = std.debug.assert;
|
||||
const testing = std.testing;
|
||||
|
||||
pub fn BufferedInStream(comptime buffer_size: usize, comptime InStreamType: type) type {
|
||||
return struct {
|
||||
unbuffered_in_stream: InStreamType,
|
||||
fifo: FifoType = FifoType.init(),
|
||||
|
||||
pub const Error = InStreamType.Error;
|
||||
pub const InStream = io.InStream(*Self, Error, read);
|
||||
|
||||
const Self = @This();
|
||||
const FifoType = std.fifo.LinearFifo(u8, std.fifo.LinearFifoBufferType{ .Static = buffer_size });
|
||||
|
||||
pub fn read(self: *Self, dest: []u8) Error!usize {
|
||||
var dest_index: usize = 0;
|
||||
while (dest_index < dest.len) {
|
||||
const written = self.fifo.read(dest[dest_index..]);
|
||||
if (written == 0) {
|
||||
// fifo empty, fill it
|
||||
const writable = self.fifo.writableSlice(0);
|
||||
assert(writable.len > 0);
|
||||
const n = try self.unbuffered_in_stream.read(writable);
|
||||
if (n == 0) {
|
||||
// reading from the unbuffered stream returned nothing
|
||||
// so we have nothing left to read.
|
||||
return dest_index;
|
||||
}
|
||||
self.fifo.update(n);
|
||||
}
|
||||
dest_index += written;
|
||||
}
|
||||
return dest.len;
|
||||
}
|
||||
|
||||
pub fn inStream(self: *Self) InStream {
|
||||
return .{ .context = self };
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn bufferedInStream(underlying_stream: var) BufferedInStream(4096, @TypeOf(underlying_stream)) {
|
||||
return .{ .unbuffered_in_stream = underlying_stream };
|
||||
}
|
||||
|
||||
test "io.BufferedInStream" {
|
||||
const OneByteReadInStream = struct {
|
||||
str: []const u8,
|
||||
curr: usize,
|
||||
|
||||
const Error = error{NoError};
|
||||
const Self = @This();
|
||||
const InStream = io.InStream(*Self, Error, read);
|
||||
|
||||
fn init(str: []const u8) Self {
|
||||
return Self{
|
||||
.str = str,
|
||||
.curr = 0,
|
||||
};
|
||||
}
|
||||
|
||||
fn read(self: *Self, dest: []u8) Error!usize {
|
||||
if (self.str.len <= self.curr or dest.len == 0)
|
||||
return 0;
|
||||
|
||||
dest[0] = self.str[self.curr];
|
||||
self.curr += 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
fn inStream(self: *Self) InStream {
|
||||
return .{ .context = self };
|
||||
}
|
||||
};
|
||||
|
||||
const str = "This is a test";
|
||||
var one_byte_stream = OneByteReadInStream.init(str);
|
||||
var buf_in_stream = bufferedInStream(one_byte_stream.inStream());
|
||||
const stream = buf_in_stream.inStream();
|
||||
|
||||
const res = try stream.readAllAlloc(testing.allocator, str.len + 1);
|
||||
defer testing.allocator.free(res);
|
||||
testing.expectEqualSlices(u8, str, res);
|
||||
}
|
41
lib/std/io/buffered_out_stream.zig
Normal file
41
lib/std/io/buffered_out_stream.zig
Normal file
@ -0,0 +1,41 @@
|
||||
const std = @import("../std.zig");
|
||||
const io = std.io;
|
||||
|
||||
pub fn BufferedOutStream(comptime buffer_size: usize, comptime OutStreamType: type) type {
|
||||
return struct {
|
||||
unbuffered_out_stream: OutStreamType,
|
||||
fifo: FifoType = FifoType.init(),
|
||||
|
||||
pub const Error = OutStreamType.Error;
|
||||
pub const OutStream = io.OutStream(*Self, Error, write);
|
||||
|
||||
const Self = @This();
|
||||
const FifoType = std.fifo.LinearFifo(u8, std.fifo.LinearFifoBufferType{ .Static = buffer_size });
|
||||
|
||||
pub fn flush(self: *Self) !void {
|
||||
while (true) {
|
||||
const slice = self.fifo.readableSlice(0);
|
||||
if (slice.len == 0) break;
|
||||
try self.unbuffered_out_stream.writeAll(slice);
|
||||
self.fifo.discard(slice.len);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn outStream(self: *Self) OutStream {
|
||||
return .{ .context = self };
|
||||
}
|
||||
|
||||
pub fn write(self: *Self, bytes: []const u8) Error!usize {
|
||||
if (bytes.len >= self.fifo.writableLength()) {
|
||||
try self.flush();
|
||||
return self.unbuffered_out_stream.write(bytes);
|
||||
}
|
||||
self.fifo.writeAssumeCapacity(bytes);
|
||||
return bytes.len;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn bufferedOutStream(underlying_stream: var) BufferedOutStream(4096, @TypeOf(underlying_stream)) {
|
||||
return .{ .unbuffered_out_stream = underlying_stream };
|
||||
}
|
@ -1,43 +1,44 @@
|
||||
const std = @import("../std.zig");
|
||||
const os = std.os;
|
||||
const OutStream = std.io.OutStream;
|
||||
const builtin = @import("builtin");
|
||||
const builtin = std.builtin;
|
||||
const io = std.io;
|
||||
const testing = std.testing;
|
||||
|
||||
/// TODO make a proposal to make `std.fs.File` use *FILE when linking libc and this just becomes
|
||||
/// std.io.FileOutStream because std.fs.File.write would do this when linking
|
||||
/// libc.
|
||||
pub const COutStream = struct {
|
||||
pub const Error = std.fs.File.WriteError;
|
||||
pub const Stream = OutStream(Error);
|
||||
pub const COutStream = io.OutStream(*std.c.FILE, std.fs.File.WriteError, cOutStreamWrite);
|
||||
|
||||
stream: Stream,
|
||||
c_file: *std.c.FILE,
|
||||
pub fn cOutStream(c_file: *std.c.FILE) COutStream {
|
||||
return .{ .context = c_file };
|
||||
}
|
||||
|
||||
pub fn init(c_file: *std.c.FILE) COutStream {
|
||||
return COutStream{
|
||||
.c_file = c_file,
|
||||
.stream = Stream{ .writeFn = writeFn },
|
||||
};
|
||||
fn cOutStreamWrite(c_file: *std.c.FILE, bytes: []const u8) std.fs.File.WriteError!usize {
|
||||
const amt_written = std.c.fwrite(bytes.ptr, 1, bytes.len, c_file);
|
||||
if (amt_written >= 0) return amt_written;
|
||||
switch (std.c._errno().*) {
|
||||
0 => unreachable,
|
||||
os.EINVAL => unreachable,
|
||||
os.EFAULT => unreachable,
|
||||
os.EAGAIN => unreachable, // this is a blocking API
|
||||
os.EBADF => unreachable, // always a race condition
|
||||
os.EDESTADDRREQ => unreachable, // connect was never called
|
||||
os.EDQUOT => return error.DiskQuota,
|
||||
os.EFBIG => return error.FileTooBig,
|
||||
os.EIO => return error.InputOutput,
|
||||
os.ENOSPC => return error.NoSpaceLeft,
|
||||
os.EPERM => return error.AccessDenied,
|
||||
os.EPIPE => return error.BrokenPipe,
|
||||
else => |err| return os.unexpectedErrno(@intCast(usize, err)),
|
||||
}
|
||||
}
|
||||
|
||||
test "" {
|
||||
if (!builtin.link_libc) return error.SkipZigTest;
|
||||
|
||||
const filename = "tmp_io_test_file.txt";
|
||||
const out_file = std.c.fopen(filename, "w") orelse return error.UnableToOpenTestFile;
|
||||
defer {
|
||||
_ = std.c.fclose(out_file);
|
||||
fs.cwd().deleteFileC(filename) catch {};
|
||||
}
|
||||
|
||||
fn writeFn(out_stream: *Stream, bytes: []const u8) Error!usize {
|
||||
const self = @fieldParentPtr(COutStream, "stream", out_stream);
|
||||
const amt_written = std.c.fwrite(bytes.ptr, 1, bytes.len, self.c_file);
|
||||
if (amt_written >= 0) return amt_written;
|
||||
switch (std.c._errno().*) {
|
||||
0 => unreachable,
|
||||
os.EINVAL => unreachable,
|
||||
os.EFAULT => unreachable,
|
||||
os.EAGAIN => unreachable, // this is a blocking API
|
||||
os.EBADF => unreachable, // always a race condition
|
||||
os.EDESTADDRREQ => unreachable, // connect was never called
|
||||
os.EDQUOT => return error.DiskQuota,
|
||||
os.EFBIG => return error.FileTooBig,
|
||||
os.EIO => return error.InputOutput,
|
||||
os.ENOSPC => return error.NoSpaceLeft,
|
||||
os.EPERM => return error.AccessDenied,
|
||||
os.EPIPE => return error.BrokenPipe,
|
||||
else => |err| return os.unexpectedErrno(@intCast(usize, err)),
|
||||
}
|
||||
}
|
||||
};
|
||||
const out_stream = &io.COutStream.init(out_file).stream;
|
||||
try out_stream.print("hi: {}\n", .{@as(i32, 123)});
|
||||
}
|
||||
|
39
lib/std/io/counting_out_stream.zig
Normal file
39
lib/std/io/counting_out_stream.zig
Normal file
@ -0,0 +1,39 @@
|
||||
const std = @import("../std.zig");
|
||||
const io = std.io;
|
||||
const testing = std.testing;
|
||||
|
||||
/// An OutStream that counts how many bytes has been written to it.
|
||||
pub fn CountingOutStream(comptime OutStreamType: type) type {
|
||||
return struct {
|
||||
bytes_written: u64,
|
||||
child_stream: OutStreamType,
|
||||
|
||||
pub const Error = OutStreamType.Error;
|
||||
pub const OutStream = io.OutStream(*Self, Error, write);
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn write(self: *Self, bytes: []const u8) Error!usize {
|
||||
const amt = try self.child_stream.write(bytes);
|
||||
self.bytes_written += amt;
|
||||
return amt;
|
||||
}
|
||||
|
||||
pub fn outStream(self: *Self) OutStream {
|
||||
return .{ .context = self };
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn countingOutStream(child_stream: var) CountingOutStream(@TypeOf(child_stream)) {
|
||||
return .{ .bytes_written = 0, .child_stream = child_stream };
|
||||
}
|
||||
|
||||
test "io.CountingOutStream" {
|
||||
var counting_stream = countingOutStream(std.io.null_out_stream);
|
||||
const stream = counting_stream.outStream();
|
||||
|
||||
const bytes = "yay" ** 100;
|
||||
stream.writeAll(bytes) catch unreachable;
|
||||
testing.expect(counting_stream.bytes_written == bytes.len);
|
||||
}
|
171
lib/std/io/fixed_buffer_stream.zig
Normal file
171
lib/std/io/fixed_buffer_stream.zig
Normal file
@ -0,0 +1,171 @@
|
||||
const std = @import("../std.zig");
|
||||
const io = std.io;
|
||||
const testing = std.testing;
|
||||
const mem = std.mem;
|
||||
const assert = std.debug.assert;
|
||||
|
||||
/// This turns a byte buffer into an `io.OutStream`, `io.InStream`, or `io.SeekableStream`.
|
||||
/// If the supplied byte buffer is const, then `io.OutStream` is not available.
|
||||
pub fn FixedBufferStream(comptime Buffer: type) type {
|
||||
return struct {
|
||||
/// `Buffer` is either a `[]u8` or `[]const u8`.
|
||||
buffer: Buffer,
|
||||
pos: usize,
|
||||
|
||||
pub const ReadError = error{};
|
||||
pub const WriteError = error{NoSpaceLeft};
|
||||
pub const SeekError = error{};
|
||||
pub const GetSeekPosError = error{};
|
||||
|
||||
pub const InStream = io.InStream(*Self, ReadError, read);
|
||||
pub const OutStream = io.OutStream(*Self, WriteError, write);
|
||||
|
||||
pub const SeekableStream = io.SeekableStream(
|
||||
*Self,
|
||||
SeekError,
|
||||
GetSeekPosError,
|
||||
seekTo,
|
||||
seekBy,
|
||||
getPos,
|
||||
getEndPos,
|
||||
);
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn inStream(self: *Self) InStream {
|
||||
return .{ .context = self };
|
||||
}
|
||||
|
||||
pub fn outStream(self: *Self) OutStream {
|
||||
return .{ .context = self };
|
||||
}
|
||||
|
||||
pub fn seekableStream(self: *Self) SeekableStream {
|
||||
return .{ .context = self };
|
||||
}
|
||||
|
||||
pub fn read(self: *Self, dest: []u8) ReadError!usize {
|
||||
const size = std.math.min(dest.len, self.buffer.len - self.pos);
|
||||
const end = self.pos + size;
|
||||
|
||||
mem.copy(u8, dest[0..size], self.buffer[self.pos..end]);
|
||||
self.pos = end;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/// If the returned number of bytes written is less than requested, the
|
||||
/// buffer is full. Returns `error.NoSpaceLeft` when no bytes would be written.
|
||||
/// Note: `error.NoSpaceLeft` matches the corresponding error from
|
||||
/// `std.fs.File.WriteError`.
|
||||
pub fn write(self: *Self, bytes: []const u8) WriteError!usize {
|
||||
if (bytes.len == 0) return 0;
|
||||
if (self.pos >= self.buffer.len) return error.NoSpaceLeft;
|
||||
|
||||
const n = if (self.pos + bytes.len <= self.buffer.len)
|
||||
bytes.len
|
||||
else
|
||||
self.buffer.len - self.pos;
|
||||
|
||||
mem.copy(u8, self.buffer[self.pos .. self.pos + n], bytes[0..n]);
|
||||
self.pos += n;
|
||||
|
||||
if (n == 0) return error.NoSpaceLeft;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
pub fn seekTo(self: *Self, pos: u64) SeekError!void {
|
||||
self.pos = if (std.math.cast(usize, pos)) |x| x else |_| self.buffer.len;
|
||||
}
|
||||
|
||||
pub fn seekBy(self: *Self, amt: i64) SeekError!void {
|
||||
if (amt < 0) {
|
||||
const abs_amt = std.math.absCast(amt);
|
||||
const abs_amt_usize = std.math.cast(usize, abs_amt) catch std.math.maxInt(usize);
|
||||
if (abs_amt_usize > self.pos) {
|
||||
self.pos = 0;
|
||||
} else {
|
||||
self.pos -= abs_amt_usize;
|
||||
}
|
||||
} else {
|
||||
const amt_usize = std.math.cast(usize, amt) catch std.math.maxInt(usize);
|
||||
const new_pos = std.math.add(usize, self.pos, amt_usize) catch std.math.maxInt(usize);
|
||||
self.pos = std.math.min(self.buffer.len, new_pos);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getEndPos(self: *Self) GetSeekPosError!u64 {
|
||||
return self.buffer.len;
|
||||
}
|
||||
|
||||
pub fn getPos(self: *Self) GetSeekPosError!u64 {
|
||||
return self.pos;
|
||||
}
|
||||
|
||||
pub fn getWritten(self: Self) []const u8 {
|
||||
return self.buffer[0..self.pos];
|
||||
}
|
||||
|
||||
pub fn reset(self: *Self) void {
|
||||
self.pos = 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn fixedBufferStream(buffer: var) FixedBufferStream(NonSentinelSpan(@TypeOf(buffer))) {
|
||||
return .{ .buffer = mem.span(buffer), .pos = 0 };
|
||||
}
|
||||
|
||||
fn NonSentinelSpan(comptime T: type) type {
|
||||
var ptr_info = @typeInfo(mem.Span(T)).Pointer;
|
||||
ptr_info.sentinel = null;
|
||||
return @Type(std.builtin.TypeInfo{ .Pointer = ptr_info });
|
||||
}
|
||||
|
||||
test "FixedBufferStream output" {
|
||||
var buf: [255]u8 = undefined;
|
||||
var fbs = fixedBufferStream(&buf);
|
||||
const stream = fbs.outStream();
|
||||
|
||||
try stream.print("{}{}!", .{ "Hello", "World" });
|
||||
testing.expectEqualSlices(u8, "HelloWorld!", fbs.getWritten());
|
||||
}
|
||||
|
||||
test "FixedBufferStream output 2" {
|
||||
var buffer: [10]u8 = undefined;
|
||||
var fbs = fixedBufferStream(&buffer);
|
||||
|
||||
try fbs.outStream().writeAll("Hello");
|
||||
testing.expect(mem.eql(u8, fbs.getWritten(), "Hello"));
|
||||
|
||||
try fbs.outStream().writeAll("world");
|
||||
testing.expect(mem.eql(u8, fbs.getWritten(), "Helloworld"));
|
||||
|
||||
testing.expectError(error.NoSpaceLeft, fbs.outStream().writeAll("!"));
|
||||
testing.expect(mem.eql(u8, fbs.getWritten(), "Helloworld"));
|
||||
|
||||
fbs.reset();
|
||||
testing.expect(fbs.getWritten().len == 0);
|
||||
|
||||
testing.expectError(error.NoSpaceLeft, fbs.outStream().writeAll("Hello world!"));
|
||||
testing.expect(mem.eql(u8, fbs.getWritten(), "Hello worl"));
|
||||
}
|
||||
|
||||
test "FixedBufferStream input" {
|
||||
const bytes = [_]u8{ 1, 2, 3, 4, 5, 6, 7 };
|
||||
var fbs = fixedBufferStream(&bytes);
|
||||
|
||||
var dest: [4]u8 = undefined;
|
||||
|
||||
var read = try fbs.inStream().read(dest[0..4]);
|
||||
testing.expect(read == 4);
|
||||
testing.expect(mem.eql(u8, dest[0..4], bytes[0..4]));
|
||||
|
||||
read = try fbs.inStream().read(dest[0..4]);
|
||||
testing.expect(read == 3);
|
||||
testing.expect(mem.eql(u8, dest[0..3], bytes[4..7]));
|
||||
|
||||
read = try fbs.inStream().read(dest[0..4]);
|
||||
testing.expect(read == 0);
|
||||
}
|
@ -1,53 +1,37 @@
|
||||
const std = @import("../std.zig");
|
||||
const builtin = @import("builtin");
|
||||
const root = @import("root");
|
||||
const builtin = std.builtin;
|
||||
const math = std.math;
|
||||
const assert = std.debug.assert;
|
||||
const mem = std.mem;
|
||||
const Buffer = std.Buffer;
|
||||
const testing = std.testing;
|
||||
|
||||
pub const default_stack_size = 1 * 1024 * 1024;
|
||||
pub const stack_size: usize = if (@hasDecl(root, "stack_size_std_io_InStream"))
|
||||
root.stack_size_std_io_InStream
|
||||
else
|
||||
default_stack_size;
|
||||
|
||||
pub fn InStream(comptime ReadError: type) type {
|
||||
pub fn InStream(
|
||||
comptime Context: type,
|
||||
comptime ReadError: type,
|
||||
/// Returns the number of bytes read. It may be less than buffer.len.
|
||||
/// If the number of bytes read is 0, it means end of stream.
|
||||
/// End of stream is not an error condition.
|
||||
comptime readFn: fn (context: Context, buffer: []u8) ReadError!usize,
|
||||
) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
pub const Error = ReadError;
|
||||
pub const ReadFn = if (std.io.is_async)
|
||||
async fn (self: *Self, buffer: []u8) Error!usize
|
||||
else
|
||||
fn (self: *Self, buffer: []u8) Error!usize;
|
||||
|
||||
context: Context,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
/// Returns the number of bytes read. It may be less than buffer.len.
|
||||
/// If the number of bytes read is 0, it means end of stream.
|
||||
/// End of stream is not an error condition.
|
||||
readFn: ReadFn,
|
||||
|
||||
/// Returns the number of bytes read. It may be less than buffer.len.
|
||||
/// If the number of bytes read is 0, it means end of stream.
|
||||
/// End of stream is not an error condition.
|
||||
pub fn read(self: *Self, buffer: []u8) Error!usize {
|
||||
if (std.io.is_async) {
|
||||
// Let's not be writing 0xaa in safe modes for upwards of 4 MiB for every stream read.
|
||||
@setRuntimeSafety(false);
|
||||
var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined;
|
||||
return await @asyncCall(&stack_frame, {}, self.readFn, self, buffer);
|
||||
} else {
|
||||
return self.readFn(self, buffer);
|
||||
}
|
||||
pub fn read(self: Self, buffer: []u8) Error!usize {
|
||||
return readFn(self.context, buffer);
|
||||
}
|
||||
|
||||
/// Deprecated: use `readAll`.
|
||||
pub const readFull = readAll;
|
||||
|
||||
/// Returns the number of bytes read. If the number read is smaller than buf.len, it
|
||||
/// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it
|
||||
/// means the stream reached the end. Reaching the end of a stream is not an error
|
||||
/// condition.
|
||||
pub fn readAll(self: *Self, buffer: []u8) Error!usize {
|
||||
pub fn readAll(self: Self, buffer: []u8) Error!usize {
|
||||
var index: usize = 0;
|
||||
while (index != buffer.len) {
|
||||
const amt = try self.read(buffer[index..]);
|
||||
@ -59,13 +43,13 @@ pub fn InStream(comptime ReadError: type) type {
|
||||
|
||||
/// Returns the number of bytes read. If the number read would be smaller than buf.len,
|
||||
/// error.EndOfStream is returned instead.
|
||||
pub fn readNoEof(self: *Self, buf: []u8) !void {
|
||||
pub fn readNoEof(self: Self, buf: []u8) !void {
|
||||
const amt_read = try self.readAll(buf);
|
||||
if (amt_read < buf.len) return error.EndOfStream;
|
||||
}
|
||||
|
||||
/// Deprecated: use `readAllArrayList`.
|
||||
pub fn readAllBuffer(self: *Self, buffer: *Buffer, max_size: usize) !void {
|
||||
pub fn readAllBuffer(self: Self, buffer: *Buffer, max_size: usize) !void {
|
||||
buffer.list.shrink(0);
|
||||
try self.readAllArrayList(&buffer.list, max_size);
|
||||
errdefer buffer.shrink(0);
|
||||
@ -75,7 +59,7 @@ pub fn InStream(comptime ReadError: type) type {
|
||||
/// Appends to the `std.ArrayList` contents by reading from the stream until end of stream is found.
|
||||
/// If the number of bytes appended would exceed `max_append_size`, `error.StreamTooLong` is returned
|
||||
/// and the `std.ArrayList` has exactly `max_append_size` bytes appended.
|
||||
pub fn readAllArrayList(self: *Self, array_list: *std.ArrayList(u8), max_append_size: usize) !void {
|
||||
pub fn readAllArrayList(self: Self, array_list: *std.ArrayList(u8), max_append_size: usize) !void {
|
||||
try array_list.ensureCapacity(math.min(max_append_size, 4096));
|
||||
const original_len = array_list.len;
|
||||
var start_index: usize = original_len;
|
||||
@ -104,7 +88,7 @@ pub fn InStream(comptime ReadError: type) type {
|
||||
/// memory would be greater than `max_size`, returns `error.StreamTooLong`.
|
||||
/// Caller owns returned memory.
|
||||
/// If this function returns an error, the contents from the stream read so far are lost.
|
||||
pub fn readAllAlloc(self: *Self, allocator: *mem.Allocator, max_size: usize) ![]u8 {
|
||||
pub fn readAllAlloc(self: Self, allocator: *mem.Allocator, max_size: usize) ![]u8 {
|
||||
var array_list = std.ArrayList(u8).init(allocator);
|
||||
defer array_list.deinit();
|
||||
try self.readAllArrayList(&array_list, max_size);
|
||||
@ -116,7 +100,7 @@ pub fn InStream(comptime ReadError: type) type {
|
||||
/// If the `std.ArrayList` length would exceed `max_size`, `error.StreamTooLong` is returned and the
|
||||
/// `std.ArrayList` is populated with `max_size` bytes from the stream.
|
||||
pub fn readUntilDelimiterArrayList(
|
||||
self: *Self,
|
||||
self: Self,
|
||||
array_list: *std.ArrayList(u8),
|
||||
delimiter: u8,
|
||||
max_size: usize,
|
||||
@ -142,7 +126,7 @@ pub fn InStream(comptime ReadError: type) type {
|
||||
/// Caller owns returned memory.
|
||||
/// If this function returns an error, the contents from the stream read so far are lost.
|
||||
pub fn readUntilDelimiterAlloc(
|
||||
self: *Self,
|
||||
self: Self,
|
||||
allocator: *mem.Allocator,
|
||||
delimiter: u8,
|
||||
max_size: usize,
|
||||
@ -159,7 +143,7 @@ pub fn InStream(comptime ReadError: type) type {
|
||||
/// function is called again after that, returns null.
|
||||
/// Returns a slice of the stream data, with ptr equal to `buf.ptr`. The
|
||||
/// delimiter byte is not included in the returned slice.
|
||||
pub fn readUntilDelimiterOrEof(self: *Self, buf: []u8, delimiter: u8) !?[]u8 {
|
||||
pub fn readUntilDelimiterOrEof(self: Self, buf: []u8, delimiter: u8) !?[]u8 {
|
||||
var index: usize = 0;
|
||||
while (true) {
|
||||
const byte = self.readByte() catch |err| switch (err) {
|
||||
@ -184,7 +168,7 @@ pub fn InStream(comptime ReadError: type) type {
|
||||
/// Reads from the stream until specified byte is found, discarding all data,
|
||||
/// including the delimiter.
|
||||
/// If end-of-stream is found, this function succeeds.
|
||||
pub fn skipUntilDelimiterOrEof(self: *Self, delimiter: u8) !void {
|
||||
pub fn skipUntilDelimiterOrEof(self: Self, delimiter: u8) !void {
|
||||
while (true) {
|
||||
const byte = self.readByte() catch |err| switch (err) {
|
||||
error.EndOfStream => return,
|
||||
@ -195,7 +179,7 @@ pub fn InStream(comptime ReadError: type) type {
|
||||
}
|
||||
|
||||
/// Reads 1 byte from the stream or returns `error.EndOfStream`.
|
||||
pub fn readByte(self: *Self) !u8 {
|
||||
pub fn readByte(self: Self) !u8 {
|
||||
var result: [1]u8 = undefined;
|
||||
const amt_read = try self.read(result[0..]);
|
||||
if (amt_read < 1) return error.EndOfStream;
|
||||
@ -203,43 +187,43 @@ pub fn InStream(comptime ReadError: type) type {
|
||||
}
|
||||
|
||||
/// Same as `readByte` except the returned byte is signed.
|
||||
pub fn readByteSigned(self: *Self) !i8 {
|
||||
pub fn readByteSigned(self: Self) !i8 {
|
||||
return @bitCast(i8, try self.readByte());
|
||||
}
|
||||
|
||||
/// Reads a native-endian integer
|
||||
pub fn readIntNative(self: *Self, comptime T: type) !T {
|
||||
pub fn readIntNative(self: Self, comptime T: type) !T {
|
||||
var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
|
||||
try self.readNoEof(bytes[0..]);
|
||||
return mem.readIntNative(T, &bytes);
|
||||
}
|
||||
|
||||
/// Reads a foreign-endian integer
|
||||
pub fn readIntForeign(self: *Self, comptime T: type) !T {
|
||||
pub fn readIntForeign(self: Self, comptime T: type) !T {
|
||||
var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
|
||||
try self.readNoEof(bytes[0..]);
|
||||
return mem.readIntForeign(T, &bytes);
|
||||
}
|
||||
|
||||
pub fn readIntLittle(self: *Self, comptime T: type) !T {
|
||||
pub fn readIntLittle(self: Self, comptime T: type) !T {
|
||||
var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
|
||||
try self.readNoEof(bytes[0..]);
|
||||
return mem.readIntLittle(T, &bytes);
|
||||
}
|
||||
|
||||
pub fn readIntBig(self: *Self, comptime T: type) !T {
|
||||
pub fn readIntBig(self: Self, comptime T: type) !T {
|
||||
var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
|
||||
try self.readNoEof(bytes[0..]);
|
||||
return mem.readIntBig(T, &bytes);
|
||||
}
|
||||
|
||||
pub fn readInt(self: *Self, comptime T: type, endian: builtin.Endian) !T {
|
||||
pub fn readInt(self: Self, comptime T: type, endian: builtin.Endian) !T {
|
||||
var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
|
||||
try self.readNoEof(bytes[0..]);
|
||||
return mem.readInt(T, &bytes, endian);
|
||||
}
|
||||
|
||||
pub fn readVarInt(self: *Self, comptime ReturnType: type, endian: builtin.Endian, size: usize) !ReturnType {
|
||||
pub fn readVarInt(self: Self, comptime ReturnType: type, endian: builtin.Endian, size: usize) !ReturnType {
|
||||
assert(size <= @sizeOf(ReturnType));
|
||||
var bytes_buf: [@sizeOf(ReturnType)]u8 = undefined;
|
||||
const bytes = bytes_buf[0..size];
|
||||
@ -247,14 +231,14 @@ pub fn InStream(comptime ReadError: type) type {
|
||||
return mem.readVarInt(ReturnType, bytes, endian);
|
||||
}
|
||||
|
||||
pub fn skipBytes(self: *Self, num_bytes: u64) !void {
|
||||
pub fn skipBytes(self: Self, num_bytes: u64) !void {
|
||||
var i: u64 = 0;
|
||||
while (i < num_bytes) : (i += 1) {
|
||||
_ = try self.readByte();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn readStruct(self: *Self, comptime T: type) !T {
|
||||
pub fn readStruct(self: Self, comptime T: type) !T {
|
||||
// Only extern and packed structs have defined in-memory layout.
|
||||
comptime assert(@typeInfo(T).Struct.layout != builtin.TypeInfo.ContainerLayout.Auto);
|
||||
var res: [1]T = undefined;
|
||||
@ -265,7 +249,7 @@ pub fn InStream(comptime ReadError: type) type {
|
||||
/// Reads an integer with the same size as the given enum's tag type. If the integer matches
|
||||
/// an enum tag, casts the integer to the enum tag and returns it. Otherwise, returns an error.
|
||||
/// TODO optimization taking advantage of most fields being in order
|
||||
pub fn readEnum(self: *Self, comptime Enum: type, endian: builtin.Endian) !Enum {
|
||||
pub fn readEnum(self: Self, comptime Enum: type, endian: builtin.Endian) !Enum {
|
||||
const E = error{
|
||||
/// An integer was read, but it did not match any of the tags in the supplied enum.
|
||||
InvalidValue,
|
||||
@ -286,8 +270,7 @@ pub fn InStream(comptime ReadError: type) type {
|
||||
|
||||
test "InStream" {
|
||||
var buf = "a\x02".*;
|
||||
var slice_stream = std.io.SliceInStream.init(&buf);
|
||||
const in_stream = &slice_stream.stream;
|
||||
const in_stream = std.io.fixedBufferStream(&buf).inStream();
|
||||
testing.expect((try in_stream.readByte()) == 'a');
|
||||
testing.expect((try in_stream.readEnum(enum(u8) {
|
||||
a = 0,
|
||||
|
@ -1,94 +1,85 @@
|
||||
const std = @import("../std.zig");
|
||||
const builtin = @import("builtin");
|
||||
const root = @import("root");
|
||||
const builtin = std.builtin;
|
||||
const mem = std.mem;
|
||||
|
||||
pub const default_stack_size = 1 * 1024 * 1024;
|
||||
pub const stack_size: usize = if (@hasDecl(root, "stack_size_std_io_OutStream"))
|
||||
root.stack_size_std_io_OutStream
|
||||
else
|
||||
default_stack_size;
|
||||
|
||||
pub fn OutStream(comptime WriteError: type) type {
|
||||
pub fn OutStream(
|
||||
comptime Context: type,
|
||||
comptime WriteError: type,
|
||||
comptime writeFn: fn (context: Context, bytes: []const u8) WriteError!usize,
|
||||
) type {
|
||||
return struct {
|
||||
context: Context,
|
||||
|
||||
const Self = @This();
|
||||
pub const Error = WriteError;
|
||||
pub const WriteFn = if (std.io.is_async)
|
||||
async fn (self: *Self, bytes: []const u8) Error!usize
|
||||
else
|
||||
fn (self: *Self, bytes: []const u8) Error!usize;
|
||||
|
||||
writeFn: WriteFn,
|
||||
|
||||
pub fn writeOnce(self: *Self, bytes: []const u8) Error!usize {
|
||||
if (std.io.is_async) {
|
||||
// Let's not be writing 0xaa in safe modes for upwards of 4 MiB for every stream write.
|
||||
@setRuntimeSafety(false);
|
||||
var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined;
|
||||
return await @asyncCall(&stack_frame, {}, self.writeFn, self, bytes);
|
||||
} else {
|
||||
return self.writeFn(self, bytes);
|
||||
}
|
||||
pub fn write(self: Self, bytes: []const u8) Error!usize {
|
||||
return writeFn(self.context, bytes);
|
||||
}
|
||||
|
||||
pub fn write(self: *Self, bytes: []const u8) Error!void {
|
||||
pub fn writeAll(self: Self, bytes: []const u8) Error!void {
|
||||
var index: usize = 0;
|
||||
while (index != bytes.len) {
|
||||
index += try self.writeOnce(bytes[index..]);
|
||||
index += try self.write(bytes[index..]);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print(self: *Self, comptime format: []const u8, args: var) Error!void {
|
||||
return std.fmt.format(self, Error, write, format, args);
|
||||
pub fn print(self: Self, comptime format: []const u8, args: var) Error!void {
|
||||
return std.fmt.format(self, Error, writeAll, format, args);
|
||||
}
|
||||
|
||||
pub fn writeByte(self: *Self, byte: u8) Error!void {
|
||||
pub fn writeByte(self: Self, byte: u8) Error!void {
|
||||
const array = [1]u8{byte};
|
||||
return self.write(&array);
|
||||
return self.writeAll(&array);
|
||||
}
|
||||
|
||||
pub fn writeByteNTimes(self: *Self, byte: u8, n: usize) Error!void {
|
||||
pub fn writeByteNTimes(self: Self, byte: u8, n: usize) Error!void {
|
||||
var bytes: [256]u8 = undefined;
|
||||
mem.set(u8, bytes[0..], byte);
|
||||
|
||||
var remaining: usize = n;
|
||||
while (remaining > 0) {
|
||||
const to_write = std.math.min(remaining, bytes.len);
|
||||
try self.write(bytes[0..to_write]);
|
||||
try self.writeAll(bytes[0..to_write]);
|
||||
remaining -= to_write;
|
||||
}
|
||||
}
|
||||
|
||||
/// Write a native-endian integer.
|
||||
pub fn writeIntNative(self: *Self, comptime T: type, value: T) Error!void {
|
||||
/// TODO audit non-power-of-two int sizes
|
||||
pub fn writeIntNative(self: Self, comptime T: type, value: T) Error!void {
|
||||
var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
|
||||
mem.writeIntNative(T, &bytes, value);
|
||||
return self.write(&bytes);
|
||||
return self.writeAll(&bytes);
|
||||
}
|
||||
|
||||
/// Write a foreign-endian integer.
|
||||
pub fn writeIntForeign(self: *Self, comptime T: type, value: T) Error!void {
|
||||
/// TODO audit non-power-of-two int sizes
|
||||
pub fn writeIntForeign(self: Self, comptime T: type, value: T) Error!void {
|
||||
var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
|
||||
mem.writeIntForeign(T, &bytes, value);
|
||||
return self.write(&bytes);
|
||||
return self.writeAll(&bytes);
|
||||
}
|
||||
|
||||
pub fn writeIntLittle(self: *Self, comptime T: type, value: T) Error!void {
|
||||
/// TODO audit non-power-of-two int sizes
|
||||
pub fn writeIntLittle(self: Self, comptime T: type, value: T) Error!void {
|
||||
var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
|
||||
mem.writeIntLittle(T, &bytes, value);
|
||||
return self.write(&bytes);
|
||||
return self.writeAll(&bytes);
|
||||
}
|
||||
|
||||
pub fn writeIntBig(self: *Self, comptime T: type, value: T) Error!void {
|
||||
/// TODO audit non-power-of-two int sizes
|
||||
pub fn writeIntBig(self: Self, comptime T: type, value: T) Error!void {
|
||||
var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
|
||||
mem.writeIntBig(T, &bytes, value);
|
||||
return self.write(&bytes);
|
||||
return self.writeAll(&bytes);
|
||||
}
|
||||
|
||||
pub fn writeInt(self: *Self, comptime T: type, value: T, endian: builtin.Endian) Error!void {
|
||||
/// TODO audit non-power-of-two int sizes
|
||||
pub fn writeInt(self: Self, comptime T: type, value: T, endian: builtin.Endian) Error!void {
|
||||
var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
|
||||
mem.writeInt(T, &bytes, value, endian);
|
||||
return self.write(&bytes);
|
||||
return self.writeAll(&bytes);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
112
lib/std/io/peek_stream.zig
Normal file
112
lib/std/io/peek_stream.zig
Normal file
@ -0,0 +1,112 @@
|
||||
const std = @import("../std.zig");
|
||||
const io = std.io;
|
||||
const mem = std.mem;
|
||||
const testing = std.testing;
|
||||
|
||||
/// Creates a stream which supports 'un-reading' data, so that it can be read again.
|
||||
/// This makes look-ahead style parsing much easier.
|
||||
/// TODO merge this with `std.io.BufferedInStream`: https://github.com/ziglang/zig/issues/4501
|
||||
pub fn PeekStream(
|
||||
comptime buffer_type: std.fifo.LinearFifoBufferType,
|
||||
comptime InStreamType: type,
|
||||
) type {
|
||||
return struct {
|
||||
unbuffered_in_stream: InStreamType,
|
||||
fifo: FifoType,
|
||||
|
||||
pub const Error = InStreamType.Error;
|
||||
pub const InStream = io.InStream(*Self, Error, read);
|
||||
|
||||
const Self = @This();
|
||||
const FifoType = std.fifo.LinearFifo(u8, buffer_type);
|
||||
|
||||
pub usingnamespace switch (buffer_type) {
|
||||
.Static => struct {
|
||||
pub fn init(base: InStreamType) Self {
|
||||
return .{
|
||||
.base = base,
|
||||
.fifo = FifoType.init(),
|
||||
};
|
||||
}
|
||||
},
|
||||
.Slice => struct {
|
||||
pub fn init(base: InStreamType, buf: []u8) Self {
|
||||
return .{
|
||||
.base = base,
|
||||
.fifo = FifoType.init(buf),
|
||||
};
|
||||
}
|
||||
},
|
||||
.Dynamic => struct {
|
||||
pub fn init(base: InStreamType, allocator: *mem.Allocator) Self {
|
||||
return .{
|
||||
.base = base,
|
||||
.fifo = FifoType.init(allocator),
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
pub fn putBackByte(self: *Self, byte: u8) !void {
|
||||
try self.putBack(&[_]u8{byte});
|
||||
}
|
||||
|
||||
pub fn putBack(self: *Self, bytes: []const u8) !void {
|
||||
try self.fifo.unget(bytes);
|
||||
}
|
||||
|
||||
pub fn read(self: *Self, dest: []u8) Error!usize {
|
||||
// copy over anything putBack()'d
|
||||
var dest_index = self.fifo.read(dest);
|
||||
if (dest_index == dest.len) return dest_index;
|
||||
|
||||
// ask the backing stream for more
|
||||
dest_index += try self.base.read(dest[dest_index..]);
|
||||
return dest_index;
|
||||
}
|
||||
|
||||
pub fn inStream(self: *Self) InStream {
|
||||
return .{ .context = self };
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn peekStream(
|
||||
comptime lookahead: comptime_int,
|
||||
underlying_stream: var,
|
||||
) PeekStream(.{ .Static = lookahead }, @TypeOf(underlying_stream)) {
|
||||
return PeekStream(.{ .Static = lookahead }, @TypeOf(underlying_stream)).init(underlying_stream);
|
||||
}
|
||||
|
||||
test "PeekStream" {
|
||||
const bytes = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
|
||||
var fbs = io.fixedBufferStream(&bytes);
|
||||
var ps = peekStream(2, fbs.inStream());
|
||||
|
||||
var dest: [4]u8 = undefined;
|
||||
|
||||
try ps.putBackByte(9);
|
||||
try ps.putBackByte(10);
|
||||
|
||||
var read = try ps.inStream().read(dest[0..4]);
|
||||
testing.expect(read == 4);
|
||||
testing.expect(dest[0] == 10);
|
||||
testing.expect(dest[1] == 9);
|
||||
testing.expect(mem.eql(u8, dest[2..4], bytes[0..2]));
|
||||
|
||||
read = try ps.inStream().read(dest[0..4]);
|
||||
testing.expect(read == 4);
|
||||
testing.expect(mem.eql(u8, dest[0..4], bytes[2..6]));
|
||||
|
||||
read = try ps.inStream().read(dest[0..4]);
|
||||
testing.expect(read == 2);
|
||||
testing.expect(mem.eql(u8, dest[0..2], bytes[6..8]));
|
||||
|
||||
try ps.putBackByte(11);
|
||||
try ps.putBackByte(12);
|
||||
|
||||
read = try ps.inStream().read(dest[0..4]);
|
||||
testing.expect(read == 2);
|
||||
testing.expect(dest[0] == 12);
|
||||
testing.expect(dest[1] == 11);
|
||||
}
|
@ -1,103 +1,36 @@
|
||||
const std = @import("../std.zig");
|
||||
const InStream = std.io.InStream;
|
||||
|
||||
pub fn SeekableStream(comptime SeekErrorType: type, comptime GetSeekPosErrorType: type) type {
|
||||
pub fn SeekableStream(
|
||||
comptime Context: type,
|
||||
comptime SeekErrorType: type,
|
||||
comptime GetSeekPosErrorType: type,
|
||||
comptime seekToFn: fn (context: Context, pos: u64) SeekErrorType!void,
|
||||
comptime seekByFn: fn (context: Context, pos: i64) SeekErrorType!void,
|
||||
comptime getPosFn: fn (context: Context) GetSeekPosErrorType!u64,
|
||||
comptime getEndPosFn: fn (context: Context) GetSeekPosErrorType!u64,
|
||||
) type {
|
||||
return struct {
|
||||
context: Context,
|
||||
|
||||
const Self = @This();
|
||||
pub const SeekError = SeekErrorType;
|
||||
pub const GetSeekPosError = GetSeekPosErrorType;
|
||||
|
||||
seekToFn: fn (self: *Self, pos: u64) SeekError!void,
|
||||
seekByFn: fn (self: *Self, pos: i64) SeekError!void,
|
||||
|
||||
getPosFn: fn (self: *Self) GetSeekPosError!u64,
|
||||
getEndPosFn: fn (self: *Self) GetSeekPosError!u64,
|
||||
|
||||
pub fn seekTo(self: *Self, pos: u64) SeekError!void {
|
||||
return self.seekToFn(self, pos);
|
||||
pub fn seekTo(self: Self, pos: u64) SeekError!void {
|
||||
return seekToFn(self.context, pos);
|
||||
}
|
||||
|
||||
pub fn seekBy(self: *Self, amt: i64) SeekError!void {
|
||||
return self.seekByFn(self, amt);
|
||||
pub fn seekBy(self: Self, amt: i64) SeekError!void {
|
||||
return seekByFn(self.context, amt);
|
||||
}
|
||||
|
||||
pub fn getEndPos(self: *Self) GetSeekPosError!u64 {
|
||||
return self.getEndPosFn(self);
|
||||
pub fn getEndPos(self: Self) GetSeekPosError!u64 {
|
||||
return getEndPosFn(self.context);
|
||||
}
|
||||
|
||||
pub fn getPos(self: *Self) GetSeekPosError!u64 {
|
||||
return self.getPosFn(self);
|
||||
pub fn getPos(self: Self) GetSeekPosError!u64 {
|
||||
return getPosFn(self.context);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub const SliceSeekableInStream = struct {
|
||||
const Self = @This();
|
||||
pub const Error = error{};
|
||||
pub const SeekError = error{EndOfStream};
|
||||
pub const GetSeekPosError = error{};
|
||||
pub const Stream = InStream(Error);
|
||||
pub const SeekableInStream = SeekableStream(SeekError, GetSeekPosError);
|
||||
|
||||
stream: Stream,
|
||||
seekable_stream: SeekableInStream,
|
||||
|
||||
pos: usize,
|
||||
slice: []const u8,
|
||||
|
||||
pub fn init(slice: []const u8) Self {
|
||||
return Self{
|
||||
.slice = slice,
|
||||
.pos = 0,
|
||||
.stream = Stream{ .readFn = readFn },
|
||||
.seekable_stream = SeekableInStream{
|
||||
.seekToFn = seekToFn,
|
||||
.seekByFn = seekByFn,
|
||||
.getEndPosFn = getEndPosFn,
|
||||
.getPosFn = getPosFn,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
fn readFn(in_stream: *Stream, dest: []u8) Error!usize {
|
||||
const self = @fieldParentPtr(Self, "stream", in_stream);
|
||||
const size = std.math.min(dest.len, self.slice.len - self.pos);
|
||||
const end = self.pos + size;
|
||||
|
||||
std.mem.copy(u8, dest[0..size], self.slice[self.pos..end]);
|
||||
self.pos = end;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
fn seekToFn(in_stream: *SeekableInStream, pos: u64) SeekError!void {
|
||||
const self = @fieldParentPtr(Self, "seekable_stream", in_stream);
|
||||
const usize_pos = @intCast(usize, pos);
|
||||
if (usize_pos > self.slice.len) return error.EndOfStream;
|
||||
self.pos = usize_pos;
|
||||
}
|
||||
|
||||
fn seekByFn(in_stream: *SeekableInStream, amt: i64) SeekError!void {
|
||||
const self = @fieldParentPtr(Self, "seekable_stream", in_stream);
|
||||
|
||||
if (amt < 0) {
|
||||
const abs_amt = @intCast(usize, -amt);
|
||||
if (abs_amt > self.pos) return error.EndOfStream;
|
||||
self.pos -= abs_amt;
|
||||
} else {
|
||||
const usize_amt = @intCast(usize, amt);
|
||||
if (self.pos + usize_amt > self.slice.len) return error.EndOfStream;
|
||||
self.pos += usize_amt;
|
||||
}
|
||||
}
|
||||
|
||||
fn getEndPosFn(in_stream: *SeekableInStream) GetSeekPosError!u64 {
|
||||
const self = @fieldParentPtr(Self, "seekable_stream", in_stream);
|
||||
return @intCast(u64, self.slice.len);
|
||||
}
|
||||
|
||||
fn getPosFn(in_stream: *SeekableInStream) GetSeekPosError!u64 {
|
||||
const self = @fieldParentPtr(Self, "seekable_stream", in_stream);
|
||||
return @intCast(u64, self.pos);
|
||||
}
|
||||
};
|
||||
|
606
lib/std/io/serialization.zig
Normal file
606
lib/std/io/serialization.zig
Normal file
@ -0,0 +1,606 @@
|
||||
const std = @import("../std.zig");
|
||||
const builtin = std.builtin;
|
||||
const io = std.io;
|
||||
|
||||
pub const Packing = enum {
|
||||
/// Pack data to byte alignment
|
||||
Byte,
|
||||
|
||||
/// Pack data to bit alignment
|
||||
Bit,
|
||||
};
|
||||
|
||||
/// Creates a deserializer that deserializes types from any stream.
|
||||
/// If `is_packed` is true, the data stream is treated as bit-packed,
|
||||
/// otherwise data is expected to be packed to the smallest byte.
|
||||
/// Types may implement a custom deserialization routine with a
|
||||
/// function named `deserialize` in the form of:
|
||||
/// pub fn deserialize(self: *Self, deserializer: var) !void
|
||||
/// which will be called when the deserializer is used to deserialize
|
||||
/// that type. It will pass a pointer to the type instance to deserialize
|
||||
/// into and a pointer to the deserializer struct.
|
||||
pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime InStreamType: type) type {
|
||||
return struct {
|
||||
in_stream: if (packing == .Bit) io.BitInStream(endian, InStreamType) else InStreamType,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn init(in_stream: InStreamType) Self {
|
||||
return Self{
|
||||
.in_stream = switch (packing) {
|
||||
.Bit => io.bitInStream(endian, in_stream),
|
||||
.Byte => in_stream,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn alignToByte(self: *Self) void {
|
||||
if (packing == .Byte) return;
|
||||
self.in_stream.alignToByte();
|
||||
}
|
||||
|
||||
//@BUG: inferred error issue. See: #1386
|
||||
fn deserializeInt(self: *Self, comptime T: type) (InStreamType.Error || error{EndOfStream})!T {
|
||||
comptime assert(trait.is(.Int)(T) or trait.is(.Float)(T));
|
||||
|
||||
const u8_bit_count = 8;
|
||||
const t_bit_count = comptime meta.bitCount(T);
|
||||
|
||||
const U = std.meta.IntType(false, t_bit_count);
|
||||
const Log2U = math.Log2Int(U);
|
||||
const int_size = (U.bit_count + 7) / 8;
|
||||
|
||||
if (packing == .Bit) {
|
||||
const result = try self.in_stream.readBitsNoEof(U, t_bit_count);
|
||||
return @bitCast(T, result);
|
||||
}
|
||||
|
||||
var buffer: [int_size]u8 = undefined;
|
||||
const read_size = try self.in_stream.read(buffer[0..]);
|
||||
if (read_size < int_size) return error.EndOfStream;
|
||||
|
||||
if (int_size == 1) {
|
||||
if (t_bit_count == 8) return @bitCast(T, buffer[0]);
|
||||
const PossiblySignedByte = std.meta.IntType(T.is_signed, 8);
|
||||
return @truncate(T, @bitCast(PossiblySignedByte, buffer[0]));
|
||||
}
|
||||
|
||||
var result = @as(U, 0);
|
||||
for (buffer) |byte, i| {
|
||||
switch (endian) {
|
||||
.Big => {
|
||||
result = (result << u8_bit_count) | byte;
|
||||
},
|
||||
.Little => {
|
||||
result |= @as(U, byte) << @intCast(Log2U, u8_bit_count * i);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return @bitCast(T, result);
|
||||
}
|
||||
|
||||
/// Deserializes and returns data of the specified type from the stream
|
||||
pub fn deserialize(self: *Self, comptime T: type) !T {
|
||||
var value: T = undefined;
|
||||
try self.deserializeInto(&value);
|
||||
return value;
|
||||
}
|
||||
|
||||
/// Deserializes data into the type pointed to by `ptr`
|
||||
pub fn deserializeInto(self: *Self, ptr: var) !void {
|
||||
const T = @TypeOf(ptr);
|
||||
comptime assert(trait.is(.Pointer)(T));
|
||||
|
||||
if (comptime trait.isSlice(T) or comptime trait.isPtrTo(.Array)(T)) {
|
||||
for (ptr) |*v|
|
||||
try self.deserializeInto(v);
|
||||
return;
|
||||
}
|
||||
|
||||
comptime assert(trait.isSingleItemPtr(T));
|
||||
|
||||
const C = comptime meta.Child(T);
|
||||
const child_type_id = @typeInfo(C);
|
||||
|
||||
//custom deserializer: fn(self: *Self, deserializer: var) !void
|
||||
if (comptime trait.hasFn("deserialize")(C)) return C.deserialize(ptr, self);
|
||||
|
||||
if (comptime trait.isPacked(C) and packing != .Bit) {
|
||||
var packed_deserializer = deserializer(endian, .Bit, self.in_stream);
|
||||
return packed_deserializer.deserializeInto(ptr);
|
||||
}
|
||||
|
||||
switch (child_type_id) {
|
||||
.Void => return,
|
||||
.Bool => ptr.* = (try self.deserializeInt(u1)) > 0,
|
||||
.Float, .Int => ptr.* = try self.deserializeInt(C),
|
||||
.Struct => {
|
||||
const info = @typeInfo(C).Struct;
|
||||
|
||||
inline for (info.fields) |*field_info| {
|
||||
const name = field_info.name;
|
||||
const FieldType = field_info.field_type;
|
||||
|
||||
if (FieldType == void or FieldType == u0) continue;
|
||||
|
||||
//it doesn't make any sense to read pointers
|
||||
if (comptime trait.is(.Pointer)(FieldType)) {
|
||||
@compileError("Will not " ++ "read field " ++ name ++ " of struct " ++
|
||||
@typeName(C) ++ " because it " ++ "is of pointer-type " ++
|
||||
@typeName(FieldType) ++ ".");
|
||||
}
|
||||
|
||||
try self.deserializeInto(&@field(ptr, name));
|
||||
}
|
||||
},
|
||||
.Union => {
|
||||
const info = @typeInfo(C).Union;
|
||||
if (info.tag_type) |TagType| {
|
||||
//we avoid duplicate iteration over the enum tags
|
||||
// by getting the int directly and casting it without
|
||||
// safety. If it is bad, it will be caught anyway.
|
||||
const TagInt = @TagType(TagType);
|
||||
const tag = try self.deserializeInt(TagInt);
|
||||
|
||||
inline for (info.fields) |field_info| {
|
||||
if (field_info.enum_field.?.value == tag) {
|
||||
const name = field_info.name;
|
||||
const FieldType = field_info.field_type;
|
||||
ptr.* = @unionInit(C, name, undefined);
|
||||
try self.deserializeInto(&@field(ptr, name));
|
||||
return;
|
||||
}
|
||||
}
|
||||
//This is reachable if the enum data is bad
|
||||
return error.InvalidEnumTag;
|
||||
}
|
||||
@compileError("Cannot meaningfully deserialize " ++ @typeName(C) ++
|
||||
" because it is an untagged union. Use a custom deserialize().");
|
||||
},
|
||||
.Optional => {
|
||||
const OC = comptime meta.Child(C);
|
||||
const exists = (try self.deserializeInt(u1)) > 0;
|
||||
if (!exists) {
|
||||
ptr.* = null;
|
||||
return;
|
||||
}
|
||||
|
||||
ptr.* = @as(OC, undefined); //make it non-null so the following .? is guaranteed safe
|
||||
const val_ptr = &ptr.*.?;
|
||||
try self.deserializeInto(val_ptr);
|
||||
},
|
||||
.Enum => {
|
||||
var value = try self.deserializeInt(@TagType(C));
|
||||
ptr.* = try meta.intToEnum(C, value);
|
||||
},
|
||||
else => {
|
||||
@compileError("Cannot deserialize " ++ @tagName(child_type_id) ++ " types (unimplemented).");
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deserializer(
|
||||
comptime endian: builtin.Endian,
|
||||
comptime packing: Packing,
|
||||
in_stream: var,
|
||||
) Deserializer(endian, packing, @TypeOf(in_stream)) {
|
||||
return Deserializer(endian, packing, @TypeOf(in_stream)).init(in_stream);
|
||||
}
|
||||
|
||||
/// Creates a serializer that serializes types to any stream.
|
||||
/// If `is_packed` is true, the data will be bit-packed into the stream.
|
||||
/// Note that the you must call `serializer.flush()` when you are done
|
||||
/// writing bit-packed data in order ensure any unwritten bits are committed.
|
||||
/// If `is_packed` is false, data is packed to the smallest byte. In the case
|
||||
/// of packed structs, the struct will written bit-packed and with the specified
|
||||
/// endianess, after which data will resume being written at the next byte boundary.
|
||||
/// Types may implement a custom serialization routine with a
|
||||
/// function named `serialize` in the form of:
|
||||
/// pub fn serialize(self: Self, serializer: var) !void
|
||||
/// which will be called when the serializer is used to serialize that type. It will
|
||||
/// pass a const pointer to the type instance to be serialized and a pointer
|
||||
/// to the serializer struct.
|
||||
pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime OutStreamType: type) type {
|
||||
return struct {
|
||||
out_stream: if (packing == .Bit) BitOutStream(endian, OutStreamType) else OutStreamType,
|
||||
|
||||
const Self = @This();
|
||||
pub const Error = OutStreamType.Error;
|
||||
|
||||
pub fn init(out_stream: OutStreamType) Self {
|
||||
return Self{
|
||||
.out_stream = switch (packing) {
|
||||
.Bit => io.bitOutStream(endian, out_stream),
|
||||
.Byte => out_stream,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/// Flushes any unwritten bits to the stream
|
||||
pub fn flush(self: *Self) Error!void {
|
||||
if (packing == .Bit) return self.out_stream.flushBits();
|
||||
}
|
||||
|
||||
fn serializeInt(self: *Self, value: var) Error!void {
|
||||
const T = @TypeOf(value);
|
||||
comptime assert(trait.is(.Int)(T) or trait.is(.Float)(T));
|
||||
|
||||
const t_bit_count = comptime meta.bitCount(T);
|
||||
const u8_bit_count = comptime meta.bitCount(u8);
|
||||
|
||||
const U = std.meta.IntType(false, t_bit_count);
|
||||
const Log2U = math.Log2Int(U);
|
||||
const int_size = (U.bit_count + 7) / 8;
|
||||
|
||||
const u_value = @bitCast(U, value);
|
||||
|
||||
if (packing == .Bit) return self.out_stream.writeBits(u_value, t_bit_count);
|
||||
|
||||
var buffer: [int_size]u8 = undefined;
|
||||
if (int_size == 1) buffer[0] = u_value;
|
||||
|
||||
for (buffer) |*byte, i| {
|
||||
const idx = switch (endian) {
|
||||
.Big => int_size - i - 1,
|
||||
.Little => i,
|
||||
};
|
||||
const shift = @intCast(Log2U, idx * u8_bit_count);
|
||||
const v = u_value >> shift;
|
||||
byte.* = if (t_bit_count < u8_bit_count) v else @truncate(u8, v);
|
||||
}
|
||||
|
||||
try self.out_stream.write(&buffer);
|
||||
}
|
||||
|
||||
/// Serializes the passed value into the stream
|
||||
pub fn serialize(self: *Self, value: var) Error!void {
|
||||
const T = comptime @TypeOf(value);
|
||||
|
||||
if (comptime trait.isIndexable(T)) {
|
||||
for (value) |v|
|
||||
try self.serialize(v);
|
||||
return;
|
||||
}
|
||||
|
||||
//custom serializer: fn(self: Self, serializer: var) !void
|
||||
if (comptime trait.hasFn("serialize")(T)) return T.serialize(value, self);
|
||||
|
||||
if (comptime trait.isPacked(T) and packing != .Bit) {
|
||||
var packed_serializer = Serializer(endian, .Bit, Error).init(self.out_stream);
|
||||
try packed_serializer.serialize(value);
|
||||
try packed_serializer.flush();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (@typeInfo(T)) {
|
||||
.Void => return,
|
||||
.Bool => try self.serializeInt(@as(u1, @boolToInt(value))),
|
||||
.Float, .Int => try self.serializeInt(value),
|
||||
.Struct => {
|
||||
const info = @typeInfo(T);
|
||||
|
||||
inline for (info.Struct.fields) |*field_info| {
|
||||
const name = field_info.name;
|
||||
const FieldType = field_info.field_type;
|
||||
|
||||
if (FieldType == void or FieldType == u0) continue;
|
||||
|
||||
//It doesn't make sense to write pointers
|
||||
if (comptime trait.is(.Pointer)(FieldType)) {
|
||||
@compileError("Will not " ++ "serialize field " ++ name ++
|
||||
" of struct " ++ @typeName(T) ++ " because it " ++
|
||||
"is of pointer-type " ++ @typeName(FieldType) ++ ".");
|
||||
}
|
||||
try self.serialize(@field(value, name));
|
||||
}
|
||||
},
|
||||
.Union => {
|
||||
const info = @typeInfo(T).Union;
|
||||
if (info.tag_type) |TagType| {
|
||||
const active_tag = meta.activeTag(value);
|
||||
try self.serialize(active_tag);
|
||||
//This inline loop is necessary because active_tag is a runtime
|
||||
// value, but @field requires a comptime value. Our alternative
|
||||
// is to check each field for a match
|
||||
inline for (info.fields) |field_info| {
|
||||
if (field_info.enum_field.?.value == @enumToInt(active_tag)) {
|
||||
const name = field_info.name;
|
||||
const FieldType = field_info.field_type;
|
||||
try self.serialize(@field(value, name));
|
||||
return;
|
||||
}
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
@compileError("Cannot meaningfully serialize " ++ @typeName(T) ++
|
||||
" because it is an untagged union. Use a custom serialize().");
|
||||
},
|
||||
.Optional => {
|
||||
if (value == null) {
|
||||
try self.serializeInt(@as(u1, @boolToInt(false)));
|
||||
return;
|
||||
}
|
||||
try self.serializeInt(@as(u1, @boolToInt(true)));
|
||||
|
||||
const OC = comptime meta.Child(T);
|
||||
const val_ptr = &value.?;
|
||||
try self.serialize(val_ptr.*);
|
||||
},
|
||||
.Enum => {
|
||||
try self.serializeInt(@enumToInt(value));
|
||||
},
|
||||
else => @compileError("Cannot serialize " ++ @tagName(@typeInfo(T)) ++ " types (unimplemented)."),
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn serializer(
|
||||
comptime endian: builtin.Endian,
|
||||
comptime packing: Packing,
|
||||
out_stream: var,
|
||||
) Serializer(endian, packing, @TypeOf(out_stream)) {
|
||||
return Serializer(endian, packing, @TypeOf(out_stream)).init(out_stream);
|
||||
}
|
||||
|
||||
fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void {
|
||||
@setEvalBranchQuota(1500);
|
||||
//@NOTE: if this test is taking too long, reduce the maximum tested bitsize
|
||||
const max_test_bitsize = 128;
|
||||
|
||||
const total_bytes = comptime blk: {
|
||||
var bytes = 0;
|
||||
comptime var i = 0;
|
||||
while (i <= max_test_bitsize) : (i += 1) bytes += (i / 8) + @boolToInt(i % 8 > 0);
|
||||
break :blk bytes * 2;
|
||||
};
|
||||
|
||||
var data_mem: [total_bytes]u8 = undefined;
|
||||
var out = io.fixedBufferStream(&data_mem);
|
||||
var serializer = serializer(endian, packing, out.outStream());
|
||||
|
||||
var in = io.fixedBufferStream(&data_mem);
|
||||
var deserializer = Deserializer(endian, packing, in.inStream());
|
||||
|
||||
comptime var i = 0;
|
||||
inline while (i <= max_test_bitsize) : (i += 1) {
|
||||
const U = std.meta.IntType(false, i);
|
||||
const S = std.meta.IntType(true, i);
|
||||
try serializer.serializeInt(@as(U, i));
|
||||
if (i != 0) try serializer.serializeInt(@as(S, -1)) else try serializer.serialize(@as(S, 0));
|
||||
}
|
||||
try serializer.flush();
|
||||
|
||||
i = 0;
|
||||
inline while (i <= max_test_bitsize) : (i += 1) {
|
||||
const U = std.meta.IntType(false, i);
|
||||
const S = std.meta.IntType(true, i);
|
||||
const x = try deserializer.deserializeInt(U);
|
||||
const y = try deserializer.deserializeInt(S);
|
||||
expect(x == @as(U, i));
|
||||
if (i != 0) expect(y == @as(S, -1)) else expect(y == 0);
|
||||
}
|
||||
|
||||
const u8_bit_count = comptime meta.bitCount(u8);
|
||||
//0 + 1 + 2 + ... n = (n * (n + 1)) / 2
|
||||
//and we have each for unsigned and signed, so * 2
|
||||
const total_bits = (max_test_bitsize * (max_test_bitsize + 1));
|
||||
const extra_packed_byte = @boolToInt(total_bits % u8_bit_count > 0);
|
||||
const total_packed_bytes = (total_bits / u8_bit_count) + extra_packed_byte;
|
||||
|
||||
expect(in.pos == if (packing == .Bit) total_packed_bytes else total_bytes);
|
||||
|
||||
//Verify that empty error set works with serializer.
|
||||
//deserializer is covered by FixedBufferStream
|
||||
var null_serializer = io.serializer(endian, packing, std.io.null_out_stream);
|
||||
try null_serializer.serialize(data_mem[0..]);
|
||||
try null_serializer.flush();
|
||||
}
|
||||
|
||||
test "Serializer/Deserializer Int" {
|
||||
try testIntSerializerDeserializer(.Big, .Byte);
|
||||
try testIntSerializerDeserializer(.Little, .Byte);
|
||||
// TODO these tests are disabled due to tripping an LLVM assertion
|
||||
// https://github.com/ziglang/zig/issues/2019
|
||||
//try testIntSerializerDeserializer(builtin.Endian.Big, true);
|
||||
//try testIntSerializerDeserializer(builtin.Endian.Little, true);
|
||||
}
|
||||
|
||||
fn testIntSerializerDeserializerInfNaN(
|
||||
comptime endian: builtin.Endian,
|
||||
comptime packing: io.Packing,
|
||||
) !void {
|
||||
const mem_size = (16 * 2 + 32 * 2 + 64 * 2 + 128 * 2) / comptime meta.bitCount(u8);
|
||||
var data_mem: [mem_size]u8 = undefined;
|
||||
|
||||
var out = io.fixedBufferStream(&data_mem);
|
||||
var serializer = serializer(endian, packing, out.outStream());
|
||||
|
||||
var in = io.fixedBufferStream(&data_mem);
|
||||
var deserializer = deserializer(endian, packing, in.inStream());
|
||||
|
||||
//@TODO: isInf/isNan not currently implemented for f128.
|
||||
try serializer.serialize(std.math.nan(f16));
|
||||
try serializer.serialize(std.math.inf(f16));
|
||||
try serializer.serialize(std.math.nan(f32));
|
||||
try serializer.serialize(std.math.inf(f32));
|
||||
try serializer.serialize(std.math.nan(f64));
|
||||
try serializer.serialize(std.math.inf(f64));
|
||||
//try serializer.serialize(std.math.nan(f128));
|
||||
//try serializer.serialize(std.math.inf(f128));
|
||||
const nan_check_f16 = try deserializer.deserialize(f16);
|
||||
const inf_check_f16 = try deserializer.deserialize(f16);
|
||||
const nan_check_f32 = try deserializer.deserialize(f32);
|
||||
deserializer.alignToByte();
|
||||
const inf_check_f32 = try deserializer.deserialize(f32);
|
||||
const nan_check_f64 = try deserializer.deserialize(f64);
|
||||
const inf_check_f64 = try deserializer.deserialize(f64);
|
||||
//const nan_check_f128 = try deserializer.deserialize(f128);
|
||||
//const inf_check_f128 = try deserializer.deserialize(f128);
|
||||
expect(std.math.isNan(nan_check_f16));
|
||||
expect(std.math.isInf(inf_check_f16));
|
||||
expect(std.math.isNan(nan_check_f32));
|
||||
expect(std.math.isInf(inf_check_f32));
|
||||
expect(std.math.isNan(nan_check_f64));
|
||||
expect(std.math.isInf(inf_check_f64));
|
||||
//expect(std.math.isNan(nan_check_f128));
|
||||
//expect(std.math.isInf(inf_check_f128));
|
||||
}
|
||||
|
||||
test "Serializer/Deserializer Int: Inf/NaN" {
|
||||
try testIntSerializerDeserializerInfNaN(.Big, .Byte);
|
||||
try testIntSerializerDeserializerInfNaN(.Little, .Byte);
|
||||
try testIntSerializerDeserializerInfNaN(.Big, .Bit);
|
||||
try testIntSerializerDeserializerInfNaN(.Little, .Bit);
|
||||
}
|
||||
|
||||
fn testAlternateSerializer(self: var, serializer: var) !void {
|
||||
try serializer.serialize(self.f_f16);
|
||||
}
|
||||
|
||||
fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void {
|
||||
const ColorType = enum(u4) {
|
||||
RGB8 = 1,
|
||||
RA16 = 2,
|
||||
R32 = 3,
|
||||
};
|
||||
|
||||
const TagAlign = union(enum(u32)) {
|
||||
A: u8,
|
||||
B: u8,
|
||||
C: u8,
|
||||
};
|
||||
|
||||
const Color = union(ColorType) {
|
||||
RGB8: struct {
|
||||
r: u8,
|
||||
g: u8,
|
||||
b: u8,
|
||||
a: u8,
|
||||
},
|
||||
RA16: struct {
|
||||
r: u16,
|
||||
a: u16,
|
||||
},
|
||||
R32: u32,
|
||||
};
|
||||
|
||||
const PackedStruct = packed struct {
|
||||
f_i3: i3,
|
||||
f_u2: u2,
|
||||
};
|
||||
|
||||
//to test custom serialization
|
||||
const Custom = struct {
|
||||
f_f16: f16,
|
||||
f_unused_u32: u32,
|
||||
|
||||
pub fn deserialize(self: *@This(), deserializer: var) !void {
|
||||
try deserializer.deserializeInto(&self.f_f16);
|
||||
self.f_unused_u32 = 47;
|
||||
}
|
||||
|
||||
pub const serialize = testAlternateSerializer;
|
||||
};
|
||||
|
||||
const MyStruct = struct {
|
||||
f_i3: i3,
|
||||
f_u8: u8,
|
||||
f_tag_align: TagAlign,
|
||||
f_u24: u24,
|
||||
f_i19: i19,
|
||||
f_void: void,
|
||||
f_f32: f32,
|
||||
f_f128: f128,
|
||||
f_packed_0: PackedStruct,
|
||||
f_i7arr: [10]i7,
|
||||
f_of64n: ?f64,
|
||||
f_of64v: ?f64,
|
||||
f_color_type: ColorType,
|
||||
f_packed_1: PackedStruct,
|
||||
f_custom: Custom,
|
||||
f_color: Color,
|
||||
};
|
||||
|
||||
const my_inst = MyStruct{
|
||||
.f_i3 = -1,
|
||||
.f_u8 = 8,
|
||||
.f_tag_align = TagAlign{ .B = 148 },
|
||||
.f_u24 = 24,
|
||||
.f_i19 = 19,
|
||||
.f_void = {},
|
||||
.f_f32 = 32.32,
|
||||
.f_f128 = 128.128,
|
||||
.f_packed_0 = PackedStruct{ .f_i3 = -1, .f_u2 = 2 },
|
||||
.f_i7arr = [10]i7{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
|
||||
.f_of64n = null,
|
||||
.f_of64v = 64.64,
|
||||
.f_color_type = ColorType.R32,
|
||||
.f_packed_1 = PackedStruct{ .f_i3 = 1, .f_u2 = 1 },
|
||||
.f_custom = Custom{ .f_f16 = 38.63, .f_unused_u32 = 47 },
|
||||
.f_color = Color{ .R32 = 123822 },
|
||||
};
|
||||
|
||||
var data_mem: [@sizeOf(MyStruct)]u8 = undefined;
|
||||
var out = io.fixedBufferStream(&data_mem);
|
||||
var serializer = serializer(endian, packing, out.outStream());
|
||||
|
||||
var in = io.fixedBufferStream(&data_mem);
|
||||
var deserializer = deserializer(endian, packing, in.inStream());
|
||||
|
||||
try serializer.serialize(my_inst);
|
||||
|
||||
const my_copy = try deserializer.deserialize(MyStruct);
|
||||
expect(meta.eql(my_copy, my_inst));
|
||||
}
|
||||
|
||||
test "Serializer/Deserializer generic" {
|
||||
if (std.Target.current.os.tag == .windows) {
|
||||
// TODO https://github.com/ziglang/zig/issues/508
|
||||
return error.SkipZigTest;
|
||||
}
|
||||
try testSerializerDeserializer(builtin.Endian.Big, .Byte);
|
||||
try testSerializerDeserializer(builtin.Endian.Little, .Byte);
|
||||
try testSerializerDeserializer(builtin.Endian.Big, .Bit);
|
||||
try testSerializerDeserializer(builtin.Endian.Little, .Bit);
|
||||
}
|
||||
|
||||
fn testBadData(comptime endian: builtin.Endian, comptime packing: io.Packing) !void {
|
||||
const E = enum(u14) {
|
||||
One = 1,
|
||||
Two = 2,
|
||||
};
|
||||
|
||||
const A = struct {
|
||||
e: E,
|
||||
};
|
||||
|
||||
const C = union(E) {
|
||||
One: u14,
|
||||
Two: f16,
|
||||
};
|
||||
|
||||
var data_mem: [4]u8 = undefined;
|
||||
var out = io.fixedBufferStream.init(&data_mem);
|
||||
var serializer = serializer(endian, packing, out.outStream());
|
||||
|
||||
var in = io.fixedBufferStream(&data_mem);
|
||||
var deserializer = deserializer(endian, packing, in.inStream());
|
||||
|
||||
try serializer.serialize(@as(u14, 3));
|
||||
expectError(error.InvalidEnumTag, deserializer.deserialize(A));
|
||||
out.pos = 0;
|
||||
try serializer.serialize(@as(u14, 3));
|
||||
try serializer.serialize(@as(u14, 88));
|
||||
expectError(error.InvalidEnumTag, deserializer.deserialize(C));
|
||||
}
|
||||
|
||||
test "Deserializer bad data" {
|
||||
try testBadData(.Big, .Byte);
|
||||
try testBadData(.Little, .Byte);
|
||||
try testBadData(.Big, .Bit);
|
||||
try testBadData(.Little, .Bit);
|
||||
}
|
90
lib/std/io/stream_source.zig
Normal file
90
lib/std/io/stream_source.zig
Normal file
@ -0,0 +1,90 @@
|
||||
const std = @import("../std.zig");
|
||||
const io = std.io;
|
||||
const testing = std.testing;
|
||||
|
||||
/// Provides `io.InStream`, `io.OutStream`, and `io.SeekableStream` for in-memory buffers as
|
||||
/// well as files.
|
||||
/// For memory sources, if the supplied byte buffer is const, then `io.OutStream` is not available.
|
||||
/// The error set of the stream functions is the error set of the corresponding file functions.
|
||||
pub const StreamSource = union(enum) {
|
||||
buffer: io.FixedBufferStream([]u8),
|
||||
const_buffer: io.FixedBufferStream([]const u8),
|
||||
file: std.fs.File,
|
||||
|
||||
pub const ReadError = std.fs.File.ReadError;
|
||||
pub const WriteError = std.fs.File.WriteError;
|
||||
pub const SeekError = std.fs.File.SeekError;
|
||||
pub const GetSeekPosError = std.fs.File.GetPosError;
|
||||
|
||||
pub const InStream = io.InStream(*StreamSource, ReadError, read);
|
||||
pub const OutStream = io.OutStream(*StreamSource, WriteError, write);
|
||||
pub const SeekableStream = io.SeekableStream(
|
||||
*StreamSource,
|
||||
SeekError,
|
||||
GetSeekPosError,
|
||||
seekTo,
|
||||
seekBy,
|
||||
getPos,
|
||||
getEndPos,
|
||||
);
|
||||
|
||||
pub fn read(self: *StreamSource, dest: []u8) ReadError!usize {
|
||||
switch (self.*) {
|
||||
.buffer => |*x| return x.read(dest),
|
||||
.const_buffer => |*x| return x.read(dest),
|
||||
.file => |x| return x.read(dest),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write(self: *StreamSource, bytes: []const u8) WriteError!usize {
|
||||
switch (self.*) {
|
||||
.buffer => |*x| return x.write(bytes),
|
||||
.const_buffer => |*x| return x.write(bytes),
|
||||
.file => |x| return x.write(bytes),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn seekTo(self: *StreamSource, pos: u64) SeekError!void {
|
||||
switch (self.*) {
|
||||
.buffer => |*x| return x.seekTo(pos),
|
||||
.const_buffer => |*x| return x.seekTo(pos),
|
||||
.file => |x| return x.seekTo(pos),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn seekBy(self: *StreamSource, amt: i64) SeekError!void {
|
||||
switch (self.*) {
|
||||
.buffer => |*x| return x.seekBy(amt),
|
||||
.const_buffer => |*x| return x.seekBy(amt),
|
||||
.file => |x| return x.seekBy(amt),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getEndPos(self: *StreamSource) GetSeekPosError!u64 {
|
||||
switch (self.*) {
|
||||
.buffer => |*x| return x.getEndPos(),
|
||||
.const_buffer => |*x| return x.getEndPos(),
|
||||
.file => |x| return x.getEndPos(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getPos(self: *StreamSource) GetSeekPosError!u64 {
|
||||
switch (self.*) {
|
||||
.buffer => |*x| return x.getPos(),
|
||||
.const_buffer => |*x| return x.getPos(),
|
||||
.file => |x| return x.getPos(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inStream(self: *StreamSource) InStream {
|
||||
return .{ .context = self };
|
||||
}
|
||||
|
||||
pub fn outStream(self: *StreamSource) OutStream {
|
||||
return .{ .context = self };
|
||||
}
|
||||
|
||||
pub fn seekableStream(self: *StreamSource) SeekableStream {
|
||||
return .{ .context = self };
|
||||
}
|
||||
};
|
@ -22,11 +22,10 @@ test "write a file, read it, then delete it" {
|
||||
var file = try cwd.createFile(tmp_file_name, .{});
|
||||
defer file.close();
|
||||
|
||||
var file_out_stream = file.outStream();
|
||||
var buf_stream = io.BufferedOutStream(File.WriteError).init(&file_out_stream.stream);
|
||||
const st = &buf_stream.stream;
|
||||
var buf_stream = io.bufferedOutStream(file.outStream());
|
||||
const st = buf_stream.outStream();
|
||||
try st.print("begin", .{});
|
||||
try st.write(data[0..]);
|
||||
try st.writeAll(data[0..]);
|
||||
try st.print("end", .{});
|
||||
try buf_stream.flush();
|
||||
}
|
||||
@ -48,9 +47,8 @@ test "write a file, read it, then delete it" {
|
||||
const expected_file_size: u64 = "begin".len + data.len + "end".len;
|
||||
expectEqual(expected_file_size, file_size);
|
||||
|
||||
var file_in_stream = file.inStream();
|
||||
var buf_stream = io.BufferedInStream(File.ReadError).init(&file_in_stream.stream);
|
||||
const st = &buf_stream.stream;
|
||||
var buf_stream = io.bufferedInStream(file.inStream());
|
||||
const st = buf_stream.inStream();
|
||||
const contents = try st.readAllAlloc(std.testing.allocator, 2 * 1024);
|
||||
defer std.testing.allocator.free(contents);
|
||||
|
||||
@ -61,224 +59,13 @@ test "write a file, read it, then delete it" {
|
||||
try cwd.deleteFile(tmp_file_name);
|
||||
}
|
||||
|
||||
test "BufferOutStream" {
|
||||
var buffer = try std.Buffer.initSize(std.testing.allocator, 0);
|
||||
defer buffer.deinit();
|
||||
var buf_stream = &std.io.BufferOutStream.init(&buffer).stream;
|
||||
|
||||
const x: i32 = 42;
|
||||
const y: i32 = 1234;
|
||||
try buf_stream.print("x: {}\ny: {}\n", .{ x, y });
|
||||
|
||||
expect(mem.eql(u8, buffer.toSlice(), "x: 42\ny: 1234\n"));
|
||||
}
|
||||
|
||||
test "SliceInStream" {
|
||||
const bytes = [_]u8{ 1, 2, 3, 4, 5, 6, 7 };
|
||||
var ss = io.SliceInStream.init(&bytes);
|
||||
|
||||
var dest: [4]u8 = undefined;
|
||||
|
||||
var read = try ss.stream.read(dest[0..4]);
|
||||
expect(read == 4);
|
||||
expect(mem.eql(u8, dest[0..4], bytes[0..4]));
|
||||
|
||||
read = try ss.stream.read(dest[0..4]);
|
||||
expect(read == 3);
|
||||
expect(mem.eql(u8, dest[0..3], bytes[4..7]));
|
||||
|
||||
read = try ss.stream.read(dest[0..4]);
|
||||
expect(read == 0);
|
||||
}
|
||||
|
||||
test "PeekStream" {
|
||||
const bytes = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
|
||||
var ss = io.SliceInStream.init(&bytes);
|
||||
var ps = io.PeekStream(.{ .Static = 2 }, io.SliceInStream.Error).init(&ss.stream);
|
||||
|
||||
var dest: [4]u8 = undefined;
|
||||
|
||||
try ps.putBackByte(9);
|
||||
try ps.putBackByte(10);
|
||||
|
||||
var read = try ps.stream.read(dest[0..4]);
|
||||
expect(read == 4);
|
||||
expect(dest[0] == 10);
|
||||
expect(dest[1] == 9);
|
||||
expect(mem.eql(u8, dest[2..4], bytes[0..2]));
|
||||
|
||||
read = try ps.stream.read(dest[0..4]);
|
||||
expect(read == 4);
|
||||
expect(mem.eql(u8, dest[0..4], bytes[2..6]));
|
||||
|
||||
read = try ps.stream.read(dest[0..4]);
|
||||
expect(read == 2);
|
||||
expect(mem.eql(u8, dest[0..2], bytes[6..8]));
|
||||
|
||||
try ps.putBackByte(11);
|
||||
try ps.putBackByte(12);
|
||||
|
||||
read = try ps.stream.read(dest[0..4]);
|
||||
expect(read == 2);
|
||||
expect(dest[0] == 12);
|
||||
expect(dest[1] == 11);
|
||||
}
|
||||
|
||||
test "SliceOutStream" {
|
||||
var buffer: [10]u8 = undefined;
|
||||
var ss = io.SliceOutStream.init(buffer[0..]);
|
||||
|
||||
try ss.stream.write("Hello");
|
||||
expect(mem.eql(u8, ss.getWritten(), "Hello"));
|
||||
|
||||
try ss.stream.write("world");
|
||||
expect(mem.eql(u8, ss.getWritten(), "Helloworld"));
|
||||
|
||||
expectError(error.OutOfMemory, ss.stream.write("!"));
|
||||
expect(mem.eql(u8, ss.getWritten(), "Helloworld"));
|
||||
|
||||
ss.reset();
|
||||
expect(ss.getWritten().len == 0);
|
||||
|
||||
expectError(error.OutOfMemory, ss.stream.write("Hello world!"));
|
||||
expect(mem.eql(u8, ss.getWritten(), "Hello worl"));
|
||||
}
|
||||
|
||||
test "BitInStream" {
|
||||
const mem_be = [_]u8{ 0b11001101, 0b00001011 };
|
||||
const mem_le = [_]u8{ 0b00011101, 0b10010101 };
|
||||
|
||||
var mem_in_be = io.SliceInStream.init(mem_be[0..]);
|
||||
const InError = io.SliceInStream.Error;
|
||||
var bit_stream_be = io.BitInStream(builtin.Endian.Big, InError).init(&mem_in_be.stream);
|
||||
|
||||
var out_bits: usize = undefined;
|
||||
|
||||
expect(1 == try bit_stream_be.readBits(u2, 1, &out_bits));
|
||||
expect(out_bits == 1);
|
||||
expect(2 == try bit_stream_be.readBits(u5, 2, &out_bits));
|
||||
expect(out_bits == 2);
|
||||
expect(3 == try bit_stream_be.readBits(u128, 3, &out_bits));
|
||||
expect(out_bits == 3);
|
||||
expect(4 == try bit_stream_be.readBits(u8, 4, &out_bits));
|
||||
expect(out_bits == 4);
|
||||
expect(5 == try bit_stream_be.readBits(u9, 5, &out_bits));
|
||||
expect(out_bits == 5);
|
||||
expect(1 == try bit_stream_be.readBits(u1, 1, &out_bits));
|
||||
expect(out_bits == 1);
|
||||
|
||||
mem_in_be.pos = 0;
|
||||
bit_stream_be.bit_count = 0;
|
||||
expect(0b110011010000101 == try bit_stream_be.readBits(u15, 15, &out_bits));
|
||||
expect(out_bits == 15);
|
||||
|
||||
mem_in_be.pos = 0;
|
||||
bit_stream_be.bit_count = 0;
|
||||
expect(0b1100110100001011 == try bit_stream_be.readBits(u16, 16, &out_bits));
|
||||
expect(out_bits == 16);
|
||||
|
||||
_ = try bit_stream_be.readBits(u0, 0, &out_bits);
|
||||
|
||||
expect(0 == try bit_stream_be.readBits(u1, 1, &out_bits));
|
||||
expect(out_bits == 0);
|
||||
expectError(error.EndOfStream, bit_stream_be.readBitsNoEof(u1, 1));
|
||||
|
||||
var mem_in_le = io.SliceInStream.init(mem_le[0..]);
|
||||
var bit_stream_le = io.BitInStream(builtin.Endian.Little, InError).init(&mem_in_le.stream);
|
||||
|
||||
expect(1 == try bit_stream_le.readBits(u2, 1, &out_bits));
|
||||
expect(out_bits == 1);
|
||||
expect(2 == try bit_stream_le.readBits(u5, 2, &out_bits));
|
||||
expect(out_bits == 2);
|
||||
expect(3 == try bit_stream_le.readBits(u128, 3, &out_bits));
|
||||
expect(out_bits == 3);
|
||||
expect(4 == try bit_stream_le.readBits(u8, 4, &out_bits));
|
||||
expect(out_bits == 4);
|
||||
expect(5 == try bit_stream_le.readBits(u9, 5, &out_bits));
|
||||
expect(out_bits == 5);
|
||||
expect(1 == try bit_stream_le.readBits(u1, 1, &out_bits));
|
||||
expect(out_bits == 1);
|
||||
|
||||
mem_in_le.pos = 0;
|
||||
bit_stream_le.bit_count = 0;
|
||||
expect(0b001010100011101 == try bit_stream_le.readBits(u15, 15, &out_bits));
|
||||
expect(out_bits == 15);
|
||||
|
||||
mem_in_le.pos = 0;
|
||||
bit_stream_le.bit_count = 0;
|
||||
expect(0b1001010100011101 == try bit_stream_le.readBits(u16, 16, &out_bits));
|
||||
expect(out_bits == 16);
|
||||
|
||||
_ = try bit_stream_le.readBits(u0, 0, &out_bits);
|
||||
|
||||
expect(0 == try bit_stream_le.readBits(u1, 1, &out_bits));
|
||||
expect(out_bits == 0);
|
||||
expectError(error.EndOfStream, bit_stream_le.readBitsNoEof(u1, 1));
|
||||
}
|
||||
|
||||
test "BitOutStream" {
|
||||
var mem_be = [_]u8{0} ** 2;
|
||||
var mem_le = [_]u8{0} ** 2;
|
||||
|
||||
var mem_out_be = io.SliceOutStream.init(mem_be[0..]);
|
||||
const OutError = io.SliceOutStream.Error;
|
||||
var bit_stream_be = io.BitOutStream(builtin.Endian.Big, OutError).init(&mem_out_be.stream);
|
||||
|
||||
try bit_stream_be.writeBits(@as(u2, 1), 1);
|
||||
try bit_stream_be.writeBits(@as(u5, 2), 2);
|
||||
try bit_stream_be.writeBits(@as(u128, 3), 3);
|
||||
try bit_stream_be.writeBits(@as(u8, 4), 4);
|
||||
try bit_stream_be.writeBits(@as(u9, 5), 5);
|
||||
try bit_stream_be.writeBits(@as(u1, 1), 1);
|
||||
|
||||
expect(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001011);
|
||||
|
||||
mem_out_be.pos = 0;
|
||||
|
||||
try bit_stream_be.writeBits(@as(u15, 0b110011010000101), 15);
|
||||
try bit_stream_be.flushBits();
|
||||
expect(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001010);
|
||||
|
||||
mem_out_be.pos = 0;
|
||||
try bit_stream_be.writeBits(@as(u32, 0b110011010000101), 16);
|
||||
expect(mem_be[0] == 0b01100110 and mem_be[1] == 0b10000101);
|
||||
|
||||
try bit_stream_be.writeBits(@as(u0, 0), 0);
|
||||
|
||||
var mem_out_le = io.SliceOutStream.init(mem_le[0..]);
|
||||
var bit_stream_le = io.BitOutStream(builtin.Endian.Little, OutError).init(&mem_out_le.stream);
|
||||
|
||||
try bit_stream_le.writeBits(@as(u2, 1), 1);
|
||||
try bit_stream_le.writeBits(@as(u5, 2), 2);
|
||||
try bit_stream_le.writeBits(@as(u128, 3), 3);
|
||||
try bit_stream_le.writeBits(@as(u8, 4), 4);
|
||||
try bit_stream_le.writeBits(@as(u9, 5), 5);
|
||||
try bit_stream_le.writeBits(@as(u1, 1), 1);
|
||||
|
||||
expect(mem_le[0] == 0b00011101 and mem_le[1] == 0b10010101);
|
||||
|
||||
mem_out_le.pos = 0;
|
||||
try bit_stream_le.writeBits(@as(u15, 0b110011010000101), 15);
|
||||
try bit_stream_le.flushBits();
|
||||
expect(mem_le[0] == 0b10000101 and mem_le[1] == 0b01100110);
|
||||
|
||||
mem_out_le.pos = 0;
|
||||
try bit_stream_le.writeBits(@as(u32, 0b1100110100001011), 16);
|
||||
expect(mem_le[0] == 0b00001011 and mem_le[1] == 0b11001101);
|
||||
|
||||
try bit_stream_le.writeBits(@as(u0, 0), 0);
|
||||
}
|
||||
|
||||
test "BitStreams with File Stream" {
|
||||
const tmp_file_name = "temp_test_file.txt";
|
||||
{
|
||||
var file = try fs.cwd().createFile(tmp_file_name, .{});
|
||||
defer file.close();
|
||||
|
||||
var file_out = file.outStream();
|
||||
var file_out_stream = &file_out.stream;
|
||||
const OutError = File.WriteError;
|
||||
var bit_stream = io.BitOutStream(builtin.endian, OutError).init(file_out_stream);
|
||||
var bit_stream = io.bitOutStream(builtin.endian, file.outStream());
|
||||
|
||||
try bit_stream.writeBits(@as(u2, 1), 1);
|
||||
try bit_stream.writeBits(@as(u5, 2), 2);
|
||||
@ -292,10 +79,7 @@ test "BitStreams with File Stream" {
|
||||
var file = try fs.cwd().openFile(tmp_file_name, .{});
|
||||
defer file.close();
|
||||
|
||||
var file_in = file.inStream();
|
||||
var file_in_stream = &file_in.stream;
|
||||
const InError = File.ReadError;
|
||||
var bit_stream = io.BitInStream(builtin.endian, InError).init(file_in_stream);
|
||||
var bit_stream = io.bitInStream(builtin.endian, file.inStream());
|
||||
|
||||
var out_bits: usize = undefined;
|
||||
|
||||
@ -317,298 +101,6 @@ test "BitStreams with File Stream" {
|
||||
try fs.cwd().deleteFile(tmp_file_name);
|
||||
}
|
||||
|
||||
fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void {
|
||||
@setEvalBranchQuota(1500);
|
||||
//@NOTE: if this test is taking too long, reduce the maximum tested bitsize
|
||||
const max_test_bitsize = 128;
|
||||
|
||||
const total_bytes = comptime blk: {
|
||||
var bytes = 0;
|
||||
comptime var i = 0;
|
||||
while (i <= max_test_bitsize) : (i += 1) bytes += (i / 8) + @boolToInt(i % 8 > 0);
|
||||
break :blk bytes * 2;
|
||||
};
|
||||
|
||||
var data_mem: [total_bytes]u8 = undefined;
|
||||
var out = io.SliceOutStream.init(data_mem[0..]);
|
||||
const OutError = io.SliceOutStream.Error;
|
||||
var out_stream = &out.stream;
|
||||
var serializer = io.Serializer(endian, packing, OutError).init(out_stream);
|
||||
|
||||
var in = io.SliceInStream.init(data_mem[0..]);
|
||||
const InError = io.SliceInStream.Error;
|
||||
var in_stream = &in.stream;
|
||||
var deserializer = io.Deserializer(endian, packing, InError).init(in_stream);
|
||||
|
||||
comptime var i = 0;
|
||||
inline while (i <= max_test_bitsize) : (i += 1) {
|
||||
const U = std.meta.IntType(false, i);
|
||||
const S = std.meta.IntType(true, i);
|
||||
try serializer.serializeInt(@as(U, i));
|
||||
if (i != 0) try serializer.serializeInt(@as(S, -1)) else try serializer.serialize(@as(S, 0));
|
||||
}
|
||||
try serializer.flush();
|
||||
|
||||
i = 0;
|
||||
inline while (i <= max_test_bitsize) : (i += 1) {
|
||||
const U = std.meta.IntType(false, i);
|
||||
const S = std.meta.IntType(true, i);
|
||||
const x = try deserializer.deserializeInt(U);
|
||||
const y = try deserializer.deserializeInt(S);
|
||||
expect(x == @as(U, i));
|
||||
if (i != 0) expect(y == @as(S, -1)) else expect(y == 0);
|
||||
}
|
||||
|
||||
const u8_bit_count = comptime meta.bitCount(u8);
|
||||
//0 + 1 + 2 + ... n = (n * (n + 1)) / 2
|
||||
//and we have each for unsigned and signed, so * 2
|
||||
const total_bits = (max_test_bitsize * (max_test_bitsize + 1));
|
||||
const extra_packed_byte = @boolToInt(total_bits % u8_bit_count > 0);
|
||||
const total_packed_bytes = (total_bits / u8_bit_count) + extra_packed_byte;
|
||||
|
||||
expect(in.pos == if (packing == .Bit) total_packed_bytes else total_bytes);
|
||||
|
||||
//Verify that empty error set works with serializer.
|
||||
//deserializer is covered by SliceInStream
|
||||
const NullError = io.NullOutStream.Error;
|
||||
var null_out = io.NullOutStream.init();
|
||||
var null_out_stream = &null_out.stream;
|
||||
var null_serializer = io.Serializer(endian, packing, NullError).init(null_out_stream);
|
||||
try null_serializer.serialize(data_mem[0..]);
|
||||
try null_serializer.flush();
|
||||
}
|
||||
|
||||
test "Serializer/Deserializer Int" {
|
||||
try testIntSerializerDeserializer(.Big, .Byte);
|
||||
try testIntSerializerDeserializer(.Little, .Byte);
|
||||
// TODO these tests are disabled due to tripping an LLVM assertion
|
||||
// https://github.com/ziglang/zig/issues/2019
|
||||
//try testIntSerializerDeserializer(builtin.Endian.Big, true);
|
||||
//try testIntSerializerDeserializer(builtin.Endian.Little, true);
|
||||
}
|
||||
|
||||
fn testIntSerializerDeserializerInfNaN(
|
||||
comptime endian: builtin.Endian,
|
||||
comptime packing: io.Packing,
|
||||
) !void {
|
||||
const mem_size = (16 * 2 + 32 * 2 + 64 * 2 + 128 * 2) / comptime meta.bitCount(u8);
|
||||
var data_mem: [mem_size]u8 = undefined;
|
||||
|
||||
var out = io.SliceOutStream.init(data_mem[0..]);
|
||||
const OutError = io.SliceOutStream.Error;
|
||||
var out_stream = &out.stream;
|
||||
var serializer = io.Serializer(endian, packing, OutError).init(out_stream);
|
||||
|
||||
var in = io.SliceInStream.init(data_mem[0..]);
|
||||
const InError = io.SliceInStream.Error;
|
||||
var in_stream = &in.stream;
|
||||
var deserializer = io.Deserializer(endian, packing, InError).init(in_stream);
|
||||
|
||||
//@TODO: isInf/isNan not currently implemented for f128.
|
||||
try serializer.serialize(std.math.nan(f16));
|
||||
try serializer.serialize(std.math.inf(f16));
|
||||
try serializer.serialize(std.math.nan(f32));
|
||||
try serializer.serialize(std.math.inf(f32));
|
||||
try serializer.serialize(std.math.nan(f64));
|
||||
try serializer.serialize(std.math.inf(f64));
|
||||
//try serializer.serialize(std.math.nan(f128));
|
||||
//try serializer.serialize(std.math.inf(f128));
|
||||
const nan_check_f16 = try deserializer.deserialize(f16);
|
||||
const inf_check_f16 = try deserializer.deserialize(f16);
|
||||
const nan_check_f32 = try deserializer.deserialize(f32);
|
||||
deserializer.alignToByte();
|
||||
const inf_check_f32 = try deserializer.deserialize(f32);
|
||||
const nan_check_f64 = try deserializer.deserialize(f64);
|
||||
const inf_check_f64 = try deserializer.deserialize(f64);
|
||||
//const nan_check_f128 = try deserializer.deserialize(f128);
|
||||
//const inf_check_f128 = try deserializer.deserialize(f128);
|
||||
expect(std.math.isNan(nan_check_f16));
|
||||
expect(std.math.isInf(inf_check_f16));
|
||||
expect(std.math.isNan(nan_check_f32));
|
||||
expect(std.math.isInf(inf_check_f32));
|
||||
expect(std.math.isNan(nan_check_f64));
|
||||
expect(std.math.isInf(inf_check_f64));
|
||||
//expect(std.math.isNan(nan_check_f128));
|
||||
//expect(std.math.isInf(inf_check_f128));
|
||||
}
|
||||
|
||||
test "Serializer/Deserializer Int: Inf/NaN" {
|
||||
try testIntSerializerDeserializerInfNaN(.Big, .Byte);
|
||||
try testIntSerializerDeserializerInfNaN(.Little, .Byte);
|
||||
try testIntSerializerDeserializerInfNaN(.Big, .Bit);
|
||||
try testIntSerializerDeserializerInfNaN(.Little, .Bit);
|
||||
}
|
||||
|
||||
fn testAlternateSerializer(self: var, serializer: var) !void {
|
||||
try serializer.serialize(self.f_f16);
|
||||
}
|
||||
|
||||
fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void {
|
||||
const ColorType = enum(u4) {
|
||||
RGB8 = 1,
|
||||
RA16 = 2,
|
||||
R32 = 3,
|
||||
};
|
||||
|
||||
const TagAlign = union(enum(u32)) {
|
||||
A: u8,
|
||||
B: u8,
|
||||
C: u8,
|
||||
};
|
||||
|
||||
const Color = union(ColorType) {
|
||||
RGB8: struct {
|
||||
r: u8,
|
||||
g: u8,
|
||||
b: u8,
|
||||
a: u8,
|
||||
},
|
||||
RA16: struct {
|
||||
r: u16,
|
||||
a: u16,
|
||||
},
|
||||
R32: u32,
|
||||
};
|
||||
|
||||
const PackedStruct = packed struct {
|
||||
f_i3: i3,
|
||||
f_u2: u2,
|
||||
};
|
||||
|
||||
//to test custom serialization
|
||||
const Custom = struct {
|
||||
f_f16: f16,
|
||||
f_unused_u32: u32,
|
||||
|
||||
pub fn deserialize(self: *@This(), deserializer: var) !void {
|
||||
try deserializer.deserializeInto(&self.f_f16);
|
||||
self.f_unused_u32 = 47;
|
||||
}
|
||||
|
||||
pub const serialize = testAlternateSerializer;
|
||||
};
|
||||
|
||||
const MyStruct = struct {
|
||||
f_i3: i3,
|
||||
f_u8: u8,
|
||||
f_tag_align: TagAlign,
|
||||
f_u24: u24,
|
||||
f_i19: i19,
|
||||
f_void: void,
|
||||
f_f32: f32,
|
||||
f_f128: f128,
|
||||
f_packed_0: PackedStruct,
|
||||
f_i7arr: [10]i7,
|
||||
f_of64n: ?f64,
|
||||
f_of64v: ?f64,
|
||||
f_color_type: ColorType,
|
||||
f_packed_1: PackedStruct,
|
||||
f_custom: Custom,
|
||||
f_color: Color,
|
||||
};
|
||||
|
||||
const my_inst = MyStruct{
|
||||
.f_i3 = -1,
|
||||
.f_u8 = 8,
|
||||
.f_tag_align = TagAlign{ .B = 148 },
|
||||
.f_u24 = 24,
|
||||
.f_i19 = 19,
|
||||
.f_void = {},
|
||||
.f_f32 = 32.32,
|
||||
.f_f128 = 128.128,
|
||||
.f_packed_0 = PackedStruct{ .f_i3 = -1, .f_u2 = 2 },
|
||||
.f_i7arr = [10]i7{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
|
||||
.f_of64n = null,
|
||||
.f_of64v = 64.64,
|
||||
.f_color_type = ColorType.R32,
|
||||
.f_packed_1 = PackedStruct{ .f_i3 = 1, .f_u2 = 1 },
|
||||
.f_custom = Custom{ .f_f16 = 38.63, .f_unused_u32 = 47 },
|
||||
.f_color = Color{ .R32 = 123822 },
|
||||
};
|
||||
|
||||
var data_mem: [@sizeOf(MyStruct)]u8 = undefined;
|
||||
var out = io.SliceOutStream.init(data_mem[0..]);
|
||||
const OutError = io.SliceOutStream.Error;
|
||||
var out_stream = &out.stream;
|
||||
var serializer = io.Serializer(endian, packing, OutError).init(out_stream);
|
||||
|
||||
var in = io.SliceInStream.init(data_mem[0..]);
|
||||
const InError = io.SliceInStream.Error;
|
||||
var in_stream = &in.stream;
|
||||
var deserializer = io.Deserializer(endian, packing, InError).init(in_stream);
|
||||
|
||||
try serializer.serialize(my_inst);
|
||||
|
||||
const my_copy = try deserializer.deserialize(MyStruct);
|
||||
expect(meta.eql(my_copy, my_inst));
|
||||
}
|
||||
|
||||
test "Serializer/Deserializer generic" {
|
||||
if (std.Target.current.os.tag == .windows) {
|
||||
// TODO https://github.com/ziglang/zig/issues/508
|
||||
return error.SkipZigTest;
|
||||
}
|
||||
try testSerializerDeserializer(builtin.Endian.Big, .Byte);
|
||||
try testSerializerDeserializer(builtin.Endian.Little, .Byte);
|
||||
try testSerializerDeserializer(builtin.Endian.Big, .Bit);
|
||||
try testSerializerDeserializer(builtin.Endian.Little, .Bit);
|
||||
}
|
||||
|
||||
fn testBadData(comptime endian: builtin.Endian, comptime packing: io.Packing) !void {
|
||||
const E = enum(u14) {
|
||||
One = 1,
|
||||
Two = 2,
|
||||
};
|
||||
|
||||
const A = struct {
|
||||
e: E,
|
||||
};
|
||||
|
||||
const C = union(E) {
|
||||
One: u14,
|
||||
Two: f16,
|
||||
};
|
||||
|
||||
var data_mem: [4]u8 = undefined;
|
||||
var out = io.SliceOutStream.init(data_mem[0..]);
|
||||
const OutError = io.SliceOutStream.Error;
|
||||
var out_stream = &out.stream;
|
||||
var serializer = io.Serializer(endian, packing, OutError).init(out_stream);
|
||||
|
||||
var in = io.SliceInStream.init(data_mem[0..]);
|
||||
const InError = io.SliceInStream.Error;
|
||||
var in_stream = &in.stream;
|
||||
var deserializer = io.Deserializer(endian, packing, InError).init(in_stream);
|
||||
|
||||
try serializer.serialize(@as(u14, 3));
|
||||
expectError(error.InvalidEnumTag, deserializer.deserialize(A));
|
||||
out.pos = 0;
|
||||
try serializer.serialize(@as(u14, 3));
|
||||
try serializer.serialize(@as(u14, 88));
|
||||
expectError(error.InvalidEnumTag, deserializer.deserialize(C));
|
||||
}
|
||||
|
||||
test "Deserializer bad data" {
|
||||
try testBadData(.Big, .Byte);
|
||||
try testBadData(.Little, .Byte);
|
||||
try testBadData(.Big, .Bit);
|
||||
try testBadData(.Little, .Bit);
|
||||
}
|
||||
|
||||
test "c out stream" {
|
||||
if (!builtin.link_libc) return error.SkipZigTest;
|
||||
|
||||
const filename = "tmp_io_test_file.txt";
|
||||
const out_file = std.c.fopen(filename, "w") orelse return error.UnableToOpenTestFile;
|
||||
defer {
|
||||
_ = std.c.fclose(out_file);
|
||||
fs.cwd().deleteFileC(filename) catch {};
|
||||
}
|
||||
|
||||
const out_stream = &io.COutStream.init(out_file).stream;
|
||||
try out_stream.print("hi: {}\n", .{@as(i32, 123)});
|
||||
}
|
||||
|
||||
test "File seek ops" {
|
||||
const tmp_file_name = "temp_test_file.txt";
|
||||
var file = try fs.cwd().createFile(tmp_file_name, .{});
|
||||
@ -621,16 +113,16 @@ test "File seek ops" {
|
||||
|
||||
// Seek to the end
|
||||
try file.seekFromEnd(0);
|
||||
std.testing.expect((try file.getPos()) == try file.getEndPos());
|
||||
expect((try file.getPos()) == try file.getEndPos());
|
||||
// Negative delta
|
||||
try file.seekBy(-4096);
|
||||
std.testing.expect((try file.getPos()) == 4096);
|
||||
expect((try file.getPos()) == 4096);
|
||||
// Positive delta
|
||||
try file.seekBy(10);
|
||||
std.testing.expect((try file.getPos()) == 4106);
|
||||
expect((try file.getPos()) == 4106);
|
||||
// Absolute position
|
||||
try file.seekTo(1234);
|
||||
std.testing.expect((try file.getPos()) == 1234);
|
||||
expect((try file.getPos()) == 1234);
|
||||
}
|
||||
|
||||
test "updateTimes" {
|
||||
@ -647,6 +139,6 @@ test "updateTimes" {
|
||||
stat_old.mtime - 5 * std.time.ns_per_s,
|
||||
);
|
||||
var stat_new = try file.stat();
|
||||
std.testing.expect(stat_new.atime < stat_old.atime);
|
||||
std.testing.expect(stat_new.mtime < stat_old.mtime);
|
||||
expect(stat_new.atime < stat_old.atime);
|
||||
expect(stat_new.mtime < stat_old.mtime);
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ const mem = std.mem;
|
||||
const maxInt = std.math.maxInt;
|
||||
|
||||
pub const WriteStream = @import("json/write_stream.zig").WriteStream;
|
||||
pub const writeStream = @import("json/write_stream.zig").writeStream;
|
||||
|
||||
const StringEscapes = union(enum) {
|
||||
None,
|
||||
@ -2107,9 +2108,9 @@ test "import more json tests" {
|
||||
test "write json then parse it" {
|
||||
var out_buffer: [1000]u8 = undefined;
|
||||
|
||||
var slice_out_stream = std.io.SliceOutStream.init(&out_buffer);
|
||||
const out_stream = &slice_out_stream.stream;
|
||||
var jw = WriteStream(@TypeOf(out_stream).Child, 4).init(out_stream);
|
||||
var fixed_buffer_stream = std.io.fixedBufferStream(&out_buffer);
|
||||
const out_stream = fixed_buffer_stream.outStream();
|
||||
var jw = writeStream(out_stream, 4);
|
||||
|
||||
try jw.beginObject();
|
||||
|
||||
@ -2140,7 +2141,7 @@ test "write json then parse it" {
|
||||
|
||||
var parser = Parser.init(testing.allocator, false);
|
||||
defer parser.deinit();
|
||||
var tree = try parser.parse(slice_out_stream.getWritten());
|
||||
var tree = try parser.parse(fixed_buffer_stream.getWritten());
|
||||
defer tree.deinit();
|
||||
|
||||
testing.expect(tree.root.Object.get("f").?.value.Bool == false);
|
||||
|
@ -30,11 +30,11 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {
|
||||
/// The string used as spacing.
|
||||
space: []const u8 = " ",
|
||||
|
||||
stream: *OutStream,
|
||||
stream: OutStream,
|
||||
state_index: usize,
|
||||
state: [max_depth]State,
|
||||
|
||||
pub fn init(stream: *OutStream) Self {
|
||||
pub fn init(stream: OutStream) Self {
|
||||
var self = Self{
|
||||
.stream = stream,
|
||||
.state_index = 1,
|
||||
@ -90,8 +90,8 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {
|
||||
self.pushState(.Value);
|
||||
try self.indent();
|
||||
try self.writeEscapedString(name);
|
||||
try self.stream.write(":");
|
||||
try self.stream.write(self.space);
|
||||
try self.stream.writeAll(":");
|
||||
try self.stream.writeAll(self.space);
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -134,16 +134,16 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {
|
||||
|
||||
pub fn emitNull(self: *Self) !void {
|
||||
assert(self.state[self.state_index] == State.Value);
|
||||
try self.stream.write("null");
|
||||
try self.stream.writeAll("null");
|
||||
self.popState();
|
||||
}
|
||||
|
||||
pub fn emitBool(self: *Self, value: bool) !void {
|
||||
assert(self.state[self.state_index] == State.Value);
|
||||
if (value) {
|
||||
try self.stream.write("true");
|
||||
try self.stream.writeAll("true");
|
||||
} else {
|
||||
try self.stream.write("false");
|
||||
try self.stream.writeAll("false");
|
||||
}
|
||||
self.popState();
|
||||
}
|
||||
@ -188,13 +188,13 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {
|
||||
try self.stream.writeByte('"');
|
||||
for (string) |s| {
|
||||
switch (s) {
|
||||
'"' => try self.stream.write("\\\""),
|
||||
'\t' => try self.stream.write("\\t"),
|
||||
'\r' => try self.stream.write("\\r"),
|
||||
'\n' => try self.stream.write("\\n"),
|
||||
8 => try self.stream.write("\\b"),
|
||||
12 => try self.stream.write("\\f"),
|
||||
'\\' => try self.stream.write("\\\\"),
|
||||
'"' => try self.stream.writeAll("\\\""),
|
||||
'\t' => try self.stream.writeAll("\\t"),
|
||||
'\r' => try self.stream.writeAll("\\r"),
|
||||
'\n' => try self.stream.writeAll("\\n"),
|
||||
8 => try self.stream.writeAll("\\b"),
|
||||
12 => try self.stream.writeAll("\\f"),
|
||||
'\\' => try self.stream.writeAll("\\\\"),
|
||||
else => try self.stream.writeByte(s),
|
||||
}
|
||||
}
|
||||
@ -231,10 +231,10 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {
|
||||
|
||||
fn indent(self: *Self) !void {
|
||||
assert(self.state_index >= 1);
|
||||
try self.stream.write(self.newline);
|
||||
try self.stream.writeAll(self.newline);
|
||||
var i: usize = 0;
|
||||
while (i < self.state_index - 1) : (i += 1) {
|
||||
try self.stream.write(self.one_indent);
|
||||
try self.stream.writeAll(self.one_indent);
|
||||
}
|
||||
}
|
||||
|
||||
@ -249,15 +249,22 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn writeStream(
|
||||
out_stream: var,
|
||||
comptime max_depth: usize,
|
||||
) WriteStream(@TypeOf(out_stream), max_depth) {
|
||||
return WriteStream(@TypeOf(out_stream), max_depth).init(out_stream);
|
||||
}
|
||||
|
||||
test "json write stream" {
|
||||
var out_buf: [1024]u8 = undefined;
|
||||
var slice_stream = std.io.SliceOutStream.init(&out_buf);
|
||||
const out = &slice_stream.stream;
|
||||
var slice_stream = std.io.fixedBufferStream(&out_buf);
|
||||
const out = slice_stream.outStream();
|
||||
|
||||
var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena_allocator.deinit();
|
||||
|
||||
var w = std.json.WriteStream(@TypeOf(out).Child, 10).init(out);
|
||||
var w = std.json.writeStream(out, 10);
|
||||
try w.emitJson(try getJson(&arena_allocator.allocator));
|
||||
|
||||
const result = slice_stream.getWritten();
|
||||
|
@ -816,7 +816,7 @@ fn linuxLookupNameFromHosts(
|
||||
};
|
||||
defer file.close();
|
||||
|
||||
const stream = &std.io.BufferedInStream(fs.File.ReadError).init(&file.inStream().stream).stream;
|
||||
const stream = std.io.bufferedInStream(file.inStream()).inStream();
|
||||
var line_buf: [512]u8 = undefined;
|
||||
while (stream.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) {
|
||||
error.StreamTooLong => blk: {
|
||||
@ -1010,7 +1010,7 @@ fn getResolvConf(allocator: *mem.Allocator, rc: *ResolvConf) !void {
|
||||
};
|
||||
defer file.close();
|
||||
|
||||
const stream = &std.io.BufferedInStream(fs.File.ReadError).init(&file.inStream().stream).stream;
|
||||
const stream = std.io.bufferedInStream(file.inStream()).inStream();
|
||||
var line_buf: [512]u8 = undefined;
|
||||
while (stream.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) {
|
||||
error.StreamTooLong => blk: {
|
||||
|
@ -113,6 +113,6 @@ fn testClient(addr: net.Address) anyerror!void {
|
||||
fn testServer(server: *net.StreamServer) anyerror!void {
|
||||
var client = try server.accept();
|
||||
|
||||
const stream = &client.file.outStream().stream;
|
||||
const stream = client.file.outStream();
|
||||
try stream.print("hello from server\n", .{});
|
||||
}
|
||||
|
@ -176,7 +176,7 @@ fn getRandomBytesDevURandom(buf: []u8) !void {
|
||||
.io_mode = .blocking,
|
||||
.async_block_allowed = std.fs.File.async_block_allowed_yes,
|
||||
};
|
||||
const stream = &file.inStream().stream;
|
||||
const stream = file.inStream();
|
||||
stream.readNoEof(buf) catch return error.Unexpected;
|
||||
}
|
||||
|
||||
|
@ -95,15 +95,41 @@ test "sendfile" {
|
||||
},
|
||||
};
|
||||
|
||||
var written_buf: [header1.len + header2.len + 10 + trailer1.len + trailer2.len]u8 = undefined;
|
||||
var written_buf: [100]u8 = undefined;
|
||||
try dest_file.writeFileAll(src_file, .{
|
||||
.in_offset = 1,
|
||||
.in_len = 10,
|
||||
.headers_and_trailers = &hdtr,
|
||||
.header_count = 2,
|
||||
});
|
||||
try dest_file.preadAll(&written_buf, 0);
|
||||
expect(mem.eql(u8, &written_buf, "header1\nsecond header\nine1\nsecontrailer1\nsecond trailer\n"));
|
||||
const amt = try dest_file.preadAll(&written_buf, 0);
|
||||
expect(mem.eql(u8, written_buf[0..amt], "header1\nsecond header\nine1\nsecontrailer1\nsecond trailer\n"));
|
||||
}
|
||||
|
||||
test "fs.copyFile" {
|
||||
const data = "u6wj+JmdF3qHsFPE BUlH2g4gJCmEz0PP";
|
||||
const src_file = "tmp_test_copy_file.txt";
|
||||
const dest_file = "tmp_test_copy_file2.txt";
|
||||
const dest_file2 = "tmp_test_copy_file3.txt";
|
||||
|
||||
try fs.cwd().writeFile(src_file, data);
|
||||
defer fs.cwd().deleteFile(src_file) catch {};
|
||||
|
||||
try fs.copyFile(src_file, dest_file);
|
||||
defer fs.cwd().deleteFile(dest_file) catch {};
|
||||
|
||||
try fs.copyFileMode(src_file, dest_file2, File.default_mode);
|
||||
defer fs.cwd().deleteFile(dest_file2) catch {};
|
||||
|
||||
try expectFileContents(dest_file, data);
|
||||
try expectFileContents(dest_file2, data);
|
||||
}
|
||||
|
||||
fn expectFileContents(file_path: []const u8, data: []const u8) !void {
|
||||
const contents = try fs.cwd().readFileAlloc(testing.allocator, file_path, 1000);
|
||||
defer testing.allocator.free(contents);
|
||||
|
||||
testing.expectEqualSlices(u8, data, contents);
|
||||
}
|
||||
|
||||
test "std.Thread.getCurrentId" {
|
||||
@ -354,8 +380,7 @@ test "mmap" {
|
||||
const file = try fs.cwd().createFile(test_out_file, .{});
|
||||
defer file.close();
|
||||
|
||||
var out_stream = file.outStream();
|
||||
const stream = &out_stream.stream;
|
||||
const stream = file.outStream();
|
||||
|
||||
var i: u32 = 0;
|
||||
while (i < alloc_size / @sizeOf(u32)) : (i += 1) {
|
||||
@ -378,8 +403,8 @@ test "mmap" {
|
||||
);
|
||||
defer os.munmap(data);
|
||||
|
||||
var mem_stream = io.SliceInStream.init(data);
|
||||
const stream = &mem_stream.stream;
|
||||
var mem_stream = io.fixedBufferStream(data);
|
||||
const stream = mem_stream.inStream();
|
||||
|
||||
var i: u32 = 0;
|
||||
while (i < alloc_size / @sizeOf(u32)) : (i += 1) {
|
||||
@ -402,8 +427,8 @@ test "mmap" {
|
||||
);
|
||||
defer os.munmap(data);
|
||||
|
||||
var mem_stream = io.SliceInStream.init(data);
|
||||
const stream = &mem_stream.stream;
|
||||
var mem_stream = io.fixedBufferStream(data);
|
||||
const stream = mem_stream.inStream();
|
||||
|
||||
var i: u32 = alloc_size / 2 / @sizeOf(u32);
|
||||
while (i < alloc_size / @sizeOf(u32)) : (i += 1) {
|
||||
|
@ -407,6 +407,7 @@ pub fn ReadFile(in_hFile: HANDLE, buffer: []u8, offset: ?u64) ReadFileError!usiz
|
||||
switch (kernel32.GetLastError()) {
|
||||
.OPERATION_ABORTED => continue,
|
||||
.BROKEN_PIPE => return index,
|
||||
.HANDLE_EOF => return index,
|
||||
else => |err| return unexpectedError(err),
|
||||
}
|
||||
}
|
||||
|
@ -495,8 +495,7 @@ const Msf = struct {
|
||||
streams: []MsfStream,
|
||||
|
||||
fn openFile(self: *Msf, allocator: *mem.Allocator, file: File) !void {
|
||||
var file_stream = file.inStream();
|
||||
const in = &file_stream.stream;
|
||||
const in = file.inStream();
|
||||
|
||||
const superblock = try in.readStruct(SuperBlock);
|
||||
|
||||
@ -529,7 +528,7 @@ const Msf = struct {
|
||||
);
|
||||
|
||||
const begin = self.directory.pos;
|
||||
const stream_count = try self.directory.stream.readIntLittle(u32);
|
||||
const stream_count = try self.directory.inStream().readIntLittle(u32);
|
||||
const stream_sizes = try allocator.alloc(u32, stream_count);
|
||||
defer allocator.free(stream_sizes);
|
||||
|
||||
@ -538,7 +537,7 @@ const Msf = struct {
|
||||
// and must be taken into account when resolving stream indices.
|
||||
const Nil = 0xFFFFFFFF;
|
||||
for (stream_sizes) |*s, i| {
|
||||
const size = try self.directory.stream.readIntLittle(u32);
|
||||
const size = try self.directory.inStream().readIntLittle(u32);
|
||||
s.* = if (size == Nil) 0 else blockCountFromSize(size, superblock.BlockSize);
|
||||
}
|
||||
|
||||
@ -553,7 +552,7 @@ const Msf = struct {
|
||||
var blocks = try allocator.alloc(u32, size);
|
||||
var j: u32 = 0;
|
||||
while (j < size) : (j += 1) {
|
||||
const block_id = try self.directory.stream.readIntLittle(u32);
|
||||
const block_id = try self.directory.inStream().readIntLittle(u32);
|
||||
const n = (block_id % superblock.BlockSize);
|
||||
// 0 is for SuperBlock, 1 and 2 for FPMs.
|
||||
if (block_id == 0 or n == 1 or n == 2 or block_id * superblock.BlockSize > try file.getEndPos())
|
||||
@ -632,11 +631,7 @@ const MsfStream = struct {
|
||||
blocks: []u32 = undefined,
|
||||
block_size: u32 = undefined,
|
||||
|
||||
/// Implementation of InStream trait for Pdb.MsfStream
|
||||
stream: Stream = undefined,
|
||||
|
||||
pub const Error = @TypeOf(read).ReturnType.ErrorSet;
|
||||
pub const Stream = io.InStream(Error);
|
||||
|
||||
fn init(block_size: u32, file: File, blocks: []u32) MsfStream {
|
||||
const stream = MsfStream{
|
||||
@ -644,7 +639,6 @@ const MsfStream = struct {
|
||||
.pos = 0,
|
||||
.blocks = blocks,
|
||||
.block_size = block_size,
|
||||
.stream = Stream{ .readFn = readFn },
|
||||
};
|
||||
|
||||
return stream;
|
||||
@ -653,7 +647,7 @@ const MsfStream = struct {
|
||||
fn readNullTermString(self: *MsfStream, allocator: *mem.Allocator) ![]u8 {
|
||||
var list = ArrayList(u8).init(allocator);
|
||||
while (true) {
|
||||
const byte = try self.stream.readByte();
|
||||
const byte = try self.inStream().readByte();
|
||||
if (byte == 0) {
|
||||
return list.toSlice();
|
||||
}
|
||||
@ -667,8 +661,7 @@ const MsfStream = struct {
|
||||
var offset = self.pos % self.block_size;
|
||||
|
||||
try self.in_file.seekTo(block * self.block_size + offset);
|
||||
var file_stream = self.in_file.inStream();
|
||||
const in = &file_stream.stream;
|
||||
const in = self.in_file.inStream();
|
||||
|
||||
var size: usize = 0;
|
||||
var rem_buffer = buffer;
|
||||
@ -715,8 +708,7 @@ const MsfStream = struct {
|
||||
return block * self.block_size + offset;
|
||||
}
|
||||
|
||||
fn readFn(in_stream: *Stream, buffer: []u8) Error!usize {
|
||||
const self = @fieldParentPtr(MsfStream, "stream", in_stream);
|
||||
return self.read(buffer);
|
||||
fn inStream(self: *MsfStream) std.io.InStream(*MsfStream, Error, read) {
|
||||
return .{ .context = self };
|
||||
}
|
||||
};
|
||||
|
@ -177,7 +177,7 @@ pub const Progress = struct {
|
||||
pub fn log(self: *Progress, comptime format: []const u8, args: var) void {
|
||||
const file = self.terminal orelse return;
|
||||
self.refresh();
|
||||
file.outStream().stream.print(format, args) catch {
|
||||
file.outStream().print(format, args) catch {
|
||||
self.terminal = null;
|
||||
return;
|
||||
};
|
||||
|
@ -42,8 +42,8 @@ pub fn main() !void {
|
||||
|
||||
var targets = ArrayList([]const u8).init(allocator);
|
||||
|
||||
const stderr_stream = &io.getStdErr().outStream().stream;
|
||||
const stdout_stream = &io.getStdOut().outStream().stream;
|
||||
const stderr_stream = io.getStdErr().outStream();
|
||||
const stdout_stream = io.getStdOut().outStream();
|
||||
|
||||
while (nextArg(args, &arg_idx)) |arg| {
|
||||
if (mem.startsWith(u8, arg, "-D")) {
|
||||
@ -159,7 +159,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void {
|
||||
try out_stream.print(" {s:22} {}\n", .{ name, top_level_step.description });
|
||||
}
|
||||
|
||||
try out_stream.write(
|
||||
try out_stream.writeAll(
|
||||
\\
|
||||
\\General Options:
|
||||
\\ --help Print this help and exit
|
||||
@ -184,7 +184,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void {
|
||||
}
|
||||
}
|
||||
|
||||
try out_stream.write(
|
||||
try out_stream.writeAll(
|
||||
\\
|
||||
\\Advanced Options:
|
||||
\\ --build-file [file] Override path to build.zig
|
||||
|
@ -5,7 +5,6 @@ pub const BloomFilter = @import("bloom_filter.zig").BloomFilter;
|
||||
pub const BufMap = @import("buf_map.zig").BufMap;
|
||||
pub const BufSet = @import("buf_set.zig").BufSet;
|
||||
pub const Buffer = @import("buffer.zig").Buffer;
|
||||
pub const BufferOutStream = @import("io.zig").BufferOutStream;
|
||||
pub const ChildProcess = @import("child_process.zig").ChildProcess;
|
||||
pub const DynLib = @import("dynamic_library.zig").DynLib;
|
||||
pub const HashMap = @import("hash_map.zig").HashMap;
|
||||
|
@ -375,7 +375,7 @@ pub const Error = union(enum) {
|
||||
token: TokenIndex,
|
||||
|
||||
pub fn render(self: *const ThisError, tokens: *Tree.TokenList, stream: var) !void {
|
||||
return stream.write(msg);
|
||||
return stream.writeAll(msg);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -2809,7 +2809,7 @@ const maxInt = std.math.maxInt;
|
||||
var fixed_buffer_mem: [100 * 1024]u8 = undefined;
|
||||
|
||||
fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *bool) ![]u8 {
|
||||
const stderr = &io.getStdErr().outStream().stream;
|
||||
const stderr = io.getStdErr().outStream();
|
||||
|
||||
const tree = try std.zig.parse(allocator, source);
|
||||
defer tree.deinit();
|
||||
@ -2824,17 +2824,17 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b
|
||||
{
|
||||
var i: usize = 0;
|
||||
while (i < loc.column) : (i += 1) {
|
||||
try stderr.write(" ");
|
||||
try stderr.writeAll(" ");
|
||||
}
|
||||
}
|
||||
{
|
||||
const caret_count = token.end - token.start;
|
||||
var i: usize = 0;
|
||||
while (i < caret_count) : (i += 1) {
|
||||
try stderr.write("~");
|
||||
try stderr.writeAll("~");
|
||||
}
|
||||
}
|
||||
try stderr.write("\n");
|
||||
try stderr.writeAll("\n");
|
||||
}
|
||||
if (tree.errors.len != 0) {
|
||||
return error.ParseError;
|
||||
@ -2843,8 +2843,7 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b
|
||||
var buffer = try std.Buffer.initSize(allocator, 0);
|
||||
errdefer buffer.deinit();
|
||||
|
||||
var buffer_out_stream = io.BufferOutStream.init(&buffer);
|
||||
anything_changed.* = try std.zig.render(allocator, &buffer_out_stream.stream, tree);
|
||||
anything_changed.* = try std.zig.render(allocator, buffer.outStream(), tree);
|
||||
return buffer.toOwnedSlice();
|
||||
}
|
||||
|
||||
|
@ -12,64 +12,58 @@ pub const Error = error{
|
||||
};
|
||||
|
||||
/// Returns whether anything changed
|
||||
pub fn render(allocator: *mem.Allocator, stream: var, tree: *ast.Tree) (@TypeOf(stream).Child.Error || Error)!bool {
|
||||
comptime assert(@typeInfo(@TypeOf(stream)) == .Pointer);
|
||||
|
||||
var anything_changed: bool = false;
|
||||
|
||||
pub fn render(allocator: *mem.Allocator, stream: var, tree: *ast.Tree) (@TypeOf(stream).Error || Error)!bool {
|
||||
// make a passthrough stream that checks whether something changed
|
||||
const MyStream = struct {
|
||||
const MyStream = @This();
|
||||
const StreamError = @TypeOf(stream).Child.Error;
|
||||
const Stream = std.io.OutStream(StreamError);
|
||||
const StreamError = @TypeOf(stream).Error;
|
||||
|
||||
anything_changed_ptr: *bool,
|
||||
child_stream: @TypeOf(stream),
|
||||
stream: Stream,
|
||||
anything_changed: bool,
|
||||
source_index: usize,
|
||||
source: []const u8,
|
||||
|
||||
fn write(iface_stream: *Stream, bytes: []const u8) StreamError!usize {
|
||||
const self = @fieldParentPtr(MyStream, "stream", iface_stream);
|
||||
|
||||
if (!self.anything_changed_ptr.*) {
|
||||
fn write(self: *MyStream, bytes: []const u8) StreamError!usize {
|
||||
if (!self.anything_changed) {
|
||||
const end = self.source_index + bytes.len;
|
||||
if (end > self.source.len) {
|
||||
self.anything_changed_ptr.* = true;
|
||||
self.anything_changed = true;
|
||||
} else {
|
||||
const src_slice = self.source[self.source_index..end];
|
||||
self.source_index += bytes.len;
|
||||
if (!mem.eql(u8, bytes, src_slice)) {
|
||||
self.anything_changed_ptr.* = true;
|
||||
self.anything_changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return self.child_stream.writeOnce(bytes);
|
||||
return self.child_stream.write(bytes);
|
||||
}
|
||||
};
|
||||
var my_stream = MyStream{
|
||||
.stream = MyStream.Stream{ .writeFn = MyStream.write },
|
||||
.child_stream = stream,
|
||||
.anything_changed_ptr = &anything_changed,
|
||||
.anything_changed = false,
|
||||
.source_index = 0,
|
||||
.source = tree.source,
|
||||
};
|
||||
const my_stream_stream: std.io.OutStream(*MyStream, MyStream.StreamError, MyStream.write) = .{
|
||||
.context = &my_stream,
|
||||
};
|
||||
|
||||
try renderRoot(allocator, &my_stream.stream, tree);
|
||||
try renderRoot(allocator, my_stream_stream, tree);
|
||||
|
||||
if (!anything_changed and my_stream.source_index != my_stream.source.len) {
|
||||
anything_changed = true;
|
||||
if (my_stream.source_index != my_stream.source.len) {
|
||||
my_stream.anything_changed = true;
|
||||
}
|
||||
|
||||
return anything_changed;
|
||||
return my_stream.anything_changed;
|
||||
}
|
||||
|
||||
fn renderRoot(
|
||||
allocator: *mem.Allocator,
|
||||
stream: var,
|
||||
tree: *ast.Tree,
|
||||
) (@TypeOf(stream).Child.Error || Error)!void {
|
||||
) (@TypeOf(stream).Error || Error)!void {
|
||||
var tok_it = tree.tokens.iterator(0);
|
||||
|
||||
// render all the line comments at the beginning of the file
|
||||
@ -189,7 +183,7 @@ fn renderRoot(
|
||||
}
|
||||
}
|
||||
|
||||
fn renderExtraNewline(tree: *ast.Tree, stream: var, start_col: *usize, node: *ast.Node) @TypeOf(stream).Child.Error!void {
|
||||
fn renderExtraNewline(tree: *ast.Tree, stream: var, start_col: *usize, node: *ast.Node) @TypeOf(stream).Error!void {
|
||||
const first_token = node.firstToken();
|
||||
var prev_token = first_token;
|
||||
if (prev_token == 0) return;
|
||||
@ -204,11 +198,11 @@ fn renderExtraNewline(tree: *ast.Tree, stream: var, start_col: *usize, node: *as
|
||||
}
|
||||
}
|
||||
|
||||
fn renderTopLevelDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, indent: usize, start_col: *usize, decl: *ast.Node) (@TypeOf(stream).Child.Error || Error)!void {
|
||||
fn renderTopLevelDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, indent: usize, start_col: *usize, decl: *ast.Node) (@TypeOf(stream).Error || Error)!void {
|
||||
try renderContainerDecl(allocator, stream, tree, indent, start_col, decl, .Newline);
|
||||
}
|
||||
|
||||
fn renderContainerDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, indent: usize, start_col: *usize, decl: *ast.Node, space: Space) (@TypeOf(stream).Child.Error || Error)!void {
|
||||
fn renderContainerDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, indent: usize, start_col: *usize, decl: *ast.Node, space: Space) (@TypeOf(stream).Error || Error)!void {
|
||||
switch (decl.id) {
|
||||
.FnProto => {
|
||||
const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
|
||||
@ -343,7 +337,7 @@ fn renderExpression(
|
||||
start_col: *usize,
|
||||
base: *ast.Node,
|
||||
space: Space,
|
||||
) (@TypeOf(stream).Child.Error || Error)!void {
|
||||
) (@TypeOf(stream).Error || Error)!void {
|
||||
switch (base.id) {
|
||||
.Identifier => {
|
||||
const identifier = @fieldParentPtr(ast.Node.Identifier, "base", base);
|
||||
@ -449,9 +443,9 @@ fn renderExpression(
|
||||
switch (op_tok_id) {
|
||||
.Asterisk, .AsteriskAsterisk => try stream.writeByte('*'),
|
||||
.LBracket => if (tree.tokens.at(prefix_op_node.op_token + 2).id == .Identifier)
|
||||
try stream.write("[*c")
|
||||
try stream.writeAll("[*c")
|
||||
else
|
||||
try stream.write("[*"),
|
||||
try stream.writeAll("[*"),
|
||||
else => unreachable,
|
||||
}
|
||||
if (ptr_info.sentinel) |sentinel| {
|
||||
@ -757,7 +751,7 @@ fn renderExpression(
|
||||
while (it.next()) |field_init| {
|
||||
var find_stream = FindByteOutStream.init('\n');
|
||||
var dummy_col: usize = 0;
|
||||
try renderExpression(allocator, &find_stream.stream, tree, 0, &dummy_col, field_init.*, Space.None);
|
||||
try renderExpression(allocator, find_stream.outStream(), tree, 0, &dummy_col, field_init.*, Space.None);
|
||||
if (find_stream.byte_found) break :blk false;
|
||||
}
|
||||
break :blk true;
|
||||
@ -909,8 +903,7 @@ fn renderExpression(
|
||||
var column_widths = widths[widths.len - row_size ..];
|
||||
|
||||
// Null stream for counting the printed length of each expression
|
||||
var null_stream = std.io.NullOutStream.init();
|
||||
var counting_stream = std.io.CountingOutStream(std.io.NullOutStream.Error).init(&null_stream.stream);
|
||||
var counting_stream = std.io.countingOutStream(std.io.null_out_stream);
|
||||
|
||||
var it = exprs.iterator(0);
|
||||
var i: usize = 0;
|
||||
@ -918,7 +911,7 @@ fn renderExpression(
|
||||
while (it.next()) |expr| : (i += 1) {
|
||||
counting_stream.bytes_written = 0;
|
||||
var dummy_col: usize = 0;
|
||||
try renderExpression(allocator, &counting_stream.stream, tree, indent, &dummy_col, expr.*, Space.None);
|
||||
try renderExpression(allocator, counting_stream.outStream(), tree, indent, &dummy_col, expr.*, Space.None);
|
||||
const width = @intCast(usize, counting_stream.bytes_written);
|
||||
const col = i % row_size;
|
||||
column_widths[col] = std.math.max(column_widths[col], width);
|
||||
@ -1336,7 +1329,7 @@ fn renderExpression(
|
||||
|
||||
// TODO: Remove condition after deprecating 'typeOf'. See https://github.com/ziglang/zig/issues/1348
|
||||
if (mem.eql(u8, tree.tokenSlicePtr(tree.tokens.at(builtin_call.builtin_token)), "@typeOf")) {
|
||||
try stream.write("@TypeOf");
|
||||
try stream.writeAll("@TypeOf");
|
||||
} else {
|
||||
try renderToken(tree, stream, builtin_call.builtin_token, indent, start_col, Space.None); // @name
|
||||
}
|
||||
@ -1505,9 +1498,9 @@ fn renderExpression(
|
||||
try renderExpression(allocator, stream, tree, indent, start_col, callconv_expr, Space.None);
|
||||
try renderToken(tree, stream, callconv_rparen, indent, start_col, Space.Space); // )
|
||||
} else if (cc_rewrite_str) |str| {
|
||||
try stream.write("callconv(");
|
||||
try stream.write(mem.toSliceConst(u8, str));
|
||||
try stream.write(") ");
|
||||
try stream.writeAll("callconv(");
|
||||
try stream.writeAll(mem.toSliceConst(u8, str));
|
||||
try stream.writeAll(") ");
|
||||
}
|
||||
|
||||
switch (fn_proto.return_type) {
|
||||
@ -1997,11 +1990,11 @@ fn renderExpression(
|
||||
.AsmInput => {
|
||||
const asm_input = @fieldParentPtr(ast.Node.AsmInput, "base", base);
|
||||
|
||||
try stream.write("[");
|
||||
try stream.writeAll("[");
|
||||
try renderExpression(allocator, stream, tree, indent, start_col, asm_input.symbolic_name, Space.None);
|
||||
try stream.write("] ");
|
||||
try stream.writeAll("] ");
|
||||
try renderExpression(allocator, stream, tree, indent, start_col, asm_input.constraint, Space.None);
|
||||
try stream.write(" (");
|
||||
try stream.writeAll(" (");
|
||||
try renderExpression(allocator, stream, tree, indent, start_col, asm_input.expr, Space.None);
|
||||
return renderToken(tree, stream, asm_input.lastToken(), indent, start_col, space); // )
|
||||
},
|
||||
@ -2009,18 +2002,18 @@ fn renderExpression(
|
||||
.AsmOutput => {
|
||||
const asm_output = @fieldParentPtr(ast.Node.AsmOutput, "base", base);
|
||||
|
||||
try stream.write("[");
|
||||
try stream.writeAll("[");
|
||||
try renderExpression(allocator, stream, tree, indent, start_col, asm_output.symbolic_name, Space.None);
|
||||
try stream.write("] ");
|
||||
try stream.writeAll("] ");
|
||||
try renderExpression(allocator, stream, tree, indent, start_col, asm_output.constraint, Space.None);
|
||||
try stream.write(" (");
|
||||
try stream.writeAll(" (");
|
||||
|
||||
switch (asm_output.kind) {
|
||||
ast.Node.AsmOutput.Kind.Variable => |variable_name| {
|
||||
try renderExpression(allocator, stream, tree, indent, start_col, &variable_name.base, Space.None);
|
||||
},
|
||||
ast.Node.AsmOutput.Kind.Return => |return_type| {
|
||||
try stream.write("-> ");
|
||||
try stream.writeAll("-> ");
|
||||
try renderExpression(allocator, stream, tree, indent, start_col, return_type, Space.None);
|
||||
},
|
||||
}
|
||||
@ -2052,7 +2045,7 @@ fn renderVarDecl(
|
||||
indent: usize,
|
||||
start_col: *usize,
|
||||
var_decl: *ast.Node.VarDecl,
|
||||
) (@TypeOf(stream).Child.Error || Error)!void {
|
||||
) (@TypeOf(stream).Error || Error)!void {
|
||||
if (var_decl.visib_token) |visib_token| {
|
||||
try renderToken(tree, stream, visib_token, indent, start_col, Space.Space); // pub
|
||||
}
|
||||
@ -2125,7 +2118,7 @@ fn renderParamDecl(
|
||||
start_col: *usize,
|
||||
base: *ast.Node,
|
||||
space: Space,
|
||||
) (@TypeOf(stream).Child.Error || Error)!void {
|
||||
) (@TypeOf(stream).Error || Error)!void {
|
||||
const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base);
|
||||
|
||||
try renderDocComments(tree, stream, param_decl, indent, start_col);
|
||||
@ -2154,7 +2147,7 @@ fn renderStatement(
|
||||
indent: usize,
|
||||
start_col: *usize,
|
||||
base: *ast.Node,
|
||||
) (@TypeOf(stream).Child.Error || Error)!void {
|
||||
) (@TypeOf(stream).Error || Error)!void {
|
||||
switch (base.id) {
|
||||
.VarDecl => {
|
||||
const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base);
|
||||
@ -2193,7 +2186,7 @@ fn renderTokenOffset(
|
||||
start_col: *usize,
|
||||
space: Space,
|
||||
token_skip_bytes: usize,
|
||||
) (@TypeOf(stream).Child.Error || Error)!void {
|
||||
) (@TypeOf(stream).Error || Error)!void {
|
||||
if (space == Space.BlockStart) {
|
||||
if (start_col.* < indent + indent_delta)
|
||||
return renderToken(tree, stream, token_index, indent, start_col, Space.Space);
|
||||
@ -2204,7 +2197,7 @@ fn renderTokenOffset(
|
||||
}
|
||||
|
||||
var token = tree.tokens.at(token_index);
|
||||
try stream.write(mem.trimRight(u8, tree.tokenSlicePtr(token)[token_skip_bytes..], " "));
|
||||
try stream.writeAll(mem.trimRight(u8, tree.tokenSlicePtr(token)[token_skip_bytes..], " "));
|
||||
|
||||
if (space == Space.NoComment)
|
||||
return;
|
||||
@ -2214,15 +2207,15 @@ fn renderTokenOffset(
|
||||
if (space == Space.Comma) switch (next_token.id) {
|
||||
.Comma => return renderToken(tree, stream, token_index + 1, indent, start_col, Space.Newline),
|
||||
.LineComment => {
|
||||
try stream.write(", ");
|
||||
try stream.writeAll(", ");
|
||||
return renderToken(tree, stream, token_index + 1, indent, start_col, Space.Newline);
|
||||
},
|
||||
else => {
|
||||
if (token_index + 2 < tree.tokens.len and tree.tokens.at(token_index + 2).id == .MultilineStringLiteralLine) {
|
||||
try stream.write(",");
|
||||
try stream.writeAll(",");
|
||||
return;
|
||||
} else {
|
||||
try stream.write(",\n");
|
||||
try stream.writeAll(",\n");
|
||||
start_col.* = 0;
|
||||
return;
|
||||
}
|
||||
@ -2246,7 +2239,7 @@ fn renderTokenOffset(
|
||||
if (next_token.id == .MultilineStringLiteralLine) {
|
||||
return;
|
||||
} else {
|
||||
try stream.write("\n");
|
||||
try stream.writeAll("\n");
|
||||
start_col.* = 0;
|
||||
return;
|
||||
}
|
||||
@ -2309,7 +2302,7 @@ fn renderTokenOffset(
|
||||
if (next_token.id == .MultilineStringLiteralLine) {
|
||||
return;
|
||||
} else {
|
||||
try stream.write("\n");
|
||||
try stream.writeAll("\n");
|
||||
start_col.* = 0;
|
||||
return;
|
||||
}
|
||||
@ -2327,7 +2320,7 @@ fn renderTokenOffset(
|
||||
const newline_count = if (loc.line == 1) @as(u8, 1) else @as(u8, 2);
|
||||
try stream.writeByteNTimes('\n', newline_count);
|
||||
try stream.writeByteNTimes(' ', indent);
|
||||
try stream.write(mem.trimRight(u8, tree.tokenSlicePtr(next_token), " "));
|
||||
try stream.writeAll(mem.trimRight(u8, tree.tokenSlicePtr(next_token), " "));
|
||||
|
||||
offset += 1;
|
||||
token = next_token;
|
||||
@ -2338,7 +2331,7 @@ fn renderTokenOffset(
|
||||
if (next_token.id == .MultilineStringLiteralLine) {
|
||||
return;
|
||||
} else {
|
||||
try stream.write("\n");
|
||||
try stream.writeAll("\n");
|
||||
start_col.* = 0;
|
||||
return;
|
||||
}
|
||||
@ -2381,7 +2374,7 @@ fn renderToken(
|
||||
indent: usize,
|
||||
start_col: *usize,
|
||||
space: Space,
|
||||
) (@TypeOf(stream).Child.Error || Error)!void {
|
||||
) (@TypeOf(stream).Error || Error)!void {
|
||||
return renderTokenOffset(tree, stream, token_index, indent, start_col, space, 0);
|
||||
}
|
||||
|
||||
@ -2391,7 +2384,7 @@ fn renderDocComments(
|
||||
node: var,
|
||||
indent: usize,
|
||||
start_col: *usize,
|
||||
) (@TypeOf(stream).Child.Error || Error)!void {
|
||||
) (@TypeOf(stream).Error || Error)!void {
|
||||
const comment = node.doc_comments orelse return;
|
||||
var it = comment.lines.iterator(0);
|
||||
const first_token = node.firstToken();
|
||||
@ -2401,7 +2394,7 @@ fn renderDocComments(
|
||||
try stream.writeByteNTimes(' ', indent);
|
||||
} else {
|
||||
try renderToken(tree, stream, line_token_index.*, indent, start_col, Space.NoComment);
|
||||
try stream.write("\n");
|
||||
try stream.writeAll("\n");
|
||||
try stream.writeByteNTimes(' ', indent);
|
||||
}
|
||||
}
|
||||
@ -2427,27 +2420,23 @@ fn nodeCausesSliceOpSpace(base: *ast.Node) bool {
|
||||
};
|
||||
}
|
||||
|
||||
// An OutStream that returns whether the given character has been written to it.
|
||||
// The contents are not written to anything.
|
||||
/// A `std.io.OutStream` that returns whether the given character has been written to it.
|
||||
/// The contents are not written to anything.
|
||||
const FindByteOutStream = struct {
|
||||
const Self = FindByteOutStream;
|
||||
pub const Error = error{};
|
||||
pub const Stream = std.io.OutStream(Error);
|
||||
|
||||
stream: Stream,
|
||||
byte_found: bool,
|
||||
byte: u8,
|
||||
|
||||
pub fn init(byte: u8) Self {
|
||||
return Self{
|
||||
.stream = Stream{ .writeFn = writeFn },
|
||||
pub const Error = error{};
|
||||
pub const OutStream = std.io.OutStream(*FindByteOutStream, Error, write);
|
||||
|
||||
pub fn init(byte: u8) FindByteOutStream {
|
||||
return FindByteOutStream{
|
||||
.byte = byte,
|
||||
.byte_found = false,
|
||||
};
|
||||
}
|
||||
|
||||
fn writeFn(out_stream: *Stream, bytes: []const u8) Error!usize {
|
||||
const self = @fieldParentPtr(Self, "stream", out_stream);
|
||||
pub fn write(self: *FindByteOutStream, bytes: []const u8) Error!usize {
|
||||
if (self.byte_found) return bytes.len;
|
||||
self.byte_found = blk: {
|
||||
for (bytes) |b|
|
||||
@ -2456,11 +2445,15 @@ const FindByteOutStream = struct {
|
||||
};
|
||||
return bytes.len;
|
||||
}
|
||||
|
||||
pub fn outStream(self: *FindByteOutStream) OutStream {
|
||||
return .{ .context = self };
|
||||
}
|
||||
};
|
||||
|
||||
fn copyFixingWhitespace(stream: var, slice: []const u8) @TypeOf(stream).Child.Error!void {
|
||||
fn copyFixingWhitespace(stream: var, slice: []const u8) @TypeOf(stream).Error!void {
|
||||
for (slice) |byte| switch (byte) {
|
||||
'\t' => try stream.write(" "),
|
||||
'\t' => try stream.writeAll(" "),
|
||||
'\r' => {},
|
||||
else => try stream.writeByte(byte),
|
||||
};
|
||||
|
@ -570,7 +570,7 @@ pub const NativeTargetInfo = struct {
|
||||
cross_target: CrossTarget,
|
||||
) AbiAndDynamicLinkerFromFileError!NativeTargetInfo {
|
||||
var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 align(@alignOf(elf.Elf64_Ehdr)) = undefined;
|
||||
_ = try preadFull(file, &hdr_buf, 0, hdr_buf.len);
|
||||
_ = try preadMin(file, &hdr_buf, 0, hdr_buf.len);
|
||||
const hdr32 = @ptrCast(*elf.Elf32_Ehdr, &hdr_buf);
|
||||
const hdr64 = @ptrCast(*elf.Elf64_Ehdr, &hdr_buf);
|
||||
if (!mem.eql(u8, hdr32.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic;
|
||||
@ -610,7 +610,7 @@ pub const NativeTargetInfo = struct {
|
||||
// Reserve some bytes so that we can deref the 64-bit struct fields
|
||||
// even when the ELF file is 32-bits.
|
||||
const ph_reserve: usize = @sizeOf(elf.Elf64_Phdr) - @sizeOf(elf.Elf32_Phdr);
|
||||
const ph_read_byte_len = try preadFull(file, ph_buf[0 .. ph_buf.len - ph_reserve], phoff, phentsize);
|
||||
const ph_read_byte_len = try preadMin(file, ph_buf[0 .. ph_buf.len - ph_reserve], phoff, phentsize);
|
||||
var ph_buf_i: usize = 0;
|
||||
while (ph_buf_i < ph_read_byte_len and ph_i < phnum) : ({
|
||||
ph_i += 1;
|
||||
@ -625,7 +625,7 @@ pub const NativeTargetInfo = struct {
|
||||
const p_offset = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset);
|
||||
const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz);
|
||||
if (p_filesz > result.dynamic_linker.buffer.len) return error.NameTooLong;
|
||||
_ = try preadFull(file, result.dynamic_linker.buffer[0..p_filesz], p_offset, p_filesz);
|
||||
_ = try preadMin(file, result.dynamic_linker.buffer[0..p_filesz], p_offset, p_filesz);
|
||||
// PT_INTERP includes a null byte in p_filesz.
|
||||
const len = p_filesz - 1;
|
||||
// dynamic_linker.max_byte is "max", not "len".
|
||||
@ -656,7 +656,7 @@ pub const NativeTargetInfo = struct {
|
||||
// Reserve some bytes so that we can deref the 64-bit struct fields
|
||||
// even when the ELF file is 32-bits.
|
||||
const dyn_reserve: usize = @sizeOf(elf.Elf64_Dyn) - @sizeOf(elf.Elf32_Dyn);
|
||||
const dyn_read_byte_len = try preadFull(
|
||||
const dyn_read_byte_len = try preadMin(
|
||||
file,
|
||||
dyn_buf[0 .. dyn_buf.len - dyn_reserve],
|
||||
dyn_off,
|
||||
@ -701,14 +701,14 @@ pub const NativeTargetInfo = struct {
|
||||
var sh_buf: [16 * @sizeOf(elf.Elf64_Shdr)]u8 align(@alignOf(elf.Elf64_Shdr)) = undefined;
|
||||
if (sh_buf.len < shentsize) return error.InvalidElfFile;
|
||||
|
||||
_ = try preadFull(file, &sh_buf, str_section_off, shentsize);
|
||||
_ = try preadMin(file, &sh_buf, str_section_off, shentsize);
|
||||
const shstr32 = @ptrCast(*elf.Elf32_Shdr, @alignCast(@alignOf(elf.Elf32_Shdr), &sh_buf));
|
||||
const shstr64 = @ptrCast(*elf.Elf64_Shdr, @alignCast(@alignOf(elf.Elf64_Shdr), &sh_buf));
|
||||
const shstrtab_off = elfInt(is_64, need_bswap, shstr32.sh_offset, shstr64.sh_offset);
|
||||
const shstrtab_size = elfInt(is_64, need_bswap, shstr32.sh_size, shstr64.sh_size);
|
||||
var strtab_buf: [4096:0]u8 = undefined;
|
||||
const shstrtab_len = std.math.min(shstrtab_size, strtab_buf.len);
|
||||
const shstrtab_read_len = try preadFull(file, &strtab_buf, shstrtab_off, shstrtab_len);
|
||||
const shstrtab_read_len = try preadMin(file, &strtab_buf, shstrtab_off, shstrtab_len);
|
||||
const shstrtab = strtab_buf[0..shstrtab_read_len];
|
||||
|
||||
const shnum = elfInt(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum);
|
||||
@ -717,7 +717,7 @@ pub const NativeTargetInfo = struct {
|
||||
// Reserve some bytes so that we can deref the 64-bit struct fields
|
||||
// even when the ELF file is 32-bits.
|
||||
const sh_reserve: usize = @sizeOf(elf.Elf64_Shdr) - @sizeOf(elf.Elf32_Shdr);
|
||||
const sh_read_byte_len = try preadFull(
|
||||
const sh_read_byte_len = try preadMin(
|
||||
file,
|
||||
sh_buf[0 .. sh_buf.len - sh_reserve],
|
||||
shoff,
|
||||
@ -751,7 +751,7 @@ pub const NativeTargetInfo = struct {
|
||||
|
||||
if (dynstr) |ds| {
|
||||
const strtab_len = std.math.min(ds.size, strtab_buf.len);
|
||||
const strtab_read_len = try preadFull(file, &strtab_buf, ds.offset, shstrtab_len);
|
||||
const strtab_read_len = try preadMin(file, &strtab_buf, ds.offset, shstrtab_len);
|
||||
const strtab = strtab_buf[0..strtab_read_len];
|
||||
// TODO this pointer cast should not be necessary
|
||||
const rpath_list = mem.toSliceConst(u8, @ptrCast([*:0]u8, strtab[rpoff..].ptr));
|
||||
@ -813,7 +813,7 @@ pub const NativeTargetInfo = struct {
|
||||
return result;
|
||||
}
|
||||
|
||||
fn preadFull(file: fs.File, buf: []u8, offset: u64, min_read_len: usize) !usize {
|
||||
fn preadMin(file: fs.File, buf: []u8, offset: u64, min_read_len: usize) !usize {
|
||||
var i: u64 = 0;
|
||||
while (i < min_read_len) {
|
||||
const len = file.pread(buf[i .. buf.len - i], offset + i) catch |err| switch (err) {
|
||||
@ -853,7 +853,7 @@ pub const NativeTargetInfo = struct {
|
||||
abi: Target.Abi,
|
||||
};
|
||||
|
||||
fn elfInt(is_64: bool, need_bswap: bool, int_32: var, int_64: var) @TypeOf(int_64) {
|
||||
pub fn elfInt(is_64: bool, need_bswap: bool, int_32: var, int_64: var) @TypeOf(int_64) {
|
||||
if (is_64) {
|
||||
if (need_bswap) {
|
||||
return @byteSwap(@TypeOf(int_64), int_64);
|
||||
|
@ -38,7 +38,7 @@ pub const LibCInstallation = struct {
|
||||
pub fn parse(
|
||||
allocator: *Allocator,
|
||||
libc_file: []const u8,
|
||||
stderr: *std.io.OutStream(fs.File.WriteError),
|
||||
stderr: var,
|
||||
) !LibCInstallation {
|
||||
var self: LibCInstallation = .{};
|
||||
|
||||
@ -123,7 +123,7 @@ pub const LibCInstallation = struct {
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn render(self: LibCInstallation, out: *std.io.OutStream(fs.File.WriteError)) !void {
|
||||
pub fn render(self: LibCInstallation, out: var) !void {
|
||||
@setEvalBranchQuota(4000);
|
||||
const include_dir = self.include_dir orelse "";
|
||||
const sys_include_dir = self.sys_include_dir orelse "";
|
||||
@ -348,7 +348,7 @@ pub const LibCInstallation = struct {
|
||||
|
||||
for (searches) |search| {
|
||||
result_buf.shrink(0);
|
||||
const stream = &std.io.BufferOutStream.init(&result_buf).stream;
|
||||
const stream = result_buf.outStream();
|
||||
try stream.print("{}\\Include\\{}\\ucrt", .{ search.path, search.version });
|
||||
|
||||
var dir = fs.cwd().openDirList(result_buf.toSliceConst()) catch |err| switch (err) {
|
||||
@ -395,7 +395,7 @@ pub const LibCInstallation = struct {
|
||||
|
||||
for (searches) |search| {
|
||||
result_buf.shrink(0);
|
||||
const stream = &std.io.BufferOutStream.init(&result_buf).stream;
|
||||
const stream = result_buf.outStream();
|
||||
try stream.print("{}\\Lib\\{}\\ucrt\\{}", .{ search.path, search.version, arch_sub_dir });
|
||||
|
||||
var dir = fs.cwd().openDirList(result_buf.toSliceConst()) catch |err| switch (err) {
|
||||
@ -459,7 +459,7 @@ pub const LibCInstallation = struct {
|
||||
|
||||
for (searches) |search| {
|
||||
result_buf.shrink(0);
|
||||
const stream = &std.io.BufferOutStream.init(&result_buf).stream;
|
||||
const stream = result_buf.outStream();
|
||||
try stream.print("{}\\Lib\\{}\\um\\{}", .{ search.path, search.version, arch_sub_dir });
|
||||
|
||||
var dir = fs.cwd().openDirList(result_buf.toSliceConst()) catch |err| switch (err) {
|
||||
|
@ -52,7 +52,7 @@ const available_libcs = [_][]const u8{
|
||||
"sparc-linux-gnu",
|
||||
"sparcv9-linux-gnu",
|
||||
"wasm32-freestanding-musl",
|
||||
"x86_64-linux-gnu (native)",
|
||||
"x86_64-linux-gnu",
|
||||
"x86_64-linux-gnux32",
|
||||
"x86_64-linux-musl",
|
||||
"x86_64-windows-gnu",
|
||||
@ -61,7 +61,8 @@ const available_libcs = [_][]const u8{
|
||||
pub fn cmdTargets(
|
||||
allocator: *Allocator,
|
||||
args: []const []const u8,
|
||||
stdout: *io.OutStream(fs.File.WriteError),
|
||||
/// Output stream
|
||||
stdout: var,
|
||||
native_target: Target,
|
||||
) !void {
|
||||
const available_glibcs = blk: {
|
||||
@ -92,9 +93,9 @@ pub fn cmdTargets(
|
||||
};
|
||||
defer allocator.free(available_glibcs);
|
||||
|
||||
const BOS = io.BufferedOutStream(fs.File.WriteError);
|
||||
var bos = BOS.init(stdout);
|
||||
var jws = std.json.WriteStream(BOS.Stream, 6).init(&bos.stream);
|
||||
var bos = io.bufferedOutStream(stdout);
|
||||
const bos_stream = bos.outStream();
|
||||
var jws = std.json.WriteStream(@TypeOf(bos_stream), 6).init(bos_stream);
|
||||
|
||||
try jws.beginObject();
|
||||
|
||||
@ -219,6 +220,6 @@ pub fn cmdTargets(
|
||||
|
||||
try jws.endObject();
|
||||
|
||||
try bos.stream.writeByte('\n');
|
||||
try bos_stream.writeByte('\n');
|
||||
return bos.flush();
|
||||
}
|
||||
|
@ -18,8 +18,8 @@ const assert = std.debug.assert;
|
||||
const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
|
||||
|
||||
var stderr_file: fs.File = undefined;
|
||||
var stderr: *io.OutStream(fs.File.WriteError) = undefined;
|
||||
var stdout: *io.OutStream(fs.File.WriteError) = undefined;
|
||||
var stderr: fs.File.OutStream = undefined;
|
||||
var stdout: fs.File.OutStream = undefined;
|
||||
|
||||
comptime {
|
||||
_ = @import("dep_tokenizer.zig");
|
||||
@ -146,7 +146,7 @@ export fn stage2_free_clang_errors(errors_ptr: [*]translate_c.ClangErrMsg, error
|
||||
}
|
||||
|
||||
export fn stage2_render_ast(tree: *ast.Tree, output_file: *FILE) Error {
|
||||
const c_out_stream = &std.io.COutStream.init(output_file).stream;
|
||||
const c_out_stream = std.io.cOutStream(output_file);
|
||||
_ = std.zig.render(std.heap.c_allocator, c_out_stream, tree) catch |e| switch (e) {
|
||||
error.WouldBlock => unreachable, // stage1 opens stuff in exclusively blocking mode
|
||||
error.SystemResources => return .SystemResources,
|
||||
@ -186,9 +186,9 @@ fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void {
|
||||
try args_list.append(mem.toSliceConst(u8, argv[arg_i]));
|
||||
}
|
||||
|
||||
stdout = &std.io.getStdOut().outStream().stream;
|
||||
stdout = std.io.getStdOut().outStream();
|
||||
stderr_file = std.io.getStdErr();
|
||||
stderr = &stderr_file.outStream().stream;
|
||||
stderr = stderr_file.outStream();
|
||||
|
||||
const args = args_list.toSliceConst()[2..];
|
||||
|
||||
@ -203,11 +203,11 @@ fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void {
|
||||
const arg = args[i];
|
||||
if (mem.startsWith(u8, arg, "-")) {
|
||||
if (mem.eql(u8, arg, "--help")) {
|
||||
try stdout.write(self_hosted_main.usage_fmt);
|
||||
try stdout.writeAll(self_hosted_main.usage_fmt);
|
||||
process.exit(0);
|
||||
} else if (mem.eql(u8, arg, "--color")) {
|
||||
if (i + 1 >= args.len) {
|
||||
try stderr.write("expected [auto|on|off] after --color\n");
|
||||
try stderr.writeAll("expected [auto|on|off] after --color\n");
|
||||
process.exit(1);
|
||||
}
|
||||
i += 1;
|
||||
@ -238,14 +238,14 @@ fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void {
|
||||
|
||||
if (stdin_flag) {
|
||||
if (input_files.len != 0) {
|
||||
try stderr.write("cannot use --stdin with positional arguments\n");
|
||||
try stderr.writeAll("cannot use --stdin with positional arguments\n");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const stdin_file = io.getStdIn();
|
||||
var stdin = stdin_file.inStream();
|
||||
|
||||
const source_code = try stdin.stream.readAllAlloc(allocator, self_hosted_main.max_src_size);
|
||||
const source_code = try stdin.readAllAlloc(allocator, self_hosted_main.max_src_size);
|
||||
defer allocator.free(source_code);
|
||||
|
||||
const tree = std.zig.parse(allocator, source_code) catch |err| {
|
||||
@ -272,7 +272,7 @@ fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void {
|
||||
}
|
||||
|
||||
if (input_files.len == 0) {
|
||||
try stderr.write("expected at least one source file argument\n");
|
||||
try stderr.writeAll("expected at least one source file argument\n");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@ -409,11 +409,11 @@ fn printErrMsgToFile(
|
||||
const end_loc = tree.tokenLocationPtr(first_token.end, last_token);
|
||||
|
||||
var text_buf = try std.Buffer.initSize(allocator, 0);
|
||||
var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
|
||||
const out_stream = &text_buf.outStream();
|
||||
try parse_error.render(&tree.tokens, out_stream);
|
||||
const text = text_buf.toOwnedSlice();
|
||||
|
||||
const stream = &file.outStream().stream;
|
||||
const stream = &file.outStream();
|
||||
try stream.print("{}:{}:{}: error: {}\n", .{ path, start_loc.line + 1, start_loc.column + 1, text });
|
||||
|
||||
if (!color_on) return;
|
||||
@ -641,7 +641,7 @@ fn cmdTargets(zig_triple: [*:0]const u8) !void {
|
||||
return @import("print_targets.zig").cmdTargets(
|
||||
std.heap.c_allocator,
|
||||
&[0][]u8{},
|
||||
&std.io.getStdOut().outStream().stream,
|
||||
std.io.getStdOut().outStream(),
|
||||
target,
|
||||
);
|
||||
}
|
||||
@ -808,7 +808,7 @@ const Stage2LibCInstallation = extern struct {
|
||||
// ABI warning
|
||||
export fn stage2_libc_parse(stage1_libc: *Stage2LibCInstallation, libc_file_z: [*:0]const u8) Error {
|
||||
stderr_file = std.io.getStdErr();
|
||||
stderr = &stderr_file.outStream().stream;
|
||||
stderr = stderr_file.outStream();
|
||||
const libc_file = mem.toSliceConst(u8, libc_file_z);
|
||||
var libc = LibCInstallation.parse(std.heap.c_allocator, libc_file, stderr) catch |err| switch (err) {
|
||||
error.ParseError => return .SemanticAnalyzeFail,
|
||||
@ -870,7 +870,7 @@ export fn stage2_libc_find_native(stage1_libc: *Stage2LibCInstallation) Error {
|
||||
// ABI warning
|
||||
export fn stage2_libc_render(stage1_libc: *Stage2LibCInstallation, output_file: *FILE) Error {
|
||||
var libc = stage1_libc.toStage2();
|
||||
const c_out_stream = &std.io.COutStream.init(output_file).stream;
|
||||
const c_out_stream = std.io.cOutStream(output_file);
|
||||
libc.render(c_out_stream) catch |err| switch (err) {
|
||||
error.WouldBlock => unreachable, // stage1 opens stuff in exclusively blocking mode
|
||||
error.SystemResources => return .SystemResources,
|
||||
|
@ -22,7 +22,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||
\\
|
||||
\\pub fn main() void {
|
||||
\\ privateFunction();
|
||||
\\ const stdout = &getStdOut().outStream().stream;
|
||||
\\ const stdout = getStdOut().outStream();
|
||||
\\ stdout.print("OK 2\n", .{}) catch unreachable;
|
||||
\\}
|
||||
\\
|
||||
@ -37,7 +37,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||
\\// purposefully conflicting function with main.zig
|
||||
\\// but it's private so it should be OK
|
||||
\\fn privateFunction() void {
|
||||
\\ const stdout = &getStdOut().outStream().stream;
|
||||
\\ const stdout = getStdOut().outStream();
|
||||
\\ stdout.print("OK 1\n", .{}) catch unreachable;
|
||||
\\}
|
||||
\\
|
||||
@ -63,7 +63,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||
tc.addSourceFile("foo.zig",
|
||||
\\usingnamespace @import("std").io;
|
||||
\\pub fn foo_function() void {
|
||||
\\ const stdout = &getStdOut().outStream().stream;
|
||||
\\ const stdout = getStdOut().outStream();
|
||||
\\ stdout.print("OK\n", .{}) catch unreachable;
|
||||
\\}
|
||||
);
|
||||
@ -74,7 +74,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||
\\
|
||||
\\pub fn bar_function() void {
|
||||
\\ if (foo_function()) {
|
||||
\\ const stdout = &getStdOut().outStream().stream;
|
||||
\\ const stdout = getStdOut().outStream();
|
||||
\\ stdout.print("OK\n", .{}) catch unreachable;
|
||||
\\ }
|
||||
\\}
|
||||
@ -106,7 +106,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||
\\pub const a_text = "OK\n";
|
||||
\\
|
||||
\\pub fn ok() void {
|
||||
\\ const stdout = &io.getStdOut().outStream().stream;
|
||||
\\ const stdout = io.getStdOut().outStream();
|
||||
\\ stdout.print(b_text, .{}) catch unreachable;
|
||||
\\}
|
||||
);
|
||||
@ -124,7 +124,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||
\\const io = @import("std").io;
|
||||
\\
|
||||
\\pub fn main() void {
|
||||
\\ const stdout = &io.getStdOut().outStream().stream;
|
||||
\\ const stdout = io.getStdOut().outStream();
|
||||
\\ stdout.print("Hello, world!\n{d:4} {x:3} {c}\n", .{@as(u32, 12), @as(u16, 0x12), @as(u8, 'a')}) catch unreachable;
|
||||
\\}
|
||||
, "Hello, world!\n 12 12 a\n");
|
||||
@ -267,7 +267,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||
\\ var x_local : i32 = print_ok(x);
|
||||
\\}
|
||||
\\fn print_ok(val: @TypeOf(x)) @TypeOf(foo) {
|
||||
\\ const stdout = &io.getStdOut().outStream().stream;
|
||||
\\ const stdout = io.getStdOut().outStream();
|
||||
\\ stdout.print("OK\n", .{}) catch unreachable;
|
||||
\\ return 0;
|
||||
\\}
|
||||
@ -349,7 +349,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||
\\pub fn main() void {
|
||||
\\ const bar = Bar {.field2 = 13,};
|
||||
\\ const foo = Foo {.field1 = bar,};
|
||||
\\ const stdout = &io.getStdOut().outStream().stream;
|
||||
\\ const stdout = io.getStdOut().outStream();
|
||||
\\ if (!foo.method()) {
|
||||
\\ stdout.print("BAD\n", .{}) catch unreachable;
|
||||
\\ }
|
||||
@ -363,7 +363,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||
cases.add("defer with only fallthrough",
|
||||
\\const io = @import("std").io;
|
||||
\\pub fn main() void {
|
||||
\\ const stdout = &io.getStdOut().outStream().stream;
|
||||
\\ const stdout = io.getStdOut().outStream();
|
||||
\\ stdout.print("before\n", .{}) catch unreachable;
|
||||
\\ defer stdout.print("defer1\n", .{}) catch unreachable;
|
||||
\\ defer stdout.print("defer2\n", .{}) catch unreachable;
|
||||
@ -376,7 +376,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||
\\const io = @import("std").io;
|
||||
\\const os = @import("std").os;
|
||||
\\pub fn main() void {
|
||||
\\ const stdout = &io.getStdOut().outStream().stream;
|
||||
\\ const stdout = io.getStdOut().outStream();
|
||||
\\ stdout.print("before\n", .{}) catch unreachable;
|
||||
\\ defer stdout.print("defer1\n", .{}) catch unreachable;
|
||||
\\ defer stdout.print("defer2\n", .{}) catch unreachable;
|
||||
@ -393,7 +393,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||
\\ do_test() catch return;
|
||||
\\}
|
||||
\\fn do_test() !void {
|
||||
\\ const stdout = &io.getStdOut().outStream().stream;
|
||||
\\ const stdout = io.getStdOut().outStream();
|
||||
\\ stdout.print("before\n", .{}) catch unreachable;
|
||||
\\ defer stdout.print("defer1\n", .{}) catch unreachable;
|
||||
\\ errdefer stdout.print("deferErr\n", .{}) catch unreachable;
|
||||
@ -412,7 +412,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||
\\ do_test() catch return;
|
||||
\\}
|
||||
\\fn do_test() !void {
|
||||
\\ const stdout = &io.getStdOut().outStream().stream;
|
||||
\\ const stdout = io.getStdOut().outStream();
|
||||
\\ stdout.print("before\n", .{}) catch unreachable;
|
||||
\\ defer stdout.print("defer1\n", .{}) catch unreachable;
|
||||
\\ errdefer stdout.print("deferErr\n", .{}) catch unreachable;
|
||||
@ -429,7 +429,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||
\\const io = @import("std").io;
|
||||
\\
|
||||
\\pub fn main() void {
|
||||
\\ const stdout = &io.getStdOut().outStream().stream;
|
||||
\\ const stdout = io.getStdOut().outStream();
|
||||
\\ stdout.print(foo_txt, .{}) catch unreachable;
|
||||
\\}
|
||||
, "1234\nabcd\n");
|
||||
@ -448,9 +448,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||
\\
|
||||
\\pub fn main() !void {
|
||||
\\ var args_it = std.process.args();
|
||||
\\ var stdout_file = io.getStdOut();
|
||||
\\ var stdout_adapter = stdout_file.outStream();
|
||||
\\ const stdout = &stdout_adapter.stream;
|
||||
\\ const stdout = io.getStdOut().outStream();
|
||||
\\ var index: usize = 0;
|
||||
\\ _ = args_it.skip();
|
||||
\\ while (args_it.next(allocator)) |arg_or_err| : (index += 1) {
|
||||
@ -489,9 +487,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||
\\
|
||||
\\pub fn main() !void {
|
||||
\\ var args_it = std.process.args();
|
||||
\\ var stdout_file = io.getStdOut();
|
||||
\\ var stdout_adapter = stdout_file.outStream();
|
||||
\\ const stdout = &stdout_adapter.stream;
|
||||
\\ const stdout = io.getStdOut().outStream();
|
||||
\\ var index: usize = 0;
|
||||
\\ _ = args_it.skip();
|
||||
\\ while (args_it.next(allocator)) |arg_or_err| : (index += 1) {
|
||||
|
@ -4,7 +4,7 @@ const io = std.io;
|
||||
const fmt = std.fmt;
|
||||
|
||||
pub fn main() !void {
|
||||
const stdout = &io.getStdOut().outStream().stream;
|
||||
const stdout = io.getStdOut().outStream();
|
||||
const stdin = io.getStdIn();
|
||||
|
||||
try stdout.print("Welcome to the Guess Number Game in Zig.\n", .{});
|
||||
|
@ -566,12 +566,9 @@ pub const StackTracesContext = struct {
|
||||
}
|
||||
child.spawn() catch |err| debug.panic("Unable to spawn {}: {}\n", .{ full_exe_path, @errorName(err) });
|
||||
|
||||
var stdout_file_in_stream = child.stdout.?.inStream();
|
||||
var stderr_file_in_stream = child.stderr.?.inStream();
|
||||
|
||||
const stdout = stdout_file_in_stream.stream.readAllAlloc(b.allocator, max_stdout_size) catch unreachable;
|
||||
const stdout = child.stdout.?.inStream().readAllAlloc(b.allocator, max_stdout_size) catch unreachable;
|
||||
defer b.allocator.free(stdout);
|
||||
const stderr = stderr_file_in_stream.stream.readAllAlloc(b.allocator, max_stdout_size) catch unreachable;
|
||||
const stderr = child.stderr.?.inStream().readAllAlloc(b.allocator, max_stdout_size) catch unreachable;
|
||||
defer b.allocator.free(stderr);
|
||||
|
||||
const term = child.wait() catch |err| {
|
||||
@ -798,11 +795,8 @@ pub const CompileErrorContext = struct {
|
||||
var stdout_buf = Buffer.initNull(b.allocator);
|
||||
var stderr_buf = Buffer.initNull(b.allocator);
|
||||
|
||||
var stdout_file_in_stream = child.stdout.?.inStream();
|
||||
var stderr_file_in_stream = child.stderr.?.inStream();
|
||||
|
||||
stdout_file_in_stream.stream.readAllBuffer(&stdout_buf, max_stdout_size) catch unreachable;
|
||||
stderr_file_in_stream.stream.readAllBuffer(&stderr_buf, max_stdout_size) catch unreachable;
|
||||
child.stdout.?.inStream().readAllBuffer(&stdout_buf, max_stdout_size) catch unreachable;
|
||||
child.stderr.?.inStream().readAllBuffer(&stderr_buf, max_stdout_size) catch unreachable;
|
||||
|
||||
const term = child.wait() catch |err| {
|
||||
debug.panic("Unable to spawn {}: {}\n", .{ zig_args.items[0], @errorName(err) });
|
||||
|
Loading…
x
Reference in New Issue
Block a user