Skip to content

Commit 3296160

Browse files
authored
Merge branch 'master' into ox/eq
2 parents 156924c + f7d4f41 commit 3296160

2 files changed

Lines changed: 133 additions & 81 deletions

File tree

src/base.jl

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,17 @@ struct ShortString{T} <: AbstractString where T
66
size_content::T
77
end
88

9-
10-
function ShortString{T}(s::Union{String, SubString{String}}) where T
11-
sz = ncodeunits(s)
12-
max_len = sizeof(T) - size_nibbles(T)
13-
if sz > max_len # the last byte is used to store the length
9+
# check if a string of size `sz` can be stored in ShortString{T}`
10+
function check_size(T, sz)
11+
max_len = sizeof(T) - size_nibbles(T) # the last few nibbles are is used to store the length
12+
if sz > max_len
1413
throw(ErrorException("sizeof(::ShortString) must be shorter than or equal to $(max_len) in length; you have supplied a string of size $sz"))
1514
end
15+
end
16+
17+
function ShortString{T}(s::Union{String, SubString{String}}) where T
18+
sz = sizeof(s)
19+
check_size(T, sz)
1620
bits_to_wipe = 8(sizeof(T) - sz)
1721
# TODO some times this can throw errors for longish strings
1822
# Exception: EXCEPTION_ACCESS_VIOLATION at 0x1e0b7afd -- bswap at C:\Users\RTX2080\.julia\packages\BitIntegers\xU40U\src\BitIntegers.jl:332 [inlined]
@@ -21,6 +25,19 @@ function ShortString{T}(s::Union{String, SubString{String}}) where T
2125
ShortString{T}(content | T(sz))
2226
end
2327

28+
ShortString{T}(s::ShortString{T}) where T = s
29+
function ShortString{T}(s::ShortString{S}) where {T, S}
30+
sz = sizeof(s)
31+
check_size(T, sz)
32+
# Flip it so empty bytes are at start, grow/shrink it, flip it back
33+
# S(size_mask(S)) will return a mask for getting the size for Shorting Strings in (content size)
34+
# format, so something like 00001111 in binary.
35+
# ~S(size_mask(S))) will yield 11110000 which can be used as maks to extract the content
36+
content = ntoh(T(ntoh(s.size_content & ~S(size_mask(S)))))
37+
ShortString{T}(content | T(sz))
38+
end
39+
40+
2441
String(s::ShortString) = String(reinterpret(UInt8, [s.size_content|>ntoh])[1:sizeof(s)])
2542

2643
Base.codeunit(s::ShortString) = UInt8
@@ -82,6 +99,10 @@ function ==(a::ShortString{A}, b::ShortString{B}) where {A,B}
8299
end
83100

84101

102+
function Base.cmp(a::ShortString{S}, b::ShortString{S}) where S
103+
return cmp(a.size_content, b.size_content)
104+
end
105+
85106
promote_rule(::Type{String}, ::Type{ShortString{S}}) where S = String
86107
promote_rule(::Type{ShortString{T}}, ::Type{ShortString{S}}) where {T,S} = ShortString{promote_rule(T,S)}
87108

test/runtests.jl

