From 863160aa310cc01a35a66587e9ae324fbd5ea709 Mon Sep 17 00:00:00 2001 From: Giovanni De Gasperis Date: Mon, 22 Sep 2014 21:55:56 +0200 Subject: [PATCH 01/19] specified OpenSim context --- README | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README b/README index 47d10fc..2e78855 100644 --- a/README +++ b/README @@ -1,9 +1,6 @@ -This is a C#/.NET client binding for the Redis server: +This is a C#/.NET client binding for the Redis server, runnable in a [OpenSim](http://opensimulator.org) script: - http://code.google.com/p/redis/ The code is licensed under the same terms that the Redis server is, the new BSD license The binding lives in the redis-sharp.cs file, a sample test file can be found on test.cs - -Miguel de Icaza, November 2009 \ No newline at end of file From 6811aed70ef213e75ac49fed4633158c4d41ec60 Mon Sep 17 00:00:00 2001 From: Giovanni De Gasperis Date: Mon, 22 Sep 2014 21:58:38 +0200 Subject: [PATCH 02/19] adapted to C# OpenSim contraints --- os-redis-sharp.cs | 927 +++++++++++++++++++++++++++++++++++++++++++++ redis-sharp.cs | 931 ---------------------------------------------- 2 files changed, 927 insertions(+), 931 deletions(-) create mode 100644 os-redis-sharp.cs delete mode 100644 redis-sharp.cs diff --git a/os-redis-sharp.cs b/os-redis-sharp.cs new file mode 100644 index 0000000..833e63e --- /dev/null +++ b/os-redis-sharp.cs @@ -0,0 +1,927 @@ +//c# + +// +// os-redis-sharp.cs: ECMA CLI Binding to the Redis key-value storage system +// runnable in a OpenSim script +// +// Authors: +// Miguel de Icaza (miguel@gnome.org) +// Giovanni De Gasperis (giovanni@giodegas.it) +// +// Licensed under the same terms of reddis: new BSD license. +// + +// using cannot be used in a OpenSim script + +public class Redis : System.IDisposable { + System.Net.Sockets.Socket socket; + System.IO.BufferedStream bstream; + + public enum KeyType { + None, String, List, Set + } + + public class ResponseException : System.Exception { + public ResponseException (string code) : base ("Response error") + { + Code = code; + } + + public string Code { get; private set; } + } + + public Redis (string host, int port) + { + if (host == null) + throw new System.ArgumentNullException ("host"); + + Host = host; + Port = port; + SendTimeout = -1; + } + + public Redis (string host) : this (host, 6379) + { + } + + public Redis () : this ("localhost", 6379) + { + } + + public string Host { get; private set; } + public int Port { get; private set; } + public int RetryTimeout { get; set; } + public int RetryCount { get; set; } + public int SendTimeout { get; set; } + public string Password { get; set; } + + int db; + public int Db { + get { + return db; + } + + set { + db = value; + SendExpectSuccess ("SELECT", db); + } + } + + public string this [string key] { + get { return GetString (key); } + set { Set (key, value); } + } + + public void Set (string key, string value) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + if (value == null) + throw new System.ArgumentNullException ("value"); + + Set (key, System.Text.Encoding.UTF8.GetBytes (value)); + } + + public void Set (string key, byte [] value) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + if (value == null) + throw new System.ArgumentNullException ("value"); + + if (value.Length > 1073741824) + throw new System.ArgumentException ("value exceeds 1G", "value"); + + if (!SendDataCommand (value, "SET", key)) + throw new System.Exception ("Unable to connect"); + ExpectSuccess (); + } + + public bool SetNX (string key, string value) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + if (value == null) + throw new System.ArgumentNullException ("value"); + + return SetNX (key, System.Text.Encoding.UTF8.GetBytes (value)); + } + + public bool SetNX (string key, byte [] value) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + if (value == null) + throw new System.ArgumentNullException ("value"); + + if (value.Length > 1073741824) + throw new System.ArgumentException ("value exceeds 1G", "value"); + + return SendDataExpectInt (value, "SETNX", key) > 0 ? true : false; + } + + // problems findind IDictionary that works here... GDG + + /*public void Set (System.Linq.IDictionary dict) + { + if (dict == null) + throw new System.ArgumentNullException ("dict"); + + Set (dict.ToDictionary(k => k.Key, v => System.Text.Encoding.UTF8.GetBytes(v.Value))); + } + + public void Set (System.Linq.IDictionary dict) + { + if (dict == null) + throw new System.ArgumentNullException ("dict"); + + MSet (dict.Keys.ToArray (), dict.Values.ToArray ()); + }*/ + + public void MSet (string [] keys, byte [][] values) + { + if (keys.Length != values.Length) + throw new System.ArgumentException ("keys and values must have the same size"); + + byte [] nl = System.Text.Encoding.UTF8.GetBytes ("\r\n"); + System.IO.MemoryStream ms = new System.IO.MemoryStream (); + + for (int i = 0; i < keys.Length; i++) { + byte [] key = System.Text.Encoding.UTF8.GetBytes(keys[i]); + byte [] val = values[i]; + byte [] kLength = System.Text.Encoding.UTF8.GetBytes ("$" + key.Length + "\r\n"); + byte [] k = System.Text.Encoding.UTF8.GetBytes (keys[i] + "\r\n"); + byte [] vLength = System.Text.Encoding.UTF8.GetBytes ("$" + val.Length + "\r\n"); + ms.Write (kLength, 0, kLength.Length); + ms.Write (k, 0, k.Length); + ms.Write (vLength, 0, vLength.Length); + ms.Write (val, 0, val.Length); + ms.Write (nl, 0, nl.Length); + } + + SendDataRESP (ms.ToArray (), "*" + (keys.Length * 2 + 1) + "\r\n$4\r\nMSET\r\n"); + ExpectSuccess (); + } + + public byte [] Get (string key) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + return SendExpectData ("GET", key); + } + + public string GetString (string key) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + return System.Text.Encoding.UTF8.GetString (Get (key)); + } + + public byte [][] Sort (SortOptions options) + { + return Sort (options.Key, options.StoreInKey, options.ToArgs()); + } + + public byte [][] Sort (string key, string destination, params object [] options) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + + int offset = string.IsNullOrEmpty (destination) ? 1 : 3; + object [] args = new object [offset + options.Length]; + + args [0] = key; + System.Array.Copy (options, 0, args, offset, options.Length); + if (offset == 1) { + return SendExpectDataArray ("SORT", args); + } + else { + args [1] = "STORE"; + args [2] = destination; + int n = SendExpectInt ("SORT", args); + return new byte [n][]; + } + } + + public byte [] GetSet (string key, byte [] value) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + if (value == null) + throw new System.ArgumentNullException ("value"); + + if (value.Length > 1073741824) + throw new System.ArgumentException ("value exceeds 1G", "value"); + + if (!SendDataCommand (value, "GETSET", key)) + throw new System.Exception ("Unable to connect"); + + return ReadData (); + } + + public string GetSet (string key, string value) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + if (value == null) + throw new System.ArgumentNullException ("value"); + return System.Text.Encoding.UTF8.GetString (GetSet (key, System.Text.Encoding.UTF8.GetBytes (value))); + } + + string ReadLine () + { + System.Text.StringBuilder sb = new System.Text.StringBuilder (); + int c; + + while ((c = bstream.ReadByte ()) != -1){ + if (c == '\r') + continue; + if (c == '\n') + break; + sb.Append ((char) c); + } + return sb.ToString (); + } + + void Connect () + { + socket = new System.Net.Sockets.Socket (System.Net.Sockets.AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp); + socket.NoDelay = true; + socket.SendTimeout = SendTimeout; + socket.Connect (Host, Port); + if (!socket.Connected){ + socket.Close (); + socket = null; + return; + } + bstream = new System.IO.BufferedStream (new System.Net.Sockets.NetworkStream (socket), 16*1024); + + if (Password != null) + SendExpectSuccess ("AUTH", Password); + } + + byte [] end_data = new byte [] { (byte) '\r', (byte) '\n' }; + + bool SendDataCommand (byte [] data, string cmd, params object [] args) + { + string resp = "*" + (1 + args.Length + 1).ToString () + "\r\n"; + resp += "$" + cmd.Length + "\r\n" + cmd + "\r\n"; + foreach (object arg in args) { + string argStr = arg.ToString (); + int argStrLength = System.Text.Encoding.UTF8.GetByteCount(argStr); + resp += "$" + argStrLength + "\r\n" + argStr + "\r\n"; + } + resp += "$" + data.Length + "\r\n"; + + return SendDataRESP (data, resp); + } + + bool SendDataRESP (byte [] data, string resp) + { + if (socket == null) + Connect (); + if (socket == null) + return false; + + byte [] r = System.Text.Encoding.UTF8.GetBytes (resp); + try { + Log ("C", resp); + socket.Send (r); + if (data != null){ + socket.Send (data); + socket.Send (end_data); + } + } catch (System.Net.Sockets.SocketException){ + // timeout; + socket.Close (); + socket = null; + + return false; + } + return true; + } + + bool SendCommand (string cmd, params object [] args) + { + if (socket == null) + Connect (); + if (socket == null) + return false; + + string resp = "*" + (1 + args.Length).ToString () + "\r\n"; + resp += "$" + cmd.Length + "\r\n" + cmd + "\r\n"; + foreach (object arg in args) { + string argStr = arg.ToString (); + int argStrLength = System.Text.Encoding.UTF8.GetByteCount(argStr); + resp += "$" + argStrLength + "\r\n" + argStr + "\r\n"; + } + + byte [] r = System.Text.Encoding.UTF8.GetBytes (resp); + try { + Log ("C", resp); + socket.Send (r); + } catch (System.Net.Sockets.SocketException){ + // timeout; + socket.Close (); + socket = null; + + return false; + } + return true; + } + + void Log (string id, string message) + { + System.Console.WriteLine(id + ": " + message.Trim().Replace("\r\n", " ")); + } + + void ExpectSuccess () + { + int c = bstream.ReadByte (); + if (c == -1) + throw new ResponseException ("No more data"); + + string s = ReadLine (); + Log ("S", (char)c + s); + if (c == '-') + throw new ResponseException (s.StartsWith ("ERR ") ? s.Substring (4) : s); + } + + void SendExpectSuccess (string cmd, params object [] args) + { + if (!SendCommand (cmd, args)) + throw new System.Exception ("Unable to connect"); + + ExpectSuccess (); + } + + int SendDataExpectInt (byte[] data, string cmd, params object [] args) + { + if (!SendDataCommand (data, cmd, args)) + throw new System.Exception ("Unable to connect"); + + int c = bstream.ReadByte (); + if (c == -1) + throw new ResponseException ("No more data"); + + string s = ReadLine (); + Log ("S", (char)c + s); + if (c == '-') + throw new ResponseException (s.StartsWith ("ERR ") ? s.Substring (4) : s); + if (c == ':'){ + int i; + if (int.TryParse (s, out i)) + return i; + } + throw new ResponseException ("Unknown reply on integer request: " + c + s); + } + + int SendExpectInt (string cmd, params object [] args) + { + if (!SendCommand (cmd, args)) + throw new System.Exception ("Unable to connect"); + + int c = bstream.ReadByte (); + if (c == -1) + throw new ResponseException ("No more data"); + + string s = ReadLine (); + Log ("S", (char)c + s); + if (c == '-') + throw new ResponseException (s.StartsWith ("ERR ") ? s.Substring (4) : s); + if (c == ':'){ + int i; + if (int.TryParse (s, out i)) + return i; + } + throw new ResponseException ("Unknown reply on integer request: " + c + s); + } + + string SendExpectString (string cmd, params object [] args) + { + if (!SendCommand (cmd, args)) + throw new System.Exception ("Unable to connect"); + + int c = bstream.ReadByte (); + if (c == -1) + throw new ResponseException ("No more data"); + + string s = ReadLine (); + Log ("S", (char)c + s); + if (c == '-') + throw new ResponseException (s.StartsWith ("ERR ") ? s.Substring (4) : s); + if (c == '+') + return s; + + throw new ResponseException ("Unknown reply on integer request: " + c + s); + } + + // + // This one does not throw errors + // + string SendGetString (string cmd, params object [] args) + { + if (!SendCommand (cmd, args)) + throw new System.Exception ("Unable to connect"); + + return ReadLine (); + } + + byte [] SendExpectData (string cmd, params object [] args) + { + if (!SendCommand (cmd, args)) + throw new System.Exception ("Unable to connect"); + + return ReadData (); + } + + byte [] ReadData () + { + string s = ReadLine (); + Log ("S", s); + if (s.Length == 0) + throw new ResponseException ("Zero length respose"); + + char c = s [0]; + if (c == '-') + throw new ResponseException (s.StartsWith ("-ERR ") ? s.Substring (5) : s.Substring (1)); + + if (c == '$'){ + if (s == "$-1") + return null; + int n; + + if (System.Int32.TryParse (s.Substring (1), out n)){ + byte [] retbuf = new byte [n]; + + int bytesRead = 0; + do { + int read = bstream.Read (retbuf, bytesRead, n - bytesRead); + if (read < 1) + throw new ResponseException("Invalid termination mid stream"); + bytesRead += read; + } + while (bytesRead < n); + if (bstream.ReadByte () != '\r' || bstream.ReadByte () != '\n') + throw new ResponseException ("Invalid termination"); + return retbuf; + } + throw new ResponseException ("Invalid length"); + } + + /* don't treat arrays here because only one element works -- use DataArray! + //returns the number of matches + if (c == '*') { + int n; + if (Int32.TryParse(s.Substring(1), out n)) + return n <= 0 ? new byte [0] : ReadData(); + + throw new ResponseException ("Unexpected length parameter" + r); + } + */ + + throw new ResponseException ("Unexpected reply: " + s); + } + + public bool ContainsKey (string key) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + return SendExpectInt ("EXISTS", key) == 1; + } + + public bool Remove (string key) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + return SendExpectInt ("DEL", key) == 1; + } + + public int Remove (params string [] args) + { + if (args == null) + throw new System.ArgumentNullException ("args"); + return SendExpectInt ("DEL", args); + } + + public int Increment (string key) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + return SendExpectInt ("INCR", key); + } + + public int Increment (string key, int count) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + return SendExpectInt ("INCRBY", key, count); + } + + public int Decrement (string key) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + return SendExpectInt ("DECR", key); + } + + public int Decrement (string key, int count) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + return SendExpectInt ("DECRBY", key, count); + } + + public KeyType TypeOf (string key) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + switch (SendExpectString ("TYPE", key)) { + case "none": + return KeyType.None; + case "string": + return KeyType.String; + case "set": + return KeyType.Set; + case "list": + return KeyType.List; + } + throw new ResponseException ("Invalid value"); + } + + public string RandomKey () + { + return SendExpectString ("RANDOMKEY"); + } + + public bool Rename (string oldKeyname, string newKeyname) + { + if (oldKeyname == null) + throw new System.ArgumentNullException ("oldKeyname"); + if (newKeyname == null) + throw new System.ArgumentNullException ("newKeyname"); + return SendGetString ("RENAME", oldKeyname, newKeyname) [0] == '+'; + } + + public bool Expire (string key, int seconds) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + return SendExpectInt ("EXPIRE", key, seconds) == 1; + } + + public bool ExpireAt (string key, int time) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + return SendExpectInt ("EXPIREAT", key, time) == 1; + } + + public int TimeToLive (string key) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + return SendExpectInt ("TTL", key); + } + + public int DbSize { + get { + return SendExpectInt ("DBSIZE"); + } + } + + public void Save () + { + SendExpectSuccess ("SAVE"); + } + + public void BackgroundSave () + { + SendExpectSuccess ("BGSAVE"); + } + + public void Shutdown () + { + SendCommand ("SHUTDOWN"); + try { + // the server may return an error + string s = ReadLine (); + Log ("S", s); + if (s.Length == 0) + throw new ResponseException ("Zero length respose"); + throw new ResponseException (s.StartsWith ("-ERR ") ? s.Substring (5) : s.Substring (1)); + } catch (System.IO.IOException) { + // this is the expected good result + socket.Close (); + socket = null; + } + } + + public void FlushAll () + { + SendExpectSuccess ("FLUSHALL"); + } + + public void FlushDb () + { + SendExpectSuccess ("FLUSHDB"); + } + + const long UnixEpoch = 621355968000000000L; + + public System.DateTime LastSave { + get { + int t = SendExpectInt ("LASTSAVE"); + + return new System.DateTime (UnixEpoch) + System.TimeSpan.FromSeconds (t); + } + } + + public Dictionary GetInfo () + { + byte [] r = SendExpectData ("INFO"); + var dict = new Dictionary(); + + foreach (var line in System.Text.Encoding.UTF8.GetString (r).Split ('\n')){ + int p = line.IndexOf (':'); + if (p == -1) + continue; + dict.Add (line.Substring (0, p), line.Substring (p+1)); + } + return dict; + } + + public string [] Keys { + get { + return GetKeys("*"); + } + } + + public string [] GetKeys (string pattern) + { + if (pattern == null) + throw new System.ArgumentNullException ("pattern"); + + return SendExpectStringArray ("KEYS", pattern); + } + + public byte [][] MGet (params string [] keys) + { + if (keys == null) + throw new System.ArgumentNullException ("keys"); + if (keys.Length == 0) + throw new System.ArgumentException ("keys"); + + return SendExpectDataArray ("MGET", keys); + } + + + public string [] SendExpectStringArray (string cmd, params object [] args) + { + byte [][] reply = SendExpectDataArray (cmd, args); + string [] keys = new string [reply.Length]; + for (int i = 0; i < reply.Length; i++) + keys[i] = System.Text.Encoding.UTF8.GetString (reply[i]); + return keys; + } + + public byte[][] SendExpectDataArray (string cmd, params object [] args) + { + if (!SendCommand (cmd, args)) + throw new System.Exception("Unable to connect"); + int c = bstream.ReadByte(); + if (c == -1) + throw new ResponseException("No more data"); + + string s = ReadLine(); + Log("S", (char)c + s); + if (c == '-') + throw new ResponseException(s.StartsWith("ERR ") ? s.Substring(4) : s); + if (c == '*') { + int count; + if (int.TryParse (s, out count)) { + byte [][] result = new byte [count][]; + + for (int i = 0; i < count; i++) + result[i] = ReadData(); + + return result; + } + } + throw new ResponseException("Unknown reply on multi-request: " + c + s); + } + + #region List commands + public byte[][] ListRange(string key, int start, int end) + { + return SendExpectDataArray ("LRANGE", key, start, end); + } + + public void LeftPush(string key, string value) + { + LeftPush(key, System.Text.Encoding.UTF8.GetBytes (value)); + } + + public void LeftPush(string key, byte [] value) + { + SendDataCommand (value, "LPUSH", key); + ExpectSuccess(); + } + + public void RightPush(string key, string value) + { + RightPush(key, System.Text.Encoding.UTF8.GetBytes (value)); + } + + public void RightPush(string key, byte [] value) + { + SendDataCommand (value, "RPUSH", key); + ExpectSuccess(); + } + + public int ListLength (string key) + { + return SendExpectInt ("LLEN", key); + } + + public byte[] ListIndex (string key, int index) + { + SendCommand ("LINDEX", key, index); + return ReadData (); + } + + public byte[] LeftPop(string key) + { + SendCommand ("LPOP", key); + return ReadData (); + } + + public byte[] RightPop(string key) + { + SendCommand ("RPOP", key); + return ReadData (); + } + #endregion + + #region Set commands + public bool AddToSet (string key, byte[] member) + { + return SendDataExpectInt(member, "SADD", key) > 0; + } + + public bool AddToSet (string key, string member) + { + return AddToSet (key, System.Text.Encoding.UTF8.GetBytes(member)); + } + + public int CardinalityOfSet (string key) + { + return SendExpectInt ("SCARD", key); + } + + public bool IsMemberOfSet (string key, byte[] member) + { + return SendDataExpectInt (member, "SISMEMBER", key) > 0; + } + + public bool IsMemberOfSet(string key, string member) + { + return IsMemberOfSet(key, System.Text.Encoding.UTF8.GetBytes(member)); + } + + public byte[][] GetMembersOfSet (string key) + { + return SendExpectDataArray ("SMEMBERS", key); + } + + public byte[] GetRandomMemberOfSet (string key) + { + return SendExpectData ("SRANDMEMBER", key); + } + + public byte[] PopRandomMemberOfSet (string key) + { + return SendExpectData ("SPOP", key); + } + + public bool RemoveFromSet (string key, byte[] member) + { + return SendDataExpectInt (member, "SREM", key) > 0; + } + + public bool RemoveFromSet (string key, string member) + { + return RemoveFromSet (key, System.Text.Encoding.UTF8.GetBytes(member)); + } + + public byte[][] GetUnionOfSets (params string[] keys) + { + if (keys == null) + throw new System.ArgumentNullException(); + + return SendExpectDataArray ("SUNION", keys); + + } + + void StoreSetCommands (string cmd, params string[] keys) + { + if (string.IsNullOrEmpty(cmd)) + throw new System.ArgumentNullException ("cmd"); + + if (keys == null) + throw new System.ArgumentNullException ("keys"); + + SendExpectSuccess (cmd, keys); + } + + public void StoreUnionOfSets (params string[] keys) + { + StoreSetCommands ("SUNIONSTORE", keys); + } + + public byte[][] GetIntersectionOfSets (params string[] keys) + { + if (keys == null) + throw new System.ArgumentNullException(); + + return SendExpectDataArray ("SINTER", keys); + } + + public void StoreIntersectionOfSets (params string[] keys) + { + StoreSetCommands ("SINTERSTORE", keys); + } + + public byte[][] GetDifferenceOfSets (params string[] keys) + { + if (keys == null) + throw new System.ArgumentNullException(); + + return SendExpectDataArray ("SDIFF", keys); + } + + public void StoreDifferenceOfSets (params string[] keys) + { + StoreSetCommands ("SDIFFSTORE", keys); + } + + public bool MoveMemberToSet (string srcKey, string destKey, byte[] member) + { + return SendDataExpectInt (member, "SMOVE", srcKey, destKey) > 0; + } + #endregion + + public void Dispose () + { + Dispose (true); + System.GC.SuppressFinalize (this); + } + + ~Redis () + { + Dispose (false); + } + + protected virtual void Dispose (bool disposing) + { + if (disposing){ + SendCommand ("QUIT"); + ExpectSuccess (); + socket.Close (); + socket = null; + } + } +} + +public class SortOptions { + public string Key { get; set; } + public bool Descending { get; set; } + public bool Lexographically { get; set; } + public System.Int32 LowerLimit { get; set; } + public System.Int32 UpperLimit { get; set; } + public string By { get; set; } + public string StoreInKey { get; set; } + public string Get { get; set; } + + public object [] ToArgs () + { + System.Collections.ArrayList args = new System.Collections.ArrayList(); + + if (LowerLimit != 0 || UpperLimit != 0) { + args.Add ("LIMIT"); + args.Add (LowerLimit); + args.Add (UpperLimit); + } + if (Lexographically) + args.Add("ALPHA"); + if (!string.IsNullOrEmpty (By)) { + args.Add("BY"); + args.Add(By); + } + if (!string.IsNullOrEmpty (Get)) { + args.Add("GET"); + args.Add(Get); + } + return args.ToArray (); + } +} diff --git a/redis-sharp.cs b/redis-sharp.cs deleted file mode 100644 index 2cb7477..0000000 --- a/redis-sharp.cs +++ /dev/null @@ -1,931 +0,0 @@ -// -// redis-sharp.cs: ECMA CLI Binding to the Redis key-value storage system -// -// Authors: -// Miguel de Icaza (miguel@gnome.org) -// -// Copyright 2010 Novell, Inc. -// -// Licensed under the same terms of reddis: new BSD license. -// -#define DEBUG - -using System; -using System.IO; -using System.Collections.Generic; -using System.Net.Sockets; -using System.Text; -using System.Diagnostics; -using System.Linq; - -public class Redis : IDisposable { - Socket socket; - BufferedStream bstream; - - public enum KeyType { - None, String, List, Set - } - - public class ResponseException : Exception { - public ResponseException (string code) : base ("Response error") - { - Code = code; - } - - public string Code { get; private set; } - } - - public Redis (string host, int port) - { - if (host == null) - throw new ArgumentNullException ("host"); - - Host = host; - Port = port; - SendTimeout = -1; - } - - public Redis (string host) : this (host, 6379) - { - } - - public Redis () : this ("localhost", 6379) - { - } - - public string Host { get; private set; } - public int Port { get; private set; } - public int RetryTimeout { get; set; } - public int RetryCount { get; set; } - public int SendTimeout { get; set; } - public string Password { get; set; } - - int db; - public int Db { - get { - return db; - } - - set { - db = value; - SendExpectSuccess ("SELECT", db); - } - } - - public string this [string key] { - get { return GetString (key); } - set { Set (key, value); } - } - - public void Set (string key, string value) - { - if (key == null) - throw new ArgumentNullException ("key"); - if (value == null) - throw new ArgumentNullException ("value"); - - Set (key, Encoding.UTF8.GetBytes (value)); - } - - public void Set (string key, byte [] value) - { - if (key == null) - throw new ArgumentNullException ("key"); - if (value == null) - throw new ArgumentNullException ("value"); - - if (value.Length > 1073741824) - throw new ArgumentException ("value exceeds 1G", "value"); - - if (!SendDataCommand (value, "SET", key)) - throw new Exception ("Unable to connect"); - ExpectSuccess (); - } - - public bool SetNX (string key, string value) - { - if (key == null) - throw new ArgumentNullException ("key"); - if (value == null) - throw new ArgumentNullException ("value"); - - return SetNX (key, Encoding.UTF8.GetBytes (value)); - } - - public bool SetNX (string key, byte [] value) - { - if (key == null) - throw new ArgumentNullException ("key"); - if (value == null) - throw new ArgumentNullException ("value"); - - if (value.Length > 1073741824) - throw new ArgumentException ("value exceeds 1G", "value"); - - return SendDataExpectInt (value, "SETNX", key) > 0 ? true : false; - } - - public void Set (IDictionary dict) - { - if (dict == null) - throw new ArgumentNullException ("dict"); - - Set (dict.ToDictionary(k => k.Key, v => Encoding.UTF8.GetBytes(v.Value))); - } - - public void Set (IDictionary dict) - { - if (dict == null) - throw new ArgumentNullException ("dict"); - - MSet (dict.Keys.ToArray (), dict.Values.ToArray ()); - } - - public void MSet (string [] keys, byte [][] values) - { - if (keys.Length != values.Length) - throw new ArgumentException ("keys and values must have the same size"); - - byte [] nl = Encoding.UTF8.GetBytes ("\r\n"); - MemoryStream ms = new MemoryStream (); - - for (int i = 0; i < keys.Length; i++) { - byte [] key = Encoding.UTF8.GetBytes(keys[i]); - byte [] val = values[i]; - byte [] kLength = Encoding.UTF8.GetBytes ("$" + key.Length + "\r\n"); - byte [] k = Encoding.UTF8.GetBytes (keys[i] + "\r\n"); - byte [] vLength = Encoding.UTF8.GetBytes ("$" + val.Length + "\r\n"); - ms.Write (kLength, 0, kLength.Length); - ms.Write (k, 0, k.Length); - ms.Write (vLength, 0, vLength.Length); - ms.Write (val, 0, val.Length); - ms.Write (nl, 0, nl.Length); - } - - SendDataRESP (ms.ToArray (), "*" + (keys.Length * 2 + 1) + "\r\n$4\r\nMSET\r\n"); - ExpectSuccess (); - } - - public byte [] Get (string key) - { - if (key == null) - throw new ArgumentNullException ("key"); - return SendExpectData ("GET", key); - } - - public string GetString (string key) - { - if (key == null) - throw new ArgumentNullException ("key"); - return Encoding.UTF8.GetString (Get (key)); - } - - public byte [][] Sort (SortOptions options) - { - return Sort (options.Key, options.StoreInKey, options.ToArgs()); - } - - public byte [][] Sort (string key, string destination, params object [] options) - { - if (key == null) - throw new ArgumentNullException ("key"); - - int offset = string.IsNullOrEmpty (destination) ? 1 : 3; - object [] args = new object [offset + options.Length]; - - args [0] = key; - Array.Copy (options, 0, args, offset, options.Length); - if (offset == 1) { - return SendExpectDataArray ("SORT", args); - } - else { - args [1] = "STORE"; - args [2] = destination; - int n = SendExpectInt ("SORT", args); - return new byte [n][]; - } - } - - public byte [] GetSet (string key, byte [] value) - { - if (key == null) - throw new ArgumentNullException ("key"); - if (value == null) - throw new ArgumentNullException ("value"); - - if (value.Length > 1073741824) - throw new ArgumentException ("value exceeds 1G", "value"); - - if (!SendDataCommand (value, "GETSET", key)) - throw new Exception ("Unable to connect"); - - return ReadData (); - } - - public string GetSet (string key, string value) - { - if (key == null) - throw new ArgumentNullException ("key"); - if (value == null) - throw new ArgumentNullException ("value"); - return Encoding.UTF8.GetString (GetSet (key, Encoding.UTF8.GetBytes (value))); - } - - string ReadLine () - { - StringBuilder sb = new StringBuilder (); - int c; - - while ((c = bstream.ReadByte ()) != -1){ - if (c == '\r') - continue; - if (c == '\n') - break; - sb.Append ((char) c); - } - return sb.ToString (); - } - - void Connect () - { - socket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - socket.NoDelay = true; - socket.SendTimeout = SendTimeout; - socket.Connect (Host, Port); - if (!socket.Connected){ - socket.Close (); - socket = null; - return; - } - bstream = new BufferedStream (new NetworkStream (socket), 16*1024); - - if (Password != null) - SendExpectSuccess ("AUTH", Password); - } - - byte [] end_data = new byte [] { (byte) '\r', (byte) '\n' }; - - bool SendDataCommand (byte [] data, string cmd, params object [] args) - { - string resp = "*" + (1 + args.Length + 1).ToString () + "\r\n"; - resp += "$" + cmd.Length + "\r\n" + cmd + "\r\n"; - foreach (object arg in args) { - string argStr = arg.ToString (); - int argStrLength = Encoding.UTF8.GetByteCount(argStr); - resp += "$" + argStrLength + "\r\n" + argStr + "\r\n"; - } - resp += "$" + data.Length + "\r\n"; - - return SendDataRESP (data, resp); - } - - bool SendDataRESP (byte [] data, string resp) - { - if (socket == null) - Connect (); - if (socket == null) - return false; - - byte [] r = Encoding.UTF8.GetBytes (resp); - try { - Log ("C", resp); - socket.Send (r); - if (data != null){ - socket.Send (data); - socket.Send (end_data); - } - } catch (SocketException){ - // timeout; - socket.Close (); - socket = null; - - return false; - } - return true; - } - - bool SendCommand (string cmd, params object [] args) - { - if (socket == null) - Connect (); - if (socket == null) - return false; - - string resp = "*" + (1 + args.Length).ToString () + "\r\n"; - resp += "$" + cmd.Length + "\r\n" + cmd + "\r\n"; - foreach (object arg in args) { - string argStr = arg.ToString (); - int argStrLength = Encoding.UTF8.GetByteCount(argStr); - resp += "$" + argStrLength + "\r\n" + argStr + "\r\n"; - } - - byte [] r = Encoding.UTF8.GetBytes (resp); - try { - Log ("C", resp); - socket.Send (r); - } catch (SocketException){ - // timeout; - socket.Close (); - socket = null; - - return false; - } - return true; - } - - [Conditional ("DEBUG")] - void Log (string id, string message) - { - Console.WriteLine(id + ": " + message.Trim().Replace("\r\n", " ")); - } - - void ExpectSuccess () - { - int c = bstream.ReadByte (); - if (c == -1) - throw new ResponseException ("No more data"); - - string s = ReadLine (); - Log ("S", (char)c + s); - if (c == '-') - throw new ResponseException (s.StartsWith ("ERR ") ? s.Substring (4) : s); - } - - void SendExpectSuccess (string cmd, params object [] args) - { - if (!SendCommand (cmd, args)) - throw new Exception ("Unable to connect"); - - ExpectSuccess (); - } - - int SendDataExpectInt (byte[] data, string cmd, params object [] args) - { - if (!SendDataCommand (data, cmd, args)) - throw new Exception ("Unable to connect"); - - int c = bstream.ReadByte (); - if (c == -1) - throw new ResponseException ("No more data"); - - string s = ReadLine (); - Log ("S", (char)c + s); - if (c == '-') - throw new ResponseException (s.StartsWith ("ERR ") ? s.Substring (4) : s); - if (c == ':'){ - int i; - if (int.TryParse (s, out i)) - return i; - } - throw new ResponseException ("Unknown reply on integer request: " + c + s); - } - - int SendExpectInt (string cmd, params object [] args) - { - if (!SendCommand (cmd, args)) - throw new Exception ("Unable to connect"); - - int c = bstream.ReadByte (); - if (c == -1) - throw new ResponseException ("No more data"); - - string s = ReadLine (); - Log ("S", (char)c + s); - if (c == '-') - throw new ResponseException (s.StartsWith ("ERR ") ? s.Substring (4) : s); - if (c == ':'){ - int i; - if (int.TryParse (s, out i)) - return i; - } - throw new ResponseException ("Unknown reply on integer request: " + c + s); - } - - string SendExpectString (string cmd, params object [] args) - { - if (!SendCommand (cmd, args)) - throw new Exception ("Unable to connect"); - - int c = bstream.ReadByte (); - if (c == -1) - throw new ResponseException ("No more data"); - - string s = ReadLine (); - Log ("S", (char)c + s); - if (c == '-') - throw new ResponseException (s.StartsWith ("ERR ") ? s.Substring (4) : s); - if (c == '+') - return s; - - throw new ResponseException ("Unknown reply on integer request: " + c + s); - } - - // - // This one does not throw errors - // - string SendGetString (string cmd, params object [] args) - { - if (!SendCommand (cmd, args)) - throw new Exception ("Unable to connect"); - - return ReadLine (); - } - - byte [] SendExpectData (string cmd, params object [] args) - { - if (!SendCommand (cmd, args)) - throw new Exception ("Unable to connect"); - - return ReadData (); - } - - byte [] ReadData () - { - string s = ReadLine (); - Log ("S", s); - if (s.Length == 0) - throw new ResponseException ("Zero length respose"); - - char c = s [0]; - if (c == '-') - throw new ResponseException (s.StartsWith ("-ERR ") ? s.Substring (5) : s.Substring (1)); - - if (c == '$'){ - if (s == "$-1") - return null; - int n; - - if (Int32.TryParse (s.Substring (1), out n)){ - byte [] retbuf = new byte [n]; - - int bytesRead = 0; - do { - int read = bstream.Read (retbuf, bytesRead, n - bytesRead); - if (read < 1) - throw new ResponseException("Invalid termination mid stream"); - bytesRead += read; - } - while (bytesRead < n); - if (bstream.ReadByte () != '\r' || bstream.ReadByte () != '\n') - throw new ResponseException ("Invalid termination"); - return retbuf; - } - throw new ResponseException ("Invalid length"); - } - - /* don't treat arrays here because only one element works -- use DataArray! - //returns the number of matches - if (c == '*') { - int n; - if (Int32.TryParse(s.Substring(1), out n)) - return n <= 0 ? new byte [0] : ReadData(); - - throw new ResponseException ("Unexpected length parameter" + r); - } - */ - - throw new ResponseException ("Unexpected reply: " + s); - } - - public bool ContainsKey (string key) - { - if (key == null) - throw new ArgumentNullException ("key"); - return SendExpectInt ("EXISTS", key) == 1; - } - - public bool Remove (string key) - { - if (key == null) - throw new ArgumentNullException ("key"); - return SendExpectInt ("DEL", key) == 1; - } - - public int Remove (params string [] args) - { - if (args == null) - throw new ArgumentNullException ("args"); - return SendExpectInt ("DEL", args); - } - - public int Increment (string key) - { - if (key == null) - throw new ArgumentNullException ("key"); - return SendExpectInt ("INCR", key); - } - - public int Increment (string key, int count) - { - if (key == null) - throw new ArgumentNullException ("key"); - return SendExpectInt ("INCRBY", key, count); - } - - public int Decrement (string key) - { - if (key == null) - throw new ArgumentNullException ("key"); - return SendExpectInt ("DECR", key); - } - - public int Decrement (string key, int count) - { - if (key == null) - throw new ArgumentNullException ("key"); - return SendExpectInt ("DECRBY", key, count); - } - - public KeyType TypeOf (string key) - { - if (key == null) - throw new ArgumentNullException ("key"); - switch (SendExpectString ("TYPE", key)) { - case "none": - return KeyType.None; - case "string": - return KeyType.String; - case "set": - return KeyType.Set; - case "list": - return KeyType.List; - } - throw new ResponseException ("Invalid value"); - } - - public string RandomKey () - { - return SendExpectString ("RANDOMKEY"); - } - - public bool Rename (string oldKeyname, string newKeyname) - { - if (oldKeyname == null) - throw new ArgumentNullException ("oldKeyname"); - if (newKeyname == null) - throw new ArgumentNullException ("newKeyname"); - return SendGetString ("RENAME", oldKeyname, newKeyname) [0] == '+'; - } - - public bool Expire (string key, int seconds) - { - if (key == null) - throw new ArgumentNullException ("key"); - return SendExpectInt ("EXPIRE", key, seconds) == 1; - } - - public bool ExpireAt (string key, int time) - { - if (key == null) - throw new ArgumentNullException ("key"); - return SendExpectInt ("EXPIREAT", key, time) == 1; - } - - public int TimeToLive (string key) - { - if (key == null) - throw new ArgumentNullException ("key"); - return SendExpectInt ("TTL", key); - } - - public int DbSize { - get { - return SendExpectInt ("DBSIZE"); - } - } - - public void Save () - { - SendExpectSuccess ("SAVE"); - } - - public void BackgroundSave () - { - SendExpectSuccess ("BGSAVE"); - } - - public void Shutdown () - { - SendCommand ("SHUTDOWN"); - try { - // the server may return an error - string s = ReadLine (); - Log ("S", s); - if (s.Length == 0) - throw new ResponseException ("Zero length respose"); - throw new ResponseException (s.StartsWith ("-ERR ") ? s.Substring (5) : s.Substring (1)); - } catch (IOException) { - // this is the expected good result - socket.Close (); - socket = null; - } - } - - public void FlushAll () - { - SendExpectSuccess ("FLUSHALL"); - } - - public void FlushDb () - { - SendExpectSuccess ("FLUSHDB"); - } - - const long UnixEpoch = 621355968000000000L; - - public DateTime LastSave { - get { - int t = SendExpectInt ("LASTSAVE"); - - return new DateTime (UnixEpoch) + TimeSpan.FromSeconds (t); - } - } - - public Dictionary GetInfo () - { - byte [] r = SendExpectData ("INFO"); - var dict = new Dictionary(); - - foreach (var line in Encoding.UTF8.GetString (r).Split ('\n')){ - int p = line.IndexOf (':'); - if (p == -1) - continue; - dict.Add (line.Substring (0, p), line.Substring (p+1)); - } - return dict; - } - - public string [] Keys { - get { - return GetKeys("*"); - } - } - - public string [] GetKeys (string pattern) - { - if (pattern == null) - throw new ArgumentNullException ("pattern"); - - return SendExpectStringArray ("KEYS", pattern); - } - - public byte [][] MGet (params string [] keys) - { - if (keys == null) - throw new ArgumentNullException ("keys"); - if (keys.Length == 0) - throw new ArgumentException ("keys"); - - return SendExpectDataArray ("MGET", keys); - } - - - public string [] SendExpectStringArray (string cmd, params object [] args) - { - byte [][] reply = SendExpectDataArray (cmd, args); - string [] keys = new string [reply.Length]; - for (int i = 0; i < reply.Length; i++) - keys[i] = Encoding.UTF8.GetString (reply[i]); - return keys; - } - - public byte[][] SendExpectDataArray (string cmd, params object [] args) - { - if (!SendCommand (cmd, args)) - throw new Exception("Unable to connect"); - int c = bstream.ReadByte(); - if (c == -1) - throw new ResponseException("No more data"); - - string s = ReadLine(); - Log("S", (char)c + s); - if (c == '-') - throw new ResponseException(s.StartsWith("ERR ") ? s.Substring(4) : s); - if (c == '*') { - int count; - if (int.TryParse (s, out count)) { - byte [][] result = new byte [count][]; - - for (int i = 0; i < count; i++) - result[i] = ReadData(); - - return result; - } - } - throw new ResponseException("Unknown reply on multi-request: " + c + s); - } - - #region List commands - public byte[][] ListRange(string key, int start, int end) - { - return SendExpectDataArray ("LRANGE", key, start, end); - } - - public void LeftPush(string key, string value) - { - LeftPush(key, Encoding.UTF8.GetBytes (value)); - } - - public void LeftPush(string key, byte [] value) - { - SendDataCommand (value, "LPUSH", key); - ExpectSuccess(); - } - - public void RightPush(string key, string value) - { - RightPush(key, Encoding.UTF8.GetBytes (value)); - } - - public void RightPush(string key, byte [] value) - { - SendDataCommand (value, "RPUSH", key); - ExpectSuccess(); - } - - public int ListLength (string key) - { - return SendExpectInt ("LLEN", key); - } - - public byte[] ListIndex (string key, int index) - { - SendCommand ("LINDEX", key, index); - return ReadData (); - } - - public byte[] LeftPop(string key) - { - SendCommand ("LPOP", key); - return ReadData (); - } - - public byte[] RightPop(string key) - { - SendCommand ("RPOP", key); - return ReadData (); - } - #endregion - - #region Set commands - public bool AddToSet (string key, byte[] member) - { - return SendDataExpectInt(member, "SADD", key) > 0; - } - - public bool AddToSet (string key, string member) - { - return AddToSet (key, Encoding.UTF8.GetBytes(member)); - } - - public int CardinalityOfSet (string key) - { - return SendExpectInt ("SCARD", key); - } - - public bool IsMemberOfSet (string key, byte[] member) - { - return SendDataExpectInt (member, "SISMEMBER", key) > 0; - } - - public bool IsMemberOfSet(string key, string member) - { - return IsMemberOfSet(key, Encoding.UTF8.GetBytes(member)); - } - - public byte[][] GetMembersOfSet (string key) - { - return SendExpectDataArray ("SMEMBERS", key); - } - - public byte[] GetRandomMemberOfSet (string key) - { - return SendExpectData ("SRANDMEMBER", key); - } - - public byte[] PopRandomMemberOfSet (string key) - { - return SendExpectData ("SPOP", key); - } - - public bool RemoveFromSet (string key, byte[] member) - { - return SendDataExpectInt (member, "SREM", key) > 0; - } - - public bool RemoveFromSet (string key, string member) - { - return RemoveFromSet (key, Encoding.UTF8.GetBytes(member)); - } - - public byte[][] GetUnionOfSets (params string[] keys) - { - if (keys == null) - throw new ArgumentNullException(); - - return SendExpectDataArray ("SUNION", keys); - - } - - void StoreSetCommands (string cmd, params string[] keys) - { - if (String.IsNullOrEmpty(cmd)) - throw new ArgumentNullException ("cmd"); - - if (keys == null) - throw new ArgumentNullException ("keys"); - - SendExpectSuccess (cmd, keys); - } - - public void StoreUnionOfSets (params string[] keys) - { - StoreSetCommands ("SUNIONSTORE", keys); - } - - public byte[][] GetIntersectionOfSets (params string[] keys) - { - if (keys == null) - throw new ArgumentNullException(); - - return SendExpectDataArray ("SINTER", keys); - } - - public void StoreIntersectionOfSets (params string[] keys) - { - StoreSetCommands ("SINTERSTORE", keys); - } - - public byte[][] GetDifferenceOfSets (params string[] keys) - { - if (keys == null) - throw new ArgumentNullException(); - - return SendExpectDataArray ("SDIFF", keys); - } - - public void StoreDifferenceOfSets (params string[] keys) - { - StoreSetCommands ("SDIFFSTORE", keys); - } - - public bool MoveMemberToSet (string srcKey, string destKey, byte[] member) - { - return SendDataExpectInt (member, "SMOVE", srcKey, destKey) > 0; - } - #endregion - - public void Dispose () - { - Dispose (true); - GC.SuppressFinalize (this); - } - - ~Redis () - { - Dispose (false); - } - - protected virtual void Dispose (bool disposing) - { - if (disposing){ - SendCommand ("QUIT"); - ExpectSuccess (); - socket.Close (); - socket = null; - } - } -} - -public class SortOptions { - public string Key { get; set; } - public bool Descending { get; set; } - public bool Lexographically { get; set; } - public Int32 LowerLimit { get; set; } - public Int32 UpperLimit { get; set; } - public string By { get; set; } - public string StoreInKey { get; set; } - public string Get { get; set; } - - public object [] ToArgs () - { - System.Collections.ArrayList args = new System.Collections.ArrayList(); - - if (LowerLimit != 0 || UpperLimit != 0) { - args.Add ("LIMIT"); - args.Add (LowerLimit); - args.Add (UpperLimit); - } - if (Lexographically) - args.Add("ALPHA"); - if (!string.IsNullOrEmpty (By)) { - args.Add("BY"); - args.Add(By); - } - if (!string.IsNullOrEmpty (Get)) { - args.Add("GET"); - args.Add(Get); - } - return args.ToArray (); - } -} From 451c77da07b594e2d529603def5d038b0a867c03 Mon Sep 17 00:00:00 2001 From: Giovanni De Gasperis Date: Mon, 22 Sep 2014 21:59:33 +0200 Subject: [PATCH 03/19] no need to have a Makefile. Just copy/paste into a OpenSim script --- Makefile | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 Makefile diff --git a/Makefile b/Makefile deleted file mode 100644 index 48e0483..0000000 --- a/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -run: test.exe - mono --debug test.exe - -test.exe: test.cs redis-sharp.cs Makefile - gmcs -debug test.cs redis-sharp.cs \ No newline at end of file From fc432db5d1ca979feda1e63ab69f876899a14fe1 Mon Sep 17 00:00:00 2001 From: Giovanni De Gasperis Date: Mon, 22 Sep 2014 21:59:55 +0200 Subject: [PATCH 04/19] no need of a Project file --- RedisSharp.csproj | 55 ----------------------------------------------- 1 file changed, 55 deletions(-) delete mode 100644 RedisSharp.csproj diff --git a/RedisSharp.csproj b/RedisSharp.csproj deleted file mode 100644 index dc160bf..0000000 --- a/RedisSharp.csproj +++ /dev/null @@ -1,55 +0,0 @@ - - - - Debug - AnyCPU - 9.0.30729 - 2.0 - {165E0505-F29B-4ED4-A004-DD6472D4D430} - Library - Properties - RedisSharp - RedisSharp - v3.5 - 512 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - 3.5 - - - - - - - - - - - - - \ No newline at end of file From 5621cad36c79494f5e5f2d5150bc7bca1d729a50 Mon Sep 17 00:00:00 2001 From: Giovanni De Gasperis Date: Mon, 22 Sep 2014 22:00:16 +0200 Subject: [PATCH 05/19] no need of a solution file --- RedisSharp.sln | 26 -------------------------- 1 file changed, 26 deletions(-) delete mode 100644 RedisSharp.sln diff --git a/RedisSharp.sln b/RedisSharp.sln deleted file mode 100644 index 8e5b60f..0000000 --- a/RedisSharp.sln +++ /dev/null @@ -1,26 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 10.00 -# Visual C# Express 2008 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RedisSharp", "RedisSharp.csproj", "{165E0505-F29B-4ED4-A004-DD6472D4D430}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RedisTest", "RedisTest\RedisTest.csproj", "{04B4656A-705E-4175-B768-FFEFDF2A075F}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {165E0505-F29B-4ED4-A004-DD6472D4D430}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {165E0505-F29B-4ED4-A004-DD6472D4D430}.Debug|Any CPU.Build.0 = Debug|Any CPU - {165E0505-F29B-4ED4-A004-DD6472D4D430}.Release|Any CPU.ActiveCfg = Release|Any CPU - {165E0505-F29B-4ED4-A004-DD6472D4D430}.Release|Any CPU.Build.0 = Release|Any CPU - {04B4656A-705E-4175-B768-FFEFDF2A075F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {04B4656A-705E-4175-B768-FFEFDF2A075F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {04B4656A-705E-4175-B768-FFEFDF2A075F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {04B4656A-705E-4175-B768-FFEFDF2A075F}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal From e3a5f37b71c4add687f8c2f66fba8ed6ca7b884e Mon Sep 17 00:00:00 2001 From: Giovanni De Gasperis Date: Mon, 22 Sep 2014 22:00:51 +0200 Subject: [PATCH 06/19] Delete RedisTest.csproj --- RedisTest/RedisTest.csproj | 59 -------------------------------------- 1 file changed, 59 deletions(-) delete mode 100644 RedisTest/RedisTest.csproj diff --git a/RedisTest/RedisTest.csproj b/RedisTest/RedisTest.csproj deleted file mode 100644 index 0b264f8..0000000 --- a/RedisTest/RedisTest.csproj +++ /dev/null @@ -1,59 +0,0 @@ - - - - Debug - AnyCPU - 9.0.30729 - 2.0 - {04B4656A-705E-4175-B768-FFEFDF2A075F} - Exe - Properties - RedisTest - RedisTest - v3.5 - 512 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - 3.5 - - - - - - - - - - - {165E0505-F29B-4ED4-A004-DD6472D4D430} - RedisSharp - - - - - \ No newline at end of file From 327792836d69000927f1be1e0e88aa2c94dadace Mon Sep 17 00:00:00 2001 From: Giovanni De Gasperis Date: Mon, 22 Sep 2014 22:01:07 +0200 Subject: [PATCH 07/19] Delete AssemblyInfo.cs --- RedisTest/Properties/AssemblyInfo.cs | 36 ---------------------------- 1 file changed, 36 deletions(-) delete mode 100644 RedisTest/Properties/AssemblyInfo.cs diff --git a/RedisTest/Properties/AssemblyInfo.cs b/RedisTest/Properties/AssemblyInfo.cs deleted file mode 100644 index 7d03141..0000000 --- a/RedisTest/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("RedisTest")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Microsoft")] -[assembly: AssemblyProduct("RedisTest")] -[assembly: AssemblyCopyright("Copyright © Microsoft 2010")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("e7313840-f768-43c6-b332-c790983b1abf")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] From 3ea63a98b3a9fdfe9ac0711e2f89d226413c2901 Mon Sep 17 00:00:00 2001 From: Giovanni De Gasperis Date: Mon, 22 Sep 2014 22:04:05 +0200 Subject: [PATCH 08/19] this file is just for reference --- test.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test.cs b/test.cs index c9fca03..aab34c5 100644 --- a/test.cs +++ b/test.cs @@ -1,3 +1,5 @@ +// this file is here just as a reference, not usable in a OpenSim C# script + using System; using System.Text; using System.Collections.Generic; From b48850648f0cbdfe62ff77e7261faecafa5f3f18 Mon Sep 17 00:00:00 2001 From: Giovanni De Gasperis Date: Mon, 22 Sep 2014 22:05:26 +0200 Subject: [PATCH 09/19] Update README --- README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README b/README index 2e78855..894e98c 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -This is a C#/.NET client binding for the Redis server, runnable in a [OpenSim](http://opensimulator.org) script: +This is a C#/.NET client binding for the Redis server, runnable in a (OpenSim)[http://opensimulator.org] script: The code is licensed under the same terms that the Redis server is, the new BSD license From 233ce1c9ff6fbbf2b3687403a63adec1b1ec57e5 Mon Sep 17 00:00:00 2001 From: Giovanni De Gasperis Date: Mon, 22 Sep 2014 22:09:28 +0200 Subject: [PATCH 10/19] Update README --- README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README b/README index 894e98c..2e78855 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -This is a C#/.NET client binding for the Redis server, runnable in a (OpenSim)[http://opensimulator.org] script: +This is a C#/.NET client binding for the Redis server, runnable in a [OpenSim](http://opensimulator.org) script: The code is licensed under the same terms that the Redis server is, the new BSD license From e8e43caba3d2edd3e9c6c14e99f79cc698c7f5bb Mon Sep 17 00:00:00 2001 From: Giovanni De Gasperis Date: Mon, 22 Sep 2014 22:11:06 +0200 Subject: [PATCH 11/19] OpenSim instructions --- README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README b/README index 2e78855..d8af6aa 100644 --- a/README +++ b/README @@ -3,4 +3,4 @@ This is a C#/.NET client binding for the Redis server, runnable in a [OpenSim](h The code is licensed under the same terms that the Redis server is, the new BSD license -The binding lives in the redis-sharp.cs file, a sample test file can be found on test.cs +To use it, cut and paste the full code in a OpenSim C# script From 5a7681c37ec7f6993d934e29dced5041a41dd6fc Mon Sep 17 00:00:00 2001 From: Giovanni De Gasperis Date: Mon, 22 Sep 2014 22:58:03 +0200 Subject: [PATCH 12/19] added specific OpenSim test code --- os-redis-sharp.cs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/os-redis-sharp.cs b/os-redis-sharp.cs index 833e63e..edf1843 100644 --- a/os-redis-sharp.cs +++ b/os-redis-sharp.cs @@ -925,3 +925,30 @@ public object [] ToArgs () return args.ToArray (); } } + +// just to output a string to the viewer console +void debug(string msg){ + llOwnerSay("debug: "+msg); +} + +// MAIN Script that uses the Redis server + +string message = "Hello avatar!"; +string REDIS_IP = "192.168.1.111"; + +public void default_event_state_entry() +{ + debug(message); +} + +public void default_event_touch_start( + LSL_Types.LSLInteger total_number) +{ + Redis R = new Redis(REDIS_IP); + var info = R.GetInfo (); + foreach (var k in info.Keys) { + llSay(0, (string)k+" "+ info[k]); + } + string value = R.GetString("T3:status"); + debug(value); +} From 1fd771aa72810ebab1139118a5fb3727f6da8c54 Mon Sep 17 00:00:00 2001 From: Giovanni De Gasperis Date: Mon, 22 Sep 2014 23:30:06 +0200 Subject: [PATCH 13/19] cleanup code --- os-redis-sharp.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/os-redis-sharp.cs b/os-redis-sharp.cs index edf1843..1641f45 100644 --- a/os-redis-sharp.cs +++ b/os-redis-sharp.cs @@ -949,6 +949,4 @@ public void default_event_touch_start( foreach (var k in info.Keys) { llSay(0, (string)k+" "+ info[k]); } - string value = R.GetString("T3:status"); - debug(value); } From 233b290aab97a7f4629f0b44f9d0151aea7924d8 Mon Sep 17 00:00:00 2001 From: Giovanni De Gasperis Date: Mon, 22 Sep 2014 23:46:25 +0200 Subject: [PATCH 14/19] debug version --- os-redis-sharp.cs => os-redis-sharp-debug.cs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename os-redis-sharp.cs => os-redis-sharp-debug.cs (100%) diff --git a/os-redis-sharp.cs b/os-redis-sharp-debug.cs similarity index 100% rename from os-redis-sharp.cs rename to os-redis-sharp-debug.cs From d69b9a1be2742465bcba5a1d6fe70ef7ce6eccc0 Mon Sep 17 00:00:00 2001 From: Giovanni De Gasperis Date: Mon, 22 Sep 2014 23:47:04 +0200 Subject: [PATCH 15/19] no debug on OpenSim console --- os-redis-sharp.cs | 941 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 941 insertions(+) create mode 100644 os-redis-sharp.cs diff --git a/os-redis-sharp.cs b/os-redis-sharp.cs new file mode 100644 index 0000000..2dffec9 --- /dev/null +++ b/os-redis-sharp.cs @@ -0,0 +1,941 @@ +//c# +// +// redis-sharp.cs: ECMA CLI Binding to the Redis key-value storage system +// +// Authors: +// Miguel de Icaza (miguel@gnome.org) +// Giovanni De Gasperis (giovanni@giodegas.it) +// +// Licensed under the same terms of reddis: new BSD license. +// + +public class Redis : System.IDisposable { + System.Net.Sockets.Socket socket; + System.IO.BufferedStream bstream; + + public enum KeyType { + None, String, List, Set + } + + public class ResponseException : System.Exception { + public ResponseException (string code) : base ("Response error") + { + Code = code; + } + + public string Code { get; private set; } + } + + public Redis (string host, int port) + { + if (host == null) + throw new System.ArgumentNullException ("host"); + + Host = host; + Port = port; + SendTimeout = -1; + } + + public Redis (string host) : this (host, 6379) + { + } + + public Redis () : this ("localhost", 6379) + { + } + + public string Host { get; private set; } + public int Port { get; private set; } + public int RetryTimeout { get; set; } + public int RetryCount { get; set; } + public int SendTimeout { get; set; } + public string Password { get; set; } + + int db; + public int Db { + get { + return db; + } + + set { + db = value; + SendExpectSuccess ("SELECT", db); + } + } + + public string this [string key] { + get { return GetString (key); } + set { Set (key, value); } + } + + public void Set (string key, string value) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + if (value == null) + throw new System.ArgumentNullException ("value"); + + Set (key, System.Text.Encoding.UTF8.GetBytes (value)); + } + + public void Set (string key, byte [] value) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + if (value == null) + throw new System.ArgumentNullException ("value"); + + if (value.Length > 1073741824) + throw new System.ArgumentException ("value exceeds 1G", "value"); + + if (!SendDataCommand (value, "SET", key)) + throw new System.Exception ("Unable to connect"); + ExpectSuccess (); + } + + public bool SetNX (string key, string value) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + if (value == null) + throw new System.ArgumentNullException ("value"); + + return SetNX (key, System.Text.Encoding.UTF8.GetBytes (value)); + } + + public bool SetNX (string key, byte [] value) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + if (value == null) + throw new System.ArgumentNullException ("value"); + + if (value.Length > 1073741824) + throw new System.ArgumentException ("value exceeds 1G", "value"); + + return SendDataExpectInt (value, "SETNX", key) > 0 ? true : false; + } + + /*public void Set (System.Linq.IDictionary dict) + { + if (dict == null) + throw new System.ArgumentNullException ("dict"); + + Set (dict.ToDictionary(k => k.Key, v => System.Text.Encoding.UTF8.GetBytes(v.Value))); + } + + public void Set (System.Linq.IDictionary dict) + { + if (dict == null) + throw new System.ArgumentNullException ("dict"); + + MSet (dict.Keys.ToArray (), dict.Values.ToArray ()); + }*/ + + public void MSet (string [] keys, byte [][] values) + { + if (keys.Length != values.Length) + throw new System.ArgumentException ("keys and values must have the same size"); + + byte [] nl = System.Text.Encoding.UTF8.GetBytes ("\r\n"); + System.IO.MemoryStream ms = new System.IO.MemoryStream (); + + for (int i = 0; i < keys.Length; i++) { + byte [] key = System.Text.Encoding.UTF8.GetBytes(keys[i]); + byte [] val = values[i]; + byte [] kLength = System.Text.Encoding.UTF8.GetBytes ("$" + key.Length + "\r\n"); + byte [] k = System.Text.Encoding.UTF8.GetBytes (keys[i] + "\r\n"); + byte [] vLength = System.Text.Encoding.UTF8.GetBytes ("$" + val.Length + "\r\n"); + ms.Write (kLength, 0, kLength.Length); + ms.Write (k, 0, k.Length); + ms.Write (vLength, 0, vLength.Length); + ms.Write (val, 0, val.Length); + ms.Write (nl, 0, nl.Length); + } + + SendDataRESP (ms.ToArray (), "*" + (keys.Length * 2 + 1) + "\r\n$4\r\nMSET\r\n"); + ExpectSuccess (); + } + + public byte [] Get (string key) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + return SendExpectData ("GET", key); + } + + public string GetString (string key) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + return System.Text.Encoding.UTF8.GetString (Get (key)); + } + + public byte [][] Sort (SortOptions options) + { + return Sort (options.Key, options.StoreInKey, options.ToArgs()); + } + + public byte [][] Sort (string key, string destination, params object [] options) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + + int offset = string.IsNullOrEmpty (destination) ? 1 : 3; + object [] args = new object [offset + options.Length]; + + args [0] = key; + System.Array.Copy (options, 0, args, offset, options.Length); + if (offset == 1) { + return SendExpectDataArray ("SORT", args); + } + else { + args [1] = "STORE"; + args [2] = destination; + int n = SendExpectInt ("SORT", args); + return new byte [n][]; + } + } + + public byte [] GetSet (string key, byte [] value) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + if (value == null) + throw new System.ArgumentNullException ("value"); + + if (value.Length > 1073741824) + throw new System.ArgumentException ("value exceeds 1G", "value"); + + if (!SendDataCommand (value, "GETSET", key)) + throw new System.Exception ("Unable to connect"); + + return ReadData (); + } + + public string GetSet (string key, string value) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + if (value == null) + throw new System.ArgumentNullException ("value"); + return System.Text.Encoding.UTF8.GetString (GetSet (key, System.Text.Encoding.UTF8.GetBytes (value))); + } + + string ReadLine () + { + System.Text.StringBuilder sb = new System.Text.StringBuilder (); + int c; + + while ((c = bstream.ReadByte ()) != -1){ + if (c == '\r') + continue; + if (c == '\n') + break; + sb.Append ((char) c); + } + return sb.ToString (); + } + + void Connect () + { + socket = new System.Net.Sockets.Socket (System.Net.Sockets.AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp); + socket.NoDelay = true; + socket.SendTimeout = SendTimeout; + socket.Connect (Host, Port); + if (!socket.Connected){ + socket.Close (); + socket = null; + return; + } + bstream = new System.IO.BufferedStream (new System.Net.Sockets.NetworkStream (socket), 16*1024); + + if (Password != null) + SendExpectSuccess ("AUTH", Password); + } + + byte [] end_data = new byte [] { (byte) '\r', (byte) '\n' }; + + bool SendDataCommand (byte [] data, string cmd, params object [] args) + { + string resp = "*" + (1 + args.Length + 1).ToString () + "\r\n"; + resp += "$" + cmd.Length + "\r\n" + cmd + "\r\n"; + foreach (object arg in args) { + string argStr = arg.ToString (); + int argStrLength = System.Text.Encoding.UTF8.GetByteCount(argStr); + resp += "$" + argStrLength + "\r\n" + argStr + "\r\n"; + } + resp += "$" + data.Length + "\r\n"; + + return SendDataRESP (data, resp); + } + + bool SendDataRESP (byte [] data, string resp) + { + if (socket == null) + Connect (); + if (socket == null) + return false; + + byte [] r = System.Text.Encoding.UTF8.GetBytes (resp); + try { + socket.Send (r); + if (data != null){ + socket.Send (data); + socket.Send (end_data); + } + } catch (System.Net.Sockets.SocketException){ + // timeout; + socket.Close (); + socket = null; + + return false; + } + return true; + } + + bool SendCommand (string cmd, params object [] args) + { + if (socket == null) + Connect (); + if (socket == null) + return false; + + string resp = "*" + (1 + args.Length).ToString () + "\r\n"; + resp += "$" + cmd.Length + "\r\n" + cmd + "\r\n"; + foreach (object arg in args) { + string argStr = arg.ToString (); + int argStrLength = System.Text.Encoding.UTF8.GetByteCount(argStr); + resp += "$" + argStrLength + "\r\n" + argStr + "\r\n"; + } + + byte [] r = System.Text.Encoding.UTF8.GetBytes (resp); + try { + socket.Send (r); + } catch (System.Net.Sockets.SocketException){ + // timeout; + socket.Close (); + socket = null; + + return false; + } + return true; + } + + void ExpectSuccess () + { + int c = bstream.ReadByte (); + if (c == -1) + throw new ResponseException ("No more data"); + + string s = ReadLine (); + if (c == '-') + throw new ResponseException (s.StartsWith ("ERR ") ? s.Substring (4) : s); + } + + void SendExpectSuccess (string cmd, params object [] args) + { + if (!SendCommand (cmd, args)) + throw new System.Exception ("Unable to connect"); + + ExpectSuccess (); + } + + int SendDataExpectInt (byte[] data, string cmd, params object [] args) + { + if (!SendDataCommand (data, cmd, args)) + throw new System.Exception ("Unable to connect"); + + int c = bstream.ReadByte (); + if (c == -1) + throw new ResponseException ("No more data"); + + string s = ReadLine (); + if (c == '-') + throw new ResponseException (s.StartsWith ("ERR ") ? s.Substring (4) : s); + if (c == ':'){ + int i; + if (int.TryParse (s, out i)) + return i; + } + throw new ResponseException ("Unknown reply on integer request: " + c + s); + } + + int SendExpectInt (string cmd, params object [] args) + { + if (!SendCommand (cmd, args)) + throw new System.Exception ("Unable to connect"); + + int c = bstream.ReadByte (); + if (c == -1) + throw new ResponseException ("No more data"); + + string s = ReadLine (); + if (c == '-') + throw new ResponseException (s.StartsWith ("ERR ") ? s.Substring (4) : s); + if (c == ':'){ + int i; + if (int.TryParse (s, out i)) + return i; + } + throw new ResponseException ("Unknown reply on integer request: " + c + s); + } + + string SendExpectString (string cmd, params object [] args) + { + if (!SendCommand (cmd, args)) + throw new System.Exception ("Unable to connect"); + + int c = bstream.ReadByte (); + if (c == -1) + throw new ResponseException ("No more data"); + + string s = ReadLine (); + if (c == '-') + throw new ResponseException (s.StartsWith ("ERR ") ? s.Substring (4) : s); + if (c == '+') + return s; + + throw new ResponseException ("Unknown reply on integer request: " + c + s); + } + + // + // This one does not throw errors + // + string SendGetString (string cmd, params object [] args) + { + if (!SendCommand (cmd, args)) + throw new System.Exception ("Unable to connect"); + + return ReadLine (); + } + + byte [] SendExpectData (string cmd, params object [] args) + { + if (!SendCommand (cmd, args)) + throw new System.Exception ("Unable to connect"); + + return ReadData (); + } + + byte [] ReadData () + { + string s = ReadLine (); + if (s.Length == 0) + throw new ResponseException ("Zero length respose"); + + char c = s [0]; + if (c == '-') + throw new ResponseException (s.StartsWith ("-ERR ") ? s.Substring (5) : s.Substring (1)); + + if (c == '$'){ + if (s == "$-1") + return null; + int n; + + if (System.Int32.TryParse (s.Substring (1), out n)){ + byte [] retbuf = new byte [n]; + + int bytesRead = 0; + do { + int read = bstream.Read (retbuf, bytesRead, n - bytesRead); + if (read < 1) + throw new ResponseException("Invalid termination mid stream"); + bytesRead += read; + } + while (bytesRead < n); + if (bstream.ReadByte () != '\r' || bstream.ReadByte () != '\n') + throw new ResponseException ("Invalid termination"); + return retbuf; + } + throw new ResponseException ("Invalid length"); + } + + /* don't treat arrays here because only one element works -- use DataArray! + //returns the number of matches + if (c == '*') { + int n; + if (Int32.TryParse(s.Substring(1), out n)) + return n <= 0 ? new byte [0] : ReadData(); + + throw new ResponseException ("Unexpected length parameter" + r); + } + */ + + throw new ResponseException ("Unexpected reply: " + s); + } + + public bool ContainsKey (string key) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + return SendExpectInt ("EXISTS", key) == 1; + } + + public bool Remove (string key) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + return SendExpectInt ("DEL", key) == 1; + } + + public int Remove (params string [] args) + { + if (args == null) + throw new System.ArgumentNullException ("args"); + return SendExpectInt ("DEL", args); + } + + public int Increment (string key) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + return SendExpectInt ("INCR", key); + } + + public int Increment (string key, int count) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + return SendExpectInt ("INCRBY", key, count); + } + + public int Decrement (string key) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + return SendExpectInt ("DECR", key); + } + + public int Decrement (string key, int count) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + return SendExpectInt ("DECRBY", key, count); + } + + public KeyType TypeOf (string key) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + switch (SendExpectString ("TYPE", key)) { + case "none": + return KeyType.None; + case "string": + return KeyType.String; + case "set": + return KeyType.Set; + case "list": + return KeyType.List; + } + throw new ResponseException ("Invalid value"); + } + + public string RandomKey () + { + return SendExpectString ("RANDOMKEY"); + } + + public bool Rename (string oldKeyname, string newKeyname) + { + if (oldKeyname == null) + throw new System.ArgumentNullException ("oldKeyname"); + if (newKeyname == null) + throw new System.ArgumentNullException ("newKeyname"); + return SendGetString ("RENAME", oldKeyname, newKeyname) [0] == '+'; + } + + public bool Expire (string key, int seconds) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + return SendExpectInt ("EXPIRE", key, seconds) == 1; + } + + public bool ExpireAt (string key, int time) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + return SendExpectInt ("EXPIREAT", key, time) == 1; + } + + public int TimeToLive (string key) + { + if (key == null) + throw new System.ArgumentNullException ("key"); + return SendExpectInt ("TTL", key); + } + + public int DbSize { + get { + return SendExpectInt ("DBSIZE"); + } + } + + public void Save () + { + SendExpectSuccess ("SAVE"); + } + + public void BackgroundSave () + { + SendExpectSuccess ("BGSAVE"); + } + + public void Shutdown () + { + SendCommand ("SHUTDOWN"); + try { + // the server may return an error + string s = ReadLine (); + if (s.Length == 0) + throw new ResponseException ("Zero length respose"); + throw new ResponseException (s.StartsWith ("-ERR ") ? s.Substring (5) : s.Substring (1)); + } catch (System.IO.IOException) { + // this is the expected good result + socket.Close (); + socket = null; + } + } + + public void FlushAll () + { + SendExpectSuccess ("FLUSHALL"); + } + + public void FlushDb () + { + SendExpectSuccess ("FLUSHDB"); + } + + const long UnixEpoch = 621355968000000000L; + + public System.DateTime LastSave { + get { + int t = SendExpectInt ("LASTSAVE"); + + return new System.DateTime (UnixEpoch) + System.TimeSpan.FromSeconds (t); + } + } + + public Dictionary GetInfo () + { + byte [] r = SendExpectData ("INFO"); + var dict = new Dictionary(); + + foreach (var line in System.Text.Encoding.UTF8.GetString (r).Split ('\n')){ + int p = line.IndexOf (':'); + if (p == -1) + continue; + dict.Add (line.Substring (0, p), line.Substring (p+1)); + } + return dict; + } + + public string [] Keys { + get { + return GetKeys("*"); + } + } + + public string [] GetKeys (string pattern) + { + if (pattern == null) + throw new System.ArgumentNullException ("pattern"); + + return SendExpectStringArray ("KEYS", pattern); + } + + public byte [][] MGet (params string [] keys) + { + if (keys == null) + throw new System.ArgumentNullException ("keys"); + if (keys.Length == 0) + throw new System.ArgumentException ("keys"); + + return SendExpectDataArray ("MGET", keys); + } + + + public string [] SendExpectStringArray (string cmd, params object [] args) + { + byte [][] reply = SendExpectDataArray (cmd, args); + string [] keys = new string [reply.Length]; + for (int i = 0; i < reply.Length; i++) + keys[i] = System.Text.Encoding.UTF8.GetString (reply[i]); + return keys; + } + + public byte[][] SendExpectDataArray (string cmd, params object [] args) + { + if (!SendCommand (cmd, args)) + throw new System.Exception("Unable to connect"); + int c = bstream.ReadByte(); + if (c == -1) + throw new ResponseException("No more data"); + + string s = ReadLine(); + if (c == '-') + throw new ResponseException(s.StartsWith("ERR ") ? s.Substring(4) : s); + if (c == '*') { + int count; + if (int.TryParse (s, out count)) { + byte [][] result = new byte [count][]; + + for (int i = 0; i < count; i++) + result[i] = ReadData(); + + return result; + } + } + throw new ResponseException("Unknown reply on multi-request: " + c + s); + } + + #region List commands + public byte[][] ListRange(string key, int start, int end) + { + return SendExpectDataArray ("LRANGE", key, start, end); + } + + public void LeftPush(string key, string value) + { + LeftPush(key, System.Text.Encoding.UTF8.GetBytes (value)); + } + + public void LeftPush(string key, byte [] value) + { + SendDataCommand (value, "LPUSH", key); + ExpectSuccess(); + } + + public void RightPush(string key, string value) + { + RightPush(key, System.Text.Encoding.UTF8.GetBytes (value)); + } + + public void RightPush(string key, byte [] value) + { + SendDataCommand (value, "RPUSH", key); + ExpectSuccess(); + } + + public int ListLength (string key) + { + return SendExpectInt ("LLEN", key); + } + + public byte[] ListIndex (string key, int index) + { + SendCommand ("LINDEX", key, index); + return ReadData (); + } + + public byte[] LeftPop(string key) + { + SendCommand ("LPOP", key); + return ReadData (); + } + + public byte[] RightPop(string key) + { + SendCommand ("RPOP", key); + return ReadData (); + } + #endregion + + #region Set commands + public bool AddToSet (string key, byte[] member) + { + return SendDataExpectInt(member, "SADD", key) > 0; + } + + public bool AddToSet (string key, string member) + { + return AddToSet (key, System.Text.Encoding.UTF8.GetBytes(member)); + } + + public int CardinalityOfSet (string key) + { + return SendExpectInt ("SCARD", key); + } + + public bool IsMemberOfSet (string key, byte[] member) + { + return SendDataExpectInt (member, "SISMEMBER", key) > 0; + } + + public bool IsMemberOfSet(string key, string member) + { + return IsMemberOfSet(key, System.Text.Encoding.UTF8.GetBytes(member)); + } + + public byte[][] GetMembersOfSet (string key) + { + return SendExpectDataArray ("SMEMBERS", key); + } + + public byte[] GetRandomMemberOfSet (string key) + { + return SendExpectData ("SRANDMEMBER", key); + } + + public byte[] PopRandomMemberOfSet (string key) + { + return SendExpectData ("SPOP", key); + } + + public bool RemoveFromSet (string key, byte[] member) + { + return SendDataExpectInt (member, "SREM", key) > 0; + } + + public bool RemoveFromSet (string key, string member) + { + return RemoveFromSet (key, System.Text.Encoding.UTF8.GetBytes(member)); + } + + public byte[][] GetUnionOfSets (params string[] keys) + { + if (keys == null) + throw new System.ArgumentNullException(); + + return SendExpectDataArray ("SUNION", keys); + + } + + void StoreSetCommands (string cmd, params string[] keys) + { + if (string.IsNullOrEmpty(cmd)) + throw new System.ArgumentNullException ("cmd"); + + if (keys == null) + throw new System.ArgumentNullException ("keys"); + + SendExpectSuccess (cmd, keys); + } + + public void StoreUnionOfSets (params string[] keys) + { + StoreSetCommands ("SUNIONSTORE", keys); + } + + public byte[][] GetIntersectionOfSets (params string[] keys) + { + if (keys == null) + throw new System.ArgumentNullException(); + + return SendExpectDataArray ("SINTER", keys); + } + + public void StoreIntersectionOfSets (params string[] keys) + { + StoreSetCommands ("SINTERSTORE", keys); + } + + public byte[][] GetDifferenceOfSets (params string[] keys) + { + if (keys == null) + throw new System.ArgumentNullException(); + + return SendExpectDataArray ("SDIFF", keys); + } + + public void StoreDifferenceOfSets (params string[] keys) + { + StoreSetCommands ("SDIFFSTORE", keys); + } + + public bool MoveMemberToSet (string srcKey, string destKey, byte[] member) + { + return SendDataExpectInt (member, "SMOVE", srcKey, destKey) > 0; + } + #endregion + + public void Dispose () + { + Dispose (true); + System.GC.SuppressFinalize (this); + } + + ~Redis () + { + Dispose (false); + } + + protected virtual void Dispose (bool disposing) + { + if (disposing){ + SendCommand ("QUIT"); + ExpectSuccess (); + socket.Close (); + socket = null; + } + } +} + +public class SortOptions { + public string Key { get; set; } + public bool Descending { get; set; } + public bool Lexographically { get; set; } + public System.Int32 LowerLimit { get; set; } + public System.Int32 UpperLimit { get; set; } + public string By { get; set; } + public string StoreInKey { get; set; } + public string Get { get; set; } + + public object [] ToArgs () + { + System.Collections.ArrayList args = new System.Collections.ArrayList(); + + if (LowerLimit != 0 || UpperLimit != 0) { + args.Add ("LIMIT"); + args.Add (LowerLimit); + args.Add (UpperLimit); + } + if (Lexographically) + args.Add("ALPHA"); + if (!string.IsNullOrEmpty (By)) { + args.Add("BY"); + args.Add(By); + } + if (!string.IsNullOrEmpty (Get)) { + args.Add("GET"); + args.Add(Get); + } + return args.ToArray (); + } +} + +// just to output a string to the viewer console +void debug(string msg){ + llOwnerSay("debug: "+msg); +} + +// MAIN Script that uses the Redis server + +string message = "Hello avatar!"; +string REDIS_IP = "192.168.1.111"; +Redis R; + +public void default_event_state_entry() +{ + debug(message); + R = new Redis(REDIS_IP); + var info = R.GetInfo (); + foreach (var k in info.Keys) { + llSay(0, (string)k+" "+ info[k]); + } + R.Set("keyTest", "OpenSim"); +} + +public void default_event_touch_start( + LSL_Types.LSLInteger total_number) +{ + llSetTimerEvent(0.5); +} + +public void default_event_timer() +{ + string value = R.GetString("keyTest"); + debug(value); +} From 949b0d6dfcde616735c8d700c43449571d3f3622 Mon Sep 17 00:00:00 2001 From: Giovanni De Gasperis Date: Sat, 16 May 2015 17:12:39 +0200 Subject: [PATCH 16/19] added function Auth --- os-redis-sharp.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/os-redis-sharp.cs b/os-redis-sharp.cs index 2dffec9..8395532 100644 --- a/os-redis-sharp.cs +++ b/os-redis-sharp.cs @@ -582,6 +582,13 @@ public void BackgroundSave () SendExpectSuccess ("BGSAVE"); } + public void Auth (string passwd) + { + if (passwd == null) + throw new System.ArgumentNullException ("passwd"); + SendCommand ("AUTH", passwd); + } + public void Shutdown () { SendCommand ("SHUTDOWN"); From dd0dc7dcbde6c994a2824de5ce940289e1566b4b Mon Sep 17 00:00:00 2001 From: Giovanni De Gasperis Date: Sat, 16 May 2015 17:51:15 +0200 Subject: [PATCH 17/19] added HGetAllArray to read hashes --- os-redis-sharp.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/os-redis-sharp.cs b/os-redis-sharp.cs index 8395532..bfc196c 100644 --- a/os-redis-sharp.cs +++ b/os-redis-sharp.cs @@ -589,6 +589,14 @@ public void Auth (string passwd) SendCommand ("AUTH", passwd); } + public string[] HGetAllArray (string hash) + { + if (hash == null) + throw new System.ArgumentNullException ("hash"); + + return SendExpectStringArray ("HGETALL", hash); + } + public void Shutdown () { SendCommand ("SHUTDOWN"); From 7d27dd66768b5e694246ae3da82b78b2c1adcadc Mon Sep 17 00:00:00 2001 From: Giovanni De Gasperis Date: Sat, 16 May 2015 18:36:16 +0200 Subject: [PATCH 18/19] added HGetAll --- os-redis-sharp.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/os-redis-sharp.cs b/os-redis-sharp.cs index bfc196c..11116d6 100644 --- a/os-redis-sharp.cs +++ b/os-redis-sharp.cs @@ -597,6 +597,24 @@ public string[] HGetAllArray (string hash) return SendExpectStringArray ("HGETALL", hash); } + public Dictionary HGetAll (string hash) + { + string [] lst = HGetAllArray (hash); + var dict = new Dictionary(); + string key = ""; + bool isKey = true; + foreach (var r in lst){ + if (isKey) { + key = r; + isKey = false; + } else { + dict.Add (key, r); + isKey = true; + } + } + return dict; + } + public void Shutdown () { SendCommand ("SHUTDOWN"); From 58e6132863c216b70b39de62f714331e5da61d8c Mon Sep 17 00:00:00 2001 From: Giovanni De Gasperis Date: Sat, 16 May 2015 18:37:59 +0200 Subject: [PATCH 19/19] compacted code --- os-redis-sharp.cs | 152 +++++++++++++--------------------------------- 1 file changed, 41 insertions(+), 111 deletions(-) diff --git a/os-redis-sharp.cs b/os-redis-sharp.cs index 11116d6..2997907 100644 --- a/os-redis-sharp.cs +++ b/os-redis-sharp.cs @@ -73,8 +73,7 @@ public void Set (string key, string value) if (key == null) throw new System.ArgumentNullException ("key"); if (value == null) - throw new System.ArgumentNullException ("value"); - + throw new System.ArgumentNullException ("value"); Set (key, System.Text.Encoding.UTF8.GetBytes (value)); } @@ -84,10 +83,8 @@ public void Set (string key, byte [] value) throw new System.ArgumentNullException ("key"); if (value == null) throw new System.ArgumentNullException ("value"); - if (value.Length > 1073741824) throw new System.ArgumentException ("value exceeds 1G", "value"); - if (!SendDataCommand (value, "SET", key)) throw new System.Exception ("Unable to connect"); ExpectSuccess (); @@ -98,8 +95,7 @@ public bool SetNX (string key, string value) if (key == null) throw new System.ArgumentNullException ("key"); if (value == null) - throw new System.ArgumentNullException ("value"); - + throw new System.ArgumentNullException ("value"); return SetNX (key, System.Text.Encoding.UTF8.GetBytes (value)); } @@ -109,29 +105,12 @@ public bool SetNX (string key, byte [] value) throw new System.ArgumentNullException ("key"); if (value == null) throw new System.ArgumentNullException ("value"); - if (value.Length > 1073741824) throw new System.ArgumentException ("value exceeds 1G", "value"); return SendDataExpectInt (value, "SETNX", key) > 0 ? true : false; } - /*public void Set (System.Linq.IDictionary dict) - { - if (dict == null) - throw new System.ArgumentNullException ("dict"); - - Set (dict.ToDictionary(k => k.Key, v => System.Text.Encoding.UTF8.GetBytes(v.Value))); - } - - public void Set (System.Linq.IDictionary dict) - { - if (dict == null) - throw new System.ArgumentNullException ("dict"); - - MSet (dict.Keys.ToArray (), dict.Values.ToArray ()); - }*/ - public void MSet (string [] keys, byte [][] values) { if (keys.Length != values.Length) @@ -151,8 +130,7 @@ public void MSet (string [] keys, byte [][] values) ms.Write (vLength, 0, vLength.Length); ms.Write (val, 0, val.Length); ms.Write (nl, 0, nl.Length); - } - + } SendDataRESP (ms.ToArray (), "*" + (keys.Length * 2 + 1) + "\r\n$4\r\nMSET\r\n"); ExpectSuccess (); } @@ -180,7 +158,6 @@ public byte [][] Sort (string key, string destination, params object [] options) { if (key == null) throw new System.ArgumentNullException ("key"); - int offset = string.IsNullOrEmpty (destination) ? 1 : 3; object [] args = new object [offset + options.Length]; @@ -209,7 +186,6 @@ public byte [] GetSet (string key, byte [] value) if (!SendDataCommand (value, "GETSET", key)) throw new System.Exception ("Unable to connect"); - return ReadData (); } @@ -225,8 +201,7 @@ public string GetSet (string key, string value) string ReadLine () { System.Text.StringBuilder sb = new System.Text.StringBuilder (); - int c; - + int c; while ((c = bstream.ReadByte ()) != -1){ if (c == '\r') continue; @@ -248,14 +223,11 @@ void Connect () socket = null; return; } - bstream = new System.IO.BufferedStream (new System.Net.Sockets.NetworkStream (socket), 16*1024); - + bstream = new System.IO.BufferedStream (new System.Net.Sockets.NetworkStream (socket), 16*1024); if (Password != null) SendExpectSuccess ("AUTH", Password); } - byte [] end_data = new byte [] { (byte) '\r', (byte) '\n' }; - bool SendDataCommand (byte [] data, string cmd, params object [] args) { string resp = "*" + (1 + args.Length + 1).ToString () + "\r\n"; @@ -266,7 +238,6 @@ bool SendDataCommand (byte [] data, string cmd, params object [] args) resp += "$" + argStrLength + "\r\n" + argStr + "\r\n"; } resp += "$" + data.Length + "\r\n"; - return SendDataRESP (data, resp); } @@ -276,7 +247,6 @@ bool SendDataRESP (byte [] data, string resp) Connect (); if (socket == null) return false; - byte [] r = System.Text.Encoding.UTF8.GetBytes (resp); try { socket.Send (r); @@ -288,7 +258,6 @@ bool SendDataRESP (byte [] data, string resp) // timeout; socket.Close (); socket = null; - return false; } return true; @@ -300,7 +269,6 @@ bool SendCommand (string cmd, params object [] args) Connect (); if (socket == null) return false; - string resp = "*" + (1 + args.Length).ToString () + "\r\n"; resp += "$" + cmd.Length + "\r\n" + cmd + "\r\n"; foreach (object arg in args) { @@ -308,7 +276,6 @@ bool SendCommand (string cmd, params object [] args) int argStrLength = System.Text.Encoding.UTF8.GetByteCount(argStr); resp += "$" + argStrLength + "\r\n" + argStr + "\r\n"; } - byte [] r = System.Text.Encoding.UTF8.GetBytes (resp); try { socket.Send (r); @@ -316,7 +283,6 @@ bool SendCommand (string cmd, params object [] args) // timeout; socket.Close (); socket = null; - return false; } return true; @@ -337,7 +303,6 @@ void SendExpectSuccess (string cmd, params object [] args) { if (!SendCommand (cmd, args)) throw new System.Exception ("Unable to connect"); - ExpectSuccess (); } @@ -345,11 +310,9 @@ int SendDataExpectInt (byte[] data, string cmd, params object [] args) { if (!SendDataCommand (data, cmd, args)) throw new System.Exception ("Unable to connect"); - int c = bstream.ReadByte (); if (c == -1) throw new ResponseException ("No more data"); - string s = ReadLine (); if (c == '-') throw new ResponseException (s.StartsWith ("ERR ") ? s.Substring (4) : s); @@ -365,11 +328,9 @@ int SendExpectInt (string cmd, params object [] args) { if (!SendCommand (cmd, args)) throw new System.Exception ("Unable to connect"); - int c = bstream.ReadByte (); if (c == -1) throw new ResponseException ("No more data"); - string s = ReadLine (); if (c == '-') throw new ResponseException (s.StartsWith ("ERR ") ? s.Substring (4) : s); @@ -385,17 +346,14 @@ string SendExpectString (string cmd, params object [] args) { if (!SendCommand (cmd, args)) throw new System.Exception ("Unable to connect"); - int c = bstream.ReadByte (); if (c == -1) throw new ResponseException ("No more data"); - string s = ReadLine (); if (c == '-') throw new ResponseException (s.StartsWith ("ERR ") ? s.Substring (4) : s); if (c == '+') - return s; - + return s; throw new ResponseException ("Unknown reply on integer request: " + c + s); } @@ -406,7 +364,6 @@ string SendGetString (string cmd, params object [] args) { if (!SendCommand (cmd, args)) throw new System.Exception ("Unable to connect"); - return ReadLine (); } @@ -414,7 +371,6 @@ byte [] SendExpectData (string cmd, params object [] args) { if (!SendCommand (cmd, args)) throw new System.Exception ("Unable to connect"); - return ReadData (); } @@ -422,17 +378,14 @@ byte [] ReadData () { string s = ReadLine (); if (s.Length == 0) - throw new ResponseException ("Zero length respose"); - + throw new ResponseException ("Zero length respose"); char c = s [0]; if (c == '-') throw new ResponseException (s.StartsWith ("-ERR ") ? s.Substring (5) : s.Substring (1)); - if (c == '$'){ if (s == "$-1") return null; - int n; - + int n; if (System.Int32.TryParse (s.Substring (1), out n)){ byte [] retbuf = new byte [n]; @@ -450,18 +403,6 @@ byte [] ReadData () } throw new ResponseException ("Invalid length"); } - - /* don't treat arrays here because only one element works -- use DataArray! - //returns the number of matches - if (c == '*') { - int n; - if (Int32.TryParse(s.Substring(1), out n)) - return n <= 0 ? new byte [0] : ReadData(); - - throw new ResponseException ("Unexpected length parameter" + r); - } - */ - throw new ResponseException ("Unexpected reply: " + s); } @@ -581,7 +522,7 @@ public void BackgroundSave () { SendExpectSuccess ("BGSAVE"); } - + public void Auth (string passwd) { if (passwd == null) @@ -589,32 +530,6 @@ public void Auth (string passwd) SendCommand ("AUTH", passwd); } - public string[] HGetAllArray (string hash) - { - if (hash == null) - throw new System.ArgumentNullException ("hash"); - - return SendExpectStringArray ("HGETALL", hash); - } - - public Dictionary HGetAll (string hash) - { - string [] lst = HGetAllArray (hash); - var dict = new Dictionary(); - string key = ""; - bool isKey = true; - foreach (var r in lst){ - if (isKey) { - key = r; - isKey = false; - } else { - dict.Add (key, r); - isKey = true; - } - } - return dict; - } - public void Shutdown () { SendCommand ("SHUTDOWN"); @@ -645,8 +560,7 @@ public void FlushDb () public System.DateTime LastSave { get { - int t = SendExpectInt ("LASTSAVE"); - + int t = SendExpectInt ("LASTSAVE"); return new System.DateTime (UnixEpoch) + System.TimeSpan.FromSeconds (t); } } @@ -654,8 +568,7 @@ public System.DateTime LastSave { public Dictionary GetInfo () { byte [] r = SendExpectData ("INFO"); - var dict = new Dictionary(); - + var dict = new Dictionary(); foreach (var line in System.Text.Encoding.UTF8.GetString (r).Split ('\n')){ int p = line.IndexOf (':'); if (p == -1) @@ -665,6 +578,31 @@ public Dictionary GetInfo () return dict; } + public string[] HGetAllArray (string hash) + { + if (hash == null) + throw new System.ArgumentNullException ("hash"); + return SendExpectStringArray ("HGETALL", hash); + } + + public Dictionary HGetAll (string hash) + { + string [] lst = HGetAllArray (hash); + var dict = new Dictionary(); + string key = ""; + bool isKey = true; + foreach (var r in lst){ + if (isKey) { + key = r; + isKey = false; + } else { + dict.Add (key, r); + isKey = true; + } + } + return dict; + } + public string [] Keys { get { return GetKeys("*"); @@ -675,7 +613,6 @@ public string [] GetKeys (string pattern) { if (pattern == null) throw new System.ArgumentNullException ("pattern"); - return SendExpectStringArray ("KEYS", pattern); } @@ -684,8 +621,7 @@ public byte [][] MGet (params string [] keys) if (keys == null) throw new System.ArgumentNullException ("keys"); if (keys.Length == 0) - throw new System.ArgumentException ("keys"); - + throw new System.ArgumentException ("keys"); return SendExpectDataArray ("MGET", keys); } @@ -705,8 +641,7 @@ public byte[][] SendExpectDataArray (string cmd, params object [] args) throw new System.Exception("Unable to connect"); int c = bstream.ReadByte(); if (c == -1) - throw new ResponseException("No more data"); - + throw new ResponseException("No more data"); string s = ReadLine(); if (c == '-') throw new ResponseException(s.StartsWith("ERR ") ? s.Substring(4) : s); @@ -716,8 +651,7 @@ public byte[][] SendExpectDataArray (string cmd, params object [] args) byte [][] result = new byte [count][]; for (int i = 0; i < count; i++) - result[i] = ReadData(); - + result[i] = ReadData(); return result; } } @@ -830,8 +764,7 @@ public bool RemoveFromSet (string key, string member) public byte[][] GetUnionOfSets (params string[] keys) { if (keys == null) - throw new System.ArgumentNullException(); - + throw new System.ArgumentNullException(); return SendExpectDataArray ("SUNION", keys); } @@ -840,10 +773,8 @@ void StoreSetCommands (string cmd, params string[] keys) { if (string.IsNullOrEmpty(cmd)) throw new System.ArgumentNullException ("cmd"); - if (keys == null) throw new System.ArgumentNullException ("keys"); - SendExpectSuccess (cmd, keys); } @@ -855,8 +786,7 @@ public void StoreUnionOfSets (params string[] keys) public byte[][] GetIntersectionOfSets (params string[] keys) { if (keys == null) - throw new System.ArgumentNullException(); - + throw new System.ArgumentNullException(); return SendExpectDataArray ("SINTER", keys); }