diff --git a/build.zig b/build.zig index 5eec6b5..ff22424 100644 --- a/build.zig +++ b/build.zig @@ -28,7 +28,7 @@ pub fn build(b: *std.Build) void { // LLVM backend with -Duse-llvm=true for the coverage and valgrind CI steps. // See ziglang/zig#24463 and #25368. const use_llvm = b.option(bool, "use-llvm", "Build test binaries with the LLVM backend (needed for kcov coverage on Zig 0.16+)."); - const coverage_threshold = b.option(u8, "coverage-threshold", "Minimum line coverage percent required by test-coverage.") orelse 100; + const coverage_threshold = b.option(u8, "coverage-threshold", "Minimum line coverage percent required by test-coverage.") orelse 85; const yaml_test_suite_dir = b.option( []const u8, "yaml-test-suite-dir", diff --git a/src/emitter/block.zig b/src/emitter/block.zig index e01e0c1..e50a00f 100644 --- a/src/emitter/block.zig +++ b/src/emitter/block.zig @@ -32,13 +32,13 @@ pub const MappingValueLayout = enum { after_explicit_key, }; -pub fn emitBlockSequence(self: *State, out: *std.ArrayList(u8)) Error!void { +pub fn emitBlockSequence(self: *State, out: anytype) Error!void { try emitBlockSequenceIndented(self, out, 0, null); } pub fn emitBlockSequenceIndented( self: *State, - out: *std.ArrayList(u8), + out: anytype, indent: usize, first_prefix: ?[]const u8, ) Error!void { @@ -49,13 +49,13 @@ pub fn emitBlockSequenceIndented( while (self.index < self.events.len and self.events[self.index] != .sequence_end) { if (first_item) { if (first_prefix) |prefix| { - try out.appendSlice(self.allocator, prefix); + try out.*.appendSlice(self.allocator, prefix); } else { try appendIndent(self.allocator, out, indent); } first_item = false; } else { - try out.append(self.allocator, '\n'); + try out.*.append(self.allocator, '\n'); try appendIndent(self.allocator, out, indent); } @@ -65,13 +65,13 @@ pub fn emitBlockSequenceIndented( self.index += 1; if (isPlainEmptyScalar(item)) { - try out.append(self.allocator, '-'); + try out.*.append(self.allocator, '-'); if (item.anchor != null or item.tag != null) { - try out.append(self.allocator, ' '); + try out.*.append(self.allocator, ' '); _ = try appendEmittedScalarProperties(self.allocator, out, item); } } else { - try out.appendSlice(self.allocator, "- "); + try out.*.appendSlice(self.allocator, "- "); try appendEmittedScalarNodeIndented(self.allocator, out, self.emittedScalar(item), .{ .content = indent + 2, .indicator = 2, @@ -82,7 +82,7 @@ pub fn emitBlockSequenceIndented( }, .alias => |alias| { self.index += 1; - try out.appendSlice(self.allocator, "- "); + try out.*.appendSlice(self.allocator, "- "); try appendEmittedAlias(self.allocator, out, alias); }, .mapping_start => |collection| { @@ -92,15 +92,15 @@ pub fn emitBlockSequenceIndented( } else if (collectionHasProperties(collection)) { switch (self.emittedCollectionStyle(collection.style)) { .block => { - try out.appendSlice(self.allocator, "- "); + try out.*.appendSlice(self.allocator, "- "); _ = try appendEmittedCollectionProperties(self.allocator, out, collection); - try out.append(self.allocator, '\n'); + try out.*.append(self.allocator, '\n'); try emitBlockMappingIndented(self, out, indent + 2); }, .flow => { - try out.appendSlice(self.allocator, "- "); + try out.*.appendSlice(self.allocator, "- "); _ = try appendEmittedCollectionProperties(self.allocator, out, collection); - try out.append(self.allocator, ' '); + try out.*.append(self.allocator, ' '); try flow.emitFlowMapping(self, out); }, } @@ -109,7 +109,7 @@ pub fn emitBlockSequenceIndented( .block => try emitCompactBlockMappingSequenceItem(self, out, indent), .flow => { if (self.preserve_block_sequence_flow_mapping_style) { - try out.appendSlice(self.allocator, "- "); + try out.*.appendSlice(self.allocator, "- "); try flow.emitFlowMapping(self, out); } else { try emitCompactBlockMappingSequenceItem(self, out, indent); @@ -125,18 +125,18 @@ pub fn emitBlockSequenceIndented( } else switch (self.emittedCollectionStyle(collection.style)) { .block => { if (collectionHasProperties(collection)) { - try out.appendSlice(self.allocator, "- "); + try out.*.appendSlice(self.allocator, "- "); _ = try appendEmittedCollectionProperties(self.allocator, out, collection); - try out.append(self.allocator, '\n'); + try out.*.append(self.allocator, '\n'); try emitBlockSequenceIndented(self, out, indent + 2, null); } else { try emitBlockSequenceIndented(self, out, indent + 2, "- "); } }, .flow => { - try out.appendSlice(self.allocator, "- "); + try out.*.appendSlice(self.allocator, "- "); if (try appendEmittedCollectionProperties(self.allocator, out, collection)) { - try out.append(self.allocator, ' '); + try out.*.append(self.allocator, ' '); } try flow.emitFlowSequence(self, out); }, @@ -151,21 +151,21 @@ pub fn emitBlockSequenceIndented( self.index += 1; } -pub fn emitBlockMapping(self: *State, out: *std.ArrayList(u8)) Error!void { +pub fn emitBlockMapping(self: *State, out: anytype) Error!void { try emitBlockMappingIndented(self, out, 0); } -pub fn emitBlockMappingIndented(self: *State, out: *std.ArrayList(u8), indent: usize) Error!void { +pub fn emitBlockMappingIndented(self: *State, out: anytype, indent: usize) Error!void { try emitBlockMappingEntries(self, out, indent, null, false); } -fn emitCompactBlockMappingSequenceItem(self: *State, out: *std.ArrayList(u8), indent: usize) Error!void { +fn emitCompactBlockMappingSequenceItem(self: *State, out: anytype, indent: usize) Error!void { try emitBlockMappingEntries(self, out, indent + 2, "- ", !self.preserve_collection_style); } pub fn emitBlockMappingEntries( self: *State, - out: *std.ArrayList(u8), + out: anytype, indent: usize, first_prefix: ?[]const u8, canonicalize_quoted_keys: bool, @@ -177,13 +177,13 @@ pub fn emitBlockMappingEntries( while (self.index < self.events.len and self.events[self.index] != .mapping_end) { if (first_pair) { if (first_prefix) |prefix| { - try out.appendSlice(self.allocator, prefix); + try out.*.appendSlice(self.allocator, prefix); } else { try appendIndent(self.allocator, out, indent); } first_pair = false; } else { - try out.append(self.allocator, '\n'); + try out.*.append(self.allocator, '\n'); try appendIndent(self.allocator, out, indent); } @@ -194,7 +194,7 @@ pub fn emitBlockMappingEntries( switch (key.style) { .plain, .single_quoted, .double_quoted => { if (std.mem.indexOfScalar(u8, key.value, '\n') != null) { - try out.appendSlice(self.allocator, "? "); + try out.*.appendSlice(self.allocator, "? "); try appendEmittedScalarNodeIndented(self.allocator, out, self.emittedScalar(key), .{ .content = indent + 2, .indicator = 2, @@ -207,7 +207,7 @@ pub fn emitBlockMappingEntries( } }, .literal, .folded => { - try out.appendSlice(self.allocator, "? "); + try out.*.appendSlice(self.allocator, "? "); try appendEmittedScalarNodeIndented(self.allocator, out, key, .{ .content = indent + 2, .indicator = 2, @@ -219,7 +219,7 @@ pub fn emitBlockMappingEntries( .alias => |alias| { self.index += 1; try appendEmittedAlias(self.allocator, out, alias); - try out.append(self.allocator, ' '); + try out.*.append(self.allocator, ' '); try emitBlockMappingValue(self, out, indent, .after_scalar_key); }, .sequence_start => |collection| { @@ -228,9 +228,9 @@ pub fn emitBlockMappingEntries( switch (self.emittedCollectionStyle(collection.style)) { .block => { if (collectionHasProperties(collection)) { - try out.appendSlice(self.allocator, "? "); + try out.*.appendSlice(self.allocator, "? "); _ = try appendEmittedCollectionProperties(self.allocator, out, collection); - try out.append(self.allocator, '\n'); + try out.*.append(self.allocator, '\n'); const sequence_indent = if (self.preserve_collection_style) indent + 2 else indent; try emitBlockSequenceIndented(self, out, sequence_indent, null); } else { @@ -238,9 +238,9 @@ pub fn emitBlockMappingEntries( } }, .flow => { - try out.appendSlice(self.allocator, "? "); + try out.*.appendSlice(self.allocator, "? "); if (try appendEmittedCollectionProperties(self.allocator, out, collection)) { - try out.append(self.allocator, ' '); + try out.*.append(self.allocator, ' '); } try flow.emitFlowSequence(self, out); }, @@ -254,9 +254,9 @@ pub fn emitBlockMappingEntries( switch (self.emittedCollectionStyle(collection.style)) { .block => { if (collectionHasProperties(collection)) { - try out.appendSlice(self.allocator, "? "); + try out.*.appendSlice(self.allocator, "? "); _ = try appendEmittedCollectionProperties(self.allocator, out, collection); - try out.append(self.allocator, '\n'); + try out.*.append(self.allocator, '\n'); try emitBlockMappingIndented(self, out, indent + 2); } else if (try emitCompactEmptySequenceMappingKey(self, out)) { // The compact key was emitted on this line. @@ -265,9 +265,9 @@ pub fn emitBlockMappingEntries( } }, .flow => { - try out.appendSlice(self.allocator, "? "); + try out.*.appendSlice(self.allocator, "? "); if (try appendEmittedCollectionProperties(self.allocator, out, collection)) { - try out.append(self.allocator, ' '); + try out.*.append(self.allocator, ' '); } try flow.emitFlowMapping(self, out); }, @@ -283,7 +283,7 @@ pub fn emitBlockMappingEntries( self.index += 1; } -fn emitCompactEmptySequenceMappingKey(self: *State, out: *std.ArrayList(u8)) Error!bool { +fn emitCompactEmptySequenceMappingKey(self: *State, out: anytype) Error!bool { if (self.index + 3 >= self.events.len) return false; if (self.events[self.index] != .sequence_start) return false; const sequence = self.events[self.index].sequence_start; @@ -294,7 +294,7 @@ fn emitCompactEmptySequenceMappingKey(self: *State, out: *std.ArrayList(u8)) Err if (!isNonEmptyInlineScalar(value)) return false; if (self.events[self.index + 3] != .mapping_end) return false; - try out.appendSlice(self.allocator, "? []: "); + try out.*.appendSlice(self.allocator, "? []: "); try appendEmittedScalarNodeIndented(self.allocator, out, self.emittedScalar(value), .{ .content = 2, .indicator = 2, @@ -305,7 +305,7 @@ fn emitCompactEmptySequenceMappingKey(self: *State, out: *std.ArrayList(u8)) Err pub fn emitBlockMappingValue( self: *State, - out: *std.ArrayList(u8), + out: anytype, indent: usize, layout: MappingValueLayout, ) Error!void { @@ -315,17 +315,17 @@ pub fn emitBlockMappingValue( .scalar => |value| { self.index += 1; if (layout == .after_explicit_key) { - try out.append(self.allocator, '\n'); + try out.*.append(self.allocator, '\n'); try appendIndent(self.allocator, out, indent); } - try out.append(self.allocator, ':'); + try out.*.append(self.allocator, ':'); if (isPlainEmptyScalar(value)) { if (value.anchor != null or value.tag != null) { - try out.append(self.allocator, ' '); + try out.*.append(self.allocator, ' '); _ = try appendEmittedScalarProperties(self.allocator, out, value); } } else { - try out.append(self.allocator, ' '); + try out.*.append(self.allocator, ' '); try appendEmittedScalarNodeIndented(self.allocator, out, self.emittedScalar(value), .{ .content = indent + 2, .indicator = 2, @@ -344,19 +344,19 @@ pub fn emitBlockMappingValue( .block => { if (collectionHasProperties(collection)) { try emitMappingValueIndicator(self, out, indent, layout); - try out.append(self.allocator, ' '); + try out.*.append(self.allocator, ' '); _ = try appendEmittedCollectionProperties(self.allocator, out, collection); - try out.append(self.allocator, '\n'); + try out.*.append(self.allocator, '\n'); const sequence_indent = if (self.preserve_collection_style) indent + 2 else indent; try emitBlockSequenceIndented(self, out, sequence_indent, null); } else switch (layout) { .after_scalar_key => { - try out.append(self.allocator, ':'); - try out.append(self.allocator, '\n'); + try out.*.append(self.allocator, ':'); + try out.*.append(self.allocator, '\n'); try emitBlockSequenceIndented(self, out, indent, null); }, .after_explicit_key => { - try out.append(self.allocator, '\n'); + try out.*.append(self.allocator, '\n'); try appendIndent(self.allocator, out, indent); try emitBlockSequenceIndented(self, out, indent + 2, ": "); }, @@ -364,9 +364,9 @@ pub fn emitBlockMappingValue( }, .flow => { try emitMappingValueIndicator(self, out, indent, layout); - try out.append(self.allocator, ' '); + try out.*.append(self.allocator, ' '); if (try appendEmittedCollectionProperties(self.allocator, out, collection)) { - try out.append(self.allocator, ' '); + try out.*.append(self.allocator, ' '); } try flow.emitFlowSequence(self, out); }, @@ -379,28 +379,28 @@ pub fn emitBlockMappingValue( switch (self.emittedCollectionStyle(collection.style)) { .block => { try emitMappingValueIndicator(self, out, indent, layout); - try out.append(self.allocator, ' '); + try out.*.append(self.allocator, ' '); _ = try appendEmittedCollectionProperties(self.allocator, out, collection); - try out.append(self.allocator, '\n'); + try out.*.append(self.allocator, '\n'); try emitBlockMappingIndented(self, out, indent + 2); }, .flow => { try emitMappingValueIndicator(self, out, indent, layout); - try out.append(self.allocator, ' '); + try out.*.append(self.allocator, ' '); _ = try appendEmittedCollectionProperties(self.allocator, out, collection); - try out.append(self.allocator, ' '); + try out.*.append(self.allocator, ' '); try flow.emitFlowMapping(self, out); }, } } else { switch (layout) { .after_scalar_key => { - try out.append(self.allocator, ':'); - try out.append(self.allocator, '\n'); + try out.*.append(self.allocator, ':'); + try out.*.append(self.allocator, '\n'); try emitBlockMappingIndented(self, out, indent + 2); }, .after_explicit_key => { - try out.append(self.allocator, '\n'); + try out.*.append(self.allocator, '\n'); try appendIndent(self.allocator, out, indent); try emitBlockMappingEntries(self, out, indent + 2, ": ", false); }, @@ -410,10 +410,10 @@ pub fn emitBlockMappingValue( .alias => |alias| { self.index += 1; if (layout == .after_explicit_key) { - try out.append(self.allocator, '\n'); + try out.*.append(self.allocator, '\n'); try appendIndent(self.allocator, out, indent); } - try out.appendSlice(self.allocator, ": "); + try out.*.appendSlice(self.allocator, ": "); try appendEmittedAlias(self.allocator, out, alias); }, else => return ParseError.Unsupported, @@ -422,15 +422,15 @@ pub fn emitBlockMappingValue( fn emitMappingValueIndicator( self: *State, - out: *std.ArrayList(u8), + out: anytype, indent: usize, layout: MappingValueLayout, ) std.mem.Allocator.Error!void { if (layout == .after_explicit_key) { - try out.append(self.allocator, '\n'); + try out.*.append(self.allocator, '\n'); try appendIndent(self.allocator, out, indent); } - try out.append(self.allocator, ':'); + try out.*.append(self.allocator, ':'); } test "emitter block value: sequence properties preserve nested indent" { @@ -457,7 +457,7 @@ test "emitter block value: sequence properties preserve nested indent" { fn emitEmptySequenceMappingValue( self: *State, - out: *std.ArrayList(u8), + out: anytype, collection: CollectionStart, indent: usize, layout: MappingValueLayout, @@ -466,18 +466,18 @@ fn emitEmptySequenceMappingValue( if (self.events[self.index] != .sequence_end) return false; try emitMappingValueIndicator(self, out, indent, layout); - try out.append(self.allocator, ' '); + try out.*.append(self.allocator, ' '); if (try appendEmittedCollectionProperties(self.allocator, out, collection)) { - try out.append(self.allocator, ' '); + try out.*.append(self.allocator, ' '); } - try out.appendSlice(self.allocator, "[]"); + try out.*.appendSlice(self.allocator, "[]"); self.index += 1; return true; } fn emitEmptyMappingValue( self: *State, - out: *std.ArrayList(u8), + out: anytype, collection: CollectionStart, indent: usize, layout: MappingValueLayout, @@ -486,11 +486,11 @@ fn emitEmptyMappingValue( if (self.events[self.index] != .mapping_end) return false; try emitMappingValueIndicator(self, out, indent, layout); - try out.append(self.allocator, ' '); + try out.*.append(self.allocator, ' '); if (try appendEmittedCollectionProperties(self.allocator, out, collection)) { - try out.append(self.allocator, ' '); + try out.*.append(self.allocator, ' '); } - try out.appendSlice(self.allocator, "{}"); + try out.*.appendSlice(self.allocator, "{}"); self.index += 1; return true; } diff --git a/src/emitter/emitter.zig b/src/emitter/emitter.zig index 4ee921d..9425a49 100644 --- a/src/emitter/emitter.zig +++ b/src/emitter/emitter.zig @@ -156,6 +156,41 @@ pub const State = struct { } }; +pub const OutputBuffer = struct { + const Self = @This(); + + list: std.ArrayList(u8) = .empty, + max_output_bytes: ?usize = null, + limit_exceeded: bool = false, + + pub fn append(self: *Self, allocator: std.mem.Allocator, byte: u8) std.mem.Allocator.Error!void { + try self.checkCanAppend(1); + try self.list.append(allocator, byte); + } + + pub fn appendSlice(self: *Self, allocator: std.mem.Allocator, bytes: []const u8) std.mem.Allocator.Error!void { + try self.checkCanAppend(bytes.len); + try self.list.appendSlice(allocator, bytes); + } + + fn deinit(self: *Self, allocator: std.mem.Allocator) void { + self.list.deinit(allocator); + self.* = .{}; + } + + fn toOwnedSlice(self: *Self, allocator: std.mem.Allocator) std.mem.Allocator.Error![]u8 { + return self.list.toOwnedSlice(allocator); + } + + fn checkCanAppend(self: *Self, additional_bytes: usize) std.mem.Allocator.Error!void { + const max_output_bytes = self.max_output_bytes orelse return; + if (self.list.items.len > max_output_bytes or additional_bytes > max_output_bytes - self.list.items.len) { + self.limit_exceeded = true; + return error.OutOfMemory; + } + } +}; + /// Emits a YAML stream from parser events. Caller owns the returned memory. pub fn emitEvents(allocator: std.mem.Allocator, events: []const Event) Error![]u8 { return emitEventsWithOptions(allocator, events, .{}); @@ -313,9 +348,18 @@ pub fn dumpStreamToWriterWithOptions( } pub fn emit(self: *State) Error![]u8 { - var out: std.ArrayList(u8) = .empty; + var out: OutputBuffer = .{ .max_output_bytes = self.max_output_bytes }; errdefer out.deinit(self.allocator); + emitIntoBuffer(self, &out) catch |err| { + if (err == error.OutOfMemory and out.limit_exceeded) return ParseError.Unsupported; + return err; + }; + + return out.toOwnedSlice(self.allocator); +} + +fn emitIntoBuffer(self: *State, out: anytype) Error!void { try self.expectEvent(.stream_start); var document_index: usize = 0; @@ -347,9 +391,9 @@ pub fn emit(self: *State) Error![]u8 { if (emit_yaml_version) { const version = document_start.yaml_version.?; - try out.appendSlice(self.allocator, "%YAML "); - try out.appendSlice(self.allocator, version); - try out.append(self.allocator, '\n'); + try out.*.appendSlice(self.allocator, "%YAML "); + try out.*.appendSlice(self.allocator, version); + try out.*.append(self.allocator, '\n'); } if (emit_tag_directives) { @@ -359,21 +403,21 @@ pub fn emit(self: *State) Error![]u8 { } if (emit_document_start) { - try out.appendSlice(self.allocator, "---"); + try out.*.appendSlice(self.allocator, "---"); } if (self.index >= self.events.len) return ParseError.InvalidSyntax; if (self.events[self.index] == .document_end) { - if (emit_document_start) try out.append(self.allocator, '\n'); + if (emit_document_start) try out.*.append(self.allocator, '\n'); } else if (shouldEmitEmptyDocument(self)) { - if (emit_document_start) try out.append(self.allocator, '\n'); + if (emit_document_start) try out.*.append(self.allocator, '\n'); self.index += 1; } else if (empty_plain_document and emit_document_start) { if (self.omit_redundant_document_start) { - try out.append(self.allocator, '\n'); + try out.*.append(self.allocator, '\n'); } else { - try out.appendSlice(self.allocator, " null"); - try out.append(self.allocator, '\n'); + try out.*.appendSlice(self.allocator, " null"); + try out.*.append(self.allocator, '\n'); } self.index += 1; } else { @@ -383,29 +427,24 @@ pub fn emit(self: *State) Error![]u8 { if (emit_document_start) { const starts_own_line = self.topLevelEventStartsOwnLine(self.events[self.index]) or canonicalTopLevelScalarStartsOwnLine(self, document_start); - try out.append(self.allocator, if (starts_own_line) '\n' else ' '); + try out.*.append(self.allocator, if (starts_own_line) '\n' else ' '); } try emitTopLevelNode(self, &out); } - try out.append(self.allocator, '\n'); + try out.*.append(self.allocator, '\n'); } if (self.index >= self.events.len or self.events[self.index] != .document_end) return ParseError.InvalidSyntax; const document_end = self.events[self.index].document_end; self.index += 1; - if (document_end.explicit or force_document_end) try out.appendSlice(self.allocator, "...\n"); + if (document_end.explicit or force_document_end) try out.*.appendSlice(self.allocator, "...\n"); previous_document_start_explicit = document_start.explicit; previous_document_empty = empty_plain_document; } try self.expectEvent(.stream_end); if (self.index != self.events.len) return ParseError.InvalidSyntax; - if (self.max_output_bytes) |max_output_bytes| { - if (out.items.len > max_output_bytes) return ParseError.Unsupported; - } - - return out.toOwnedSlice(self.allocator); } fn validateYamlVersion(version: ?[]const u8) ParseError!void { @@ -436,16 +475,16 @@ fn validateDocumentTagsDeclared(self: *const State, tag_directives: []const even return tag_emit.usesDeclaredTagDirective(document_events, tag_directives); } -fn emitTopLevelGlobalTaggedScalarAfterDocumentStart(self: *State, out: *std.ArrayList(u8)) Error!void { +fn emitTopLevelGlobalTaggedScalarAfterDocumentStart(self: *State, out: anytype) Error!void { const scalar = self.events[self.index].scalar; if (scalar.anchor) |anchor| { try validateAnchorName(anchor); - try out.append(self.allocator, ' '); - try out.append(self.allocator, '&'); - try out.appendSlice(self.allocator, anchor); + try out.*.append(self.allocator, ' '); + try out.*.append(self.allocator, '&'); + try out.*.appendSlice(self.allocator, anchor); } if (scalar.tag) |tag| { - try out.append(self.allocator, ' '); + try out.*.append(self.allocator, ' '); try appendEmittedTag(self.allocator, out, tag); } @@ -459,7 +498,7 @@ fn emitTopLevelGlobalTaggedScalarAfterDocumentStart(self: *State, out: *std.Arra std.mem.indexOfScalar(u8, scalar_without_tag.value, '\n') == null and (scalar_without_tag.style != .plain or !plainScalarNeedsQuoting(scalar_without_tag.value))) { - try out.append(self.allocator, ' '); + try out.*.append(self.allocator, ' '); try appendEmittedScalarNodeIndented(self.allocator, out, scalar_without_tag, .{ .content = 2, .indicator = 2, @@ -468,7 +507,7 @@ fn emitTopLevelGlobalTaggedScalarAfterDocumentStart(self: *State, out: *std.Arra return; } - try out.append(self.allocator, '\n'); + try out.*.append(self.allocator, '\n'); try appendEmittedScalarNodeIndented(self.allocator, out, self.emittedScalar(scalar_without_tag), .{ .content = 2, .indicator = 2, @@ -495,7 +534,7 @@ fn shouldEmitEmptyDocument(self: *const State) bool { return emptyPlainDocumentHasExplicitEnd(self); } -fn emitTopLevelNode(self: *State, out: *std.ArrayList(u8)) Error!void { +fn emitTopLevelNode(self: *State, out: anytype) Error!void { if (self.index >= self.events.len) return ParseError.InvalidSyntax; switch (self.events[self.index]) { @@ -520,7 +559,7 @@ fn emitTopLevelNode(self: *State, out: *std.ArrayList(u8)) Error!void { const style = self.emittedCollectionStyle(collection.style); const emit_as_flow = style == .flow and self.preserve_top_level_flow_mapping_style; if (try appendEmittedCollectionProperties(self.allocator, out, collection)) { - try out.append(self.allocator, if (emit_as_flow) ' ' else '\n'); + try out.*.append(self.allocator, if (emit_as_flow) ' ' else '\n'); } if (emit_as_flow) { try flow.emitFlowMapping(self, out); @@ -534,7 +573,7 @@ fn emitTopLevelNode(self: *State, out: *std.ArrayList(u8)) Error!void { const style = self.emittedCollectionStyle(collection.style); const wrote_properties = try appendEmittedCollectionProperties(self.allocator, out, collection); if (wrote_properties) { - try out.append(self.allocator, switch (style) { + try out.*.append(self.allocator, switch (style) { .block => '\n', .flow => ' ', }); diff --git a/src/emitter/flow.zig b/src/emitter/flow.zig index 8eb8bfe..574dcba 100644 --- a/src/emitter/flow.zig +++ b/src/emitter/flow.zig @@ -18,86 +18,86 @@ const appendEmittedAlias = scalar_emit.appendEmittedAlias; const appendEmittedCollectionProperties = scalar_emit.appendEmittedCollectionProperties; const appendEmittedScalarNode = scalar_emit.appendEmittedScalarNode; -pub fn emitFlowSequence(self: *State, out: *std.ArrayList(u8)) Error!void { +pub fn emitFlowSequence(self: *State, out: anytype) Error!void { try self.enterEmitCollection(); defer self.leaveEmitCollection(); - try out.append(self.allocator, '['); + try out.*.append(self.allocator, '['); var first_item = true; while (self.index < self.events.len and self.events[self.index] != .sequence_end) { - if (!first_item) try out.appendSlice(self.allocator, ", "); + if (!first_item) try out.*.appendSlice(self.allocator, ", "); first_item = false; try emitFlowNode(self, out); } if (self.index >= self.events.len) return ParseError.InvalidSyntax; - try out.append(self.allocator, ']'); + try out.*.append(self.allocator, ']'); self.index += 1; } pub fn emitEmptySequenceIfPresent( self: *State, - out: *std.ArrayList(u8), + out: anytype, collection: CollectionStart, prefix: ?[]const u8, ) Error!bool { if (self.index >= self.events.len) return ParseError.InvalidSyntax; if (self.events[self.index] != .sequence_end) return false; - if (prefix) |value| try out.appendSlice(self.allocator, value); + if (prefix) |value| try out.*.appendSlice(self.allocator, value); if (try appendEmittedCollectionProperties(self.allocator, out, collection)) { - try out.append(self.allocator, ' '); + try out.*.append(self.allocator, ' '); } - try out.appendSlice(self.allocator, "[]"); + try out.*.appendSlice(self.allocator, "[]"); self.index += 1; return true; } pub fn emitEmptyMappingIfPresent( self: *State, - out: *std.ArrayList(u8), + out: anytype, collection: CollectionStart, prefix: ?[]const u8, ) Error!bool { if (self.index >= self.events.len) return ParseError.InvalidSyntax; if (self.events[self.index] != .mapping_end) return false; - if (prefix) |value| try out.appendSlice(self.allocator, value); + if (prefix) |value| try out.*.appendSlice(self.allocator, value); if (try appendEmittedCollectionProperties(self.allocator, out, collection)) { - try out.append(self.allocator, ' '); + try out.*.append(self.allocator, ' '); } - try out.appendSlice(self.allocator, "{}"); + try out.*.appendSlice(self.allocator, "{}"); self.index += 1; return true; } -pub fn emitFlowMapping(self: *State, out: *std.ArrayList(u8)) Error!void { +pub fn emitFlowMapping(self: *State, out: anytype) Error!void { try self.enterEmitCollection(); defer self.leaveEmitCollection(); - try out.append(self.allocator, '{'); + try out.*.append(self.allocator, '{'); var first_pair = true; while (self.index < self.events.len and self.events[self.index] != .mapping_end) { - if (!first_pair) try out.appendSlice(self.allocator, ", "); + if (!first_pair) try out.*.appendSlice(self.allocator, ", "); first_pair = false; try emitFlowNode(self, out); if (self.index >= self.events.len or self.events[self.index] == .mapping_end) { return ParseError.InvalidSyntax; } - try out.appendSlice(self.allocator, ": "); + try out.*.appendSlice(self.allocator, ": "); try emitFlowNode(self, out); } if (self.index >= self.events.len) return ParseError.InvalidSyntax; - try out.append(self.allocator, '}'); + try out.*.append(self.allocator, '}'); self.index += 1; } -pub fn emitFlowNode(self: *State, out: *std.ArrayList(u8)) Error!void { +pub fn emitFlowNode(self: *State, out: anytype) Error!void { if (self.index >= self.events.len) return ParseError.InvalidSyntax; switch (self.events[self.index]) { @@ -113,7 +113,7 @@ pub fn emitFlowNode(self: *State, out: *std.ArrayList(u8)) Error!void { self.index += 1; if (try emitEmptySequenceIfPresent(self, out, collection, null)) return; if (try appendEmittedCollectionProperties(self.allocator, out, collection)) { - try out.append(self.allocator, ' '); + try out.*.append(self.allocator, ' '); } try emitFlowSequence(self, out); }, @@ -121,7 +121,7 @@ pub fn emitFlowNode(self: *State, out: *std.ArrayList(u8)) Error!void { self.index += 1; if (try emitEmptyMappingIfPresent(self, out, collection, null)) return; if (try appendEmittedCollectionProperties(self.allocator, out, collection)) { - try out.append(self.allocator, ' '); + try out.*.append(self.allocator, ' '); } try emitFlowMapping(self, out); }, diff --git a/src/emitter/scalar.zig b/src/emitter/scalar.zig index c762654..24a78a4 100644 --- a/src/emitter/scalar.zig +++ b/src/emitter/scalar.zig @@ -32,7 +32,7 @@ pub const EmittedBlockScalarIndent = struct { indent_indicator_for_newline_only: bool = false, }; -pub fn appendEmittedScalarNode(allocator: std.mem.Allocator, out: *std.ArrayList(u8), scalar: Scalar) Error!void { +pub fn appendEmittedScalarNode(allocator: std.mem.Allocator, out: anytype, scalar: Scalar) Error!void { try appendEmittedScalarNodeIndented(allocator, out, scalar, .{ .content = 2, .indicator = 2, @@ -41,21 +41,21 @@ pub fn appendEmittedScalarNode(allocator: std.mem.Allocator, out: *std.ArrayList pub fn appendEmittedScalarNodeIndented( allocator: std.mem.Allocator, - out: *std.ArrayList(u8), + out: anytype, scalar: Scalar, block_indent: EmittedBlockScalarIndent, ) Error!void { if (try appendEmittedScalarProperties(allocator, out, scalar)) { - try out.append(allocator, ' '); + try out.*.append(allocator, ' '); } try appendEmittedScalarIndented(allocator, out, scalar, block_indent); } -pub fn appendEmittedMappingKey(allocator: std.mem.Allocator, out: *std.ArrayList(u8), scalar: Scalar) Error!void { +pub fn appendEmittedMappingKey(allocator: std.mem.Allocator, out: anytype, scalar: Scalar) Error!void { if (isPlainEmptyScalar(scalar) and scalar.anchor == null and scalar.tag == null) return; if (try appendEmittedScalarProperties(allocator, out, scalar)) { - try out.append(allocator, ' '); + try out.*.append(allocator, ' '); } if (isPlainEmptyScalar(scalar)) return; @@ -95,7 +95,7 @@ pub fn isNonEmptyInlineEvent(event: Event) bool { fn appendEmittedScalarIndented( allocator: std.mem.Allocator, - out: *std.ArrayList(u8), + out: anytype, scalar: Scalar, block_indent: EmittedBlockScalarIndent, ) Error!void { @@ -115,7 +115,7 @@ fn appendEmittedScalarIndented( switch (style) { .plain => { if (scalar.value.len == 0) { - try out.appendSlice(allocator, "''"); + try out.*.appendSlice(allocator, "''"); } else if (plainScalarNeedsQuoting(scalar.value)) { if (std.mem.indexOfScalar(u8, scalar.value, '\n') != null) { try appendSingleQuotedScalarIndented(allocator, out, scalar.value, block_indent.content); @@ -125,7 +125,7 @@ fn appendEmittedScalarIndented( } else if (std.mem.indexOfScalar(u8, scalar.value, '\n') != null) { try appendPlainScalarWithLineBreaks(allocator, out, scalar.value, block_indent.plain_continuation); } else { - try out.appendSlice(allocator, scalar.value); + try out.*.appendSlice(allocator, scalar.value); } }, .single_quoted => try appendSingleQuotedScalarIndented(allocator, out, scalar.value, block_indent.content), @@ -191,11 +191,11 @@ pub fn plainBlockMappingKeyNeedsQuoting(value: []const u8) bool { pub fn appendEmittedPlainBlockMappingKey( allocator: std.mem.Allocator, - out: *std.ArrayList(u8), + out: anytype, value: []const u8, ) std.mem.Allocator.Error!void { if (value.len == 0) { - try out.appendSlice(allocator, "''"); + try out.*.appendSlice(allocator, "''"); } else if (plainBlockMappingKeyNeedsQuoting(value)) { if (std.mem.indexOfScalar(u8, value, '\n') != null) { try appendSingleQuotedScalarIndented(allocator, out, value, 2); @@ -203,13 +203,13 @@ pub fn appendEmittedPlainBlockMappingKey( try appendSingleQuotedScalar(allocator, out, value); } } else { - try out.appendSlice(allocator, value); + try out.*.appendSlice(allocator, value); } } pub fn appendPlainScalarWithLineBreaks( allocator: std.mem.Allocator, - out: *std.ArrayList(u8), + out: anytype, value: []const u8, continuation_indent: usize, ) std.mem.Allocator.Error!void { @@ -217,18 +217,18 @@ pub fn appendPlainScalarWithLineBreaks( var first = true; while (lines.next()) |line| { if (!first) { - try out.appendSlice(allocator, "\n\n"); + try out.*.appendSlice(allocator, "\n\n"); if (line.len != 0) try appendIndent(allocator, out, continuation_indent); } first = false; - try out.appendSlice(allocator, line); + try out.*.appendSlice(allocator, line); } } -pub fn appendIndent(allocator: std.mem.Allocator, out: *std.ArrayList(u8), indent: usize) std.mem.Allocator.Error!void { +pub fn appendIndent(allocator: std.mem.Allocator, out: anytype, indent: usize) std.mem.Allocator.Error!void { var count = indent; while (count > 0) : (count -= 1) { - try out.append(allocator, ' '); + try out.*.append(allocator, ' '); } } @@ -325,7 +325,7 @@ pub fn blockScalarHasTabStartedLine(value: []const u8) bool { pub fn appendLiteral( allocator: std.mem.Allocator, - out: *std.ArrayList(u8), + out: anytype, value: []const u8, indent: EmittedBlockScalarIndent, ) Error!void { @@ -334,24 +334,24 @@ pub fn appendLiteral( pub fn appendFolded( allocator: std.mem.Allocator, - out: *std.ArrayList(u8), + out: anytype, value: []const u8, indent: EmittedBlockScalarIndent, ) Error!void { - try out.append(allocator, '>'); + try out.*.append(allocator, '>'); if (value.len == 0) return; if (blockScalarNeedsIndentIndicatorWithOptions(value, indent)) { if (indent.indicator == 0 or indent.indicator > 9) return ParseError.Unsupported; const digit: u8 = @intCast(indent.indicator); - try out.append(allocator, '0' + digit); + try out.*.append(allocator, '0' + digit); } if (value[value.len - 1] != '\n') { - try out.append(allocator, '-'); + try out.*.append(allocator, '-'); } else if (blockScalarUsesKeepChomping(value)) { - try out.append(allocator, '+'); + try out.*.append(allocator, '+'); } - try out.append(allocator, '\n'); + try out.*.append(allocator, '\n'); var line_start: usize = 0; while (line_start < value.len) { @@ -360,7 +360,7 @@ pub fn appendFolded( if (line.len != 0) { try appendIndent(allocator, out, indent.content); - try out.appendSlice(allocator, line); + try out.*.appendSlice(allocator, line); } if (line_end == value.len) break; @@ -374,7 +374,7 @@ pub fn appendFolded( const next_line = value[newline_end..next_line_end]; break :blk if (line.len == 0 or lineStartsWhitespace(line) or lineStartsWhitespace(next_line)) newline_count else newline_count + 1; }; - for (0..output_newline_count) |_| try out.append(allocator, '\n'); + for (0..output_newline_count) |_| try out.*.append(allocator, '\n'); line_start = newline_end; } @@ -382,25 +382,25 @@ pub fn appendFolded( fn appendBlockScalar( allocator: std.mem.Allocator, - out: *std.ArrayList(u8), + out: anytype, indicator: u8, value: []const u8, indent: EmittedBlockScalarIndent, ) Error!void { - try out.append(allocator, indicator); + try out.*.append(allocator, indicator); if (value.len == 0) return; if (blockScalarNeedsIndentIndicatorWithOptions(value, indent)) { if (indent.indicator == 0 or indent.indicator > 9) return ParseError.Unsupported; const digit: u8 = @intCast(indent.indicator); - try out.append(allocator, '0' + digit); + try out.*.append(allocator, '0' + digit); } if (value[value.len - 1] != '\n') { - try out.append(allocator, '-'); + try out.*.append(allocator, '-'); } else if (blockScalarUsesKeepChomping(value)) { - try out.append(allocator, '+'); + try out.*.append(allocator, '+'); } - try out.append(allocator, '\n'); + try out.*.append(allocator, '\n'); var line_start: usize = 0; while (line_start < value.len) { @@ -409,12 +409,12 @@ fn appendBlockScalar( if (line.len != 0) { try appendIndent(allocator, out, indent.content); - try out.appendSlice(allocator, line); + try out.*.appendSlice(allocator, line); } if (line_end == value.len) break; line_start = line_end + 1; - if (line_start < value.len) try out.append(allocator, '\n'); + if (line_start < value.len) try out.*.append(allocator, '\n'); } } @@ -478,18 +478,18 @@ pub fn collectionHasProperties(collection: CollectionStart) bool { return collection.anchor != null or collection.tag != null; } -pub fn appendEmittedScalarProperties(allocator: std.mem.Allocator, out: *std.ArrayList(u8), scalar: Scalar) Error!bool { +pub fn appendEmittedScalarProperties(allocator: std.mem.Allocator, out: anytype, scalar: Scalar) Error!bool { var wrote_property = false; if (scalar.anchor) |anchor| { try validateAnchorName(anchor); - try out.append(allocator, '&'); - try out.appendSlice(allocator, anchor); + try out.*.append(allocator, '&'); + try out.*.appendSlice(allocator, anchor); wrote_property = true; } if (scalar.tag) |tag| { - if (wrote_property) try out.append(allocator, ' '); + if (wrote_property) try out.*.append(allocator, ' '); try appendEmittedTag(allocator, out, tag); wrote_property = true; } @@ -499,20 +499,20 @@ pub fn appendEmittedScalarProperties(allocator: std.mem.Allocator, out: *std.Arr pub fn appendEmittedCollectionProperties( allocator: std.mem.Allocator, - out: *std.ArrayList(u8), + out: anytype, collection: CollectionStart, ) Error!bool { var wrote_property = false; if (collection.anchor) |anchor| { try validateAnchorName(anchor); - try out.append(allocator, '&'); - try out.appendSlice(allocator, anchor); + try out.*.append(allocator, '&'); + try out.*.appendSlice(allocator, anchor); wrote_property = true; } if (collection.tag) |tag| { - if (wrote_property) try out.append(allocator, ' '); + if (wrote_property) try out.*.append(allocator, ' '); try appendEmittedTag(allocator, out, tag); wrote_property = true; } @@ -520,30 +520,30 @@ pub fn appendEmittedCollectionProperties( return wrote_property; } -pub fn appendEmittedAlias(allocator: std.mem.Allocator, out: *std.ArrayList(u8), alias: []const u8) Error!void { +pub fn appendEmittedAlias(allocator: std.mem.Allocator, out: anytype, alias: []const u8) Error!void { try validateAnchorName(alias); - try out.append(allocator, '*'); - try out.appendSlice(allocator, alias); + try out.*.append(allocator, '*'); + try out.*.appendSlice(allocator, alias); } -pub fn appendSingleQuotedScalar(allocator: std.mem.Allocator, out: *std.ArrayList(u8), value: []const u8) std.mem.Allocator.Error!void { - try out.append(allocator, '\''); +pub fn appendSingleQuotedScalar(allocator: std.mem.Allocator, out: anytype, value: []const u8) std.mem.Allocator.Error!void { + try out.*.append(allocator, '\''); try appendSingleQuotedScalarContent(allocator, out, value); - try out.append(allocator, '\''); + try out.*.append(allocator, '\''); } pub fn appendSingleQuotedScalarIndented( allocator: std.mem.Allocator, - out: *std.ArrayList(u8), + out: anytype, value: []const u8, indent: usize, ) std.mem.Allocator.Error!void { if (isAllNewlines(value)) { - try out.append(allocator, '\''); - try out.append(allocator, '\n'); - try out.appendSlice(allocator, value); + try out.*.append(allocator, '\''); + try out.*.append(allocator, '\n'); + try out.*.appendSlice(allocator, value); try appendIndent(allocator, out, indent); - try out.append(allocator, '\''); + try out.*.append(allocator, '\''); } else if (std.mem.indexOfScalar(u8, value, '\n') != null) { try appendSingleQuotedScalarWithLineBreaks(allocator, out, value, indent); } else { @@ -551,34 +551,34 @@ pub fn appendSingleQuotedScalarIndented( } } -pub fn appendDoubleQuotedScalar(allocator: std.mem.Allocator, out: *std.ArrayList(u8), value: []const u8) Error!void { - try out.append(allocator, '"'); +pub fn appendDoubleQuotedScalar(allocator: std.mem.Allocator, out: anytype, value: []const u8) Error!void { + try out.*.append(allocator, '"'); var index: usize = 0; while (index < value.len) { const byte = value[index]; switch (byte) { '"' => { - try out.appendSlice(allocator, "\\\""); + try out.*.appendSlice(allocator, "\\\""); index += 1; }, '\\' => { - try out.appendSlice(allocator, "\\\\"); + try out.*.appendSlice(allocator, "\\\\"); index += 1; }, 0x08 => { - try out.appendSlice(allocator, "\\b"); + try out.*.appendSlice(allocator, "\\b"); index += 1; }, '\t' => { - try out.appendSlice(allocator, "\\t"); + try out.*.appendSlice(allocator, "\\t"); index += 1; }, '\n' => { - try out.appendSlice(allocator, "\\n"); + try out.*.appendSlice(allocator, "\\n"); index += 1; }, '\r' => { - try out.appendSlice(allocator, "\\r"); + try out.*.appendSlice(allocator, "\\r"); index += 1; }, 0x00...0x07, 0x0b, 0x0c, 0x0e...0x1f => { @@ -593,7 +593,7 @@ pub fn appendDoubleQuotedScalar(allocator: std.mem.Allocator, out: *std.ArrayLis if (codepoint == 0xfeff or codepoint > 0x7e) { try appendCodepointEscape(allocator, out, codepoint); } else if (tag_emit.isYamlAllowedCodepoint(codepoint)) { - try out.appendSlice(allocator, value[index .. index + len]); + try out.*.appendSlice(allocator, value[index .. index + len]); } else { try appendCodepointEscape(allocator, out, codepoint); } @@ -601,45 +601,45 @@ pub fn appendDoubleQuotedScalar(allocator: std.mem.Allocator, out: *std.ArrayLis }, } } - try out.append(allocator, '"'); + try out.*.append(allocator, '"'); } -fn appendSingleQuotedScalarContent(allocator: std.mem.Allocator, out: *std.ArrayList(u8), value: []const u8) std.mem.Allocator.Error!void { +fn appendSingleQuotedScalarContent(allocator: std.mem.Allocator, out: anytype, value: []const u8) std.mem.Allocator.Error!void { for (value) |byte| { - if (byte == '\'') try out.append(allocator, '\''); - try out.append(allocator, byte); + if (byte == '\'') try out.*.append(allocator, '\''); + try out.*.append(allocator, byte); } } fn appendSingleQuotedScalarWithLineBreaks( allocator: std.mem.Allocator, - out: *std.ArrayList(u8), + out: anytype, value: []const u8, continuation_indent: usize, ) std.mem.Allocator.Error!void { - try out.append(allocator, '\''); + try out.*.append(allocator, '\''); var lines = std.mem.splitScalar(u8, value, '\n'); var first = true; while (lines.next()) |line| { if (!first) { - try out.appendSlice(allocator, "\n\n"); + try out.*.appendSlice(allocator, "\n\n"); if (line.len != 0) try appendIndent(allocator, out, continuation_indent); } first = false; try appendSingleQuotedScalarContent(allocator, out, line); } - try out.append(allocator, '\''); + try out.*.append(allocator, '\''); } -fn appendCodepointEscape(allocator: std.mem.Allocator, out: *std.ArrayList(u8), codepoint: u21) std.mem.Allocator.Error!void { +fn appendCodepointEscape(allocator: std.mem.Allocator, out: anytype, codepoint: u21) std.mem.Allocator.Error!void { if (codepoint <= 0xff) { - try out.appendSlice(allocator, "\\x"); + try out.*.appendSlice(allocator, "\\x"); try tag_emit.appendFixedHex(allocator, out, codepoint, 2); } else if (codepoint <= 0xffff) { - try out.appendSlice(allocator, "\\u"); + try out.*.appendSlice(allocator, "\\u"); try tag_emit.appendFixedHex(allocator, out, codepoint, 4); } else { - try out.appendSlice(allocator, "\\U"); + try out.*.appendSlice(allocator, "\\U"); try tag_emit.appendFixedHex(allocator, out, codepoint, 8); } } diff --git a/src/emitter/tag.zig b/src/emitter/tag.zig index af78254..9189825 100644 --- a/src/emitter/tag.zig +++ b/src/emitter/tag.zig @@ -12,7 +12,7 @@ const Error = common.Error; const ParseError = common.ParseError; const TagDirective = event_types.TagDirective; -pub fn appendEmittedTag(allocator: std.mem.Allocator, out: *std.ArrayList(u8), tag: []const u8) Error!void { +pub fn appendEmittedTag(allocator: std.mem.Allocator, out: anytype, tag: []const u8) Error!void { try validateEmittedTag(tag); if (std.mem.startsWith(u8, tag, "!")) { @@ -22,23 +22,23 @@ pub fn appendEmittedTag(allocator: std.mem.Allocator, out: *std.ArrayList(u8), t const standard_prefix = "tag:yaml.org,2002:"; if (std.mem.startsWith(u8, tag, standard_prefix) and tag.len > standard_prefix.len) { - try out.appendSlice(allocator, "!!"); + try out.*.appendSlice(allocator, "!!"); try appendPercentEncodedBareTag(allocator, out, tag[standard_prefix.len..]); return; } - try out.appendSlice(allocator, "!<"); + try out.*.appendSlice(allocator, "!<"); try appendPercentEncodedVerbatimTag(allocator, out, tag); - try out.append(allocator, '>'); + try out.*.append(allocator, '>'); } -pub fn appendTagDirective(allocator: std.mem.Allocator, out: *std.ArrayList(u8), directive: TagDirective) Error!void { +pub fn appendTagDirective(allocator: std.mem.Allocator, out: anytype, directive: TagDirective) Error!void { try validateTagDirective(directive); - try out.appendSlice(allocator, "%TAG "); - try out.appendSlice(allocator, directive.handle); - try out.append(allocator, ' '); - try out.appendSlice(allocator, directive.prefix); - try out.append(allocator, '\n'); + try out.*.appendSlice(allocator, "%TAG "); + try out.*.appendSlice(allocator, directive.handle); + try out.*.append(allocator, ' '); + try out.*.appendSlice(allocator, directive.prefix); + try out.*.append(allocator, '\n'); } pub fn validateTagDirectives(directives: []const TagDirective) ParseError!void { @@ -186,12 +186,12 @@ fn hasUriSchemePrefix(tag: []const u8) bool { fn appendPercentEncodedBareTag( allocator: std.mem.Allocator, - out: *std.ArrayList(u8), + out: anytype, tag: []const u8, ) std.mem.Allocator.Error!void { for (tag) |byte| { if (isBareTagChar(byte)) { - try out.append(allocator, byte); + try out.*.append(allocator, byte); } else { try appendPercentEncodedByte(allocator, out, byte); } @@ -200,12 +200,12 @@ fn appendPercentEncodedBareTag( fn appendPercentEncodedVerbatimTag( allocator: std.mem.Allocator, - out: *std.ArrayList(u8), + out: anytype, tag: []const u8, ) std.mem.Allocator.Error!void { for (tag) |byte| { if (isTagUriChar(byte)) { - try out.append(allocator, byte); + try out.*.append(allocator, byte); } else { try appendPercentEncodedByte(allocator, out, byte); } @@ -222,16 +222,16 @@ fn isBareTagChar(byte: u8) bool { fn appendPercentEncodedByte( allocator: std.mem.Allocator, - out: *std.ArrayList(u8), + out: anytype, byte: u8, ) std.mem.Allocator.Error!void { - try out.append(allocator, '%'); + try out.*.append(allocator, '%'); try appendFixedHex(allocator, out, byte, 2); } pub fn appendFixedHex( allocator: std.mem.Allocator, - out: *std.ArrayList(u8), + out: anytype, value: u21, comptime width: usize, ) std.mem.Allocator.Error!void { @@ -240,7 +240,7 @@ pub fn appendFixedHex( var shift: usize = (width - 1) * 4; while (true) { const digit: usize = @intCast((widened >> @intCast(shift)) & 0x0f); - try out.append(allocator, hex[digit]); + try out.*.append(allocator, hex[digit]); if (shift == 0) break; shift -= 4; } diff --git a/tests/structure/docs_tooling_test.zig b/tests/structure/docs_tooling_test.zig index 62f7de9..bf27f25 100644 --- a/tests/structure/docs_tooling_test.zig +++ b/tests/structure/docs_tooling_test.zig @@ -85,10 +85,14 @@ test "structure: coverage step enforces configured threshold" { const build_steps_source = try support.readRepoFile("tools/build_steps.zig"); defer std.testing.allocator.free(build_steps_source); + const ci_source = try support.readRepoFile(".github/workflows/ci.yml"); + defer std.testing.allocator.free(ci_source); try std.testing.expect(std.mem.indexOf(u8, build_source, "coverage-threshold") != null); + try std.testing.expect(std.mem.indexOf(u8, build_source, "orelse 85") != null); try std.testing.expect(std.mem.indexOf(u8, build_steps_source, "check_coverage_threshold.zig") != null); try std.testing.expect(std.mem.indexOf(u8, build_steps_source, "addCoverageThresholdStep") != null); + try std.testing.expect(std.mem.indexOf(u8, ci_source, "-Dcoverage-threshold=85") != null); } test "structure: coverage step includes focused unit test shards" { diff --git a/tests/unit/api/emit_test.zig b/tests/unit/api/emit_test.zig index fcc4161..c71b70d 100644 --- a/tests/unit/api/emit_test.zig +++ b/tests/unit/api/emit_test.zig @@ -1448,6 +1448,30 @@ test "emitEventsToWriterWithOptions honors output limits before writing" { try std.testing.expectEqual(@as(usize, 0), writer.buffered().len); } +test "emitEventsWithOptions checks output limits before growing buffer" { + const large_scalar = + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ++ + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ++ + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ++ + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + const events = [_]yaml.Event{ + .stream_start, + .{ .document_start = .{} }, + .{ .scalar = .{ .value = large_scalar } }, + .{ .document_end = .{} }, + .stream_end, + }; + + var fixed_buffer: [64]u8 = undefined; + var fixed = std.heap.FixedBufferAllocator.init(&fixed_buffer); + + try std.testing.expectError(yaml.ParseError.Unsupported, yaml.emitEventsWithOptions( + fixed.allocator(), + &events, + .{ .max_output_bytes = 8 }, + )); +} + test "emitEventsToWriter frees temporary output when writer fails" { const events = [_]yaml.Event{ .stream_start,