Lines changed: 107 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,107 @@
1-
using ShortStrings
2-
using BitIntegers: UInt256, UInt512, UInt1024, @define_integers
3-
using Test, Random
4-
5-
include("getindex.jl")
6-
7-
function basic_test(constructor, max_len)
8-
@testset "$constructor" begin
9-
for string_type in (String, SubString{String})
10-
@testset "$string_type" begin
11-
basic_test(string_type, constructor, max_len)
12-
end
13-
end
14-
end
15-
end
16-
17-
function basic_test(string_type, constructor, max_len)
18-
r = string_type.(randstring.(1:max_len))
19-
@test all(constructor.(r) .== r)
20-
@test all(hash(constructor.(r)) .== hash(r))
21-
a = constructor.(r)
22-
@test fsort(a) |> issorted
23-
24-
@test collect(constructor("z"^max_len)) == fill('z', max_len)
25-
@test_throws ErrorException constructor("a"^(max_len+1))
26-
27-
# equality
28-
@test constructor("c"^max_len) == "c"^max_len
29-
@test "c"^max_len == constructor("c"^max_len)
30-
@test constructor("c"^max_len) == constructor("c"^max_len)
31-
@test constructor("c"^max_len) != constructor("d"^max_len)
32-
@test constructor("c"^max_len) != constructor("c"^(max_len-1))
33-
@test constructor("c"^(max_len-1)) != constructor("c"^max_len)
34-
@test constructor("c"^max_len) != "c"^(max_len-1)
35-
@test constructor("c"^(max_len-1)) != "c"^max_len
36-
end
37-
38-
39-
basic_test(ShortString3, 3)
40-
basic_test(ShortString7, 7)
41-
basic_test(ShortString15, 15)
42-
basic_test(ShortString30, 30)
43-
basic_test(ShortString62, 62)
44-
basic_test(ShortString126, 126)
45-
46-
basic_test(ShortString{UInt16}, 1)
47-
basic_test(ShortString{UInt32}, 3)
48-
basic_test(ShortString{UInt64}, 7)
49-
basic_test(ShortString{UInt128}, 15)
50-
basic_test(ShortString{UInt256}, 30)
51-
basic_test(ShortString{UInt512}, 62)
52-
basic_test(ShortString{UInt1024}, 126)
53-
54-
@define_integers 2048 MyInt2048 MyUInt2048
55-
basic_test(ShortString{MyUInt2048}, 254)
56-
57-
@test ss126"Be honest, do you actually need a string longer than this. Seriously. C'mon this is pretty long." === ShortString126("Be honest, do you actually need a string longer than this. Seriously. C'mon this is pretty long.")
58-
@test ss62"Basically a failly long string really" === ShortString62("Basically a failly long string really")
59-
@test ss30"A Longer String!!!" === ShortString30("A Longer String!!!")
60-
61-
@test ss15"Short String!!!" === ShortString15("Short String!!!")
62-
@test ss7"ShrtStr" === ShortString7("ShrtStr")
63-
@test ss3"ss3" === ShortString3("ss3")
64-
65-
66-
@testset "equality of different sized ShortStrings" begin
67-
@test ShortString15("ab") == ShortString3("ab")
68-
@test ShortString3("ab") == ShortString15("ab")
69-
70-
@test ShortString30("x") != ShortString3("y")
71-
@test ShortString30("y") != ShortString3("x")
72-
73-
# this one is too big to fit in the other
74-
@test ShortString15("abcd") != ShortString3("ab")
75-
@test ShortString3("ab") != ShortString15("abcd")
76-
end
1+
using ShortStrings
2+
using BitIntegers: UInt256, UInt512, UInt1024, @define_integers
3+
using Test, Random
4+
5+
include("getindex.jl")
6+
7+
function basic_test(constructor, max_len)
8+
@testset "$constructor" begin
9+
for string_type in (String, SubString{String})
10+
@testset "$string_type" begin
11+
basic_test(string_type, constructor, max_len)
12+
end
13+
end
14+
end
15+
end
16+
17+
function basic_test(string_type, constructor, max_len)
18+
r = string_type.(randstring.(1:max_len))
19+
@test all(constructor.(r) .== r)
20+
@test all(hash(constructor.(r)) .== hash(r))
21+
a = constructor.(r)
22+
@test fsort(a) |> issorted
23+
24+
@test collect(constructor("z"^max_len)) == fill('z', max_len)
25+
@test_throws ErrorException constructor("a"^(max_len+1))
26+
27+
# equality
28+
@test constructor("c"^max_len) == "c"^max_len
29+
@test "c"^max_len == constructor("c"^max_len)
30+
@test constructor("c"^max_len) == constructor("c"^max_len)
31+
@test constructor("c"^max_len) != constructor("d"^max_len)
32+
@test constructor("c"^max_len) != constructor("c"^(max_len-1))
33+
@test constructor("c"^(max_len-1)) != constructor("c"^max_len)
34+
@test constructor("c"^max_len) != "c"^(max_len-1)
35+
@test constructor("c"^(max_len-1)) != "c"^max_len
36+
end
37+
38+
39+
basic_test(ShortString3, 3)
40+
basic_test(ShortString7, 7)
41+
basic_test(ShortString15, 15)
42+
basic_test(ShortString30, 30)
43+
basic_test(ShortString62, 62)
44+
basic_test(ShortString126, 126)
45+
46+
basic_test(ShortString{UInt16}, 1)
47+
basic_test(ShortString{UInt32}, 3)
48+
basic_test(ShortString{UInt64}, 7)
49+
basic_test(ShortString{UInt128}, 15)
50+
basic_test(ShortString{UInt256}, 30)
51+
basic_test(ShortString{UInt512}, 62)
52+
basic_test(ShortString{UInt1024}, 126)
53+
54+
@define_integers 2048 MyInt2048 MyUInt2048
55+
basic_test(ShortString{MyUInt2048}, 254)
56+
57+
@test ss126"Be honest, do you actually need a string longer than this. Seriously. C'mon this is pretty long." === ShortString126("Be honest, do you actually need a string longer than this. Seriously. C'mon this is pretty long.")
58+
@test ss62"Basically a failly long string really" === ShortString62("Basically a failly long string really")
59+
@test ss30"A Longer String!!!" === ShortString30("A Longer String!!!")
60+
61+
@test ss15"Short String!!!" === ShortString15("Short String!!!")
62+
@test ss7"ShrtStr" === ShortString7("ShrtStr")
63+
@test ss3"ss3" === ShortString3("ss3")
64+
65+
66+
@testset "equality of different sized ShortStrings" begin
67+
@test ShortString15("ab") == ShortString3("ab")
68+
@test ShortString3("ab") == ShortString15("ab")
69+
70+
@test ShortString30("x") != ShortString3("y")
71+
@test ShortString30("y") != ShortString3("x")
72+
73+
# this one is too big to fit in the other
74+
@test ShortString15("abcd") != ShortString3("ab")
75+
@test ShortString3("ab") != ShortString15("abcd")
76+
end
77+
78+
@testset "cmp" begin
79+
@test cmp(ShortString3("abc"), ShortString3("abc")) == 0
80+
@test cmp(ShortString3("ab"), ShortString3("abc")) == -1
81+
@test cmp(ShortString3("abc"), ShortString3("ab")) == 1
82+
@test cmp(ShortString3("ab"), ShortString3("ac")) == -1
83+
@test cmp(ShortString3("ac"), ShortString3("ab")) == 1
84+
@test cmp(ShortString3("α"), ShortString3("a")) == 1
85+
@test cmp(ShortString3("b"), ShortString3("β")) == -1
86+
87+
@test cmp(ShortString3("abc"), "abc") == 0
88+
@test cmp(ShortString3("ab"), "abc") == -1
89+
@test cmp(ShortString3("abc"), "ab") == 1
90+
@test cmp(ShortString3("ab"), "ac") == -1
91+
@test cmp(ShortString3("ac"), "ab") == 1
92+
@test cmp(ShortString3("α"), "a") == 1
93+
@test cmp(ShortString3("b"), "β") == -1
94+
end
95+
96+
@testset "Construction from other ShortStrings" begin
97+
@test ShortString7(ShortString3("ab")) == "ab"
98+
@test ShortString7(ShortString3("ab")) isa ShortString7
99+
100+
@test ShortString3(ShortString7("ab")) == "ab"
101+
@test ShortString3(ShortString7("ab")) isa ShortString3
102+
103+
@test ShortString7(ShortString7("ab")) == "ab"
104+
@test ShortString7(ShortString7("ab")) isa ShortString7
105+
106+
@test_throws ErrorException ShortString3(ShortString7("123456"))
107+
end

0 commit comments

Comments
 (0)