Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A few performance improvements #263

Merged
merged 5 commits into from
Nov 4, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@ os:
- linux
julia:
- 0.7
- 1.0
- nightly
notifications:
email: false
sudo: false
script:
- julia -e 'import Pkg; Pkg.clone(pwd()); Pkg.build("JSON"); Pkg.test("JSON"; coverage=true)';
after_success:
- julia -e 'cd(Pkg.dir("JSON")); Pkg.add("Coverage"); using Coverage; Codecov.submit(process_folder())';
- julia -e 'import Pkg; Pkg.add("Coverage"); using Coverage; Codecov.submit(process_folder())';
19 changes: 13 additions & 6 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
environment:
matrix:
- julia_version: 0.7
- julia_version: latest
- julia_version: 1
- julia_version: nightly

platform:
- x86 # 32-bit
- x64 # 64-bit

## uncomment the following lines to allow failures on nightly julia
## (tests will run but not make your overall status red)
#matrix:
# allow_failures:
# - julia_version: latest
# # Uncomment the following lines to allow failures on nightly julia
# # (tests will run but not make your overall status red)
# matrix:
# allow_failures:
# - julia_version: nightly

branches:
only:
Expand All @@ -34,3 +35,9 @@ build_script:
test_script:
- echo "%JL_TEST_SCRIPT%"
- C:\julia\bin\julia -e "%JL_TEST_SCRIPT%"

# # Uncomment to support code coverage upload. Should only be enabled for packages
# # which would have coverage gaps without running on Windows
# on_success:
# - echo "%JL_CODECOV_SCRIPT%"
# - C:\julia\bin\julia -e "%JL_CODECOV_SCRIPT%"
34 changes: 25 additions & 9 deletions src/Parser.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ abstract type ParserState end
mutable struct MemoryParserState <: ParserState
utf8::String
s::Int
utf8array::Vector{UInt8}
end

# it is convenient to access MemoryParserState like a Vector{UInt8} to avoid copies
Expand All @@ -31,8 +32,9 @@ mutable struct StreamingParserState{T <: IO} <: ParserState
io::T
cur::UInt8
used::Bool
utf8array::Vector{UInt8}
end
StreamingParserState(io::IO) = StreamingParserState(io, 0x00, true)
StreamingParserState(io::IO) = StreamingParserState(io, 0x00, true, UInt8[])

struct ParserContext{DictType, IntType} end

Expand Down Expand Up @@ -76,9 +78,10 @@ skip past that byte. Otherwise, an error is thrown.
if byteat(ps) == c
incr!(ps)
else
_error("Expected '$(Char(c))' here", ps)
_error_expected_char(c, ps)
end
end
@noinline _error_expected_char(c, ps) = _error("Expected '$(Char(c))' here", ps)

function skip!(ps::ParserState, cs::UInt8...)
for c in cs
Expand Down Expand Up @@ -328,8 +331,9 @@ function int_from_bytes(pc::ParserContext{<:Any,IntType},
num = IntType(0)
@inbounds for i in from:to
c = bytes[i]
if isjsondigit(c)
num = IntType(10) * num + IntType(c - DIGIT_ZERO)
dig = c - DIGIT_ZERO
if dig < 0x10
num = IntType(10) * num + IntType(dig)
else
_error(E_BAD_NUMBER, ps)
end
Expand Down Expand Up @@ -362,7 +366,7 @@ end
function parse_number(pc::ParserContext, ps::ParserState)
# Determine the end of the floating point by skipping past ASCII values
# 0-9, +, -, e, E, and .
number = UInt8[]
number = ps.utf8array
isint = true

@inbounds while hasmore(ps)
Expand All @@ -380,7 +384,9 @@ function parse_number(pc::ParserContext, ps::ParserState)
incr!(ps)
end

number_from_bytes(pc, ps, isint, number, 1, length(number))
v = number_from_bytes(pc, ps, isint, number, 1, length(number))
resize!(number, 0)
return v
end

unparameterize_type(x) = x # Fallback for nontypes -- functions etc
Expand All @@ -389,11 +395,21 @@ function unparameterize_type(T::Type)
candidate <: Union{} ? T : candidate
end

# Workaround for slow dynamic dispatch for creating objects
const DEFAULT_PARSERCONTEXT = ParserContext{Dict{String, Any}, Int64}()
function _get_parsercontext(dicttype, inttype)
if dicttype == Dict{String, Any} && inttype == Int64
DEFAULT_PARSERCONTEXT
else
ParserContext{unparameterize_type(dicttype), inttype}.instance
end
end

function parse(str::AbstractString;
dicttype=Dict{String,Any},
inttype::Type{<:Real}=Int64)
pc = ParserContext{unparameterize_type(dicttype), inttype}()
ps = MemoryParserState(str, 1)
pc = _get_parsercontext(dicttype, inttype)
ps = MemoryParserState(str, 1, UInt8[])
v = parse_value(pc, ps)
chomp_space!(ps)
if hasmore(ps)
Expand All @@ -405,7 +421,7 @@ end
function parse(io::IO;
dicttype=Dict{String,Any},
inttype::Type{<:Real}=Int64)
pc = ParserContext{unparameterize_type(dicttype), inttype}()
pc = _get_parsercontext(dicttype, inttype)
ps = StreamingParserState(io)
parse_value(pc, ps)
end
Expand Down