| author | |
| committer | |
| log | 21df0010012b9a428f678a11586a50f24e54cf6f |
| tree | 7324b6cbf6f37ababd26392621f3ed70eb7ebb75 |
| parent | 3650cd3e8efd730fb6305bdefbbf8053c352bb0c |
bad API7 files changed, 595 insertions(+), 596 deletions(-)
CMakeLists.txt-1| ... | @@ -434,7 +434,6 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -434,7 +434,6 @@ set(ZIG_STAGE2_SOURCES |
| 434 | lib/std/dwarf/OP.zig | 434 | lib/std/dwarf/OP.zig |
| 435 | lib/std/dwarf/TAG.zig | 435 | lib/std/dwarf/TAG.zig |
| 436 | lib/std/elf.zig | 436 | lib/std/elf.zig |
| 437 | lib/std/fifo.zig | ||
| 438 | lib/std/fmt.zig | 437 | lib/std/fmt.zig |
| 439 | lib/std/fmt/parse_float.zig | 438 | lib/std/fmt/parse_float.zig |
| 440 | lib/std/fs.zig | 439 | lib/std/fs.zig |
lib/compiler/resinator/compile.zig+1-3| ... | @@ -1269,9 +1269,7 @@ pub const Compiler = struct { | ... | @@ -1269,9 +1269,7 @@ pub const Compiler = struct { |
| 1269 | pub fn writeResourceDataNoPadding(writer: anytype, data_reader: anytype, data_size: u32) !void { | 1269 | pub fn writeResourceDataNoPadding(writer: anytype, data_reader: anytype, data_size: u32) !void { |
| 1270 | var limited_reader = std.io.limitedReader(data_reader, data_size); | 1270 | var limited_reader = std.io.limitedReader(data_reader, data_size); |
| 1271 | 1271 | ||
| 1272 | const FifoBuffer = std.fifo.LinearFifo(u8, .{ .Static = 4096 }); | 1272 | try limited_reader.reader().readRemaining(writer); |
| 1273 | var fifo = FifoBuffer.init(); | ||
| 1274 | try fifo.pump(limited_reader.reader(), writer); | ||
| 1275 | } | 1273 | } |
| 1276 | 1274 | ||
| 1277 | pub fn writeResourceData(writer: anytype, data_reader: anytype, data_size: u32) !void { | 1275 | pub fn writeResourceData(writer: anytype, data_reader: anytype, data_size: u32) !void { |
lib/std/fifo.zig deleted-587| ... | @@ -1,587 +0,0 @@ | ||
| 1 | // FIFO of fixed size items | ||
| 2 | // Usually used for e.g. byte buffers | ||
| 3 | |||
| 4 | const std = @import("std"); | ||
| 5 | const math = std.math; | ||
| 6 | const mem = std.mem; | ||
| 7 | const Allocator = mem.Allocator; | ||
| 8 | const assert = std.debug.assert; | ||
| 9 | const testing = std.testing; | ||
| 10 | |||
| 11 | pub const LinearFifoBufferType = union(enum) { | ||
| 12 | /// The buffer is internal to the fifo; it is of the specified size. | ||
| 13 | Static: usize, | ||
| 14 | |||
| 15 | /// The buffer is passed as a slice to the initialiser. | ||
| 16 | Slice, | ||
| 17 | |||
| 18 | /// The buffer is managed dynamically using a `mem.Allocator`. | ||
| 19 | Dynamic, | ||
| 20 | }; | ||
| 21 | |||
| 22 | pub fn LinearFifo( | ||
| 23 | comptime T: type, | ||
| 24 | comptime buffer_type: LinearFifoBufferType, | ||
| 25 | ) type { | ||
| 26 | const autoalign = false; | ||
| 27 | |||
| 28 | const powers_of_two = switch (buffer_type) { | ||
| 29 | .Static => std.math.isPowerOfTwo(buffer_type.Static), | ||
| 30 | .Slice => false, // Any size slice could be passed in | ||
| 31 | .Dynamic => true, // This could be configurable in future | ||
| 32 | }; | ||
| 33 | |||
| 34 | return struct { | ||
| 35 | allocator: if (buffer_type == .Dynamic) Allocator else void, | ||
| 36 | buf: if (buffer_type == .Static) [buffer_type.Static]T else []T, | ||
| 37 | head: usize, | ||
| 38 | count: usize, | ||
| 39 | |||
| 40 | const Self = @This(); | ||
| 41 | |||
| 42 | // Type of Self argument for slice operations. | ||
| 43 | // If buffer is inline (Static) then we need to ensure we haven't | ||
| 44 | // returned a slice into a copy on the stack | ||
| 45 | const SliceSelfArg = if (buffer_type == .Static) *Self else Self; | ||
| 46 | |||
| 47 | pub const init = switch (buffer_type) { | ||
| 48 | .Static => initStatic, | ||
| 49 | .Slice => initSlice, | ||
| 50 | .Dynamic => initDynamic, | ||
| 51 | }; | ||
| 52 | |||
| 53 | fn initStatic() Self { | ||
| 54 | comptime assert(buffer_type == .Static); | ||
| 55 | return .{ | ||
| 56 | .allocator = {}, | ||
| 57 | .buf = undefined, | ||
| 58 | .head = 0, | ||
| 59 | .count = 0, | ||
| 60 | }; | ||
| 61 | } | ||
| 62 | |||
| 63 | fn initSlice(buf: []T) Self { | ||
| 64 | comptime assert(buffer_type == .Slice); | ||
| 65 | return .{ | ||
| 66 | .allocator = {}, | ||
| 67 | .buf = buf, | ||
| 68 | .head = 0, | ||
| 69 | .count = 0, | ||
| 70 | }; | ||
| 71 | } | ||
| 72 | |||
| 73 | fn initDynamic(allocator: Allocator) Self { | ||
| 74 | comptime assert(buffer_type == .Dynamic); | ||
| 75 | return .{ | ||
| 76 | .allocator = allocator, | ||
| 77 | .buf = &.{}, | ||
| 78 | .head = 0, | ||
| 79 | .count = 0, | ||
| 80 | }; | ||
| 81 | } | ||
| 82 | |||
| 83 | pub fn deinit(self: Self) void { | ||
| 84 | if (buffer_type == .Dynamic) self.allocator.free(self.buf); | ||
| 85 | } | ||
| 86 | |||
| 87 | pub fn realign(self: *Self) void { | ||
| 88 | if (self.buf.len - self.head >= self.count) { | ||
| 89 | mem.copyForwards(T, self.buf[0..self.count], self.buf[self.head..][0..self.count]); | ||
| 90 | self.head = 0; | ||
| 91 | } else { | ||
| 92 | var tmp: [4096 / 2 / @sizeOf(T)]T = undefined; | ||
| 93 | |||
| 94 | while (self.head != 0) { | ||
| 95 | const n = @min(self.head, tmp.len); | ||
| 96 | const m = self.buf.len - n; | ||
| 97 | @memcpy(tmp[0..n], self.buf[0..n]); | ||
| 98 | mem.copyForwards(T, self.buf[0..m], self.buf[n..][0..m]); | ||
| 99 | @memcpy(self.buf[m..][0..n], tmp[0..n]); | ||
| 100 | self.head -= n; | ||
| 101 | } | ||
| 102 | } | ||
| 103 | { // set unused area to undefined | ||
| 104 | const unused = mem.sliceAsBytes(self.buf[self.count..]); | ||
| 105 | @memset(unused, undefined); | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 109 | /// Reduce allocated capacity to `size`. | ||
| 110 | pub fn shrink(self: *Self, size: usize) void { | ||
| 111 | assert(size >= self.count); | ||
| 112 | if (buffer_type == .Dynamic) { | ||
| 113 | self.realign(); | ||
| 114 | self.buf = self.allocator.realloc(self.buf, size) catch |e| switch (e) { | ||
| 115 | error.OutOfMemory => return, // no problem, capacity is still correct then. | ||
| 116 | }; | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | /// Ensure that the buffer can fit at least `size` items | ||
| 121 | pub fn ensureTotalCapacity(self: *Self, size: usize) !void { | ||
| 122 | if (self.buf.len >= size) return; | ||
| 123 | if (buffer_type == .Dynamic) { | ||
| 124 | self.realign(); | ||
| 125 | const new_size = if (powers_of_two) math.ceilPowerOfTwo(usize, size) catch return error.OutOfMemory else size; | ||
| 126 | self.buf = try self.allocator.realloc(self.buf, new_size); | ||
| 127 | } else { | ||
| 128 | return error.OutOfMemory; | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | /// Makes sure at least `size` items are unused | ||
| 133 | pub fn ensureUnusedCapacity(self: *Self, size: usize) error{OutOfMemory}!void { | ||
| 134 | if (self.writableLength() >= size) return; | ||
| 135 | |||
| 136 | return try self.ensureTotalCapacity(math.add(usize, self.count, size) catch return error.OutOfMemory); | ||
| 137 | } | ||
| 138 | |||
| 139 | /// Returns number of items currently in fifo | ||
| 140 | pub fn readableLength(self: Self) usize { | ||
| 141 | return self.count; | ||
| 142 | } | ||
| 143 | |||
| 144 | /// Returns a writable slice from the 'read' end of the fifo | ||
| 145 | fn readableSliceMut(self: SliceSelfArg, offset: usize) []T { | ||
| 146 | if (offset > self.count) return &[_]T{}; | ||
| 147 | |||
| 148 | var start = self.head + offset; | ||
| 149 | if (start >= self.buf.len) { | ||
| 150 | start -= self.buf.len; | ||
| 151 | return self.buf[start .. start + (self.count - offset)]; | ||
| 152 | } else { | ||
| 153 | const end = @min(self.head + self.count, self.buf.len); | ||
| 154 | return self.buf[start..end]; | ||
| 155 | } | ||
| 156 | } | ||
| 157 | |||
| 158 | /// Returns a readable slice from `offset` | ||
| 159 | pub fn readableSlice(self: SliceSelfArg, offset: usize) []const T { | ||
| 160 | return self.readableSliceMut(offset); | ||
| 161 | } | ||
| 162 | |||
| 163 | pub fn readableSliceOfLen(self: *Self, len: usize) []const T { | ||
| 164 | assert(len <= self.count); | ||
| 165 | const buf = self.readableSlice(0); | ||
| 166 | if (buf.len >= len) { | ||
| 167 | return buf[0..len]; | ||
| 168 | } else { | ||
| 169 | self.realign(); | ||
| 170 | return self.readableSlice(0)[0..len]; | ||
| 171 | } | ||
| 172 | } | ||
| 173 | |||
| 174 | /// Discard first `count` items in the fifo | ||
| 175 | pub fn discard(self: *Self, count: usize) void { | ||
| 176 | assert(count <= self.count); | ||
| 177 | { // set old range to undefined. Note: may be wrapped around | ||
| 178 | const slice = self.readableSliceMut(0); | ||
| 179 | if (slice.len >= count) { | ||
| 180 | const unused = mem.sliceAsBytes(slice[0..count]); | ||
| 181 | @memset(unused, undefined); | ||
| 182 | } else { | ||
| 183 | const unused = mem.sliceAsBytes(slice[0..]); | ||
| 184 | @memset(unused, undefined); | ||
| 185 | const unused2 = mem.sliceAsBytes(self.readableSliceMut(slice.len)[0 .. count - slice.len]); | ||
| 186 | @memset(unused2, undefined); | ||
| 187 | } | ||
| 188 | } | ||
| 189 | if (autoalign and self.count == count) { | ||
| 190 | self.head = 0; | ||
| 191 | self.count = 0; | ||
| 192 | } else { | ||
| 193 | var head = self.head + count; | ||
| 194 | if (powers_of_two) { | ||
| 195 | // Note it is safe to do a wrapping subtract as | ||
| 196 | // bitwise & with all 1s is a noop | ||
| 197 | head &= self.buf.len -% 1; | ||
| 198 | } else { | ||
| 199 | head %= self.buf.len; | ||
| 200 | } | ||
| 201 | self.head = head; | ||
| 202 | self.count -= count; | ||
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | /// Read the next item from the fifo | ||
| 207 | pub fn readItem(self: *Self) ?T { | ||
| 208 | if (self.count == 0) return null; | ||
| 209 | |||
| 210 | const c = self.buf[self.head]; | ||
| 211 | self.discard(1); | ||
| 212 | return c; | ||
| 213 | } | ||
| 214 | |||
| 215 | /// Read data from the fifo into `dst`, returns number of items copied. | ||
| 216 | pub fn read(self: *Self, dst: []T) usize { | ||
| 217 | var dst_left = dst; | ||
| 218 | |||
| 219 | while (dst_left.len > 0) { | ||
| 220 | const slice = self.readableSlice(0); | ||
| 221 | if (slice.len == 0) break; | ||
| 222 | const n = @min(slice.len, dst_left.len); | ||
| 223 | @memcpy(dst_left[0..n], slice[0..n]); | ||
| 224 | self.discard(n); | ||
| 225 | dst_left = dst_left[n..]; | ||
| 226 | } | ||
| 227 | |||
| 228 | return dst.len - dst_left.len; | ||
| 229 | } | ||
| 230 | |||
| 231 | /// Same as `read` except it returns an error union | ||
| 232 | /// The purpose of this function existing is to match `std.io.Reader` API. | ||
| 233 | fn readFn(self: *Self, dest: []u8) error{}!usize { | ||
| 234 | return self.read(dest); | ||
| 235 | } | ||
| 236 | |||
| 237 | pub fn reader(self: *Self) std.io.Reader { | ||
| 238 | return .{ | ||
| 239 | .context = self, | ||
| 240 | .vtable = &.{ | ||
| 241 | .read = &readerRead, | ||
| 242 | .readVec = &readerReadVec, | ||
| 243 | .discard = &readerDiscard, | ||
| 244 | }, | ||
| 245 | }; | ||
| 246 | } | ||
| 247 | fn readerRead( | ||
| 248 | ctx: ?*anyopaque, | ||
| 249 | bw: *std.io.BufferedWriter, | ||
| 250 | limit: std.io.Limit, | ||
| 251 | ) std.io.Reader.RwError!usize { | ||
| 252 | const fifo: *Self = @alignCast(@ptrCast(ctx)); | ||
| 253 | _ = fifo; | ||
| 254 | _ = bw; | ||
| 255 | _ = limit; | ||
| 256 | @panic("TODO"); | ||
| 257 | } | ||
| 258 | fn readerReadVec(ctx: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { | ||
| 259 | const fifo: *Self = @alignCast(@ptrCast(ctx)); | ||
| 260 | _ = fifo; | ||
| 261 | _ = data; | ||
| 262 | @panic("TODO"); | ||
| 263 | } | ||
| 264 | fn readerDiscard(ctx: ?*anyopaque, limit: std.io.Limit) std.io.Reader.Error!usize { | ||
| 265 | const fifo: *Self = @alignCast(@ptrCast(ctx)); | ||
| 266 | _ = fifo; | ||
| 267 | _ = limit; | ||
| 268 | @panic("TODO"); | ||
| 269 | } | ||
| 270 | |||
| 271 | /// Returns number of items available in fifo | ||
| 272 | pub fn writableLength(self: Self) usize { | ||
| 273 | return self.buf.len - self.count; | ||
| 274 | } | ||
| 275 | |||
| 276 | /// Returns the first section of writable buffer. | ||
| 277 | /// Note that this may be of length 0 | ||
| 278 | pub fn writableSlice(self: SliceSelfArg, offset: usize) []T { | ||
| 279 | if (offset > self.buf.len) return &[_]T{}; | ||
| 280 | |||
| 281 | const tail = self.head + offset + self.count; | ||
| 282 | if (tail < self.buf.len) { | ||
| 283 | return self.buf[tail..]; | ||
| 284 | } else { | ||
| 285 | return self.buf[tail - self.buf.len ..][0 .. self.writableLength() - offset]; | ||
| 286 | } | ||
| 287 | } | ||
| 288 | |||
| 289 | /// Returns a writable buffer of at least `size` items, allocating memory as needed. | ||
| 290 | /// Use `fifo.update` once you've written data to it. | ||
| 291 | pub fn writableWithSize(self: *Self, size: usize) ![]T { | ||
| 292 | try self.ensureUnusedCapacity(size); | ||
| 293 | |||
| 294 | // try to avoid realigning buffer | ||
| 295 | var slice = self.writableSlice(0); | ||
| 296 | if (slice.len < size) { | ||
| 297 | self.realign(); | ||
| 298 | slice = self.writableSlice(0); | ||
| 299 | } | ||
| 300 | return slice; | ||
| 301 | } | ||
| 302 | |||
| 303 | /// Update the tail location of the buffer (usually follows use of writable/writableWithSize) | ||
| 304 | pub fn update(self: *Self, count: usize) void { | ||
| 305 | assert(self.count + count <= self.buf.len); | ||
| 306 | self.count += count; | ||
| 307 | } | ||
| 308 | |||
| 309 | /// Appends the data in `src` to the fifo. | ||
| 310 | /// You must have ensured there is enough space. | ||
| 311 | pub fn writeAssumeCapacity(self: *Self, src: []const T) void { | ||
| 312 | assert(self.writableLength() >= src.len); | ||
| 313 | |||
| 314 | var src_left = src; | ||
| 315 | while (src_left.len > 0) { | ||
| 316 | const writable_slice = self.writableSlice(0); | ||
| 317 | assert(writable_slice.len != 0); | ||
| 318 | const n = @min(writable_slice.len, src_left.len); | ||
| 319 | @memcpy(writable_slice[0..n], src_left[0..n]); | ||
| 320 | self.update(n); | ||
| 321 | src_left = src_left[n..]; | ||
| 322 | } | ||
| 323 | } | ||
| 324 | |||
| 325 | /// Write a single item to the fifo | ||
| 326 | pub fn writeItem(self: *Self, item: T) !void { | ||
| 327 | try self.ensureUnusedCapacity(1); | ||
| 328 | return self.writeItemAssumeCapacity(item); | ||
| 329 | } | ||
| 330 | |||
| 331 | pub fn writeItemAssumeCapacity(self: *Self, item: T) void { | ||
| 332 | var tail = self.head + self.count; | ||
| 333 | if (powers_of_two) { | ||
| 334 | tail &= self.buf.len - 1; | ||
| 335 | } else { | ||
| 336 | tail %= self.buf.len; | ||
| 337 | } | ||
| 338 | self.buf[tail] = item; | ||
| 339 | self.update(1); | ||
| 340 | } | ||
| 341 | |||
| 342 | /// Appends the data in `src` to the fifo. | ||
| 343 | /// Allocates more memory as necessary | ||
| 344 | pub fn write(self: *Self, src: []const T) !void { | ||
| 345 | try self.ensureUnusedCapacity(src.len); | ||
| 346 | |||
| 347 | return self.writeAssumeCapacity(src); | ||
| 348 | } | ||
| 349 | |||
| 350 | /// Same as `write` except it returns the number of bytes written, which is always the same | ||
| 351 | /// as `bytes.len`. The purpose of this function existing is to match `std.io.Writer` API. | ||
| 352 | fn appendWrite(self: *Self, bytes: []const u8) error{OutOfMemory}!usize { | ||
| 353 | try self.write(bytes); | ||
| 354 | return bytes.len; | ||
| 355 | } | ||
| 356 | |||
| 357 | pub fn writer(fifo: *Self) std.io.Writer { | ||
| 358 | return .{ | ||
| 359 | .context = fifo, | ||
| 360 | .vtable = &.{ | ||
| 361 | .writeSplat = writerWriteSplat, | ||
| 362 | .writeFile = writerWriteFile, | ||
| 363 | }, | ||
| 364 | }; | ||
| 365 | } | ||
| 366 | fn writerWriteSplat(ctx: ?*anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Error!usize { | ||
| 367 | const fifo: *Self = @alignCast(@ptrCast(ctx)); | ||
| 368 | _ = fifo; | ||
| 369 | _ = data; | ||
| 370 | _ = splat; | ||
| 371 | @panic("TODO"); | ||
| 372 | } | ||
| 373 | fn writerWriteFile( | ||
| 374 | ctx: ?*anyopaque, | ||
| 375 | file: std.fs.File, | ||
| 376 | offset: std.io.Writer.Offset, | ||
| 377 | limit: std.io.Writer.Limit, | ||
| 378 | headers_and_trailers: []const []const u8, | ||
| 379 | headers_len: usize, | ||
| 380 | ) std.io.Writer.Error!usize { | ||
| 381 | const fifo: *Self = @alignCast(@ptrCast(ctx)); | ||
| 382 | _ = fifo; | ||
| 383 | _ = file; | ||
| 384 | _ = offset; | ||
| 385 | _ = limit; | ||
| 386 | _ = headers_and_trailers; | ||
| 387 | _ = headers_len; | ||
| 388 | @panic("TODO"); | ||
| 389 | } | ||
| 390 | |||
| 391 | /// Make `count` items available before the current read location | ||
| 392 | fn rewind(self: *Self, count: usize) void { | ||
| 393 | assert(self.writableLength() >= count); | ||
| 394 | |||
| 395 | var head = self.head + (self.buf.len - count); | ||
| 396 | if (powers_of_two) { | ||
| 397 | head &= self.buf.len - 1; | ||
| 398 | } else { | ||
| 399 | head %= self.buf.len; | ||
| 400 | } | ||
| 401 | self.head = head; | ||
| 402 | self.count += count; | ||
| 403 | } | ||
| 404 | |||
| 405 | /// Place data back into the read stream | ||
| 406 | pub fn unget(self: *Self, src: []const T) !void { | ||
| 407 | try self.ensureUnusedCapacity(src.len); | ||
| 408 | |||
| 409 | self.rewind(src.len); | ||
| 410 | |||
| 411 | const slice = self.readableSliceMut(0); | ||
| 412 | if (src.len < slice.len) { | ||
| 413 | @memcpy(slice[0..src.len], src); | ||
| 414 | } else { | ||
| 415 | @memcpy(slice, src[0..slice.len]); | ||
| 416 | const slice2 = self.readableSliceMut(slice.len); | ||
| 417 | @memcpy(slice2[0 .. src.len - slice.len], src[slice.len..]); | ||
| 418 | } | ||
| 419 | } | ||
| 420 | |||
| 421 | /// Returns the item at `offset`. | ||
| 422 | /// Asserts offset is within bounds. | ||
| 423 | pub fn peekItem(self: Self, offset: usize) T { | ||
| 424 | assert(offset < self.count); | ||
| 425 | |||
| 426 | var index = self.head + offset; | ||
| 427 | if (powers_of_two) { | ||
| 428 | index &= self.buf.len - 1; | ||
| 429 | } else { | ||
| 430 | index %= self.buf.len; | ||
| 431 | } | ||
| 432 | return self.buf[index]; | ||
| 433 | } | ||
| 434 | |||
| 435 | pub fn toOwnedSlice(self: *Self) Allocator.Error![]T { | ||
| 436 | if (self.head != 0) self.realign(); | ||
| 437 | assert(self.head == 0); | ||
| 438 | assert(self.count <= self.buf.len); | ||
| 439 | const allocator = self.allocator; | ||
| 440 | if (allocator.resize(self.buf, self.count)) { | ||
| 441 | const result = self.buf[0..self.count]; | ||
| 442 | self.* = Self.init(allocator); | ||
| 443 | return result; | ||
| 444 | } | ||
| 445 | const new_memory = try allocator.dupe(T, self.buf[0..self.count]); | ||
| 446 | allocator.free(self.buf); | ||
| 447 | self.* = Self.init(allocator); | ||
| 448 | return new_memory; | ||
| 449 | } | ||
| 450 | }; | ||
| 451 | } | ||
| 452 | |||
| 453 | test "LinearFifo(u8, .Dynamic) discard(0) from empty buffer should not error on overflow" { | ||
| 454 | var fifo = LinearFifo(u8, .Dynamic).init(testing.allocator); | ||
| 455 | defer fifo.deinit(); | ||
| 456 | |||
| 457 | // If overflow is not explicitly allowed this will crash in debug / safe mode | ||
| 458 | fifo.discard(0); | ||
| 459 | } | ||
| 460 | |||
| 461 | test "LinearFifo(u8, .Dynamic)" { | ||
| 462 | var fifo = LinearFifo(u8, .Dynamic).init(testing.allocator); | ||
| 463 | defer fifo.deinit(); | ||
| 464 | |||
| 465 | try fifo.write("HELLO"); | ||
| 466 | try testing.expectEqual(@as(usize, 5), fifo.readableLength()); | ||
| 467 | try testing.expectEqualSlices(u8, "HELLO", fifo.readableSlice(0)); | ||
| 468 | |||
| 469 | { | ||
| 470 | var i: usize = 0; | ||
| 471 | while (i < 5) : (i += 1) { | ||
| 472 | try fifo.write(&[_]u8{fifo.peekItem(i)}); | ||
| 473 | } | ||
| 474 | try testing.expectEqual(@as(usize, 10), fifo.readableLength()); | ||
| 475 | try testing.expectEqualSlices(u8, "HELLOHELLO", fifo.readableSlice(0)); | ||
| 476 | } | ||
| 477 | |||
| 478 | { | ||
| 479 | try testing.expectEqual(@as(u8, 'H'), fifo.readItem().?); | ||
| 480 | try testing.expectEqual(@as(u8, 'E'), fifo.readItem().?); | ||
| 481 | try testing.expectEqual(@as(u8, 'L'), fifo.readItem().?); | ||
| 482 | try testing.expectEqual(@as(u8, 'L'), fifo.readItem().?); | ||
| 483 | try testing.expectEqual(@as(u8, 'O'), fifo.readItem().?); | ||
| 484 | } | ||
| 485 | try testing.expectEqual(@as(usize, 5), fifo.readableLength()); | ||
| 486 | |||
| 487 | { // Writes that wrap around | ||
| 488 | try testing.expectEqual(@as(usize, 11), fifo.writableLength()); | ||
| 489 | try testing.expectEqual(@as(usize, 6), fifo.writableSlice(0).len); | ||
| 490 | fifo.writeAssumeCapacity("6<chars<11"); | ||
| 491 | try testing.expectEqualSlices(u8, "HELLO6<char", fifo.readableSlice(0)); | ||
| 492 | try testing.expectEqualSlices(u8, "s<11", fifo.readableSlice(11)); | ||
| 493 | try testing.expectEqualSlices(u8, "11", fifo.readableSlice(13)); | ||
| 494 | try testing.expectEqualSlices(u8, "", fifo.readableSlice(15)); | ||
| 495 | fifo.discard(11); | ||
| 496 | try testing.expectEqualSlices(u8, "s<11", fifo.readableSlice(0)); | ||
| 497 | fifo.discard(4); | ||
| 498 | try testing.expectEqual(@as(usize, 0), fifo.readableLength()); | ||
| 499 | } | ||
| 500 | |||
| 501 | { | ||
| 502 | const buf = try fifo.writableWithSize(12); | ||
| 503 | try testing.expectEqual(@as(usize, 12), buf.len); | ||
| 504 | var i: u8 = 0; | ||
| 505 | while (i < 10) : (i += 1) { | ||
| 506 | buf[i] = i + 'a'; | ||
| 507 | } | ||
| 508 | fifo.update(10); | ||
| 509 | try testing.expectEqualSlices(u8, "abcdefghij", fifo.readableSlice(0)); | ||
| 510 | } | ||
| 511 | |||
| 512 | { | ||
| 513 | try fifo.unget("prependedstring"); | ||
| 514 | var result: [30]u8 = undefined; | ||
| 515 | try testing.expectEqualSlices(u8, "prependedstringabcdefghij", result[0..fifo.read(&result)]); | ||
| 516 | try fifo.unget("b"); | ||
| 517 | try fifo.unget("a"); | ||
| 518 | try testing.expectEqualSlices(u8, "ab", result[0..fifo.read(&result)]); | ||
| 519 | } | ||
| 520 | |||
| 521 | fifo.shrink(0); | ||
| 522 | |||
| 523 | { | ||
| 524 | try fifo.writer().print("{s}, {s}!", .{ "Hello", "World" }); | ||
| 525 | var result: [30]u8 = undefined; | ||
| 526 | try testing.expectEqualSlices(u8, "Hello, World!", result[0..fifo.read(&result)]); | ||
| 527 | try testing.expectEqual(@as(usize, 0), fifo.readableLength()); | ||
| 528 | } | ||
| 529 | |||
| 530 | { | ||
| 531 | try fifo.writer().writeAll("This is a test"); | ||
| 532 | var result: [30]u8 = undefined; | ||
| 533 | try testing.expectEqualSlices(u8, "This", (try fifo.reader().readUntilDelimiterOrEof(&result, ' ')).?); | ||
| 534 | try testing.expectEqualSlices(u8, "is", (try fifo.reader().readUntilDelimiterOrEof(&result, ' ')).?); | ||
| 535 | try testing.expectEqualSlices(u8, "a", (try fifo.reader().readUntilDelimiterOrEof(&result, ' ')).?); | ||
| 536 | try testing.expectEqualSlices(u8, "test", (try fifo.reader().readUntilDelimiterOrEof(&result, ' ')).?); | ||
| 537 | } | ||
| 538 | |||
| 539 | { | ||
| 540 | try fifo.ensureTotalCapacity(1); | ||
| 541 | var in_fbs = std.io.fixedBufferStream("pump test"); | ||
| 542 | var out_buf: [50]u8 = undefined; | ||
| 543 | var out_fbs = std.io.fixedBufferStream(&out_buf); | ||
| 544 | try fifo.pump(in_fbs.reader(), out_fbs.writer()); | ||
| 545 | try testing.expectEqualSlices(u8, in_fbs.buffer, out_fbs.getWritten()); | ||
| 546 | } | ||
| 547 | } | ||
| 548 | |||
| 549 | test LinearFifo { | ||
| 550 | inline for ([_]type{ u1, u8, u16, u64 }) |T| { | ||
| 551 | inline for ([_]LinearFifoBufferType{ LinearFifoBufferType{ .Static = 32 }, .Slice, .Dynamic }) |bt| { | ||
| 552 | const FifoType = LinearFifo(T, bt); | ||
| 553 | var buf: if (bt == .Slice) [32]T else void = undefined; | ||
| 554 | var fifo = switch (bt) { | ||
| 555 | .Static => FifoType.init(), | ||
| 556 | .Slice => FifoType.init(buf[0..]), | ||
| 557 | .Dynamic => FifoType.init(testing.allocator), | ||
| 558 | }; | ||
| 559 | defer fifo.deinit(); | ||
| 560 | |||
| 561 | try fifo.write(&[_]T{ 0, 1, 1, 0, 1 }); | ||
| 562 | try testing.expectEqual(@as(usize, 5), fifo.readableLength()); | ||
| 563 | |||
| 564 | { | ||
| 565 | try testing.expectEqual(@as(T, 0), fifo.readItem().?); | ||
| 566 | try testing.expectEqual(@as(T, 1), fifo.readItem().?); | ||
| 567 | try testing.expectEqual(@as(T, 1), fifo.readItem().?); | ||
| 568 | try testing.expectEqual(@as(T, 0), fifo.readItem().?); | ||
| 569 | try testing.expectEqual(@as(T, 1), fifo.readItem().?); | ||
| 570 | try testing.expectEqual(@as(usize, 0), fifo.readableLength()); | ||
| 571 | } | ||
| 572 | |||
| 573 | { | ||
| 574 | try fifo.writeItem(1); | ||
| 575 | try fifo.writeItem(1); | ||
| 576 | try fifo.writeItem(1); | ||
| 577 | try testing.expectEqual(@as(usize, 3), fifo.readableLength()); | ||
| 578 | } | ||
| 579 | |||
| 580 | { | ||
| 581 | var readBuf: [3]T = undefined; | ||
| 582 | const n = fifo.read(&readBuf); | ||
| 583 | try testing.expectEqual(@as(usize, 3), n); // NOTE: It should be the number of items. | ||
| 584 | } | ||
| 585 | } | ||
| 586 | } | ||
| 587 | } | ||
lib/std/std.zig-1| ... | @@ -58,7 +58,6 @@ pub const debug = @import("debug.zig"); | ... | @@ -58,7 +58,6 @@ pub const debug = @import("debug.zig"); |
| 58 | pub const dwarf = @import("dwarf.zig"); | 58 | pub const dwarf = @import("dwarf.zig"); |
| 59 | pub const elf = @import("elf.zig"); | 59 | pub const elf = @import("elf.zig"); |
| 60 | pub const enums = @import("enums.zig"); | 60 | pub const enums = @import("enums.zig"); |
| 61 | pub const fifo = @import("fifo.zig"); | ||
| 62 | pub const fmt = @import("fmt.zig"); | 61 | pub const fmt = @import("fmt.zig"); |
| 63 | pub const fs = @import("fs.zig"); | 62 | pub const fs = @import("fs.zig"); |
| 64 | pub const gpu = @import("gpu.zig"); | 63 | pub const gpu = @import("gpu.zig"); |
src/Compilation.zig+5-3| ... | @@ -44,6 +44,8 @@ const Builtin = @import("Builtin.zig"); | ... | @@ -44,6 +44,8 @@ const Builtin = @import("Builtin.zig"); |
| 44 | const LlvmObject = @import("codegen/llvm.zig").Object; | 44 | const LlvmObject = @import("codegen/llvm.zig").Object; |
| 45 | const dev = @import("dev.zig"); | 45 | const dev = @import("dev.zig"); |
| 46 | 46 | ||
| 47 | const DeprecatedLinearFifo = @import("deprecated.zig").LinearFifo; | ||
| 48 | |||
| 47 | pub const Config = @import("Compilation/Config.zig"); | 49 | pub const Config = @import("Compilation/Config.zig"); |
| 48 | 50 | ||
| 49 | /// General-purpose allocator. Used for both temporary and long-term storage. | 51 | /// General-purpose allocator. Used for both temporary and long-term storage. |
| ... | @@ -121,15 +123,15 @@ work_queues: [ | ... | @@ -121,15 +123,15 @@ work_queues: [ |
| 121 | } | 123 | } |
| 122 | break :len len; | 124 | break :len len; |
| 123 | } | 125 | } |
| 124 | ]std.fifo.LinearFifo(Job, .Dynamic), | 126 | ]DeprecatedLinearFifo(Job), |
| 125 | 127 | ||
| 126 | /// These jobs are to invoke the Clang compiler to create an object file, which | 128 | /// These jobs are to invoke the Clang compiler to create an object file, which |
| 127 | /// gets linked with the Compilation. | 129 | /// gets linked with the Compilation. |
| 128 | c_object_work_queue: std.fifo.LinearFifo(*CObject, .Dynamic), | 130 | c_object_work_queue: DeprecatedLinearFifo(*CObject), |
| 129 | 131 | ||
| 130 | /// These jobs are to invoke the RC compiler to create a compiled resource file (.res), which | 132 | /// These jobs are to invoke the RC compiler to create a compiled resource file (.res), which |
| 131 | /// gets linked with the Compilation. | 133 | /// gets linked with the Compilation. |
| 132 | win32_resource_work_queue: if (dev.env.supports(.win32_resource)) std.fifo.LinearFifo(*Win32Resource, .Dynamic) else struct { | 134 | win32_resource_work_queue: if (dev.env.supports(.win32_resource)) DeprecatedLinearFifo(*Win32Resource) else struct { |
| 133 | pub fn ensureUnusedCapacity(_: @This(), _: u0) error{}!void {} | 135 | pub fn ensureUnusedCapacity(_: @This(), _: u0) error{}!void {} |
| 134 | pub fn readItem(_: @This()) ?noreturn { | 136 | pub fn readItem(_: @This()) ?noreturn { |
| 135 | return null; | 137 | return null; |
src/deprecated.zig created+587| ... | @@ -0,0 +1,587 @@ | ||
| 1 | // FIFO of fixed size items | ||
| 2 | // Usually used for e.g. byte buffers | ||
| 3 | |||
| 4 | const std = @import("std"); | ||
| 5 | const math = std.math; | ||
| 6 | const mem = std.mem; | ||
| 7 | const Allocator = mem.Allocator; | ||
| 8 | const assert = std.debug.assert; | ||
| 9 | const testing = std.testing; | ||
| 10 | |||
| 11 | pub const LinearFifoBufferType = union(enum) { | ||
| 12 | /// The buffer is internal to the fifo; it is of the specified size. | ||
| 13 | Static: usize, | ||
| 14 | |||
| 15 | /// The buffer is passed as a slice to the initialiser. | ||
| 16 | Slice, | ||
| 17 | |||
| 18 | /// The buffer is managed dynamically using a `mem.Allocator`. | ||
| 19 | Dynamic, | ||
| 20 | }; | ||
| 21 | |||
| 22 | pub fn LinearFifo( | ||
| 23 | comptime T: type, | ||
| 24 | comptime buffer_type: LinearFifoBufferType, | ||
| 25 | ) type { | ||
| 26 | const autoalign = false; | ||
| 27 | |||
| 28 | const powers_of_two = switch (buffer_type) { | ||
| 29 | .Static => std.math.isPowerOfTwo(buffer_type.Static), | ||
| 30 | .Slice => false, // Any size slice could be passed in | ||
| 31 | .Dynamic => true, // This could be configurable in future | ||
| 32 | }; | ||
| 33 | |||
| 34 | return struct { | ||
| 35 | allocator: if (buffer_type == .Dynamic) Allocator else void, | ||
| 36 | buf: if (buffer_type == .Static) [buffer_type.Static]T else []T, | ||
| 37 | head: usize, | ||
| 38 | count: usize, | ||
| 39 | |||
| 40 | const Self = @This(); | ||
| 41 | |||
| 42 | // Type of Self argument for slice operations. | ||
| 43 | // If buffer is inline (Static) then we need to ensure we haven't | ||
| 44 | // returned a slice into a copy on the stack | ||
| 45 | const SliceSelfArg = if (buffer_type == .Static) *Self else Self; | ||
| 46 | |||
| 47 | pub const init = switch (buffer_type) { | ||
| 48 | .Static => initStatic, | ||
| 49 | .Slice => initSlice, | ||
| 50 | .Dynamic => initDynamic, | ||
| 51 | }; | ||
| 52 | |||
| 53 | fn initStatic() Self { | ||
| 54 | comptime assert(buffer_type == .Static); | ||
| 55 | return .{ | ||
| 56 | .allocator = {}, | ||
| 57 | .buf = undefined, | ||
| 58 | .head = 0, | ||
| 59 | .count = 0, | ||
| 60 | }; | ||
| 61 | } | ||
| 62 | |||
| 63 | fn initSlice(buf: []T) Self { | ||
| 64 | comptime assert(buffer_type == .Slice); | ||
| 65 | return .{ | ||
| 66 | .allocator = {}, | ||
| 67 | .buf = buf, | ||
| 68 | .head = 0, | ||
| 69 | .count = 0, | ||
| 70 | }; | ||
| 71 | } | ||
| 72 | |||
| 73 | fn initDynamic(allocator: Allocator) Self { | ||
| 74 | comptime assert(buffer_type == .Dynamic); | ||
| 75 | return .{ | ||
| 76 | .allocator = allocator, | ||
| 77 | .buf = &.{}, | ||
| 78 | .head = 0, | ||
| 79 | .count = 0, | ||
| 80 | }; | ||
| 81 | } | ||
| 82 | |||
| 83 | pub fn deinit(self: Self) void { | ||
| 84 | if (buffer_type == .Dynamic) self.allocator.free(self.buf); | ||
| 85 | } | ||
| 86 | |||
| 87 | pub fn realign(self: *Self) void { | ||
| 88 | if (self.buf.len - self.head >= self.count) { | ||
| 89 | mem.copyForwards(T, self.buf[0..self.count], self.buf[self.head..][0..self.count]); | ||
| 90 | self.head = 0; | ||
| 91 | } else { | ||
| 92 | var tmp: [4096 / 2 / @sizeOf(T)]T = undefined; | ||
| 93 | |||
| 94 | while (self.head != 0) { | ||
| 95 | const n = @min(self.head, tmp.len); | ||
| 96 | const m = self.buf.len - n; | ||
| 97 | @memcpy(tmp[0..n], self.buf[0..n]); | ||
| 98 | mem.copyForwards(T, self.buf[0..m], self.buf[n..][0..m]); | ||
| 99 | @memcpy(self.buf[m..][0..n], tmp[0..n]); | ||
| 100 | self.head -= n; | ||
| 101 | } | ||
| 102 | } | ||
| 103 | { // set unused area to undefined | ||
| 104 | const unused = mem.sliceAsBytes(self.buf[self.count..]); | ||
| 105 | @memset(unused, undefined); | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 109 | /// Reduce allocated capacity to `size`. | ||
| 110 | pub fn shrink(self: *Self, size: usize) void { | ||
| 111 | assert(size >= self.count); | ||
| 112 | if (buffer_type == .Dynamic) { | ||
| 113 | self.realign(); | ||
| 114 | self.buf = self.allocator.realloc(self.buf, size) catch |e| switch (e) { | ||
| 115 | error.OutOfMemory => return, // no problem, capacity is still correct then. | ||
| 116 | }; | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | /// Ensure that the buffer can fit at least `size` items | ||
| 121 | pub fn ensureTotalCapacity(self: *Self, size: usize) !void { | ||
| 122 | if (self.buf.len >= size) return; | ||
| 123 | if (buffer_type == .Dynamic) { | ||
| 124 | self.realign(); | ||
| 125 | const new_size = if (powers_of_two) math.ceilPowerOfTwo(usize, size) catch return error.OutOfMemory else size; | ||
| 126 | self.buf = try self.allocator.realloc(self.buf, new_size); | ||
| 127 | } else { | ||
| 128 | return error.OutOfMemory; | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | /// Makes sure at least `size` items are unused | ||
| 133 | pub fn ensureUnusedCapacity(self: *Self, size: usize) error{OutOfMemory}!void { | ||
| 134 | if (self.writableLength() >= size) return; | ||
| 135 | |||
| 136 | return try self.ensureTotalCapacity(math.add(usize, self.count, size) catch return error.OutOfMemory); | ||
| 137 | } | ||
| 138 | |||
| 139 | /// Returns number of items currently in fifo | ||
| 140 | pub fn readableLength(self: Self) usize { | ||
| 141 | return self.count; | ||
| 142 | } | ||
| 143 | |||
| 144 | /// Returns a writable slice from the 'read' end of the fifo | ||
| 145 | fn readableSliceMut(self: SliceSelfArg, offset: usize) []T { | ||
| 146 | if (offset > self.count) return &[_]T{}; | ||
| 147 | |||
| 148 | var start = self.head + offset; | ||
| 149 | if (start >= self.buf.len) { | ||
| 150 | start -= self.buf.len; | ||
| 151 | return self.buf[start .. start + (self.count - offset)]; | ||
| 152 | } else { | ||
| 153 | const end = @min(self.head + self.count, self.buf.len); | ||
| 154 | return self.buf[start..end]; | ||
| 155 | } | ||
| 156 | } | ||
| 157 | |||
| 158 | /// Returns a readable slice from `offset` | ||
| 159 | pub fn readableSlice(self: SliceSelfArg, offset: usize) []const T { | ||
| 160 | return self.readableSliceMut(offset); | ||
| 161 | } | ||
| 162 | |||
| 163 | pub fn readableSliceOfLen(self: *Self, len: usize) []const T { | ||
| 164 | assert(len <= self.count); | ||
| 165 | const buf = self.readableSlice(0); | ||
| 166 | if (buf.len >= len) { | ||
| 167 | return buf[0..len]; | ||
| 168 | } else { | ||
| 169 | self.realign(); | ||
| 170 | return self.readableSlice(0)[0..len]; | ||
| 171 | } | ||
| 172 | } | ||
| 173 | |||
| 174 | /// Discard first `count` items in the fifo | ||
| 175 | pub fn discard(self: *Self, count: usize) void { | ||
| 176 | assert(count <= self.count); | ||
| 177 | { // set old range to undefined. Note: may be wrapped around | ||
| 178 | const slice = self.readableSliceMut(0); | ||
| 179 | if (slice.len >= count) { | ||
| 180 | const unused = mem.sliceAsBytes(slice[0..count]); | ||
| 181 | @memset(unused, undefined); | ||
| 182 | } else { | ||
| 183 | const unused = mem.sliceAsBytes(slice[0..]); | ||
| 184 | @memset(unused, undefined); | ||
| 185 | const unused2 = mem.sliceAsBytes(self.readableSliceMut(slice.len)[0 .. count - slice.len]); | ||
| 186 | @memset(unused2, undefined); | ||
| 187 | } | ||
| 188 | } | ||
| 189 | if (autoalign and self.count == count) { | ||
| 190 | self.head = 0; | ||
| 191 | self.count = 0; | ||
| 192 | } else { | ||
| 193 | var head = self.head + count; | ||
| 194 | if (powers_of_two) { | ||
| 195 | // Note it is safe to do a wrapping subtract as | ||
| 196 | // bitwise & with all 1s is a noop | ||
| 197 | head &= self.buf.len -% 1; | ||
| 198 | } else { | ||
| 199 | head %= self.buf.len; | ||
| 200 | } | ||
| 201 | self.head = head; | ||
| 202 | self.count -= count; | ||
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | /// Read the next item from the fifo | ||
| 207 | pub fn readItem(self: *Self) ?T { | ||
| 208 | if (self.count == 0) return null; | ||
| 209 | |||
| 210 | const c = self.buf[self.head]; | ||
| 211 | self.discard(1); | ||
| 212 | return c; | ||
| 213 | } | ||
| 214 | |||
| 215 | /// Read data from the fifo into `dst`, returns number of items copied. | ||
| 216 | pub fn read(self: *Self, dst: []T) usize { | ||
| 217 | var dst_left = dst; | ||
| 218 | |||
| 219 | while (dst_left.len > 0) { | ||
| 220 | const slice = self.readableSlice(0); | ||
| 221 | if (slice.len == 0) break; | ||
| 222 | const n = @min(slice.len, dst_left.len); | ||
| 223 | @memcpy(dst_left[0..n], slice[0..n]); | ||
| 224 | self.discard(n); | ||
| 225 | dst_left = dst_left[n..]; | ||
| 226 | } | ||
| 227 | |||
| 228 | return dst.len - dst_left.len; | ||
| 229 | } | ||
| 230 | |||
| 231 | /// Same as `read` except it returns an error union | ||
| 232 | /// The purpose of this function existing is to match `std.io.Reader` API. | ||
| 233 | fn readFn(self: *Self, dest: []u8) error{}!usize { | ||
| 234 | return self.read(dest); | ||
| 235 | } | ||
| 236 | |||
| 237 | pub fn reader(self: *Self) std.io.Reader { | ||
| 238 | return .{ | ||
| 239 | .context = self, | ||
| 240 | .vtable = &.{ | ||
| 241 | .read = &readerRead, | ||
| 242 | .readVec = &readerReadVec, | ||
| 243 | .discard = &readerDiscard, | ||
| 244 | }, | ||
| 245 | }; | ||
| 246 | } | ||
| 247 | fn readerRead( | ||
| 248 | ctx: ?*anyopaque, | ||
| 249 | bw: *std.io.BufferedWriter, | ||
| 250 | limit: std.io.Limit, | ||
| 251 | ) std.io.Reader.RwError!usize { | ||
| 252 | const fifo: *Self = @alignCast(@ptrCast(ctx)); | ||
| 253 | _ = fifo; | ||
| 254 | _ = bw; | ||
| 255 | _ = limit; | ||
| 256 | @panic("TODO"); | ||
| 257 | } | ||
| 258 | fn readerReadVec(ctx: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { | ||
| 259 | const fifo: *Self = @alignCast(@ptrCast(ctx)); | ||
| 260 | _ = fifo; | ||
| 261 | _ = data; | ||
| 262 | @panic("TODO"); | ||
| 263 | } | ||
| 264 | fn readerDiscard(ctx: ?*anyopaque, limit: std.io.Limit) std.io.Reader.Error!usize { | ||
| 265 | const fifo: *Self = @alignCast(@ptrCast(ctx)); | ||
| 266 | _ = fifo; | ||
| 267 | _ = limit; | ||
| 268 | @panic("TODO"); | ||
| 269 | } | ||
| 270 | |||
| 271 | /// Returns number of items available in fifo | ||
| 272 | pub fn writableLength(self: Self) usize { | ||
| 273 | return self.buf.len - self.count; | ||
| 274 | } | ||
| 275 | |||
| 276 | /// Returns the first section of writable buffer. | ||
| 277 | /// Note that this may be of length 0 | ||
| 278 | pub fn writableSlice(self: SliceSelfArg, offset: usize) []T { | ||
| 279 | if (offset > self.buf.len) return &[_]T{}; | ||
| 280 | |||
| 281 | const tail = self.head + offset + self.count; | ||
| 282 | if (tail < self.buf.len) { | ||
| 283 | return self.buf[tail..]; | ||
| 284 | } else { | ||
| 285 | return self.buf[tail - self.buf.len ..][0 .. self.writableLength() - offset]; | ||
| 286 | } | ||
| 287 | } | ||
| 288 | |||
| 289 | /// Returns a writable buffer of at least `size` items, allocating memory as needed. | ||
| 290 | /// Use `fifo.update` once you've written data to it. | ||
| 291 | pub fn writableWithSize(self: *Self, size: usize) ![]T { | ||
| 292 | try self.ensureUnusedCapacity(size); | ||
| 293 | |||
| 294 | // try to avoid realigning buffer | ||
| 295 | var slice = self.writableSlice(0); | ||
| 296 | if (slice.len < size) { | ||
| 297 | self.realign(); | ||
| 298 | slice = self.writableSlice(0); | ||
| 299 | } | ||
| 300 | return slice; | ||
| 301 | } | ||
| 302 | |||
| 303 | /// Update the tail location of the buffer (usually follows use of writable/writableWithSize) | ||
| 304 | pub fn update(self: *Self, count: usize) void { | ||
| 305 | assert(self.count + count <= self.buf.len); | ||
| 306 | self.count += count; | ||
| 307 | } | ||
| 308 | |||
| 309 | /// Appends the data in `src` to the fifo. | ||
| 310 | /// You must have ensured there is enough space. | ||
| 311 | pub fn writeAssumeCapacity(self: *Self, src: []const T) void { | ||
| 312 | assert(self.writableLength() >= src.len); | ||
| 313 | |||
| 314 | var src_left = src; | ||
| 315 | while (src_left.len > 0) { | ||
| 316 | const writable_slice = self.writableSlice(0); | ||
| 317 | assert(writable_slice.len != 0); | ||
| 318 | const n = @min(writable_slice.len, src_left.len); | ||
| 319 | @memcpy(writable_slice[0..n], src_left[0..n]); | ||
| 320 | self.update(n); | ||
| 321 | src_left = src_left[n..]; | ||
| 322 | } | ||
| 323 | } | ||
| 324 | |||
| 325 | /// Write a single item to the fifo | ||
| 326 | pub fn writeItem(self: *Self, item: T) !void { | ||
| 327 | try self.ensureUnusedCapacity(1); | ||
| 328 | return self.writeItemAssumeCapacity(item); | ||
| 329 | } | ||
| 330 | |||
| 331 | pub fn writeItemAssumeCapacity(self: *Self, item: T) void { | ||
| 332 | var tail = self.head + self.count; | ||
| 333 | if (powers_of_two) { | ||
| 334 | tail &= self.buf.len - 1; | ||
| 335 | } else { | ||
| 336 | tail %= self.buf.len; | ||
| 337 | } | ||
| 338 | self.buf[tail] = item; | ||
| 339 | self.update(1); | ||
| 340 | } | ||
| 341 | |||
| 342 | /// Appends the data in `src` to the fifo. | ||
| 343 | /// Allocates more memory as necessary | ||
| 344 | pub fn write(self: *Self, src: []const T) !void { | ||
| 345 | try self.ensureUnusedCapacity(src.len); | ||
| 346 | |||
| 347 | return self.writeAssumeCapacity(src); | ||
| 348 | } | ||
| 349 | |||
| 350 | /// Same as `write` except it returns the number of bytes written, which is always the same | ||
| 351 | /// as `bytes.len`. The purpose of this function existing is to match `std.io.Writer` API. | ||
| 352 | fn appendWrite(self: *Self, bytes: []const u8) error{OutOfMemory}!usize { | ||
| 353 | try self.write(bytes); | ||
| 354 | return bytes.len; | ||
| 355 | } | ||
| 356 | |||
| 357 | pub fn writer(fifo: *Self) std.io.Writer { | ||
| 358 | return .{ | ||
| 359 | .context = fifo, | ||
| 360 | .vtable = &.{ | ||
| 361 | .writeSplat = writerWriteSplat, | ||
| 362 | .writeFile = writerWriteFile, | ||
| 363 | }, | ||
| 364 | }; | ||
| 365 | } | ||
| 366 | fn writerWriteSplat(ctx: ?*anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Error!usize { | ||
| 367 | const fifo: *Self = @alignCast(@ptrCast(ctx)); | ||
| 368 | _ = fifo; | ||
| 369 | _ = data; | ||
| 370 | _ = splat; | ||
| 371 | @panic("TODO"); | ||
| 372 | } | ||
| 373 | fn writerWriteFile( | ||
| 374 | ctx: ?*anyopaque, | ||
| 375 | file: std.fs.File, | ||
| 376 | offset: std.io.Writer.Offset, | ||
| 377 | limit: std.io.Writer.Limit, | ||
| 378 | headers_and_trailers: []const []const u8, | ||
| 379 | headers_len: usize, | ||
| 380 | ) std.io.Writer.Error!usize { | ||
| 381 | const fifo: *Self = @alignCast(@ptrCast(ctx)); | ||
| 382 | _ = fifo; | ||
| 383 | _ = file; | ||
| 384 | _ = offset; | ||
| 385 | _ = limit; | ||
| 386 | _ = headers_and_trailers; | ||
| 387 | _ = headers_len; | ||
| 388 | @panic("TODO"); | ||
| 389 | } | ||
| 390 | |||
| 391 | /// Make `count` items available before the current read location | ||
| 392 | fn rewind(self: *Self, count: usize) void { | ||
| 393 | assert(self.writableLength() >= count); | ||
| 394 | |||
| 395 | var head = self.head + (self.buf.len - count); | ||
| 396 | if (powers_of_two) { | ||
| 397 | head &= self.buf.len - 1; | ||
| 398 | } else { | ||
| 399 | head %= self.buf.len; | ||
| 400 | } | ||
| 401 | self.head = head; | ||
| 402 | self.count += count; | ||
| 403 | } | ||
| 404 | |||
| 405 | /// Place data back into the read stream | ||
| 406 | pub fn unget(self: *Self, src: []const T) !void { | ||
| 407 | try self.ensureUnusedCapacity(src.len); | ||
| 408 | |||
| 409 | self.rewind(src.len); | ||
| 410 | |||
| 411 | const slice = self.readableSliceMut(0); | ||
| 412 | if (src.len < slice.len) { | ||
| 413 | @memcpy(slice[0..src.len], src); | ||
| 414 | } else { | ||
| 415 | @memcpy(slice, src[0..slice.len]); | ||
| 416 | const slice2 = self.readableSliceMut(slice.len); | ||
| 417 | @memcpy(slice2[0 .. src.len - slice.len], src[slice.len..]); | ||
| 418 | } | ||
| 419 | } | ||
| 420 | |||
| 421 | /// Returns the item at `offset`. | ||
| 422 | /// Asserts offset is within bounds. | ||
| 423 | pub fn peekItem(self: Self, offset: usize) T { | ||
| 424 | assert(offset < self.count); | ||
| 425 | |||
| 426 | var index = self.head + offset; | ||
| 427 | if (powers_of_two) { | ||
| 428 | index &= self.buf.len - 1; | ||
| 429 | } else { | ||
| 430 | index %= self.buf.len; | ||
| 431 | } | ||
| 432 | return self.buf[index]; | ||
| 433 | } | ||
| 434 | |||
| 435 | pub fn toOwnedSlice(self: *Self) Allocator.Error![]T { | ||
| 436 | if (self.head != 0) self.realign(); | ||
| 437 | assert(self.head == 0); | ||
| 438 | assert(self.count <= self.buf.len); | ||
| 439 | const allocator = self.allocator; | ||
| 440 | if (allocator.resize(self.buf, self.count)) { | ||
| 441 | const result = self.buf[0..self.count]; | ||
| 442 | self.* = Self.init(allocator); | ||
| 443 | return result; | ||
| 444 | } | ||
| 445 | const new_memory = try allocator.dupe(T, self.buf[0..self.count]); | ||
| 446 | allocator.free(self.buf); | ||
| 447 | self.* = Self.init(allocator); | ||
| 448 | return new_memory; | ||
| 449 | } | ||
| 450 | }; | ||
| 451 | } | ||
| 452 | |||
| 453 | test "LinearFifo(u8, .Dynamic) discard(0) from empty buffer should not error on overflow" { | ||
| 454 | var fifo = LinearFifo(u8, .Dynamic).init(testing.allocator); | ||
| 455 | defer fifo.deinit(); | ||
| 456 | |||
| 457 | // If overflow is not explicitly allowed this will crash in debug / safe mode | ||
| 458 | fifo.discard(0); | ||
| 459 | } | ||
| 460 | |||
| 461 | test "LinearFifo(u8, .Dynamic)" { | ||
| 462 | var fifo = LinearFifo(u8, .Dynamic).init(testing.allocator); | ||
| 463 | defer fifo.deinit(); | ||
| 464 | |||
| 465 | try fifo.write("HELLO"); | ||
| 466 | try testing.expectEqual(@as(usize, 5), fifo.readableLength()); | ||
| 467 | try testing.expectEqualSlices(u8, "HELLO", fifo.readableSlice(0)); | ||
| 468 | |||
| 469 | { | ||
| 470 | var i: usize = 0; | ||
| 471 | while (i < 5) : (i += 1) { | ||
| 472 | try fifo.write(&[_]u8{fifo.peekItem(i)}); | ||
| 473 | } | ||
| 474 | try testing.expectEqual(@as(usize, 10), fifo.readableLength()); | ||
| 475 | try testing.expectEqualSlices(u8, "HELLOHELLO", fifo.readableSlice(0)); | ||
| 476 | } | ||
| 477 | |||
| 478 | { | ||
| 479 | try testing.expectEqual(@as(u8, 'H'), fifo.readItem().?); | ||
| 480 | try testing.expectEqual(@as(u8, 'E'), fifo.readItem().?); | ||
| 481 | try testing.expectEqual(@as(u8, 'L'), fifo.readItem().?); | ||
| 482 | try testing.expectEqual(@as(u8, 'L'), fifo.readItem().?); | ||
| 483 | try testing.expectEqual(@as(u8, 'O'), fifo.readItem().?); | ||
| 484 | } | ||
| 485 | try testing.expectEqual(@as(usize, 5), fifo.readableLength()); | ||
| 486 | |||
| 487 | { // Writes that wrap around | ||
| 488 | try testing.expectEqual(@as(usize, 11), fifo.writableLength()); | ||
| 489 | try testing.expectEqual(@as(usize, 6), fifo.writableSlice(0).len); | ||
| 490 | fifo.writeAssumeCapacity("6<chars<11"); | ||
| 491 | try testing.expectEqualSlices(u8, "HELLO6<char", fifo.readableSlice(0)); | ||
| 492 | try testing.expectEqualSlices(u8, "s<11", fifo.readableSlice(11)); | ||
| 493 | try testing.expectEqualSlices(u8, "11", fifo.readableSlice(13)); | ||
| 494 | try testing.expectEqualSlices(u8, "", fifo.readableSlice(15)); | ||
| 495 | fifo.discard(11); | ||
| 496 | try testing.expectEqualSlices(u8, "s<11", fifo.readableSlice(0)); | ||
| 497 | fifo.discard(4); | ||
| 498 | try testing.expectEqual(@as(usize, 0), fifo.readableLength()); | ||
| 499 | } | ||
| 500 | |||
| 501 | { | ||
| 502 | const buf = try fifo.writableWithSize(12); | ||
| 503 | try testing.expectEqual(@as(usize, 12), buf.len); | ||
| 504 | var i: u8 = 0; | ||
| 505 | while (i < 10) : (i += 1) { | ||
| 506 | buf[i] = i + 'a'; | ||
| 507 | } | ||
| 508 | fifo.update(10); | ||
| 509 | try testing.expectEqualSlices(u8, "abcdefghij", fifo.readableSlice(0)); | ||
| 510 | } | ||
| 511 | |||
| 512 | { | ||
| 513 | try fifo.unget("prependedstring"); | ||
| 514 | var result: [30]u8 = undefined; | ||
| 515 | try testing.expectEqualSlices(u8, "prependedstringabcdefghij", result[0..fifo.read(&result)]); | ||
| 516 | try fifo.unget("b"); | ||
| 517 | try fifo.unget("a"); | ||
| 518 | try testing.expectEqualSlices(u8, "ab", result[0..fifo.read(&result)]); | ||
| 519 | } | ||
| 520 | |||
| 521 | fifo.shrink(0); | ||
| 522 | |||
| 523 | { | ||
| 524 | try fifo.writer().print("{s}, {s}!", .{ "Hello", "World" }); | ||
| 525 | var result: [30]u8 = undefined; | ||
| 526 | try testing.expectEqualSlices(u8, "Hello, World!", result[0..fifo.read(&result)]); | ||
| 527 | try testing.expectEqual(@as(usize, 0), fifo.readableLength()); | ||
| 528 | } | ||
| 529 | |||
| 530 | { | ||
| 531 | try fifo.writer().writeAll("This is a test"); | ||
| 532 | var result: [30]u8 = undefined; | ||
| 533 | try testing.expectEqualSlices(u8, "This", (try fifo.reader().readUntilDelimiterOrEof(&result, ' ')).?); | ||
| 534 | try testing.expectEqualSlices(u8, "is", (try fifo.reader().readUntilDelimiterOrEof(&result, ' ')).?); | ||
| 535 | try testing.expectEqualSlices(u8, "a", (try fifo.reader().readUntilDelimiterOrEof(&result, ' ')).?); | ||
| 536 | try testing.expectEqualSlices(u8, "test", (try fifo.reader().readUntilDelimiterOrEof(&result, ' ')).?); | ||
| 537 | } | ||
| 538 | |||
| 539 | { | ||
| 540 | try fifo.ensureTotalCapacity(1); | ||
| 541 | var in_fbs = std.io.fixedBufferStream("pump test"); | ||
| 542 | var out_buf: [50]u8 = undefined; | ||
| 543 | var out_fbs = std.io.fixedBufferStream(&out_buf); | ||
| 544 | try fifo.pump(in_fbs.reader(), out_fbs.writer()); | ||
| 545 | try testing.expectEqualSlices(u8, in_fbs.buffer, out_fbs.getWritten()); | ||
| 546 | } | ||
| 547 | } | ||
| 548 | |||
| 549 | test LinearFifo { | ||
| 550 | inline for ([_]type{ u1, u8, u16, u64 }) |T| { | ||
| 551 | inline for ([_]LinearFifoBufferType{ LinearFifoBufferType{ .Static = 32 }, .Slice, .Dynamic }) |bt| { | ||
| 552 | const FifoType = LinearFifo(T, bt); | ||
| 553 | var buf: if (bt == .Slice) [32]T else void = undefined; | ||
| 554 | var fifo = switch (bt) { | ||
| 555 | .Static => FifoType.init(), | ||
| 556 | .Slice => FifoType.init(buf[0..]), | ||
| 557 | .Dynamic => FifoType.init(testing.allocator), | ||
| 558 | }; | ||
| 559 | defer fifo.deinit(); | ||
| 560 | |||
| 561 | try fifo.write(&[_]T{ 0, 1, 1, 0, 1 }); | ||
| 562 | try testing.expectEqual(@as(usize, 5), fifo.readableLength()); | ||
| 563 | |||
| 564 | { | ||
| 565 | try testing.expectEqual(@as(T, 0), fifo.readItem().?); | ||
| 566 | try testing.expectEqual(@as(T, 1), fifo.readItem().?); | ||
| 567 | try testing.expectEqual(@as(T, 1), fifo.readItem().?); | ||
| 568 | try testing.expectEqual(@as(T, 0), fifo.readItem().?); | ||
| 569 | try testing.expectEqual(@as(T, 1), fifo.readItem().?); | ||
| 570 | try testing.expectEqual(@as(usize, 0), fifo.readableLength()); | ||
| 571 | } | ||
| 572 | |||
| 573 | { | ||
| 574 | try fifo.writeItem(1); | ||
| 575 | try fifo.writeItem(1); | ||
| 576 | try fifo.writeItem(1); | ||
| 577 | try testing.expectEqual(@as(usize, 3), fifo.readableLength()); | ||
| 578 | } | ||
| 579 | |||
| 580 | { | ||
| 581 | var readBuf: [3]T = undefined; | ||
| 582 | const n = fifo.read(&readBuf); | ||
| 583 | try testing.expectEqual(@as(usize, 3), n); // NOTE: It should be the number of items. | ||
| 584 | } | ||
| 585 | } | ||
| 586 | } | ||
| 587 | } | ||
src/link/MachO/dyld_info/Trie.zig+2-1| ... | @@ -138,7 +138,7 @@ fn finalize(self: *Trie, allocator: Allocator) !void { | ... | @@ -138,7 +138,7 @@ fn finalize(self: *Trie, allocator: Allocator) !void { |
| 138 | defer ordered_nodes.deinit(); | 138 | defer ordered_nodes.deinit(); |
| 139 | try ordered_nodes.ensureTotalCapacityPrecise(self.nodes.items(.is_terminal).len); | 139 | try ordered_nodes.ensureTotalCapacityPrecise(self.nodes.items(.is_terminal).len); |
| 140 | 140 | ||
| 141 | var fifo = std.fifo.LinearFifo(Node.Index, .Dynamic).init(allocator); | 141 | var fifo = DeprecatedLinearFifo(Node.Index).init(allocator); |
| 142 | defer fifo.deinit(); | 142 | defer fifo.deinit(); |
| 143 | 143 | ||
| 144 | try fifo.writeItem(self.root.?); | 144 | try fifo.writeItem(self.root.?); |
| ... | @@ -409,6 +409,7 @@ const mem = std.mem; | ... | @@ -409,6 +409,7 @@ const mem = std.mem; |
| 409 | const std = @import("std"); | 409 | const std = @import("std"); |
| 410 | const testing = std.testing; | 410 | const testing = std.testing; |
| 411 | const trace = @import("../../../tracy.zig").trace; | 411 | const trace = @import("../../../tracy.zig").trace; |
| 412 | const DeprecatedLinearFifo = @import("../../../deprecated.zig").LinearFifo; | ||
| 412 | 413 | ||
| 413 | const Allocator = mem.Allocator; | 414 | const Allocator = mem.Allocator; |
| 414 | const MachO = @import("../../MachO.zig"); | 415 | const MachO = @import("../../MachO.zig"); |