Skip to content

Commit

Permalink
use a PushVector to work around slow push! in Base
Browse files Browse the repository at this point in the history
  • Loading branch information
KristofferC committed Nov 4, 2018
1 parent e8d39a6 commit 3747b4a
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 4 deletions.
11 changes: 7 additions & 4 deletions src/Parser.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ module Parser # JSON
using Mmap
using ..Common

include("pushvector.jl")

"""
Like `isspace`, but work on bytes and includes only the four whitespace
characters defined by the JSON standard: space, tab, line feed, and carriage
Expand All @@ -20,10 +22,11 @@ abstract type ParserState end
mutable struct MemoryParserState <: ParserState
utf8::String
s::Int
utf8array::Vector{UInt8}
utf8array::PushVector{UInt8, Vector{UInt8}}
end

# it is convenient to access MemoryParserState like a Vector{UInt8} to avoid copies
MemoryParserState(str::AbstractString) = MemoryParserState(str, 1, PushVector{UInt8}())
Base.@propagate_inbounds Base.getindex(state::MemoryParserState, i::Int) = codeunit(state.utf8, i)
Base.length(state::MemoryParserState) = sizeof(state.utf8)
Base.unsafe_convert(::Type{Ptr{UInt8}}, state::MemoryParserState) = unsafe_convert(Ptr{UInt8}, state.utf8)
Expand All @@ -32,9 +35,9 @@ mutable struct StreamingParserState{T <: IO} <: ParserState
io::T
cur::UInt8
used::Bool
utf8array::Vector{UInt8}
utf8array::PushVector{UInt8, Vector{UInt8}}
end
StreamingParserState(io::IO) = StreamingParserState(io, 0x00, true, UInt8[])
StreamingParserState(io::IO) = StreamingParserState(io, 0x00, true, PushVector{UInt8}())

struct ParserContext{DictType, IntType} end

Expand Down Expand Up @@ -409,7 +412,7 @@ function parse(str::AbstractString;
dicttype=Dict{String,Any},
inttype::Type{<:Real}=Int64)
pc = _get_parsercontext(dicttype, inttype)
ps = MemoryParserState(str, 1, UInt8[])
ps = MemoryParserState(str)
v = parse_value(pc, ps)
chomp_space!(ps)
if hasmore(ps)
Expand Down
33 changes: 33 additions & 0 deletions src/pushvector.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# This is a vector wrapper that we use as a workaround for `push!`
# being slow (it always calls into the runtime even if the underlying buffer,
# has enough space). Here we keep track of the length using an extra field
mutable struct PushVector{T, A<:AbstractVector{T}} <: AbstractVector{T}
v::A
l::Int
end

# Default length of 20 should be enough to never need to grow in most cases
PushVector{T}() where {T} = PushVector(Vector{T}(undef, 20), 0)

Base.unsafe_convert(::Type{Ptr{UInt8}}, v::PushVector) = pointer(v.v)
Base.length(v::PushVector) = v.l
Base.size(v::PushVector) = (v.l,)
@inline function Base.getindex(v::PushVector, i)
@boundscheck checkbounds(v, i)
@inbounds v.v[i]
end

function Base.push!(v::PushVector, i)
v.l += 1
if v.l > length(v.v)
resize!(v.v, v.l * 2)
end
v.v[v.l] = i
return v
end

function Base.resize!(v::PushVector, l::Integer)
# Only support shrinking for now, since that is all we need
@assert l <= v.l
v.l = l
end

0 comments on commit 3747b4a

Please sign in to comment.