diff --git a/.idea/.idea.FireflyClient/.idea/.gitignore b/.idea/.idea.FireflyClient/.idea/.gitignore
new file mode 100644
index 0000000..4ae9bca
--- /dev/null
+++ b/.idea/.idea.FireflyClient/.idea/.gitignore
@@ -0,0 +1,13 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Rider ignored files
+/modules.xml
+/projectSettingsUpdater.xml
+/.idea.FireflyClient.iml
+/contentModel.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/.idea.FireflyClient/.idea/.name b/.idea/.idea.FireflyClient/.idea/.name
new file mode 100644
index 0000000..ec3e592
--- /dev/null
+++ b/.idea/.idea.FireflyClient/.idea/.name
@@ -0,0 +1 @@
+FireflyClient
\ No newline at end of file
diff --git a/.idea/.idea.FireflyClient/.idea/encodings.xml b/.idea/.idea.FireflyClient/.idea/encodings.xml
new file mode 100644
index 0000000..df87cf9
--- /dev/null
+++ b/.idea/.idea.FireflyClient/.idea/encodings.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.FireflyClient/.idea/indexLayout.xml b/.idea/.idea.FireflyClient/.idea/indexLayout.xml
new file mode 100644
index 0000000..7b08163
--- /dev/null
+++ b/.idea/.idea.FireflyClient/.idea/indexLayout.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.FireflyClient/.idea/vcs.xml b/.idea/.idea.FireflyClient/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/.idea.FireflyClient/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/artifacts/exe/FireflyClient b/artifacts/exe/FireflyClient
index a1bf1eb..64b668f 100644
Binary files a/artifacts/exe/FireflyClient and b/artifacts/exe/FireflyClient differ
diff --git a/artifacts/exe/FireflyClient.dbg b/artifacts/exe/FireflyClient.dbg
index 25c0e5c..2e96188 100644
Binary files a/artifacts/exe/FireflyClient.dbg and b/artifacts/exe/FireflyClient.dbg differ
diff --git a/artifacts/exe/FireflyClient.exe b/artifacts/exe/FireflyClient.exe
index d280d11..f112e19 100644
Binary files a/artifacts/exe/FireflyClient.exe and b/artifacts/exe/FireflyClient.exe differ
diff --git a/artifacts/exe/FireflyClient.pdb b/artifacts/exe/FireflyClient.pdb
index c349a4b..287cbef 100644
Binary files a/artifacts/exe/FireflyClient.pdb and b/artifacts/exe/FireflyClient.pdb differ
diff --git a/artifacts/exe/FireflyClient.xml b/artifacts/exe/FireflyClient.xml
index 5f487b8..eaa7895 100644
--- a/artifacts/exe/FireflyClient.xml
+++ b/artifacts/exe/FireflyClient.xml
@@ -161,6 +161,11 @@
Removes and returns the last element of a list
+
+
+ Gets the length of a list
+
+
Gets a range of elements from a list
@@ -191,6 +196,61 @@
Removes elements equal to the given value from a list
+
+
+ String array structure for interop
+
+
+
+
+ Pointer to an array of string pointers
+
+
+
+
+ Number of strings in the array
+
+
+
+
+ Key-value pair structure for interop
+
+
+
+
+ Pointer to the key string
+
+
+
+
+ Pointer to the value string
+
+
+
+
+ Dictionary structure for interop
+
+
+
+
+ Pointer to an array of key-value pairs
+
+
+
+
+ Number of pairs in the array
+
+
+
+
+ Marshals a List to a NativeStringList without string conversion
+
+
+
+
+ Marshals a Dictionary to a NativeDictionary without string conversion
+
+
Creates a new FireflyClient instance for native interop
@@ -217,6 +277,16 @@
Frees a string allocated by the native interop methods
+
+
+ Frees a NativeStringList allocated by the native interop methods
+
+
+
+
+ Frees a NativeDictionary allocated by the native interop methods
+
+
Sets a string value for a given key (Native Interop).
@@ -277,6 +347,14 @@
Pointer to a null-terminated UTF-8 string representing the list key.
Pointer to a null-terminated UTF-8 string result allocated via AllocHGlobal (caller must free with FreeString), or IntPtr.Zero on error or if list is empty.
+
+
+ Gets the length of a list (Native Interop).
+
+ The GCHandle (as IntPtr) representing the client instance.
+ Pointer to a null-terminated UTF-8 string representing the list key.
+ The length of the list, or 0 on error.
+
Gets a range of elements from a list (Native Interop).
diff --git a/artifacts/native/FireflyClient.pdb b/artifacts/native/FireflyClient.pdb
index c3bcbbf..72dc6e7 100644
Binary files a/artifacts/native/FireflyClient.pdb and b/artifacts/native/FireflyClient.pdb differ
diff --git a/artifacts/native/FireflyClient.so.dbg b/artifacts/native/FireflyClient.so.dbg
index d8c3807..216807b 100644
Binary files a/artifacts/native/FireflyClient.so.dbg and b/artifacts/native/FireflyClient.so.dbg differ
diff --git a/artifacts/native/FireflyClient.xml b/artifacts/native/FireflyClient.xml
index 5f487b8..eaa7895 100644
--- a/artifacts/native/FireflyClient.xml
+++ b/artifacts/native/FireflyClient.xml
@@ -161,6 +161,11 @@
Removes and returns the last element of a list
+
+
+ Gets the length of a list
+
+
Gets a range of elements from a list
@@ -191,6 +196,61 @@
Removes elements equal to the given value from a list
+
+
+ String array structure for interop
+
+
+
+
+ Pointer to an array of string pointers
+
+
+
+
+ Number of strings in the array
+
+
+
+
+ Key-value pair structure for interop
+
+
+
+
+ Pointer to the key string
+
+
+
+
+ Pointer to the value string
+
+
+
+
+ Dictionary structure for interop
+
+
+
+
+ Pointer to an array of key-value pairs
+
+
+
+
+ Number of pairs in the array
+
+
+
+
+ Marshals a List to a NativeStringList without string conversion
+
+
+
+
+ Marshals a Dictionary to a NativeDictionary without string conversion
+
+
Creates a new FireflyClient instance for native interop
@@ -217,6 +277,16 @@
Frees a string allocated by the native interop methods
+
+
+ Frees a NativeStringList allocated by the native interop methods
+
+
+
+
+ Frees a NativeDictionary allocated by the native interop methods
+
+
Sets a string value for a given key (Native Interop).
@@ -277,6 +347,14 @@
Pointer to a null-terminated UTF-8 string representing the list key.
Pointer to a null-terminated UTF-8 string result allocated via AllocHGlobal (caller must free with FreeString), or IntPtr.Zero on error or if list is empty.
+
+
+ Gets the length of a list (Native Interop).
+
+ The GCHandle (as IntPtr) representing the client instance.
+ Pointer to a null-terminated UTF-8 string representing the list key.
+ The length of the list, or 0 on error.
+
Gets a range of elements from a list (Native Interop).
diff --git a/artifacts/native/libFireflyClient.dll b/artifacts/native/libFireflyClient.dll
index 6364094..d36b935 100644
Binary files a/artifacts/native/libFireflyClient.dll and b/artifacts/native/libFireflyClient.dll differ
diff --git a/artifacts/native/libFireflyClient.so b/artifacts/native/libFireflyClient.so
index 4f8273b..c08bbd8 100644
Binary files a/artifacts/native/libFireflyClient.so and b/artifacts/native/libFireflyClient.so differ
diff --git a/firefly.h b/firefly.h
index f221e30..969c90c 100644
--- a/firefly.h
+++ b/firefly.h
@@ -7,6 +7,22 @@
extern "C" {
#endif
+// C header definitions
+typedef struct {
+ const char** strings;
+ int count;
+} StringArray;
+
+typedef struct {
+ const char* key;
+ const char* value;
+} KeyValuePair;
+
+typedef struct {
+ KeyValuePair* pairs;
+ int count;
+} Dictionary;
+
// --- Client Management ---
/**
@@ -113,12 +129,11 @@ char* ListRightPop(void* handle, const char* key);
* @param key Null-terminated UTF-8 string for the list key.
* @param start The start index (0-based).
* @param stop The stop index (0-based, inclusive). Use -1 to specify the end of the list.
- * @return A pointer to a null-terminated UTF-8 string containing the list elements, separated by newline characters (\n).
- * NOTE: This is a single string containing all elements. The caller is responsible for parsing this string (e.g., splitting by '\n').
- * This string is allocated by the library and MUST be freed by the caller using FreeString().
+ * @return A StringArray structure containing the elements in the specified range.
+ * The caller is responsible for freeing the memory allocated for the StringArray structure using FreeStringArray().
* Returns NULL if the key does not exist or an error occurs.
*/
-char* ListRange(void* handle, const char* key, int start, int stop);
+StringArray ListRange(void* handle, const char* key, int start, int stop);
/**
* @brief Gets the element at the specified index in a list.
@@ -175,6 +190,20 @@ bool ListTrim(void* handle, const char* key, int start, int stop);
*/
int ListRemove(void* handle, const char* key, int count, const char* element);
+/**
+ * @brief Gets the length of a list.
+ * @param handle The client handle.
+ * @param key Null-terminated UTF-8 string for the list key.
+ * @return The length of the list, or 0 on error.
+ */
+int ListLength(void* handle, const char* key);
+
+/**
+ * @brief Frees a StringArray structure and its contents.
+ * @param array Pointer to the StringArray structure to be freed.
+ */
+void FreeStringList(StringArray array);
+
// --- Hash Operations ---
/**
@@ -220,14 +249,11 @@ bool HashFieldExists(void* handle, const char* key, const char* field);
* @brief Gets all fields and values from a hash.
* @param handle The client handle.
* @param key Null-terminated UTF-8 string for the hash key.
- * @return A pointer to a null-terminated UTF-8 string containing the hash fields and values.
- * Each field/value pair is represented as "field=value", separated by newline characters (\n).
- * NOTE: This is a single string containing all field-value pairs. The caller is responsible for parsing this string (e.g., splitting by '\n' then by '=').
- * Example: "field1=value1\nfield2=value2\nfield3=value3"
- * This string is allocated by the library and MUST be freed by the caller using FreeString().
+ * @return A Dictionary structure containing all fields and values in the hash.
+ * The caller is responsible for freeing the memory allocated for the Dictionary structure using FreeDictionary().
* Returns NULL if the key does not exist or an error occurs.
*/
-char* HashGetAll(void* handle, const char* key);
+Dictionary HashGetAll(void* handle, const char* key);
/**
* @brief Sets multiple fields and values in a hash.
@@ -240,6 +266,12 @@ char* HashGetAll(void* handle, const char* key);
*/
bool HashMultiSet(void* handle, const char* key, const char* fieldValuePairs);
+/**
+ * @brief Frees the memory allocated for a Dictionary structure.
+ * @param dict Pointer to the Dictionary structure to be freed.
+ */
+void FreeDictionary(Dictionary dict);
+
// --- Raw Command Execution ---
/**
diff --git a/src/FireflyClient.cs b/src/FireflyClient.cs
index ee387d3..4245679 100644
--- a/src/FireflyClient.cs
+++ b/src/FireflyClient.cs
@@ -11,15 +11,12 @@ namespace FireflyClient
{
private readonly TcpClient _client;
private readonly NetworkStream _stream;
- private readonly string _host;
- private readonly int _port;
- private readonly string _password;
private readonly byte[] _responseBuffer = new byte[1024 * 1024]; // 1MB buffer for responses
private int _maxBatchSize = 1000;
private readonly int _maxPipelineSize = 10000;
private readonly Queue _commandQueue = new();
- private bool _isPipelineMode = false;
- private bool _isAuthenticated = false;
+ private bool _isPipelineMode;
+ private bool _isAuthenticated;
// Static instance for native interop
// private static FireflyClient? _instance;
@@ -29,11 +26,8 @@ namespace FireflyClient
///
public FireflyClient(string host = "127.0.0.1", int port = 6379)
{
- _host = host;
- _port = port;
- _password = string.Empty;
_client = new TcpClient();
- _client.Connect(_host, _port);
+ _client.Connect(host, port);
_stream = _client.GetStream();
}
@@ -42,10 +36,9 @@ namespace FireflyClient
///
public FireflyClient(string host, int port, string password) : this(host, port)
{
- _password = password;
- if (!string.IsNullOrEmpty(_password))
+ if (!string.IsNullOrEmpty(password))
{
- Authenticate(_password);
+ Authenticate(password);
}
}
@@ -54,7 +47,7 @@ namespace FireflyClient
///
public bool Authenticate(string password)
{
- string response = ExecuteCommand("AUTH", password);
+ var response = ExecuteCommand("AUTH", password);
_isAuthenticated = response.StartsWith("+OK");
return _isAuthenticated;
}
@@ -70,7 +63,7 @@ namespace FireflyClient
}
// Build the command string
- string fullCommand = $"{command.ToUpperInvariant()}{string.Join("", args.Select(arg => $" {QuoteIfNeeded(arg)}"))}";
+ var fullCommand = $"{command.ToUpperInvariant()}{string.Join("", args.Select(arg => $" {QuoteIfNeeded(arg)}"))}";
// Handle special commands that should not be pipelined
if (command.Equals("AUTH", StringComparison.OrdinalIgnoreCase) ||
@@ -118,11 +111,11 @@ namespace FireflyClient
command += "\r\n";
}
- byte[] commandBytes = Encoding.UTF8.GetBytes(command);
+ var commandBytes = Encoding.UTF8.GetBytes(command);
_stream.Write(commandBytes, 0, commandBytes.Length);
// Read response
- int bytesRead = _stream.Read(_responseBuffer, 0, _responseBuffer.Length);
+ var bytesRead = _stream.Read(_responseBuffer, 0, _responseBuffer.Length);
return bytesRead > 0 ? Encoding.UTF8.GetString(_responseBuffer, 0, bytesRead) : string.Empty;
}
@@ -131,7 +124,7 @@ namespace FireflyClient
if (_commandQueue.Count == 0) return string.Empty;
// Build the pipeline command string
- string pipelineCommand = string.Join("\r\n", _commandQueue) + "\r\n";
+ var pipelineCommand = string.Join("\r\n", _commandQueue) + "\r\n";
_commandQueue.Clear();
return SendCommandInternal(pipelineCommand);
@@ -140,7 +133,7 @@ namespace FireflyClient
///
/// Enables or disables pipeline mode
///
- public void SetPipelineMode(bool enabled)
+ private void SetPipelineMode(bool enabled)
{
_isPipelineMode = enabled;
if (!enabled)
@@ -152,7 +145,7 @@ namespace FireflyClient
///
/// Flushes any queued commands in pipeline mode
///
- public string FlushPipeline()
+ private string FlushPipeline()
{
return ProcessCommandQueue();
}
@@ -160,7 +153,7 @@ namespace FireflyClient
///
/// Sets the maximum number of commands to batch before sending
///
- public void SetBatchSize(int size)
+ private void SetBatchSize(int size)
{
if (size <= 0)
{
@@ -172,17 +165,17 @@ namespace FireflyClient
///
/// Gets the current number of queued commands
///
- public int QueuedCommandCount => _commandQueue.Count;
+ private int QueuedCommandCount => _commandQueue.Count;
///
/// Gets whether pipeline mode is enabled
///
- public bool IsPipelineMode => _isPipelineMode;
+ private bool IsPipelineMode => _isPipelineMode;
///
/// Gets the maximum batch size
///
- public int MaxBatchSize => _maxBatchSize;
+ private int MaxBatchSize => _maxBatchSize;
///
/// Gets whether the client is connected to the server
@@ -199,18 +192,18 @@ namespace FireflyClient
///
/// Pattern to match against keys. Use * for wildcard matches.
/// List of matching keys, or empty list if none found or on error
- public List Keys(string pattern = "*")
+ private List Keys(string pattern = "*")
{
try
{
- string response = ExecuteCommand("KEYS", pattern);
+ var response = ExecuteCommand("KEYS", pattern);
if (string.IsNullOrEmpty(response) || !response.StartsWith('+'))
{
return [];
}
// Remove the '+' prefix and split by newlines
- string keysStr = response[1..].Trim();
+ var keysStr = response[1..].Trim();
return string.IsNullOrEmpty(keysStr)
? []
: [.. keysStr.Split('\n')];
@@ -224,7 +217,7 @@ namespace FireflyClient
///
/// Parses an array response from the server
///
- protected static List ParseArrayResponse(string response)
+ private static List ParseArrayResponse(string response)
{
var result = new List();
@@ -237,7 +230,7 @@ namespace FireflyClient
string[] parts = response.Split("\r\n");
// Skip the first line which just contains the * prefix
- for (int i = 1; i < parts.Length; i++)
+ for (var i = 1; i < parts.Length; i++)
{
if (!string.IsNullOrEmpty(parts[i]) && (parts[i].StartsWith('+') || parts[i].StartsWith('$')))
{
diff --git a/src/HashOperations.cs b/src/HashOperations.cs
index e551b47..92b97c5 100644
--- a/src/HashOperations.cs
+++ b/src/HashOperations.cs
@@ -7,53 +7,49 @@ namespace FireflyClient
///
/// Sets a field in a hash
///
- public bool HashSet(string key, string field, string value)
+ private bool HashSet(string key, string field, string value)
{
- string response = ExecuteCommand("HSET", key, field, value);
+ var response = ExecuteCommand("HSET", key, field, value);
return response.StartsWith(":1");
}
///
/// Gets a field from a hash
///
- public string HashGet(string key, string field)
+ private string HashGet(string key, string field)
{
- string response = ExecuteCommand("HGET", key, field);
- if (response.StartsWith('+'))
- {
- return response[1..].TrimEnd('\r', '\n');
- }
- return string.Empty;
+ var response = ExecuteCommand("HGET", key, field);
+ return response.StartsWith('+') ? response[1..].TrimEnd('\r', '\n') : string.Empty;
}
///
/// Deletes a field from a hash
///
- public bool HashDelete(string key, string field)
+ private bool HashDelete(string key, string field)
{
- string response = ExecuteCommand("HDEL", key, field);
+ var response = ExecuteCommand("HDEL", key, field);
return response.StartsWith(":1");
}
///
/// Checks if a field exists in a hash
///
- public bool HashFieldExists(string key, string field)
+ private bool HashFieldExists(string key, string field)
{
- string response = ExecuteCommand("HEXISTS", key, field);
+ var response = ExecuteCommand("HEXISTS", key, field);
return response.StartsWith(":1");
}
///
/// Gets all fields and values from a hash
///
- public Dictionary HashGetAll(string key)
+ private Dictionary HashGetAll(string key)
{
- string response = ExecuteCommand("HGETALL", key);
- List items = ParseArrayResponse(response);
+ var response = ExecuteCommand("HGETALL", key);
+ var items = ParseArrayResponse(response);
var result = new Dictionary();
- for (int i = 0; i < items.Count; i += 2)
+ for (var i = 0; i < items.Count; i += 2)
{
if (i + 1 < items.Count)
{
@@ -67,7 +63,7 @@ namespace FireflyClient
///
/// Sets multiple fields in a hash at once
///
- public bool HashMultiSet(string key, Dictionary fieldValues)
+ private bool HashMultiSet(string key, Dictionary fieldValues)
{
List args = [key];
@@ -77,7 +73,7 @@ namespace FireflyClient
args.Add(kvp.Value);
}
- string response = ExecuteCommand("HMSET", [.. args]);
+ var response = ExecuteCommand("HMSET", [.. args]);
return response.StartsWith("+OK");
}
diff --git a/src/ListOperations.cs b/src/ListOperations.cs
index 41a8dd3..632e9f4 100644
--- a/src/ListOperations.cs
+++ b/src/ListOperations.cs
@@ -7,102 +7,88 @@ namespace FireflyClient
///
/// Adds values to the beginning of a list
///
- public int ListLeftPush(string key, params string[] values)
+ private int ListLeftPush(string key, params string[] values)
{
var args = new List { key };
args.AddRange(values);
- string response = ExecuteCommand("LPUSH", [.. args]);
- if (response.StartsWith(':'))
- {
- if (int.TryParse(response[1..].TrimEnd('\r', '\n'), out int result))
- {
- return result;
- }
- }
- return 0;
+ var response = ExecuteCommand("LPUSH", [.. args]);
+ if (!response.StartsWith(':')) return 0;
+ return int.TryParse(response[1..].TrimEnd('\r', '\n'), out var result) ? result : 0;
}
///
/// Adds values to the end of a list
///
- public int ListRightPush(string key, params string[] values)
+ private int ListRightPush(string key, params string[] values)
{
var args = new List { key };
args.AddRange(values);
- string response = ExecuteCommand("RPUSH", [.. args]);
- if (response.StartsWith(':'))
- {
- if (int.TryParse(response[1..].TrimEnd('\r', '\n'), out int result))
- {
- return result;
- }
- }
- return 0;
+ var response = ExecuteCommand("RPUSH", [.. args]);
+ if (!response.StartsWith(':')) return 0;
+ return int.TryParse(response[1..].TrimEnd('\r', '\n'), out var result) ? result : 0;
}
///
/// Removes and returns the first element of a list
///
- public string ListLeftPop(string key)
+ private string ListLeftPop(string key)
{
- string response = ExecuteCommand("LPOP", key);
- if (response.StartsWith('+'))
- {
- return response[1..].TrimEnd('\r', '\n');
- }
- return string.Empty;
+ var response = ExecuteCommand("LPOP", key);
+ return response.StartsWith('+') ? response[1..].TrimEnd('\r', '\n') : string.Empty;
}
///
/// Removes and returns the last element of a list
///
- public string ListRightPop(string key)
+ private string ListRightPop(string key)
{
- string response = ExecuteCommand("RPOP", key);
- if (response.StartsWith('+'))
- {
- return response[1..].TrimEnd('\r', '\n');
- }
- return string.Empty;
+ var response = ExecuteCommand("RPOP", key);
+ return response.StartsWith('+') ? response[1..].TrimEnd('\r', '\n') : string.Empty;
+ }
+
+ ///
+ /// Gets the length of a list
+ ///
+ private int ListLength(string key)
+ {
+ var response = ExecuteCommand("LLEN", key);
+ if (!response.StartsWith(':')) return 0;
+ return int.TryParse(response[1..].TrimEnd('\r', '\n'), out var result) ? result : 0;
}
///
/// Gets a range of elements from a list
///
- public List ListRange(string key, int start, int stop)
+ private List ListRange(string key, int start, int stop)
{
- string response = ExecuteCommand("LRANGE", key, start.ToString(), stop.ToString());
+ var response = ExecuteCommand("LRANGE", key, start.ToString(), stop.ToString());
return ParseArrayResponse(response);
}
///
/// Gets the element at the specified index in a list
///
- public string ListIndex(string key, int index)
+ private string ListIndex(string key, int index)
{
- string response = ExecuteCommand("LINDEX", key, index.ToString());
- if (response.StartsWith('+'))
- {
- return response[1..].TrimEnd('\r', '\n');
- }
- return string.Empty;
+ var response = ExecuteCommand("LINDEX", key, index.ToString());
+ return response.StartsWith('+') ? response[1..].TrimEnd('\r', '\n') : string.Empty;
}
///
/// Sets the element at the specified index in a list
///
- public bool ListSet(string key, int index, string value)
+ private bool ListSet(string key, int index, string value)
{
- string response = ExecuteCommand("LSET", key, index.ToString(), value);
+ var response = ExecuteCommand("LSET", key, index.ToString(), value);
return response.StartsWith("+OK");
}
///
/// Returns the index of the first occurrence of an element in a list
///
- public int ListPosition(string key, string element, int rank = 1, int maxlen = 0)
+ private int ListPosition(string key, string element, int rank = 1, int maxlen = 0)
{
var args = new List { key, element };
@@ -118,40 +104,28 @@ namespace FireflyClient
args.Add(maxlen.ToString());
}
- string response = ExecuteCommand("LPOS", [.. args]);
- if (response.StartsWith(':'))
- {
- if (int.TryParse(response[1..].TrimEnd('\r', '\n'), out int result))
- {
- return result;
- }
- }
- return -1;
+ var response = ExecuteCommand("LPOS", [.. args]);
+ if (!response.StartsWith(':')) return -1;
+ return int.TryParse(response[1..].TrimEnd('\r', '\n'), out var result) ? result : -1;
}
///
/// Trims a list to the specified range
///
- public bool ListTrim(string key, int start, int stop)
+ private bool ListTrim(string key, int start, int stop)
{
- string response = ExecuteCommand("LTRIM", key, start.ToString(), stop.ToString());
+ var response = ExecuteCommand("LTRIM", key, start.ToString(), stop.ToString());
return response.StartsWith("+OK");
}
///
/// Removes elements equal to the given value from a list
///
- public int ListRemove(string key, int count, string element)
+ private int ListRemove(string key, int count, string element)
{
- string response = ExecuteCommand("LREM", key, count.ToString(), element);
- if (response.StartsWith(':'))
- {
- if (int.TryParse(response[1..].TrimEnd('\r', '\n'), out int result))
- {
- return result;
- }
- }
- return 0;
+ var response = ExecuteCommand("LREM", key, count.ToString(), element);
+ if (!response.StartsWith(':')) return 0;
+ return int.TryParse(response[1..].TrimEnd('\r', '\n'), out var result) ? result : 0;
}
#endregion
diff --git a/src/NativeInterop.cs b/src/NativeInterop.cs
index 6f8c2a5..698e57c 100644
--- a/src/NativeInterop.cs
+++ b/src/NativeInterop.cs
@@ -5,13 +5,66 @@ namespace FireflyClient
{
public partial class FireflyClient
{
+ // Native structure definitions for interop
+
+ ///
+ /// String array structure for interop
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public struct NativeStringList
+ {
+ ///
+ /// Pointer to an array of string pointers
+ ///
+ public IntPtr Strings;
+
+ ///
+ /// Number of strings in the array
+ ///
+ public int Count;
+ }
+
+ ///
+ /// Key-value pair structure for interop
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public struct NativeKeyValuePair
+ {
+ ///
+ /// Pointer to the key string
+ ///
+ public IntPtr Key;
+
+ ///
+ /// Pointer to the value string
+ ///
+ public IntPtr Value;
+ }
+
+ ///
+ /// Dictionary structure for interop
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public struct NativeDictionary
+ {
+ ///
+ /// Pointer to an array of key-value pairs
+ ///
+ public IntPtr Pairs;
+
+ ///
+ /// Number of pairs in the array
+ ///
+ public int Count;
+ }
+
// Helper to safely get client from handle
private static FireflyClient? GetClientFromHandle(IntPtr handle)
{
if (handle == IntPtr.Zero) return null;
try
{
- GCHandle gch = (GCHandle)handle;
+ var gch = (GCHandle)handle;
return gch.Target as FireflyClient;
}
catch
@@ -21,31 +74,80 @@ namespace FireflyClient
}
// Helper to marshal string result to IntPtr
- private static IntPtr MarshalStringResult(string? result)
+ private static IntPtr MarshalString(string? result)
{
if (result == null) return IntPtr.Zero;
- byte[] bytes = Encoding.UTF8.GetBytes(result);
- IntPtr ptr = Marshal.AllocHGlobal(bytes.Length + 1);
+ var bytes = Encoding.UTF8.GetBytes(result);
+ var ptr = Marshal.AllocHGlobal(bytes.Length + 1);
Marshal.Copy(bytes, 0, ptr, bytes.Length);
Marshal.WriteByte(ptr + bytes.Length, 0); // Null terminator
return ptr;
}
-
+
// Helper to marshal List result to IntPtr (newline-delimited)
private static IntPtr MarshalStringListResult(List? result)
{
if (result == null || result.Count == 0) return IntPtr.Zero;
- string joinedResult = string.Join("\n", result); // Using \n as delimiter
- return MarshalStringResult(joinedResult);
+ var joinedResult = string.Join("\n", result); // Using \n as delimiter
+ return MarshalString(joinedResult);
}
-
- // Helper to marshal Dictionary result to IntPtr (newline-delimited field=value)
- private static IntPtr MarshalStringDictionaryResult(Dictionary? result)
+
+ ///
+ /// Marshals a List to a NativeStringList without string conversion
+ ///
+ private static NativeStringList MarshalStringList(List? list)
{
- if (result == null || result.Count == 0) return IntPtr.Zero;
- string joinedResult = string.Join("\n", result.Select(kvp => $"{kvp.Key}={kvp.Value}"));
- return MarshalStringResult(joinedResult);
+ if (list == null || list.Count == 0)
+ {
+ return new NativeStringList { Strings = IntPtr.Zero, Count = 0 };
+ }
+
+ // Allocate memory for the array of strings
+ var arrayPtr = Marshal.AllocHGlobal(list.Count * IntPtr.Size);
+
+ // Copy each string to the allocated memory
+ for (var i = 0; i < list.Count; i++)
+ {
+ var stringPtr = MarshalString(list[i]);
+ Marshal.WriteIntPtr(arrayPtr + i * IntPtr.Size, stringPtr);
+ }
+
+ return new NativeStringList { Strings = arrayPtr, Count = list.Count };
+ }
+
+ ///
+ /// Marshals a Dictionary to a NativeDictionary without string conversion
+ ///
+ private static NativeDictionary MarshalDictionary(Dictionary? dict)
+ {
+ if (dict == null || dict.Count == 0)
+ {
+ return new NativeDictionary { Pairs = IntPtr.Zero, Count = 0 };
+ }
+
+ // Allocate memory for the array of key-value pairs
+ var structSize = Marshal.SizeOf();
+ var pairsPtr = Marshal.AllocHGlobal(dict.Count * structSize);
+
+ // For each key-value pair, create a NativeKeyValuePair
+ var index = 0;
+ foreach (var kvp in dict)
+ {
+ var keyPtr = MarshalString(kvp.Key);
+ var valuePtr = MarshalString(kvp.Value);
+
+ // Create a NativeKeyValuePair and write it to the allocated memory
+ var pair = new NativeKeyValuePair
+ {
+ Key = keyPtr,
+ Value = valuePtr
+ };
+ Marshal.StructureToPtr(pair, pairsPtr + index * structSize, false);
+ index++;
+ }
+
+ return new NativeDictionary { Pairs = pairsPtr, Count = dict.Count };
}
#region Native Interop Methods
@@ -59,7 +161,7 @@ namespace FireflyClient
try
{
// Use UTF8 for host string
- string host = Marshal.PtrToStringUTF8(hostPtr) ?? "127.0.0.1";
+ var host = Marshal.PtrToStringUTF8(hostPtr) ?? "127.0.0.1";
var client = new FireflyClient(host, port);
var handle = GCHandle.Alloc(client);
return GCHandle.ToIntPtr(handle);
@@ -80,7 +182,7 @@ namespace FireflyClient
{
try
{
- GCHandle gch = GCHandle.FromIntPtr(handle);
+ var gch = GCHandle.FromIntPtr(handle);
if (gch.Target is FireflyClient client)
{
client.Dispose();
@@ -104,7 +206,7 @@ namespace FireflyClient
try
{
// Use UTF8 for password string
- string password = Marshal.PtrToStringUTF8(passwordPtr) ?? string.Empty;
+ var password = Marshal.PtrToStringUTF8(passwordPtr) ?? string.Empty;
var client = GetClientFromHandle(handle);
return client?.Authenticate(password) ?? false;
}
@@ -124,13 +226,13 @@ namespace FireflyClient
try
{
// Use UTF8 for command and args strings
- string command = Marshal.PtrToStringUTF8(commandPtr) ?? string.Empty;
- string args = Marshal.PtrToStringUTF8(argsPtr) ?? string.Empty;
+ var command = Marshal.PtrToStringUTF8(commandPtr) ?? string.Empty;
+ var args = Marshal.PtrToStringUTF8(argsPtr) ?? string.Empty;
var client = GetClientFromHandle(handle);
// WARNING: Basic split, unreliable if args have spaces
string[] argArray = args.Split(' ', StringSplitOptions.RemoveEmptyEntries);
- string? result = client?.ExecuteCommand(command, argArray);
- return MarshalStringResult(result);
+ var result = client?.ExecuteCommand(command, argArray);
+ return MarshalString(result);
}
catch
{
@@ -149,7 +251,52 @@ namespace FireflyClient
Marshal.FreeHGlobal(ptr);
}
}
-
+
+ ///
+ /// Frees a NativeStringList allocated by the native interop methods
+ ///
+ [UnmanagedCallersOnly(EntryPoint = "FreeStringList")]
+ public static void NativeFreeStringList(NativeStringList list)
+ {
+ if (list.Strings != IntPtr.Zero && list.Count > 0)
+ {
+ for (var i = 0; i < list.Count; i++)
+ {
+ var stringPtr = Marshal.ReadIntPtr(list.Strings + i * IntPtr.Size);
+ if (stringPtr != IntPtr.Zero)
+ {
+ Marshal.FreeHGlobal(stringPtr);
+ }
+ }
+ Marshal.FreeHGlobal(list.Strings);
+ }
+ }
+
+ ///
+ /// Frees a NativeDictionary allocated by the native interop methods
+ ///
+ [UnmanagedCallersOnly(EntryPoint = "FreeDictionary")]
+ public static void NativeFreeDictionary(NativeDictionary dict)
+ {
+ if (dict.Pairs != IntPtr.Zero && dict.Count > 0)
+ {
+ var structSize = Marshal.SizeOf();
+
+ for (var i = 0; i < dict.Count; i++)
+ {
+ var pairPtr = dict.Pairs + i * structSize;
+ var pair = Marshal.PtrToStructure(pairPtr);
+
+ if (pair.Key != IntPtr.Zero)
+ Marshal.FreeHGlobal(pair.Key);
+
+ if (pair.Value != IntPtr.Zero)
+ Marshal.FreeHGlobal(pair.Value);
+ }
+ Marshal.FreeHGlobal(dict.Pairs);
+ }
+ }
+
// --- String Operations ---
///
@@ -165,8 +312,8 @@ namespace FireflyClient
try
{
var client = GetClientFromHandle(handle);
- string key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
- string value = Marshal.PtrToStringUTF8(valuePtr) ?? string.Empty;
+ var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
+ var value = Marshal.PtrToStringUTF8(valuePtr) ?? string.Empty;
return client?.StringSet(key, value) ?? false;
}
catch { return false; }
@@ -184,9 +331,9 @@ namespace FireflyClient
try
{
var client = GetClientFromHandle(handle);
- string key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
- string? result = client?.StringGet(key);
- return MarshalStringResult(result);
+ var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
+ var result = client?.StringGet(key);
+ return MarshalString(result);
}
catch { return IntPtr.Zero; }
}
@@ -204,7 +351,7 @@ namespace FireflyClient
try
{
var client = GetClientFromHandle(handle);
- string key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
+ var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
// Assuming Delete returns the number of keys deleted (usually 1 or 0)
return client?.Delete(key) ?? 0;
}
@@ -226,8 +373,8 @@ namespace FireflyClient
try
{
var client = GetClientFromHandle(handle);
- string key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
- string value = Marshal.PtrToStringUTF8(valuePtr) ?? string.Empty;
+ var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
+ var value = Marshal.PtrToStringUTF8(valuePtr) ?? string.Empty;
// Assuming LPUSH returns the new length of the list
return client?.ListLeftPush(key, value) ?? 0;
}
@@ -247,8 +394,8 @@ namespace FireflyClient
try
{
var client = GetClientFromHandle(handle);
- string key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
- string value = Marshal.PtrToStringUTF8(valuePtr) ?? string.Empty;
+ var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
+ var value = Marshal.PtrToStringUTF8(valuePtr) ?? string.Empty;
// Assuming RPUSH returns the new length of the list
return client?.ListRightPush(key, value) ?? 0;
}
@@ -267,9 +414,9 @@ namespace FireflyClient
try
{
var client = GetClientFromHandle(handle);
- string key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
- string? result = client?.ListLeftPop(key);
- return MarshalStringResult(result);
+ var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
+ var result = client?.ListLeftPop(key);
+ return MarshalString(result);
}
catch { return IntPtr.Zero; }
}
@@ -286,13 +433,32 @@ namespace FireflyClient
try
{
var client = GetClientFromHandle(handle);
- string key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
- string? result = client?.ListRightPop(key);
- return MarshalStringResult(result);
+ var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
+ var result = client?.ListRightPop(key);
+ return MarshalString(result);
}
catch { return IntPtr.Zero; }
}
+ ///
+ /// Gets the length of a list (Native Interop).
+ ///
+ /// The GCHandle (as IntPtr) representing the client instance.
+ /// Pointer to a null-terminated UTF-8 string representing the list key.
+ /// The length of the list, or 0 on error.
+ [UnmanagedCallersOnly(EntryPoint = "ListLength")]
+ public static int NativeListLength(IntPtr handle, IntPtr keyPtr)
+ {
+ try
+ {
+ var client = GetClientFromHandle(handle);
+ var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
+ var result = client?.ListLength(key) ?? 0;
+ return result;
+ }
+ catch { return 0; }
+ }
+
///
/// Gets a range of elements from a list (Native Interop).
///
@@ -303,17 +469,19 @@ namespace FireflyClient
/// Pointer to a null-terminated UTF-8 string containing newline-delimited list elements, allocated via AllocHGlobal (caller must free with FreeString), or IntPtr.Zero on error.
/// The returned IntPtr points to a single string. The native caller must parse this string (e.g., split by '\n') and free the pointer using NativeFreeString.
[UnmanagedCallersOnly(EntryPoint = "ListRange")]
- public static IntPtr NativeListRange(IntPtr handle, IntPtr keyPtr, int start, int stop)
+ public static NativeStringList NativeListRange(IntPtr handle, IntPtr keyPtr, int start, int stop)
{
- try
- {
- var client = GetClientFromHandle(handle);
- string key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
- List? result = client?.ListRange(key, start, stop);
- // Marshal List as a single newline-delimited string
- return MarshalStringListResult(result);
- }
- catch { return IntPtr.Zero; }
+ try
+ {
+ var client = GetClientFromHandle(handle);
+ var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
+ List? result = client?.ListRange(key, start, stop);
+ return MarshalStringList(result);
+ }
+ catch
+ {
+ return new NativeStringList { Strings = IntPtr.Zero, Count = 0 };
+ }
}
///
@@ -329,9 +497,9 @@ namespace FireflyClient
try
{
var client = GetClientFromHandle(handle);
- string key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
- string? result = client?.ListIndex(key, index);
- return MarshalStringResult(result);
+ var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
+ var result = client?.ListIndex(key, index);
+ return MarshalString(result);
}
catch { return IntPtr.Zero; }
}
@@ -350,8 +518,8 @@ namespace FireflyClient
try
{
var client = GetClientFromHandle(handle);
- string key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
- string value = Marshal.PtrToStringUTF8(valuePtr) ?? string.Empty;
+ var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
+ var value = Marshal.PtrToStringUTF8(valuePtr) ?? string.Empty;
return client?.ListSet(key, index, value) ?? false;
}
catch { return false; }
@@ -373,8 +541,8 @@ namespace FireflyClient
try
{
var client = GetClientFromHandle(handle);
- string key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
- string element = Marshal.PtrToStringUTF8(elementPtr) ?? string.Empty;
+ var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
+ var element = Marshal.PtrToStringUTF8(elementPtr) ?? string.Empty;
// Note: C# ListPosition returns the first index or -1
var position = client?.ListPosition(key, element, rank, maxlen);
return position ?? -1;
@@ -396,7 +564,7 @@ namespace FireflyClient
try
{
var client = GetClientFromHandle(handle);
- string key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
+ var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
return client?.ListTrim(key, start, stop) ?? false;
}
catch { return false; }
@@ -416,8 +584,8 @@ namespace FireflyClient
try
{
var client = GetClientFromHandle(handle);
- string key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
- string element = Marshal.PtrToStringUTF8(elementPtr) ?? string.Empty;
+ var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
+ var element = Marshal.PtrToStringUTF8(elementPtr) ?? string.Empty;
return client?.ListRemove(key, count, element) ?? 0;
}
catch { return 0; }
@@ -439,9 +607,9 @@ namespace FireflyClient
try
{
var client = GetClientFromHandle(handle);
- string key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
- string field = Marshal.PtrToStringUTF8(fieldPtr) ?? string.Empty;
- string value = Marshal.PtrToStringUTF8(valuePtr) ?? string.Empty;
+ var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
+ var field = Marshal.PtrToStringUTF8(fieldPtr) ?? string.Empty;
+ var value = Marshal.PtrToStringUTF8(valuePtr) ?? string.Empty;
// Assuming HSET returns 1 if field is new, 0 if updated
return client?.HashSet(key, field, value) ?? false; // Adapt if C# returns differently
}
@@ -461,10 +629,10 @@ namespace FireflyClient
try
{
var client = GetClientFromHandle(handle);
- string key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
- string field = Marshal.PtrToStringUTF8(fieldPtr) ?? string.Empty;
- string? result = client?.HashGet(key, field);
- return MarshalStringResult(result);
+ var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
+ var field = Marshal.PtrToStringUTF8(fieldPtr) ?? string.Empty;
+ var result = client?.HashGet(key, field);
+ return MarshalString(result);
}
catch { return IntPtr.Zero; }
}
@@ -482,8 +650,8 @@ namespace FireflyClient
try
{
var client = GetClientFromHandle(handle);
- string key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
- string field = Marshal.PtrToStringUTF8(fieldPtr) ?? string.Empty;
+ var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
+ var field = Marshal.PtrToStringUTF8(fieldPtr) ?? string.Empty;
// Assuming HDEL returns true if field was deleted, false otherwise
return client?.HashDelete(key, field) ?? false;
}
@@ -503,8 +671,8 @@ namespace FireflyClient
try
{
var client = GetClientFromHandle(handle);
- string key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
- string field = Marshal.PtrToStringUTF8(fieldPtr) ?? string.Empty;
+ var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
+ var field = Marshal.PtrToStringUTF8(fieldPtr) ?? string.Empty;
return client?.HashFieldExists(key, field) ?? false;
}
catch { return false; }
@@ -524,8 +692,8 @@ namespace FireflyClient
try
{
var client = GetClientFromHandle(handle);
- string key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
- string fieldValuePairs = Marshal.PtrToStringUTF8(fieldValuePairsPtr) ?? string.Empty;
+ var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
+ var fieldValuePairs = Marshal.PtrToStringUTF8(fieldValuePairsPtr) ?? string.Empty;
string[] pairs = fieldValuePairs.Split(' ', StringSplitOptions.RemoveEmptyEntries);
if (pairs.Length % 2 != 0)
@@ -535,7 +703,7 @@ namespace FireflyClient
}
var dict = new Dictionary();
- for (int i = 0; i < pairs.Length; i += 2)
+ for (var i = 0; i < pairs.Length; i += 2)
{
dict[pairs[i]] = pairs[i + 1];
}
@@ -553,17 +721,19 @@ namespace FireflyClient
/// Pointer to a null-terminated UTF-8 string containing newline-delimited "field=value" pairs, allocated via AllocHGlobal (caller must free with FreeString), or IntPtr.Zero on error or if hash not found.
/// The returned IntPtr points to a single string. The native caller must parse this string (e.g., split by '\n', then by '=') and free the pointer using NativeFreeString.
[UnmanagedCallersOnly(EntryPoint = "HashGetAll")]
- public static IntPtr NativeHashGetAll(IntPtr handle, IntPtr keyPtr)
+ public static NativeDictionary NativeHashGetAll(IntPtr handle, IntPtr keyPtr)
{
- try
- {
- var client = GetClientFromHandle(handle);
- string key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
- Dictionary? result = client?.HashGetAll(key);
- // Marshal Dictionary as a single newline-delimited string
- return MarshalStringDictionaryResult(result);
- }
- catch { return IntPtr.Zero; }
+ try
+ {
+ var client = GetClientFromHandle(handle);
+ var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
+ Dictionary? result = client?.HashGetAll(key);
+ return MarshalDictionary(result);
+ }
+ catch
+ {
+ return new NativeDictionary { Pairs = IntPtr.Zero, Count = 0 };
+ }
}
// --- Pipeline Operations (Already Implemented) ---
@@ -625,8 +795,8 @@ namespace FireflyClient
try
{
var client = GetClientFromHandle(handle);
- string? response = client?.FlushPipeline();
- return MarshalStringResult(response);
+ var response = client?.FlushPipeline();
+ return MarshalString(response);
}
catch
{
@@ -703,7 +873,7 @@ namespace FireflyClient
try
{
var client = GetClientFromHandle(handle);
- string pattern = Marshal.PtrToStringUTF8(patternPtr) ?? "*";
+ var pattern = Marshal.PtrToStringUTF8(patternPtr) ?? "*";
List? result = client?.Keys(pattern);
return MarshalStringListResult(result);
}
diff --git a/src/Program.cs b/src/Program.cs
index e8c0534..b5213a1 100644
--- a/src/Program.cs
+++ b/src/Program.cs
@@ -7,9 +7,9 @@ namespace FireflyClient
///
/// Command-line interface for Firefly
///
- public class Program
+ public abstract class Program
{
- static async Task Main(string[] args)
+ private static async Task Main(string[] args)
{
Console.WriteLine("Firefly Client");
Console.WriteLine("Enter commands (type EXIT to quit)");
@@ -17,29 +17,28 @@ namespace FireflyClient
Console.WriteLine("Type HELP for basic commands or HELP EMAIL for email examples");
// Parse command line arguments
- string host = "127.0.0.1";
- int port = 6379;
- string password = string.Empty;
+ var host = "127.0.0.1";
+ var port = 6379;
+ var password = string.Empty;
- for (int i = 0; i < args.Length; i++)
+ for (var i = 0; i < args.Length; i++)
{
- if ((args[i] == "--host" || args[i] == "-h") && i + 1 < args.Length)
+ switch (args[i])
{
- host = args[++i];
- }
- else if ((args[i] == "--port" || args[i] == "-p") && i + 1 < args.Length && int.TryParse(args[i + 1], out int parsedPort))
- {
- port = parsedPort;
- i++;
- }
- else if ((args[i] == "--password" || args[i] == "--pass") && i + 1 < args.Length)
- {
- password = args[++i];
- }
- else if (args[i] == "--help" || args[i] == "-?")
- {
- PrintClientHelp();
- return;
+ case "--host" or "-h" when i + 1 < args.Length:
+ host = args[++i];
+ break;
+ case "--port" or "-p" when i + 1 < args.Length && int.TryParse(args[i + 1], out var parsedPort):
+ port = parsedPort;
+ i++;
+ break;
+ case "--password" or "--pass" when i + 1 < args.Length:
+ password = args[++i];
+ break;
+ case "--help":
+ case "-?":
+ PrintClientHelp();
+ return;
}
}
@@ -53,7 +52,7 @@ namespace FireflyClient
}
}
- static void PrintClientHelp()
+ private static void PrintClientHelp()
{
Console.WriteLine("\nFirefly Client Usage:");
Console.WriteLine(" --host, -h Server hostname or IP (default: 127.0.0.1)");
@@ -63,7 +62,7 @@ namespace FireflyClient
Console.WriteLine("Example: FireflyClient --host localhost --port 6380 --password secret123\n");
}
- static Task RunInteractiveClientAsync(string host, int port, string password)
+ private static Task RunInteractiveClientAsync(string host, int port, string password)
{
using var client = new FireflyClient(host, port);
Console.WriteLine($"Connecting to server at {host}:{port}...");
@@ -79,7 +78,7 @@ namespace FireflyClient
// Authenticate if password was provided
if (!string.IsNullOrEmpty(password))
{
- bool success = client.Authenticate(password);
+ var success = client.Authenticate(password);
// Check if authentication failed
if (!success)
@@ -93,15 +92,15 @@ namespace FireflyClient
{
// Get command from user
Console.Write("> ");
- string input = Console.ReadLine() ?? string.Empty;
+ var input = Console.ReadLine() ?? string.Empty;
if (string.IsNullOrEmpty(input) || input.Equals("EXIT", StringComparison.OrdinalIgnoreCase))
{
// Send QUIT command before exiting
try
{
- string response = client.ExecuteCommand("QUIT");
- Console.WriteLine($"Server: {response.TrimEnd('\r', '\n')}");
+ var response = client.ExecuteCommand("QUIT");
+ Console.WriteLine($"{response.TrimEnd('\r', '\n')}");
}
catch (Exception ex)
{
@@ -127,14 +126,14 @@ namespace FireflyClient
try
{
// Parse the command
- string[] parts = SplitCommandLine(input);
+ var parts = SplitCommandLine(input);
if (parts.Length == 0) continue;
- string command = parts[0];
+ var command = parts[0];
string[] args = [.. parts.Skip(1)];
// Execute the command
- string response = client.ExecuteCommand(command, args);
+ var response = client.ExecuteCommand(command, args);
FormatAndPrintResponse(response);
}
catch (Exception ex)
@@ -148,33 +147,31 @@ namespace FireflyClient
}
// Helper method to split command line respecting quotes
- static string[] SplitCommandLine(string commandLine)
+ private static string[] SplitCommandLine(string commandLine)
{
var result = new List();
- bool inQuotes = false;
+ var inQuotes = false;
StringBuilder currentArg = new();
- for (int i = 0; i < commandLine.Length; i++)
+ foreach (var c in commandLine)
{
- char c = commandLine[i];
-
- if (c == '"')
+ switch (c)
{
- inQuotes = !inQuotes;
- // Don't include the quote character
- }
- else if (c == ' ' && !inQuotes)
- {
- // End of argument
- if (currentArg.Length > 0)
+ case '"':
+ inQuotes = !inQuotes;
+ // Don't include the quote character
+ break;
+ case ' ' when !inQuotes:
{
+ // End of argument
+ if (currentArg.Length <= 0) continue;
result.Add(currentArg.ToString());
currentArg.Clear();
+ break;
}
- }
- else
- {
- currentArg.Append(c);
+ default:
+ currentArg.Append(c);
+ break;
}
}
@@ -186,8 +183,8 @@ namespace FireflyClient
return [.. result];
}
-
- static void FormatAndPrintResponse(string response)
+
+ private static void FormatAndPrintResponse(string response)
{
// Remove trailing whitespace
response = response.TrimEnd();
@@ -195,23 +192,20 @@ namespace FireflyClient
if (response.StartsWith('*') && response.Contains("\r\n"))
{
// Format array responses for better readability
- string[] parts = response.Split("\r\n");
+ var parts = response.Split("\r\n");
if (parts.Length > 1)
{
- Console.WriteLine("Server: Array response:");
- for (int i = 1; i < parts.Length; i++)
+ for (var i = 1; i < parts.Length; i++)
{
- if (!string.IsNullOrEmpty(parts[i]))
+ if (string.IsNullOrEmpty(parts[i])) continue;
+ // For simple string and bulk string responses in an array
+ if (parts[i].StartsWith('+') || parts[i].StartsWith('$'))
{
- // For simple string and bulk string responses in an array
- if (parts[i].StartsWith('+') || parts[i].StartsWith('$'))
- {
- Console.WriteLine($"{i}) {parts[i][1..]}");
- }
- else if (!parts[i].StartsWith('*')) // Skip the array count line
- {
- Console.WriteLine($"{i}) {parts[i]}");
- }
+ Console.WriteLine($"{i}) {parts[i][1..]}");
+ }
+ else if (!parts[i].StartsWith('*')) // Skip the array count line
+ {
+ Console.WriteLine($"{i}) {parts[i]}");
}
}
return;
@@ -219,11 +213,11 @@ namespace FireflyClient
}
// Default formatting
- Console.WriteLine($"Server: {response}");
+ Console.WriteLine($"{response}");
}
// Helper method: Print basic commands
- static void PrintBasicCommands()
+ private static void PrintBasicCommands()
{
Console.WriteLine("\n==== Basic Firefly Commands ====");
diff --git a/src/StringOperations.cs b/src/StringOperations.cs
index de56e3c..54d08ed 100644
--- a/src/StringOperations.cs
+++ b/src/StringOperations.cs
@@ -7,39 +7,29 @@ namespace FireflyClient
///
/// Sets a key-value pair
///
- public bool StringSet(string key, string value)
+ private bool StringSet(string key, string value)
{
- string response = ExecuteCommand("SET", key, value);
+ var response = ExecuteCommand("SET", key, value);
return response.StartsWith("+OK");
}
///
/// Gets a value by key
///
- public string StringGet(string key)
+ private string StringGet(string key)
{
- string response = ExecuteCommand("GET", key);
- if (response.StartsWith('+'))
- {
- return response[1..].TrimEnd('\r', '\n');
- }
- return string.Empty;
+ var response = ExecuteCommand("GET", key);
+ return response.StartsWith('+') ? response[1..].TrimEnd('\r', '\n') : string.Empty;
}
///
/// Deletes a key from all stores (string, list, hash)
///
- public int Delete(string key)
+ private int Delete(string key)
{
- string response = ExecuteCommand("DEL", key);
- if (response.StartsWith(':'))
- {
- if (int.TryParse(response[1..].TrimEnd('\r', '\n'), out int result))
- {
- return result;
- }
- }
- return 0;
+ var response = ExecuteCommand("DEL", key);
+ if (!response.StartsWith(':')) return 0;
+ return int.TryParse(response[1..].TrimEnd('\r', '\n'), out var result) ? result : 0;
}
#endregion