-
Notifications
You must be signed in to change notification settings - Fork 25
Expand file tree
/
Copy pathchunkcodecs.jl
More file actions
128 lines (124 loc) · 5.31 KB
/
chunkcodecs.jl
File metadata and controls
128 lines (124 loc) · 5.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# Allow a `TranscodingStreams.Codec` to be used as a decoder.
using ChunkCodecCore:
check_in_range,
check_contiguous,
grow_dst!,
MaybeSize,
NOT_SIZE
import ChunkCodecCore:
try_decode!,
try_resize_decode!,
try_find_decoded_size
# `Codec` subtypes may want to specialize `try_find_decoded_size`
function try_find_decoded_size(::Codec, src::AbstractVector{UInt8})::Nothing
nothing
end
function try_decode!(codec::Codec, dst::AbstractVector{UInt8}, src::AbstractVector{UInt8}; kwargs...)::MaybeSize
try_resize_decode!(codec, dst, src, Int64(length(dst)))
end
function try_resize_decode!(codec::Codec, dst::AbstractVector{UInt8}, src::AbstractVector{UInt8}, max_size::Int64; kwargs...)::MaybeSize
dst_size::Int64 = length(dst)
src_size::Int64 = length(src)
check_contiguous(dst)
check_contiguous(src)
cconv_src = Base.cconvert(Ptr{UInt8}, src)
if dst_size < max_size
# Do the equivalent of `_default_output_buffer(codec, input)` in `transcode`
# by resizing `dst`
expected_dst_size::Int64 = GC.@preserve(cconv_src, min(initial_output_size(
codec,
Memory(Base.unsafe_convert(Ptr{UInt8}, cconv_src), src_size),
), max_size))
if expected_dst_size > dst_size
resize!(dst, expected_dst_size)
dst_size = expected_dst_size
end
end
src_left::Int64 = src_size
dst_left::Int64 = dst_size
err = Error()
# Outer loop to decode a concatenation of multiple compressed streams.
while true
if startproc(codec, :write, err) === :error
@goto handle_error
end
if pledgeinsize(codec, src_left, err) === :error
@goto handle_error
end
# The process loop
while true
GC.@preserve cconv_src begin
local src_p = Base.unsafe_convert(Ptr{UInt8}, cconv_src) + (src_size - src_left)
local input = Memory(src_p, src_left)
# ensure minoutsize is provided
local dst_space_needed = minoutsize(codec, input)
while dst_space_needed > dst_left
local next_size = grow_dst!(dst, max_size)
if isnothing(next_size)
break # reached max_size limit
end
dst_left += next_size - dst_size
dst_size = next_size
@assert dst_left > 0
end
cconv_dst = Base.cconvert(Ptr{UInt8}, dst)
GC.@preserve cconv_dst begin
local dst_p = Base.unsafe_convert(Ptr{UInt8}, cconv_dst) + (dst_size - dst_left)
if dst_space_needed > dst_left
# Try to do the decoding into a scratch buffer
# The scratch buffer should typically be just a few bytes.
# This enables handling the `return NOT_SIZE` case
# while respecting the `minoutsize` restrictions.
local scratch_dst = zeros(UInt8, dst_space_needed)
local cconv_scratch_dst = Base.cconvert(Ptr{UInt8}, scratch_dst)
GC.@preserve cconv_scratch_dst let
local scratch_p = Base.unsafe_convert(Ptr{UInt8}, cconv_scratch_dst)
local output = Memory(scratch_p, dst_space_needed)
local Δin, Δout, code = process(codec, input, output, err)
if code === :error
@goto handle_error
end
local valid_Δout = min(Δout, dst_left)
src_left -= Δin
unsafe_copyto!(dst_p, scratch_p, valid_Δout)
if Δout > dst_left
# Ran out of dst space
return NOT_SIZE
end
dst_left -= Δout
if code === :end
if src_left > 0
break # out of the process loop to decode next stream
else
# Done
return dst_size - dst_left
end
end
end
else
local output = Memory(dst_p, dst_left)
local Δin, Δout, code = process(codec, input, output, err)
if code === :error
@goto handle_error
end
src_left -= Δin
dst_left -= Δout
if code === :end
if src_left > 0
break # out of the process loop to decode next stream
else
# Done
return dst_size - dst_left
end
end
end
end
end
end
end
@label handle_error
if !haserror(err)
set_default_error!(err)
end
throw(err[])
end