Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
S
StackExchange.Redis
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
tsai
StackExchange.Redis
Commits
e48955c6
Commit
e48955c6
authored
Jul 27, 2018
by
Marc Gravell
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
more efficient CommandBytes; fixed size, don't store hashcode, use ulong for all internals
parent
20fcc512
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
117 additions
and
74 deletions
+117
-74
CommandBytes.cs
StackExchange.Redis.Server/CommandBytes.cs
+116
-55
RedisRequest.cs
StackExchange.Redis.Server/RedisRequest.cs
+1
-19
No files found.
StackExchange.Redis.Server/CommandBytes.cs
View file @
e48955c6
using
System
;
using
System.Buffers
;
using
System.Text
;
namespace
StackExchange.Redis
{
public
unsafe
struct
CommandBytes
:
IEquatable
<
CommandBytes
>
public
readonly
struct
CommandBytes
:
IEquatable
<
CommandBytes
>
{
public
override
int
GetHashCode
()
=>
_hashcode
;
public
override
string
ToString
()
private
static
Encoding
Encoding
=>
Encoding
.
UTF8
;
// Uses [n=4] x UInt64 values to store a command payload,
// allowing allocation free storage and efficient
// equality tests. If you're glancing at this and thinking
// "that's what fixed buffers are for", please see:
// https://github.com/dotnet/coreclr/issues/19149
//
// note: this tries to use case insensitive comparison
private
readonly
ulong
_0
,
_1
,
_2
;
private
const
int
ChunkLength
=
3
;
// must reflect qty above
public
const
int
MaxLength
=
(
ChunkLength
*
sizeof
(
ulong
))
-
1
;
public
override
int
GetHashCode
()
{
var
hashCode
=
-
1923861349
;
hashCode
=
hashCode
*
-
1521134295
+
_0
.
GetHashCode
();
hashCode
=
hashCode
*
-
1521134295
+
_1
.
GetHashCode
();
hashCode
=
hashCode
*
-
1521134295
+
_2
.
GetHashCode
();
return
hashCode
;
}
public
override
bool
Equals
(
object
obj
)
=>
obj
is
CommandBytes
cb
&&
Equals
(
cb
);
public
bool
Equals
(
CommandBytes
value
)
=>
_0
==
value
.
_0
&&
_1
==
value
.
_1
&&
_2
==
value
.
_2
;
public
static
bool
operator
==
(
CommandBytes
x
,
CommandBytes
y
)
=>
x
.
Equals
(
y
);
public
static
bool
operator
!=(
CommandBytes
x
,
CommandBytes
y
)
=>
!
x
.
Equals
(
y
);
public
override
unsafe
string
ToString
()
{
fixed
(
byte
*
ptr
=
_bytes
)
fixed
(
ulong
*
uPtr
=
&
_0
)
{
return
Encoding
.
UTF8
.
GetString
(
ptr
,
Length
);
var
bPtr
=
(
byte
*)
uPtr
;
int
len
=
*
bPtr
;
return
len
==
0
?
""
:
Encoding
.
GetString
(
bPtr
+
1
,
*
bPtr
);
}
}
public
byte
this
[
int
index
]
public
unsafe
int
Length
{
get
{
if
(
index
<
0
||
index
>=
Length
)
throw
new
IndexOutOfRangeException
();
fixed
(
byte
*
ptr
=
_bytes
)
fixed
(
ulong
*
uPtr
=
&
_0
)
{
return
ptr
[
index
]
;
return
*(
byte
*)
uPtr
;
}
}
}
public
const
int
MaxLength
=
32
;
// mut be multiple of 8
public
int
Length
{
get
;
}
readonly
int
_hashcode
;
fixed
byte
_bytes
[
MaxLength
];
public
CommandBytes
(
string
value
)
public
unsafe
byte
this
[
int
index
]
{
value
=
value
.
ToLowerInvariant
();
Length
=
Encoding
.
UTF8
.
GetByteCount
(
value
);
if
(
Length
>
MaxLength
)
throw
new
ArgumentOutOfRangeException
(
"Maximum command length exceeed"
);
fixed
(
byte
*
bPtr
=
_bytes
)
get
{
Clear
((
long
*)
bPtr
);
fixed
(
char
*
cPtr
=
value
)
fixed
(
ulong
*
uPtr
=
&
_0
)
{
Encoding
.
UTF8
.
GetBytes
(
cPtr
,
value
.
Length
,
bPtr
,
Length
);
byte
*
bPtr
=
(
byte
*)
uPtr
;
int
len
=
*
bPtr
;
if
(
index
<
0
||
index
>=
len
)
throw
new
IndexOutOfRangeException
();
return
bPtr
[
index
+
1
];
}
_hashcode
=
GetHashCode
(
bPtr
,
Length
);
}
}
public
override
bool
Equals
(
object
obj
)
=>
obj
is
CommandBytes
cb
&&
Equals
(
cb
);
public
bool
Equals
(
CommandBytes
value
)
public
unsafe
CommandBytes
(
string
value
)
{
if
(
_hashcode
!=
value
.
_hashcode
||
Length
!=
value
.
Length
)
return
false
;
fixed
(
byte
*
thisB
=
_bytes
)
var
len
=
Encoding
.
GetByteCount
(
value
);
if
(
len
>
MaxLength
)
throw
new
ArgumentOutOfRangeException
(
"Maximum command length exceeed"
);
_0
=
_1
=
_2
=
0L
;
fixed
(
ulong
*
uPtr
=
&
_0
)
{
var
thisL
=
(
long
*)
thisB
;
var
otherL
=
(
long
*)
value
.
_bytes
;
int
chunks
=
(
Length
+
7
)
>>
3
;
for
(
int
i
=
0
;
i
<
chunks
;
i
++)
byte
*
bPtr
=
(
byte
*)
uPtr
;
fixed
(
char
*
cPtr
=
value
)
{
if
(
thisL
[
i
]
!=
otherL
[
i
])
return
false
;
len
=
Encoding
.
GetBytes
(
cPtr
,
value
.
Length
,
bPtr
+
1
,
MaxLength
)
;
}
*
bPtr
=
(
byte
)
LowerCasify
(
len
,
bPtr
+
1
);
}
return
true
;
}
private
static
void
Clear
(
long
*
ptr
)
{
for
(
int
i
=
0
;
i
<
(
MaxLength
>>
3
)
;
i
++)
public
unsafe
CommandBytes
(
ReadOnlySpan
<
byte
>
value
)
{
if
(
value
.
Length
>
MaxLength
)
throw
new
ArgumentOutOfRangeException
(
"Maximum command length exceeed"
);
_0
=
_1
=
_2
=
0L
;
fixed
(
ulong
*
uPtr
=
&
_0
)
{
ptr
[
i
]
=
0
;
byte
*
bPtr
=
(
byte
*)
uPtr
;
value
.
CopyTo
(
new
Span
<
byte
>(
bPtr
+
1
,
value
.
Length
));
*
bPtr
=
(
byte
)
LowerCasify
(
value
.
Length
,
bPtr
+
1
);
}
}
public
CommandBytes
(
ReadOnlySpan
<
byte
>
value
)
public
unsafe
CommandBytes
(
ReadOnlySequence
<
byte
>
value
)
{
Length
=
value
.
Length
;
if
(
Length
>
MaxLength
)
throw
new
ArgumentOutOfRangeException
(
"Maximum command length exceeed"
);
fixed
(
byte
*
bPtr
=
_bytes
)
if
(
value
.
Length
>
MaxLength
)
throw
new
ArgumentOutOfRangeException
(
"Maximum command length exceeed"
);
int
len
=
unchecked
((
int
)
value
.
Length
);
_0
=
_1
=
_2
=
0L
;
fixed
(
ulong
*
uPtr
=
&
_0
)
{
Clear
((
long
*)
bPtr
);
for
(
int
i
=
0
;
i
<
value
.
Length
;
i
++)
byte
*
bPtr
=
(
byte
*)
uPtr
;
var
target
=
new
Span
<
byte
>(
bPtr
+
1
,
len
);
if
(
value
.
IsSingleSegment
)
{
bPtr
[
i
]
=
ToLowerInvariant
(
value
[
i
]
);
value
.
First
.
Span
.
CopyTo
(
target
);
}
_hashcode
=
GetHashCode
(
bPtr
,
Length
);
else
{
foreach
(
var
segment
in
value
)
{
segment
.
Span
.
CopyTo
(
target
);
target
=
target
.
Slice
(
segment
.
Length
);
}
}
*
bPtr
=
(
byte
)
LowerCasify
(
len
,
bPtr
+
1
);
}
}
static
int
GetHashCode
(
byte
*
ptr
,
int
count
)
private
unsafe
int
LowerCasify
(
int
len
,
byte
*
bPtr
)
{
var
hc
=
count
;
for
(
int
i
=
0
;
i
<
count
;
i
++)
const
ulong
HighBits
=
0x8080808080808080
;
if
(((
_0
|
_1
|
_2
)
&
HighBits
)
==
0
)
{
// no unicode; use ASCII bit bricks
for
(
int
i
=
0
;
i
<
len
;
i
++)
{
*
bPtr
=
ToLowerInvariantAscii
(*
bPtr
++);
}
return
len
;
}
else
{
hc
=
(
hc
*
-
13547
)
+
ptr
[
i
]
;
return
LowerCasifyUnicode
(
len
,
bPtr
)
;
}
return
hc
;
}
static
byte
ToLowerInvariant
(
byte
b
)
=>
b
>=
'A'
&&
b
<=
'Z'
?
(
byte
)(
b
|
32
)
:
b
;
internal
byte
[]
ToArray
()
private
static
unsafe
int
LowerCasifyUnicode
(
int
oldLen
,
byte
*
bPtr
)
{
const
int
MaxChars
=
ChunkLength
*
sizeof
(
ulong
);
// leave rounded up; helps stackalloc
char
*
workspace
=
stackalloc
char
[
MaxChars
];
int
charCount
=
Encoding
.
GetChars
(
bPtr
,
oldLen
,
workspace
,
MaxChars
);
char
*
c
=
workspace
;
for
(
int
i
=
0
;
i
<
charCount
;
i
++)
*
c
=
char
.
ToLowerInvariant
((*
c
++));
int
newLen
=
Encoding
.
GetBytes
(
workspace
,
charCount
,
bPtr
,
MaxLength
);
// don't forget to zero any shrink
for
(
int
i
=
newLen
;
i
<
oldLen
;
i
++)
bPtr
[
i
]
=
0
;
return
newLen
;
}
static
byte
ToLowerInvariantAscii
(
byte
b
)
=>
b
>=
'A'
&&
b
<=
'Z'
?
(
byte
)(
b
|
32
)
:
b
;
internal
unsafe
byte
[]
ToArray
()
{
fixed
(
byte
*
ptr
=
_bytes
)
fixed
(
ulong
*
uPtr
=
&
_0
)
{
return
new
Span
<
byte
>(
ptr
,
Length
).
ToArray
();
byte
*
bPtr
=
(
byte
*)
uPtr
;
return
new
Span
<
byte
>(
bPtr
+
1
,
*
bPtr
).
ToArray
();
}
}
}
...
...
StackExchange.Redis.Server/RedisRequest.cs
View file @
e48955c6
...
...
@@ -59,25 +59,7 @@ internal bool TryGetCommandBytes(int i, out CommandBytes command)
return
false
;
}
if
(
payload
.
Length
==
0
)
{
command
=
default
;
}
else
if
(
payload
.
IsSingleSegment
)
{
command
=
new
CommandBytes
(
payload
.
First
.
Span
);
}
else
{
Span
<
byte
>
span
=
stackalloc
byte
[
CommandBytes
.
MaxLength
];
var
sliced
=
span
;
foreach
(
var
segment
in
payload
)
{
segment
.
Span
.
CopyTo
(
sliced
);
sliced
=
sliced
.
Slice
(
segment
.
Length
);
}
command
=
new
CommandBytes
(
span
.
Slice
(
0
,
(
int
)
payload
.
Length
));
}
command
=
payload
.
IsEmpty
?
default
:
new
CommandBytes
(
payload
);
return
true
;
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment