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

Refactor strings again, make explicit type for views into existing strings #4

Merged
merged 9 commits into from
May 1, 2022
52 changes: 52 additions & 0 deletions .github/workflows/CI-integration.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: CI (Integration)
on:
push:
branches:
- main
paths-ignore:
- 'LICENSE.md'
- 'README.md'
- '.github/workflows/TagBot.yml'
tags: '*'
pull_request:
branches:
- main
paths-ignore:
- 'LICENSE.md'
- 'README.md'
- '.github/workflows/TagBot.yml'
concurrency:
# Skip intermediate builds: always.
# Cancel intermediate builds: only if it is a pull request build.
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }}
jobs:
test:
name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
version:
- '1.8.0-beta3'
os:
- macos-latest
arch:
- x64
group:
- Integration
steps:
- uses: actions/checkout@v2
- uses: julia-actions/setup-julia@v1
with:
version: ${{ matrix.version }}
arch: ${{ matrix.arch }}
- uses: julia-actions/cache@v1
- uses: julia-actions/julia-buildpkg@v1
- uses: julia-actions/julia-runtest@v1
env:
GROUP: ${{ matrix.group }}
- uses: julia-actions/julia-processcoverage@v1
- uses: codecov/codecov-action@v2
with:
files: lcov.info
6 changes: 5 additions & 1 deletion .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@ jobs:
fail-fast: false
matrix:
version:
- '~1.8.0-0'
- '1.8.0-beta3'
os:
- ubuntu-latest
arch:
- x64
group:
- Core
steps:
- uses: actions/checkout@v2
- uses: julia-actions/setup-julia@v1
Expand All @@ -42,6 +44,8 @@ jobs:
- uses: julia-actions/cache@v1
- uses: julia-actions/julia-buildpkg@v1
- uses: julia-actions/julia-runtest@v1
env:
GROUP: ${{ matrix.group }}
- uses: julia-actions/julia-processcoverage@v1
- uses: codecov/codecov-action@v2
with:
Expand Down
6 changes: 0 additions & 6 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,3 @@ ManualMemory = "d125e4d3-2237-4719-b19c-fa641b8a4667"
[compat]
julia = "1.8"
ManualMemory = "0.1.8"

[extras]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Test"]
4 changes: 3 additions & 1 deletion src/StaticTools.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ module StaticTools
include("staticrng.jl")

# String handling
include("abstractstaticstring.jl") # Shared string infrastructure
include("unescape.jl") # You don't want to know
include("staticstring.jl") # StaticCompiler-safe stack-allocated strings
include("mallocstring.jl") # StaticCompiler-safe heap-allocated strings
Expand All @@ -27,7 +28,8 @@ module StaticTools
include("parsedlm.jl")

# Types
export StaticString, MallocString, MallocArray, MallocMatrix, MallocVector
export StaticString, MallocString, StringView, AbstractStaticString
export MallocArray, MallocMatrix, MallocVector
# Macros
export @c_str, @m_str, @mm_str
# Functions
Expand Down
97 changes: 97 additions & 0 deletions src/abstractstaticstring.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@

# General string interface

# Supertype for all strings in this package
abstract type AbstractStaticString end

# A subtype for strings that are backed by a pointer and a length alone
abstract type AbstractPointerString <: AbstractStaticString end

# Lightweight type for taking a view into an existing string
# Like MallocString, but not necessarily null-terminated
struct StringView <: AbstractPointerString
pointer::Ptr{UInt8}
length::Int
end
@inline Base.length(s::StringView) = s.length

# Custom replshow for interactive use (n.b. _NOT_ static-compilerable)
function Base.show(io::IO, s::StringView)
Base.print(io, "$(length(s))-byte StringView:\n \"")
Base.escape_string(io, Base.unsafe_string(pointer(s), length(s)))
Base.print(io, "\"")
end

# Custom printing
@inline Base.print(s::AbstractStaticString) = printf(s)
@inline Base.println(s::AbstractStaticString) = puts(s)
@inline Base.print(fp::Ptr{FILE}, s::AbstractStaticString) = printf(fp, s)
@inline Base.println(fp::Ptr{FILE}, s::AbstractStaticString) = puts(fp, s)


