Skip to content

Commit

Permalink
Merge branch 'daurnimator-less-buffer'
Browse files Browse the repository at this point in the history
Closes #4405
Closes #4656
  • Loading branch information
andrewrk committed Mar 6, 2020
2 parents fa46bcb + 231a4b8 commit 7f975bf
Show file tree
Hide file tree
Showing 11 changed files with 165 additions and 131 deletions.
1 change: 0 additions & 1 deletion build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ const BufMap = std.BufMap;
const warn = std.debug.warn;
const mem = std.mem;
const ArrayList = std.ArrayList;
const Buffer = std.Buffer;
const io = std.io;
const fs = std.fs;
const InstallDirectoryOptions = std.build.InstallDirectoryOptions;
Expand Down
82 changes: 48 additions & 34 deletions lib/std/array_list.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@ const testing = std.testing;
const mem = std.mem;
const Allocator = mem.Allocator;

/// List of items.
///
/// This is a wrapper around an array of T values. Initialize with
/// `init`.
/// A contiguous, growable list of items in memory.
/// This is a wrapper around an array of T values. Initialize with `init`.
pub fn ArrayList(comptime T: type) type {
return AlignedArrayList(T, null);
}
Expand All @@ -22,7 +20,7 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type {
return struct {
const Self = @This();

/// Use toSlice instead of slicing this directly, because if you don't
/// Use `span` instead of slicing this directly, because if you don't
/// specify the end position of the slice, this will potentially give
/// you uninitialized memory.
items: Slice,
Expand Down Expand Up @@ -56,34 +54,37 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type {

/// Return contents as a slice. Only valid while the list
/// doesn't change size.
pub fn toSlice(self: Self) Slice {
pub fn span(self: var) @TypeOf(self.items[0..self.len]) {
return self.items[0..self.len];
}

/// Return list as const slice. Only valid while the list
/// doesn't change size.
/// Deprecated: use `span`.
pub fn toSlice(self: Self) Slice {
return self.span();
}

/// Deprecated: use `span`.
pub fn toSliceConst(self: Self) SliceConst {
return self.items[0..self.len];
return self.span();
}

/// Safely access index i of the list.
/// Deprecated: use `span()[i]`.
pub fn at(self: Self, i: usize) T {
return self.toSliceConst()[i];
return self.span()[i];
}

/// Safely access ptr to index i of the list.
/// Deprecated: use `&span()[i]`.
pub fn ptrAt(self: Self, i: usize) *T {
return &self.toSlice()[i];
return &self.span()[i];
}

/// Sets the value at index `i`, or returns `error.OutOfBounds` if
/// the index is not in range.
/// Deprecated: use `if (i >= list.len) return error.OutOfBounds else span()[i] = item`.
pub fn setOrError(self: Self, i: usize, item: T) !void {
if (i >= self.len) return error.OutOfBounds;
self.items[i] = item;
}

/// Sets the value at index `i`, asserting that the value is in range.
/// Deprecated: use `list.span()[i] = item`.
pub fn set(self: *Self, i: usize, item: T) void {
assert(i < self.len);
self.items[i] = item;
Expand Down Expand Up @@ -124,18 +125,18 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type {
self.items[n] = item;
}

/// Insert slice `items` at index `n`. Moves
/// `list[n .. list.len]` to make room.
pub fn insertSlice(self: *Self, n: usize, items: SliceConst) !void {
/// Insert slice `items` at index `i`. Moves
/// `list[i .. list.len]` to make room.
/// This operation is O(N).
pub fn insertSlice(self: *Self, i: usize, items: SliceConst) !void {
try self.ensureCapacity(self.len + items.len);
self.len += items.len;

mem.copyBackwards(T, self.items[n + items.len .. self.len], self.items[n .. self.len - items.len]);
mem.copy(T, self.items[n .. n + items.len], items);
mem.copyBackwards(T, self.items[i + items.len .. self.len], self.items[i .. self.len - items.len]);
mem.copy(T, self.items[i .. i + items.len], items);
}

/// Extend the list by 1 element. Allocates more memory as
/// necessary.
/// Extend the list by 1 element. Allocates more memory as necessary.
pub fn append(self: *Self, item: T) !void {
const new_item_ptr = try self.addOne();
new_item_ptr.* = item;
Expand All @@ -148,8 +149,9 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type {
new_item_ptr.* = item;
}

/// Remove the element at index `i` from the list and return
/// its value. Asserts the array has at least one item.
/// Remove the element at index `i` from the list and return its value.
/// Asserts the array has at least one item.
/// This operation is O(N).
pub fn orderedRemove(self: *Self, i: usize) T {
const newlen = self.len - 1;
if (newlen == i) return self.pop();
Expand All @@ -163,18 +165,17 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type {

/// Removes the element at the specified index and returns it.
/// The empty slot is filled from the end of the list.
/// This operation is O(1).
pub fn swapRemove(self: *Self, i: usize) T {
if (self.len - 1 == i) return self.pop();

const slice = self.toSlice();
const slice = self.span();
const old_item = slice[i];
slice[i] = self.pop();
return old_item;
}

/// Removes the element at the specified index and returns it
/// or an error.OutOfBounds is returned. If no error then
/// the empty slot is filled from the end of the list.
/// Deprecated: use `if (i >= list.len) return error.OutOfBounds else list.swapRemove(i)`.
pub fn swapRemoveOrError(self: *Self, i: usize) !T {
if (i >= self.len) return error.OutOfBounds;
return self.swapRemove(i);
Expand Down Expand Up @@ -204,6 +205,7 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type {
}

/// Reduce allocated capacity to `new_len`.
/// Invalidates element pointers.
pub fn shrink(self: *Self, new_len: usize) void {
assert(new_len <= self.len);
self.len = new_len;
Expand All @@ -222,28 +224,40 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type {
self.items = try self.allocator.realloc(self.items, better_capacity);
}

/// Increases the array's length to match the full capacity that is already allocated.
/// The new elements have `undefined` values. This operation does not invalidate any
/// element pointers.
pub fn expandToCapacity(self: *Self) void {
self.len = self.items.len;
}

/// Increase length by 1, returning pointer to the new item.
/// The returned pointer becomes invalid when the list is resized.
pub fn addOne(self: *Self) !*T {
const new_length = self.len + 1;
try self.ensureCapacity(new_length);
return self.addOneAssumeCapacity();
}

/// Increase length by 1, returning pointer to the new item.
/// Asserts that there is already space for the new item without allocating more.
/// The returned pointer becomes invalid when the list is resized.
pub fn addOneAssumeCapacity(self: *Self) *T {
assert(self.len < self.capacity());
const result = &self.items[self.len];
self.len += 1;
return result;
}

/// Remove and return the last element from the list. Asserts
/// the list has at least one item.
/// Remove and return the last element from the list.
/// Asserts the list has at least one item.
pub fn pop(self: *Self) T {
self.len -= 1;
return self.items[self.len];
}

/// Like `pop` but returns `null` if empty.
/// Remove and return the last element from the list.
/// If the list is empty, returns `null`.
pub fn popOrNull(self: *Self) ?T {
if (self.len == 0) return null;
return self.pop();
Expand Down Expand Up @@ -287,7 +301,7 @@ test "std.ArrayList.basic" {
}
}

for (list.toSlice()) |v, i| {
for (list.span()) |v, i| {
testing.expect(v == @intCast(i32, i + 1));
}

Expand Down Expand Up @@ -325,7 +339,7 @@ test "std.ArrayList.appendNTimes" {

try list.appendNTimes(2, 10);
testing.expectEqual(@as(usize, 10), list.len);
for (list.toSlice()) |element| {
for (list.span()) |element| {
testing.expectEqual(@as(i32, 2), element);
}
}
Expand Down
10 changes: 8 additions & 2 deletions lib/std/buffer.zig
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,18 @@ pub const Buffer = struct {
self.list.deinit();
}

pub fn span(self: var) @TypeOf(self.list.items[0 .. self.list.len - 1 :0]) {
return self.list.span()[0..self.len() :0];
}

/// Deprecated: use `span`
pub fn toSlice(self: Buffer) [:0]u8 {
return self.list.toSlice()[0..self.len() :0];
return self.span();
}

/// Deprecated: use `span`
pub fn toSliceConst(self: Buffer) [:0]const u8 {
return self.list.toSliceConst()[0..self.len() :0];
return self.span();
}

pub fn shrink(self: *Buffer, new_len: usize) void {
Expand Down
8 changes: 3 additions & 5 deletions lib/std/build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -926,11 +926,9 @@ pub const Builder = struct {

try child.spawn();

var stdout = std.Buffer.initNull(self.allocator);
defer std.Buffer.deinit(&stdout);

var stdout_file_in_stream = child.stdout.?.inStream();
try stdout_file_in_stream.stream.readAllBuffer(&stdout, max_output_size);
const stdout = try stdout_file_in_stream.stream.readAllAlloc(self.allocator, max_output_size);
errdefer self.allocator.free(stdout);

const term = try child.wait();
switch (term) {
Expand All @@ -939,7 +937,7 @@ pub const Builder = struct {
out_code.* = @truncate(u8, code);
return error.ExitCodeFailure;
}
return stdout.toOwnedSlice();
return stdout;
},
.Signal, .Stopped, .Unknown => |code| {
out_code.* = @truncate(u8, code);
Expand Down
32 changes: 17 additions & 15 deletions lib/std/build/run.zig
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ const mem = std.mem;
const process = std.process;
const ArrayList = std.ArrayList;
const BufMap = std.BufMap;
const Buffer = std.Buffer;
const warn = std.debug.warn;

const max_stdout_size = 1 * 1024 * 1024; // 1 MiB
Expand Down Expand Up @@ -169,23 +168,26 @@ pub const RunStep = struct {
return err;
};

var stdout = Buffer.initNull(self.builder.allocator);
var stderr = Buffer.initNull(self.builder.allocator);

// TODO need to poll to read these streams to prevent a deadlock (or rely on evented I/O).

var stdout: ?[]const u8 = null;
defer if (stdout) |s| self.builder.allocator.free(s);

switch (self.stdout_action) {
.expect_exact, .expect_matches => {
var stdout_file_in_stream = child.stdout.?.inStream();
stdout_file_in_stream.stream.readAllBuffer(&stdout, max_stdout_size) catch unreachable;
stdout = stdout_file_in_stream.stream.readAllAlloc(self.builder.allocator, max_stdout_size) catch unreachable;
},
.inherit, .ignore => {},
}

switch (self.stdout_action) {
var stderr: ?[]const u8 = null;
defer if (stderr) |s| self.builder.allocator.free(s);

switch (self.stderr_action) {
.expect_exact, .expect_matches => {
var stderr_file_in_stream = child.stderr.?.inStream();
stderr_file_in_stream.stream.readAllBuffer(&stderr, max_stdout_size) catch unreachable;
stderr = stderr_file_in_stream.stream.readAllAlloc(self.builder.allocator, max_stdout_size) catch unreachable;
},
.inherit, .ignore => {},
}
Expand Down Expand Up @@ -216,29 +218,29 @@ pub const RunStep = struct {
switch (self.stderr_action) {
.inherit, .ignore => {},
.expect_exact => |expected_bytes| {
if (!mem.eql(u8, expected_bytes, stderr.toSliceConst())) {
if (!mem.eql(u8, expected_bytes, stderr.?)) {
warn(
\\
\\========= Expected this stderr: =========
\\{}
\\========= But found: ====================
\\{}
\\
, .{ expected_bytes, stderr.toSliceConst() });
, .{ expected_bytes, stderr.? });
printCmd(cwd, argv);
return error.TestFailed;
}
},
.expect_matches => |matches| for (matches) |match| {
if (mem.indexOf(u8, stderr.toSliceConst(), match) == null) {
if (mem.indexOf(u8, stderr.?, match) == null) {
warn(
\\
\\========= Expected to find in stderr: =========
\\{}
\\========= But stderr does not contain it: =====
\\{}
\\
, .{ match, stderr.toSliceConst() });
, .{ match, stderr.? });
printCmd(cwd, argv);
return error.TestFailed;
}
Expand All @@ -248,29 +250,29 @@ pub const RunStep = struct {
switch (self.stdout_action) {
.inherit, .ignore => {},
.expect_exact => |expected_bytes| {
if (!mem.eql(u8, expected_bytes, stdout.toSliceConst())) {
if (!mem.eql(u8, expected_bytes, stdout.?)) {
warn(
\\
\\========= Expected this stdout: =========
\\{}
\\========= But found: ====================
\\{}
\\
, .{ expected_bytes, stdout.toSliceConst() });
, .{ expected_bytes, stdout.? });
printCmd(cwd, argv);
return error.TestFailed;
}
},
.expect_matches => |matches| for (matches) |match| {
if (mem.indexOf(u8, stdout.toSliceConst(), match) == null) {
if (mem.indexOf(u8, stdout.?, match) == null) {
warn(
\\
\\========= Expected to find in stdout: =========
\\{}
\\========= But stdout does not contain it: =====
\\{}
\\
, .{ match, stdout.toSliceConst() });
, .{ match, stdout.? });
printCmd(cwd, argv);
return error.TestFailed;
}
Expand Down
16 changes: 7 additions & 9 deletions lib/std/child_process.zig
Original file line number Diff line number Diff line change
Expand Up @@ -217,21 +217,19 @@ pub const ChildProcess = struct {

try child.spawn();

var stdout = Buffer.initNull(args.allocator);
var stderr = Buffer.initNull(args.allocator);
defer Buffer.deinit(&stdout);
defer Buffer.deinit(&stderr);

var stdout_file_in_stream = child.stdout.?.inStream();
var stderr_file_in_stream = child.stderr.?.inStream();

try stdout_file_in_stream.stream.readAllBuffer(&stdout, args.max_output_bytes);
try stderr_file_in_stream.stream.readAllBuffer(&stderr, args.max_output_bytes);
// 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);
errdefer args.allocator.free(stdout);
const stderr = try stderr_file_in_stream.stream.readAllAlloc(args.allocator, args.max_output_bytes);
errdefer args.allocator.free(stderr);

return ExecResult{
.term = try child.wait(),
.stdout = stdout.toOwnedSlice(),
.stderr = stderr.toOwnedSlice(),
.stdout = stdout,
.stderr = stderr,
};
}

Expand Down
Loading

0 comments on commit 7f975bf

Please sign in to comment.