128 lines
4.3 KiB
Plaintext
128 lines
4.3 KiB
Plaintext
import "@std/mem"
|
|
import "@std/arraylist"
|
|
import "@std/testing"
|
|
|
|
@hide:file
|
|
Input :: struct {
|
|
bytes []u8
|
|
offset usize = 0
|
|
chunk usize = 137
|
|
fail_at usize = maxval!(usize)
|
|
remaining usize = maxval!(usize)
|
|
live usize = 0
|
|
}
|
|
|
|
@hide:file
|
|
input_state proc(context ?@mut anyopaque) @mut Input {
|
|
if (context) |pointer| return ptrcast!(pointer)
|
|
unreachable
|
|
}
|
|
|
|
@hide:file
|
|
input_read proc(context ?@mut anyopaque, _ Handle, buffer []mut u8) usize ! ReadError {
|
|
state :: input_state(context)
|
|
if (state.offset >= state.fail_at) return .read_failed
|
|
count := state.bytes.len - state.offset
|
|
if (count > buffer.len) count = buffer.len
|
|
if (count > state.chunk) count = state.chunk
|
|
memcopy!(buffer[..count], state.bytes[state.offset..state.offset + count])
|
|
state.offset += count
|
|
return count
|
|
}
|
|
|
|
@hide:file
|
|
input_reader proc(state @mut Input) Reader {
|
|
return Reader{ context = state, handle = Handle{ file_desc = -1 }, read = input_read }
|
|
}
|
|
|
|
@hide:file
|
|
tracked_alloc proc(context ?@mut anyopaque, size usize, alignment usize) ?*mut u8 {
|
|
state :: input_state(context)
|
|
if (state.remaining == 0) return null
|
|
state.remaining -= 1
|
|
memory :: mem.raw_alloc(mem.c_allocator, size, alignment)
|
|
if (memory) |_| state.live += 1
|
|
return memory
|
|
}
|
|
|
|
@hide:file
|
|
tracked_realloc proc(context ?@mut anyopaque, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8 {
|
|
state :: input_state(context)
|
|
if (state.remaining == 0) return null
|
|
state.remaining -= 1
|
|
resized :: mem.raw_realloc(mem.c_allocator, memory, old_size, new_size, alignment)
|
|
if (resized) |_| {
|
|
if (memory) |_| {} else state.live += 1
|
|
}
|
|
return resized
|
|
}
|
|
|
|
@hide:file
|
|
tracked_free proc(context ?@mut anyopaque, memory ?*mut u8, size usize, alignment usize) void {
|
|
state :: input_state(context)
|
|
if (memory) |_| state.live -= 1
|
|
mem.raw_free(mem.c_allocator, memory, size, alignment)
|
|
}
|
|
|
|
@hide:file
|
|
tracked_vtable :: mem.AllocatorVTable{ alloc = tracked_alloc, realloc = tracked_realloc, free = tracked_free }
|
|
|
|
read_all_preserves_short_reads_across_buffer_growth test {
|
|
source [9000]mut u8 := undefined
|
|
for (0..source.len) |i| source[i] = u8(rem!(i, 251))
|
|
state := Input{ bytes = source[..] }
|
|
bytes := try read_all(mem.c_allocator, input_reader(&state), maxval!(usize))
|
|
defer arraylist.deinit(&bytes)
|
|
try testing.expect_equal(source[..], bytes.items)
|
|
}
|
|
|
|
read_all_accepts_exact_limits_and_probes_for_excess test {
|
|
for (usize(0)..4) |maximum| {
|
|
state := Input{ bytes = "abc"[..maximum], chunk = 1 }
|
|
bytes := try read_all(mem.c_allocator, input_reader(&state), maximum)
|
|
defer arraylist.deinit(&bytes)
|
|
try testing.expect_equal(state.bytes, bytes.items)
|
|
|
|
excess := Input{ bytes = "abcd", chunk = 1 }
|
|
unexpected := read_all(mem.c_allocator, input_reader(&excess), maximum) catch |err| {
|
|
try testing.expect(err == .limit_exceeded)
|
|
try testing.expect_equal(maximum + 1, excess.offset)
|
|
continue
|
|
}
|
|
arraylist.deinit(&unexpected)
|
|
try testing.expect(false)
|
|
}
|
|
}
|
|
|
|
read_all_releases_partial_storage_on_read_failure_and_limit_excess test {
|
|
for (0..2) |failure| {
|
|
state := Input{ bytes = "abcdef", chunk = 2 }
|
|
if (failure == 0) state.fail_at = 2
|
|
allocator :: mem.Allocator{ context = &state, vtable = &tracked_vtable }
|
|
unexpected := read_all(allocator, input_reader(&state), 4) catch |err| {
|
|
expected ReadAllError :: if (failure == 0) .read_failed else .limit_exceeded
|
|
try testing.expect_equal(expected, err)
|
|
try testing.expect_equal(0, state.live)
|
|
continue
|
|
}
|
|
arraylist.deinit(&unexpected)
|
|
try testing.expect(false)
|
|
}
|
|
}
|
|
|
|
read_all_releases_storage_at_each_failed_growth test {
|
|
source [9000]mut u8 := undefined
|
|
mem.set(source[..], u8('x'))
|
|
for (usize(0)..3) |remaining| {
|
|
state := Input{ bytes = source[..], remaining = remaining }
|
|
allocator :: mem.Allocator{ context = &state, vtable = &tracked_vtable }
|
|
unexpected := read_all(allocator, input_reader(&state), source.len) catch |err| {
|
|
try testing.expect(err == .out_of_memory)
|
|
try testing.expect_equal(0, state.live)
|
|
continue
|
|
}
|
|
arraylist.deinit(&unexpected)
|
|
try testing.expect(false)
|
|
}
|
|
}
|