Commit 431d76c023

Andrew Kelley <andrew@ziglang.org>
2020-03-11 20:40:34
add std.io.StreamSource and fixes to emitRaw
1 parent c71991c
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| {
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);
 }
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 };
+    }
+};
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;
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);