[
  {
    "path": ".gitignore",
    "content": "other/\n"
  },
  {
    "path": "README.md",
    "content": "# LuaKit\nLua核心工具包，提供对面向对象，组件系统，mvc模块化加载，事件分发系统等常用模式的封装。同时提供打印，内存泄漏检测，字符串操作等常用工具类。\nPS：添加了注释的Lua源码可以参考<a href=\"https://github.com/iwiniwin/source-code/tree/master/lua-5.1.4/src\">这里</a>\n\nLuaKit部分特性介绍如下，用法/测试用例请参考<a href=\"test.lua\">这里</a>\n\n# Contents  \n- [打印复杂表结构](#打印复杂表结构)  \n- [组件系统](#组件系统)  \n- [事件分发系统](#事件分发系统)  \n- [面向对象封装](#面向对象封装)  \n- [分模块加载](#分模块加载)  \n- [性能分析](#性能分析)  \n- [内存泄漏检测](#内存泄漏检测)  \n\n### 打印复杂表结构\ndump支持按照指定格式打印任意类型的数据\ndump_to_file支持将数据序列化到文件\n```lua\nlocal data = {\n    key1 = 34,\n    key2 = \"str\",\n    key3 = {\n        key4 = {\n            key5 = 56\n        },\n        key6 = 78\n    }\n}\ndump(data, \"this is a dump test\")\n```\n输出结果如下：\n```\n- \"this is a dump test\" = {\n-     \"key1\" = 34\n-     \"key2\" = \"str\"\n-     \"key3\" = {\n-         \"key4\" = {\n-             \"key5\" = 56\n-         }\n-         \"key6\" = 78\n-     }\n- }\n```\n### 组件系统\n游戏开发中很多功能无法单纯靠继承实现，因为类继承会导致难以轻易改变结构，功能全都向上依赖，子类的数据爆炸，大量冗余数据和方法导致内存消耗过大。而采用组件系统，组件才是功能的携带者，可以实时增减，动态为对象增减功能。对象绑定组件就可以拥有该组件提供的功能，解绑组件则移除对应功能，通过组合构建拥有完整功能的对象，更加灵活解耦。\n\n例如: Fly组件提供飞的能力，鸟对象绑定Fly组件就可以飞，移除Fly组件就不能飞\n```lua\nlocal ComponentBase = require(\"core.component.component_base\")\nlocal ComponentExtend = require(\"core.component.component_extend\")\n\nlocal A = class()\nComponentExtend(A)\n\n-- 组件1\nlocal Component1 = class(ComponentBase)\nComponent1.exportInterface = {\n    {\"test1\"},\n}\nfunction Component1:test1( ... )\n    dump(\"call test1 ...\")\nend\n\n-- 组件2\nlocal Component2 = class(ComponentBase)\nComponent2.exportInterface = {\n    {\"test2\"},\n}\nfunction Component2:test2( ... )\n    dump(\"call test2 ...\")\nend\n\nlocal a = new(A)\n\na:bind_component(Component1)  -- 对象a绑定组件1 拥有test1方法\na:bind_component(Component2)  -- 对象a绑定组件2 拥有test2方法\na:test1()\na:test2()\n\na:unbind_component(Component1)  -- 解绑组件1 丧失test1方法\n-- a:test1()  -- 报错 attempt to call method 'test1' (a nil value)\n```\n### 事件分发系统\n基于观察者模式封装的一套事件分发系统\n\n```lua\nlocal EventSystem = new(require(\"core.event.event_system\"))\nlocal Event = require(\"core.event.event\")\n\n-- 简单用法\nEventSystem:on(\"test\", function ( ... )\n    dump({...})\nend)\n\nEventSystem:emit(\"test\", \"param1\", \"param2\")\n\n-- 高级用法\nlocal A = class()\nfunction A:on_key_down( key )\n    dump(key, \"key name A\")\nend\nEventSystem:on(Event.KeyDown, A.on_key_down, {target = A})\n\nlocal B = class()\nfunction B:on_key_down( key )\n    dump(key, \"key name B\")\n\n    return true  -- 可以中断事件派发\nend\n\n-- 后注册的事件通过提高优先级可以保证先被调用\nEventSystem:on(Event.KeyDown, B.on_key_down, {target = B, priority = 2})\n\nEventSystem:emit(Event.KeyDown, \"Ctrl\")\n\nEventSystem:off_all(B)  -- 通过target取消注册\n\nEventSystem:emit(Event.KeyDown, \"Ctrl\")\n```\n高级用法中，第一次emit时，首先触发B，B的回调返回true中断了派发，导致A的回调不会被执行，所以只打印了key name B\n第二次emit时，B已经被off_all，不会触发B的回调，自然也没有人再中断事件的派发，所以只打印了key name A\n\n输出结果如下所示：\n```\n- dump from: E:\\Project\\LuaKit\\test.lua:155: in function 'func'\n- \"<var>\" = {\n-     1 = \"param1\"\n-     2 = \"param2\"\n- }\n- dump from: E:\\Project\\LuaKit\\test.lua:170: in function 'func'\n- \"key name B\" = \"Ctrl\"\n- dump from: E:\\Project\\LuaKit\\test.lua:164: in function 'func'\n- \"key name A\" = \"Ctrl\"\n```\n\n### 面向对象封装\n基于Lua原表提供了`class`, `new`, `delete`等面向对象中思想中的常用函数\n```lua\nlocal Class1 = class()\nfunction Class1:ctor( ... )\n    dump(\"Class1:ctor\")\nend\nfunction Class1:dtor( ... )\n    dump(\"Class1:dtor\")\nend\n\n-- Class2集成于Class1\nlocal Class2 = class(Class1)\nfunction Class2:ctor( ... )\n    dump(\"Class2:ctor\")\nend\nfunction Class2:dtor( ... )\n    dump(\"Class2:dtor\")\nend\n-- 实例化对象\nlocal c1 = new(Class1)\nlocal c2 = new (Class2)\n-- 销毁对象\ndelete(c1)\ndelete(c2)\n```\n\n### 分模块加载\n分模块加载是一种模块化设计思想，通过配置文件，实现对模块的按需加载。再结合mvc，可以将一个大系统，看做由多个子模块组合而成。配置了指定模块，则系统拥有该模块的功能，取消配置则不会加载该模块。\n```lua\nlocal ModuleConfig = {}\n\nModuleConfig.Module1 = {\n    file = \"mvc.module1.test1_view\",\n    initOrder = 2,  -- 配置模块加载顺序\n}\n\nModuleConfig.Module2 = {\n    file = \"mvc.module2.test2_view\",\n    initOrder = 1,\n}\n\nreturn ModuleConfig\n```\n\n### 性能分析\n通过LuaKit提供的profile工具，可以获取函数的调用情况，调用次数，调用时间，子函数调用时间等信息，以此来分析是否存在异常的函数调用或耗时操作。在需要进行性能优化时十分有用。\n```lua\nlocal new_profiler = require(\"utils.profiler\")\nlocal profiler = new_profiler(\"call\")\nprofiler:start()  -- 开启性能分析\n\nlocal function aaa(  )\n    for i = 1, 10000000 do\n\n    end\nend\nlocal function ttt(  )\n    aaa()\nend\nttt()\n-- 同时支持分析协程内的函数调用情况\nlocal co = coroutine.create(function ( ... )\n    aaa()\nend)\ncoroutine.resume(co)\n\nprofiler:stop()  -- 停止性能分析\n-- 输出分析结果到文件\nprofiler:dump_report_to_file(\"profile.txt\")\n```\n分析结果部分内容如下\n```\nTotal time spent in profiled functions: 0.113s\nTotal call count spent in profiled functions: 12\nLua Profile output created by profiler.lua. author: iwiniwin \n\n-----------------------------------------------------------------------------------\n| FILE                            : FUNCTION         : TIME   : %     : Call count|\n-----------------------------------------------------------------------------------\n| L:E:\\Project\\LuaKit\\test.lua:61 : aaa              : 0.1130 : 100.0 :         2 |\n| L:E:\\Project\\LuaKit\\test.lua:71 : unknow           : 0.0580 : 51.3  :         1 |\n| C:resume@=[C]:-1                : resume           : 0.0580 : 51.3  :         1 |\n| L:E:\\Project\\LuaKit\\test.lua:66 : ttt              : 0.0550 : 48.7  :         1 |\n| C:insert@=[C]:-1                : insert           : ~      : ~     :         1 |\n| C:sethook@=[C]:-1               : sethook          : ~      : ~     :         2 |\n| C:coroutine_create@=[C]:-1      : coroutine_create : ~      : ~     :         1 |\n| L:.\\utils\\profiler.lua:122      : stop             : ~      : ~     :         1 |\n| C:clock@=[C]:-1                 : clock            : ~      : ~     :         1 |\n| L:.\\utils\\profiler.lua:106      : create           : ~      : ~     :         1 |\n-----------------------------------------------------------------------------------\n\n--------------------- L:aaa@@E:\\Project\\LuaKit\\test.lua:61 ---------------------\nCall count:            2\nTime spend total:       0.1130s\nTime spent in children: 0.0000s\nTime spent in self:     0.1130s\n```\n\n### 内存泄漏检测\n对于游戏开发而言，内存泄露往往是最容易忽视的问题，很多开发者并不知道自己的代码是否存在内存泄露。此类问题可以借助MemoryMonitor来检测，具体原理是借助lua的弱引用，把某个需要观察的对象加入到弱表，如果不存在外部引用，那么在gc时候，弱表上的该对象也就自然消失，如果弱表还存在该对象，说明外部仍存在引用。\n```lua\nlocal MemoryMonitor = require(\"utils.memory_monitor\")\nlocal memoryMonitor = new(MemoryMonitor)\n\na = {}\nfunction test( ... )\n    local b = {xxx = \"xxx\"}\n    a.b = b\n    memoryMonitor:add_to_leak_monitor(b, \"b\")  --将b添加到内存检测工具，此时a没有被释放掉 则b也释放不掉\nend\ntest()\n\n-- 由于a在引用b，因此b存在内存泄漏\nmemoryMonitor:update()  -- 这里会打印日志\n\n-- a不再引用b，b也被释放\na = nil\nmemoryMonitor:update()  -- 没有内存泄漏，这里不会打印日志\n```\n第一次update时存在内存泄漏，输出如下所示\n```\n存在以下内存泄漏：    \nb@table: 02C5F7A8 = table: 02C5F7A8\n请仔细检查代码！！！\n```\n"
  },
  {
    "path": "_load.lua",
    "content": "--[[--\nLuaKit入口\n@module _load\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin\nLast Modified time 2020-01-16 13:57:21\n]]\nrequire(\"init\")()\n\ndump(\"Hello LuaKit\")\n\n-- require(\"test\")  -- 运行测试用例\n\n--[[\n    什么叫组合 Composition\n    在类中增加一个私有域，引用另一个已有的类的实例，通过调用实例的方法从而获得新的功能\n]]"
  },
  {
    "path": "build/int64.c",
    "content": "#include <lua.h>\n#include <lauxlib.h>\n#include <stdint.h>\n#include <math.h>\n#include <stdlib.h>\n#include <stdbool.h>\n\n// 将指定索引处的值转换为int64\nstatic int64_t\n_int64(lua_State *L, int index) {\n\tint type = lua_type(L,index);\n\tint64_t n = 0;\n\tswitch(type) {\n\tcase LUA_TNUMBER: {\n\t\t// lua number 类型直接强转\n\t\tlua_Number d = lua_tonumber(L,index);\n\t\tn = (int64_t)d;\n\t\tbreak;\n\t}\n\tcase LUA_TSTRING: {\n\t\tsize_t len = 0;\n\t\tconst uint8_t * str = (const uint8_t *)lua_tolstring(L, index, &len);\n\t\tif (len>8) {\n\t\t\treturn luaL_error(L, \"The string (length = %d) is not an int64 string\", len);\n\t\t}\n\t\tint i = 0;\n\t\tuint64_t n64 = 0;\n\t\t// 最多8个字节字符串，每个字符1个字节对应8位，将每个字符对应的8位拼起来\n\t\tfor (i=0;i<(int)len;i++) {\n\t\t\tn64 |= (uint64_t)str[i] << (i*8);\n\t\t}\n\t\tn = (int64_t)n64;\n\t\tbreak;\n\t}\n\tcase LUA_TLIGHTUSERDATA: {\n\t\t// 指针地址值就是int64值\n\t\tvoid * p = lua_touserdata(L,index);\n\t\tn = (intptr_t)p;\n\t\tbreak;\n\t}\n\tdefault:\n\t\treturn luaL_error(L, \"argument %d error type %s\", index, lua_typename(L,type));\n\t}\n\treturn n;\n}\n\n// 入栈一个int64，转换成指针入栈的\nstatic inline void\n_pushint64(lua_State *L, int64_t n) {\n\tvoid * p = (void *)(intptr_t)n;\n\tlua_pushlightuserdata(L,p);\n}\n\nstatic int\nint64_add(lua_State *L) {\n\tint64_t a = _int64(L,1);\n\tint64_t b = _int64(L,2);\n\t_pushint64(L, a+b);\n\t\n\treturn 1;\n}\n\nstatic int\nint64_sub(lua_State *L) {\n\tint64_t a = _int64(L,1);\n\tint64_t b = _int64(L,2);\n\t_pushint64(L, a-b);\n\t\n\treturn 1;\n}\n\nstatic int\nint64_mul(lua_State *L) {\n\tint64_t a = _int64(L,1);\n\tint64_t b = _int64(L,2);\n\t_pushint64(L, a * b);\n\t\n\treturn 1;\n}\n\nstatic int\nint64_div(lua_State *L) {\n\tint64_t a = _int64(L,1);\n\tint64_t b = _int64(L,2);\n\tif (b == 0) {\n\t\treturn luaL_error(L, \"div by zero\");\n\t}\n\t_pushint64(L, a / b);\n\t\n\treturn 1;\n}\n\nstatic int\nint64_mod(lua_State *L) {\n\tint64_t a = _int64(L,1);\n\tint64_t b = _int64(L,2);\n\tif (b == 0) {\n\t\treturn luaL_error(L, \"mod by zero\");\n\t}\n\t_pushint64(L, a % b);\n\t\n\treturn 1;\n}\n\nstatic int64_t\n_pow64(int64_t a, int64_t b) {\n\tif (b == 1) {\n\t\treturn a;\n\t}\n\tint64_t a2 = a * a;\n\tif (b % 2 == 1) {\n\t\treturn _pow64(a2, b/2) * a;\n\t} else {\n\t\treturn _pow64(a2, b/2);\n\t}\n}\n\nstatic int\nint64_pow(lua_State *L) {\n\tint64_t a = _int64(L,1);\n\tint64_t b = _int64(L,2);\n\tint64_t p;\n\tif (b > 0) {\n\t\tp = _pow64(a,b);\n\t} else if (b == 0) {\n\t\tp = 1;\n\t} else {\n\t\treturn luaL_error(L, \"pow by nagtive number %d\",(int)b);\n\t} \n\t_pushint64(L, p);\n\n\treturn 1;\n}\n\nstatic int\nint64_unm(lua_State *L) {\n\tint64_t a = _int64(L,1);\n\t_pushint64(L, -a);\n\treturn 1;\n}\n\n// 构建int64\nstatic int\nint64_new(lua_State *L) {\n\tint top = lua_gettop(L);\n\tint64_t n;\n\tswitch(top) {\n\t\tcase 0 : \n\t\t\tlua_pushlightuserdata(L,NULL);\n\t\t\tbreak;\n\t\tcase 1 :\n\t\t\tn = _int64(L,1);\n\t\t\t_pushint64(L,n);\n\t\t\tbreak;\n\t\tdefault: {\n\t\t\tint base = luaL_checkinteger(L,2);  // 第二个参数，指明几进制\n\t\t\tif (base < 2) {\n\t\t\t\tluaL_error(L, \"base must be >= 2\");\n\t\t\t}\n\t\t\tconst char * str = luaL_checkstring(L, 1);\n\t\t\tn = strtoll(str, NULL, base);  // 将字符串根据base转换成长整型数\n\t\t\t_pushint64(L,n);\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn 1;\n}\n\nstatic int\nint64_eq(lua_State *L) {\n\tint64_t a = _int64(L,1);\n\tint64_t b = _int64(L,2);\n\tprintf(\"%s %s\\n\",lua_typename(L,1),lua_typename(L,2));\n\tprintf(\"%ld %ld\\n\",a,b);\n\tlua_pushboolean(L,a == b);\n\treturn 1;\n}\n\nstatic int\nint64_lt(lua_State *L) {\n\tint64_t a = _int64(L,1);\n\tint64_t b = _int64(L,2);\n\tlua_pushboolean(L,a < b);\n\treturn 1;\n}\n\nstatic int\nint64_le(lua_State *L) {\n\tint64_t a = _int64(L,1);\n\tint64_t b = _int64(L,2);\n\tlua_pushboolean(L,a <= b);\n\treturn 1;\n}\n\nstatic int\nint64_len(lua_State *L) {\n\tint64_t a = _int64(L,1);\n\tlua_pushnumber(L,(lua_Number)a);\n\treturn 1;\n}\n\nstatic int\ntostring(lua_State *L) {\n\tstatic char hex[16] = \"0123456789ABCDEF\";\n\tuintptr_t n = (uintptr_t)lua_touserdata(L,1);\n\tif (lua_gettop(L) == 1) {\n\t\t// 只传了n, 不带base处理\n\t\tluaL_Buffer b;\n\t\t// 开辟一个字符串缓存，预分配28大小的空间\n\t\tluaL_buffinitsize(L , &b , 28);\n\t\tluaL_addstring(&b, \"int64: 0x\");\n\t\tint i;\n\t\tbool strip = true;\n\t\t// 将int64转换为十六进制显示\n\t\tfor (i=15;i>=0;i--) {\n\t\t\tint c = (n >> (i*4)) & 0xf;\n\t\t\tif (strip && c ==0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tstrip = false;\n\t\t\tluaL_addchar(&b, hex[c]);\n\t\t}\n\t\tif (strip) {\n\t\t\tluaL_addchar(&b , '0');\n\t\t}\n\t\tluaL_pushresult(&b);\n\t} else {\n\t\t// 带base处理\n\t\tint base = luaL_checkinteger(L,2);\n\t\tint shift , mask;\n\t\tswitch(base) {\n\t\tcase 0: {\n\t\t\tunsigned char buffer[8];\n\t\t\tint i;\n\t\t\tfor (i=0;i<8;i++) {\n\t\t\t\tbuffer[i] = (n >> (i*8)) & 0xff;\n\t\t\t}\n\t\t\tlua_pushlstring(L,(const char *)buffer, 8);\n\t\t\treturn 1;\n\t\t\t}\n\t\tcase 10: {\n\t\t\t// int64转换为10进制显示\n\t\t\tint64_t dec = (int64_t)n;\n\t\t\tluaL_Buffer b;\n\t\t\tluaL_buffinitsize(L , &b , 28);\n\t\t\tif (dec<0) {\n\t\t\t\tluaL_addchar(&b, '-');\n\t\t\t\tdec = -dec;\n\t\t\t}\n\t\t\tint buffer[32];\n\t\t\tint i;\n\t\t\tfor (i=0;i<32;i++) {\n\t\t\t\tbuffer[i] = dec%10;\n\t\t\t\tdec /= 10;\n\t\t\t\tif (dec == 0)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\twhile (i>=0) {\n\t\t\t\tluaL_addchar(&b, hex[buffer[i]]);\n\t\t\t\t--i;\n\t\t\t}\n\t\t\tluaL_pushresult(&b);\n\t\t\treturn 1;\n\t\t}\n\t\tcase 2:\n\t\t\tshift = 1;\n\t\t\tmask = 1;\n\t\t\tbreak;\n\t\tcase 8:\n\t\t\tshift = 3;\n\t\t\tmask = 7;\n\t\t\tbreak;\n\t\tcase 16:\n\t\t\tshift = 4;\n\t\t\tmask = 0xf;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tluaL_error(L, \"Unsupport base %d\",base);\n\t\t\tbreak;\n\t\t}\n\t\tint i;\n\t\tchar buffer[64];\n\t\tfor (i=0;i<64;i+=shift) {\n\t\t\tbuffer[i/shift] = hex[(n>>(64-shift-i)) & mask];\n\t\t}\n\t\tlua_pushlstring(L, buffer, 64 / shift);\n\t}\n\treturn 1;\n}\n\nstatic void\nmake_mt(lua_State *L) {\n\t// 创建包含以下函数的元表\n\tluaL_Reg lib[] = {\n\t\t{ \"__add\", int64_add },\n\t\t{ \"__sub\", int64_sub },\n\t\t{ \"__mul\", int64_mul },\n\t\t{ \"__div\", int64_div },\n\t\t{ \"__mod\", int64_mod },\n\t\t{ \"__unm\", int64_unm },\n\t\t{ \"__pow\", int64_pow },\n\t\t{ \"__eq\", int64_eq },\n\t\t{ \"__lt\", int64_lt },\n\t\t{ \"__le\", int64_le },\n\t\t{ \"__len\", int64_len },\n\t\t{ \"__tostring\", tostring },\n\t\t{ NULL, NULL },\n\t};\n\tluaL_newlib(L,lib);\n}\n\nint\nluaopen_int64(lua_State *L) {\n\t// 64位机器上指针占8字节，32位机器上指针占用4字节\n\tif (sizeof(intptr_t)!=sizeof(int64_t)) {\n\t\treturn luaL_error(L, \"Only support 64bit architecture\");\n\t}\n\tlua_pushlightuserdata(L,NULL);\n\tmake_mt(L);\n\tlua_setmetatable(L,-2);\n\tlua_pop(L,1);\n\n\tlua_newtable(L);\n\tlua_pushcfunction(L, int64_new);\n\tlua_setfield(L, -2, \"new\");\n\tlua_pushcfunction(L, tostring);\n\tlua_setfield(L, -2, \"tostring\");\n\n\treturn 1;\n}"
  },
  {
    "path": "core/component/component_base.lua",
    "content": "--[[--组件基类  \r\n注意：所有组件必须继承该类\r\n@module ComponentBase\r\n@author iwiniwin\r\n\r\nDate   2020-01-16 13:27:14\r\nLast Modified by   iwiniwin\r\nLast Modified time 2020-01-16 13:45:20\r\n]]\r\nlocal ComponentBase = class()\r\n\r\nComponentBase._class_name = \"ComponentBase\"\r\n\r\nComponentBase.exportInterface = {\r\n\r\n}\r\n\r\n--[[--\r\n        组件依赖列表 \r\n    ]]\r\nComponentBase.depends = {}\r\n\r\n--[[--\r\n    组件优先级 \r\n]]\r\nComponentBase.priority = 1;\r\n\r\nfunction ComponentBase:ctor( componentName )\r\n    --[[--\r\n        组件名称\r\n    ]]\r\n    self.name = componentName or \"Component\";\r\nend\r\n\r\n--[[--\r\n    组件绑定对象\r\n    @tparam Object object 需要绑定组件的“宿主”\r\n    \r\n]]\r\nfunction ComponentBase:bind( object )\r\n    for i,v in ipairs(self.exportInterface) do\r\n        object:bind_method(self, v[1],   handler(self, self[v[1]]), v[2], v[3]);\r\n    end \r\nend\r\n\r\n--[[--\r\n    组件解绑对象\r\n    @tparam Object object 需要解除绑定的“宿主”\r\n    \r\n]]\r\nfunction ComponentBase:unbind( object )\r\n    for i,v in ipairs(self.exportInterface) do\r\n        object:unbind_method(self, v[1]);\r\n    end \r\nend\r\n\r\n--[[--\r\n    重置组件，按优先级调用,需要注意的是reset是配合priority使用priority越高组件越先执行\r\n    @tparam Object object 需要解除绑定的“宿主”\r\n\r\n]]\r\nfunction ComponentBase:reset( object )\r\nend\r\n\r\nreturn ComponentBase"
  },
  {
    "path": "core/component/component_extend.lua",
    "content": "--[[--组件扩展\n赋予类绑定解绑组件的能力\n@module ComponentExtend\n@author iwiniwin\n\nDate   2020-01-16 13:27:14\nLast Modified by   iwiniwin\nLast Modified time 2020-01-16 13:45:20\n]]\nlocal ComponentFactory = import(\"core.component.component_factory\")\n\nlocal component_extend = function ( Class )\n\n    function Class:has_component( ComponentClass )\n        local component_name = tostring(ComponentClass)\n        return self._component_objects and self._component_objects[component_name]\n    end\n\n    function Class:get_component( ComponentClass )\n        if not self._component_objects then return end\n        local component_name = tostring(ComponentClass)\n        return self._component_objects[component_name]\n    end\n\n    function Class:bind_component( ComponentClass )\n        assert(ComponentClass, \"required component class\")\n        local component_name = tostring(ComponentClass);\n        if not self._component_objects then self._component_objects = {} end\n        if self._component_objects[component_name] then return end\n\n        local component = ComponentFactory.create_component(ComponentClass);\n        for i,DependComponentClass in ipairs(component.depends) do\n            \n            self:bind_component(DependComponentClass)\n\n            local depend_component_name = tostring(DependComponentClass)\n            if not self._component_depends then self._component_depends = {} end\n            if not self._component_depends[depend_component_name] then\n                self._component_depends[depend_component_name] = {}\n            end\n            \n            table.insert(self._component_depends[depend_component_name], component_name)\n        end\n\n        component.object = self\n        component:bind(self)\n        self._component_objects[component_name] = component\n        return component\n    end\n\n    function Class:unbind_component( ComponentClass )\n        local component_name = tostring(ComponentClass);\n        assert(self._component_objects and self._component_objects[component_name],\n            string.format(\"component %s not binding\", component_name))\n        assert(not self._component_depends or not self._component_depends[component_name],\n            string.format(\"component %s depends by other binding\", component_name))\n\n        local component = self._component_objects[component_name]\n        for i,DependComponentClass in ipairs(component.depends) do\n            local depend_component_name = tostring(DependComponentClass)\n            for i,name in ipairs(self._component_depends[depend_component_name]) do\n                if name == component_name then\n                    table.remove(self._component_depends[depend_component_name], i)\n                    if #self._component_depends[depend_component_name] == 0 then\n                        self._component_depends[depend_component_name] = nil\n                    end\n                    break\n                end\n            end\n        end\n\n        component:unbind(self)\n        self._component_objects[component_name] = nil\n        component.object = nil\n        delete(component)\n    end\n\n    function Class:unbind_all_component(  )\n        if not self._component_objects then return end\n        for component_name,component in pairs(self._component_objects) do\n            component:unbind(self)\n            component.object = nil\n            delete(component)\n        end\n        self._component_objects = {}\n        self._component_depends = {}\n    end\n\n    function Class:bind_method( component, method_name, method, deprecate_origin_method, call_origin_method_last )\n        \n        if not self._bind_methods then\n            self._bind_methods = {}\n        end\n\n        if not self._bind_methods[method_name] then\n            self._bind_methods[method_name] = {}\n        end\n\n        -- 根据优先级确定绑定\n        local index\n        local component_priority = component.priority or 1\n        for i,c in ipairs(self._bind_methods[method_name]) do\n            local priority = c[1].priority or 1\n            if component_priority > priority then\n                index = i\n                break\n            end \n        end\n\n        local chain = { component }\n\n        local new_method\n\n        if index then\n            chain[2] = self._bind_methods[method_name][index][2]\n        else\n            chain[2] = self[method_name]\n        end\n        \n        if deprecate_origin_method then\n            new_method = method\n        elseif call_origin_method_last then\n            new_method = function ( ... )\n                if chain[2] then\n                    method(...)\n                    return chain[2](...)\n                else\n                    return method(...)\n                end\n            end\n        else\n            new_method = function ( ... )\n                local func = chain[2]\n                if func then\n                    local ret = func(...)\n                    if ret then\n                        local args = {...}\n                        args[#args + 1] = ret\n                        return method(unpack(args))\n                    else\n                        return method(...)\n                    end\n                else\n                    return method(...)\n                end\n            end\n        end\n\n        if index then\n            self._bind_methods[method_name][index][2] = new_method\n            table.insert(self._bind_methods[method_name], index, chain)\n        else\n            self[method_name] = new_method\n            table.insert(self._bind_methods[method_name], chain)\n        end\n\n        chain[3] = new_method\n    end\n\n    function Class:unbind_method( component, method_name )\n        if not self._bind_methods or not self._bind_methods[method_name] then\n            return\n        end\n        local methods = self._bind_methods[method_name]\n        local count = #methods\n        for i = count, 1, -1 do\n            local chain = methods[i]\n            if chain[1] == component then\n                if i < count then\n                    methods[i + 1][2] = chain[2]\n                elseif count > 1 then\n                    self[method_name] = methods[i - 1][3]\n                elseif count == 1 then\n                    self[method_name] = chain[2]\n                    self._bind_methods[method_name] = nil\n                end\n                table.remove(methods, i)\n                break;\n            end\n        end\n    end\n\nend\n\nreturn component_extend"
  },
  {
    "path": "core/component/component_factory.lua",
    "content": "--[[--组件工厂类  \n@module ComponentFactory\n@author iwiniwin\n\nDate   2020-01-16 13:27:14\nLast Modified by   iwiniwin\nLast Modified time 2020-01-16 13:45:20\n]]\nlocal ComponentBase = import(\"core.component.component_base\")\n\nlocal ComponentFactory = {}\n\nfunction ComponentFactory.create_component( component )\n    assert(component ~= nil, \"invalid component : \" .. tostring(component))\n    if typeof(component, ComponentBase) then\n        return new(component);\n    elseif type(component) == \"table\" and component.require and component.path then\n        return new(component.require(component.path))\n    else\n        error(\"invalid component : \" .. tostring(component))\n    end\nend\n\nreturn ComponentFactory\n"
  },
  {
    "path": "core/event/event.lua",
    "content": "--[[--事件配置，便于统一管理项目中所有使用的事件\n@module Event\n@author iwiniwin\n\nDate   2020-01-16 13:27:14\nLast Modified by   iwiniwin\nLast Modified time 2020-04-01 14:47:14\n]]\nlocal Event = {}\n\nlocal event_id = 1;\n\nEvent.get_unique_id = function (  )\n    event_id = event_id + 1\n    return \"event_\" .. event_id\nend\n\nEvent.KeyDown           = Event.get_unique_id()\n\nreturn Event\n"
  },
  {
    "path": "core/event/event_system.lua",
    "content": "--[[--事件分发系统\r\n@module EventSystem\r\n@author iwiniwin\r\n\r\nDate   2020-03-30 14:23:50\r\nLast Modified by   iwiniwin\r\nLast Modified time 2020-04-01 16:18:51\r\n]]\r\nlocal EventSystem = class()\r\n\r\nlocal id = 1\r\n\r\nEventSystem._class_name = \"EventSystem\"\r\n\r\n\r\nfunction EventSystem:ctor(  )\r\n    self._listeners = {}\r\nend\r\n\r\nfunction EventSystem:dtor(  )\r\n    self._listeners = nil\r\nend\r\n\r\n--[[\r\n    注册监听一个事件\r\n]]\r\nfunction EventSystem:on( event, func, params )\r\n    event = tostring(event)\r\n    if not self._listeners[event] then\r\n        self._listeners[event] = {list = {}, emit_count = 0}\r\n    end\r\n    local event_listener = self._listeners[event]\r\n\r\n    params = params or {}\r\n    local priority = params.priority or 0\r\n    local target = params.target\r\n    \r\n    for i,cb in ipairs(event_listener.list) do\r\n        if cb.target == target and cb.func == func then\r\n            error(\"register the same callback multiple times\")\r\n        end\r\n    end\r\n\r\n    local cb = {target = target, func = func, id = id, priority = priority}\r\n    table.insert(event_listener.list, cb)\r\n\r\n    id = id + 1\r\n\r\n    if priority > 0 then\r\n        event_listener.need_sort = true\r\n        self:sort(event_listener)\r\n    end\r\nend\r\n\r\n--[[\r\n    反注册事件，取消对一个事件的监听\r\n]]\r\nfunction EventSystem:off( event, func, params )\r\n    event = tostring(event)\r\n    if not self._listeners[event] then\r\n        return \r\n    end\r\n    local event_listener = self._listeners[event]\r\n\r\n    params = params or {}\r\n\r\n    for i,cb in ipairs(event_listener.list) do\r\n        if cb.func == func and cb.target == params.target then\r\n            if event_listener.emit_count > 0 then\r\n                -- 派发过程中只进行标记删除\r\n                cb.need_remove = true\r\n                event_listener.need_clean = true\r\n            else\r\n                table.remove(event_listener.list, i)\r\n            end\r\n            break;\r\n        end\r\n    end\r\nend\r\n\r\n--[[\r\n    根据target移除所有其注册的事件监听\r\n]]\r\nfunction EventSystem:off_all( target )\r\n    for event,listener in pairs(self._listeners) do\r\n\r\n        for i,cb in ipairs(listener.list) do\r\n            if cb.target == target then\r\n                cb.need_remove = true\r\n            end\r\n        end\r\n\r\n        listener.need_clean = true\r\n        self:clean(listener)\r\n    end\r\nend\r\n\r\n--[[\r\n    派发一个事件\r\n]]\r\nfunction EventSystem:emit( event, ... )\r\n    event = tostring(event)\r\n    if not self._listeners[event] then\r\n        return \r\n    end\r\n    local event_listener = self._listeners[event]\r\n\r\n    local interrupt = false\r\n\r\n    -- 这里不能使用ipairs，确保不会触发在派发过程中注册的事件\r\n    -- 只取当前已经注册的事件数量，如果在派发过程中再注册 则当次不会调用\r\n    local length = #event_listener.list\r\n    for i = 1, length do\r\n        if interrupt == true then\r\n            break\r\n        end\r\n        local cb = event_listener.list[i]\r\n        if cb.func and cb.need_remove ~= true then\r\n            -- 这里必须使用count计数形式判断是否处于派发过程中\r\n            -- 如果仅使用true/false标记，在派发的回调中又进行派发，会导致标记错误\r\n            event_listener.emit_count = event_listener.emit_count + 1\r\n            if cb.target then\r\n                interrupt = cb.func(cb.target, ...)\r\n            else\r\n                interrupt = cb.func(...)\r\n            end\r\n            event_listener.emit_count = event_listener.emit_count - 1\r\n        end\r\n    end\r\n\r\n    self:sort(event_listener);\r\n    self:clean(event_listener);\r\n\r\n    return interrupt\r\nend\r\n\r\nfunction EventSystem:sort( listener )\r\n    --[[\r\n        在派发过程中不会进行优先级排序\r\n        比如当前事件有4个回调 1, 2, 3, 4，已经执行到3回调\r\n        如果在3回调中又注册了一个优先级最高的回调，需要往第一个插入，此时会导致3回调又被调用一次\r\n    ]]\r\n    if listener.need_sort == true and listener.emit_count == 0 then\r\n\r\n        table.sort(listener.list, function ( a, b )\r\n            if a.priority == b.priority then\r\n                return a.id < b.id\r\n            else\r\n                return a.priority > b.priority\r\n            end\r\n        end)\r\n\r\n        listener.need_sort = false;\r\n    end\r\nend\r\n\r\nfunction EventSystem:clean( listener )\r\n    --[[\r\n        在派发过程中不会进行移除\r\n        比如当前事件有4个回调 1, 2, 3, 4，已经执行到3回调\r\n        如果在3回调中移除了自己，则会导致4回调不会被触发\r\n    ]]\r\n    if listener.need_clean == true and listener.emit_count == 0 then\r\n        for i = #listener.list, 1, -1 do\r\n            if listener.list[i].need_remove then\r\n                table.remove(listener.list, i)\r\n            end\r\n        end\r\n        listener.need_clean = false;\r\n    end\r\nend\r\n\r\n--[[\r\n    修改已经注册的一个事件监听的优先级\r\n]]\r\nfunction EventSystem:update_priority( event, func, params )\r\n    event = tostring(event)\r\n    if not self._listeners[event] then\r\n        return \r\n    end\r\n    local event_listener = self._listeners[event]\r\n\r\n    params = params or {}\r\n    local priority = params.priority or 0\r\n    for i,cb in ipairs(event_listener.list) do\r\n        if cb.func == func and cb.target == params.target then\r\n            cb.priority = priority\r\n            event_listener.need_sort = true\r\n            self:sort(event_listener);\r\n            break;\r\n        end\r\n    end\r\nend\r\n\r\nreturn EventSystem"
  },
  {
    "path": "core/object.lua",
    "content": "--[[--用于模拟面向对象\n@usage require(\"core/object\")\n@module object\n@author iwiniwin\n\nDate   2020-01-16 13:27:14\nLast Modified by   iwiniwin\nLast Modified time 2020-01-16 13:45:13\n]]\n--------------------------------------------------------------------------------\n\n-- Description: Provide object mechanism for lua\n-- Note for the object model here:\n--\t\t1.The feature like C++ static members is not support so perfect.\n--\t\tWhat that means is that if u need something like c++ static members,\n--\t\tU can access it as a rvalue like C++, but if u need access it\n--\t\tas a lvalue u must use [class.member] to access,but not [object.member].\n--\t\t2.The function delete cannot release the object, because the gc is based on\n--\t\treference count in lua.If u want to relase all the object memory, u have to\n--      set the obj to nil to enable lua gc to recover the memory after calling delete.\n\n\n---------------------Global functon class ---------------------------------------------------\n--Parameters:   super               -- The super class\n--              autoConstructSuper   -- If it is true, it will call super ctor automatic,when\n--                                      new a class obj. Vice versa.\n--Return    :   return an new class type\n--Note      :   This function make single inheritance possible.\n---------------------------------------------------------------------------------------------\n\n---\n-- 用于定义一个类.\n--\n-- @param #table super 父类。如果不指定，则表示不继承任何类，如果指定，则该指定的对象也必须是使用class()函数定义的类。\n-- @param #boolean autoConstructSuper 是否自动调用父类构造函数，默认为true。如果指定为false，若不在ctor()中手动调用super()函数则不会执行父类的构造函数。\n-- @return #table class 返回定义的类。\n-- @usage\n-- Human = class()\n-- Human.ctor = function(self)\n--  self.m_type = \"human\"\n-- end\n-- Human.dtor = function(self)\n--  print_string(\"deleted\")\n-- end\n-- Human.speak = function(self)\n--  print_string(\"I am a \" .. self.m_type)\n-- end\n--\n-- Man = class(Human, true)\n-- Man.ctor = function(self, name)\n--  self.m_sex = \"m\"\n--  self.m_name = name\n-- end\n\nfunction class( super, autoConstructSuper )\n    local classType = {\n        autoConstructSuper = autoConstructSuper or (autoConstructSuper == nil)\n    }\n    if super then\n        classType.super = super\n        local mt = getmetatable(super)\n        setmetatable(classType, {__index = super})\n    else\n        classType.setDelegate = function( self, delegate )\n            self.m_delegate = delegate\n        end\n    end\n    return classType\nend\n\n---------------------Global functon super ----------------------------------------------\n--Parameters:   obj         -- The current class which not contruct completely.\n--              ...         -- The super class ctor params.\n--Return    :   return an class obj.\n--Note      :   This function should be called when newClass = class(super,false).\n-----------------------------------------------------------------------------------------\n\n---\n-- 手动调用父类的构造函数.\n-- 只有当定义类时采用class(super,false)的调用方式时才可以调用此方法，若此时不手动调用则不会执行父类的构造函数。\n-- **只能在子类的构造函数中调用。**\n-- @param #table obj 类的实例。\n-- @param ... 父类构造函数需要传入的参数。\n-- @usage\n-- local baseClass = class()\n-- local derivedClass = class(baseClass,false)\n-- derivedClass.ctor = function()\n--     super(self) - -此处如果不手动调用super()则不会执行基类的ctor()\n-- end\nfunction super(obj, ...)\n  do\n    local create;\n    create =\n    function(c, ...)\n      if c.super and c.autoConstructSuper then\n        create(c.super, ...);\n      end\n      if rawget(c,\"ctor\") then\n        obj.currentSuper = c.super;\n        c.ctor(obj, ...);\n      elseif rawget(c, \"__init__\") then\n        obj.currentSuper = c.super;\n        c.__init__(obj, ...);\n      end\n    end\n\n    create(obj.currentSuper, ...);\n  end\nend\n\n---------------------Global functon new -------------------------------------------------\n--Parameters: \tclassType -- Table(As Class in C++)\n-- \t\t\t\t...\t\t   -- All other parameters requisted in constructor\n--Return \t:   return an object\n--Note\t\t:\tThis function is defined to simulate C++ new function.\n--\t\t\t\tFirst it called the constructor of base class then to be derived class's.\n-----------------------------------------------------------------------------------------\n\n---\n-- 创建一个类的实例.\n-- 调用此方法时会按照类的继承顺序，自上而下调用每个类的构造函数，并返回新创建的实例。\n--\n-- @param #table classType 类名。  使用class()返回的类。\n-- @param ... 构造函数需要传入的参数。\n-- @return #table obj 新创建的实例。\n-- @usage\n-- local me = new(Man, \"zzp\")\n-- me:speak()\nfunction new(classType, ...)\n  local obj = {};\n  setmetatable(obj, { __index = classType, __object=1});\n  do\n    local create;\n    create =\n    function(c, ...)\n      if c.super and c.autoConstructSuper then\n        create(c.super, ...);\n      end\n      if rawget(c,\"ctor\") then\n        obj.currentSuper = c.super;\n        c.ctor(obj, ...);\n      elseif rawget(c,\"__init__\") then\n        obj.currentSuper = c.super;\n        c.__init__(obj, ...);\n      end\n\n    end\n\n    create(classType, ...);\n  end\n  obj.currentSuper = nil;\n  return obj;\nend\n\n---------------------Global functon delete ----------------------------------------------\n--Parameters: \tobj -- the object to be deleted\n--Return \t:   no return\n--Note\t\t:\tThis function is defined to simulate C++ delete function.\n--\t\t\t\tFirst it called the destructor of derived class then to be base class's.\n-----------------------------------------------------------------------------------------\n\n---\n-- 删除某个实例.\n-- 类似c++里的delete ，会按照继承顺序，依次自下而上调用每个类的析构方法。\n--\n-- **需要留意的是，删除此实例后，lua里该对象的引用(obj)依然有效，再次使用可能会发生无法预知的意外。**\n--\n-- @param #table obj 需要删除的实例。\nfunction delete(obj)\n  do\n    local destory =\n    function(c)\n      while c do\n        if rawget(c,\"dtor\") then\n          c.dtor(obj);\n        end\n\n        c = getmetatable(c);\n        c = c and c.__index;\n      end\n    end\n    destory(obj);\n  end\nend\n\n---------------------Global functon delete ----------------------------------------------\n--Parameters:   class       -- The class type to add property\n--              varName     -- The class member name to be get or set\n--              propName    -- The name to be added after get or set to organize a function name.\n--              createGetter-- if need getter, true,otherwise false.\n--              createSetter-- if need setter, true,otherwise false.\n--Return    :   no return\n--Note      :   This function is going to add get[PropName] / set[PropName] to [class].\n-----------------------------------------------------------------------------------------\n\n---\n-- 为类定义一个property (java里的getter/setter).\n-- 会自动为类生成getter/setter方法。\n--\n-- @param #table class 使用class()方法定义的类。\n-- @param #string varName 类里的成员变量名。\n-- @param #string propName 属性名，也就是生成的方法setXX/getXX里的'XX'。\n-- @param #boolean createGetter 是否生成getter。\n-- @param #boolean createSetter 是否生成setter。<br>\n-- 如果createGetter不为false或nil，则给class生成一个get#propName()方法,可以获取class的varName的值。<br>\n-- 如果createSetter不为false或nil，则给class生成一个set#propName(Value)方法，可以设置class的varName为Value。\n-- @usage\n-- property(Man, \"m_name\", \"Name\", true, false)\n-- local me = new(Man, \"zzp\")\n-- print_string(me:getName())\nfunction property(class, varName, propName, createGetter, createSetter)\n  createGetter = createGetter or (createGetter == nil);\n  createSetter = createSetter or (createSetter == nil);\n\n  if createGetter then\n    class[string.format(\"get%s\",propName)] = function(self)\n      return self[varName];\n    end\n  end\n\n  if createSetter then\n    class[string.format(\"set%s\",propName)] = function(self,var)\n      self[varName] = var;\n    end\n  end\nend\n\n---------------------Global functon delete ----------------------------------------------\n--Parameters:   obj         -- A class object\n--              classType   -- A class\n--Return    :   return true, if the obj is a object of the classType or a object of the\n--              classType's derive class. otherwise ,return false;\n-----------------------------------------------------------------------------------------\n\n---\n-- 判断一个对象是否是某个类(包括其父类)的实例.\n-- 类似java里的instanceof。\n--\n-- @param obj 需要判断的对象。\n-- @param classType 使用class()方法定义的类。\n-- @return #boolean 若obj是classType的实例，则返回true；否则，返回false。\n-- @usage\n-- local me = new(Man, \"zzp\")\n-- if typeof(me, Man) == true then\n--     print_string(\"me is instance of Man\")\n-- end\nfunction typeof(obj, classType)\n  if type(obj) ~= type(table) or type(classType) ~= type(table) then\n    return type(obj) == type(classType);\n  end\n\n  while obj do\n    if obj == classType then\n      return true;\n    end\n    obj = getmetatable(obj) and getmetatable(obj).__index;\n  end\n\n  return false;\nend\n\n---------------------Global functon delete ----------------------------------------------\n--Parameters:   obj         -- A class object\n--Return    :   return the object's type class.\n-----------------------------------------------------------------------------------------\n\n---\n-- 通过一个对象反向得到此对象的类.\n--\n-- @param obj 对象。\n-- @return class 此对象的类。\n-- @return #nil 如果obj不是某个类的对象，则返回nil。\nfunction decltype(obj)\n  if type(obj) ~= type(table) or obj.autoConstructSuper == nil then\n    --error(\"Not a class obj\");\n    return nil;\n  end\n\n  if rawget(obj,\"autoConstructSuper\") ~= nil then\n    --error(\"It is a class but not a class obj\");\n    return nil;\n  end\n\n  local class = getmetatable(obj) and getmetatable(obj).__index;\n  if not class then\n    --error(\"No class reference\");\n    return nil;\n  end\n\n  return class;\nend\n\nfunction mkproperty(getter, setter)\n    return setmetatable({}, {\n        __get = getter,\n        __set = setter,\n    })\nend\n"
  },
  {
    "path": "core/sandbox.lua",
    "content": "--[[--lua沙盒\n@module sandbox\n@author iwiniwin\n\nDate   2020-01-16 13:27:14\nLast Modified by   iwiniwin\nLast Modified time 2020-01-16 13:45:08\n]]\n--[[\n2. lua沙盒\n理解：lua沙盒就是通过改变上下文环境，使函数可以在不同的环境表中运行，访问得到限制，从而避免相互\n影响。可以利用其构建一个安全的环境，用来执行一些未知的危险代码\n]]\n\n\nfunction test( ... )\n    print(\"hello\")\nend\n\ntest()\n\n-- 设置test在环境表e运行，此时test不能调用_G中的print\n-- local e = {}\n-- setfenv(test, {})\n-- test()\n-- setfenv(test, _G)\n-- test()\n\n\n-- 在e环境表对x赋值不会影响到f环境\nlocal e = {print = print, setfenv = setfenv}\nsetfenv(1, e)\nx = 3\nprint(x)\nlocal f = {print = print, setfenv = setfenv}\nsetfenv(1, f)\nprint(x)\n\n---保护环境 人人有责\nlocal env = getfenv();\nlocal protectEnv = function(env)\n    local mt  = getmetatable(env);\n    local cache = {};\n    mt.__newindex = function( t,k,v )\n        if cache[k] == nil then\n            cache[k] = v;\n        else\n            if cache[k] ~= v then\n                error(\"不允许复写\" .. k)\n            end\n        end\n        rawset(mt.__index, k, v)\n    end\nend\nprotectEnv(env);\n\n-- http://timothyqiu.com/archives/lua-note-sandboxes/"
  },
  {
    "path": "init.lua",
    "content": "--[[--\nLuaKit初始化函数，初始化全局变量\n@module init\n@author iwiniwin\n\nDate   2021-09-04 20:20:39\nLast Modified by   iwiniwin\nLast Modified time 2021-09-04 20:20:39\n]]\nlocal init = function ( requirePrefix )\n\n\tif requirePrefix then\n\t\timport = function ( modname )\n\t\t\treturn require(requirePrefix .. modname)\n\t\tend\n\telse\n\t\timport = require\n\tend\n\n\timport(\"core.object\")\n\timport(\"lib.string\")\n\tlocal func_lib = import(\"lib.function\")\n\n\ttime = import(\"lib.time\")\n\n\tdump = import(\"utils.dump\")\n\n\n\tdump_to_file = import(\"utils.dump_to_file\").dump_to_file\n\n\thandler = func_lib.handler\n\nend\n\nreturn init"
  },
  {
    "path": "lib/file.lua",
    "content": "--[[--文件操作\n@module File\n@author iwiniwin\n\nDate   2020-01-16 13:27:14\nLast Modified by   iwiniwin\nLast Modified time 2020-01-16 13:45:04\n]]\nlocal File = {}\n\n---插入文件内容到指定行\n--@table content 插入内容\n--@string path 目标文件\n--@number row 行数，默认插入到文件末尾\nFile.insert_to_file = function (content, path, row)\n\tfile = io.open(path, \"r\")\n\tlocal lines = {}\n\tfor line in file:lines() do\n\t\tif row and #lines == row - 1 then\n\t\t\tfor _, l in ipairs(content) do\n\t\t\t\ttable.insert(lines, l)\n\t\t\tend\n\t\tend\n\t\ttable.insert(lines, line)\n\tend \n\tfile:close()\n\tif not row then\n\t\tfor _, l in ipairs(content) do\n\t\t\ttable.insert(lines, l)\n\t\tend\n\tend\n\tfile = io.open(path, \"w+\")\n\tfor _,line in ipairs(lines) do\n\t\tfile:write(line .. \"\\n\")\n\tend\n\tfile:close()\nend\n\n---查询文件是否包含指定内容\n--@string path 目标文件\n--@string text 关键字\n--@return 关键字所在行数，不存在返回空表\nFile.find_text = function (path, text)\n\tlocal result = {}\n\tlocal row = 0\n\tfile = io.open(path, \"r\")\n    for line in file:lines() do\n    \trow = row + 1\n        if string.find(line, text) then\n        \ttable.insert(result, row)\n        end\n    end \n    return result\nend\n\nreturn File"
  },
  {
    "path": "lib/function.lua",
    "content": "--[[--常用函数集合\n@module Function\n@author iwiniwin\n\nDate   2020-02-27 13:27:14\nLast Modified by   iwiniwin\nLast Modified time 2020-01-16 13:45:04\n]]\nlocal M = {}\n\nfunction M.handler( obj, method )\n    return function ( ... )\n        if obj and method then\n            method(obj, ...)\n        end\n    end\nend\n\nreturn M"
  },
  {
    "path": "lib/string.lua",
    "content": "--[[--字符串操作\n@module string\n@author iwiniwin\n\nDate   2020-01-16 13:27:14\nLast Modified by   iwiniwin\nLast Modified time 2020-01-16 13:44:59\n]]\n\n\n-- local string = {};\n---Allows the ability to index into a string using square-bracket notation\n-- For example:\n--\t\ts = \"hello\"\n--\t\ts[1] = \"h\"\ngetmetatable('').__index = function(str, i)\n\tif (type(i) == 'number') then\n\t\treturn string.sub(str, i, i)\n\tend\n\t\n\treturn string[i]\nend\n \n\n--- Allows the ability to index into a string like above, but using normal brackes to\n-- return the substring\n-- For example:\n--\t\ts = \"hello\"\n--\t\ts(2,5) = \"ello\"\n--\n-- However, it also allows indexing into the string to return the byte (unicode) value\n-- of the character found at the index. This only occurs if the second value is omitted\n-- For example:\n--      s = \"hello\"\n--      s(2) = 101 (e)\n--\n-- Furthermore, it also allows for the ability to replace a character at the given index\n-- with the given characters, iff the second value is a string\n-- For example:\n--\t\ts = \"hello\"\n--\t\ts(2,'p') = \"hpllo\"\ngetmetatable('').__call = function(str, i, j)\n\tif (type(i) == 'number' and type(j) == 'number') then\n\t\treturn string.sub(str, i, j)\n\telseif (type(i) == 'number' and type(j) == 'string') then\n\t\treturn table.concat{string.sub(str, 1, i - 1), j, string.sub(str, i + 1)}\n\telseif (type(i) == 'number' and type(j) == 'nil') then\n\t\treturn string.byte(str, i)\n\tend\n\t\n\treturn string[i]\nend\n\n\n\n---Checks to see if the string starts with the given characters\nfunction string.starts_with(str, chars)\n\treturn chars == '' or string.sub(str, 1, string.len(chars)) == chars\nend\n\n\n\n---Checks to see if the string ends with the given characters\nfunction string.ends_with(str, chars)\n\treturn chars == '' or string.sub(str, -string.len(chars)) == chars\nend\n\n\n\n---Removes the length from the start of the string, returning the result\n---Length can be a number or string\nfunction string.remove_from_start(str, length)\n\tif (type(length) == 'number') then\n\t\treturn string.sub(str, length + 1, string.len(str))\n\telseif (type(length) == 'string') then\n\t\treturn string.sub(str, string.len(length) + 1, string.len(str))\n\telse\n\t\treturn str\n\tend\nend\n\n\n\n---Removes the length from the end of the string, returning the result\n---Length can be a number or string\nfunction string.remove_from_end(str, length)\n\tif (type(length) == 'number') then\n\t\treturn string.sub(str, 1, string.len(str) - length)\n\telseif (type(length) == 'string') then\n\t\treturn string.sub(str, 1, string.len(str) - string.len(length))\n\telse\n\t\treturn str\n\tend\nend\n\n\n\n---Removes a number of occurrences of the pattern from the string\n---If limit is blank, removes all occurrences\nfunction string.remove(str, pattern, limit)\n\tif (pattern == '' or pattern == nil) then\n\t\treturn str\n\tend\n\n\tif (limit == '' or limit == nil) then\n\t\tstr = string.gsub(str, pattern, '')\n\telse\n\t\tstr = string.gsub(str, pattern, '', limit)\n\tend\n\treturn str\nend\n\n\n--拼接字符串\nfunction string.concat(str1, str2, concatStr)\n\tlocal ret =  table.concat({str1, str2}, concatStr)\n\treturn ret\nend\n\n\n\n---Removes all occurrences of the pattern from the string\nfunction string.remove_all(str, pattern)\n\tif (pattern == '' or pattern == nil) then\n\t\treturn str\n\tend\n\n\tstr = string.gsub(str, pattern, '')\n\treturn str\nend\n\n\n\n\n\n---Removes the first occurrence of the pattern from the string\nfunction string.remove_first(str, pattern)\n\tif (pattern == '' or pattern == nil) then\n\t\treturn str\n\tend\n\n\tstr = string.gsub(str, pattern, '', 1)\n\treturn str\nend\n\n\n\n---Returns whether the string contains the pattern\nfunction string.contains(str, pattern)\n\tif (pattern == '' or string.find(str, pattern, 1)) then\n\t\treturn true\n\tend\n\t\n\treturn false\nend\n\n\n\n---A case-insensitive string.find, returning start and end position of pattern in string\nfunction string.findi(str, pattern)\n\treturn string.find(string.lower(str), string.lower(pattern), 1)\nend\n\n\n\n---Returns the first substring which matches the pattern in the string from a start index\nfunction string.find_pattern(str, pattern, start)\n\tif (pattern == '' or pattern == nil) then\n\t\treturn ''\n\tend\n\t\n\tif (start == '' or start == nil) then\n\t\tstart = 1\n\tend\n\n\treturn string.sub(str, string.find(str, pattern, start))\nend\n\n\n\n------Split the string by the given pattern, returning an array of the result\n------If pattern is omitted or nil, then default is to split on spaces\n------Array index starts at 1\n---function string.split(str, pattern)\n---\tlocal split = {}\n---\tlocal index = 1\n\t\n---\tif (pattern == '' or pattern == nil) then\n---\t\tpattern = '%s'\n---\tend\n\t\n---\tlocal previousstart = 1\n---\tlocal startpos, endpos = string.find(str, pattern, 1)\n\t\n---\twhile (startpos ~= nil) do\n---\t\tsplit[index] = string.sub(str, previousstart, startpos - 1)\n---\t\tpreviousstart = endpos + 1\n---\t\tindex = index + 1\n---\t\tstartpos, endpos = string.find(str, pattern, endpos + 1)\n---\tend\n\t\n---\tsplit[index] = string.sub(str, previousstart, string.len(str))\n\t\n---\treturn split\n---end\n\n---分割字字符串\n--@usage split[index] = string.sub(str, previousstart, string.len(str))\n\n--[[--\n分割字字符串\n]]\nfunction string.split(str, delimiter)\n\tif (delimiter == '') then return false end\n\tlocal pos, arr = 0, {}\n\t-- for each divider found\n\tfor st, sp in function() return string.find(str, delimiter, pos, true) end do\n\t\ttable.insert(arr, string.sub(str, pos, st - 1))\n\t\tpos = sp + 1\n\tend\n\ttable.insert(arr, string.sub(str, pos))\n\treturn arr\nend\n\n\n\n---Returns the array of word contained within the string\n---Array index starts at 1\nfunction string.to_word_array(str)\n\tlocal words = {}\n\tlocal index = 1\n\t\n\tfor word in string.gmatch(str, '%w+') do\n\t\twords[index] = word\n\t\tindex = index + 1\n\tend\n\t\n\treturn words\nend\n\n\n\n---Returns the number of letters within the string\nfunction string.letter_count(str)\n\tlocal _, count = string.gsub(str, '%a', '')\n\treturn count\nend\n\n\n\n---Returns the number of spaces within the string\nfunction string.space_count(str)\n\tlocal _, count = string.gsub(str, '%s', '')\n\treturn count\nend\n\n\n\n---Returns the number of times the pattern occurs within the string\nfunction string.pattern_count(str, pattern)\n\tif (pattern == '' or pattern == nil) then\n\t\treturn nil\n\tend\n\n\tlocal _, count = string.gsub(str, pattern, '')\n\treturn count\nend\n\n\n\n---Returns a table of how many of each character appears in the string\n---Table in the format: [\"char\"] = 2\nfunction string.char_totals(str)\n\tlocal totals = {}\n\tlocal temp = ''\n\t\n\tfor i = 1, string.len(str), 1 do\n\t\ttemp = str[i]\n\t\tif (totals[temp]) then\n\t\t\ttotals[temp] = totals[temp] + 1\n\t\telse\n\t\t\ttotals[temp] = 1\n\t\tend\n\tend\n\t\n\treturn totals\nend\n\n--模糊搜索，返回true、false,匹配单词中如果含有特殊字符串，返回false\nfunction string.fuzzy_match(sourceStr,  searchStr)\n\tif string.find(searchStr,\"[().%+-*?[^$]\")then\n\t\treturn \n\tend \n\tsourceStr = string.upper(sourceStr)\n\tsearchStr = string.upper(searchStr)\n\tsearchStr = string.gsub(searchStr,\"\",\".*\")\n\tif string.find(sourceStr,searchStr) then \n\t\treturn true\n\tend \nend\n\n---Returns the number of words within the string\nfunction string.word_count(str)\n\tlocal _, count = string.gsub(str, '%w+', '')\n\treturn count\nend\n\n\n\n---Returns a string which contains the lengths of each each word found in the given string\nfunction string.word_length(str)\n\tlocal lengths = string.gsub(str, '%w+', function(w) return string.len(w) end)\n\treturn lengths\nend\n\n\n\n---Returns a table of how many of each word appears in the string\n---Table in the format: [\"word\"] = 2\nfunction string.word_totals(str)\n\tlocal totals = {}\n\t\n\tfor word in string.gmatch(str, '%w+') do\n\t\tif (totals[word]) then\n\t\t\ttotals[word] = totals[word] + 1\n\t\telse\n\t\t\ttotals[word] = 1\n\t\tend\n\tend\n\t\n\treturn totals\nend\n\n\n\n---Returns byte (unicode) representation of each character within the string as an array\n---Array index starts at 1\nfunction string.to_byte_array(str)\n\tlocal bytes = {}\n\t\n\tfor i = 1, string.len(str), 1 do\n\t\tbytes[i] = string.byte(str, i)\n\tend\n\t\n\treturn bytes\nend\n\n\n\n---Returns character representation of each character within the string as an array\n---Array index starts at 1\nfunction string.to_char_array(str)\n\tlocal chars = {}\n\t\n\tfor i = 1, string.len(str), 1 do\n\t\tchars[i] = str[i]\n\tend\n\t\n\treturn chars\nend\n\n\n\n---Returns a string where occurrences of the pattern are put into upper-case\nfunction string.pattern_to_upper(str, pattern)\n\tif (pattern == '' or pattern == nil) then\n\t\treturn str\n\tend\n\n\tlocal upper = string.gsub(str, pattern, string.upper)\n\treturn upper\nend\n\n\n\n---Returns a string where occurrences of the pattern are put into lower-case\nfunction string.pattern_to_lower(str, pattern)\n\tif (pattern == '' or pattern == nil) then\n\t\treturn str\n\tend\n\n\tlocal lower = string.gsub(str, pattern, string.lower)\n\treturn lower\nend\n\n\n\n---Returns a string, where the given string's occurrences of the pattern is replaced by\n---the given characters, restricted by the given limit\nfunction string.replace(str, pattern, chars, limit)\n\tif (pattern == '' or pattern == nil) then\n\t\treturn str\n\tend\n\n\tif (limit == '' or limit == nil) then\n\t\tstr = string.gsub(str, pattern, chars)\n\telse\n\t\tstr = string.gsub(str, pattern, chars, limit)\n\tend\n\treturn str\nend\n\n\n\n---Replaces the character at the given index with the given characters\nfunction string.replace_at(str, index, chars)\n\treturn table.concat{string.sub(str, 1, index - 1), chars, string.sub(str, index + 1)}\nend\n\n\n\n---Returns a string, where the given string's occurrences of the pattern is replaced by\n---the given characters\nfunction string.replace_all(str, pattern, chars)\n\tif (pattern == '' or pattern == nil) then\n\t\treturn str\n\tend\n\n\tstr = string.gsub(str, pattern, chars)\n\treturn str\nend\n\n\n\n---Returns a string, where the given string's first occurrence of the pattern is replaced\n---by the given characters\nfunction string.replace_first(str, pattern, chars)\n\tif (pattern == '' or pattern == nil) then\n\t\treturn str\n\tend\n\n\tstr = string.gsub(str, pattern, chars, 1)\n\treturn str\nend\n\n\n\n---Returns the index within the string for the first occurrence of the pattern after the\n---given starting index\nfunction string.index_of(str, pattern, start)\n\tif (pattern == '' or pattern == nil) then\n\t\treturn nil\n\tend\n\t\n\tif (start == '' or start == nil) then\n\t\tstart = 1\n\tend\n\n\tlocal position = string.find(str, pattern, start)\n\treturn position\nend\n\n\n\n---Returns the index within the string for the first occurrence of the pattern\nfunction string.first_index_of(str, pattern)\n\tif (pattern == '' or pattern == nil) then\n\t\treturn nil\n\tend\n\n\tlocal position = string.find(str, pattern, 1)\n\treturn position\nend\n\n\n\n---Returns the index within the string for the last occurrence of the pattern\nfunction string.last_index_of(str, pattern)\n\tif (pattern == '' or pattern == nil) then\n\t\treturn nil\n\tend\n\t\n\tlocal position = string.find(str, pattern, 1)\n\tlocal previous = nil\n\t\n\twhile (position ~= nil) do\n\t\tprevious = position\n\t\tposition = string.find(str, pattern, previous + 1)\n\tend\n\t\n\treturn previous\nend\n\n\n\n---Returns the character at the specified index in the string\nfunction string.char_at(str, index)\n\treturn str[index]\nend\n\n\n\n---Returns the byte (unicode) value of the character at given index in the string\n---Basically the same as 'string.byte'\nfunction string.byte_at(str, index)\n\treturn string.byte(str, index)\nend\n\n\n\n---Returns the byte (unicode) value for the single given character\n---nil is returned if not single character or otherwise\nfunction string.byte_value(char)\n\tif (string.len(char) == 1) then\n\t\treturn string.byte(char, 1)\n\tend\n\t\n\treturn nil\nend\n\n\n\n---Compares two strings lexiographically. 1 is returned if str1 is greater than\n---str2. -1 if str1 is less than str2. And 0 if they are equal\n---This comparing is case-sensitive\nfunction string.compare(str1, str2)\n\tlocal len1 = string.len(str1)\n\tlocal len2 = string.len(str2)\n\tlocal smallestLen = 0;\n\t\n\tif (len1 <= len2) then\n\t\tsmallestLen = len1\n\telse\n\t\tsmallestLen = len2\n\tend\n\t\n\tfor i = 1, smallestLen, 1 do\n\t\tif (str1(i) > str2(i)) then\n\t\t\treturn 1\n\t\telseif (str1(i) < str2(i)) then\n\t\t\treturn -1\n\t\tend\n\tend\n\t\n\tlocal lengthDiff = len1 - len2\n\tif (lengthDiff < 0) then\n\t\treturn -1\n\telseif (lengthDiff > 0) then\n\t\treturn 1\n\telse\n\t\treturn 0\n\tend\nend\n\n\n\n---Compares two strings lexiographically. 1 is returned if str1 is greater than\n---str2. -1 if str1 is less than str2. And 0 if they are equal\n---This comparing is case-insensitive\nfunction string.comparei(str1, str2)\n\treturn string.compare(string.lower(str1), string.lower(str2))\nend\n\n\n\n---Returns whether the two strings are equal to one another. True of they are,\n---false otherwise\n---This equals function is case-sensitive\nfunction string.equal(str1, str2)\n\tlocal len1 = string.len(str1)\n\tlocal len2 = string.len(str2)\n\t\n\tif (len1 ~= len2) then\n\t\treturn false\n\tend\n\t\n\tfor i = 1, len1, 1 do\n\t\tif (str1[i] ~= str2[i]) then\n\t\t\treturn false\n\t\tend\n\tend\n\t\n\treturn true\nend\n\n\n\n---Returns whether the two strings are equal to one another. True of they are,\n---false otherwise\n---This equals function is case-insensitive\nfunction string.equali(str1, str2)\n\treturn string.equal(string.lower(str1), string.lower(str1))\nend\n\n---Returns the string representation of the given value. Be it either a\n---number, boolean, string or a table. nil is returned otherwise for functions,\n---threads, userdata and nil.\nfunction string.valueof(value)\n\tlocal t = type(value)\n\n\tif (t == 'string') then\n\t\treturn value\n\telseif (t == 'number') then\n\t\treturn '' .. value .. ''\n\telseif (t == 'boolean') then\n\t\tif (value) then\n\t\t\treturn \"true\"\n\t\telse\n\t\t\treturn \"false\"\n\t\tend\n\telseif (t == 'table') then\n\t\tlocal str = \"\"\n\t\tfor k,v in pairs(value) do\n\t\t\tstr = str .. \"[\" .. k .. \"] = \" .. v .. \"\\n\"\n\t\tend\n\t\tstr = string.sub(str, 1, string.len(str) - string.len(\"\\n\"))\n\t\treturn str\n\telse\n\t\treturn \"nil\"\n\tend\nend\n\n\n\n---Returns a string, where the given characters have been inserted into the\n---string at the required index. An index of 0 specifies the front of the string\nfunction string.insert(str, chars, index)\n\tif (index == 0) then\n\t\treturn chars .. str\n\telseif (index == string.len(str)) then\n\t\treturn str .. chars\n\telse\n\t\treturn string.sub(str, 1, index) .. chars .. string.sub(str, index + 1, string.len(str))\n\tend\nend\n\n\n\n---Returns a string, where the given characters have been inserted into the\n---string rep times at the required index. An index of 0 specifies the front of\n---the string\n---For example:\n--\t\tstring.insert_rep(\"ello\", \"h\", 4, 0) = \"hhhhello\"\nfunction string.insert_rep(str, chars, rep, index)\n\tlocal rep = string.rep(chars, rep)\n\treturn string.insert(str, rep, index)\nend\n\n\n\n---Returns a string where all characters starting at the given index have\n---been removed up to the end of the string (including the start index character)\nfunction string.remove_to_end(str, index)\n\tif (index == 1) then\n\t\treturn \"\"\n\telse\n\t\treturn string.sub(str, 1, index - 1)\n\tend\nend\n\n\n\n---Returns a string where all char_aters starting at the given index have\n---been removed down to the start of the string (including the start index character)\nfunction string.remove_to_start(str, index)\n\tif (index == string.len(str)) then\n\t\treturn \"\"\n\telse\n\t\treturn string.sub(str, index + 1, string.len(str))\n\tend\nend\n\n\n\n---Returns a string where the given string has had any leading and\n---trailing characters removed\n---If char is left blank, then whitespaces are removed\n--@usage\n--string.trim(\"[[[word[[[\", \"%[\") => \"word\"\n--string.trim(\"   word   \") => \"word\"\nfunction string.trim(str, char)\n\tif (char == '' or char == nil) then\n\t\tchar = '%s'\n\tend\n\n\tlocal trimmed = string.gsub(str, '^' .. char .. '*(.-)' .. char .. '*$', '%1')\n\treturn trimmed\nend\n\n\n\n---Returns a string where the given string has had any leading\n---characters removed\n---If char is left blank, then whitespaces are removed\nfunction string.trim_start(str, char)\n\tif (char == '' or char == nil) then\n\t\tchar = '%s'\n\tend\n\n\tlocal trimmed = string.gsub(str, '^' .. char .. '*', '')\n\treturn trimmed\nend\n\n\n\n---Returns a string where the gievn string has had any trailing\n---characters removed\n---If char is left blank, then whitespaces are removed\nfunction string.trim_end(str, char)\n\tif (char == '' or char == nil) then\n\t\tchar = '%s'\n\tend\n\n\tlocal length = string.len(str)\n\t\n\twhile (length > 0 and string.find(str, '^' .. char .. '', length)) do\n\t\tlength = length - 1\n\tend\n\t\n\treturn string.sub(str, 1, length)\nend\n\n\n\n---Returns a string where the given string has had variables substituted into it\n--@usage\n--string.subvar(\"x=$(x), y=$(y)\", {x=200, y=300}) => \"x=200, y=300\"\n--string.subvar(\"x=$(x), y=$(y)\", {['x']=200, ['y']=300}) => \"x=200, y=300\"\nfunction string.subvar(str, _table)\n\tstr = string.gsub(str, \"%$%(([%w_]+)%)\", function(key)\n\t\tlocal value = _table[key]\n\t\treturn value ~= nil and tostring(value)\n\tend)\n\t\n\treturn str\nend\n\n\n\n---Rotates the string about the given index, returning the result.\n--@usage\n--string.rotate(\"hello, 3) => \"lohel\"\nfunction string.rotate(str, index)\n\tlocal str1 = string.sub(str, 1, index)\n\tlocal str2 = string.sub(str, index + 1, string.len(str))\n\treturn str2 .. str1\nend\n\n\n\n---Averages the two strings together. This is done by adding the byte (unicode) values\n---of parallel characters and dividing by 2.\nfunction string.average(str1, str2)\n\tlocal len1 = string.len(str1)\n\tlocal len2 = string.len(str2)\n\tlocal smallestLen = 0\n\tlocal newstr = ''\n\t\n\tif (len1 <= len2) then\n\t\tsmallestLen = len1\n\telse\n\t\tsmallestLen = len2\n\tend\n\t\n\tfor i = 1, smallestLen, 1 do\n\t\tnewstr = newstr .. string.char( (str1(i) + str2(i)) / 2 )\n\tend\n\t\n\tif (len1 <= len2) then\n\t\tnewstr = newstr .. string.sub(str2, smallestLen + 1, string.len(str2))\n\telse\n\t\tnewstr = newstr .. string.sub(str1, smallestLen + 1, string.len(str1))\n\tend\n\t\n\treturn newstr\nend\n\n\n\n---Swaps the two characters at the given indices of the string\nfunction string.swap(str, index1, index2)\n\tlocal temp = str[index1]\n\tstr = str(index1, str[index2])\n\treturn str(index2, temp)\nend\n\n\n\n---Sorts the string into ascending order according to their unicode values.\nfunction string.sort_ascending(str)\n\tlocal chars = str:to_char_array()\n\ttable.sort(chars, function(a,b) return a(1) < b(1) end)\n\treturn table.concat(chars)\nend\n\n\n\n---Sorts the string into descending order according to their unicode values.\nfunction string.sort_descending(str)\n\tlocal chars = str:to_char_array()\n\ttable.sort(chars, function(a,b) return a(1) > b(1) end)\n\treturn table.concat(chars)\nend\n\n\n\n---Returns the character with the highest byte (unicode) value\nfunction string.highest(str)\n\tlocal s = string.sort_descending(str)\n\treturn s[1]\nend\n\n\n\n---Returns the character with the lowest byte (unicode) value\nfunction string.lowest(str)\n\tlocal s = string.sort_ascending(str)\n\treturn s[1]\nend\n\n\n\n---Checks to see if the string is empty\nfunction string.is_empty(str)\n\tif (str == '' or str == nil) then\n\t\treturn true\n\tend\n\t\n\treturn false\nend\n\n\n\n---Returns a table for the percentage of how much the string is formed of\n---each word.\n--@usage\n--string.word_percents(\"hello, world!\") = {\"hello\" = 38.46, \"world\" = 38.46}\nfunction string.word_percents(str)\n\tlocal t = string.word_totals(str)\n\tlocal count = string.len(str)\n\t\n\tfor k,v in pairs(t) do\n\t\tt[k] = ((string.len(k) * v) / count) * 100.0\n\tend\n\t\n\treturn t\nend\n\n\n\n---Returns the percentage for how much of the string is formed by the given word\n--@usage\n--string.word_percent(\"hello, world!\", \"hello\") = 50\nfunction string.word_percent(str, word)\n\tlocal t = string.word_percents(str)\n\t\n\tif (t[word]) then\n\t\treturn t[word]\n\tend\n\t\n\treturn 0\nend\n\n\n\n---Returns a table for the percentage of how much the string is formed of\n---each character.\n--@usage\n--string.char_percents(\"hello\") = {\"h\" = 20, \"e\" = 20, \"l\" = 40, \"o\" = 20}\nfunction string.char_percents(str)\n\tlocal t = string.char_totals(str)\n\tlocal count = string.len(str)\n\t\n\tfor k,v in pairs(t) do\n\t\tt[k] = (v/count) * 100.0\n\tend\n\t\n\treturn t\nend\n\n\n\n---Returns the percentage for how much of the string is formed by the given character\n--@usage\n--string.char_percent(\"hello\", \"h\") = 20\nfunction string.char_percent(str, char)\n\tlocal t = string.char_percents(str)\n\t\n\tif (t[char]) then\n\t\treturn t[char]\n\tend\n\t\n\treturn 0\nend\n\n\n\n---Returns the percentage for how much of the string is formed by whitespace\nfunction string.space_percent(str)\n\tlocal count = string.space_count(str)\n\treturn (count / string.len(str)) * 100.0\nend\n\n\n\n---Returns the number of uppercase characters in the string\nfunction string.upper_count(str)\n\tlocal _, count = string.gsub(str, '%u', '')\n\treturn count\nend\n\n\n\n---Returns the percentage for how much of the string is formed by uppercase\n---characters\nfunction string.upper_percent(str)\n\tlocal count = string.upper_count(str)\n\treturn (count / string.len(str)) * 100.0\nend\n\n\n\n---Returns the number of lowercase characters in the string\nfunction string.lower_count(str)\n\tlocal _, count = string.gsub(str, '%l', '')\n\treturn count\nend\n\n\n\n---Returns the percentage for how much of the string is formed by lowercase\n---characters\nfunction string.lower_percent(str)\n\tlocal count = string.lower_count(str)\n\treturn (count / string.len(str)) * 100.0\nend\n\n\n\n---Returns the number of single digits in the string\nfunction string.digit_count(str)\n\tlocal _, count = string.gsub(str, '%d', '')\n\treturn count\nend\n\n\n\n---Returns a table of how many of each single digit appears in the string\nfunction string.digit_totals(str)\n\tlocal totals = {}\n\t\n\tfor digit in string.gmatch(str, '%d') do\n\t\tif (totals[digit]) then\n\t\t\ttotals[digit] = totals[digit] + 1\n\t\telse\n\t\t\ttotals[digit] = 1\n\t\tend\n\tend\n\t\n\treturn totals\nend\n\n\n\n---Returns a table for the percentage of how much the string is formed of\n---each single digit.\n--@usage\n--string.digit_percents(\"hello, 2world!\") = {\"2\" = 7.14}\nfunction string.digit_percents(str)\n\tlocal t = string.digit_totals(str)\n\tlocal count = string.len(str)\n\t\n\tfor k,v in pairs(t) do\n\t\tt[k] = ((string.len(k) * v) / count) * 100.0\n\tend\n\t\n\treturn t\nend\n\n\n\n---Returns the percentage for how much of the string is formed by the given single digit\n--@usage\n--string.digit_percent(\"hello2\", \"2\") = 16.67\nfunction string.digit_percent(str, digit)\n\tlocal t = string.digit_percents(str)\n\t\n\tif (t[digit]) then\n\t\treturn t[digit]\n\tend\n\t\n\treturn 0\nend\n\n\n\n---Returns the amount of punctuation in the string\nfunction string.punc_count(str)\n\tlocal _, count = string.gsub(str, '%p', '')\n\treturn count\nend\n\n\n\n---Returns a table of how many of each punctuation appears in the string\nfunction string.punc_totals(str)\n\tlocal totals = {}\n\t\n\tfor punc in string.gmatch(str, '%p') do\n\t\tif (totals[punc]) then\n\t\t\ttotals[punc] = totals[punc] + 1\n\t\telse\n\t\t\ttotals[punc] = 1\n\t\tend\n\tend\n\t\n\treturn totals\nend\n\n\n\n---Returns a table for the percentage of how much the string is formed of\n---each punctuation.\n--@usage\n--string.punc_percents(\"hello, world!\") = {\",\" = 7.69, \"!\" = 7.69}\nfunction string.punc_percents(str)\n\tlocal t = string.punc_totals(str)\n\tlocal count = string.len(str)\n\t\n\tfor k,v in pairs(t) do\n\t\tt[k] = ((string.len(k) * v) / count) * 100.0\n\tend\n\t\n\treturn t\nend\n\n\n\n---Returns the percentage for how much of the string is formed by the given punctuation\n--@usage\n--string.punc_percent(\"hello, world!\", \",\") = 7.69\nfunction string.punc_percent(str, punc)\n\tlocal t = string.punc_percents(str)\n\t\n\tif (t[punc]) then\n\t\treturn t[punc]\n\tend\n\t\n\treturn 0\nend\n\n\n\n---Concatenates an array of strings together, with optional seperation characters\n---This is basically the same as doing table.concat(table, sep)\nfunction string.join(array, sep)\n\treturn table.concat(array, sep)\nend\n\n\n\n---Returns the Levenshtein distance between the two given strings\nfunction string.levenshtein(str1, str2)\n\tlocal len1 = string.len(str1)\n\tlocal len2 = string.len(str2)\n\tlocal matrix = {}\n\tlocal cost = 0\n\t\n\tif (len1 == 0) then\n\t\treturn len2\n\telseif (len2 == 0) then\n\t\treturn len1\n\telseif (str1 == str2) then\n\t\treturn 0\n\tend\n\t\n\tfor i = 0, len1, 1 do\n\t\tmatrix[i] = {}\n\t\tmatrix[i][0] = i\n\tend\n\tfor j = 0, len2, 1 do\n\t\tmatrix[0][j] = j\n\tend\n\t\n\tfor i = 1, len1, 1 do\n\t\tfor j = 1, len2, 1 do\n\t\t\tif (str1[i] == str2[j]) then\n\t\t\t\tcost = 0\n\t\t\telse\n\t\t\t\tcost = 1\n\t\t\tend\n\t\t\t\n\t\t\tmatrix[i][j] = math.min(matrix[i-1][j] + 1, matrix[i][j-1] + 1, matrix[i-1][j-1] + cost)\n\t\tend\n\tend\n\t\n\treturn matrix[len1][len2]\nend\n\n\n\n---Makes the string's first character lowercase\nfunction string.lower_first(str)\n\treturn str(1, string.lower(str[1]))\nend\n\n\n\n---Makes the string's first character uppercase\nfunction string.upper_first(str)\n\treturn str(1, string.upper(str[1]))\nend\n\n\n\n---Randomly shuffles the given string\nfunction string.shuffle(str)\n\tlocal temp = ''\n\tlocal length = string.len(str)\n\tlocal ran1, ran2 = 0, 0\n\tmath.randomseed(os.time())\n\t\n\tfor i = 1, length , 1 do\n\t\tran1 = math.random(length)\n\t\tran2 = math.random(length)\n\t\ttemp = str[ran1]\n\t\tstr = str(ran1, str[ran2])\n\t\tstr = str(ran2, temp)\n\tend\n\t\n\treturn str\nend\n\n\n\n---Converts the given integer value into a binary string of length limit\n---If limit is omitted, then a binary string of length 8 is returned\nfunction dectobin(dec, limit)\n\tif (limit == '' or limit == nil) then\n\t\tlimit = 8\n\tend\n\n\tlocal bin = ''\n\tlocal rem = 0\n\t\n\tfor i = 1, dec, 1 do\n\t\trem = dec % 2\n\t\tdec = dec - rem\n\t\tbin = rem .. bin\n\t\tdec = dec / 2\n\t\tif (dec <= 0) then break end\n\tend\n\t\n\tlocal padding = limit - (string.len(bin) % limit)\n\tif (padding ~= limit) then\n\t\tbin = string.insert_rep(bin, '0', padding, 0)\n\tend\n\t\n\treturn bin\nend\n\n\n\n---Returns the uuencoded representation of the given string\nfunction string.uuencode(str)\n\tlocal padding = 3 - (string.len(str) % 3)\n\tif (padding ~= 3) then\n\t\tstr = string.insert_rep(str, string.char(1), padding, string.len(str))\n\tend\n\t\n\tlocal uuenc = ''\n\tlocal bin1, bin2, bin3, binall = '', '', '', ''\n\t\n\tfor i = 1, string.len(str) - 2, 3 do\n\t\tbin1 = dectobin(string.byte(str[i]), 8)\n\t\tbin2 = dectobin(string.byte(str[i+1]), 8)\n\t\tbin3 = dectobin(string.byte(str[i+2]), 8)\n\t\t\n\t\tbinall = bin1 .. bin2 .. bin3\n\n\t\tuuenc = uuenc .. string.char(tonumber(binall(1,6), 2) + 32)\n\t\tuuenc = uuenc .. string.char(tonumber(binall(7,12), 2) + 32)\n\t\tuuenc = uuenc .. string.char(tonumber(binall(13,18), 2) + 32)\n\t\tuuenc = uuenc .. string.char(tonumber(binall(19,24), 2) + 32)\n\tend\n\t\n\treturn uuenc\nend\n\n\n\n---Returns the actual string from a uuencoded string\nfunction string.uudecode(str)\t\n\tlocal padding = 4 - (string.len(str) % 4)\n\tif (padding ~= 4) then\n\t\tstr = string.insert_rep(str, string.char(1), padding, string.len(str))\n\tend\n\t\n\tlocal uudec = ''\n\tlocal bin1, bin2, bin3, bin4, binall = '', '', '', '', ''\n\t\n\tfor i = 1, string.len(str) - 3, 4 do\n\t\tbin1 = dectobin(string.byte(str[i]) - 32, 6)\n\t\tbin2 = dectobin(string.byte(str[i+1]) - 32, 6)\n\t\tbin3 = dectobin(string.byte(str[i+2]) - 32, 6)\n\t\tbin4 = dectobin(string.byte(str[i+3]) - 32, 6)\n\t\t\n\t\tbinall = bin1 .. bin2 .. bin3 .. bin4\n\t\t\n\t\tuudec = uudec .. string.char(tonumber(binall(1,8), 2))\n\t\tuudec = uudec .. string.char(tonumber(binall(9,16), 2))\n\t\tuudec = uudec .. string.char(tonumber(binall(17,24), 2))\n\tend\n\t\n\treturn string.trim(uudec, string.char(1))\nend\n\n\n\n---Returns a simple hash key for a string. If the check value is ommited\n---then the string is hashed by the prime value of 17\n---Best results occur when the check value is prime\nfunction string.hash(str, check)\n\tlocal sum = 0\n\tlocal checksum = 17\n\tlocal length = string.len(str)\n\t\n\tif (check ~= '' and check ~= nil) then checksum = check end\n\t\n\tsum = str(1) + 1\n\tsum = sum + str(length) + length\n\tsum = sum + str(length/2) + math.ceil(length/2)\n\t\n\treturn sum % checksum\nend\n\n---url字符转换\nfunction string.urlencode_char(char)\n\treturn \"%\" .. string.format(\"%02X\", string.byte(char))\nend\n\n---url字符转换\nfunction string.urlencode(str)\n\t---convert line endings\n\tstr = string.gsub(tostring(str), \"\\n\", \"\\r\\n\")\n\t---escape all characters but alphanumeric, '.' and '-'\n\tstr = string.gsub(str, \"([^%w%.%- ])\", string.urlencode_char)\n\t---convert spaces to \"+\" symbols\n\treturn string.gsub(str, \" \", \"+\")\nend\n\nfunction string.ltrim(str)\n    return string.gsub(str, \"^[ \\t\\n\\r]+\", \"\")\nend\n\nfunction string.rtrim(str)\n    return string.gsub(str, \"[ \\t\\n\\r]+$\", \"\")\nend\n\nfunction string.trim(str)\n    str = string.gsub(str, \"^[ \\t\\n\\r]+\", \"\")\n    return string.gsub(str, \"[ \\t\\n\\r]+$\", \"\")\nend\n\n---检测手机号码是否正确\nfunction string.is_phone_num( PhoneNumText )\n    local phoneNum = string.trim(PhoneNumText);\n    local start, length = string.find(phoneNum, \"^1[3|4|5|8|7][0-9]%d+$\"); ---判断手机号码是否正确\n    if start ~= nil and length == 11 then\n        return true ;\n    end\n    return false;\nend\n\n---计算文字utf8的长度\nfunction string.utf8len(str)\n\tlocal len = #str\n\tlocal left = len\n\tlocal cnt = 0\n\tlocal arr = {0, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc }\n\twhile left > 0 do\n\t\tlocal tmp = string.byte(str, -left)\n\t\tlocal i = #arr\n\t\twhile arr[i] do\n\t\t\tif tmp >= arr[i] then\n\t\t\t\tleft = left - i\n\t\t\t\tbreak\n\t\t\tend\n\t\t\ti = i - 1\n\t\tend\n\t\tcnt = cnt + 1\n\tend\n\treturn cnt\nend\n\n--按照utf8格式取子串\nfunction string.utf8_sub_str(str,subLen)\n\n\tif subLen == 0 then return \"\" end \n\tif str == nil then \n\t\tdebug.traceback()\n\tprint(str, \"str\")\n\tend\n\n\tlocal len = #str\n\tlocal left = len\n\tlocal cnt = 0\n\tlocal arr = {0, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc }\n\twhile left > 0 do\n\t\tlocal tmp = string.byte(str, -left)\n\t\tlocal i = #arr\n\t\twhile arr[i] do\n\t\t\tif tmp >= arr[i] then\n\t\t\t\tleft = left - i\n\t\t\t\tbreak\n\t\t\tend\n\t\t\ti = i - 1\n\t\tend\n\t\tcnt = cnt + 1\n\t\tif cnt >= subLen then\n\t\t\tbreak;\n\t\tend\n\tend\n\tlocal temp = string.sub(str,0,len - left);\n\treturn temp\nend\n\n\nfunction string.utf8_char_str( str, index )\n \n\t local last = string.utf8_sub_str(str, index - 1)\n\n\t local tem = string.utf8_sub_str(str, index) \n\t local utf8_char_str = string.sub(str, #last + 1, #tem)\n\n\treturn utf8_char_str\nend\n\n-- local string = _G['string'];\n\n\n-- for k,v in pairs(string) do\n--     -- print_string(k)\n--     -- print_string(v)\n--     if string[k] then\n--     \t-- print(\"k---\",k)\n--     else\n--     \tstring[k] = v;\n--     \t-- print(\"k2---\",k)\n--     end\n-- end\n\nreturn string;"
  },
  {
    "path": "lib/table.lua",
    "content": "--[[--table操作\n@module table\n@author iwiniwin\n\nDate   2020-01-16 13:27:14\nLast Modified by   iwiniwin\nLast Modified time 2020-01-16 13:45:20\n]]\n\nlocal tableConcat = table.concat\nlocal tableInsert = table.insert\n\nlocal type = type\nlocal pairs = pairs\nlocal tostring = tostring\nlocal next = next\nlocal TableLib = {}\n\n--[[--\n    计算表格包含的字段数量\n    Lua table 的 \"#\" 操作只对依次排序的数值下标数组有效，TableLib.nums() 则计算 table 中所有不为 nil 的值的个数。\n\n    @tparam table t 要计算的表格\n    @return number 结果\n    @usage\n    local TableLib = import(\"bos.utils\").TableLib\n    TableLib.nums({a= 1,b=2})\n]]\nfunction TableLib.nums(t)\n    local temp = checktable(t)\n    local count = 0\n    for k, v in pairs(temp) do\n        count = count + 1\n    end\n    return count\nend\n\n\n--[[--\n    将来源表格中所有键及其值复制到目标表格对象中，如果存在同名键，则覆盖其值\n    @tparam table dest 目标表格\n    @tparam table src  来源表格\n    @usage\n    local dest = {a = 1, b = 2}\n    local src  = {c = 3, d = 4}\n    TableLib.merge(dest, src)\n    -- dest = {a = 1, b = 2, c = 3, d = 4}\n]]\nfunction TableLib.merge(dest, src)\n    if not src or not dest then\n        return;\n    end\n    for k, v in pairs(src) do\n        dest[k] = v\n    end\nend\n\n--[[--\n    合并两个表格的内容\n    @tparam table src1 来源表格1\n    @tparam table src2 来源表格2\n    @return table 合并后的新表\n    @usage\n    local src1 = {a = 1, b = 2}\n    local src2  = {c = 3, d = 4}\n    local temp = TableLib.merge(src1, src2)\n    -- src1 = {a = 1, b = 2}\n    -- temp = {a = 1, b = 2, c = 3, d = 4}\n]]\nfunction TableLib.merge2(src1, src2)\n    local tb ={}\n    if src1 and next(src1) then\n        for k, v in pairs(src1) do\n            tableInsert(tb,v);\n        end\n    end\n    if src2 and next(src2) then\n        for k, v in pairs(src2) do\n            tableInsert(tb,v);\n        end\n    end\n    return tb;\nend\n\n--[[--\n    合并两个表格的以数字开头的内容\n    @tparam table src1 来源表格1\n    @tparam table src2 来源表格2\n    @return table 合并后的新表\n    @usage\n    local src1 = {a = 1, b = 2, 3}\n    local src2  = {c = 3, d = 4, 4}\n    local temp = TableLib.merge3(src1, src2)\n     return {3, 4}\n]]\nfunction TableLib.merge3(src1, src2)\n    local tb ={}\n    if src1 and next(src1) then\n        for k, v in pairs(src1) do\n            if type(k) == \"number\" then\n                tableInsert(tb,v);\n            end\n        end\n    end\n    if src2 and next(src2) then\n        for k, v in pairs(src2) do\n            if type(k) == \"number\" then\n                tableInsert(tb,v);\n            end\n        end\n    end\n    return tb;\nend\n\n--[[--\n    同步数据,把tab2 的数据同步到 tab1（不是合并）\n    @tparam table tab1 来源表格1\n    @tparam table tab2 来源表格2\n    @usage\n    local tab1 = {c = 1, b = 2,g=9}\n    local tab2  = {c = 3, d = 4}\n    TableLib.sync(tab1, tab2)\n    -- tab1  = {c = 3, b = 2,g=9}\n    -- tab2  = {c = 3, d = 4}\n]]\nfunction TableLib.sync(tab1, tab2)\n    for k, v in pairs(tab2) do\n        if tab1[k] ~= nil then\n            tab1[k] = v;\n        end\n    end\nend\n\n--[[--\n    从表格中查找指定值，返回其 key，如果没找到返回 nil\n    @tparam table hashtable 表格\n    @tparam mixed value 要查找的值\n    @return string 该值对应的 key\n    @usage\n    local hashtable = {name = \"dualface\", comp = \"chukong\"}\n    print(TableLib.key_of(hashtable, \"chukong\")) -- 输出 comp\n]]\nfunction TableLib.key_of(hashtable, value)\n    for k, v in pairs(hashtable) do\n        if v == value then return k end\n    end\n    return nil\nend\n\n--[[--\n    从表格中查找指定值，返回其索引，如果没找到返回 false\n    @tparam table array 表格\n    @tparam mixed value 要查找的值\n    @tparam number begin 起始索引值\n    @return number \n    @usage\n    local a = {\"a\",\"b\",\"c\"}\n    TableLib.index_of(a,\"b\",1)\n]]\n\nfunction TableLib.index_of(array, value, begin)\n    for i = begin or 1, #array do\n        if array[i] == value then return i end\n    end\n    return false\nend\n\n--[[--\n    从表格中删除指定值，返回删除的值的个数\n    @tparam table array 表格\n    @tparam mixed value 要删除的值\n    @tparam boolean remove_all 是否删除所有相同的值\n    @return number 删除的值的个数\n    @usage \n    local array = {\"a\", \"b\", \"c\", \"c\"}\n    print(TableLib.remove_by_value(array, \"c\", true)) -- 输出 2\n]]\nfunction TableLib.remove_by_value(array, value, remove_all)\n    local c, i, max = 0, 1, #array\n    while i <= max do\n        if array[i] == value then\n            table.remove(array, i)\n            c = c + 1\n            i = i - 1\n            max = max - 1\n            if not remove_all then break end\n        end\n        i = i + 1\n    end\n    return c\nend\n\n--[[--\n    判断table是否为空\n    @tparam table t 表格\n    @return boolean 是否为空\n    @usage\n    TableLib.is_empty({}) -- true\n]]\nfunction TableLib.is_empty(t)\n    if t and type(t)==\"table\" then --FIXME 此句可以判空，为何还要循环表内元素？\n        return next(t)==nil;\n    end\n    return true;\nend\n\n--[[--\n    判断table是否为table\n    @tparam table t 表格\n    @return boolean 是否为table\n    @usage\n    TableLib.is_table({}) -- true\n]]\nfunction TableLib.is_table(t)\n    if type(t)==\"table\" then\n        return true;\n    end\n    return false;\nend\n\n--[[--\n    复制table\n    @tparam table st 表格\n    @return table 复制后的新表\n    @usage\n    TableLib.copy_tab({1,2,3,4,5}) \n]]\nfunction TableLib.copy_tab(st)\n    local tab = {}\n    for k, v in pairs(st or {}) do\n        if type(v) ~= \"table\" then\n            tab[k] = v\n        else\n            tab[k] = TableLib.copy_tab(v)\n        end\n    end\n    return tab\nend\n\n--[[--\n    从table1复制到table2\n    @tparam table target 目标表格\n    @tparam table source 被复制表格\n    @usage\n    TableLib.copy_to({1,2,3,4,5},{\"c\",\"c\",\"c\"}) \n]]\n\nfunction TableLib.copy_to(target, source)\n    for _,v in ipairs(source or {}) do\n        table.insert(target, v)\n    end\nend\n\n\n--[[--\n    table校验，返回自身或者{}\n    @tparam table t 目标表格\n    @return table 返回自身或者{}\n    @usage\n    TableLib.checktable({1,2,3,4,5}) \n]]\nfunction TableLib.checktable(t)\n    return TableLib.verify(t)\nend\n\n--[[--\n    table校验，返回自身或者{}\n    @tparam table t 目标表格\n    @return table 返回自身或者{}\n    @usage\n    TableLib.verify({1,2,3,4,5}) \n]]\nfunction TableLib.verify(t)\n    if t and type(t)==\"table\" then\n        return t;\n    end\n    return {};\nend\n\n--[[--\n    获取table 元素数量\n    @tparam table t 目标表格\n    @return number 数量\n    @usage\n    TableLib.size({1,2,3,4,5}) \n]]\nfunction TableLib.size(t)\n    if type(t) ~= \"table\" then\n        return 0;\n    end\n\n    local count = 0;\n    for _,v in pairs(t) do\n        count = count + 1;\n    end\n\n    return count;\nend\n\n--[[--\n    比较两个table的内容是否相同\n    @tparam table t1 目标表格\n    @tparam table t2 目标表格\n    @return boolean 是否相同\n    @usage\n    TableLib.equal({1,2,3,4,5},{5,4,3,2,1}) \n]]\nfunction TableLib.equal(t1,t2)\n    if type(t1) ~= type(t2) then\n        return false;\n    else\n        if type(t1) ~= \"table\" then\n            return t1 == t2;\n        else\n            local len1 = TableLib.size(t1);\n            local len2 = TableLib.size(t2);\n            if len1 ~= len2 then\n                return false;\n            else\n                local isEqual = true;\n                for k,v in pairs(t1) do\n                    if t2[k]  == nil then\n                        isEqual = false;\n                        break;\n                    else\n                        if type(t2[k]) ~= type(v) then\n                            isEqual = false;\n                            break;\n                        else\n                            if type(v) ~= \"table\" then\n                                if t2[k] ~= v then\n                                    isEqual = false;\n                                    break;\n                                end\n                            else\n                                isEqual = TableLib.equal(v,t2[k]);\n                                if not isEqual then\n                                    break;\n                                end\n                            end\n                        end\n                    end\n                end\n\n                return isEqual;\n            end\n        end\n    end\nend\n\n--[[--\n    从表里获取n个随机值\n    @tparam table t 目标表格\n    @tparam number num 获取个数\n    @return table 获取的值的table\n    @usage\n    TableLib.random({1,2,3,4,5},2) \n]]\nfunction TableLib.random(t, num)\n    assert(type(t) == \"table\", \"invalid arg\");\n    local randomList = { }\n\n    if not num or num > #t then\n        num = #t;\n    end\n\n    local rangeList = { };\n    for i,v in ipairs(t) do\n        rangeList[i] = v;\n    end\n\n    for i = 1, num do\n        local index = math.random(i, #rangeList);--生成一个随机数\n        rangeList[i], rangeList[index] = rangeList[index], rangeList[i];--交换\n        randomList[i] = rangeList[i];--交换以后把i位置的牌放到要返回的函数中\n    end\n\n    return randomList;\nend\n\n--[[--\n    序列化table\n    @tparam table root 目标表格\n    @return string 结果\n    @usage\n    TableLib.tostring({1,2,3,4,5}) \n]]\nfunction TableLib.tostring(root)\n    if not root then return end\n    local cache = {  [root] = \"root\" }\n    local flag = {};\n    local function _dump(t,name)\n        local mt = getmetatable(t)\n        if mt and mt.__tostring then\n            return tostring(t)\n        end\n        local temp = {}\n        for i,v in ipairs(t) do\n            flag[i] = true;\n            if cache[v] then\n                tableInsert(temp, cache[v])\n            elseif type(v) == \"table\" then\n                cache[v] = string.format(\"%s[%d]\", name, i)\n                tableInsert(temp, string.format(\"%s\", _dump(v, cache[v])))\n            else\n                tableInsert(temp, tostring(v))\n            end\n        end\n        for k,v in pairs(t) do\n            if not flag[k] then\n                local key = tostring(k)\n                if cache[v] then\n                    tableInsert(temp, string.format(\"%s=%s\", key, cache[v]))\n                elseif type(v) == \"table\" then\n                    cache[v] = string.format(\"%s.%s\", name, key)\n                    tableInsert(temp, string.format(\"%s=%s\", key, _dump(v, cache[v])))\n                else\n                    tableInsert(temp, string.format(\"%s=%s\", key, tostring(v)))\n                end\n            end\n        end\n        return string.format(\"{%s}\", tableConcat(temp,\",\"));\n    end\n    return _dump(root, \"root\");\nend\n\n--[[--\n    合并多个表到src\n    @tparam table src 目标表格\n    @tparam tables ... 待合并表，可多个\n    @usage\n    TableLib.deep_merge({1,2,3,4,5},{\"c\",\"c\",\"s\",\"c\"},{a = \"ccc\"}) \n]]\nfunction TableLib.deep_merge( src, ... )\n    local arg = {...};\n    for i,v1 in ipairs(arg) do\n        for k,v in pairs(v1) do\n            if type(v) == \"table\" and type(src[k]) == \"table\" then\n                TableLib.deep_merge(src[k], v);\n            else\n                src[k] = v;\n            end\n        end\n    end\nend\n\n--[[--\n    遍历表，处理函数返回true终止\n    @tparam table t 目标表格\n    @tparam function func 处理函数\n    @usage\n    local function a(i,v)\n        -- return true 返回true时停止遍历\n    end\n     TableLib.select({1,2,3,4}, a)\n]]\nfunction TableLib.select(t, func)\n    for i,v in ipairs(t) do\n        if func and func(i,v) == true then\n            return i, v;\n        end\n    end\nend\n\n--[[--\n    遍历所有元素，返回所有处理函数return true的元素\n    @tparam table t 目标表格\n    @tparam function func 处理函数\n    @return table array所有处理函数return true的元素\n    @usage\n    local function a(i,v)\n        -- return true 返回true时最后要返回该元素\n    end\n     TableLib.select({1,2,3,4}, a)\n]]\nfunction TableLib.select_all(t, func)\n    local temp = {};\n    for i,v in ipairs(t) do\n        if func and func(i,v) == true then\n            temp[#temp+1] = v;\n        end\n    end\n    return temp;\nend\n\n--[[--\n    检索某个元素\n    @tparam table t 目标表格\n    @tparam mixed ...  键值索引,传入多个会递归检索\n    @return nil|mixed 返回键值对应的值\n    @usage\n    local a = {\n        b = {  -- 传入\"b\"作为参数时就返回该表\n            c = 1  -- 在\"b\"之后传入\"c\"，就返回1\n        }\n    }\n     TableLib.retrive(a,\"b\",\"c\") -- return 1\n]]\nfunction TableLib.retrive(t, ...)\n    if not t then\n        return\n    end\n    local arg = {...}\n    local tmp = t;\n    for _,v in ipairs( arg ) do\n        if tmp[v] then\n            tmp = tmp[v];\n        else\n            return;\n        end\n    end\n    return tmp;\nend\n\n--[[--\n    设置table为只读\n    @tparam table t 目标表格\n    @usage\n    local a = {1,1,1,1}\n    TableLib.lock_write(a)\n]]\nfunction TableLib.lock_write(t)\n    local mt = getmetatable(t) or {};\n    mt.__newindex = function(_,k,v)\n        error(string.format(\"can't write [%s] into table\",k))\n    end;\n    if not getmetatable(t) then\n        setmetatable(t, mt);\n    end\nend\n\n--[[--\n    取消设置table为只读\n    @tparam table t 目标表格\n    @usage\n    local a = {1,1,1,1}\n    TableLib.lock_write(a)\n    TableLib.release_lock_write(a)\n]]\nfunction TableLib.release_lock_write(t)\n    local mt = getmetatable(t);\n    if not (mt and mt.__newindex) then\n        return\n    end\n    mt.__newindex = nil\nend\n\n--[[--\n    通过连续下标，获取table子集\n    @tparam table t 目标表格,array\n    @tparam number from 起始下标\n    @tparam number to 截止下标\n    @return table 子集\n    @usage\n    local a = {1,1,1,1}\n    TableLib.get_subset(a,2,3)\n]]\nfunction TableLib.get_subset(t, from, to)\n    assert(from > 0 and from <= to and to <= #t, string.format(\"invalid range : %d, %d\", from, to));\n    local sub = {}\n    for i=from,to do\n        sub[#sub + 1] = t[i]\n    end\n    return sub\nend\n\n--[[--\n    克隆一份表数据\n    @tparam table object 目标表格\n    @return table 克隆表\n    @usage\n    local a = {1,1,1,1}\n    TableLib.clone(a)\n]]\nfunction TableLib.clone(object)\n    local lookup_table = {}\n    local function _copy(object)\n        if type(object) ~= \"table\" then\n            return object\n        elseif lookup_table[object] then\n            return lookup_table[object]\n        end\n        local new_table = {}\n        lookup_table[object] = new_table\n        for key, value in pairs(object) do\n            new_table[_copy(key)] = _copy(value)\n        end\n        return setmetatable(new_table, getmetatable(object))\n    end\n    return _copy(object)\nend\n\nlocal function infilter( filter, key, value )\n    if filter.key then\n        for i,v in ipairs(filter.key) do\n            if v == key then return i end\n        end\n    end\n    if filter.class then\n        for i,v in ipairs(filter.class) do\n            if typeof and typeof(value, v) then return i end\n        end\n    end\n    return false\nend\n\n--[[--\n    克隆一份表数据，可过滤\n    @tparam table object 目标表格\n    @tparam table filter 过滤器{key = {\"ingorekey1\",...},class = {class1,...} }\n    @return table 克隆表\n    @usage\n    local a = {1,1,1,1}\n    local filter = {\n        key = {\"1\",\"3\"} -- 键值1、3对应的值不克隆\n    }\n    TableLib.clone(a,filter)\n]]\nfunction TableLib.clone2(object, filter)\n    local lookup_table = {}\n    filter = filter or {}\n    local function _copy(object, filter)\n        if type(object) ~= \"table\" then\n            return object\n        elseif lookup_table[object] then\n            return lookup_table[object]\n        end\n        local new_table = {}\n        lookup_table[object] = new_table\n        for key, value in pairs(object) do\n            if not infilter(filter, key, value) then\n                new_table[_copy(key, filter)] = _copy(value, filter)\n            end\n        end\n        return setmetatable(new_table, getmetatable(object))\n    end\n    return _copy(object, filter)\nend\n\n--[[--\n    加载string为table\n    @tparam string strTab 定义table的字符串\n    @return table 定义的表\n    @usage\n    local a = \"{1,1,1,1}\"\n    TableLib.load_str_tab(a)\n]]\nfunction TableLib.load_str_tab(strTab)\n    if string.match(strTab, \"^%s*%{.*%}%s*$\") then\n        return loadstring(\"return \" .. strTab)()\n    end\n    return strTab;\nend\n\n--[[--\n    返回table中所有key的集合\n    @tparam table tab 表\n    @return table key表\n    @usage\n    local a = \"{1,1,1,1}\"\n    TableLib.all_keys(a)\n]]\nfunction TableLib.all_keys( tab )\n    if next(tab) == nil then\n        print(\"nilTab\")\n        return\n    end\n\n    local keys = {}\n    for k,_ in pairs(tab) do\n        table.insert(keys, k)\n    end\n    return keys\nend\n\nreturn TableLib;"
  },
  {
    "path": "lib/time.lua",
    "content": "--[[--时间操作\n@module time\n@author iwiniwin\n\nDate   2020-01-16 13:27:14\nLast Modified by   iwiniwin\nLast Modified time 2020-01-16 13:44:56\n]]\nlocal time = {}\n\n-- time.sleep = function ( num )\n--     -- print(\"hhhh\")\n--     -- os.execute(\"sleep 1000\")\n--     io.popen(\"sleep 10000\")\n-- end\n-- print(os.clock())\n\n-- time.sleep(500000)\n-- print(\"你好\")\n\n-- 单位是秒\n-- require(\"socket\")\n-- time.sleep = function ( second )\n--     socket.select(nil, nil, second)\n-- end\n\n-- time.sleep = function ( second )\n--     if second > 0 then\n--         os.execute(\"ping -n \" .. second + 1 .. \" localhost > NUL\")\n--     end\n-- end\n\n\n-- time.sleep()\n\n-- print(\"uuuuuu\")\n\n\nreturn time"
  },
  {
    "path": "mvc/loader.lua",
    "content": "--[[--\n模块加载器\n@module Loader\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin\nLast Modified time 2020-01-16 13:58:34\n]]\nlocal ModuleConfig = import(\"mvc.module_config\")\n\n-- 根据配置读取模块\nlocal config = {}\nfor k,v in pairs(ModuleConfig) do\n   table.insert(config, {key = k, value = v})\nend\ntable.sort(config, function ( a, b )\n    -- 根据initOrder排序，确定加载顺序\n    if a.value.initOrder and not b.value.initOrder then\n        return true\n    end\n    if a.value.initOrder and b.value.initOrder then\n        return a.value.initOrder < b.value.initOrder\n    end\n    return false\nend)\n\n-- 模块创建函数\nlocal function create_module( name, params )\n    params = params or {}\n    assert(params.file)\n    local viewClass = require(params.file)\n    local view = new(viewClass)\n    view._tag = {name = name, params = params}\n    return view\nend\n\n-- 根据配置加载模块\nlocal moduleList = {}\nfor i,v in ipairs(config) do\n    if moduleList[v.key] then\n        error(\"模块已经存在：\" .. v.key)\n    end\n    local mod = create_module(v.key, v.value)\n    moduleList[v.key] = mod\nend\n\nreturn moduleList"
  },
  {
    "path": "mvc/module1/test1_ctr.lua",
    "content": "--[[--Test1Ctr 示例Ctr\n@module Test1Ctr\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin\nLast Modified time 2020-01-16 13:44:50\n]]\nlocal Test1Ctr = class()\nTest1Ctr._class_name = \"Test1Ctr\"\n\nfunction Test1Ctr:ctor( delegate )\n    dump(\"load Test1Ctr\")\n    self.m_delegate = delegate\nend\n\nfunction Test1Ctr:get_ui(  )\n    return self.m_delegate\nend\n\n-- 刷新视图\nfunction Test1Ctr:update_view( data )\n    -- Ctr负责逻辑处理，转换视图可识别的数据\n    -- data = process(data)\n    \n    -- 由View负责刷新视图\n    local ui = self:get_ui();\n    if ui then\n        ui:update_view(data)\n    end\nend\n\nfunction Test1Ctr:dtor( ... )\n    dump(\"unload Test1Ctr\")\n    self.m_delegate = nil\nend\n\nreturn Test1Ctr;"
  },
  {
    "path": "mvc/module1/test1_view.lua",
    "content": "--[[--ldoc desc\nTest1View 示例View\n@module Test1View\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin\nLast Modified time 2020-01-16 14:00:03\n]]\nlocal Test1Ctr = import(\"mvc.module1.test1_ctr\")\nlocal Test1View = class()\nTest1View._class_name = \"Test1View\"\n\nfunction Test1View:ctor( ... )\n    dump(\"load Test1View\")\n    self:bind_ctr()\nend\n\nfunction Test1View:bind_ctr(  )\n    if self.mCtr then\n        return false\n    else\n        self.mCtr = new(Test1Ctr, self)\n        return true\n    end\nend\n\nfunction Test1View:get_ctr( ... )\n    return self.mCtr\nend\n\n-- 更新视图\nfunction Test1View:update_view( data )\n    -- 数据驱动\n    -- 视图与逻辑分离，数据有什么就更新什么\n    if data.title then\n        -- 更新title\n    end\n    if data.content then\n        -- 更新content\n    end\n    if data.other then\n        -- 更新other\n    end\nend\n\nfunction Test1View:unbind_ctr( ... )\n    if self.mCtr then\n        delete(self.mCtr)\n        self.mCtr = nil\n    end\nend\n\nfunction Test1View:dtor( ... )\n    dump(\"unload Test1View\")\n    self:unbind_ctr()\nend\n\nreturn Test1View;"
  },
  {
    "path": "mvc/module2/test2_ctr.lua",
    "content": "--[[--ldoc desc\nTest2Ctr 示例Ctr\n@module Test2Ctr\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin\nLast Modified time 2020-01-16 13:37:37\n]]\nlocal Test2Ctr = class()\nTest2Ctr._class_name = \"Test2Ctr\"\n\nfunction Test2Ctr:ctor( delegate )\n    dump(\"load Test2Ctr\")\n    self.m_delegate = delegate\nend\n\nfunction Test2Ctr:getUI(  )\n    return self.m_delegate\nend\n\n-- 刷新视图\nfunction Test2Ctr:update_view( data )\n    -- Ctr负责逻辑处理，转换视图可识别的数据\n    -- data = process(data)\n    \n    -- 由View负责刷新视图\n    local ui = self:getUI();\n    if ui then\n        ui:update_view(data)\n    end\nend\n\nfunction Test2Ctr:dtor( ... )\n    dump(\"unload Test2Ctr\")\n    self.m_delegate = nil\nend\n\nreturn Test2Ctr;"
  },
  {
    "path": "mvc/module2/test2_view.lua",
    "content": "--[[--ldoc desc\nTest2View 示例View\n@module Test2View\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin\nLast Modified time 2020-01-16 13:59:57\n]]\nlocal Test2Ctr = import(\"mvc.module2.test2_ctr\")\nlocal Test2View = class()\nTest2View._class_name = \"Test2View\"\n\nfunction Test2View:ctor( ... )\n    dump(\"load Test2View\")\n    self:bind_ctr()\nend\n\nfunction Test2View:bind_ctr(  )\n    if self.mCtr then\n        return false\n    else\n        self.mCtr = new(Test2Ctr, self)\n        return true\n    end\nend\n\nfunction Test2View:get_ctr( ... )\n    return self.mCtr\nend\n\n-- 更新视图\nfunction Test2View:update_view( data )\n    -- 数据驱动\n    -- 视图与逻辑分离，数据有什么就更新什么\n    if data.title then\n        -- 更新title\n    end\n    if data.content then\n        -- 更新content\n    end\n    if data.other then\n        -- 更新other\n    end\nend\n\nfunction Test2View:unbind_ctr( ... )\n    if self.mCtr then\n        delete(self.mCtr)\n        self.mCtr = nil\n    end\nend\n\nfunction Test2View:dtor( ... )\n    dump(\"unload Test2View\")\n    self:unbind_ctr()\nend\n\nreturn Test2View;"
  },
  {
    "path": "mvc/module_config.lua",
    "content": "--[[--\r\nModuleConfig\r\n@module ModuleConfig\r\n@author iwiniwin\r\n\r\nDate   2019-11-15 19:20:39\r\nLast Modified by   iwiniwin\r\nLast Modified time 2020-01-16 13:59:10\r\n]]\r\nlocal ModuleConfig = {}\r\n\r\nModuleConfig.Module1 = {\r\n    file = \"mvc.module1.test1_view\",\r\n    initOrder = 2,\r\n}\r\n\r\nModuleConfig.Module2 = {\r\n    file = \"mvc.module2.test2_view\",\r\n    initOrder = 1,\r\n}\r\n\r\nreturn ModuleConfig"
  },
  {
    "path": "pattern/AbstractFactoryPattern.lua",
    "content": "--[[--\n抽象工厂模式\n@module AbstractFactoryPattern\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin\nLast Modified time 2020-01-16 13:39:53\n]]\npackage.path = package.path .. \";..\\\\?.lua;\"\nrequire(\"_load\")\n\n\n--[[\n    抽象工厂模式\n    定义：\n        抽象工厂模式提供一个接口，用于创建相关或依赖对象的家族，而不需要明确指定具体类\n    优点：\n        1. 抽象工厂允许客户使用抽象的接口来创建一组相关的产品，\n        而不需要知道（或关心）实际产出的具体产品是什么。这样一来，客户就从具体的产品中被解耦\n    设计原则：\n        1. 依赖倒置原则，依赖抽象，而不依赖具体类\n]]\n\n-- 抽象工厂，披萨原料工厂，定义一组接口用于生产产品家族。原料的获取采用了抽象工厂模式\nlocal PizzaIngredientFactory = class();\n-- 创建面团接口\nfunction PizzaIngredientFactory:createDough( ... )\n    -- body\nend\n-- 创建酱料接口\nfunction PizzaIngredientFactory:createSauce( ... )\n    -- body\nend\n\n-- 具体工厂类1\nlocal NYPizzaIngredientFactory = class();\n-- 实现创建面团接口\nfunction NYPizzaIngredientFactory:createDough( ... )\n    dump(\"create NY Ingredient Factory dough\")\nend\n-- 实现创建酱料接口\nfunction NYPizzaIngredientFactory:createSauce( ... )\n    dump(\"create NY Ingredient Factory sauce\")\nend\n\n-- 具体工厂类2\nlocal ChicagoPizzaIngredientFactory = class();\n-- 实现创建面团接口\nfunction ChicagoPizzaIngredientFactory:createDough( ... )\n    dump(\"create Chicago Ingredient Factory dough\")\nend\n-- 实现创建酱料接口\nfunction ChicagoPizzaIngredientFactory:createSauce( ... )\n    dump(\"create Chicago Ingredient Factory sauce\")\nend\n\n-- 抽象披萨类\nlocal Pizza = class();\nfunction Pizza:prepare( ... )\n    -- body\nend\n\n-- 具体披萨类\nlocal CheesePizza = class();\n-- 接收PizzaIngredientFactory对象\nfunction CheesePizza:ctor( factory )\n    self.factory = factory\nend\n-- 实现prepare接口\nfunction CheesePizza:prepare( ... )\n    local dough = self.factory:createDough();\n    local sauce = self.factory:createSauce();\nend\n\nlocal PizzaStore = class();\n\nfunction PizzaStore:orderPizza( type )\n    local pizza = self:createPizza(type);  -- 调用子类的创建披萨方法\n    pizza:prepare()\n    -- pizza:bake()\n    -- pizza:cut()\n    -- pizza:box()\n    return pizza;\nend\n\n-- 声明一个抽象的工厂方法，由子类去实现。pizza的获取采用了工厂方法模式\nfunction PizzaStore:createPizza( type )\n    \nend\n\nlocal NYStyleCheesePizza = class();\n-- 第一个子类，纽约披萨店\nlocal NYPizzaStore = class(PizzaStore)\n\nfunction NYPizzaStore:createPizza( type )\n    local factory = new(NYPizzaIngredientFactory);  -- 使用具体的原料工厂\n    if type == \"cheese\" then\n        return new(CheesePizza, factory);\n    end\nend\n\n\nlocal ChicagoCheesePizza = class();\n-- 第二个子类，芝加哥披萨店\nlocal ChicagoStore = class(PizzaStore)\n\nfunction ChicagoStore:createPizza( type )\n    local factory = new(ChicagoPizzaIngredientFactory);  -- 使用具体的原料工厂\n    if type == \"cheese\" then\n        return new(CheesePizza, factory)\n    end\nend\n\n-------------- 测试 -------------- \n\nlocal store = new(ChicagoStore)\nstore:orderPizza(\"cheese\")\n\n-- create Chicago Ingredient Factory dough\n-- create Chicago Ingredient Factory sauce"
  },
  {
    "path": "pattern/AdapterPattern.lua",
    "content": "--[[--\n适配器模式\n@module AdapterPattern\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin\nLast Modified time 2020-01-16 13:39:53\n]]\npackage.path = package.path .. \";..\\\\?.lua;\"\nrequire(\"_load\")\n\n--[[\n    适配器模式\n    定义：\n        将一个类的接口，转换成客户期望的另一个接口。适配器让原本接口不相容的类可以合作无间\n    优点：\n        可以通过创建适配器进行接口转换，让不兼容的接口编程兼容。这可以让客户从实现的接口解耦\n        如果在一段时间以后，我们想改变接口，适配器可以将改变的部分封装起来，客户就不必为了应对不同的系统而每次跟着修改\n]]\n\n-- 鸭子类\nlocal Duck = class();  \n-- 鸭子有呱呱叫能力\nfunction Duck:quack( ... )\n    -- body\nend\n-- 鸭子有飞行能力\nfunction Duck:fly( ... )\n    -- body\nend\n\n-- 绿头鸭\nlocal MallardDuck = class(Duck);\nfunction MallardDuck:quack( ... )\n    dump(\"mallard duck quack\")\nend\nfunction MallardDuck:fly( ... )\n    dump(\"mallard duck fly\")\nend\n\n-- 火鸡类\nlocal Turkey = class();\n-- 火鸡有咯咯叫能力\nfunction Turkey:gobble( ... )\n    -- body\nend\n-- 火鸡有飞行能力\nfunction Turkey:fly( ... )\n    -- body\nend\n\n-- 野生火鸡\nlocal WildTurkey = class(Turkey);\nfunction WildTurkey:gobble( ... )\n    dump(\"wild turkey gobble\")\nend\nfunction WildTurkey:fly( ... )\n    dump(\"wild turkey fly\")\nend\n\n-- 适配器，让火鸡来冒充鸭子\nlocal TurkeyAdapter = class(Duck);  -- 适配器需要实现想转换成的类型接口，也就是客户所期望看到的接口。即quack和fly\nfunction TurkeyAdapter:ctor( turkey )\n    self.turkey = turkey\nend\nfunction TurkeyAdapter:quack( ... )\n    self.turkey:gobble();\nend\n-- 火鸡有飞行能力\nfunction TurkeyAdapter:fly( ... )\n    self.turkey:fly();\nend\n\n\n-------------- 测试 -------------- \n\nlocal turkey = new(WildTurkey);\nlocal turkeyAdapter = new(TurkeyAdapter, turkey);  -- 将火鸡包装进一个火鸡适配器，使它看起来像是一只鸭子\n\n-- 测试鸭子\nlocal duck = turkeyAdapter;\nduck:quack();  -- wild turkey gobble\nduck:fly();  -- wild turkey fly"
  },
  {
    "path": "pattern/CORPattern.lua",
    "content": "--[[--\n责任链模式\n@module CORPattern\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin\nLast Modified time 2020-01-16 13:39:53\n]]\npackage.path = package.path .. \";..\\\\?.lua;\"\nrequire(\"_load\")\n--[[\n    责任链模式\n    定义：\n        将接收者对象连成一条链，并在该链上传递请求，直到有一个接收者对象处理它\n        通过让更多对象有机会处理请求，避免了请求发送者和接收者之间的耦合\n    使用场景：\n        经常被用在窗口系统中，处理鼠标和键盘之类的事件\n    优点：\n        1. 将请求的发送者和接收者解耦\n        2. 通过改变链内的成员或调动它们的次序，允许动态地新增或者删除责任\n    缺点：\n        1. 内存消耗，链上的所有对象都需要创建，可能有些对象根本不会被用到（或很少走进满足该对象处理的条件）\n        2. 性能消耗，处理需要一层层的传递，才能被正确的对象所处理\n]]\n\n\n--[[\n    责任链模式实例\n    客户到售楼处买房，请求折扣的例子\n]]\n-- 价格处理类\nlocal PriceHandler = class()\n\nfunction PriceHandler:ctor( ... )\n    -- 抽象方法 processDiscount\n    assert(self.processDiscount, \"子类必须实现processDiscount接口\")\nend\n\nfunction PriceHandler:setSuccessor( successor )\n    self.successor = successor\nend\n\n-- 目前是三个Handler，sale销售 和 manager经理 和 ceo\n\nlocal Sale = class(PriceHandler)\nSale._class_name = \"Sale\"\n\nfunction Sale:processDiscount( discount )\n    if discount < 0.2 then\n        print(\"sale处理了\" .. discount .. \"的折扣\")\n    else\n        self.successor:processDiscount(discount)\n    end\nend\n\nlocal Manager = class(PriceHandler)\nManager._class_name = \"Manager\"\n\nfunction Manager:processDiscount( discount )\n    if discount < 0.4 then\n        print(\"manager处理了\" .. discount .. \"的折扣\")\n    else\n        self.successor:processDiscount(discount)\n    end\nend\n\nlocal CEO = class(PriceHandler)\n\nfunction CEO:processDiscount( discount )\n    if discount < 0.8 then\n        print(\"ceo处理了\" .. discount .. \"的折扣\")\n    else\n        print(\"ceo拒绝了\" .. discount .. \"的折扣\")\n    end\nend\n\n-- PriceHandler工厂类\n-- 添加一个工厂类提供createPriceHandler方法，\n-- 而不直接在PriceHandler中提供的原因是基于单一职责原则，\n-- PriceHandler见名知意是用于价格处理的，而不应该有提供PriceHandler的功能\nlocal PriceHandlerFactor = class()\n\nfunction PriceHandlerFactor.createPriceHandler( ... )\n    -- 构造责任链\n    local sale = new(Sale)\n    local manager = new(Manager)\n    local ceo = new(CEO)\n    -- 销售设置后继是经理\n    sale:setSuccessor(manager)\n    -- 经理的后继是ceo\n    manager:setSuccessor(ceo)\n    -- ceo不存在直接后继\n\n    -- 由sale优先处理\n    return sale\nend\n\n\n-- 顾客\nlocal Customer = class()\n\nfunction Customer:setPriceHandler( priceHandler )\n    self.priceHandler = priceHandler\nend\n\nfunction Customer:requestDiscount( discount )\n    self.priceHandler:processDiscount(discount)\nend\n\n\n-- 测试\nlocal customer = new(Customer)\n\ncustomer:setPriceHandler(PriceHandlerFactor.createPriceHandler())\n\nfor i = 1, 10 do \n    -- 100次折扣申请\n    customer:requestDiscount(math.random())\nend\n\n-- 如何应对变化\n-- 如果此时ceo希望添加一个vp的角色，帮他审核0.5以下的折扣\n-- 只需要添加一个vp类，同时修改以下工厂方法\nprint(\"加入一个vp角色\")\n\nlocal VP = class(PriceHandler)\n\nfunction VP:processDiscount( discount )\n    if discount < 0.6 then\n        print(\"vp处理了\" .. discount .. \"的折扣\")\n    else\n        self.successor:processDiscount(discount)\n    end\nend\n\nfunction PriceHandlerFactor.createPriceHandler( ... )\n    local sale = new(Sale)\n    local manager = new(Manager)\n    local ceo = new(CEO)\n    -- 添加一个vp\n    local vp = new(VP)\n    -- 销售设置后继是经理\n    sale:setSuccessor(manager)\n\n    -- 修改经理的后继为vp\n    manager:setSuccessor(vp)\n\n    -- vp的后继为ceo\n    vp:setSuccessor(ceo)\n    -- ceo不存在直接后继\n\n    -- 由sale优先处理\n    return sale\nend\n\n\n-- 测试\nlocal customer = new(Customer)\n\ncustomer:setPriceHandler(PriceHandlerFactor.createPriceHandler())\n\nfor i = 1, 10 do \n    -- 100次折扣申请\n    customer:requestDiscount(math.random())\nend\n\n\n"
  },
  {
    "path": "pattern/CompositePattern.lua",
    "content": "--[[--\n组合模式\n@module CompositePattern\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin\nLast Modified time 2020-01-16 13:39:53\n]]\npackage.path = package.path .. \";..\\\\?.lua;\"\nrequire(\"_load\")\n\n--[[\n    适配器模式\n    定义：\n        将一个类的接口，转换成客户期望的另一个接口。适配器让原本接口不相容的类可以合作无间\n    优点：\n        组合以单一责任设计原则换取透明性。什么是透明性？通过让组件的接口同时包含一些管理子节点和叶子节点的操作，\n        客户就可以将组合和叶子节点一视同仁。也就是说，一个元素究竟是组合还是叶子节点，对客户是透明的\n]]\n\n-- 利用组合模式来设计菜单\n\n-- 菜单组件提供了一组接口，让菜单和菜单项共同使用\nlocal MenuComponent = class();\nfunction MenuComponent:getName( ... )\nend\nfunction MenuComponent:getPrice( ... )\nend\nfunction MenuComponent:add( component )\nend\nfunction MenuComponent:remove( component )\nend\nfunction MenuComponent:getChild( index )\nend\nfunction MenuComponent:print(  )\nend\n\n\n-- 菜单（组合菜单）。覆盖一些菜单组件对它有用的方法。此组合类可以持有菜单项和其它菜单\nlocal Menu = class(MenuComponent);\nfunction Menu:ctor( name )\n    self.name = name\n    self.menuComponents = {}  -- 菜单下可以有更多组件\nend\nfunction Menu:getName( ... )\nend\nfunction Menu:add( component )\n    table.insert(self.menuComponents, component)\nend\nfunction Menu:remove( component )\n    table.remove(self.menuComponents, component)\nend\nfunction Menu:getChild( index )\n    return self.menuComponents[index]\nend\nfunction Menu:print(  )\n    dump(\"menu : name is \" .. self.name)\n    for i,v in ipairs(self.menuComponents) do\n        v:print();\n    end\nend\n\n-- 菜单项。也覆盖一些对它有意义的方法。没有意义的就置之不理。因为菜单项已经是叶子节点，所以它的下面不能有任何组件\nlocal MenuItem = class(MenuComponent);\nfunction MenuItem:ctor( name, price )\n    self.name = name\n    self.price = price\nend\nfunction MenuItem:getName( ... )\nend\nfunction MenuItem:getPrice( ... )\nend\nfunction MenuItem:print( ... )\n    dump(\"menu item : name is \" .. self.name .. \", price is \" .. self.price)\nend\n\n-------------- 测试 -------------- \n\nlocal pancakeHouseMenu = new(Menu, \"PANCAKE HOUSE MENU\");  -- 煎饼屋菜单\nlocal dinerMenu = new(Menu, \"DINER MENU\");  -- 餐厅菜单\nlocal cafeMenu = new(Menu, \"CAFE MENU\");  -- 咖啡菜单\n\nlocal allMenus = new(Menu, \"ALL MENUS\")\nallMenus:add(pancakeHouseMenu);\nallMenus:add(dinerMenu)\nallMenus:add(cafeMenu)\n\ndinerMenu:add(new(MenuItem, \"Pasta\", 3.89));  -- 加入菜单项，面团\n\nlocal dessertMenu = new(Menu, \"DESSERT MENU\");  -- 甜点菜单\ndessertMenu:add(new(MenuItem, \"Apple Pie\", 1.59));  -- 加入菜单项\n\ndinerMenu:add(dessertMenu);  -- 加入菜单，甜点菜单（甜点菜单属于餐厅菜单的子菜单）\n\nallMenus:print()\n\n--[[\nmenu : name is ALL MENUS\nmenu : name is PANCAKE HOUSE MENU\"\nmenu : name is DINER MENU\"\nmenu item : name is Pasta, price is 3.89\"\nmenu : name is DESSERT MENU\"\nmenu item : name is Apple Pie, price is 1.59\"\nmenu : name is CAFE MENU\"\n]]"
  },
  {
    "path": "pattern/FactoryMethodPattern.lua",
    "content": "--[[--\n工厂方法模式\n@module FactoryMethodPattern\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin\nLast Modified time 2020-01-16 13:39:53\n]]\npackage.path = package.path .. \";..\\\\?.lua;\"\nrequire(\"_load\")\n\n\n--[[\n    简单工厂模式，不是一个真正的模式，但经常被用于封装创建对象的代码\n]]\n\nlocal CheesePizza = class();  -- 芝士披萨\nlocal PepperoniPizza = class();  -- 意大利香肠披萨\n\nlocal SimplePizzaFactory = class() \n\n-- 简单工厂，根据传入的参数，决定创建出哪一种产品类的实例\nSimplePizzaFactory.createPizza = function ( type )\n    if type == \"cheese\" then\n        return new(CheesePizza) \n    elseif type == \"pepperoni\" then\n        return new(PepperoniPizza)\n    end\nend\n\n--[[\n    工厂方法模式\n    定义：\n        定义了一个创建对象的接口，但由子类决定要实例化的类是哪一个。\n        工厂方法让类把实例化推迟到子类\n    优点：\n        1. 通过让子类决定该创建的对象是什么，来达到将对象创建的过程封装的目的\n    设计原则：\n        1. 依赖倒置原则，依赖抽象，而不依赖具体类\n]]\n\nlocal PizzaStore = class();\n\nfunction PizzaStore:orderPizza( type )\n    local pizza = self:createPizza(type);  -- 调用子类的创建披萨方法\n    -- pizza:prepare()\n    -- pizza:bake()\n    -- pizza:cut()\n    -- pizza:box()\n    return pizza;\nend\n\n-- 声明一个抽象的工厂方法，由子类去实现。实例化的责任被移到一个方法中，此方法就如同是一个工厂\nfunction PizzaStore:createPizza( type )\n    -- body\nend\n\nlocal NYStyleCheesePizza = class();\n-- 第一个子类，纽约披萨店\nlocal NYPizzaStore = class(PizzaStore)\n\nfunction NYPizzaStore:createPizza( type )\n    if type == \"cheese\" then\n        dump(\"create ny cheese pizza\")\n        return new(NYStyleChesePizza);\n    end\nend\n\n\nlocal ChicagoCheesePizza = class();\n-- 第二个子类，芝加哥披萨店\nlocal ChicagoStore = class(PizzaStore)\n\nfunction ChicagoStore:createPizza( type )\n    if type == \"cheese\" then\n        dump(\"create chicago cheese pizza\")\n        return new(ChicagoCheesePizza)\n    end\nend\n\n-------------- 测试 -------------- \n\nlocal store = new(ChicagoStore)\nstore:orderPizza(\"cheese\")  -- create chicago cheese pizza"
  },
  {
    "path": "pattern/ObserverPattern.lua",
    "content": "--[[--\n观察者模式\n@module ObserverPattern\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin\nLast Modified time 2020-01-16 13:40:08\n]]\npackage.path = package.path .. \";..\\\\?.lua;\"\nrequire(\"_load\")\n--[[\n    观察者模式\n    定义：\n        定义了对象之间的一对多依赖，这样一来，\n        当一个对象改变状态时，它的所有依赖者都会收到通知并自动更新\n    优点：\n        1. 主题和观察者之间是松耦合，主题只知道观察者实现了观察者接口，不需要知道\n            观察者的具体是谁，做了些什么\n        2. 任何时候都可以增加或删除观察者，主题不会受到任何影响\n    设计原则：\n        1. 为了交互对象之间的松耦合设计而努力\n        松耦合的设计能让我们建立有弹性的OO系统，能够应对变化，因为 对象之间的依赖降到了最低\n        2. 找出程序会变化的方面，然后将其和固定不变的方面相分离\n        3. 针对接口编程，不针对实现编程\n        观察者利用主题的接口注册，主题利用观察者的接口通知观察者\n        4. 多用组合，少用继承\n        观察者模式利用组合将许多观察者组合进主题中\n    注意：\n        有多个观察者时，不可以依赖特定的通知顺序\n]]\n\n\n--[[\n    观察者模式原型\n]]\n-- 主题 出版者\nlocal Subject = class()\n\nfunction Subject:ctor( ... )\n    -- 观察者队列\n    self.observers = {}\nend\n\n-- 注册观察者\nfunction Subject:registerObserver( observer )\n    table.insert(self.observers, observer)\nend\n\n-- 移除观察者\nfunction Subject:removeObserver( observer )\n    for i,v in ipairs(self.observers) do\n        if v == observer then\n            table.remove(self.observers, i)\n        end\n    end\nend\n\n-- 通知所有的观察者对象\nfunction Subject:notifyObservers( ... )\n    -- ...参数可以是自己，拉模型，观察者通过自己这个对象去获取更新的信息\n    -- 也可以是具体的状态信息，推模型（推荐这个）\n    for i,v in ipairs(self.observers) do\n        v:update( ... )\n    end\nend\n\n-- 观察者 订阅者\nlocal Observer = class() -- 纯接口\n-- 所有观察者必须实现观察接口\nfunction Observer:ctor( ... )\n    -- 定义了update接口\n    assert(self.update, \"必选实现update方法\")\nend\n\n--[[\n    观察者模式实例\n    小明和小红订阅天气信息\n]]\n\n-- 天气（主题）\nlocal Weather = class(Subject)\n\n-- 扩展内容 \n-- 添加changed标志 java.util.Observer中有\nfunction Weather:setChanged( ... )\n    self.changed = true\nend\n\nfunction Weather:clearChanged( ... )\n    self.changed = false\nend\n\nfunction Weather:hasChanged( ... )\n    return self.changed\nend\n\n-- 发布天气信息\n-- 利用changed的好处，使更新观察者时有更多的弹性\n-- 如果没有changed则一旦天气信息有了变化就会通知观察者，太过明锐\n-- 而通过changed，可以在天气变化达到某个条件时，再调用setChanged()进行有效的更新\nfunction Weather:setWeatcherInfo( ... )\n    if self.changed then\n        self:notifyObservers( ... )\n    end\n    self.changed = false\nend\n\n-- 订阅天气的人（观察者）\nlocal People = class(Observer)\n\nfunction People:ctor( name )\n    self.name = name\nend\n\nfunction People:update( ... )\n    print(string.format(\"%s收到了天气信息：%s\", self.name, ...))\nend\n\n-- 测试\nlocal weather = new(Weather)\n\n-- object.lua new实现有问题 这两个的name都是小红\nlocal ming = new(People, \"小明\")\nlocal hong = new(People, \"小红\")\n\nweather:registerObserver(ming)\nweather:registerObserver(hong)\n\n-- 发布天气信息\n-- 设置changed通知观察者\nweather:setChanged()\nweather:setWeatcherInfo(\"晴朗\")\n\n-- 移除一个观察者\nweather:removeObserver(ming)\n\n-- 没有设置changed，不会通知观察者\nprint(weather:hasChanged(), \"change状态\")\nweather:setWeatcherInfo(\"阴天\")\n\n"
  },
  {
    "path": "pattern/ProxyPattern.lua",
    "content": "--[[--\r\n@module ProxyPattern\r\n@author iwiniwin\r\n\r\nDate   2019-11-15 19:20:39\r\nLast Modified by   iwiniwin\r\nLast Modified time 2020-01-16 13:40:27\r\n]]\r\n"
  },
  {
    "path": "pattern/StrategyPattern.lua",
    "content": "--[[--策略模式\n@module StrategyPattern\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin\nLast Modified time 2020-01-16 13:40:54\n]]\npackage.path = package.path .. \";..\\\\?.lua;\"\nrequire(\"_load\")\n--[[\n    策略模式\n    定义：\n        将可变的部分从程序中抽象分离成算法接口\n        在该接口下分别封装一系列算法实现，并使他们可以相互替换\n        从而导致客户端程序独立于算法的改变\n    优点：\n        1. 足够灵活，不同的策略只需要给出封装的接口的不同实现即可，富有弹性，可以较好地应对变化\n        2. 复用代码，更易于维护，可以复用相同的策略\n        3. 消除大量的条件语句\n    缺点：\n        1. 增加了对象的数目\n        2. 客户代码需要了解策略的具体细节\n    设计原则：\n        1. 找出应用中需要变化的部分，把他们独立出来，不要和那些不需要变化的代码混在一起\n        鸭子的飞行行为是千变万化的，但是鸭子具有飞行行为是不变的，\n        将这个不变的部分抽象为飞行策略接口，而具体的飞行行为交给子类去实现\n        2. 面向接口编程，而不是面向实现编程\n        不如鸭子超类只是持有了飞行策略接口，而不是具体的飞行实现\n        3. 多用组合，少用继承\n]]\n\n\n--[[\n    策略模式原型\n]]\nlocal Super = class()\nfunction Super:ctor( ... )\n    -- body\nend\n\nfunction Super:setStrategy( strategy )\n    -- 通过组合注入策略\n    self.strategy = strategy\nend\n\nfunction Super:interface( ... )\n    -- 通过策略实现某个功能\n    self.strategy:interface()\nend\n\n-- 策略接口\nlocal Strategy = class()\nfunction Strategy:ctor( ... )\n    -- 声明了某个策略接口\n    assert(self.interface, \"必须实现某策略\")\nend\n\n\n--[[\n    策略模式实例\n    有一个鸭子的父类，已经有一个正常鸭子的实现，后面需要再实现橡皮鸭（不会飞），太空鸭（坐火箭飞）\n]]\n\n-- 鸭子超类\nlocal Duck = class()\n-- 鸭子都有一个外观\nfunction Duck:display( ... )\n\nend\n\nfunction Duck:setFlyStrategy( flyStrategy )\n    self.flyStrategy = flyStrategy\nend\n\nfunction Duck:fly( ... )\n    self.flyStrategy:fly()\nend\n\n-- 飞行策略接口\nlocal FlyStrategy = class()\nfunction FlyStrategy:ctor( ... )\n    assert(self.fly, \"必须实现飞行接口\")\nend\n\n\n-- 具体飞行策略\n\n-- 振翅高飞\nlocal FlyWithWin = class(FlyStrategy)\nfunction FlyWithWin:fly( ... )\n    print(\"振翅高飞\")\nend\nlocal flyWithWin = new(FlyWithWin)\n\n\n-- 正常鸭\nlocal NormalDuck = class(Duck)\nfunction NormalDuck:ctor( ... )\n    self:setFlyStrategy(flyWithWin)\nend\nfunction NormalDuck:display( ... )\n    print(\"我是正常鸭\")\nend\n\n-- 测试\nlocal normalDuck = new(NormalDuck)\nnormalDuck:display()\nnormalDuck:fly()\n\n\n-- 不会飞（也是一种飞行策略）\nlocal FlyNoWay = class(FlyStrategy)\nfunction FlyWithWin:fly( ... )\n    print(\"不会飞\")\nend\nlocal flyNoWay = new(FlyNoWay)\n\n-- 橡皮鸭\nlocal RubberDuck = class(Duck)\nfunction RubberDuck:ctor( ... )\n    self:setFlyStrategy(flyNoWay)\nend\nfunction RubberDuck:display( ... )\n    print(\"我是橡皮鸭\")\nend\n\n-- 测试\nlocal rubberDuck = new(RubberDuck)\nrubberDuck:display()\nrubberDuck:fly()\n\n\n-- 坐火箭飞\nlocal FlyWithRocket = class(FlyStrategy)\nfunction FlyWithRocket:fly( ... )\n    print(\"坐火箭飞\")\nend\nlocal flyWithRocket = new(FlyWithRocket)\n\n-- 太空鸭\nlocal SpaceDuck = class(Duck)\nfunction SpaceDuck:ctor( ... )\n    self:setFlyStrategy(flyWithRocket)\nend\nfunction SpaceDuck:display( ... )\n    print(\"我是正常鸭\")\nend\n\n-- 测试\nlocal spaceDuck = new(SpaceDuck)\nspaceDuck:display()\nspaceDuck:fly()\n\n-- 对于鸭子超类，没有在其中直接定义fly方法，或者fly接口的原因\n-- 不直接定义fly方法，不是所有的鸭子都会飞，对于不会飞的鸭子需要覆盖该方法，但是可能会由于某些原因忘记覆盖\n-- 不直接定义fly接口，这样的话，所有的子类都必须要实现该接口，\n-- 而且代码无法复用，重复代码多，比如同一个飞行策略，在具有相同的策略的子类中都要写一遍\n\n-- 采用策略模式的好处，灵活不同的策略可以有不同的实现，当某些子类有共同的飞行策略，还可以直接复用该策略\n\n"
  },
  {
    "path": "pattern/TemplatePattern.lua",
    "content": "--[[--模板方法模式\n@module TemplatePattern\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin\nLast Modified time 2020-01-16 13:41:10\n]]\npackage.path = package.path .. \";..\\\\?.lua;\"\nrequire(\"_load\")\n--[[\n    模板方法模式\n    定义：\n\n        在一个方法中定义一个算法的骨架，而将一些步骤延迟到子类中。\n        模板方法使得子类可以在不改变算法结构的情况下，重新定义算法中的某些步骤\n    优点：\n        1. 对算法有更多的控制权，超类主导一切，拥有且保护这个算法\n        2. 超类的存在将代码的复用最大化，算法只存在超类中，容易修改\n        3. 模板方法提供了一个框架，可以让各种子类插进来，不同的子类实现自己的方法就可以\n    设计原则：\n        好莱坞原则：别调用我们，我们会调用你\n        允许底层组件将自己挂钩到系统上，但是高层组件会决定什么时候和怎样使用这些底层组件\n]]\n\n\n--[[\n    模板方法模式原型\n]]\nlocal SuperClass = class()\n\nfunction SuperClass:templateMethod( ... )\n    -- 模板方法 子类不应该覆盖它\n\n    assert(self.primitiveOperation1, \"子类必须实现primitiveOperation1\")\n    assert(self.primitiveOperation2, \"子类必须实现primitiveOperation2\")\n\n    self.primitiveOperation1()\n    self.primitiveOperation1()\n\n    self.concreteOperation()\n\n    self.hook()\nend\n\nfunction SuperClass:concreteOperation( ... )\n    -- 在超类中具体实现，子类不应该覆盖\n    -- 可以被模板方法直接使用，或者被子类使用\nend\n\nfunction SuperClass:hook( ... )\n    -- 一个具体方法，但什么也不做\n    -- 钩子方法，子类可以根据情况决定要不要覆盖他\n    -- 如果算法的这个部分是可选的，就用钩子\n    -- 钩子可以让子类能够有机会对模板方法中某些即将发生的步骤做出反应\nend\n\n--[[\n    模板方法模式实例\n    封装一个制作饮品的具体算法\n]]\n\nlocal Drink = class()\n\nfunction Drink:prepareDrink( ... )\n\n    assert(self.brew)\n    assert(self.addCondiments)\n\n    -- 煮沸水\n    self.boilWater()\n    -- 冲泡\n    self.brew()\n    -- 倒入杯中\n    self.pourInCap()\n    -- 添加调料\n    if self:wantsCondiments() then\n        self.addCondiments()\n    end\nend\n\nfunction Drink:boilWater( ... )\n    print(\"把水煮沸。。。\")\nend\n\nfunction Drink:pourInCap( ... )\n    print(\"倒入杯中。。。\")\nend\n\nfunction Drink:wantsCondiments( ... )\n    -- 钩子方法\n    return true\nend\n-- 子类\n\nlocal Coffee = class(Drink)\n\nfunction Coffee:brew( ... )\n    print(\"用沸水冲泡咖啡粉\")\nend\n\nfunction Coffee:addCondiments( ... )\n    print(\"添加牛奶和糖\")\nend\n\n\nlocal coffee = new(Coffee)\ncoffee:prepareDrink()\n\nlocal Tea = class(Drink)\n\nfunction Tea:brew( ... )\n    print(\"用沸水浸泡茶叶\")\nend\n\nfunction Tea:addCondiments( ... )\n    print(\"添加柠檬\")\nend\n\nlocal tea = new(Tea)\ntea:prepareDrink()\n\n-- 不加调料的茶\n\nlocal TeaNoCondiments = class(Drink)\n\nfunction TeaNoCondiments:brew( ... )\n    print(\"用沸水浸泡茶叶\")\nend\n\nfunction TeaNoCondiments:addCondiments( ... )\n    print(\"添加柠檬\")\nend\n\nfunction TeaNoCondiments:wantsCondiments( ... )\n    return false\nend\n\nlocal tea = new(Tea)\ntea:prepareDrink()"
  },
  {
    "path": "test.lua",
    "content": "--[[--\nLuaKit测试用例\n@module test\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin\nLast Modified time 2020-04-01 16:59:33\n]]\n\n-- 测试面向对象\nlocal function test_oop( ... )\n    local Class1 = class()\n    function Class1:ctor( ... )\n        dump(\"Class1:ctor\")\n    end\n    function Class1:dtor( ... )\n        dump(\"Class1:dtor\")\n    end\n\n    -- Class2集成于Class1\n    local Class2 = class(Class1)\n    function Class2:ctor( ... )\n        dump(\"Class2:ctor\")\n    end\n    function Class2:dtor( ... )\n        dump(\"Class2:dtor\")\n    end\n    -- 实例化对象\n    local c1 = new(Class1)\n    local c2 = new (Class2)\n    -- 销毁对象\n    delete(c1)\n    delete(c2)\n\nend\n\n-- 测试dump\nlocal function test_dump()\n    local data = {\n        key1 = 34,\n        key2 = \"str\",\n        key3 = {\n            key4 = {\n                key5 = 56\n            },\n            key6 = 78\n        }\n    }\n    dump(data, \"this is a dump test\")\nend\n\n-- 测试分模块加载\nlocal function test_load_module( ... )\n    local moduleList = import(\"mvc.loader\")\n\n    -- 卸载模块\n    for k,v in pairs(moduleList) do\n        delete(v)\n    end\nend\n\n-- 测试性能分析\nlocal function test_profile( ... )\n    local new_profiler = import(\"utils.profiler\")\n    local profiler = new_profiler(\"call\")\n    profiler:start()  -- 开启性能分析\n\n    local function aaa(  )\n        for i = 1, 10000000 do\n\n        end\n    end\n    local function ttt(  )\n        aaa()\n    end\n    ttt()\n\n    -- 同时支持分析协程内的函数调用情况\n    local co = coroutine.create(function ( ... )\n        aaa()\n    end)\n    coroutine.resume(co)\n\n    profiler:stop()  -- 停止性能分析\n    -- 输出分析结果到文件\n    profiler:dump_report_to_file(\"profile.txt\")\nend\n\n-- 测试内存泄漏检测工具\nlocal function test_memory_monitor( ... )\n    local MemoryMonitor = import(\"utils.memory_monitor\")\n    local memoryMonitor = new(MemoryMonitor)\n\n    a = {}\n    function test( ... )\n        local b = {xxx = \"xxx\"}\n        a.b = b\n        memoryMonitor:add_to_leak_monitor(b, \"b\")  --将b添加到内存检测工具，此时a没有被释放掉 则b也释放不掉\n    end\n    test()\n\n    -- 由于a在引用b，因此b存在内存泄漏\n    memoryMonitor:update()\n\n    -- a不再引用b，b也被释放\n    a = nil\n    memoryMonitor:update()  -- 没有内存泄漏，这里不会打印日志\nend\n\n-- 测试组件系统\nlocal function test_component( ... )\n    local ComponentBase = import(\"core.component.component_base\")\n    local ComponentExtend = import(\"core.component.component_extend\")\n\n    local A = class()\n    ComponentExtend(A)\n\n    -- 组件1\n    local Component1 = class(ComponentBase)\n    Component1.exportInterface = {\n        {\"test1\"},\n    }\n    function Component1:test1( ... )\n        dump(\"call test1 ...\")\n    end\n\n    -- 组件2\n    local Component2 = class(ComponentBase)\n    Component2.exportInterface = {\n        {\"test2\"},\n    }\n    function Component2:test2( ... )\n        dump(\"call test2 ...\")\n    end\n\n    local a = new(A)\n\n    a:bind_component(Component1)  -- 对象a绑定组件1 拥有test1方法\n    a:bind_component(Component2)  -- 对象a绑定组件2 拥有test2方法\n    a:test1()\n    a:test2()\n    \n    a:unbind_component(Component1)  -- 解绑组件1 丧失test1方法\n    -- a:test1()  -- 报错 attempt to call method 'test1' (a nil value)\nend\n\n-- 测试事件分发系统\nlocal function test_event_system( ... )\n    local EventSystem = new(import(\"core.event.event_system\"))\n    local Event = import(\"core.event.event\")\n\n    -- 简单用法\n    EventSystem:on(\"test\", function ( ... )\n        dump({...})\n    end)\n\n    EventSystem:emit(\"test\", \"param1\", \"param2\")\n\n    -- 高级用法\n    local A = class()\n    function A:on_key_down( key )\n        dump(key, \"key name A\")\n    end\n    EventSystem:on(Event.KeyDown, A.on_key_down, {target = A})\n\n    local B = class()\n    function B:on_key_down( key )\n        dump(key, \"key name B\")\n\n        return true  -- 可以中断事件派发\n    end\n\n    -- 后注册的事件通过提高优先级可以保证先被调用\n    EventSystem:on(Event.KeyDown, B.on_key_down, {target = B, priority = 2})\n\n    EventSystem:emit(Event.KeyDown, \"Ctrl\")\n\n    EventSystem:off_all(B)  -- 通过target取消注册\n\n    EventSystem:emit(Event.KeyDown, \"Ctrl\")\n\nend\n\n-- test_oop()\n-- test_dump()\n-- test_load_module()\n-- test_profile()\n-- test_memory_monitor()\n-- test_component()\ntest_event_system()\n\n-- 数据观察追踪 回退系统\n"
  },
  {
    "path": "utils/dump.lua",
    "content": "--[[--\n格式化输出table（格式化过程中，排序操作会比较耗时）\n@module dump\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin\nLast Modified time 2020-01-16 13:41:51\n]]\n\nlocal isSort  = true;\n\nlocal table_format = string.format\nlocal string_len = string.len\nlocal string_rep = string.rep\n\nlocal math_randomseed = math.randomseed\nlocal debug_traceback = debug.traceback\n\nlocal function _dump_value(v)\n    if type(v) == \"string\" then\n        v = \"\\\"\" .. v .. \"\\\"\"\n    end\n    return tostring(v)\nend\n\n--- dump 输出table的内容\n--@param value 输出的table\n--@string desciption 调试信息格式\n--@int nesting 输出时的嵌套层级，默认为 15\n--@usage local t = {key = \"xxx\"}\n--dump(t)\nlocal function dump(value, desciption, nesting)\n    if type(nesting) ~= \"number\" then nesting = 10 end\n\n    local lookup = {}\n    local result = {}\n    local traceback = string.split(debug_traceback(\"\", 2), \"\\n\")\n    \n    local str = \"- dump from: \" .. string.trim(traceback[3]);\n    local function _dump(value, desciption, indent, nest, keylen)\n        desciption = desciption or \"<var>\"\n        local spc = \"\"\n        if type(keylen) == \"number\" then\n            spc = string_rep(\" \", keylen - string_len(_dump_value(desciption)))\n        end\n        if type(value) ~= \"table\" then\n            result[#result +1 ] = table_format(\"%s%s%s = %s\", indent, _dump_value(desciption), spc, _dump_value(value))\n        elseif lookup[tostring(value)] then\n            result[#result +1 ] = table_format(\"%s%s%s = *REF*\", indent, _dump_value(desciption), spc)\n        else\n            lookup[tostring(value)] = true\n            if nest > nesting then\n                result[#result +1 ] = table_format(\"%s%s = *MAX NESTING*\", indent, _dump_value(desciption))\n            else\n                result[#result +1 ] = table_format(\"%s%s = {\", indent, _dump_value(desciption))\n                local indent2 = indent..\"    \"\n                local keys = {}\n                local keylen = 0\n                local values = {}\n                for k, v in pairs(value) do\n                    if k~=\"___message\" then\n                        keys[#keys + 1] = k\n                        local vk = _dump_value(k)\n                        local vkl = string_len(vk)\n                        if vkl > keylen then keylen = vkl end\n                        values[k] = v\n                    end\n                end\n                if isSort == true then\n                    table.sort(keys, function(a, b)\n                        if type(a) == \"number\" and type(b) == \"number\" then\n                            return a < b\n                        else\n                            return tostring(a) < tostring(b)\n                        end\n                    end)\n                end\n\n\n                for i, k in ipairs(keys) do\n                    _dump(values[k], k, indent2, nest + 1, keylen)\n                end\n                result[#result +1] = table_format(\"%s}\", indent)\n            end\n        end\n    end\n    _dump(value, desciption, \"- \", 1)\n\n    str = str .. \"\\n\" .. table.concat(result, \"\\n\")\n    print(str)\nend\n\nreturn dump;"
  },
  {
    "path": "utils/dump_to_file.lua",
    "content": "--[[--\n序列化lua table\n@module dump_to_file\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin\nLast Modified time 2020-01-16 13:42:17\n]]\n\nlocal M = {};\n\nlocal table_format = string.format\nlocal string_len = string.len\nlocal string_rep = string.rep\n\n\n---数列化table 报错到文件\n--@string t 表\n--@string tabName 表名\n--@string path 保存目录，可选\n--@retrurn tableStr\nfunction M.serialize(t, tabName,path)\n    local function dump(value, desciption, nesting)\n    \tlocal lookup = {}\n    \tlocal result = {}\n    \tif type(nesting) ~= \"number\" then nesting = 100 end\n\n\t\tlocal function _dump_value(v)\n\t\t    if type(v) == \"string\" then\n\t\t        v = string.format(\"%q\", v)\n\t\t    end\n\t\t    if type(v) == \"function\" or type(v) == \"userdata\" then\n\t\t    \tv = string.format(\"%q\", tostring(v))\n\t\t    end\n\n\t\t \t-- if type(v) == \"userdata\" then\n\t\t  --   \tv = string.format(\"%q\", tostring(v))\n\t\t  --   end\n\n\t\t    -- if type(v) == \"number\" then\n\t\t    \t-- v = string.format(\"%.2f\",v)\n\t\t    \t-- 如果是小数,保留小数点后两位\n\t\t    \t-- if math.floor(v) < v then\n\t\t    \t-- \tv = string.format(\"%.2f\",v)\n\t\t    \t-- end\n\t\t    -- end\n\t\t    return tostring(v)\n\t\tend\n\n\t\tlocal function _dump_key(v)\n\t\t    if type(v) == \"number\" then\n\t\t        v = \"[\" .. v .. \"]\"\n\t\t    end\n\t\t    return v\n\t\tend\n\n\t    local function _dump(value, desciption, indent, nest, keylen)\n\t        desciption = desciption or \"<var>\"\n\t        local spc = \"\"\n\t        if type(keylen) == \"number\" then\n\t            spc = string_rep(\" \", keylen - string_len(_dump_value(desciption)))\n\t        end\n\n\t        if type(value) ~= \"table\" then\n\t            result[#result +1 ] = table_format(\"%s%s%s = %s,\", indent, _dump_key(desciption), spc, _dump_value(value))\n\t        elseif lookup[tostring(value)] then\n\t            result[#result +1 ] = table_format(\"%s%s%s = '*REF*%s',\", indent, desciption, spc,tostring(value))\n\t        else\n\t            lookup[tostring(value)] = true\n\t            if nest > nesting then\n\t                result[#result +1 ] = table_format(\"%s%s = '*MAX NESTING*',\", indent, desciption)\n\t            else\n\t            \tresult[#result +1 ] = table_format(\"%s%s = {\", indent, _dump_key(desciption))\n\t                local indent2 = indent..\"    \"\n\t                local keys = {}\n\t                local keylen = 0\n\t                local values = {}\n\t                for k, v in pairs(value) do\n\t                \tif k~=\"___message\" then\n\t                \t\tkeys[#keys + 1] = k\n\t\t                    local vk = _dump_value(k)\n\t\t                    local vkl = string_len(vk)\n\t\t                    if vkl > keylen then keylen = vkl end\n\t\t                    values[k] = v\n\t                \tend\n\t                end\n\t                -- table.sort(keys, function(a, b)\n\t                --     if type(a) == \"number\" and type(b) == \"number\" then\n\t                --         return a < b\n\t                --     else\n\t                --         return tostring(a) < tostring(b)\n\t                --     end\n\t                -- end)\n\t                for i, k in ipairs(keys) do\n\t                    _dump(values[k], k, indent2, nest + 1, keylen)\n\t                end\n\t                result[#result +1] = table_format(\"%s},\", indent)\n\t            end\n\t        end\n\t    end\n\n    \t_dump(value, desciption, \"\", 1)\n    \tresult[1] \t\t= \"{\";\n    \tresult[#result] = \"}\";\n\n    \tlocal ret = \"\"\n\t    for i, line in ipairs(result) do\n\t        ret = ret .. line .. \"\\n\";\n\t    end\n\t    return ret;\n\tend\n\n\ttabName = tabName or \"ret\"\n    \n\n    local str = \"do local \" .. tabName .. \" =\\n\"..dump(t) .. string.format(\"\\nreturn %s end\",tabName);\n    -- local path = System.getStorageTempPath()  .. tabName .. \".lua\"\n    local filePath = tabName .. \".lua\"\n    if path then\n        filePath = path .. \"/\" .. filePath\n    end\n    M.writefile(str,filePath)\n    return str;\nend\n\n\nfunction M.writefile(str, file)\n    --os.remove(file);\n    local file =io.open(file,\"w\");\n    if file then\n    \tfile:write(str);\n    \tfile:close();\n    end\nend\n\n\nfunction M.dump_to_file(t,tabName,path)\n\treturn M.serialize(t,tabName,path)\nend\n\n\nreturn M;"
  },
  {
    "path": "utils/memory_monitor.lua",
    "content": "--[[--\nlua内存泄漏检测工具\n@module MemoryMonitor\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin\nLast Modified time 2020-01-16 13:42:34\n]]\n--[[\n    lua内存泄漏检测工具\n    原理：弱表中的引用是弱引用，不会导致对象的引用计数发生变化\n    即如果一个对象只有弱引用指向它，那么gc会自动回收该对象的内存\n]]\n--[[\n    Lua运行了一个垃圾收集器来收集所有死对象来完成自动内存管理的工作\n    Lua实现了一个增量标记-扫描收集器。它使用间歇率和步进倍率这两个数字来控制垃圾收集循环，都是以百分数为单位（例如：值100在内部表示1）\n\n    间歇率控制收集器在开启新的循环前要等待多久，增大这个值将减少收集器的积极性。当这个值比100小的时候，收集器在开始新的循环前不会有等待，\n    设置这个值为200就会让收集器等到总内存使用量达到之前的两倍时才开始新的循环\n\n    步进倍率控制着收集器运行速度相对于内存分配速度的倍率，增大这个值不仅会让收集器更加积极，还会增加每个增量步骤的长度。不要把这个值设置\n    的小于100，那样的话收集器就工作的太慢了以至于永远都干不完一个循环。默认值是200，表示收集器以内存分配的两倍速工作\n\n    collectgarbage(\"collect\") :  做一次完整的垃圾收集循环\n    collectgarbage(\"count\") : 以k字节数为单位返回Lua使用的总内存数，这个值有小数部分，所以只需要乘上1024就能得到Lua使用的准确字节数\n    collectgarbage(\"restart\") : 重启垃圾收集器的自动运行\n    collectgarbage(\"setpause\") : 将arg设为收集器的间歇率，并返回间歇率的前一个值\n    collectgarbage(\"setstepmul\") : 将arg设为收集器的步进倍率，并返回步进倍率的前一个值\n    collectgarbage(\"step\") : 单步运行垃圾收集器，步长大小由arg控制，传入0时，收集器步进一步，传入非0值，收集器收集相当于Lua分配这么多（）内存的工作。如果垃圾收集器结束一个循环将返回true\n    collectgarbage(\"stop\") : 停止垃圾收集器的运行。在调用重启前，收集器只会因显示的调用运行\n]]\n\n-- 监控间隔配置（单位：秒）\nlocal MonitorConfig = {\n    -- 内存泄漏监控间隔\n    memLeakInterval = 1,\n}\n\nlocal MemoryMonitor = {}\n\nfunction MemoryMonitor:ctor( ... )\n    -- 内存泄漏弱引用表\n    self.__memLeakTable = {}\n    -- mode字段可以取 k, v, kv 分别表示table中的 key, value，是弱引用的， kv就是二者的组合\n    -- 对于一个table，任何情况下，只要它的key或者value中的一个被gc，那么这个key-value pair就从表中移除了\n    setmetatable(self.__memLeakTable, {__mode = \"kv\"})\n    -- 内存泄漏监控器\n    self.__memLeakMonitor = nil\n\n    self:start()\nend\n\n-- 开始检测\nfunction MemoryMonitor:start( ... )\n    self.__memLeakMonitor = self:__mem_leak_monitoring()\nend\n\n\n--[[\n把一个表或者对象添加到内存检测工具中，如果该表或者对象不存在外部引用，则说明释放干净\n否则内存泄漏工具会输出日志\n@table t 观察的对象 表\n@string tName 表的别名\n\n@usage \nlocal memoryMonitor = new(MemoryMonitor)\nmemoryMonitor:add_to_leak_monitor(self, \"xx模块\")\n]]\nfunction MemoryMonitor:add_to_leak_monitor( t, tName )\n    if not self.__memLeakMonitor then\n        return\n    end\n\n    assert(\"string\" == type(tName), \"invalid params\")\n\n    -- 必须以名字+地址的方式作为键值\n    -- 内存泄漏经常是一句代码多次分配出内存而忘了回收，因此tName经常是相同的\n    local name = string.format(\"%s@%s\", tName, tostring(t))\n    if nil == self.__memLeakTable[name] then\n        self.__memLeakTable[name] = t\n    end\nend\n\n-- 更新弱表信息\nfunction MemoryMonitor:update( dt )\n    dt = dt or 10\n    if self.__memLeakMonitor then\n        self.__memLeakMonitor(dt)\n    end\nend\n\n\n\nfunction MemoryMonitor:__mem_leak_monitoring( ... )\n    local monitorTime = MonitorConfig.memLeakInterval\n    local interval = MonitorConfig.memLeakInterval\n    local str = nil\n    return function( dt )\n        interval = interval + dt\n        if interval >= monitorTime then\n            interval = interval - monitorTime\n\n            -- 强制调用gc\n            collectgarbage(\"collect\")\n            collectgarbage(\"collect\")\n            collectgarbage(\"collect\")\n            collectgarbage(\"collect\")\n\n            local flag = false\n            -- 打印当前内存泄漏监控表中依然存在（没有被释放）的对象信息\n            str = \"存在以下内存泄漏：\"\n            for k,v in pairs(self.__memLeakTable) do\n                str = str .. string.format(\"    \\n%s = %s\", tostring(k), tostring(v))\n                flag = true\n            end\n            str = str .. \"\\n请仔细检查代码！！！\"\n            if flag then\n                print(str)\n            end\n        end\n    end\nend\n\nreturn MemoryMonitor\n\n--[[\n-- TODO 待研究情况\nprint(\"--------------分隔符--------------\")\na = {}\n\nlocal b = {xxx = \"xxx\"}\n\na.b = b\n\nmemoryMonitor:add_to_leak_monitor(b, \"b\")\n\na = nil\n-- 此时b仍然没有被释放掉\n-- 可能是由于b是local变量，不仅有a在引用b，可能lua的堆栈也在对其引用，导致无法被释放。\n-- 可以对比上面在函数里定义b的区别\nmemoryMonitor:update()\n--]]\n"
  },
  {
    "path": "utils/profiler.lua",
    "content": "--[[--\nlua性能分析工具\n@module profiler\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin\nLast Modified time 2020-01-16 13:45:50\n]]\n--[[\n    debug.getinfo(level, arg) : 返回一个包含函数信息的table\n    level表示函数调用的层级，表示要输出哪个函数的信息\n    arg是一个字符串，其中每个字符代表一组字段，用于指定希望获取那些信息，可以是\"n\",\"S\",\"I\",\"u\",\"f\",\"L\"中的一个或组合\n    n : 表示name（函数名）和namewhat（函数类型，field, upvalue, global）\n    S : 表示source（函数所属文件名）, linedefined（函数定义起始行号）, lastlinedefined（函数定义结束行号）, what（函数类型，Lua, C, main）, short_src（函数所属文件名，source的短版本）\n    l : 表示currentline（上级函数被调用的行号）\n    u : 表示nups（函数的upvalue值的个数）\n    f : 表示func（函数本身）\n    L : 表示activelines（一个包含行号的table，可理解为该函数运行的代码的行号）\n    debug.sethook(hook, mask, count) : 将一个函数作为钩子函数设入。字符串mask以及数字count决定了钩子将在何时调用\n    掩码是由下列字符组合成的字符串\n    \"c\" : 每当lua调用一个函数时，调用钩子\n    \"r\" : 每当lua从一个函数内返回时，调用钩子\n    \"l\" : 每当lua进入新的一行时，调用钩子\n    当count值大于0的时候，每执行完count数量的指令后就会触发钩子\n\n]]\n-- package.path = package.path .. \";..\\\\?.lua;\"\n-- require(\"_load\")\nlocal EMPTY_TIME                       = \"0.0000\"       -- Detect empty time, replace with tag below\nlocal emptyToThis                      = \"~\"\n\nlocal timeWidth                        = 7\nlocal relaWidth                        = 6\nlocal callWidth                        = 10\n\nlocal divider = \"\";\nlocal formatOutput                     = \"\";\nlocal formatFunTime                    = \"%04.4f\"\nlocal formatFunRelative                = \"%03.1f\"\nlocal formatFunCount                   = \"%\"..(callWidth-1)..\"i\"\nlocal formatHeader                     = \"\"\nlocal scale                            = 1;\n\nlocal function charRepetition(n, character)\n    local s   = {}\n    character = character or \" \"\n    for _ = 1, n do\n        table.insert(s,character)\n    end\n    return table.concat(s)\nend\n\nlocal Profiler = {}\n\n--[[\n创建一个性能分析工具对象\n@string variant 性能分析模式 \"call\" or \"time\"\n@usage\nlocal profiler = new_profiler(\"call\")\nprofiler:start()\n-- do something\nprofiler:stop()\nprofiler:dump_report_to_file(\"profile.txt\")\n]]\nlocal function new_profiler( variant )\n    if Profiler.running then\n        print(\"Profiler already running\")\n        return\n    end\n\n    variant = variant or \"time\"\n\n    if variant ~= \"time\" and variant ~= \"call\" then\n        print(\"Profiler method must be 'time' or 'call'\")\n        return\n    end\n\n    local newprof = {}\n    for k,v in pairs(Profiler) do\n        newprof[k] = v\n    end\n    newprof.variant = variant\n    return newprof\nend\n\n--[[\n启动性能分析，核心是利用debug.sethook对函数调用进行钩子\n每次只能启动一个\n]]\nfunction Profiler:start( ... )\n    if Profiler.running then\n        return\n    end\n    Profiler.running = self\n\n    self.caller_cache = {}\n    self.callstack = {}\n\n    self.start_time = os.clock()\n    if self.variant == \"time\" then\n\n    elseif self.variant == \"call\" then \n        -- 因为垃圾回收会导致性能分析下降严重，所以先放缓垃圾回收\n        self.setpause = collectgarbage(\"setpause\")\n        self.setstepmul = collectgarbage(\"setstepmul\")\n        collectgarbage(\"setpause\", 300)\n        collectgarbage(\"setstepmul\", 5000)\n\n        self.coroutine_create = coroutine.create\n        self.coroutines = {}\n        coroutine.create = function(...)\n            local co = self.coroutine_create(...)\n            table.insert(self.coroutines, co)\n            debug.sethook(co,profiler_hook_wrapper_by_call, \"cr\")\n            return co\n        end\n\n        debug.sethook(profiler_hook_wrapper_by_call, \"cr\")\n    else\n        error(\"Profiler method must be 'time' or 'call'\")\n    end\nend\n\n--[[\n    停止性能分析\n]]\nfunction Profiler:stop( ... )\n    if Profiler.running ~= self then\n        -- 如果没有启动则没有任何效果\n        return\n    end\n    self.end_time = os.clock()\n\n    if self.coroutine_create then\n        coroutine.create = self.coroutine_create\n        self.coroutine_create = nil\n    end\n    \n    -- 停止性能分析\n    debug.sethook(nil)\n    if self.variant == \"call\" then\n        -- 还原之前的垃圾回收设置\n        collectgarbage(\"setpause\", self.setpause) \n        collectgarbage(\"setstepmul\", self.setstepmul)\n    end\n    collectgarbage(\"collect\")\n    collectgarbage(\"collect\")\n    Profiler.running = nil\nend\n\n--[[\n    钩子函数入口\n]]\nfunction profiler_hook_wrapper_by_call( action )\n    if Profiler.running == nil then\n        debug.sethook(nil)\n    end\n    Profiler.running:analysis_call_info(action)\nend\n\n--[[\n    分析函数调用信息\n    @string action 函数调用类型 action return tail return\n]]\nfunction Profiler:analysis_call_info( action )\n    -- 获取当前的调用信息，注意该函数有一定的损耗\n    -- 0表示当前函数，即getinfo，1表示上一层调用即analysis_call_info，2表示再上一层，即profiler_hook_wrapper_by_call， 3即客户函数\n    local caller_info = debug.getinfo(3, \"Slfn\")\n\n    if caller_info == nil then\n        return\n    end\n\n    local last_caller = self.callstack[1]\n\n    if action == \"call\" then -- 进入函数，标记堆栈\n        local this_caller = self:get_func_info_by_cache(caller_info)\n        this_caller.parent = last_caller\n        this_caller.clock_start = os.clock()\n        this_caller.count = this_caller.count + 1\n        table.insert(self.callstack, 1, this_caller)\n    else\n        table.remove(self.callstack, 1) -- 移除顶部堆栈，有可能粗发连续触发return\n\n        if action == \"tail return\" then\n            return\n        end\n\n        local this_caller = self.caller_cache[caller_info.func]\n        if this_caller == nil then\n            return\n        end\n\n        -- 计算本次函数调用时长\n        this_caller.this_time = os.clock() - this_caller.clock_start \n        -- 该函数累加调用时间\n        this_caller.time = this_caller.time + this_caller.this_time  \n\n        -- 更新父类信息\n        if this_caller.parent then\n            local func = this_caller.func\n            -- 更新父类中存储的该子函数的调用次数\n            this_caller.parent.children[func] = (this_caller.parent.children[func] or 0) + 1\n            -- 更新父类中存储的该子函数的总调用时间\n            this_caller.parent.children_time[func] = (this_caller.parent.children_time[func] or 0) + this_caller.this_time\n            \n            if caller_info.name == nil then\n                -- 统计无名函数调用时间\n                this_caller.parent.unknow_child_time = this_caller.parent.unknow_child_time + this_caller.this_time\n            else\n                -- 统计有名函数调用时间\n                this_caller.parent.name_child_time = this_caller.parent.name_child_time + this_caller.this_time\n            end\n        end\n    end\nend\n\n--[[\n    获取缓存里的函数信息\n    @info 函数调用信息debug.getinfo返回的数据\n]]\nfunction Profiler:get_func_info_by_cache( info )\n    local func = info.func\n    local ret = self.caller_cache[func]\n    if ret == nil then\n        ret = {}\n        ret.func = func\n        ret.count = 0 -- 调用次数\n        ret.time = 0 -- 时间\n        ret.unknow_child_time = 0 --没有名字的函数的调用时间\n        ret.name_child_time = 0 -- 有名字的函数的调用时间\n        ret.children = {}\n        ret.children_time = {}\n        ret.func_info = info\n        self.caller_cache[func] = ret\n    end\n    return ret\nend\n\n--格式化成表格样式\nfunction Profiler:format_header(ordering,lines,totalTime)\n    local TABL_REPORTS = {};\n    local maxFileLen = 0;\n    local maxFuncLen = 0;\n    for i,func in ipairs(ordering) do\n        local record = self.caller_cache[func]\n        local reportInfo                         = {\n            count  = record.count,\n            timer  = record.time,\n            src     = record.func_info.short_src,\n            name    = record.func_info.name or \"unknow\",\n            linedefined = record.func_info.linedefined,\n            what = record.func_info.what,\n            source = record.func_info.source;\n        }\n\n        reportInfo.src = self:pretty_name(func,true);\n\n        --计算最长的名字\n        if string.len(reportInfo.src) > maxFileLen and reportInfo.count > 0  then\n            maxFileLen = string.len(reportInfo.src) + 1;\n        end\n\n        if string.len(reportInfo.name) > maxFuncLen and reportInfo.count > 0 then\n            maxFuncLen = string.len(reportInfo.name) + 1;\n        end\n\n        table.insert(TABL_REPORTS,reportInfo);\n\n    end\n\n    if maxFileLen>=99 then --必须如此处理，不然会报错越界\n        maxFileLen = 99;\n    end\n\n    --     if maxFuncLen>100 then\n    --     maxFuncLen = 100;\n    -- end\n\n\n    -- print(maxFileLen,\"maxFileLen\")\n    formatOutput                     = \"| %-\"..maxFileLen..\"s: %-\"..maxFuncLen..\"s: %-\"..timeWidth..\"s: %-\"..relaWidth..\"s: %-\"..callWidth..\"s|\\n\"\n    -- dump(formatOutput)\n    formatHeader                     = string.format(formatOutput, \"FILE\", \"FUNCTION\", \"TIME\", \"%\", \"Call count\")\n    divider = charRepetition(#formatHeader-1, \"-\")..\"\\n\"\n\n\n    table.insert(lines, \"\\n\"..divider)\n    table.insert(lines, formatHeader)\n    table.insert(lines, divider)\n\n    local totalCount = 0;\n    for i,reportInfo in ipairs(TABL_REPORTS) do\n        if reportInfo.count > 0 and reportInfo.timer <= totalTime then\n            local count             = string.format(formatFunCount, reportInfo.count)\n            local timer             = string.format(formatFunTime, reportInfo.timer)\n            local relTime           = string.format(formatFunRelative, (reportInfo.timer / totalTime) * 100)\n            if timer == EMPTY_TIME then\n                timer             = emptyToThis\n                relTime           = emptyToThis\n            end\n            local outputLine    = string.format(formatOutput, reportInfo.src,reportInfo.name, timer, relTime, count)\n            table.insert(lines, outputLine)\n\n            totalCount = totalCount + reportInfo.count;\n        end\n    end\n    table.insert(lines, divider)\n    table.insert(lines, \"\\n\\n\")\n\n    table.insert(lines, 2,\"Total call count spent in profiled functions: \" ..\n        totalCount.. \"\\n\\n\")\nend\n\n--[[--\n    生成报表table\n    @return     table     报表\n    @return     number     性能分析总时间\n    @usage\n        local new_profiler = import(\"bos.core.profiler\")\n        local profiler = new_profiler(\"call\")\n        profiler:start();\n        -- do something\n        profiler:stop();\n        profiler:report();\n]]\nfunction Profiler:report()\n    local lines = {};\n    table.insert(lines,[[Lua Profile output created by profiler.lua. author: iwiniwin ]])\n    table.insert(lines, \"\\n\\n\" )\n    local total_time = self.end_time - self.start_time\n\n    table.insert(lines, 1,\"Total time spent in profiled functions: \" ..\n        string.format(\"%5.3g\",total_time) .. \"s\\n\\n\")\n\n    -- This is pretty awful.\n    local terms = {}\n    if self.variant == \"time\" then\n\n    elseif self.variant == \"call\" then\n        terms.capitalized = \"Call\"\n        terms.single = \"call\"\n        terms.pastverb = \"called\"\n        local ordering = {}\n\n        for func,record in pairs(self.caller_cache) do\n            table.insert(ordering, func)\n        end\n\n        table.sort( ordering,\n            function(a,b) return self.caller_cache[a].time > self.caller_cache[b].time end\n        )\n\n        --生成头部表格信息\n        self:format_header(ordering,lines,total_time);\n\n        for i,v in ipairs(ordering) do\n            local func = ordering[i]\n            local record = self.caller_cache[func]\n            if record.count and record.count > 0 then --- 标记数量大于0的\n                local thisfuncname = \" \" .. self:pretty_name(func) .. \" \"\n                if string.len( thisfuncname ) < 42 then\n                    thisfuncname =\n                        string.rep( \"-\", math.floor((42 - string.len(thisfuncname))/2 )) .. thisfuncname\n                    thisfuncname =\n                        thisfuncname .. string.rep( \"-\", 42 - string.len(thisfuncname) )\n                end\n\n                --单个函数的总时间减去子函数的时间,获得自身的时间\n                local timeinself = record.time - (record.unknow_child_time + record.name_child_time)\n                if timeinself < 0 then\n                    timeinself = 0;\n                end\n\n                local children =  record.unknow_child_time+record.name_child_time\n                if children > record.time then\n                    children = record.time\n                end\n\n                timeinself = timeinself * scale;\n\n                table.insert(lines, string.rep( \"-\", 19 ) .. thisfuncname ..\n                    string.rep( \"-\", 19 ) .. \"\\n\" )\n\n                table.insert(lines, terms.capitalized..\" count:         \" ..\n                    string.format( \"%4d\", record.count ) .. \"\\n\" )\n                table.insert(lines, \"Time spend total:       \" ..\n                    string.format( \"%4.4f\", record.time * scale) .. \"s\\n\" )\n                table.insert(lines, \"Time spent in children: \" ..\n                    string.format(\"%4.4f\",(children) * scale) ..\n                    \"s\\n\" )\n\n                table.insert(lines, \"Time spent in self:     \" ..\n                    string.format(\"%4.4f\", timeinself) .. \"s\\n\" )\n\n                -- Report on each child in the form\n                -- Child  <funcname> called n times and took a.bs\n                local added_blank = 0\n                for k,v in pairs(record.children) do\n                    if added_blank == 0 then\n                        table.insert(lines, \"\\n\" ) -- extra separation line\n                        added_blank = 1\n                    end\n                    table.insert(lines, \"Child \" .. self:pretty_name(k) ..\n                        string.rep( \" \", 41-string.len(self:pretty_name(k)) ) .. \" \" ..\n                        terms.pastverb..\" \" .. string.format(\"%6d\", v) )\n                    table.insert(lines, \" times. Took \" ..\n                        string.format(\"%4.5f\", record.children_time[k] * scale ) .. \"s\\n\" )\n\n                end\n\n                table.insert(lines, \"\\n\" ) -- extra separation line\n\n            end\n\n        end\n    end\n\n    table.insert(lines, [[\nEND\n]] )\n\n\n    return lines,total_time\nend\n\n--[[--\n    输出报表到文件\n    @tparam     table     self    Profiler对象\n    @tparam     string     outfile    文件名称\n    @return   number 本次总共花费时间\n    @usage\n        local new_profiler = import(\"bos.core.profiler\")\n        local profiler = new_profiler(\"call\")\n        profiler:start();\n        -- do something\n        profiler:stop();\n        profiler:dump_report_to_file(\"path\");\n]]\nfunction Profiler.dump_report_to_file(self,outfile)\n    local outfile = io.open(outfile, \"w+\" )\n    local lines, total_time= self:report()\n    for i,v in ipairs(lines) do\n        outfile:write(v)\n    end\n    outfile:flush()\n    outfile:close()\n    return total_time\nend\n\n\n--[[\n    美化名称，输出可以看懂的信息\n    @tparam     function    func  函数\n    @boolean    force 是否强制\n]]\nfunction Profiler:pretty_name(func,force)\n\n    -- Only the data collected during the actual\n    -- run seems to be correct.... why?\n    local info = self.caller_cache[func].func_info\n\n    local name = \"\"\n    if info.what == \"Lua\" and force then\n        name = \"L:\" .. info.short_src ..\":\" .. info.linedefined\n        return name;\n    end\n    if info.what == \"Lua\" then\n        name = \"L:\"\n    end\n    if info.what == \"C\" then\n        name = \"C:\"\n    end\n    if info.what == \"main\" then\n        name = \" :\"\n    end\n    if info.name == nil then\n        name = name .. \"<\"..tostring(func) .. \">\"\n    else\n        name = name .. info.name\n    end\n\n    if info.source then\n        name = name .. \"@\" .. info.source\n    else\n        if info.what == \"C\" then\n            name = name .. \"@?\"\n        else\n            name = name .. \"@<string>\"\n        end\n    end\n    name = name .. \":\"\n    -- if info.what == \"C\" then\n    --     name = name .. \"unknow line\"\n    -- else\n    --     name = name .. info.linedefined\n    -- end\n    name = name .. info.linedefined\n\n    return name\nend\n\nreturn new_profiler\n\n\n\n"
  }
]