diff --git a/src/staticstring.jl b/src/staticstring.jl index 4d58c31..7cc5b7d 100644 --- a/src/staticstring.jl +++ b/src/staticstring.jl @@ -1,5 +1,6 @@ # https://github.com/mkitti/StaticStrings.jl # https://github.com/JuliaPy/PythonCall.jl/blob/main/src/Utils/Utils.jl +using Base: between struct StaticString{N, T} <: AbstractString codeunits::NTuple{N, T} @@ -60,10 +61,33 @@ function Base.String(x::StaticString{N, T}) where {N, T} return String(b .= x.codeunits) end -@inline Base.ncodeunits(::StaticString{N}) where N = N +@inline Base.ncodeunits(::StaticString{N}) where {N} = N +Base.codeunit(::StaticString{N, T}) where {N, T} = T +Base.@propagate_inbounds Base.codeunit(s::StaticString, i::Int) = s.codeunits[i] -function StaticString(cu::Base.CodeUnits{T}) where T +function StaticString(cu::Base.CodeUnits{T}) where {T} N = length(cu) return StaticString{N, T}(NTuple{N, T}(cu)) end -StaticString(s::AbstractString) = StaticString(codeunits(s)) \ No newline at end of file +StaticString(s::AbstractString) = StaticString(codeunits(s)) + +Base.isvalid(s::StaticString, i::Int) = checkbounds(Bool, s, i) && thisind(s, i) == i +Base.thisind(s::StaticString, i::Int) = _thisind_str(s, i) + +@inline function _thisind_str(s, i::Int) + i == 0 && return 0 + n = ncodeunits(s) + i == n + 1 && return i + @boundscheck between(i, 1, n) || throw(BoundsError(s, i)) + @inbounds b = codeunit(s, i) + (b & 0xc0 == 0x80) & (i - 1 > 0) || return i + @inbounds b = codeunit(s, i - 1) + between(b, 0b11000000, 0b11110111) && return i - 1 + (b & 0xc0 == 0x80) & (i - 2 > 0) || return i + @inbounds b = codeunit(s, i - 2) + between(b, 0b11100000, 0b11110111) && return i - 2 + (b & 0xc0 == 0x80) & (i - 3 > 0) || return i + @inbounds b = codeunit(s, i - 3) + between(b, 0b11110000, 0b11110111) && return i - 3 + return i +end diff --git a/test/staticstring.jl b/test/staticstring.jl index f21693c..20695a3 100644 --- a/test/staticstring.jl +++ b/test/staticstring.jl @@ -1,4 +1,5 @@ using CommonDataFormat: StaticString +using Test @testset "StaticString" begin s = "Hello, World!" @@ -8,4 +9,9 @@ using CommonDataFormat: StaticString @test ss == s @test String(ss) == s @test !isempty(ss) -end \ No newline at end of file + @test contains(ss, "World") + @test occursin("World", ss) + @test replace(ss, "World" => "Julia") == "Hello, Julia!" + + @test codeunit(ss) == UInt8 +end