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
9a0f258e
Commit
9a0f258e
authored
Jun 27, 2018
by
Marc Gravell
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
RedisValue internals tweaked; writing complete; read not started
parent
4f182469
Changes
9
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
247 additions
and
254 deletions
+247
-254
KeysAndValues.cs
StackExchange.Redis.Tests/KeysAndValues.cs
+18
-3
StackExchange.Redis.csproj
StackExchange.Redis/StackExchange.Redis.csproj
+2
-0
DebuggingAids.cs
StackExchange.Redis/StackExchange/Redis/DebuggingAids.cs
+25
-19
LoggingTextStream.cs
StackExchange.Redis/StackExchange/Redis/LoggingTextStream.cs
+57
-118
PhysicalConnection.cs
...kExchange.Redis/StackExchange/Redis/PhysicalConnection.cs
+43
-18
RedisDatabase.cs
StackExchange.Redis/StackExchange/Redis/RedisDatabase.cs
+3
-1
RedisValue.cs
StackExchange.Redis/StackExchange/Redis/RedisValue.cs
+91
-88
Program.cs
TestConsole/Program.cs
+2
-5
TestConsole.csproj
TestConsole/TestConsole.csproj
+6
-2
No files found.
StackExchange.Redis.Tests/KeysAndValues.cs
View file @
9a0f258e
using
System
;
using
System.Collections.Generic
;
using
System.Globalization
;
using
Xunit
;
...
...
@@ -64,6 +65,13 @@ public void TestValues()
private
void
CheckSame
(
RedisValue
x
,
RedisValue
y
)
{
Assert
.
True
(
Equals
(
x
,
y
));
Assert
.
True
(
Equals
(
y
,
x
));
Assert
.
True
(
EqualityComparer
<
RedisValue
>.
Default
.
Equals
(
x
,
y
));
Assert
.
True
(
EqualityComparer
<
RedisValue
>.
Default
.
Equals
(
y
,
x
));
Assert
.
True
(
x
==
y
);
Assert
.
True
(
y
==
x
);
Assert
.
False
(
x
!=
y
);
Assert
.
False
(
y
!=
x
);
Assert
.
True
(
x
.
Equals
(
y
));
Assert
.
True
(
y
.
Equals
(
x
));
Assert
.
True
(
x
.
GetHashCode
()
==
y
.
GetHashCode
());
...
...
@@ -72,6 +80,13 @@ private void CheckSame(RedisValue x, RedisValue y)
private
void
CheckNotSame
(
RedisValue
x
,
RedisValue
y
)
{
Assert
.
False
(
Equals
(
x
,
y
));
Assert
.
False
(
Equals
(
y
,
x
));
Assert
.
False
(
EqualityComparer
<
RedisValue
>.
Default
.
Equals
(
x
,
y
));
Assert
.
False
(
EqualityComparer
<
RedisValue
>.
Default
.
Equals
(
y
,
x
));
Assert
.
False
(
x
==
y
);
Assert
.
False
(
y
==
x
);
Assert
.
True
(
x
!=
y
);
Assert
.
True
(
y
!=
x
);
Assert
.
False
(
x
.
Equals
(
y
));
Assert
.
False
(
y
.
Equals
(
x
));
Assert
.
False
(
x
.
GetHashCode
()
==
y
.
GetHashCode
());
// well, very unlikely
...
...
@@ -107,9 +122,9 @@ private void CheckNull(RedisValue value)
Assert
.
Equal
(
0L
,
(
long
)
value
);
CheckSame
(
value
,
value
);
CheckSame
(
value
,
default
(
RedisValue
));
CheckSame
(
value
,
(
string
)
null
);
CheckSame
(
value
,
(
byte
[])
null
);
//
CheckSame(value, default(RedisValue));
//
CheckSame(value, (string)null);
//
CheckSame(value, (byte[])null);
}
[
Fact
]
...
...
StackExchange.Redis/StackExchange.Redis.csproj
View file @
9a0f258e
...
...
@@ -11,6 +11,8 @@
<SignAssembly>true</SignAssembly>
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
<LangVersion>latest</LangVersion>
<!--<DefineConstants>$(DefineConstants);LOGOUTPUT</DefineConstants>-->
</PropertyGroup>
<ItemGroup>
...
...
StackExchange.Redis/StackExchange/Redis/DebuggingAids.cs
View file @
9a0f258e
using
System
;
using
System.Diagnostics
;
using
System.IO
;
using
System.IO.Pipelines
;
using
System.Runtime.InteropServices
;
using
System.Text
;
using
System.Threading
;
...
...
@@ -324,28 +326,32 @@ partial class ConnectionMultiplexer
partial
class
PhysicalConnection
{
private
Stream
echo
;
partial
void
OnCreateEcho
()
{
if
(!
string
.
IsNullOrEmpty
(
ConnectionMultiplexer
.
EchoPath
))
{
string
fullPath
=
Path
.
Combine
(
ConnectionMultiplexer
.
EchoPath
,
Regex
.
Replace
(
physicalName
,
@"[\-\.\@\#\:]"
,
"_"
));
echo
=
File
.
Open
(
Path
.
ChangeExtension
(
fullPath
,
"txt"
),
FileMode
.
Create
,
FileAccess
.
Write
,
FileShare
.
ReadWrite
);
}
}
partial
void
OnCloseEcho
()
{
if
(
echo
!=
null
)
{
try
{
echo
.
Close
();
}
catch
{
}
try
{
echo
.
Dispose
();
}
catch
{
}
echo
=
null
;
}
}
partial
void
OnWrapForLogging
(
ref
Stream
stream
,
string
name
)
{
stream
=
new
LoggingTextStream
(
stream
,
physicalName
,
echo
);
//private Stream echo;
//partial void OnCreateEcho()
//{
// if (!string.IsNullOrEmpty(ConnectionMultiplexer.EchoPath))
// {
// string fullPath = Path.Combine(ConnectionMultiplexer.EchoPath,
// Regex.Replace(physicalName, @"[\-\.\@\#\:]", "_"));
// echo = File.Open(Path.ChangeExtension(fullPath, "txt"), FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
// }
//}
//partial void OnCloseEcho()
//{
// if (echo != null)
// {
// try { echo.Close(); } catch { }
// try { echo.Dispose(); } catch { }
// echo = null;
// }
//}
partial
void
OnWrapForLogging
(
ref
IDuplexPipe
pipe
,
string
name
)
{
foreach
(
var
c
in
Path
.
GetInvalidFileNameChars
())
{
name
=
name
.
Replace
(
c
,
'_'
);
}
pipe
=
new
LoggingPipe
(
pipe
,
$"
{
name
}
.in"
,
$"
{
name
}
.out"
);
}
}
#endif
...
...
StackExchange.Redis/StackExchange/Redis/LoggingTextStream.cs
View file @
9a0f258e
namespace
StackExchange.Redis
using
System
;
using
System.Buffers
;
using
System.IO
;
using
System.IO.Pipelines
;
using
System.Runtime.InteropServices
;
namespace
StackExchange.Redis
{
#if LOGOUTPUT
sealed
class
LoggingTextStream
:
Stream
{
[
Conditional
(
"VERBOSE"
)]
void
Trace
(
string
value
,
[
CallerMemberName
]
string
caller
=
null
)
sealed
class
LoggingPipe
:
IDuplexPipe
{
Debug
.
WriteLine
(
value
,
this
.
category
+
":"
+
caller
);
}
[
Conditional
(
"VERBOSE"
)]
void
Trace
(
char
value
,
[
CallerMemberName
]
string
caller
=
null
)
{
Debug
.
WriteLine
(
value
,
this
.
category
+
":"
+
caller
);
}
private
IDuplexPipe
_inner
;
private
readonly
Stream
stream
,
echo
;
private
readonly
string
category
;
public
LoggingTextStream
(
Stream
stream
,
string
category
,
Stream
echo
)
public
LoggingPipe
(
IDuplexPipe
inner
,
string
inPath
,
string
outPath
)
{
if
(
stream
==
null
)
throw
new
ArgumentNullException
(
"stream"
);
if
(
string
.
IsNullOrWhiteSpace
(
category
))
category
=
GetType
().
Name
;
this
.
stream
=
stream
;
this
.
category
=
category
;
this
.
echo
=
echo
;
}
public
override
IAsyncResult
BeginRead
(
byte
[]
buffer
,
int
offset
,
int
count
,
AsyncCallback
callback
,
object
state
)
_inner
=
inner
;
if
(
string
.
IsNullOrWhiteSpace
(
inPath
))
{
asyncBuffer
=
buffer
;
asyncOffset
=
offset
;
asyncCount
=
count
;
return
stream
.
BeginRead
(
buffer
,
offset
,
count
,
callback
,
state
);
}
private
volatile
byte
[]
asyncBuffer
;
private
volatile
int
asyncOffset
,
asyncCount
;
public
override
int
EndRead
(
IAsyncResult
asyncResult
)
{
int
bytes
=
stream
.
EndRead
(
asyncResult
);
if
(
bytes
<=
0
)
{
Trace
(
"<EOF>"
);
Input
=
inner
.
Input
;
}
else
{
Trace
(
Encoding
.
UTF8
.
GetString
(
asyncBuffer
,
asyncOffset
,
asyncCount
));
}
return
bytes
;
}
public
override
bool
CanRead
{
get
{
return
stream
.
CanRead
;
}
}
public
override
bool
CanSeek
{
get
{
return
stream
.
CanSeek
;
}
}
public
override
bool
CanWrite
{
get
{
return
stream
.
CanWrite
;
}
}
public
override
bool
CanTimeout
{
get
{
return
stream
.
CanTimeout
;
}
}
public
override
long
Length
{
get
{
return
stream
.
Length
;
}
}
public
override
long
Position
{
get
{
return
stream
.
Position
;
}
set
{
stream
.
Position
=
value
;
}
}
public
override
int
ReadTimeout
{
get
{
return
stream
.
ReadTimeout
;
}
set
{
stream
.
ReadTimeout
=
value
;
}
}
public
override
int
WriteTimeout
{
get
{
return
stream
.
WriteTimeout
;
}
set
{
stream
.
WriteTimeout
=
value
;
}
}
protected
override
void
Dispose
(
bool
disposing
)
{
if
(
disposing
)
{
stream
.
Dispose
();
if
(
echo
!=
null
)
echo
.
Flush
();
var
pipe
=
new
Pipe
();
Input
=
pipe
.
Reader
;
CloneAsync
(
inPath
,
inner
.
Input
,
pipe
.
Writer
);
}
base
.
Dispose
(
disposing
);
}
public
override
void
Close
()
{
Trace
(
"Close"
);
stream
.
Close
();
if
(
echo
!=
null
)
echo
.
Close
();
base
.
Close
();
}
public
override
void
Flush
()
{
Trace
(
"Flush"
);
stream
.
Flush
();
if
(
echo
!=
null
)
echo
.
Flush
();
}
public
override
long
Seek
(
long
offset
,
SeekOrigin
origin
)
if
(
string
.
IsNullOrWhiteSpace
(
outPath
))
{
return
stream
.
Seek
(
offset
,
origin
)
;
Output
=
inner
.
Output
;
}
public
override
void
SetLength
(
long
value
)
else
{
stream
.
SetLength
(
value
);
var
pipe
=
new
Pipe
();
Output
=
pipe
.
Writer
;
CloneAsync
(
outPath
,
pipe
.
Reader
,
inner
.
Output
);
}
public
override
void
WriteByte
(
byte
value
)
{
Trace
((
char
)
value
);
stream
.
WriteByte
(
value
);
if
(
echo
!=
null
)
echo
.
WriteByte
(
value
);
}
public
override
int
ReadByte
()
private
async
void
CloneAsync
(
string
path
,
PipeReader
from
,
PipeWriter
to
)
{
int
value
=
stream
.
ReadByte
();
if
(
value
<
0
)
to
.
OnReaderCompleted
((
ex
,
o
)
=>
((
PipeReader
)
o
).
Complete
(
ex
),
from
);
from
.
OnWriterCompleted
((
ex
,
o
)
=>
((
PipeWriter
)
o
).
Complete
(
ex
),
to
);
while
(
true
)
{
Trace
(
"<EOF>"
);
}
else
var
result
=
await
from
.
ReadAsync
();
var
buffer
=
result
.
Buffer
;
if
(
result
.
IsCompleted
&&
buffer
.
IsEmpty
)
break
;
using
(
var
file
=
new
FileStream
(
path
,
FileMode
.
Append
,
FileAccess
.
Write
))
{
Trace
((
char
)
value
);
}
return
value
;
}
public
override
int
Read
(
byte
[]
buffer
,
int
offset
,
int
count
)
foreach
(
var
segment
in
buffer
)
{
int
bytes
=
stream
.
Read
(
buffer
,
offset
,
count
);
if
(
bytes
<=
0
)
// append it to the file
bool
leased
=
false
;
if
(!
MemoryMarshal
.
TryGetArray
(
segment
,
out
var
arr
))
{
Trace
(
"<EOF>"
);
var
tmp
=
ArrayPool
<
byte
>.
Shared
.
Rent
(
segment
.
Length
);
segment
.
CopyTo
(
tmp
);
arr
=
new
ArraySegment
<
byte
>(
tmp
,
0
,
segment
.
Length
);
leased
=
true
;
}
else
{
Trace
(
Encoding
.
UTF8
.
GetString
(
buffer
,
offset
,
bytes
));
await
file
.
WriteAsync
(
arr
.
Array
,
arr
.
Offset
,
arr
.
Count
);
await
file
.
FlushAsync
();
if
(
leased
)
ArrayPool
<
byte
>.
Shared
.
Return
(
arr
.
Array
);
// and flush it upstream
await
to
.
WriteAsync
(
segment
);
}
return
bytes
;
}
public
override
void
Write
(
byte
[]
buffer
,
int
offset
,
int
count
)
{
if
(
count
!=
0
)
{
Trace
(
Encoding
.
UTF8
.
GetString
(
buffer
,
offset
,
count
));
from
.
AdvanceTo
(
buffer
.
End
);
}
stream
.
Write
(
buffer
,
offset
,
count
);
if
(
echo
!=
null
)
echo
.
Write
(
buffer
,
offset
,
count
);
}
public
PipeReader
Input
{
get
;
}
public
PipeWriter
Output
{
get
;
}
}
#endif
}
StackExchange.Redis/StackExchange/Redis/PhysicalConnection.cs
View file @
9a0f258e
...
...
@@ -421,13 +421,24 @@ internal void Write(RedisChannel channel)
internal
void
Write
(
RedisValue
value
)
{
if
(
value
.
IsInteger
)
switch
(
value
.
Type
)
{
case
RedisValue
.
StorageType
.
Null
:
WriteUnified
(
_ioPipe
.
Output
,
(
byte
[])
null
);
break
;
case
RedisValue
.
StorageType
.
Int64
:
WriteUnified
(
_ioPipe
.
Output
,
(
long
)
value
);
}
else
{
WriteUnified
(
_ioPipe
.
Output
,
(
byte
[])
value
);
break
;
case
RedisValue
.
StorageType
.
Double
:
// use string
case
RedisValue
.
StorageType
.
String
:
WriteUnified
(
_ioPipe
.
Output
,
null
,
(
string
)
value
);
break
;
case
RedisValue
.
StorageType
.
Raw
:
WriteUnified
(
_ioPipe
.
Output
,
((
ReadOnlyMemory
<
byte
>)
value
).
Span
);
break
;
default
:
throw
new
InvalidOperationException
(
$"Unexpected
{
value
.
Type
}
value: '
{
value
}
'"
);
}
}
...
...
@@ -492,7 +503,7 @@ static void WriteCrlf(PipeWriter writer)
span
[
1
]
=
(
byte
)
'\n'
;
writer
.
Advance
(
2
);
}
private
static
int
WriteRaw
(
Span
<
byte
>
span
,
long
value
,
bool
withLengthPrefix
=
false
,
int
offset
=
0
)
internal
static
int
WriteRaw
(
Span
<
byte
>
span
,
long
value
,
bool
withLengthPrefix
=
false
,
int
offset
=
0
)
{
if
(
value
>=
0
&&
value
<=
9
)
{
...
...
@@ -594,16 +605,24 @@ internal void WakeWriterAndCheckForThrottle()
static
readonly
byte
[]
NullBulkString
=
Encoding
.
ASCII
.
GetBytes
(
"$-1\r\n"
),
EmptyBulkString
=
Encoding
.
ASCII
.
GetBytes
(
"$0\r\n\r\n"
);
private
static
void
WriteUnified
(
PipeWriter
writer
,
byte
[]
value
)
{
const
int
MaxQuickSpanSize
=
512
;
// ${len}\r\n = 3 + MaxInt32TextLen
// {value}\r\n = 2 + value.Length
if
(
value
==
null
)
{
// special case:
writer
.
Write
(
NullBulkString
);
}
else
if
(
value
.
Length
==
0
)
else
{
WriteUnified
(
writer
,
new
ReadOnlySpan
<
byte
>(
value
));
}
}
private
static
void
WriteUnified
(
PipeWriter
writer
,
ReadOnlySpan
<
byte
>
value
)
{
// ${len}\r\n = 3 + MaxInt32TextLen
// {value}\r\n = 2 + value.Length
const
int
MaxQuickSpanSize
=
512
;
if
(
value
.
Length
==
0
)
{
// special case:
writer
.
Write
(
EmptyBulkString
);
...
...
@@ -611,7 +630,8 @@ private static void WriteUnified(PipeWriter writer, byte[] value)
else
if
(
value
.
Length
<=
MaxQuickSpanSize
)
{
var
span
=
writer
.
GetSpan
(
5
+
MaxInt32TextLen
+
value
.
Length
);
int
bytes
=
WriteUnified
(
span
,
value
);
span
[
0
]
=
(
byte
)
'$'
;
int
bytes
=
WriteUnified
(
span
,
value
,
1
);
writer
.
Advance
(
bytes
);
}
else
...
...
@@ -619,7 +639,7 @@ private static void WriteUnified(PipeWriter writer, byte[] value)
// too big to guarantee can do in a single span
var
span
=
writer
.
GetSpan
(
3
+
MaxInt32TextLen
);
span
[
0
]
=
(
byte
)
'$'
;
int
bytes
=
WriteRaw
(
span
,
value
.
L
ongL
ength
,
offset
:
1
);
int
bytes
=
WriteRaw
(
span
,
value
.
Length
,
offset
:
1
);
writer
.
Advance
(
bytes
);
writer
.
Write
(
value
);
...
...
@@ -635,12 +655,17 @@ private static int WriteUnified(Span<byte> span, byte[] value, int offset = 0)
offset
=
WriteRaw
(
span
,
-
1
,
offset
:
offset
);
// note that not many things like this...
}
else
{
offset
=
WriteUnified
(
span
,
new
ReadOnlySpan
<
byte
>(
value
),
offset
);
}
return
offset
;
}
private
static
int
WriteUnified
(
Span
<
byte
>
span
,
ReadOnlySpan
<
byte
>
value
,
int
offset
=
0
)
{
offset
=
WriteRaw
(
span
,
value
.
Length
,
offset
:
offset
);
new
ReadOnlySpan
<
byte
>(
value
)
.
CopyTo
(
span
.
Slice
(
offset
,
value
.
Length
));
value
.
CopyTo
(
span
.
Slice
(
offset
,
value
.
Length
));
offset
+=
value
.
Length
;
offset
=
WriteCrlf
(
span
,
offset
);
}
return
offset
;
}
...
...
StackExchange.Redis/StackExchange/Redis/RedisDatabase.cs
View file @
9a0f258e
...
...
@@ -2484,7 +2484,9 @@ internal override void WriteImpl(PhysicalConnection physical)
}
else
{
// recognises well-known types
physical
.
Write
(
RedisValue
.
Parse
(
arg
));
var
val
=
RedisValue
.
TryParse
(
arg
);
if
(
val
.
IsNull
&&
arg
!=
null
)
throw
new
InvalidCastException
(
$"Unable to parse value: '
{
arg
}
'"
);
physical
.
Write
(
val
);
}
}
}
...
...
StackExchange.Redis/StackExchange/Redis/RedisValue.cs
View file @
9a0f258e
...
...
@@ -82,22 +82,22 @@ public bool IsNullOrEmpty
/// <param name="y">The second <see cref="RedisValue"/> to compare.</param>
public
static
bool
operator
==(
RedisValue
x
,
RedisValue
y
)
{
Compar
eType
xType
=
x
.
Type
,
yType
=
y
.
Type
;
Storag
eType
xType
=
x
.
Type
,
yType
=
y
.
Type
;
if
(
xType
==
CompareType
.
Null
)
return
yType
==
Compar
eType
.
Null
;
if
(
xType
==
Compar
eType
.
Null
)
return
false
;
if
(
xType
==
StorageType
.
Null
)
return
yType
==
Storag
eType
.
Null
;
if
(
xType
==
Storag
eType
.
Null
)
return
false
;
if
(
xType
==
yType
)
{
switch
(
xType
)
{
case
Compar
eType
.
Double
:
case
Storag
eType
.
Double
:
return
x
.
OverlappedValueDouble
==
y
.
OverlappedValueDouble
;
case
Compar
eType
.
Int64
:
case
Storag
eType
.
Int64
:
return
x
.
_overlappedValue64
==
y
.
_overlappedValue64
;
case
Compar
eType
.
String
:
case
Storag
eType
.
String
:
return
(
string
)
x
.
_objectOrSentinel
==
(
string
)
y
.
_objectOrSentinel
;
case
Compar
eType
.
Raw
:
case
Storag
eType
.
Raw
:
return
x
.
_memory
.
Span
.
SequenceEqual
(
y
.
_memory
.
Span
);
}
}
...
...
@@ -113,7 +113,7 @@ public bool IsNullOrEmpty
public
override
bool
Equals
(
object
obj
)
{
if
(
obj
==
null
)
return
IsNull
;
if
(
obj
is
RedisValue
typed
)
return
Equals
(
typed
);
var
other
=
TryParse
(
obj
);
if
(
other
.
IsNull
)
return
false
;
// parse fail
return
this
==
other
;
...
...
@@ -132,15 +132,15 @@ public override int GetHashCode()
{
switch
(
Type
)
{
case
Compar
eType
.
Null
:
case
Storag
eType
.
Null
:
return
-
1
;
case
Compar
eType
.
Double
:
case
Storag
eType
.
Double
:
return
OverlappedValueDouble
.
GetHashCode
();
case
Compar
eType
.
Int64
:
case
Storag
eType
.
Int64
:
return
_overlappedValue64
.
GetHashCode
();
case
Compar
eType
.
Raw
:
case
Storag
eType
.
Raw
:
return
GetHashCode
(
_memory
);
case
Compar
eType
.
String
:
case
Storag
eType
.
String
:
default
:
return
_objectOrSentinel
.
GetHashCode
();
}
...
...
@@ -264,22 +264,22 @@ internal void AssertNotNull()
if
(
IsNull
)
throw
new
ArgumentException
(
"A null value is not valid in this context"
);
}
private
enum
Compar
eType
internal
enum
Storag
eType
{
Null
,
Int64
,
Double
,
Raw
,
String
,
}
private
Compar
eType
Type
internal
Storag
eType
Type
{
get
{
var
objectOrSentinel
=
_objectOrSentinel
;
if
(
objectOrSentinel
==
null
)
return
Compar
eType
.
Null
;
if
(
objectOrSentinel
==
Sentinel_Integer
)
return
Compar
eType
.
Int64
;
if
(
objectOrSentinel
==
Sentinel_Double
)
return
Compar
eType
.
Double
;
if
(
objectOrSentinel
==
Sentinel_Raw
)
return
Compar
eType
.
Raw
;
if
(
objectOrSentinel
is
string
)
return
Compar
eType
.
String
;
if
(
objectOrSentinel
is
byte
[])
return
Compar
eType
.
Raw
;
// doubled-up, but retaining the array
if
(
objectOrSentinel
==
null
)
return
Storag
eType
.
Null
;
if
(
objectOrSentinel
==
Sentinel_Integer
)
return
Storag
eType
.
Int64
;
if
(
objectOrSentinel
==
Sentinel_Double
)
return
Storag
eType
.
Double
;
if
(
objectOrSentinel
==
Sentinel_Raw
)
return
Storag
eType
.
Raw
;
if
(
objectOrSentinel
is
string
)
return
Storag
eType
.
String
;
if
(
objectOrSentinel
is
byte
[])
return
Storag
eType
.
Raw
;
// doubled-up, but retaining the array
throw
new
InvalidOperationException
(
"Unknown type"
);
}
}
...
...
@@ -292,23 +292,23 @@ public int CompareTo(RedisValue other)
{
try
{
Compar
eType
thisType
=
this
.
Type
,
Storag
eType
thisType
=
this
.
Type
,
otherType
=
other
.
Type
;
if
(
thisType
==
CompareType
.
Null
)
return
otherType
==
Compar
eType
.
Null
?
0
:
-
1
;
if
(
otherType
==
Compar
eType
.
Null
)
return
1
;
if
(
thisType
==
StorageType
.
Null
)
return
otherType
==
Storag
eType
.
Null
?
0
:
-
1
;
if
(
otherType
==
Storag
eType
.
Null
)
return
1
;
if
(
thisType
==
otherType
)
{
switch
(
thisType
)
{
case
Compar
eType
.
Double
:
case
Storag
eType
.
Double
:
return
this
.
OverlappedValueDouble
.
CompareTo
(
other
.
OverlappedValueDouble
);
case
Compar
eType
.
Int64
:
case
Storag
eType
.
Int64
:
return
this
.
_overlappedValue64
.
CompareTo
(
other
.
_overlappedValue64
);
case
Compar
eType
.
String
:
case
Storag
eType
.
String
:
return
string
.
CompareOrdinal
((
string
)
this
.
_objectOrSentinel
,
(
string
)
other
.
_objectOrSentinel
);
case
Compar
eType
.
Raw
:
case
Storag
eType
.
Raw
:
return
this
.
_memory
.
Span
.
SequenceCompareTo
(
other
.
_memory
.
Span
);
}
}
...
...
@@ -479,19 +479,19 @@ internal static RedisValue TryParse(object obj)
{
switch
(
value
.
Type
)
{
case
Compar
eType
.
Null
:
case
Storag
eType
.
Null
:
return
0
;
// in redis, an arithmetic zero is kinda the same thing as not-exists (think "incr")
case
Compar
eType
.
Int64
:
case
Storag
eType
.
Int64
:
return
value
.
_overlappedValue64
;
case
Compar
eType
.
Double
:
case
Storag
eType
.
Double
:
var
f64
=
value
.
OverlappedValueDouble
;
var
i64
=
(
long
)
f64
;
if
(
f64
==
i64
)
return
i64
;
break
;
case
Compar
eType
.
String
:
case
Storag
eType
.
String
:
if
(
TryParseInt64
((
string
)
value
.
_objectOrSentinel
,
out
i64
))
return
i64
;
break
;
case
Compar
eType
.
Raw
:
case
Storag
eType
.
Raw
:
if
(
TryParseInt64
(
value
.
_memory
.
Span
,
out
i64
))
return
i64
;
break
;
}
...
...
@@ -506,16 +506,16 @@ internal static RedisValue TryParse(object obj)
{
switch
(
value
.
Type
)
{
case
Compar
eType
.
Null
:
case
Storag
eType
.
Null
:
return
0
;
// in redis, an arithmetic zero is kinda the same thing as not-exists (think "incr")
case
Compar
eType
.
Int64
:
case
Storag
eType
.
Int64
:
return
value
.
_overlappedValue64
;
case
Compar
eType
.
Double
:
case
Storag
eType
.
Double
:
return
value
.
OverlappedValueDouble
;
case
Compar
eType
.
String
:
case
Storag
eType
.
String
:
if
(
TryParseInt64
((
string
)
value
.
_objectOrSentinel
,
out
var
f64
))
return
f64
;
break
;
case
Compar
eType
.
Raw
:
case
Storag
eType
.
Raw
:
if
(
TryParseInt64
(
value
.
_memory
.
Span
,
out
f64
))
return
f64
;
break
;
}
...
...
@@ -582,11 +582,11 @@ private static bool TryParseDouble(ReadOnlySpan<byte> blob, out double value)
{
switch
(
value
.
Type
)
{
case
Compar
eType
.
Null
:
return
null
;
case
Compar
eType
.
Double
:
return
Format
.
ToString
(
value
.
OverlappedValueDouble
.
ToString
());
case
Compar
eType
.
Int64
:
return
Format
.
ToString
(
value
.
_overlappedValue64
);
case
Compar
eType
.
String
:
return
(
string
)
value
.
_objectOrSentinel
;
case
Compar
eType
.
Raw
:
case
Storag
eType
.
Null
:
return
null
;
case
Storag
eType
.
Double
:
return
Format
.
ToString
(
value
.
OverlappedValueDouble
.
ToString
());
case
Storag
eType
.
Int64
:
return
Format
.
ToString
(
value
.
_overlappedValue64
);
case
Storag
eType
.
String
:
return
(
string
)
value
.
_objectOrSentinel
;
case
Storag
eType
.
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
...
...
@@ -633,10 +633,26 @@ static string ToHex(ReadOnlySpan<byte> src)
/// <param name="value">The <see cref="RedisValue"/> to convert.</param>
public
static
implicit
operator
byte
[]
(
RedisValue
value
)
{
if
(
value
.
Type
==
CompareType
.
Raw
)
switch
(
value
.
Type
)
{
return
value
.
_objectOrSentinel
is
byte
[]
arr
?
arr
:
value
.
_memory
.
ToArray
();
case
StorageType
.
Null
:
return
null
;
case
StorageType
.
Raw
:
if
(
value
.
_objectOrSentinel
is
byte
[]
arr
)
return
arr
;
if
(
MemoryMarshal
.
TryGetArray
(
value
.
_memory
,
out
var
segment
)
&&
segment
.
Offset
==
0
&&
segment
.
Count
==
(
segment
.
Array
?.
Length
??
-
1
))
return
segment
.
Array
;
// the memory is backed by an array, and we're reading all of it
return
value
.
_memory
.
ToArray
();
case
StorageType
.
Int64
:
Span
<
byte
>
span
=
stackalloc
byte
[
PhysicalConnection
.
MaxInt64TextLen
];
int
len
=
PhysicalConnection
.
WriteRaw
(
span
,
value
.
_overlappedValue64
,
false
,
0
);
arr
=
new
byte
[
len
-
2
];
// don't need the CRLF
span
.
Slice
(
0
,
arr
.
Length
).
CopyTo
(
arr
);
return
arr
;
}
// fallback: stringify and encode
return
Encoding
.
UTF8
.
GetBytes
((
string
)
value
);
}
...
...
@@ -644,14 +660,8 @@ static string ToHex(ReadOnlySpan<byte> src)
/// 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
value
.
_memory
;
}
return
Encoding
.
UTF8
.
GetBytes
((
string
)
value
);
}
public
static
implicit
operator
ReadOnlyMemory
<
byte
>(
RedisValue
value
)
=>
value
.
Type
==
StorageType
.
Raw
?
value
.
_memory
:
(
byte
[])
value
;
TypeCode
IConvertible
.
GetTypeCode
()
=>
TypeCode
.
Object
;
...
...
@@ -709,26 +719,23 @@ 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
)
switch
(
Type
)
{
case
CompareType
.
Int64
:
val
=
_overlappedValue64
;
return
true
;
case
CompareType
.
String
:
}
var
blob
=
valueBlob
;
if
(
blob
==
IntegerSentinel
)
case
StorageType
.
Null
:
val
=
0
;
return
true
;
// in redis-land 0 approx. equal null; so roll with it
case
StorageType
.
Int64
:
val
=
_overlappedValue64
;
return
true
;
case
StorageType
.
String
:
return
TryParseInt64
((
string
)
_objectOrSentinel
,
out
val
);
case
StorageType
.
Raw
:
return
TryParseInt64
(
_memory
.
Span
,
out
val
);
case
StorageType
.
Double
:
var
f64
=
OverlappedValueDouble
;
if
(
f64
>=
long
.
MinValue
&&
f64
<=
long
.
MaxValue
)
{
val
=
valueInt
64
;
val
=
(
long
)
f
64
;
return
true
;
}
if
(
blob
==
null
)
{
// in redis-land 0 approx. equal null; so roll with it
val
=
0
;
return
true
;
break
;
}
return
TryParseInt64
(
blob
,
0
,
blob
.
Length
,
out
val
)
;
val
=
default
;
return
false
;
}
/// <summary>
...
...
@@ -739,15 +746,15 @@ public bool TryParse(out long val)
/// <param name="val">The <see cref="int"/> value, if conversion was possible.</param>
public
bool
TryParse
(
out
int
val
)
{
if
(
!
TryParse
(
out
long
l
)
||
l
>
int
.
MaxValue
||
l
<
int
.
Min
Value
)
if
(
TryParse
(
out
long
l
)
&&
l
>=
int
.
MinValue
&&
l
<=
int
.
Max
Value
)
{
val
=
0
;
return
false
;
}
val
=
(
int
)
l
;
return
true
;
}
val
=
default
;
return
false
;
}
/// <summary>
/// Convert to a double if possible, returning true.
...
...
@@ -757,20 +764,16 @@ public bool TryParse(out int val)
/// <param name="val">The <see cref="double"/> value, if conversion was possible.</param>
public
bool
TryParse
(
out
double
val
)
{
var
blob
=
valueBlob
;
if
(
blob
==
IntegerSentinel
)
switch
(
Type
)
{
val
=
valueInt64
;
return
true
;
case
StorageType
.
Null
:
val
=
0
;
return
true
;
case
StorageType
.
Int64
:
val
=
_overlappedValue64
;
return
true
;
case
StorageType
.
Double
:
val
=
OverlappedValueDouble
;
return
true
;
case
StorageType
.
String
:
return
Format
.
TryParseDouble
((
string
)
_objectOrSentinel
,
out
val
);
case
StorageType
.
Raw
:
return
TryParseDouble
(
_memory
.
Span
,
out
val
);
}
if
(
blob
==
null
)
{
// in redis-land 0 approx. equal null; so roll with it
val
=
0
;
return
true
;
}
return
TryParseDouble
(
blob
,
out
val
);
val
=
default
;
return
false
;
}
}
}
TestConsole/Program.cs
View file @
9a0f258e
...
...
@@ -9,9 +9,6 @@ class Program
{
static
int
Main
()
{
var
options
=
PipeOptions
.
Default
;
Console
.
WriteLine
(
options
.
PauseWriterThreshold
);
Console
.
WriteLine
(
options
.
ResumeWriterThreshold
);
var
s
=
new
StringWriter
();
try
{
...
...
@@ -38,8 +35,8 @@ static int Main()
}
finally
{
//
Console.WriteLine();
//
Console.WriteLine(s);
Console
.
WriteLine
();
//Console.WriteLine(s);
}
}
...
...
TestConsole/TestConsole.csproj
View file @
9a0f258e
...
...
@@ -6,13 +6,17 @@
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Computername)'=='OCHO' or '$(Computername)'=='SKINK'">
<LocalReference>true</LocalReference>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\StackExchange.Redis\StackExchange.Redis.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(
Computername)'=='OCHO
'">
<ItemGroup Condition="'$(
LocalReference)'=='true
'">
<ProjectReference Include="..\..\Pipelines.Sockets.Unofficial\src\Pipelines.Sockets.Unofficial\Pipelines.Sockets.Unofficial.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(
Computername)'!='OCHO
'">
<ItemGroup Condition="'$(
LocalReference)'!='true
'">
<PackageReference Include="Pipelines.Sockets.Unofficial" Version="0.2.1-alpha.48" />
</ItemGroup>
...
...
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