Containers.lua 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. ----------------------------------------------------------------------------------------------------
  2. --
  3. -- Copyright (c) Contributors to the Open 3D Engine Project.
  4. -- For complete copyright and license terms please see the LICENSE at the root of this distribution.
  5. --
  6. -- SPDX-License-Identifier: Apache-2.0 OR MIT
  7. --
  8. --
  9. --
  10. --
  11. --
  12. -- Description: Containers for Lua
  13. -- Docs:
  14. --
  15. ----------------------------------------------------------------------------------------------------
  16. local countTable = Set and Set.countTable or {};
  17. -- Create factory and management table
  18. Set = {};
  19. Set.countTable = countTable;
  20. -- All tables are registered in Set.countTable, therefore won't GCed!
  21. -- Unless we make their entries weak references. See Lua docs.
  22. -- The effect is that these references are ignored by the garbage collector.
  23. setmetatable(Set.countTable, { __mode="kv" });
  24. -- Create and return a new Set table
  25. Set.New = function()
  26. local nt = {}
  27. Set.countTable[nt] = 0
  28. return nt
  29. end
  30. Set.SerializeValues = function(tbl)
  31. local result = {}
  32. for k,v in pairs(tbl) do
  33. local item = {}
  34. table.insert( item, 1, k );
  35. table.insert( item, 2, v );
  36. table.insert( result, item );
  37. end
  38. return result
  39. end
  40. Set.DeserializeValues = function(tbl)
  41. local result = Set.New()
  42. for i,item in ipairs(tbl) do
  43. Set.Add( result, item[1], item[2] );
  44. end
  45. return result;
  46. end
  47. Set.DeserializeEntities = function(tbl)
  48. local result = Set.New()
  49. for i,entityID in ipairs(tbl) do
  50. Set.Add(result, entityID)
  51. end
  52. return result
  53. end
  54. Set.SerializeEntities = function(tbl)
  55. local result = {};
  56. for entityID,v in pairs(tbl) do
  57. table.insert(result, entityID)
  58. end
  59. return result;
  60. end
  61. Set.DeserializeItems = function(tbl)
  62. local result = Set.New();
  63. for i,item in ipairs(tbl) do
  64. if (item) then
  65. Set.Add(result, item);
  66. end
  67. end
  68. return result;
  69. end
  70. Set.SerializeItems = function(tbl)
  71. local result = {};
  72. local index = 1;
  73. for item,v in pairs(tbl) do
  74. result[index] = item;
  75. index = index + 1;
  76. end
  77. return result;
  78. end
  79. -- Is this table registered as a Set?
  80. -- When performance and best-effort execution are required, this function
  81. -- can easily be reduced to a stub or even all calls to it removed with regexp
  82. Set.Check = function(table)
  83. -- Run-time checking
  84. if (not Set.countTable[table]) then
  85. -- Throwing an error here would be preferable
  86. if (print) then
  87. print(tostring(table).."is not registered as a Set");
  88. else
  89. System.Log(tostring(table).."is not registered as a Set");
  90. end
  91. System.ShowDebugger()
  92. Set.throwAnError.throwAnError = true;
  93. end
  94. end
  95. -- Add an entry into a Set iff it does not already exist
  96. -- if v == nil, we set it to true - leaving it nil would break things
  97. -- Returns true on success, false if entry already exists
  98. Set.Add = function(table, k, v)
  99. v = v or true;
  100. Set.Check(table);
  101. if (not table[k]) then
  102. table[k] = v;
  103. Set.countTable[table] = Set.countTable[table] + 1;
  104. return true;
  105. end
  106. return false;
  107. end
  108. -- Removes an entry from a Set
  109. -- if k == nil, does nothing, which is debatable semantics
  110. -- Returns true on success, false if entry did not exist
  111. Set.Remove = function(table, k)
  112. Set.Check(table);
  113. if (table[k]) then
  114. table[k] = nil;
  115. Set.countTable[table] = Set.countTable[table] - 1;
  116. return true;
  117. end
  118. return false;
  119. end
  120. -- Get a value from a Set
  121. -- Return the value or nil if none
  122. Set.Get = function(table, k)
  123. Set.Check(table);
  124. return table[k]
  125. end
  126. -- Set the value associated with a key, even if it already exists
  127. -- if v == nil, we set it to true - leaving it nil would break things
  128. -- Returns true if it did already exist, false if not
  129. Set.Set = function(table, k, v)
  130. v = v or true;
  131. Set.Check(table);
  132. if (not table[k]) then
  133. table[k] = v;
  134. Set.countTable[table] = Set.countTable[table] + 1;
  135. return false;
  136. else
  137. -- Replace, no need to change count
  138. table[k] = v;
  139. return true;
  140. end
  141. end
  142. -- Get the number of entries in the Set
  143. Set.Size = function(table)
  144. Set.Check(table);
  145. return Set.countTable[table];
  146. end
  147. -- Remove all entries
  148. -- No return value
  149. Set.RemoveAll = function(table)
  150. Set.Check(table);
  151. for k,v in pairs(table) do
  152. table[k] = nil;
  153. end
  154. Set.countTable[table] = 0;
  155. end
  156. -- Add entries of one Set into another
  157. -- Keys shared by both will not be overwritten
  158. -- Returns true iff keys were disjoint, none shared
  159. Set.Merge = function(dest, source)
  160. local disjoint = true;
  161. Set.Check(dest);
  162. Set.Check(source);
  163. for k,v in pairs(source) do
  164. if (not Set.Add(dest, k, v)) then
  165. disjoint = false;
  166. end
  167. end
  168. return disjoint;
  169. end
  170. -- Iterating:
  171. -- For now, treat a Set as any table.
  172. -- This should change.
  173. -- Utility/debugging function:
  174. -- Sanity check a suspicious Set
  175. -- Tries to find evidence of tampering
  176. -- Returns true iff it passes
  177. Set.SanityCheck = function(table)
  178. -- Check this was created as a Set
  179. Set.Check(table);
  180. -- Find as many things as you can in the Set
  181. local count = 0;
  182. for i,v in pairs(table) do
  183. count = count + 1;
  184. end
  185. local size = Set.Size(table);
  186. if (count ~= size) then
  187. System.Log("[Set] Sanity check failed - Set size is "..tostring(size)..", counted "..count);
  188. return false;
  189. end
  190. -- Check that all entries have the same type
  191. -- This is just good practice, rather than an error
  192. local indexType = nil;
  193. local valueType = nil;
  194. for i,v in pairs(table) do
  195. -- Check index
  196. if (indexType) then
  197. if (indexType ~= type(i)) then
  198. System.Log("[Set] Sanity check failed - Found indices of both types "..indexType.." and "..type(i));
  199. return false;
  200. end
  201. else
  202. indexType = type(i);
  203. end
  204. -- Check value
  205. if (valueType) then
  206. if (valueType ~= type(v)) then
  207. System.Log("[Set] Sanity check failed - Found values of both types "..valueType.." and "..type(v));
  208. return false;
  209. end
  210. else
  211. valueType = type(v);
  212. end
  213. end
  214. return true;
  215. end
  216. -- Unit test
  217. -- Return true for success or false on failure
  218. Set.Test = function(fullTest)
  219. local A = Set.New();
  220. local B = Set.New();
  221. if ( Set.Add(A, "key1", 1) == false ) then return false; end
  222. if ( Set.Add(A, "key2") == false ) then return false; end
  223. if ( Set.Add(A, "key3", 3) == false ) then return false; end
  224. if ( Set.Add(A, "key1", 1) == true ) then return false; end
  225. if ( Set.Remove(A, "key1") == false ) then return false; end
  226. if ( Set.Get(A, "key2")== false ) then return false; end
  227. if ( Set.Get(A, "key1") ~= nil ) then return false; end
  228. if ( Set.Get(A, "key3") ~= 3 ) then return false; end
  229. if ( Set.Size(A) ~= 2 ) then return false; end
  230. Set.RemoveAll(A);
  231. if ( Set.Size(A) ~= 0 ) then return false; end
  232. if ( Set.Add(A, "key1", 1) == false ) then return false; end
  233. if ( Set.Set(A, "key1", 9) == false ) then return false; end
  234. if ( Set.Set(A, "key0", 9) == true ) then return false; end
  235. if ( Set.Add(A, "keyF", 3) == false ) then return false; end
  236. if ( Set.Size(A) ~= 3 ) then return false; end
  237. if ( Set.Add(B, "key3", 3) == false ) then return false; end
  238. if ( Set.Add(B, "key4", 4) == false ) then return false; end
  239. if ( Set.Add(B, "key5", 5) == false ) then return false; end
  240. if ( Set.Add(B, "key3", 3) == true ) then return false; end
  241. if ( Set.Set(B, "key9") == true ) then return false; end
  242. if ( Set.Get(B, "key9") == nil ) then return false; end
  243. if (not Set.Merge(A,B)) then return false; end
  244. if (Set.Size(A) ~= 7) then return false; end
  245. if (Set.Merge(A,B)) then return false; end
  246. if (Set.Size(A) ~= 7) then return false; end
  247. if (fullTest) then
  248. -- Check countTable and GC (slow)
  249. -- Shouldn't assume there are no Sets when testing
  250. collectgarbage();
  251. local baseCount = 0;
  252. for i,v in pairs(Set.countTable) do
  253. baseCount = baseCount + 1;
  254. end
  255. B = nil;
  256. collectgarbage();
  257. local count = 0;
  258. for i,v in pairs(Set.countTable) do
  259. count = count + 1;
  260. end
  261. if (baseCount - count ~= 1) then return false; end
  262. end
  263. -- Move this line around for testing
  264. do return true; end
  265. return true;
  266. end
  267. -- Perform quick version of unit test
  268. if (not Set.Test()) then
  269. System.Log("Containers: ... Error - Failed Unit Test");
  270. else
  271. --System.Log("Containers: ... End loading!");
  272. end
  273. -- Useful testing lines in Editor:
  274. -- #Script.ReloadScript("Scripts/Utils/Containers.lua");
  275. -- #System.Log(tostring(Set.Test(true)))