MineOS/lib/advancedLua.lua
Igor Timofeev 4783fa5f8a Revert "afaef"
This reverts commit 5f12afa6fd07c7092577068f7439e0e5e8bbabad.
2016-11-14 17:49:00 +03:00

384 lines
11 KiB
Lua
Executable File

--[[
Advanced Lua Library v1.1 by ECS
This library extends a lot of default Lua methods
and adds some really cool features that haven't been
implemented yet, such as fastest table serialization,
table binary searching, string wrapping, numbers rounding, etc.
]]
_G.filesystem = _G.filesystem or require("filesystem")
_G.unicode = _G.unicode or require("unicode")
_G.bit32 = _G.bit32 or require("bit32")
-------------------------------------------------- System extensions --------------------------------------------------
function _G.getCurrentScript()
local runLevel, info = 0
while true do
info = debug.getinfo(runLevel)
if info then
if info.what == "main" and fs.exists(info.short_src) then return info.short_src end
else
error("Failed to get running script: current runLevel is " .. runLevel)
end
runLevel = runLevel + 1
end
end
function enum(...)
local args, enums = {...}, {}
for i = 1, #args do
if type(args[i]) ~= "string" then error("Function argument " .. i .. " have non-string type: " .. type(args[i])) end
enums[args[i]] = i
end
return enums
end
function swap(a, b)
return b, a
end
-------------------------------------------------- Bit32 extensions --------------------------------------------------
function bit32.numberToByteArray(number)
local byteArray = {}
while number > 0 do
table.insert(byteArray, 1, bit32.band(number, 0xFF))
number = bit32.rshift(number, 8)
end
return byteArray
end
function bit32.byteArrayToNumber(byteArray)
local number = byteArray[1]
for i = 2, #byteArray do
number = bit32.bor(byteArray[i], bit32.lshift(number, 8))
end
return number
end
function bit32.bitArrayToByte(bitArray)
local number = 0
for i = 1, #bitArray do
number = bit32.bor(bitArray[i], bit32.lshift(number, 1))
end
return number
end
bit32.byteArrayFromNumber = bit32.numberToByteArray
bit32.numberFromByteArray = bit32.byteArrayToNumber
-------------------------------------------------- Math extensions --------------------------------------------------
function math.round(num)
if num >= 0 then
return math.floor(num + 0.5)
else
return math.ceil(num - 0.5)
end
end
function math.roundToDecimalPlaces(num, decimalPlaces)
local mult = 10 ^ (decimalPlaces or 0)
return math.floor(num * mult + 0.5) / mult
end
function math.getDigitCount(num)
return num == 0 and 1 or math.ceil(math.log(num + 1, 10))
end
function math.doubleToString(num, digitCount)
return string.format("%." .. (digitCount or 1) .. "f", num)
end
function math.shortenNumber(number, digitCount)
local shortcuts = {
"K",
"M",
"B",
"T"
}
local index = math.floor(math.log(number, 1000))
if number < 1000 then
return number
elseif index > #shortcuts then
index = #shortcuts
end
return math.roundToDecimalPlaces(number / 1000 ^ index, digitCount) .. shortcuts[index]
end
-------------------------------------------------- Table extensions --------------------------------------------------
local function doSerialize(array, text, prettyLook, indentationSymbol, oldIndentationSymbol, equalsSymbol, currentRecusrionStack, recursionStackLimit)
text = {"{"}
table.insert(text, (prettyLook and "\n" or nil))
for key, value in pairs(array) do
local keyType, valueType, stringValue = type(key), type(value), tostring(value)
if keyType == "number" or keyType == "string" then
table.insert(text, (prettyLook and indentationSymbol or nil))
table.insert(text, "[")
table.insert(text, (keyType == "string" and table.concat({"\"", key, "\""}) or key))
table.insert(text, "]")
table.insert(text, equalsSymbol)
if valueType == "number" or valueType == "boolean" or valueType == "nil" then
table.insert(text, stringValue)
elseif valueType == "string" or valueType == "function" then
table.insert(text, "\"")
table.insert(text, stringValue)
table.insert(text, "\"")
elseif valueType == "table" then
-- Ограничение стека рекурсии
if currentRecusrionStack < recursionStackLimit then
table.insert(text, table.concat(doSerialize(value, text, prettyLook, table.concat({indentationSymbol, indentationSymbol}), table.concat({oldIndentationSymbol, indentationSymbol}), equalsSymbol, currentRecusrionStack + 1, recursionStackLimit)))
else
table.insert(text, "...")
end
else
-- error("Unsupported table value type: " .. valueType)
end
table.insert(text, ",")
table.insert(text, (prettyLook and "\n" or nil))
else
-- error("Unsupported table key type: " .. keyType)
end
end
table.remove(text, (prettyLook and #text - 1 or #text))
table.insert(text, (prettyLook and oldIndentationSymbol or nil))
table.insert(text, "}")
return text
end
function table.serialize(array, prettyLook, indentationWidth, indentUsingTabs, recursionStackLimit)
checkArg(1, array, "table")
indentationWidth = indentationWidth or 2
local indentationSymbol = indentUsingTabs and " " or " "
indentationSymbol, indentationSymbolHalf = string.rep(indentationSymbol, indentationWidth)
return table.concat(doSerialize(array, {}, prettyLook, indentationSymbol, "", prettyLook and " = " or "=", 1, recursionStackLimit or math.huge))
end
function table.unserialize(serializedString)
checkArg(1, serializedString, "string")
local success, result = pcall(load("return " .. serializedString))
if success then return result else return nil, result end
end
function table.toString(...)
return table.serialize(...)
end
function table.fromString(...)
return table.unserialize(...)
end
function table.toFile(path, array, prettyLook, indentationWidth, indentUsingTabs, recursionStackLimit, appendToFile)
checkArg(1, path, "string")
checkArg(2, array, "table")
filesystem.makeDirectory(filesystem.path(path) or "")
local file = io.open(path, appendToFile and "a" or "w")
file:write(table.serialize(array, prettyLook, indentationWidth, indentUsingTabs, recursionStackLimit))
file:close()
end
function table.fromFile(path)
checkArg(1, path, "string")
if filesystem.exists(path) then
if filesystem.isDirectory(path) then
error("\"" .. path .. "\" is a directory")
else
local file = io.open(path, "r")
local data = table.unserialize(file:read("*a"))
file:close()
return data
end
else
error("\"" .. path .. "\" doesn't exists")
end
end
function table.copy(tableToCopy)
local function recursiveCopy(source, destination)
for key, value in pairs(source) do
if type(value) == "table" then
destination[key] = {}
recursiveCopy(source[key], destination[key])
else
destination[key] = value
end
end
end
local tableThatCopied = {}
recursiveCopy(tableToCopy, tableThatCopied)
return tableThatCopied
end
function table.binarySearch(t, requestedValue)
local function recursiveSearch(startIndex, endIndex)
local difference = endIndex - startIndex
local centerIndex = math.floor(difference / 2 + startIndex)
if difference > 1 then
if requestedValue >= t[centerIndex] then
return recursiveSearch(centerIndex, endIndex)
else
return recursiveSearch(startIndex, centerIndex)
end
else
if math.abs(requestedValue - t[startIndex]) > math.abs(t[endIndex] - requestedValue) then
return t[endIndex]
else
return t[startIndex]
end
end
end
return recursiveSearch(1, #t)
end
function table.size(t)
local size = #t
if size == 0 then for key in pairs(t) do size = size + 1 end end
return size
end
-------------------------------------------------- String extensions --------------------------------------------------
function string.canonicalPath(str)
return string.gsub("/" .. str, "%/+", "/")
end
function string.optimize(str, indentationWidth)
str = string.gsub(str, "\r\n", "\n")
str = string.gsub(str, " ", string.rep(" ", indentationWidth or 2))
return str
end
function string.optimizeForURLRequests(code)
if code then
code = string.gsub(code, "([^%w ])", function (c)
return string.format("%%%02X", string.byte(c))
end)
code = string.gsub(code, " ", "+")
end
return code
end
function string.unicodeFind(str, pattern, init, plain)
if init then
if init < 0 then
init = -#unicode.sub(str,init)
elseif init > 0 then
init = #unicode.sub(str, 1, init - 1) + 1
end
end
a, b = string.find(str, pattern, init, plain)
if a then
local ap, bp = str:sub(1, a - 1), str:sub(a,b)
a = unicode.len(ap) + 1
b = a + unicode.len(bp) - 1
return a, b
else
return a
end
end
function string.limit(text, size, fromLeft, noDots)
local length = unicode.len(text)
if length <= size then return text end
if fromLeft then
if noDots then
return unicode.sub(text, length - size + 1, -1)
else
return "" .. unicode.sub(text, length - size + 2, -1)
end
else
if noDots then
return unicode.sub(text, 1, size)
else
return unicode.sub(text, 1, size - 1) .. ""
end
end
end
function string.wrap(strings, limit)
strings = type(strings) == "string" and {strings} or strings
local currentString = 1
while currentString <= #strings do
local words = {}; for word in string.gmatch(tostring(strings[currentString]), "[^%s]+") do table.insert(words, word) end
local newStringThatFormedFromWords, oldStringThatFormedFromWords = "", ""
local word = 1
local overflow = false
while word <= #words do
oldStringThatFormedFromWords = oldStringThatFormedFromWords .. (word > 1 and " " or "") .. words[word]
if unicode.len(oldStringThatFormedFromWords) > limit then
if unicode.len(words[word]) > limit then
local left = unicode.sub(oldStringThatFormedFromWords, 1, limit)
local right = unicode.sub(strings[currentString], unicode.len(left) + 1, -1)
overflow = true
strings[currentString] = left
if strings[currentString + 1] then
strings[currentString + 1] = right .. " " .. strings[currentString + 1]
else
strings[currentString + 1] = right
end
end
break
else
newStringThatFormedFromWords = oldStringThatFormedFromWords
end
word = word + 1
end
if word <= #words and not overflow then
local fuckToAdd = table.concat(words, " ", word, #words)
if strings[currentString + 1] then
strings[currentString + 1] = fuckToAdd .. " " .. strings[currentString + 1]
else
strings[currentString + 1] = fuckToAdd
end
strings[currentString] = newStringThatFormedFromWords
end
currentString = currentString + 1
end
return strings
end
-------------------------------------------------- Playground --------------------------------------------------
-- local function safeCall(method, ...)
-- local arguments = {...}
-- return xpcall(function() return method(table.unpack(arguments)) end, debug.traceback)
-- end
-- local function safeCallString(str)
-- return safeCall(load(str))
-- end
-- print(safeCallString("return 123"))
------------------------------------------------------------------------------------------------------------------
return {loaded = true}