# Fundamentals
@inline Base.unsafe_convert(::Type{Ptr{T}}, m::AbstractPointerString) where {T} = Ptr{T}(s.pointer)
@inline Base.pointer(s::AbstractPointerString) = s.pointer
@inline Base.sizeof(s::AbstractPointerString) = s.length
@inline function Base.:(==)(a::AbstractStaticString, b::AbstractStaticString)
(N = length(a)) == length(b) || return false
pa, pb = pointer(a), pointer(b)
for n in 0:N-1
unsafe_load(pa + n) == unsafe_load(pb + n) || return false
end
return true
end
@inline function Base.:(==)(a::AbstractStaticString, b::AbstractString)
GC.@preserve a b begin
(N = length(a)) == sizeof(b) || return false
pa, pb = pointer(a), pointer(b)
for n in 0:N-1
unsafe_load(pa + n) == unsafe_load(pb + n) || return false
end
return true
end
end
@inline function Base.:(==)(a::AbstractString, b::AbstractStaticString)
GC.@preserve a b begin
(N = sizeof(a)) == length(b) || return false
pa, pb = pointer(a), pointer(b)
for n in 0:N-1
unsafe_load(pa + n) == unsafe_load(pb + n) || return false
end
return true
end
end


# Some of the AbstractArray interface:
@inline Base.firstindex(s::AbstractStaticString) = 1
@inline Base.lastindex(s::AbstractPointerString) = s.length
@inline Base.eachindex(s::AbstractPointerString) = 1:s.length
@inline Base.getindex(s::AbstractStaticString, i::Int) = unsafe_load(pointer(s)+(i-1))
@inline Base.setindex!(s::AbstractStaticString, x::UInt8, i::Integer) = unsafe_store!(pointer(s)+(i-1), x)
@inline Base.setindex!(s::AbstractStaticString, x, i::Integer) = unsafe_store!(pointer(s)+(i-1), convert(UInt8,x))
@inline Base.getindex(s::AbstractStaticString, ::Colon) = s
@inline function Base.getindex(s::AbstractStaticString, r::UnitRange{<:Integer})
i₀ = max(first(r), 1) - 1
l = min(length(r), length(s))
return StringView(pointer(s)+i₀, l)
end
@inline function Base.setindex!(s::AbstractStaticString, x, r::UnitRange{<:Integer})
is₀ = first(r)-1
ix₀ = firstindex(x)-1
@inbounds for i = 1:length(r)
s[i+is₀] = x[i+ix₀]
end
end
@inline function Base.setindex!(s::AbstractStaticString, x, ::Colon)
ix₀ = firstindex(x)-firstindex(s)
@inbounds for i ∈ eachindex(s)
s[i] = x[i+ix₀]
end
end

# Some of the AbstractString interface
@inline Base.ncodeunits(s::AbstractPointerString) = s.length
@inline Base.codeunits(s::AbstractPointerString) = MallocVector{UInt8}(s.pointer, s.length)
@inline Base.codeunit(s::AbstractStaticString) = UInt8
@inline Base.codeunit(s::AbstractStaticString, i::Integer) = s[i]
14 changes: 14 additions & 0 deletions src/llvmio.jl
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,8 @@ end
""", "main"), Int32, Tuple{Ptr{FILE}, Ptr{UInt8}}, fp, s)
newline(fp) # puts appends `\n`, but fputs doesn't (!)
end
puts(s::StringView) = (printf(s); newline())
puts(fp::Ptr{FILE}, s::StringView) = (printf(fp, s); newline(fp))

## --- gets/fgets

Expand Down Expand Up @@ -587,6 +589,18 @@ end
}
""", "main"), Int32, Tuple{Ptr{FILE}, Ptr{UInt8}}, fp, s)
end
function printf(s::StringView)
for i ∈ eachindex(s)
putchar(s[i])
end
return length(s) % Int32
end
function printf(fp::Ptr{FILE}, s::StringView)
for i ∈ eachindex(s)
putchar(fp, s[i])
end
return length(s) % Int32
end

## --- printf/fprintf, with a format string, just like in C

Expand Down
Loading