-
Notifications
You must be signed in to change notification settings - Fork 1
/
LuaHashMap.h
4613 lines (4266 loc) · 228 KB
/
LuaHashMap.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
LuaHashMap
Copyright (C) 2011-2012 PlayControl Software, LLC.
Eric Wing <ewing . public @ playcontrol.net>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
/**
@mainpage LuaHashMap: An easy to use hash table library for C
Introduction:
=============
LuaHashMap is a hash table library/implementation for use in C. Since C lacks a hash table in the standard library,
this library provides a way to fill that hole.
But instead of reinventing the wheel again to implement a hash table for C,
LuaHashMap cleverly wraps Lua to leverage a proven implementation, while providing a friendly C API for hash tables
without needing to learn/use the low-level Lua C-API.
For those not familar with Lua,
Lua is a scripting language implemented in pure ANSI C designed to be embedded in larger applications.
It is the de-facto scripting language used in the video game industry for the past 15 years
(e.g. Escape from Monkey Island, World of Warcraft, Angry Birds)
as well as other non-gaming high profile products such as Adobe Lightroom, Wireshark, and MediaWiki (Wikipedia).
Lua is fast, small, lightweight, simple, and under the MIT license.
And tables are the sole data structure in Lua so the underlying hash table is
fast, reliable, well documented, understood, and proven.
So rather than reinventing a new hash table implementation,
LuaHashMap simply wraps Lua's to mimimize bugs and leverage known performance and behaviors.
But to you (the end user/developer), LuaHashMap provides a simple/straight-forward C API
for hash tables and Lua is simply an implementation detail, so if you don't know anything
about Lua, it is not a problem.
(But for those wondering about the implementation details,
LuaHashMap itself is completely written in C and uses Lua's C API
exclusively without any actual Lua script code.)
LuaHashMap is designed to be easy to use and convenient.
LuaHashMap can work with keys and values of the following types: strings, numbers, and pointers.
The API provides explicit function names for each permutation of types to
avoid macro hell and hard to debug situations.
(Though C11 users, check out the _Generic macros to use C++ like overloading while still preserving a degree of type safety, and without all the namemangling problems.)
And there are no hashing functions you need to figure-out/write/provide. (All of this was already done/tuned by Lua itself.)
Audience:
=========
LuaHashMap is ideal for projects that may agree with one the following:
- Need a portable hash table for C
- Need a hash table for C++ but can't use the STL or templates (yes, this happens)
- Want a really friendly, simple to use API/interface (you don't need to know anything about Lua)
- Want an easy to integrate API that doesn't require you to modify or wrap all your key objects to conform to an interface
- Don't want to be bothered with writing your own hashing functions
- Want to explicit support and optimizations for different types (numbers, strings, pointers)
- Want an explicit, type safe API that doesn't use macro-hell
- Want to mix different types (numbers, strings, pointers) in the same hash table (and avoid casting everything to the same type)
- Already using Lua elsewhere in your project or are considering using Lua
- Not already using Lua but don't think the ~100-200KB library (disk) size of Lua is a big deal. Lua is ~100KB for the core, and ~100KB for the standard library which is not needed by LuaHashMap. (pfft! My icon takes more space.)
- Don't mind the ~4KB overhead (RAM size) of creating a Lua state instance. (Seriously, Apple Mac icons are full color 1024x1024 now.)
- Want a time proven, heavily tested/used, and documented implementation (i.e. Lua)
- Want well-known/predictable behavior and performance characteristics
- Need a library under a permissive license (MIT)
- Like overall good performance (but doesn't necessarily need to be the best)
- Want something with minimal dependencies and easy to embed in your application
LuaHashMap may not be ideal for projects that:
- Need to fine-tune every single possible detail for the utmost performance (speed/memory/battery)
- Not already using Lua and bothered by the ~100-200KB disk space of including Lua
- Can't afford the ~4KB overhead for a Lua state instance
- Have alternative hash table libraries that better suit your specific needs
Portability Notes:
==================
- LuaHashMap is compatible with both Lua 5.1 and Lua 5.2 (though you must recompile LuaHashMap if you switch between them).
- LuaHashMap was written to be portable to all platforms with at least a C89 compiler (because Microsoft Visual Studio refuses to update their pathetic C compiler).
- LuaHashMap was developed with clang, gcc, and Visual Studio on Mac, iOS, Linux, Android, and Windows.
- However, there is an underlying desire to modernize and utilize C99 and C11 features when available so there are conditionals in the code.
Programming Overview:
=====================
LuaHashMap provides explicit interfaces for 4 types: integers, floating point, strings, and pointers.
There is no macro-hell here. (Yes, I typed-out/implemented all the permutations. You can thank me later.)
So you get nice straight-forward compiler warnings and more type safety.
The general pattern for all the permutations of the APIs is:
LuaHashMap_SetValue<TV>ForKey<TK>
where TV is the value type and TK is the key type.
The names are "String", "Pointer", "Number" (for floating point), and "Integer".
There are two general ways to access the hash map, either by key or by iterator.
The following example inserts values into the table with string keys.
@code
LuaHashMap* hash_map = LuaHashMap_Create();
LuaHashMap_SetValueNumberForKeyString(hash_map, 3.99, "milk");
LuaHashMap_SetValueNumberForKeyString(hash_map, 4.349, "gas");
LuaHashMap_SetValueNumberForKeyString(hash_map, 2.99, "bread");
printf("Price of gas: %lf\n", LuaHashMap_GetValueNumberForKeyString(hash_map, "gas"));
LuaHashMap_Free(hash_map);
@endcode
Iterators in LuaHashMap are conceptually similar to the C++ STL notion of an iterator (but much simpler).
They let you iterate (traverse) through a hash table and let you get and set values.
Here's a similar example that prints everything in the hash table using an iterator.
@code
LuaHashMap* hash_map = LuaHashMap_Create();
LuaHashMap_SetValueNumberForKeyString(hash_map, 3.99, "milk");
LuaHashMap_SetValueNumberForKeyString(hash_map, 4.349, "gas");
LuaHashMap_SetValueNumberForKeyString(hash_map, 2.99, "bread");
// This loop will iterate through the whole table and print out each entry.
LuaHashMapIterator hash_iterator = LuaHashMap_GetIteratorAtBegin(hash_map);
do
{
printf("Price of %s: %lf\n",
LuaHashMap_GetKeyStringAtIterator(&hash_iterator),
LuaHashMap_GetCachedValueNumberAtIterator(&hash_iterator));
} while(LuaHashMap_IteratorNext(&hash_iterator));
LuaHashMap_Free(hash_map);
@endcode
Of course this is just a taste. There are a lot more APIs available to make this a full featured library.
More on Iterators:
------------------
Iterators are the way to iterate/traverse through a hash table.
To keep things simple, notice that the GetIterator family of functions all return a full copy of a struct (LuaHashMapIterator).
A major concept with iterators to notice is they are simply a struct that are intended to live on the stack when you use them.
This allows you to use iterators without worrying about leaking.
They are also generally seen as short term, inexpensive objects you should be able to refetch at will (an O(1) lookup by key).
Functions that operate on Iterators like IteratorNext() operate on the iterator by reference so it can change the data in the struct. The pattern is to pass your struct in by reference:
LuaHashMap_IteratorNext(&hash_iterator)
In Lua (and thus LuaHashMap), it is safe to remove a key/value entry from the hash table while iterating through it. Note that Lua does not reshuffle entries or compact the table when clearing entries.
Unlike removing, it is not safe to add new entries to the table while iterating because the table may reshuffle.
Also of note, there are two ways to extract values from iterators:
- LuaHashMap_GetCachedValue<TV>AtIterator
- LuaHashMap_GetValue<TV>AtIterator
The "Cached" value is the value copied in the struct when the iterator was last created/iterated/updated.
The non-cached version incurs a full lookup in the hash to find the value.
Generally, if you have the iterator and haven't modified the hash table behind the back of the iterator
(e.g. use a non-iterator LuaHashMap function to change or remove a value), the cached value is correct and will be faster.
Proper programming style of the LuaHashMap iterator functions should keep the "Cached" value up to date so you should rarely (if ever) need the non-cached version.
Iterator Patterns:
------------------
This demonstrates a typical pattern for using iterators to traverse an entire hash map. You get an iterator at the begin position and another at the end position. You increment your begin position iterator with ItereratorNext() as long as it does not equal the end iterator.
@code
// Showing a different way to loop
for(LuaHashMapIterator hash_iterator = LuaHashMap_GetIteratorAtBegin(hash_map), hash_iterator_end = LuaHashMap_GetIteratorAtEnd(hash_map);
! LuaHashMap_IteratorIsEqual(&hash_iterator, &hash_iterator_end);
LuaHashMap_IteratorNext(&hash_iterator)
)
{
fprintf(stderr, "Price of %s: %lf\n",
LuaHashMap_GetKeyStringAtIterator(&hash_iterator),
LuaHashMap_GetCachedValueNumberAtIterator(&hash_iterator));
}
@endcode
Alternatively, if you know you have at least one entry in the hash, this is a slightly more succinct pattern which uses a do-while loop and the return value of IteratorNext.
@code
LuaHashMapIterator hash_iterator = LuaHashMap_GetIteratorAtBegin(hash_map);
do
{
fprintf(stderr, "Price of %s: %lf\n",
LuaHashMap_GetKeyStringAtIterator(&hash_iterator),
LuaHashMap_GetCachedValueNumberAtIterator(&hash_iterator));
} while(LuaHashMap_IteratorNext(&hash_iterator));
@endcode
Another pattern is to get a value only if it exists. The non-iterator versions of the GetKey family of functions can't distinguish between a non-existent entry and a NULL/0/empty value.
While you could use the ExistsKey family of functions and then get the value with the GetKey family of functions, this will cause you to do two hash table look ups instead of just one.
Instead, you should get the iterator and use the ExistsAtIterator function followed by GetCachedValue so only one hash table look up is done.
@code
// This is the one hash look up.
LuaHashMapIterator hash_iterator = LuaHashMap_GetIteratorForKeyInteger(hash_map, 10);
// Thie remaining information is in the iterator itself which avoids another hash lookup.
bool exists = LuaHashMap_ExistsAtIterator(&hash_iterator);
if(exists)
{
fprintf(stderr, "The value for key=10 exists: %lf\n", LuaHashMap_GetCachedValueNumberAtIterator(&hash_iterator));
}
else
{
fprintf(stderr, "The key/value pair for key=10 does not exist\n");
}
@endcode
Extra Lua Details:
==================
I said you didn't need to know anything about Lua. Well, that's mostly true,
but there are some interesting details you should know about in Lua which may be useful.
lua_Number and lua_Integer
--------------------------
Canonical (unmodified) Lua only has one number type, which by default is double.
Double has enough bits to store a 32-bit integer without any loss of precision which is why this works.
But you may notice that LuaHashMap has APIs to deal with both lua_Number (floating point) and lua_Integer (integer).
Canonical Lua converts everything to lua_Number so if you provide a lua_Number key that happens to be the same value as a lua_Integer key,
these may in fact be the same key.
(But if you are using a patched version of Lua (e.g. LNUM) Lua that may avoid conversion and keep types separated.
Please note that some of the iterator based functions in LuaHashMap may still convert due to the lack of a formal way to detect integer types.)
Lua internalizes strings
------------------------
As an implementation detail, all strings in Lua are internalized which means there is only one immutable instance of a string within Lua for duplicate strings.
This potentially can be exploited to yield further optimizations in your code.
LuaHashMap does not rely on this fact,
but do note that the APIs return the internalized string pointer when you add a key (which may be different than the one you fed the API).
This was intended to allow you to exploit this fact if you choose in your own code.
However, if you use alternative implementations of Lua, they are not guaranteed to behave in this same way.
Memory management for strings
-----------------------------
Related to Lua's string internalization, for LuaHashMap, this means when you add a string into the hash table,
Lua creates its own copy so you don't need to hold on to the original.
But be careful. If you Get a string from LuaHashMap and then completely remove the entries that are holding it,
the memory could be deleted out from under you if you still are holding on to the string pointer and trying to use it.
Lua does not have a moving garbage collector nor does it move its memory around invalidating pointers.
------------------------------------------------------------------------------------------------------
LuaHashMap does exploit an implementation detail of Lua for functions that return strings,
such as const char* LuaHashMap_GetValueStringForKeyString.
In the Lua API, you communicate between C and Lua using a stack API provided by Lua.
When you are finished with an operation, the stack should generally be returned to the state you started in.
That means when this function finishes, the Lua stack should be reset.
Technically, the pointer to string that is returned is not guaranteed by the Lua specification to still be valid and the pointer could be dangling.
But as an implementation detail, the Lua authors have stated that this pointer is still valid as long as the string is still in Lua (e.g. not removed).
It is likely that all implementations of Lua share this behavior (since moving collectors are hard to deal with in C for this exact reason of pointers),
so this implementation detail is probably safe. However, if you do use a completely different implementation of Lua that is not from PUC-Rio, you may want to verify this behavior.
Lua tables have an array optimization.
--------------------------------------
If you create a hash with sequential, continuous integer keys, you may trigger an optimization in Lua which treats that as an array.
This will yield very fast performance as Lua is working with C arrays instead of hashing.
It is safe/supported to remove keys from a hash while iterating it. (But adding keys while iterating is not supported.)
-----------------------------------------------------------------------------------------------------------------------
This is a behavior of Lua which LuaHashMap keeps. (It is related to the fact that Lua does not recompact tables when keys are removed.)
Lua does not recompact/reshuffle tables when you remove keys.
------------------------------------------------------------
Also note that the LuaHashMap_Clear() function will only clear the entries from the table. The number of allocated buckets will not shrink.
If you want to clear all the entries and free the memory, use LuaHashMap_Purge().
Read "Lua Gems"
---------------
The book "Lua Gems" gives wonderful insights to how tables are designed and work in Lua, and what their behavior and performance characteristics are.
The free sample chapter tells you everything you need to know.
http://www.lua.org/gems/sample.pdf
Advanced tricks:
================
LuaHashMap_CreateShare:
-----------------------
Unfortunately, using the standard API, each LuaHashMap instance also creates an entirely new Lua virtual machine instance.
On a 64-bit machine, this amounts to about 4-5KB of RAM (measured on a Mac). For large data sets, you will not notice,
but for small data sets and a lot of separate instances of LuaHashMap, this might eventually add up.
So an addition to the standard API, a CreateShare API exists to allow you to create a new instance of LuaHashMap
without creating an entirely new virtual machine instance. Instead it will simply create a new Lua table instance
in the existing virtual machine and not incur any more RAM overhead.
To the rest of the LuaHashMap API, everything is transparent and requires no other special handling (everything except CreateShare and FreeShare).
The hash map instance looks like a separate instance with its own hash and you may think about them in those terms.
Technically speaking, the original and shared maps are peers of each other. The implementation does not make a distinction
about which one the original is so any hash map with the lua_State you want to share may be passed in as the parameter.
Every CreateShare should be balanced by FreeShare. Free should balance the original Create and should be the very last thing to be called.
(Free will destroy the entire virtual machine instance as it calls lua_close().)
@code
LuaHashMap* first_hash_map = LuaHashMap_Create();
LuaHashMap* second_hash_map = LuaHashMap_CreateShare(first_hash_map);
LuaHashMap* third_hash_map = LuaHashMap_CreateShare(second_hash_map);
LuaHashMap* fourth_hash_map = LuaHashMap_CreateShare(first_hash_map);
// (do stuff as you normally do to use each hash map)
// When done, free resources.
LuaHashMap_FreeShare(fourth_hash_map);
LuaHashMap_FreeShare(second_hash_map);
LuaHashMap_FreeShare(third_hash_map);
// Make sure that LuaHashMap_Free is called last, after all the shares are closed.
LuaHashMap_Free(first_hash_map);
@endcode
Mixed Types in the same hash map:
---------------------------------
Lua supports mixed types (i.e. numbers, strings, pointers) in the same table.
However you need to be careful about doing this with LuaHashMap since typing systems are not as dynamic/automatic in C.
Putting in mixed types is easy. You simply call the normal API functions as you always do:
@code
LuaHashMap_SetValueStringForKeyString(hash_map, "string_value1", "string_key1");
LuaHashMap_SetValuePointerForKeyNumber(hash_map, (void*)0x2, 2.0);
LuaHashMap_SetValueStringForKeyPointer(hash_map, "string_value3", (void*)0x3);
// You can even change the value type for an existing key
LuaHashMap_SetValueNumberForKeyString(hash_map, 4.0, "string_key1");
@endcode
But accessing the key/value pairs is the real trick. The fundamental rule is that you must call the correct type function to access the value.
So if you know for a fact that for the "string_key1" is a number type (from the last line above), then you can do as you always do.
@code
// The last time we set this key, the value was a number, so call the GetValueNumber version of the function.
// Don't call the the GetValueString or GetValuePointer version in this case; the behavior is undefined.
luaNumber the_number = LuaHashMap_GetValueNumberForKeyString(hash_map, "string_key1");
@endcode
In the case where you don't know what the key types and value types are, iterators are the solution.
To help support mixed types, three functions are provided:
- int LuaHashMap_GetKeyTypeAtIterator(const LuaHashMapIterator* hash_iterator);
- int LuaHashMap_GetValueTypeAtIterator(LuaHashMapIterator* hash_iterator);
- int LuaHashMap_GetCachedValueTypeAtIterator(const LuaHashMapIterator* hash_iterator);
These return the int values defined by Lua which are LUA_TSTRING, LUA_TNUMBER, LUA_TLIGHTUSERDATA for strings, numbers, and pointers respectively.
You may have noticed that there is no distinct type for integers in this list.
This is an extremely important point. You must understand that Lua only has one number type and thus integers and numbers
(doubles in stock Lua) are the same. So an integer key of say 100 would be the same as a number key of 100.00,
so be very careful about understanding which of your keys are unique.
In addition, when you Get a number, there is no identifying information (in stock Lua) about whether the number was originally an
integer or number, so it is up to you to decide if that number is really an integer or not.
That said, here is an example that iterates through a collection and dynamically figures out the correct types and calls the correct API functions to extract the values.
@code
LuaHashMapIterator shared_iterator = LuaHashMap_GetIteratorAtBegin(hash_map);
// Assumes at least 1 entry in the collection
do
{
int keytype = LuaHashMap_GetKeyTypeAtIterator(&shared_iterator);
int valuetype = LuaHashMap_GetCachedValueTypeAtIterator(&shared_iterator);
switch(keytype)
{
case LUA_TLIGHTUSERDATA:
fprintf(stderr, "\tKeyPointer:%zd", LuaHashMap_GetKeyPointerAtIterator(&shared_iterator));
break;
case LUA_TNUMBER:
fprintf(stderr, "\tKeyNumber:%lf", LuaHashMap_GetKeyNumberAtIterator(&shared_iterator));
break;
case LUA_TSTRING:
fprintf(stderr, "\tKeyString:%s", LuaHashMap_GetKeyStringAtIterator(&shared_iterator));
break;
}
switch(valuetype)
{
case LUA_TLIGHTUSERDATA:
fprintf(stderr, "\tValuePointer:%zd", LuaHashMap_GetCachedValuePointerAtIterator(&shared_iterator));
break;
case LUA_TNUMBER:
fprintf(stderr, "\tValueNumber:%lf", LuaHashMap_GetCachedValueNumberAtIterator(&shared_iterator));
break;
case LUA_TSTRING:
fprintf(stderr, "\tValueString:%s", LuaHashMap_GetCachedValueStringAtIterator(&shared_iterator));
break;
}
fprintf(stderr, "\n");
} while(LuaHashMap_IteratorNext(&shared_iterator));
@endcode
So while possible to mix types in a single hash map instance, you may find it cumbersome to deal with depending on what you are doing.
You may find keeping separate hash map instances may be easier to work with.
But if mixing types is advantageous for you, then this is a powerful feature of Lua/LuaHashMap at your disposal.
C++ STL like interface (Experimental):
--------------------------------------
For kicks and giggles, there is also a STL (C++ Standard Template Library) like template interface wrapper
included which gives an STL hash_map/unordered_map like interface for LuaHashMap.
Theoretically you might be able to switch between implementations fairly easily using a strategic typedef.
(Though if you can use the STL, I'm not sure why you would be here. And yes, this STL interface was a colossal waste of time.)
This probably won't be maintained over the long haul.
@code
// Create
lhm::lua_hash_map<lua_Number, lua_Number> hash_map;
hash_map.insert(std::pair<lua_Number, lua_Number>(1.1, 1.1));
hash_map.insert(std::pair<lua_Number, lua_Number>(2.2, 2.2));
hash_map.insert(std::pair<lua_Number, lua_Number>(3.3, 3.3));
size_t ret_val = hash_map.size();
assert(3 == ret_val);
// find key 1.1
lhm::lua_hash_map<lua_Number, lua_Number>::iterator iter;
iter = hash_map.find(1.1);
std::cerr << "*iter (pair)=" << (*iter).first << ", " << (*iter).second << std::endl;
assert(1.1 == (*iter).second);
// erase key 1.1 with an iterator
ret_val = hash_map.erase(iter);
assert(1 == ret_val);
ret_val = hash_map.size();
assert(2 == ret_val);
// Erase key 3.3 by key
ret_val = hash_map.erase(3.3);
assert(1 == ret_val);
// Remove everything
hash_map.clear();
assert(true == hash_map.empty());
@endcode
C11 _Generic (Generic Selection) (Experimental):
-----------------------------
Yes, I said there is "no macro-hell" earlier. But trust me; this isn't really that bad and it's optional. (And it's really quite cool.)
C11 introduces _Generic which extends the C preprocessor to allow you to map different functions to a single macro function name based on their parameter types.
Essentially this brings C++-like function overloading to C, but without all the name-mangling problems and other C++ baggage.
And this was a lot less code than the C++ partial template specialization mentioned above.
So for example, these 16 following functions:
- LuaHashMap_SetValueStringForKeyString
- LuaHashMap_SetValueStringForKeyPointer
- LuaHashMap_SetValueStringForKeyNumber
- LuaHashMap_SetValueStringForKeyInteger
- LuaHashMap_SetValuePointerForKeyString
- LuaHashMap_SetValuePointerForKeyPointer
- LuaHashMap_SetValuePointerForKeyNumber
- LuaHashMap_SetValuePointerForKeyInteger
- LuaHashMap_SetValueNumberForKeyString
- LuaHashMap_SetValueNumberForKeyPointer
- LuaHashMap_SetValueNumberForKeyNumber
- LuaHashMap_SetValueNumberForKeyInteger
- LuaHashMap_SetValueIntegerForKeyString
- LuaHashMap_SetValueIntegerForKeyPointer
- LuaHashMap_SetValueIntegerForKeyNumber
- LuaHashMap_SetValueIntegerForKeyInteger
can now be called with just this one macro:
- LuaHashMap_SetValueForKey(hash_map, value, key);
The preprocessor/compiler will look at your types and automatically inject the correct version of the function to call.
We can take this a step further with a few more macro tricks from C99.
Utilizing these features, we can also handle variable number of arguments
to also unify the WithLength versions of the String permutations:
- LuaHashMap_SetValueStringForKeyStringWithLength
- LuaHashMap_SetValueStringForKeyPointerWithLength
- LuaHashMap_SetValueStringForKeyNumberWithLength
- LuaHashMap_SetValueStringForKeyIntegerWithLength
- LuaHashMap_SetValuePointerForKeyStringWithLength
- LuaHashMap_SetValueNumberForKeyStringWithLength
- LuaHashMap_SetValueIntegerForKeyStringWithLength
as well as the Iterator versions:
- LuaHashMap_SetValueStringAtIterator
- LuaHashMap_SetValueStringAtIteratorWithLength
- LuaHashMap_SetValuePointerAtIterator
- LuaHashMap_SetValueNumberAtIterator
- LuaHashMap_SetValueIntegerAtIterator
So all of these can be combined and reduced down to just:
- LuaHashMap_SetValue(...)
So for example:
@code
LuaHashMap_SetValue(hash_map, 1, 2.2); // LuaHashMap_SetValueIntegerForKeyNumber
LuaHashMap_SetValue(hash_map, (const char*)"hello", 0xABCD); // LuaHashMap_SetValueStringForKeyInteger
LuaHashMap_SetValue(hash_map, (const char*)"hello", (void*)0xABCDE, strlen("hello")); // LuaHashMap_SetValueStringForKeyPointerWithLength
LuaHashMap_SetValue(hash_map, (const char*)"cat", (const char*)"animal", strlen("cat"), strlen("animal")); // LuaHashMap_SetValueStringForKeyStringWithLength
LuaHashMap_SetValue(&iterator, (const char*)"goodbye"); // LuaHashMap_SetValueStringAtIterator
@endcode
@note Please watch out for string literals. They technically are not const char*, but an array type which I can't seem to express. So you should use an explict cast for those or they get mapped to the void* (Pointer) rule.
For convenience, if C11 _Generic support is detected on your compiler, LUAHASHMAP_SUPPORTS_GENERICS will be defined to 1.
Refer to the API documentation to see all the macros available.
Compiler flag to put instances in the Lua global table instead of the registry (Experimental):
----------------------------------------------------------------------------------------------
This is another (experimental) flag designed to put your table instances in the main Lua script's global table instead of the (private) registry.
The advantage of this is if you are using Lua in your project already and have Lua tables in your scripts that you would like to easily interoperate with on the C side.
To activate this, recompile with the flag LUAHASHMAP_USE_GLOBAL_TABLE defined.
Also of related note, there are defines for LUAHASHMAP_SETTABLE and LUAHASHMAP_GETTABLE in the implementation which are set to lua_rawset and lua_rawget.
Particularly if you are doing advanced things with tables in your scripts, you may want metamethod behaviors (which raw* bypasses for speed).
So in that case, you'll want to redefine these to lua_settable and lua_gettable.
Performance Benchmarks:
=======================
Yes, I actually did benchmarks.
Check out the main site:
http://playcontrol.net/opensource/LuaHashMap
Unit Tests:
===========
Yes, I actually did unit tests too. You might refer to these for more examples on how to use LuaHashMap.
- luahashmap.c: The first test program written, which started turning into a small benchmark program in some places.
- luahashmap.cpp: Uses the C++ STL-like template interface wrapper. The coverage is pretty exhaustive and manages to hit most of the core C API indirectly.
- luahashmapshared.c: Tests the CreateShare APIs. Mixed typed tests are also in here.
- luahashmap_c11: Tests the C11 _Generic macros. This covers every API that is put into a convenience macro which is the majority of the API.
Note: Depending on your compiler and platform, you may see a lot of compiler warnings in the test programs.
These are from using fprintf to print values to the console for visual spot checking/testing.
To make these compile cleanly, C99 format tokens are very convenient to handle unpredicatable sizes for standard typedefs.
But since this code needs to compile on legacy compilers, it is not possible to take advantage of these.
(The C11 test should compile cleanly on C11 compilers.)
The actual LuaHashMap library should compile cleanly so these test warnings are not really an issue.
Future Directions:
==================
I think LuaHashMap is pretty much done.
- There may still be some minor optimization work that can be done (maybe allow safety checks to be disabled via compile flag).
- There maybe some tweaking on C11 features on _Generics to tighten up the "default" rules.
- Investigate using a metamethod to keep an up-to-date count of the number of elements for those who really need an O(1) access. This would probably be a compile time switch which would leverage the ability to swap out rawset.
- Investigate adding a new explicit type for LuaHashMap instances in tables. While this can be done with Pointer, maybe there is some utility in keeping a one-to-one mapping of Lua tables with LuaHashMaps, particularly if using backdoor access to interoperate directly with Lua. (There is probably little demand for this.)
- The one major thing I would like to see (but I don't have the time or expertise to do) is see if LuaHashMap could be implemented
by ripping out just the table implementation from Lua and removing all the other unnecessary stuff. I would love to shrink both the
disk size profile as well as the memory overhead needed by having an entire virtual machine. I would like to be able to create lots of stand-alone instances of LuaHashMap
without worrying that I'm wasting memory or need to resort to the current CreateShare workaround.
So if anybody is interested, I would love to see it.
Additional Resoruces and References:
====================================
Official LuaHashMap Site
http://playcontrol.net/opensource/LuaHashMap
Lua Performance Tips (explains tables)
http://www.lua.org/gems/sample.pdf
Mailing List Reference: How is string passed from Lua to C
http://lua-users.org/lists/lua-l/2011-12/msg00222.html
Programming in Lua
http://www.lua.org/pil/
Lua Reference Manual
http://www.lua.org/manual/5.1/manual.html
Please look at LuaHashMap.h for the complete list of API functions.
And for more examples, refer to the unit tests in the repository.
@author Eric Wing <ewing . public - at - playcontrol . net>
*/
#ifndef C_LUA_HASH_MAP_H
#define C_LUA_HASH_MAP_H
#ifdef __cplusplus
extern "C" {
#endif
#ifndef DOXYGEN_SHOULD_IGNORE_THIS
/** @cond DOXYGEN_SHOULD_IGNORE_THIS */
/* Note: For Doxygen to produce clean output, you should set the
* PREDEFINED option to remove DECLSPEC, CALLCONVENTION, and
* the DOXYGEN_SHOULD_IGNORE_THIS blocks.
* PREDEFINED = DOXYGEN_SHOULD_IGNORE_THIS=1 LUAHASHMAP_EXPORT= LUAHASHMAP_BUILD_AS_DLL=
*/
#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L)
/* Not ISO/IEC 9899:1999-compliant. */
#if !defined(restrict)
#define restrict
#define __LUAHASHMAP_RESTRICT_KEYWORD_DEFINED__
#endif
#if !defined(bool)
#define bool char
#define __LUAHASHMAP_BOOL_KEYWORD_DEFINED__
#endif
#if !defined(false)
#define false (bool)0
#define __LUAHASHMAP_FALSE_KEYWORD_DEFINED__
#endif
#if !defined(true)
#define true (bool)1
#define __LUAHASHMAP_TRUE_KEYWORD_DEFINED__
#endif
#else
#include <stdbool.h>
#endif
#include <stddef.h>
/* This define should generally not be used.
It is here mostly for hacking/experimentation and dirty tricks to get going for non-production code.
Make sure these defines match what was actually used in your Lua library.
*/
#if defined(LUAHASHMAP_DONT_EXPOSE_LUA_HEADER)
#if !defined(lua_Number)
#define lua_Number double
#define __LUAHASHMAP_LUA_NUMBER_DEFINED__
#endif
#if !defined(lua_Integer)
#define lua_Integer ptrdiff_t
#define __LUAHASHMAP_LUA_INTEGER_DEFINED__
#endif
#if !defined(lua_State)
#define lua_State void
#define __LUAHASHMAP_LUA_STATE_DEFINED__
#endif
/* Some of my inline functions use these Lua type (int) definitions */
#if !defined(LUA_TNONE)
#define LUA_TNONE (-1)
#define __LUAHASHMAP_LUA_TNONE_DEFINED__
#endif
#if !defined(LUA_TLIGHTUSERDATA)
#define LUA_TLIGHTUSERDATA 2
#define __LUAHASHMAP_LUA_TLIGHTUSERDATA_DEFINED__
#endif
#if !defined(LUA_TNUMBER)
#define LUA_TNUMBER 3
#define __LUAHASHMAP_LUA_TNUMBER_DEFINED__
#endif
#if !defined(LUA_TSTRING)
#define LUA_TSTRING 4
#define __LUAHASHMAP_LUA_TSTRING_DEFINED__
#endif
#if !defined(LUA_NOREF)
#define LUA_NOREF (-2)
#define __LUAHASHMAP_LUA_NOREF_DEFINED__
#endif
#if !defined(lua_h) /* You detect nor undo a typedef */
typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
#define __LUAHASHMAP_LUA_ALLOC_DEFINED__
#endif
#else
#include "lua.h"
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
/* I only need this for the define of LUA_NOREF which is only needed here for my inline function which requires C99. */
#include "lauxlib.h"
#endif
#endif
/** Windows needs to know explicitly which functions to export in a DLL. */
#ifdef LUAHASHMAP_BUILD_AS_DLL
#ifdef WIN32
#define LUAHASHMAP_EXPORT __declspec(dllexport)
#elif defined(__GNUC__) && __GNUC__ >= 4
#define LUAHASHMAP_EXPORT __attribute__ ((visibility("default")))
#else
#define LUAHASHMAP_EXPORT
#endif
#else
#define LUAHASHMAP_EXPORT
#endif /* LUAHASHMAP_BUILD_AS_DLL */
/** @endcond DOXYGEN_SHOULD_IGNORE_THIS */
#endif /* DOXYGEN_SHOULD_IGNORE_THIS */
/**
* Struct that contains the version information of this library.
* This represents the library's version as three levels: major revision
* (increments with massive changes, additions, and enhancements),
* minor revision (increments with backwards-compatible changes to the
* major revision), and patchlevel (increments with fixes to the minor
* revision).
* @see LUAHASHMAP_GET_COMPILED_VERSION, LuaHashMap_GetLinkedVersion
*/
typedef struct LuaHashMapVersion
{
unsigned char major;
unsigned char minor;
unsigned char patch;
} LuaHashMapVersion;
/* Printable format: "%d.%d.%d", MAJOR, MINOR, PATCHLEVEL
*/
#define LUAHASHMAP_MAJOR_VERSION 1
#define LUAHASHMAP_MINOR_VERSION 0
#define LUAHASHMAP_PATCHLEVEL 0
/**
* This macro fills in a version structure with the version of the
* library you compiled against. This is determined by what header the
* compiler uses. Note that if you dynamically linked the library, you might
* have a slightly newer or older version at runtime. That version can be
* determined with LuaHashMap_GetLinkedVersion(), which, unlike
* LUAHASHMAP_GET_COMPILED_VERSION, is not a macro.
*
* @param X A pointer to a LuaHashMapVersion struct to initialize.
*
* @see LuaHashMapVersion, LuaHashMap_GetLinkedVersion
*/
#define LUAHASHMAP_GET_COMPILED_VERSION(X) \
{ \
(X)->major = LUAHASHMAP_MAJOR_VERSION; \
(X)->minor = LUAHASHMAP_MINOR_VERSION; \
(X)->patch = LUAHASHMAP_PATCHLEVEL; \
}
/**
* Gets the library version of the dynamically linked library you are using.
* This gets the version of the library that is linked against your program.
* If you are using a shared library (DLL) version, then it is
* possible that it will be different than the version you compiled against.
*
* This is a real function; the macro LUAHASHMAP_GET_COMPILED_VERSION
* tells you what version of the library you compiled against:
*
* @code
* LuaHashMapVersion compiled;
* LuaHashMapVersion linked;
*
* LUAHASHMAP_GET_COMPILED_VERSION(&compiled);
* LuaHashMap_GetLinkedVersion(&linked);
* printf("We compiled against version %d.%d.%d ...\n",
* compiled.major, compiled.minor, compiled.patch);
* printf("But we linked against version %d.%d.%d.\n",
* linked.major, linked.minor, linked.patch);
* @endcode
*
* @see LuaHashMapVersion, LUAHASHMAP_GET_COMPILED_VERSION
*/
LUAHASHMAP_EXPORT const LuaHashMapVersion* LuaHashMap_GetLinkedVersion(void);
typedef struct LuaHashMap LuaHashMap;
typedef int LuaHashMap_InternalGlobalKeyType;
/* This exists because anonymous inline structs are not available until C11 */
struct LuaHashMapStringContainer
{
size_t stringLength;
const char* stringPointer;
};
typedef struct LuaHashMapStringContainer LuaHashMapStringContainer;
/* This exists because anonymous inline unions are not available until C11 */
union LuaHashMapKeyValueType
{
/* const char* theString; */
/* If the size of lua_Number is the same or greater than the StringContainer, we succeed in saving some space using a union.
* This can happen in 32-bit where double is 8-bytes and size_t + pointer is 4-bytes + 4-bytes == 8 bytes. */
LuaHashMapStringContainer theString;
lua_Number theNumber;
/* lua_Integer theInteger; */
void* thePointer;
};
/**
* Defines the iterator type for LuaHashMap.
* Iterators are the way to iterate through a hash table.
* Iterators in LuaHashMap conceptually similar to the C++ STL notion of an iterator (but much simpler).
*
* Mental Model: LuaHashMapIterators (unlike LuaHashMap) are stack objects. No dynamic memory is required.
* This allows you to use iterators without worrying about freeing them when done and leaking.
* They are also generally seen as short term, inexpensive objects you should be able to refetch at will (an O(1) lookup by key).
* (But it is certainly possible to create iterators with dynamic memory and keep them around. Just watch out for them getting stale.)
*
* Best practice is to use these structs as opaque objects. Use the API functions to retrieve values from the struct/iterators instead of direct access.
*
* Trivia: You may notice that iterator functions that operate on the LuaHashMap instance don't require an explicit LuaHashMap instance
* passed into the function. This is because LuaHashMapIterator saves a copy of the LuaHashMap pointer when it was created.
*/
struct LuaHashMapIterator
{
/* These are all implementation details.
* You should probably not directly touch.
*/
union LuaHashMapKeyValueType currentKey;
union LuaHashMapKeyValueType currentValue;
LuaHashMap* hashMap;
LuaHashMap_InternalGlobalKeyType whichTable;
int keyType;
int valueType;
bool atEnd;
bool isNext;
};
typedef struct LuaHashMapIterator LuaHashMapIterator;
/** @defgroup Create Create family of functions
* @{
*/
/**
* Creates a new instance of a hash table.
* This creates a new instance of a LuaHashMap.
* You will use this as your opaque LuaHashMap "object" for all the per-instance hash map operations.
* @return Returns a pointer to the LuaHashMap instance created or NULL if a failure.
*
* @see LuaHashMap_Free, LuaHashMap_CreateWithAllocator, LuaHashMap_CreateWithSizeHints, LuaHashMap_CreateWithAllocatorAndSizeHints, LuaHashMap_CreateShare, LuaHashMap_CreateShareWithSizeHints, LuaHashMap_CreateShareFromLuaState, uaHashMap_CreateShareFromLuaStateWithAllocatorAndSizeHints, LuaHashMap_CreateShareFromLuaStateWithSizeHints
*/
LUAHASHMAP_EXPORT LuaHashMap* LuaHashMap_Create(void);
/**
* Creates a new instance of a hash table with a custom allocator.
* This creates a new instance of a LuaHashMap with a custom allocator.
* The allocator adopts the lua_Alloc model as defined by Lua.
*
* You will use this as your opaque LuaHashMap "object" for all the per-instance hash map operations.
*
* @param the_allocator The custom memory allocator you want to provide.
* @param user_data A user data/context pointer to go with the allocator.
* @return Returns a pointer to the LuaHashMap instance created or NULL if a failure.
*
* @see LuaHashMap_Free, LuaHashMap_Create, LuaHashMap_CreateWithSizeHints, LuaHashMap_CreateWithAllocatorAndSizeHints, LuaHashMap_CreateShare, LuaHashMap_CreateShareWithSizeHints
*/
LUAHASHMAP_EXPORT LuaHashMap* LuaHashMap_CreateWithAllocator(lua_Alloc the_allocator, void* user_data);
/**
* Creates a new instance of a hash table with a suggested starting size.
* This creates a new instance of a LuaHashMap with a hint to Lua on how many elements to pre-size the hash table for.
* If you know the size your hash is going to grow to, this you should specify this number as a performance optimization.
* Growing the hash table after running out of buckets is generally an expensive operation so pre-sizing it can help.
*
* You will use this as your opaque LuaHashMap "object" for all the per-instance hash map operations.
*
* @param number_of_array_elements Lua tables double as both arrays and hash tables in Lua. As such, this parameter let's you pre-size the number of array elements. Most users of this library will probably use 0 because they don't need the array part.
* @param number_of_hash_elements This parameter is used to pre-size the number of hash buckets.
* @return Returns a pointer to the LuaHashMap instance created or NULL if a failure.
*
* @note Read the Lua Gems free chapter for more information about how Lua tables work.
* @see LuaHashMap_Free, LuaHashMap_Create, LuaHashMap_CreateWithAllocator, LuaHashMap_CreateWithAllocatorAndSizeHints, LuaHashMap_CreateShare, LuaHashMap_CreateShareWithSizeHints
*/
LUAHASHMAP_EXPORT LuaHashMap* LuaHashMap_CreateWithSizeHints(int number_of_array_elements, int number_of_hash_elements);
/**
* Creates a new instance of a hash table with a custom allocator and a suggested starting size.
* This creates a new instance of a LuaHashMap with a custom allocator.
* The allocator adopts the lua_Alloc model as defined by Lua.
* This also let's you hint Lua on how many elements to pre-size the hash table for.
* If you know the size your hash is going to grow to, this you should specify this number as a performance optimization.
* Growing the hash table after running out of buckets is generally an expensive operation so pre-sizing it can help.
*
* You will use this as your opaque LuaHashMap "object" for all the per-instance hash map operations.
*
* @param the_allocator The custom memory allocator you want to provide.
* @param user_data A user data/context pointer to go with the allocator.*
* @param number_of_array_elements Lua tables double as both arrays and hash tables in Lua. As such, this parameter let's you pre-size the number of array elements. Most users of this library will probably use 0 because they don't need the array part.
* @param number_of_hash_elements This parameter is used to pre-size the number of hash buckets.
* @return Returns a pointer to the LuaHashMap instance created or NULL if a failure.
*
* @see LuaHashMap_Free, LuaHashMap_Create, LuaHashMap_CreateWithAllocator, LuaHashMap_CreateWithSizeHints, LuaHashMap_CreateShare, LuaHashMap_CreateShareWithSizeHints
*/
LUAHASHMAP_EXPORT LuaHashMap* LuaHashMap_CreateWithAllocatorAndSizeHints(lua_Alloc the_allocator, void* user_data, int number_of_array_elements, int number_of_hash_elements);
/**
* Special Memory Optimization: Allows you to create new LuaHashMaps from an existing one which will share the same lua_State under the hood.
* Currently, every LuaHashMap instance created with the Create family of functions (excluding CreateShare) will create a brand new
* virtual machine (lua_State*). My measurements of a new lua_State instance seem to take about 4-5KB on 64-bit Mac.
* This function was designed to let you avoid incuring that cost by letting you reuse another LuaHashMap's lua_State.
* But for all other purposes (besides freeing), your hash map instances will appear completely independent and the API calls you make don't change in any other way.
*
@code
LuaHashMap* first_hash_map = LuaHashMap_Create();
LuaHashMap* second_hash_map = LuaHashMap_CreateShare(first_hash_map);
LuaHashMap* third_hash_map = LuaHashMap_CreateShare(second_hash_map);
LuaHashMap* fourth_hash_map = LuaHashMap_CreateShare(first_hash_map);
// (do stuff as you normally do to use each hash map)
// When done, free resources.
LuaHashMap_FreeShare(fourth_hash_map);
LuaHashMap_FreeShare(second_hash_map);
LuaHashMap_FreeShare(third_hash_map);
// Make sure that LuaHashMap_Free is called last, after all the shares are closed.
LuaHashMap_Free(first_hash_map);
@endcode
*
* @param original_hash_map The hash map you want to reuse a lua_State from.
* @return Returns a pointer to the LuaHashMap instance created or NULL if a failure.
*
* @note Technically speaking, the original and shared maps are peers of each other. The implementation does not make a distinction
* about which one the original is so any hash map with the lua_State you want to share may be passed in as the parameter.
* Make sure to free any shared maps with FreeShare() before you close the final hash map with Free() as Free() calls lua_close() which destroys the lua_State.
*
* @see LuaHashMap_FreeShare, LuaHashMap_Free, LuaHashMap_CreateShareWithSizeHints, LuaHashMap_CreateShareFromLuaState, LuaHashMap_CreateShareFromLuaStateWithAllocatorAndSizeHints, LuaHashMap_CreateShareFromLuaStateWithSizeHints, LuaHashMap_CreateShareFromLuaState.
*/
LUAHASHMAP_EXPORT LuaHashMap* LuaHashMap_CreateShare(LuaHashMap* original_hash_map);
/**
* Just like LuaHashMap_CreateShare, except it allows you to pre-size the hash map.
*
* @param original_hash_map The hash map you want to reuse a lua_State from.
* @param number_of_array_elements Lua tables double as both arrays and hash tables in Lua. As such, this parameter let's you pre-size the number of array elements. Most users of this library will probably use 0 because they don't need the array part.
* @param number_of_hash_elements This parameter is used to pre-size the number of hash buckets.
* @return Returns a pointer to the LuaHashMap instance created or NULL if a failure.
*
* @see LuaHashMap_FreeShare, LuaHashMap_Free, LuaHashMap_CreateShare, LuaHashMap_CreateWithSizeHints, LuaHashMap_CreateShareFromLuaState
*/
LUAHASHMAP_EXPORT LuaHashMap* LuaHashMap_CreateShareWithSizeHints(LuaHashMap* original_hash_map, int number_of_array_elements, int number_of_hash_elements);
/**
* Special Memory Optimization: Allows you to create new LuaHashMaps from an existing lua_State.
* This will create a new LuaHashMap instance from a pre-existing lua_State with the intention of saving memory.
* Currently, every LuaHashMap instance created with the Create family of functions (excluding CreateShare) will create a brand new
* virtual machine (lua_State*). My measurements of a new lua_State instance seem to take about 4-5KB on 64-bit Mac.
* This function was designed to let you avoid incuring that cost by letting you reuse a lua_State that you may already have for other purposes.
* But for all other purposes (besides freeing), your hash map instances will appear completely independent and the API calls you make don't change in any other way.
* LuaHashMap uses the Lua registry with luaL_ref/luaL_unref (to get a unique table), so it should not collide or intefere with anything you are doing in your Lua state.
*
* @param lua_state A lua_State you wish to use/share with your hash map
* @return Returns a pointer to the LuaHashMap instance created or NULL if a failure.
* @note Use LuaHashMap_FreeShare to free the hash_map when you are done. You must do this before you close your lua_State.
*
* @warning This is for very advanced use cases that want to directly interact with the Lua State. An understanding of the implementation details of LuaHashMap is strongly recommended in order to avoid trampling over each other.
* @see LuaHashMap_FreeShare, LuaHashMap_Free, LuaHashMap_CreateShare, LuaHashMap_CreateWithSizeHints, LuaHashMap_CreateShareFromLuaState
*/
LUAHASHMAP_EXPORT LuaHashMap* LuaHashMap_CreateShareFromLuaState(lua_State* lua_state);
/**
* Just like LuaHashMap_CreateShareFromLuaState but lets you specify your own allocator and pre-size hints.
* @param lua_state A lua_State you wish to use/share with your hash map
* @param the_allocator The custom memory allocator you want to provide.
* @param user_data A user data/context pointer to go with the allocator.*
* @param number_of_array_elements Lua tables double as both arrays and hash tables in Lua. As such, this parameter let's you pre-size the number of array elements. Most users of this library will probably use 0 because they don't need the array part.
* @param number_of_hash_elements This parameter is used to pre-size the number of hash buckets.
* @return Returns a pointer to the LuaHashMap instance created or NULL if a failure.
*
* @note Use LuaHashMap_FreeShare to free the hash_map when you are done. You must do this before you close your lua_State.
*
* @warning This is for very advanced use cases that want to directly interact with the Lua State. An understanding of the implementation details of LuaHashMap is strongly recommended in order to avoid trampling over each other.
* @see LuaHashMap_FreeShare, LuaHashMap_Free, LuaHashMap_CreateShare, LuaHashMap_CreateWithAllocatorAndSizeHints, LuaHashMap_CreateShareFromLuaState
*/
LUAHASHMAP_EXPORT LuaHashMap* LuaHashMap_CreateShareFromLuaStateWithAllocatorAndSizeHints(lua_State* lua_state, lua_Alloc the_allocator, void* user_data, int number_of_array_elements, int number_of_hash_elements);
/**
* Just like LuaHashMap_CreateShareFromLuaState but lets you specify your own pre-size hints.