-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor(tools): separate table related functions from utils #11723
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,323 @@ | ||
local type = type | ||
local pairs = pairs | ||
local ipairs = ipairs | ||
local select = select | ||
local tostring = tostring | ||
local insert = table.insert | ||
local setmetatable = setmetatable | ||
local getmetatable = getmetatable | ||
|
||
|
||
local _M = {} | ||
|
||
|
||
--- packs a set of arguments in a table. | ||
-- Explicitly sets field `n` to the number of arguments, so it is `nil` safe | ||
_M.pack = function(...) return {n = select("#", ...), ...} end | ||
|
||
|
||
--- unpacks a table to a list of arguments. | ||
-- Explicitly honors the `n` field if given in the table, so it is `nil` safe | ||
_M.unpack = function(t, i, j) return unpack(t, i or 1, j or t.n or #t) end | ||
|
||
|
||
--- Merges two table together. | ||
-- A new table is created with a non-recursive copy of the provided tables | ||
-- @param t1 The first table | ||
-- @param t2 The second table | ||
-- @return The (new) merged table | ||
function _M.table_merge(t1, t2) | ||
local res = {} | ||
if t1 then | ||
for k,v in pairs(t1) do | ||
res[k] = v | ||
end | ||
end | ||
if t2 then | ||
for k,v in pairs(t2) do | ||
res[k] = v | ||
end | ||
end | ||
return res | ||
end | ||
|
||
|
||
--- Checks if a value exists in a table. | ||
-- @param arr The table to use | ||
-- @param val The value to check | ||
-- @return Returns `true` if the table contains the value, `false` otherwise | ||
function _M.table_contains(arr, val) | ||
if arr then | ||
for _, v in pairs(arr) do | ||
if v == val then | ||
return true | ||
end | ||
end | ||
end | ||
return false | ||
end | ||
|
||
|
||
do | ||
local floor = math.floor | ||
local max = math.max | ||
|
||
local is_array_fast = require "table.isarray" | ||
|
||
local is_array_strict = function(t) | ||
local m, c = 0, 0 | ||
for k in pairs(t) do | ||
if type(k) ~= "number" or k < 1 or floor(k) ~= k then | ||
return false | ||
end | ||
m = max(m, k) | ||
c = c + 1 | ||
end | ||
return c == m | ||
end | ||
|
||
local is_array_lapis = function(t) | ||
if type(t) ~= "table" then | ||
return false | ||
end | ||
local i = 0 | ||
for _ in pairs(t) do | ||
i = i + 1 | ||
if t[i] == nil and t[tostring(i)] == nil then | ||
return false | ||
end | ||
end | ||
return true | ||
end | ||
|
||
--- Checks if a table is an array and not an associative array. | ||
-- @param t The table to check | ||
-- @param mode: `"strict"`: only sequential indices starting from 1 are allowed (no holes) | ||
-- `"fast"`: OpenResty optimized version (holes and negative indices are ok) | ||
-- `"lapis"`: Allows numeric indices as strings (no holes) | ||
-- @return Returns `true` if the table is an array, `false` otherwise | ||
function _M.is_array(t, mode) | ||
if type(t) ~= "table" then | ||
return false | ||
end | ||
|
||
if mode == "lapis" then | ||
return is_array_lapis(t) | ||
end | ||
|
||
if mode == "fast" then | ||
return is_array_fast(t) | ||
end | ||
|
||
return is_array_strict(t) | ||
end | ||
end | ||
|
||
|
||
--- Checks if a table is an array and not an associative array. | ||
-- *** NOTE *** string-keys containing integers are considered valid array entries! | ||
-- @param t The table to check | ||
-- @return Returns `true` if the table is an array, `false` otherwise | ||
function _M.is_lapis_array(t) | ||
if type(t) ~= "table" then | ||
return false | ||
end | ||
local i = 0 | ||
for _ in pairs(t) do | ||
i = i + 1 | ||
if t[i] == nil and t[tostring(i)] == nil then | ||
return false | ||
end | ||
end | ||
return true | ||
end | ||
|
||
|
||
--- Deep copies a table into a new table. | ||
-- Tables used as keys are also deep copied, as are metatables | ||
-- @param orig The table to copy | ||
-- @param copy_mt Copy metatable (default is true) | ||
-- @return Returns a copy of the input table | ||
function _M.deep_copy(orig, copy_mt) | ||
if copy_mt == nil then | ||
copy_mt = true | ||
end | ||
local copy | ||
if type(orig) == "table" then | ||
copy = {} | ||
for orig_key, orig_value in next, orig, nil do | ||
copy[_M.deep_copy(orig_key)] = _M.deep_copy(orig_value, copy_mt) | ||
end | ||
if copy_mt then | ||
setmetatable(copy, _M.deep_copy(getmetatable(orig))) | ||
end | ||
else | ||
copy = orig | ||
end | ||
return copy | ||
end | ||
|
||
|
||
do | ||
local clone = require "table.clone" | ||
|
||
--- Copies a table into a new table. | ||
-- neither sub tables nor metatables will be copied. | ||
-- @param orig The table to copy | ||
-- @return Returns a copy of the input table | ||
function _M.shallow_copy(orig) | ||
local copy | ||
if type(orig) == "table" then | ||
copy = clone(orig) | ||
else -- number, string, boolean, etc | ||
copy = orig | ||
end | ||
return copy | ||
end | ||
end | ||
|
||
|
||
--- Merges two tables recursively | ||
-- For each sub-table in t1 and t2, an equivalent (but different) table will | ||
-- be created in the resulting merge. If t1 and t2 have a sub-table with the | ||
-- same key k, res[k] will be a deep merge of both sub-tables. | ||
-- Metatables are not taken into account. | ||
-- Keys are copied by reference (if tables are used as keys they will not be | ||
-- duplicated) | ||
-- @param t1 one of the tables to merge | ||
-- @param t2 one of the tables to merge | ||
-- @return Returns a table representing a deep merge of the new table | ||
function _M.deep_merge(t1, t2) | ||
local res = _M.deep_copy(t1) | ||
|
||
for k, v in pairs(t2) do | ||
if type(v) == "table" and type(res[k]) == "table" then | ||
res[k] = _M.deep_merge(res[k], v) | ||
else | ||
res[k] = _M.deep_copy(v) -- returns v when it is not a table | ||
end | ||
end | ||
|
||
return res | ||
end | ||
|
||
|
||
--- Cycle aware deep copies a table into a new table. | ||
-- Cycle aware means that a table value is only copied once even | ||
-- if it is referenced multiple times in input table or its sub-tables. | ||
-- Tables used as keys are not deep copied. Metatables are set to same | ||
-- on copies as they were in the original. | ||
-- @param orig The table to copy | ||
-- @param remove_metatables Removes the metatables when set to `true`. | ||
-- @param deep_copy_keys Deep copies the keys (and not only the values) when set to `true`. | ||
-- @param cycle_aware_cache Cached tables that are not copied (again). | ||
-- (the function creates this table when not given) | ||
-- @return Returns a copy of the input table | ||
function _M.cycle_aware_deep_copy(orig, remove_metatables, deep_copy_keys, cycle_aware_cache) | ||
if type(orig) ~= "table" then | ||
return orig | ||
end | ||
|
||
cycle_aware_cache = cycle_aware_cache or {} | ||
if cycle_aware_cache[orig] then | ||
return cycle_aware_cache[orig] | ||
end | ||
|
||
local copy = _M.shallow_copy(orig) | ||
|
||
cycle_aware_cache[orig] = copy | ||
|
||
local mt | ||
if not remove_metatables then | ||
mt = getmetatable(orig) | ||
end | ||
|
||
for k, v in pairs(orig) do | ||
if type(v) == "table" then | ||
copy[k] = _M.cycle_aware_deep_copy(v, remove_metatables, deep_copy_keys, cycle_aware_cache) | ||
end | ||
|
||
if deep_copy_keys and type(k) == "table" then | ||
local new_k = _M.cycle_aware_deep_copy(k, remove_metatables, deep_copy_keys, cycle_aware_cache) | ||
copy[new_k] = copy[k] | ||
copy[k] = nil | ||
end | ||
end | ||
|
||
if mt then | ||
setmetatable(copy, mt) | ||
end | ||
|
||
return copy | ||
end | ||
|
||
|
||
--- Cycle aware merges two tables recursively | ||
-- The table t1 is deep copied using cycle_aware_deep_copy function. | ||
-- The table t2 is deep merged into t1. The t2 values takes precedence | ||
-- over t1 ones. Tables used as keys are not deep copied. Metatables | ||
-- are set to same on copies as they were in the original. | ||
-- @param t1 one of the tables to merge | ||
-- @param t2 one of the tables to merge | ||
-- @param remove_metatables Removes the metatables when set to `true`. | ||
-- @param deep_copy_keys Deep copies the keys (and not only the values) when set to `true`. | ||
-- @param cycle_aware_cache Cached tables that are not copied (again) | ||
-- (the function creates this table when not given) | ||
-- @return Returns a table representing a deep merge of the new table | ||
function _M.cycle_aware_deep_merge(t1, t2, remove_metatables, deep_copy_keys, cycle_aware_cache) | ||
cycle_aware_cache = cycle_aware_cache or {} | ||
local merged = _M.cycle_aware_deep_copy(t1, remove_metatables, deep_copy_keys, cycle_aware_cache) | ||
for k, v in pairs(t2) do | ||
if type(v) == "table" then | ||
if type(merged[k]) == "table" then | ||
merged[k] = _M.cycle_aware_deep_merge(merged[k], v, remove_metatables, deep_copy_keys, cycle_aware_cache) | ||
else | ||
merged[k] = _M.cycle_aware_deep_copy(v, remove_metatables, deep_copy_keys, cycle_aware_cache) | ||
end | ||
else | ||
merged[k] = v | ||
end | ||
end | ||
return merged | ||
end | ||
|
||
|
||
--- Concatenates lists into a new table. | ||
function _M.concat(...) | ||
local result = {} | ||
for _, t in ipairs({...}) do | ||
for _, v in ipairs(t) do insert(result, v) end | ||
end | ||
return result | ||
end | ||
|
||
|
||
local err_list_mt = {} | ||
|
||
|
||
--- Add an error message to a key/value table. | ||
-- If the key already exists, a sub table is created with the original and the new value. | ||
-- @param errors (Optional) Table to attach the error to. If `nil`, the table will be created. | ||
-- @param k Key on which to insert the error in the `errors` table. | ||
-- @param v Value of the error | ||
-- @return The `errors` table with the new error inserted. | ||
function _M.add_error(errors, k, v) | ||
if not errors then | ||
errors = {} | ||
end | ||
|
||
if errors and errors[k] then | ||
if getmetatable(errors[k]) ~= err_list_mt then | ||
errors[k] = setmetatable({errors[k]}, err_list_mt) | ||
end | ||
|
||
insert(errors[k], v) | ||
else | ||
errors[k] = v | ||
end | ||
|
||
return errors | ||
end | ||
|
||
|
||
return _M |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know why the original code has two same functions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have the same feeling with you, but this PR is refactoring, won't change anything.
After this PR, we could check the code carefully.