diff --git a/CMakeLists.txt b/CMakeLists.txt index 2a93144d1d49eb9d56b4ded0f1af587390e79c15..6ec48c438267b72d2c2b8d2341175cbd6022cbf4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -434,7 +434,6 @@ set(ZIG_STAGE2_SOURCES lib/std/dwarf/OP.zig lib/std/dwarf/TAG.zig lib/std/elf.zig - lib/std/fifo.zig lib/std/fmt.zig lib/std/fmt/parse_float.zig lib/std/fs.zig diff --git a/lib/compiler/resinator/compile.zig b/lib/compiler/resinator/compile.zig index 1f35af898856ef07f4bd2bdff067d5b8d6169141..ab4784d9b0046848056880dd191ae8e6a7d826b9 100644 --- a/lib/compiler/resinator/compile.zig +++ b/lib/compiler/resinator/compile.zig @@ -1269,9 +1269,7 @@ pub const Compiler = struct { pub fn writeResourceDataNoPadding(writer: anytype, data_reader: anytype, data_size: u32) !void { var limited_reader = std.io.limitedReader(data_reader, data_size); - const FifoBuffer = std.fifo.LinearFifo(u8, .{ .Static = 4096 }); - var fifo = FifoBuffer.init(); - try fifo.pump(limited_reader.reader(), writer); + try limited_reader.reader().readRemaining(writer); } pub fn writeResourceData(writer: anytype, data_reader: anytype, data_size: u32) !void { diff --git a/lib/std/fifo.zig b/lib/std/fifo.zig deleted file mode 100644 index 5fa50dc739980c1497b16a732c53b1bb953c7b50..0000000000000000000000000000000000000000 --- a/lib/std/fifo.zig +++ /dev/null @@ -1,587 +0,0 @@ -// FIFO of fixed size items -// Usually used for e.g. byte buffers - -const std = @import("std"); -const math = std.math; -const mem = std.mem; -const Allocator = mem.Allocator; -const assert = std.debug.assert; -const testing = std.testing; - -pub const LinearFifoBufferType = union(enum) { - /// The buffer is internal to the fifo; it is of the specified size. - Static: usize, - - /// The buffer is passed as a slice to the initialiser. - Slice, - - /// The buffer is managed dynamically using a `mem.Allocator`. - Dynamic, -}; - -pub fn LinearFifo( - comptime T: type, - comptime buffer_type: LinearFifoBufferType, -) type { - const autoalign = false; - - const powers_of_two = switch (buffer_type) { - .Static => std.math.isPowerOfTwo(buffer_type.Static), - .Slice => false, // Any size slice could be passed in - .Dynamic => true, // This could be configurable in future - }; - - return struct { - allocator: if (buffer_type == .Dynamic) Allocator else void, - buf: if (buffer_type == .Static) [buffer_type.Static]T else []T, - head: usize, - count: usize, - - const Self = @This(); - - // Type of Self argument for slice operations. - // If buffer is inline (Static) then we need to ensure we haven't - // returned a slice into a copy on the stack - const SliceSelfArg = if (buffer_type == .Static) *Self else Self; - - pub const init = switch (buffer_type) { - .Static => initStatic, - .Slice => initSlice, - .Dynamic => initDynamic, - }; - - fn initStatic() Self { - comptime assert(buffer_type == .Static); - return .{ - .allocator = {}, - .buf = undefined, - .head = 0, - .count = 0, - }; - } - - fn initSlice(buf: []T) Self { - comptime assert(buffer_type == .Slice); - return .{ - .allocator = {}, - .buf = buf, - .head = 0, - .count = 0, - }; - } - - fn initDynamic(allocator: Allocator) Self { - comptime assert(buffer_type == .Dynamic); - return .{ - .allocator = allocator, - .buf = &.{}, - .head = 0, - .count = 0, - }; - } - - pub fn deinit(self: Self) void { - if (buffer_type == .Dynamic) self.allocator.free(self.buf); - } - - pub fn realign(self: *Self) void { - if (self.buf.len - self.head >= self.count) { - mem.copyForwards(T, self.buf[0..self.count], self.buf[self.head..][0..self.count]); - self.head = 0; - } else { - var tmp: [4096 / 2 / @sizeOf(T)]T = undefined; - - while (self.head != 0) { - const n = @min(self.head, tmp.len); - const m = self.buf.len - n; - @memcpy(tmp[0..n], self.buf[0..n]); - mem.copyForwards(T, self.buf[0..m], self.buf[n..][0..m]); - @memcpy(self.buf[m..][0..n], tmp[0..n]); - self.head -= n; - } - } - { // set unused area to undefined - const unused = mem.sliceAsBytes(self.buf[self.count..]); - @memset(unused, undefined); - } - } - - /// Reduce allocated capacity to `size`. - pub fn shrink(self: *Self, size: usize) void { - assert(size >= self.count); - if (buffer_type == .Dynamic) { - self.realign(); - self.buf = self.allocator.realloc(self.buf, size) catch |e| switch (e) { - error.OutOfMemory => return, // no problem, capacity is still correct then. - }; - } - } - - /// Ensure that the buffer can fit at least `size` items - pub fn ensureTotalCapacity(self: *Self, size: usize) !void { - if (self.buf.len >= size) return; - if (buffer_type == .Dynamic) { - self.realign(); - const new_size = if (powers_of_two) math.ceilPowerOfTwo(usize, size) catch return error.OutOfMemory else size; - self.buf = try self.allocator.realloc(self.buf, new_size); - } else { - return error.OutOfMemory; - } - } - - /// Makes sure at least `size` items are unused - pub fn ensureUnusedCapacity(self: *Self, size: usize) error{OutOfMemory}!void { - if (self.writableLength() >= size) return; - - return try self.ensureTotalCapacity(math.add(usize, self.count, size) catch return error.OutOfMemory); - } - - /// Returns number of items currently in fifo - pub fn readableLength(self: Self) usize { - return self.count; - } - - /// Returns a writable slice from the 'read' end of the fifo - fn readableSliceMut(self: SliceSelfArg, offset: usize) []T { - if (offset > self.count) return &[_]T{}; - - var start = self.head + offset; - if (start >= self.buf.len) { - start -= self.buf.len; - return self.buf[start .. start + (self.count - offset)]; - } else { - const end = @min(self.head + self.count, self.buf.len); - return self.buf[start..end]; - } - } - - /// Returns a readable slice from `offset` - pub fn readableSlice(self: SliceSelfArg, offset: usize) []const T { - return self.readableSliceMut(offset); - } - - pub fn readableSliceOfLen(self: *Self, len: usize) []const T { - assert(len <= self.count); - const buf = self.readableSlice(0); - if (buf.len >= len) { - return buf[0..len]; - } else { - self.realign(); - return self.readableSlice(0)[0..len]; - } - } - - /// Discard first `count` items in the fifo - pub fn discard(self: *Self, count: usize) void { - assert(count <= self.count); - { // set old range to undefined. Note: may be wrapped around - const slice = self.readableSliceMut(0); - if (slice.len >= count) { - const unused = mem.sliceAsBytes(slice[0..count]); - @memset(unused, undefined); - } else { - const unused = mem.sliceAsBytes(slice[0..]); - @memset(unused, undefined); - const unused2 = mem.sliceAsBytes(self.readableSliceMut(slice.len)[0 .. count - slice.len]); - @memset(unused2, undefined); - } - } - if (autoalign and self.count == count) { - self.head = 0; - self.count = 0; - } else { - var head = self.head + count; - if (powers_of_two) { - // Note it is safe to do a wrapping subtract as - // bitwise & with all 1s is a noop - head &= self.buf.len -% 1; - } else { - head %= self.buf.len; - } - self.head = head; - self.count -= count; - } - } - - /// Read the next item from the fifo - pub fn readItem(self: *Self) ?T { - if (self.count == 0) return null; - - const c = self.buf[self.head]; - self.discard(1); - return c; - } - - /// Read data from the fifo into `dst`, returns number of items copied. - pub fn read(self: *Self, dst: []T) usize { - var dst_left = dst; - - while (dst_left.len > 0) { - const slice = self.readableSlice(0); - if (slice.len == 0) break; - const n = @min(slice.len, dst_left.len); - @memcpy(dst_left[0..n], slice[0..n]); - self.discard(n); - dst_left = dst_left[n..]; - } - - return dst.len - dst_left.len; - } - - /// Same as `read` except it returns an error union - /// The purpose of this function existing is to match `std.io.Reader` API. - fn readFn(self: *Self, dest: []u8) error{}!usize { - return self.read(dest); - } - - pub fn reader(self: *Self) std.io.Reader { - return .{ - .context = self, - .vtable = &.{ - .read = &readerRead, - .readVec = &readerReadVec, - .discard = &readerDiscard, - }, - }; - } - fn readerRead( - ctx: ?*anyopaque, - bw: *std.io.BufferedWriter, - limit: std.io.Limit, - ) std.io.Reader.RwError!usize { - const fifo: *Self = @alignCast(@ptrCast(ctx)); - _ = fifo; - _ = bw; - _ = limit; - @panic("TODO"); - } - fn readerReadVec(ctx: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { - const fifo: *Self = @alignCast(@ptrCast(ctx)); - _ = fifo; - _ = data; - @panic("TODO"); - } - fn readerDiscard(ctx: ?*anyopaque, limit: std.io.Limit) std.io.Reader.Error!usize { - const fifo: *Self = @alignCast(@ptrCast(ctx)); - _ = fifo; - _ = limit; - @panic("TODO"); - } - - /// Returns number of items available in fifo - pub fn writableLength(self: Self) usize { - return self.buf.len - self.count; - } - - /// Returns the first section of writable buffer. - /// Note that this may be of length 0 - pub fn writableSlice(self: SliceSelfArg, offset: usize) []T { - if (offset > self.buf.len) return &[_]T{}; - - const tail = self.head + offset + self.count; - if (tail < self.buf.len) { - return self.buf[tail..]; - } else { - return self.buf[tail - self.buf.len ..][0 .. self.writableLength() - offset]; - } - } - - /// Returns a writable buffer of at least `size` items, allocating memory as needed. - /// Use `fifo.update` once you've written data to it. - pub fn writableWithSize(self: *Self, size: usize) ![]T { - try self.ensureUnusedCapacity(size); - - // try to avoid realigning buffer - var slice = self.writableSlice(0); - if (slice.len < size) { - self.realign(); - slice = self.writableSlice(0); - } - return slice; - } - - /// Update the tail location of the buffer (usually follows use of writable/writableWithSize) - pub fn update(self: *Self, count: usize) void { - assert(self.count + count <= self.buf.len); - self.count += count; - } - - /// Appends the data in `src` to the fifo. - /// You must have ensured there is enough space. - pub fn writeAssumeCapacity(self: *Self, src: []const T) void { - assert(self.writableLength() >= src.len); - - var src_left = src; - while (src_left.len > 0) { - const writable_slice = self.writableSlice(0); - assert(writable_slice.len != 0); - const n = @min(writable_slice.len, src_left.len); - @memcpy(writable_slice[0..n], src_left[0..n]); - self.update(n); - src_left = src_left[n..]; - } - } - - /// Write a single item to the fifo - pub fn writeItem(self: *Self, item: T) !void { - try self.ensureUnusedCapacity(1); - return self.writeItemAssumeCapacity(item); - } - - pub fn writeItemAssumeCapacity(self: *Self, item: T) void { - var tail = self.head + self.count; - if (powers_of_two) { - tail &= self.buf.len - 1; - } else { - tail %= self.buf.len; - } - self.buf[tail] = item; - self.update(1); - } - - /// Appends the data in `src` to the fifo. - /// Allocates more memory as necessary - pub fn write(self: *Self, src: []const T) !void { - try self.ensureUnusedCapacity(src.len); - - return self.writeAssumeCapacity(src); - } - - /// Same as `write` except it returns the number of bytes written, which is always the same - /// as `bytes.len`. The purpose of this function existing is to match `std.io.Writer` API. - fn appendWrite(self: *Self, bytes: []const u8) error{OutOfMemory}!usize { - try self.write(bytes); - return bytes.len; - } - - pub fn writer(fifo: *Self) std.io.Writer { - return .{ - .context = fifo, - .vtable = &.{ - .writeSplat = writerWriteSplat, - .writeFile = writerWriteFile, - }, - }; - } - fn writerWriteSplat(ctx: ?*anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Error!usize { - const fifo: *Self = @alignCast(@ptrCast(ctx)); - _ = fifo; - _ = data; - _ = splat; - @panic("TODO"); - } - fn writerWriteFile( - ctx: ?*anyopaque, - file: std.fs.File, - offset: std.io.Writer.Offset, - limit: std.io.Writer.Limit, - headers_and_trailers: []const []const u8, - headers_len: usize, - ) std.io.Writer.Error!usize { - const fifo: *Self = @alignCast(@ptrCast(ctx)); - _ = fifo; - _ = file; - _ = offset; - _ = limit; - _ = headers_and_trailers; - _ = headers_len; - @panic("TODO"); - } - - /// Make `count` items available before the current read location - fn rewind(self: *Self, count: usize) void { - assert(self.writableLength() >= count); - - var head = self.head + (self.buf.len - count); - if (powers_of_two) { - head &= self.buf.len - 1; - } else { - head %= self.buf.len; - } - self.head = head; - self.count += count; - } - - /// Place data back into the read stream - pub fn unget(self: *Self, src: []const T) !void { - try self.ensureUnusedCapacity(src.len); - - self.rewind(src.len); - - const slice = self.readableSliceMut(0); - if (src.len < slice.len) { - @memcpy(slice[0..src.len], src); - } else { - @memcpy(slice, src[0..slice.len]); - const slice2 = self.readableSliceMut(slice.len); - @memcpy(slice2[0 .. src.len - slice.len], src[slice.len..]); - } - } - - /// Returns the item at `offset`. - /// Asserts offset is within bounds. - pub fn peekItem(self: Self, offset: usize) T { - assert(offset < self.count); - - var index = self.head + offset; - if (powers_of_two) { - index &= self.buf.len - 1; - } else { - index %= self.buf.len; - } - return self.buf[index]; - } - - pub fn toOwnedSlice(self: *Self) Allocator.Error![]T { - if (self.head != 0) self.realign(); - assert(self.head == 0); - assert(self.count <= self.buf.len); - const allocator = self.allocator; - if (allocator.resize(self.buf, self.count)) { - const result = self.buf[0..self.count]; - self.* = Self.init(allocator); - return result; - } - const new_memory = try allocator.dupe(T, self.buf[0..self.count]); - allocator.free(self.buf); - self.* = Self.init(allocator); - return new_memory; - } - }; -} - -test "LinearFifo(u8, .Dynamic) discard(0) from empty buffer should not error on overflow" { - var fifo = LinearFifo(u8, .Dynamic).init(testing.allocator); - defer fifo.deinit(); - - // If overflow is not explicitly allowed this will crash in debug / safe mode - fifo.discard(0); -} - -test "LinearFifo(u8, .Dynamic)" { - var fifo = LinearFifo(u8, .Dynamic).init(testing.allocator); - defer fifo.deinit(); - - try fifo.write("HELLO"); - try testing.expectEqual(@as(usize, 5), fifo.readableLength()); - try testing.expectEqualSlices(u8, "HELLO", fifo.readableSlice(0)); - - { - var i: usize = 0; - while (i < 5) : (i += 1) { - try fifo.write(&[_]u8{fifo.peekItem(i)}); - } - try testing.expectEqual(@as(usize, 10), fifo.readableLength()); - try testing.expectEqualSlices(u8, "HELLOHELLO", fifo.readableSlice(0)); - } - - { - try testing.expectEqual(@as(u8, 'H'), fifo.readItem().?); - try testing.expectEqual(@as(u8, 'E'), fifo.readItem().?); - try testing.expectEqual(@as(u8, 'L'), fifo.readItem().?); - try testing.expectEqual(@as(u8, 'L'), fifo.readItem().?); - try testing.expectEqual(@as(u8, 'O'), fifo.readItem().?); - } - try testing.expectEqual(@as(usize, 5), fifo.readableLength()); - - { // Writes that wrap around - try testing.expectEqual(@as(usize, 11), fifo.writableLength()); - try testing.expectEqual(@as(usize, 6), fifo.writableSlice(0).len); - fifo.writeAssumeCapacity("6 FifoType.init(), - .Slice => FifoType.init(buf[0..]), - .Dynamic => FifoType.init(testing.allocator), - }; - defer fifo.deinit(); - - try fifo.write(&[_]T{ 0, 1, 1, 0, 1 }); - try testing.expectEqual(@as(usize, 5), fifo.readableLength()); - - { - try testing.expectEqual(@as(T, 0), fifo.readItem().?); - try testing.expectEqual(@as(T, 1), fifo.readItem().?); - try testing.expectEqual(@as(T, 1), fifo.readItem().?); - try testing.expectEqual(@as(T, 0), fifo.readItem().?); - try testing.expectEqual(@as(T, 1), fifo.readItem().?); - try testing.expectEqual(@as(usize, 0), fifo.readableLength()); - } - - { - try fifo.writeItem(1); - try fifo.writeItem(1); - try fifo.writeItem(1); - try testing.expectEqual(@as(usize, 3), fifo.readableLength()); - } - - { - var readBuf: [3]T = undefined; - const n = fifo.read(&readBuf); - try testing.expectEqual(@as(usize, 3), n); // NOTE: It should be the number of items. - } - } - } -} diff --git a/lib/std/std.zig b/lib/std/std.zig index 7c18485ab9cf8c2fe74ba123301737bd065f98d9..73a29ed28e02845bbba494bd88b5c6b02bec3a5d 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -58,7 +58,6 @@ pub const debug = @import("debug.zig"); pub const dwarf = @import("dwarf.zig"); pub const elf = @import("elf.zig"); pub const enums = @import("enums.zig"); -pub const fifo = @import("fifo.zig"); pub const fmt = @import("fmt.zig"); pub const fs = @import("fs.zig"); pub const gpu = @import("gpu.zig"); diff --git a/src/Compilation.zig b/src/Compilation.zig index bbcdc4b83aa91d53795b62ded41e6701741157a1..7e650c4ab71bd13626dde10f938b33c0eabe3a9a 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -44,6 +44,8 @@ const Builtin = @import("Builtin.zig"); const LlvmObject = @import("codegen/llvm.zig").Object; const dev = @import("dev.zig"); +const DeprecatedLinearFifo = @import("deprecated.zig").LinearFifo; + pub const Config = @import("Compilation/Config.zig"); /// General-purpose allocator. Used for both temporary and long-term storage. @@ -121,15 +123,15 @@ work_queues: [ } break :len len; } -]std.fifo.LinearFifo(Job, .Dynamic), +]DeprecatedLinearFifo(Job), /// These jobs are to invoke the Clang compiler to create an object file, which /// gets linked with the Compilation. -c_object_work_queue: std.fifo.LinearFifo(*CObject, .Dynamic), +c_object_work_queue: DeprecatedLinearFifo(*CObject), /// These jobs are to invoke the RC compiler to create a compiled resource file (.res), which /// gets linked with the Compilation. -win32_resource_work_queue: if (dev.env.supports(.win32_resource)) std.fifo.LinearFifo(*Win32Resource, .Dynamic) else struct { +win32_resource_work_queue: if (dev.env.supports(.win32_resource)) DeprecatedLinearFifo(*Win32Resource) else struct { pub fn ensureUnusedCapacity(_: @This(), _: u0) error{}!void {} pub fn readItem(_: @This()) ?noreturn { return null; diff --git a/src/deprecated.zig b/src/deprecated.zig new file mode 100644 index 0000000000000000000000000000000000000000..5fa50dc739980c1497b16a732c53b1bb953c7b50 --- /dev/null +++ b/src/deprecated.zig @@ -0,0 +1,587 @@ +// FIFO of fixed size items +// Usually used for e.g. byte buffers + +const std = @import("std"); +const math = std.math; +const mem = std.mem; +const Allocator = mem.Allocator; +const assert = std.debug.assert; +const testing = std.testing; + +pub const LinearFifoBufferType = union(enum) { + /// The buffer is internal to the fifo; it is of the specified size. + Static: usize, + + /// The buffer is passed as a slice to the initialiser. + Slice, + + /// The buffer is managed dynamically using a `mem.Allocator`. + Dynamic, +}; + +pub fn LinearFifo( + comptime T: type, + comptime buffer_type: LinearFifoBufferType, +) type { + const autoalign = false; + + const powers_of_two = switch (buffer_type) { + .Static => std.math.isPowerOfTwo(buffer_type.Static), + .Slice => false, // Any size slice could be passed in + .Dynamic => true, // This could be configurable in future + }; + + return struct { + allocator: if (buffer_type == .Dynamic) Allocator else void, + buf: if (buffer_type == .Static) [buffer_type.Static]T else []T, + head: usize, + count: usize, + + const Self = @This(); + + // Type of Self argument for slice operations. + // If buffer is inline (Static) then we need to ensure we haven't + // returned a slice into a copy on the stack + const SliceSelfArg = if (buffer_type == .Static) *Self else Self; + + pub const init = switch (buffer_type) { + .Static => initStatic, + .Slice => initSlice, + .Dynamic => initDynamic, + }; + + fn initStatic() Self { + comptime assert(buffer_type == .Static); + return .{ + .allocator = {}, + .buf = undefined, + .head = 0, + .count = 0, + }; + } + + fn initSlice(buf: []T) Self { + comptime assert(buffer_type == .Slice); + return .{ + .allocator = {}, + .buf = buf, + .head = 0, + .count = 0, + }; + } + + fn initDynamic(allocator: Allocator) Self { + comptime assert(buffer_type == .Dynamic); + return .{ + .allocator = allocator, + .buf = &.{}, + .head = 0, + .count = 0, + }; + } + + pub fn deinit(self: Self) void { + if (buffer_type == .Dynamic) self.allocator.free(self.buf); + } + + pub fn realign(self: *Self) void { + if (self.buf.len - self.head >= self.count) { + mem.copyForwards(T, self.buf[0..self.count], self.buf[self.head..][0..self.count]); + self.head = 0; + } else { + var tmp: [4096 / 2 / @sizeOf(T)]T = undefined; + + while (self.head != 0) { + const n = @min(self.head, tmp.len); + const m = self.buf.len - n; + @memcpy(tmp[0..n], self.buf[0..n]); + mem.copyForwards(T, self.buf[0..m], self.buf[n..][0..m]); + @memcpy(self.buf[m..][0..n], tmp[0..n]); + self.head -= n; + } + } + { // set unused area to undefined + const unused = mem.sliceAsBytes(self.buf[self.count..]); + @memset(unused, undefined); + } + } + + /// Reduce allocated capacity to `size`. + pub fn shrink(self: *Self, size: usize) void { + assert(size >= self.count); + if (buffer_type == .Dynamic) { + self.realign(); + self.buf = self.allocator.realloc(self.buf, size) catch |e| switch (e) { + error.OutOfMemory => return, // no problem, capacity is still correct then. + }; + } + } + + /// Ensure that the buffer can fit at least `size` items + pub fn ensureTotalCapacity(self: *Self, size: usize) !void { + if (self.buf.len >= size) return; + if (buffer_type == .Dynamic) { + self.realign(); + const new_size = if (powers_of_two) math.ceilPowerOfTwo(usize, size) catch return error.OutOfMemory else size; + self.buf = try self.allocator.realloc(self.buf, new_size); + } else { + return error.OutOfMemory; + } + } + + /// Makes sure at least `size` items are unused + pub fn ensureUnusedCapacity(self: *Self, size: usize) error{OutOfMemory}!void { + if (self.writableLength() >= size) return; + + return try self.ensureTotalCapacity(math.add(usize, self.count, size) catch return error.OutOfMemory); + } + + /// Returns number of items currently in fifo + pub fn readableLength(self: Self) usize { + return self.count; + } + + /// Returns a writable slice from the 'read' end of the fifo + fn readableSliceMut(self: SliceSelfArg, offset: usize) []T { + if (offset > self.count) return &[_]T{}; + + var start = self.head + offset; + if (start >= self.buf.len) { + start -= self.buf.len; + return self.buf[start .. start + (self.count - offset)]; + } else { + const end = @min(self.head + self.count, self.buf.len); + return self.buf[start..end]; + } + } + + /// Returns a readable slice from `offset` + pub fn readableSlice(self: SliceSelfArg, offset: usize) []const T { + return self.readableSliceMut(offset); + } + + pub fn readableSliceOfLen(self: *Self, len: usize) []const T { + assert(len <= self.count); + const buf = self.readableSlice(0); + if (buf.len >= len) { + return buf[0..len]; + } else { + self.realign(); + return self.readableSlice(0)[0..len]; + } + } + + /// Discard first `count` items in the fifo + pub fn discard(self: *Self, count: usize) void { + assert(count <= self.count); + { // set old range to undefined. Note: may be wrapped around + const slice = self.readableSliceMut(0); + if (slice.len >= count) { + const unused = mem.sliceAsBytes(slice[0..count]); + @memset(unused, undefined); + } else { + const unused = mem.sliceAsBytes(slice[0..]); + @memset(unused, undefined); + const unused2 = mem.sliceAsBytes(self.readableSliceMut(slice.len)[0 .. count - slice.len]); + @memset(unused2, undefined); + } + } + if (autoalign and self.count == count) { + self.head = 0; + self.count = 0; + } else { + var head = self.head + count; + if (powers_of_two) { + // Note it is safe to do a wrapping subtract as + // bitwise & with all 1s is a noop + head &= self.buf.len -% 1; + } else { + head %= self.buf.len; + } + self.head = head; + self.count -= count; + } + } + + /// Read the next item from the fifo + pub fn readItem(self: *Self) ?T { + if (self.count == 0) return null; + + const c = self.buf[self.head]; + self.discard(1); + return c; + } + + /// Read data from the fifo into `dst`, returns number of items copied. + pub fn read(self: *Self, dst: []T) usize { + var dst_left = dst; + + while (dst_left.len > 0) { + const slice = self.readableSlice(0); + if (slice.len == 0) break; + const n = @min(slice.len, dst_left.len); + @memcpy(dst_left[0..n], slice[0..n]); + self.discard(n); + dst_left = dst_left[n..]; + } + + return dst.len - dst_left.len; + } + + /// Same as `read` except it returns an error union + /// The purpose of this function existing is to match `std.io.Reader` API. + fn readFn(self: *Self, dest: []u8) error{}!usize { + return self.read(dest); + } + + pub fn reader(self: *Self) std.io.Reader { + return .{ + .context = self, + .vtable = &.{ + .read = &readerRead, + .readVec = &readerReadVec, + .discard = &readerDiscard, + }, + }; + } + fn readerRead( + ctx: ?*anyopaque, + bw: *std.io.BufferedWriter, + limit: std.io.Limit, + ) std.io.Reader.RwError!usize { + const fifo: *Self = @alignCast(@ptrCast(ctx)); + _ = fifo; + _ = bw; + _ = limit; + @panic("TODO"); + } + fn readerReadVec(ctx: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { + const fifo: *Self = @alignCast(@ptrCast(ctx)); + _ = fifo; + _ = data; + @panic("TODO"); + } + fn readerDiscard(ctx: ?*anyopaque, limit: std.io.Limit) std.io.Reader.Error!usize { + const fifo: *Self = @alignCast(@ptrCast(ctx)); + _ = fifo; + _ = limit; + @panic("TODO"); + } + + /// Returns number of items available in fifo + pub fn writableLength(self: Self) usize { + return self.buf.len - self.count; + } + + /// Returns the first section of writable buffer. + /// Note that this may be of length 0 + pub fn writableSlice(self: SliceSelfArg, offset: usize) []T { + if (offset > self.buf.len) return &[_]T{}; + + const tail = self.head + offset + self.count; + if (tail < self.buf.len) { + return self.buf[tail..]; + } else { + return self.buf[tail - self.buf.len ..][0 .. self.writableLength() - offset]; + } + } + + /// Returns a writable buffer of at least `size` items, allocating memory as needed. + /// Use `fifo.update` once you've written data to it. + pub fn writableWithSize(self: *Self, size: usize) ![]T { + try self.ensureUnusedCapacity(size); + + // try to avoid realigning buffer + var slice = self.writableSlice(0); + if (slice.len < size) { + self.realign(); + slice = self.writableSlice(0); + } + return slice; + } + + /// Update the tail location of the buffer (usually follows use of writable/writableWithSize) + pub fn update(self: *Self, count: usize) void { + assert(self.count + count <= self.buf.len); + self.count += count; + } + + /// Appends the data in `src` to the fifo. + /// You must have ensured there is enough space. + pub fn writeAssumeCapacity(self: *Self, src: []const T) void { + assert(self.writableLength() >= src.len); + + var src_left = src; + while (src_left.len > 0) { + const writable_slice = self.writableSlice(0); + assert(writable_slice.len != 0); + const n = @min(writable_slice.len, src_left.len); + @memcpy(writable_slice[0..n], src_left[0..n]); + self.update(n); + src_left = src_left[n..]; + } + } + + /// Write a single item to the fifo + pub fn writeItem(self: *Self, item: T) !void { + try self.ensureUnusedCapacity(1); + return self.writeItemAssumeCapacity(item); + } + + pub fn writeItemAssumeCapacity(self: *Self, item: T) void { + var tail = self.head + self.count; + if (powers_of_two) { + tail &= self.buf.len - 1; + } else { + tail %= self.buf.len; + } + self.buf[tail] = item; + self.update(1); + } + + /// Appends the data in `src` to the fifo. + /// Allocates more memory as necessary + pub fn write(self: *Self, src: []const T) !void { + try self.ensureUnusedCapacity(src.len); + + return self.writeAssumeCapacity(src); + } + + /// Same as `write` except it returns the number of bytes written, which is always the same + /// as `bytes.len`. The purpose of this function existing is to match `std.io.Writer` API. + fn appendWrite(self: *Self, bytes: []const u8) error{OutOfMemory}!usize { + try self.write(bytes); + return bytes.len; + } + + pub fn writer(fifo: *Self) std.io.Writer { + return .{ + .context = fifo, + .vtable = &.{ + .writeSplat = writerWriteSplat, + .writeFile = writerWriteFile, + }, + }; + } + fn writerWriteSplat(ctx: ?*anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Error!usize { + const fifo: *Self = @alignCast(@ptrCast(ctx)); + _ = fifo; + _ = data; + _ = splat; + @panic("TODO"); + } + fn writerWriteFile( + ctx: ?*anyopaque, + file: std.fs.File, + offset: std.io.Writer.Offset, + limit: std.io.Writer.Limit, + headers_and_trailers: []const []const u8, + headers_len: usize, + ) std.io.Writer.Error!usize { + const fifo: *Self = @alignCast(@ptrCast(ctx)); + _ = fifo; + _ = file; + _ = offset; + _ = limit; + _ = headers_and_trailers; + _ = headers_len; + @panic("TODO"); + } + + /// Make `count` items available before the current read location + fn rewind(self: *Self, count: usize) void { + assert(self.writableLength() >= count); + + var head = self.head + (self.buf.len - count); + if (powers_of_two) { + head &= self.buf.len - 1; + } else { + head %= self.buf.len; + } + self.head = head; + self.count += count; + } + + /// Place data back into the read stream + pub fn unget(self: *Self, src: []const T) !void { + try self.ensureUnusedCapacity(src.len); + + self.rewind(src.len); + + const slice = self.readableSliceMut(0); + if (src.len < slice.len) { + @memcpy(slice[0..src.len], src); + } else { + @memcpy(slice, src[0..slice.len]); + const slice2 = self.readableSliceMut(slice.len); + @memcpy(slice2[0 .. src.len - slice.len], src[slice.len..]); + } + } + + /// Returns the item at `offset`. + /// Asserts offset is within bounds. + pub fn peekItem(self: Self, offset: usize) T { + assert(offset < self.count); + + var index = self.head + offset; + if (powers_of_two) { + index &= self.buf.len - 1; + } else { + index %= self.buf.len; + } + return self.buf[index]; + } + + pub fn toOwnedSlice(self: *Self) Allocator.Error![]T { + if (self.head != 0) self.realign(); + assert(self.head == 0); + assert(self.count <= self.buf.len); + const allocator = self.allocator; + if (allocator.resize(self.buf, self.count)) { + const result = self.buf[0..self.count]; + self.* = Self.init(allocator); + return result; + } + const new_memory = try allocator.dupe(T, self.buf[0..self.count]); + allocator.free(self.buf); + self.* = Self.init(allocator); + return new_memory; + } + }; +} + +test "LinearFifo(u8, .Dynamic) discard(0) from empty buffer should not error on overflow" { + var fifo = LinearFifo(u8, .Dynamic).init(testing.allocator); + defer fifo.deinit(); + + // If overflow is not explicitly allowed this will crash in debug / safe mode + fifo.discard(0); +} + +test "LinearFifo(u8, .Dynamic)" { + var fifo = LinearFifo(u8, .Dynamic).init(testing.allocator); + defer fifo.deinit(); + + try fifo.write("HELLO"); + try testing.expectEqual(@as(usize, 5), fifo.readableLength()); + try testing.expectEqualSlices(u8, "HELLO", fifo.readableSlice(0)); + + { + var i: usize = 0; + while (i < 5) : (i += 1) { + try fifo.write(&[_]u8{fifo.peekItem(i)}); + } + try testing.expectEqual(@as(usize, 10), fifo.readableLength()); + try testing.expectEqualSlices(u8, "HELLOHELLO", fifo.readableSlice(0)); + } + + { + try testing.expectEqual(@as(u8, 'H'), fifo.readItem().?); + try testing.expectEqual(@as(u8, 'E'), fifo.readItem().?); + try testing.expectEqual(@as(u8, 'L'), fifo.readItem().?); + try testing.expectEqual(@as(u8, 'L'), fifo.readItem().?); + try testing.expectEqual(@as(u8, 'O'), fifo.readItem().?); + } + try testing.expectEqual(@as(usize, 5), fifo.readableLength()); + + { // Writes that wrap around + try testing.expectEqual(@as(usize, 11), fifo.writableLength()); + try testing.expectEqual(@as(usize, 6), fifo.writableSlice(0).len); + fifo.writeAssumeCapacity("6 FifoType.init(), + .Slice => FifoType.init(buf[0..]), + .Dynamic => FifoType.init(testing.allocator), + }; + defer fifo.deinit(); + + try fifo.write(&[_]T{ 0, 1, 1, 0, 1 }); + try testing.expectEqual(@as(usize, 5), fifo.readableLength()); + + { + try testing.expectEqual(@as(T, 0), fifo.readItem().?); + try testing.expectEqual(@as(T, 1), fifo.readItem().?); + try testing.expectEqual(@as(T, 1), fifo.readItem().?); + try testing.expectEqual(@as(T, 0), fifo.readItem().?); + try testing.expectEqual(@as(T, 1), fifo.readItem().?); + try testing.expectEqual(@as(usize, 0), fifo.readableLength()); + } + + { + try fifo.writeItem(1); + try fifo.writeItem(1); + try fifo.writeItem(1); + try testing.expectEqual(@as(usize, 3), fifo.readableLength()); + } + + { + var readBuf: [3]T = undefined; + const n = fifo.read(&readBuf); + try testing.expectEqual(@as(usize, 3), n); // NOTE: It should be the number of items. + } + } + } +} diff --git a/src/link/MachO/dyld_info/Trie.zig b/src/link/MachO/dyld_info/Trie.zig index 59d765c4328c1a1489da871243e341f9577a1a66..53e73c334dc92fd2626c8b68a993a5263f9e2fc4 100644 --- a/src/link/MachO/dyld_info/Trie.zig +++ b/src/link/MachO/dyld_info/Trie.zig @@ -138,7 +138,7 @@ fn finalize(self: *Trie, allocator: Allocator) !void { defer ordered_nodes.deinit(); try ordered_nodes.ensureTotalCapacityPrecise(self.nodes.items(.is_terminal).len); - var fifo = std.fifo.LinearFifo(Node.Index, .Dynamic).init(allocator); + var fifo = DeprecatedLinearFifo(Node.Index).init(allocator); defer fifo.deinit(); try fifo.writeItem(self.root.?); @@ -409,6 +409,7 @@ const mem = std.mem; const std = @import("std"); const testing = std.testing; const trace = @import("../../../tracy.zig").trace; +const DeprecatedLinearFifo = @import("../../../deprecated.zig").LinearFifo; const Allocator = mem.Allocator; const MachO = @import("../../MachO.zig");