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
4f182469
Commit
4f182469
authored
Jun 27, 2018
by
Marc Gravell
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
intermediate commit
parent
15319500
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
376 additions
and
190 deletions
+376
-190
StackExchange.Redis.csproj
StackExchange.Redis/StackExchange.Redis.csproj
+1
-0
Format.cs
StackExchange.Redis/StackExchange/Redis/Format.cs
+73
-0
PhysicalConnection.cs
...kExchange.Redis/StackExchange/Redis/PhysicalConnection.cs
+2
-2
RedisKey.cs
StackExchange.Redis/StackExchange/Redis/RedisKey.cs
+2
-2
RedisValue.cs
StackExchange.Redis/StackExchange/Redis/RedisValue.cs
+298
-186
No files found.
StackExchange.Redis/StackExchange.Redis.csproj
View file @
4f182469
...
...
@@ -14,6 +14,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Numerics.Vectors" Version="4.5.0" />
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.3.0" />
<PackageReference Include="System.IO.Pipelines" Version="$(CoreFxVersion)" />
<PackageReference Include="System.Diagnostics.PerformanceCounter" Version="$(CoreFxVersion)" />
...
...
StackExchange.Redis/StackExchange/Redis/Format.cs
View file @
4f182469
using
System
;
using
System.Globalization
;
using
System.Net
;
using
System.Numerics
;
using
System.Runtime.InteropServices
;
using
System.Text
;
namespace
StackExchange.Redis
{
...
...
@@ -137,6 +140,50 @@ internal static bool TryParseDouble(string s, out double value)
return
double
.
TryParse
(
s
,
NumberStyles
.
Any
,
NumberFormatInfo
.
InvariantInfo
,
out
value
);
}
internal
static
bool
TryParseDouble
(
ReadOnlySpan
<
byte
>
s
,
out
double
value
)
{
if
(
s
.
IsEmpty
)
{
value
=
0
;
return
false
;
}
if
(
s
.
Length
==
1
&&
s
[
0
]
>=
'0'
&&
s
[
0
]
<=
'9'
)
{
value
=
(
int
)(
s
[
0
]
-
'0'
);
return
true
;
}
// need to handle these
if
(
CaseInsensitiveASCIIEqual
(
"+inf"
,
s
)
||
CaseInsensitiveASCIIEqual
(
"inf"
,
s
))
{
value
=
double
.
PositiveInfinity
;
return
true
;
}
if
(
CaseInsensitiveASCIIEqual
(
"-inf"
,
s
))
{
value
=
double
.
NegativeInfinity
;
return
true
;
}
var
ss
=
DecodeUtf8
(
s
);
return
double
.
TryParse
(
ss
,
NumberStyles
.
Any
,
NumberFormatInfo
.
InvariantInfo
,
out
value
);
}
internal
static
unsafe
string
DecodeUtf8
(
ReadOnlySpan
<
byte
>
s
)
{
if
(
s
.
IsEmpty
)
return
""
;
fixed
(
byte
*
ptr
=
&
MemoryMarshal
.
GetReference
(
s
))
{
return
Encoding
.
UTF8
.
GetString
(
ptr
,
s
.
Length
);
}
}
static
bool
CaseInsensitiveASCIIEqual
(
string
xLowerCase
,
ReadOnlySpan
<
byte
>
y
)
{
if
(
y
.
Length
!=
xLowerCase
.
Length
)
return
false
;
for
(
int
i
=
0
;
i
<
y
.
Length
;
i
++)
{
if
(
char
.
ToLower
((
char
)
y
[
i
])
!=
xLowerCase
[
i
])
return
false
;
}
return
true
;
}
internal
static
EndPoint
TryParseEndPoint
(
string
endpoint
)
{
if
(
string
.
IsNullOrWhiteSpace
(
endpoint
))
return
null
;
...
...
@@ -159,5 +206,31 @@ internal static EndPoint TryParseEndPoint(string endpoint)
return
ParseEndPoint
(
host
,
port
);
}
static
readonly
Vector
<
ushort
>
NonAsciiMask
=
new
Vector
<
ushort
>(
0xFF80
);
internal
static
unsafe
int
GetEncodedLength
(
string
value
)
{
if
(
value
.
Length
==
0
)
return
0
;
int
offset
=
0
;
if
(
Vector
.
IsHardwareAccelerated
&&
value
.
Length
>=
Vector
<
ushort
>.
Count
)
{
var
vecSpan
=
MemoryMarshal
.
Cast
<
char
,
Vector
<
ushort
>>(
value
.
AsSpan
());
var
nonAscii
=
NonAsciiMask
;
int
i
;
for
(
i
=
0
;
i
<
vecSpan
.
Length
;
i
++)
{
if
((
vecSpan
[
i
]
&
nonAscii
)
!=
Vector
<
ushort
>.
Zero
)
break
;
}
offset
=
Vector
<
ushort
>.
Count
*
i
;
}
int
remaining
=
value
.
Length
-
offset
;
if
(
remaining
==
0
)
return
offset
;
// all ASCII (nice round length, and Vector support)
// handles a) no Vector support, b) anything from the fisrt non-ASCII chunk, c) tail end
fixed
(
char
*
ptr
=
value
)
{
return
offset
+
Encoding
.
UTF8
.
GetByteCount
(
ptr
+
offset
,
remaining
);
}
}
}
}
StackExchange.Redis/StackExchange/Redis/PhysicalConnection.cs
View file @
4f182469
...
...
@@ -697,7 +697,7 @@ private void WriteUnified(PipeWriter writer, byte[] prefix, string value)
{
// ${total-len}\r\n 3 + MaxInt32TextLen
// {prefix}{value}\r\n
int
encodedLength
=
Encoding
.
UTF8
.
GetByteCount
(
value
),
int
encodedLength
=
Format
.
GetEncodedLength
(
value
),
prefixLength
=
prefix
==
null
?
0
:
prefix
.
Length
,
totalLength
=
prefixLength
+
encodedLength
;
...
...
StackExchange.Redis/StackExchange/Redis/RedisKey.cs
View file @
4f182469
...
...
@@ -255,7 +255,7 @@ internal static byte[] ConcatenateBytes(byte[] a, object b, byte[] c)
int
aLen
=
a
?.
Length
??
0
,
bLen
=
b
==
null
?
0
:
(
b
is
string
?
Encoding
.
UTF8
.
GetByteCount
((
string
)
b
)
?
Format
.
GetEncodedLength
((
string
)
b
)
:
((
byte
[])
b
).
Length
),
cLen
=
c
?.
Length
??
0
;
...
...
StackExchange.Redis/StackExchange/Redis/RedisValue.cs
View file @
4f182469
using
System
;
using
System.Runtime.InteropServices
;
using
System.Text
;
namespace
StackExchange.Redis
...
...
@@ -8,43 +9,57 @@ namespace StackExchange.Redis
/// </summary>
public
struct
RedisValue
:
IEquatable
<
RedisValue
>,
IComparable
<
RedisValue
>,
IComparable
,
IConvertible
{
internal
static
readonly
RedisValue
[]
EmptyArray
=
new
RedisValue
[
0
]
;
private
static
readonly
byte
[]
EmptyByteArr
=
new
byte
[
0
];
private
static
readonly
byte
[]
IntegerSentinel
=
new
byte
[
0
]
;
private
readonly
byte
[]
valueBlob
;
private
readonly
long
valueInt
64
;
internal
static
readonly
RedisValue
[]
EmptyArray
=
Array
.
Empty
<
RedisValue
>()
;
private
readonly
object
_objectOrSentinel
;
private
readonly
ReadOnlyMemory
<
byte
>
_memory
;
private
readonly
long
_overlappedValue
64
;
// internal bool IsNullOrDefaultValue { get { return (valueBlob == null && valueInt64 == 0L) || ((object)valueBlob == (object)NullSentinel); } }
private
RedisValue
(
long
valueInt64
,
byte
[]
valueBlob
)
private
RedisValue
(
long
overlappedValue64
,
ReadOnlyMemory
<
byte
>
memory
,
object
objectOrSentinel
)
{
this
.
valueInt64
=
valueInt64
;
this
.
valueBlob
=
valueBlob
;
_overlappedValue64
=
overlappedValue64
;
_memory
=
memory
;
_objectOrSentinel
=
objectOrSentinel
;
}
private
readonly
static
object
Sentinel_Integer
=
new
object
();
private
readonly
static
object
Sentinel_Raw
=
new
object
();
private
readonly
static
object
Sentinel_Double
=
new
object
();
/// <summary>
/// Represents the string <c>""</c>
/// </summary>
public
static
RedisValue
EmptyString
{
get
;
}
=
new
RedisValue
(
0
,
EmptyByteArr
);
public
static
RedisValue
EmptyString
{
get
;
}
=
new
RedisValue
(
0
,
default
,
Sentinel_Raw
);
/// <summary>
/// A null value
/// </summary>
public
static
RedisValue
Null
{
get
;
}
=
new
RedisValue
(
0
,
null
);
public
static
RedisValue
Null
{
get
;
}
=
new
RedisValue
(
0
,
default
,
null
);
/// <summary>
/// Indicates whether the value is a primitive integer
/// </summary>
public
bool
IsInteger
=>
valueBlob
==
IntegerSentinel
;
public
bool
IsInteger
=>
_objectOrSentinel
==
Sentinel_Integer
;
/// <summary>
/// Indicates whether the value should be considered a null value
/// </summary>
public
bool
IsNull
=>
valueBlob
==
null
;
public
bool
IsNull
=>
_objectOrSentinel
==
null
;
/// <summary>
/// Indicates whether the value is either null or a zero-length value
/// </summary>
public
bool
IsNullOrEmpty
=>
valueBlob
==
null
||
(
valueBlob
.
Length
==
0
&&
!(
valueBlob
==
IntegerSentinel
));
public
bool
IsNullOrEmpty
{
get
{
if
(
IsNull
)
return
true
;
if
(
_objectOrSentinel
==
Sentinel_Raw
&&
_memory
.
Length
==
0
)
return
true
;
if
(
_objectOrSentinel
is
string
s
&&
s
.
Length
==
0
)
return
true
;
if
(
_objectOrSentinel
is
byte
[]
arr
&&
arr
.
Length
==
0
)
return
true
;
return
false
;
}
}
/// <summary>
/// Indicates whether the value is greater than zero-length or has an integer value
...
...
@@ -58,6 +73,8 @@ private RedisValue(long valueInt64, byte[] valueBlob)
/// <param name="y">The second <see cref="RedisValue"/> to compare.</param>
public
static
bool
operator
!=(
RedisValue
x
,
RedisValue
y
)
=>
!(
x
==
y
);
private
double
OverlappedValueDouble
=>
BitConverter
.
Int64BitsToDouble
(
_overlappedValue64
);
/// <summary>
/// Indicates whether two RedisValue values are equivalent
/// </summary>
...
...
@@ -65,25 +82,28 @@ private RedisValue(long valueInt64, byte[] valueBlob)
/// <param name="y">The second <see cref="RedisValue"/> to compare.</param>
public
static
bool
operator
==(
RedisValue
x
,
RedisValue
y
)
{
if
(
x
.
valueBlob
==
null
)
return
y
.
valueBlob
==
null
;
CompareType
xType
=
x
.
Type
,
yType
=
y
.
Type
;
if
(
xType
==
CompareType
.
Null
)
return
yType
==
CompareType
.
Null
;
if
(
xType
==
CompareType
.
Null
)
return
false
;
if
(
x
.
valueBlob
==
IntegerSentinel
)
if
(
x
Type
==
yType
)
{
if
(
y
.
valueBlob
==
IntegerSentinel
)
switch
(
xType
)
{
return
x
.
valueInt64
==
y
.
valueInt64
;
case
CompareType
.
Double
:
return
x
.
OverlappedValueDouble
==
y
.
OverlappedValueDouble
;
case
CompareType
.
Int64
:
return
x
.
_overlappedValue64
==
y
.
_overlappedValue64
;
case
CompareType
.
String
:
return
(
string
)
x
.
_objectOrSentinel
==
(
string
)
y
.
_objectOrSentinel
;
case
CompareType
.
Raw
:
return
x
.
_memory
.
Span
.
SequenceEqual
(
y
.
_memory
.
Span
);
}
else
{
return
Equals
((
byte
[])
x
,
(
byte
[])
y
);
}
}
else
if
(
y
.
valueBlob
==
IntegerSentinel
)
{
return
Equals
((
byte
[])
x
,
(
byte
[])
y
);
}
return
Equals
(
x
.
valueBlob
,
y
.
valueBlob
);
// otherwise, compare as strings
return
(
string
)
x
==
(
string
)
y
;
}
/// <summary>
...
...
@@ -92,29 +112,11 @@ private RedisValue(long valueInt64, byte[] valueBlob)
/// <param name="obj">The other <see cref="RedisValue"/> to compare.</param>
public
override
bool
Equals
(
object
obj
)
{
if
(
obj
==
null
)
return
valueBlob
==
n
ull
;
if
(
obj
==
null
)
return
IsN
ull
;
if
(
obj
is
RedisValue
)
{
return
Equals
((
RedisValue
)
obj
);
}
if
(
obj
is
string
)
{
return
(
string
)
obj
==
(
string
)
this
;
}
if
(
obj
is
byte
[])
{
return
Equals
((
byte
[])
obj
,
(
byte
[])
this
);
}
if
(
obj
is
long
)
{
return
(
long
)
obj
==
(
long
)
this
;
}
if
(
obj
is
int
)
{
return
(
int
)
obj
==
(
int
)
this
;
}
return
false
;
var
other
=
TryParse
(
obj
);
if
(
other
.
IsNull
)
return
false
;
// parse fail
return
this
==
other
;
}
/// <summary>
...
...
@@ -128,9 +130,20 @@ public override bool Equals(object obj)
/// </summary>
public
override
int
GetHashCode
()
{
if
(
valueBlob
==
IntegerSentinel
)
return
valueInt64
.
GetHashCode
();
if
(
valueBlob
==
null
)
return
-
1
;
return
GetHashCode
(
valueBlob
);
switch
(
Type
)
{
case
CompareType
.
Null
:
return
-
1
;
case
CompareType
.
Double
:
return
OverlappedValueDouble
.
GetHashCode
();
case
CompareType
.
Int64
:
return
_overlappedValue64
.
GetHashCode
();
case
CompareType
.
Raw
:
return
GetHashCode
(
_memory
);
case
CompareType
.
String
:
default
:
return
_objectOrSentinel
.
GetHashCode
();
}
}
/// <summary>
...
...
@@ -162,35 +175,31 @@ internal static unsafe bool Equals(byte[] x, byte[] y)
return
true
;
}
internal
static
unsafe
int
GetHashCode
(
byte
[]
value
)
internal
static
unsafe
int
GetHashCode
(
ReadOnlyMemory
<
byte
>
memory
)
{
unchecked
{
if
(
value
==
null
)
return
-
1
;
int
len
=
value
.
Length
;
var
span8
=
memory
.
Span
;
int
len
=
span8
.
Length
;
if
(
len
==
0
)
return
0
;
int
octets
=
len
/
8
,
spare
=
len
%
8
;
int
acc
=
728271210
;
fixed
(
byte
*
ptr8
=
value
)
{
long
*
ptr64
=
(
long
*)
ptr8
;
for
(
int
i
=
0
;
i
<
octets
;
i
++)
var
span64
=
MemoryMarshal
.
Cast
<
byte
,
long
>(
span8
);
for
(
int
i
=
0
;
i
<
span64
.
Length
;
i
++)
{
long
val
=
ptr
64
[
i
];
var
val
=
span
64
[
i
];
int
valHash
=
(((
int
)
val
)
^
((
int
)(
val
>>
32
)));
acc
=
(((
acc
<<
5
)
+
acc
)
^
valHash
);
}
int
offset
=
len
-
spare
;
int
spare
=
len
%
8
,
offset
=
len
-
spare
;
while
(
spare
--
!=
0
)
{
acc
=
(((
acc
<<
5
)
+
acc
)
^
ptr8
[
offset
++]);
}
acc
=
(((
acc
<<
5
)
+
acc
)
^
span8
[
offset
++]);
}
return
acc
;
}
}
internal
static
bool
TryParseInt64
(
byte
[]
value
,
int
offset
,
int
count
,
out
long
result
)
=>
TryParseInt64
(
new
ReadOnlySpan
<
byte
>(
value
,
offset
,
count
),
out
result
);
internal
static
bool
TryParseInt64
(
ReadOnlySpan
<
byte
>
value
,
out
long
result
)
{
result
=
0
;
...
...
@@ -220,6 +229,35 @@ internal static bool TryParseInt64(ReadOnlySpan<byte> value, out long result)
}
}
}
internal
static
bool
TryParseInt64
(
string
value
,
out
long
result
)
{
result
=
0
;
if
(
string
.
IsNullOrEmpty
(
value
))
return
false
;
checked
{
int
max
=
value
.
Length
;
if
(
value
[
0
]
==
'-'
)
{
for
(
int
i
=
1
;
i
<
max
;
i
++)
{
var
b
=
value
[
i
];
if
(
b
<
'0'
||
b
>
'9'
)
return
false
;
result
=
(
result
*
10
)
-
(
b
-
'0'
);
}
return
true
;
}
else
{
for
(
int
i
=
0
;
i
<
max
;
i
++)
{
var
b
=
value
[
i
];
if
(
b
<
'0'
||
b
>
'9'
)
return
false
;
result
=
(
result
*
10
)
+
(
b
-
'0'
);
}
return
true
;
}
}
}
internal
void
AssertNotNull
()
{
...
...
@@ -228,37 +266,22 @@ internal void AssertNotNull()
private
enum
CompareType
{
Null
,
Int64
,
Double
,
Raw
Null
,
Int64
,
Double
,
Raw
,
String
,
}
private
CompareType
ResolveType
(
out
long
i64
,
out
double
r8
)
{
byte
[]
blob
=
valueBlob
;
if
(
blob
==
IntegerSentinel
)
private
CompareType
Type
{
i64
=
valueInt64
;
r8
=
default
(
double
);
return
CompareType
.
Int64
;
}
if
(
blob
==
null
)
get
{
i64
=
default
(
long
);
r8
=
default
(
double
);
return
CompareType
.
Null
;
var
objectOrSentinel
=
_objectOrSentinel
;
if
(
objectOrSentinel
==
null
)
return
CompareType
.
Null
;
if
(
objectOrSentinel
==
Sentinel_Integer
)
return
CompareType
.
Int64
;
if
(
objectOrSentinel
==
Sentinel_Double
)
return
CompareType
.
Double
;
if
(
objectOrSentinel
==
Sentinel_Raw
)
return
CompareType
.
Raw
;
if
(
objectOrSentinel
is
string
)
return
CompareType
.
String
;
if
(
objectOrSentinel
is
byte
[])
return
CompareType
.
Raw
;
// doubled-up, but retaining the array
throw
new
InvalidOperationException
(
"Unknown type"
);
}
if
(
TryParseInt64
(
blob
,
0
,
blob
.
Length
,
out
i64
))
{
r8
=
default
(
double
);
return
CompareType
.
Int64
;
}
if
(
TryParseDouble
(
blob
,
out
r8
))
{
i64
=
default
(
long
);
return
CompareType
.
Double
;
}
i64
=
default
(
long
);
r8
=
default
(
double
);
return
CompareType
.
Raw
;
}
/// <summary>
...
...
@@ -269,30 +292,29 @@ public int CompareTo(RedisValue other)
{
try
{
CompareType
thisType
=
ResolveType
(
out
long
thisInt64
,
out
double
thisDouble
)
,
otherType
=
other
.
ResolveType
(
out
long
otherInt64
,
out
double
otherDouble
)
;
CompareType
thisType
=
this
.
Type
,
otherType
=
other
.
Type
;
if
(
thisType
==
CompareType
.
Null
)
{
return
otherType
==
CompareType
.
Null
?
0
:
-
1
;
}
if
(
otherType
==
CompareType
.
Null
)
{
return
1
;
}
if
(
thisType
==
CompareType
.
Null
)
return
otherType
==
CompareType
.
Null
?
0
:
-
1
;
if
(
otherType
==
CompareType
.
Null
)
return
1
;
if
(
thisType
==
CompareType
.
Int64
)
if
(
thisType
==
otherType
)
{
if
(
otherType
==
CompareType
.
Int64
)
return
thisInt64
.
CompareTo
(
otherInt64
);
if
(
otherType
==
CompareType
.
Double
)
return
((
double
)
thisInt64
).
CompareTo
(
otherDouble
);
}
else
if
(
thisType
==
CompareType
.
Double
)
switch
(
thisType
)
{
if
(
otherType
==
CompareType
.
Int64
)
return
thisDouble
.
CompareTo
((
double
)
otherInt64
);
if
(
otherType
==
CompareType
.
Double
)
return
thisDouble
.
CompareTo
(
otherDouble
);
case
CompareType
.
Double
:
return
this
.
OverlappedValueDouble
.
CompareTo
(
other
.
OverlappedValueDouble
);
case
CompareType
.
Int64
:
return
this
.
_overlappedValue64
.
CompareTo
(
other
.
_overlappedValue64
);
case
CompareType
.
String
:
return
string
.
CompareOrdinal
((
string
)
this
.
_objectOrSentinel
,
(
string
)
other
.
_objectOrSentinel
);
case
CompareType
.
Raw
:
return
this
.
_memory
.
Span
.
SequenceCompareTo
(
other
.
_memory
.
Span
);
}
}
// otherwise, compare as strings
return
StringComparer
.
InvariantCulture
.
Compare
((
string
)
this
,
(
string
)
other
);
return
string
.
CompareOrdinal
((
string
)
this
,
(
string
)
other
);
}
catch
(
Exception
ex
)
{
...
...
@@ -304,20 +326,36 @@ public int CompareTo(RedisValue other)
int
IComparable
.
CompareTo
(
object
obj
)
{
if
(
obj
is
RedisValue
)
return
CompareTo
((
RedisValue
)
obj
);
if
(
obj
is
long
)
return
CompareTo
((
RedisValue
)(
long
)
obj
);
if
(
obj
is
double
)
return
CompareTo
((
RedisValue
)(
double
)
obj
);
if
(
obj
is
string
)
return
CompareTo
((
RedisValue
)(
string
)
obj
);
if
(
obj
is
byte
[])
return
CompareTo
((
RedisValue
)(
byte
[])
obj
);
if
(
obj
is
bool
)
return
CompareTo
((
RedisValue
)(
bool
)
obj
);
return
-
1
;
if
(
obj
==
null
)
return
CompareTo
(
Null
);
var
val
=
TryParse
(
obj
);
if
(
val
.
IsNull
)
return
-
1
;
// parse fail
return
CompareTo
(
val
);
}
internal
static
RedisValue
TryParse
(
object
obj
)
{
if
(
obj
==
null
)
return
RedisValue
.
Null
;
if
(
obj
is
RedisValue
)
return
(
RedisValue
)
obj
;
if
(
obj
is
string
)
return
(
RedisValue
)(
string
)
obj
;
if
(
obj
is
int
)
return
(
RedisValue
)(
int
)
obj
;
if
(
obj
is
double
)
return
(
RedisValue
)(
double
)
obj
;
if
(
obj
is
byte
[])
return
(
RedisValue
)(
byte
[])
obj
;
if
(
obj
is
bool
)
return
(
RedisValue
)(
bool
)
obj
;
if
(
obj
is
long
)
return
(
RedisValue
)(
long
)
obj
;
if
(
obj
is
float
)
return
(
RedisValue
)(
float
)
obj
;
if
(
obj
is
ReadOnlyMemory
<
byte
>)
return
(
RedisValue
)(
ReadOnlyMemory
<
byte
>)
obj
;
if
(
obj
is
Memory
<
byte
>)
return
(
RedisValue
)(
Memory
<
byte
>)
obj
;
return
Null
;
}
/// <summary>
/// Creates a new <see cref="RedisValue"/> from an <see cref="int"/>.
/// </summary>
/// <param name="value">The <see cref="int"/> to convert to a <see cref="RedisValue"/>.</param>
public
static
implicit
operator
RedisValue
(
int
value
)
=>
new
RedisValue
(
value
,
IntegerSentinel
);
public
static
implicit
operator
RedisValue
(
int
value
)
=>
new
RedisValue
(
value
,
default
,
Sentinel_Integer
);
/// <summary>
/// Creates a new <see cref="RedisValue"/> from an <see cref="T:Nullable{int}"/>.
...
...
@@ -329,7 +367,7 @@ int IComparable.CompareTo(object obj)
/// Creates a new <see cref="RedisValue"/> from an <see cref="long"/>.
/// </summary>
/// <param name="value">The <see cref="long"/> to convert to a <see cref="RedisValue"/>.</param>
public
static
implicit
operator
RedisValue
(
long
value
)
=>
new
RedisValue
(
value
,
IntegerSentinel
);
public
static
implicit
operator
RedisValue
(
long
value
)
=>
new
RedisValue
(
value
,
default
,
Sentinel_Integer
);
/// <summary>
/// Creates a new <see cref="RedisValue"/> from an <see cref="T:Nullable{long}"/>.
...
...
@@ -341,7 +379,16 @@ int IComparable.CompareTo(object obj)
/// Creates a new <see cref="RedisValue"/> from an <see cref="double"/>.
/// </summary>
/// <param name="value">The <see cref="double"/> to convert to a <see cref="RedisValue"/>.</param>
public
static
implicit
operator
RedisValue
(
double
value
)
=>
Format
.
ToString
(
value
);
public
static
implicit
operator
RedisValue
(
double
value
)
{
try
{
var
i64
=
(
long
)
value
;
if
(
value
==
i64
)
return
new
RedisValue
(
i64
,
default
,
Sentinel_Integer
);
}
catch
{
}
return
new
RedisValue
(
BitConverter
.
DoubleToInt64Bits
(
value
),
default
,
Sentinel_Double
);
}
/// <summary>
/// Creates a new <see cref="RedisValue"/> from an <see cref="T:Nullable{double}"/>.
...
...
@@ -349,52 +396,48 @@ int IComparable.CompareTo(object obj)
/// <param name="value">The <see cref="T:Nullable{double}"/> to convert to a <see cref="RedisValue"/>.</param>
public
static
implicit
operator
RedisValue
(
double
?
value
)
=>
value
==
null
?
Null
:
(
RedisValue
)
value
.
GetValueOrDefault
();
/// <summary>
/// Creates a new <see cref="RedisValue"/> from a <see cref="T:ReadOnlyMemory{byte}"/>.
/// </summary>
public
static
implicit
operator
RedisValue
(
ReadOnlyMemory
<
byte
>
value
)
{
if
(
value
.
Length
==
0
)
return
EmptyString
;
return
new
RedisValue
(
0
,
value
,
Sentinel_Raw
);
}
/// <summary>
/// Creates a new <see cref="RedisValue"/> from a <see cref="T:Memory{byte}"/>.
/// </summary>
public
static
implicit
operator
RedisValue
(
Memory
<
byte
>
value
)
=>
(
ReadOnlyMemory
<
byte
>)
value
;
/// <summary>
/// Creates a new <see cref="RedisValue"/> from an <see cref="string"/>.
/// </summary>
/// <param name="value">The <see cref="string"/> to convert to a <see cref="RedisValue"/>.</param>
public
static
implicit
operator
RedisValue
(
string
value
)
{
byte
[]
blob
;
if
(
value
==
null
)
blob
=
null
;
else
if
(
value
.
Length
==
0
)
blob
=
EmptyByteArr
;
else
blob
=
Encoding
.
UTF8
.
GetBytes
(
value
);
return
new
RedisValue
(
0
,
blob
);
if
(
value
==
null
)
return
Null
;
if
(
value
.
Length
==
0
)
return
EmptyString
;
return
new
RedisValue
(
0
,
default
,
value
);
}
/// <summary>
/// Creates a new <see cref="RedisValue"/> from an <see cref="T:byte[]"/>.
/// </summary>
/// <param name="value">The <see cref="T:byte[]"/> to convert to a <see cref="RedisValue"/>.</param>
public
static
implicit
operator
RedisValue
(
byte
[]
value
)
{
byte
[]
blob
;
if
(
value
==
null
)
blob
=
null
;
else
if
(
value
.
Length
==
0
)
blob
=
EmptyByteArr
;
else
blob
=
value
;
return
new
RedisValue
(
0
,
blob
);
}
internal
static
RedisValue
Parse
(
object
obj
)
{
if
(
obj
==
null
)
return
RedisValue
.
Null
;
if
(
obj
is
RedisValue
)
return
(
RedisValue
)
obj
;
if
(
obj
is
string
)
return
(
RedisValue
)(
string
)
obj
;
if
(
obj
is
int
)
return
(
RedisValue
)(
int
)
obj
;
if
(
obj
is
double
)
return
(
RedisValue
)(
double
)
obj
;
if
(
obj
is
byte
[])
return
(
RedisValue
)(
byte
[])
obj
;
if
(
obj
is
bool
)
return
(
RedisValue
)(
bool
)
obj
;
if
(
obj
is
long
)
return
(
RedisValue
)(
long
)
obj
;
if
(
obj
is
float
)
return
(
RedisValue
)(
float
)
obj
;
throw
new
InvalidOperationException
(
"Unable to format type for redis: "
+
obj
.
GetType
().
FullName
);
if
(
value
==
null
)
return
Null
;
if
(
value
.
Length
==
0
)
return
EmptyString
;
return
new
RedisValue
(
0
,
new
Memory
<
byte
>(
value
),
value
);
}
/// <summary>
/// Creates a new <see cref="RedisValue"/> from an <see cref="bool"/>.
/// </summary>
/// <param name="value">The <see cref="bool"/> to convert to a <see cref="RedisValue"/>.</param>
public
static
implicit
operator
RedisValue
(
bool
value
)
=>
new
RedisValue
(
value
?
1
:
0
,
IntegerSentinel
);
public
static
implicit
operator
RedisValue
(
bool
value
)
=>
new
RedisValue
(
value
?
1
:
0
,
default
,
Sentinel_Integer
);
/// <summary>
/// Creates a new <see cref="RedisValue"/> from an <see cref="T:Nullable{bool}"/>.
...
...
@@ -434,10 +477,24 @@ internal static RedisValue Parse(object obj)
/// <param name="value">The <see cref="RedisValue"/> to convert.</param>
public
static
explicit
operator
long
(
RedisValue
value
)
{
var
blob
=
value
.
valueBlob
;
if
(
blob
==
IntegerSentinel
)
return
value
.
valueInt64
;
if
(
blob
==
null
)
return
0
;
// in redis, an arithmetic zero is kinda the same thing as not-exists (think "incr")
if
(
TryParseInt64
(
blob
,
0
,
blob
.
Length
,
out
long
i64
))
return
i64
;
switch
(
value
.
Type
)
{
case
CompareType
.
Null
:
return
0
;
// in redis, an arithmetic zero is kinda the same thing as not-exists (think "incr")
case
CompareType
.
Int64
:
return
value
.
_overlappedValue64
;
case
CompareType
.
Double
:
var
f64
=
value
.
OverlappedValueDouble
;
var
i64
=
(
long
)
f64
;
if
(
f64
==
i64
)
return
i64
;
break
;
case
CompareType
.
String
:
if
(
TryParseInt64
((
string
)
value
.
_objectOrSentinel
,
out
i64
))
return
i64
;
break
;
case
CompareType
.
Raw
:
if
(
TryParseInt64
(
value
.
_memory
.
Span
,
out
i64
))
return
i64
;
break
;
}
throw
new
InvalidCastException
();
}
...
...
@@ -447,24 +504,34 @@ internal static RedisValue Parse(object obj)
/// <param name="value">The <see cref="RedisValue"/> to convert.</param>
public
static
explicit
operator
double
(
RedisValue
value
)
{
var
blob
=
value
.
valueBlob
;
if
(
blob
==
IntegerSentinel
)
return
value
.
valueInt64
;
if
(
blob
==
null
)
return
0
;
// in redis, an arithmetic zero is kinda the same thing as not-exists (think "incr")
if
(
TryParseDouble
(
blob
,
out
double
r8
))
return
r8
;
switch
(
value
.
Type
)
{
case
CompareType
.
Null
:
return
0
;
// in redis, an arithmetic zero is kinda the same thing as not-exists (think "incr")
case
CompareType
.
Int64
:
return
value
.
_overlappedValue64
;
case
CompareType
.
Double
:
return
value
.
OverlappedValueDouble
;
case
CompareType
.
String
:
if
(
TryParseInt64
((
string
)
value
.
_objectOrSentinel
,
out
var
f64
))
return
f64
;
break
;
case
CompareType
.
Raw
:
if
(
TryParseInt64
(
value
.
_memory
.
Span
,
out
f64
))
return
f64
;
break
;
}
throw
new
InvalidCastException
();
}
private
static
bool
TryParseDouble
(
byte
[]
blob
,
out
double
value
)
private
static
bool
TryParseDouble
(
ReadOnlySpan
<
byte
>
blob
,
out
double
value
)
{
// simple integer?
if
(
blob
.
Length
==
1
&&
blob
[
0
]
>=
'0'
&&
blob
[
0
]
<=
'9'
)
if
(
TryParseInt64
(
blob
,
out
var
i64
)
)
{
value
=
blob
[
0
]
-
'0'
;
value
=
i64
;
return
true
;
}
return
Format
.
TryParseDouble
(
Encoding
.
UTF8
.
GetString
(
blob
)
,
out
value
);
return
Format
.
TryParseDouble
(
blob
,
out
value
);
}
/// <summary>
...
...
@@ -473,7 +540,7 @@ private static bool TryParseDouble(byte[] blob, out double value)
/// <param name="value">The <see cref="RedisValue"/> to convert.</param>
public
static
explicit
operator
double
?
(
RedisValue
value
)
{
if
(
value
.
valueBlob
==
n
ull
)
return
null
;
if
(
value
.
IsN
ull
)
return
null
;
return
(
double
)
value
;
}
...
...
@@ -483,7 +550,7 @@ private static bool TryParseDouble(byte[] blob, out double value)
/// <param name="value">The <see cref="RedisValue"/> to convert.</param>
public
static
explicit
operator
long
?
(
RedisValue
value
)
{
if
(
value
.
valueBlob
==
n
ull
)
return
null
;
if
(
value
.
IsN
ull
)
return
null
;
return
(
long
)
value
;
}
...
...
@@ -493,7 +560,7 @@ private static bool TryParseDouble(byte[] blob, out double value)
/// <param name="value">The <see cref="RedisValue"/> to convert.</param>
public
static
explicit
operator
int
?
(
RedisValue
value
)
{
if
(
value
.
valueBlob
==
n
ull
)
return
null
;
if
(
value
.
IsN
ull
)
return
null
;
return
(
int
)
value
;
}
...
...
@@ -503,7 +570,7 @@ private static bool TryParseDouble(byte[] blob, out double value)
/// <param name="value">The <see cref="RedisValue"/> to convert.</param>
public
static
explicit
operator
bool
?
(
RedisValue
value
)
{
if
(
value
.
valueBlob
==
n
ull
)
return
null
;
if
(
value
.
IsN
ull
)
return
null
;
return
(
bool
)
value
;
}
...
...
@@ -513,24 +580,51 @@ private static bool TryParseDouble(byte[] blob, out double value)
/// <param name="value">The <see cref="RedisValue"/> to convert.</param>
public
static
implicit
operator
string
(
RedisValue
value
)
{
var
valueBlob
=
value
.
valueBlob
;
if
(
valueBlob
==
IntegerSentinel
)
return
Format
.
ToString
(
value
.
valueInt64
);
if
(
valueBlob
==
null
)
return
null
;
if
(
valueBlob
.
Length
==
0
)
return
""
;
if
(
valueBlob
.
Length
==
2
&&
valueBlob
[
0
]
==
(
byte
)
'O'
&&
valueBlob
[
1
]
==
(
byte
)
'K'
)
switch
(
value
.
Type
)
{
return
"OK"
;
// special case for +OK status results from modules
}
case
CompareType
.
Null
:
return
null
;
case
CompareType
.
Double
:
return
Format
.
ToString
(
value
.
OverlappedValueDouble
.
ToString
());
case
CompareType
.
Int64
:
return
Format
.
ToString
(
value
.
_overlappedValue64
);
case
CompareType
.
String
:
return
(
string
)
value
.
_objectOrSentinel
;
case
CompareType
.
Raw
:
var
span
=
value
.
_memory
.
Span
;
if
(
span
.
IsEmpty
)
return
""
;
if
(
span
.
Length
==
2
&&
span
[
0
]
==
(
byte
)
'O'
&&
span
[
1
]
==
(
byte
)
'K'
)
return
"OK"
;
// frequent special-case
try
{
return
Encoding
.
UTF8
.
GetString
(
valueBlob
);
return
Format
.
DecodeUtf8
(
span
);
}
catch
{
return
BitConverter
.
ToString
(
valueBlob
);
return
ToHex
(
span
);
}
default
:
throw
new
InvalidOperationException
();
}
}
static
string
ToHex
(
ReadOnlySpan
<
byte
>
src
)
{
const
string
HexValues
=
"0123456789ABCDEF"
;
if
(
src
.
IsEmpty
)
return
""
;
var
s
=
new
string
((
char
)
0
,
src
.
Length
*
3
-
1
);
var
dst
=
MemoryMarshal
.
AsMemory
(
s
.
AsMemory
()).
Span
;
int
i
=
0
;
int
j
=
0
;
byte
b
=
src
[
i
++];
dst
[
j
++]
=
HexValues
[
b
>>
4
];
dst
[
j
++]
=
HexValues
[
b
&
0xF
];
while
(
i
<
src
.
Length
)
{
b
=
src
[
i
++];
dst
[
j
++]
=
'-'
;
dst
[
j
++]
=
HexValues
[
b
>>
4
];
dst
[
j
++]
=
HexValues
[
b
&
0xF
];
}
return
s
;
}
/// <summary>
...
...
@@ -539,12 +633,24 @@ private static bool TryParseDouble(byte[] blob, out double value)
/// <param name="value">The <see cref="RedisValue"/> to convert.</param>
public
static
implicit
operator
byte
[]
(
RedisValue
value
)
{
var
valueBlob
=
value
.
valueBlob
;
if
(
valueBlob
==
IntegerSentinel
)
if
(
value
.
Type
==
CompareType
.
Raw
)
{
return
value
.
_objectOrSentinel
is
byte
[]
arr
?
arr
:
value
.
_memory
.
ToArray
();
}
return
Encoding
.
UTF8
.
GetBytes
((
string
)
value
);
}
/// <summary>
/// Converts a <see cref="RedisValue"/> to a ReadOnlyMemory
/// </summary>
/// <param name="value">The <see cref="RedisValue"/> to convert.</param>
public
static
implicit
operator
ReadOnlyMemory
<
byte
>
(
RedisValue
value
)
{
if
(
value
.
Type
==
CompareType
.
Raw
)
{
return
Encoding
.
UTF8
.
GetBytes
(
Format
.
ToString
(
value
.
valueInt64
))
;
return
value
.
_memory
;
}
return
valueBlob
;
return
Encoding
.
UTF8
.
GetBytes
((
string
)
value
)
;
}
TypeCode
IConvertible
.
GetTypeCode
()
=>
TypeCode
.
Object
;
...
...
@@ -566,8 +672,9 @@ object IConvertible.ToType(Type conversionType, IFormatProvider provider)
{
if
(
conversionType
==
null
)
throw
new
ArgumentNullException
(
nameof
(
conversionType
));
if
(
conversionType
==
typeof
(
byte
[]))
return
(
byte
[])
this
;
if
(
conversionType
==
typeof
(
ReadOnlyMemory
<
byte
>))
return
(
ReadOnlyMemory
<
byte
>)
this
;
if
(
conversionType
==
typeof
(
RedisValue
))
return
this
;
switch
(
Type
.
GetTypeCode
(
conversionType
))
switch
(
System
.
Type
.
GetTypeCode
(
conversionType
))
{
case
TypeCode
.
Boolean
:
return
(
bool
)
this
;
case
TypeCode
.
Byte
:
return
(
byte
)
this
;
...
...
@@ -602,6 +709,11 @@ object IConvertible.ToType(Type conversionType, IFormatProvider provider)
/// <param name="val">The <see cref="long"/> value, if conversion was possible.</param>
public
bool
TryParse
(
out
long
val
)
{
switch
(
Type
)
{
case
CompareType
.
Int64
:
val
=
_overlappedValue64
;
return
true
;
case
CompareType
.
String
:
}
var
blob
=
valueBlob
;
if
(
blob
==
IntegerSentinel
)
{
...
...
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