From 431d76c0233271de6a30aa626ccb1553dc241722 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 11 Mar 2020 15:40:34 -0400 Subject: [PATCH] add std.io.StreamSource and fixes to emitRaw --- lib/std/build/emit_raw.zig | 4 +- lib/std/elf.zig | 24 ++++---- lib/std/io.zig | 2 + lib/std/io/fixed_buffer_stream.zig | 41 +++++++------- lib/std/io/stream_source.zig | 90 ++++++++++++++++++++++++++++++ 5 files changed, 127 insertions(+), 34 deletions(-) create mode 100644 lib/std/io/stream_source.zig diff --git a/lib/std/build/emit_raw.zig b/lib/std/build/emit_raw.zig index b56305403c..8b4747e8a7 100644 --- a/lib/std/build/emit_raw.zig +++ b/lib/std/build/emit_raw.zig @@ -48,8 +48,6 @@ const BinaryElfOutput = struct { }; const elf_hdrs = try std.elf.readAllHeaders(allocator, elf_file); - var binaryElfOutput = BinaryElfOutput.init(arena_allocator); - for (elf_hdrs.section_headers) |section, i| { if (sectionValidForOutput(section)) { const newSection = try allocator.create(BinaryElfSection); @@ -164,7 +162,7 @@ fn emitRaw(allocator: *Allocator, elf_path: []const u8, raw_path: []const u8) !v var out_file = try fs.cwd().createFile(raw_path, .{}); defer out_file.close(); - const binary_elf_output = try BinaryElfOutput.parse(allocator, elf_file); + var binary_elf_output = try BinaryElfOutput.parse(allocator, elf_file); defer binary_elf_output.deinit(); for (binary_elf_output.sections.toSlice()) |section| { diff --git a/lib/std/elf.zig b/lib/std/elf.zig index 5cb3c566af..127dbe8a31 100644 --- a/lib/std/elf.zig +++ b/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; @@ -352,7 +352,7 @@ pub fn readHeader(file: File) !Header { 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 = switch (hdr32.e_ident[EI_DATA]) { + const endian: std.builtin.Endian = switch (hdr32.e_ident[EI_DATA]) { ELFDATA2LSB => .Little, ELFDATA2MSB => .Big, else => return error.InvalidElfEndian, @@ -406,8 +406,8 @@ pub fn readAllHeaders(allocator: *mem.Allocator, file: File) !AllHeaders { // Treat section headers and program headers as byte buffers. For 32-bit ELF and // non-matching endian files, we post-process to correct integer endianness and offsets. - const shdr_buf = std.mem.sliceToBytes(hdrs.section_headers)[0 .. hdrs.header.shentsize * hdrs.header.shnum]; - const phdr_buf = std.mem.sliceToBytes(hdrs.program_headers)[0 .. hdrs.header.phentsize * hdrs.header.phnum]; + const shdr_buf = std.mem.sliceAsBytes(hdrs.section_headers)[0 .. hdrs.header.shentsize * hdrs.header.shnum]; + const phdr_buf = std.mem.sliceAsBytes(hdrs.program_headers)[0 .. hdrs.header.phentsize * hdrs.header.phnum]; try preadNoEof(file, shdr_buf, hdrs.header.shoff); try preadNoEof(file, phdr_buf, hdrs.header.phoff); @@ -430,14 +430,14 @@ pub fn readAllHeaders(allocator: *mem.Allocator, file: File) !AllHeaders { } for (hdrs.program_headers) |*phdr, i| { phdr.* = .{ - .p_type = int(is_64, need_bswap, phdrs32[i].p_type, shdr.p_type), - .p_offset = int(is_64, need_bswap, phdrs32[i].p_offset, shdr.p_offset), - .p_vaddr = int(is_64, need_bswap, phdrs32[i].p_vaddr, shdr.p_vaddr), - .p_paddr = int(is_64, need_bswap, phdrs32[i].p_paddr, shdr.p_paddr), - .p_filesz = int(is_64, need_bswap, phdrs32[i].p_filesz, shdr.p_filesz), - .p_memsz = int(is_64, need_bswap, phdrs32[i].p_memsz, shdr.p_memsz), - .p_flags = int(is_64, need_bswap, phdrs32[i].p_flags, shdr.p_flags), - .p_align = int(is_64, need_bswap, phdrs32[i].p_align, shdr.p_align), + .p_type = int(is_64, need_bswap, phdrs32[i].p_type, phdr.p_type), + .p_offset = int(is_64, need_bswap, phdrs32[i].p_offset, phdr.p_offset), + .p_vaddr = int(is_64, need_bswap, phdrs32[i].p_vaddr, phdr.p_vaddr), + .p_paddr = int(is_64, need_bswap, phdrs32[i].p_paddr, phdr.p_paddr), + .p_filesz = int(is_64, need_bswap, phdrs32[i].p_filesz, phdr.p_filesz), + .p_memsz = int(is_64, need_bswap, phdrs32[i].p_memsz, phdr.p_memsz), + .p_flags = int(is_64, need_bswap, phdrs32[i].p_flags, phdr.p_flags), + .p_align = int(is_64, need_bswap, phdrs32[i].p_align, phdr.p_align), }; } return hdrs; diff --git a/lib/std/io.zig b/lib/std/io.zig index 22af5c06b1..243dd5ca95 100644 --- a/lib/std/io.zig +++ b/lib/std/io.zig @@ -126,6 +126,8 @@ pub const deserializer = @import("io/serialization.zig").deserializer; pub const BufferedAtomicFile = @import("io/buffered_atomic_file.zig").BufferedAtomicFile; +pub const StreamSource = @import("io/stream_source.zig").StreamSource; + /// Deprecated; use `std.fs.Dir.writeFile`. pub fn writeFile(path: []const u8, data: []const u8) !void { return fs.cwd().writeFile(path, data); diff --git a/lib/std/io/fixed_buffer_stream.zig b/lib/std/io/fixed_buffer_stream.zig index 0f2b542201..cc4b9c0193 100644 --- a/lib/std/io/fixed_buffer_stream.zig +++ b/lib/std/io/fixed_buffer_stream.zig @@ -12,9 +12,9 @@ pub fn FixedBufferStream(comptime Buffer: type) type { buffer: Buffer, pos: usize, - pub const ReadError = error{EndOfStream}; - pub const WriteError = error{OutOfMemory}; - pub const SeekError = error{EndOfStream}; + 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); @@ -51,16 +51,16 @@ pub fn FixedBufferStream(comptime Buffer: type) type { mem.copy(u8, dest[0..size], self.buffer[self.pos..end]); self.pos = end; - if (size == 0) return error.EndOfStream; return size; } /// If the returned number of bytes written is less than requested, the - /// buffer is full. Returns `error.OutOfMemory` when no bytes would be written. + /// 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; - - assert(self.pos <= self.buffer.len); + if (self.pos >= self.buffer.len) return error.NoSpaceLeft; const n = if (self.pos + bytes.len <= self.buffer.len) bytes.len @@ -70,26 +70,27 @@ pub fn FixedBufferStream(comptime Buffer: type) type { mem.copy(u8, self.buffer[self.pos .. self.pos + n], bytes[0..n]); self.pos += n; - if (n == 0) return error.OutOfMemory; + if (n == 0) return error.NoSpaceLeft; return n; } pub fn seekTo(self: *Self, pos: u64) SeekError!void { - const usize_pos = std.math.cast(usize, pos) catch return error.EndOfStream; - if (usize_pos > self.buffer.len) return error.EndOfStream; + const usize_pos = std.math.cast(usize, pos) catch std.math.maxInt(usize); self.pos = usize_pos; } pub fn seekBy(self: *Self, amt: i64) SeekError!void { if (amt < 0) { - const abs_amt = std.math.cast(usize, -amt) catch return error.EndOfStream; - if (abs_amt > self.pos) return error.EndOfStream; - self.pos -= abs_amt; + const abs_amt = std.math.cast(usize, -amt) catch std.math.maxInt(usize); + if (abs_amt > self.pos) { + self.pos = 0; + } else { + self.pos -= abs_amt; + } } else { - const usize_amt = std.math.cast(usize, amt) catch return error.EndOfStream; - if (self.pos + usize_amt > self.buffer.len) return error.EndOfStream; - self.pos += usize_amt; + const usize_amt = std.math.cast(usize, amt) catch std.math.maxInt(usize); + self.pos = std.math.add(usize, self.pos, usize_amt) catch std.math.maxInt(usize); } } @@ -101,6 +102,7 @@ pub fn FixedBufferStream(comptime Buffer: type) type { return self.pos; } + /// Asserts that the seek pos is within the buffer range. pub fn getWritten(self: Self) []const u8 { return self.buffer[0..self.pos]; } @@ -140,13 +142,13 @@ test "FixedBufferStream output 2" { try fbs.outStream().writeAll("world"); testing.expect(mem.eql(u8, fbs.getWritten(), "Helloworld")); - testing.expectError(error.OutOfMemory, fbs.outStream().writeAll("!")); + 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.OutOfMemory, fbs.outStream().writeAll("Hello world!")); + testing.expectError(error.NoSpaceLeft, fbs.outStream().writeAll("Hello world!")); testing.expect(mem.eql(u8, fbs.getWritten(), "Hello worl")); } @@ -164,5 +166,6 @@ test "FixedBufferStream input" { testing.expect(read == 3); testing.expect(mem.eql(u8, dest[0..3], bytes[4..7])); - testing.expectError(error.EndOfStream, fbs.inStream().read(dest[0..4])); + read = try fbs.inStream().read(dest[0..4]); + testing.expect(read == 0); } diff --git a/lib/std/io/stream_source.zig b/lib/std/io/stream_source.zig new file mode 100644 index 0000000000..7e39554337 --- /dev/null +++ b/lib/std/io/stream_source.zig @@ -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 }; + } +};