Type Function Reader [src]
Alias for std.json.scanner.Reader
Connects a std.io.Reader to a std.json.Scanner.
All next*() methods here handle error.BufferUnderrun from std.json.Scanner, and then read from the reader.
Prototype
pub fn Reader(comptime buffer_size: usize, comptime ReaderType: type) type
Parameters
buffer_size: usize
ReaderType: type
Source
pub fn Reader(comptime buffer_size: usize, comptime ReaderType: type) type {
return struct {
scanner: Scanner,
reader: ReaderType,
buffer: [buffer_size]u8 = undefined,
/// The allocator is only used to track `[]` and `{}` nesting levels.
pub fn init(allocator: Allocator, io_reader: ReaderType) @This() {
return .{
.scanner = Scanner.initStreaming(allocator),
.reader = io_reader,
};
}
pub fn deinit(self: *@This()) void {
self.scanner.deinit();
self.* = undefined;
}
/// Calls `std.json.Scanner.enableDiagnostics`.
pub fn enableDiagnostics(self: *@This(), diagnostics: *Diagnostics) void {
self.scanner.enableDiagnostics(diagnostics);
}
pub const NextError = ReaderType.Error || Error || Allocator.Error;
pub const SkipError = NextError;
pub const AllocError = NextError || error{ValueTooLong};
pub const PeekError = ReaderType.Error || Error;
/// Equivalent to `nextAllocMax(allocator, when, default_max_value_len);`
/// See also `std.json.Token` for documentation of `nextAlloc*()` function behavior.
pub fn nextAlloc(self: *@This(), allocator: Allocator, when: AllocWhen) AllocError!Token {
return self.nextAllocMax(allocator, when, default_max_value_len);
}
/// See also `std.json.Token` for documentation of `nextAlloc*()` function behavior.
pub fn nextAllocMax(self: *@This(), allocator: Allocator, when: AllocWhen, max_value_len: usize) AllocError!Token {
const token_type = try self.peekNextTokenType();
switch (token_type) {
.number, .string => {
var value_list = ArrayList(u8).init(allocator);
errdefer {
value_list.deinit();
}
if (try self.allocNextIntoArrayListMax(&value_list, when, max_value_len)) |slice| {
return if (token_type == .number)
Token{ .number = slice }
else
Token{ .string = slice };
} else {
return if (token_type == .number)
Token{ .allocated_number = try value_list.toOwnedSlice() }
else
Token{ .allocated_string = try value_list.toOwnedSlice() };
}
},
// Simple tokens never alloc.
.object_begin,
.object_end,
.array_begin,
.array_end,
.true,
.false,
.null,
.end_of_document,
=> return try self.next(),
}
}
/// Equivalent to `allocNextIntoArrayListMax(value_list, when, default_max_value_len);`
pub fn allocNextIntoArrayList(self: *@This(), value_list: *ArrayList(u8), when: AllocWhen) AllocError!?[]const u8 {
return self.allocNextIntoArrayListMax(value_list, when, default_max_value_len);
}
/// Calls `std.json.Scanner.allocNextIntoArrayListMax` and handles `error.BufferUnderrun`.
pub fn allocNextIntoArrayListMax(self: *@This(), value_list: *ArrayList(u8), when: AllocWhen, max_value_len: usize) AllocError!?[]const u8 {
while (true) {
return self.scanner.allocNextIntoArrayListMax(value_list, when, max_value_len) catch |err| switch (err) {
error.BufferUnderrun => {
try self.refillBuffer();
continue;
},
else => |other_err| return other_err,
};
}
}
/// Like `std.json.Scanner.skipValue`, but handles `error.BufferUnderrun`.
pub fn skipValue(self: *@This()) SkipError!void {
switch (try self.peekNextTokenType()) {
.object_begin, .array_begin => {
try self.skipUntilStackHeight(self.stackHeight());
},
.number, .string => {
while (true) {
switch (try self.next()) {
.partial_number,
.partial_string,
.partial_string_escaped_1,
.partial_string_escaped_2,
.partial_string_escaped_3,
.partial_string_escaped_4,
=> continue,
.number, .string => break,
else => unreachable,
}
}
},
.true, .false, .null => {
_ = try self.next();
},
.object_end, .array_end, .end_of_document => unreachable, // Attempt to skip a non-value token.
}
}
/// Like `std.json.Scanner.skipUntilStackHeight()` but handles `error.BufferUnderrun`.
pub fn skipUntilStackHeight(self: *@This(), terminal_stack_height: usize) NextError!void {
while (true) {
return self.scanner.skipUntilStackHeight(terminal_stack_height) catch |err| switch (err) {
error.BufferUnderrun => {
try self.refillBuffer();
continue;
},
else => |other_err| return other_err,
};
}
}
/// Calls `std.json.Scanner.stackHeight`.
pub fn stackHeight(self: *const @This()) usize {
return self.scanner.stackHeight();
}
/// Calls `std.json.Scanner.ensureTotalStackCapacity`.
pub fn ensureTotalStackCapacity(self: *@This(), height: usize) Allocator.Error!void {
try self.scanner.ensureTotalStackCapacity(height);
}
/// See `std.json.Token` for documentation of this function.
pub fn next(self: *@This()) NextError!Token {
while (true) {
return self.scanner.next() catch |err| switch (err) {
error.BufferUnderrun => {
try self.refillBuffer();
continue;
},
else => |other_err| return other_err,
};
}
}
/// See `std.json.Scanner.peekNextTokenType()`.
pub fn peekNextTokenType(self: *@This()) PeekError!TokenType {
while (true) {
return self.scanner.peekNextTokenType() catch |err| switch (err) {
error.BufferUnderrun => {
try self.refillBuffer();
continue;
},
else => |other_err| return other_err,
};
}
}
fn refillBuffer(self: *@This()) ReaderType.Error!void {
const input = self.buffer[0..try self.reader.read(self.buffer[0..])];
if (input.len > 0) {
self.scanner.feedInput(input);
} else {
self.scanner.endInput();
}
}
};
}