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
Hide 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
;
using
System.Collections.Generic
;
using
System.Globalization
;
using
System.Globalization
;
using
Xunit
;
using
Xunit
;
...
@@ -64,6 +65,13 @@ public void TestValues()
...
@@ -64,6 +65,13 @@ public void TestValues()
private
void
CheckSame
(
RedisValue
x
,
RedisValue
y
)
private
void
CheckSame
(
RedisValue
x
,
RedisValue
y
)
{
{
Assert
.
True
(
Equals
(
x
,
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
(
x
.
Equals
(
y
));
Assert
.
True
(
y
.
Equals
(
x
));
Assert
.
True
(
y
.
Equals
(
x
));
Assert
.
True
(
x
.
GetHashCode
()
==
y
.
GetHashCode
());
Assert
.
True
(
x
.
GetHashCode
()
==
y
.
GetHashCode
());
...
@@ -72,6 +80,13 @@ private void CheckSame(RedisValue x, RedisValue y)
...
@@ -72,6 +80,13 @@ private void CheckSame(RedisValue x, RedisValue y)
private
void
CheckNotSame
(
RedisValue
x
,
RedisValue
y
)
private
void
CheckNotSame
(
RedisValue
x
,
RedisValue
y
)
{
{
Assert
.
False
(
Equals
(
x
,
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
(
x
.
Equals
(
y
));
Assert
.
False
(
y
.
Equals
(
x
));
Assert
.
False
(
y
.
Equals
(
x
));
Assert
.
False
(
x
.
GetHashCode
()
==
y
.
GetHashCode
());
// well, very unlikely
Assert
.
False
(
x
.
GetHashCode
()
==
y
.
GetHashCode
());
// well, very unlikely
...
@@ -107,9 +122,9 @@ private void CheckNull(RedisValue value)
...
@@ -107,9 +122,9 @@ private void CheckNull(RedisValue value)
Assert
.
Equal
(
0L
,
(
long
)
value
);
Assert
.
Equal
(
0L
,
(
long
)
value
);
CheckSame
(
value
,
value
);
CheckSame
(
value
,
value
);
CheckSame
(
value
,
default
(
RedisValue
));
//
CheckSame(value, default(RedisValue));
CheckSame
(
value
,
(
string
)
null
);
//
CheckSame(value, (string)null);
CheckSame
(
value
,
(
byte
[])
null
);
//
CheckSame(value, (byte[])null);
}
}
[
Fact
]
[
Fact
]
...
...
StackExchange.Redis/StackExchange.Redis.csproj
View file @
9a0f258e
...
@@ -11,6 +11,8 @@
...
@@ -11,6 +11,8 @@
<SignAssembly>true</SignAssembly>
<SignAssembly>true</SignAssembly>
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
<LangVersion>latest</LangVersion>
<LangVersion>latest</LangVersion>
<!--<DefineConstants>$(DefineConstants);LOGOUTPUT</DefineConstants>-->
</PropertyGroup>
</PropertyGroup>
<ItemGroup>
<ItemGroup>
...
...
StackExchange.Redis/StackExchange/Redis/DebuggingAids.cs
View file @
9a0f258e
using
System
;
using
System
;
using
System.Diagnostics
;
using
System.Diagnostics
;
using
System.IO
;
using
System.IO.Pipelines
;
using
System.Runtime.InteropServices
;
using
System.Runtime.InteropServices
;
using
System.Text
;
using
System.Text
;
using
System.Threading
;
using
System.Threading
;
...
@@ -324,28 +326,32 @@ partial class ConnectionMultiplexer
...
@@ -324,28 +326,32 @@ partial class ConnectionMultiplexer
partial
class
PhysicalConnection
partial
class
PhysicalConnection
{
{
private
Stream
echo
;
//private Stream echo;
partial
void
OnCreateEcho
()
//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
)
{
{
if
(!
string
.
IsNullOrEmpty
(
ConnectionMultiplexer
.
EchoPath
))
foreach
(
var
c
in
Path
.
GetInvalidFileNameChars
(
))
{
{
string
fullPath
=
Path
.
Combine
(
ConnectionMultiplexer
.
EchoPath
,
name
=
name
.
Replace
(
c
,
'_'
);
Regex
.
Replace
(
physicalName
,
@"[\-\.\@\#\:]"
,
"_"
));
echo
=
File
.
Open
(
Path
.
ChangeExtension
(
fullPath
,
"txt"
),
FileMode
.
Create
,
FileAccess
.
Write
,
FileShare
.
ReadWrite
);
}
}
}
pipe
=
new
LoggingPipe
(
pipe
,
$"
{
name
}
.in"
,
$"
{
name
}
.out"
);
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
);
}
}
}
}
#endif
#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
#if LOGOUTPUT
sealed
class
Logging
TextStream
:
Stream
sealed
class
Logging
Pipe
:
IDuplexPipe
{
{
[
Conditional
(
"VERBOSE"
)]
private
IDuplexPipe
_inner
;
void
Trace
(
string
value
,
[
CallerMemberName
]
string
caller
=
null
)
{
Debug
.
WriteLine
(
value
,
this
.
category
+
":"
+
caller
);
}
[
Conditional
(
"VERBOSE"
)]
void
Trace
(
char
value
,
[
CallerMemberName
]
string
caller
=
null
)
{
Debug
.
WriteLine
(
value
,
this
.
category
+
":"
+
caller
);
}
private
readonly
Stream
stream
,
echo
;
public
LoggingPipe
(
IDuplexPipe
inner
,
string
inPath
,
string
outPath
)
private
readonly
string
category
;
public
LoggingTextStream
(
Stream
stream
,
string
category
,
Stream
echo
)
{
{
if
(
stream
==
null
)
throw
new
ArgumentNullException
(
"stream"
);
_inner
=
inner
;
if
(
string
.
IsNullOrWhiteSpace
(
category
))
category
=
GetType
().
Name
;
if
(
string
.
IsNullOrWhiteSpace
(
inPath
))
this
.
stream
=
stream
;
this
.
category
=
category
;
this
.
echo
=
echo
;
}
public
override
IAsyncResult
BeginRead
(
byte
[]
buffer
,
int
offset
,
int
count
,
AsyncCallback
callback
,
object
state
)
{
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
else
{
{
Trace
(
Encoding
.
UTF8
.
GetString
(
asyncBuffer
,
asyncOffset
,
asyncCount
));
var
pipe
=
new
Pipe
();
}
Input
=
pipe
.
Reader
;
return
bytes
;
CloneAsync
(
inPath
,
inner
.
Input
,
pipe
.
Writer
);
}
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
();
}
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
)
{
return
stream
.
Seek
(
offset
,
origin
);
}
public
override
void
SetLength
(
long
value
)
{
stream
.
SetLength
(
value
);
}
public
override
void
WriteByte
(
byte
value
)
{
Trace
((
char
)
value
);
stream
.
WriteByte
(
value
);
if
(
echo
!=
null
)
echo
.
WriteByte
(
value
);
}
public
override
int
ReadByte
()
{
int
value
=
stream
.
ReadByte
();
if
(
value
<
0
)
{
Trace
(
"<EOF>"
);
}
else
{
Trace
((
char
)
value
);
}
}
return
value
;
}
if
(
string
.
IsNullOrWhiteSpace
(
outPath
))
public
override
int
Read
(
byte
[]
buffer
,
int
offset
,
int
count
)
{
int
bytes
=
stream
.
Read
(
buffer
,
offset
,
count
);
if
(
bytes
<=
0
)
{
{
Trace
(
"<EOF>"
)
;
Output
=
inner
.
Output
;
}
}
else
else
{
{
Trace
(
Encoding
.
UTF8
.
GetString
(
buffer
,
offset
,
bytes
));
var
pipe
=
new
Pipe
();
Output
=
pipe
.
Writer
;
CloneAsync
(
outPath
,
pipe
.
Reader
,
inner
.
Output
);
}
}
return
bytes
;
}
}
public
override
void
Write
(
byte
[]
buffer
,
int
offset
,
int
count
)
private
async
void
CloneAsync
(
string
path
,
PipeReader
from
,
PipeWriter
to
)
{
{
if
(
count
!=
0
)
to
.
OnReaderCompleted
((
ex
,
o
)
=>
((
PipeReader
)
o
).
Complete
(
ex
),
from
);
from
.
OnWriterCompleted
((
ex
,
o
)
=>
((
PipeWriter
)
o
).
Complete
(
ex
),
to
);
while
(
true
)
{
{
Trace
(
Encoding
.
UTF8
.
GetString
(
buffer
,
offset
,
count
));
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
))
{
foreach
(
var
segment
in
buffer
)
{
// append it to the file
bool
leased
=
false
;
if
(!
MemoryMarshal
.
TryGetArray
(
segment
,
out
var
arr
))
{
var
tmp
=
ArrayPool
<
byte
>.
Shared
.
Rent
(
segment
.
Length
);
segment
.
CopyTo
(
tmp
);
arr
=
new
ArraySegment
<
byte
>(
tmp
,
0
,
segment
.
Length
);
leased
=
true
;
}
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
);
}
}
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
#endif
}
}
StackExchange.Redis/StackExchange/Redis/PhysicalConnection.cs
View file @
9a0f258e
...
@@ -421,13 +421,24 @@ internal void Write(RedisChannel channel)
...
@@ -421,13 +421,24 @@ internal void Write(RedisChannel channel)
internal
void
Write
(
RedisValue
value
)
internal
void
Write
(
RedisValue
value
)
{
{
if
(
value
.
IsInteger
)
switch
(
value
.
Type
)
{
{
WriteUnified
(
_ioPipe
.
Output
,
(
long
)
value
);
case
RedisValue
.
StorageType
.
Null
:
}
WriteUnified
(
_ioPipe
.
Output
,
(
byte
[])
null
);
else
break
;
{
case
RedisValue
.
StorageType
.
Int64
:
WriteUnified
(
_ioPipe
.
Output
,
(
byte
[])
value
);
WriteUnified
(
_ioPipe
.
Output
,
(
long
)
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)
...
@@ -492,7 +503,7 @@ static void WriteCrlf(PipeWriter writer)
span
[
1
]
=
(
byte
)
'\n'
;
span
[
1
]
=
(
byte
)
'\n'
;
writer
.
Advance
(
2
);
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
)
if
(
value
>=
0
&&
value
<=
9
)
{
{
...
@@ -594,16 +605,24 @@ internal void WakeWriterAndCheckForThrottle()
...
@@ -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"
);
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
)
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
)
if
(
value
==
null
)
{
{
// special case:
// special case:
writer
.
Write
(
NullBulkString
);
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:
// special case:
writer
.
Write
(
EmptyBulkString
);
writer
.
Write
(
EmptyBulkString
);
...
@@ -611,7 +630,8 @@ private static void WriteUnified(PipeWriter writer, byte[] value)
...
@@ -611,7 +630,8 @@ private static void WriteUnified(PipeWriter writer, byte[] value)
else
if
(
value
.
Length
<=
MaxQuickSpanSize
)
else
if
(
value
.
Length
<=
MaxQuickSpanSize
)
{
{
var
span
=
writer
.
GetSpan
(
5
+
MaxInt32TextLen
+
value
.
Length
);
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
);
writer
.
Advance
(
bytes
);
}
}
else
else
...
@@ -619,7 +639,7 @@ private static void WriteUnified(PipeWriter writer, byte[] value)
...
@@ -619,7 +639,7 @@ private static void WriteUnified(PipeWriter writer, byte[] value)
// too big to guarantee can do in a single span
// too big to guarantee can do in a single span
var
span
=
writer
.
GetSpan
(
3
+
MaxInt32TextLen
);
var
span
=
writer
.
GetSpan
(
3
+
MaxInt32TextLen
);
span
[
0
]
=
(
byte
)
'$'
;
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
.
Advance
(
bytes
);
writer
.
Write
(
value
);
writer
.
Write
(
value
);
...
@@ -636,13 +656,18 @@ private static int WriteUnified(Span<byte> span, byte[] value, int offset = 0)
...
@@ -636,13 +656,18 @@ private static int WriteUnified(Span<byte> span, byte[] value, int offset = 0)
}
}
else
else
{
{
offset
=
WriteRaw
(
span
,
value
.
Length
,
offset
:
offset
);
offset
=
WriteUnified
(
span
,
new
ReadOnlySpan
<
byte
>(
value
),
offset
);
new
ReadOnlySpan
<
byte
>(
value
).
CopyTo
(
span
.
Slice
(
offset
,
value
.
Length
));
offset
+=
value
.
Length
;
offset
=
WriteCrlf
(
span
,
offset
);
}
}
return
offset
;
return
offset
;
}
}
private
static
int
WriteUnified
(
Span
<
byte
>
span
,
ReadOnlySpan
<
byte
>
value
,
int
offset
=
0
)
{
offset
=
WriteRaw
(
span
,
value
.
Length
,
offset
:
offset
);
value
.
CopyTo
(
span
.
Slice
(
offset
,
value
.
Length
));
offset
+=
value
.
Length
;
offset
=
WriteCrlf
(
span
,
offset
);
return
offset
;
}
internal
void
WriteSha1AsHex
(
byte
[]
value
)
internal
void
WriteSha1AsHex
(
byte
[]
value
)
{
{
...
...
StackExchange.Redis/StackExchange/Redis/RedisDatabase.cs
View file @
9a0f258e
...
@@ -2484,7 +2484,9 @@ internal override void WriteImpl(PhysicalConnection physical)
...
@@ -2484,7 +2484,9 @@ internal override void WriteImpl(PhysicalConnection physical)
}
}
else
else
{
// recognises well-known types
{
// 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
...
@@ -82,22 +82,22 @@ public bool IsNullOrEmpty
/// <param name="y">The second <see cref="RedisValue"/> to compare.</param>
/// <param name="y">The second <see cref="RedisValue"/> to compare.</param>
public
static
bool
operator
==(
RedisValue
x
,
RedisValue
y
)
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
==
StorageType
.
Null
)
return
yType
==
Storag
eType
.
Null
;
if
(
xType
==
Compar
eType
.
Null
)
return
false
;
if
(
xType
==
Storag
eType
.
Null
)
return
false
;
if
(
xType
==
yType
)
if
(
xType
==
yType
)
{
{
switch
(
xType
)
switch
(
xType
)
{
{
case
Compar
eType
.
Double
:
case
Storag
eType
.
Double
:
return
x
.
OverlappedValueDouble
==
y
.
OverlappedValueDouble
;
return
x
.
OverlappedValueDouble
==
y
.
OverlappedValueDouble
;
case
Compar
eType
.
Int64
:
case
Storag
eType
.
Int64
:
return
x
.
_overlappedValue64
==
y
.
_overlappedValue64
;
return
x
.
_overlappedValue64
==
y
.
_overlappedValue64
;
case
Compar
eType
.
String
:
case
Storag
eType
.
String
:
return
(
string
)
x
.
_objectOrSentinel
==
(
string
)
y
.
_objectOrSentinel
;
return
(
string
)
x
.
_objectOrSentinel
==
(
string
)
y
.
_objectOrSentinel
;
case
Compar
eType
.
Raw
:
case
Storag
eType
.
Raw
:
return
x
.
_memory
.
Span
.
SequenceEqual
(
y
.
_memory
.
Span
);
return
x
.
_memory
.
Span
.
SequenceEqual
(
y
.
_memory
.
Span
);
}
}
}
}
...
@@ -113,7 +113,7 @@ public bool IsNullOrEmpty
...
@@ -113,7 +113,7 @@ public bool IsNullOrEmpty
public
override
bool
Equals
(
object
obj
)
public
override
bool
Equals
(
object
obj
)
{
{
if
(
obj
==
null
)
return
IsNull
;
if
(
obj
==
null
)
return
IsNull
;
if
(
obj
is
RedisValue
typed
)
return
Equals
(
typed
);
var
other
=
TryParse
(
obj
);
var
other
=
TryParse
(
obj
);
if
(
other
.
IsNull
)
return
false
;
// parse fail
if
(
other
.
IsNull
)
return
false
;
// parse fail
return
this
==
other
;
return
this
==
other
;
...
@@ -132,15 +132,15 @@ public override int GetHashCode()
...
@@ -132,15 +132,15 @@ public override int GetHashCode()
{
{
switch
(
Type
)
switch
(
Type
)
{
{
case
Compar
eType
.
Null
:
case
Storag
eType
.
Null
:
return
-
1
;
return
-
1
;
case
Compar
eType
.
Double
:
case
Storag
eType
.
Double
:
return
OverlappedValueDouble
.
GetHashCode
();
return
OverlappedValueDouble
.
GetHashCode
();
case
Compar
eType
.
Int64
:
case
Storag
eType
.
Int64
:
return
_overlappedValue64
.
GetHashCode
();
return
_overlappedValue64
.
GetHashCode
();
case
Compar
eType
.
Raw
:
case
Storag
eType
.
Raw
:
return
GetHashCode
(
_memory
);
return
GetHashCode
(
_memory
);
case
Compar
eType
.
String
:
case
Storag
eType
.
String
:
default
:
default
:
return
_objectOrSentinel
.
GetHashCode
();
return
_objectOrSentinel
.
GetHashCode
();
}
}
...
@@ -264,22 +264,22 @@ internal void AssertNotNull()
...
@@ -264,22 +264,22 @@ internal void AssertNotNull()
if
(
IsNull
)
throw
new
ArgumentException
(
"A null value is not valid in this context"
);
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
,
Null
,
Int64
,
Double
,
Raw
,
String
,
}
}
private
Compar
eType
Type
internal
Storag
eType
Type
{
{
get
get
{
{
var
objectOrSentinel
=
_objectOrSentinel
;
var
objectOrSentinel
=
_objectOrSentinel
;
if
(
objectOrSentinel
==
null
)
return
Compar
eType
.
Null
;
if
(
objectOrSentinel
==
null
)
return
Storag
eType
.
Null
;
if
(
objectOrSentinel
==
Sentinel_Integer
)
return
Compar
eType
.
Int64
;
if
(
objectOrSentinel
==
Sentinel_Integer
)
return
Storag
eType
.
Int64
;
if
(
objectOrSentinel
==
Sentinel_Double
)
return
Compar
eType
.
Double
;
if
(
objectOrSentinel
==
Sentinel_Double
)
return
Storag
eType
.
Double
;
if
(
objectOrSentinel
==
Sentinel_Raw
)
return
Compar
eType
.
Raw
;
if
(
objectOrSentinel
==
Sentinel_Raw
)
return
Storag
eType
.
Raw
;
if
(
objectOrSentinel
is
string
)
return
Compar
eType
.
String
;
if
(
objectOrSentinel
is
string
)
return
Storag
eType
.
String
;
if
(
objectOrSentinel
is
byte
[])
return
Compar
eType
.
Raw
;
// doubled-up, but retaining the array
if
(
objectOrSentinel
is
byte
[])
return
Storag
eType
.
Raw
;
// doubled-up, but retaining the array
throw
new
InvalidOperationException
(
"Unknown type"
);
throw
new
InvalidOperationException
(
"Unknown type"
);
}
}
}
}
...
@@ -292,23 +292,23 @@ public int CompareTo(RedisValue other)
...
@@ -292,23 +292,23 @@ public int CompareTo(RedisValue other)
{
{
try
try
{
{
Compar
eType
thisType
=
this
.
Type
,
Storag
eType
thisType
=
this
.
Type
,
otherType
=
other
.
Type
;
otherType
=
other
.
Type
;
if
(
thisType
==
CompareType
.
Null
)
return
otherType
==
Compar
eType
.
Null
?
0
:
-
1
;
if
(
thisType
==
StorageType
.
Null
)
return
otherType
==
Storag
eType
.
Null
?
0
:
-
1
;
if
(
otherType
==
Compar
eType
.
Null
)
return
1
;
if
(
otherType
==
Storag
eType
.
Null
)
return
1
;
if
(
thisType
==
otherType
)
if
(
thisType
==
otherType
)
{
{
switch
(
thisType
)
switch
(
thisType
)
{
{
case
Compar
eType
.
Double
:
case
Storag
eType
.
Double
:
return
this
.
OverlappedValueDouble
.
CompareTo
(
other
.
OverlappedValueDouble
);
return
this
.
OverlappedValueDouble
.
CompareTo
(
other
.
OverlappedValueDouble
);
case
Compar
eType
.
Int64
:
case
Storag
eType
.
Int64
:
return
this
.
_overlappedValue64
.
CompareTo
(
other
.
_overlappedValue64
);
return
this
.
_overlappedValue64
.
CompareTo
(
other
.
_overlappedValue64
);
case
Compar
eType
.
String
:
case
Storag
eType
.
String
:
return
string
.
CompareOrdinal
((
string
)
this
.
_objectOrSentinel
,
(
string
)
other
.
_objectOrSentinel
);
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
);
return
this
.
_memory
.
Span
.
SequenceCompareTo
(
other
.
_memory
.
Span
);
}
}
}
}
...
@@ -479,19 +479,19 @@ internal static RedisValue TryParse(object obj)
...
@@ -479,19 +479,19 @@ internal static RedisValue TryParse(object obj)
{
{
switch
(
value
.
Type
)
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")
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
;
return
value
.
_overlappedValue64
;
case
Compar
eType
.
Double
:
case
Storag
eType
.
Double
:
var
f64
=
value
.
OverlappedValueDouble
;
var
f64
=
value
.
OverlappedValueDouble
;
var
i64
=
(
long
)
f64
;
var
i64
=
(
long
)
f64
;
if
(
f64
==
i64
)
return
i64
;
if
(
f64
==
i64
)
return
i64
;
break
;
break
;
case
Compar
eType
.
String
:
case
Storag
eType
.
String
:
if
(
TryParseInt64
((
string
)
value
.
_objectOrSentinel
,
out
i64
))
return
i64
;
if
(
TryParseInt64
((
string
)
value
.
_objectOrSentinel
,
out
i64
))
return
i64
;
break
;
break
;
case
Compar
eType
.
Raw
:
case
Storag
eType
.
Raw
:
if
(
TryParseInt64
(
value
.
_memory
.
Span
,
out
i64
))
return
i64
;
if
(
TryParseInt64
(
value
.
_memory
.
Span
,
out
i64
))
return
i64
;
break
;
break
;
}
}
...
@@ -506,16 +506,16 @@ internal static RedisValue TryParse(object obj)
...
@@ -506,16 +506,16 @@ internal static RedisValue TryParse(object obj)
{
{
switch
(
value
.
Type
)
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")
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
;
return
value
.
_overlappedValue64
;
case
Compar
eType
.
Double
:
case
Storag
eType
.
Double
:
return
value
.
OverlappedValueDouble
;
return
value
.
OverlappedValueDouble
;
case
Compar
eType
.
String
:
case
Storag
eType
.
String
:
if
(
TryParseInt64
((
string
)
value
.
_objectOrSentinel
,
out
var
f64
))
return
f64
;
if
(
TryParseInt64
((
string
)
value
.
_objectOrSentinel
,
out
var
f64
))
return
f64
;
break
;
break
;
case
Compar
eType
.
Raw
:
case
Storag
eType
.
Raw
:
if
(
TryParseInt64
(
value
.
_memory
.
Span
,
out
f64
))
return
f64
;
if
(
TryParseInt64
(
value
.
_memory
.
Span
,
out
f64
))
return
f64
;
break
;
break
;
}
}
...
@@ -582,11 +582,11 @@ private static bool TryParseDouble(ReadOnlySpan<byte> blob, out double value)
...
@@ -582,11 +582,11 @@ private static bool TryParseDouble(ReadOnlySpan<byte> blob, out double value)
{
{
switch
(
value
.
Type
)
switch
(
value
.
Type
)
{
{
case
Compar
eType
.
Null
:
return
null
;
case
Storag
eType
.
Null
:
return
null
;
case
Compar
eType
.
Double
:
return
Format
.
ToString
(
value
.
OverlappedValueDouble
.
ToString
());
case
Storag
eType
.
Double
:
return
Format
.
ToString
(
value
.
OverlappedValueDouble
.
ToString
());
case
Compar
eType
.
Int64
:
return
Format
.
ToString
(
value
.
_overlappedValue64
);
case
Storag
eType
.
Int64
:
return
Format
.
ToString
(
value
.
_overlappedValue64
);
case
Compar
eType
.
String
:
return
(
string
)
value
.
_objectOrSentinel
;
case
Storag
eType
.
String
:
return
(
string
)
value
.
_objectOrSentinel
;
case
Compar
eType
.
Raw
:
case
Storag
eType
.
Raw
:
var
span
=
value
.
_memory
.
Span
;
var
span
=
value
.
_memory
.
Span
;
if
(
span
.
IsEmpty
)
return
""
;
if
(
span
.
IsEmpty
)
return
""
;
if
(
span
.
Length
==
2
&&
span
[
0
]
==
(
byte
)
'O'
&&
span
[
1
]
==
(
byte
)
'K'
)
return
"OK"
;
// frequent special-case
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)
...
@@ -633,10 +633,26 @@ static string ToHex(ReadOnlySpan<byte> src)
/// <param name="value">The <see cref="RedisValue"/> to convert.</param>
/// <param name="value">The <see cref="RedisValue"/> to convert.</param>
public
static
implicit
operator
byte
[]
(
RedisValue
value
)
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
);
return
Encoding
.
UTF8
.
GetBytes
((
string
)
value
);
}
}
...
@@ -644,14 +660,8 @@ static string ToHex(ReadOnlySpan<byte> src)
...
@@ -644,14 +660,8 @@ static string ToHex(ReadOnlySpan<byte> src)
/// Converts a <see cref="RedisValue"/> to a ReadOnlyMemory
/// Converts a <see cref="RedisValue"/> to a ReadOnlyMemory
/// </summary>
/// </summary>
/// <param name="value">The <see cref="RedisValue"/> to convert.</param>
/// <param name="value">The <see cref="RedisValue"/> to convert.</param>
public
static
implicit
operator
ReadOnlyMemory
<
byte
>
(
RedisValue
value
)
public
static
implicit
operator
ReadOnlyMemory
<
byte
>(
RedisValue
value
)
{
=>
value
.
Type
==
StorageType
.
Raw
?
value
.
_memory
:
(
byte
[])
value
;
if
(
value
.
Type
==
CompareType
.
Raw
)
{
return
value
.
_memory
;
}
return
Encoding
.
UTF8
.
GetBytes
((
string
)
value
);
}
TypeCode
IConvertible
.
GetTypeCode
()
=>
TypeCode
.
Object
;
TypeCode
IConvertible
.
GetTypeCode
()
=>
TypeCode
.
Object
;
...
@@ -709,26 +719,23 @@ object IConvertible.ToType(Type conversionType, IFormatProvider provider)
...
@@ -709,26 +719,23 @@ object IConvertible.ToType(Type conversionType, IFormatProvider provider)
/// <param name="val">The <see cref="long"/> value, if conversion was possible.</param>
/// <param name="val">The <see cref="long"/> value, if conversion was possible.</param>
public
bool
TryParse
(
out
long
val
)
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
)
{
val
=
valueInt64
;
return
true
;
}
if
(
blob
==
null
)
{
{
// in redis-land 0 approx. equal null; so roll with it
case
StorageType
.
Null
:
val
=
0
;
return
true
;
// in redis-land 0 approx. equal null; so roll with it
val
=
0
;
case
StorageType
.
Int64
:
val
=
_overlappedValue64
;
return
true
;
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
=
(
long
)
f64
;
return
true
;
}
break
;
}
}
val
=
default
;
return
TryParseInt64
(
blob
,
0
,
blob
.
Length
,
out
val
)
;
return
false
;
}
}
/// <summary>
/// <summary>
...
@@ -739,14 +746,14 @@ public bool TryParse(out long val)
...
@@ -739,14 +746,14 @@ public bool TryParse(out long val)
/// <param name="val">The <see cref="int"/> value, if conversion was possible.</param>
/// <param name="val">The <see cref="int"/> value, if conversion was possible.</param>
public
bool
TryParse
(
out
int
val
)
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
;
val
=
(
int
)
l
;
return
fals
e
;
return
tru
e
;
}
}
val
=
default
;
return
false
;
val
=
(
int
)
l
;
return
true
;
}
}
/// <summary>
/// <summary>
...
@@ -757,20 +764,16 @@ public bool TryParse(out int val)
...
@@ -757,20 +764,16 @@ public bool TryParse(out int val)
/// <param name="val">The <see cref="double"/> value, if conversion was possible.</param>
/// <param name="val">The <see cref="double"/> value, if conversion was possible.</param>
public
bool
TryParse
(
out
double
val
)
public
bool
TryParse
(
out
double
val
)
{
{
var
blob
=
valueBlob
;
switch
(
Type
)
if
(
blob
==
IntegerSentinel
)
{
val
=
valueInt64
;
return
true
;
}
if
(
blob
==
null
)
{
{
// in redis-land 0 approx. equal null; so roll with it
case
StorageType
.
Null
:
val
=
0
;
return
true
;
val
=
0
;
case
StorageType
.
Int64
:
val
=
_overlappedValue64
;
return
true
;
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
);
}
}
val
=
default
;
return
TryParseDouble
(
blob
,
out
val
)
;
return
false
;
}
}
}
}
}
}
TestConsole/Program.cs
View file @
9a0f258e
...
@@ -9,9 +9,6 @@ class Program
...
@@ -9,9 +9,6 @@ class Program
{
{
static
int
Main
()
static
int
Main
()
{
{
var
options
=
PipeOptions
.
Default
;
Console
.
WriteLine
(
options
.
PauseWriterThreshold
);
Console
.
WriteLine
(
options
.
ResumeWriterThreshold
);
var
s
=
new
StringWriter
();
var
s
=
new
StringWriter
();
try
try
{
{
...
@@ -38,8 +35,8 @@ static int Main()
...
@@ -38,8 +35,8 @@ static int Main()
}
}
finally
finally
{
{
//
Console.WriteLine();
Console
.
WriteLine
();
//
Console.WriteLine(s);
//Console.WriteLine(s);
}
}
}
}
...
...
TestConsole/TestConsole.csproj
View file @
9a0f258e
...
@@ -6,13 +6,17 @@
...
@@ -6,13 +6,17 @@
<LangVersion>latest</LangVersion>
<LangVersion>latest</LangVersion>
</PropertyGroup>
</PropertyGroup>
<PropertyGroup Condition="'$(Computername)'=='OCHO' or '$(Computername)'=='SKINK'">
<LocalReference>true</LocalReference>
</PropertyGroup>
<ItemGroup>
<ItemGroup>
<ProjectReference Include="..\StackExchange.Redis\StackExchange.Redis.csproj" />
<ProjectReference Include="..\StackExchange.Redis\StackExchange.Redis.csproj" />
</ItemGroup>
</ItemGroup>
<ItemGroup Condition="'$(
Computername)'=='OCHO
'">
<ItemGroup Condition="'$(
LocalReference)'=='true
'">
<ProjectReference Include="..\..\Pipelines.Sockets.Unofficial\src\Pipelines.Sockets.Unofficial\Pipelines.Sockets.Unofficial.csproj" />
<ProjectReference Include="..\..\Pipelines.Sockets.Unofficial\src\Pipelines.Sockets.Unofficial\Pipelines.Sockets.Unofficial.csproj" />
</ItemGroup>
</ItemGroup>
<ItemGroup Condition="'$(
Computername)'!='OCHO
'">
<ItemGroup Condition="'$(
LocalReference)'!='true
'">
<PackageReference Include="Pipelines.Sockets.Unofficial" Version="0.2.1-alpha.48" />
<PackageReference Include="Pipelines.Sockets.Unofficial" Version="0.2.1-alpha.48" />
</ItemGroup>
</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