@@ -19,6 +19,11 @@ public async Task<ValkeyResult> InvokeScriptAsync(
1919 CommandFlags flags = CommandFlags . None ,
2020 CancellationToken cancellationToken = default )
2121 {
22+ if ( script == null )
23+ {
24+ throw new ArgumentNullException ( nameof ( script ) ) ;
25+ }
26+
2227 Utils . Requires < NotImplementedException > ( flags == CommandFlags . None , "Command flags are not supported by GLIDE" ) ;
2328 return await InvokeScriptInternalAsync ( script . Hash , null , null , null ) ;
2429 }
@@ -30,6 +35,16 @@ public async Task<ValkeyResult> InvokeScriptAsync(
3035 CommandFlags flags = CommandFlags . None ,
3136 CancellationToken cancellationToken = default )
3237 {
38+ if ( script == null )
39+ {
40+ throw new ArgumentNullException ( nameof ( script ) ) ;
41+ }
42+
43+ if ( options == null )
44+ {
45+ throw new ArgumentNullException ( nameof ( options ) ) ;
46+ }
47+
3348 Utils . Requires < NotImplementedException > ( flags == CommandFlags . None , "Command flags are not supported by GLIDE" ) ;
3449 return await InvokeScriptInternalAsync ( script . Hash , options . Keys , options . Args , null ) ;
3550 }
@@ -43,17 +58,23 @@ private async Task<ValkeyResult> InvokeScriptInternalAsync(
4358 // Convert hash to C string
4459 IntPtr hashPtr = Marshal . StringToHGlobalAnsi ( hash ) ;
4560
61+ // Track allocated memory for cleanup
62+ IntPtr [ ] ? keyPtrs = null ;
63+ IntPtr keysPtr = IntPtr . Zero ;
64+ IntPtr keysLenPtr = IntPtr . Zero ;
65+ IntPtr [ ] ? argPtrs = null ;
66+ IntPtr argsPtr = IntPtr . Zero ;
67+ IntPtr argsLenPtr = IntPtr . Zero ;
68+
4669 try
4770 {
4871 // Prepare keys
49- IntPtr keysPtr = IntPtr . Zero ;
50- IntPtr keysLenPtr = IntPtr . Zero ;
5172 ulong keysCount = 0 ;
5273
5374 if ( keys != null && keys . Length > 0 )
5475 {
5576 keysCount = ( ulong ) keys . Length ;
56- IntPtr [ ] keyPtrs = new IntPtr [ keys . Length ] ;
77+ keyPtrs = new IntPtr [ keys . Length ] ;
5778 ulong [ ] keyLens = new ulong [ keys . Length ] ;
5879
5980 for ( int i = 0 ; i < keys . Length ; i ++ )
@@ -72,14 +93,12 @@ private async Task<ValkeyResult> InvokeScriptInternalAsync(
7293 }
7394
7495 // Prepare args
75- IntPtr argsPtr = IntPtr . Zero ;
76- IntPtr argsLenPtr = IntPtr . Zero ;
7796 ulong argsCount = 0 ;
7897
7998 if ( args != null && args . Length > 0 )
8099 {
81100 argsCount = ( ulong ) args . Length ;
82- IntPtr [ ] argPtrs = new IntPtr [ args . Length ] ;
101+ argPtrs = new IntPtr [ args . Length ] ;
83102 ulong [ ] argLens = new ulong [ args . Length ] ;
84103
85104 for ( int i = 0 ; i < args . Length ; i ++ )
@@ -129,12 +148,70 @@ private async Task<ValkeyResult> InvokeScriptInternalAsync(
129148 }
130149 finally
131150 {
132- // Free allocated memory
133- if ( hashPtr != IntPtr . Zero )
151+ FreeScriptMemory ( hashPtr , keyPtrs , keysPtr , keysLenPtr , argPtrs , argsPtr , argsLenPtr ) ;
152+ }
153+ }
154+
155+ /// <summary>
156+ /// Frees all allocated memory for script invocation.
157+ /// </summary>
158+ private static void FreeScriptMemory (
159+ IntPtr hashPtr ,
160+ IntPtr [ ] ? keyPtrs ,
161+ IntPtr keysPtr ,
162+ IntPtr keysLenPtr ,
163+ IntPtr [ ] ? argPtrs ,
164+ IntPtr argsPtr ,
165+ IntPtr argsLenPtr )
166+ {
167+ // Free hash
168+ if ( hashPtr != IntPtr . Zero )
169+ {
170+ Marshal . FreeHGlobal ( hashPtr ) ;
171+ }
172+
173+ // Free individual key strings
174+ if ( keyPtrs != null )
175+ {
176+ foreach ( IntPtr ptr in keyPtrs )
134177 {
135- Marshal . FreeHGlobal ( hashPtr ) ;
178+ if ( ptr != IntPtr . Zero )
179+ {
180+ Marshal . FreeHGlobal ( ptr ) ;
181+ }
136182 }
137- // TODO: Free keys and args memory
183+ }
184+
185+ // Free keys array and lengths
186+ if ( keysPtr != IntPtr . Zero )
187+ {
188+ Marshal . FreeHGlobal ( keysPtr ) ;
189+ }
190+ if ( keysLenPtr != IntPtr . Zero )
191+ {
192+ Marshal . FreeHGlobal ( keysLenPtr ) ;
193+ }
194+
195+ // Free individual arg strings
196+ if ( argPtrs != null )
197+ {
198+ foreach ( IntPtr ptr in argPtrs )
199+ {
200+ if ( ptr != IntPtr . Zero )
201+ {
202+ Marshal . FreeHGlobal ( ptr ) ;
203+ }
204+ }
205+ }
206+
207+ // Free args array and lengths
208+ if ( argsPtr != IntPtr . Zero )
209+ {
210+ Marshal . FreeHGlobal ( argsPtr ) ;
211+ }
212+ if ( argsLenPtr != IntPtr . Zero )
213+ {
214+ Marshal . FreeHGlobal ( argsLenPtr ) ;
138215 }
139216 }
140217
@@ -280,14 +357,10 @@ public async Task<string> FunctionFlushAsync(
280357 public async Task < ValkeyResult > ScriptEvaluateAsync ( string script , ValkeyKey [ ] ? keys = null , ValkeyValue [ ] ? values = null ,
281358 CommandFlags flags = CommandFlags . None )
282359 {
283- if ( string . IsNullOrEmpty ( script ) )
284- {
285- throw new ArgumentException ( "Script cannot be null or empty" , nameof ( script ) ) ;
286- }
287-
288360 Utils . Requires < NotImplementedException > ( flags == CommandFlags . None , "Command flags are not supported by GLIDE" ) ;
289361
290362 // Use the optimized InvokeScript path via Script object
363+ // Script constructor will validate the script parameter
291364 using Script scriptObj = new ( script ) ;
292365
293366 // Convert keys and values to string arrays
@@ -302,15 +375,10 @@ public async Task<ValkeyResult> ScriptEvaluateAsync(string script, ValkeyKey[]?
302375 public async Task < ValkeyResult > ScriptEvaluateAsync ( byte [ ] hash , ValkeyKey [ ] ? keys = null , ValkeyValue [ ] ? values = null ,
303376 CommandFlags flags = CommandFlags . None )
304377 {
305- if ( hash == null || hash . Length == 0 )
306- {
307- throw new ArgumentException ( "Hash cannot be null or empty" , nameof ( hash ) ) ;
308- }
309-
310378 Utils . Requires < NotImplementedException > ( flags == CommandFlags . None , "Command flags are not supported by GLIDE" ) ;
311379
312380 // Convert hash to hex string
313- string hashString = BitConverter . ToString ( hash ) . Replace ( "-" , "" ) . ToLowerInvariant ( ) ;
381+ string hashString = Convert . ToHexStringLower ( hash ) ;
314382
315383 // Convert keys and values to string arrays
316384 string [ ] ? keyStrings = keys ? . Select ( k => k . ToString ( ) ) . ToArray ( ) ;
@@ -369,7 +437,7 @@ public async Task<ValkeyResult> ScriptEvaluateAsync(LoadedLuaScript script, obje
369437
370438 // Convert the hash from byte[] to hex string
371439 // The hash in LoadedLuaScript is the hash of the script that was actually loaded on the server
372- string hashString = BitConverter . ToString ( script . Hash ) . Replace ( "-" , "" ) . ToLowerInvariant ( ) ;
440+ string hashString = Convert . ToHexStringLower ( script . Hash ) ;
373441
374442 // Use InvokeScriptInternalAsync with the hash from LoadedLuaScript
375443 // The script was already loaded on the server, so EVALSHA will work
0 commit comments