mirror of
https://github.com/halleysfifthinc/AVCLAN-Mockingboard.git
synced 2026-08-12 00:42:52 +00:00
103 lines
3.3 KiB
Julia
103 lines
3.3 KiB
Julia
strip_nothing_(::Type{Union{Nothing, T}}) where T = T
|
|
strip_nothing_(::Type{T}) where T = T
|
|
|
|
progress_noop_(n) = nothing
|
|
|
|
mutable struct SplitCapOutput{S}
|
|
work_buffer::Vector{UInt8}
|
|
complete_buffers::Channel{Vector{UInt8}}
|
|
stream::S
|
|
end
|
|
|
|
# Since Julia (as of 1.5) doesn't support task migration,
|
|
# continue with new task after each buffer write, to rebalance across threads
|
|
function write_one_and_continue_(output::SplitCapOutput, free_buffers::Channel, pending::Threads.Atomic{Int})
|
|
i = iterate(output.complete_buffers)
|
|
if i === nothing
|
|
Threads.atomic_sub!(pending, 1)
|
|
return nothing
|
|
end
|
|
b, _ = i
|
|
write(output.stream, b)
|
|
empty!(b)
|
|
put!(free_buffers, b)
|
|
Threads.@spawn write_one_and_continue_($output, $free_buffers, $pending)
|
|
end
|
|
|
|
function splitcap(
|
|
::Type{KeyType},
|
|
::Type{StreamType},
|
|
reader::PcapReader,
|
|
record2key,
|
|
key2stream,
|
|
progress_callback = progress_noop_;
|
|
own_streams::Bool = true
|
|
) where {KeyType, StreamType}
|
|
buffer_size = 1024 * 1024 * 2
|
|
max_pending_buffers = 4
|
|
outputs = Dict{KeyType, SplitCapOutput{StreamType}}()
|
|
free_buffers = Channel{Vector{UInt8}}(Inf)
|
|
n = 0
|
|
pending = Threads.Atomic{Int}(0)
|
|
try
|
|
while !eof(reader)
|
|
record = read(reader)
|
|
dst = record2key(record)
|
|
if dst isa KeyType
|
|
output = get!(outputs, dst) do
|
|
stream = key2stream(dst)
|
|
buffer = sizehint!(UInt8[], buffer_size + 1500)
|
|
own_streams && append!(buffer, reader.raw_header)
|
|
output = SplitCapOutput{StreamType}(
|
|
buffer,
|
|
Channel{Vector{UInt8}}(max_pending_buffers),
|
|
stream)
|
|
Threads.atomic_add!(pending, 1)
|
|
Threads.@spawn write_one_and_continue_($output, $free_buffers, $pending)
|
|
output
|
|
end
|
|
append!(output.work_buffer, record.raw)
|
|
if length(output.work_buffer) >= buffer_size
|
|
put!(output.complete_buffers, output.work_buffer)
|
|
if isready(free_buffers)
|
|
output.work_buffer = take!(free_buffers)
|
|
else
|
|
output.work_buffer = sizehint!(UInt8[], buffer_size + 1500)
|
|
end
|
|
end
|
|
end
|
|
n += 1
|
|
progress_callback(n)
|
|
GC.safepoint()
|
|
end
|
|
for output in values(outputs)
|
|
if !isempty(output.work_buffer)
|
|
put!(output.complete_buffers, output.work_buffer)
|
|
end
|
|
close(output.complete_buffers)
|
|
end
|
|
while pending[] != 0
|
|
sleep(0.1)
|
|
end
|
|
finally
|
|
if own_streams
|
|
for output in values(outputs)
|
|
close(output.stream)
|
|
end
|
|
end
|
|
end
|
|
nothing
|
|
end
|
|
|
|
function splitcap(
|
|
reader::PcapReader,
|
|
record2key,
|
|
key2stream,
|
|
progress_callback = progress_noop_;
|
|
kwargs...
|
|
)
|
|
KeyType = strip_nothing_(Core.Compiler.return_type(record2key, Tuple{PcapRecord}))
|
|
StreamType = Core.Compiler.return_type(key2stream, Tuple{KeyType})
|
|
splitcap(KeyType, StreamType, reader, record2key, key2stream, progress_callback; kwargs...)
|
|
end
|